chore: 容器框架升级,修复项目命令行异常问题

This commit is contained in:
wangxuefeng
2025-03-11 10:05:28 +08:00
parent de679d4289
commit 3e1a1b4a66
1187 changed files with 95352 additions and 12509 deletions

View File

@@ -0,0 +1,93 @@
<script setup lang="ts">
import { useRoute } from 'vue-router';
import { logout } from '@/api/common';
import avatar from '@/assets/avatar.png';
import { useUserInfoStore } from '@/stores/useUserInfoStore';
import { YCODE_BASEURL } from '@/utils/request';
import { FullscreenOutlined, HomeOutlined } from '@ant-design/icons-vue';
const emits = defineEmits(['requestFullscreen']);
const route = useRoute();
const userInfoStore = useUserInfoStore();
const handleLogout = () => {
logout().then(() => {
window.location.href = `${YCODE_BASEURL}/login?redirect=${encodeURIComponent(
window.location.href,
)}`;
});
};
</script>
<template>
<div class="root">
<a-breadcrumb>
<a-breadcrumb-item v-for="item in route.matched" :key="item.path">
<HomeOutlined v-if="item.path === '/' && item.name === 'layout'" />
<span v-else>{{ item.meta.title || item.path }}</span>
</a-breadcrumb-item>
</a-breadcrumb>
<div class="user-area">
<div class="fullscreen-icon-area" @click="emits('requestFullscreen')">
<FullscreenOutlined class="fullscreen-icon" />
</div>
<a-dropdown placement="bottom">
<div style="display: flex; align-items: center; cursor: pointer">
<img :src="userInfoStore.userInfo?.avatar || avatar" class="avatar" />
<div>{{ userInfoStore.userInfo?.alias || '-' }}</div>
</div>
<template #overlay>
<a-menu>
<a-menu-item @click="handleLogout">
<span>退出登录</span>
</a-menu-item>
<a-menu-item>
<a class="back-oa" :href="`${OA_BASEURL}/front/`"> 返回OA </a>
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</div>
</div>
</template>
<style lang="less" scoped>
.root {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 30px;
background-color: #fff;
box-shadow: 0 0 12px 0 rgba(0, 0, 0, 0.08);
.user-area {
display: flex;
align-items: center;
color: rgb(51, 51, 51);
.fullscreen-icon-area {
width: 35px;
height: 35px;
display: flex;
justify-content: center;
align-items: center;
background-color: #ecf4fe;
border-radius: 50%;
cursor: pointer;
.fullscreen-icon {
font-size: 20px;
color: #1890ff;
}
}
.avatar {
width: 35px;
height: 35px;
border-radius: 50%;
margin-left: 16px;
margin-right: 8px;
}
}
}
</style>

View File

@@ -0,0 +1,70 @@
<script lang="ts"></script>
<script setup lang="ts">
import type { RouteType } from '@/router/routes';
import type { ItemType } from 'ant-design-vue';
import { computed, ref, watch } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import routeList from '@/router/routes';
function formatMemu(list: RouteType[], path: string = ''): ItemType[] {
return list
.filter((i) => i.isMenu)
.map((item) => {
const key = item.path.startsWith('/')
? item.path
: `${path}/${item.path}`;
return {
key,
icon: item.icon,
children:
item.children.length > 0 ? formatMemu(item.children, key) : void 0,
label: item.meta.title || '-',
};
});
}
const selectedKeys = ref<string[]>(['1']);
const openKeys = ref<string[]>(['sub1']);
const menuList = computed(() => formatMemu(routeList[0].children));
const route = useRoute();
const router = useRouter();
watch(
() => route.path,
(val) => {
if (!selectedKeys.value.includes(val)) {
selectedKeys.value = [val];
openKeys.value = route.matched.slice(1).map((i) => i.path);
}
},
{ immediate: true },
);
const handleClick = (config: { key: string }) => {
router.push(config.key);
};
</script>
<template>
<a-menu
v-model:open-keys="openKeys"
v-model:selected-keys="selectedKeys"
mode="inline"
:items="menuList"
class="sider-root"
@click="handleClick"
v-bind="$attrs"
/>
</template>
<style lang="less" scoped>
.sider-root {
flex-grow: 1;
overflow-y: auto;
border-inline-end: none !important;
}
</style>

