diff --git a/src/components/Form/src/componentMap.ts b/src/components/Form/src/componentMap.ts index 687db268b..27e117fe2 100644 --- a/src/components/Form/src/componentMap.ts +++ b/src/components/Form/src/componentMap.ts @@ -34,6 +34,7 @@ import { IconPicker } from '@/components/Icon'; import { CountdownInput } from '@/components/CountDown'; import { BasicTitle } from '@/components/Basic'; import { CropperAvatar } from '@/components/Cropper'; +import SmartApiSelectDict from './smart-boot/components/SmartApiSelectDict.vue'; const componentMap = new Map(); @@ -79,6 +80,8 @@ componentMap.set('CropperAvatar', CropperAvatar); componentMap.set('BasicTitle', BasicTitle); +componentMap.set('SmartApiSelectDict', SmartApiSelectDict); + export function add( compName: ComponentType | T, component: R, diff --git a/src/components/Form/src/types/index.ts b/src/components/Form/src/types/index.ts index 50d14007e..7e8f57571 100644 --- a/src/components/Form/src/types/index.ts +++ b/src/components/Form/src/types/index.ts @@ -124,6 +124,9 @@ interface _CustomComponents { InputCountDown: ExtractPropTypes< (typeof import('@/components/CountDown/src/CountdownInput.vue'))['default'] >; + SmartApiSelectDict: ExtractPropTypes< + (typeof import('@/components/Form/src/smart-boot/components/SmartApiSelectDict.vue'))['default'] + >; } type CustomComponents = { @@ -173,4 +176,5 @@ export interface ComponentProps { Transfer: ExtractPropTypes<(typeof import('ant-design-vue/es/transfer'))['default']>; CropperAvatar: CustomComponents['CropperAvatar']; BasicTitle: CustomComponents['BasicTitle']; + SmartApiSelectDict: CustomComponents['SmartApiSelectDict'] & ComponentProps['ApiSelect']; } diff --git a/src/components/SmartTable/src/components/SmartTableColumnConfig.tsx b/src/components/SmartTable/src/components/SmartTableColumnConfig.tsx index 333db1d5e..1a91990cf 100644 --- a/src/components/SmartTable/src/components/SmartTableColumnConfig.tsx +++ b/src/components/SmartTable/src/components/SmartTableColumnConfig.tsx @@ -122,7 +122,7 @@ export default defineComponent({ const buttonEvent = { onClick: () => { const trigger = props.config.trigger; - if (trigger === 'click') { + if (!trigger || trigger === 'click') { showPanel(); } }, diff --git a/src/components/registerGlobComp.ts b/src/components/registerGlobComp.ts index bd48d4e55..a4ccff398 100644 --- a/src/components/registerGlobComp.ts +++ b/src/components/registerGlobComp.ts @@ -1,6 +1,6 @@ import type { App } from 'vue'; import { Button } from './Button'; -import { Input, Layout, Radio, Tag, Select, Tooltip, Tree } from 'ant-design-vue'; +import { Input, Layout, Radio, Tag, Select, Tooltip, Tree, Tabs } from 'ant-design-vue'; import VXETable from 'vxe-table'; import { i18n } from '@/locales/setupI18n'; @@ -30,5 +30,6 @@ export function registerGlobComp(app: App) { .use(Select) .use(Tooltip) .use(Tree) + .use(Tabs) .use(VXETable); } diff --git a/src/hooks/web/useCrud.ts b/src/hooks/web/useCrud.ts new file mode 100644 index 000000000..4a58f2356 --- /dev/null +++ b/src/hooks/web/useCrud.ts @@ -0,0 +1,345 @@ +import { ref, reactive, computed, createVNode } from 'vue'; +import type { Ref } from 'vue'; +import { message, Modal } from 'ant-design-vue'; +import { ExclamationCircleOutlined } from '@ant-design/icons-vue'; + +type Params = { page?: number; limit?: number; sortName?: string; sortOrder?: string }; + +type Data = { total: number; rows: Array } | Array; + +/** + * 参数类型 + */ +type Parameter = { + defaultPageSize?: number; + paging: boolean; + defaultSorter?: { sortName: string; sortOrder: string }; + // 搜索参数 + defaultParameter?: { [index: string]: any }; +}; + +/** + * VXE TABLE加载数据hook + * @param service 加载数据服务 + * @param parameter hook 参数 + */ +export const useVxeTable = ( + service: (params: Params, searchParameter: any) => Promise, + parameter: Parameter = { paging: true }, +) => { + // 数据加载状态 + const loading = ref(false); + // 表格数据 + const data = ref>([]); + // 分页数据 + const tablePage: any = parameter.paging + ? reactive({ + total: 0, + currentPage: 1, + pageSize: parameter.defaultPageSize || 500, + }) + : {}; + const searchModel = ref(Object.assign({}, parameter.defaultParameter || {})); + // 排序数据 + const sortData: any = reactive(parameter.defaultSorter || {}); + const sortConfig: any = { + remote: true, + }; + if (parameter.defaultSorter) { + sortConfig.defaultSort = { + field: parameter.defaultSorter.sortName, + order: parameter.defaultSorter.sortOrder, + }; + } + + /** + * 加载数据函数 + */ + const loadData = async () => { + const allParameter: any = { + ...sortData, + }; + if (parameter.paging) { + // 添加分页数据 + Object.assign(allParameter, { + limit: tablePage.pageSize, + page: tablePage.currentPage, + }); + } + loading.value = true; + try { + const result = await service(allParameter, searchModel.value); + if (parameter.paging) { + const { total, rows } = result as any; + tablePage.total = total; + data.value = rows; + } else { + data.value = result as Array; + } + } catch (e) { + // do nothing + } finally { + loading.value = false; + } + }; + /** + * 重置擦欧总 + */ + const handleReset = () => { + searchModel.value = Object.assign({}, parameter.defaultParameter || {}); + loadData(); + }; + + /** + * 重置分页页数 + */ + const handleResetPage = () => { + tablePage.currentPage = 1; + }; + + /** + * 排序变化时触发 + * @param property + * @param order + */ + const sortChange = ({ property, order }: any) => { + sortData.sortName = property; + sortData.sortOrder = order; + loadData(); + }; + /** + * 分页改变时触发 + * @param currentPage 当前页面 + * @param pageSize 页面大小 + */ + const handlePageChange = ({ currentPage, pageSize }: any) => { + if (parameter.paging) { + tablePage.currentPage = currentPage; + tablePage.pageSize = pageSize; + loadData(); + } + }; + + return { + tableProps: computed(() => { + return { + loading: loading.value, + data: data.value, + onSortChange: sortChange, + sortConfig: reactive(sortConfig), + }; + }), + loadData, + handleReset, + handleResetPage, + searchModel, + pageProps: computed(() => { + return { + currentPage: tablePage.currentPage, + pageSize: tablePage.pageSize, + total: tablePage.total, + onPageChange: handlePageChange, + }; + }), + }; +}; + +type AddEditParameter = { + idField?: string; + defaultModel?: any; +}; + +/** + * 添加编辑操作 + * @param gridRef grid 引用 + * @param loadHandler 加载数据函数 + * @param listHandler 查询数据函数 + * @param saveHandler 保存函数 + * @param i18nRender 国际化函数 + * @param parameter 参数 + */ +export const useAddEdit = ( + gridRef: Ref, + loadHandler: (id: any) => Promise, + listHandler: Function | null, + saveHandler: (model: any) => Promise, + i18nRender: Function, + parameter: AddEditParameter = {}, +) => { + const formRef = ref(); + // 是否是加载状态 + const isAdd = ref(false); + // 保存加载状态 + const saveLoading = ref(false); + // 加载状态 + const getLoading = ref(false); + // modal显示状态 + const modalVisible = ref(false); + const addEditModel = ref(Object.assign({}, parameter.defaultModel || {})); + // modal标题计算属性 + const computedTitle = computed(() => { + return isAdd.value ? i18nRender('common.button.add') : i18nRender('common.button.edit'); + }); + + /** + * 添加编辑操作 + * @param add 是否是添加操作 + * @param id ID + */ + const handleAddEdit = async (add: boolean, id: any | null) => { + isAdd.value = add; + // 显示弹窗 + modalVisible.value = true; + if (add) { + addEditModel.value = Object.assign({}, parameter.defaultModel || {}); + } else { + try { + getLoading.value = true; + addEditModel.value = (await loadHandler(id)) || {}; + } catch (e) { + // doNotiong + } finally { + getLoading.value = false; + } + } + }; + + /** + * 设置model函数 + * @param model model + */ + const handleSetModel = (model: any) => { + addEditModel.value = model; + }; + + /** + * 通过checkbox 获取需要编辑的数据 + */ + const handleEditByCheckbox = () => { + // 获取选中行 + const selectRows = gridRef.value.getCheckboxRecords(); + if (selectRows.length !== 1) { + message.error(i18nRender('common.notice.choseOne')); + return false; + } + handleAddEdit(false, selectRows[0][parameter.idField!]); + }; + + /** + * 保存修改操作 + */ + const handleSaveUpdate = async () => { + try { + await formRef.value.validate(); + } catch (e) { + return false; + } + saveLoading.value = true; + try { + await saveHandler(addEditModel.value); + } catch (e) { + // do noting + return false; + } finally { + saveLoading.value = false; + } + // 关闭弹窗 + modalVisible.value = false; + if (listHandler) { + listHandler(); + } + }; + + const handleCancel = () => { + modalVisible.value = false; + }; + return { + modalProps: computed(() => { + return { + title: computedTitle.value, + visible: modalVisible.value, + confirmLoading: saveLoading.value, + onOk: handleSaveUpdate, + onCancel: handleCancel, + }; + }), + formProps: computed(() => { + return { + model: addEditModel.value, + ref: 'formRef', + }; + }), + spinning: getLoading, + handleAddEdit, + handleEditByCheckbox, + formRef, + handleSetModel, + formModel: addEditModel, + }; +}; + +type DeleteParameter = { + idField: string; + listHandler?: Function; + afterDelete?: Function; +}; + +/** + * 删除操作 + */ +export const useVxeDelete = ( + gridRef: Ref | null, + i18nRender: Function, + deleteHandler: (idList: Array) => Promise, + parameter: DeleteParameter, +) => { + const doDelete = (idList: Array) => { + Modal.confirm({ + title: i18nRender('common.button.confirm'), + icon: createVNode(ExclamationCircleOutlined), + content: i18nRender('common.notice.deleteConfirm'), + onOk: async () => { + await deleteHandler(idList); + message.success(i18nRender('common.message.deleteSuccess')); + if (parameter.afterDelete) { + parameter.afterDelete(); + } + }, + }); + }; + + /** + * 删除checkbox选中 + */ + const handleDeleteByCheckbox = () => { + // 获取选中行 + const selectRows = gridRef?.value.getCheckboxRecords(); + if (selectRows.length === 0) { + message.error(i18nRender('common.notice.deleteChoose')); + return false; + } + doDelete(selectRows.map((item: any) => item[parameter.idField])); + }; + + /** + * 通过ID删除 + * @param id + */ + const handleDeleteById = (id: any) => { + doDelete([id]); + }; + + /** + * 通过行删除 + * @param row + */ + const handleDeleteByRow = (row: any) => { + doDelete([row[parameter.idField]]); + }; + + return { + handleDeleteByCheckbox, + handleDeleteById, + handleDeleteByRow, + }; +}; diff --git a/src/modules/system/views/dept/SysDept.api.ts b/src/modules/system/views/dept/SysDept.api.ts new file mode 100644 index 000000000..61e98a836 --- /dev/null +++ b/src/modules/system/views/dept/SysDept.api.ts @@ -0,0 +1,31 @@ +import { ApiServiceEnum, defHttp } from '@/utils/http/axios'; + +enum Api { + getById = 'sys/dept/getById', + saveUpdateBatch = 'sys/dept/saveUpdateBatch', + delete = 'sys/dept/batchDeleteById', +} + +export const getByIdApi = (params) => { + return defHttp.post({ + service: ApiServiceEnum.SMART_SYSTEM, + url: Api.getById, + data: params, + }); +}; + +export const saveUpdateBatchApi = (modelList: any[]) => { + return defHttp.post({ + service: ApiServiceEnum.SMART_SYSTEM, + url: Api.saveUpdateBatch, + data: modelList, + }); +}; + +export const deleteApi = (ids: number[]) => { + return defHttp.post({ + service: ApiServiceEnum.SMART_SYSTEM, + url: Api.delete, + data: ids, + }); +}; diff --git a/src/modules/system/views/dept/SysDeptTreeList.vue b/src/modules/system/views/dept/SysDeptTreeList.vue new file mode 100644 index 000000000..87f04f698 --- /dev/null +++ b/src/modules/system/views/dept/SysDeptTreeList.vue @@ -0,0 +1,214 @@ + + + + + diff --git a/src/modules/system/views/dept/components/SysDeptEdit.vue b/src/modules/system/views/dept/components/SysDeptEdit.vue new file mode 100644 index 000000000..4d18519f5 --- /dev/null +++ b/src/modules/system/views/dept/components/SysDeptEdit.vue @@ -0,0 +1,195 @@ + + + + + diff --git a/src/modules/system/views/dept/components/SysDeptEditModal.vue b/src/modules/system/views/dept/components/SysDeptEditModal.vue new file mode 100644 index 000000000..ff9001e47 --- /dev/null +++ b/src/modules/system/views/dept/components/SysDeptEditModal.vue @@ -0,0 +1,46 @@ + + + + + diff --git a/src/modules/system/views/dept/components/SysDeptTree.vue b/src/modules/system/views/dept/components/SysDeptTree.vue new file mode 100644 index 000000000..5d7bafde2 --- /dev/null +++ b/src/modules/system/views/dept/components/SysDeptTree.vue @@ -0,0 +1,233 @@ + + + + + diff --git a/src/modules/system/views/dept/lang/en_US.ts b/src/modules/system/views/dept/lang/en_US.ts new file mode 100644 index 000000000..76376f0ee --- /dev/null +++ b/src/modules/system/views/dept/lang/en_US.ts @@ -0,0 +1,45 @@ +/** + * 部门表 国际化信息 + */ +export default { + trans: true, + key: 'system.views.dept', + data: { + title: { + deptId: '部门ID', + parent: 'Parent org', + deptCode: 'Org code', + deptType: 'Org type', + deptName: 'Org name', + email: 'Email', + director: 'Director', + phone: 'Phone', + baseMessage: 'Basic message', + }, + validate: { + deptId: '请输入部门ID', + parentId: '请输入上级ID', + deptCode: 'Please enter org code', + deptType: 'Please select org type', + deptName: 'Please enter org name', + email: 'Please enter email', + director: 'Please enter director', + phone: 'Please enter phone', + }, + rules: { + deptCode_NOT_EMPTY: 'Org code cannot be empty', + deptType_NOT_EMPTY: 'Please select org type', + deptName_NOT_EMPTY: 'Org name cannot be empty', + }, + search: { + deptCode: 'Please enter org code', + deptName: 'Please enter org name', + }, + button: { + addChild: 'Add child', + }, + message: { + selectDeptError: 'Please select parent org', + }, + }, +}; diff --git a/src/modules/system/views/dept/lang/zh_CN.ts b/src/modules/system/views/dept/lang/zh_CN.ts new file mode 100644 index 000000000..e51505b38 --- /dev/null +++ b/src/modules/system/views/dept/lang/zh_CN.ts @@ -0,0 +1,50 @@ +/** + * 部门表 国际化信息 + */ +export default { + trans: true, + key: 'system.views.dept', + data: { + title: { + deptId: '部门ID', + parent: '上级组织', + deptCode: '组织编码', + deptType: '组织类型', + deptName: '组织名称', + email: '邮箱', + director: '负责人', + phone: '电话', + baseMessage: '基本信息', + }, + validate: { + deptId: '请输入部门ID', + parentId: '请输入上级ID', + deptCode: '请输入组织编码', + deptType: '请输入组织类型', + deptName: '请输入组织名称', + email: '请输入邮箱', + director: '请输入负责人', + phone: '请输入电话', + deleteYn: '请输入删除状态', + createUserId: '请输入创建人ID', + createTime: '请输入创建时间', + updateUserId: '请输入更新人员ID', + updateTime: '请输入更新时间', + }, + rules: { + deptCode_NOT_EMPTY: '组织编码不能为空', + deptType_NOT_EMPTY: '请选择组织类型', + deptName_NOT_EMPTY: '请输入组织名称', + }, + search: { + deptCode: '请输入组织编码', + deptName: '请输入组织名称', + }, + button: { + addChild: '添加下级', + }, + message: { + selectDeptError: '请选择上级组织', + }, + }, +}; diff --git a/src/modules/system/views/i18n/I18nMainView.vue b/src/modules/system/views/i18n/I18nMainView.vue new file mode 100644 index 000000000..f2b044b03 --- /dev/null +++ b/src/modules/system/views/i18n/I18nMainView.vue @@ -0,0 +1,59 @@ + + + + + diff --git a/src/modules/system/views/i18n/components/I18nGroupList.vue b/src/modules/system/views/i18n/components/I18nGroupList.vue new file mode 100644 index 000000000..a34936c38 --- /dev/null +++ b/src/modules/system/views/i18n/components/I18nGroupList.vue @@ -0,0 +1,136 @@ + + + + + diff --git a/src/modules/system/views/i18n/components/I18nItemList.vue b/src/modules/system/views/i18n/components/I18nItemList.vue new file mode 100644 index 000000000..0f907d111 --- /dev/null +++ b/src/modules/system/views/i18n/components/I18nItemList.vue @@ -0,0 +1,112 @@ + + + + + diff --git a/src/modules/system/views/i18n/components/i18n.api.ts b/src/modules/system/views/i18n/components/i18n.api.ts new file mode 100644 index 000000000..cdaa4c820 --- /dev/null +++ b/src/modules/system/views/i18n/components/i18n.api.ts @@ -0,0 +1,111 @@ +import { ApiServiceEnum, defHttp } from '@/utils/http/axios'; + +enum Api { + listI18n = 'sys/i18n/list', + getI18nById = 'sys/i18n/getById', + i18nSaveUpdate = 'sys/i18n/saveUpdate', + i18nDelete = 'sys/i18n/batchDeleteById', + getGroupById = 'sys/i18n/getGroupById', + listGroup = 'sys/i18n/listGroup', + saveUpdateGroup = 'sys/i18n/saveOrUpdateGroup', + deleteGroup = 'sys/i18n/deleteGroup', + getI18nItemById = 'sys/i18nItem/getById', + saveUpdateI18nItem = 'sys/i18nItem/saveUpdate', + batchDeleteI18nItemById = 'sys/i18nItem/batchDeleteById', + listI18nItem = 'sys/i18nItem/list', +} + +export const listI18nApi = (params: Recordable) => { + return defHttp.post({ + service: ApiServiceEnum.SMART_SYSTEM, + url: Api.listI18n, + data: params, + }); +}; + +export const getI18nByIdApi = (id: number) => { + return defHttp.post({ + service: ApiServiceEnum.SMART_SYSTEM, + url: Api.getI18nById, + data: id, + }); +}; + +export const i18nSaveUpdateApi = (model: any) => { + return defHttp.post({ + service: ApiServiceEnum.SMART_SYSTEM, + url: Api.i18nSaveUpdate, + data: model, + }); +}; + +export const i18nDeleteApi = (deleteData: any[]) => { + return defHttp.post({ + service: ApiServiceEnum.SMART_SYSTEM, + url: Api.i18nDelete, + data: deleteData.map((item) => item.i18nId), + }); +}; + +export const getGroupByIdApi = (groupId: number) => { + return defHttp.post({ + service: ApiServiceEnum.SMART_SYSTEM, + url: Api.getGroupById, + data: groupId, + }); +}; + +export const listGroupApi = () => { + return defHttp.post({ + service: ApiServiceEnum.SMART_SYSTEM, + url: Api.listGroup, + }); +}; + +export const saveUpdateGroupApi = (model: Recordable) => { + return defHttp.post({ + service: ApiServiceEnum.SMART_SYSTEM, + url: Api.saveUpdateGroup, + data: model, + }); +}; + +export const deleteGroupApi = (ids: number[]) => { + return defHttp.post({ + service: ApiServiceEnum.SMART_SYSTEM, + url: Api.deleteGroup, + data: ids, + }); +}; + +export const getI18nItemByIdApi = (id: number) => { + return defHttp.post({ + service: ApiServiceEnum.SMART_SYSTEM, + url: Api.getI18nItemById, + data: id, + }); +}; + +export const saveUpdateI18nItemApi = (data: Recordable) => { + return defHttp.post({ + service: ApiServiceEnum.SMART_SYSTEM, + url: Api.saveUpdateI18nItem, + data, + }); +}; + +export const batchDeleteI18nItemByIdApi = (idList) => { + return defHttp.post({ + service: ApiServiceEnum.SMART_SYSTEM, + url: Api.batchDeleteI18nItemById, + data: idList, + }); +}; + +export const listI18nItemApi = (data) => { + return defHttp.post({ + service: ApiServiceEnum.SMART_SYSTEM, + url: Api.listI18nItem, + data: data, + }); +}; diff --git a/src/modules/system/views/i18n/components/i18n.config.ts b/src/modules/system/views/i18n/components/i18n.config.ts new file mode 100644 index 000000000..7cb60c54d --- /dev/null +++ b/src/modules/system/views/i18n/components/i18n.config.ts @@ -0,0 +1,189 @@ +import type { SmartColumn } from '@/components/SmartTable'; +import type { FormSchema } from '@/components/Form'; +import dayjs from 'dayjs'; +import { tableUseYnClass } from '@/components/SmartTable'; + +export const getI18nTableColumns = (): SmartColumn[] => { + return [ + { + type: 'checkbox', + width: 60, + align: 'center', + fixed: 'left', + }, + { + field: 'platform', + title: '{system.views.i18n.i18n.titlePlatform}', + width: 120, + }, + { + field: 'i18nCode', + title: '{system.views.i18n.i18n.titleI18nCode}', + minWidth: 260, + }, + { + field: 'remark', + title: '{common.table.remark}', + width: 200, + }, + { + field: 'createTime', + title: '{common.table.createTime}', + width: 160, + }, + { + field: 'createUser', + title: '{common.table.createUser}', + width: 120, + }, + { + field: 'updateTime', + title: '{common.table.updateTime}', + width: 160, + }, + { + field: 'updateUser', + title: '{common.table.updateUser}', + width: 120, + }, + { + ...tableUseYnClass(), + sortable: true, + }, + { + field: 'seq', + title: '{common.table.seq}', + width: 120, + sortable: true, + }, + { + field: 'i18nId', + title: '{common.table.operation}', + width: 120, + fixed: 'right', + slots: { + default: 'table-operation', + }, + }, + ]; +}; + +export const getI18nAddEditSchemas = (t: Function): FormSchema[] => { + return [ + { + label: '', + field: 'groupId', + component: 'Input', + show: false, + }, + { + label: '', + field: 'i18nId', + component: 'Input', + show: false, + }, + { + label: t('system.views.i18n.i18n.titlePlatform'), + field: 'platform', + component: 'Select', + required: true, + componentProps: { + options: [ + { + label: 'backstage', + value: 'backstage', + }, + ], + }, + }, + { + label: t('system.views.i18n.i18n.titleI18nCode'), + field: 'i18nCode', + component: 'Input', + required: true, + }, + { + label: t('common.table.remark'), + field: 'remark', + component: 'InputTextArea', + }, + { + label: t('common.table.seq'), + field: 'seq', + component: 'InputNumber', + componentProps: { + style: { width: '100%' }, + }, + required: true, + }, + ]; +}; + +export const getI18nItemListTableColumn = (): SmartColumn[] => { + return [ + { + type: 'checkbox', + width: 60, + align: 'center', + fixed: 'left', + }, + { + title: '{system.views.i18n.i18nItem.titleLocale}', + field: 'locale', + width: 120, + }, + { + title: '{system.views.i18n.i18nItem.titleValue}', + field: 'value', + minWidth: 160, + }, + { + field: 'createTime', + title: '{common.table.createTime}', + width: 160, + formatter: ({ cellValue }: any) => { + if (cellValue) { + return dayjs(cellValue).format('YYYY-MM-DD HH:mm:ss'); + } + return ''; + }, + }, + { + field: 'createUser', + title: '{common.table.createUser}', + width: 120, + }, + { + field: 'i18nItemId', + title: '{common.table.operation}', + width: 120, + fixed: 'right', + slots: { + default: 'table-operation', + }, + }, + ]; +}; + +export const getI18nItemListAddEditFormSchema = (t: Function): FormSchema[] => { + return [ + { + label: '', + field: 'i18nItemId', + component: 'Input', + show: false, + }, + { + label: t('system.views.i18n.i18nItem.titleLocale'), + field: 'locale', + component: 'Input', + required: true, + }, + { + label: t('system.views.i18n.i18nItem.titleValue'), + field: 'value', + component: 'InputTextArea', + required: true, + }, + ]; +}; diff --git a/src/modules/system/views/i18n/components/i18nList.vue b/src/modules/system/views/i18n/components/i18nList.vue new file mode 100644 index 000000000..d91c88207 --- /dev/null +++ b/src/modules/system/views/i18n/components/i18nList.vue @@ -0,0 +1,176 @@ + + + + + diff --git a/src/modules/system/views/i18n/lang/en_US.ts b/src/modules/system/views/i18n/lang/en_US.ts new file mode 100644 index 000000000..533cdd095 --- /dev/null +++ b/src/modules/system/views/i18n/lang/en_US.ts @@ -0,0 +1,39 @@ +export default { + system: { + views: { + i18n: { + group: { + groupName: 'Group name', + seq: 'Seq', + groupNameValidate: 'Please enter i18n group', + seqValidate: 'Please enter seq', + }, + i18n: { + titlePlatform: 'Platform', + titleI18nCode: 'I18n code', + platformValidate: 'Please select a platform', + i18nCodeValidate: 'Please enter i18n code', + groupIdValidate: 'Please click Group first, and then add', + platform: { + backstage: 'Backstage', + }, + button: { + reload: 'Reload', + }, + message: { + reloadConfirm: 'Are you sure you want to reload I18N?', + reloadContent: 'You need to log in again after reloading', + reloadSuccess: 'Reload success', + }, + }, + i18nItem: { + titleLocale: 'Locale', + titleValue: 'Value', + localeValidate: '请输入语言', + valueValidate: 'Please enter locale', + i18nIdValidate: 'Please select i18n before adding', + }, + }, + }, + }, +}; diff --git a/src/modules/system/views/i18n/lang/zh_CN.ts b/src/modules/system/views/i18n/lang/zh_CN.ts new file mode 100644 index 000000000..5d506277c --- /dev/null +++ b/src/modules/system/views/i18n/lang/zh_CN.ts @@ -0,0 +1,39 @@ +export default { + system: { + views: { + i18n: { + group: { + groupName: '国际化组', + seq: '序号', + groupNameValidate: '请输入国际化组', + seqValidate: '请输入序号', + }, + i18n: { + titlePlatform: '平台', + titleI18nCode: '国际化编码', + platformValidate: '请选择平台', + i18nCodeValidate: '请输入国际化编码', + groupIdValidate: '请先点击分组,再执行添加操作', + platform: { + backstage: '后台', + }, + button: { + reload: '重新载入', + }, + message: { + reloadConfirm: '确定要刷新国际化信息吗?', + reloadContent: '刷新后需要重新登陆', + reloadSuccess: '重新加载成功', + }, + }, + i18nItem: { + titleLocale: '语言', + titleValue: 'value', + localeValidate: '请输入语言', + valueValidate: '请输入value', + i18nIdValidate: '请先选择国际化信息,再执行添加操作', + }, + }, + }, + }, +}; diff --git a/src/modules/system/views/userGroup/UserGroupListView.api.ts b/src/modules/system/views/userGroup/UserGroupListView.api.ts new file mode 100644 index 000000000..f0d3c98c9 --- /dev/null +++ b/src/modules/system/views/userGroup/UserGroupListView.api.ts @@ -0,0 +1,61 @@ +import { ApiServiceEnum, defHttp } from '@/utils/http/axios'; + +enum Api { + list = 'sys/userGroup/list', + batchSaveUpdate = 'sys/userGroup/batchSaveUpdate', + delete = 'sys/userGroup/batchDeleteById', + getById = 'sys/userGroup/getById', + listUserIdByGroupId = 'sys/userGroup/listUserIdById', + setUser = 'sys/userGroup/saveUserGroupByGroupId', +} + +export const listApi = (parameter) => { + return defHttp.post({ + service: ApiServiceEnum.SMART_SYSTEM, + url: Api.list, + data: parameter, + }); +}; + +export const batchSaveUpdateApi = (dataList: any[]) => { + return defHttp.post({ + service: ApiServiceEnum.SMART_SYSTEM, + url: Api.batchSaveUpdate, + data: dataList, + }); +}; + +export const deleteApi = (dataList: any[]) => { + return defHttp.post({ + service: ApiServiceEnum.SMART_SYSTEM, + url: Api.delete, + data: dataList.map((item) => item.groupId), + }); +}; + +export const getByIdApi = (data) => { + return defHttp.post({ + service: ApiServiceEnum.SMART_SYSTEM, + url: Api.getById, + data: data.groupId, + }); +}; + +export const listUserIdByGroupIdApi = (groupId: number) => { + return defHttp.post({ + service: ApiServiceEnum.SMART_SYSTEM, + url: Api.listUserIdByGroupId, + data: groupId, + }); +}; + +export const setUserApi = (groupId: number, userIdList: number[]) => { + return defHttp.post({ + service: ApiServiceEnum.SMART_SYSTEM, + url: Api.setUser, + data: { + groupId, + userIdList, + }, + }); +}; diff --git a/src/modules/system/views/userGroup/UserGroupListView.config.ts b/src/modules/system/views/userGroup/UserGroupListView.config.ts new file mode 100644 index 000000000..ee7b0a0dd --- /dev/null +++ b/src/modules/system/views/userGroup/UserGroupListView.config.ts @@ -0,0 +1,164 @@ +import type { SmartColumn, SmartSearchFormSchema } from '@/components/SmartTable'; +import { tableUseYnClass } from '@/components/SmartTable'; +import type { FormSchema } from '@/components/Form'; + +export const getTableColumns = (): SmartColumn[] => { + return [ + { + type: 'checkbox', + width: 60, + align: 'center', + fixed: 'left', + }, + { + title: '{system.views.userGroup.table.groupCode}', + field: 'groupCode', + fixed: 'left', + width: 160, + }, + { + title: '{system.views.userGroup.table.groupName}', + field: 'groupName', + fixed: 'left', + width: 120, + }, + { + ...tableUseYnClass(), + sortable: true, + }, + { + title: '{common.table.remark}', + field: 'remark', + minWidth: 160, + }, + { + title: '{common.table.seq}', + field: 'seq', + width: 100, + sortable: true, + }, + { + title: '{common.table.createTime}', + field: 'createTime', + width: 165, + sortable: true, + }, + { + title: '{common.table.createUser}', + field: 'createUserId', + width: 120, + formatter: ({ row }: any) => { + if (row.createUser) { + return row.createUser.fullName; + } + return ''; + }, + }, + { + title: '{common.table.updateTime}', + field: 'updateTime', + width: 165, + sortable: true, + }, + { + title: '{common.table.updateUser}', + field: 'updateUserId', + width: 120, + formatter: ({ row }: any) => { + if (row.updateUser) { + return row.updateUser.fullName; + } + return ''; + }, + }, + { + title: '{common.table.operation}', + field: 'operation', + width: 120, + fixed: 'right', + slots: { + default: 'table-operation', + }, + }, + ]; +}; + +export const getSearchSchemas = (t: Function): SmartSearchFormSchema[] => { + return [ + { + label: t('system.views.userGroup.table.groupCode'), + field: 'groupCode', + component: 'Input', + searchSymbol: 'like', + }, + { + label: t('system.views.userGroup.table.groupName'), + field: 'groupName', + component: 'Input', + searchSymbol: 'like', + }, + { + label: t('system.views.userGroup.search.useYnTitle'), + field: 'useYn', + component: 'Select', + defaultValue: 1, + searchSymbol: '=', + componentProps: { + style: { + width: '100px', + }, + options: [ + { + label: t('common.form.use'), + value: 1, + }, + { + label: t('common.form.noUse'), + value: 0, + }, + ], + }, + }, + ]; +}; + +export const getAddEditFormSchemas = (t: Function): FormSchema[] => { + return [ + { + label: '', + field: 'groupId', + component: 'Input', + show: false, + }, + { + label: t('system.views.userGroup.table.groupCode'), + field: 'groupCode', + component: 'Input', + required: true, + }, + { + label: t('system.views.userGroup.table.groupName'), + field: 'groupName', + component: 'Input', + required: true, + }, + { + label: t('common.table.useYn'), + field: 'useYn', + component: 'Switch', + defaultValue: true, + }, + { + label: t('common.table.seq'), + field: 'seq', + component: 'InputNumber', + defaultValue: 1, + required: true, + }, + { + label: t('common.table.remark'), + field: 'remark', + component: 'InputTextArea', + }, + ]; +}; diff --git a/src/modules/system/views/userGroup/UserGroupListView.vue b/src/modules/system/views/userGroup/UserGroupListView.vue new file mode 100644 index 000000000..197a37404 --- /dev/null +++ b/src/modules/system/views/userGroup/UserGroupListView.vue @@ -0,0 +1,134 @@ + + + + + diff --git a/src/modules/system/views/userGroup/UserGroupSupport.ts b/src/modules/system/views/userGroup/UserGroupSupport.ts new file mode 100644 index 000000000..1dd90cf1a --- /dev/null +++ b/src/modules/system/views/userGroup/UserGroupSupport.ts @@ -0,0 +1,313 @@ +import { ref, onMounted, reactive, createVNode, unref } from 'vue'; +import type { Ref } from 'vue'; +import { useI18n } from 'vue-i18n'; + +import { message, Modal } from 'ant-design-vue'; +import { ExclamationCircleOutlined } from '@ant-design/icons-vue'; +import { ApiServiceEnum, defHttp } from '@/utils/http/axios'; + +const defaultSearchModel = { + groupCode: '', + groupName: '', + useYn: 1, +}; + +/** + * 加载数据 + */ +export const vueLoadData = () => { + const sortData = reactive({ + sortName: 'seq', + sortOrder: 'asc', + }); + // 数据 + const data = ref([]); + // 数据加载状态 + const loading = ref(false); + // 搜索表单 + const searchModel = ref(Object.assign({}, defaultSearchModel)); + // 分页信息 + const tablePage = reactive({ + total: 0, + currentPage: 1, + pageSize: 500, + }); + /** + * 加载数据函数 + */ + const loadData = async () => { + // 构建参数 + const allParameter: any = { + ...sortData, + limit: tablePage.pageSize, + page: tablePage.currentPage, + }; + // 自定义参数 + const customParameter: any = { + QUERY_CREATE_UPDATE_USER: true, + }; + Object.keys(searchModel.value).forEach((key) => { + const value = searchModel.value[key]; + if (value !== null && value !== '') { + if (typeof value === 'string') { + if (value.trim() !== '') { + customParameter[key + '@like'] = value; + } + } else { + customParameter[key + '@='] = value; + } + } + }); + allParameter.parameter = customParameter; + loading.value = true; + try { + const { rows, total } = await defHttp.post({ + service: ApiServiceEnum.SMART_SYSTEM, + url: 'sys/userGroup/list', + data: allParameter, + }); + tablePage.total = total; + data.value = rows; + } finally { + loading.value = false; + } + }; + /** + * 排序变化时触发 + */ + const handleSortChange = ({ property, order }: any) => { + sortData.sortOrder = order; + sortData.sortName = property; + loadData(); + }; + /** + * 分页触发事件 + */ + const handlePageChange = ({ currentPage, pageSize }: any) => { + tablePage.currentPage = currentPage; + tablePage.pageSize = pageSize; + loadData(); + }; + /** + * 重置搜索表单 + */ + const resetSearch = () => { + searchModel.value = Object.assign({}, defaultSearchModel); + }; + onMounted(loadData); + return { + data, + loading, + searchModel, + tablePage, + handlePageChange, + resetSearch, + loadData, + handleSortChange, + }; +}; + +const defaultAddEditModel = { + useYn: true, + seq: 1, +}; + +/** + * 添加修改编码验证规则 + */ +const addEditFormRules = (t: Function) => { + return { + groupCode: [ + { required: true, trigger: 'blur', message: t('system.views.userGroup.validate.groupCode') }, + ], + groupName: [ + { required: true, trigger: 'blur', message: t('system.views.userGroup.validate.groupName') }, + ], + }; +}; + +export const vueAddUpdate = (loadData: any) => { + const t = useI18n().t; + // 保存加载状态 + const saveLoading = ref(false); + // 查询加载状态 + const getLoading = ref(false); + // 弹窗状态 + const addEditModalVisible = ref(false); + // 是否是添加 + const isAdd = ref(false); + // 添加修改表单 + const addEditModel = ref(Object.assign({}, defaultAddEditModel)); + /** + * 添加操作 + */ + const handleShowAdd = () => { + addEditModel.value = Object.assign({}, defaultAddEditModel); + isAdd.value = true; + addEditModalVisible.value = true; + }; + /** + * 编辑操作 + * @param id + */ + const handleShowEdit = async (id: number) => { + isAdd.value = false; + addEditModalVisible.value = true; + getLoading.value = true; + try { + addEditModel.value = await defHttp.post({ + service: ApiServiceEnum.SMART_SYSTEM, + url: 'sys/userGroup/getById', + data: id, + }); + } finally { + getLoading.value = false; + } + }; + /** + * 保存操作 + */ + const handleSave = async () => { + saveLoading.value = true; + try { + await defHttp.post({ + service: ApiServiceEnum.SMART_SYSTEM, + url: 'sys/userGroup/saveUpdate', + data: unref(addEditModel), + }); + addEditModalVisible.value = false; + loadData(); + } finally { + saveLoading.value = false; + } + }; + return { + saveLoading, + getLoading, + addEditModalVisible, + isAdd, + addEditModel, + handleShowAdd, + handleShowEdit, + handleSave, + formRules: reactive(addEditFormRules(t)), + }; +}; + +/** + * 删除操作 + */ +export const vueDelete = (gridRef: Ref, loadData: any) => { + /** + * 删除操作 + */ + const handleDelete = () => { + // 获取选中行 + const selectRows = gridRef.value.getCheckboxRecords(); + if (selectRows.length === 0) { + message.error('请选择要删除的数据'); + return false; + } + Modal.confirm({ + title: '确认', + icon: createVNode(ExclamationCircleOutlined), + content: '确定要删除吗?', + onOk: async () => { + await defHttp.post({ + service: ApiServiceEnum.SMART_SYSTEM, + url: 'sys/userGroup/batchDeleteById', + data: selectRows.map((item: any) => item.groupId), + }); + loadData(); + }, + }); + }; + return { + handleDelete, + }; +}; + +/** + * 设置用户 + */ +export const vueSetUser = () => { + const loadUserLoading = ref(false); + const setUserLoading = ref(false); + const setUserModalVisible = ref(false); + // 所有用户数据 + const allUserData = ref>([]); + // 选中的key + const targetKeys = ref>([]); + let selectRow: any = null; + /** + * 设置用户操作 + */ + const handleShowSetUser = async (row: any) => { + selectRow = row; + setUserModalVisible.value = true; + loadUserLoading.value = true; + try { + loadUserLoading.value = true; + // 加载所有用户数据 + if (allUserData.value.length === 0) { + const result: Array = await defHttp.post({ + service: ApiServiceEnum.SMART_SYSTEM, + url: 'sys/user/list', + data: { + sortName: 'seq', + parameter: { + 'useYn@=': true, + }, + }, + }); + allUserData.value = result.map(({ userId, fullName, username }: any) => { + return { + key: userId + '', + title: `${fullName}[${username}]`, + }; + }); + } + // 加载用户关联数据 + const result: Array = await defHttp.post({ + service: ApiServiceEnum.SMART_SYSTEM, + url: 'sys/userGroup/listUserIdById', + data: selectRow.groupId, + }); + targetKeys.value = result.map((item) => item + ''); + } finally { + loadUserLoading.value = false; + } + }; + const handleTransChange = (targetKeyList: Array) => { + targetKeys.value = targetKeyList; + }; + /** + * 设置用户操作 + */ + const handleSetUser = async () => { + try { + setUserLoading.value = true; + await defHttp.post({ + service: ApiServiceEnum.SMART_SYSTEM, + url: 'sys/userGroup/saveUserGroupByGroupId', + data: { + groupId: selectRow.groupId, + userIdList: targetKeys.value, + }, + }); + setUserModalVisible.value = false; + } finally { + setUserLoading.value = false; + } + }; + return { + loadUserLoading, + setUserLoading, + handleShowSetUser, + setUserModalVisible, + handleSetUser, + allUserData, + targetKeys, + handleTransChange, + }; +}; diff --git a/src/modules/system/views/userGroup/hooks/useSetUser.ts b/src/modules/system/views/userGroup/hooks/useSetUser.ts new file mode 100644 index 000000000..3192de29c --- /dev/null +++ b/src/modules/system/views/userGroup/hooks/useSetUser.ts @@ -0,0 +1,53 @@ +import { useModal } from '@/components/Modal'; +import { ref, unref } from 'vue'; +import { message } from 'ant-design-vue'; + +import { listUserIdByGroupIdApi, setUserApi } from '../UserGroupListView.api'; + +/** + * 设置用户组人员信息 + */ +export const useSetUser = (t: Function) => { + const [registerSetUserModal, { openModal, setModalProps, closeModal }] = useModal(); + const currentUserGroup = ref(null); + const selectUserList = ref([]); + + /** + * 显示设置用户弹窗 + * @param userGroup + */ + const handleShowSetUser = async (userGroup: Recordable) => { + currentUserGroup.value = userGroup; + openModal(true); + try { + setModalProps({ loading: true }); + // 获取已关联的用户信息 + selectUserList.value = await listUserIdByGroupIdApi(userGroup.groupId); + } finally { + setModalProps({ loading: false }); + } + }; + + /** + * 设置用户 + * @param userIdList + */ + const handleUserSelected = async (userIdList: number[]) => { + selectUserList.value = userIdList; + try { + setModalProps({ confirmLoading: true }); + await setUserApi(unref(currentUserGroup)!.groupId, userIdList); + message.success(t('common.message.OperationSucceeded')); + closeModal(); + } finally { + setModalProps({ confirmLoading: false }); + } + }; + + return { + registerSetUserModal, + handleShowSetUser, + handleUserSelected, + selectUserList, + }; +}; diff --git a/src/modules/system/views/userGroup/lang/en_US.ts b/src/modules/system/views/userGroup/lang/en_US.ts new file mode 100644 index 000000000..d7d0b9eb9 --- /dev/null +++ b/src/modules/system/views/userGroup/lang/en_US.ts @@ -0,0 +1,24 @@ +export default { + system: { + views: { + userGroup: { + title: 'User group', + table: { + groupCode: 'Group code', + groupName: 'Group name', + remark: 'Remark', + }, + button: { + setUser: 'Set user', + }, + search: { + useYnTitle: 'Use yn', + }, + validate: { + groupCode: 'Please enter group code', + groupName: 'Please enter group name', + }, + }, + }, + }, +}; diff --git a/src/modules/system/views/userGroup/lang/zh_CN.ts b/src/modules/system/views/userGroup/lang/zh_CN.ts new file mode 100644 index 000000000..aeec870ca --- /dev/null +++ b/src/modules/system/views/userGroup/lang/zh_CN.ts @@ -0,0 +1,24 @@ +export default { + system: { + views: { + userGroup: { + title: '用户组', + table: { + groupCode: '用户组编码', + groupName: '用户组名称', + remark: '备注', + }, + button: { + setUser: '设置用户', + }, + search: { + useYnTitle: '启用标志', + }, + validate: { + groupCode: '请输入用户组编码', + groupName: '请输入用户组名称', + }, + }, + }, + }, +};