Skip to content

Commit fe25e1c

Browse files
KAICharles7c
authored andcommitted
refactor: 使用分步表单重构新增角色交互
1 parent b29960f commit fe25e1c

File tree

3 files changed

+318
-5
lines changed

3 files changed

+318
-5
lines changed
Lines changed: 310 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,310 @@
1+
<template>
2+
<a-modal
3+
v-model:visible="visible"
4+
:title="title"
5+
:mask-closable="false"
6+
:esc-to-close="true"
7+
:width="width >= 600 ? 600 : '100%'"
8+
@close="reset"
9+
>
10+
<a-steps :current="current" class="mb-15" @change="onChangeCurrent">
11+
<a-step>创建角色</a-step>
12+
<a-step>功能权限</a-step>
13+
<a-step>数据权限</a-step>
14+
</a-steps>
15+
16+
<a-form ref="formRef" :model="form" :rules="rules" size="large" auto-label-width>
17+
<fieldset v-show="current === 1">
18+
<a-form-item label="名称" field="name">
19+
<a-input v-model.trim="form.name" placeholder="请输入名称" />
20+
</a-form-item>
21+
<a-form-item label="编码" field="code">
22+
<a-input v-model.trim="form.code" placeholder="请输入编码" :disabled="isUpdate" />
23+
</a-form-item>
24+
<a-form-item label="排序" field="sort">
25+
<a-input-number v-model="form.sort" placeholder="请输入排序" :min="1" mode="button" />
26+
</a-form-item>
27+
<a-form-item label="描述" field="description">
28+
<a-textarea
29+
v-model.trim="form.description"
30+
placeholder="请输入描述"
31+
show-word-limit
32+
:max-length="200"
33+
:auto-size="{ minRows: 3, maxRows: 5 }"
34+
/>
35+
</a-form-item>
36+
</fieldset>
37+
<fieldset v-show="current === 2">
38+
<a-form-item hide-label :disabled="form.isSystem" class="w-full">
39+
<a-space>
40+
<a-checkbox v-model="isMenuExpanded" @change="onExpanded('menu')">展开/折叠</a-checkbox>
41+
<a-checkbox v-model="isMenuCheckAll" @change="onCheckAll('menu')">全选/全不选</a-checkbox>
42+
<a-checkbox v-model="isMenuCheckStrictly">父子联动</a-checkbox>
43+
</a-space>
44+
<template #extra>
45+
<a-tree
46+
ref="menuTreeRef"
47+
v-model:checked-keys="form.menuIds"
48+
class="w-full"
49+
:data="menuList"
50+
:default-expand-all="isMenuExpanded"
51+
:check-strictly="!isMenuCheckStrictly"
52+
:virtual-list-props="{ height: 400 }"
53+
checkable
54+
/>
55+
</template>
56+
</a-form-item>
57+
</fieldset>
58+
<fieldset v-show="current === 3">
59+
<a-form-item hide-label field="dataScope">
60+
<a-select
61+
v-model.trim="form.dataScope"
62+
:options="data_scope_enum"
63+
placeholder="请选择数据权限"
64+
:disabled="form.isSystem"
65+
/>
66+
</a-form-item>
67+
<a-form-item v-if="form.dataScope === 5" hide-label :disabled="form.isSystem">
68+
<a-space>
69+
<a-checkbox v-model="isDeptExpanded" @change="onExpanded('dept')">展开/折叠</a-checkbox>
70+
<a-checkbox v-model="isDeptCheckAll" @change="onCheckAll('dept')">全选/全不选</a-checkbox>
71+
<a-checkbox v-model="isDeptCheckStrictly">父子联动</a-checkbox>
72+
</a-space>
73+
<template #extra>
74+
<a-tree
75+
ref="deptTreeRef"
76+
v-model:checked-keys="form.deptIds"
77+
class="w-full"
78+
:data="deptList"
79+
:default-expand-all="isDeptExpanded"
80+
:check-strictly="!isDeptCheckStrictly"
81+
:virtual-list-props="{ height: 350 }"
82+
checkable
83+
/>
84+
</template>
85+
</a-form-item>
86+
</fieldset>
87+
</a-form>
88+
<template #footer>
89+
<a-space size="large">
90+
<a-button :disabled="current === 1" type="secondary" @click="onPrev">
91+
<IconLeft />
92+
上一步
93+
</a-button>
94+
<a-button v-if="current !== 3" :disabled="current === 3" type="primary" @click="onNext">
95+
下一步
96+
<IconRight />
97+
</a-button>
98+
<a-button v-if="current === 3" type="primary" @click="onClickOk">确定</a-button>
99+
</a-space>
100+
</template>
101+
</a-modal>
102+
</template>
103+
104+
<script setup lang="ts">
105+
import { type FormInstance, Message, type TreeNodeData } from '@arco-design/web-vue'
106+
import { useWindowSize } from '@vueuse/core'
107+
import { addRole, getRole, updateRole } from '@/apis'
108+
import { useForm } from '@/hooks'
109+
import { useDept, useDict, useMenu } from '@/hooks/app'
110+
111+
const emit = defineEmits<{
112+
(e: 'save-success'): void
113+
}>()
114+
const { width } = useWindowSize()
115+
const { data_scope_enum } = useDict('data_scope_enum')
116+
const { deptList, getDeptList } = useDept()
117+
const { menuList, getMenuList } = useMenu()
118+
const current = ref<number>(1)
119+
const dataId = ref('')
120+
const isUpdate = computed(() => !!dataId.value)
121+
const title = computed(() => (isUpdate.value ? '修改角色' : '新增角色'))
122+
const formRef = ref<FormInstance>()
123+
124+
const rules: FormInstance['rules'] = {
125+
name: [{ required: true, message: '请输入名称' }],
126+
code: [{ required: true, message: '请输入编码' }],
127+
dataScope: [{ required: true, message: '请选择数据权限' }]
128+
}
129+
130+
const { form, resetForm } = useForm({
131+
sort: 999,
132+
dataScope: 4
133+
})
134+
135+
const menuTreeRef = ref()
136+
const deptTreeRef = ref()
137+
const isMenuExpanded = ref(false)
138+
const isDeptExpanded = ref(true)
139+
const isMenuCheckAll = ref(false)
140+
const isDeptCheckAll = ref(false)
141+
const isMenuCheckStrictly = ref(true)
142+
const isDeptCheckStrictly = ref(true)
143+
// 重置
144+
const reset = () => {
145+
isMenuExpanded.value = false
146+
isMenuCheckAll.value = false
147+
isDeptExpanded.value = true
148+
isDeptCheckAll.value = false
149+
menuTreeRef.value?.expandAll(isMenuExpanded.value)
150+
deptTreeRef.value?.expandAll(isDeptExpanded.value)
151+
formRef.value?.resetFields()
152+
current.value = 1
153+
resetForm()
154+
}
155+
156+
const visible = ref(false)
157+
// 新增
158+
const onAdd = () => {
159+
if (!menuList.value.length) {
160+
getMenuList()
161+
}
162+
reset()
163+
isMenuCheckStrictly.value = true
164+
isDeptCheckStrictly.value = true
165+
dataId.value = ''
166+
visible.value = true
167+
if (!deptList.value.length) {
168+
getDeptList()
169+
}
170+
}
171+
// 上一步
172+
const onPrev = () => {
173+
current.value = Math.max(1, current.value - 1)
174+
}
175+
// 下一步
176+
const onNext = async () => {
177+
try {
178+
if (current.value === 1) {
179+
const isInvalid = await formRef.value?.validateField(['name', 'code', 'sort', 'description'])
180+
if (isInvalid) return
181+
}
182+
current.value = Math.min(3, current.value + 1)
183+
} catch (error) {
184+
console.error(error)
185+
}
186+
}
187+
// 当前页
188+
const onChangeCurrent = (page: number) => {
189+
current.value = page
190+
}
191+
// 修改
192+
const onUpdate = async (id: string) => {
193+
if (!menuList.value.length) {
194+
await getMenuList()
195+
}
196+
if (!deptList.value.length) {
197+
await getDeptList()
198+
}
199+
reset()
200+
isMenuCheckStrictly.value = false
201+
isDeptCheckStrictly.value = false
202+
dataId.value = id
203+
const res = await getRole(id)
204+
Object.assign(form, res.data)
205+
visible.value = true
206+
}
207+
208+
// 获取所有选中的菜单
209+
const getMenuAllCheckedKeys = () => {
210+
// 获取目前被选中的菜单
211+
const checkedNodes = menuTreeRef.value?.getCheckedNodes()
212+
const checkedKeys = checkedNodes.map((item: TreeNodeData) => item.key)
213+
// 获取半选中的菜单
214+
const halfCheckedNodes = menuTreeRef.value?.getHalfCheckedNodes()
215+
const halfCheckedKeys = halfCheckedNodes.map((item: TreeNodeData) => item.key)
216+
checkedKeys.unshift.apply(checkedKeys, halfCheckedKeys)
217+
return checkedKeys
218+
}
219+
220+
// 获取所有选中的部门
221+
const getDeptAllCheckedKeys = () => {
222+
if (!deptTreeRef.value) {
223+
return []
224+
}
225+
// 获取目前被选中的部门
226+
const checkedNodes = deptTreeRef.value?.getCheckedNodes()
227+
const checkedKeys = checkedNodes.map((item: TreeNodeData) => item.key)
228+
// 获取半选中的部门
229+
const halfCheckedNodes = deptTreeRef.value?.getHalfCheckedNodes()
230+
const halfCheckedKeys = halfCheckedNodes.map((item: TreeNodeData) => item.key)
231+
// eslint-disable-next-line prefer-spread
232+
checkedKeys.unshift.apply(checkedKeys, halfCheckedKeys)
233+
return checkedKeys
234+
}
235+
236+
// 保存
237+
const save = async () => {
238+
try {
239+
const isInvalid = await formRef.value?.validate()
240+
if (isInvalid) return false
241+
form.menuIds = getMenuAllCheckedKeys()
242+
form.deptIds = getDeptAllCheckedKeys()
243+
if (isUpdate.value) {
244+
await updateRole(form, dataId.value)
245+
Message.success('修改成功')
246+
} else {
247+
await addRole(form)
248+
Message.success('新增成功')
249+
}
250+
emit('save-success')
251+
return true
252+
} catch (error) {
253+
return false
254+
}
255+
}
256+
257+
const handleTreeAction = (type, action) => {
258+
const refMap = {
259+
menu: menuTreeRef,
260+
dept: deptTreeRef
261+
}
262+
const ref = refMap[type]
263+
if (ref && action === 'expand') {
264+
ref.value?.expandAll(type === 'menu' ? isMenuExpanded.value : isDeptExpanded.value)
265+
} else if (ref && action === 'check') {
266+
ref.value?.checkAll(type === 'menu' ? isMenuCheckAll.value : isDeptCheckAll.value)
267+
}
268+
}
269+
270+
// 调用时
271+
const onExpanded = (type) => handleTreeAction(type, 'expand')
272+
const onCheckAll = (type) => handleTreeAction(type, 'check')
273+
274+
// 确认时
275+
const onClickOk = () => {
276+
if (unref(current) === 3) {
277+
const isSaved = save()
278+
if (isSaved) visible.value = false
279+
}
280+
}
281+
defineExpose({ onAdd, onUpdate })
282+
</script>
283+
284+
<style lang="scss" scoped>
285+
fieldset {
286+
padding: 15px 15px 0 15px;
287+
margin-bottom: 10px;
288+
border: 1px solid var(--color-neutral-3);
289+
border-radius: 3px;
290+
height: 440px;
291+
}
292+
293+
fieldset legend {
294+
color: rgb(var(--gray-10));
295+
padding: 2px 5px 2px 5px;
296+
border: 1px solid var(--color-neutral-3);
297+
border-radius: 3px;
298+
}
299+
300+
.mb-15 {
301+
margin-bottom: 15px
302+
}
303+
304+
:deep(.arco-form-item-extra) {
305+
width: 100%;
306+
}
307+
:deep(.arco-modal-footer){
308+
margin-top: -20px;
309+
}
310+
</style>

