本文件为 AI Agent 代码生成提供强制规则约束,定义生成边界与规范。 所有规则均为强制性,Agent 生成代码时必须严格遵守。违反规则的代码不可提交。
| 技术 | 版本 | 禁止事项 |
|---|---|---|
| uni-app | 3.0.0-5000720260410001 | 禁止使用 uni-app v2 |
| Vue | 3.5.32 | Composition API only,禁止 Options API |
| TypeScript | ^4.9.4 | 注意:uni-app 限制,不可升级到 TS 5 |
| Vite | 5.2.8 | 禁止升级到 Vite 6+(uni-app 兼容性) |
| Pinia | ^3.0.4 | 禁止 Vuex |
| Sass | ^1.99.0 | 样式预处理统一使用 SCSS |
| 微信小程序 appid | wxef6ffc2591d04b1b |
禁止修改 |
mp-weixin)pnpm dev:mp-weixin(开发)、pnpm build:mp-weixin(构建)| 配置项 | 值 |
|---|---|
| 后端服务地址 | https://dev-haha.kuaiyuman.cn/api(开发环境) |
| 后端应用 | haha-miniapp(端口 7077,context-path: /api) |
| 请求方式 | uni.request(封装在 src/utils/request.ts) |
| 认证方式 | 请求头 accessToken: {token} |
| 请求超时 | 30000ms |
src/
├── api/ # API 请求函数(每个模块一个文件)
├── components/ # 公共组件
├── pages/ # 小程序页面(每个页面一个目录,含 .vue 文件)
├── static/ # 静态资源(图标、图片、动画样式)
│ ├── icons/ # SVG 图标
│ └── images/ # 图片资源
├── styles/ # 全局样式
├── utils/ # 工具函数
│ ├── request.ts # HTTP 请求封装
│ ├── auth.ts # 认证工具
│ └── config.ts # API 配置
├── App.vue # 应用入口(onLaunch/onShow/onHide)
├── main.ts # 创建应用(createSSRApp + Pinia)
├── manifest.json # 小程序配置
├── pages.json # 页面路由与导航栏配置
└── uni.scss # 全局 SCSS 变量(Design Tokens)
强制结构:每个页面是一个独立目录,包含同名的 .vue 文件
pages/{pageName}/
└── {pageName}.vue # 页面组件
页面 Vue 文件模板:
<template>
<view class="container">
<!-- 页面内容 -->
</view>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { onShow, onLoad } from '@dcloudio/uni-app';
import { xxxApi } from '@/api/xxx';
// 响应式数据
const dataList = ref<XxxType[]>([]);
const loading = ref(false);
// 页面加载
onLoad((options: any) => {
// 处理页面参数
});
// 页面显示(每次从后台切回都触发)
onShow(() => {
// 刷新数据
});
// 方法
const loadData = async () => {
if (loading.value) return;
loading.value = true;
try {
uni.showLoading({ title: '加载中...', mask: true });
const result = await xxxApi();
dataList.value = result;
} catch (error: any) {
console.error('加载失败:', error);
// 错误已在 request.ts 中统一 toast 提示
} finally {
uni.hideLoading();
loading.value = false;
}
};
</script>
<style lang="scss">
/* 使用 Design Token 变量,禁止硬编码颜色/间距 */
.container {
min-height: 100vh;
background: $color-bg-secondary;
padding: $spacing-lg;
box-sizing: border-box;
}
</style>
页面注册:新增页面必须在 pages.json 中注册
{
"path": "pages/xxx/xxx",
"style": {
"navigationBarTitleText": "页面标题",
"navigationBarBackgroundColor": "#FFD700",
"navigationBarTextStyle": "black"
}
}
强制规则:
#FFD700(品牌黄)black<view> 代替 <div>,<text> 代替 <span><image> 代替 <img>,必须指定 mode 属性uni.xxx API 代替浏览器原生 APIrpx 单位代替 px(1rpx = 屏幕宽度/750)@dcloudio/uni-app 生命周期钩子(onLoad、onShow、onHide)document、window、localStorage)uni.getStorageSync/setStorageSync 代替 localStorageuni.navigateTo/uni.reLaunch/uni.switchTab强制规则:
src/utils/request.ts 中的 request/get/post/put/delAPI 文件模板:
/**
* xxx相关API
*/
import { get, post } from '../utils/request';
/**
* 数据接口定义
*/
export interface XxxInfo {
id: string; // 雪花ID,必须为 string 类型
name: string;
status: number;
createTime: string;
}
/**
* 请求参数接口
*/
export interface XxxListRequest {
page?: number;
pageSize?: number;
status?: number;
}
/**
* 获取xxx列表
* @param params 查询参数
*/
export const getXxxList = (params?: XxxListRequest): Promise<XxxInfo[]> => {
return post<XxxInfo[]>('/xxx/list', params || {});
};
/**
* 获取xxx详情
* @param id xxx ID
*/
export const getXxxDetail = (id: string): Promise<XxxInfo> => {
return get<XxxInfo>(`/xxx/${id}`);
};
禁止事项:
uni.request(必须通过 request.ts 封装)uni.showToast)axios(小程序不支持)API_CONFIG.baseUrl)已有封装,禁止重写。关键行为:
accessToken 请求头code === 200 || code === 0 为成功,返回 data 字段uni.showToast 提示skipAuth 参数跳过认证(仅用于免登录接口)免登录接口示例:
export const getDeviceProducts = (deviceId: string): Promise<DeviceProductsResponse> => {
return get<DeviceProductsResponse>(`/device/products/${deviceId}`, undefined, { skipAuth: true });
};
已有封装,禁止重写。关键行为:
| 函数 | 用途 |
|---|---|
getToken() |
获取 accessToken |
setToken(token) |
存储 accessToken |
removeToken() |
清除 accessToken |
isLoggedIn() |
判断是否已登录 |
checkAuth(redirectUrl?) |
路由守卫,未登录跳转登录页 |
clearAuth() |
清除所有登录信息 + 购物流程数据 |
logout() |
清除登录信息 + reLaunch 到登录页 |
Token 存储 key:accessToken(uni.getStorageSync)
必须使用 uni.scss 中定义的 Design Token,禁止硬编码:
/* 主色系 */
$color-primary: #FFC107; // 品牌黄
$color-primary-light: #FFE082;
$color-primary-dark: #FFA000;
/* 文字颜色 */
$color-text-primary: #2C2C2C; // 主文字
$color-text-secondary: #8C8C8C; // 辅助文字
$color-text-tertiary: #BDBDBD; // 三级文字
/* 背景色 */
$color-bg-primary: #FFFFFF;
$color-bg-secondary: #FAFAFA;
$color-bg-tertiary: #F5F5F5;
/* 边框/阴影 */
$color-border: #EEEEEE;
$shadow-primary: 0 8rpx 24rpx rgba(255, 193, 7, 0.25);
/* 间距系统 */
$spacing-xs: 8rpx;
$spacing-sm: 16rpx;
$spacing-md: 24rpx;
$spacing-lg: 32rpx;
$spacing-xl: 48rpx;
$spacing-xxl: 64rpx;
/* 圆角 */
$radius-sm: 8rpx;
$radius-md: 16rpx;
$radius-lg: 24rpx;
$radius-xl: 32rpx;
/* 动画 */
$duration-fast: 150ms;
$duration-normal: 300ms;
$ease-out: cubic-bezier(0.25, 0.1, 0.25, 1);
$bounce: cubic-bezier(0.68, -0.55, 0.265, 1.55);
| 规则 | 说明 |
|---|---|
| 单位 | 统一使用 rpx(设计稿 750rpx 宽度),禁止 px/rem/em |
| 颜色 | 使用 Design Token 变量,禁止硬编码颜色值 |
| 间距 | 使用 $spacing-xxx 变量 |
| 圆角 | 使用 $radius-xxx 变量 |
| 阴影 | 使用 $shadow-xxx 变量 |
| 动画 | 使用 $duration-xxx + $ease-out |
| 作用域 | 全局样式不加 scoped,页面级样式尽量加 scoped |
| 性能 | 动画元素添加 will-change: transform, opacity + transform: translateZ(0) |
px 作为布局单位(仅 rpx)filter 属性(小程序性能差)backdrop-filter(兼容性问题)position: fixed 在列表项中(性能问题)box-shadow 动画| 场景 | API | 说明 |
|---|---|---|
| 普通跳转 | uni.navigateTo |
可返回,最多 10 层栈 |
| 不可返回跳转 | uni.reLaunch |
关闭所有页面,打开目标页 |
| Tab 切换 | uni.switchTab |
用于 tabBar 页面(当前无 tabBar) |
| 返回上一页 | uni.navigateBack |
-- |
| 重定向 | uni.redirectTo |
关闭当前页,打开目标页 |
// 传递参数
uni.navigateTo({
url: '/pages/xxx/xxx?id=' + String(id) + '&name=' + name
});
// 接收参数
onLoad((options: any) => {
const id = options.id; // 字符串
});
强制规则:
String(id) 防止精度丢失redirect 参数:/pages/login/login?redirect=/pages/index/index以下页面允许未登录用户直接访问(定义在 App.vue):
const LOGIN_WHITE_LIST = [
'pages/login/login',
'pages/products/products', // 柜内商品展示
];
新增免登录页面时,必须同步更新此白名单。
用户扫码 → 检查登录 → 检查支付分 → 调用 scanDoor API → 存储 deviceId/outTradeNo/orderNo → 跳转购物页
强制规则:
https://hh.hahabianli.com/B142977?_wxpmm0=.../\/([A-Z0-9]+)(\?|$)/paidAmount || totalAmount(优先使用 paidAmount)(amount || 0).toFixed(2)¥status === 1)可申请退款open-type="getPhoneNumber")/login/miniapp-phone,参数 { code, phoneCode }| 编号 | 约束 | 说明 |
|---|---|---|
| C1 | 禁止 DOM API | 无 document/window/localStorage |
| C2 | 禁止 axios | 小程序不支持 XHR,必须用 uni.request |
| C3 | 图片必须指定 mode | <image mode="aspectFit/aspectFill"> |
| C4 | 禁止 v-html | 小程序不支持 |
| C5 | 禁止动态组件 | <component :is> 有限制 |
| C6 | 使用 rpx 单位 | 适配不同屏幕宽度 |
| C7 | 事件使用 @tap | 优先使用 @tap 代替 @click |
| C8 | 按钮反馈 | 操作按钮添加 hover-class |
| C9 | 安全区适配 | 底部 padding-bottom: env(safe-area-inset-bottom) |
| C10 | 包体积 | 单个包不超过 2MB,主包不超过 2MB |
| 编号 | 规则 | 说明 |
|---|---|---|
| P1 | 动画使用 transform + opacity |
避免触发 layout/paint |
| P2 | 动画元素添加 will-change |
will-change: transform, opacity |
| P3 | 动画元素添加 transform: translateZ(0) |
开启 GPU 加速 |
| P4 | 禁止 filter: blur() |
小程序性能极差 |
| P5 | 禁止复杂 box-shadow 动画 |
改用静态阴影 |
| P6 | 长列表使用虚拟列表 | 超过 100 项必须虚拟滚动 |
| P7 | 图片懒加载 | <image lazy-load> |
| P8 | 轮询必须有超时和清理 | 避免页面切换后持续请求 |
| 需求 | 使用 | 禁止 |
|---|---|---|
| 存储 | uni.getStorageSync/setStorageSync |
localStorage |
| 路由 | uni.navigateTo/reLaunch |
router.push |
| 提示 | uni.showToast/showLoading/showModal |
自定义 toast |
| 扫码 | uni.scanCode |
第三方扫码 |
| 拨号 | uni.makePhoneCall |
tel: 链接 |
| 震动 | uni.vibrateShort |
-- |
| 登录 | uni.login |
-- |
| 编号 | 陷阱 | 规则 |
|---|---|---|
| T1 | 雪花 ID 使用 number 类型 | 禁止。必须为 string 类型,防止 JS 精度丢失 |
| T2 | 路由传参直接传 number ID | 禁止。必须 String(id) |
| T3 | 使用 localStorage |
禁止。必须 uni.getStorageSync/setStorageSync |
| T4 | 使用 axios |
禁止。小程序不支持,使用封装的 request.ts |
| T5 | 使用 document/window |
禁止。小程序无 DOM |
| T6 | 使用 px 单位 |
禁止。统一使用 rpx |
| T7 | 硬编码颜色值 | 禁止。使用 uni.scss 中的 Design Token |
| T8 | 新页面不注册 pages.json | 禁止。每个新页面必须在 pages.json 中配置 |
| T9 | 免登录页面不更新白名单 | 禁止。同步更新 App.vue 中 LOGIN_WHITE_LIST |
| T10 | 轮询不设置超时 | 禁止。所有轮询必须有 timeout + 页面卸载清理 |
| T11 | 在 API 层处理 UI 逻辑 | 禁止。API 层只负责请求 |
| T12 | 使用 <div>/<span>/<img> |
禁止。使用 <view>/<text>/<image> |
| T13 | 导航栏颜色不一致 | 禁止。统一 #FFD700 背景 + black 文字 |
| T14 | 实付金额取 totalAmount | 禁止。优先取 paidAmount,fallback totalAmount |
| T15 | 不处理安全区 | 禁止。底部内容必须适配 safe-area-inset-bottom |