feat(permission): replace OA roles with post-bound roles and assignee posts
Unify effective roles via post-role bindings, remove dedicated OA role UI, and update OA assignee resolution plus related SQL/docs. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
a3c73d76bb
commit
06db3bc966
66 files changed
+55337
-38647
No files matched your search
@@ -103,17 +103,21 @@
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="操作"
|
||||
min-width="160"
|
||||
min-width="320"
|
||||
fixed="right"
|
||||
align="center"
|
||||
header-align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<div style="display: flex; justify-content: center; gap: 8px">
|
||||
<div style="display: flex; justify-content: center; gap: 8px; flex-wrap: wrap">
|
||||
<el-button class="btn-op-item primary" @click="handlePostBindUsers(row)">
|
||||
<el-icon><User /></el-icon>
|
||||
关联用户
|
||||
</el-button>
|
||||
<el-button class="btn-op-item" @click="handlePostBindRoles(row)">
|
||||
<el-icon><Key /></el-icon>
|
||||
角色配置
|
||||
</el-button>
|
||||
<el-dropdown trigger="hover" @command="(cmd: string) => handleDropdownCommand(cmd, row)">
|
||||
<el-button class="btn-op-item">
|
||||
<el-icon><More /></el-icon>
|
||||
@@ -308,6 +312,58 @@
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 角色配置 -->
|
||||
<el-dialog
|
||||
v-model="postRoleDialogVisible"
|
||||
title="角色配置"
|
||||
width="560px"
|
||||
class="dialog-form-layout"
|
||||
align-center
|
||||
append-to-body
|
||||
modal-class="protocol-dialog-mask"
|
||||
destroy-on-close
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<DialogFormBody :loading="postRoleLoading">
|
||||
<p v-if="postRolePostName" class="post-bind-subtitle">
|
||||
职位:<strong>{{ postRolePostName }}</strong>
|
||||
</p>
|
||||
<el-form label-position="top">
|
||||
<el-form-item label="关联角色">
|
||||
<el-select
|
||||
v-model="postRoleCheckedIds"
|
||||
multiple
|
||||
filterable
|
||||
placeholder="请选择角色"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in postRoleOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</DialogFormBody>
|
||||
<template #footer>
|
||||
<el-button class="btn-ghost" @click="postRoleDialogVisible = false">
|
||||
<el-icon><Close /></el-icon>
|
||||
取消
|
||||
</el-button>
|
||||
<el-button
|
||||
class="btn-main"
|
||||
type="primary"
|
||||
@click="handlePostRoleSubmit"
|
||||
:loading="postRoleSubmitLoading"
|
||||
>
|
||||
<el-icon><Check /></el-icon>
|
||||
确定
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<SystemViewDialog
|
||||
v-model:visible="viewVisible"
|
||||
:title="viewRow?.postName || '职位详情'"
|
||||
@@ -328,6 +384,7 @@
|
||||
Close,
|
||||
Check,
|
||||
User,
|
||||
Key,
|
||||
View,
|
||||
Edit,
|
||||
Delete,
|
||||
@@ -349,7 +406,10 @@
|
||||
fetchDeptTree,
|
||||
fetchUserList,
|
||||
fetchPostBoundUserIds,
|
||||
fetchUpdatePostBoundUsers
|
||||
fetchUpdatePostBoundUsers,
|
||||
fetchPostBoundRoleIds,
|
||||
fetchUpdatePostBoundRoles,
|
||||
fetchRoleList
|
||||
} from '@/api/admin'
|
||||
|
||||
defineOptions({ name: 'SystemPost' })
|
||||
@@ -385,6 +445,7 @@
|
||||
const viewVisible = ref(false)
|
||||
const viewRow = ref<Api.Post.PostItem | null>(null)
|
||||
const viewBoundUserNames = ref<string[]>([])
|
||||
const viewBoundRoleNames = ref<string[]>([])
|
||||
const viewSections = computed(() => {
|
||||
const row = viewRow.value
|
||||
if (!row) return []
|
||||
@@ -404,6 +465,12 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
if (viewBoundRoleNames.value.length > 0) {
|
||||
sections.push({
|
||||
title: '关联角色',
|
||||
fields: [{ label: '已关联角色', value: viewBoundRoleNames.value.join('、'), span: true }]
|
||||
})
|
||||
}
|
||||
if (viewBoundUserNames.value.length > 0) {
|
||||
sections.push({
|
||||
title: '关联用户',
|
||||
@@ -415,16 +482,68 @@
|
||||
async function handleView(row: Api.Post.PostItem) {
|
||||
viewRow.value = row
|
||||
viewBoundUserNames.value = []
|
||||
viewBoundRoleNames.value = []
|
||||
viewVisible.value = true
|
||||
try {
|
||||
const [boundRes] = await Promise.all([fetchPostBoundUserIds(row.id), fetchDeptTree()])
|
||||
const [boundRes, roleRes] = await Promise.all([
|
||||
fetchPostBoundUserIds(row.id),
|
||||
fetchPostBoundRoleIds(row.id)
|
||||
])
|
||||
const roleIds = roleRes?.roleIds || []
|
||||
if (roleIds.length > 0) {
|
||||
const roleListRes = await fetchRoleList({ page: 1, pageSize: 100 })
|
||||
const idToName = new Map(
|
||||
(roleListRes?.list || []).map((r: any) => [r.id, r.roleName || r.id])
|
||||
)
|
||||
viewBoundRoleNames.value = roleIds.map((id: string) => idToName.get(id) || id)
|
||||
}
|
||||
const boundIds = boundRes?.userIds || []
|
||||
if (boundIds.length === 0) return
|
||||
const users = await fetchAllUsersForPostBind()
|
||||
const idToName = new Map(users.map((u) => [u.id, u.nickName || u.userNo || u.id]))
|
||||
viewBoundUserNames.value = boundIds.map((id: string) => idToName.get(id) || id)
|
||||
} catch (e) {
|
||||
console.error('加载关联用户失败:', e)
|
||||
console.error('加载职位关联信息失败:', e)
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 角色配置弹窗 ----
|
||||
const postRoleDialogVisible = ref(false)
|
||||
const postRoleLoading = ref(false)
|
||||
const postRoleSubmitLoading = ref(false)
|
||||
const postRolePostId = ref('')
|
||||
const postRolePostName = ref('')
|
||||
const postRoleCheckedIds = ref<string[]>([])
|
||||
const postRoleOptions = ref<Array<{ label: string; value: string }>>([])
|
||||
|
||||
async function handlePostBindRoles(row: Api.Post.PostItem) {
|
||||
postRolePostId.value = row.id
|
||||
postRolePostName.value = row.postName || row.postCode || ''
|
||||
postRoleCheckedIds.value = []
|
||||
postRoleDialogVisible.value = true
|
||||
postRoleLoading.value = true
|
||||
try {
|
||||
const [boundRes, roleListRes] = await Promise.all([
|
||||
fetchPostBoundRoleIds(row.id),
|
||||
fetchRoleList({ page: 1, pageSize: 100 })
|
||||
])
|
||||
postRoleCheckedIds.value = [...(boundRes?.roleIds || [])]
|
||||
postRoleOptions.value = (roleListRes?.list || []).map((r: any) => ({
|
||||
label: r.roleName || r.id,
|
||||
value: r.id
|
||||
}))
|
||||
} finally {
|
||||
postRoleLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handlePostRoleSubmit() {
|
||||
postRoleSubmitLoading.value = true
|
||||
try {
|
||||
await fetchUpdatePostBoundRoles(postRolePostId.value, [...postRoleCheckedIds.value])
|
||||
postRoleDialogVisible.value = false
|
||||
} finally {
|
||||
postRoleSubmitLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -361,6 +361,7 @@
|
||||
<el-select
|
||||
v-model="formData.roleIds"
|
||||
multiple
|
||||
filterable
|
||||
placeholder="请选择角色"
|
||||
style="width: 100%"
|
||||
>
|
||||
@@ -372,6 +373,22 @@
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="职位" style="grid-column: 1 / -1">
|
||||
<el-select
|
||||
v-model="formData.postIds"
|
||||
multiple
|
||||
filterable
|
||||
placeholder="请选择职位"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in postOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" style="grid-column: 1 / -1">
|
||||
<el-input v-model="formData.remark" type="textarea" :rows="2" placeholder="可选" />
|
||||
</el-form-item>
|
||||
@@ -426,12 +443,14 @@
|
||||
fetchDeleteUser,
|
||||
fetchResetPassword,
|
||||
fetchDeptTree,
|
||||
fetchRoleOptions
|
||||
fetchRoleOptions,
|
||||
fetchPostList
|
||||
} from '@/api/admin'
|
||||
import { buildPathLabelMapFromTreeRoots } from '@/utils/tree-path'
|
||||
import { resolveUserAvatarFromInfo } from '@/utils/user-avatar'
|
||||
|
||||
const roleOptions = ref<Array<{ label: string; value: string }>>([])
|
||||
const postOptions = ref<Array<{ label: string; value: string }>>([])
|
||||
|
||||
async function loadRoleOptions() {
|
||||
try {
|
||||
@@ -441,6 +460,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function loadPostOptions() {
|
||||
try {
|
||||
const res = await fetchPostList({ page: 1, pageSize: 100 })
|
||||
postOptions.value = (res?.list || []).map((p) => ({
|
||||
label: p.postName || p.postCode || p.id,
|
||||
value: p.id
|
||||
}))
|
||||
} catch {
|
||||
postOptions.value = []
|
||||
}
|
||||
}
|
||||
|
||||
defineOptions({ name: 'SystemUser' })
|
||||
|
||||
const deptKeyword = ref('')
|
||||
@@ -489,6 +520,15 @@
|
||||
return labels.length ? labels.join('、') : '—'
|
||||
}
|
||||
|
||||
function formatUserPostDisplay(row: Api.User.UserItem) {
|
||||
const ids = row.postIds || []
|
||||
if (!ids.length) return '—'
|
||||
const labels = ids
|
||||
.map((id) => postOptions.value.find((o) => o.value === id)?.label)
|
||||
.filter((name): name is string => Boolean(name))
|
||||
return labels.length ? labels.join('、') : '—'
|
||||
}
|
||||
|
||||
const searchForm = reactive<{ keyword: string; status: string }>({ keyword: '', status: '' })
|
||||
const tableData = ref<Api.User.UserItem[]>([])
|
||||
const selectedRows = ref<Api.User.UserItem[]>([])
|
||||
@@ -518,7 +558,8 @@
|
||||
status: '1',
|
||||
orderNum: 0,
|
||||
remark: '',
|
||||
roleIds: [] as string[]
|
||||
roleIds: [] as string[],
|
||||
postIds: [] as string[]
|
||||
})
|
||||
|
||||
const supervisorUserOptions = ref<Api.User.UserItem[]>([])
|
||||
@@ -627,6 +668,7 @@
|
||||
formData.orderNum = 0
|
||||
formData.remark = ''
|
||||
formData.roleIds = []
|
||||
formData.postIds = []
|
||||
supervisorUserOptions.value = []
|
||||
editingId.value = null
|
||||
}
|
||||
@@ -667,6 +709,7 @@
|
||||
formData.orderNum = detail.orderNum ?? 0
|
||||
formData.remark = detail.remark || ''
|
||||
formData.roleIds = detail.roleIds ? [...detail.roleIds] : []
|
||||
formData.postIds = detail.postIds ? [...detail.postIds] : []
|
||||
if (formData.supervisorUserId) {
|
||||
await remoteSearchSupervisorUsers(detail.nickName || '')
|
||||
if (!supervisorUserOptions.value.some((u) => u.id === formData.supervisorUserId)) {
|
||||
@@ -701,7 +744,8 @@
|
||||
status: formData.status,
|
||||
orderNum: formData.orderNum,
|
||||
remark: formData.remark?.trim() || undefined,
|
||||
roleIds: [...formData.roleIds]
|
||||
roleIds: [...formData.roleIds],
|
||||
postIds: [...formData.postIds]
|
||||
})
|
||||
} else {
|
||||
await fetchCreateUser({
|
||||
@@ -719,7 +763,8 @@
|
||||
status: formData.status,
|
||||
orderNum: formData.orderNum,
|
||||
remark: formData.remark?.trim() || undefined,
|
||||
roleIds: formData.roleIds.length ? formData.roleIds : undefined
|
||||
roleIds: formData.roleIds.length ? formData.roleIds : undefined,
|
||||
postIds: formData.postIds.length ? formData.postIds : undefined
|
||||
})
|
||||
}
|
||||
dialogVisible.value = false
|
||||
@@ -838,6 +883,7 @@
|
||||
tagType: row.status === '1' ? 'success' : 'info'
|
||||
},
|
||||
{ label: '角色', value: formatUserRoleDisplay(row), span: true },
|
||||
{ label: '职位', value: formatUserPostDisplay(row), span: true },
|
||||
{ label: '创建时间', value: row.createTime },
|
||||
{ label: '备注', value: row.remark, span: true }
|
||||
]
|
||||
@@ -868,6 +914,7 @@
|
||||
loadDeptTree()
|
||||
loadData()
|
||||
loadRoleOptions()
|
||||
loadPostOptions()
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
Reference in new issue
Block a user