|
@@ -1,3 +1,378 @@
|
|
|
+<script setup lang="ts">
|
|
|
+import { nextTick, onMounted, ref } from 'vue';
|
|
|
+import { message } from 'ant-design-vue';
|
|
|
+
|
|
|
+import OrganizationalStructure from '@/components/OrganizationalStructure.vue';
|
|
|
+import SvgIcon from '@/components/SvgIcon.vue';
|
|
|
+import { useRequest } from '@/hooks/request';
|
|
|
+import { getSubOrgsByToken } from '@/api';
|
|
|
+
|
|
|
+import type { DataNode } from 'ant-design-vue/es/tree';
|
|
|
+
|
|
|
+const characterList = ref([
|
|
|
+ {
|
|
|
+ name: '管理员',
|
|
|
+ id: 1,
|
|
|
+ show: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: '工程师',
|
|
|
+ id: 2,
|
|
|
+ show: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: '技术员',
|
|
|
+ id: 3,
|
|
|
+ show: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: '操作工',
|
|
|
+ id: 4,
|
|
|
+ show: false,
|
|
|
+ },
|
|
|
+]);
|
|
|
+const { handleRequest } = useRequest();
|
|
|
+const characterListId = ref(1);
|
|
|
+const inputRef = ref<HTMLInputElement[]>([]); // 明确的类型声明
|
|
|
+const permissions = ref<string>('dataPermissions');
|
|
|
+const equipmentChecked = ref<boolean>(false);
|
|
|
+const editorChecked = ref<boolean>(true);
|
|
|
+const valueTime = ref<string>('1');
|
|
|
+const pagePermissionsSelectedKeys = ref<number[]>([]);
|
|
|
+
|
|
|
+const pagePermissionsTree = ref<DataNode[]>([
|
|
|
+ {
|
|
|
+ title: '父节点 1',
|
|
|
+ key: '0-0',
|
|
|
+ children: [
|
|
|
+ {
|
|
|
+ title: '子节点 1',
|
|
|
+ key: '0-0-0',
|
|
|
+ children: [
|
|
|
+ { title: '孙子节点 1', key: '0-0-0-0' },
|
|
|
+ { title: '孙子节点 2', key: '0-0-0-1' },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ { title: '子节点 2', key: '0-0-1' },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+]);
|
|
|
+const addCharacter = async () => {
|
|
|
+ if (characterList.value.some((item) => item.name === '')) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ characterList.value.push({
|
|
|
+ name: '',
|
|
|
+ id: 5,
|
|
|
+ show: true,
|
|
|
+ });
|
|
|
+ await nextTick();
|
|
|
+ console.log(inputRef.value);
|
|
|
+ inputRef.value[0].focus();
|
|
|
+};
|
|
|
+const clickCharacter = (id: number) => {
|
|
|
+ if (characterList.value.some((item) => item.name === '')) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ characterListId.value = id;
|
|
|
+};
|
|
|
+const addEditor = async (index: number) => {
|
|
|
+ if (characterList.value.some((item) => item.name === '')) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ characterList.value[index].show = true;
|
|
|
+ await nextTick();
|
|
|
+ inputRef.value[0].focus();
|
|
|
+};
|
|
|
+const editorcharacter = (index: number, name: string) => {
|
|
|
+ if (name) {
|
|
|
+ characterList.value[index].show = false;
|
|
|
+ } else {
|
|
|
+ return message.warning('名称不能为空!');
|
|
|
+ }
|
|
|
+};
|
|
|
+const editorPermission = () => {
|
|
|
+ editorChecked.value = false;
|
|
|
+};
|
|
|
+const cancelPermission = () => {
|
|
|
+ editorChecked.value = true;
|
|
|
+};
|
|
|
+const savePermission = () => {
|
|
|
+ editorChecked.value = true;
|
|
|
+};
|
|
|
+const clickOrganizationChange = (id: number) => {
|
|
|
+ console.log(id);
|
|
|
+};
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ handleRequest(async () => {
|
|
|
+ await getSubOrgsByToken();
|
|
|
+ });
|
|
|
+});
|
|
|
+</script>
|
|
|
+
|
|
|
<template>
|
|
|
- <div>角色管理</div>
|
|
|
+ <div>
|
|
|
+ <div class="text-top">角色管理</div>
|
|
|
+ <AFlex>
|
|
|
+ <OrganizationalStructure @change="clickOrganizationChange" />
|
|
|
+ <div class="content">
|
|
|
+ <AFlex justify="space-between" align="center" class="content-top">
|
|
|
+ <div class="content-text">角色</div>
|
|
|
+ <div class="icon-style pointer" @click="addCharacter">
|
|
|
+ <AFlex align="center"
|
|
|
+ ><SvgIcon name="plus" />
|
|
|
+ <div class="text-left">添加</div>
|
|
|
+ </AFlex>
|
|
|
+ </div>
|
|
|
+ </AFlex>
|
|
|
+ <div>
|
|
|
+ <div v-for="(item, index) in characterList" :key="index">
|
|
|
+ <div class="character-input" v-if="item.show">
|
|
|
+ <AInput
|
|
|
+ class="input-heught"
|
|
|
+ v-model:value="item.name"
|
|
|
+ :bordered="false"
|
|
|
+ ref="inputRef"
|
|
|
+ @pressEnter="editorcharacter(index, item.name)"
|
|
|
+ @blur="editorcharacter(index, item.name)"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <AFlex
|
|
|
+ v-else
|
|
|
+ justify="space-between"
|
|
|
+ align="center"
|
|
|
+ :class="item.id === characterListId ? 'character-list character-list-color' : 'character-list'"
|
|
|
+ >
|
|
|
+ <div class="pointer text-height" @click="clickCharacter(item.id)">{{ item.name }}</div>
|
|
|
+ <div v-if="item.id === characterListId">
|
|
|
+ <SvgIcon class="pointer" name="edit-o" @click="addEditor(index)" />
|
|
|
+ <SvgIcon class="pointer icon-left" name="delete" />
|
|
|
+ </div>
|
|
|
+ </AFlex>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="permission-management">
|
|
|
+ <AFlex justify="space-between" align="center" class="content-top">
|
|
|
+ <div class="content-text">权限</div>
|
|
|
+ <div class="pointer" @click="editorPermission" v-if="editorChecked">
|
|
|
+ <AFlex align="center"
|
|
|
+ ><SvgIcon name="edit-o" />
|
|
|
+ <div class="text-left">编辑</div>
|
|
|
+ </AFlex>
|
|
|
+ </div>
|
|
|
+ <AFlex v-else>
|
|
|
+ <div class="pointer" @click="cancelPermission">
|
|
|
+ <AFlex align="center"
|
|
|
+ ><SvgIcon name="close" />
|
|
|
+ <div class="text-left">取消</div>
|
|
|
+ </AFlex>
|
|
|
+ </div>
|
|
|
+ <div class="pointer pointer-left" @click="savePermission">
|
|
|
+ <AFlex align="center"
|
|
|
+ ><SvgIcon name="close" />
|
|
|
+ <div class="text-left">保存</div>
|
|
|
+ </AFlex>
|
|
|
+ </div>
|
|
|
+ </AFlex>
|
|
|
+ </AFlex>
|
|
|
+
|
|
|
+ <ARadioGroup v-model:value="permissions" button-style="solid" size="large">
|
|
|
+ <ARadioButton value="dataPermissions">数据权限</ARadioButton>
|
|
|
+ <ARadioButton value="functionPermissions">功能权限</ARadioButton>
|
|
|
+ </ARadioGroup>
|
|
|
+ <div v-if="permissions === 'dataPermissions'">
|
|
|
+ <AFlex align="center" class="device-permissions">
|
|
|
+ <ACheckbox class="select-all" v-model:checked="equipmentChecked" :disabled="editorChecked"
|
|
|
+ >设备组权限</ACheckbox
|
|
|
+ >
|
|
|
+ </AFlex>
|
|
|
+ <ATree
|
|
|
+ v-model:selected-keys="pagePermissionsSelectedKeys"
|
|
|
+ :tree-data="pagePermissionsTree"
|
|
|
+ checkable
|
|
|
+ default-expand-all
|
|
|
+ :disabled="editorChecked"
|
|
|
+ />
|
|
|
+ <AFlex align="center" class="device-permissions div-top">
|
|
|
+ <ACheckbox class="select-all" :disabled="editorChecked" v-model:checked="equipmentChecked"
|
|
|
+ >启用时间查询颗粒度设置</ACheckbox
|
|
|
+ >
|
|
|
+ </AFlex>
|
|
|
+ <ARadioGroup v-model:value="valueTime" name="radioGroup" class="radio-group" :disabled="editorChecked">
|
|
|
+ <ARadio value="1">分钟</ARadio>
|
|
|
+ <ARadio value="2">小时</ARadio>
|
|
|
+ <ARadio value="3">天</ARadio>
|
|
|
+ <ARadio value="4">月</ARadio>
|
|
|
+ </ARadioGroup>
|
|
|
+ </div>
|
|
|
+ <div v-if="permissions === 'functionPermissions'">
|
|
|
+ <AFlex align="center" class="device-permissions">
|
|
|
+ <div>查看权限</div>
|
|
|
+ </AFlex>
|
|
|
+ <ATree
|
|
|
+ v-model:selected-keys="pagePermissionsSelectedKeys"
|
|
|
+ :tree-data="pagePermissionsTree"
|
|
|
+ checkable
|
|
|
+ default-expand-all
|
|
|
+ class="tree-permissions"
|
|
|
+ :disabled="editorChecked"
|
|
|
+ />
|
|
|
+ <AFlex align="center" class="device-permissions div-top">
|
|
|
+ <div>操作权限</div>
|
|
|
+ </AFlex>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </AFlex>
|
|
|
+ </div>
|
|
|
</template>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+.pointer-left {
|
|
|
+ margin-left: 24px;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.permission-management) {
|
|
|
+ .ant-tree-list-holder-inner > div {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ width: 100%;
|
|
|
+ height: 48px;
|
|
|
+ padding-left: 26px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .tree-permissions .ant-tree-list-holder-inner > div {
|
|
|
+ border-bottom: 1px solid #e4e7ed;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ant-tree-list-holder-inner > div > .ant-tree-checkbox {
|
|
|
+ margin-block-start: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ant-tree-list-holder-inner > div > .ant-tree-switcher > span {
|
|
|
+ margin-top: 16px;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.radio-group {
|
|
|
+ margin-top: 13px;
|
|
|
+ margin-left: 48px;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.radio-group) {
|
|
|
+ .ant-radio-wrapper {
|
|
|
+ margin-inline-end: 20px;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.device-permissions {
|
|
|
+ width: 100%;
|
|
|
+ height: 48px;
|
|
|
+ padding-left: 24px;
|
|
|
+ margin-top: 16px;
|
|
|
+ background: #f5f7fa;
|
|
|
+ border-radius: 4px;
|
|
|
+}
|
|
|
+
|
|
|
+.permission-management .div-top {
|
|
|
+ margin-top: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.permission-management {
|
|
|
+ width: 100%;
|
|
|
+ height: calc(100vh - 80px);
|
|
|
+ padding: 16px;
|
|
|
+ background: #fff;
|
|
|
+ border-radius: 16px;
|
|
|
+}
|
|
|
+
|
|
|
+.input-heught {
|
|
|
+ height: 38px;
|
|
|
+}
|
|
|
+
|
|
|
+.character-input {
|
|
|
+ width: 214px;
|
|
|
+ height: 40px;
|
|
|
+ background: rgb(255 255 255 / 15%);
|
|
|
+ border: 1px solid var(--antd-color-primary);
|
|
|
+ border-radius: 4px;
|
|
|
+}
|
|
|
+
|
|
|
+.text-left {
|
|
|
+ margin-left: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.icon-style {
|
|
|
+ color: var(--antd-color-primary);
|
|
|
+}
|
|
|
+
|
|
|
+.text-height {
|
|
|
+ height: 40px;
|
|
|
+ line-height: 40px;
|
|
|
+}
|
|
|
+
|
|
|
+.icon-left {
|
|
|
+ margin-left: 13px;
|
|
|
+}
|
|
|
+
|
|
|
+.pointer {
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+
|
|
|
+.character-list {
|
|
|
+ width: 214px;
|
|
|
+ height: 40px;
|
|
|
+ padding: 0 12px;
|
|
|
+ font-size: 14px;
|
|
|
+ font-style: normal;
|
|
|
+ font-weight: 400;
|
|
|
+ line-height: 22px;
|
|
|
+ color: #000;
|
|
|
+ text-align: left;
|
|
|
+ border-radius: 4px;
|
|
|
+}
|
|
|
+
|
|
|
+.character-list-color {
|
|
|
+ color: var(--antd-color-primary);
|
|
|
+ background: var(--antd-color-primary-opacity-15);
|
|
|
+}
|
|
|
+
|
|
|
+.content-top {
|
|
|
+ margin-bottom: 16px;
|
|
|
+}
|
|
|
+
|
|
|
+.button-style {
|
|
|
+ color: var(--antd-color-primary);
|
|
|
+}
|
|
|
+
|
|
|
+.content-text {
|
|
|
+ font-size: 16px;
|
|
|
+ font-style: normal;
|
|
|
+ font-weight: 600;
|
|
|
+ line-height: 24px;
|
|
|
+ color: #333;
|
|
|
+ text-align: left;
|
|
|
+}
|
|
|
+
|
|
|
+.content {
|
|
|
+ width: 246px;
|
|
|
+ height: calc(100vh - 80px);
|
|
|
+ padding: 16px;
|
|
|
+ margin-right: 16px;
|
|
|
+ background: #fff;
|
|
|
+ border-radius: 16px;
|
|
|
+}
|
|
|
+
|
|
|
+.text-top {
|
|
|
+ margin-bottom: 16px;
|
|
|
+ font-size: 20px;
|
|
|
+ font-style: normal;
|
|
|
+ font-weight: 500;
|
|
|
+ line-height: 32px;
|
|
|
+ color: rgb(0 0 0 / 85%);
|
|
|
+ text-align: left;
|
|
|
+}
|
|
|
+</style>
|