mirror of https://gitee.com/xiaonuobase/snowy
【新增】新增用户管理的授权角色与授权资源,新增前端导入功能,新增导出某用户的模板信息,优化部分代码
parent
e4a6b7aad0
commit
b87a7f5d65
|
@ -69,5 +69,43 @@ export default {
|
|||
// 给用户授权角色
|
||||
grantRole(data) {
|
||||
return request('grantRole', data)
|
||||
},
|
||||
// 获取用户拥有资源
|
||||
userOwnResource(data) {
|
||||
return request('ownResource', data, 'get')
|
||||
},
|
||||
// 给用户授权资源
|
||||
userGrantResource(data) {
|
||||
return request('grantResource', data)
|
||||
},
|
||||
// 获取用户拥有权限
|
||||
userOwnPermission(data) {
|
||||
return request('ownPermission', data, 'get')
|
||||
},
|
||||
// 给用户授权权限
|
||||
userGrantPermission(data) {
|
||||
return request('grantPermission', data)
|
||||
},
|
||||
// 下载用户导入模板
|
||||
userDownloadImportUserTemplate(data) {
|
||||
return request('downloadImportUserTemplate', data, 'get', {
|
||||
responseType: 'blob'
|
||||
})
|
||||
},
|
||||
// 用户导入
|
||||
userImport(data) {
|
||||
return request('import', data)
|
||||
},
|
||||
// 用户导出
|
||||
userExport(data) {
|
||||
return request('export', data, 'get', {
|
||||
responseType: 'blob'
|
||||
})
|
||||
},
|
||||
// 按模板导出用户个人信息
|
||||
userExportUserInfo(data) {
|
||||
return request('exportUserInfo', data, 'get', {
|
||||
responseType: 'blob'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,10 @@ export default {
|
|||
editButton: 'edit',
|
||||
removeButton: 'delete',
|
||||
batchRemoveButton: 'batch Remove',
|
||||
detailButton: 'detail'
|
||||
detailButton: 'detail',
|
||||
searchKey: 'Search Key',
|
||||
imports: 'import',
|
||||
more: 'More'
|
||||
},
|
||||
model: {
|
||||
user: 'user',
|
||||
|
@ -50,5 +53,10 @@ export default {
|
|||
emailCodePlaceholder: 'Please input a Email code',
|
||||
restPhoneType: 'For phone rest',
|
||||
restEmailType: 'For email rest'
|
||||
},
|
||||
user: {
|
||||
userStatus: 'User Status',
|
||||
resetPassword: 'Reset Password',
|
||||
role: 'Role',
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,10 @@ export default {
|
|||
editButton: '编辑',
|
||||
removeButton: '删除',
|
||||
batchRemoveButton: '批量删除',
|
||||
detailButton: '详情'
|
||||
detailButton: '详情',
|
||||
searchKey: '关键词',
|
||||
imports: '导入',
|
||||
more: '更多',
|
||||
},
|
||||
model: {
|
||||
user: '用户',
|
||||
|
@ -52,5 +55,10 @@ export default {
|
|||
emailCodePlaceholder: '请输入邮件验证码',
|
||||
restPhoneType: '手机号找回',
|
||||
restEmailType: '邮箱找回'
|
||||
},
|
||||
user: {
|
||||
userStatus: '用户状态',
|
||||
resetPassword: '重置密码',
|
||||
role: '角色',
|
||||
}
|
||||
}
|
||||
|
|
|
@ -109,16 +109,14 @@
|
|||
})
|
||||
// 验证并提交数据
|
||||
const onSubmit = () => {
|
||||
formRef.value
|
||||
.validate()
|
||||
.then(() => {
|
||||
submitLoading.value = true
|
||||
positionApi.submitForm(formData.value, !formData.value.id).then(() => {
|
||||
visible = false
|
||||
submitLoading.value = false
|
||||
emit('successful')
|
||||
})
|
||||
formRef.value.validate().then(() => {
|
||||
submitLoading.value = true
|
||||
positionApi.submitForm(formData.value, !formData.value.id).then(() => {
|
||||
visible = false
|
||||
submitLoading.value = false
|
||||
emit('successful')
|
||||
})
|
||||
})
|
||||
}
|
||||
// 调用这个函数将子组件的一些数据和方法暴露出去
|
||||
defineExpose({
|
||||
|
|
|
@ -57,20 +57,18 @@
|
|||
|
||||
// 验证并提交数据
|
||||
const onSubmit = () => {
|
||||
formRef.value
|
||||
.validate()
|
||||
.then(() => {
|
||||
const defParam = {
|
||||
category: 'BUTTON',
|
||||
// module: recordData.value.module,
|
||||
parentId: recordData.value.id
|
||||
}
|
||||
const param = Object.assign(defParam, formData.value)
|
||||
buttonApi.submitForm(param, !formData.value.id).then((res) => {
|
||||
onClose()
|
||||
emit('successful')
|
||||
})
|
||||
formRef.value.validate().then(() => {
|
||||
const defParam = {
|
||||
category: 'BUTTON',
|
||||
// module: recordData.value.module,
|
||||
parentId: recordData.value.id
|
||||
}
|
||||
const param = Object.assign(defParam, formData.value)
|
||||
buttonApi.submitForm(param, !formData.value.id).then((res) => {
|
||||
onClose()
|
||||
emit('successful')
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 调用这个函数将子组件的一些数据和方法暴露出去
|
||||
|
|
|
@ -63,19 +63,17 @@
|
|||
}
|
||||
// 验证并提交数据
|
||||
const onSubmit = () => {
|
||||
formRef.value
|
||||
.validate()
|
||||
.then(() => {
|
||||
const defParam = {
|
||||
category: 'FIELD',
|
||||
parentId: recordData.value.id
|
||||
}
|
||||
const param = Object.assign(defParam, formData.value)
|
||||
fieldApi.submitForm(param, !formData.value.id).then((res) => {
|
||||
onClose()
|
||||
emit('successful')
|
||||
})
|
||||
formRef.value.validate().then(() => {
|
||||
const defParam = {
|
||||
category: 'FIELD',
|
||||
parentId: recordData.value.id
|
||||
}
|
||||
const param = Object.assign(defParam, formData.value)
|
||||
fieldApi.submitForm(param, !formData.value.id).then((res) => {
|
||||
onClose()
|
||||
emit('successful')
|
||||
})
|
||||
})
|
||||
}
|
||||
// 调用这个函数将子组件的一些数据和方法暴露出去
|
||||
defineExpose({
|
||||
|
|
|
@ -10,21 +10,16 @@
|
|||
>
|
||||
<a-form ref="formRef" :model="formData" :rules="formRules" layout="vertical">
|
||||
<a-form-item label="显示名称:" name="title">
|
||||
<span>{{formData.title}}</span>
|
||||
<span>{{ formData.title }}</span>
|
||||
</a-form-item>
|
||||
<a-form-item label="所属目录:" name="module" v-if="formData.parentId === '0'" >
|
||||
<a-form-item label="所属目录:" name="module" v-if="formData.parentId === '0'">
|
||||
<a-radio-group v-model:value="formData.module" button-style="solid">
|
||||
<a-radio-button
|
||||
v-for="module in moduleTypeList"
|
||||
:key="module.id"
|
||||
:value="module.id"
|
||||
>
|
||||
<a-radio-button v-for="module in moduleTypeList" :key="module.id" :value="module.id">
|
||||
<component :is="module.icon" />
|
||||
{{ module.title }}</a-radio-button
|
||||
>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
|
||||
</a-form>
|
||||
<template #footer>
|
||||
<a-button style="margin-right: 8px" @click="onClose">关闭</a-button>
|
||||
|
@ -65,26 +60,27 @@
|
|||
|
||||
// 默认要校验的
|
||||
const formRules = {
|
||||
module: [required('请选择所属目录')],
|
||||
module: [required('请选择所属目录')]
|
||||
}
|
||||
|
||||
// 验证并提交数据
|
||||
const onSubmit = () => {
|
||||
formRef.value
|
||||
.validate()
|
||||
.then(() => {
|
||||
const param = {
|
||||
id: formData.value.id,
|
||||
module: formData.value.module
|
||||
}
|
||||
submitLoading.value = true
|
||||
menuApi.menuChangeModule(param).then(() => {
|
||||
formRef.value.validate().then(() => {
|
||||
const param = {
|
||||
id: formData.value.id,
|
||||
module: formData.value.module
|
||||
}
|
||||
submitLoading.value = true
|
||||
menuApi
|
||||
.menuChangeModule(param)
|
||||
.then(() => {
|
||||
submitLoading.value = false
|
||||
emit('successful')
|
||||
}).finally(() => {
|
||||
})
|
||||
.finally(() => {
|
||||
visible = false
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
// 调用这个函数将子组件的一些数据和方法暴露出去
|
||||
defineExpose({
|
||||
|
|
|
@ -199,7 +199,8 @@
|
|||
onClose()
|
||||
emit('successful')
|
||||
})
|
||||
}).finally(() => {
|
||||
})
|
||||
.finally(() => {
|
||||
submitLoading.value = false
|
||||
})
|
||||
}
|
||||
|
|
|
@ -45,11 +45,11 @@
|
|||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex === 'path'">
|
||||
<span v-if="record.menuType === 'MENU'">{{record.path}}</span>
|
||||
<span v-if="record.menuType === 'MENU'">{{ record.path }}</span>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
<template v-if="column.dataIndex === 'component'">
|
||||
<span v-if="record.menuType === 'MENU'">{{record.component}}</span>
|
||||
<span v-if="record.menuType === 'MENU'">{{ record.component }}</span>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
<template v-if="column.dataIndex === 'icon'">
|
||||
|
@ -101,7 +101,7 @@
|
|||
</s-table>
|
||||
</a-card>
|
||||
<Form ref="form" @successful="table.refresh(true)" />
|
||||
<changeModuleForm ref="changeModuleFormRef" @successful="table.refresh(true)"/>
|
||||
<changeModuleForm ref="changeModuleFormRef" @successful="table.refresh(true)" />
|
||||
<Button ref="button" />
|
||||
</template>
|
||||
|
||||
|
|
|
@ -74,14 +74,12 @@
|
|||
|
||||
// 验证并提交数据
|
||||
const onSubmit = () => {
|
||||
formRef.value
|
||||
.validate()
|
||||
.then(() => {
|
||||
moduleApi.submitForm(formData.value, !formData.value.id).then(() => {
|
||||
onClose()
|
||||
emit('successful')
|
||||
})
|
||||
formRef.value.validate().then(() => {
|
||||
moduleApi.submitForm(formData.value, !formData.value.id).then(() => {
|
||||
onClose()
|
||||
emit('successful')
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 调用这个函数将子组件的一些数据和方法暴露出去
|
||||
|
|
|
@ -143,18 +143,19 @@
|
|||
|
||||
// 验证并提交数据
|
||||
const onSubmit = () => {
|
||||
formRef.value
|
||||
.validate()
|
||||
.then(() => {
|
||||
const param = parameterChanges(formData.value)
|
||||
submitLoading.value = true
|
||||
spaApi.submitForm(param, !param.id).then(() => {
|
||||
formRef.value.validate().then(() => {
|
||||
const param = parameterChanges(formData.value)
|
||||
submitLoading.value = true
|
||||
spaApi
|
||||
.submitForm(param, !param.id)
|
||||
.then(() => {
|
||||
visible = false
|
||||
emit('successful')
|
||||
}).finally(() => {
|
||||
})
|
||||
.finally(() => {
|
||||
submitLoading.value = false
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
const parameterChanges = (data) => {
|
||||
if (!data.component) {
|
||||
|
|
|
@ -102,20 +102,18 @@
|
|||
})
|
||||
// 验证并提交数据
|
||||
const onSubmit = () => {
|
||||
formRef.value
|
||||
.validate()
|
||||
.then(() => {
|
||||
submitLoading.value = true
|
||||
roleApi
|
||||
.submitForm(formData.value, !formData.value.id)
|
||||
.then(() => {
|
||||
visible = false
|
||||
emit('successful')
|
||||
})
|
||||
.finally(() => {
|
||||
submitLoading.value = false
|
||||
})
|
||||
})
|
||||
formRef.value.validate().then(() => {
|
||||
submitLoading.value = true
|
||||
roleApi
|
||||
.submitForm(formData.value, !formData.value.id)
|
||||
.then(() => {
|
||||
visible = false
|
||||
emit('successful')
|
||||
})
|
||||
.finally(() => {
|
||||
submitLoading.value = false
|
||||
})
|
||||
})
|
||||
}
|
||||
// 调用这个函数将子组件的一些数据和方法暴露出去
|
||||
defineExpose({
|
||||
|
|
|
@ -32,16 +32,16 @@
|
|||
|
||||
<template v-if="column.dataIndex === 'title'">
|
||||
<a-checkbox :checked="record.nameCheck" @update:checked="(val) => changeSub(record, val)">{{
|
||||
record.title
|
||||
}}</a-checkbox>
|
||||
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>
|
||||
item.title
|
||||
}}</a-checkbox>
|
||||
<br v-if="(index + 1) % 5 === 0" />
|
||||
</template>
|
||||
</template>
|
||||
|
|
|
@ -225,7 +225,7 @@
|
|||
}
|
||||
// 可伸缩列
|
||||
const handleResizeColumn = (w, col) => {
|
||||
col.width = w;
|
||||
col.width = w
|
||||
}
|
||||
// 删除
|
||||
const removeOrg = (record) => {
|
||||
|
|
|
@ -0,0 +1,337 @@
|
|||
<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-alert
|
||||
message="注:此功能界面需要与代码查询条件配合使用,并非所有接口都需设置数据范围,多用于业务模块!"
|
||||
type="warning"
|
||||
closable
|
||||
/>
|
||||
<a-spin :spinning="spinningLoading">
|
||||
<a-table
|
||||
class="mt-4"
|
||||
size="middle"
|
||||
:columns="columns"
|
||||
:data-source="loadDatas"
|
||||
bordered
|
||||
:row-key="(record) => record.api"
|
||||
>
|
||||
<template #headerCell="{ column }">
|
||||
<template v-if="column.key === 'api'">
|
||||
<a-checkbox @update:checked="(val) => onCheckAllChange(val)"> 接口 </a-checkbox>
|
||||
</template>
|
||||
</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" />
|
||||
</a-drawer>
|
||||
</template>
|
||||
|
||||
<script setup name="grantResourceForm">
|
||||
import userApi from '@/api/sys/userApi'
|
||||
import roleApi from '@/api/sys/roleApi'
|
||||
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 columns = [
|
||||
{
|
||||
key: 'api',
|
||||
title: '接口',
|
||||
dataIndex: 'api',
|
||||
width: 380
|
||||
},
|
||||
{
|
||||
key: 'dataScope',
|
||||
title: '数据范围',
|
||||
dataIndex: 'dataScope'
|
||||
}
|
||||
]
|
||||
// 获取数据
|
||||
const loadData = async () => {
|
||||
spinningLoading.value = true
|
||||
const res = await roleApi.rolePermissionTreeSelector()
|
||||
// 获取他已有的权限
|
||||
const param = {
|
||||
id: grantPermissionParam.id
|
||||
}
|
||||
const resOwn = await userApi.userOwnPermission(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 = []
|
||||
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
|
||||
userApi
|
||||
.userGrantPermission(param)
|
||||
.then(() => {
|
||||
onClose()
|
||||
emit('successful')
|
||||
})
|
||||
.finally(() => {
|
||||
submitLoading.value = false
|
||||
})
|
||||
}
|
||||
// 调用这个函数将子组件的一些数据和方法暴露出去
|
||||
defineExpose({
|
||||
onOpen
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 重写复选框的样式 */
|
||||
.ant-checkbox-wrapper {
|
||||
margin-left: 0px !important;
|
||||
padding-top: 2px !important;
|
||||
padding-bottom: 2px !important;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,315 @@
|
|||
<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>
|
|
@ -0,0 +1,112 @@
|
|||
<template>
|
||||
<a-drawer
|
||||
title="导入导出"
|
||||
:width="620"
|
||||
:visible="visible"
|
||||
:destroy-on-close="true"
|
||||
:footer-style="{ textAlign: 'right' }"
|
||||
@close="onClose"
|
||||
>
|
||||
<span
|
||||
>导入数据格式严格按照系统模板进行数据录入,请点击
|
||||
<a-button type="primary" size="small" @click="downloadImportUserTemplate">下载模板</a-button>
|
||||
</span>
|
||||
<a-divider dashed />
|
||||
<div>
|
||||
<a-spin :spinning="impUploadLoading">
|
||||
<a-upload-dragger :show-upload-list="false" :custom-request="customRequestLocal">
|
||||
<p class="ant-upload-drag-icon">
|
||||
<inbox-outlined></inbox-outlined>
|
||||
</p>
|
||||
<p class="ant-upload-text">单击或拖动文件到此区域进行上传</p>
|
||||
<p class="ant-upload-hint">仅支持xls、xlsx格式文件</p>
|
||||
</a-upload-dragger>
|
||||
</a-spin>
|
||||
</div>
|
||||
<a-alert v-if="impAlertStatus" type="info" :show-icon="false" banner closable @close="onImpClose" class="mt-3">
|
||||
<template #description>
|
||||
<p>导入总数:{{ impResultData.totalCount }} 条</p>
|
||||
<p>导入成功:{{ impResultData.successCount }} 条</p>
|
||||
<div v-if="impResultData.errorCount > 0">
|
||||
<p><span style="color: red">失败条数:</span>{{ impResultData.errorCount }} 条</p>
|
||||
<a-table :dataSource="impResultErrorDataSource" :columns="impErrorColumns" size="small" />
|
||||
</div>
|
||||
</template>
|
||||
</a-alert>
|
||||
</a-drawer>
|
||||
</template>
|
||||
|
||||
<script setup name="userImpExp">
|
||||
import userApi from '@/api/sys/userApi'
|
||||
|
||||
const impUploadLoading = ref(false)
|
||||
const impAlertStatus = ref(false)
|
||||
const impResultData = ref({})
|
||||
const impResultErrorDataSource = ref([])
|
||||
// 导入
|
||||
const customRequestLocal = (data) => {
|
||||
impUploadLoading.value = true
|
||||
const fileData = new FormData()
|
||||
fileData.append('file', data.file)
|
||||
return userApi
|
||||
.userImport(fileData)
|
||||
.then((res) => {
|
||||
impAlertStatus.value = true
|
||||
impResultData.value = res
|
||||
impResultErrorDataSource.value = res.errorDetail
|
||||
})
|
||||
.finally(() => {
|
||||
impUploadLoading.value = false
|
||||
})
|
||||
}
|
||||
// 关闭导入提示
|
||||
const onImpClose = () => {
|
||||
impAlertStatus.value = false
|
||||
}
|
||||
const impErrorColumns = [
|
||||
{
|
||||
title: '索引',
|
||||
dataIndex: 'index',
|
||||
width: '80px'
|
||||
},
|
||||
{
|
||||
title: '原因',
|
||||
dataIndex: 'msg'
|
||||
}
|
||||
]
|
||||
// 定义emit事件
|
||||
const emit = defineEmits({ successful: null })
|
||||
// 默认是关闭状态
|
||||
let visible = ref(false)
|
||||
const submitLoading = ref(false)
|
||||
|
||||
// 打开抽屉
|
||||
const onOpen = () => {
|
||||
visible.value = true
|
||||
}
|
||||
// 关闭抽屉
|
||||
const onClose = () => {
|
||||
visible.value = false
|
||||
// 关闭导入的提示
|
||||
onImpClose()
|
||||
}
|
||||
// 下载用户导入模板
|
||||
const downloadImportUserTemplate = () => {
|
||||
userApi.userDownloadImportUserTemplate().then((res) => {
|
||||
const blob = new Blob([res.data], { type: 'application/octet-stream;charset=UTF-8' })
|
||||
const contentDisposition = res.headers['content-disposition']
|
||||
const patt = new RegExp('filename=([^;]+\\.[^\\.;]+);*')
|
||||
const $link = document.createElement('a')
|
||||
$link.href = URL.createObjectURL(blob)
|
||||
$link.download = decodeURIComponent(patt.exec(contentDisposition)[1])
|
||||
$link.click()
|
||||
document.body.appendChild($link)
|
||||
document.body.removeChild($link) // 下载完成移除元素
|
||||
window.URL.revokeObjectURL($link.href) // 释放掉blob对象
|
||||
})
|
||||
}
|
||||
// 调用这个函数将子组件的一些数据和方法暴露出去
|
||||
defineExpose({
|
||||
onOpen
|
||||
})
|
||||
</script>
|
|
@ -14,6 +14,36 @@
|
|||
</a-card>
|
||||
</a-col>
|
||||
<a-col :span="19">
|
||||
<a-card :bordered="false" style="margin-bottom: 10px">
|
||||
<a-form ref="searchFormRef" name="advanced_search" class="ant-advanced-search-form" :model="searchFormState">
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="8">
|
||||
<a-form-item name="searchKey" :label="$t('common.searchKey')">
|
||||
<a-input v-model:value="searchFormState.searchKey" placeholder="请输入姓名或账号关键词"></a-input>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item name="userStatus" :label="$t('user.userStatus')">
|
||||
<a-select v-model:value="searchFormState.userStatus" placeholder="请选择状态">
|
||||
<a-select-option v-for="item in statusData" :key="item.dictValue" :value="item.dictValue">{{
|
||||
item.name
|
||||
}}</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-button type="primary" @click="table.refresh(true)">
|
||||
<template #icon><SearchOutlined /></template>
|
||||
{{ $t('common.searchButton') }}
|
||||
</a-button>
|
||||
<a-button class="snowy-buttom-left" @click="() => searchFormRef.resetFields()">
|
||||
<template #icon><redo-outlined /></template>
|
||||
{{ $t('common.resetButton') }}
|
||||
</a-button>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</a-card>
|
||||
<a-card :bordered="false">
|
||||
<s-table
|
||||
ref="table"
|
||||
|
@ -26,43 +56,18 @@
|
|||
:row-selection="options.rowSelection"
|
||||
>
|
||||
<template #operator class="table-operator">
|
||||
<a-form
|
||||
ref="searchFormRef"
|
||||
name="advanced_search"
|
||||
class="ant-advanced-search-form"
|
||||
:model="searchFormState"
|
||||
>
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="6">
|
||||
<a-form-item name="searchKey">
|
||||
<a-input v-model:value="searchFormState.searchKey" placeholder="请输入姓名或账号"></a-input>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-form-item name="userStatus">
|
||||
<a-select v-model:value="searchFormState.userStatus" placeholder="请选择状态">
|
||||
<a-select-option v-for="item in statusData" :key="item.dictValue" :value="item.dictValue">{{
|
||||
item.name
|
||||
}}</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-button type="primary" @click="table.refresh(true)">{{ $t('common.searchButton') }}</a-button>
|
||||
<a-button class="snowy-buttom-left" @click="() => searchFormRef.resetFields()">{{
|
||||
$t('common.resetButton')
|
||||
}}</a-button>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-button type="primary" class="primaryAdd" @click="form.onOpen()">
|
||||
<span>{{ $t('common.addButton') }}{{ $t('model.user') }}</span>
|
||||
</a-button>
|
||||
<a-button danger @click="removeBatchUser()">{{
|
||||
$t('common.batchRemoveButton')
|
||||
}}</a-button>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
<a-button type="primary" class="primaryAdd" @click="form.onOpen()">
|
||||
<template #icon><plus-outlined /></template>
|
||||
<span>{{ $t('common.addButton') }}{{ $t('model.user') }}</span>
|
||||
</a-button>
|
||||
<a-button class="primaryAdd" @click="ImpExpRef.onOpen()">
|
||||
<template #icon><import-outlined /></template>
|
||||
<span>{{ $t('common.imports') }}</span>
|
||||
</a-button>
|
||||
<a-button danger @click="removeBatchUser()">
|
||||
<template #icon><delete-outlined /></template>
|
||||
{{ $t('common.batchRemoveButton') }}
|
||||
</a-button>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex === 'avatar'">
|
||||
|
@ -72,26 +77,44 @@
|
|||
{{ $TOOL.dictTypeData('GENDER', record.gender) }}
|
||||
</template>
|
||||
<template v-if="column.dataIndex === 'userStatus'">
|
||||
<a-switch
|
||||
:loading="loading"
|
||||
:checked="record.userStatus === 'ENABLE'"
|
||||
@change="editStatus(record)"
|
||||
/>
|
||||
<a-switch :loading="loading" :checked="record.userStatus === 'ENABLE'" @change="editStatus(record)" />
|
||||
</template>
|
||||
<template v-if="column.dataIndex === 'action'">
|
||||
<a @click="form.onOpen(record)">{{ $t('common.editButton') }}</a>
|
||||
<a-divider type="vertical" />
|
||||
<a @click="selectRole(record)">角色</a>
|
||||
<a-divider type="vertical" />
|
||||
<a-popconfirm title="确定重置此用户密码?" @confirm="resetPassword(record)">
|
||||
<a>重置密码</a>
|
||||
<a-popconfirm title="确定要删除此用户吗?" placement="topRight" @confirm="removeUser(record)">
|
||||
<a-button type="link" danger size="small">
|
||||
{{ $t('common.removeButton') }}
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
<a-divider type="vertical" />
|
||||
<a-popconfirm title="确定要删除此用户吗?" @confirm="removeUser(record)">
|
||||
<a-button type="link" danger size="small">{{
|
||||
$t('common.removeButton')
|
||||
}}</a-button>
|
||||
</a-popconfirm>
|
||||
<a-dropdown>
|
||||
<a class="ant-dropdown-link">
|
||||
{{ $t('common.more') }}
|
||||
<DownOutlined />
|
||||
</a>
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item>
|
||||
<a-popconfirm title="确定重置此用户密码?" placement="topRight" @confirm="resetPassword(record)">
|
||||
<a>{{ $t('user.resetPassword') }}</a>
|
||||
</a-popconfirm>
|
||||
</a-menu-item>
|
||||
<a-menu-item>
|
||||
<a @click="selectRole(record)">授权角色</a>
|
||||
</a-menu-item>
|
||||
<a-menu-item>
|
||||
<a @click="grantResourceFormRef.onOpen(record)">授权资源</a>
|
||||
</a-menu-item>
|
||||
<a-menu-item>
|
||||
<a @click="grantPermissionFormRef.onOpen(record)">授权权限</a>
|
||||
</a-menu-item>
|
||||
<a-menu-item>
|
||||
<a @click="exportUserInfo(record)">导出信息</a>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
</template>
|
||||
</s-table>
|
||||
|
@ -105,14 +128,20 @@
|
|||
org-url="/sys/user/orgTreeSelector"
|
||||
@onBack="roleBack"
|
||||
/>
|
||||
<ImpExp ref="ImpExpRef" />
|
||||
<grantResourceForm ref="grantResourceFormRef" @successful="table.refresh(true)" />
|
||||
<grantPermissionForm ref="grantPermissionFormRef" @successful="table.refresh(true)" />
|
||||
</template>
|
||||
|
||||
<script setup name="sysUser">
|
||||
import { message, Empty } from 'ant-design-vue'
|
||||
import { getCurrentInstance } from 'vue'
|
||||
import tool from '@/utils/tool'
|
||||
import userApi from '@/api/sys/userApi'
|
||||
import roleSelectorPlus from '@/components/Selector/roleSelectorPlus.vue'
|
||||
import Form from './form.vue'
|
||||
import ImpExp from './impExp.vue'
|
||||
import grantResourceForm from './grantResourceForm.vue'
|
||||
import grantPermissionForm from './grantPermissionForm.vue'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
|
@ -159,11 +188,10 @@
|
|||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
align: 'center',
|
||||
width: '240px'
|
||||
width: '220px'
|
||||
}
|
||||
]
|
||||
const { proxy } = getCurrentInstance()
|
||||
const statusData = proxy.$TOOL.dictTypeList('COMMON_STATUS')
|
||||
const statusData = tool.dictTypeList('COMMON_STATUS')
|
||||
const searchFormRef = ref()
|
||||
let defaultExpandedKeys = ref([])
|
||||
let searchFormState = reactive({})
|
||||
|
@ -172,11 +200,13 @@
|
|||
let selectedRowKeys = ref([])
|
||||
const treeFieldNames = { children: 'children', title: 'name', key: 'id' }
|
||||
let form = ref(null)
|
||||
let RoleSelector = ref()
|
||||
let RoleSelectorPlus = ref()
|
||||
const selectedRecord = ref({})
|
||||
const loading = ref(false)
|
||||
const cardLoading = ref(true)
|
||||
const ImpExpRef = ref()
|
||||
const grantResourceFormRef = ref()
|
||||
const grantPermissionFormRef = ref()
|
||||
|
||||
// 表格查询 返回 Promise 对象
|
||||
const loadData = (parameter) => {
|
||||
|
@ -304,6 +334,24 @@
|
|||
const resetPassword = (record) => {
|
||||
userApi.userResetPassword(record).then(() => {})
|
||||
}
|
||||
// 导出用户信息
|
||||
const exportUserInfo = (record) => {
|
||||
const params = {
|
||||
id: record.id
|
||||
}
|
||||
userApi.userExportUserInfo(params).then((res) => {
|
||||
const blob = new Blob([res.data], { type: 'application/octet-stream;charset=UTF-8' })
|
||||
const contentDisposition = res.headers['content-disposition']
|
||||
const patt = new RegExp('filename=([^;]+\\.[^\\.;]+);*')
|
||||
const $link = document.createElement('a')
|
||||
$link.href = URL.createObjectURL(blob)
|
||||
$link.download = decodeURIComponent(patt.exec(contentDisposition)[1])
|
||||
$link.click()
|
||||
document.body.appendChild($link)
|
||||
document.body.removeChild($link) // 下载完成移除元素
|
||||
window.URL.revokeObjectURL($link.href) // 释放掉blob对象
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
<template>
|
||||
<a-modal
|
||||
v-model:visible="visible"
|
||||
title="选择机构"
|
||||
:width="400"
|
||||
:mask-closable="false"
|
||||
:destroy-on-close="true"
|
||||
@ok="handleOk"
|
||||
@cancel="onClose"
|
||||
>
|
||||
<div class="scopeDefineOrgTreeDiv">
|
||||
<a-tree
|
||||
v-model:expandedKeys="defaultExpandedKeys"
|
||||
v-model:checkedKeys="checkedKeys"
|
||||
:tree-data="treeData"
|
||||
:field-names="treeFieldNames"
|
||||
checkable
|
||||
:selectable="false"
|
||||
@check="treeCheck"
|
||||
>
|
||||
</a-tree>
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script setup="props, context" name="userScopeDefineOrg">
|
||||
import userApi from '@/api/sys/userApi'
|
||||
const visible = ref(false)
|
||||
let defaultExpandedKeys = ref([])
|
||||
let checkedKeys = ref([])
|
||||
const treeData = ref([])
|
||||
|
||||
const resultDataModel = {
|
||||
dataScopeId: '',
|
||||
defineOrgIdData: {
|
||||
scopeCategory: 'SCOPE_ORG_DEFINE',
|
||||
scopeDefineOrgIdList: []
|
||||
}
|
||||
}
|
||||
|
||||
// 打开此界面需要具体某条菜单的id跟选中的
|
||||
const onOpen = (id, checkKeys) => {
|
||||
visible.value = true
|
||||
resultDataModel.dataScopeId = id
|
||||
// const treeData = data.data;
|
||||
userApi.userOrgTreeSelector().then((res) => {
|
||||
if (res !== null) {
|
||||
treeData.value = res
|
||||
// 赋值选中项
|
||||
echoOrgSelectKeys(checkKeys)
|
||||
// 默认展开2级
|
||||
treeData.value.forEach((item) => {
|
||||
// 因为0的顶级
|
||||
if (item.parentId === '0') {
|
||||
defaultExpandedKeys.value.push(item.id)
|
||||
// 取到下级ID
|
||||
if (item.children) {
|
||||
item.children.forEach((items) => {
|
||||
defaultExpandedKeys.value.push(items.id)
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
const onClose = () => {
|
||||
visible.value = false
|
||||
}
|
||||
// 回显机构的选中项
|
||||
const echoOrgSelectKeys = (checkKeys) => {
|
||||
checkedKeys.value = []
|
||||
if (checkKeys && checkKeys.length > 0) {
|
||||
checkKeys
|
||||
.toString()
|
||||
.split(',')
|
||||
.forEach((key) => {
|
||||
checkedKeys.value.push(key)
|
||||
})
|
||||
}
|
||||
}
|
||||
// 替换treeNode 中 title,key,children
|
||||
const treeFieldNames = { children: 'children', title: 'name', key: 'id' }
|
||||
|
||||
// 选中触发
|
||||
const treeCheck = (checkedKeys) => {
|
||||
resultDataModel.defineOrgIdData.scopeDefineOrgIdList = checkedKeys
|
||||
}
|
||||
// 定义emit事件
|
||||
const emit = defineEmits({
|
||||
click: null
|
||||
})
|
||||
const handleOk = () => {
|
||||
emit('click', resultDataModel)
|
||||
visible.value = false
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
onOpen
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
// 穿梭框宽度重写
|
||||
.ant-transfer-list {
|
||||
width: 220px !important;
|
||||
}
|
||||
.scopeDefineOrgTreeDiv {
|
||||
max-height: 450px;
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
|
@ -64,7 +64,7 @@
|
|||
|
||||
<script setup>
|
||||
import { onMounted } from 'vue'
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useRoute } from 'vue-router'
|
||||
import tool from '@/utils/tool'
|
||||
import store from '@/store'
|
||||
import userCenterApi from '@/api/sys/userCenterApi'
|
||||
|
@ -108,12 +108,12 @@
|
|||
const onTabChange = (key) => {
|
||||
noTitleKey.value = key
|
||||
}
|
||||
const Route = useRoute();
|
||||
const Route = useRoute()
|
||||
onMounted(() => {
|
||||
if (Route.query.tab) {
|
||||
noTitleKey.value = Route.query.tab
|
||||
}
|
||||
});
|
||||
})
|
||||
// 头像裁剪图片回调
|
||||
const cropUploadSuccess = (data) => {
|
||||
// 转换为file类型
|
||||
|
|
Loading…
Reference in New Issue