From 3fdcf1d5e6468c735532e67bde5ff1cdf85bb0c6 Mon Sep 17 00:00:00 2001
From: zhou zhou <3272660260@qq.com>
Date: 星期一, 30 三月 2026 09:14:16 +0800
Subject: [PATCH] refactor: simplify role page and fix pagination keys
---
rsf-design/src/views/system/common/useCrudPage.js | 115 ++++++++
rsf-design/tests/system-role-scope-contract.test.mjs | 16 +
rsf-design/src/views/system/common/usePrintExportPage.js | 80 ++++++
rsf-design/tests/system-user-page-contract.test.mjs | 8
rsf-design/src/views/system/user/index.vue | 2
rsf-design/src/views/system/role/roleTable.columns.js | 104 ++++++++
rsf-design/src/views/system/user/userPage.helpers.js | 7
rsf-design/src/views/system/role/rolePage.helpers.js | 11
rsf-design/src/views/system/role/index.vue | 409 ++++++++----------------------
rsf-design/src/views/system/role/modules/role-permission-dialog.vue | 27 -
10 files changed, 456 insertions(+), 323 deletions(-)
diff --git a/rsf-design/src/views/system/common/useCrudPage.js b/rsf-design/src/views/system/common/useCrudPage.js
new file mode 100644
index 0000000..2c2b7e8
--- /dev/null
+++ b/rsf-design/src/views/system/common/useCrudPage.js
@@ -0,0 +1,115 @@
+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
+ }
+}
diff --git a/rsf-design/src/views/system/common/usePrintExportPage.js b/rsf-design/src/views/system/common/usePrintExportPage.js
new file mode 100644
index 0000000..c202543
--- /dev/null
+++ b/rsf-design/src/views/system/common/usePrintExportPage.js
@@ -0,0 +1,80 @@
+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
+ }
+}
diff --git a/rsf-design/src/views/system/role/index.vue b/rsf-design/src/views/system/role/index.vue
index b6787aa..49d2e34 100644
--- a/rsf-design/src/views/system/role/index.vue
+++ b/rsf-design/src/views/system/role/index.vue
@@ -20,6 +20,7 @@
<ElButton v-auth="'add'" @click="showDialog('add')" v-ripple>鏂板瑙掕壊</ElButton>
<ElButton
v-auth="'delete'"
+ type="danger"
:disabled="selectedRows.length === 0"
@click="handleBatchDelete"
v-ripple
@@ -85,13 +86,14 @@
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,
@@ -100,7 +102,7 @@
buildRoleSavePayload,
buildRoleSearchParams,
createRoleSearchState,
- getRoleStatusMeta,
+ getRolePaginationKey,
normalizeRoleListRow,
ROLE_REPORT_STYLE,
ROLE_REPORT_TITLE,
@@ -111,184 +113,19 @@
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)
@@ -313,137 +150,121 @@
}
}
- 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>
diff --git a/rsf-design/src/views/system/role/modules/role-permission-dialog.vue b/rsf-design/src/views/system/role/modules/role-permission-dialog.vue
index 552ba28..779b85f 100644
--- a/rsf-design/src/views/system/role/modules/role-permission-dialog.vue
+++ b/rsf-design/src/views/system/role/modules/role-permission-dialog.vue
@@ -72,6 +72,8 @@
import {
buildRoleScopeSubmitPayload,
getRoleScopeConfig,
+ normalizeScopeKeys,
+ normalizeScopeKey,
normalizeRoleScopeTreeData
} from '../rolePage.helpers'
import { fetchGetRoleScopeList, fetchGetRoleScopeTree, fetchUpdateRoleScope } from '@/api/system-manage'
@@ -152,31 +154,6 @@
}
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) => {
diff --git a/rsf-design/src/views/system/role/rolePage.helpers.js b/rsf-design/src/views/system/role/rolePage.helpers.js
index 77b7b81..d01d937 100644
--- a/rsf-design/src/views/system/role/rolePage.helpers.js
+++ b/rsf-design/src/views/system/role/rolePage.helpers.js
@@ -41,6 +41,13 @@
}
}
+export function getRolePaginationKey() {
+ return {
+ current: 'current',
+ size: 'pageSize'
+ }
+}
+
const ROLE_REPORT_COLUMNS = [
{ source: 'name', label: '瑙掕壊鍚嶇О' },
{ source: 'code', label: '瑙掕壊缂栫爜' },
@@ -278,7 +285,7 @@
}
}
-function normalizeScopeKeys(keys = []) {
+export function normalizeScopeKeys(keys = []) {
if (!Array.isArray(keys)) {
return []
}
@@ -292,7 +299,7 @@
)
}
-function normalizeScopeKey(value) {
+export function normalizeScopeKey(value) {
const normalized = normalizeRoleId(value)
return normalized === void 0 ? '' : String(normalized)
}
diff --git a/rsf-design/src/views/system/role/roleTable.columns.js b/rsf-design/src/views/system/role/roleTable.columns.js
new file mode 100644
index 0000000..b8a95f0
--- /dev/null
+++ b/rsf-design/src/views/system/role/roleTable.columns.js
@@ -0,0 +1,104 @@
+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)
+ })
+ ])
+ }
+ ]
+}
diff --git a/rsf-design/src/views/system/user/index.vue b/rsf-design/src/views/system/user/index.vue
index 634f8c9..5adc86b 100644
--- a/rsf-design/src/views/system/user/index.vue
+++ b/rsf-design/src/views/system/user/index.vue
@@ -70,6 +70,7 @@
buildUserSavePayload,
buildUserSearchParams,
createUserSearchState,
+ getUserPaginationKey,
getUserStatusMeta,
mergeUserDetailRecord,
normalizeDeptTreeOptions,
@@ -118,6 +119,7 @@
core: {
apiFn: fetchUserPage,
apiParams: buildUserPageQueryParams(searchForm.value),
+ paginationKey: getUserPaginationKey(),
columnsFactory: () => [
{
prop: 'username',
diff --git a/rsf-design/src/views/system/user/userPage.helpers.js b/rsf-design/src/views/system/user/userPage.helpers.js
index a2173a2..8189f25 100644
--- a/rsf-design/src/views/system/user/userPage.helpers.js
+++ b/rsf-design/src/views/system/user/userPage.helpers.js
@@ -71,6 +71,13 @@
}
}
+export function getUserPaginationKey() {
+ return {
+ current: 'current',
+ size: 'pageSize'
+ }
+}
+
export function buildUserDialogModel(record = {}) {
const roleIds = normalizeRoleIds(record)
return {
diff --git a/rsf-design/tests/system-role-scope-contract.test.mjs b/rsf-design/tests/system-role-scope-contract.test.mjs
index dd8b2af..c44030d 100644
--- a/rsf-design/tests/system-role-scope-contract.test.mjs
+++ b/rsf-design/tests/system-role-scope-contract.test.mjs
@@ -8,6 +8,7 @@
buildRoleSavePayload,
buildRoleScopeSubmitPayload,
buildRoleSearchParams,
+ getRolePaginationKey,
getRoleScopeConfig,
normalizeRoleScopeTreeData,
normalizeRoleListRow,
@@ -25,6 +26,10 @@
)
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'
)
@@ -60,6 +65,13 @@
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', () => {
@@ -196,8 +208,8 @@
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', () => {
diff --git a/rsf-design/tests/system-user-page-contract.test.mjs b/rsf-design/tests/system-user-page-contract.test.mjs
index 37da9ce..484333b 100644
--- a/rsf-design/tests/system-user-page-contract.test.mjs
+++ b/rsf-design/tests/system-user-page-contract.test.mjs
@@ -6,6 +6,7 @@
buildUserPageQueryParams,
buildUserSavePayload,
buildUserSearchParams,
+ getUserPaginationKey,
getUserStatusMeta,
mergeUserDetailRecord,
normalizeDeptTreeOptions,
@@ -63,6 +64,13 @@
)
})
+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({
--
Gitblit v1.9.1