Skip to content

Commit 72ddb0c

Browse files
YunaiVgitee-org
authored andcommitted
!63 【重构】Vue3 管理后台:[系统管理 -> 角色管理] 使用 Element Plus 原生实现
Merge pull request !63 from Chika/dev
2 parents f517733 + 7380165 commit 72ddb0c

File tree

4 files changed

+561
-375
lines changed

4 files changed

+561
-375
lines changed
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
<template>
2+
<Dialog :title="dialogScopeTitle" v-model="dialogScopeVisible" width="800">
3+
<el-form
4+
ref="menuPermissionFormRef"
5+
:model="dataScopeForm"
6+
:inline="true"
7+
label-width="80px"
8+
v-loading="formLoading"
9+
>
10+
<el-form-item label="角色名称">
11+
<el-tag>{{ dataScopeForm.name }}</el-tag>
12+
</el-form-item>
13+
<el-form-item label="角色标识">
14+
<el-tag>{{ dataScopeForm.code }}</el-tag>
15+
</el-form-item>
16+
<!-- 分配角色的数据权限对话框 -->
17+
<el-form-item label="权限范围" v-if="actionScopeType === 'data'">
18+
<el-select v-model="dataScopeForm.dataScope">
19+
<el-option
20+
v-for="item in dataScopeDictDatas"
21+
:key="item.value"
22+
:label="item.label"
23+
:value="item.value"
24+
/>
25+
</el-select>
26+
</el-form-item>
27+
<!-- 分配角色的菜单权限对话框 -->
28+
<el-row>
29+
<el-col :span="24">
30+
<el-form-item
31+
label="权限范围"
32+
v-if="
33+
actionScopeType === 'menu' ||
34+
dataScopeForm.dataScope === SystemDataScopeEnum.DEPT_CUSTOM
35+
"
36+
style="display: flex"
37+
>
38+
<el-card class="card" shadow="never">
39+
<template #header>
40+
父子联动(选中父节点,自动选择子节点):
41+
<el-switch
42+
v-model="checkStrictly"
43+
inline-prompt
44+
active-text=""
45+
inactive-text=""
46+
/>
47+
全选/全不选:
48+
<el-switch
49+
v-model="treeNodeAll"
50+
inline-prompt
51+
active-text=""
52+
inactive-text=""
53+
@change="handleCheckedTreeNodeAll()"
54+
/>
55+
</template>
56+
<el-tree
57+
ref="treeRef"
58+
node-key="id"
59+
show-checkbox
60+
:check-strictly="!checkStrictly"
61+
:props="defaultProps"
62+
:data="dataScopeForm"
63+
empty-text="加载中,请稍后"
64+
/>
65+
</el-card>
66+
</el-form-item> </el-col
67+
></el-row>
68+
</el-form>
69+
<!-- 操作按钮 -->
70+
<template #footer>
71+
<div class="dialog-footer">
72+
<el-button
73+
:title="t('action.save')"
74+
:loading="actionLoading"
75+
@click="submitScope()"
76+
type="primary"
77+
:disabled="formLoading"
78+
>
79+
保存
80+
</el-button>
81+
<el-button
82+
:loading="actionLoading"
83+
:title="t('dialog.close')"
84+
@click="dialogScopeVisible = false"
85+
>取 消</el-button
86+
>
87+
</div>
88+
</template>
89+
</Dialog>
90+
</template>
91+
92+
<script setup lang="ts">
93+
import * as RoleApi from '@/api/system/role'
94+
import type { ElTree } from 'element-plus'
95+
import type { FormExpose } from '@/components/Form'
96+
import { handleTree, defaultProps } from '@/utils/tree'
97+
import { SystemDataScopeEnum } from '@/utils/constants'
98+
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
99+
import { listSimpleMenusApi } from '@/api/system/menu'
100+
import { listSimpleDeptApi } from '@/api/system/dept'
101+
import * as PermissionApi from '@/api/system/permission'
102+
// ========== CRUD 相关 ==========
103+
const actionLoading = ref(false) // 遮罩层
104+
const menuPermissionFormRef = ref<FormExpose>() // 表单 Ref
105+
const { t } = useI18n() // 国际化
106+
const dialogScopeTitle = ref('菜单权限')
107+
const dataScopeDictDatas = ref()
108+
const message = useMessage() // 消息弹窗
109+
const actionScopeType = ref('')
110+
// 选项
111+
const checkStrictly = ref(true)
112+
const treeNodeAll = ref(false)
113+
const dialogScopeVisible = ref(false) // 弹窗的是否展示
114+
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
115+
const treeOptions = ref<any[]>([]) // 菜单树形结构
116+
const treeRef = ref<InstanceType<typeof ElTree>>()
117+
// ========== 数据权限 ==========
118+
const dataScopeForm = reactive({
119+
id: 0,
120+
name: '',
121+
code: '',
122+
dataScope: 0,
123+
checkList: []
124+
})
125+
126+
/** 打开弹窗 */
127+
const openModal = async (type: string, row: RoleApi.RoleVO) => {
128+
dataScopeForm.id = row.id
129+
dataScopeForm.name = row.name
130+
dataScopeForm.code = row.code
131+
actionScopeType.value = type
132+
dialogScopeVisible.value = true
133+
if (type === 'menu') {
134+
const menuRes = await listSimpleMenusApi()
135+
treeOptions.value = handleTree(menuRes)
136+
const role = await PermissionApi.listRoleMenusApi(row.id)
137+
if (role) {
138+
role?.forEach((item: any) => {
139+
unref(treeRef)?.setChecked(item, true, false)
140+
})
141+
}
142+
} else if (type === 'data') {
143+
const deptRes = await listSimpleDeptApi()
144+
treeOptions.value = handleTree(deptRes)
145+
const role = await RoleApi.getRole(row.id)
146+
dataScopeForm.dataScope = role.dataScope
147+
if (role.dataScopeDeptIds) {
148+
role.dataScopeDeptIds?.forEach((item: any) => {
149+
unref(treeRef)?.setChecked(item, true, false)
150+
})
151+
}
152+
}
153+
}
154+
155+
// 保存权限
156+
const submitScope = async () => {
157+
if ('data' === actionScopeType.value) {
158+
const data = ref<PermissionApi.PermissionAssignRoleDataScopeReqVO>({
159+
roleId: dataScopeForm.id,
160+
dataScope: dataScopeForm.dataScope,
161+
dataScopeDeptIds:
162+
dataScopeForm.dataScope !== SystemDataScopeEnum.DEPT_CUSTOM
163+
? []
164+
: (treeRef.value!.getCheckedKeys(false) as unknown as Array<number>)
165+
})
166+
await PermissionApi.assignRoleDataScopeApi(data.value)
167+
} else if ('menu' === actionScopeType.value) {
168+
const data = ref<PermissionApi.PermissionAssignRoleMenuReqVO>({
169+
roleId: dataScopeForm.id,
170+
menuIds: [
171+
...(treeRef.value!.getCheckedKeys(false) as unknown as Array<number>),
172+
...(treeRef.value!.getHalfCheckedKeys() as unknown as Array<number>)
173+
]
174+
})
175+
await PermissionApi.assignRoleMenuApi(data.value)
176+
}
177+
message.success(t('common.updateSuccess'))
178+
dialogScopeVisible.value = false
179+
}
180+
181+
// 全选/全不选
182+
const handleCheckedTreeNodeAll = () => {
183+
treeRef.value!.setCheckedNodes(treeNodeAll.value ? treeOptions.value : [])
184+
}
185+
186+
const init = () => {
187+
dataScopeDictDatas.value = getIntDictOptions(DICT_TYPE.SYSTEM_DATA_SCOPE)
188+
}
189+
190+
defineExpose({ openModal }) // 提供 openModal 方法,用于打开弹窗
191+
// ========== 初始化 ==========
192+
onMounted(() => {
193+
init()
194+
})
195+
</script>
196+
<style scoped>
197+
.card {
198+
width: 100%;
199+
max-height: 400px;
200+
overflow-y: scroll;
201+
}
202+
</style>

