diff --git a/vue/aaa.md b/vue/aaa.md deleted file mode 100644 index 641d002..0000000 --- a/vue/aaa.md +++ /dev/null @@ -1,269 +0,0 @@ -import { h, ref } from 'vue'; -import type { VxeGridProps } from '#/adapter/vxe-table'; -import { getPopupContainer } from '@vben/utils'; -import type { DescItem } from '#/components/description'; -import { cloneDeep } from 'lodash-es'; -import type { VbenFormSchema } from '@vben/common-ui'; -import { z } from '@vben/common-ui'; -import { renderDict, renderPopoverMemberSumma, type MemberSumma } from '#/utils'; -import { getDictOptions } from '#/utils/dict'; -import FormOptions from '#/constants/FormOption'; - -export class State { - public id = 0; // 主键ID - public apiName = ''; // 接口名称 - public apiCode = ''; // 接口权限标识 - public method = ''; // 请求方法:GET/POST/PUT/DELETE - public path = ''; // 接口路径 - public status = 0; // 状态:0启用,1停用 - public version = ''; // 接口版本 - public remark = ''; // 备注 - public tenantId = ''; // 租户ID - public createdDept = 0; // 创建部门 - public createdBy = 0; // 创建者 - public createdBySumma?: null | MemberSumma = null; // 创建者摘要信息 - public createdAt = ''; // 创建时间 - public updatedBy = 0; // 更新者 - public updatedBySumma?: null | MemberSumma = null; // 更新者摘要信息 - public updatedAt = ''; // 更新时间 - - constructor(state?: Partial) { - if (state) { - Object.assign(this, state); - } - } -} - -export function newState(state: State | Record | null): State { - if (state !== null) { - if (state instanceof State) { - return cloneDeep(state); - } - return new State(state); - } - return new State(); -} - -// 表单验证规则 - -// 表格搜索表单 -export const querySchema: VbenFormSchema[] = [ - { - fieldName: 'id', - component: 'InputNumber', - label: '主键ID', - componentProps: { - placeholder: '请输入主键ID', - onUpdateValue: (e: any) => { - console.log(e); - }, - }, rules:null, - formItemClass: 'col-span-1', - }, { - fieldName: 'status', - component: 'Select', - label: '状态:0启用,1停用', - defaultValue: null, - componentProps: { - placeholder: '请选择状态:0启用,1停用', - options: getDictOptions('sys_common_status'), - onUpdateValue: (e: any) => { - console.log(e); - }, - }, - rules:'selectRequired', - formItemClass: 'col-span-1', - }, - { - fieldName: 'createdAt', - component: 'RangePicker', - label: '创建时间', - componentProps: { - type: 'datetimerange', - clearable: true, - valueFormat: 'YYYY-MM-DD HH:mm:ss', - onUpdateValue: (e: any) => { - console.log(e); - }, - }, rules:null, - formItemClass: 'col-span-1', - },]; - -// 表格列 -export const columns: VxeGridProps['columns'] = [ - { - title: '接口名称', - field: 'apiName', - align: 'left', - width: -1, - }, - { - title: '接口权限标识', - field: 'apiCode', - align: 'left', - width: -1, - }, - { - title: '请求方法', - field: 'method', - align: 'left', - width: -1, - slots: { - default: ({ row }) => { - return renderDict(row.method, DictEnum.T_METHOD); - } - }, - }, - { - title: '接口路径', - field: 'path', - align: 'left', - width: -1, - }, - { - title: '状态', field: 'status', align: 'left', width: -1, - slots: { - default: ({ row }) => { - return renderDict(row.status, DictEnum.GLOBAL_ENABLED); - } - }, - }, - { - title: '接口版本', - field: 'version', - align: 'left', - width: -1, - }, - - { title: '操作', width: 120, slots: { default: 'action' } }, -]; - -// 表格列接口 -export interface RowType { - id: number; - apiName: string; - apiCode: string; - method: string; - path: string; - status: number; - version: string; - remark: string; - tenantId: string; - createdDept: number; - createdBy: number; - createdAt: string; - updatedBy: number; - updatedAt: string; -}; - -// 查看字段列表 -export const viewSchema: DescItem[] = [ - { field: 'apiName', label: '接口名称'}, - { field: 'apiCode', label: '接口权限标识'}, - { field: 'method', label: '请求方法:GET/POST/PUT/DELETE'}, - { field: 'path', label: '接口路径'}, - { - field: 'status', - label: '状态', - render: (val) => renderDict(val, 'sys_common_status'), - }, - { field: 'version', label: '接口版本'}, - { field: 'remark', label: '备注'}, -]; - -// 编辑字段列表 -export const editSchema: VbenFormSchema[] = [ - { - fieldName: 'id', - component: 'Input', - label: '主键ID', - dependencies: { show: () => false, triggerFields: [''], }, - componentProps: { - placeholder: '', - onUpdateValue: (e: any) => { - console.log(e); - }, - }, - formItemClass: 'col-span-1', - }, { - fieldName: 'apiName', - component: 'Input', - label: '接口名称', - componentProps: { - placeholder: '请输入接口名称', - onUpdateValue: (e: any) => { - console.log(e); - }, - }, - rules:'required', - formItemClass: 'col-span-1', - }, { - fieldName: 'apiCode', - component: 'Input', - label: '接口权限标识', - componentProps: { - placeholder: '请输入接口权限标识', - onUpdateValue: (e: any) => { - console.log(e); - }, - }, - rules:'required', - formItemClass: 'col-span-1', - }, { - fieldName: 'method', - component: 'Select', - label: '请求方法', - componentProps: { - placeholder: '请选择请求方法', - options: getDictOptions(DictEnum.T_METHOD ), - onUpdateValue: (e: any) => { - console.log(e); - }, - }, - rules:'required', - formItemClass: 'col-span-1', - }, { - fieldName: 'path', - component: 'Input', - label: '接口路径', - componentProps: { - placeholder: '请输入接口路径', - onUpdateValue: (e: any) => { - console.log(e); - }, - }, - rules:'required', - formItemClass: 'col-span-1', - }, - { - fieldName: 'status', - component: 'Switch', - label: '状态', - defaultValue: 0, - componentProps: FormOptions.Component.SwitchEnabled(), - }, - { - fieldName: 'version', - component: 'Input', - label: '接口版本', - componentProps: { - placeholder: '请输入接口版本', - onUpdateValue: (e: any) => { - console.log(e); - }, - }, - rules:'required', - formItemClass: 'col-span-1', - }, { - fieldName: 'remark', - component: 'Input', - label: '备注', - componentProps: { - placeholder: '请输入备注', - onUpdateValue: (e: any) => { - console.log(e); - }, - }, - rules:null, - formItemClass: 'col-span-1', - }, ]; \ No newline at end of file diff --git a/vue/apps/web-antd/src/template/iotlcm-crud-style/MEMORY.md b/vue/apps/web-antd/src/template/iotlcm-crud-style/MEMORY.md deleted file mode 100644 index 6c61e21..0000000 --- a/vue/apps/web-antd/src/template/iotlcm-crud-style/MEMORY.md +++ /dev/null @@ -1,107 +0,0 @@ -# 任务组:IOTLCM CRUD 新风格模板的正确使用与弹窗内表格扩展 - -scope(范围):`apps/web-antd/src/template/iotlcm-crud-style` 模板学习与迁移工作,尤其是把默认生成或旧风格 CRUD 页面迁移到新风格时,遇到查看弹窗内新增 Tab、弹窗内 Grid、接口契约不明确、字段配置归属不清等场景。 - -applies_to(适用条件):cwd=E:/Projects/Componany/XiuJieZhiLian/IotLcm/iotlcm-admin-ui/vue;适用于 `apps/web-antd/src/views/iotlcm/...` 与对应 `apps/web-antd/src/api/iotlcm/...` 的 Vben 页面迁移;每次学习 `iotlcm-crud-style` 目录时,必须先读 `README.md`,并同步读取本文件。 - -## 任务 1:修正 `tSimFlowPool` 查看弹窗中的流量池号卡 Tab,成功 - -### keywords(关键词) - -- iotlcm-crud-style, tSimFlowPool, view.vue, model.tsx, PoolCardGridColumns, PoolCardGridSchema, ViewDescSchema, useVbenVxeGrid, proxyConfig, BetterVben.grid.gridOptions.modal, GetCardsByPackageCodes, any, packageCodes, flowPoolCode - -## 用户偏好 - -- 当用户要求参考 `apps/web-antd/src/template/iotlcm-crud-style` 修正页面时,不要只套外层模板;要把列表、弹窗、表单、查看详情、弹窗内 Grid、对应 API 一起放到当前模板语境中判断。 -- 当接口返回结构没有本地后端契约、类型文件、Swagger 或已有实现能确认时,不要凭前端列名推断返回类型;保留 `any`,并在汇报中明确说明“接口返回未确认,所以未定义响应模型”。 -- 当用户指出“columns 应该放哪里”时,默认遵循配置归属:所有表格列配置放在 `model.tsx`,组件文件只负责组合实例、状态与调用。 -- 当查看弹窗新增 Tab 内含表格时,这个表格属于查看弹窗的扩展配置,不属于主页列表配置;相关列和查询表单应放在 `ViewDescSchema` 后面,而不是插在 `IndexGridColumns` 和 `EditFormSchema` 中间。 -- 当用户指出“proxyConfig 不会用?”时,弹窗内 Grid 的数据加载也应优先使用 `useVbenVxeGrid({ gridOptions: { proxyConfig: { ajax: { query }}}})`,不要手动 `setGridOptions({ data })` 塞数据。 -- 当用户明确纠正实现方式时,先核对当前项目已有写法和模板文档,再做最小改动;不要为了消除 lint 或 any 而编造类型、字段或接口契约。 - -## 可复用知识 - -- `model.tsx` 是表单字段、表格列、查看描述等配置的集中位置;普通页面顺序仍为: - - `IndexGridSchema` - - `IndexGridColumns` - - `EditFormSchema` - - `ViewDescSchema` -- 如果查看弹窗内新增了业务 Tab 和 Grid,相关配置应追加在查看配置之后,例如: - - `ViewDescSchema` - - `PoolCardGridColumns` - - `PoolCardGridSchema` -- `view.vue` 中的弹窗内 Grid 推荐结构: - -```ts -const [Grid, gridApi] = useVbenVxeGrid({ - formOptions: { - ...(BetterVben.grid.formOptions.default as any), - schema: PoolCardGridSchema, - }, - gridOptions: { - ...(BetterVben.grid.gridOptions.modal as any), - columns: PoolCardGridColumns, - proxyConfig: { - ajax: { - query: async ({ page }: any, formValues: any) => { - if (!flowPoolCode.value) { - return {}; - } - return await GetCardsByPackageCodes({ - page: page.currentPage, - pageSize: page.pageSize, - ...formValues, - packageCodes: packageCodes.value, - flowPoolCode: flowPoolCode.value, - }); - }, - }, - }, - }, -}); -``` - -- 查看弹窗打开时应只做详情加载、上下文状态设置和触发表格查询: - -```ts -const record = await View({ id: row.id }); -setDescProps({ data: record }, true); -flowPoolCode.value = record.flowPoolCode || ''; -packageCodes.value = record.packageCodes || []; -await gridApi.query(); -``` - -- 查看弹窗关闭时应清空弹窗局部状态,避免再次打开时沿用上一次数据: - -```ts -activeKey.value = '1'; -flowPoolCode.value = ''; -packageCodes.value = []; -setDescProps({ data: {} }, true); -``` - -- 弹窗内 Grid 应使用 `BetterVben.grid.gridOptions.modal`,不是随手使用 `default` 或手写一套 `height/cellStyle/headerCellStyle/rowStyle`。 -- `GetCardsByPackageCodes` 当前返回结构未确认时,可保留: - -```ts -export function GetCardsByPackageCodes(params: any) { - return requestClient.post( - '/iot/tSimFlowPool/getCardsByPackageCodes', - params, - ); -} -``` - -或在只确认请求参数时保留请求参数的最小结构,但不要定义未验证的响应模型。 - -- 对弹窗内号卡表格,列配置应尽量参考已有 `tCommunication/tSimCardInfo/model.tsx` 的字段含义、字典渲染和通道选择配置,而不是凭字段名临时拼列。 -- `PoolCardGridSchema` 这类弹窗内表格搜索表单也属于 `model.tsx` 配置;不要写在 `view.vue` 里。 - -## 失败与改进方式 - -- 症状:为了清掉 API 扫描里的 `any`,凭 `view.vue` 中的列名定义了 `TSimFlowPoolCardModel` 与响应类型。原因:没有确认后端路由实现或真实响应契约。修复:撤掉伪精确类型,保留 `any` 并说明接口返回未知。 -- 症状:把 `cardGridOptions.columns` 写在 `view.vue`。原因:把弹窗内表格当成组件临时 UI,而不是页面配置的一部分。修复:把 `PoolCardGridColumns` 移到 `model.tsx`,并放在 `ViewDescSchema` 后面。 -- 症状:通过 `cardTableApi.setGridOptions({ data })` 手动塞表格数据。原因:忽略了 Vben Grid 的 `proxyConfig.ajax.query` 数据源模式。修复:把加载逻辑放入 `useVbenVxeGrid` 的 `proxyConfig`,打开弹窗后调用 `gridApi.query()`。 -- 症状:弹窗内 Grid 没有使用 `BetterVben.grid.gridOptions.modal`。原因:只想到“有表格列”而没有区分主页面 Grid 与弹窗 Grid。修复:弹窗内表格使用 `modal` 配置,主页面表格使用 `default` 配置。 -- 症状:为了“同步处理 API”删除或改造了不确定的旧接口。原因:把模板要求机械执行,未区分已使用、未使用和契约未知的接口。修复:先 `rg` 调用点;契约不明时保留 `any` 或最小改动,不做伪类型化。 -- 症状:学习模板目录时只读 `README.md`,漏掉实战纠错知识。修复:每次学习 `apps/web-antd/src/template/iotlcm-crud-style` 时必须同时读取本文件。