refactor: simplify role page and fix pagination keys
| New file |
| | |
| | | import { ref } from 'vue' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | |
| | | export function useCrudPage({ |
| | | createEmptyModel, |
| | | buildEditModel, |
| | | buildSavePayload, |
| | | saveRequest, |
| | | updateRequest, |
| | | deleteRequest, |
| | | entityName, |
| | | resolveRecordLabel, |
| | | refreshCreate, |
| | | refreshUpdate, |
| | | refreshRemove |
| | | }) { |
| | | const dialogVisible = ref(false) |
| | | const dialogType = ref('add') |
| | | const currentRecord = ref(createEmptyModel()) |
| | | const selectedRows = ref([]) |
| | | |
| | | const resetCurrentRecord = () => { |
| | | currentRecord.value = createEmptyModel() |
| | | } |
| | | |
| | | const handleSelectionChange = (rows) => { |
| | | selectedRows.value = Array.isArray(rows) ? rows : [] |
| | | } |
| | | |
| | | const showDialog = (type, record) => { |
| | | dialogType.value = type |
| | | currentRecord.value = type === 'edit' ? buildEditModel(record) : createEmptyModel() |
| | | dialogVisible.value = true |
| | | } |
| | | |
| | | const closeDialog = () => { |
| | | dialogVisible.value = false |
| | | resetCurrentRecord() |
| | | } |
| | | |
| | | const handleDialogSubmit = async (formData) => { |
| | | const payload = buildSavePayload(formData) |
| | | try { |
| | | if (dialogType.value === 'edit') { |
| | | await updateRequest(payload) |
| | | ElMessage.success('修改成功') |
| | | closeDialog() |
| | | await refreshUpdate?.() |
| | | return |
| | | } |
| | | |
| | | await saveRequest(payload) |
| | | ElMessage.success('新增成功') |
| | | closeDialog() |
| | | await refreshCreate?.() |
| | | } catch (error) { |
| | | ElMessage.error(error?.message || '提交失败') |
| | | } |
| | | } |
| | | |
| | | const handleDelete = async (record) => { |
| | | try { |
| | | const recordLabel = resolveRecordLabel?.(record) || record?.id |
| | | await ElMessageBox.confirm(`确定要删除${entityName}「${recordLabel}」吗?`, '删除确认', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | }) |
| | | await deleteRequest(record.id) |
| | | ElMessage.success('删除成功') |
| | | await refreshRemove?.() |
| | | } catch (error) { |
| | | if (error !== 'cancel') { |
| | | ElMessage.error(error?.message || '删除失败') |
| | | } |
| | | } |
| | | } |
| | | |
| | | 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} 个${entityName}吗?`, '批量删除确认', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | }) |
| | | await deleteRequest(ids.join(',')) |
| | | ElMessage.success('批量删除成功') |
| | | selectedRows.value = [] |
| | | await refreshRemove?.() |
| | | } catch (error) { |
| | | if (error !== 'cancel') { |
| | | ElMessage.error(error?.message || '批量删除失败') |
| | | } |
| | | } |
| | | } |
| | | |
| | | return { |
| | | dialogVisible, |
| | | dialogType, |
| | | currentRecord, |
| | | selectedRows, |
| | | handleSelectionChange, |
| | | showDialog, |
| | | closeDialog, |
| | | handleDialogSubmit, |
| | | handleDelete, |
| | | handleBatchDelete |
| | | } |
| | | } |
| New file |
| | |
| | | import { ref } from 'vue' |
| | | import { ElMessage } from 'element-plus' |
| | | |
| | | export function usePrintExportPage({ |
| | | downloadFileName, |
| | | requestExport, |
| | | resolvePrintRecords, |
| | | buildPreviewRows, |
| | | buildPreviewMeta |
| | | }) { |
| | | const previewVisible = ref(false) |
| | | const previewRows = ref([]) |
| | | const previewMeta = ref({}) |
| | | const previewToken = ref(0) |
| | | const activePrintToken = ref(0) |
| | | |
| | | const handlePreviewVisibleChange = (visible) => { |
| | | previewVisible.value = Boolean(visible) |
| | | if (!visible) { |
| | | activePrintToken.value = 0 |
| | | } |
| | | } |
| | | |
| | | const handleExport = async (payload) => { |
| | | try { |
| | | const response = await requestExport(payload) |
| | | 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 = downloadFileName |
| | | document.body.appendChild(link) |
| | | link.click() |
| | | link.remove() |
| | | window.URL.revokeObjectURL(downloadUrl) |
| | | ElMessage.success('导出成功') |
| | | } catch (error) { |
| | | ElMessage.error(error?.message || '导出失败') |
| | | } |
| | | } |
| | | |
| | | const handlePrint = async (payload) => { |
| | | const token = previewToken.value + 1 |
| | | previewToken.value = token |
| | | activePrintToken.value = token |
| | | previewVisible.value = false |
| | | previewRows.value = [] |
| | | previewMeta.value = {} |
| | | |
| | | try { |
| | | const records = await resolvePrintRecords(payload) |
| | | if (activePrintToken.value !== token) { |
| | | return |
| | | } |
| | | |
| | | const rows = buildPreviewRows(records) |
| | | previewRows.value = rows |
| | | previewMeta.value = buildPreviewMeta(rows) |
| | | handlePreviewVisibleChange(true) |
| | | } catch (error) { |
| | | if (activePrintToken.value !== token) { |
| | | return |
| | | } |
| | | ElMessage.error(error?.message || '打印失败') |
| | | } |
| | | } |
| | | |
| | | return { |
| | | previewVisible, |
| | | previewRows, |
| | | previewMeta, |
| | | handlePreviewVisibleChange, |
| | | handleExport, |
| | | handlePrint |
| | | } |
| | | } |
| | |
| | | <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 { |
| | | 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 [] |
| | | } |
| | | |
| | | 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 |
| | | 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 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 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, { |
| | | 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 || '导出失败') |
| | | |
| | | 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 handlePrint = async (payload) => { |
| | | const token = previewToken.value + 1 |
| | | previewToken.value = token |
| | | activePrintToken.value = token |
| | | previewVisible.value = false |
| | | previewRows.value = [] |
| | | previewMeta.value = {} |
| | | |
| | | try { |
| | | const resolvePrintRecords = async (payload) => { |
| | | const response = Array.isArray(payload?.ids) && payload.ids.length > 0 |
| | | ? await fetchGetRoleMany(payload.ids) |
| | | : await fetchRolePrintPage({ |
| | |
| | | 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 |
| | | return defaultResponseAdapter(response).records |
| | | } |
| | | |
| | | 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 |
| | | const { |
| | | previewVisible, |
| | | previewRows, |
| | | previewMeta, |
| | | handlePreviewVisibleChange, |
| | | handleExport, |
| | | handlePrint |
| | | } = usePrintExportPage({ |
| | | downloadFileName: 'role.xlsx', |
| | | requestExport: (payload) => |
| | | fetchExportRoleReport(payload, { |
| | | headers: { |
| | | Authorization: userStore.accessToken || '' |
| | | } |
| | | handlePreviewVisibleChange(true) |
| | | } catch (error) { |
| | | if (activePrintToken.value !== token) { |
| | | return |
| | | }), |
| | | 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() |
| | | } |
| | | ElMessage.error(error?.message || '打印失败') |
| | | } |
| | | |
| | | const handleReset = () => { |
| | | Object.assign(searchForm.value, createRoleSearchState()) |
| | | resetSearchParams() |
| | | } |
| | | </script> |
| | |
| | | import { |
| | | buildRoleScopeSubmitPayload, |
| | | getRoleScopeConfig, |
| | | normalizeScopeKeys, |
| | | normalizeScopeKey, |
| | | normalizeRoleScopeTreeData |
| | | } from '../rolePage.helpers' |
| | | import { fetchGetRoleScopeList, fetchGetRoleScopeTree, fetchUpdateRoleScope } from '@/api/system-manage' |
| | |
| | | } |
| | | |
| | | await loadScopeData(scopeType, { reloadSelection }) |
| | | } |
| | | |
| | | const normalizeScopeKeys = (keys = []) => { |
| | | if (!Array.isArray(keys)) { |
| | | return [] |
| | | } |
| | | |
| | | return Array.from( |
| | | new Set( |
| | | keys |
| | | .map((key) => normalizeScopeKey(key)) |
| | | .filter((key) => key !== '') |
| | | ) |
| | | ) |
| | | } |
| | | |
| | | const normalizeScopeKey = (value) => { |
| | | if (value === '' || value === null || value === void 0) { |
| | | return '' |
| | | } |
| | | const numeric = Number(value) |
| | | if (Number.isNaN(numeric)) { |
| | | return String(value) |
| | | } |
| | | return String(numeric) |
| | | } |
| | | |
| | | const setTreeRef = (scopeType, el) => { |
| | |
| | | } |
| | | } |
| | | |
| | | export function getRolePaginationKey() { |
| | | return { |
| | | current: 'current', |
| | | size: 'pageSize' |
| | | } |
| | | } |
| | | |
| | | const ROLE_REPORT_COLUMNS = [ |
| | | { source: 'name', label: '角色名称' }, |
| | | { source: 'code', label: '角色编码' }, |
| | |
| | | } |
| | | } |
| | | |
| | | function normalizeScopeKeys(keys = []) { |
| | | export function normalizeScopeKeys(keys = []) { |
| | | if (!Array.isArray(keys)) { |
| | | return [] |
| | | } |
| | |
| | | ) |
| | | } |
| | | |
| | | function normalizeScopeKey(value) { |
| | | export function normalizeScopeKey(value) { |
| | | const normalized = normalizeRoleId(value) |
| | | return normalized === void 0 ? '' : String(normalized) |
| | | } |
| New file |
| | |
| | | import { h } from 'vue' |
| | | import ArtButtonMore from '@/components/core/forms/art-button-more/index.vue' |
| | | import { ElTag } from 'element-plus' |
| | | import { getRoleStatusMeta } from './rolePage.helpers' |
| | | |
| | | const ROLE_MORE_ACTIONS = [ |
| | | { |
| | | 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' |
| | | } |
| | | ] |
| | | |
| | | export function createRoleTableColumns(handleActionClick) { |
| | | return [ |
| | | { 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: ROLE_MORE_ACTIONS, |
| | | onClick: (item) => handleActionClick(item, row) |
| | | }) |
| | | ]) |
| | | } |
| | | ] |
| | | } |
| | |
| | | buildUserSavePayload, |
| | | buildUserSearchParams, |
| | | createUserSearchState, |
| | | getUserPaginationKey, |
| | | getUserStatusMeta, |
| | | mergeUserDetailRecord, |
| | | normalizeDeptTreeOptions, |
| | |
| | | core: { |
| | | apiFn: fetchUserPage, |
| | | apiParams: buildUserPageQueryParams(searchForm.value), |
| | | paginationKey: getUserPaginationKey(), |
| | | columnsFactory: () => [ |
| | | { |
| | | prop: 'username', |
| | |
| | | } |
| | | } |
| | | |
| | | export function getUserPaginationKey() { |
| | | return { |
| | | current: 'current', |
| | | size: 'pageSize' |
| | | } |
| | | } |
| | | |
| | | export function buildUserDialogModel(record = {}) { |
| | | const roleIds = normalizeRoleIds(record) |
| | | return { |
| | |
| | | buildRoleSavePayload, |
| | | buildRoleScopeSubmitPayload, |
| | | buildRoleSearchParams, |
| | | getRolePaginationKey, |
| | | getRoleScopeConfig, |
| | | normalizeRoleScopeTreeData, |
| | | normalizeRoleListRow, |
| | |
| | | ) |
| | | const roleIndexSource = fs.readFileSync( |
| | | new URL('../src/views/system/role/index.vue', import.meta.url), |
| | | 'utf8' |
| | | ) |
| | | const roleTableColumnsSource = fs.readFileSync( |
| | | new URL('../src/views/system/role/roleTable.columns.js', import.meta.url), |
| | | 'utf8' |
| | | ) |
| | | |
| | |
| | | name: '管理员' |
| | | } |
| | | ) |
| | | }) |
| | | |
| | | test('role page uses backend pageSize pagination key', () => { |
| | | assert.deepEqual(getRolePaginationKey(), { |
| | | current: 'current', |
| | | size: 'pageSize' |
| | | }) |
| | | }) |
| | | |
| | | test('buildRoleDialogModel normalizes backend role data into the form model', () => { |
| | |
| | | assert.match(roleIndexSource, /v-auth=\"'add'\"/) |
| | | assert.match(roleIndexSource, /v-auth=\"'delete'\"/) |
| | | assert.match(roleIndexSource, /v-auth=\"'query'\"/) |
| | | assert.match(roleIndexSource, /auth: 'edit'/) |
| | | assert.match(roleIndexSource, /auth: 'delete'/) |
| | | assert.match(roleTableColumnsSource, /auth: 'edit'/) |
| | | assert.match(roleTableColumnsSource, /auth: 'delete'/) |
| | | }) |
| | | |
| | | test('createRoleSearchState exposes the role search form model', () => { |
| | |
| | | buildUserPageQueryParams, |
| | | buildUserSavePayload, |
| | | buildUserSearchParams, |
| | | getUserPaginationKey, |
| | | getUserStatusMeta, |
| | | mergeUserDetailRecord, |
| | | normalizeDeptTreeOptions, |
| | |
| | | ) |
| | | }) |
| | | |
| | | test('user page uses backend pageSize pagination key', () => { |
| | | assert.deepEqual(getUserPaginationKey(), { |
| | | current: 'current', |
| | | size: 'pageSize' |
| | | }) |
| | | }) |
| | | |
| | | test('buildUserDialogModel normalizes backend edit data into the form model', () => { |
| | | assert.deepEqual( |
| | | buildUserDialogModel({ |