| | |
| | | <ElButton v-auth="'add'" @click="showDialog('add')" v-ripple>新增角色</ElButton> |
| | | <ElButton |
| | | v-auth="'delete'" |
| | | type="danger" |
| | | :disabled="selectedRows.length === 0" |
| | | @click="handleBatchDelete" |
| | | v-ripple |
| | |
| | | fetchUpdateRole |
| | | } from '@/api/system-manage' |
| | | import { useTable } from '@/hooks/core/useTable' |
| | | import { useCrudPage } from '@/views/system/common/useCrudPage' |
| | | import { usePrintExportPage } from '@/views/system/common/usePrintExportPage' |
| | | import ListExportPrint from '@/components/biz/list-export-print/index.vue' |
| | | import RoleSearch from './modules/role-search.vue' |
| | | import RoleEditDialog from './modules/role-edit-dialog.vue' |
| | | import RolePermissionDialog from './modules/role-permission-dialog.vue' |
| | | import ArtButtonMore from '@/components/core/forms/art-button-more/index.vue' |
| | | import { createRoleTableColumns } from './roleTable.columns' |
| | | import { defaultResponseAdapter } from '@/utils/table/tableUtils' |
| | | import { ElMessage, ElMessageBox, ElTag } from 'element-plus' |
| | | import { |
| | | buildRoleDialogModel, |
| | | buildRolePageQueryParams, |
| | |
| | | buildRoleSavePayload, |
| | | buildRoleSearchParams, |
| | | createRoleSearchState, |
| | | getRoleStatusMeta, |
| | | getRolePaginationKey, |
| | | normalizeRoleListRow, |
| | | ROLE_REPORT_STYLE, |
| | | ROLE_REPORT_TITLE, |
| | |
| | | |
| | | const searchForm = ref(createRoleSearchState()) |
| | | const showSearchBar = ref(false) |
| | | const dialogVisible = ref(false) |
| | | const dialogType = ref('add') |
| | | const currentRoleData = ref(buildRoleDialogModel()) |
| | | const permissionDialogVisible = ref(false) |
| | | const permissionScopeType = ref('menu') |
| | | const selectedRows = ref([]) |
| | | const previewVisible = ref(false) |
| | | const previewRows = ref([]) |
| | | const previewMeta = ref({}) |
| | | const previewToken = ref(0) |
| | | const activePrintToken = ref(0) |
| | | const userStore = useUserStore() |
| | | const reportTitle = ROLE_REPORT_TITLE |
| | | const reportQueryParams = computed(() => buildRoleSearchParams(searchForm.value)) |
| | | |
| | | const { |
| | | columns, |
| | | columnChecks, |
| | | data, |
| | | loading, |
| | | pagination, |
| | | getData, |
| | | replaceSearchParams, |
| | | resetSearchParams, |
| | | handleSizeChange, |
| | | handleCurrentChange, |
| | | refreshData, |
| | | refreshCreate, |
| | | refreshUpdate, |
| | | refreshRemove |
| | | } = useTable({ |
| | | core: { |
| | | apiFn: fetchRolePage, |
| | | apiParams: buildRolePageQueryParams(searchForm.value), |
| | | columnsFactory: () => [ |
| | | { type: 'selection', width: 52, fixed: 'left' }, |
| | | { |
| | | prop: 'name', |
| | | label: '角色名称', |
| | | minWidth: 140, |
| | | showOverflowTooltip: true |
| | | }, |
| | | { |
| | | prop: 'code', |
| | | label: '角色编码', |
| | | minWidth: 140, |
| | | showOverflowTooltip: true |
| | | }, |
| | | { |
| | | prop: 'memo', |
| | | label: '备注', |
| | | minWidth: 180, |
| | | showOverflowTooltip: true |
| | | }, |
| | | { |
| | | prop: 'status', |
| | | label: '状态', |
| | | width: 120, |
| | | formatter: (row) => { |
| | | const statusMeta = getRoleStatusMeta(row.statusBool ?? row.status) |
| | | return h(ElTag, { type: statusMeta.type, effect: 'light' }, () => statusMeta.text) |
| | | } |
| | | }, |
| | | { |
| | | prop: 'updateTimeText', |
| | | label: '更新时间', |
| | | minWidth: 180, |
| | | sortable: true, |
| | | formatter: (row) => row.updateTimeText || '-' |
| | | }, |
| | | { |
| | | prop: 'createTimeText', |
| | | label: '创建时间', |
| | | minWidth: 180, |
| | | sortable: true, |
| | | formatter: (row) => row.createTimeText || '-' |
| | | }, |
| | | { |
| | | prop: 'operation', |
| | | label: '操作', |
| | | width: 120, |
| | | fixed: 'right', |
| | | formatter: (row) => |
| | | h('div', [ |
| | | h(ArtButtonMore, { |
| | | list: [ |
| | | { |
| | | key: 'scope-menu', |
| | | label: '网页权限', |
| | | icon: 'ri:layout-2-line', |
| | | auth: 'edit' |
| | | }, |
| | | { |
| | | key: 'scope-pda', |
| | | label: 'PDA权限', |
| | | icon: 'ri:smartphone-line', |
| | | auth: 'edit' |
| | | }, |
| | | { |
| | | key: 'scope-matnr', |
| | | label: '物料权限', |
| | | icon: 'ri:archive-line', |
| | | auth: 'edit' |
| | | }, |
| | | { |
| | | key: 'scope-warehouse', |
| | | label: '仓库权限', |
| | | icon: 'ri:store-2-line', |
| | | auth: 'edit' |
| | | }, |
| | | { |
| | | key: 'edit', |
| | | label: '编辑角色', |
| | | icon: 'ri:edit-2-line', |
| | | auth: 'edit' |
| | | }, |
| | | { |
| | | key: 'delete', |
| | | label: '删除角色', |
| | | icon: 'ri:delete-bin-4-line', |
| | | color: '#f56c6c', |
| | | auth: 'delete' |
| | | } |
| | | ], |
| | | onClick: (item) => handleActionClick(item, row) |
| | | }) |
| | | ]) |
| | | } |
| | | ] |
| | | }, |
| | | transform: { |
| | | dataTransformer: (records) => { |
| | | if (!Array.isArray(records)) { |
| | | return [] |
| | | } |
| | | return records.map((item) => normalizeRoleListRow(item)) |
| | | } |
| | | } |
| | | }) |
| | | |
| | | const roleReportColumns = computed(() => resolveRoleReportColumns(columns.value)) |
| | | const resolvedPreviewMeta = computed(() => |
| | | buildRoleReportMeta({ |
| | | previewMeta: previewMeta.value, |
| | | count: previewRows.value.length, |
| | | titleAlign: ROLE_REPORT_STYLE.titleAlign, |
| | | titleLevel: ROLE_REPORT_STYLE.titleLevel |
| | | }) |
| | | ) |
| | | |
| | | const handleSearch = (params) => { |
| | | replaceSearchParams(buildRoleSearchParams(params)) |
| | | getData() |
| | | function openScopeDialog(scopeType, row) { |
| | | permissionScopeType.value = scopeType |
| | | currentRoleData.value = buildRoleDialogModel(row) |
| | | permissionDialogVisible.value = true |
| | | } |
| | | |
| | | const handleReset = () => { |
| | | Object.assign(searchForm.value, createRoleSearchState()) |
| | | resetSearchParams() |
| | | } |
| | | |
| | | const handleSelectionChange = (rows) => { |
| | | selectedRows.value = Array.isArray(rows) ? rows : [] |
| | | } |
| | | |
| | | const handlePreviewVisibleChange = (visible) => { |
| | | previewVisible.value = Boolean(visible) |
| | | if (!visible) { |
| | | activePrintToken.value = 0 |
| | | } |
| | | } |
| | | |
| | | const showDialog = (type, row) => { |
| | | dialogType.value = type |
| | | currentRoleData.value = type === 'edit' ? buildRoleDialogModel(row) : buildRoleDialogModel() |
| | | dialogVisible.value = true |
| | | } |
| | | |
| | | const handleActionClick = (item, row) => { |
| | | function handleActionClick(item, row) { |
| | | switch (item.key) { |
| | | case 'scope-menu': |
| | | openScopeDialog('menu', row) |
| | |
| | | } |
| | | } |
| | | |
| | | const openScopeDialog = (scopeType, row) => { |
| | | permissionScopeType.value = scopeType |
| | | currentRoleData.value = buildRoleDialogModel(row) |
| | | permissionDialogVisible.value = true |
| | | } |
| | | |
| | | const handleDialogSubmit = async (formData) => { |
| | | const payload = buildRoleSavePayload(formData) |
| | | try { |
| | | if (dialogType.value === 'edit') { |
| | | await fetchUpdateRole(payload) |
| | | ElMessage.success('修改成功') |
| | | dialogVisible.value = false |
| | | currentRoleData.value = buildRoleDialogModel() |
| | | await refreshUpdate() |
| | | return |
| | | const { |
| | | columns, |
| | | columnChecks, |
| | | data, |
| | | loading, |
| | | pagination, |
| | | getData, |
| | | replaceSearchParams, |
| | | resetSearchParams, |
| | | handleSizeChange, |
| | | handleCurrentChange, |
| | | refreshData, |
| | | refreshCreate, |
| | | refreshUpdate, |
| | | refreshRemove |
| | | } = useTable({ |
| | | core: { |
| | | apiFn: fetchRolePage, |
| | | apiParams: buildRolePageQueryParams(searchForm.value), |
| | | paginationKey: getRolePaginationKey(), |
| | | columnsFactory: () => createRoleTableColumns(handleActionClick) |
| | | }, |
| | | transform: { |
| | | dataTransformer: (records) => { |
| | | if (!Array.isArray(records)) { |
| | | return [] |
| | | } |
| | | return records.map((item) => normalizeRoleListRow(item)) |
| | | } |
| | | await fetchSaveRole(payload) |
| | | ElMessage.success('新增成功') |
| | | dialogVisible.value = false |
| | | currentRoleData.value = buildRoleDialogModel() |
| | | await refreshCreate() |
| | | } catch (error) { |
| | | ElMessage.error(error?.message || '提交失败') |
| | | } |
| | | }) |
| | | |
| | | const { |
| | | dialogVisible, |
| | | dialogType, |
| | | currentRecord: currentRoleData, |
| | | selectedRows, |
| | | handleSelectionChange, |
| | | showDialog, |
| | | handleDialogSubmit, |
| | | handleDelete, |
| | | handleBatchDelete |
| | | } = useCrudPage({ |
| | | createEmptyModel: () => buildRoleDialogModel(), |
| | | buildEditModel: (record) => buildRoleDialogModel(record), |
| | | buildSavePayload: (formData) => buildRoleSavePayload(formData), |
| | | saveRequest: fetchSaveRole, |
| | | updateRequest: fetchUpdateRole, |
| | | deleteRequest: fetchDeleteRole, |
| | | entityName: '角色', |
| | | resolveRecordLabel: (record) => record?.name || record?.code || record?.id, |
| | | refreshCreate, |
| | | refreshUpdate, |
| | | refreshRemove |
| | | }) |
| | | |
| | | const buildPreviewDialogMeta = (rows) => { |
| | | const now = new Date() |
| | | return { |
| | | reportTitle, |
| | | reportDate: now.toLocaleDateString('zh-CN'), |
| | | printedAt: now.toLocaleString('zh-CN', { hour12: false }), |
| | | operator: userStore.getUserInfo?.name || userStore.getUserInfo?.username || '', |
| | | count: rows.length |
| | | } |
| | | } |
| | | |
| | | const handleDelete = async (row) => { |
| | | try { |
| | | await ElMessageBox.confirm(`确定要删除角色「${row.name || row.code || row.id}」吗?`, '删除确认', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | }) |
| | | await fetchDeleteRole(row.id) |
| | | ElMessage.success('删除成功') |
| | | await refreshRemove() |
| | | } catch (error) { |
| | | if (error !== 'cancel') { |
| | | ElMessage.error(error?.message || '删除失败') |
| | | } |
| | | } |
| | | const resolvePrintRecords = async (payload) => { |
| | | const response = Array.isArray(payload?.ids) && payload.ids.length > 0 |
| | | ? await fetchGetRoleMany(payload.ids) |
| | | : await fetchRolePrintPage({ |
| | | ...reportQueryParams.value, |
| | | current: 1, |
| | | pageSize: Number(pagination.total) > 0 ? Number(pagination.total) : Number(payload?.pageSize) || 20 |
| | | }) |
| | | return defaultResponseAdapter(response).records |
| | | } |
| | | |
| | | const handleBatchDelete = async () => { |
| | | if (!selectedRows.value.length) return |
| | | const ids = selectedRows.value.map((item) => item.id).filter((id) => id !== void 0 && id !== null) |
| | | if (!ids.length) return |
| | | |
| | | try { |
| | | await ElMessageBox.confirm(`确定要批量删除选中的 ${ids.length} 个角色吗?`, '批量删除确认', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | }) |
| | | await fetchDeleteRole(ids.join(',')) |
| | | ElMessage.success('批量删除成功') |
| | | selectedRows.value = [] |
| | | await refreshRemove() |
| | | } catch (error) { |
| | | if (error !== 'cancel') { |
| | | ElMessage.error(error?.message || '批量删除失败') |
| | | } |
| | | } |
| | | } |
| | | |
| | | const handleExport = async (payload) => { |
| | | try { |
| | | const response = await fetchExportRoleReport(payload, { |
| | | const { |
| | | previewVisible, |
| | | previewRows, |
| | | previewMeta, |
| | | handlePreviewVisibleChange, |
| | | handleExport, |
| | | handlePrint |
| | | } = usePrintExportPage({ |
| | | downloadFileName: 'role.xlsx', |
| | | requestExport: (payload) => |
| | | fetchExportRoleReport(payload, { |
| | | headers: { |
| | | Authorization: userStore.accessToken || '' |
| | | } |
| | | }) |
| | | if (!response.ok) { |
| | | throw new Error(`导出失败 (${response.status})`) |
| | | } |
| | | const blob = await response.blob() |
| | | const downloadUrl = window.URL.createObjectURL(blob) |
| | | const link = document.createElement('a') |
| | | link.href = downloadUrl |
| | | link.download = 'role.xlsx' |
| | | document.body.appendChild(link) |
| | | link.click() |
| | | link.remove() |
| | | window.URL.revokeObjectURL(downloadUrl) |
| | | ElMessage.success('导出成功') |
| | | } catch (error) { |
| | | ElMessage.error(error?.message || '导出失败') |
| | | } |
| | | }), |
| | | resolvePrintRecords, |
| | | buildPreviewRows: (records) => buildRolePrintRows(records), |
| | | buildPreviewMeta: (rows) => buildPreviewDialogMeta(rows) |
| | | }) |
| | | |
| | | const roleReportColumns = computed(() => resolveRoleReportColumns(columns.value)) |
| | | const resolvedPreviewMeta = computed(() => |
| | | buildRoleReportMeta({ |
| | | previewMeta: previewMeta.value, |
| | | count: previewRows.value.length, |
| | | titleAlign: ROLE_REPORT_STYLE.titleAlign, |
| | | titleLevel: ROLE_REPORT_STYLE.titleLevel |
| | | }) |
| | | ) |
| | | |
| | | const handleSearch = (params) => { |
| | | replaceSearchParams(buildRoleSearchParams(params)) |
| | | getData() |
| | | } |
| | | |
| | | const handlePrint = async (payload) => { |
| | | const token = previewToken.value + 1 |
| | | previewToken.value = token |
| | | activePrintToken.value = token |
| | | previewVisible.value = false |
| | | previewRows.value = [] |
| | | previewMeta.value = {} |
| | | |
| | | try { |
| | | const response = Array.isArray(payload?.ids) && payload.ids.length > 0 |
| | | ? await fetchGetRoleMany(payload.ids) |
| | | : await fetchRolePrintPage({ |
| | | ...reportQueryParams.value, |
| | | current: 1, |
| | | pageSize: Number(pagination.total) > 0 ? Number(pagination.total) : Number(payload?.pageSize) || 20 |
| | | }) |
| | | if (activePrintToken.value !== token) { |
| | | return |
| | | } |
| | | const records = defaultResponseAdapter(response).records |
| | | if (activePrintToken.value !== token) { |
| | | return |
| | | } |
| | | |
| | | const rows = buildRolePrintRows(records) |
| | | const now = new Date() |
| | | previewRows.value = rows |
| | | previewMeta.value = { |
| | | reportTitle, |
| | | reportDate: now.toLocaleDateString('zh-CN'), |
| | | printedAt: now.toLocaleString('zh-CN', { hour12: false }), |
| | | operator: userStore.getUserInfo?.name || userStore.getUserInfo?.username || '', |
| | | count: rows.length |
| | | } |
| | | handlePreviewVisibleChange(true) |
| | | } catch (error) { |
| | | if (activePrintToken.value !== token) { |
| | | return |
| | | } |
| | | ElMessage.error(error?.message || '打印失败') |
| | | } |
| | | const handleReset = () => { |
| | | Object.assign(searchForm.value, createRoleSearchState()) |
| | | resetSearchParams() |
| | | } |
| | | </script> |