Files
yx-platform-prototype/AGENTS.md
T
fengpuandClaude Opus 4.7 74d1488d3b feat: 设备中心模块全量落地 + 列表页同级数据切换规范
- 设备管理(manage):5 类设备 tab 改为 a-radio-group button-style="solid"(参考 vue 研发项目 unitReportData);新增投用状态字段、固件版本→软件版本、移除数据中转;所属租户/布点场所非必填,留空自动判定在库;导出改异步(导出列表数据 + 查看导出任务)
- 设备统计(statistics):从图标展示重构为列表页,14 列定义 + 12 维搜索条件 + 4 张统计卡 + a-table-summary 统计行 + 异步导出
- 设备工作台(workbench/overview):新增概览页 + DonutChart 组件
- 资产/库存/运维/分发/租户场所等子模块全量落地(含 mock handler、6 文件结构、modal/drawer 子组件)
- 系统设置/运营中心扁平化页面落地(员工/角色/菜单/应用/字典/租户列表/小程序用户)
- 实施运维小程序(miniprogram/ops):工作台/工单/扫码/巡检/我的 5 个 tab 页
- 规范:list-page-spec §7.4 新增「同级数据切换(radio-button 样式,强制)」;tabs-page-spec 文首新增「〇、适用范围」明确与同表过滤场景的边界
- 路由/菜单(nav.ts + routes.ts):同步设备中心菜单层级与 ops 小程序实例

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-03 14:53:56 +08:00

14 KiB
Raw Blame History

AGENTS.md — Codex 约束文件

本文件为 Codex / Codex CLI 等 AI 编码助手在本项目工作时的约束规范。

权威源:详细规则以同目录 CLAUDE.md 为准;本文件为 Codex 友好的精简快查版。 当本文件与 CLAUDE.md 冲突时,CLAUDE.md 为准


0. 语言强制

  • 所有对话回复必须使用简体中文(代码标识符除外)
  • 所有代码注释必须使用简体中文JavaDoc / JSDoc / Docstring 同样要求)
  • 变量名、函数名、类名仍使用英文(遵循编程语言规范)

1. 项目定位

智养餐饮平台 V3 的高保真交互原型工程:Vue 3 + Vite + Ant Design Vue + Vant 半工程化方案。

核心目标:让产品经理画的原型 ≈ 开发要写的代码骨架,研发接手时改动最小。

四个终端(互不引用)

