fix
This commit is contained in:
@@ -10,15 +10,15 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="layoutAside">
|
||||
import {computed, defineAsyncComponent, onBeforeMount, reactive, ref, watch} from 'vue';
|
||||
import {storeToRefs} from 'pinia';
|
||||
import { computed, defineAsyncComponent, onBeforeMount, reactive, ref, watch } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import pinia from '/@/stores/index';
|
||||
import {useRoutesList} from '/@/stores/routesList';
|
||||
import {useThemeConfig} from '/@/stores/themeConfig';
|
||||
import {useTagsViewRoutes} from '/@/stores/tagsViewRoutes';
|
||||
import { useRoutesList } from '/@/stores/routesList';
|
||||
import { useThemeConfig } from '/@/stores/themeConfig';
|
||||
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
|
||||
import mittBus from '/@/utils/mitt';
|
||||
import {useI18n} from 'vue-i18n';
|
||||
import {useRoute} from "vue-router";
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
// 引入组件
|
||||
const Logo = defineAsyncComponent(() => import('/@/layout/logo/index.vue'));
|
||||
@@ -78,12 +78,12 @@ const setCollapseStyle = computed(() => {
|
||||
const route = useRoute();
|
||||
// 设置是否显示左侧菜单栏
|
||||
const setShowAside = computed(() => {
|
||||
let { layout } = themeConfig.value;
|
||||
if(layout !== 'classic'){
|
||||
return true
|
||||
}
|
||||
// 首页不显示侧边栏
|
||||
return route.path !== '/home'
|
||||
let { layout } = themeConfig.value;
|
||||
if (layout !== 'classic') {
|
||||
return true;
|
||||
}
|
||||
// 首页不显示侧边栏
|
||||
return route.path !== '/home';
|
||||
});
|
||||
|
||||
// 设置显示/隐藏 logo
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
</el-scrollbar>
|
||||
<el-backtop :target="setBacktopClass" />
|
||||
<check-token />
|
||||
<chat/>
|
||||
<chat />
|
||||
</el-main>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -200,10 +200,10 @@ const onLockScreenSubmit = async () => {
|
||||
} catch (err: any) {
|
||||
// 捕获异常并将错误提示信息赋值给mes变量
|
||||
mes.value = err.msg;
|
||||
themeConfig.value.isLockScreen = false;
|
||||
themeConfig.value.lockScreenTime = 30;
|
||||
// 将最新的主题配置保存到本地存储中
|
||||
setLocalThemeConfig();
|
||||
themeConfig.value.isLockScreen = false;
|
||||
themeConfig.value.lockScreenTime = 30;
|
||||
// 将最新的主题配置保存到本地存储中
|
||||
setLocalThemeConfig();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,90 +1,90 @@
|
||||
<template>
|
||||
<div class="layout-logo" v-if="setShowLogo" @click="onThemeConfigChange" :style="{backgroundColor: setBackgroundColor}">
|
||||
<span :style="{color:setFontColor}">{{ themeConfig.globalTitle }}</span>
|
||||
</div>
|
||||
<div class="layout-logo-size" v-else @click="onThemeConfigChange">
|
||||
<img :src="logoMini" class="layout-logo-size-img"/>
|
||||
</div>
|
||||
<div class="layout-logo" v-if="setShowLogo" @click="onThemeConfigChange" :style="{ backgroundColor: setBackgroundColor }">
|
||||
<span :style="{ color: setFontColor }">{{ themeConfig.globalTitle }}</span>
|
||||
</div>
|
||||
<div class="layout-logo-size" v-else @click="onThemeConfigChange">
|
||||
<img :src="logoMini" class="layout-logo-size-img" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="layoutLogo">
|
||||
import {useThemeConfig} from '/@/stores/themeConfig';
|
||||
import { useThemeConfig } from '/@/stores/themeConfig';
|
||||
import logoMini from '/@/assets/logo-mini.svg';
|
||||
|
||||
// 定义变量内容
|
||||
const storesThemeConfig = useThemeConfig();
|
||||
const {themeConfig} = storeToRefs(storesThemeConfig);
|
||||
const { themeConfig } = storeToRefs(storesThemeConfig);
|
||||
|
||||
// 设置 logo 的显示。classic 经典布局默认显示 logo
|
||||
const setShowLogo = computed(() => {
|
||||
let {isCollapse, layout} = themeConfig.value;
|
||||
return !isCollapse || layout === 'classic' || document.body.clientWidth < 1000;
|
||||
let { isCollapse, layout } = themeConfig.value;
|
||||
return !isCollapse || layout === 'classic' || document.body.clientWidth < 1000;
|
||||
});
|
||||
|
||||
// 设置 title 的显示颜色。使用顶栏配置,与顶部导航栏保持一致
|
||||
const setFontColor = computed(() => {
|
||||
// let {layout} = themeConfig.value;
|
||||
// return layout === 'classic' || layout === 'transverse' ? `var(--next-bg-topBarColor)` : 'var(--el-color-primary)';
|
||||
return themeConfig.value.topBarColor;
|
||||
// let {layout} = themeConfig.value;
|
||||
// return layout === 'classic' || layout === 'transverse' ? `var(--next-bg-topBarColor)` : 'var(--el-color-primary)';
|
||||
return themeConfig.value.topBarColor;
|
||||
});
|
||||
|
||||
// 设置背景色。使用顶栏配置,与顶部导航栏保持一致
|
||||
const setBackgroundColor = computed(() => {
|
||||
return themeConfig.value.topBar;
|
||||
return themeConfig.value.topBar;
|
||||
});
|
||||
|
||||
// logo 点击实现菜单展开/收起
|
||||
const onThemeConfigChange = () => {
|
||||
if (themeConfig.value.layout === 'transverse') return false;
|
||||
themeConfig.value.isCollapse = !themeConfig.value.isCollapse;
|
||||
if (themeConfig.value.layout === 'transverse') return false;
|
||||
themeConfig.value.isCollapse = !themeConfig.value.isCollapse;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.layout-logo {
|
||||
width: 200px;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: rgb(0 21 41 / 2%) 0px 1px 4px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
animation: logoAnimation 0.3s ease-in-out;
|
||||
// 背景色通过内联样式动态设置,使用顶栏配置
|
||||
width: 200px;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: rgb(0 21 41 / 2%) 0px 1px 4px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
animation: logoAnimation 0.3s ease-in-out;
|
||||
// 背景色通过内联样式动态设置,使用顶栏配置
|
||||
|
||||
span {
|
||||
white-space: nowrap;
|
||||
display: inline-block;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
white-space: nowrap;
|
||||
// 文字颜色通过内联样式动态设置,使用顶栏配置
|
||||
}
|
||||
span {
|
||||
white-space: nowrap;
|
||||
display: inline-block;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
white-space: nowrap;
|
||||
// 文字颜色通过内联样式动态设置,使用顶栏配置
|
||||
}
|
||||
|
||||
&:hover {
|
||||
span {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
span {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.layout-logo-size {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
animation: logoAnimation 0.3s ease-in-out;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
animation: logoAnimation 0.3s ease-in-out;
|
||||
|
||||
&-img {
|
||||
width: 20px;
|
||||
margin: auto;
|
||||
}
|
||||
&-img {
|
||||
width: 20px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
img {
|
||||
animation: logoAnimation 0.3s ease-in-out;
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
img {
|
||||
animation: logoAnimation 0.3s ease-in-out;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="layoutClassic">
|
||||
import {useThemeConfig} from '/@/stores/themeConfig';
|
||||
import { useThemeConfig } from '/@/stores/themeConfig';
|
||||
|
||||
// 引入组件
|
||||
const LayoutAside = defineAsyncComponent(() => import('/@/layout/component/aside.vue'));
|
||||
@@ -28,11 +28,11 @@ const { themeConfig } = storeToRefs(storesThemeConfig);
|
||||
|
||||
// 判断是否显示 tasgview
|
||||
const isTagsview = computed(() => {
|
||||
let { layout } = themeConfig.value;
|
||||
// 经典模式首页没有tagview
|
||||
if(layout === 'classic' && route.path === '/home'){
|
||||
return false
|
||||
}
|
||||
let { layout } = themeConfig.value;
|
||||
// 经典模式首页没有tagview
|
||||
if (layout === 'classic' && route.path === '/home') {
|
||||
return false;
|
||||
}
|
||||
return themeConfig.value.isTagsview;
|
||||
});
|
||||
// 重置滚动条高度,更新子级 scrollbar
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
<template>
|
||||
<el-drawer
|
||||
v-model="visible"
|
||||
title="上传和下载任务"
|
||||
direction="rtl"
|
||||
size="50%"
|
||||
destroy-on-close
|
||||
@open="onOpen"
|
||||
>
|
||||
<el-drawer v-model="visible" title="上传和下载任务" direction="rtl" size="50%" destroy-on-close @open="onOpen">
|
||||
<el-tabs v-model="activeTab">
|
||||
<el-tab-pane label="上传" name="upload" />
|
||||
<el-tab-pane label="下载" name="download" />
|
||||
@@ -28,12 +21,20 @@
|
||||
>
|
||||
<el-table-column label="所属模块" prop="moduleName" width="120" show-overflow-tooltip />
|
||||
<el-table-column label="任务类型" prop="typeLabel" width="100" show-overflow-tooltip />
|
||||
<el-table-column label="任务名称" prop="detailType" min-width="150" show-overflow-tooltip >
|
||||
<template #default="{ row }">
|
||||
<el-button v-if="row.type==2" type="text" icon="Download" class="task-name-text" :loading="downloadingId === row.id" @click="handleDownloadFile(row)">{{row.detailType}}</el-button>
|
||||
<span v-else class="task-name-text">{{row.detailType}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="任务名称" prop="detailType" min-width="150" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-button
|
||||
v-if="row.type == 2"
|
||||
type="text"
|
||||
icon="Download"
|
||||
class="task-name-text"
|
||||
:loading="downloadingId === row.id"
|
||||
@click="handleDownloadFile(row)"
|
||||
>{{ row.detailType }}</el-button
|
||||
>
|
||||
<span v-else class="task-name-text">{{ row.detailType }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="任务状态" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-tag>{{ row.status }}</el-tag>
|
||||
@@ -62,25 +63,25 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, watch, computed } from 'vue'
|
||||
import { fetchList, downloadTaskFile } from '/@/api/basic/basicasynctask'
|
||||
import { useMessage } from '/@/hooks/message'
|
||||
import { ref, reactive, watch, computed } from 'vue';
|
||||
import { fetchList, downloadTaskFile } from '/@/api/basic/basicasynctask';
|
||||
import { useMessage } from '/@/hooks/message';
|
||||
|
||||
type TaskTab = 'upload' | 'download' | 'other'
|
||||
type TaskTab = 'upload' | 'download' | 'other';
|
||||
|
||||
const visible = ref(false)
|
||||
const activeTab = ref<TaskTab>('upload')
|
||||
const loading = reactive<Record<TaskTab, boolean>>({ upload: false, download: false, other: false })
|
||||
const list = reactive<Record<TaskTab, any[]>>({ upload: [], download: [], other: [] })
|
||||
const visible = ref(false);
|
||||
const activeTab = ref<TaskTab>('upload');
|
||||
const loading = reactive<Record<TaskTab, boolean>>({ upload: false, download: false, other: false });
|
||||
const list = reactive<Record<TaskTab, any[]>>({ upload: [], download: [], other: [] });
|
||||
const pagination = reactive<Record<TaskTab, { current: number; size: number; total: number }>>({
|
||||
upload: { current: 1, size: 10, total: 0 },
|
||||
download: { current: 1, size: 10, total: 0 },
|
||||
other: { current: 1, size: 10, total: 0 },
|
||||
})
|
||||
});
|
||||
|
||||
/** Tab 对应接口 type:1 上传 2 下载 3 其他 */
|
||||
const TAB_TYPE_MAP: Record<TaskTab, number> = { upload: 1, download: 2, other: 3 }
|
||||
const EMPTY_TEXT_MAP: Record<TaskTab, string> = { upload: '暂无上传任务', download: '暂无下载任务', other: '暂无其他任务' }
|
||||
const TAB_TYPE_MAP: Record<TaskTab, number> = { upload: 1, download: 2, other: 3 };
|
||||
const EMPTY_TEXT_MAP: Record<TaskTab, string> = { upload: '暂无上传任务', download: '暂无下载任务', other: '暂无其他任务' };
|
||||
|
||||
// 表格样式,参考主列表页通用样式
|
||||
const tableStyle = {
|
||||
@@ -90,87 +91,85 @@ const tableStyle = {
|
||||
background: 'var(--el-table-row-hover-bg-color)',
|
||||
color: 'var(--el-text-color-primary)',
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
const emptyText = computed(() => EMPTY_TEXT_MAP[activeTab.value])
|
||||
const message = useMessage()
|
||||
const emptyText = computed(() => EMPTY_TEXT_MAP[activeTab.value]);
|
||||
const message = useMessage();
|
||||
|
||||
const loadList = async () => {
|
||||
const type = activeTab.value
|
||||
const p = pagination[type]
|
||||
loading[type] = true
|
||||
const type = activeTab.value;
|
||||
const p = pagination[type];
|
||||
loading[type] = true;
|
||||
try {
|
||||
const res = await fetchList({ type: TAB_TYPE_MAP[type], current: p.current, size: p.size })
|
||||
const data = res?.data ?? res
|
||||
const records = data?.records ?? []
|
||||
const total = data?.total ?? 0
|
||||
list[type] = Array.isArray(records) ? records : []
|
||||
p.total = Number(total) || 0
|
||||
const res = await fetchList({ type: TAB_TYPE_MAP[type], current: p.current, size: p.size });
|
||||
const data = res?.data ?? res;
|
||||
const records = data?.records ?? [];
|
||||
const total = data?.total ?? 0;
|
||||
list[type] = Array.isArray(records) ? records : [];
|
||||
p.total = Number(total) || 0;
|
||||
} catch {
|
||||
list[type] = []
|
||||
list[type] = [];
|
||||
} finally {
|
||||
loading[type] = false
|
||||
loading[type] = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onPageChange = (page: number) => {
|
||||
pagination[activeTab.value].current = page
|
||||
loadList()
|
||||
}
|
||||
pagination[activeTab.value].current = page;
|
||||
loadList();
|
||||
};
|
||||
|
||||
const onSizeChange = (size: number) => {
|
||||
pagination[activeTab.value].size = size
|
||||
pagination[activeTab.value].current = 1
|
||||
loadList()
|
||||
}
|
||||
pagination[activeTab.value].size = size;
|
||||
pagination[activeTab.value].current = 1;
|
||||
loadList();
|
||||
};
|
||||
|
||||
const onOpen = () => {
|
||||
loadList()
|
||||
}
|
||||
loadList();
|
||||
};
|
||||
|
||||
// 切换 Tab 时加载对应列表
|
||||
watch(activeTab, () => {
|
||||
if (visible.value) loadList()
|
||||
})
|
||||
if (visible.value) loadList();
|
||||
});
|
||||
|
||||
const formatTime = (item: any) => {
|
||||
const t = item?.createTime ?? item?.create_time ?? item?.updateTime ?? item?.update_time ?? ''
|
||||
return t ? (t.slice(0, 16).replace('T', ' ')) : '-'
|
||||
}
|
||||
const t = item?.createTime ?? item?.create_time ?? item?.updateTime ?? item?.update_time ?? '';
|
||||
return t ? t.slice(0, 16).replace('T', ' ') : '-';
|
||||
};
|
||||
|
||||
const open = () => {
|
||||
visible.value = true
|
||||
}
|
||||
visible.value = true;
|
||||
};
|
||||
|
||||
const downloadingId = ref<string | number | null>(null)
|
||||
const downloadingId = ref<string | number | null>(null);
|
||||
const handleDownloadFile = async (row: any) => {
|
||||
if (!row?.id) return
|
||||
downloadingId.value = row.id
|
||||
if (!row?.id) return;
|
||||
downloadingId.value = row.id;
|
||||
try {
|
||||
const response: any = await downloadTaskFile({ id: row.id })
|
||||
const blob = (response && response.data instanceof Blob)
|
||||
? response.data
|
||||
: (response instanceof Blob ? response : new Blob([response]))
|
||||
const dateStr = new Date().toISOString().slice(0, 10)
|
||||
const baseName = row.detailType ? String(row.detailType).replace(/\s+/g, '_') : '下载文件'
|
||||
const fileName = `${baseName}_${dateStr}.xls`
|
||||
const elink = document.createElement('a')
|
||||
elink.download = fileName
|
||||
elink.style.display = 'none'
|
||||
elink.href = URL.createObjectURL(blob)
|
||||
document.body.appendChild(elink)
|
||||
elink.click()
|
||||
URL.revokeObjectURL(elink.href)
|
||||
document.body.removeChild(elink)
|
||||
message.success('下载成功')
|
||||
const response: any = await downloadTaskFile({ id: row.id });
|
||||
const blob = response && response.data instanceof Blob ? response.data : response instanceof Blob ? response : new Blob([response]);
|
||||
const dateStr = new Date().toISOString().slice(0, 10);
|
||||
const baseName = row.detailType ? String(row.detailType).replace(/\s+/g, '_') : '下载文件';
|
||||
const fileName = `${baseName}_${dateStr}.xls`;
|
||||
const elink = document.createElement('a');
|
||||
elink.download = fileName;
|
||||
elink.style.display = 'none';
|
||||
elink.href = URL.createObjectURL(blob);
|
||||
document.body.appendChild(elink);
|
||||
elink.click();
|
||||
URL.revokeObjectURL(elink.href);
|
||||
document.body.removeChild(elink);
|
||||
message.success('下载成功');
|
||||
} catch {
|
||||
message.error('下载失败')
|
||||
message.error('下载失败');
|
||||
} finally {
|
||||
downloadingId.value = null
|
||||
downloadingId.value = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
defineExpose({ open })
|
||||
defineExpose({ open });
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -11,11 +11,13 @@
|
||||
<el-breadcrumb-item v-for="(v, k) in state.breadcrumbList" :key="!v.meta.tagsViewName ? v.name : v.meta.tagsViewName">
|
||||
<span v-if="k === state.breadcrumbList.length - 1" class="layout-navbars-breadcrumb-span">
|
||||
<SvgIcon :name="v.meta.icon" class="layout-navbars-breadcrumb-iconfont" v-if="themeConfig.isBreadcrumbIcon" />
|
||||
<div v-if="!v.meta.tagsViewName">{{ $t(v.name.split("_")[0]) }}</div>
|
||||
<div v-else>{{ v.meta.tagsViewName.split("_")[0] }}</div>
|
||||
<div v-if="!v.meta.tagsViewName">{{ $t(v.name.split('_')[0]) }}</div>
|
||||
<div v-else>{{ v.meta.tagsViewName.split('_')[0] }}</div>
|
||||
</span>
|
||||
<a v-else @click.prevent="onBreadcrumbClick(v)">
|
||||
<SvgIcon :name="v.meta.icon" class="layout-navbars-breadcrumb-iconfont" v-if="themeConfig.isBreadcrumbIcon" />{{ $t(v.name.split('_')[0]) }}
|
||||
<SvgIcon :name="v.meta.icon" class="layout-navbars-breadcrumb-iconfont" v-if="themeConfig.isBreadcrumbIcon" />{{
|
||||
$t(v.name.split('_')[0])
|
||||
}}
|
||||
</a>
|
||||
</el-breadcrumb-item>
|
||||
</transition-group>
|
||||
@@ -85,21 +87,21 @@ const getBreadcrumbList = (arr: RouteItems) => {
|
||||
|
||||
// 当前路由字符串切割成数组,并删除第一项空内容
|
||||
const initRouteSplit = (toRoute: RouteLocation) => {
|
||||
let path = toRoute.path;
|
||||
if (!themeConfig.value.isBreadcrumb) return false;
|
||||
state.breadcrumbList = [routesList.value[0]];
|
||||
state.routeSplit = path.split('/');
|
||||
state.routeSplit.shift();
|
||||
state.routeSplitFirst = `/${state.routeSplit[0]}`;
|
||||
state.routeSplitIndex = 1;
|
||||
getBreadcrumbList(routesList.value);
|
||||
state.breadcrumbList.push(route);
|
||||
// 首页或异常页只显示第一个
|
||||
if (toRoute.name === 'router.home' || (toRoute.name === 'staticRoutes.notFound' && state.breadcrumbList.length > 1)) {
|
||||
state.breadcrumbList.splice(0, state.breadcrumbList.length - 1);
|
||||
} else if (state.breadcrumbList.length > 0) {
|
||||
state.breadcrumbList[state.breadcrumbList.length - 1].meta.tagsViewName = other.setTagsViewNameI18n(<RouteToFrom>route);
|
||||
}
|
||||
let path = toRoute.path;
|
||||
if (!themeConfig.value.isBreadcrumb) return false;
|
||||
state.breadcrumbList = [routesList.value[0]];
|
||||
state.routeSplit = path.split('/');
|
||||
state.routeSplit.shift();
|
||||
state.routeSplitFirst = `/${state.routeSplit[0]}`;
|
||||
state.routeSplitIndex = 1;
|
||||
getBreadcrumbList(routesList.value);
|
||||
state.breadcrumbList.push(route);
|
||||
// 首页或异常页只显示第一个
|
||||
if (toRoute.name === 'router.home' || (toRoute.name === 'staticRoutes.notFound' && state.breadcrumbList.length > 1)) {
|
||||
state.breadcrumbList.splice(0, state.breadcrumbList.length - 1);
|
||||
} else if (state.breadcrumbList.length > 0) {
|
||||
state.breadcrumbList[state.breadcrumbList.length - 1].meta.tagsViewName = other.setTagsViewNameI18n(<RouteToFrom>route);
|
||||
}
|
||||
};
|
||||
|
||||
// 页面加载时
|
||||
|
||||
@@ -59,41 +59,41 @@ const delClassicChildren = <T extends ChilType>(arr: T[]): T[] => {
|
||||
|
||||
// 路由过滤递归函数
|
||||
const filterRoutesFun = <T extends RouteItem>(arr: T[]): T[] => {
|
||||
return arr.reduce<T[]>((acc, item) => {
|
||||
if (!item.meta?.isHide) {
|
||||
const newItem = { ...item };
|
||||
if (newItem.children) newItem.children = filterRoutesFun(newItem.children);
|
||||
acc.push(newItem);
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
return arr.reduce<T[]>((acc, item) => {
|
||||
if (!item.meta?.isHide) {
|
||||
const newItem = { ...item };
|
||||
if (newItem.children) newItem.children = filterRoutesFun(newItem.children);
|
||||
acc.push(newItem);
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
};
|
||||
|
||||
// 传送当前子级数据到菜单中
|
||||
const setSendClassicChildren = (path: string) => {
|
||||
let currentData: MittMenu = { children: [] };
|
||||
const route = searchParent(routesList.value, path as string);
|
||||
if (route) {
|
||||
const filteredRoutes = filterRoutesFun(routesList.value);
|
||||
const matchedRoute = filteredRoutes.find(v => v.path === route.path);
|
||||
if (matchedRoute) {
|
||||
currentData['item'] = { ...matchedRoute };
|
||||
currentData['children'] = matchedRoute.children || [];
|
||||
}
|
||||
}
|
||||
return currentData;
|
||||
let currentData: MittMenu = { children: [] };
|
||||
const route = searchParent(routesList.value, path as string);
|
||||
if (route) {
|
||||
const filteredRoutes = filterRoutesFun(routesList.value);
|
||||
const matchedRoute = filteredRoutes.find((v) => v.path === route.path);
|
||||
if (matchedRoute) {
|
||||
currentData['item'] = { ...matchedRoute };
|
||||
currentData['children'] = matchedRoute.children || [];
|
||||
}
|
||||
}
|
||||
return currentData;
|
||||
};
|
||||
|
||||
// 使用递归查询对应的父级路由
|
||||
const searchParent = (routesList: any, path: string) => {
|
||||
for (const item of routesList) {
|
||||
if (item.path === path) return item;
|
||||
if (item.children) {
|
||||
const parent = searchParent(item.children, path);
|
||||
if (parent) return item;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
for (const item of routesList) {
|
||||
if (item.path === path) return item;
|
||||
if (item.children) {
|
||||
const parent = searchParent(item.children, path);
|
||||
if (parent) return item;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
// 页面加载时
|
||||
|
||||
@@ -95,7 +95,7 @@ defineExpose({
|
||||
.layout-search-dialog {
|
||||
position: relative;
|
||||
:deep(.el-dialog) {
|
||||
width: 560px;
|
||||
width: 560px;
|
||||
.el-dialog__header,
|
||||
.el-dialog__body {
|
||||
display: none;
|
||||
@@ -108,8 +108,8 @@ defineExpose({
|
||||
}
|
||||
}
|
||||
:deep(.el-autocomplete) {
|
||||
position: absolute;
|
||||
width: 560px;
|
||||
position: absolute;
|
||||
width: 560px;
|
||||
top: 53vh;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
|
||||
@@ -21,14 +21,13 @@
|
||||
<ele-Lock />
|
||||
</el-icon>
|
||||
</div>
|
||||
<div class="layout-navbars-breadcrumb-user-icon" v-if="useFlowJob().jsonFlowEnable()" @click="useFlowJob().onTodoJobClick()">
|
||||
<el-badge :value="useFlowJob().jobLen"
|
||||
:max="999">
|
||||
<el-icon :title="`${useFlowJob().jobLen}` + $t('user.title7')">
|
||||
<ele-ChatLineRound />
|
||||
</el-icon>
|
||||
</el-badge>
|
||||
</div>
|
||||
<div class="layout-navbars-breadcrumb-user-icon" v-if="useFlowJob().jsonFlowEnable()" @click="useFlowJob().onTodoJobClick()">
|
||||
<el-badge :value="useFlowJob().jobLen" :max="999">
|
||||
<el-icon :title="`${useFlowJob().jobLen}` + $t('user.title7')">
|
||||
<ele-ChatLineRound />
|
||||
</el-icon>
|
||||
</el-badge>
|
||||
</div>
|
||||
<div class="layout-navbars-breadcrumb-user-icon" @click="onSearchClick">
|
||||
<el-icon :title="$t('user.title2')">
|
||||
<ele-Search />
|
||||
@@ -76,11 +75,11 @@
|
||||
</template>
|
||||
</el-dropdown>
|
||||
<Search ref="searchRef" />
|
||||
<!-- <global-websocket uri="/admin/ws/info" v-if="websocketEnable" @rollback="rollback" />-->
|
||||
<!-- <global-websocket uri="/jsonflow/ws/info" v-if="useFlowJob().jsonFlowEnable()" @rollback="rollback" />-->
|
||||
<!-- <global-websocket uri="/admin/ws/info" v-if="websocketEnable" @rollback="rollback" />-->
|
||||
<!-- <global-websocket uri="/jsonflow/ws/info" v-if="useFlowJob().jsonFlowEnable()" @rollback="rollback" />-->
|
||||
<personal-drawer ref="personalDrawerRef"></personal-drawer>
|
||||
|
||||
<change-role ref="ChangeRoleRef" />
|
||||
<change-role ref="ChangeRoleRef" />
|
||||
<AsyncTaskDrawer ref="asyncTaskDrawerRef" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -98,13 +97,12 @@ import { Local, Session } from '/@/utils/storage';
|
||||
import { formatAxis } from '/@/utils/formatTime';
|
||||
import { useMsg } from '/@/stores/msg';
|
||||
import { fetchUserMessageList } from '/@/api/admin/message';
|
||||
import {useFlowJob} from "/@/flow/stores/flowJob";
|
||||
import { useFlowJob } from '/@/flow/stores/flowJob';
|
||||
|
||||
|
||||
const ChangeRoleRef = ref()
|
||||
const ChangeRole = defineAsyncComponent(() => import('/@/views/admin/system/role/change-role.vue'))
|
||||
const asyncTaskDrawerRef = ref()
|
||||
const AsyncTaskDrawer = defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/asyncTaskDrawer.vue'))
|
||||
const ChangeRoleRef = ref();
|
||||
const ChangeRole = defineAsyncComponent(() => import('/@/views/admin/system/role/change-role.vue'));
|
||||
const asyncTaskDrawerRef = ref();
|
||||
const AsyncTaskDrawer = defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/asyncTaskDrawer.vue'));
|
||||
// 引入组件
|
||||
const GlobalWebsocket = defineAsyncComponent(() => import('/@/components/Websocket/index.vue'));
|
||||
const UserNews = defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/userNews.vue'));
|
||||
@@ -204,10 +202,9 @@ const onHandleCommandClick = (path: string) => {
|
||||
} else if (path === 'personal') {
|
||||
// 打开个人页面
|
||||
personalDrawerRef.value.open();
|
||||
}else if(path === 'changeRole'){
|
||||
ChangeRoleRef.value.open()
|
||||
}
|
||||
else {
|
||||
} else if (path === 'changeRole') {
|
||||
ChangeRoleRef.value.open();
|
||||
} else {
|
||||
router.push(path);
|
||||
}
|
||||
};
|
||||
@@ -261,8 +258,8 @@ onMounted(() => {
|
||||
initI18nOrSize('globalComponentSize', 'disabledSize');
|
||||
initI18nOrSize('globalI18n', 'disabledI18n');
|
||||
}
|
||||
useFlowJob().topJobList()
|
||||
getIsDot()
|
||||
useFlowJob().topJobList();
|
||||
getIsDot();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,120 +1,119 @@
|
||||
<template>
|
||||
<div class="layout-navbars-breadcrumb-user-news">
|
||||
<div class="head-box">
|
||||
<div class="head-box-title">{{ $t('user.newTitle') }}</div>
|
||||
<div class="head-box-btn" @click="listRef.openDialog('1')">{{ $t('user.newBtn') }}</div>
|
||||
</div>
|
||||
<div class="content-box">
|
||||
<template v-if="newsList.length > 0">
|
||||
<div class="relative">
|
||||
<div class="content-box-item" v-for="(v, k) in newsList" :key="k" @click="contentRef.openDialog(v)">
|
||||
<button
|
||||
class="py-1 px-3 -left-8 -top-2 border bg-primary text-white font-bold">
|
||||
{{parseDate(v.createTime)}}
|
||||
</button>
|
||||
<div class="purple_border p-2 border border-gray-400">
|
||||
{{ v.title }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-empty :description="$t('user.newDesc')" v-else></el-empty>
|
||||
</div>
|
||||
<div class="layout-navbars-breadcrumb-user-news">
|
||||
<div class="head-box">
|
||||
<div class="head-box-title">{{ $t('user.newTitle') }}</div>
|
||||
<div class="head-box-btn" @click="listRef.openDialog('1')">{{ $t('user.newBtn') }}</div>
|
||||
</div>
|
||||
<div class="content-box">
|
||||
<template v-if="newsList.length > 0">
|
||||
<div class="relative">
|
||||
<div class="content-box-item" v-for="(v, k) in newsList" :key="k" @click="contentRef.openDialog(v)">
|
||||
<button class="py-1 px-3 -left-8 -top-2 border bg-primary text-white font-bold">
|
||||
{{ parseDate(v.createTime) }}
|
||||
</button>
|
||||
<div class="purple_border p-2 border border-gray-400">
|
||||
{{ v.title }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-empty :description="$t('user.newDesc')" v-else></el-empty>
|
||||
</div>
|
||||
|
||||
<!-- 消息列表 -->
|
||||
<news-lists ref="listRef"/>
|
||||
<!-- 消息列表 -->
|
||||
<news-lists ref="listRef" />
|
||||
|
||||
<!-- 消息内容 -->
|
||||
<news-content ref="contentRef" @refresh="getUserMessage"/>
|
||||
</div>
|
||||
<!-- 消息内容 -->
|
||||
<news-content ref="contentRef" @refresh="getUserMessage" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="layoutBreadcrumbUserNews">
|
||||
import {fetchUserMessageList} from "/@/api/admin/message";
|
||||
import { fetchUserMessageList } from '/@/api/admin/message';
|
||||
|
||||
const NewsContent = defineAsyncComponent(() => import('/@/views/home/news/content.vue'));
|
||||
const NewsLists = defineAsyncComponent(() => import('/@/views/home/news/list.vue'));
|
||||
|
||||
const newsList = ref([])
|
||||
const listRef = ref()
|
||||
const contentRef = ref()
|
||||
const newsList = ref([]);
|
||||
const listRef = ref();
|
||||
const contentRef = ref();
|
||||
|
||||
// 获取用户的信息
|
||||
const getUserMessage = () => {
|
||||
// 取前五条数据
|
||||
return fetchUserMessageList({current: 1, size: 5, category: '1', readFlag: '0'}).then(res => {
|
||||
newsList.value = res.data.records;
|
||||
})
|
||||
}
|
||||
// 取前五条数据
|
||||
return fetchUserMessageList({ current: 1, size: 5, category: '1', readFlag: '0' }).then((res) => {
|
||||
newsList.value = res.data.records;
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getUserMessage()
|
||||
})
|
||||
getUserMessage();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.layout-navbars-breadcrumb-user-news {
|
||||
.head-box {
|
||||
display: flex;
|
||||
border-bottom: 1px solid var(--el-border-color-lighter);
|
||||
box-sizing: border-box;
|
||||
color: var(--el-text-color-primary);
|
||||
justify-content: space-between;
|
||||
height: 35px;
|
||||
align-items: center;
|
||||
.head-box {
|
||||
display: flex;
|
||||
border-bottom: 1px solid var(--el-border-color-lighter);
|
||||
box-sizing: border-box;
|
||||
color: var(--el-text-color-primary);
|
||||
justify-content: space-between;
|
||||
height: 35px;
|
||||
align-items: center;
|
||||
|
||||
.head-box-btn {
|
||||
color: var(--el-color-primary);
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
opacity: 0.8;
|
||||
.head-box-btn {
|
||||
color: var(--el-color-primary);
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
opacity: 0.8;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content-box {
|
||||
font-size: 13px;
|
||||
.content-box {
|
||||
font-size: 13px;
|
||||
|
||||
.content-box-item {
|
||||
padding-top: 12px;
|
||||
.content-box-item {
|
||||
padding-top: 12px;
|
||||
|
||||
&:last-of-type {
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
&:last-of-type {
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
||||
.content-box-msg {
|
||||
color: var(--el-text-color-secondary);
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.content-box-msg {
|
||||
color: var(--el-text-color-secondary);
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.content-box-time {
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
}
|
||||
}
|
||||
.content-box-time {
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.foot-box {
|
||||
height: 35px;
|
||||
color: var(--el-color-primary);
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
opacity: 0.8;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-top: 1px solid var(--el-border-color-lighter);
|
||||
.foot-box {
|
||||
height: 35px;
|
||||
color: var(--el-color-primary);
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
opacity: 0.8;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-top: 1px solid var(--el-border-color-lighter);
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-empty__description p) {
|
||||
font-size: 13px;
|
||||
}
|
||||
:deep(.el-empty__description p) {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -121,8 +121,7 @@ const isAffixTagAllowed = (v: RouteItem) => v.path === '/home' || v.path === '/h
|
||||
/** 后端菜单 sortOrder/sort_order/weight 为 1 的项常被误当“默认”加载为 tag,排除(首页除外) */
|
||||
const isSortOrderOneExcluded = (v: RouteItem) => {
|
||||
const meta = v.meta as any;
|
||||
const order =
|
||||
meta?.sortOrder ?? meta?.sort_order ?? meta?.weight ?? (v as any).sort_order ?? (v as any).weight;
|
||||
const order = meta?.sortOrder ?? meta?.sort_order ?? meta?.weight ?? (v as any).sort_order ?? (v as any).weight;
|
||||
return Number(order) === 1 && !isAffixTagAllowed(v);
|
||||
};
|
||||
/** 递归扁平化路由(含 children),用于校验 path 是否在当前角色菜单中 */
|
||||
@@ -137,10 +136,7 @@ const flattenRoutes = (routes: RouteItem[]): RouteItem[] => {
|
||||
/** 当前 tag 的 path 是否存在于当前角色路由列表中(避免恢复出“不存在的 tag”) */
|
||||
const pathInCurrentRoutes = (tag: RouteItem): boolean => {
|
||||
const flat = flattenRoutes(state.tagsViewRoutesList);
|
||||
return flat.some(
|
||||
(r: RouteItem) =>
|
||||
r.path === tag.path || (r.meta?.isDynamic && (r.meta as any).isDynamicPath === tag.path)
|
||||
);
|
||||
return flat.some((r: RouteItem) => r.path === tag.path || (r.meta?.isDynamic && (r.meta as any).isDynamicPath === tag.path));
|
||||
};
|
||||
// 存储 tagsViewList 到浏览器临时缓存中,页面刷新时,保留记录
|
||||
const addBrowserSetSession = (tagsViewList: Array<object>) => {
|
||||
@@ -161,11 +157,7 @@ const initTagsView = async () => {
|
||||
// 仅当当前角色已有路由列表时,才从缓存恢复;恢复时只保留当前路由中存在的 path,避免切换角色后出现“不存在的 tag”
|
||||
if (hasValidCache && state.tagsViewRoutesList.length > 0) {
|
||||
state.tagsViewList = cached.filter(
|
||||
(v: RouteItem) =>
|
||||
!isCarouselTag(v) &&
|
||||
!isSortOrderOneExcluded(v) &&
|
||||
pathInCurrentRoutes(v) &&
|
||||
(v.meta?.isAffix ? isAffixTagAllowed(v) : true)
|
||||
(v: RouteItem) => !isCarouselTag(v) && !isSortOrderOneExcluded(v) && pathInCurrentRoutes(v) && (v.meta?.isAffix ? isAffixTagAllowed(v) : true)
|
||||
);
|
||||
} else {
|
||||
await state.tagsViewRoutesList.map((v: RouteItem) => {
|
||||
@@ -429,10 +421,10 @@ const onMousedownMenu = (v: RouteItem, e: MouseEvent) => {
|
||||
};
|
||||
// 当前的 tagsView 项点击时
|
||||
const onTagsClick = (v: RouteItem, k: number) => {
|
||||
state.tagsRefsIndex = k;
|
||||
if(v.name.indexOf("router.home")!=0){
|
||||
v.name=v.name.replaceAll("_","").replaceAll(v.id,"")+"_"+v.id
|
||||
}
|
||||
state.tagsRefsIndex = k;
|
||||
if (v.name.indexOf('router.home') != 0) {
|
||||
v.name = v.name.replaceAll('_', '').replaceAll(v.id, '') + '_' + v.id;
|
||||
}
|
||||
router.push(v);
|
||||
};
|
||||
// 处理 url,地址栏链接有参数时,tagsview 右键菜单刷新功能失效问题,感谢 @ZzZz-RIPPER、@dejavuuuuu
|
||||
|
||||
@@ -1,41 +1,40 @@
|
||||
<template>
|
||||
<div class="el-menu-horizontal-warp">
|
||||
<el-scrollbar @wheel.native.prevent="onElMenuHorizontalScroll" ref="elMenuHorizontalScrollRef">
|
||||
<el-menu router :default-active="state.defaultActive" :ellipsis="false" background-color="transparent"
|
||||
mode="horizontal">
|
||||
<template v-for="val in menuLists">
|
||||
<el-sub-menu :index="val.path" v-if="val.children && val.children.length > 0" :key="val.path">
|
||||
<template #title>
|
||||
<SvgIcon :name="getMenuIcon(val.meta.icon)"/>
|
||||
<span class="font-semibold">{{ $t(val.name.split('_')[0]) }}</span>
|
||||
</template>
|
||||
<SubItem :chil="val.children"/>
|
||||
</el-sub-menu>
|
||||
<template v-else>
|
||||
<el-menu-item :index="val.path" :key="val.path">
|
||||
<template #title v-if="!val.meta.isLink || (val.meta.isLink && val.meta.isIframe)">
|
||||
<!-- 此处可 指定 color='red' 等指定顶栏SVG颜色 -->
|
||||
<SvgIcon :name="getMenuIcon(val.meta.icon)"/>
|
||||
<p class="font-semibold">{{ $t(val.name.split('_')[0]) }}</p>
|
||||
</template>
|
||||
<template #title v-else>
|
||||
<a class="w100" @click.prevent="onALinkClick(val)">
|
||||
<SvgIcon :name="getMenuIcon(val.meta.icon)"/>
|
||||
{{ $t(val.name.split('_')[0]) }}
|
||||
</a>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
</template>
|
||||
</template>
|
||||
</el-menu>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
<div class="el-menu-horizontal-warp">
|
||||
<el-scrollbar @wheel.native.prevent="onElMenuHorizontalScroll" ref="elMenuHorizontalScrollRef">
|
||||
<el-menu router :default-active="state.defaultActive" :ellipsis="false" background-color="transparent" mode="horizontal">
|
||||
<template v-for="val in menuLists">
|
||||
<el-sub-menu :index="val.path" v-if="val.children && val.children.length > 0" :key="val.path">
|
||||
<template #title>
|
||||
<SvgIcon :name="getMenuIcon(val.meta.icon)" />
|
||||
<span class="font-semibold">{{ $t(val.name.split('_')[0]) }}</span>
|
||||
</template>
|
||||
<SubItem :chil="val.children" />
|
||||
</el-sub-menu>
|
||||
<template v-else>
|
||||
<el-menu-item :index="val.path" :key="val.path">
|
||||
<template #title v-if="!val.meta.isLink || (val.meta.isLink && val.meta.isIframe)">
|
||||
<!-- 此处可 指定 color='red' 等指定顶栏SVG颜色 -->
|
||||
<SvgIcon :name="getMenuIcon(val.meta.icon)" />
|
||||
<p class="font-semibold">{{ $t(val.name.split('_')[0]) }}</p>
|
||||
</template>
|
||||
<template #title v-else>
|
||||
<a class="w100" @click.prevent="onALinkClick(val)">
|
||||
<SvgIcon :name="getMenuIcon(val.meta.icon)" />
|
||||
{{ $t(val.name.split('_')[0]) }}
|
||||
</a>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
</template>
|
||||
</template>
|
||||
</el-menu>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="navMenuHorizontal">
|
||||
import {RouteRecordRaw} from 'vue-router';
|
||||
import {useRoutesList} from '/@/stores/routesList';
|
||||
import {useThemeConfig} from '/@/stores/themeConfig';
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
import { useRoutesList } from '/@/stores/routesList';
|
||||
import { useThemeConfig } from '/@/stores/themeConfig';
|
||||
import other from '/@/utils/other';
|
||||
import mittBus from '/@/utils/mitt';
|
||||
|
||||
@@ -44,147 +43,147 @@ const SubItem = defineAsyncComponent(() => import('/@/layout/navMenu/subItem.vue
|
||||
|
||||
// 定义父组件传过来的值
|
||||
const props = defineProps({
|
||||
// 菜单列表
|
||||
menuList: {
|
||||
type: Array<RouteRecordRaw>,
|
||||
default: () => [],
|
||||
},
|
||||
// 菜单列表
|
||||
menuList: {
|
||||
type: Array<RouteRecordRaw>,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
|
||||
// 定义变量内容
|
||||
const elMenuHorizontalScrollRef = ref();
|
||||
const stores = useRoutesList();
|
||||
const storesThemeConfig = useThemeConfig();
|
||||
const {routesList} = storeToRefs(stores);
|
||||
const {themeConfig} = storeToRefs(storesThemeConfig);
|
||||
const { routesList } = storeToRefs(stores);
|
||||
const { themeConfig } = storeToRefs(storesThemeConfig);
|
||||
const route = useRoute();
|
||||
const state = reactive({
|
||||
defaultActive: '' as string | undefined,
|
||||
defaultActive: '' as string | undefined,
|
||||
});
|
||||
|
||||
// 获取父级菜单数据
|
||||
const menuLists = computed(() => {
|
||||
return <RouteItems>props.menuList;
|
||||
return <RouteItems>props.menuList;
|
||||
});
|
||||
// 获取菜单图标,如果没有则返回默认图标
|
||||
const getMenuIcon = (icon?: string) => {
|
||||
return icon && icon.trim() ? icon : 'ele-Menu';
|
||||
return icon && icon.trim() ? icon : 'ele-Menu';
|
||||
};
|
||||
// 设置横向滚动条可以鼠标滚轮滚动
|
||||
const onElMenuHorizontalScroll = (e: WheelEventType) => {
|
||||
const eventDelta = e.wheelDelta || -e.deltaY * 40;
|
||||
elMenuHorizontalScrollRef.value.$refs.wrapRef.scrollLeft = elMenuHorizontalScrollRef.value.$refs.wrapRef.scrollLeft + eventDelta / 4;
|
||||
const eventDelta = e.wheelDelta || -e.deltaY * 40;
|
||||
elMenuHorizontalScrollRef.value.$refs.wrapRef.scrollLeft = elMenuHorizontalScrollRef.value.$refs.wrapRef.scrollLeft + eventDelta / 4;
|
||||
};
|
||||
// 初始化数据,页面刷新时,滚动条滚动到对应位置
|
||||
const initElMenuOffsetLeft = () => {
|
||||
nextTick(() => {
|
||||
let els = <HTMLElement>document.querySelector('.el-menu.el-menu--horizontal li.is-active');
|
||||
if (!els) return false;
|
||||
elMenuHorizontalScrollRef.value.$refs.wrapRef.scrollLeft = els.offsetLeft;
|
||||
});
|
||||
nextTick(() => {
|
||||
let els = <HTMLElement>document.querySelector('.el-menu.el-menu--horizontal li.is-active');
|
||||
if (!els) return false;
|
||||
elMenuHorizontalScrollRef.value.$refs.wrapRef.scrollLeft = els.offsetLeft;
|
||||
});
|
||||
};
|
||||
// 路由过滤递归函数
|
||||
const filterRoutesFun = <T extends RouteItem>(arr: T[]): T[] => {
|
||||
return arr
|
||||
.filter((item: T) => !item.meta?.isHide)
|
||||
.map((item: T) => {
|
||||
item = Object.assign({}, item);
|
||||
if (item.children) item.children = filterRoutesFun(item.children);
|
||||
return item;
|
||||
});
|
||||
return arr
|
||||
.filter((item: T) => !item.meta?.isHide)
|
||||
.map((item: T) => {
|
||||
item = Object.assign({}, item);
|
||||
if (item.children) item.children = filterRoutesFun(item.children);
|
||||
return item;
|
||||
});
|
||||
};
|
||||
// 传送当前子级数据到菜单中
|
||||
const setSendClassicChildren = (path: string) => {
|
||||
let currentData: MittMenu = {children: []};
|
||||
if (!state.defaultActive) {
|
||||
const route = searchParent(routesList.value, path as string) as any;
|
||||
state.defaultActive = route!.path;
|
||||
}
|
||||
filterRoutesFun(routesList.value).map((v, k) => {
|
||||
if (v.path === state.defaultActive) {
|
||||
v['k'] = k;
|
||||
currentData['item'] = {...v};
|
||||
currentData['children'] = [{...v}];
|
||||
if (v.children) currentData['children'] = v.children;
|
||||
}
|
||||
});
|
||||
return currentData;
|
||||
let currentData: MittMenu = { children: [] };
|
||||
if (!state.defaultActive) {
|
||||
const route = searchParent(routesList.value, path as string) as any;
|
||||
state.defaultActive = route!.path;
|
||||
}
|
||||
filterRoutesFun(routesList.value).map((v, k) => {
|
||||
if (v.path === state.defaultActive) {
|
||||
v['k'] = k;
|
||||
currentData['item'] = { ...v };
|
||||
currentData['children'] = [{ ...v }];
|
||||
if (v.children) currentData['children'] = v.children;
|
||||
}
|
||||
});
|
||||
return currentData;
|
||||
};
|
||||
// 设置页面当前路由高亮
|
||||
const setCurrentRouterHighlight = (currentRoute: RouteToFrom) => {
|
||||
const {path, meta} = currentRoute;
|
||||
const route = searchParent(routesList.value, path as string) as any;
|
||||
if (themeConfig.value.layout === 'classic') {
|
||||
if (route) {
|
||||
state.defaultActive = route!.path;
|
||||
} else {
|
||||
state.defaultActive = `/${path?.split('/')[1]}`;
|
||||
}
|
||||
} else {
|
||||
const pathSplit = meta?.isDynamic ? meta.isDynamicPath!.split('/') : path!.split('/');
|
||||
if (pathSplit.length >= 4 && meta?.isHide) state.defaultActive = pathSplit.splice(0, 3).join('/');
|
||||
else state.defaultActive = path;
|
||||
}
|
||||
const { path, meta } = currentRoute;
|
||||
const route = searchParent(routesList.value, path as string) as any;
|
||||
if (themeConfig.value.layout === 'classic') {
|
||||
if (route) {
|
||||
state.defaultActive = route!.path;
|
||||
} else {
|
||||
state.defaultActive = `/${path?.split('/')[1]}`;
|
||||
}
|
||||
} else {
|
||||
const pathSplit = meta?.isDynamic ? meta.isDynamicPath!.split('/') : path!.split('/');
|
||||
if (pathSplit.length >= 4 && meta?.isHide) state.defaultActive = pathSplit.splice(0, 3).join('/');
|
||||
else state.defaultActive = path;
|
||||
}
|
||||
};
|
||||
|
||||
// 使用递归查询对应的父级路由
|
||||
const searchParent = (routesList: any, path: string) => {
|
||||
let route = undefined;
|
||||
routesList.forEach((item) => {
|
||||
if (item.path === path) {
|
||||
route = item;
|
||||
return;
|
||||
}
|
||||
if (item.children && searchParent(item.children, path)) {
|
||||
route = item;
|
||||
return;
|
||||
}
|
||||
});
|
||||
return route;
|
||||
let route = undefined;
|
||||
routesList.forEach((item) => {
|
||||
if (item.path === path) {
|
||||
route = item;
|
||||
return;
|
||||
}
|
||||
if (item.children && searchParent(item.children, path)) {
|
||||
route = item;
|
||||
return;
|
||||
}
|
||||
});
|
||||
return route;
|
||||
};
|
||||
|
||||
// 打开外部链接
|
||||
const onALinkClick = (val: RouteItem) => {
|
||||
other.handleOpenLink(val);
|
||||
other.handleOpenLink(val);
|
||||
};
|
||||
// 页面加载前
|
||||
onBeforeMount(() => {
|
||||
setCurrentRouterHighlight(route);
|
||||
setCurrentRouterHighlight(route);
|
||||
});
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
initElMenuOffsetLeft();
|
||||
initElMenuOffsetLeft();
|
||||
});
|
||||
// 路由更新时
|
||||
onBeforeRouteUpdate((to) => {
|
||||
setCurrentRouterHighlight(to);
|
||||
// 修复经典布局开启切割菜单时,点击tagsView后左侧导航菜单数据不变的问题
|
||||
let {layout, isClassicSplitMenu} = themeConfig.value;
|
||||
if (layout === 'classic' && isClassicSplitMenu) {
|
||||
mittBus.emit('setSendClassicChildren', setSendClassicChildren(to.path));
|
||||
}
|
||||
setCurrentRouterHighlight(to);
|
||||
// 修复经典布局开启切割菜单时,点击tagsView后左侧导航菜单数据不变的问题
|
||||
let { layout, isClassicSplitMenu } = themeConfig.value;
|
||||
if (layout === 'classic' && isClassicSplitMenu) {
|
||||
mittBus.emit('setSendClassicChildren', setSendClassicChildren(to.path));
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.el-menu-horizontal-warp {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
margin-right: 30px;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
margin-right: 30px;
|
||||
|
||||
:deep(.el-scrollbar__bar.is-vertical) {
|
||||
display: none;
|
||||
}
|
||||
:deep(.el-scrollbar__bar.is-vertical) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:deep(a) {
|
||||
width: 100%;
|
||||
}
|
||||
:deep(a) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.el-menu.el-menu--horizontal {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.el-menu.el-menu--horizontal {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -3,26 +3,26 @@
|
||||
<el-sub-menu :index="val.path" :key="val.path" v-if="val.children && val.children.length > 0">
|
||||
<template #title>
|
||||
<SvgIcon :name="getMenuIcon(val.meta.icon)" />
|
||||
<!-- <span>{{ $t(val.name) }}</span>-->
|
||||
<span>{{ $t(val.name.split('_')[0]) }}</span>
|
||||
<!-- <span>{{ $t(val.name) }}</span>-->
|
||||
<span>{{ $t(val.name.split('_')[0]) }}</span>
|
||||
</template>
|
||||
<sub-item :chil="val.children" />
|
||||
<sub-item :chil="val.children" />
|
||||
</el-sub-menu>
|
||||
<template v-else>
|
||||
<el-menu-item :index="val.path" :key="val.path">
|
||||
<template v-if="!val.meta.isLink || (val.meta.isLink && val.meta.isIframe)">
|
||||
<SvgIcon :name="getMenuIcon(val.meta.icon)" />
|
||||
<!-- <span>{{ $t(val.name) }}</span>-->
|
||||
<span>{{ $t(val.name.split('_')[0]) }}</span>
|
||||
</template>
|
||||
<!-- <span>{{ $t(val.name) }}</span>-->
|
||||
<span>{{ $t(val.name.split('_')[0]) }}</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a class="w100" @click.prevent="onALinkClick(val)">
|
||||
<a class="w100" @click.prevent="onALinkClick(val)">
|
||||
<SvgIcon :name="getMenuIcon(val.meta.icon)" />
|
||||
<!-- {{ $t(val.name) }}-->
|
||||
{{ $t(val.name.split('_')[0]) }}
|
||||
<!-- {{ $t(val.name) }}-->
|
||||
{{ $t(val.name.split('_')[0]) }}
|
||||
</a>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
</el-menu-item>
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
:collapse-transition="false"
|
||||
>
|
||||
<template v-for="val in menuLists">
|
||||
<!-- 三级菜单 -->
|
||||
<!-- 三级菜单 -->
|
||||
<el-sub-menu :index="val.path" v-if="val.children && val.children.length > 0" :key="val.path">
|
||||
<template #title>
|
||||
<SvgIcon :name="getMenuIcon(val.meta.icon)" />
|
||||
@@ -17,7 +17,7 @@
|
||||
<SubItem :chil="val.children" />
|
||||
</el-sub-menu>
|
||||
<template v-else>
|
||||
<!-- 二级菜单 -->
|
||||
<!-- 二级菜单 -->
|
||||
<el-menu-item :index="val.path" :key="val.path">
|
||||
<SvgIcon :name="getMenuIcon(val.meta.icon)" />
|
||||
<template #title v-if="!val.meta.isLink || (val.meta.isLink && val.meta.isIframe)">
|
||||
@@ -33,8 +33,8 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="navMenuVertical">
|
||||
import {RouteRecordRaw} from 'vue-router';
|
||||
import {useThemeConfig} from '/@/stores/themeConfig';
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
import { useThemeConfig } from '/@/stores/themeConfig';
|
||||
import other from '/@/utils/other';
|
||||
|
||||
// 引入组件
|
||||
|
||||
Reference in New Issue
Block a user