From 5ccd27da90299f8e51e5216820ee05b33bbeddcc Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Mon, 20 Nov 2023 22:55:13 +0800 Subject: [PATCH] =?UTF-8?q?perf(editable):=20=E5=A2=9E=E5=8A=A0=E7=A4=BA?= =?UTF-8?q?=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/mock/base.ts | 59 ++++- src/router/modules/crud.ts | 65 ++++- src/styles/common.less | 4 + src/views/crud/editable/cell/api.ts | 67 ++++++ src/views/crud/editable/cell/crud.tsx | 225 ++++++++++++++++++ src/views/crud/editable/cell/index.vue | 99 ++++++++ src/views/crud/editable/cell/mock.ts | 25 ++ src/views/crud/editable/free/api.ts | 52 ++++ src/views/crud/editable/free/crud.tsx | 128 ++++++++++ src/views/crud/editable/free/index.vue | 77 ++++++ src/views/crud/editable/free/mock.ts | 24 ++ src/views/crud/editable/row/api.ts | 52 ++++ src/views/crud/editable/row/crud.tsx | 153 ++++++++++++ src/views/crud/editable/row/index.vue | 45 ++++ src/views/crud/editable/row/mock.ts | 24 ++ src/views/crud/editable/vmodel/api.ts | 52 ++++ src/views/crud/editable/vmodel/crud.tsx | 89 +++++++ src/views/crud/editable/vmodel/free/api.ts | 52 ++++ src/views/crud/editable/vmodel/free/crud.tsx | 103 ++++++++ src/views/crud/editable/vmodel/free/index.vue | 69 ++++++ src/views/crud/editable/vmodel/free/mock.ts | 20 ++ src/views/crud/editable/vmodel/index.vue | 41 ++++ src/views/crud/editable/vmodel/mock.ts | 20 ++ 23 files changed, 1532 insertions(+), 13 deletions(-) create mode 100644 src/views/crud/editable/cell/api.ts create mode 100644 src/views/crud/editable/cell/crud.tsx create mode 100644 src/views/crud/editable/cell/index.vue create mode 100644 src/views/crud/editable/cell/mock.ts create mode 100644 src/views/crud/editable/free/api.ts create mode 100644 src/views/crud/editable/free/crud.tsx create mode 100644 src/views/crud/editable/free/index.vue create mode 100644 src/views/crud/editable/free/mock.ts create mode 100644 src/views/crud/editable/row/api.ts create mode 100644 src/views/crud/editable/row/crud.tsx create mode 100644 src/views/crud/editable/row/index.vue create mode 100644 src/views/crud/editable/row/mock.ts create mode 100644 src/views/crud/editable/vmodel/api.ts create mode 100644 src/views/crud/editable/vmodel/crud.tsx create mode 100644 src/views/crud/editable/vmodel/free/api.ts create mode 100644 src/views/crud/editable/vmodel/free/crud.tsx create mode 100644 src/views/crud/editable/vmodel/free/index.vue create mode 100644 src/views/crud/editable/vmodel/free/mock.ts create mode 100644 src/views/crud/editable/vmodel/index.vue create mode 100644 src/views/crud/editable/vmodel/mock.ts diff --git a/src/mock/base.ts b/src/mock/base.ts index 0fe388e9..193311ac 100644 --- a/src/mock/base.ts +++ b/src/mock/base.ts @@ -1,7 +1,8 @@ import _ from 'lodash-es'; function copyList(originList: any, newList: any, options: any, parentId?: any) { for (const item of originList) { - const newItem = { ...item, parentId }; + const newItem: any = _.cloneDeep(item); + newItem.parentId = parentId; newItem.id = ++options.idGenerator; newList.push(newItem); if (item.children != null) { @@ -297,6 +298,62 @@ const mockUtil: any = { }; }, }, + { + path: '/mock/' + name + '/cellUpdate', + method: 'post', + handle(req: any): any { + console.log('req', req); + let item = findById(req.body.id, list); + if (item) { + _.mergeWith(item, { [req.body.key]: req.body.value }, (objValue, srcValue) => { + if (srcValue == null) { + return; + } + // 如果被合并对象为数组,则直接被覆盖对象覆盖,只要覆盖对象不为空 + if (_.isArray(objValue)) { + return srcValue; + } + }); + } else { + item = { + id: ++options.idGenerator, + [req.body.key]: req.body.value, + }; + list.unshift(item); + } + + return { + code: 0, + msg: 'success', + data: item, + }; + }, + }, + { + path: '/mock/' + name + '/columnUpdate', + method: 'post', + handle(req: any): any { + for (const item of req.body) { + const item2 = findById(item.id, list); + if (item2) { + _.mergeWith(item2, item, (objValue, srcValue) => { + if (srcValue == null) { + return; + } + // 如果被合并对象为数组,则直接被覆盖对象覆盖,只要覆盖对象不为空 + if (_.isArray(objValue)) { + return srcValue; + } + }); + } + } + return { + code: 0, + msg: 'success', + data: null, + }; + }, + }, ]; }, }; diff --git a/src/router/modules/crud.ts b/src/router/modules/crud.ts index e3a918ab..4ef82af8 100644 --- a/src/router/modules/crud.ts +++ b/src/router/modules/crud.ts @@ -12,6 +12,7 @@ import { ExtensionPuzzleOutline, FlameOutline, CubeOutline, + PencilOutline, } from '@vicons/ionicons5'; const basicRoute: RouteRecordRaw = { @@ -700,18 +701,6 @@ const featureRoutes = { path: 'height', component: () => import('@/views/crud/feature/height/index.vue'), }, - { - meta: { title: '可编辑', keepAlive: true }, - name: 'FeatureEditable', - path: 'editable', - component: () => import('@/views/crud/feature/editable/index.vue'), - }, - { - meta: { title: '行编辑', keepAlive: true }, - name: 'FeatureEditableRow', - path: 'editable-row', - component: () => import('@/views/crud/feature/editable-row/index.vue'), - }, { meta: { title: '查询框', keepAlive: true }, name: 'FeatureSearch', @@ -780,6 +769,57 @@ const featureRoutes = { }, ], }; + +const editableRoute: RouteRecordRaw = { + path: 'editable', + name: 'Editable', + redirect: '/crud/editable/free', + meta: { + title: '可编辑', + icon: renderIcon(PencilOutline), + }, + component: ParentLayout, + children: [ + { + name: 'EditableFree', + path: 'free', + meta: { + title: '自由编辑', + keepAlive: true, + }, + component: () => import('@/views/crud/editable/free/index.vue'), + }, + { + name: 'EditableRow', + path: 'row', + meta: { + title: '行编辑', + keepAlive: true, + }, + component: () => import('@/views/crud/editable/row/index.vue'), + }, + + { + name: 'EditableCell', + path: 'cell', + meta: { + title: '单元格编辑', + keepAlive: true, + }, + component: () => import('@/views/crud/editable/cell/index.vue'), + }, + + { + name: 'EditableVModel', + path: 'vmodel', + meta: { + title: '子表格编辑', + keepAlive: true, + }, + component: () => import('@/views/crud/editable/vmodel/index.vue'), + }, + ], +}; const slotRoutes = { name: 'Slots', path: 'slots', @@ -931,6 +971,7 @@ const routes = [ rowHandleRoute, componentRoute, featureRoutes, + editableRoute, formRoutes, slotRoutes, advancedRoutes, diff --git a/src/styles/common.less b/src/styles/common.less index f435051b..f5626cb6 100644 --- a/src/styles/common.less +++ b/src/styles/common.less @@ -175,3 +175,7 @@ img, video{ margin-left: 5px; margin-right: 5px; } + +.ml-5{ + margin-left:5px; +} diff --git a/src/views/crud/editable/cell/api.ts b/src/views/crud/editable/cell/api.ts new file mode 100644 index 00000000..04141b8d --- /dev/null +++ b/src/views/crud/editable/cell/api.ts @@ -0,0 +1,67 @@ +import { requestForMock } from '@/utils/http/service'; +const request = (req) => { + return requestForMock(req); +}; +const apiPrefix = '/mock/EditableCell'; +export function GetList(query: any) { + return request({ + url: apiPrefix + '/page', + method: 'get', + data: query, + }); +} + +export function AddObj(obj: any) { + return request({ + url: apiPrefix + '/add', + method: 'post', + data: obj, + }); +} + +export function UpdateObj(obj: any) { + return request({ + url: apiPrefix + '/update', + method: 'post', + data: obj, + }); +} + +export function DelObj(id: any) { + return request({ + url: apiPrefix + '/delete', + method: 'post', + params: { id }, + }); +} + +export function GetObj(id: any) { + return request({ + url: apiPrefix + '/get', + method: 'get', + params: { id }, + }); +} + +export function BatchDelete(ids: any) { + return request({ + url: apiPrefix + '/batchDelete', + method: 'post', + data: { ids }, + }); +} + +export function UpdateCell(id: number, key: string, value: any) { + return request({ + url: apiPrefix + '/cellUpdate', + method: 'post', + data: { id, key, value }, + }); +} +export function UpdateColumn(data) { + return request({ + url: apiPrefix + '/columnUpdate', + method: 'post', + data, + }); +} diff --git a/src/views/crud/editable/cell/crud.tsx b/src/views/crud/editable/cell/crud.tsx new file mode 100644 index 00000000..d6a4e66e --- /dev/null +++ b/src/views/crud/editable/cell/crud.tsx @@ -0,0 +1,225 @@ +import * as api from "./api"; +import { + dict, + compute, + CreateCrudOptionsProps, + CreateCrudOptionsRet, + UserPageQuery, + UserPageRes, + EditReq, + DelReq, + AddReq +} from "@fast-crud/fast-crud"; +import { computed, reactive, ref } from "vue"; +import _ from "lodash-es"; +import { EditableEachCellsOpts } from "@fast-crud/fast-crud/src/components/crud/editable/d"; +export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet { + const { crudBinding } = crudExpose; + const pageRequest = async (query: UserPageQuery): Promise => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }: EditReq) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }: DelReq) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }: AddReq) => { + return await api.AddObj(form); + }; + + const radioDictRef = dict({ + url: "/mock/dicts/OpenStatusEnum?single" + }); + + const radioColumnValue = ref(""); + function columnUpdate(event: Event) { + //批量设置值 + // _.forEach(crudBinding.value?.data, (item) => { + // item.radio = event.target.value; + // }); + crudExpose.editable.eachCells((opts: EditableEachCellsOpts) => { + const { key, cell, rowData } = opts; + if (key === "radio") { + if (cell.isEditing) { + //@ts-ignore + rowData.radio = event.target.value; + } + } + }); + } + const radioColumnEditor = reactive({ + editing: false, + loading: false, + onSubmit: async () => { + console.log("onSubmit"); + radioColumnEditor.loading = true; + try { + const data: any[] = []; + for (const row of crudBinding.value.data) { + data.push({ id: row.id, radio: row.radio }); + } + await api.UpdateColumn(data); + crudExpose.editable.persist(); + radioColumnEditor.editing = false; + } finally { + radioColumnEditor.loading = false; + } + }, + onCancel: () => { + console.log("cancel"); + crudExpose.editable.cancel(); + radioColumnEditor.editing = false; + }, + "onUpdate:editing": (value: boolean) => { + radioColumnEditor.editing = value; + if (value === true) { + crudExpose.editable.activeCols({ cols: ["radio"], showAction: false }); + } + }, + vSlots: { + edit: () => { + return ( + + ); + } + } + }); + + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + actionbar: { + buttons: { + // add: { + // show: computed(() => { + // if (crudBinding.value) { + // return !crudBinding.value?.table.editable.enabled; + // } + // return false; + // }) + // }, + // addRow: { + // show: computed(() => { + // if (crudBinding.value) { + // return crudBinding.value?.table.editable.enabled; + // } + // return false; + // }) + // } + } + }, + table: { + editable: { + mode: "cell", + exclusive: true, + exclusiveEffect: "cancel", + async updateCell(opts) { + const { row, key, value } = opts; + return await api.UpdateCell(row.id, key, value); + } + }, + slots: { + //编辑列 + headerCell({ column }: any) { + if (column.key === "radio") { + return ( +
+ + {column.title} (点我批量编辑) + +
+ ); + } + } + } + }, + columns: { + id: { + title: "ID", + type: "number", + form: { + show: false + }, + column: { width: 80, align: "center" } + }, + disable: { + title: "禁止编辑", + type: "text", + column: { + editable: { + disabled: true //也可以配置为方法,根据条件禁用或启用编辑 + // disabled: ({ column, index, row }) => { + // return index % 2 === 0; + // } + } + } + }, + radio: { + title: "状态", + search: { show: true }, + type: "dict-radio", + dict: radioDictRef, + column: { + width: 300 + }, + form: { + rule: [{ required: true, message: "请选择状态" }] + } + }, + name: { + title: "姓名", + type: "text", + form: { + rule: [ + { required: true, message: "请输入姓名" }, + { + type: "string", + min: 2, + max: 10, + message: "长度在 2 到 10 个字符" + } + ] + } + }, + address: { + title: "地址", + children: { + province: { + title: "省份", + search: { show: true }, + type: "text" + }, + city: { + title: "城市", + search: { show: true }, + type: "dict-select", + dict: dict({ + value: "id", + label: "text", + data: [ + { id: "sz", text: "深圳", color: "success" }, + { id: "gz", text: "广州", color: "primary" }, + { id: "bj", text: "北京" }, + { id: "wh", text: "武汉" }, + { id: "sh", text: "上海" } + ] + }) + } + } + } + } + } + }; +} diff --git a/src/views/crud/editable/cell/index.vue b/src/views/crud/editable/cell/index.vue new file mode 100644 index 00000000..2129a133 --- /dev/null +++ b/src/views/crud/editable/cell/index.vue @@ -0,0 +1,99 @@ + + + diff --git a/src/views/crud/editable/cell/mock.ts b/src/views/crud/editable/cell/mock.ts new file mode 100644 index 00000000..430b6aa2 --- /dev/null +++ b/src/views/crud/editable/cell/mock.ts @@ -0,0 +1,25 @@ +// @ts-ignore +import mockUtil from '/src/mock/base'; +const options: any = { + name: 'EditableCell', + idGenerator: 0, +}; +const list = [ + { + radio: '1', + children: [ + { + radio: '2', + }, + ], + }, + { + radio: '2', + }, + { + radio: '0', + }, +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/src/views/crud/editable/free/api.ts b/src/views/crud/editable/free/api.ts new file mode 100644 index 00000000..eebdb554 --- /dev/null +++ b/src/views/crud/editable/free/api.ts @@ -0,0 +1,52 @@ +import { requestForMock } from '@/utils/http/service'; +const request = (req) => { + return requestForMock(req); +}; +const apiPrefix = '/mock/EditableFree'; +export function GetList(query: any) { + return request({ + url: apiPrefix + '/page', + method: 'get', + data: query, + }); +} + +export function AddObj(obj: any) { + return request({ + url: apiPrefix + '/add', + method: 'post', + data: obj, + }); +} + +export function UpdateObj(obj: any) { + return request({ + url: apiPrefix + '/update', + method: 'post', + data: obj, + }); +} + +export function DelObj(id: any) { + return request({ + url: apiPrefix + '/delete', + method: 'post', + params: { id }, + }); +} + +export function GetObj(id: any) { + return request({ + url: apiPrefix + '/get', + method: 'get', + params: { id }, + }); +} + +export function BatchDelete(ids: any) { + return request({ + url: apiPrefix + '/batchDelete', + method: 'post', + data: { ids }, + }); +} diff --git a/src/views/crud/editable/free/crud.tsx b/src/views/crud/editable/free/crud.tsx new file mode 100644 index 00000000..0084c43d --- /dev/null +++ b/src/views/crud/editable/free/crud.tsx @@ -0,0 +1,128 @@ +import * as api from './api'; +import { + dict, + compute, + CreateCrudOptionsProps, + CreateCrudOptionsRet, + UserPageQuery, + UserPageRes, + EditReq, + DelReq, + AddReq, +} from '@fast-crud/fast-crud'; +export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet { + const { crudBinding } = crudExpose; + const pageRequest = async (query: UserPageQuery): Promise => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }: EditReq) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }: DelReq) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }: AddReq) => { + return await api.AddObj(form); + }; + + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest, + }, + actionbar: { + buttons: { + add: { + show: false, + }, + addRow: { + show: true, + }, + }, + }, + table: { + editable: { + mode: 'free', + }, + }, + pagination: { + pageSize: 5, + pageSizes: [5, 10, 20, 50, 100], + }, + columns: { + id: { + title: 'ID', + type: 'number', + form: { + show: false, + }, + column: { width: 80, align: 'center' }, + }, + disable: { + title: '禁止编辑', + type: 'text', + column: { + editable: { + disabled: true, //也可以配置为方法,根据条件禁用或启用编辑 + // disabled: ({ column, index, row }) => { + // return index % 2 === 0; + // } + }, + }, + }, + radio: { + title: '状态', + search: { show: true }, + type: 'dict-radio', + dict: dict({ + url: '/mock/dicts/OpenStatusEnum?single', + }), + form: { + rule: [{ required: true, message: '请选择状态' }], + }, + }, + name: { + title: '姓名', + type: 'text', + form: { + rule: [ + { required: true, message: '请输入姓名' }, + { min: 2, max: 10, message: '长度在 2 到 10 个字符' }, + ], + }, + }, + address: { + title: '地址', + children: { + province: { + title: '省份', + search: { show: true }, + type: 'text', + }, + city: { + title: '城市', + search: { show: true }, + type: 'dict-select', + dict: dict({ + value: 'id', + label: 'text', + data: [ + { id: 'sz', text: '深圳', color: 'success' }, + { id: 'gz', text: '广州', color: 'primary' }, + { id: 'bj', text: '北京' }, + { id: 'wh', text: '武汉' }, + { id: 'sh', text: '上海' }, + ], + }), + }, + }, + }, + }, + }, + }; +} diff --git a/src/views/crud/editable/free/index.vue b/src/views/crud/editable/free/index.vue new file mode 100644 index 00000000..b954f4f0 --- /dev/null +++ b/src/views/crud/editable/free/index.vue @@ -0,0 +1,77 @@ + + + diff --git a/src/views/crud/editable/free/mock.ts b/src/views/crud/editable/free/mock.ts new file mode 100644 index 00000000..8b830cb2 --- /dev/null +++ b/src/views/crud/editable/free/mock.ts @@ -0,0 +1,24 @@ +import mockUtil from '/src/mock/base'; +const options: any = { + name: 'EditableFree', + idGenerator: 0, +}; +const list = [ + { + radio: '1', + children: [ + { + radio: '2', + }, + ], + }, + { + radio: '2', + }, + { + radio: '0', + }, +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/src/views/crud/editable/row/api.ts b/src/views/crud/editable/row/api.ts new file mode 100644 index 00000000..ccdb5bfd --- /dev/null +++ b/src/views/crud/editable/row/api.ts @@ -0,0 +1,52 @@ +import { requestForMock } from '@/utils/http/service'; +const request = (req) => { + return requestForMock(req); +}; +const apiPrefix = '/mock/EditableRow'; +export function GetList(query: any) { + return request({ + url: apiPrefix + '/page', + method: 'get', + data: query, + }); +} + +export function AddObj(obj: any) { + return request({ + url: apiPrefix + '/add', + method: 'post', + data: obj, + }); +} + +export function UpdateObj(obj: any) { + return request({ + url: apiPrefix + '/update', + method: 'post', + data: obj, + }); +} + +export function DelObj(id: any) { + return request({ + url: apiPrefix + '/delete', + method: 'post', + params: { id }, + }); +} + +export function GetObj(id: any) { + return request({ + url: apiPrefix + '/get', + method: 'get', + params: { id }, + }); +} + +export function BatchDelete(ids: any) { + return request({ + url: apiPrefix + '/batchDelete', + method: 'post', + data: { ids }, + }); +} diff --git a/src/views/crud/editable/row/crud.tsx b/src/views/crud/editable/row/crud.tsx new file mode 100644 index 00000000..4f0f1acd --- /dev/null +++ b/src/views/crud/editable/row/crud.tsx @@ -0,0 +1,153 @@ +import * as api from './api'; +import { + AddReq, + compute, + CreateCrudOptionsProps, + CreateCrudOptionsRet, + DelReq, + dict, + EditReq, + UserPageQuery, + UserPageRes, +} from '@fast-crud/fast-crud'; +export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet { + const pageRequest = async (query: UserPageQuery): Promise => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }: EditReq) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }: DelReq) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }: AddReq) => { + const id = await api.AddObj(form); + return { id }; + }; + + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest, + }, + //将 addRow 按钮启用 + actionbar: { buttons: { add: { show: false }, addRow: { show: true } } }, + table: { + editable: { + enabled: true, + mode: 'row', + }, + }, + columns: { + id: { + title: 'ID', + type: 'number', + form: { + show: false, + }, + column: { width: 80, align: 'center' }, + }, + disable: { + title: '禁止编辑', + type: 'text', + column: { + editable: { + disabled: true, //也可以配置为方法,根据条件禁用或启用编辑 + // disabled: ({ column, index, row }) => { + // return index % 2 === 0; + // } + }, + }, + }, + radio: { + title: '状态', + search: { show: true }, + type: 'dict-radio', + dict: dict({ + url: '/mock/dicts/OpenStatusEnum?single', + }), + column: { + width: 300, + }, + form: { + rule: { + async asyncValidator(context) { + console.log('context', context); + return true; + }, + message: '远程校验测试', + }, + }, + }, + target: { + title: '根据状态动态显隐', + search: { show: true }, + type: 'text', + form: { + conditionalRender: { + match: ({ form }) => { + return form.radio === '2'; + }, + render: ({ form }) => { + return
已停止
; + }, + }, + show: compute(({ form }) => { + return form.radio !== '0'; + }), + }, + }, + 'user.name': { + title: '姓名', + type: 'text', + form: { + key: ['user', 'name'], + rule: [ + { required: true, message: '请输入姓名' }, + { + type: 'string', + min: 2, + max: 10, + message: '长度在 2 到 10 个字符', + }, + ], + }, + }, + address: { + title: '地址', + children: { + province: { + title: '省份', + search: { show: true }, + type: 'text', + form: { + rule: [{ required: true, message: '请输入省份' }], + }, + }, + city: { + title: '城市', + search: { show: true }, + type: 'dict-select', + dict: dict({ + value: 'id', + label: 'text', + data: [ + { id: 'sz', text: '深圳', color: 'success' }, + { id: 'gz', text: '广州', color: 'primary' }, + { id: 'bj', text: '北京' }, + { id: 'wh', text: '武汉' }, + { id: 'sh', text: '上海' }, + ], + }), + }, + }, + }, + }, + }, + }; +} diff --git a/src/views/crud/editable/row/index.vue b/src/views/crud/editable/row/index.vue new file mode 100644 index 00000000..932eb41d --- /dev/null +++ b/src/views/crud/editable/row/index.vue @@ -0,0 +1,45 @@ + + + diff --git a/src/views/crud/editable/row/mock.ts b/src/views/crud/editable/row/mock.ts new file mode 100644 index 00000000..b251a9e4 --- /dev/null +++ b/src/views/crud/editable/row/mock.ts @@ -0,0 +1,24 @@ +import mockUtil from '/src/mock/base'; +const options: any = { + name: 'EditableRow', + idGenerator: 0, +}; +const list = [ + { + radio: '1', + children: [ + { + radio: '2', + }, + ], + }, + { + radio: '2', + }, + { + radio: '0', + }, +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/src/views/crud/editable/vmodel/api.ts b/src/views/crud/editable/vmodel/api.ts new file mode 100644 index 00000000..b1316c99 --- /dev/null +++ b/src/views/crud/editable/vmodel/api.ts @@ -0,0 +1,52 @@ +import { requestForMock } from '@/utils/http/service'; +const request = (req) => { + return requestForMock(req); +}; +const apiPrefix = '/mock/EditableVModel'; +export function GetList(query: any) { + return request({ + url: apiPrefix + '/page', + method: 'get', + data: query, + }); +} + +export function AddObj(obj: any) { + return request({ + url: apiPrefix + '/add', + method: 'post', + data: obj, + }); +} + +export function UpdateObj(obj: any) { + return request({ + url: apiPrefix + '/update', + method: 'post', + data: obj, + }); +} + +export function DelObj(id: any) { + return request({ + url: apiPrefix + '/delete', + method: 'post', + params: { id }, + }); +} + +export function GetObj(id: any) { + return request({ + url: apiPrefix + '/get', + method: 'get', + params: { id }, + }); +} + +export function BatchDelete(ids: any) { + return request({ + url: apiPrefix + '/batchDelete', + method: 'post', + data: { ids }, + }); +} diff --git a/src/views/crud/editable/vmodel/crud.tsx b/src/views/crud/editable/vmodel/crud.tsx new file mode 100644 index 00000000..a6c004cb --- /dev/null +++ b/src/views/crud/editable/vmodel/crud.tsx @@ -0,0 +1,89 @@ +import * as api from './api'; +import { + dict, + compute, + CreateCrudOptionsProps, + CreateCrudOptionsRet, + UserPageQuery, + UserPageRes, + EditReq, + DelReq, + AddReq, +} from '@fast-crud/fast-crud'; +import EditableFreeSub from './free/index.vue'; +export default function (props: CreateCrudOptionsProps): CreateCrudOptionsRet { + const { crudBinding } = props.crudExpose; + const { crudExpose } = props; + const pageRequest = async (query: UserPageQuery): Promise => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }: EditReq) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }: DelReq) => { + return await api.DelObj(row.id); + }; + + const addRequest = async ({ form }: AddReq) => { + return await api.AddObj(form); + }; + + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest, + }, + form: { + wrapper: { + width: '80%', + }, + async beforeSubmit() { + const validate = await crudExpose.getFormComponentRef('subTable')?.validate(); + if (validate !== true) { + return false; + } + }, + }, + columns: { + id: { + title: 'ID', + type: 'number', + form: { + show: false, + }, + column: { width: 80, align: 'center' }, + }, + radio: { + title: '状态', + search: { show: true }, + type: 'dict-radio', + dict: dict({ + url: '/mock/dicts/OpenStatusEnum?single', + }), + }, + subTable: { + title: '子表格', + type: 'text', + form: { + component: { + name: EditableFreeSub, + vModel: 'modelValue', + }, + col: { + span: 24, + }, + }, + column: { + formatter: ({ row }) => { + return row.subTable?.length + '条数据'; + }, + }, + }, + }, + }, + }; +} diff --git a/src/views/crud/editable/vmodel/free/api.ts b/src/views/crud/editable/vmodel/free/api.ts new file mode 100644 index 00000000..4a9e690c --- /dev/null +++ b/src/views/crud/editable/vmodel/free/api.ts @@ -0,0 +1,52 @@ +import { requestForMock } from '@/utils/http/service'; +const request = (req) => { + return requestForMock(req); +}; +const apiPrefix = '/mock/EditableFreeSub'; +export function GetList(query: any) { + return request({ + url: apiPrefix + '/page', + method: 'get', + data: query, + }); +} + +export function AddObj(obj: any) { + return request({ + url: apiPrefix + '/add', + method: 'post', + data: obj, + }); +} + +export function UpdateObj(obj: any) { + return request({ + url: apiPrefix + '/update', + method: 'post', + data: obj, + }); +} + +export function DelObj(id: any) { + return request({ + url: apiPrefix + '/delete', + method: 'post', + params: { id }, + }); +} + +export function GetObj(id: any) { + return request({ + url: apiPrefix + '/get', + method: 'get', + params: { id }, + }); +} + +export function BatchDelete(ids: any) { + return request({ + url: apiPrefix + '/batchDelete', + method: 'post', + data: { ids }, + }); +} diff --git a/src/views/crud/editable/vmodel/free/crud.tsx b/src/views/crud/editable/vmodel/free/crud.tsx new file mode 100644 index 00000000..3795f6cd --- /dev/null +++ b/src/views/crud/editable/vmodel/free/crud.tsx @@ -0,0 +1,103 @@ +import * as api from './api'; +import { + dict, + compute, + CreateCrudOptionsProps, + CreateCrudOptionsRet, + UserPageQuery, + UserPageRes, + EditReq, + DelReq, + AddReq, +} from '@fast-crud/fast-crud'; +export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet { + const { crudBinding } = crudExpose; + // const pageRequest = async (query: UserPageQuery): Promise => { + // return await api.GetList(query); + // }; + // const editRequest = async ({ form, row }: EditReq) => { + // form.id = row.id; + // return await api.UpdateObj(form); + // }; + // const delRequest = async ({ row }: DelReq) => { + // return await api.DelObj(row.id); + // }; + // + // const addRequest = async ({ form }: AddReq) => { + // return await api.AddObj(form); + // }; + + return { + crudOptions: { + actionbar: { + buttons: { + add: { + show: false, + }, + addRow: { + show: true, + }, + }, + }, + search: { + show: false, + }, + toolbar: { + show: false, + buttons: { + refresh: { + show: false, + }, + search: { + show: false, + }, + }, + }, + mode: { + name: 'local', + isMergeWhenUpdate: true, + isAppendWhenAdd: true, + }, + table: { + editable: { + enabled: true, + mode: 'free', + }, + }, + pagination: { show: false, pageSize: 9999999 }, + columns: { + id: { + title: 'ID', + type: 'number', + form: { + show: false, + }, + column: { width: 80, align: 'center' }, + }, + radio: { + title: '状态', + search: { show: true }, + type: 'dict-radio', + dict: dict({ + url: '/mock/dicts/OpenStatusEnum?single', + }), + }, + name: { + title: '姓名', + type: 'text', + form: { + rule: [ + { required: true, message: '请输入姓名' }, + { min: 2, max: 10, message: '长度在 2 到 10 个字符' }, + ], + }, + }, + createdAt: { + column: { + show: false, + }, + }, + }, + }, + }; +} diff --git a/src/views/crud/editable/vmodel/free/index.vue b/src/views/crud/editable/vmodel/free/index.vue new file mode 100644 index 00000000..34a355b7 --- /dev/null +++ b/src/views/crud/editable/vmodel/free/index.vue @@ -0,0 +1,69 @@ + + + diff --git a/src/views/crud/editable/vmodel/free/mock.ts b/src/views/crud/editable/vmodel/free/mock.ts new file mode 100644 index 00000000..bce84d92 --- /dev/null +++ b/src/views/crud/editable/vmodel/free/mock.ts @@ -0,0 +1,20 @@ +import mockUtil from '/src/mock/base'; +const options: any = { + name: 'EditableFreeSub', + idGenerator: 0, +}; +const list = [ + { + radio: '1', + name: '王强', + }, + { + radio: '2', + }, + { + radio: '0', + }, +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock; diff --git a/src/views/crud/editable/vmodel/index.vue b/src/views/crud/editable/vmodel/index.vue new file mode 100644 index 00000000..f6c9de39 --- /dev/null +++ b/src/views/crud/editable/vmodel/index.vue @@ -0,0 +1,41 @@ + + + diff --git a/src/views/crud/editable/vmodel/mock.ts b/src/views/crud/editable/vmodel/mock.ts new file mode 100644 index 00000000..9b544de8 --- /dev/null +++ b/src/views/crud/editable/vmodel/mock.ts @@ -0,0 +1,20 @@ +import mockUtil from '/src/mock/base'; +const options: any = { + name: 'EditableVModel', + idGenerator: 0, +}; +const list = [ + { + radio: '1', + subTable: [{ id: 0, name: '王小虎' }], + }, + { + radio: '2', + }, + { + radio: '0', + }, +]; +options.list = list; +const mock = mockUtil.buildMock(options); +export default mock;