467 lines
12 KiB
Vue
467 lines
12 KiB
Vue
<template>
|
||
<div class="perm-box">
|
||
<div class="header-actions">
|
||
<Icon class="icon" icon="ion:add-outline" width="23" height="23" @click="openAddRole" />
|
||
<Icon class="icon" icon="ion:reload" width="18" height="18" @click="refresh" />
|
||
</div>
|
||
<div class="loading" v-if="tableLoading">
|
||
<loading/>
|
||
</div>
|
||
<el-scrollbar v-else class="perm-scrollbar">
|
||
<div>
|
||
<el-table
|
||
:data="roles"
|
||
style="width: 100%;"
|
||
>
|
||
<el-table-column width="10" />
|
||
<el-table-column :label="$t('roleName')" prop="name" :min-width="roleWidth">
|
||
<template #default="props">
|
||
<div class="role-name">
|
||
<span >{{props.row.name}}</span>
|
||
<span v-if="props.row.isDefault"><el-tag class="def-tag" >{{$t('default')}}</el-tag></span>
|
||
</div>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column :label="$t('order')" :width="sortWidth" prop="sort"/>
|
||
<el-table-column v-if="desShow" :label="$t('description')" min-width="200" prop="description" >
|
||
<template #default="props">
|
||
<div class="description">
|
||
<span >{{props.row.description}}</span>
|
||
</div>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column :label="$t('tabSetting')" :width="settingWidth">
|
||
<template #default="props">
|
||
<el-dropdown trigger="click">
|
||
<el-button size="small" type="primary">{{$t('action')}}</el-button>
|
||
<template #dropdown>
|
||
<el-dropdown-menu>
|
||
<el-dropdown-item @click="openRoleSet(props.row)">{{$t('change')}}</el-dropdown-item>
|
||
<el-dropdown-item @click="setDef(props.row)">{{$t('default')}}</el-dropdown-item>
|
||
<el-dropdown-item @click="delRole(props.row)">{{$t('delete')}}</el-dropdown-item>
|
||
</el-dropdown-menu>
|
||
</template>
|
||
</el-dropdown>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</div>
|
||
</el-scrollbar>
|
||
<el-dialog top="5vh" class="dialog" v-model="roleFormShow" :title="dialogType.title" @closed="resetForm">
|
||
<div class="dialog-box">
|
||
<el-input class="dialog-input" v-model="form.name" type="text" :maxlength="12" :placeholder="$t('roleName')" autocomplete="off" />
|
||
<el-input class="dialog-input" v-model="form.description" :maxlength="30" type="text" :placeholder="$t('description')" autocomplete="off" />
|
||
<el-input-tag class="dialog-input-tag" tag-type="warning" :class="form.banEmail.length === 0 ? 'dialog-input' : '' " v-model="form.banEmail" @add-tag="banEmailAddTag" type="text" :placeholder="$t('emailBlock')" autocomplete="off" />
|
||
<el-radio-group class="dialog-radio" v-model="form.banEmailType" v-if="form.banEmail.length > 0">
|
||
<el-radio :label="$t('removeAll')" :value="0" />
|
||
<el-radio :label="$t('removeBody')" :value="1" />
|
||
</el-radio-group>
|
||
<div class="dialog-input">
|
||
<el-input-number :placeholder="$t('order')" :min="0" :max="9999" v-model.number="form.sort" controls-position="right" autocomplete="off" />
|
||
</div>
|
||
<el-radio-group v-model="expand" size="small" @change="expandChange" class="perm-expand">
|
||
<el-radio-button :label="$t('expand')" :value="true" />
|
||
<el-radio-button :label="$t('collapse')" :value="false" />
|
||
</el-radio-group>
|
||
<el-tree
|
||
:expand-on-click-node="false"
|
||
:check-on-click-node="false"
|
||
ref="tree"
|
||
:data="treeList"
|
||
show-checkbox
|
||
node-key="permId"
|
||
:default-expand-all="expand"
|
||
:props="{
|
||
label: 'name'
|
||
}"
|
||
>
|
||
<template #default="{ node, data }">
|
||
<div>
|
||
<span>{{node.label}}</span>
|
||
<span class="send-num" v-if="data.permKey === 'email:send'" @click.stop>
|
||
<el-input-number v-model="form.sendCount" controls-position="right" :max="99999" size="small" :placeholder="$t('total')" >
|
||
</el-input-number>
|
||
<el-select v-model="form.sendType" placeholder="Select" size="small" style="width: 65px;margin-left: 5px;">
|
||
<el-option :label="$t('total')" value="count" />
|
||
<el-option :label="$t('daily')" value="day" />
|
||
<el-option :label="$t('btnBan')" value="ban" />
|
||
</el-select>
|
||
</span>
|
||
<span class="send-num" v-if="data.permKey === 'account:add'" @click.stop>
|
||
<el-input-number v-model="form.accountCount" controls-position="right" :min="0" :max="99999" size="small" :placeholder="$t('total')" >
|
||
</el-input-number>
|
||
</span>
|
||
</div>
|
||
</template>
|
||
</el-tree>
|
||
<el-button class="btn" type="primary" :loading="permLoading" @click="roleFormClick"
|
||
>{{$t('save')}}
|
||
</el-button>
|
||
</div>
|
||
</el-dialog>
|
||
</div>
|
||
</template>
|
||
<script setup>
|
||
import {Icon} from "@iconify/vue";
|
||
import {defineOptions, nextTick, reactive, ref} from "vue";
|
||
import {roleAdd, roleDelete, rolePermTree, roleRoleList, roleSet, roleSetDef} from "@/request/role.js";
|
||
import loading from '@/components/loading/index.vue';
|
||
import {useRoleStore} from "@/store/role.js";
|
||
import {useUserStore} from "@/store/user.js";
|
||
import {isEmail} from "@/utils/verify-utils.js";
|
||
import {useI18n} from "vue-i18n";
|
||
|
||
defineOptions({
|
||
name: 'role'
|
||
})
|
||
|
||
const { t, locale } = useI18n();
|
||
const userStore = useUserStore();
|
||
const roleStore = useRoleStore();
|
||
const roleFormShow = ref(false)
|
||
const treeList = reactive([])
|
||
const roles = ref([])
|
||
const tree = ref({})
|
||
const permLoading = ref(false)
|
||
const tableLoading = ref(false)
|
||
const desShow = ref(true)
|
||
const settingWidth = ref(null)
|
||
const sortWidth = ref(null)
|
||
const roleWidth = ref(200)
|
||
|
||
const dialogType = reactive({
|
||
title: '',
|
||
type: ''
|
||
})
|
||
|
||
const form = reactive({
|
||
name: null,
|
||
description: null,
|
||
banEmail: [],
|
||
banEmailType: 0,
|
||
sendType: 'count',
|
||
sendCount: 0,
|
||
accountCount: 0,
|
||
sort: 0,
|
||
isDefault: 0,
|
||
})
|
||
|
||
const expand = ref(false)
|
||
|
||
let chooseRole = {}
|
||
|
||
refresh()
|
||
|
||
rolePermTree().then(tree => {
|
||
treeList.push(...tree)
|
||
})
|
||
|
||
function banEmailAddTag(val) {
|
||
const emails = Array.from(new Set(
|
||
val.split(/[,,]/).map(item => item.trim()).filter(item => item)
|
||
));
|
||
|
||
form.banEmail.splice(form.banEmail.length - 1, 1)
|
||
|
||
emails.forEach(email => {
|
||
if (isEmail(email) && !form.banEmail.includes(email)) {
|
||
form.banEmail.push(email)
|
||
}
|
||
})
|
||
}
|
||
|
||
|
||
function roleFormClick() {
|
||
if (dialogType.type === 'add') {
|
||
addRole()
|
||
} else {
|
||
setRole()
|
||
}
|
||
}
|
||
|
||
function setDef(role) {
|
||
roleSetDef(role.roleId).then(() => {
|
||
ElMessage({
|
||
message: t('changSuccessMsg'),
|
||
type: "success",
|
||
plain: true
|
||
})
|
||
getRoleList()
|
||
})
|
||
}
|
||
|
||
function delRole(role) {
|
||
ElMessageBox.confirm(t('delConfirm', {msg: role.name}), {
|
||
confirmButtonText: t('confirm'),
|
||
cancelButtonText: t('confirm'),
|
||
type: 'warning'
|
||
}).then(() => {
|
||
roleDelete(role.roleId).then(() => {
|
||
ElMessage({
|
||
message: t('copySuccessMsg'),
|
||
type: "success",
|
||
plain: true
|
||
})
|
||
getRoleList()
|
||
userStore.refreshUserList()
|
||
roleStore.refreshSelect()
|
||
})
|
||
});
|
||
}
|
||
|
||
function expandChange(e) {
|
||
if (e) {
|
||
const nodes = tree.value?.store.nodesMap;
|
||
for (const key in nodes) {
|
||
nodes[key].expanded = true;
|
||
}
|
||
} else {
|
||
const nodes = tree.value?.store.nodesMap;
|
||
for (const key in nodes) {
|
||
nodes[key].expanded = false;
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
function setRole() {
|
||
|
||
if (!form.name) {
|
||
ElMessage({
|
||
message: t('emptyRoleNameMsg'),
|
||
type: "error",
|
||
plain: true
|
||
})
|
||
return
|
||
}
|
||
|
||
const params = {...form,roleId: chooseRole.roleId}
|
||
const checkedId = tree.value.getCheckedKeys()
|
||
const halfId = tree.value.getHalfCheckedKeys()
|
||
params.permIds = [...checkedId, ...halfId]
|
||
|
||
permLoading.value = true
|
||
roleSet(params).then(() => {
|
||
ElMessage({
|
||
message: t('changSuccessMsg'),
|
||
type: "success",
|
||
plain: true
|
||
})
|
||
|
||
const names = roles.value.map(role => role.name)
|
||
|
||
if (!names.includes(params.name)) {
|
||
roleStore.refreshSelect()
|
||
}
|
||
|
||
roleFormShow.value = false
|
||
getRoleList()
|
||
}).finally(() => {
|
||
permLoading.value = false
|
||
})
|
||
}
|
||
|
||
function resetForm() {
|
||
form.name = null
|
||
form.description = null
|
||
form.sort = 0
|
||
form.sendType = 'count'
|
||
form.sendCount = 0
|
||
form.accountCount = 0
|
||
form.banEmail = []
|
||
form.banEmailType = 0
|
||
tree.value.setCheckedKeys([])
|
||
}
|
||
|
||
function openRoleSet(role) {
|
||
chooseRole = role
|
||
dialogType.title = t('changeRoleTitle')
|
||
dialogType.type = 'set'
|
||
roleFormShow.value = true
|
||
form.sort = role.sort
|
||
form.name = role.name
|
||
form.description = role.description
|
||
form.sendType = role.sendType
|
||
form.sendCount = role.sendCount
|
||
form.accountCount = role.accountCount
|
||
form.banEmail = role.banEmail
|
||
nextTick(() => {
|
||
tree.value.setCheckedKeys(role.permIds)
|
||
})
|
||
}
|
||
|
||
|
||
function openAddRole() {
|
||
dialogType.title = t('addRoleTitle')
|
||
dialogType.type = 'add'
|
||
roleFormShow.value = true
|
||
}
|
||
|
||
function addRole() {
|
||
const params = {...form}
|
||
const checkedId = tree.value.getCheckedKeys()
|
||
const halfId = tree.value.getHalfCheckedKeys()
|
||
params.permIds = [...checkedId, ...halfId]
|
||
|
||
permLoading.value = true
|
||
roleAdd(params).then(() => {
|
||
ElMessage({
|
||
message: t('addSuccessMsg'),
|
||
type: "success",
|
||
plain: true
|
||
})
|
||
roleFormShow.value = false
|
||
getRoleList()
|
||
roleStore.refreshSelect()
|
||
}).finally(() => {
|
||
permLoading.value = false
|
||
})
|
||
}
|
||
|
||
|
||
function refresh() {
|
||
tableLoading.value = true
|
||
roles.length = 0
|
||
getRoleList()
|
||
}
|
||
|
||
function getRoleList() {
|
||
roleRoleList().then(list => {
|
||
roles.value = list
|
||
}).finally(() => {
|
||
tableLoading.value = false
|
||
})
|
||
}
|
||
|
||
function adjustWidth() {
|
||
desShow.value = window.innerWidth > 767
|
||
settingWidth.value = window.innerWidth < 480 ? (locale.value === 'en' ? 85 : 75) : null
|
||
sortWidth.value = window.innerWidth < 480 ? 75 : null
|
||
roleWidth.value = window.innerWidth < 480 ? 180 : 200
|
||
}
|
||
|
||
adjustWidth()
|
||
|
||
window.onresize = () => {
|
||
adjustWidth()
|
||
};
|
||
|
||
|
||
</script>
|
||
<style scoped lang="scss">
|
||
|
||
.perm-box {
|
||
height: 100%;
|
||
overflow: hidden;
|
||
width: 100%;
|
||
.perm-scrollbar {
|
||
height: 100%;
|
||
}
|
||
}
|
||
|
||
.send-num {
|
||
margin-left: 10px;
|
||
.el-input-number {
|
||
width: 95px;
|
||
}
|
||
}
|
||
|
||
.def-tag {
|
||
margin-left: 10px;
|
||
height: 20px;
|
||
}
|
||
|
||
.header-actions {
|
||
padding: 9px 15px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 18px;
|
||
box-shadow: inset 0 -1px 0 0 rgba(100, 121, 143, 0.12);
|
||
font-size: 18px;
|
||
.search {
|
||
:deep(.el-input-group) {
|
||
height: 28px;
|
||
}
|
||
:deep(.el-input__inner) {
|
||
height: 28px;
|
||
}
|
||
}
|
||
.icon {
|
||
cursor: pointer;
|
||
}
|
||
}
|
||
|
||
.warning {
|
||
position: relative;
|
||
left: 5px;
|
||
top: 5px;
|
||
color: gray;
|
||
cursor: pointer;
|
||
}
|
||
|
||
:deep(.description) {
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
|
||
.loading {
|
||
height: 100%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.role-name {
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
|
||
.description {
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
|
||
|
||
:deep(.el-segmented--small .el-segmented__item) {
|
||
border-radius: 8px !important;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.dialog-box {
|
||
.dialog-input {
|
||
margin-bottom: 15px !important;
|
||
}
|
||
.dialog-radio {
|
||
margin-top: 5px;
|
||
margin-bottom: 5px;
|
||
}
|
||
.dialog-input-tag {
|
||
}
|
||
}
|
||
|
||
.perm-expand {
|
||
margin-bottom: 5px;
|
||
--el-border-radius-base: 4px;
|
||
position: relative;
|
||
bottom: 5px;
|
||
}
|
||
|
||
|
||
:deep(.el-dialog) {
|
||
margin-bottom: 20px !important;
|
||
width: 460px !important;
|
||
@media (max-width: 500px) {
|
||
width: calc(100% - 40px) !important;
|
||
margin-right: 20px !important;
|
||
margin-left: 20px !important;
|
||
|
||
}
|
||
}
|
||
.btn {
|
||
width: 100%;
|
||
margin-top: 15px;
|
||
}
|
||
</style> |