fix:完善组织管理逻辑
This commit is contained in:
2
components.d.ts
vendored
2
components.d.ts
vendored
@@ -11,6 +11,7 @@ export {}
|
|||||||
/* prettier-ignore */
|
/* prettier-ignore */
|
||||||
declare module 'vue' {
|
declare module 'vue' {
|
||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
|
AutoTooltip: typeof import('./src/components/autoTooltip/index.vue')['default']
|
||||||
CardItem: typeof import('./src/components/cardItem/index.vue')['default']
|
CardItem: typeof import('./src/components/cardItem/index.vue')['default']
|
||||||
Comment: typeof import('./src/components/comment/index.vue')['default']
|
Comment: typeof import('./src/components/comment/index.vue')['default']
|
||||||
CommonFilter: typeof import('./src/components/commonFilter/index.vue')['default']
|
CommonFilter: typeof import('./src/components/commonFilter/index.vue')['default']
|
||||||
@@ -56,6 +57,7 @@ declare module 'vue' {
|
|||||||
ElSkeleton: typeof import('element-plus/es')['ElSkeleton']
|
ElSkeleton: typeof import('element-plus/es')['ElSkeleton']
|
||||||
ElSkeletonItem: typeof import('element-plus/es')['ElSkeletonItem']
|
ElSkeletonItem: typeof import('element-plus/es')['ElSkeletonItem']
|
||||||
ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
|
ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
|
||||||
|
ElSwitch: typeof import('element-plus/es')['ElSwitch']
|
||||||
ElTable: typeof import('element-plus/es')['ElTable']
|
ElTable: typeof import('element-plus/es')['ElTable']
|
||||||
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
|
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
|
||||||
ElTabPane: typeof import('element-plus/es')['ElTabPane']
|
ElTabPane: typeof import('element-plus/es')['ElTabPane']
|
||||||
|
|||||||
39
src/components/autoTooltip/index.vue
Normal file
39
src/components/autoTooltip/index.vue
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<template>
|
||||||
|
<el-tooltip
|
||||||
|
:content="content"
|
||||||
|
:disabled="!isOverflow"
|
||||||
|
placement="top"
|
||||||
|
v-bind="$attrs"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="ellipsis-content"
|
||||||
|
@mouseenter="checkOverflow"
|
||||||
|
>
|
||||||
|
<slot>{{ content }}</slot>
|
||||||
|
</div>
|
||||||
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
defineOptions({ name: 'AutoEllipsis' })
|
||||||
|
defineProps({
|
||||||
|
content: String
|
||||||
|
});
|
||||||
|
|
||||||
|
const isOverflow = ref(false);
|
||||||
|
|
||||||
|
const checkOverflow = (event) => {
|
||||||
|
const el = event.currentTarget;
|
||||||
|
isOverflow.value = el.scrollWidth > el.clientWidth;
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.ellipsis-content {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,14 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="tabs-outer-container" ref="containerRef" :style="{ height: height + 'px' }">
|
<div class="tabs-outer-container" ref="containerRef" :style="{ height: height + 'px' }">
|
||||||
|
<div class="ghost-wrapper" ref="ghostRef">
|
||||||
|
<div v-for="item in items" :key="'ghost' + item[itemMap.id]" class="tab-item">
|
||||||
|
<span class="tab-text">{{ item[itemMap.label] }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="tabs-wrapper">
|
<div class="tabs-wrapper">
|
||||||
<div
|
<div
|
||||||
v-for="(item, index) in visibleItems"
|
v-for="(item, index) in visibleItems"
|
||||||
:key="item.id"
|
:key="item[itemMap.id]"
|
||||||
class="tab-item"
|
class="tab-item"
|
||||||
:class="{ active: modelValue === item.id }"
|
:class="{ active: modelValue === item[itemMap.id] }"
|
||||||
@click="$emit('update:modelValue', item.id)"
|
@click="$emit('update:modelValue', item[itemMap.id])"
|
||||||
>
|
>
|
||||||
<span class="tab-text">{{ item.label }}</span>
|
<span class="tab-text">{{ item[itemMap.label] }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-dropdown
|
<el-dropdown
|
||||||
@@ -17,7 +23,7 @@
|
|||||||
@command="handleCommand"
|
@command="handleCommand"
|
||||||
class="more-dropdown"
|
class="more-dropdown"
|
||||||
>
|
>
|
||||||
<div class="tab-item more-trigger">
|
<div class="tab-item more-trigger" :class="{ 'is-more-active': isHiddenActive }">
|
||||||
<span>更多</span>
|
<span>更多</span>
|
||||||
<el-icon class="el-icon--right"><ArrowDown /></el-icon>
|
<el-icon class="el-icon--right"><ArrowDown /></el-icon>
|
||||||
</div>
|
</div>
|
||||||
@@ -25,11 +31,12 @@
|
|||||||
<el-dropdown-menu>
|
<el-dropdown-menu>
|
||||||
<el-dropdown-item
|
<el-dropdown-item
|
||||||
v-for="item in hiddenItems"
|
v-for="item in hiddenItems"
|
||||||
:key="item.id"
|
:key="item[itemMap.id]"
|
||||||
:command="item.id"
|
:command="item[itemMap.id]"
|
||||||
|
|
||||||
>
|
>
|
||||||
<span :class="{ 'is-active-item-overflow-tabs': modelValue === item.id }">{{ item.label }}</span>
|
<span :class="{ 'is-active-item-overflow-tabs': modelValue === item[itemMap.id] }">
|
||||||
|
{{ item[itemMap.label] }}
|
||||||
|
</span>
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
</template>
|
</template>
|
||||||
@@ -42,53 +49,94 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted, onUnmounted, computed, nextTick, watch } from "vue";
|
import { ref, onMounted, onUnmounted, computed, nextTick, watch } from "vue";
|
||||||
|
import { ArrowDown } from "@element-plus/icons-vue";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: [String, Number],
|
modelValue: [String, Number],
|
||||||
items: {
|
items: { type: Array, default: () => [] },
|
||||||
type: Array,
|
itemMap: { type: Object, default: () => ({ id: 'id', label: 'label' }) },
|
||||||
default: () => [],
|
height: { type: Number, default: 32 },
|
||||||
},
|
|
||||||
height: {
|
|
||||||
type: Number,
|
|
||||||
default: 32,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(["update:modelValue"]);
|
const emit = defineEmits(["update:modelValue"]);
|
||||||
|
|
||||||
const containerRef = ref(null);
|
const containerRef = ref(null);
|
||||||
|
const ghostRef = ref(null);
|
||||||
const splitIndex = ref(props.items.length);
|
const splitIndex = ref(props.items.length);
|
||||||
|
const itemWidths = ref([]); // 缓存所有项的宽度
|
||||||
let timer = null;
|
let timer = null;
|
||||||
|
|
||||||
const activeBarStyle = ref({
|
const activeBarStyle = ref({ width: "0px", left: "0px", opacity: 0 });
|
||||||
width: "0px",
|
|
||||||
left: "0px",
|
|
||||||
opacity: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
const visibleItems = computed(() => props.items.slice(0, splitIndex.value));
|
const visibleItems = computed(() => props.items.slice(0, splitIndex.value));
|
||||||
const hiddenItems = computed(() => props.items.slice(splitIndex.value));
|
const hiddenItems = computed(() => props.items.slice(splitIndex.value));
|
||||||
const isHiddenActive = computed(() => {
|
const isHiddenActive = computed(() => {
|
||||||
return hiddenItems.value.some((item) => item.id === props.modelValue);
|
return hiddenItems.value.some((item) => item[props.itemMap.id] === props.modelValue);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 获取所有 Tab 的初始宽度
|
||||||
|
const measureWidths = () => {
|
||||||
|
if (!ghostRef.value) return;
|
||||||
|
const nodes = ghostRef.value.querySelectorAll(".tab-item");
|
||||||
|
if (nodes.length === 0) return;
|
||||||
|
|
||||||
|
const widths = Array.from(nodes).map(node => node.getBoundingClientRect().width);
|
||||||
|
|
||||||
|
// 只有拿到有效宽度才更新,防止在某些极端情况下宽度全为 0 导致计算错误
|
||||||
|
if (widths.some(w => w > 0)) {
|
||||||
|
itemWidths.value = widths;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const calculateLayout = () => {
|
||||||
|
if (!containerRef.value) return;
|
||||||
|
|
||||||
|
// 如果没有宽度数据,先测一遍
|
||||||
|
if (itemWidths.value.length === 0) {
|
||||||
|
measureWidths();
|
||||||
|
}
|
||||||
|
|
||||||
|
const containerWidth = containerRef.value.offsetWidth;
|
||||||
|
// 如果容器本身没宽度(比如在隐藏的弹窗里),直接返回
|
||||||
|
if (containerWidth <= 0) return;
|
||||||
|
|
||||||
|
const moreBtnWidth = 80;
|
||||||
|
let currentWidth = 0;
|
||||||
|
let newSplitIndex = props.items.length;
|
||||||
|
|
||||||
|
for (let i = 0; i < itemWidths.value.length; i++) {
|
||||||
|
const w = itemWidths.value[i];
|
||||||
|
// 加上 20px 的 padding 补偿 (对应你 CSS 里的 padding: 0 20px)
|
||||||
|
// 最好在 measureWidths 阶段就包含 padding,或者在这里统一加
|
||||||
|
const fullWidth = w;
|
||||||
|
|
||||||
|
if (currentWidth + fullWidth > containerWidth) {
|
||||||
|
newSplitIndex = i;
|
||||||
|
// 预留更多按钮位置
|
||||||
|
while (newSplitIndex > 0 && currentWidth + moreBtnWidth > containerWidth) {
|
||||||
|
newSplitIndex--;
|
||||||
|
currentWidth -= itemWidths.value[newSplitIndex];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
currentWidth += fullWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
splitIndex.value = newSplitIndex;
|
||||||
|
};
|
||||||
|
|
||||||
const updateActiveBar = async () => {
|
const updateActiveBar = async () => {
|
||||||
await nextTick();
|
await nextTick();
|
||||||
if (!containerRef.value) return;
|
if (!containerRef.value) return;
|
||||||
|
|
||||||
// 1. 检查激活项是否在可见区域
|
const activeIndex = visibleItems.value.findIndex(item => item[props.itemMap.id] === props.modelValue);
|
||||||
const activeIndex = visibleItems.value.findIndex((item) => item.id === props.modelValue);
|
|
||||||
|
|
||||||
if (activeIndex >= 0) {
|
if (activeIndex >= 0) {
|
||||||
// 激活项在可见区域:计算位置并显示下划线
|
const tabItems = containerRef.value.querySelectorAll(".tabs-wrapper > .tab-item:not(.more-trigger)");
|
||||||
const tabItems = containerRef.value.querySelectorAll(".tab-item:not(.more-trigger)");
|
|
||||||
const activeElement = tabItems[activeIndex];
|
const activeElement = tabItems[activeIndex];
|
||||||
|
|
||||||
if (activeElement) {
|
if (activeElement) {
|
||||||
const rect = activeElement.getBoundingClientRect();
|
const rect = activeElement.getBoundingClientRect();
|
||||||
const containerRect = containerRef.value.getBoundingClientRect();
|
const containerRect = containerRef.value.getBoundingClientRect();
|
||||||
|
|
||||||
activeBarStyle.value = {
|
activeBarStyle.value = {
|
||||||
width: `${rect.width * 0.6}px`,
|
width: `${rect.width * 0.6}px`,
|
||||||
left: `${rect.left - containerRect.left + rect.width * 0.2}px`,
|
left: `${rect.left - containerRect.left + rect.width * 0.2}px`,
|
||||||
@@ -97,66 +145,44 @@ const updateActiveBar = async () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
activeBarStyle.value.opacity = 0;
|
||||||
// 2. 如果在隐藏区域或未找到,将下划线宽度设为 0
|
|
||||||
activeBarStyle.value = {
|
|
||||||
...activeBarStyle.value,
|
|
||||||
width: "0px",
|
|
||||||
opacity: 0,
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const calculateLayout = () => {
|
const handleResize = () => {
|
||||||
if (!containerRef.value) return;
|
|
||||||
const containerWidth = Math.floor(containerRef.value.getBoundingClientRect().width);
|
|
||||||
const itemsNodes = containerRef.value.querySelectorAll(".tab-item:not(.more-trigger)");
|
|
||||||
const moreBtnWidth = 90;
|
|
||||||
|
|
||||||
let currentWidth = 0;
|
|
||||||
let newSplitIndex = props.items.length;
|
|
||||||
|
|
||||||
for (let i = 0; i < itemsNodes.length; i++) {
|
|
||||||
currentWidth += Math.ceil(itemsNodes[i].getBoundingClientRect().width) + 20;
|
|
||||||
if (currentWidth + moreBtnWidth > containerWidth) {
|
|
||||||
newSplitIndex = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (splitIndex.value !== newSplitIndex) {
|
|
||||||
splitIndex.value = newSplitIndex;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const debouncedCalc = () => {
|
|
||||||
if (timer) clearTimeout(timer);
|
|
||||||
timer = setTimeout(() => {
|
|
||||||
calculateLayout();
|
calculateLayout();
|
||||||
|
nextTick(() => {
|
||||||
updateActiveBar();
|
updateActiveBar();
|
||||||
}, 100);
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
let resizeObserver = null;
|
let resizeObserver = null;
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await nextTick();
|
await nextTick();
|
||||||
|
measureWidths();
|
||||||
calculateLayout();
|
calculateLayout();
|
||||||
updateActiveBar();
|
updateActiveBar();
|
||||||
resizeObserver = new ResizeObserver(() => debouncedCalc());
|
|
||||||
|
resizeObserver = new ResizeObserver(() => {
|
||||||
|
// 尽量不要在 Resize 里用太长的 debounce,
|
||||||
|
// 否则你会感觉 Tab 是“跳”出来的,而不是“滑”出来的
|
||||||
|
handleResize();
|
||||||
|
});
|
||||||
|
|
||||||
if (containerRef.value) resizeObserver.observe(containerRef.value);
|
if (containerRef.value) resizeObserver.observe(containerRef.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
if (resizeObserver) resizeObserver.disconnect();
|
resizeObserver?.disconnect();
|
||||||
if (timer) clearTimeout(timer);
|
clearTimeout(timer);
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleCommand = (id) => emit("update:modelValue", id);
|
const handleCommand = (id) => emit("update:modelValue", id);
|
||||||
|
|
||||||
watch(() => props.modelValue, () => updateActiveBar());
|
watch(() => props.modelValue, () => updateActiveBar());
|
||||||
watch(() => props.items, async () => {
|
watch(() => props.items, async () => {
|
||||||
splitIndex.value = props.items.length;
|
|
||||||
await nextTick();
|
await nextTick();
|
||||||
|
measureWidths();
|
||||||
calculateLayout();
|
calculateLayout();
|
||||||
updateActiveBar();
|
updateActiveBar();
|
||||||
}, { deep: true });
|
}, { deep: true });
|
||||||
@@ -165,7 +191,17 @@ watch(() => props.items, async () => {
|
|||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.tabs-outer-container {
|
.tabs-outer-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow: hidden;
|
position: relative;
|
||||||
|
|
||||||
|
// 关键:测量层不可见且不占位,但必须渲染以获取宽度
|
||||||
|
.ghost-wrapper {
|
||||||
|
position: absolute;
|
||||||
|
top: -9999px;
|
||||||
|
left: -9999px;
|
||||||
|
visibility: hidden;
|
||||||
|
display: flex;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
.tabs-wrapper {
|
.tabs-wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -173,6 +209,7 @@ watch(() => props.items, async () => {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
width: 100%; // 确保占满父级
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-item {
|
.tab-item {
|
||||||
@@ -183,37 +220,36 @@ watch(() => props.items, async () => {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: #606266;
|
color: #606266;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
position: relative;
|
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
transition: color 0.2s ease;
|
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
color: #409eff;
|
color: #409eff;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.is-more-active {
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.more-trigger {
|
.more-trigger {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
border-left: 1px solid #f0f2f5;
|
border-left: 1px solid #f0f2f5;
|
||||||
|
|
||||||
// 选中更多中的数据时,仅文字和图标变蓝
|
|
||||||
&.is-more-active {
|
|
||||||
color: #409eff;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.active-bar {
|
.active-bar {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height: 2px;
|
height: 2px;
|
||||||
background-color: #409eff;
|
background-color: #409eff;
|
||||||
transition: left 0.3s cubic-bezier(0.4, 0, 0.2, 1),
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
width 0.3s cubic-bezier(0.4, 0, 0.2, 1),
|
|
||||||
opacity 0.2s;
|
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.is-active-item-overflow-tabs {
|
||||||
|
color: #409eff;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -38,8 +38,8 @@
|
|||||||
>
|
>
|
||||||
<div class="info-label">{{ base.name }}</div>
|
<div class="info-label">{{ base.name }}</div>
|
||||||
<div class="info-value">
|
<div class="info-value">
|
||||||
<el-button link type="primary" v-if="base.slotName === 'link'">{{base.value}} ></el-button>
|
<el-button link type="primary" v-if="base.slotName === 'link'" @click="onOpenWeixin">已开启 ></el-button>
|
||||||
<span v-else>{{ base.value }}</span>
|
<span v-else>{{ info[base.value] }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -78,20 +78,25 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
const props = defineProps({
|
||||||
|
info:{
|
||||||
|
Object,
|
||||||
|
default: () => ({})
|
||||||
|
}
|
||||||
|
})
|
||||||
const turnOn = ref(true);
|
const turnOn = ref(true);
|
||||||
const systemList = [
|
const systemList = [
|
||||||
{
|
{
|
||||||
name: "生效时间",
|
name: "生效时间",
|
||||||
value: "2024-01-01",
|
value: "",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "过期时间",
|
name: "过期时间",
|
||||||
value: "永久生效",
|
value: "",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "最后更新",
|
name: "最后更新",
|
||||||
value: "2025-12-29 16:06",
|
value: "",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "更新人",
|
name: "更新人",
|
||||||
@@ -103,20 +108,20 @@ const systemList = [
|
|||||||
const baseList = [
|
const baseList = [
|
||||||
{
|
{
|
||||||
name: "部门/公司名称",
|
name: "部门/公司名称",
|
||||||
value: "广州分公司",
|
value: "",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "上级单位",
|
name: "上级单位",
|
||||||
value: "集团1",
|
value: "",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "同步企微",
|
name: "同步企微",
|
||||||
value: "已开启",
|
value: "",
|
||||||
slotName: "link",
|
slotName: "link",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "部门负责人",
|
name: "部门负责人",
|
||||||
value: "赵康, 李思奇, 董峥",
|
value: "",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -125,6 +130,14 @@ const baseList = [
|
|||||||
const onButtonCheck = () =>{
|
const onButtonCheck = () =>{
|
||||||
turnOn.value = !turnOn.value;
|
turnOn.value = !turnOn.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 开启微信
|
||||||
|
|
||||||
|
const onOpenWeixin = () =>{
|
||||||
|
console.log('onOpenWeixin')
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@@ -20,12 +20,29 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<el-form :model="form" layout="vertical" label-position="top">
|
<el-form :model="form" layout="vertical" label-position="top">
|
||||||
|
<!-- 编辑的时候展示 同步按钮 -->
|
||||||
|
|
||||||
|
<template v-if="isSyncConfig">
|
||||||
|
<el-form-item>
|
||||||
|
<div class="sync-card">
|
||||||
|
<div class="sync-card__content">
|
||||||
|
<h3 class="sync-card__title">开启企微同步</h3>
|
||||||
|
<p class="sync-card__desc">开启后将定期拉取企微通讯录数据</p>
|
||||||
|
</div>
|
||||||
|
<div class="sync-card__action">
|
||||||
|
<el-switch v-model="form.syncEnabled" size="large" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
<el-form-item label="企业名称">
|
<el-form-item label="企业名称">
|
||||||
<el-input v-model="form.name" placeholder="如:名匠视界" />
|
<el-input v-model="form.name" placeholder="如:名匠视界" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
</template>
|
||||||
|
|
||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :span="12">
|
<el-col :span="12" v-if="!isSyncConfig">
|
||||||
<el-form-item label="域名">
|
<el-form-item label="域名">
|
||||||
<el-input v-model="form.domain" placeholder="example.com">
|
<el-input v-model="form.domain" placeholder="example.com">
|
||||||
<template #prefix
|
<template #prefix
|
||||||
@@ -43,9 +60,6 @@
|
|||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
|
||||||
|
|
||||||
<el-row :gutter="20">
|
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="应用 ID">
|
<el-form-item label="应用 ID">
|
||||||
<el-input v-model="form.appId" placeholder="1000001">
|
<el-input v-model="form.appId" placeholder="1000001">
|
||||||
@@ -55,7 +69,7 @@
|
|||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="isSyncConfig ? 24 : 12">
|
||||||
<el-form-item label="应用密钥">
|
<el-form-item label="应用密钥">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="form.appSecret"
|
v-model="form.appSecret"
|
||||||
@@ -64,8 +78,7 @@
|
|||||||
show-password
|
show-password
|
||||||
>
|
>
|
||||||
<template #prefix
|
<template #prefix
|
||||||
><el-icon><Key /></el-icon
|
><el-icon><Key /></el-icon></template>
|
||||||
></template>
|
|
||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
@@ -82,8 +95,13 @@
|
|||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="org-ganization-footer">
|
<div class="org-ganization-footer">
|
||||||
<el-button @click="dialogVisible = false" round>取消</el-button>
|
<el-button @click="dialogVisible = false" round size="large">取消</el-button>
|
||||||
<el-button type="primary" class="btn-confirm" round @click="handleSubmit"
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
class="btn-confirm"
|
||||||
|
round
|
||||||
|
size="large"
|
||||||
|
@click="handleSubmit"
|
||||||
>确认创建</el-button
|
>确认创建</el-button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
@@ -95,8 +113,8 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
defineOptions({ name: "AddOrgan" });
|
defineOptions({ name: "AddOrgan" });
|
||||||
|
|
||||||
|
const dialogVisible = defineModel("visible");
|
||||||
const dialogVisible = defineModel("visible")
|
const isSyncConfig = ref(true);
|
||||||
|
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
name: "",
|
name: "",
|
||||||
@@ -172,7 +190,42 @@ const handleSubmit = () => {};
|
|||||||
width: 50%;
|
width: 50%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sync-card {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 18px 20px;
|
||||||
|
background-color: #f8faff;
|
||||||
|
border-radius: 16px;
|
||||||
|
border: 1px solid #edf2f9;
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px; // 标题和描述的间距
|
||||||
|
flex: 1;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1d2129; // 深色标题
|
||||||
|
}
|
||||||
|
|
||||||
|
&__desc {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #86909c; // 灰色描述
|
||||||
|
}
|
||||||
|
|
||||||
|
&__action {
|
||||||
|
margin-left: 24px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -4,10 +4,12 @@
|
|||||||
<div class="organization-tabs">
|
<div class="organization-tabs">
|
||||||
<stageBreadcrumbs title="组织管理">
|
<stageBreadcrumbs title="组织管理">
|
||||||
<template #content>
|
<template #content>
|
||||||
<OverflowTabs v-model="activeTab" :items="tabList" :height="60"/>
|
<OverflowTabs :itemMap="{id:'id',label:'name'}" v-model="activeTab" :items="tabList" :height="60" />
|
||||||
</template>
|
</template>
|
||||||
<template #action>
|
<template #action>
|
||||||
<el-button type="primary" :icon="'Plus'" plain @click="onAddGroup">新增集团</el-button>
|
<el-button type="primary" :icon="'Plus'" plain @click="onAddGroup"
|
||||||
|
>新增集团</el-button
|
||||||
|
>
|
||||||
</template>
|
</template>
|
||||||
</stageBreadcrumbs>
|
</stageBreadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
@@ -18,7 +20,7 @@
|
|||||||
<div class="mj-panel-title org-tree-head">组织架构</div>
|
<div class="mj-panel-title org-tree-head">组织架构</div>
|
||||||
<div class="org-tree-search">
|
<div class="org-tree-search">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="search"
|
v-model="filterText"
|
||||||
:prefix-icon="'Search'"
|
:prefix-icon="'Search'"
|
||||||
placeholder="搜索部门或公司"
|
placeholder="搜索部门或公司"
|
||||||
/>
|
/>
|
||||||
@@ -26,7 +28,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="org-tree-list">
|
<div class="org-tree-list">
|
||||||
<el-tree
|
<el-tree
|
||||||
:data="data"
|
v-loading="treeLoading"
|
||||||
|
ref="treeRef"
|
||||||
|
:data="treeData"
|
||||||
|
lazy
|
||||||
|
:load="loadNode"
|
||||||
|
:filter-node-method="filterNode"
|
||||||
:props="defaultProps"
|
:props="defaultProps"
|
||||||
@node-click="handleNodeClick"
|
@node-click="handleNodeClick"
|
||||||
>
|
>
|
||||||
@@ -39,10 +46,11 @@
|
|||||||
:hover-color="'#67c23a'"
|
:hover-color="'#67c23a'"
|
||||||
:color="getIconColor(node)"
|
:color="getIconColor(node)"
|
||||||
/>
|
/>
|
||||||
<span>{{ node.label }}</span>
|
|
||||||
|
<AutoTooltip :content="node.label" class="tree-node-label" />
|
||||||
</div>
|
</div>
|
||||||
<div class="org-tree-item-right">
|
<div class="org-tree-item-right">
|
||||||
<el-icon :size="15">
|
<el-icon :size="15" @click.stop="onOrgTreeDelete(node,data)">
|
||||||
<Delete />
|
<Delete />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
</div>
|
</div>
|
||||||
@@ -62,10 +70,10 @@
|
|||||||
<div class="mj-organization-card organization-info">
|
<div class="mj-organization-card organization-info">
|
||||||
<el-tabs v-model="activeName" class="organization-info-tabs">
|
<el-tabs v-model="activeName" class="organization-info-tabs">
|
||||||
<el-tab-pane label="基础信息" name="baseInfo">
|
<el-tab-pane label="基础信息" name="baseInfo">
|
||||||
<OrganizationDetail />
|
<OrganizationDetail :info="detailInfo" />
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane label="动态日志" name="auditLogs">
|
<el-tab-pane label="动态日志" name="auditLogs">
|
||||||
<AuditLogs />
|
<AuditLogs :info="detailInfo"/>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
</div>
|
</div>
|
||||||
@@ -80,31 +88,62 @@ import stageBreadcrumbs from "@/components/stageBreadcrumbs/index.vue";
|
|||||||
import OverflowTabs from "@/components/overflowTabs/index.vue";
|
import OverflowTabs from "@/components/overflowTabs/index.vue";
|
||||||
import AuditLogs from "./auditLogs.vue";
|
import AuditLogs from "./auditLogs.vue";
|
||||||
import OrganizationDetail from "./organizationDetail.vue";
|
import OrganizationDetail from "./organizationDetail.vue";
|
||||||
import DynamicSvgIcon from '@/components/dynamicSvgIcon/index.vue';
|
import DynamicSvgIcon from "@/components/dynamicSvgIcon/index.vue";
|
||||||
import addOrgan from "./addOrgan.vue";
|
import addOrgan from "./addOrgan.vue";
|
||||||
|
import AutoTooltip from "@/components/autoTooltip/index.vue";
|
||||||
defineOptions({ name: "Organization" });
|
import { debounce } from 'lodash-es'
|
||||||
|
import {
|
||||||
const addValue = ref("");
|
getEnterprise,
|
||||||
const search = ref("");
|
addEnterprise,
|
||||||
const activeName = ref("baseInfo");
|
enableEnterprise,
|
||||||
|
disableEnterprise,
|
||||||
const showAddOrgan = ref(false);
|
getEnterpriseOrg,
|
||||||
|
getEnterpriseUser,
|
||||||
// 集团Tabs切换
|
getEnterpriseDetail,
|
||||||
const activeTab = ref(1);
|
getEnterpriseOrgDetail
|
||||||
const tabList = ref([
|
} from "@/api/stage/organization";
|
||||||
{ id: 1, label: '集团1' },
|
|
||||||
{ id: 2, label: '集团2' },
|
|
||||||
{ id: 3, label: '集团3' },
|
|
||||||
{ id: 4, label: '集团4' },
|
|
||||||
{ id: 5, label: '集团5' },
|
|
||||||
{ id: 6, label: '集团6' },
|
|
||||||
]);
|
|
||||||
interface Tree {
|
interface Tree {
|
||||||
label: string;
|
label: string;
|
||||||
children?: Tree[];
|
children?: Tree[];
|
||||||
}
|
}
|
||||||
|
defineOptions({ name: "Organization" });
|
||||||
|
|
||||||
|
const addValue = ref("");
|
||||||
|
const activeName = ref("baseInfo");
|
||||||
|
|
||||||
|
const showAddOrgan = ref(false);
|
||||||
|
const treeData = ref([]);
|
||||||
|
// 集团Tabs切换
|
||||||
|
const activeTab = ref('');
|
||||||
|
const tabList = ref([]);
|
||||||
|
const filterText = ref('');
|
||||||
|
const treeRef = ref(null);
|
||||||
|
const treeLoading = ref(false);
|
||||||
|
const detailInfo = reactive<Record<string, any>>({});
|
||||||
|
const defaultProps = {
|
||||||
|
children: "children",
|
||||||
|
label: "name",
|
||||||
|
isLeaf:'leaf'
|
||||||
|
};
|
||||||
|
|
||||||
|
// 加载子机构
|
||||||
|
const loadNode = async (node, resolve, reject) =>{
|
||||||
|
try {
|
||||||
|
const response = await getEnterpriseOrg(node.id);
|
||||||
|
resolve(response);
|
||||||
|
} catch (error) {
|
||||||
|
console.log('search error:',error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(filterText, (val) => {
|
||||||
|
treeRef.value!.filter(val)
|
||||||
|
})
|
||||||
|
// 过滤节点
|
||||||
|
const filterNode = (value: string, data:Record<string, any>) => {
|
||||||
|
if (!value) return true
|
||||||
|
return data.name.includes(value)
|
||||||
|
}
|
||||||
|
|
||||||
// 添加集团
|
// 添加集团
|
||||||
const onAddGroup = () => {
|
const onAddGroup = () => {
|
||||||
@@ -113,92 +152,113 @@ const onAddGroup = () => {
|
|||||||
// 获取图标组件
|
// 获取图标组件
|
||||||
const getIconComponent = (node: any) => {
|
const getIconComponent = (node: any) => {
|
||||||
if (node.level === 1) {
|
if (node.level === 1) {
|
||||||
return 'building'; // 一级节点使用
|
return "building"; // 一级节点使用
|
||||||
} else if (node.level === 2) {
|
} else if (node.level === 2) {
|
||||||
return `flag`; // 二级节点使用其他图标
|
return `flag`; // 二级节点使用其他图标
|
||||||
} else {
|
} else {
|
||||||
return 'flag'; // 更深层级的默认图标
|
return "flag"; // 更深层级的默认图标
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取图标颜色(可选)
|
// 获取图标颜色(可选)
|
||||||
const getIconColor = (node: any) => {
|
const getIconColor = (node: any) => {
|
||||||
return '#9EADC2';
|
return "#9EADC2";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// 获取企业列表
|
||||||
|
const getEnterpriseList = async () => {
|
||||||
|
try {
|
||||||
|
const queryParams = { name: "", domain: "" };
|
||||||
|
const res = await getEnterprise(queryParams);
|
||||||
|
activeTab.value = res[0]?.id || "";
|
||||||
|
getOrgTree(activeTab.value);
|
||||||
|
tabList.value = res;
|
||||||
|
} catch (error) {
|
||||||
|
console.log('getEnterpriseList error:',error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取组织架构数
|
||||||
|
const getOrgTree = async (id:number|string) => {
|
||||||
|
treeLoading.value = true;
|
||||||
|
try {
|
||||||
|
const response = await getEnterpriseOrg(id);
|
||||||
|
treeData.value = response;
|
||||||
|
} catch (error) {
|
||||||
|
console.log('get org error:',error);
|
||||||
|
treeData.value = [];
|
||||||
|
} finally {
|
||||||
|
treeLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取机构-企业详情数据
|
||||||
|
const apiMap = {
|
||||||
|
1: getEnterpriseOrg, //机构详情
|
||||||
|
2: getEnterpriseUser //员工详情
|
||||||
|
}
|
||||||
|
const orgDetail = async (data:Record<string,any>) =>{
|
||||||
|
const { kind,id } = data
|
||||||
|
try {
|
||||||
|
const response = await apiMap[kind](id);
|
||||||
|
Object.assign(detailInfo, response);
|
||||||
|
} catch (error) {
|
||||||
|
console.log('orgDetail error:',error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取企业详情
|
||||||
|
const getEnterpriseOrgDetail = async () => {
|
||||||
|
try {
|
||||||
|
const response = await getEnterpriseDetail();
|
||||||
|
console.log('获取当前的企业详情数据信息:',response);
|
||||||
|
} catch (error) {
|
||||||
|
console.log('getEnterpriseDetail error:',error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除部门-人员-组织
|
||||||
|
const onOrgTreeDelete = async (node,data) => {
|
||||||
|
ElMessageBox.confirm("确定要删除吗?", "提示", { type: "warning" })
|
||||||
|
.then(async () => {
|
||||||
|
try {
|
||||||
|
// await disableEnterprise(data.id);
|
||||||
|
treeRef.value && treeRef.value.remove(node);
|
||||||
|
} catch (error) {
|
||||||
|
console.log('fetch error',error);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 请求机构
|
||||||
|
const debouncedGetOrgTree = debounce((id) => {
|
||||||
|
if(!id) return;
|
||||||
|
getOrgTree(id);
|
||||||
|
}, 300);
|
||||||
|
|
||||||
|
// 监听切换
|
||||||
|
watch(activeTab, (val) => {
|
||||||
|
debouncedGetOrgTree(val);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 点击树节点获取数据
|
||||||
const handleNodeClick = (data: Tree) => {
|
const handleNodeClick = (data: Tree) => {
|
||||||
console.log(data);
|
orgDetail(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
const data: Tree[] = [
|
|
||||||
{
|
|
||||||
label: "Level one 1",
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
label: "Level two 1-1",
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
label: "Level three 1-1-1",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Level one 2",
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
label: "Level two 2-1",
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
label: "Level three 2-1-1",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Level two 2-2",
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
label: "Level three 2-2-1",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Level one 3",
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
label: "Level two 3-1",
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
label: "Level three 3-1-1",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Level two 3-2",
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
label: "Level three 3-2-1",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const defaultProps = {
|
onMounted(()=>{
|
||||||
children: "children",
|
Promise.all([getEnterpriseOrgDetail(),getEnterpriseList()]);
|
||||||
label: "label",
|
})
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@use "sass:math";
|
@use "sass:math";
|
||||||
.mj-organization {
|
.mj-organization {
|
||||||
|
height: 100%;
|
||||||
.organization-tabs {
|
.organization-tabs {
|
||||||
:deep(.stage-breadcrumbs) {
|
:deep(.stage-breadcrumbs) {
|
||||||
// border-bottom: none;
|
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -210,6 +270,7 @@ const defaultProps = {
|
|||||||
box-shadow: 0 0 6px #e9e8e8;
|
box-shadow: 0 0 6px #e9e8e8;
|
||||||
}
|
}
|
||||||
.organization-content {
|
.organization-content {
|
||||||
|
height: calc(100% - 60px);
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
.org-tree {
|
.org-tree {
|
||||||
@@ -230,14 +291,15 @@ const defaultProps = {
|
|||||||
overflow: auto;
|
overflow: auto;
|
||||||
padding: math.div($mj-padding-standard, 2) $mj-padding-standard;
|
padding: math.div($mj-padding-standard, 2) $mj-padding-standard;
|
||||||
.org-tree-item {
|
.org-tree-item {
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
min-width: 0;
|
||||||
|
width: 100%;
|
||||||
.org-tree-item-right {
|
.org-tree-item-right {
|
||||||
color:#A5ADB8;
|
color: #a5adb8;
|
||||||
&:hover {
|
&:hover {
|
||||||
color:#2065FC;
|
color: #2065fc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,9 +307,20 @@ const defaultProps = {
|
|||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
.org-tree-item-left {
|
.org-tree-item-left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
:deep(.el-icon) {
|
:deep(.el-icon) {
|
||||||
margin-right: 3px;
|
margin-right: 3px;
|
||||||
}
|
}
|
||||||
|
.tree-node-label{
|
||||||
|
display: inline-block;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,7 +143,44 @@ const addDynamicRoutes = async () => {
|
|||||||
// 从后端获取路由菜单数据 (这边需要区分 后台的菜单 和用户的菜单)
|
// 从后端获取路由菜单数据 (这边需要区分 后台的菜单 和用户的菜单)
|
||||||
let allRoutes:any[] = [];
|
let allRoutes:any[] = [];
|
||||||
if (userStore.isBackendUser) {
|
if (userStore.isBackendUser) {
|
||||||
const backendResponse = await getRouteMenus();
|
// const backendResponse = await getRouteMenus();
|
||||||
|
const backendResponse = [
|
||||||
|
{
|
||||||
|
"name": "字典管理",
|
||||||
|
"code": "dict",
|
||||||
|
"icon": "OfficeBuilding",
|
||||||
|
"metadata": null,
|
||||||
|
"children": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "组织管理",
|
||||||
|
"code": "origanization",
|
||||||
|
"icon": "OfficeBuilding",
|
||||||
|
"metadata": null,
|
||||||
|
"children": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "人员管理",
|
||||||
|
"code": "personnel",
|
||||||
|
"icon": "OfficeBuilding",
|
||||||
|
"metadata": null,
|
||||||
|
"children": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "权限管理",
|
||||||
|
"code": "permission",
|
||||||
|
"icon": "OfficeBuilding",
|
||||||
|
"metadata": null,
|
||||||
|
"children": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "流程管理",
|
||||||
|
"code": "flow",
|
||||||
|
"icon": "OfficeBuilding",
|
||||||
|
"metadata": null,
|
||||||
|
"children": null
|
||||||
|
}
|
||||||
|
];
|
||||||
allRoutes = [
|
allRoutes = [
|
||||||
{
|
{
|
||||||
code: "stage",
|
code: "stage",
|
||||||
|
|||||||
Reference in New Issue
Block a user