终端 key UI 库 业务定位
综合管理后台 admin-portal Ant Design Vue 平台级管理(单应用形态,3 个一级菜单:系统设置 / 运营中心 / 设备中心)
租户运营后台 tenant-portal Ant Design Vue 多应用形态(17 个独立可部署应用,如 nutrition / monitor 等)
C 端微信小程序 miniprogram Vant 4375×812 用户自助查询、上报、咨询
硬件终端屏 hardware 自定义(动态 viewport 净菜柜屏 / 自助查询屏 / 大屏

URL 规范

  • 综合后台:/admin-portal/<section>/<page>(无应用层)
  • 租户后台:/tenant-portal/<app>/<section>/<page>(例:/tenant-portal/nutrition/arc/employee
  • 应用首页:/tenant-portal(应用网格卡片)

2. 技术栈与启动

类别 选型
框架 Vue 3.5 + TypeScript 5.9
构建 Vite 8(端口 5180
Web 端 UI Ant Design Vue 4.2
移动端 UI Vant 4
路由 Vue Router 4Hash 模式)
状态管理 不引入 Pinia
样式 LESS + CSS 变量
自动导入 unplugin-auto-import + unplugin-vue-components
npm install
npm run dev       # 端口 5180
npm run build

门禁密码.env):设计 design2026 / 研发 dev2026,登录后 7 天有效。


3. 路径别名(与 vue 研发项目 1:1 对齐)

别名 指向
@ src/
@meta src/meta/(菜单数据,原型特有)
@layouts src/layouts/
@components src/components/
@composables src/composables/(原型特有)
@pages src/pages/(研发项目用 @views
@router src/router/
@utils src/utils/
@axios src/axios/
@assets src/assets/
@directives src/directives/
@api src/api/

4. 目录结构(精简版)

src/
├── main.ts                  入口:mock handlers / directives / antd 静态方法 初始化
├── App.vue                  必须用 <a-config-provider :locale="zhCN"> 包裹
├── router/
│   ├── index.ts             门禁 + designerOnly 守卫
│   └── routes.ts            由 nav.ts 自动派生
├── meta/nav.ts              🌟 菜单数据单一来源(adminApps / tenantApps
├── axios/                   🆕 假 axios 层(签名与研发 1:1
│   ├── index.ts             getRequest / postRequest / putRequest / deleteRequest / restfulRequest / uploadRequest / downloadRequest
│   ├── mockBus.ts           registerMock / dispatchMock200~500ms 延迟)
│   └── handlers/<模块>/<页面>.ts   🌟 mock handler 注册(禁止写在 useTable.ts
├── api/index.ts             🆕 跨模块公共接口(字典 / 部门树 / 组织树)
├── utils/antDesign/         table.ts / popUp.ts / select.ts
├── directives/              v-no-space / v-only-number / v-only-alphanumeric ...
├── assets/styles/listPage.less   列表页统一布局
├── composables/useVisibility.ts  可见性核心(原型特有)
├── layouts/
│   ├── PortalLayout.vue     admin / tenant 共用
│   ├── MiniprogramLayout.vue
│   ├── HardwareLayout.vue
│   └── components/          TopBar / Sidebar / AppTabBar / RoleBadge
├── components/              StatCard / FilterBar / TableCard / PageHeader / PlaceholderPage / AppPlaceholder
├── pages/                   按 端/应用/一级菜单 分目录
│   ├── home/Home.vue        🏠 全局导航首页
│   ├── gate/Gate.vue        🔒 门禁页
│   ├── console/             🎛 设计完成度控制台
│   ├── prd/                 📄 PRD 查看
│   ├── overview/            🖼 设计总览
│   ├── admin-portal/<section>/
│   ├── tenant-portal/<app>/<section>/<page>/
│   ├── miniprogram/<section>/
│   └── hardware/<section>/
├── prd/                     markdown 文档 + 截图(≤ 60KB
├── docs/                    🌟 业务架构 + 代码规范(生成代码前必读)
└── types/                   自动生成 + shims

5. 列表页 6 文件结构(强制)

arcEmployee/
├── arcEmployee.vue          模板:FilterBar + TableCard,仅引用 hook 暴露的字段/方法
├── types.ts                 接口/表单/记录类型
├── api/index.ts             listRequest / addRequest / editRequest / deleteRequest / detailRequest
└── init/
    ├── usePage.ts           组合根:发起接口 / 子组件 ref / 打开方法
    ├── useSearch.ts         reactive(search) + createSearchKey() + 下拉数据
    └── useTable.ts          TableState 对象 + columns(禁调接口、禁存数据 ref)

子组件按需追加:

  • component/modal/<名>/(编辑/新增弹框,emit load
  • component/drawer/<名>/(详情抽屉,emit ok

6. 代码风格强制约束

6.1 函数与请求

// ✅ 正确:箭头函数 + .then 链式
const listRequest = () => {
  table.spin = true
  getList(searchParams)
    .then((res) => {
      table.dataSource = res.data
    })
    .catch(() => {})
    .finally(() => {
      table.spin = false
    })
}

// ❌ 禁止:function 关键字 + async/await
async function listRequest() {
  const res = await getList(searchParams)
}

6.2 HTTP 调用

// ✅ 走 @axios,签名与 vue 研发项目 1:1
import { postRequest, getRequest } from '@axios'
postRequest('/api/employee/list', params)

// ❌ 禁止直接 import axios
import axios from 'axios'

6.3 弹窗静态方法

// ✅ 必须用 useAntdStaticMethods
import { useAntdStaticMethods } from '@utils/antDesign/popUp'
const { message, Modal, notification } = useAntdStaticMethods()

// ❌ 禁止直接 import
import { message } from 'ant-design-vue'

6.4 表格状态

// ✅ useTable 返回 TableState 对象,数据存 table.dataSource
const table = reactive<TableState<Employee>>({
  dataSource: [],
  loading: false,
  pagination: createPaginationConfig(),
  columns: [...],
})

// ❌ 禁止单独 dataList ref
const dataList = ref<Employee[]>([])

6.5 搜索状态

// ✅ reactive + createSearchKey 工厂 + Object.assign 重置
const createSearchKey = () => ({ name: undefined, status: undefined })
const search = reactive(createSearchKey())
const resetSearch = () => {
  Object.assign(search, createSearchKey())
  searchQuery()
}

// ✅ searchQuery 重置页码但保留排序
const searchQuery = () => {
  table.pagination.current = 1
  listRequest()
}

// ❌ 禁止 Object.keys 遍历清空
// ❌ 禁止 searchQuery 调 resetTable()

6.6 子组件 ref

// ✅ 在父 usePage.ts 定义,命名 open + 组件名
const editModalRef = ref<InstanceType<typeof EditEmployeeModal>>()
const openEditEmployeeModal = (record?: Employee) => {
  editModalRef.value?.open(record)
}

// ❌ 禁止在 .vue 中定义子组件 ref
// ❌ 禁止命名为 open / show / visible

6.7 删除二次确认

// ✅ Modal.confirm + okType: 'danger'
const onDelete = (record: Employee) => {
  Modal.confirm({
    title: '确认删除?',
    okType: 'danger',
    onOk: () => deleteRequest({ id: record.id }),
  })
}

// ✅ 批量按钮必须 disabled
<a-button :disabled="!selectedRowKeys.length" @click="onBatchDelete">批量删除</a-button>

6.8 空值 / 时间 / ID

  • 空值统一 {{ x ?? '—' }}(禁空串/null
  • 时间 YYYY-MM-DD HH:mm:ss,日期 YYYY-MM-DD
  • ID 统一 string | undefined(禁 number

6.9 类型

  • 禁用 anyTS 严格模式

7. Mock 数据规则

// ✅ 位置:src/axios/handlers/<模块>/<页面>.ts
// 文件示例:src/axios/handlers/nutrition/employee.ts
import { registerMock } from '@axios/mockBus'

registerMock('GET', '/api/employee/list', (params) => {
  return {
    code: '00000',
    data: { records: [...], total: 8 }, // ⚠️ 单个 dataset ≤ 8 条
  }
})

禁止

  • 写在 useTable.ts
  • 单个 dataset 超过 8 条(原型用于展示而非性能压测)
  • api/index.ts 写业务判断(如 if res.code === '00000'

8. 列表页 UI 规范

  • 不渲染 PageHeader(菜单 + 顶部历史栏已表达所在位置)
  • TableCard:table 对象(推荐),不显示 title
  • FilterBar 用 default slot 写裸 a-form-item(推荐),或 :fields 配置驱动
  • 顶部按钮(导出/新增/导入/批量删除)放 TableCard #toolbar 左上方,必须带 antd icon
  • 操作列 <a-button type="link"> 一律不加图标;多个按钮用 <a-divider type="vertical" /> 分隔
  • 次要操作TableCard #extra 右上方
  • 统计卡FilterBar 上方(可选)
  • 顶栏高度 固定 64px

⚠️ CRUD 不是列表页标配:是否有"新增/编辑/删除/导出"由业务决定,Codex 不要默认生成。


9. 图标规范(强制)

  • 菜单图标 / 操作按钮图标必须取自 @ant-design/icons-vue 4.x
  • 禁止 emoji / 自定义 svgNavApp.icon 仅在应用网格首页 + 应用切换器允许 emoji)

标准映射

操作 图标
查询 SearchOutlined
重置 RedoOutlined
新增 PlusOutlined
编辑 EditOutlined
删除 DeleteOutlined
导入 UploadOutlined
导出 DownloadOutlined
查看详情 EyeOutlined
查看任务 UnorderedListOutlined

10. Modal / Drawer 约束

  • a-modal 必须 :keyboard="false" :mask-closable="false"(防编辑数据丢失)
  • a-drawer(含列表型)必须 destroy-on-close
  • 弹框/抽屉 .vue禁写业务逻辑,逻辑放 init/ hooks
  • params.id 判断新增/编辑(禁用 pageInfo.title / type 文本判断)
  • addRequest 必须 delete params.id
  • finally 中必须 pageInfo.spin = false
  • Modal emit loadDrawer emit ok

11. 禁止事项(速查)

跨模块 / 跨端

  • 跨端引用(admin ↛ tenant ↛ miniprogram ↛ hardware
  • 跨应用引用(同 portal 下不同 NavApp 之间)
  • Vant 用在后台 / antd 用在小程序
  • api/index.ts 写业务私有接口

列表页结构

  • 省略 init 三件套
  • 业务逻辑写在模板
  • 渲染 PageHeader
  • TableCard 显式传 title
  • 主操作按钮放在页面顶部独立位置

Mock / 接口

  • mock 数据写在 useTable.ts
  • 单个 dataset 超过 8 条
  • 业务代码直接 import axios
  • api/index.ts 写业务判断

代码风格

  • async/await(统一 .then().catch().finally()
  • function 关键字(统一箭头函数)
  • 直接 import { message } from 'ant-design-vue'
  • any 类型

Hook 拆分

  • useTable.ts 调接口
  • usePage.ts 维护独立 dataList ref
  • usePage.ts 写表格列或下拉请求
  • useSearchObject.keys 清空
  • searchQueryresetTable()
  • .vue 定义子组件 ref
  • 子组件打开方法命名 open / show / visible

Modal / Drawer

  • 弹框/抽屉 .vue 写业务逻辑
  • a-modal 不加 :keyboard="false" :mask-closable="false"
  • 省略 finallypageInfo.spin = false
  • 用文本判断新增/编辑
  • addRequest 保留 id 字段
  • 省略 a-drawer 的 destroy-on-close

数据展示

  • 删除/批量不经二次确认
  • 批量按钮未选中时可点击
  • 空值显示为空串/null
  • 时间/日期格式不统一

图标 / UI

  • emoji / 自定义 svg 作菜单或按钮图标
  • 操作列 <a-button type="link"> 加图标
  • AI 脚手架默认生成 CRUD 按钮
  • 操作列按钮之间不加 <a-divider type="vertical" />
  • customRender 写 JSX(统一用 h()
  • a-input-number 不设 style="width: 100%"
  • 顶栏高度偏离 64px
  • App.vue 漏写 <a-config-provider :locale="zhCN">

12. 进一步阅读

生成代码前 Codex 必须先读对应文档:

任务 必读文档
任何业务页面 src/docs/business-architecture.md
列表页 src/docs/list-page-spec.md
弹框 src/docs/modal-spec.md
抽屉 src/docs/drawer-spec.md
详情页 src/docs/detail-page-spec.md
Tabs 页 src/docs/tabs-page-spec.md
表格工具 src/docs/utils-table.md
弹窗工具 src/docs/utils-popup.md
对照研发项目 src/docs/cross-project-mapping.md
完整规则 CLAUDE.md(项目根目录,权威源)

13. 迁移到 vue 研发项目(设计意图)

研发接手时步骤(Codex 编写代码时应保证可平滑迁移):

  1. 替换 src/axios/ 为真实 axios 封装(保留方法签名
  2. 删除 src/axios/handlers/
  3. 删除原型独有页面(gate / console / prd / overview)和 composables/useVisibility
  4. <TableCard :table> 拆为 <a-row class="listPageTable"> + <a-table>
  5. <FilterBar v-model> 拆为 <a-row class="listPageForm"> + <a-form>
  6. 业务逻辑 100% 不改