【新增】新增用户管理的授权角色与授权资源,新增前端导入功能,新增导出某用户的模板信息,优化部分代码
This commit is contained in:
parent
e4a6b7aad0
commit
b87a7f5d65
@ -69,5 +69,43 @@ export default {
|
|||||||
// 给用户授权角色
|
// 给用户授权角色
|
||||||
grantRole(data) {
|
grantRole(data) {
|
||||||
return request('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',
|
editButton: 'edit',
|
||||||
removeButton: 'delete',
|
removeButton: 'delete',
|
||||||
batchRemoveButton: 'batch Remove',
|
batchRemoveButton: 'batch Remove',
|
||||||
detailButton: 'detail'
|
detailButton: 'detail',
|
||||||
|
searchKey: 'Search Key',
|
||||||
|
imports: 'import',
|
||||||
|
more: 'More'
|
||||||
},
|
},
|
||||||
model: {
|
model: {
|
||||||
user: 'user',
|
user: 'user',
|
||||||
@ -50,5 +53,10 @@ export default {
|
|||||||
emailCodePlaceholder: 'Please input a Email code',
|
emailCodePlaceholder: 'Please input a Email code',
|
||||||
restPhoneType: 'For phone rest',
|
restPhoneType: 'For phone rest',
|
||||||
restEmailType: 'For email rest'
|
restEmailType: 'For email rest'
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
userStatus: 'User Status',
|
||||||
|
resetPassword: 'Reset Password',
|
||||||
|
role: 'Role',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,10 @@ export default {
|
|||||||
editButton: '编辑',
|
editButton: '编辑',
|
||||||
removeButton: '删除',
|
removeButton: '删除',
|
||||||
batchRemoveButton: '批量删除',
|
batchRemoveButton: '批量删除',
|
||||||
detailButton: '详情'
|
detailButton: '详情',
|
||||||
|
searchKey: '关键词',
|
||||||
|
imports: '导入',
|
||||||
|
more: '更多',
|
||||||
},
|
},
|
||||||
model: {
|
model: {
|
||||||
user: '用户',
|
user: '用户',
|
||||||
@ -52,5 +55,10 @@ export default {
|
|||||||
emailCodePlaceholder: '请输入邮件验证码',
|
emailCodePlaceholder: '请输入邮件验证码',
|
||||||
restPhoneType: '手机号找回',
|
restPhoneType: '手机号找回',
|
||||||
restEmailType: '邮箱找回'
|
restEmailType: '邮箱找回'
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
userStatus: '用户状态',
|
||||||
|
resetPassword: '重置密码',
|
||||||
|
role: '角色',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,16 +109,14 @@
|
|||||||
})
|
})
|
||||||
// 验证并提交数据
|
// 验证并提交数据
|
||||||
const onSubmit = () => {
|
const onSubmit = () => {
|
||||||
formRef.value
|
formRef.value.validate().then(() => {
|
||||||
.validate()
|
submitLoading.value = true
|
||||||
.then(() => {
|
positionApi.submitForm(formData.value, !formData.value.id).then(() => {
|
||||||
submitLoading.value = true
|
visible = false
|
||||||
positionApi.submitForm(formData.value, !formData.value.id).then(() => {
|
submitLoading.value = false
|
||||||
visible = false
|
emit('successful')
|
||||||
submitLoading.value = false
|
|
||||||
emit('successful')
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
// 调用这个函数将子组件的一些数据和方法暴露出去
|
// 调用这个函数将子组件的一些数据和方法暴露出去
|
||||||
defineExpose({
|
defineExpose({
|
||||||
|
@ -57,20 +57,18 @@
|
|||||||
|
|
||||||
// 验证并提交数据
|
// 验证并提交数据
|
||||||
const onSubmit = () => {
|
const onSubmit = () => {
|
||||||
formRef.value
|
formRef.value.validate().then(() => {
|
||||||
.validate()
|
const defParam = {
|
||||||
.then(() => {
|
category: 'BUTTON',
|
||||||
const defParam = {
|
// module: recordData.value.module,
|
||||||
category: 'BUTTON',
|
parentId: recordData.value.id
|
||||||
// module: recordData.value.module,
|
}
|
||||||
parentId: recordData.value.id
|
const param = Object.assign(defParam, formData.value)
|
||||||
}
|
buttonApi.submitForm(param, !formData.value.id).then((res) => {
|
||||||
const param = Object.assign(defParam, formData.value)
|
onClose()
|
||||||
buttonApi.submitForm(param, !formData.value.id).then((res) => {
|
emit('successful')
|
||||||
onClose()
|
|
||||||
emit('successful')
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 调用这个函数将子组件的一些数据和方法暴露出去
|
// 调用这个函数将子组件的一些数据和方法暴露出去
|
||||||
|
@ -63,19 +63,17 @@
|
|||||||
}
|
}
|
||||||
// 验证并提交数据
|
// 验证并提交数据
|
||||||
const onSubmit = () => {
|
const onSubmit = () => {
|
||||||
formRef.value
|
formRef.value.validate().then(() => {
|
||||||
.validate()
|
const defParam = {
|
||||||
.then(() => {
|
category: 'FIELD',
|
||||||
const defParam = {
|
parentId: recordData.value.id
|
||||||
category: 'FIELD',
|
}
|
||||||
parentId: recordData.value.id
|
const param = Object.assign(defParam, formData.value)
|
||||||
}
|
fieldApi.submitForm(param, !formData.value.id).then((res) => {
|
||||||
const param = Object.assign(defParam, formData.value)
|
onClose()
|
||||||
fieldApi.submitForm(param, !formData.value.id).then((res) => {
|
emit('successful')
|
||||||
onClose()
|
|
||||||
emit('successful')
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
// 调用这个函数将子组件的一些数据和方法暴露出去
|
// 调用这个函数将子组件的一些数据和方法暴露出去
|
||||||
defineExpose({
|
defineExpose({
|
||||||
|
@ -10,21 +10,16 @@
|
|||||||
>
|
>
|
||||||
<a-form ref="formRef" :model="formData" :rules="formRules" layout="vertical">
|
<a-form ref="formRef" :model="formData" :rules="formRules" layout="vertical">
|
||||||
<a-form-item label="显示名称:" name="title">
|
<a-form-item label="显示名称:" name="title">
|
||||||
<span>{{formData.title}}</span>
|
<span>{{ formData.title }}</span>
|
||||||
</a-form-item>
|
</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-group v-model:value="formData.module" button-style="solid">
|
||||||
<a-radio-button
|
<a-radio-button v-for="module in moduleTypeList" :key="module.id" :value="module.id">
|
||||||
v-for="module in moduleTypeList"
|
|
||||||
:key="module.id"
|
|
||||||
:value="module.id"
|
|
||||||
>
|
|
||||||
<component :is="module.icon" />
|
<component :is="module.icon" />
|
||||||
{{ module.title }}</a-radio-button
|
{{ module.title }}</a-radio-button
|
||||||
>
|
>
|
||||||
</a-radio-group>
|
</a-radio-group>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
</a-form>
|
</a-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<a-button style="margin-right: 8px" @click="onClose">关闭</a-button>
|
<a-button style="margin-right: 8px" @click="onClose">关闭</a-button>
|
||||||
@ -65,26 +60,27 @@
|
|||||||
|
|
||||||
// 默认要校验的
|
// 默认要校验的
|
||||||
const formRules = {
|
const formRules = {
|
||||||
module: [required('请选择所属目录')],
|
module: [required('请选择所属目录')]
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证并提交数据
|
// 验证并提交数据
|
||||||
const onSubmit = () => {
|
const onSubmit = () => {
|
||||||
formRef.value
|
formRef.value.validate().then(() => {
|
||||||
.validate()
|
const param = {
|
||||||
.then(() => {
|
id: formData.value.id,
|
||||||
const param = {
|
module: formData.value.module
|
||||||
id: formData.value.id,
|
}
|
||||||
module: formData.value.module
|
submitLoading.value = true
|
||||||
}
|
menuApi
|
||||||
submitLoading.value = true
|
.menuChangeModule(param)
|
||||||
menuApi.menuChangeModule(param).then(() => {
|
.then(() => {
|
||||||
submitLoading.value = false
|
submitLoading.value = false
|
||||||
emit('successful')
|
emit('successful')
|
||||||
}).finally(() => {
|
})
|
||||||
|
.finally(() => {
|
||||||
visible = false
|
visible = false
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// 调用这个函数将子组件的一些数据和方法暴露出去
|
// 调用这个函数将子组件的一些数据和方法暴露出去
|
||||||
defineExpose({
|
defineExpose({
|
||||||
|
@ -199,7 +199,8 @@
|
|||||||
onClose()
|
onClose()
|
||||||
emit('successful')
|
emit('successful')
|
||||||
})
|
})
|
||||||
}).finally(() => {
|
})
|
||||||
|
.finally(() => {
|
||||||
submitLoading.value = false
|
submitLoading.value = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -45,11 +45,11 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #bodyCell="{ column, record }">
|
<template #bodyCell="{ column, record }">
|
||||||
<template v-if="column.dataIndex === 'path'">
|
<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>
|
<span v-else>-</span>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="column.dataIndex === 'component'">
|
<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>
|
<span v-else>-</span>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="column.dataIndex === 'icon'">
|
<template v-if="column.dataIndex === 'icon'">
|
||||||
@ -101,7 +101,7 @@
|
|||||||
</s-table>
|
</s-table>
|
||||||
</a-card>
|
</a-card>
|
||||||
<Form ref="form" @successful="table.refresh(true)" />
|
<Form ref="form" @successful="table.refresh(true)" />
|
||||||
<changeModuleForm ref="changeModuleFormRef" @successful="table.refresh(true)"/>
|
<changeModuleForm ref="changeModuleFormRef" @successful="table.refresh(true)" />
|
||||||
<Button ref="button" />
|
<Button ref="button" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -74,14 +74,12 @@
|
|||||||
|
|
||||||
// 验证并提交数据
|
// 验证并提交数据
|
||||||
const onSubmit = () => {
|
const onSubmit = () => {
|
||||||
formRef.value
|
formRef.value.validate().then(() => {
|
||||||
.validate()
|
moduleApi.submitForm(formData.value, !formData.value.id).then(() => {
|
||||||
.then(() => {
|
onClose()
|
||||||
moduleApi.submitForm(formData.value, !formData.value.id).then(() => {
|
emit('successful')
|
||||||
onClose()
|
|
||||||
emit('successful')
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 调用这个函数将子组件的一些数据和方法暴露出去
|
// 调用这个函数将子组件的一些数据和方法暴露出去
|
||||||
|
@ -143,18 +143,19 @@
|
|||||||
|
|
||||||
// 验证并提交数据
|
// 验证并提交数据
|
||||||
const onSubmit = () => {
|
const onSubmit = () => {
|
||||||
formRef.value
|
formRef.value.validate().then(() => {
|
||||||
.validate()
|
const param = parameterChanges(formData.value)
|
||||||
.then(() => {
|
submitLoading.value = true
|
||||||
const param = parameterChanges(formData.value)
|
spaApi
|
||||||
submitLoading.value = true
|
.submitForm(param, !param.id)
|
||||||
spaApi.submitForm(param, !param.id).then(() => {
|
.then(() => {
|
||||||
visible = false
|
visible = false
|
||||||
emit('successful')
|
emit('successful')
|
||||||
}).finally(() => {
|
})
|
||||||
|
.finally(() => {
|
||||||
submitLoading.value = false
|
submitLoading.value = false
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const parameterChanges = (data) => {
|
const parameterChanges = (data) => {
|
||||||
if (!data.component) {
|
if (!data.component) {
|
||||||
|
@ -102,20 +102,18 @@
|
|||||||
})
|
})
|
||||||
// 验证并提交数据
|
// 验证并提交数据
|
||||||
const onSubmit = () => {
|
const onSubmit = () => {
|
||||||
formRef.value
|
formRef.value.validate().then(() => {
|
||||||
.validate()
|
submitLoading.value = true
|
||||||
.then(() => {
|
roleApi
|
||||||
submitLoading.value = true
|
.submitForm(formData.value, !formData.value.id)
|
||||||
roleApi
|
.then(() => {
|
||||||
.submitForm(formData.value, !formData.value.id)
|
visible = false
|
||||||
.then(() => {
|
emit('successful')
|
||||||
visible = false
|
})
|
||||||
emit('successful')
|
.finally(() => {
|
||||||
})
|
submitLoading.value = false
|
||||||
.finally(() => {
|
})
|
||||||
submitLoading.value = false
|
})
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
// 调用这个函数将子组件的一些数据和方法暴露出去
|
// 调用这个函数将子组件的一些数据和方法暴露出去
|
||||||
defineExpose({
|
defineExpose({
|
||||||
|
@ -32,16 +32,16 @@
|
|||||||
|
|
||||||
<template v-if="column.dataIndex === 'title'">
|
<template v-if="column.dataIndex === 'title'">
|
||||||
<a-checkbox :checked="record.nameCheck" @update:checked="(val) => changeSub(record, val)">{{
|
<a-checkbox :checked="record.nameCheck" @update:checked="(val) => changeSub(record, val)">{{
|
||||||
record.title
|
record.title
|
||||||
}}</a-checkbox>
|
}}</a-checkbox>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="column.dataIndex === 'button'">
|
<template v-if="column.dataIndex === 'button'">
|
||||||
<template v-if="record.button.length > 0">
|
<template v-if="record.button.length > 0">
|
||||||
<template v-for="(item, index) in record.button" :key="item.id">
|
<template v-for="(item, index) in record.button" :key="item.id">
|
||||||
<a-checkbox v-model:checked="item.check" @change="(evt) => changeChildCheckBox(record, evt)">{{
|
<a-checkbox v-model:checked="item.check" @change="(evt) => changeChildCheckBox(record, evt)">{{
|
||||||
item.title
|
item.title
|
||||||
}}</a-checkbox>
|
}}</a-checkbox>
|
||||||
<br v-if="(index + 1) % 5 === 0" />
|
<br v-if="(index + 1) % 5 === 0" />
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
@ -225,7 +225,7 @@
|
|||||||
}
|
}
|
||||||
// 可伸缩列
|
// 可伸缩列
|
||||||
const handleResizeColumn = (w, col) => {
|
const handleResizeColumn = (w, col) => {
|
||||||
col.width = w;
|
col.width = w
|
||||||
}
|
}
|
||||||
// 删除
|
// 删除
|
||||||
const removeOrg = (record) => {
|
const removeOrg = (record) => {
|
||||||
|
337
snowy-admin-web/src/views/sys/user/grantPermissionForm.vue
Normal file
337
snowy-admin-web/src/views/sys/user/grantPermissionForm.vue
Normal file
@ -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>
|
315
snowy-admin-web/src/views/sys/user/grantResourceForm.vue
Normal file
315
snowy-admin-web/src/views/sys/user/grantResourceForm.vue
Normal file
@ -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>
|
112
snowy-admin-web/src/views/sys/user/impExp.vue
Normal file
112
snowy-admin-web/src/views/sys/user/impExp.vue
Normal file
@ -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-card>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="19">
|
<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">
|
<a-card :bordered="false">
|
||||||
<s-table
|
<s-table
|
||||||
ref="table"
|
ref="table"
|
||||||
@ -26,43 +56,18 @@
|
|||||||
:row-selection="options.rowSelection"
|
:row-selection="options.rowSelection"
|
||||||
>
|
>
|
||||||
<template #operator class="table-operator">
|
<template #operator class="table-operator">
|
||||||
<a-form
|
<a-button type="primary" class="primaryAdd" @click="form.onOpen()">
|
||||||
ref="searchFormRef"
|
<template #icon><plus-outlined /></template>
|
||||||
name="advanced_search"
|
<span>{{ $t('common.addButton') }}{{ $t('model.user') }}</span>
|
||||||
class="ant-advanced-search-form"
|
</a-button>
|
||||||
:model="searchFormState"
|
<a-button class="primaryAdd" @click="ImpExpRef.onOpen()">
|
||||||
>
|
<template #icon><import-outlined /></template>
|
||||||
<a-row :gutter="24">
|
<span>{{ $t('common.imports') }}</span>
|
||||||
<a-col :span="6">
|
</a-button>
|
||||||
<a-form-item name="searchKey">
|
<a-button danger @click="removeBatchUser()">
|
||||||
<a-input v-model:value="searchFormState.searchKey" placeholder="请输入姓名或账号"></a-input>
|
<template #icon><delete-outlined /></template>
|
||||||
</a-form-item>
|
{{ $t('common.batchRemoveButton') }}
|
||||||
</a-col>
|
</a-button>
|
||||||
<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>
|
|
||||||
</template>
|
</template>
|
||||||
<template #bodyCell="{ column, record }">
|
<template #bodyCell="{ column, record }">
|
||||||
<template v-if="column.dataIndex === 'avatar'">
|
<template v-if="column.dataIndex === 'avatar'">
|
||||||
@ -72,26 +77,44 @@
|
|||||||
{{ $TOOL.dictTypeData('GENDER', record.gender) }}
|
{{ $TOOL.dictTypeData('GENDER', record.gender) }}
|
||||||
</template>
|
</template>
|
||||||
<template v-if="column.dataIndex === 'userStatus'">
|
<template v-if="column.dataIndex === 'userStatus'">
|
||||||
<a-switch
|
<a-switch :loading="loading" :checked="record.userStatus === 'ENABLE'" @change="editStatus(record)" />
|
||||||
:loading="loading"
|
|
||||||
:checked="record.userStatus === 'ENABLE'"
|
|
||||||
@change="editStatus(record)"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
<template v-if="column.dataIndex === 'action'">
|
<template v-if="column.dataIndex === 'action'">
|
||||||
<a @click="form.onOpen(record)">{{ $t('common.editButton') }}</a>
|
<a @click="form.onOpen(record)">{{ $t('common.editButton') }}</a>
|
||||||
<a-divider type="vertical" />
|
<a-divider type="vertical" />
|
||||||
<a @click="selectRole(record)">角色</a>
|
<a-popconfirm title="确定要删除此用户吗?" placement="topRight" @confirm="removeUser(record)">
|
||||||
<a-divider type="vertical" />
|
<a-button type="link" danger size="small">
|
||||||
<a-popconfirm title="确定重置此用户密码?" @confirm="resetPassword(record)">
|
{{ $t('common.removeButton') }}
|
||||||
<a>重置密码</a>
|
</a-button>
|
||||||
</a-popconfirm>
|
</a-popconfirm>
|
||||||
<a-divider type="vertical" />
|
<a-divider type="vertical" />
|
||||||
<a-popconfirm title="确定要删除此用户吗?" @confirm="removeUser(record)">
|
<a-dropdown>
|
||||||
<a-button type="link" danger size="small">{{
|
<a class="ant-dropdown-link">
|
||||||
$t('common.removeButton')
|
{{ $t('common.more') }}
|
||||||
}}</a-button>
|
<DownOutlined />
|
||||||
</a-popconfirm>
|
</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>
|
||||||
</template>
|
</template>
|
||||||
</s-table>
|
</s-table>
|
||||||
@ -105,14 +128,20 @@
|
|||||||
org-url="/sys/user/orgTreeSelector"
|
org-url="/sys/user/orgTreeSelector"
|
||||||
@onBack="roleBack"
|
@onBack="roleBack"
|
||||||
/>
|
/>
|
||||||
|
<ImpExp ref="ImpExpRef" />
|
||||||
|
<grantResourceForm ref="grantResourceFormRef" @successful="table.refresh(true)" />
|
||||||
|
<grantPermissionForm ref="grantPermissionFormRef" @successful="table.refresh(true)" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup name="sysUser">
|
<script setup name="sysUser">
|
||||||
import { message, Empty } from 'ant-design-vue'
|
import { message, Empty } from 'ant-design-vue'
|
||||||
import { getCurrentInstance } from 'vue'
|
import tool from '@/utils/tool'
|
||||||
import userApi from '@/api/sys/userApi'
|
import userApi from '@/api/sys/userApi'
|
||||||
import roleSelectorPlus from '@/components/Selector/roleSelectorPlus.vue'
|
import roleSelectorPlus from '@/components/Selector/roleSelectorPlus.vue'
|
||||||
import Form from './form.vue'
|
import Form from './form.vue'
|
||||||
|
import ImpExp from './impExp.vue'
|
||||||
|
import grantResourceForm from './grantResourceForm.vue'
|
||||||
|
import grantPermissionForm from './grantPermissionForm.vue'
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
@ -159,11 +188,10 @@
|
|||||||
title: '操作',
|
title: '操作',
|
||||||
dataIndex: 'action',
|
dataIndex: 'action',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
width: '240px'
|
width: '220px'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
const { proxy } = getCurrentInstance()
|
const statusData = tool.dictTypeList('COMMON_STATUS')
|
||||||
const statusData = proxy.$TOOL.dictTypeList('COMMON_STATUS')
|
|
||||||
const searchFormRef = ref()
|
const searchFormRef = ref()
|
||||||
let defaultExpandedKeys = ref([])
|
let defaultExpandedKeys = ref([])
|
||||||
let searchFormState = reactive({})
|
let searchFormState = reactive({})
|
||||||
@ -172,11 +200,13 @@
|
|||||||
let selectedRowKeys = ref([])
|
let selectedRowKeys = ref([])
|
||||||
const treeFieldNames = { children: 'children', title: 'name', key: 'id' }
|
const treeFieldNames = { children: 'children', title: 'name', key: 'id' }
|
||||||
let form = ref(null)
|
let form = ref(null)
|
||||||
let RoleSelector = ref()
|
|
||||||
let RoleSelectorPlus = ref()
|
let RoleSelectorPlus = ref()
|
||||||
const selectedRecord = ref({})
|
const selectedRecord = ref({})
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const cardLoading = ref(true)
|
const cardLoading = ref(true)
|
||||||
|
const ImpExpRef = ref()
|
||||||
|
const grantResourceFormRef = ref()
|
||||||
|
const grantPermissionFormRef = ref()
|
||||||
|
|
||||||
// 表格查询 返回 Promise 对象
|
// 表格查询 返回 Promise 对象
|
||||||
const loadData = (parameter) => {
|
const loadData = (parameter) => {
|
||||||
@ -304,6 +334,24 @@
|
|||||||
const resetPassword = (record) => {
|
const resetPassword = (record) => {
|
||||||
userApi.userResetPassword(record).then(() => {})
|
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>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
112
snowy-admin-web/src/views/sys/user/scopeDefineOrg.vue
Normal file
112
snowy-admin-web/src/views/sys/user/scopeDefineOrg.vue
Normal file
@ -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>
|
<script setup>
|
||||||
import { onMounted } from 'vue'
|
import { onMounted } from 'vue'
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router'
|
||||||
import tool from '@/utils/tool'
|
import tool from '@/utils/tool'
|
||||||
import store from '@/store'
|
import store from '@/store'
|
||||||
import userCenterApi from '@/api/sys/userCenterApi'
|
import userCenterApi from '@/api/sys/userCenterApi'
|
||||||
@ -108,12 +108,12 @@
|
|||||||
const onTabChange = (key) => {
|
const onTabChange = (key) => {
|
||||||
noTitleKey.value = key
|
noTitleKey.value = key
|
||||||
}
|
}
|
||||||
const Route = useRoute();
|
const Route = useRoute()
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (Route.query.tab) {
|
if (Route.query.tab) {
|
||||||
noTitleKey.value = Route.query.tab
|
noTitleKey.value = Route.query.tab
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
// 头像裁剪图片回调
|
// 头像裁剪图片回调
|
||||||
const cropUploadSuccess = (data) => {
|
const cropUploadSuccess = (data) => {
|
||||||
// 转换为file类型
|
// 转换为file类型
|
||||||
|
Loading…
x
Reference in New Issue
Block a user