--- description: 后台页面(列表/表单/详情)开发范式 —— 新增或修改 views 下页面时必读 globs: ["src/views/**/*.vue", "src/views/**/*.ts", "src/components/**/*.vue"] --- # 页面开发范式 ## 标准列表页结构 一个规范的列表页自上而下固定为四段: ```vue ``` - 搜索栏支持回车查询与"重置"重置到初始查询条件。 - 表格必须 `v-loading="loading"`、`border`、操作列 `fixed="right"`。 - 分页器 `layout="total, sizes, prev, pager, next, jumper"`,与 `queryParams.pageNum/pageSize` 双向联动。 - 查询条件与分页状态变更时用 `watch` 或统一 `handleQuery` 重新拉取,**禁止在模板里调用接口**。 ## 标准数据流 ```ts const loading = ref(false) const total = ref(0) const list = ref([]) const queryParams = reactive({ pageNum: 1, pageSize: 10 }) async function handleQuery() { loading.value = true try { const { list: rows, total: t } = await UserAPI.query(queryParams) list.value = rows total.value = t } finally { loading.value = false // loading 必须在 finally 里关闭 } } onMounted(handleQuery) ``` - 请求失败由 `request` 拦截器统一提示,页面**只负责数据与 loading**;确需额外处理时才 `catch`。 - `loading` 必须放在 `finally` 关闭,避免异常时永久转圈。 ## 表单弹窗 1. 弹窗组件独立文件,父组件通过 `ref` + `defineExpose({ open })` 调用: ```vue ``` 2. 弹窗内部:`el-dialog` + `el-form`,必须有 `ref`、`rules`、`:model`;关闭时 `resetFields()` 并清空隐藏主键。 3. 提交按钮在请求期间设置 `confirmLoading`,防止重复提交。 4. 新增与编辑复用同一弹窗,通过是否有 `id` 区分,标题用 computed 切换。 ## 删除与危险操作 - 删除必须走 `ElMessageBox.confirm` 二次确认,成功后 `ElMessage.success` + 刷新列表。 - 若当前页删完且非第一页,需回退 `pageNum` 再查询。 ## 表单校验 - 规则写在 `rules` 常量里(`const rules = reactive>({...})`),不在模板内联。 - 提交前必须 `await formRef.value.validate()`,校验失败直接返回。 - 后端返回字段级错误需映射到 `formRef` 对应字段。 ## 页面内复用 - 仅当前页面使用的子组件放 `views/{module}/components/`,**不要**放进全局 `src/components/`。 - 跨页面复用的逻辑抽成 `useXxx` 组合式函数,放 `src/composables/`。 - 字典/枚举下拉数据统一走 Pinia 的字典 store 缓存,不得每次打开弹窗都重新请求。