chore: 整理项目资源和配置

This commit is contained in:
Ray.Hao
2026-06-12 21:21:49 +08:00
parent 830651c0d7
commit 288ea9b43d
30 changed files with 128 additions and 199 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 487 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 306 KiB

View File

@@ -49,10 +49,8 @@
</span>
<!-- 内容全屏 -->
<span class="tags-actions__btn" @click="appStore.toggleContentFullscreen()">
<el-icon :size="16">
<FullScreen v-if="!appStore.contentFullscreen" />
<Close v-else />
</el-icon>
<div v-if="!appStore.contentFullscreen" class="i-svg:fullscreen icon-16" />
<div v-else class="i-svg:fullscreen-exit icon-16" />
</span>
<!-- 下拉菜单 -->
<el-dropdown trigger="click" @command="handleActionCommand">
@@ -62,39 +60,27 @@
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="refresh">
<el-icon :size="14" class="tags-actions__dropdown-icon">
<Refresh />
</el-icon>
<el-icon :size="14" class="icon-14"><Refresh /></el-icon>
<span>刷新当前</span>
</el-dropdown-item>
<el-dropdown-item v-if="currentTag && !currentTag.affix" command="closeCurrent">
<el-icon :size="14" class="tags-actions__dropdown-icon">
<Close />
</el-icon>
<div class="i-svg:close icon-14" />
<span>关闭当前</span>
</el-dropdown-item>
<el-dropdown-item divided command="closeOthers">
<el-icon :size="14" class="tags-actions__dropdown-icon">
<Remove />
</el-icon>
<el-dropdown-item divided command="closeOtherTags">
<div class="i-svg:close_other icon-14" />
<span>关闭其它</span>
</el-dropdown-item>
<el-dropdown-item command="closeLeft">
<el-icon :size="14" class="tags-actions__dropdown-icon">
<DArrowLeft />
</el-icon>
<el-dropdown-item command="closeLeftTags">
<div class="i-svg:close_left icon-14" />
<span>关闭左侧</span>
</el-dropdown-item>
<el-dropdown-item command="closeRight">
<el-icon :size="14" class="tags-actions__dropdown-icon">
<DArrowRight />
</el-icon>
<el-dropdown-item command="closeRightTags">
<div class="i-svg:close_right icon-14" />
<span>关闭右侧</span>
</el-dropdown-item>
<el-dropdown-item divided command="closeAll">
<el-icon :size="14" class="tags-actions__dropdown-icon">
<CircleClose />
</el-icon>
<el-dropdown-item divided command="closeAllTags">
<div class="i-svg:close_all icon-14" />
<span>关闭所有</span>
</el-dropdown-item>
</el-dropdown-menu>
@@ -114,25 +100,25 @@
class="contextmenu__danger"
@click="closeSelectedTag(selectedTag)"
>
<el-icon :size="16" class="contextmenu__icon"><Close /></el-icon>
<div class="i-svg:close contextmenu__icon" />
<span>关闭</span>
</li>
<li class="contextmenu__divider" />
<li @click="closeOtherTags">
<el-icon :size="16" class="contextmenu__icon"><Remove /></el-icon>
<div class="i-svg:close_other contextmenu__icon" />
<span>关闭其它</span>
</li>
<li v-if="!isFirstView" @click="closeLeftTags">
<el-icon :size="16" class="contextmenu__icon"><DArrowLeft /></el-icon>
<div class="i-svg:close_left contextmenu__icon" />
<span>关闭左侧</span>
</li>
<li v-if="!isLastView" @click="closeRightTags">
<el-icon :size="16" class="contextmenu__icon"><DArrowRight /></el-icon>
<div class="i-svg:close_right contextmenu__icon" />
<span>关闭右侧</span>
</li>
<li class="contextmenu__divider" />
<li @click="closeAllTags(selectedTag)">
<el-icon :size="16" class="contextmenu__icon"><CircleClose /></el-icon>
<div class="i-svg:close_all contextmenu__icon" />
<span>关闭所有</span>
</li>
</ul>
@@ -212,16 +198,16 @@ const handleActionCommand = (command: string) => {
case "closeCurrent":
closeSelectedTag(currentTag.value);
break;
case "closeOthers":
case "closeOtherTags":
closeOtherTagsForActive();
break;
case "closeLeft":
case "closeLeftTags":
closeLeftTagsForActive();
break;
case "closeRight":
case "closeRightTags":
closeRightTagsForActive();
break;
case "closeAll":
case "closeAllTags":
closeAllTags(currentTag.value);
break;
}
@@ -379,9 +365,7 @@ const closeSelectedTag = (tag: TagView | null) => {
});
};
/* ------------------------------------------------------------------ */
/* 批量关闭标签(左/右/其它方向) */
/* ------------------------------------------------------------------ */
// 批量关闭标签(左/右/其它方向)
/**
* 按方向批量关闭标签的通用执行器。
@@ -407,7 +391,9 @@ function closeDirectional(
});
}
/** 关闭当前激活标签之外的其它标签 */
/**
* 关闭当前激活标签之外的其它标签。
*/
const closeOtherTagsForActive = () => {
if (!currentTag.value) return;
tagsViewStore.delOtherViews(currentTag.value).then(() => {
@@ -646,19 +632,33 @@ useContextMenuManager();
background-color: var(--el-fill-color-light);
}
:deep(.el-icon) {
:deep(.el-icon),
:deep(div) {
opacity: 0.45;
}
&:hover :deep(.el-icon) {
&:hover :deep(.el-icon),
&:hover :deep(div) {
opacity: 0.7;
}
}
}
&__dropdown-icon {
flex-shrink: 0;
margin-right: 6px;
opacity: 0.55;
.icon-16 {
width: 16px;
height: 16px;
}
.icon-14 {
flex-shrink: 0;
width: 14px;
height: 14px;
margin-right: 6px;
opacity: 0.55;
&.el-icon {
width: 14px;
height: 14px;
}
}
@@ -697,6 +697,8 @@ useContextMenuManager();
&__icon {
flex-shrink: 0;
width: 16px;
height: 16px;
opacity: 0.55;
}

View File

@@ -1,13 +1,5 @@
/**
* 混合布局菜单逻辑
*
* 从 MixLayout.vue 提取,负责:
* - 顶部菜单项扁平化(单子菜单向上合并 title/icon
* - 侧边菜单路径解析与激活判定
* - 菜单切换导航(自动跳转到第一个可访问子菜单)
* - 路由变化时同步顶部菜单激活态与侧边菜单数据
*
* 模板只绑定返回值,无需关心内部实现。
*/
import type { LocationQueryRaw, RouteRecordRaw } from "vue-router";
import { useLayout } from "../useLayout";
@@ -15,10 +7,6 @@ import { useAppStore, usePermissionStore, useSettingsStore } from "@/stores";
import { isExternal } from "@/utils/index";
import { ElIcon } from "element-plus";
/* ------------------------------------------------------------------ */
/* MenuIcon — 菜单图标渲染组件Element Plus / SVG 双模式) */
/* ------------------------------------------------------------------ */
const MenuIcon = defineComponent({
props: { icon: String },
setup(props) {
@@ -39,9 +27,6 @@ const MenuIcon = defineComponent({
},
});
/* ------------------------------------------------------------------ */
/* useMixMenu */
/* ------------------------------------------------------------------ */
export function useMixMenu() {
const route = useRoute();
@@ -53,7 +38,6 @@ export function useMixMenu() {
const { activeTopMenuPath, sideMenuRoutes } = useLayout();
/* ------ 顶部菜单 ------------------------------------------------- */
/**
* 顶部菜单项。
@@ -84,9 +68,12 @@ export function useMixMenu() {
});
});
/* ------ 侧边菜单 ------------------------------------------------- */
/** 当前路由对应的侧边菜单激活路径(优先取 meta.activeMenu */
/**
* 当前路由对应的侧边菜单激活路径。
*
* 优先取 meta.activeMenu否则使用当前路由路径。
*/
const activeSideMenuPath = computed(() => {
const { meta, path } = route;
return typeof meta?.activeMenu === "string" ? meta.activeMenu : path;
@@ -105,8 +92,6 @@ export function useMixMenu() {
return `${activeTopMenuPath.value}/${routePath}`;
}
/* ------ 路径工具 ------------------------------------------------- */
/**
* 从完整路径中提取第一段作为顶级菜单路径。
*
@@ -114,14 +99,15 @@ export function useMixMenu() {
* "/dashboard" → "/"
*/
function extractTopMenuPath(path: string): string {
return path.split("/").filter(Boolean).length > 1
? path.match(/^\/[^/]+/)?.[0] || "/"
: "/";
return path.split("/").filter(Boolean).length > 1 ? path.match(/^\/[^/]+/)?.[0] || "/" : "/";
}
/* ------ 菜单切换 ------------------------------------------------- */
/** 顶部菜单点击:切换侧边菜单数据,并导航到第一个可访问叶子菜单 */
/**
* 顶部菜单点击。
*
* 切换侧边菜单数据,并导航到第一个可访问叶子菜单。
*/
function handleTopMenuSelect(menuPath: string) {
if (menuPath === activeTopMenuPath.value) return;
@@ -153,7 +139,7 @@ export function useMixMenu() {
}
}
/* ------ 路由同步 ------------------------------------------------- */
// 路由同步
/**
* 监听路由变化,保持顶部菜单激活态和侧边菜单数据同步。

View File

@@ -58,16 +58,30 @@ export function useLayout() {
// 菜单数据
// ============================================
/** 路由列表(左侧/顶部菜单) */
/**
* 路由列表。
*
* 左侧/顶部菜单共用,由权限 store 生成。
*/
const routes = computed(() => permissionStore.routes);
/** 混合布局侧边菜单 */
/**
* 混合布局侧边菜单。
*
* 根据当前激活的顶部菜单路径动态计算。
*/
const sideMenuRoutes = computed(() => permissionStore.mixLayoutSideMenus);
/** 顶部菜单激活路径 */
/**
* 顶部菜单激活路径。
*/
const activeTopMenuPath = computed(() => appStore.activeTopMenuPath);
/** 当前激活菜单 */
/**
* 当前激活菜单。
*
* 优先取路由 meta.activeMenu否则取当前路径。
*/
const activeMenu = computed(() => {
const { meta, path } = route;
return meta?.activeMenu || path;

View File

@@ -1,41 +0,0 @@
export interface PageQueryParams {
pageNum: number;
pageSize: number;
[key: string]: any;
}
export interface OptionItem {
label: string;
value: string | number;
meta?: any;
children?: OptionItem[];
}
export interface ExcelResult {
/** 状态码 */
code: number;
/** 无效数据条数 */
invalidCount: number;
/** 有效数据条数 */
validCount: number;
/** 错误信息 */
messageList: Array<string>;
}
export interface PageResult<T> {
total: number;
list: T[];
params: PageQueryParams;
}
export interface ApiResponse<T = any> {
code: number | string;
msg: string;
data: T;
}
export interface BaseEntity {
id: number;
createTime: string;
updateTime: string;
}

View File

@@ -4,11 +4,6 @@
* @deprecated 请使用 @/types 下的具名导出
*/
declare global {
type ApiResponse<T = unknown> = import("@/types/api").ApiResponse<T>;
type BaseQueryParams = import("@/types/api").BaseQueryParams;
type PageResult<T> = import("@/types/api").PageResult<T>;
type OptionItem = import("@/types/api").OptionItem;
type ExcelResult = import("@/types/api").ExcelResult;
type TagView = import("@/types/ui").TagView;
type AppSettings = import("@/types/ui").AppSettings;
}

View File

@@ -44,7 +44,7 @@ function extractFileName(contentDisposition: string): string {
* downloadFile(response, "用户列表.xlsx");
* ```
*/
export function downloadFile(response: any, customFileName?: string): void {
export function downloadFile(response: { data: any; headers: Record<string, string> }, customFileName?: string): void {
try {
const fileData = response.data;
const contentDisposition = response.headers["content-disposition"];

View File

@@ -579,11 +579,8 @@ $radius: 10px;
&__end {
display: flex;
gap: 16px;
gap: 20px;
align-items: center;
padding: 10px 14px;
background: var(--el-color-primary-light-9);
border-radius: 8px;
}
}

View File

@@ -31,7 +31,7 @@
</el-button>
<el-button
v-hasPerm="['sys:config:refresh']"
color="#626aef"
type="primary"
icon="RefreshLeft"
@click="refreshCache"
>

View File

@@ -328,17 +328,10 @@ function handleQuery(): void {
}
/**
* 重置查询条件
*/
function resetQuery(): void {
queryFormRef.value?.resetFields();
}
/**
* 重置查询条件并重新查询
* 重置搜索条件并重新查询。
*/
function handleResetQuery(): void {
resetQuery();
queryFormRef.value?.resetFields();
handleQuery();
}

View File

@@ -78,7 +78,7 @@
导入
</el-button>
<el-button v-hasPerm="'sys:user:export'" icon="download" @click="exportUsers">
<el-button v-hasPerm="'sys:user:export'" icon="download" @click="handleExport">
导出
</el-button>
</div>
@@ -347,19 +347,12 @@ function handleQuery(): void {
}
/**
* 重置查询条件
* 重置搜索条件并重新查询。
*/
function resetQuery(): void {
function handleResetQuery(): void {
queryFormRef.value?.resetFields();
tableData.params.deptId = undefined;
tableData.params.createTime = undefined;
}
/**
* 重置查询条件并重新查询
*/
function handleResetQuery(): void {
resetQuery();
handleQuery();
}
@@ -509,10 +502,7 @@ function handleDelete(id?: string): void {
);
}
/**
* 导出用户列表
*/
async function exportUsers(): Promise<void> {
async function handleExport(): Promise<void> {
const response = await UserAPI.export(tableData.params);
downloadFile(response);
ElMessage.success("导出成功");