docs: 移除开发过程文档,开源版文档统一见文档站
- vue/aaa.md(临时草稿,实为 Vue 代码片段,扩展名误写成 .md) - vue/apps/web-antd/src/template/iotlcm-crud-style/MEMORY.md(内部迁移实战记忆与踩坑记录,含本机绝对路径) 文档站:https://git.5iot.com/iotlcm-public/iotlcm-docs
This commit is contained in:
-269
@@ -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<State>) {
|
||||
if (state) {
|
||||
Object.assign(this, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function newState(state: State | Record<string, any> | 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',
|
||||
}, ];
|
||||
@@ -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<any>(
|
||||
'/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` 时必须同时读取本文件。
|
||||
Reference in New Issue
Block a user