mirror of https://gitee.com/xiaonuobase/snowy
316 lines
8.4 KiB
Vue
316 lines
8.4 KiB
Vue
<template>
|
||
<a-drawer
|
||
title="授权资源"
|
||
:width="drawerWidth"
|
||
:visible="visible"
|
||
:destroy-on-close="true"
|
||
:show-pagination="false"
|
||
:body-style="{ paddingBottom: '80px' }"
|
||
:footer-style="{ textAlign: 'right' }"
|
||
@close="onClose"
|
||
>
|
||
<a-spin :spinning="spinningLoading">
|
||
<a-radio-group v-model:value="moduleId" button-style="solid" style="padding-bottom: 10px">
|
||
<a-radio-button
|
||
:key="module.id"
|
||
v-for="module in echoDatalist"
|
||
:value="module.id"
|
||
@click="moduleClock(module.id)"
|
||
>
|
||
<component :is="module.icon" />
|
||
{{ module.title }}</a-radio-button
|
||
>
|
||
</a-radio-group>
|
||
|
||
<a-table size="middle" :columns="columns" :data-source="loadDatas" :pagination="false" bordered>
|
||
<template #bodyCell="{ column, record }">
|
||
<template v-if="column.dataIndex === 'parentName'">
|
||
<a-checkbox :checked="record.parentCheck" @update:checked="(val) => changeParent(record, val)">
|
||
{{ record.parentName }}
|
||
</a-checkbox>
|
||
</template>
|
||
|
||
<template v-if="column.dataIndex === 'title'">
|
||
<a-checkbox :checked="record.nameCheck" @update:checked="(val) => changeSub(record, val)">{{
|
||
record.title
|
||
}}</a-checkbox>
|
||
</template>
|
||
|
||
<template v-if="column.dataIndex === 'button'">
|
||
<template v-if="record.button.length > 0">
|
||
<template v-for="(item, index) in record.button" :key="item.id">
|
||
<a-checkbox v-model:checked="item.check" @change="(evt) => changeChildCheckBox(record, evt)">{{
|
||
item.title
|
||
}}</a-checkbox>
|
||
<br v-if="(index + 1) % 5 === 0" />
|
||
</template>
|
||
</template>
|
||
</template>
|
||
</template>
|
||
</a-table>
|
||
</a-spin>
|
||
<template #footer>
|
||
<a-button style="margin-right: 8px" @click="onClose">关闭</a-button>
|
||
<a-button type="primary" :loading="submitLoading" @click="onSubmit">保存</a-button>
|
||
</template>
|
||
</a-drawer>
|
||
</template>
|
||
|
||
<script setup name="grantResourceForm">
|
||
import { nextTick } from 'vue'
|
||
import tool from '@/utils/tool'
|
||
import userApi from '@/api/sys/userApi'
|
||
import roleApi from '@/api/sys/roleApi'
|
||
import userCenterApi from '@/api/sys/userCenterApi'
|
||
const spinningLoading = ref(false)
|
||
let firstShowMap = $ref({})
|
||
const emit = defineEmits({ successful: null })
|
||
const submitLoading = ref(false)
|
||
// 抽屉的宽度
|
||
const drawerWidth = 1000
|
||
// 自动获取宽度,默认获取浏览器的宽度的90%
|
||
//(window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth) * 0.9
|
||
|
||
const columns = [
|
||
{
|
||
key: 'parentName',
|
||
title: '一级目录',
|
||
dataIndex: 'parentName',
|
||
customCell: (row, index) => {
|
||
const parentName = row.parentName
|
||
const indexArr = firstShowMap[parentName]
|
||
if (index === indexArr[0]) {
|
||
return { rowSpan: indexArr.length }
|
||
}
|
||
return { rowSpan: 0 }
|
||
},
|
||
width: 150
|
||
},
|
||
{
|
||
key: 'title',
|
||
title: '菜单',
|
||
dataIndex: 'title',
|
||
width: 200
|
||
},
|
||
{
|
||
key: 'button',
|
||
title: '按钮授权',
|
||
dataIndex: 'button'
|
||
}
|
||
]
|
||
const echoDatalist = ref([])
|
||
const moduleId = ref('')
|
||
const loadDatas = ref([])
|
||
|
||
// 获取数据
|
||
const loadData = async () => {
|
||
// firstShowMap = {} // 重置单元格合并映射
|
||
// 如果有数据,我们再不去反复的查询
|
||
if (echoDatalist.value.length > 0) {
|
||
let data = echoDatalist.value.find((f) => f.id === moduleId.value).menu
|
||
loadDatas.value = data
|
||
} else {
|
||
// 获取表格数据
|
||
spinningLoading.value = true
|
||
const res = await roleApi.roleResourceTreeSelector()
|
||
const param = {
|
||
id: resultDataModel.id
|
||
}
|
||
// 获取回显数据
|
||
const resEcho = await userApi.userOwnResource(param)
|
||
spinningLoading.value = false
|
||
echoDatalist.value = echoModuleData(res, resEcho)
|
||
moduleId.value = res[0].id
|
||
loadDatas.value = echoDatalist.value[0].menu
|
||
}
|
||
}
|
||
const checkFieldKeys = ['button']
|
||
let visible = $ref(false)
|
||
// 返回的数据模型,最终需要转换成这样
|
||
let resultDataModel = {
|
||
id: '',
|
||
grantInfoList: []
|
||
}
|
||
// 打开抽屉
|
||
const onOpen = (record) => {
|
||
resultDataModel.id = record.id
|
||
visible = true
|
||
firstShowMap = {}
|
||
loadData()
|
||
}
|
||
// 数据转换
|
||
const echoModuleData = (data, resEcho) => {
|
||
// 通过应用循环
|
||
data.forEach((module) => {
|
||
if (module.menu) {
|
||
// 加入回显内容
|
||
module.menu.forEach((item) => {
|
||
const menueCheck = ref(0)
|
||
if (resEcho.grantInfoList.length > 0) {
|
||
resEcho.grantInfoList.forEach((grant) => {
|
||
if (item.id === grant.menuId) {
|
||
menueCheck.value++
|
||
// 处理按钮
|
||
if (grant.buttonInfo.length > 0) {
|
||
grant.buttonInfo.forEach((button) => {
|
||
item.button.forEach((itemButton) => {
|
||
if (button === itemButton.id) {
|
||
itemButton.check = true
|
||
}
|
||
})
|
||
})
|
||
}
|
||
}
|
||
})
|
||
}
|
||
// 回显前面的2个
|
||
if (menueCheck.value > 0) {
|
||
item.parentCheck = true
|
||
item.nameCheck = true
|
||
}
|
||
})
|
||
|
||
// 排序
|
||
module.menu = module.menu.sort((a, b) => {
|
||
return a.parentId - b.parentId
|
||
})
|
||
// 缓存加入索引
|
||
module.menu.forEach((item, index) => {
|
||
// 下面就是用来知道不同的一级菜单里面有几个二级菜单,以及他们所在的索引
|
||
if (firstShowMap[item.parentName]) {
|
||
firstShowMap[item.parentName].push(index)
|
||
} else {
|
||
firstShowMap[item.parentName] = [index]
|
||
}
|
||
})
|
||
}
|
||
})
|
||
return data
|
||
}
|
||
|
||
// 通过应用分菜单
|
||
const moduleClock = (value) => {
|
||
moduleId.value = value
|
||
loadData()
|
||
}
|
||
// 遍历字段
|
||
const handleOnlySelf = (record, key, val) => {
|
||
record[key].forEach((item) => {
|
||
// 处理'button'选中状态
|
||
item.check = val
|
||
})
|
||
}
|
||
const checkAllChildNotChecked = (record) => {
|
||
const allChecked = checkFieldKeys.every((key) => {
|
||
// 遍历所有的字段
|
||
const child = record[key]
|
||
return child.every((field) => !field.check)
|
||
})
|
||
return allChecked
|
||
}
|
||
const changeChildCheckBox = (record, evt) => {
|
||
let checked = evt.target.checked
|
||
if (!checked && checkAllChildNotChecked(record)) {
|
||
// 这里注释掉勾选去掉所有按钮,联动去掉菜单
|
||
/*record.nameCheck = false
|
||
record.parentCheck = false*/
|
||
} else if (checked) {
|
||
record.nameCheck = checked
|
||
record.parentCheck = checked
|
||
}
|
||
}
|
||
// 二级菜单的勾选
|
||
const changeSub = (record, val) => {
|
||
// 选中二级菜单
|
||
record.nameCheck = val
|
||
checkFieldKeys.forEach((key) => {
|
||
// 遍历所有的字段
|
||
handleOnlySelf(record, key, val)
|
||
})
|
||
}
|
||
// 当点击首列的勾选
|
||
const changeParent = (record, val) => {
|
||
record.parentCheck = val
|
||
// 通过这个应用id,找到应用下的所有菜单
|
||
const moduleMenu = echoDatalist.value.find((f) => record.module === f.id)
|
||
const parentName = record.parentName
|
||
// 获取同一级菜单的所有索引
|
||
const indexArr = firstShowMap[parentName]
|
||
indexArr.forEach((indexItem) => {
|
||
// 获取同一级菜单的所有行
|
||
const row = moduleMenu.menu[indexItem]
|
||
// 给这些菜单的索引去勾选
|
||
changeSub(row, val)
|
||
})
|
||
}
|
||
// 关闭抽屉
|
||
const onClose = () => {
|
||
// 将这些缓存的给清空
|
||
echoDatalist.value = []
|
||
moduleId.value = ''
|
||
loadDatas.value = []
|
||
firstShowMap = {}
|
||
visible = false
|
||
}
|
||
// 提交之前转换数据
|
||
const convertData = () => {
|
||
resultDataModel.grantInfoList = []
|
||
echoDatalist.value.forEach((table) => {
|
||
if (table.menu) {
|
||
table.menu.forEach((item) => {
|
||
const grantInfo = {
|
||
menuId: '',
|
||
buttonInfo: []
|
||
}
|
||
if (item.nameCheck) {
|
||
grantInfo.menuId = item.id
|
||
item.button.forEach((button) => {
|
||
if (button.check) {
|
||
grantInfo.buttonInfo.push(button.id)
|
||
}
|
||
})
|
||
resultDataModel.grantInfoList.push(grantInfo)
|
||
}
|
||
})
|
||
}
|
||
})
|
||
return resultDataModel
|
||
}
|
||
// 验证并提交数据
|
||
const onSubmit = () => {
|
||
const param = convertData()
|
||
submitLoading.value = true
|
||
userApi
|
||
.userGrantResource(param)
|
||
.then(() => {
|
||
onClose()
|
||
emit('successful')
|
||
refreshCacheMenu()
|
||
})
|
||
.finally(() => {
|
||
submitLoading.value = false
|
||
})
|
||
}
|
||
// 刷新缓存的菜单
|
||
const refreshCacheMenu = () => {
|
||
nextTick(() => {
|
||
userCenterApi.userLoginMenu().then((res) => {
|
||
tool.data.set('MENU', res)
|
||
})
|
||
})
|
||
}
|
||
// 调用这个函数将子组件的一些数据和方法暴露出去
|
||
defineExpose({
|
||
onOpen
|
||
})
|
||
</script>
|
||
|
||
<style scoped>
|
||
/* 重写复选框的样式 */
|
||
.ant-checkbox-wrapper {
|
||
margin-left: 0px !important;
|
||
padding-top: 2px !important;
|
||
padding-bottom: 2px !important;
|
||
}
|
||
</style>
|