20260712212917
This commit is contained in:
1 parent
4677f6c519
commit
00472688ab
361 files changed
-153949
No files matched your search
@@ -1,109 +0,0 @@
|
||||
<script setup>
|
||||
import { computed, defineComponent, h, watch } from "vue";
|
||||
import { RouterView } from "vue-router";
|
||||
import { useTabStore } from "@/store/tab";
|
||||
|
||||
const tabStore = useTabStore();
|
||||
|
||||
const includeKeys = computed(() => tabStore.cachedKeys);
|
||||
const wrapperMap = new Map();
|
||||
|
||||
function formatComponentInstance(component, fullPath) {
|
||||
if (!component) return null;
|
||||
|
||||
let wrapper = wrapperMap.get(fullPath);
|
||||
|
||||
if (!wrapper) {
|
||||
wrapper = defineComponent({
|
||||
name: fullPath,
|
||||
inheritAttrs: false,
|
||||
setup(_, { attrs }) {
|
||||
return () => h("div", { class: "g3-content-page" }, [h(component, attrs)]);
|
||||
},
|
||||
});
|
||||
|
||||
wrapperMap.set(fullPath, wrapper);
|
||||
}
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
watch(
|
||||
() => tabStore.tabs,
|
||||
(tabs) => {
|
||||
const alive = new Set(tabs.map((t) => t.fullPath));
|
||||
for (const key of wrapperMap.keys()) {
|
||||
if (!alive.has(key)) {
|
||||
wrapperMap.delete(key);
|
||||
}
|
||||
}
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="g3-content">
|
||||
<RouterView v-slot="{ Component, route: childRoute }">
|
||||
<Transition name="slide" mode="out-in">
|
||||
<template v-if="childRoute.meta.keepAlive">
|
||||
<keep-alive :include="includeKeys">
|
||||
<component
|
||||
:is="formatComponentInstance(Component, childRoute.fullPath)"
|
||||
:key="childRoute.fullPath"
|
||||
/>
|
||||
</keep-alive>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<div :key="childRoute.fullPath" class="g3-content-page">
|
||||
<component :is="Component" />
|
||||
</div>
|
||||
</template>
|
||||
</Transition>
|
||||
</RouterView>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.g3-content {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
margin-top: 4px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.g3-content-page {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.slide-enter-active,
|
||||
.slide-leave-active {
|
||||
transition:
|
||||
opacity 0.24s ease,
|
||||
transform 0.24s ease;
|
||||
}
|
||||
|
||||
.slide-enter-from {
|
||||
opacity: 0;
|
||||
transform: translateX(18px);
|
||||
}
|
||||
|
||||
.slide-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateX(-18px);
|
||||
}
|
||||
|
||||
.slide-enter-to,
|
||||
.slide-leave-from {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
</style>
|
||||
@@ -1,134 +0,0 @@
|
||||
<script lang="js" setup>
|
||||
import { onBeforeUnmount, ref } from "vue";
|
||||
import Menu from "./menu.vue";
|
||||
import Topbar from "./topbar.vue";
|
||||
import Tab from "./tab.vue";
|
||||
import Content from "./content.vue";
|
||||
|
||||
const EXPANDED_MENU_WIDTH = 220;
|
||||
const COLLAPSED_MENU_WIDTH = 64;
|
||||
const COLLAPSE_ANIMATION_TIME = 300;
|
||||
|
||||
const isCollapse = ref(false);
|
||||
const leftSize = ref(EXPANDED_MENU_WIDTH);
|
||||
const isCollapsing = ref(false);
|
||||
let collapseTimer = null;
|
||||
|
||||
function handleCollapse() {
|
||||
if (isCollapsing.value) return;
|
||||
|
||||
isCollapsing.value = true;
|
||||
isCollapse.value = !isCollapse.value;
|
||||
leftSize.value = isCollapse.value ? COLLAPSED_MENU_WIDTH : EXPANDED_MENU_WIDTH;
|
||||
|
||||
window.clearTimeout(collapseTimer);
|
||||
collapseTimer = window.setTimeout(() => {
|
||||
isCollapsing.value = false;
|
||||
}, COLLAPSE_ANIMATION_TIME);
|
||||
}
|
||||
|
||||
function handleResizeEnd(index, sizes) {
|
||||
window.clearTimeout(collapseTimer);
|
||||
isCollapsing.value = false;
|
||||
leftSize.value = sizes[0];
|
||||
isCollapse.value = sizes[0] <= 80;
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.clearTimeout(collapseTimer);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="g3-layout" :class="{ 'is-collapsing': isCollapsing }">
|
||||
<el-splitter @resize-end="handleResizeEnd">
|
||||
<el-splitter-panel
|
||||
class="g3-layout-panel-left"
|
||||
:min="COLLAPSED_MENU_WIDTH"
|
||||
:size="leftSize"
|
||||
:max="400"
|
||||
>
|
||||
<div class="g3-layout-left">
|
||||
<Menu :collapse="isCollapse" :animating="isCollapsing" @collapse="handleCollapse" />
|
||||
</div>
|
||||
</el-splitter-panel>
|
||||
<el-splitter-panel class="g3-layout-panel-right">
|
||||
<div class="g3-layout-right">
|
||||
<Topbar />
|
||||
<Tab />
|
||||
<Content />
|
||||
</div>
|
||||
</el-splitter-panel>
|
||||
</el-splitter>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.g3-layout {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #f0f2f5;
|
||||
overflow: hidden;
|
||||
|
||||
:deep(.el-splitter-bar__dragger-horizontal::after),
|
||||
:deep(.el-splitter-bar__dragger-horizontal::before) {
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
:deep(.el-splitter-bar__dragger::after),
|
||||
:deep(.el-splitter-bar__dragger::before) {
|
||||
// background-color: var(--g3-layout-divider-color);
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.g3-layout-left {
|
||||
flex-shrink: 0;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
// border-right: 1px solid #bfbfc6;
|
||||
}
|
||||
|
||||
/* 为 el-splitter-panel 添加过渡动画 */
|
||||
:deep(.el-splitter-panel) {
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
:deep(.g3-layout-panel-left),
|
||||
:deep(.g3-layout-panel-right) {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:deep(.g3-layout-panel-left) {
|
||||
z-index: 1;
|
||||
overflow: visible;
|
||||
border-right: 1px solid var(--g3-layout-divider-color);
|
||||
}
|
||||
|
||||
/* 为 el-splitter-bar 添加过渡动画 */
|
||||
:deep(.el-splitter-bar) {
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
.g3-layout.is-collapsing {
|
||||
:deep(.el-splitter-panel) {
|
||||
transition: flex-basis 0.3s ease !important;
|
||||
}
|
||||
|
||||
:deep(.el-splitter-bar) {
|
||||
transition:
|
||||
left 0.3s ease,
|
||||
right 0.3s ease !important;
|
||||
}
|
||||
}
|
||||
|
||||
.g3-layout-right {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0px 10px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,82 +0,0 @@
|
||||
<template>
|
||||
<el-sub-menu v-if="hasChildren" :index="menuIndex" :class="levelClass">
|
||||
<template #title>
|
||||
<el-icon v-if="item.icon">
|
||||
<G3Icon :icon="item.icon" />
|
||||
</el-icon>
|
||||
<span class="menu-label">{{ item.label }}</span>
|
||||
</template>
|
||||
|
||||
<MenuItem
|
||||
v-for="(child, idx) in item.children"
|
||||
:key="child.path || idx"
|
||||
:item="child"
|
||||
:parent-index="menuIndex + '-' + idx"
|
||||
/>
|
||||
</el-sub-menu>
|
||||
|
||||
<el-menu-item
|
||||
v-else
|
||||
:index="item.path"
|
||||
:route="{
|
||||
path: item.path,
|
||||
}"
|
||||
>
|
||||
<el-icon v-if="item.icon">
|
||||
<G3Icon :icon="item.icon" />
|
||||
</el-icon>
|
||||
<span class="menu-label">{{ item.label }}</span>
|
||||
</el-menu-item>
|
||||
</template>
|
||||
|
||||
<script setup lang="js">
|
||||
import { computed } from "vue";
|
||||
import G3Icon from "@/components/g3-icon/index.vue";
|
||||
|
||||
const props = defineProps({
|
||||
item: { type: Object, required: true },
|
||||
parentIndex: { type: String, default: "0" },
|
||||
});
|
||||
|
||||
const hasChildren = props.item.children && props.item.children.length > 0;
|
||||
|
||||
// 父菜单没有 path,用索引生成唯一 index,子菜单 path 就用真实路由
|
||||
const menuIndex = props.item.path || props.parentIndex;
|
||||
|
||||
// 一级、二级 class
|
||||
const levelClass = computed(() => {
|
||||
if (!hasChildren) return "";
|
||||
|
||||
const level = props.parentIndex.split("-").length;
|
||||
|
||||
// 一级菜单
|
||||
if (level === 1) {
|
||||
// 判断子节点是否还有 children
|
||||
const childHasChildren = props.item.children.some(
|
||||
(child) => child.children && child.children.length > 0,
|
||||
);
|
||||
|
||||
// 子节点都是叶子节点,才加 g3level1
|
||||
return childHasChildren ? "" : "g3level1";
|
||||
}
|
||||
|
||||
// 二级菜单,只要有子菜单就加 g3level2
|
||||
if (level === 2) {
|
||||
return "g3level2";
|
||||
}
|
||||
|
||||
return "";
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.menu-label {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
:deep(.el-icon svg) {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,422 +0,0 @@
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import axios from "@/utils/axios";
|
||||
import G3Icon from "@/components/g3-icon/index.vue";
|
||||
import MenuItem from "./menu-item.vue";
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const props = defineProps({
|
||||
collapse: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
animating: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits(["collapse"]);
|
||||
|
||||
const title = ref("G3后台管理系统");
|
||||
|
||||
// 方便以后根据不同的权限显示不同的菜单
|
||||
const selectedMenuKey = computed(() => route.fullPath);
|
||||
const menuList = ref([
|
||||
// 首页
|
||||
{
|
||||
label: "首页",
|
||||
path: "/home",
|
||||
icon: "svghome",
|
||||
},
|
||||
// 业务管理
|
||||
{
|
||||
label: "业务管理",
|
||||
icon: "ri:folder-chart-line",
|
||||
children: [
|
||||
{ label: "业务管理", path: "/business" },
|
||||
{ label: "排车列表", path: "/business/listcontainer" },
|
||||
],
|
||||
},
|
||||
// 财务管理
|
||||
{
|
||||
label: "财务管理",
|
||||
icon: "ri:wallet-line",
|
||||
children: [
|
||||
{
|
||||
label: "应收管理",
|
||||
children: [
|
||||
{ label: "应收费用列表", path: "/cw/ys/list" },
|
||||
{ label: "应收账单", path: "/cw/ys/listbill" },
|
||||
{ label: "应收发票", path: "/cw/ys/listinvoice" },
|
||||
{ label: "收款管理", path: "/cw/ys/listskd" },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "应付管理",
|
||||
children: [
|
||||
{ label: "应付费用列表", path: "/cw/yf/list" },
|
||||
{ label: "应付账单", path: "/cw/yf/listbill" },
|
||||
{ label: "应付发票", path: "/cw/yf/listinvoice" },
|
||||
{ label: "付款管理", path: "/cw/yf/listfkd" },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "日常费用",
|
||||
children: [
|
||||
{ label: "日常费用", path: "/cw/assistance/daily-expenses" },
|
||||
{ label: "收付账户", path: "/cw/assistance/payment-account" },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
// 统计分析
|
||||
{
|
||||
label: "统计分析",
|
||||
icon: "ri:bar-chart-box-line",
|
||||
children: [
|
||||
{
|
||||
label: "收款统计分析",
|
||||
children: [
|
||||
{ label: "应收未收明细", path: "/tj/ystj/tj_nots" },
|
||||
{ label: "应收未收按单位汇总", path: "/tj/ystj/tj_notsbydw" },
|
||||
{ label: "未收款按业务员汇总", path: "/tj/ystj/tj_notsbysales" },
|
||||
{ label: "收款明细统计", path: "/tj/ystj/tj_cwskxz" },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "付款统计分析",
|
||||
children: [
|
||||
{ label: "应付未付明细", path: "/tj/yftj/tj_alone_notf" },
|
||||
{ label: "应付未付按单位汇总", path: "/tj/yftj/tj_notfbydw" },
|
||||
{ label: "付款明细统计", path: "/tj/yftj/tj_cwfkxz" },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "利润统计分析",
|
||||
children: [
|
||||
{ label: "利润分析单票明细", path: "/tj/lrtj/tj_lr_module_id" },
|
||||
{ label: "利润分析按业务员", path: "/tj/lrtj/tj_lrbysales" },
|
||||
{ label: "利润分析按客户", path: "/tj/lrtj/tj_lrbycustomer" },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
// 审批中心
|
||||
{
|
||||
label: "管理中心",
|
||||
icon: "ri:government-line",
|
||||
children: [
|
||||
{ label: "审批中心", path: "/shcenter" },
|
||||
{ label: "合同管理", path: "/partners/khht" },
|
||||
{ label: "运费模版", path: "/pricelist/list" },
|
||||
{
|
||||
label: "企业公告",
|
||||
path: "/system/oa-management/companyAnnouncement",
|
||||
},
|
||||
{ label: "审批设置", path: "/system/approvalSettings" },
|
||||
{ label: "提醒设置", path: "/system/oa-management/userReminder" },
|
||||
{ label: "结算汇率", path: "/partners/hl" },
|
||||
],
|
||||
},
|
||||
// 基础数据
|
||||
{
|
||||
label: "基础数据",
|
||||
icon: "ri:database-line",
|
||||
children: [
|
||||
{ label: "合作伙伴", path: "/partners" },
|
||||
{ label: "联系人", path: "/partners/contact" },
|
||||
{ label: "其他数据", path: "/base/other" },
|
||||
],
|
||||
},
|
||||
// 系统管理
|
||||
{
|
||||
label: "系统管理",
|
||||
icon: "ri:settings-line",
|
||||
children: [
|
||||
{ label: "权限管理", path: "/system/permission" },
|
||||
{ label: "字段配置", path: "/system/field" },
|
||||
{ label: "查询设置", path: "/system/statisticalSettings" },
|
||||
{ label: "映射配置", path: "/system/mappingConfig" },
|
||||
{ label: "模块设置", path: "/system/moduleSettings" },
|
||||
{ label: "自动编码", path: "/system/autoId" },
|
||||
],
|
||||
},
|
||||
// 系统帮助
|
||||
{
|
||||
label: "系统帮助",
|
||||
icon: "ri:question-line",
|
||||
children: [
|
||||
{ label: "帮助文档", path: "/help" },
|
||||
{ label: "操作视频", path: "/videos" },
|
||||
],
|
||||
},
|
||||
// 功能测试
|
||||
{
|
||||
label: "功能测试",
|
||||
icon: "ri:bug-line",
|
||||
children: [{ label: "测试页面", path: "/test/test" }],
|
||||
},
|
||||
]);
|
||||
|
||||
function handleSelect(val) {
|
||||
router.push(val);
|
||||
}
|
||||
|
||||
function handleCollapse() {
|
||||
emits("collapse");
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
let searchsql = `select * from s_keyvalue where b_key='bodycaption'`;
|
||||
const response = await axios.get("/selectlistbysql", {
|
||||
params: { sql: searchsql },
|
||||
});
|
||||
if (response.data?.success) {
|
||||
const records = response.data.data.records || [];
|
||||
if (records.length > 0) {
|
||||
title.value = records[0].b_value;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="app-menu" :class="{ collapse: props.collapse, 'is-animating': props.animating }">
|
||||
<div
|
||||
class="menu-logo"
|
||||
@click="
|
||||
() => {
|
||||
router.push('/home');
|
||||
}
|
||||
"
|
||||
>
|
||||
<img src="@/assets/g3logo.png" alt="Logo" class="logo" />
|
||||
<div v-show="!props.collapse" class="title">{{ title }}</div>
|
||||
</div>
|
||||
<div class="menu-container">
|
||||
<el-menu
|
||||
:collapse="props.collapse"
|
||||
:collapse-transition="false"
|
||||
class="menu-inner"
|
||||
:default-active="selectedMenuKey"
|
||||
@select="handleSelect"
|
||||
>
|
||||
<MenuItem
|
||||
v-for="(item, idx) in menuList"
|
||||
:key="item.path || idx"
|
||||
:item="item"
|
||||
:parent-index="idx.toString()"
|
||||
/>
|
||||
</el-menu>
|
||||
</div>
|
||||
<div class="menu-footer">
|
||||
<div @click="handleCollapse()">
|
||||
<G3Icon :icon="props.collapse ? 'ri:menu-fold-4-line' : 'ri:menu-fold-3-line'" :size="20" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.app-menu {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background-color: #f8f8f8;
|
||||
overflow: hidden;
|
||||
transition: background-color 0.3s ease;
|
||||
|
||||
&.collapse {
|
||||
.menu-logo .logo {
|
||||
width: 46px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.menu-footer {
|
||||
justify-content: center;
|
||||
padding-right: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-animating {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 42px;
|
||||
padding: 19px 0px;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
transition: padding 0.3s ease;
|
||||
|
||||
.logo {
|
||||
width: 80px;
|
||||
height: 24px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-left: 10px;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: #70cfb1;
|
||||
white-space: nowrap;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-container {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
background-color: #f8f8f8;
|
||||
.menu-inner {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
min-width: 64px;
|
||||
box-sizing: border-box;
|
||||
width: 100% !important;
|
||||
border-right: none !important;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #cfd3dc transparent;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color: #cfd3dc;
|
||||
border-radius: 999px;
|
||||
|
||||
&:hover {
|
||||
background-color: #a8abb2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-sub-menu__title) {
|
||||
height: 28px !important;
|
||||
line-height: 28px !important;
|
||||
box-sizing: border-box;
|
||||
color: #525252;
|
||||
font-size: 13px;
|
||||
font-family:
|
||||
"InterVariable", "Inter", "-apple-system", "BlinkMacSystemFont", "Segoe UI", "Roboto",
|
||||
"Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
|
||||
}
|
||||
|
||||
:deep(.el-menu-item) {
|
||||
height: 28px !important;
|
||||
line-height: 28px !important;
|
||||
box-sizing: border-box;
|
||||
color: #525252;
|
||||
font-size: 13px;
|
||||
font-family:
|
||||
"InterVariable", "Inter", "-apple-system", "BlinkMacSystemFont", "Segoe UI", "Roboto",
|
||||
"Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
|
||||
|
||||
&.is-active {
|
||||
background-color: var(--el-color-primary-light-9);
|
||||
color: var(--el-color-primary);
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
:deep(.el-menu-item:hover) {
|
||||
background-color: #f3f3f3;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
:deep(.el-sub-menu) {
|
||||
&.is-active {
|
||||
> .el-sub-menu__title {
|
||||
color: var(--el-color-primary);
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
:deep(.el-sub-menu),
|
||||
:deep(.el-menu) {
|
||||
background-color: #f8f8f8 !important;
|
||||
}
|
||||
:deep(.el-sub-menu.is-active .el-sub-menu__title) {
|
||||
border-bottom-color: #525252;
|
||||
}
|
||||
:deep(.el-sub-menu__title:hover) {
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
:deep(.menu-container .el-menu-item.is-active) {
|
||||
color: #525252;
|
||||
font-weight: 500;
|
||||
background: rgba(255, 255, 255, 1);
|
||||
box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 8px;
|
||||
}
|
||||
:deep(.menu-container .el-sub-menu.is-active > .el-sub-menu__title) {
|
||||
color: #525252;
|
||||
font-weight: 500;
|
||||
}
|
||||
:deep(.g3level1 .el-menu) {
|
||||
margin-left: 30px;
|
||||
}
|
||||
:deep(.g3level1 .el-menu .el-menu-item) {
|
||||
padding: 0px 20px 0px 20px !important;
|
||||
}
|
||||
:deep(.g3level2 .el-sub-menu__title) {
|
||||
margin-left: 30px;
|
||||
padding: 0px 20px 0px 20px !important;
|
||||
}
|
||||
:deep(.g3level2 .el-menu) {
|
||||
margin-left: 42px;
|
||||
}
|
||||
:deep(.g3level2 .el-menu .el-menu-item) {
|
||||
padding: 0px 20px 0px 20px !important;
|
||||
}
|
||||
|
||||
.menu-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
box-sizing: border-box;
|
||||
padding: 8px 0px 8px 0px;
|
||||
padding-right: 12px;
|
||||
overflow: hidden;
|
||||
transition: padding-right 0.3s ease;
|
||||
|
||||
div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(45, 140, 240, 0.1);
|
||||
color: rgba(45, 140, 240, 0.8);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,370 +0,0 @@
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import axios from "@/utils/axios";
|
||||
import G3Icon from "@/components/g3-icon/index.vue";
|
||||
import MenuItem from "./menu-item.vue";
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const props = defineProps({
|
||||
collapse: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
animating: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits(["collapse"]);
|
||||
|
||||
const title = ref("G3后台管理系统");
|
||||
|
||||
// 方便以后根据不同的权限显示不同的菜单
|
||||
const selectedMenuKey = computed(() => route.fullPath);
|
||||
const menuList = ref([
|
||||
// 首页
|
||||
{
|
||||
label: "首页",
|
||||
path: "/home",
|
||||
icon: "mdi:home",
|
||||
},
|
||||
// 业务管理
|
||||
{
|
||||
label: "业务管理",
|
||||
icon: "ri:folder-chart-line",
|
||||
children: [
|
||||
{ label: "业务管理", path: "/business" },
|
||||
{ label: "排车列表", path: "/business/listcontainer" },
|
||||
],
|
||||
},
|
||||
// 财务管理
|
||||
{
|
||||
label: "财务管理",
|
||||
icon: "ri:wallet-line",
|
||||
children: [
|
||||
{
|
||||
label: "应收管理",
|
||||
children: [
|
||||
{ label: "应收费用列表", path: "/cw/ys/list" },
|
||||
{ label: "应收账单", path: "/cw/ys/listbill" },
|
||||
{ label: "应收发票", path: "/cw/ys/listinvoice" },
|
||||
{ label: "收款管理", path: "/cw/ys/listskd" },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "应付管理",
|
||||
children: [
|
||||
{ label: "应付费用列表", path: "/cw/yf/list" },
|
||||
{ label: "应付账单", path: "/cw/yf/listbill" },
|
||||
{ label: "应付发票", path: "/cw/yf/listinvoice" },
|
||||
{ label: "付款管理", path: "/cw/yf/listfkd" },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "日常费用",
|
||||
children: [
|
||||
{ label: "日常费用", path: "/cw/assistance/daily-expenses" },
|
||||
{ label: "收付账户", path: "/cw/assistance/payment-account" },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
// 统计分析
|
||||
{
|
||||
label: "统计分析",
|
||||
icon: "ri:bar-chart-box-line",
|
||||
children: [
|
||||
{
|
||||
label: "收款统计分析",
|
||||
children: [
|
||||
{ label: "应收未收明细", path: "/tj/ystj/tj_nots" },
|
||||
{ label: "应收未收按单位汇总", path: "/tj/ystj/tj_notsbydw" },
|
||||
{ label: "未收款按业务员汇总", path: "/tj/ystj/tj_notsbysales" },
|
||||
{ label: "收款明细统计", path: "/tj/ystj/tj_cwskxz" },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "付款统计分析",
|
||||
children: [
|
||||
{ label: "应付未付明细", path: "/tj/yftj/tj_alone_notf" },
|
||||
{ label: "应付未付按单位汇总", path: "/tj/yftj/tj_notfbydw" },
|
||||
{ label: "付款明细统计", path: "/tj/yftj/tj_cwfkxz" },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "利润统计分析",
|
||||
children: [
|
||||
{ label: "利润分析单票明细", path: "/tj/lrtj/tj_lr_module_id" },
|
||||
{ label: "利润分析按业务员", path: "/tj/lrtj/tj_lrbysales" },
|
||||
{ label: "利润分析按客户", path: "/tj/lrtj/tj_lrbycustomer" },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
// 审批中心
|
||||
{
|
||||
label: "管理中心",
|
||||
icon: "ri:government-line",
|
||||
children: [
|
||||
{ label: "审批中心", path: "/shcenter" },
|
||||
{ label: "合同管理", path: "/partners/khht" },
|
||||
{ label: "运费模版", path: "/pricelist/list" },
|
||||
{
|
||||
label: "企业公告",
|
||||
path: "/system/oa-management/companyAnnouncement",
|
||||
},
|
||||
{ label: "审批设置", path: "/system/approvalSettings" },
|
||||
{ label: "提醒设置", path: "/system/oa-management/userReminder" },
|
||||
{ label: "结算汇率", path: "/partners/hl" },
|
||||
],
|
||||
},
|
||||
// 基础数据
|
||||
{
|
||||
label: "基础数据",
|
||||
icon: "ri:database-line",
|
||||
children: [
|
||||
{ label: "合作伙伴", path: "/partners" },
|
||||
{ label: "联系人", path: "/partners/contact" },
|
||||
{ label: "其他数据", path: "/base/other" },
|
||||
],
|
||||
},
|
||||
// 系统管理
|
||||
{
|
||||
label: "系统管理",
|
||||
icon: "ri:settings-line",
|
||||
children: [
|
||||
{ label: "权限管理", path: "/system/permission" },
|
||||
{ label: "字段配置", path: "/system/field" },
|
||||
{ label: "查询设置", path: "/system/statisticalSettings" },
|
||||
{ label: "映射配置", path: "/system/mappingConfig" },
|
||||
{ label: "模块设置", path: "/system/moduleSettings" },
|
||||
{ label: "自动编码", path: "/system/autoId" },
|
||||
],
|
||||
},
|
||||
// 系统帮助
|
||||
{
|
||||
label: "系统帮助",
|
||||
icon: "ri:question-line",
|
||||
children: [
|
||||
{ label: "帮助文档", path: "/help" },
|
||||
{ label: "操作视频", path: "/videos" },
|
||||
],
|
||||
},
|
||||
// 功能测试
|
||||
{
|
||||
label: "功能测试",
|
||||
icon: "ri:bug-line",
|
||||
children: [{ label: "测试页面", path: "/test/test" }],
|
||||
},
|
||||
]);
|
||||
|
||||
function handleSelect(val) {
|
||||
router.push(val);
|
||||
}
|
||||
|
||||
function handleCollapse() {
|
||||
emits("collapse");
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
let searchsql = `select * from s_keyvalue where b_key='bodycaption'`;
|
||||
const response = await axios.get("/selectlistbysql", {
|
||||
params: { sql: searchsql },
|
||||
});
|
||||
if (response.data?.success) {
|
||||
const records = response.data.data.records || [];
|
||||
if (records.length > 0) {
|
||||
title.value = records[0].b_value;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="app-menu" :class="{ collapse: props.collapse, 'is-animating': props.animating }">
|
||||
<div
|
||||
class="menu-logo"
|
||||
@click="
|
||||
() => {
|
||||
router.push('/home');
|
||||
}
|
||||
"
|
||||
>
|
||||
<img src="@/assets/g3logo.png" alt="Logo" class="logo" />
|
||||
<div v-show="!props.collapse" class="title">{{ title }}</div>
|
||||
</div>
|
||||
<div class="menu-container">
|
||||
<el-menu
|
||||
:collapse="props.collapse"
|
||||
:collapse-transition="false"
|
||||
class="menu-inner"
|
||||
:default-active="selectedMenuKey"
|
||||
@select="handleSelect"
|
||||
>
|
||||
<MenuItem
|
||||
v-for="(item, idx) in menuList"
|
||||
:key="item.path || idx"
|
||||
:item="item"
|
||||
:parent-index="idx.toString()"
|
||||
/>
|
||||
</el-menu>
|
||||
</div>
|
||||
<div class="menu-footer">
|
||||
<div @click="handleCollapse()">
|
||||
<G3Icon :icon="props.collapse ? 'ri:menu-fold-4-line' : 'ri:menu-fold-3-line'" :size="20" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.app-menu {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background-color: white;
|
||||
overflow: hidden;
|
||||
transition: background-color 0.3s ease;
|
||||
|
||||
&.collapse {
|
||||
.menu-logo .logo {
|
||||
width: 46px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.menu-footer {
|
||||
justify-content: center;
|
||||
padding-right: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-animating {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 42px;
|
||||
padding: 19px 0px;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
transition: padding 0.3s ease;
|
||||
|
||||
.logo {
|
||||
width: 80px;
|
||||
height: 24px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-left: 10px;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: #70cfb1;
|
||||
white-space: nowrap;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-container {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
.menu-inner {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
min-width: 64px;
|
||||
box-sizing: border-box;
|
||||
width: 100% !important;
|
||||
border-right: none !important;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #cfd3dc transparent;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color: #cfd3dc;
|
||||
border-radius: 999px;
|
||||
|
||||
&:hover {
|
||||
background-color: #a8abb2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-sub-menu__title) {
|
||||
height: 42px !important;
|
||||
line-height: 42px !important;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:deep(.el-menu-item) {
|
||||
height: 42px !important;
|
||||
line-height: 42px !important;
|
||||
box-sizing: border-box;
|
||||
|
||||
&.is-active {
|
||||
background-color: var(--el-color-primary-light-9);
|
||||
color: var(--el-color-primary);
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-sub-menu) {
|
||||
&.is-active {
|
||||
> .el-sub-menu__title {
|
||||
color: var(--el-color-primary);
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.menu-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
box-sizing: border-box;
|
||||
padding: 8px 0px 8px 0px;
|
||||
padding-right: 12px;
|
||||
overflow: hidden;
|
||||
transition: padding-right 0.3s ease;
|
||||
|
||||
div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(45, 140, 240, 0.1);
|
||||
color: rgba(45, 140, 240, 0.8);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,402 +0,0 @@
|
||||
<script lang="js" setup>
|
||||
import G3Icon from "@/components/g3-icon/index.vue";
|
||||
|
||||
import { useRoute } from "vue-router";
|
||||
import { useRouter } from "vue-router";
|
||||
import { nextTick, reactive, ref, watch } from "vue";
|
||||
import { useTabStore } from "@/store/tab";
|
||||
|
||||
const { refs, setRefs } = useRefs();
|
||||
|
||||
const showScroll = ref(false);
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const tabStore = useTabStore();
|
||||
|
||||
watch(
|
||||
() => tabStore.activeTab,
|
||||
(newVal) => {
|
||||
nextTick(() => {
|
||||
updateScroll();
|
||||
|
||||
const tabIndex = tabStore.findTabIndex(newVal);
|
||||
moveScroll(tabIndex);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
function useRefs() {
|
||||
const refs = reactive({});
|
||||
|
||||
function setRefs(name) {
|
||||
return (el) => {
|
||||
refs[name] = el;
|
||||
return () => refs[name];
|
||||
};
|
||||
}
|
||||
|
||||
return { refs, setRefs };
|
||||
}
|
||||
|
||||
function updateScroll() {
|
||||
const wrap = refs.scroller;
|
||||
if (!wrap) return;
|
||||
showScroll.value = wrap.scrollWidth > wrap.clientWidth;
|
||||
}
|
||||
|
||||
function moveScroll(index) {
|
||||
nextTick(() => {
|
||||
const el = refs[`item-${index}`];
|
||||
const wrap = refs.scroller;
|
||||
if (!el || !wrap) return;
|
||||
|
||||
let attempts = 0;
|
||||
const maxAttempts = 5;
|
||||
|
||||
const scrollToActiveTab = () => {
|
||||
const scrollLeft =
|
||||
el.getBoundingClientRect().left -
|
||||
wrap.getBoundingClientRect().left +
|
||||
wrap.scrollLeft -
|
||||
wrap.clientWidth / 2 +
|
||||
el.clientWidth / 2;
|
||||
|
||||
wrap.scrollLeft = scrollLeft;
|
||||
|
||||
if (Math.abs(wrap.scrollLeft - scrollLeft) > 1 && attempts < maxAttempts) {
|
||||
attempts++;
|
||||
requestAnimationFrame(scrollToActiveTab);
|
||||
}
|
||||
};
|
||||
|
||||
scrollToActiveTab();
|
||||
});
|
||||
}
|
||||
|
||||
// 切换标签
|
||||
function scrollTabs(direction) {
|
||||
const wrap = refs.scroller;
|
||||
if (!wrap) return;
|
||||
|
||||
const distance = wrap.clientWidth * 0.8;
|
||||
wrap.scrollLeft += direction === "left" ? -distance : distance;
|
||||
}
|
||||
|
||||
function switchTab(tab, index) {
|
||||
router.push(tab.fullPath);
|
||||
moveScroll(index);
|
||||
}
|
||||
|
||||
// 关闭标签
|
||||
async function closeTab(tab) {
|
||||
if (tab.fullPath === "/home") return; // ❗首页不能关
|
||||
|
||||
const activePath = await tabStore.closeTab(tab.fullPath);
|
||||
|
||||
if (activePath && route.fullPath !== activePath) {
|
||||
router.push(activePath);
|
||||
} else if (!activePath) {
|
||||
router.push("/home");
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭左侧
|
||||
function closeLeft(data) {
|
||||
const currentIndex = tabStore.tabs.findIndex((item) => item.fullPath === data.fullPath);
|
||||
if (currentIndex === -1) return;
|
||||
|
||||
tabStore.tabs
|
||||
.slice(0, currentIndex)
|
||||
.filter((item) => item.fullPath !== "/home") // ❗过滤首页
|
||||
.forEach((item) => {
|
||||
const index = tabStore.tabs.findIndex((t) => t.fullPath === item.fullPath);
|
||||
if (index !== -1) tabStore.tabs.splice(index, 1);
|
||||
|
||||
if (item.fullPath === route.fullPath) {
|
||||
router.push(data.fullPath);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 关闭右侧
|
||||
function closeRight(data) {
|
||||
const currentIndex = tabStore.tabs.findIndex((item) => item.fullPath === data.fullPath);
|
||||
if (currentIndex === -1) return;
|
||||
|
||||
tabStore.tabs
|
||||
.slice(currentIndex + 1)
|
||||
.filter((item) => item.fullPath !== "/home") // ❗过滤首页
|
||||
.forEach((item) => {
|
||||
const index = tabStore.tabs.findIndex((t) => t.fullPath === item.fullPath);
|
||||
if (index !== -1) tabStore.tabs.splice(index, 1);
|
||||
|
||||
if (item.fullPath === route.fullPath) {
|
||||
router.push(data.fullPath);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 关闭其他
|
||||
function closeOther(tab) {
|
||||
tabStore.tabs = tabStore.tabs.filter(
|
||||
(t) => t.fullPath === tab.fullPath || t.fullPath === "/home", // ❗保留首页
|
||||
);
|
||||
|
||||
router.push(tab.fullPath);
|
||||
}
|
||||
|
||||
// 关闭全部
|
||||
function closeAll() {
|
||||
tabStore.tabs = tabStore.tabs.filter(
|
||||
(t) => t.fullPath === "/home", // ❗只保留首页
|
||||
);
|
||||
|
||||
router.push("/home");
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="g3-tabs" :class="{ 'has-scroll': showScroll }">
|
||||
<div v-if="showScroll" class="g3-action-icon g3-scroll-more" @click="scrollTabs('left')">
|
||||
<G3Icon icon="lucide:chevrons-left" />
|
||||
</div>
|
||||
|
||||
<div class="g3-tab-container">
|
||||
<div :ref="setRefs('scroller')" class="app-tab-scroller">
|
||||
<el-dropdown
|
||||
v-for="(item, index) in tabStore.tabs"
|
||||
:key="item.fullPath"
|
||||
trigger="contextmenu"
|
||||
:persistent="false"
|
||||
style="
|
||||
height: 24px;
|
||||
border: 1px solid #f2f3f5;
|
||||
margin-right: 6px;
|
||||
background-color: #f2f3f5;
|
||||
"
|
||||
>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu v-if="item.fullPath !== '/home'">
|
||||
<el-dropdown-item @click="closeTab(item)"> 关闭 </el-dropdown-item>
|
||||
<el-dropdown-item @click="closeOther(item)"> 关闭其他 </el-dropdown-item>
|
||||
<el-dropdown-item @click="closeLeft(item)"> 关闭左侧 </el-dropdown-item>
|
||||
<el-dropdown-item @click="closeRight(item)"> 关闭右侧 </el-dropdown-item>
|
||||
<el-dropdown-item @click="closeAll"> 关闭所有 </el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
<div
|
||||
:ref="setRefs(`item-${index}`)"
|
||||
class="app-tab-item"
|
||||
:class="{ active: item.fullPath === tabStore.activeTab }"
|
||||
@click="switchTab(item)"
|
||||
>
|
||||
<span class="label">{{ item.title }}</span>
|
||||
<G3Icon
|
||||
v-if="item.fullPath !== '/home'"
|
||||
icon="lucide:x"
|
||||
size="12"
|
||||
class="icon"
|
||||
@click.stop="closeTab(item)"
|
||||
/>
|
||||
</div>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="showScroll" class="g3-action-icon g3-scroll-more" @click="scrollTabs('right')">
|
||||
<G3Icon icon="lucide:chevrons-right" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.g3-tabs {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 32px;
|
||||
user-select: none;
|
||||
background-color: #fff;
|
||||
border-radius: 6px;
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
.g3-action-icon {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
width: 38px;
|
||||
transition: all 0.2s ease-in-out;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
color: #18181b;
|
||||
background-color: #fff;
|
||||
|
||||
&:first-child {
|
||||
border-radius: 0 0 0 6px;
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 1px;
|
||||
height: 100%;
|
||||
background-color: var(--g3-layout-divider-color);
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-radius: 0 0 6px 0;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 1px;
|
||||
height: 100%;
|
||||
background-color: var(--g3-layout-divider-color);
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--el-color-primary);
|
||||
background-color: var(--el-color-primary-light-8);
|
||||
}
|
||||
}
|
||||
|
||||
.g3-tab-container {
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
position: relative;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
:deep(.el-dropdown) {
|
||||
position: relative;
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 1px;
|
||||
height: 100%;
|
||||
background-color: var(--g3-layout-divider-color);
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.g3-tabs.has-scroll {
|
||||
.g3-tab-container {
|
||||
:deep(.el-dropdown:last-child) {
|
||||
&::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.app-tab-scroller {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
scrollbar-width: none;
|
||||
overflow: hidden;
|
||||
scroll-behavior: smooth;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 12px;
|
||||
}
|
||||
|
||||
.app-tab-item {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
padding: 0px 6px;
|
||||
cursor: pointer;
|
||||
color: #4e5969;
|
||||
background-color: #f2f3f5;
|
||||
min-width: 40px;
|
||||
.label {
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.05em;
|
||||
font-family:
|
||||
Inter,
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
PingFang SC,
|
||||
Hiragino Sans GB,
|
||||
noto sans,
|
||||
Microsoft YaHei,
|
||||
Helvetica Neue,
|
||||
Helvetica,
|
||||
Arial,
|
||||
sans-serif;
|
||||
}
|
||||
|
||||
.icon {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--el-color-primary-light-8);
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: var(--el-color-primary-light-9);
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
height: 2px;
|
||||
background-color: var(--el-color-primary);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.label {
|
||||
color: var(--el-color-primary);
|
||||
font-weight: 500;
|
||||
}
|
||||
.icon {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
&:hover {
|
||||
background-color: var(--el-color-primary-light-9);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.g3-tabs:not(.has-scroll) :deep(.el-dropdown:first-child) {
|
||||
.app-tab-item {
|
||||
overflow: hidden;
|
||||
border-radius: 0 0 0 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.g3-tab-container .el-dropdown::after {
|
||||
display: none;
|
||||
}
|
||||
.app-tab-item.active::after {
|
||||
display: none;
|
||||
}
|
||||
.app-tab-item.active {
|
||||
background-color: unset;
|
||||
}
|
||||
</style>
|
||||
@@ -1,287 +0,0 @@
|
||||
<script setup>
|
||||
import { ArrowDown } from "@element-plus/icons-vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { onMounted, ref } from "vue";
|
||||
import axios from "@/utils/axios";
|
||||
import updatePassword from "@/views/user-center/updatepassword.vue";
|
||||
import { useTabStore, useUserStore } from "@/store";
|
||||
|
||||
const router = useRouter();
|
||||
const userStore = useUserStore();
|
||||
const tabStore = useTabStore();
|
||||
|
||||
const title = ref("G3服务平台");
|
||||
|
||||
let yjt_svg =
|
||||
'<svg data-v-c361ffca="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="18px" height="18px" viewBox="0 0 48 48" class="iconify iconify--icon-park-outline"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="4" d="m19 12l12 12l-12 12"></path></svg>';
|
||||
|
||||
let grzx_svg =
|
||||
'<svg data-v-c361ffca="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="18px" height="18px" viewBox="0 0 24 24" class="iconify iconify--tdesign"><g fill="none"><path d="M16.5 7.5a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2z"></path><path stroke="currentColor" stroke-linecap="square" stroke-width="2" d="M16.5 7.5a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0ZM20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2z"></path></g></svg>';
|
||||
|
||||
let xgmm_svg =
|
||||
'<svg data-v-c361ffca="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="18px" height="18px" viewBox="0 0 24 24" class="iconify iconify--mdi"><path fill="currentColor" d="M12 17a2 2 0 0 1-2-2c0-1.11.89-2 2-2a2 2 0 0 1 2 2a2 2 0 0 1-2 2m6 3V10H6v10zm0-12a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V10c0-1.11.89-2 2-2h1V6a5 5 0 0 1 5-5a5 5 0 0 1 5 5v2zm-6-5a3 3 0 0 0-3 3v2h6V6a3 3 0 0 0-3-3"></path></svg>';
|
||||
|
||||
let logout_svg =
|
||||
'<svg data-v-c361ffca="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="18px" height="18px" viewBox="0 0 24 24" class="iconify iconify--mdi"><path fill="currentColor" d="M14.08 15.59L16.67 13H7v-2h9.67l-2.59-2.59L15.5 7l5 5l-5 5zM19 3a2 2 0 0 1 2 2v4.67l-2-2V5H5v14h14v-2.67l2-2V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5c0-1.11.89-2 2-2z"></path></svg>';
|
||||
// 跳转到个人中心
|
||||
function goToProfile() {
|
||||
router.push("/user-center");
|
||||
}
|
||||
|
||||
const passwordDialogVisible = ref(false);
|
||||
function handleUpdatePassword() {
|
||||
passwordDialogVisible.value = true;
|
||||
}
|
||||
|
||||
// 退出登录
|
||||
function logout() {
|
||||
tabStore.reset();
|
||||
userStore.reset();
|
||||
|
||||
router.push("/login");
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
let searchsql = `select * from s_keyvalue where b_key='maincaption'`;
|
||||
const response = await axios.get("/selectlistbysql", { params: { sql: searchsql } });
|
||||
if (response.data?.success) {
|
||||
const records = response.data.data.records || [];
|
||||
if (records.length > 0) {
|
||||
title.value = records[0].b_value;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="g3-topbar">
|
||||
<div />
|
||||
<div class="g3-topbar-right">
|
||||
<div class="company-name">
|
||||
{{ title }}
|
||||
</div>
|
||||
|
||||
<div class="topbar-divider" />
|
||||
|
||||
<el-dropdown placement="bottom-end">
|
||||
<div class="user-card">
|
||||
<img
|
||||
class="avatar"
|
||||
src="https://api.dicebear.com/9.x/initials/svg?seed=User&backgroundColor=e5e7eb&fontColor=6b7280"
|
||||
/>
|
||||
<div class="name">{{ userStore.userInfo.userid || "未登录" }}</div>
|
||||
<el-icon :size="14">
|
||||
<arrow-down />
|
||||
</el-icon>
|
||||
</div>
|
||||
|
||||
<template #dropdown>
|
||||
<div class="user-dropdown">
|
||||
<div class="user-info">
|
||||
<img
|
||||
class="avatar"
|
||||
src="https://api.dicebear.com/9.x/initials/svg?seed=User&backgroundColor=e5e7eb&fontColor=6b7280"
|
||||
/>
|
||||
<div>
|
||||
<div class="name">{{ userStore.userInfo.userid || "未登录" }}</div>
|
||||
<div class="orgid">
|
||||
<div>机构码:</div>
|
||||
<div>{{ userStore.userInfo.orgid || "无机构码" }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item divided @click="goToProfile">
|
||||
<div class="g3flex g3color">
|
||||
<div>
|
||||
<div class="g3grzx" v-html="grzx_svg"></div>
|
||||
<div class="g3lable">个人中心</div>
|
||||
</div>
|
||||
<div class="g3yjtsvg" v-html="yjt_svg"></div>
|
||||
</div>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click="handleUpdatePassword">
|
||||
<div class="g3flex g3color">
|
||||
<div>
|
||||
<div class="g3grzx" v-html="xgmm_svg"></div>
|
||||
<div class="g3lable">修改密码</div>
|
||||
</div>
|
||||
<div class="g3yjtsvg" v-html="yjt_svg"></div>
|
||||
</div>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
<div class="g3hr"></div>
|
||||
<div class="g3logout">
|
||||
<div @click="logout">
|
||||
<div class="g3logoutsvg" v-html="logout_svg"></div>
|
||||
<div class="g3lable">退出登录</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<updatePassword v-model:visible="passwordDialogVisible" />
|
||||
</template>
|
||||
|
||||
<style lang="css" scoped>
|
||||
.g3-topbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
max-width: 100%;
|
||||
height: 48px;
|
||||
flex-shrink: 0;
|
||||
/* border-bottom: 1px solid var(--g3-layout-divider-color); */
|
||||
padding: 0 10px;
|
||||
border-radius: 0 0 6px 6px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.g3-topbar-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0px 4px;
|
||||
}
|
||||
|
||||
.company-name {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.topbar-divider {
|
||||
width: 1px;
|
||||
height: 16px;
|
||||
background-color: var(--g3-layout-divider-color);
|
||||
margin: 0px 12px;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.user-info > div {
|
||||
min-width: 157px;
|
||||
}
|
||||
|
||||
.user-card {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
|
||||
.name {
|
||||
margin: 0 6px;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background-color: rgba(204, 204, 204, 1);
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.user-dropdown {
|
||||
padding: 8px 14px;
|
||||
.avatar {
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
background-color: rgba(204, 204, 204, 1);
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.name {
|
||||
display: flex;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: rgb(31, 34, 37);
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
.orgid {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
color: rgb(118, 124, 130);
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-dropdown-menu__item) {
|
||||
padding: 12px 20px;
|
||||
}
|
||||
.g3flex {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
}
|
||||
.g3flex > div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.g3grzx {
|
||||
margin-right: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
:deep(.el-dropdown-menu__item:not(.is-disabled):focus),
|
||||
:deep(.el-dropdown-menu__item:not(.is-disabled):hover) {
|
||||
background-color: rgba(243, 243, 245, 1);
|
||||
color: unset;
|
||||
border-radius: 8px;
|
||||
}
|
||||
.g3color {
|
||||
color: rgb(51, 54, 57);
|
||||
}
|
||||
.g3lable {
|
||||
font-size: 14px;
|
||||
font-family:
|
||||
v-sans,
|
||||
system-ui,
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
"Segoe UI",
|
||||
sans-serif,
|
||||
"Apple Color Emoji",
|
||||
"Segoe UI Emoji",
|
||||
"Segoe UI Symbol";
|
||||
}
|
||||
.g3logout {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 14px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.g3logout > div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 500;
|
||||
padding: 0px 14px;
|
||||
font-size: 14px;
|
||||
height: 34px;
|
||||
border-radius: 8px;
|
||||
color: rgb(118, 124, 130);
|
||||
background-color: rgba(46, 51, 56, 0.05);
|
||||
cursor: pointer;
|
||||
}
|
||||
.g3logoutsvg {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 6px;
|
||||
color: rgb(118, 124, 130);
|
||||
}
|
||||
.g3hr {
|
||||
background-color: #ebeef5;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user