27 changed files with 2803 additions and 2 deletions
@ -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<any> } | Array<any>; |
|||
|
|||
/** |
|||
* 参数类型 |
|||
*/ |
|||
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<Data>, |
|||
parameter: Parameter = { paging: true }, |
|||
) => { |
|||
// 数据加载状态
|
|||
const loading = ref(false); |
|||
// 表格数据
|
|||
const data = ref<Array<any>>([]); |
|||
// 分页数据
|
|||
const tablePage: any = parameter.paging |
|||
? reactive({ |
|||
total: 0, |
|||
currentPage: 1, |
|||
pageSize: parameter.defaultPageSize || 500, |
|||
}) |
|||
: {}; |
|||
const searchModel = ref<any>(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<any>; |
|||
} |
|||
} 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<any>, |
|||
listHandler: Function | null, |
|||
saveHandler: (model: any) => Promise<void>, |
|||
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<any>) => Promise<void>, |
|||
parameter: DeleteParameter, |
|||
) => { |
|||
const doDelete = (idList: Array<any>) => { |
|||
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, |
|||
}; |
|||
}; |
|||
@ -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, |
|||
}); |
|||
}; |
|||
@ -0,0 +1,214 @@ |
|||
<template> |
|||
<div class="full-height dept-container" style="padding: 10px"> |
|||
<div class="full-height left-tree" style="padding: 10px; background: white"> |
|||
<div> |
|||
<a-button |
|||
v-permission="'sys:dept:save'" |
|||
type="primary" |
|||
:size="buttonSizeConfig" |
|||
@click="handleAdd" |
|||
> |
|||
<plus-outlined /> |
|||
{{ $t('common.button.add') }} |
|||
</a-button> |
|||
<a-button |
|||
type="primary" |
|||
v-permission="'sys:dept:save'" |
|||
:size="buttonSizeConfig" |
|||
style="margin-left: 5px" |
|||
@click="handleAddChild" |
|||
> |
|||
<plus-outlined /> |
|||
{{ $t('system.views.dept.button.addChild') }} |
|||
</a-button> |
|||
<a-button |
|||
type="primary" |
|||
style="margin-left: 5px" |
|||
v-permission="'sys:dept:delete'" |
|||
danger |
|||
:size="buttonSizeConfig" |
|||
@click="handleDelete" |
|||
> |
|||
<delete-outlined /> |
|||
{{ $t('common.button.delete') }} |
|||
</a-button> |
|||
</div> |
|||
<sys-dept-tree style="margin-top: 5px" ref="treeRef" show-search @select="handleTreeSelect" /> |
|||
</div> |
|||
<div class="full-height right-tab"> |
|||
<a-tabs> |
|||
<a-tab-pane :tab="$t('system.views.dept.title.baseMessage')"> |
|||
<sys-dept-edit ref="formRef" :filter-field="false" :dept-id="currentDeptRef?.deptId" /> |
|||
<div style="text-align: right"> |
|||
<a-button |
|||
:loading="saveLoading" |
|||
@click="handleSave" |
|||
:disabled="currentDeptRef === null || currentDeptRef === undefined" |
|||
style=" margin-right: 10px;text-align: right" |
|||
type="primary" |
|||
> |
|||
{{ $t('common.button.save') }} |
|||
</a-button> |
|||
</div> |
|||
</a-tab-pane> |
|||
</a-tabs> |
|||
</div> |
|||
<SysDeptEditModal @after-save="reloadDeptTree" @register="registerModal" /> |
|||
</div> |
|||
</template> |
|||
|
|||
<script lang="ts"> |
|||
import { defineComponent, ref, unref } from 'vue'; |
|||
import { useI18n } from 'vue-i18n'; |
|||
|
|||
import { PlusOutlined, DeleteOutlined } from '@ant-design/icons-vue'; |
|||
|
|||
import { useSizeSetting } from '@/hooks/setting/UseSizeSetting'; |
|||
import { useModal } from '@/components/Modal'; |
|||
import { errorMessage, successMessage } from '@/utils/message/SystemNotice'; |
|||
|
|||
import SysDeptTree from './components/SysDeptTree.vue'; |
|||
import SysDeptEdit from './components/SysDeptEdit.vue'; |
|||
import SysDeptEditModal from './components/SysDeptEditModal.vue'; |
|||
import { deleteApi, saveUpdateBatchApi } from './SysDept.api'; |
|||
import { useVxeDelete } from '@/hooks/web/useCrud'; |
|||
|
|||
/** |
|||
* 部门管理树 |
|||
*/ |
|||
export default defineComponent({ |
|||
name: 'SysDeptTreeList', |
|||
components: { |
|||
SysDeptTree, |
|||
PlusOutlined, |
|||
DeleteOutlined, |
|||
SysDeptEdit, |
|||
SysDeptEditModal, |
|||
}, |
|||
setup() { |
|||
const gridRef = ref(); |
|||
const treeRef = ref(); |
|||
const formRef = ref(); |
|||
|
|||
const { t } = useI18n(); |
|||
const parentFieldVisible = ref(false); |
|||
|
|||
const [registerModal, { openModal }] = useModal(); |
|||
|
|||
/** |
|||
* 当前选中节点的code |
|||
*/ |
|||
const currentDeptRef = ref<Recordable | null>(null); |
|||
const handleTreeSelect = (_: Array<number>, { selectedNodes, selected }) => { |
|||
if (!selected || selectedNodes.length === 0) { |
|||
currentDeptRef.value = null; |
|||
} |
|||
currentDeptRef.value = selectedNodes[0]; |
|||
}; |
|||
|
|||
const reloadDeptTree = () => { |
|||
treeRef.value.reload(); |
|||
}; |
|||
|
|||
/** |
|||
* 添加操作函数 |
|||
*/ |
|||
const handleAdd = () => { |
|||
openModal(true, { |
|||
parentId: 0, |
|||
parentName: '根', |
|||
}); |
|||
}; |
|||
/** |
|||
* 添加下级 |
|||
*/ |
|||
const handleAddChild = () => { |
|||
const currentDept = unref(currentDeptRef); |
|||
if (!currentDept) { |
|||
errorMessage(t('system.views.dept.message.selectDeptError')); |
|||
return false; |
|||
} |
|||
const { deptId, deptName } = unref(currentDeptRef) || {}; |
|||
openModal(true, { |
|||
parentId: deptId, |
|||
parentName: deptName, |
|||
}); |
|||
}; |
|||
|
|||
/** |
|||
* 删除hook |
|||
*/ |
|||
const { handleDeleteById } = useVxeDelete(null, t, deleteApi, { |
|||
idField: 'deptId', |
|||
afterDelete: () => reloadDeptTree(), |
|||
}); |
|||
const handleDelete = () => { |
|||
const currentDept = unref(currentDeptRef); |
|||
if (!currentDept) { |
|||
errorMessage(t('common.notice.deleteChoose')); |
|||
return false; |
|||
} |
|||
handleDeleteById(currentDept.deptId); |
|||
}; |
|||
|
|||
/** |
|||
* 保存操作 |
|||
*/ |
|||
const saveLoading = ref(false); |
|||
const handleSave = async () => { |
|||
const formModel = await unref(formRef).validate(); |
|||
try { |
|||
saveLoading.value = true; |
|||
await saveUpdateBatchApi([formModel]); |
|||
console.log(); |
|||
successMessage(t('common.message.saveSuccess')); |
|||
await reloadDeptTree(); |
|||
} finally { |
|||
saveLoading.value = false; |
|||
} |
|||
}; |
|||
return { |
|||
gridRef, |
|||
treeRef, |
|||
handleTreeSelect, |
|||
parentFieldVisible, |
|||
handleDelete, |
|||
...useSizeSetting(), |
|||
handleAdd, |
|||
registerModal, |
|||
reloadDeptTree, |
|||
currentDeptRef, |
|||
handleAddChild, |
|||
formRef, |
|||
handleSave, |
|||
saveLoading, |
|||
}; |
|||
}, |
|||
}); |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
@leftWidth: 40%; |
|||
|
|||
.left-tree { |
|||
display: inline-block; |
|||
width: @leftWidth; |
|||
} |
|||
|
|||
.right-tab { |
|||
display: inline-block; |
|||
width: calc(60% - 10px); |
|||
margin-left: 10px; |
|||
padding: 10px; |
|||
float: right; |
|||
background: white; |
|||
|
|||
:deep(.ant-tabs) { |
|||
height: 100%; |
|||
|
|||
.ant-tabs-content { |
|||
height: 100%; |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,195 @@ |
|||
<template> |
|||
<Spin :spinning="getLoading"> |
|||
<BasicForm @register="registerForm" :size="getFormSize" /> |
|||
</Spin> |
|||
</template> |
|||
|
|||
<script lang="ts" setup> |
|||
import type { FormSchema } from '@/components/Form'; |
|||
import { Spin } from 'ant-design-vue'; |
|||
|
|||
import { ref, watch } from 'vue'; |
|||
import { useForm, BasicForm } from '@/components/Form'; |
|||
import { propTypes } from '@/utils/propTypes'; |
|||
import { useI18n } from '@/hooks/web/useI18n'; |
|||
import { useSizeSetting } from '@/hooks/setting/UseSizeSetting'; |
|||
|
|||
import { getByIdApi } from '../SysDept.api'; |
|||
|
|||
const props = defineProps({ |
|||
filterField: propTypes.bool, |
|||
deptId: propTypes.number, |
|||
}); |
|||
|
|||
const { t } = useI18n(); |
|||
const { getFormSize } = useSizeSetting(); |
|||
|
|||
const getLoading = ref(false); |
|||
|
|||
watch( |
|||
() => props.deptId, |
|||
async (value) => { |
|||
if (!value) { |
|||
await resetFields(); |
|||
} else { |
|||
try { |
|||
getLoading.value = true; |
|||
const deptData = await getByIdApi(value); |
|||
if (deptData.parentDept) { |
|||
deptData.parentName = deptData.parentDept.deptName; |
|||
} else { |
|||
deptData.parentName = '根'; |
|||
} |
|||
await setFieldsValue(deptData); |
|||
} finally { |
|||
getLoading.value = false; |
|||
} |
|||
} |
|||
}, |
|||
); |
|||
|
|||
const formSchemas: Array<FormSchema & { filter?: boolean }> = [ |
|||
{ |
|||
label: '', |
|||
field: 'deptId', |
|||
component: 'Input', |
|||
show: false, |
|||
}, |
|||
{ |
|||
label: '', |
|||
field: 'parentId', |
|||
component: 'Input', |
|||
show: false, |
|||
}, |
|||
{ |
|||
label: t('system.views.dept.title.parent'), |
|||
field: 'parentName', |
|||
component: 'Input', |
|||
componentProps: { |
|||
disabled: true, |
|||
}, |
|||
// filter: true, |
|||
}, |
|||
{ |
|||
label: t('system.views.dept.title.deptCode'), |
|||
field: 'deptCode', |
|||
component: 'Input', |
|||
required: true, |
|||
}, |
|||
{ |
|||
label: t('system.views.dept.title.deptType'), |
|||
field: 'deptType', |
|||
component: 'SmartApiSelectDict', |
|||
required: true, |
|||
componentProps: { |
|||
dictCode: 'SYSTEM_ORGANIZATION_TYPE', |
|||
}, |
|||
}, |
|||
{ |
|||
label: t('system.views.dept.title.deptName'), |
|||
field: 'deptName', |
|||
required: true, |
|||
component: 'Input', |
|||
}, |
|||
{ |
|||
label: t('common.table.useYn'), |
|||
field: 'useYn', |
|||
component: 'Switch', |
|||
defaultValue: true, |
|||
}, |
|||
{ |
|||
label: t('system.views.dept.title.email'), |
|||
field: 'email', |
|||
component: 'Input', |
|||
}, |
|||
{ |
|||
label: t('system.views.dept.title.director'), |
|||
field: 'director', |
|||
component: 'Input', |
|||
}, |
|||
{ |
|||
label: t('system.views.dept.title.phone'), |
|||
field: 'phone', |
|||
component: 'Input', |
|||
}, |
|||
{ |
|||
label: t('common.table.seq'), |
|||
field: 'seq', |
|||
component: 'InputNumber', |
|||
defaultValue: 1, |
|||
required: true, |
|||
}, |
|||
{ |
|||
label: t('common.table.remark'), |
|||
field: 'remark', |
|||
component: 'InputTextArea', |
|||
}, |
|||
{ |
|||
label: t('common.table.createUser'), |
|||
field: 'createBy', |
|||
component: 'Input', |
|||
filter: true, |
|||
componentProps: { |
|||
disabled: true, |
|||
}, |
|||
}, |
|||
{ |
|||
label: t('common.table.createTime'), |
|||
field: 'createTime', |
|||
component: 'Input', |
|||
filter: true, |
|||
componentProps: { |
|||
disabled: true, |
|||
}, |
|||
}, |
|||
{ |
|||
label: t('common.table.updateUser'), |
|||
field: 'updateBy', |
|||
component: 'Input', |
|||
filter: true, |
|||
componentProps: { |
|||
disabled: true, |
|||
}, |
|||
}, |
|||
{ |
|||
label: t('common.table.updateTime'), |
|||
field: 'updateTime', |
|||
component: 'Input', |
|||
filter: true, |
|||
componentProps: { |
|||
disabled: true, |
|||
}, |
|||
}, |
|||
]; |
|||
|
|||
const getFormSchemas = () => { |
|||
if (!props.filterField) { |
|||
return formSchemas; |
|||
} |
|||
return formSchemas.filter((item) => item.filter === undefined || !item.filter); |
|||
}; |
|||
|
|||
const [registerForm, { setFieldsValue, validate, clearValidate, resetFields }] = useForm({ |
|||
colon: true, |
|||
schemas: getFormSchemas(), |
|||
labelCol: { |
|||
span: 6, |
|||
}, |
|||
wrapperCol: { |
|||
span: 17, |
|||
}, |
|||
baseColProps: { |
|||
span: 24, |
|||
}, |
|||
showActionButtonGroup: false, |
|||
}); |
|||
|
|||
defineExpose({ |
|||
setFieldsValue, |
|||
validate, |
|||
clearValidate, |
|||
resetFields, |
|||
}); |
|||
</script> |
|||
|
|||
<style scoped></style> |
|||
@ -0,0 +1,46 @@ |
|||
<template> |
|||
<BasicModal |
|||
@register="registerModal" |
|||
@ok="handleOk" |
|||
:okText="$t('common.button.save')" |
|||
:title="$t('common.button.add')" |
|||
> |
|||
<SysDeptEdit ref="formRef" filter-field /> |
|||
</BasicModal> |
|||
</template> |
|||
|
|||
<script lang="ts" setup> |
|||
import { useModalInner, BasicModal } from '@/components/Modal'; |
|||
import { ref, unref } from 'vue'; |
|||
import { successMessage } from '@/utils/message/SystemNotice'; |
|||
import { useI18n } from '@/hooks/web/useI18n'; |
|||
|
|||
import SysDeptEdit from './SysDeptEdit.vue'; |
|||
import { saveUpdateBatchApi } from '../SysDept.api'; |
|||
|
|||
const emit = defineEmits(['after-save', 'register']); |
|||
|
|||
const { t } = useI18n(); |
|||
|
|||
const formRef = ref(); |
|||
|
|||
const [registerModal, { changeOkLoading, closeModal }] = useModalInner(async (data) => { |
|||
await unref(formRef).resetFields({}); |
|||
await unref(formRef).setFieldsValue(data); |
|||
}); |
|||
|
|||
const handleOk = async () => { |
|||
const model = await unref(formRef).validate(); |
|||
try { |
|||
changeOkLoading(true); |
|||
await saveUpdateBatchApi([model]); |
|||
successMessage(t('common.message.saveSuccess')); |
|||
closeModal(); |
|||
emit('after-save'); |
|||
} finally { |
|||
changeOkLoading(false); |
|||
} |
|||
}; |
|||
</script> |
|||
|
|||
<style scoped></style> |
|||
@ -0,0 +1,233 @@ |
|||
<template> |
|||
<div> |
|||
<div v-if="showSearch" class="search-container"> |
|||
<a-input-search |
|||
v-model:value="searchValue" |
|||
:placeholder="$t('system.views.dept.search.deptName')" |
|||
/> |
|||
</div> |
|||
<Spin :spinning="loading"> |
|||
<a-tree |
|||
v-bind="getAttrs" |
|||
:expanded-keys="expandedKeys" |
|||
:auto-expand-parent="autoExpandParent" |
|||
@expand="onExpand" |
|||
:field-names="fieldNames" |
|||
:tree-data="computedTreeData" |
|||
> |
|||
<template #title="{ deptName }"> |
|||
<span v-if="!showSearch"> |
|||
{{ deptName }} |
|||
</span> |
|||
<span v-else-if="deptName.indexOf(searchValue) > -1"> |
|||
{{ deptName.substr(0, deptName.indexOf(searchValue)) }} |
|||
<span style="color: #f50">{{ searchValue }}</span> |
|||
{{ deptName.substr(deptName.indexOf(searchValue) + searchValue.length) }} |
|||
</span> |
|||
<span v-else>{{ deptName }}</span> |
|||
</template> |
|||
</a-tree> |
|||
</Spin> |
|||
</div> |
|||
</template> |
|||
|
|||
<script lang="ts"> |
|||
import { computed, defineComponent, onMounted, reactive, ref, toRefs, unref, watch } from 'vue'; |
|||
|
|||
import { Spin } from 'ant-design-vue'; |
|||
import { errorMessage } from '@/utils/message/SystemNotice'; |
|||
import TreeUtils from '@/utils/TreeUtils'; |
|||
import { propTypes } from '@/utils/propTypes'; |
|||
import { ApiServiceEnum, defHttp } from '@/utils/http/axios'; |
|||
|
|||
const getParentKey = (key: number, treeData: Array<any>): number => { |
|||
let parentKey; |
|||
for (let i = 0; i < treeData.length; i++) { |
|||
const node = treeData[i]; |
|||
if (node.children) { |
|||
if (node.children.some((item: any) => item.deptId === key)) { |
|||
parentKey = node.deptId; |
|||
} else { |
|||
const secondParentKey = getParentKey(key, node.children); |
|||
if (secondParentKey) { |
|||
parentKey = secondParentKey; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
return parentKey; |
|||
}; |
|||
|
|||
export default defineComponent({ |
|||
name: 'SysDeptTree', |
|||
components: { Spin }, |
|||
props: { |
|||
// 是否支持搜索 |
|||
showSearch: propTypes.bool.def(true), |
|||
// 是否异步加载 |
|||
async: propTypes.bool, |
|||
}, |
|||
setup(props, { attrs }) { |
|||
const { async: asyncRef } = toRefs(props); |
|||
const searchValue = ref<string>(''); |
|||
|
|||
const dataList = ref<Array<any>>([]); |
|||
const autoExpandParent = ref(false); |
|||
const expandedKeys = ref<Array<number>>([]); |
|||
const loading = ref(false); |
|||
|
|||
const getAttrs = computed(() => { |
|||
const result: any = { |
|||
...attrs, |
|||
}; |
|||
if (unref(asyncRef)) { |
|||
result.loadData = handleAsyncLoadData; |
|||
} |
|||
return result; |
|||
}); |
|||
|
|||
/** |
|||
* 树形数据计算属性 |
|||
*/ |
|||
const computedTreeData = computed(() => { |
|||
const async = unref(asyncRef); |
|||
if (async) { |
|||
return unref(dataList); |
|||
} |
|||
return ( |
|||
TreeUtils.convertList2Tree( |
|||
dataList.value, |
|||
(item) => item.deptId, |
|||
(item) => item.parentId, |
|||
0, |
|||
) || [] |
|||
); |
|||
}); |
|||
|
|||
const onExpand = (keys: Array<number>) => { |
|||
expandedKeys.value = keys; |
|||
autoExpandParent.value = false; |
|||
}; |
|||
|
|||
/** |
|||
* 所有数据 |
|||
*/ |
|||
const getAllDataList = computed(() => { |
|||
const result: any[] = []; |
|||
if (unref(asyncRef)) { |
|||
recursionAddChildren(unref(dataList), result); |
|||
} else { |
|||
result.push(...unref(dataList)); |
|||
} |
|||
return result; |
|||
}); |
|||
|
|||
const recursionAddChildren = (list: any[], allData: any[]) => { |
|||
list.forEach((item) => { |
|||
allData.push(item); |
|||
if (item.children && item.children.length > 0) { |
|||
recursionAddChildren(item.children, allData); |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
watch(searchValue, (value) => { |
|||
const allData = unref(getAllDataList); |
|||
expandedKeys.value = allData |
|||
.map(({ deptName, deptId }: any) => { |
|||
if (deptName.indexOf(value) > -1) { |
|||
return getParentKey(deptId, computedTreeData.value); |
|||
} |
|||
return null; |
|||
}) |
|||
.filter((item, i, self) => item && self.indexOf(item) === i) as Array<number>; |
|||
autoExpandParent.value = true; |
|||
}); |
|||
|
|||
const handleAsyncLoadData = async (treeNode) => { |
|||
const dataRef = treeNode.dataRef; |
|||
dataRef.children = await loadData(dataRef.deptId); |
|||
dataList.value = [...unref(dataList)]; |
|||
}; |
|||
|
|||
const reload = () => loadData(); |
|||
|
|||
/** |
|||
* 加载数据函数 |
|||
*/ |
|||
const loadData = async (parentId?: number | null) => { |
|||
const parameter: Recordable = { |
|||
sortName: 'seq', |
|||
sortOrder: 'asc', |
|||
}; |
|||
if (parentId !== undefined && parentId !== null) { |
|||
parameter.parameter = { |
|||
'parentId@=': parentId, |
|||
}; |
|||
} |
|||
try { |
|||
loading.value = true; |
|||
const result = (await defHttp.post({ |
|||
service: ApiServiceEnum.SMART_SYSTEM, |
|||
url: 'sys/dept/list', |
|||
data: parameter, |
|||
})) as any[]; |
|||
|
|||
result.forEach((item) => { |
|||
if (item.hasChild !== true) { |
|||
item.isLeaf = true; |
|||
} |
|||
}); |
|||
if (unref(asyncRef)) { |
|||
if (parentId === 0) { |
|||
dataList.value = result; |
|||
} else { |
|||
return result; |
|||
} |
|||
} else { |
|||
dataList.value = result; |
|||
} |
|||
} catch (e) { |
|||
errorMessage(e); |
|||
} finally { |
|||
loading.value = false; |
|||
} |
|||
}; |
|||
|
|||
/** |
|||
* 加载数据 |
|||
*/ |
|||
onMounted(() => { |
|||
let parentId: number | undefined = undefined; |
|||
if (unref(asyncRef)) { |
|||
parentId = 0; |
|||
} |
|||
loadData(parentId); |
|||
}); |
|||
|
|||
return { |
|||
computedTreeData, |
|||
autoExpandParent, |
|||
onExpand, |
|||
loadData, |
|||
loading, |
|||
expandedKeys, |
|||
fieldNames: reactive({ |
|||
children: 'children', |
|||
title: 'deptName', |
|||
key: 'deptId', |
|||
}), |
|||
getAttrs, |
|||
handleAsyncLoadData, |
|||
searchValue, |
|||
reload, |
|||
}; |
|||
}, |
|||
}); |
|||
</script> |
|||
|
|||
<style scoped lang="less"> |
|||
.search-container { |
|||
margin-bottom: 10px; |
|||
} |
|||
</style> |
|||
@ -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', |
|||
}, |
|||
}, |
|||
}; |
|||
@ -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: '请选择上级组织', |
|||
}, |
|||
}, |
|||
}; |
|||
@ -0,0 +1,59 @@ |
|||
<template> |
|||
<div class="full-height page-container"> |
|||
<a-layout class="full-height i18n-main-container"> |
|||
<a-layout-sider theme="light" width="300px" class="full-height i18n-group-container"> |
|||
<!-- 国际化分组 --> |
|||
<I18nGroupList @current-change="({ row }) => (groupId = row.groupId)" /> |
|||
</a-layout-sider> |
|||
<a-layout> |
|||
<a-layout-content class="i18n-container" style=" margin-bottom: 2px;background: #f0f2f5"> |
|||
<I18nList :group-id="groupId" @change="(id) => (i18nId = id)" /> |
|||
</a-layout-content> |
|||
<a-layout-content class="i18n-item-container"> |
|||
<I18nItemList :i18n-id="i18nId" /> |
|||
</a-layout-content> |
|||
</a-layout> |
|||
</a-layout> |
|||
</div> |
|||
</template> |
|||
|
|||
<script lang="ts"> |
|||
import { defineComponent, ref } from 'vue'; |
|||
|
|||
import I18nGroupList from './components/I18nGroupList.vue'; |
|||
import I18nList from './components/i18nList.vue'; |
|||
import I18nItemList from './components/I18nItemList.vue'; |
|||
|
|||
export default defineComponent({ |
|||
name: 'I18nMainView', |
|||
components: { |
|||
I18nGroupList, |
|||
I18nList, |
|||
I18nItemList, |
|||
}, |
|||
setup() { |
|||
const groupId = ref<number>(); |
|||
const i18nId = ref<number>(); |
|||
return { |
|||
groupId, |
|||
i18nId, |
|||
}; |
|||
}, |
|||
}); |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
.i18n-main-container { |
|||
.i18n-group-container { |
|||
margin-right: 5px; |
|||
} |
|||
|
|||
.i18n-container { |
|||
height: 60%; |
|||
} |
|||
|
|||
.i18n-item-container { |
|||
height: 40%; |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,136 @@ |
|||
<template> |
|||
<div class="full-height" :class="prefixCls"> |
|||
<div class="table-container"> |
|||
<SmartTable @register="registerTable" v-bind="$attrs"> |
|||
<template #table-groupName="{ row }"> |
|||
<span @contextmenu="(e) => handleContext(e, row)">{{ row.groupName }}</span> |
|||
</template> |
|||
</SmartTable> |
|||
</div> |
|||
<div class="button-container"> |
|||
<a-button |
|||
v-permission="SystemPermissions.i18n.add" |
|||
class="button" |
|||
block |
|||
type="primary" |
|||
@click="() => showAddModal()" |
|||
> |
|||
{{ $t('common.button.add') }} |
|||
</a-button> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { SmartTable, useSmartTable } from '@/components/SmartTable'; |
|||
import { useDesign } from '@/hooks/web/useDesign'; |
|||
import { useContextMenu } from '@/hooks/web/useContextMenu'; |
|||
import { useI18n } from '@/hooks/web/useI18n'; |
|||
import { listGroupApi, getGroupByIdApi, saveUpdateGroupApi, deleteGroupApi } from './i18n.api'; |
|||
import { SystemPermissions } from '@/modules/system/constants/SystemConstants'; |
|||
|
|||
const { t } = useI18n(); |
|||
|
|||
const { prefixCls } = useDesign('smart-system-i18nGroup'); |
|||
|
|||
const [createContextMenu] = useContextMenu(); |
|||
const handleContext = (e: MouseEvent, row: Recordable) => { |
|||
createContextMenu({ |
|||
event: e, |
|||
items: [ |
|||
{ |
|||
label: t('common.button.edit'), |
|||
icon: 'ant-design:edit-outlined', |
|||
handler: () => { |
|||
editByRowModal(row); |
|||
}, |
|||
}, |
|||
{ |
|||
label: t('common.button.delete'), |
|||
icon: 'ant-design:delete-outlined', |
|||
handler: () => { |
|||
deleteByRow(row); |
|||
}, |
|||
}, |
|||
], |
|||
}); |
|||
}; |
|||
|
|||
const [registerTable, { editByRowModal, deleteByRow, showAddModal }] = useSmartTable({ |
|||
size: 'small', |
|||
stripe: true, |
|||
rowConfig: { isCurrent: true }, |
|||
height: 'auto', |
|||
cellClassName: 'cursor-pointer', |
|||
columns: [ |
|||
{ |
|||
title: '{system.views.i18n.group.groupName}', |
|||
field: 'groupName', |
|||
slots: { |
|||
default: 'table-groupName', |
|||
}, |
|||
}, |
|||
], |
|||
addEditConfig: { |
|||
formConfig: { |
|||
colon: true, |
|||
schemas: [ |
|||
{ |
|||
label: '', |
|||
component: 'Input', |
|||
field: 'groupId', |
|||
show: false, |
|||
}, |
|||
{ |
|||
label: t('system.views.i18n.group.groupName'), |
|||
component: 'Input', |
|||
field: 'groupName', |
|||
required: true, |
|||
}, |
|||
{ |
|||
label: t('system.views.i18n.group.seq'), |
|||
component: 'InputNumber', |
|||
field: 'seq', |
|||
defaultValue: 1, |
|||
required: true, |
|||
}, |
|||
], |
|||
}, |
|||
}, |
|||
proxyConfig: { |
|||
ajax: { |
|||
query: listGroupApi, |
|||
getById: (row) => getGroupByIdApi(row.groupId), |
|||
save: ({ body: { insertRecords, updateRecords } }) => |
|||
saveUpdateGroupApi([...insertRecords, ...updateRecords][0]), |
|||
delete: ({ body: { removeRecords } }) => { |
|||
return deleteGroupApi(removeRecords.map((item) => item.groupId)); |
|||
}, |
|||
}, |
|||
}, |
|||
}); |
|||
</script> |
|||
|
|||
<style lang="less"> |
|||
@buttonContainerHeight: 60px; |
|||
@prefix-cls: ~'@{namespace}-smart-system-i18nGroup'; |
|||
.@{prefix-cls} { |
|||
.smart-table-container { |
|||
padding: 0; |
|||
} |
|||
|
|||
.table-container { |
|||
height: calc(100% - @buttonContainerHeight); |
|||
} |
|||
|
|||
.button-container { |
|||
height: @buttonContainerHeight; |
|||
line-height: @buttonContainerHeight; |
|||
text-align: center; |
|||
|
|||
.button { |
|||
width: 90%; |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,112 @@ |
|||
<template> |
|||
<div class="full-height"> |
|||
<SmartTable @register="registerTable" :size="getTableSize"> |
|||
<template #table-operation="{ row }"> |
|||
<SmartVxeTableAction :actions="getTableActions(row)" /> |
|||
</template> |
|||
</SmartTable> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { |
|||
useSmartTable, |
|||
SmartTable, |
|||
SmartVxeTableAction, |
|||
type ActionItem, |
|||
} from '@/components/SmartTable'; |
|||
import { useSizeSetting } from '@/hooks/setting/UseSizeSetting'; |
|||
|
|||
import { getI18nItemListTableColumn, getI18nItemListAddEditFormSchema } from './i18n.config'; |
|||
import { |
|||
listI18nItemApi, |
|||
batchDeleteI18nItemByIdApi, |
|||
saveUpdateI18nItemApi, |
|||
getI18nItemByIdApi, |
|||
} from './i18n.api'; |
|||
import { propTypes } from '@/utils/propTypes'; |
|||
import { SystemPermissions } from '@/modules/system/constants/SystemConstants'; |
|||
import { useI18n } from '@/hooks/web/useI18n'; |
|||
import { watch } from 'vue'; |
|||
|
|||
const props = defineProps({ |
|||
i18nId: propTypes.number, |
|||
}); |
|||
|
|||
const { t } = useI18n(); |
|||
const { getTableSize } = useSizeSetting(); |
|||
|
|||
const getTableActions = (row): ActionItem[] => { |
|||
return [ |
|||
{ |
|||
auth: SystemPermissions.i18n.update, |
|||
label: t('common.button.edit'), |
|||
onClick: () => { |
|||
editByRowModal(row); |
|||
}, |
|||
}, |
|||
]; |
|||
}; |
|||
|
|||
watch( |
|||
() => props.i18nId, |
|||
() => query(), |
|||
); |
|||
|
|||
const [registerTable, { editByRowModal, query }] = useSmartTable({ |
|||
id: 'smart-system-i18n-i18nItemList', |
|||
height: 'auto', |
|||
stripe: true, |
|||
border: true, |
|||
columns: getI18nItemListTableColumn(), |
|||
columnConfig: { |
|||
resizable: true, |
|||
}, |
|||
rowConfig: { |
|||
isCurrent: true, |
|||
}, |
|||
customConfig: { storage: true }, |
|||
addEditConfig: { |
|||
formConfig: { |
|||
schemas: getI18nItemListAddEditFormSchema(t), |
|||
}, |
|||
}, |
|||
proxyConfig: { |
|||
ajax: { |
|||
query: async ({ ajaxParameter }) => { |
|||
if (!props.i18nId) { |
|||
return []; |
|||
} |
|||
return listI18nItemApi({ |
|||
...ajaxParameter, |
|||
parameter: { |
|||
...ajaxParameter?.parameter, |
|||
'i18nId@=': props.i18nId, |
|||
}, |
|||
}); |
|||
}, |
|||
delete: ({ body: { removeRecords } }) => |
|||
batchDeleteI18nItemByIdApi(removeRecords.map((item) => item.i18nItemId)), |
|||
getById: ({ i18nItemId }) => getI18nItemByIdApi(i18nItemId), |
|||
save: ({ body: { insertRecords, updateRecords } }) => { |
|||
const model = [...insertRecords, ...updateRecords][0]; |
|||
return saveUpdateI18nItemApi({ |
|||
...model, |
|||
i18nId: props.i18nId, |
|||
}); |
|||
}, |
|||
}, |
|||
}, |
|||
toolbarConfig: { |
|||
refresh: true, |
|||
resizable: true, |
|||
zoom: true, |
|||
column: { |
|||
columnOrder: true, |
|||
}, |
|||
buttons: [{ code: 'ModalAdd' }, { code: 'delete' }], |
|||
}, |
|||
}); |
|||
</script> |
|||
|
|||
<style scoped lang="less"></style> |
|||
@ -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, |
|||
}); |
|||
}; |
|||
@ -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, |
|||
}, |
|||
]; |
|||
}; |
|||
@ -0,0 +1,176 @@ |
|||
<template> |
|||
<div class="full-height"> |
|||
<SmartTable |
|||
@register="registerTable" |
|||
:size="getTableSize" |
|||
@current-change="handleCurrentChange" |
|||
> |
|||
<template #table-operation="{ row }"> |
|||
<SmartVxeTableAction :actions="getTableActions(row)" /> |
|||
</template> |
|||
</SmartTable> |
|||
</div> |
|||
</template> |
|||
|
|||
<script lang="ts" setup> |
|||
import { useSmartTable, SmartTable, SmartVxeTableAction } from '@/components/SmartTable'; |
|||
import { propTypes } from '@/utils/propTypes'; |
|||
import { useI18n } from '@/hooks/web/useI18n'; |
|||
import { useSizeSetting } from '@/hooks/setting/UseSizeSetting'; |
|||
import { message, Modal } from 'ant-design-vue'; |
|||
import { createVNode, watch } from 'vue'; |
|||
import { ExclamationCircleOutlined } from '@ant-design/icons-vue'; |
|||
import { ApiServiceEnum, defHttp } from '@/utils/http/axios'; |
|||
|
|||
import { getI18nTableColumns, getI18nAddEditSchemas } from './i18n.config'; |
|||
import { listI18nApi, getI18nByIdApi, i18nSaveUpdateApi, i18nDeleteApi } from './i18n.api'; |
|||
import { SystemPermissions } from '@/modules/system/constants/SystemConstants'; |
|||
|
|||
const props = defineProps({ |
|||
groupId: propTypes.number, |
|||
}); |
|||
const emit = defineEmits(['change']); |
|||
|
|||
const { t } = useI18n(); |
|||
const { getTableSize } = useSizeSetting(); |
|||
|
|||
watch( |
|||
() => props.groupId, |
|||
async () => { |
|||
await query(); |
|||
emit('change', null); |
|||
}, |
|||
); |
|||
|
|||
const permissions = SystemPermissions.i18n; |
|||
|
|||
const getTableActions = (row: any) => { |
|||
return [ |
|||
{ |
|||
label: t('common.button.edit'), |
|||
preIcon: 'ant-design:edit-out-lined', |
|||
auth: permissions.update, |
|||
onClick: () => editByRowModal(row), |
|||
}, |
|||
]; |
|||
}; |
|||
|
|||
const handleCurrentChange = ({ row }) => { |
|||
emit('change', row.i18nId); |
|||
}; |
|||
|
|||
const [registerTable, { query, editByRowModal }] = useSmartTable({ |
|||
id: 'smart-system-i18n-i18nList', |
|||
height: 'auto', |
|||
stripe: true, |
|||
columns: getI18nTableColumns(), |
|||
useSearchForm: true, |
|||
pagerConfig: true, |
|||
border: true, |
|||
showOverflow: 'tooltip', |
|||
rowConfig: { |
|||
isCurrent: true, |
|||
}, |
|||
customConfig: { storage: true }, |
|||
columnConfig: { |
|||
resizable: true, |
|||
}, |
|||
sortConfig: { |
|||
remote: true, |
|||
defaultSort: { field: 'seq', order: 'asc' }, |
|||
}, |
|||
searchFormConfig: { |
|||
searchWithSymbol: true, |
|||
schemas: [ |
|||
{ |
|||
label: t('system.views.i18n.i18n.titleI18nCode'), |
|||
field: 'i18nCode', |
|||
component: 'Input', |
|||
searchSymbol: 'like', |
|||
}, |
|||
], |
|||
compact: true, |
|||
colon: true, |
|||
layout: 'inline', |
|||
actionColOptions: { span: undefined }, |
|||
}, |
|||
addEditConfig: { |
|||
formConfig: { |
|||
baseColProps: { |
|||
span: 24, |
|||
}, |
|||
schemas: getI18nAddEditSchemas(t), |
|||
labelCol: { span: 5 }, |
|||
wrapperCol: { span: 17 }, |
|||
}, |
|||
}, |
|||
toolbarConfig: { |
|||
refresh: true, |
|||
resizable: true, |
|||
zoom: true, |
|||
column: { |
|||
columnOrder: true, |
|||
}, |
|||
buttons: [ |
|||
{ |
|||
name: t('system.views.i18n.i18n.button.reload'), |
|||
customRender: 'ant', |
|||
auth: permissions.reload, |
|||
props: { |
|||
preIcon: 'ant-design:reload-outlined', |
|||
type: 'primary', |
|||
onClick: () => handleReload(), |
|||
size: 'small', |
|||
}, |
|||
}, |
|||
{ code: 'ModalAdd' }, |
|||
{ code: 'ModalEdit' }, |
|||
{ code: 'delete' }, |
|||
], |
|||
}, |
|||
proxyConfig: { |
|||
ajax: { |
|||
query: ({ ajaxParameter }) => { |
|||
const parameter = { |
|||
...ajaxParameter, |
|||
parameter: { |
|||
...ajaxParameter?.parameter, |
|||
'groupId@=': props.groupId, |
|||
}, |
|||
}; |
|||
return listI18nApi(parameter); |
|||
}, |
|||
getById: (model) => getI18nByIdApi(model.i18nId), |
|||
save: ({ body: { insertRecords, updateRecords } }) => { |
|||
if (insertRecords?.length > 0) { |
|||
insertRecords.forEach((item) => { |
|||
item.groupId = props.groupId; |
|||
}); |
|||
} |
|||
return i18nSaveUpdateApi([...insertRecords, ...updateRecords][0]); |
|||
}, |
|||
delete: ({ body: { removeRecords } }) => i18nDeleteApi(removeRecords), |
|||
}, |
|||
}, |
|||
}); |
|||
|
|||
/** |
|||
* 刷新国际化信息 |
|||
*/ |
|||
const handleReload = async () => { |
|||
Modal.confirm({ |
|||
title: t('system.views.i18n.i18n.message.reloadConfirm'), |
|||
content: t('system.views.i18n.i18n.message.reloadContent'), |
|||
icon: createVNode(ExclamationCircleOutlined), |
|||
onOk: async () => { |
|||
await defHttp.post({ |
|||
service: ApiServiceEnum.SMART_SYSTEM, |
|||
url: 'sys/i18n/reload', |
|||
}); |
|||
message.success(t('system.views.i18n.i18n.message.reloadSuccess')); |
|||
}, |
|||
}); |
|||
}; |
|||
</script> |
|||
|
|||
<style lang="less" scoped></style> |
|||
@ -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', |
|||
}, |
|||
}, |
|||
}, |
|||
}, |
|||
}; |
|||
@ -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: '请先选择国际化信息,再执行添加操作', |
|||
}, |
|||
}, |
|||
}, |
|||
}, |
|||
}; |
|||
@ -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, |
|||
}, |
|||
}); |
|||
}; |
|||
@ -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', |
|||
}, |
|||
]; |
|||
}; |
|||
@ -0,0 +1,134 @@ |
|||
<template> |
|||
<div class="full-height page-container"> |
|||
<SmartTable @register="registerTable" :size="getTableSize"> |
|||
<template #table-operation="{ row }"> |
|||
<SmartVxeTableAction :actions="getTableActions(row)" /> |
|||
</template> |
|||
</SmartTable> |
|||
<SmartUserSelectModal |
|||
@register="registerSetUserModal" |
|||
width="1500px" |
|||
showSelect |
|||
@selected="handleUserSelected" |
|||
:select-values="selectUserList" |
|||
:title="$t('system.views.userGroup.button.setUser')" |
|||
/> |
|||
</div> |
|||
</template> |
|||
|
|||
<script lang="ts" setup> |
|||
import { |
|||
useSmartTable, |
|||
SmartTable, |
|||
SmartVxeTableAction, |
|||
ActionItem, |
|||
} from '@/components/SmartTable'; |
|||
import { useSizeSetting } from '@/hooks/setting/UseSizeSetting'; |
|||
import { useI18n } from '@/hooks/web/useI18n'; |
|||
import { SmartUserSelectModal } from '@/components/Form'; |
|||
|
|||
import { |
|||
getAddEditFormSchemas, |
|||
getSearchSchemas, |
|||
getTableColumns, |
|||
} from './UserGroupListView.config'; |
|||
import { listApi, deleteApi, batchSaveUpdateApi, getByIdApi } from './UserGroupListView.api'; |
|||
import { SystemPermissions } from '@/modules/system/constants/SystemConstants'; |
|||
import { useSetUser } from './hooks/useSetUser'; |
|||
|
|||
const { getTableSize } = useSizeSetting(); |
|||
const { t } = useI18n(); |
|||
const permissions = SystemPermissions.userGroup; |
|||
const { registerSetUserModal, handleShowSetUser, handleUserSelected, selectUserList } = |
|||
useSetUser(t); |
|||
|
|||
const getTableActions = (row: any): ActionItem[] => { |
|||
return [ |
|||
{ |
|||
label: t('common.button.edit'), |
|||
preIcon: 'ant-design:edit-out-lined', |
|||
auth: permissions.update, |
|||
onClick: () => editByRowModal(row), |
|||
}, |
|||
{ |
|||
label: t('system.views.userGroup.button.setUser'), |
|||
preIcon: 'ant-design:user-add-outlined', |
|||
auth: permissions.setUser, |
|||
onClick: () => { |
|||
handleShowSetUser(row); |
|||
}, |
|||
}, |
|||
]; |
|||
}; |
|||
|
|||
const [registerTable, { editByRowModal }] = useSmartTable({ |
|||
columns: getTableColumns(), |
|||
height: 'auto', |
|||
stripe: true, |
|||
highlightHoverRow: true, |
|||
pagerConfig: true, |
|||
useSearchForm: true, |
|||
border: true, |
|||
sortConfig: { |
|||
remote: true, |
|||
defaultSort: { |
|||
field: 'seq', |
|||
order: 'asc', |
|||
}, |
|||
}, |
|||
searchFormConfig: { |
|||
layout: 'inline', |
|||
schemas: getSearchSchemas(t), |
|||
autoSubmitOnEnter: true, |
|||
colon: true, |
|||
searchWithSymbol: true, |
|||
actionColOptions: { |
|||
span: undefined, |
|||
}, |
|||
}, |
|||
addEditConfig: { |
|||
formConfig: { |
|||
colon: true, |
|||
schemas: getAddEditFormSchemas(t), |
|||
labelCol: { |
|||
span: 5, |
|||
}, |
|||
wrapperCol: { |
|||
span: 18, |
|||
}, |
|||
baseColProps: { |
|||
span: 24, |
|||
}, |
|||
}, |
|||
}, |
|||
proxyConfig: { |
|||
ajax: { |
|||
query: ({ ajaxParameter }) => listApi(ajaxParameter), |
|||
delete: ({ body: { removeRecords } }) => deleteApi(removeRecords), |
|||
getById: (data) => getByIdApi(data), |
|||
save: ({ body: { insertRecords, updateRecords } }) => |
|||
batchSaveUpdateApi([...insertRecords, ...updateRecords]), |
|||
}, |
|||
}, |
|||
columnConfig: { |
|||
resizable: true, |
|||
}, |
|||
toolbarConfig: { |
|||
refresh: true, |
|||
custom: true, |
|||
zoom: true, |
|||
buttons: [ |
|||
{ |
|||
code: 'ModalAdd', |
|||
auth: permissions.add, |
|||
}, |
|||
{ |
|||
code: 'delete', |
|||
auth: permissions.delete, |
|||
}, |
|||
], |
|||
}, |
|||
}); |
|||
</script> |
|||
|
|||
<style scoped></style> |
|||
@ -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<any>(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<any>(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<Array<any>>([]); |
|||
// 选中的key
|
|||
const targetKeys = ref<Array<string>>([]); |
|||
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<any> = 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<number> = 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<string>) => { |
|||
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, |
|||
}; |
|||
}; |
|||
@ -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<Recordable | null>(null); |
|||
const selectUserList = ref<number[]>([]); |
|||
|
|||
/** |
|||
* 显示设置用户弹窗 |
|||
* @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, |
|||
}; |
|||
}; |
|||
@ -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', |
|||
}, |
|||
}, |
|||
}, |
|||
}, |
|||
}; |
|||
@ -0,0 +1,24 @@ |
|||
export default { |
|||
system: { |
|||
views: { |
|||
userGroup: { |
|||
title: '用户组', |
|||
table: { |
|||
groupCode: '用户组编码', |
|||
groupName: '用户组名称', |
|||
remark: '备注', |
|||
}, |
|||
button: { |
|||
setUser: '设置用户', |
|||
}, |
|||
search: { |
|||
useYnTitle: '启用标志', |
|||
}, |
|||
validate: { |
|||
groupCode: '请输入用户组编码', |
|||
groupName: '请输入用户组名称', |
|||
}, |
|||
}, |
|||
}, |
|||
}, |
|||
}; |
|||
Loading…
Reference in new issue