View File

@@ -0,0 +1,136 @@
<script setup lang="ts">
import { computed, onMounted, ref } from "vue";
import Header from "./components/Header.vue";
import Sider from "./components/Sider.vue";
import {
MenuFoldOutlined,
MenuUnfoldOutlined,
FullscreenExitOutlined,
} from "@ant-design/icons-vue";
import { useEventListener } from "@vueuse/core";
import { qiankunWindow } from 'vite-plugin-qiankun/dist/helper'
const __POWERED_BY_QIANKUN__ = computed(() => {
return qiankunWindow.__POWERED_BY_QIANKUN__ || window?.proxy?.__POWERED_BY_QIANKUN__
})
// const userInfoStore = useUserInfoStore();
const isCollapsed = ref(false);
const isFullscreen = ref(false);
const container = ref<HTMLDivElement>();
onMounted(() => {
// userInfoStore.fetchUserInfo();
});
useEventListener(window, "fullscreenchange", () => {
isFullscreen.value = !!document.fullscreenElement;
});
const handleFullscreen = () => {
if (container.value) {
container.value.requestFullscreen();
}
};
const handleExitFullscreen = () => {
document.exitFullscreen?.();
};
</script>
<template>
<section v-if="!__POWERED_BY_QIANKUN__" class="root">
<section
class="left-aside"
:class="{ 'left-aside-collapsed': isCollapsed }"
>
<Sider :inlineCollapsed="isCollapsed" />
<div class="collapsed-icon">
<component
:is="isCollapsed ? MenuUnfoldOutlined : MenuFoldOutlined"
@click="isCollapsed = !isCollapsed"
/>
</div>
</section>
<section
class="container"
:class="{
'container-fullscreen': isFullscreen,
'container-collapsed': isCollapsed,
}"
ref="container"
>
<header class="header">
<Header @requestFullscreen="handleFullscreen" />
</header>
<div class="i-container">
<router-view />
</div>
<a-float-button @click="handleExitFullscreen" v-if="isFullscreen">
<template #icon>
<FullscreenExitOutlined />
</template>
</a-float-button>
</section>
</section>
<router-view v-else />
</template>
<style lang="less" scoped>
@header-height: 60px;
@aside-width: 220px;
@aside-width-collapsed: 60px;
@header-margin: 12px;
.root {
height: 100%;
.header {
height: @header-height;
position: fixed;
width: calc(100% - 220px);
z-index: 1;
top: 0;
}
.left-aside {
width: @aside-width;
position: fixed;
left: 0;
z-index: 1;
height: 100vh;
overflow-y: hidden;
box-shadow: 0 0 12px 0 rgba(0, 0, 0, 0.08);
display: flex;
flex-direction: column;
background-color: #fff;
transition: width 0.3s cubic-bezier(0.2, 0, 0, 1) 0s;
.collapsed-icon {
flex-shrink: 0;
display: flex;
justify-content: center;
margin-bottom: 8px;
font-size: 18px;
}
}
.container {
padding-left: @aside-width;
padding-top: @header-height + @header-margin;
height: calc(100% - @header-height - @header-margin);
position: relative;
background-color: #f8f8f8;
transition: padding 0.3s cubic-bezier(0.2, 0, 0, 1) 0s;
margin-right: 8px;
}
.container-collapsed {
padding-left: @aside-width-collapsed + 8px;
}
.container-fullscreen {
padding: 0;
height: 100%;
}
:deep(
:where(.css-dev-only-do-not-override-1hsjdkk).ant-menu-inline-collapsed
),
.left-aside-collapsed {
width: @aside-width-collapsed;
}
}
</style>