src/views/system/role/index.vue

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,20 +50,22 @@
5050
</template>
5151
</GiTable>
5252

53-
<RoleAddDrawer ref="RoleAddDrawerRef" @save-success="search" />
53+
<RoleAddModal ref="RoleAddModalRef" @save-success="search"/>
54+
<RoleEditDrawer ref="RoleEditDrawerRef" @save-success="search" />
5455
<RoleDetailDrawer ref="RoleDetailDrawerRef" />
5556
</div>
5657
</template>
5758

5859
<script setup lang="ts">
59-
import RoleAddDrawer from './RoleAddDrawer.vue'
60+
import RoleEditDrawer from './RoleEditDrawer.vue'
6061
import RoleDetailDrawer from './RoleDetailDrawer.vue'
6162
import { type RoleQuery, type RoleResp, deleteRole, listRole } from '@/apis'
6263
import type { TableInstanceColumns } from '@/components/GiTable/type'
6364
import { useTable } from '@/hooks'
6465
import { useDict } from '@/hooks/app'
6566
import { isMobile } from '@/utils'
6667
import has from '@/utils/has'
68+
import RoleAddModal from './RoleAddModal.vue'
6769
6870
defineOptions({ name: 'SystemRole' })
6971
@@ -119,15 +121,16 @@ const onDelete = (record: RoleResp) => {
119121
return handleDelete(() => deleteRole(record.id), { content: `是否确定删除 [${record.name}]?`, showModal: true })
120122
}
121123
122-
const RoleAddDrawerRef = ref<InstanceType<typeof RoleAddDrawer>>()
124+
const RoleEditDrawerRef = ref<InstanceType<typeof RoleEditDrawer>>()
125+
const RoleAddModalRef = ref<InstanceType<typeof RoleAddModal>>()
123126
// 新增
124127
const onAdd = () => {
125-
RoleAddDrawerRef.value?.onAdd()
128+
RoleAddModalRef.value?.onAdd()
126129
}
127130
128131
// 修改
129132
const onUpdate = (record: RoleResp) => {
130-
RoleAddDrawerRef.value?.onUpdate(record.id)
133+
RoleEditDrawerRef.value?.onUpdate(record.id)
131134
}
132135
133136
const RoleDetailDrawerRef = ref<InstanceType<typeof RoleDetailDrawer>>()

0 commit comments

Comments
 (0)