fix:完善组织管理逻辑

This commit is contained in:
liangdong
2026-01-07 18:59:34 +08:00
parent 9a8a2e3064
commit 90297a14ed
7 changed files with 484 additions and 231 deletions

2
components.d.ts vendored
View File

@@ -11,6 +11,7 @@ export {}
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
AutoTooltip: typeof import('./src/components/autoTooltip/index.vue')['default']
CardItem: typeof import('./src/components/cardItem/index.vue')['default']
Comment: typeof import('./src/components/comment/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']
ElSkeletonItem: typeof import('element-plus/es')['ElSkeletonItem']
ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
ElSwitch: typeof import('element-plus/es')['ElSwitch']
ElTable: typeof import('element-plus/es')['ElTable']
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
ElTabPane: typeof import('element-plus/es')['ElTabPane']

View 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>

View File

@@ -1,14 +1,20 @@
<template>
<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
v-for="(item, index) in visibleItems"
:key="item.id"
:key="item[itemMap.id]"
class="tab-item"
:class="{ active: modelValue === item.id }"
@click="$emit('update:modelValue', item.id)"
:class="{ active: modelValue === item[itemMap.id] }"
@click="$emit('update:modelValue', item[itemMap.id])"
>
<span class="tab-text">{{ item.label }}</span>
<span class="tab-text">{{ item[itemMap.label] }}</span>
</div>
<el-dropdown
@@ -17,7 +23,7 @@
@command="handleCommand"
class="more-dropdown"
>
<div class="tab-item more-trigger">
<div class="tab-item more-trigger" :class="{ 'is-more-active': isHiddenActive }">
<span>更多</span>
<el-icon class="el-icon--right"><ArrowDown /></el-icon>
</div>
@@ -25,11 +31,12 @@
<el-dropdown-menu>
<el-dropdown-item
v-for="item in hiddenItems"
:key="item.id"
:command="item.id"
:key="item[itemMap.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-menu>
</template>
@@ -42,53 +49,94 @@
<script setup>
import { ref, onMounted, onUnmounted, computed, nextTick, watch } from "vue";
import { ArrowDown } from "@element-plus/icons-vue";
const props = defineProps({
modelValue: [String, Number],
items: {
type: Array,
default: () => [],
},
height: {
type: Number,
default: 32,
},
items: { type: Array, default: () => [] },
itemMap: { type: Object, default: () => ({ id: 'id', label: 'label' }) },
height: { type: Number, default: 32 },
});
const emit = defineEmits(["update:modelValue"]);
const containerRef = ref(null);
const ghostRef = ref(null);
const splitIndex = ref(props.items.length);
const itemWidths = ref([]); // 缓存所有项的宽度
let timer = null;
const activeBarStyle = ref({
width: "0px",
left: "0px",
opacity: 0,
});
const activeBarStyle = ref({ width: "0px", left: "0px", opacity: 0 });
const visibleItems = computed(() => props.items.slice(0, splitIndex.value));
const hiddenItems = computed(() => props.items.slice(splitIndex.value));
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 () => {
await nextTick();
if (!containerRef.value) return;
// 1. 检查激活项是否在可见区域
const activeIndex = visibleItems.value.findIndex((item) => item.id === props.modelValue);
const activeIndex = visibleItems.value.findIndex(item => item[props.itemMap.id] === props.modelValue);
if (activeIndex >= 0) {
// 激活项在可见区域:计算位置并显示下划线
const tabItems = containerRef.value.querySelectorAll(".tab-item:not(.more-trigger)");
const tabItems = containerRef.value.querySelectorAll(".tabs-wrapper > .tab-item:not(.more-trigger)");
const activeElement = tabItems[activeIndex];
if (activeElement) {
const rect = activeElement.getBoundingClientRect();
const containerRect = containerRef.value.getBoundingClientRect();
activeBarStyle.value = {
width: `${rect.width * 0.6}px`,
left: `${rect.left - containerRect.left + rect.width * 0.2}px`,
@@ -97,66 +145,44 @@ const updateActiveBar = async () => {
return;
}
}
// 2. 如果在隐藏区域或未找到,将下划线宽度设为 0
activeBarStyle.value = {
...activeBarStyle.value,
width: "0px",
opacity: 0,
};
activeBarStyle.value.opacity = 0;
};
const calculateLayout = () => {
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();
const handleResize = () => {
calculateLayout();
nextTick(() => {
updateActiveBar();
}, 100);
});
};
let resizeObserver = null;
onMounted(async () => {
await nextTick();
measureWidths();
calculateLayout();
updateActiveBar();
resizeObserver = new ResizeObserver(() => debouncedCalc());
resizeObserver = new ResizeObserver(() => {
// 尽量不要在 Resize 里用太长的 debounce
// 否则你会感觉 Tab 是“跳”出来的,而不是“滑”出来的
handleResize();
});
if (containerRef.value) resizeObserver.observe(containerRef.value);
});
onUnmounted(() => {
if (resizeObserver) resizeObserver.disconnect();
if (timer) clearTimeout(timer);
resizeObserver?.disconnect();
clearTimeout(timer);
});
const handleCommand = (id) => emit("update:modelValue", id);
watch(() => props.modelValue, () => updateActiveBar());
watch(() => props.items, async () => {
splitIndex.value = props.items.length;
await nextTick();
measureWidths();
calculateLayout();
updateActiveBar();
}, { deep: true });
@@ -165,7 +191,17 @@ watch(() => props.items, async () => {
<style scoped lang="scss">
.tabs-outer-container {
width: 100%;
overflow: hidden;
position: relative;
// 关键:测量层不可见且不占位,但必须渲染以获取宽度
.ghost-wrapper {
position: absolute;
top: -9999px;
left: -9999px;
visibility: hidden;
display: flex;
white-space: nowrap;
}
.tabs-wrapper {
display: flex;
@@ -173,6 +209,7 @@ watch(() => props.items, async () => {
height: 100%;
position: relative;
user-select: none;
width: 100%; // 确保占满父级
}
.tab-item {
@@ -183,37 +220,36 @@ watch(() => props.items, async () => {
cursor: pointer;
color: #606266;
font-size: 14px;
position: relative;
white-space: nowrap;
flex-shrink: 0;
transition: color 0.2s ease;
&.active {
color: #409eff;
font-weight: 600;
}
&.is-more-active {
color: #409eff;
}
}
.more-trigger {
margin-left: auto;
border-left: 1px solid #f0f2f5;
// 选中更多中的数据时,仅文字和图标变蓝
&.is-more-active {
color: #409eff;
font-weight: 600;
}
}
.active-bar {
position: absolute;
height: 2px;
background-color: #409eff;
transition: left 0.3s cubic-bezier(0.4, 0, 0.2, 1),
width 0.3s cubic-bezier(0.4, 0, 0.2, 1),
opacity 0.2s;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
bottom: 0;
pointer-events: none;
}
}
.is-active-item-overflow-tabs {
color: #409eff;
font-weight: 600;
}
</style>

View File

@@ -38,8 +38,8 @@
>
<div class="info-label">{{ base.name }}</div>
<div class="info-value">
<el-button link type="primary" v-if="base.slotName === 'link'">{{base.value}} &gt;</el-button>
<span v-else>{{ base.value }}</span>
<el-button link type="primary" v-if="base.slotName === 'link'" @click="onOpenWeixin">已开启 &gt;</el-button>
<span v-else>{{ info[base.value] }}</span>
</div>
</div>
</div>
@@ -78,20 +78,25 @@
</template>
<script setup lang="ts">
const props = defineProps({
info:{
Object,
default: () => ({})
}
})
const turnOn = ref(true);
const systemList = [
{
name: "生效时间",
value: "2024-01-01",
value: "",
},
{
name: "过期时间",
value: "永久生效",
value: "",
},
{
name: "最后更新",
value: "2025-12-29 16:06",
value: "",
},
{
name: "更新人",
@@ -103,20 +108,20 @@ const systemList = [
const baseList = [
{
name: "部门/公司名称",
value: "广州分公司",
value: "",
},
{
name: "上级单位",
value: "集团1",
value: "",
},
{
name: "同步企微",
value: "已开启",
value: "",
slotName: "link",
},
{
name: "部门负责人",
value: "赵康, 李思奇, 董峥",
value: "",
},
];
@@ -125,6 +130,14 @@ const baseList = [
const onButtonCheck = () =>{
turnOn.value = !turnOn.value;
}
// 开启微信
const onOpenWeixin = () =>{
console.log('onOpenWeixin')
}
</script>
<style lang="scss" scoped>

View File

@@ -20,12 +20,29 @@
</template>
<el-form :model="form" layout="vertical" label-position="top">
<el-form-item label="企业名称">
<el-input v-model="form.name" placeholder="如:名匠视界" />
</el-form-item>
<!-- 编辑的时候展示 同步按钮 -->
<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-input v-model="form.name" placeholder="如:名匠视界" />
</el-form-item>
</template>
<el-row :gutter="20">
<el-col :span="12">
<el-col :span="12" v-if="!isSyncConfig">
<el-form-item label="域名">
<el-input v-model="form.domain" placeholder="example.com">
<template #prefix
@@ -43,9 +60,6 @@
</el-input>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="应用 ID">
<el-input v-model="form.appId" placeholder="1000001">
@@ -55,7 +69,7 @@
</el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-col :span="isSyncConfig ? 24 : 12">
<el-form-item label="应用密钥">
<el-input
v-model="form.appSecret"
@@ -64,8 +78,7 @@
show-password
>
<template #prefix
><el-icon><Key /></el-icon
></template>
><el-icon><Key /></el-icon></template>
</el-input>
</el-form-item>
</el-col>
@@ -82,8 +95,13 @@
<template #footer>
<div class="org-ganization-footer">
<el-button @click="dialogVisible = false" round>取消</el-button>
<el-button type="primary" class="btn-confirm" round @click="handleSubmit"
<el-button @click="dialogVisible = false" round size="large">取消</el-button>
<el-button
type="primary"
class="btn-confirm"
round
size="large"
@click="handleSubmit"
>确认创建</el-button
>
</div>
@@ -95,8 +113,8 @@
<script lang="ts" setup>
defineOptions({ name: "AddOrgan" });
const dialogVisible = defineModel("visible")
const dialogVisible = defineModel("visible");
const isSyncConfig = ref(true);
const form = reactive({
name: "",
@@ -164,15 +182,50 @@ const handleSubmit = () => {};
}
}
.org-ganization-footer{
.org-ganization-footer {
display: flex;
align-items: center;
justify-content: center;
.el-button{
.el-button {
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>

View File

@@ -4,10 +4,12 @@
<div class="organization-tabs">
<stageBreadcrumbs title="组织管理">
<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 #action>
<el-button type="primary" :icon="'Plus'" plain @click="onAddGroup">新增集团</el-button>
<el-button type="primary" :icon="'Plus'" plain @click="onAddGroup"
>新增集团</el-button
>
</template>
</stageBreadcrumbs>
</div>
@@ -18,7 +20,7 @@
<div class="mj-panel-title org-tree-head">组织架构</div>
<div class="org-tree-search">
<el-input
v-model="search"
v-model="filterText"
:prefix-icon="'Search'"
placeholder="搜索部门或公司"
/>
@@ -26,29 +28,35 @@
</div>
<div class="org-tree-list">
<el-tree
:data="data"
v-loading="treeLoading"
ref="treeRef"
:data="treeData"
lazy
:load="loadNode"
:filter-node-method="filterNode"
:props="defaultProps"
@node-click="handleNodeClick"
>
<template #default="{ node, data }">
<div class="org-tree-item">
<div class="org-tree-item-left">
<DynamicSvgIcon
:name="getIconComponent(node)"
:size="15"
:hover-color="'#67c23a'"
:color="getIconColor(node)"
/>
<span>{{ node.label }}</span>
<template #default="{ node, data }">
<div class="org-tree-item">
<div class="org-tree-item-left">
<DynamicSvgIcon
:name="getIconComponent(node)"
:size="15"
:hover-color="'#67c23a'"
:color="getIconColor(node)"
/>
<AutoTooltip :content="node.label" class="tree-node-label" />
</div>
<div class="org-tree-item-right">
<el-icon :size="15" @click.stop="onOrgTreeDelete(node,data)">
<Delete />
</el-icon>
</div>
</div>
<div class="org-tree-item-right">
<el-icon :size="15">
<Delete />
</el-icon>
</div>
</div>
</template>
</el-tree>
</template>
</el-tree>
</div>
<div class="org-bottom-add">
@@ -62,17 +70,17 @@
<div class="mj-organization-card organization-info">
<el-tabs v-model="activeName" class="organization-info-tabs">
<el-tab-pane label="基础信息" name="baseInfo">
<OrganizationDetail />
<OrganizationDetail :info="detailInfo" />
</el-tab-pane>
<el-tab-pane label="动态日志" name="auditLogs">
<AuditLogs />
<AuditLogs :info="detailInfo"/>
</el-tab-pane>
</el-tabs>
</div>
</div>
<!-- 添加集团-->
<addOrgan v-model:visible="showAddOrgan"/>
<addOrgan v-model:visible="showAddOrgan" />
</div>
</template>
<script setup lang="ts">
@@ -80,31 +88,62 @@ import stageBreadcrumbs from "@/components/stageBreadcrumbs/index.vue";
import OverflowTabs from "@/components/overflowTabs/index.vue";
import AuditLogs from "./auditLogs.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";
defineOptions({ name: "Organization" });
const addValue = ref("");
const search = ref("");
const activeName = ref("baseInfo");
const showAddOrgan = ref(false);
// 集团Tabs切换
const activeTab = ref(1);
const tabList = ref([
{ id: 1, label: '集团1' },
{ id: 2, label: '集团2' },
{ id: 3, label: '集团3' },
{ id: 4, label: '集团4' },
{ id: 5, label: '集团5' },
{ id: 6, label: '集团6' },
]);
import AutoTooltip from "@/components/autoTooltip/index.vue";
import { debounce } from 'lodash-es'
import {
getEnterprise,
addEnterprise,
enableEnterprise,
disableEnterprise,
getEnterpriseOrg,
getEnterpriseUser,
getEnterpriseDetail,
getEnterpriseOrgDetail
} from "@/api/stage/organization";
interface Tree {
label: string;
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 = () => {
@@ -113,93 +152,114 @@ const onAddGroup = () => {
// 获取图标组件
const getIconComponent = (node: any) => {
if (node.level === 1) {
return 'building'; // 一级节点使用
return "building"; // 一级节点使用
} else if (node.level === 2) {
return `flag`; // 二级节点使用其他图标
} else {
return 'flag'; // 更深层级的默认图标
return "flag"; // 更深层级的默认图标
}
};
// 获取图标颜色(可选)
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) => {
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 = {
children: "children",
label: "label",
};
onMounted(()=>{
Promise.all([getEnterpriseOrgDetail(),getEnterpriseList()]);
})
</script>
<style lang="scss" scoped>
@use "sass:math";
.mj-organization {
.organization-tabs{
:deep(.stage-breadcrumbs){
// border-bottom: none;
padding:0;
height: 100%;
.organization-tabs {
:deep(.stage-breadcrumbs) {
padding: 0;
}
}
.mj-organization-card {
@@ -210,6 +270,7 @@ const defaultProps = {
box-shadow: 0 0 6px #e9e8e8;
}
.organization-content {
height: calc(100% - 60px);
display: flex;
gap: 16px;
.org-tree {
@@ -229,25 +290,37 @@ const defaultProps = {
flex: 1;
overflow: auto;
padding: math.div($mj-padding-standard, 2) $mj-padding-standard;
.org-tree-item{
flex: 1;
.org-tree-item {
display: flex;
justify-content: space-between;
align-items: center;
.org-tree-item-right{
color:#A5ADB8;
&:hover{
color:#2065FC;
min-width: 0;
width: 100%;
.org-tree-item-right {
color: #a5adb8;
&:hover {
color: #2065fc;
}
}
:deep(.el-icon){
:deep(.el-icon) {
vertical-align: middle;
}
.org-tree-item-left{
:deep(.el-icon){
.org-tree-item-left {
display: flex;
align-items: center;
flex: 1;
min-width: 0;
:deep(.el-icon) {
margin-right: 3px;
}
.tree-node-label{
display: inline-block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 100%;
}
}
}
}

View File

@@ -143,7 +143,44 @@ const addDynamicRoutes = async () => {
// 从后端获取路由菜单数据 (这边需要区分 后台的菜单 和用户的菜单)
let allRoutes:any[] = [];
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 = [
{
code: "stage",