src/views/system/role/RoleForm.vue

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
<template>
2+
<Dialog :title="dialogTitle" v-model="modelVisible" width="800">
3+
<el-form
4+
ref="formRef"
5+
:model="formData"
6+
:rules="formRules"
7+
label-width="80px"
8+
v-loading="formLoading"
9+
>
10+
<el-form-item label="角色名称" prop="name">
11+
<el-input v-model="formData.name" placeholder="请输入角色名称" />
12+
</el-form-item>
13+
<el-form-item label="角色类型" prop="type">
14+
<el-input :model-value="formData.type" placeholder="请输入角色类型" height="150px" />
15+
</el-form-item>
16+
<el-form-item label="角色标识" prop="code">
17+
<el-input :model-value="formData.code" placeholder="请输入角色标识" height="150px" />
18+
</el-form-item>
19+
<el-form-item label="显示顺序" prop="sort">
20+
<el-input :model-value="formData.sort" placeholder="请输入显示顺序" height="150px" />
21+
</el-form-item>
22+
<el-form-item label="状态" prop="status">
23+
<el-select v-model="formData.status" placeholder="请选择状态" clearable>
24+
<el-option
25+
v-for="dict in getDictOptions(DICT_TYPE.COMMON_STATUS)"
26+
:key="parseInt(dict.value)"
27+
:label="dict.label"
28+
:value="parseInt(dict.value)"
29+
/>
30+
</el-select>
31+
</el-form-item>
32+
<el-form-item label="备注" prop="remark">
33+
<el-input v-model="formData.remark" type="textarea" placeholder="请输备注" />
34+
</el-form-item>
35+
</el-form>
36+
<template #footer>
37+
<div class="dialog-footer">
38+
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
39+
<el-button @click="modelVisible = false">取 消</el-button>
40+
</div>
41+
</template>
42+
</Dialog>
43+
</template>
44+
<script setup lang="ts">
45+
import { getDictOptions } from '@/utils/dict'
46+
import { CommonStatusEnum } from '@/utils/constants'
47+
import type { FormExpose } from '@/components/Form'
48+
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
49+
import * as RoleApi from '@/api/system/role'
50+
// ========== CRUD 相关 ==========
51+
const dialogTitle = ref('edit') // 弹出层标题
52+
const formRef = ref<FormExpose>() // 表单 Ref
53+
const { t } = useI18n() // 国际化
54+
const dataScopeDictDatas = ref()
55+
const message = useMessage() // 消息弹窗
56+
57+
const modelVisible = ref(false) // 弹窗的是否展示
58+
const modelTitle = ref('') // 弹窗的标题
59+
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
60+
const formType = ref('') // 表单的类型:create - 新增;update - 修改
61+
62+
const formData = ref({
63+
id: undefined,
64+
name: '',
65+
type: '',
66+
code: '',
67+
sort: undefined,
68+
status: CommonStatusEnum.ENABLE,
69+
remark: ''
70+
})
71+
const formRules = reactive({
72+
name: [{ required: true, message: '岗位标题不能为空', trigger: 'blur' }],
73+
code: [{ required: true, message: '岗位编码不能为空', trigger: 'change' }],
74+
sort: [{ required: true, message: '岗位顺序不能为空', trigger: 'change' }],
75+
status: [{ required: true, message: '岗位状态不能为空', trigger: 'change' }],
76+
remark: [{ required: false, message: '岗位内容不能为空', trigger: 'blur' }]
77+
})
78+
79+
/** 打开弹窗 */
80+
const openModal = async (type: string, id?: number) => {
81+
modelVisible.value = true
82+
modelTitle.value = t('action.' + type)
83+
formType.value = type
84+
resetForm()
85+
// 修改时,设置数据
86+
if (id) {
87+
formLoading.value = true
88+
try {
89+
formData.value = await RoleApi.getRole(id)
90+
} finally {
91+
formLoading.value = false
92+
}
93+
}
94+
}
95+
/** 重置表单 */
96+
const resetForm = () => {
97+
formData.value = {
98+
id: undefined,
99+
name: '',
100+
code: '',
101+
sort: undefined,
102+
status: CommonStatusEnum.ENABLE,
103+
remark: ''
104+
}
105+
formRef.value?.resetFields()
106+
}
107+
defineExpose({ openModal }) // 提供 openModal 方法,用于打开弹窗
108+
/** 提交表单 */
109+
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
110+
const submitForm = async () => {
111+
// 校验表单
112+
if (!formRef) return
113+
const valid = await formRef.value.validate()
114+
if (!valid) return
115+
// 提交请求
116+
formLoading.value = true
117+
try {
118+
const data = formData.value as unknown as RoleApi.RoleVO
119+
if (formType.value === 'create') {
120+
await RoleApi.createRole(data)
121+
message.success(t('common.createSuccess'))
122+
} else {
123+
await RoleApi.updateRole(data)
124+
message.success(t('common.updateSuccess'))
125+
}
126+
modelVisible.value = false
127+
// 发送操作成功的事件
128+
emit('success')
129+
} finally {
130+
formLoading.value = false
131+
}
132+
}
133+
134+
const init = () => {
135+
dataScopeDictDatas.value = getIntDictOptions(DICT_TYPE.SYSTEM_DATA_SCOPE)
136+
}
137+
// ========== 初始化 ==========
138+
onMounted(() => {
139+
init()
140+
})
141+
</script>
142+
<style scoped>
143+
.card {
144+
width: 100%;
145+
max-height: 400px;
146+
overflow-y: scroll;
147+
}
148+
</style>

0 commit comments

Comments
 (0)