snowy/snowy-admin-web/src/views/sys/role/grantPermissionForm.vue

408 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<xn-form-container
title="授权权限"
:width="drawerWidth"
:visible="visible"
:destroy-on-close="true"
:show-pagination="false"
@close="onClose">
<a-alert message="注:此功能界面需要与代码查询条件配合使用,并非所有接口都需设置数据范围,多用于业务模块!" type="warning" closable/>
<a-spin :spinning="spinningLoading">
<a-table
class="mt-4"
size="middle"
:columns="columns"
:data-source="loadDatas"
:row-key="(record) => record.api"
bordered>
<template #headerCell="{ column }">
<template v-if="column.key === 'api'">
<a-checkbox @update:checked="(val) => onCheckAllChange(val)"> 接口 </a-checkbox>
</template>
<template v-if="column.key === 'dataScope'">
<span>{{ column.title }}</span>
<a-radio-group v-model:value="scopeRadioValue"
@change="radioChange" style="padding: 0 10px">
<a-radio value="SCOPE_ALL"> 全部 </a-radio>
<a-radio value="SCOPE_SELF"> 仅自已 </a-radio>
<a-radio value="SCOPE_ORG"> 所属机构 </a-radio>
<a-radio value="SCOPE_ORG_CHILD"> 所属机构及以下 </a-radio>
</a-radio-group>
</template>
</template>
<!---->
<template #customFilterDropdown="{ setSelectedKeys, selectedKeys, confirm, clearFilters, column }">
<div style="padding: 8px">
<a-input
ref="searchInput"
:placeholder="`Search ${column.dataIndex}`"
:value="selectedKeys[0]"
style="width: 188px; margin-bottom: 8px; display: block"
@change="e => setSelectedKeys(e.target.value ? [e.target.value] : [])"
@pressEnter="handleSearch(selectedKeys, confirm, column.dataIndex)"
/>
<a-button
type="primary"
size="small"
style="width: 90px; margin-right: 8px"
@click="handleSearch(selectedKeys, confirm, column.dataIndex)"
>
<template #icon><SearchOutlined /></template>
Search
</a-button>
<a-button size="small" style="width: 90px" @click="handleReset(clearFilters)">
Reset
</a-button>
</div>
</template>
<template #customFilterIcon="{ filtered }">
<search-outlined :style="{ color: filtered ? '#108ee9' : undefined }" />
</template>
<!---->
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'api'">
<a-checkbox :checked="record.check" @update:checked="(val) => changeApi(record, val)">
{{ record.api }}
</a-checkbox>
</template>
<template v-if="column.dataIndex === 'dataScope'">
<template v-if="record.dataScope.length > 0">
<template v-for="item in record.dataScope" :key="item.id + record.api">
<a-radio
v-model:checked="item.check"
:name="item.title"
@change="(evt) => changeDataScope(record, evt)">
<a-badge
v-if="
(item.value === 'SCOPE_ORG_DEFINE') &
record.dataScope[4].check &
(item.scopeDefineOrgIdList !== undefined)
"
:count="item.scopeDefineOrgIdList.length"
:number-style="{ backgroundColor: '#52c41a' }">
{{ item.title }}</a-badge>
<div v-else>{{ item.title }}</div>
</a-radio>
</template>
<a-button v-if="record.dataScope[4].check" type="link" size="small" @click="handleDefineOrg(record)"
>选择机构</a-button
>
</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>
<ScopeDefineOrg ref="scopeDefineOrgModal" @click="scopeDefineOrgClick" />
</xn-form-container>
</template>
<script setup name="grantResourceForm">
import { SearchOutlined } from '@ant-design/icons-vue';
import roleApi from '@/api/sys/roleApi'
import { remove } from 'lodash-es'
import ScopeDefineOrg from './scopeDefineOrg.vue'
let visible = ref(false)
const spinningLoading = ref(false)
const scopeDefineOrgModal = ref(null)
const emit = defineEmits({ successful: null })
const submitLoading = ref(false)
const CustomValue = 'SCOPE_ORG_DEFINE'
// 抽屉的宽度
const drawerWidth = 1000
// 自动获取宽度默认获取浏览器的宽度的90%
//(window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth) * 0.9
let loadDatas = ref([])
// 标题接口列搜索, 数据范围默认
const scopeRadioValue = ref()
const state = reactive({
searchText: '',
searchedColumn: '',
});
const searchInput = ref();
const columns = [
{
key: 'api',
title: '接口',
dataIndex: 'api',
width: 380,
customFilterDropdown: true,
onFilter: (value, record) => record.api.includes(value),
onFilterDropdownVisibleChange: visible => {
if (visible) {
setTimeout(() => {
searchInput.value.focus();
}, 100);
}
},
},
{
key: 'dataScope',
title: '数据范围',
dataIndex: 'dataScope'
}
]
// 获取数据
const loadData = async () => {
spinningLoading.value = true
const res = await roleApi.rolePermissionTreeSelector()
// 获取他已有的权限
const param = {
id: grantPermissionParam.id
}
const resOwn = await roleApi.roleOwnPermission(param)
// 数据转换
echoModuleData(res, resOwn)
spinningLoading.value = false
}
// 数据转换
const echoModuleData = (res, resOwn) => {
res.forEach((api) => {
const obj = {
api: api,
dataScope: datascope(api),
check: false
}
if (resOwn.grantInfoList.length > 0) {
resOwn.grantInfoList.forEach((item) => {
if (item.apiUrl === subStrApi(api)) {
obj.check = true
// dataScopeInfo
obj.dataScope.forEach((o) => {
if (o.value === item.scopeCategory) {
o.check = true
// 如果是自定义
if (item.scopeCategory === 'SCOPE_ORG_DEFINE') {
o.scopeDefineOrgIdList = item.scopeDefineOrgIdList
}
}
})
}
})
}
loadDatas.value.push(obj)
})
}
const datascope = (id) => {
return [
{
id: `SCOPE_ALL_${id}`,
title: '全部',
value: 'SCOPE_ALL',
check: false
},
{
id: `SCOPE_SELF_${id}`,
title: '仅自己',
value: 'SCOPE_SELF',
check: false
},
{
id: `SCOPE_ORG_${id}`,
title: '所属机构',
value: 'SCOPE_ORG',
check: false
},
{
id: `SCOPE_ORG_CHILD_${id}`,
title: '所属机构及以下',
value: 'SCOPE_ORG_CHILD',
check: false
},
{
id: `SCOPE_ORG_DEFINE_${id}`,
title: '自定义',
value: 'SCOPE_ORG_DEFINE',
check: false
}
]
}
// 点击数据权限选择
const changeDataScope = (record, evt) => {
const name = evt.target.name
// 这里做互斥,每个
record.dataScope.forEach((item) => {
if (item.title !== name) {
item.check = false
}
})
changeChildCheckBox(record, evt)
}
// 处理自定义
const handleDefineOrg = (recordDataScope) => {
// 弹框选择子自定义
const data = recordDataScope.dataScope.find((f) => f.value === CustomValue)
// 选中了
if (data.check) {
// 获取到选中的key数组传过去让其那边回显
const checkKeysStr = recordDataScope.dataScope[4].scopeDefineOrgIdList
scopeDefineOrgModal.value.onOpen(data.id, checkKeysStr)
} else {
// 清理缓存中的结构,去掉就行
handleDatascope(false, record.id, null)
}
}
// 自定义数据弹窗回调
const scopeDefineOrgClick = (value) => {
handleDatascope(true, value.dataScopeId, value.defineOrgIdData.scopeDefineOrgIdList)
}
// 处理Datascope数据被选中自定义或取消自定义数据
const handleDatascope = (check, id, orgData) => {
loadDatas.value.forEach((item) => {
if (id === 'SCOPE_ORG_DEFINE_' + item.api) {
item.dataScope.forEach((items) => {
if (items.value === 'SCOPE_ORG_DEFINE') {
if (check) {
items.scopeDefineOrgIdList = orgData
} else {
items.scopeDefineOrgIdList = []
}
}
})
}
})
}
// 打开抽屉
const onOpen = (record) => {
grantPermissionParam.id = record.id
visible.value = true
loadData()
}
// 关闭抽屉
const onClose = () => {
// 将这些缓存的给清空
loadDatas.value = []
scopeRadioValue.value = ''
visible.value = false
}
// 全选
const onCheckAllChange = (value) => {
spinningLoading.value = true
loadDatas.value.forEach((data) => {
changeApi(data, value)
spinningLoading.value = false
})
}
// 选中接口
const changeApi = (record, val) => {
record.check = val
if (val) {
let checkStatus = 0
for (let i = 0; i < record.dataScope.length; i++) {
if (record.dataScope[i].check) {
checkStatus++
}
}
if (checkStatus === 0) {
record.dataScope[0].check = true
}
} else {
// 去掉已选中的
record.dataScope.forEach((item) => {
item.check = false
if (item.value === 'SCOPE_ORG_DEFINE') {
item.scopeDefineOrgIdList = []
}
})
}
}
// 设置选中状态
const changeChildCheckBox = (record, evt) => {
let checked = evt.target.checked
if (!checked) {
record.check = false
} else if (checked) {
record.check = checked
}
}
// 提交数据模型
let grantPermissionParam = {
// 角色id
id: '',
// 授权权限信息
grantInfoList: []
}
// 提交之前转换数据
const convertData = () => {
grantPermissionParam.grantInfoList = []
loadDatas.value.forEach((table) => {
if (table.check) {
table.dataScope.forEach((item) => {
if (item.check) {
const dataScopeInfo = {
apiUrl: subStrApi(table.api),
scopeCategory: item.value,
scopeDefineOrgIdList: item.scopeDefineOrgIdList === undefined ? [] : item.scopeDefineOrgIdList
}
grantPermissionParam.grantInfoList.push(dataScopeInfo)
}
})
}
})
return grantPermissionParam
}
// 截取api串中的中文及括号
const subStrApi = (api) => {
return api.substring(0, api.indexOf('['))
}
// 验证并提交数据
const onSubmit = () => {
const param = convertData()
submitLoading.value = true
roleApi
.roleGrantPermission(param)
.then(() => {
onClose()
emit('successful')
})
.finally(() => {
submitLoading.value = false
})
}
// 标题接口列搜索
const handleSearch = (selectedKeys, confirm, dataIndex) => {
confirm();
state.searchText = selectedKeys[0];
state.searchedColumn = dataIndex;
}
// 标题接口列搜索重置
const handleReset = clearFilters => {
clearFilters();
state.searchText = '';
}
// 标题数据范围列radio-group事件
const radioChange = (obj) => {
loadDatas.value.forEach((data) => {
data.dataScope.forEach((data) => {
if (obj.target.value === data.value){
data.check = true
}else {
data.check = false
}
})
})
}
// 调用这个函数将子组件的一些数据和方法暴露出去
defineExpose({
onOpen
})
</script>
<style scoped>
/* 重写复选框的样式 */
.ant-checkbox-wrapper {
margin-left: 0px !important;
padding-top: 2px !important;
padding-bottom: 2px !important;
}
</style>