-
+
+
-
+
+
+
+
+
+
- 搜索
- 重置
+
+
+
+
+ {{ button.text }}
+
+
+
+
+
+
+
+
+ 搜索
+
+
+
+ 重置
+
+
+
@@ -51,6 +103,17 @@
+
+
+
+
@@ -59,9 +122,26 @@
diff --git a/frontend/src/components/CURD/types.ts b/frontend/src/components/CURD/types.ts
index fd9d87ef..909c1618 100644
--- a/frontend/src/components/CURD/types.ts
+++ b/frontend/src/components/CURD/types.ts
@@ -19,8 +19,23 @@ export type IObject = Record
;
*/
type DateComponent = "date-picker" | "time-picker" | "time-select" | "custom-tag" | "input-tag";
type InputComponent = "input" | "select" | "input-number" | "cascader" | "tree-select";
-type OtherComponent = "text" | "radio" | "checkbox" | "switch" | "icon-select" | "custom";
-export type ISearchComponent = DateComponent | InputComponent;
+type OtherComponent =
+ | "text"
+ | "radio"
+ | "checkbox"
+ | "switch"
+ | "rate"
+ | "slider"
+ | "icon-select"
+ | "custom";
+export type ISearchComponent =
+ | DateComponent
+ | InputComponent
+ | "radio"
+ | "checkbox"
+ | "switch"
+ | "rate"
+ | "slider";
export type IComponentType = DateComponent | InputComponent | OtherComponent;
/**
@@ -87,6 +102,33 @@ export interface ISearchConfig {
form?: IForm;
/** 自适应网格布局(使用时表单不要添加 style: { width: "200px" }) */
grid?: boolean | "left" | "right";
+ /** 自定义组件映射 */
+ customComponents?: Record;
+ /** 是否显示搜索按钮(默认:true) */
+ showSearchButton?: boolean;
+ /** 是否显示重置按钮(默认:true) */
+ showResetButton?: boolean;
+ /** 搜索按钮权限 */
+ searchButtonPerm?: string | string[];
+ /** 重置按钮权限 */
+ resetButtonPerm?: string | string[];
+ /** 是否显示搜索区域切换按钮(默认:false) */
+ showToggle?: boolean;
+ /** 搜索区域切换按钮权限 */
+ togglePerm?: string | string[];
+ /** 自定义按钮组 */
+ customButtons?: Array<{
+ /** 按钮唯一标识 */
+ key: string;
+ /** 按钮文本 */
+ text: string;
+ /** 按钮属性 */
+ attrs?: IObject;
+ /** 按钮权限 */
+ perm?: string | string[];
+ /** 点击事件处理函数 */
+ handler?: (params: IObject, instance: any) => void;
+ }>;
}
/**
@@ -105,13 +147,13 @@ export interface IContentConfig {
pagePosition?: "left" | "right";
/** pagination组件属性 */
pagination?:
- | boolean
- | Partial<
- Omit<
- PaginationProps,
- "v-model:page-size" | "v-model:current-page" | "total" | "currentPage"
- >
- >;
+ | boolean
+ | Partial<
+ Omit<
+ PaginationProps,
+ "v-model:page-size" | "v-model:current-page" | "total" | "currentPage"
+ >
+ >;
/** 列表的网络请求函数(需返回promise) */
indexAction: (queryParams: T) => Promise;
/** 默认的分页相关的请求参数 */
@@ -173,17 +215,17 @@ export interface IContentConfig {
show?: boolean;
/** 模板类型 */
templet?:
- | "image"
- | "list"
- | "url"
- | "switch"
- | "input"
- | "price"
- | "percent"
- | "icon"
- | "date"
- | "tool"
- | "custom";
+ | "image"
+ | "list"
+ | "url"
+ | "switch"
+ | "input"
+ | "price"
+ | "percent"
+ | "icon"
+ | "date"
+ | "tool"
+ | "custom";
/** image模板相关参数 */
imageWidth?: number;
/** image模板相关参数 */
@@ -270,7 +312,7 @@ export type IFormItems = Array<{
/** 属性 */
attrs?: IObject;
/** 选项 */
- options?: Array<{ label: string; value: any;[key: string]: any }> | Ref;
+ options?: Array<{ label: string; value: any; [key: string]: any }> | Ref;
/** 规则 */
rules?: FormItemRule[];
/** 初始值 */
diff --git a/frontend/src/components/CURD/usePage.ts b/frontend/src/components/CURD/usePage.ts
index bf836331..0005a30d 100644
--- a/frontend/src/components/CURD/usePage.ts
+++ b/frontend/src/components/CURD/usePage.ts
@@ -55,7 +55,9 @@ function usePage() {
}
} catch (error) {
console.error("打开新增模态框失败:", error);
- ElMessage.error("打开新增模态框失败: " + (error instanceof Error ? error.message : String(error)));
+ ElMessage.error(
+ "打开新增模态框失败: " + (error instanceof Error ? error.message : String(error))
+ );
}
}
@@ -84,7 +86,9 @@ function usePage() {
}
} catch (error) {
console.error("打开编辑模态框失败:", error);
- ElMessage.error("打开编辑模态框失败: " + (error instanceof Error ? error.message : String(error)));
+ ElMessage.error(
+ "打开编辑模态框失败: " + (error instanceof Error ? error.message : String(error))
+ );
}
}
@@ -113,7 +117,9 @@ function usePage() {
}
} catch (error) {
console.error("打开查看模态框失败:", error);
- ElMessage.error("打开查看模态框失败: " + (error instanceof Error ? error.message : String(error)));
+ ElMessage.error(
+ "打开查看模态框失败: " + (error instanceof Error ? error.message : String(error))
+ );
}
}
@@ -153,7 +159,9 @@ function usePage() {
searchRef.value?.toggleVisible();
} catch (error) {
console.error("切换搜索栏失败:", error);
- ElMessage.error("切换搜索栏失败: " + (error instanceof Error ? error.message : String(error)));
+ ElMessage.error(
+ "切换搜索栏失败: " + (error instanceof Error ? error.message : String(error))
+ );
}
}
@@ -190,7 +198,9 @@ function usePage() {
handleSubmitClick();
} catch (error) {
console.error("处理更多操作失败:", error);
- ElMessage.error("处理更多操作失败: " + (error instanceof Error ? error.message : String(error)));
+ ElMessage.error(
+ "处理更多操作失败: " + (error instanceof Error ? error.message : String(error))
+ );
}
}
diff --git a/frontend/src/components/DatePicker/index.vue b/frontend/src/components/DatePicker/index.vue
index 805b0860..6982b4d0 100644
--- a/frontend/src/components/DatePicker/index.vue
+++ b/frontend/src/components/DatePicker/index.vue
@@ -15,67 +15,66 @@
diff --git a/frontend/src/components/IntervalTab/index.vue b/frontend/src/components/IntervalTab/index.vue
index 75e2b752..8eeba257 100644
--- a/frontend/src/components/IntervalTab/index.vue
+++ b/frontend/src/components/IntervalTab/index.vue
@@ -1,33 +1,58 @@
-
+
*
- {{ second }}
+
+ {{ second }}
+
*
- {{min}}
+
+ {{ min }}
+
*
- {{hour}}
+
+ {{ hour }}
+
*
- {{day}}
+ {{ day }}
*
- {{week.label}}
+
+ {{ week.label }}
+
@@ -39,7 +64,6 @@
@@ -54,7 +54,9 @@ const displayText = computed(() => {
white-space: pre-wrap;
word-break: break-word;
margin: 0;
- font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
+ font-family:
+ ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New",
+ monospace;
font-size: 12px;
}
diff --git a/frontend/src/components/MenuSearch/index.vue b/frontend/src/components/MenuSearch/index.vue
index 4fd06e7e..2036b54d 100644
--- a/frontend/src/components/MenuSearch/index.vue
+++ b/frontend/src/components/MenuSearch/index.vue
@@ -296,7 +296,8 @@ function loadRoutes(routes: RouteRecordRaw[], parentPath = "") {
: `${parentPath}${parentPath.endsWith("/") ? "" : "/"}${route.path}`;
// 检查是否需要排除
- if (excludedRoutes.value.includes(route.path) || isExternal(route.path) || route.meta?.hidden) return;
+ if (excludedRoutes.value.includes(route.path) || isExternal(route.path) || route.meta?.hidden)
+ return;
// 处理有子路由的情况
if (route.children) {
@@ -316,7 +317,7 @@ function loadRoutes(routes: RouteRecordRaw[], parentPath = "") {
}
// 递归处理子路由
loadRoutes(route.children, path);
- }
+ }
// 处理没有子路由但有title的情况
else if (route.meta?.title) {
const title = route.meta.title === "dashboard" ? "首页" : route.meta.title;
diff --git a/frontend/src/components/Notification/index.vue b/frontend/src/components/Notification/index.vue
index c5887c7f..56866001 100644
--- a/frontend/src/components/Notification/index.vue
+++ b/frontend/src/components/Notification/index.vue
@@ -13,11 +13,11 @@
- {{ item.notice_type === '1' ? '通知' : '公告' }}
+ {{ item.notice_type === "1" ? "通知" : "公告" }}
-
+
{{ item.notice_content }}
@@ -28,7 +28,7 @@
-
+
查看更多
@@ -36,7 +36,12 @@
-
+
全部已读
@@ -50,7 +55,12 @@
-
+
@@ -89,7 +99,7 @@ const noticeDetail = ref(null);
* 获取我的通知公告
*/
async function featchMyNotice() {
- await noticeStore.getNotice()
+ await noticeStore.getNotice();
noticeList.value = noticeStore.noticeList;
}
@@ -100,7 +110,9 @@ function handleViewMoreNotice() {
// 全部已读:将这些公告禁用(status=false),刷新后不再出现
function handleMarkAllAsRead() {
- const ids = noticeList.value.map((item) => item.id).filter((id): id is number => id !== undefined);
+ const ids = noticeList.value
+ .map((item) => item.id)
+ .filter((id): id is number => id !== undefined);
NoticeAPI.batchAvailableNotice({ ids, status: false }).then(async () => {
await noticeStore.getNotice();
noticeList.value = noticeStore.noticeList;
@@ -110,8 +122,6 @@ function handleMarkAllAsRead() {
onMounted(() => {
featchMyNotice();
});
-
-
diff --git a/frontend/src/components/OperationColumn/index.vue b/frontend/src/components/OperationColumn/index.vue
index b5d007ea..eb5c1354 100644
--- a/frontend/src/components/OperationColumn/index.vue
+++ b/frontend/src/components/OperationColumn/index.vue
@@ -49,8 +49,8 @@ const calculateWidth = () => {
// 计算最终宽度
const finalWidth = computed(() => {
- const widthNum = typeof props.width === 'number' ? props.width : Number(props.width);
- const minWidthNum = typeof props.minWidth === 'number' ? props.minWidth : Number(props.minWidth);
+ const widthNum = typeof props.width === "number" ? props.width : Number(props.width);
+ const minWidthNum = typeof props.minWidth === "number" ? props.minWidth : Number(props.minWidth);
return widthNum || operationWidth.value || minWidthNum;
});
diff --git a/frontend/src/components/SizeSelect/index.vue b/frontend/src/components/SizeSelect/index.vue
index e4993724..a329289d 100644
--- a/frontend/src/components/SizeSelect/index.vue
+++ b/frontend/src/components/SizeSelect/index.vue
@@ -1,4 +1,4 @@
-
+
diff --git a/frontend/src/components/TableSelect/index.vue b/frontend/src/components/TableSelect/index.vue
index fc992d1d..aeed5054 100644
--- a/frontend/src/components/TableSelect/index.vue
+++ b/frontend/src/components/TableSelect/index.vue
@@ -35,7 +35,7 @@
-
+
@@ -156,7 +156,9 @@ export interface ISelectConfig {
// 占位符
placeholder?: string;
// popover组件属性
- popover?: Partial> & { width?: number | string };
+ popover?: Partial> & {
+ width?: number | string;
+ };
// 列表的网络请求函数(需返回promise)
indexAction: (_queryParams: T) => Promise;
// 主键名(跨页选择必填,默认为id)
diff --git a/frontend/src/components/Upload/SingleImageUpload.vue b/frontend/src/components/Upload/SingleImageUpload.vue
index e2852fc9..9cd1cf1b 100644
--- a/frontend/src/components/Upload/SingleImageUpload.vue
+++ b/frontend/src/components/Upload/SingleImageUpload.vue
@@ -25,9 +25,9 @@
:preview-teleported="true"
@click.stop="handleImageClick"
/>
-
@@ -49,7 +49,7 @@
@@ -308,7 +317,7 @@ const onError = (error: any) => {
position: relative;
width: v-bind("props.style.width ?? '150px'");
height: v-bind("props.style.height ?? '150px'");
-
+
&:hover {
.single-upload__delete-btn {
display: block;
@@ -337,7 +346,7 @@ const onError = (error: any) => {
color: #ff4500;
}
}
-
+
&__add-btn {
font-size: 28px;
color: #8c939d;
diff --git a/frontend/src/components/WangEditor/index.vue b/frontend/src/components/WangEditor/index.vue
index 1eaadc5f..b28b1260 100644
--- a/frontend/src/components/WangEditor/index.vue
+++ b/frontend/src/components/WangEditor/index.vue
@@ -50,10 +50,10 @@ const FileAPI = {
// 这里应该是实际的文件上传逻辑
// 暂时返回一个mock数据
return Promise.resolve({
- url: 'https://example.com/image.jpg',
- name: file.name
+ url: "https://example.com/image.jpg",
+ name: file.name,
});
- }
+ },
};
defineProps({
diff --git a/frontend/src/directives/permission/index.ts b/frontend/src/directives/permission/index.ts
index 49d67380..245dcbe2 100644
--- a/frontend/src/directives/permission/index.ts
+++ b/frontend/src/directives/permission/index.ts
@@ -21,7 +21,10 @@ export const hasPerm: Directive = {
const userPrems = useUserStore().prems;
// 超级管理员拥有所有权限,如果是"*:*:*"权限标识,则不需要进行权限校验
- if ((roles && roles.map((role) => role.code).includes(ROLE_ROOT)) || requiredPerms.includes("*:*:*")) {
+ if (
+ (roles && roles.map((role) => role.code).includes(ROLE_ROOT)) ||
+ requiredPerms.includes("*:*:*")
+ ) {
return;
}
@@ -55,8 +58,12 @@ export const hasRole: Directive = {
// 检查是否有对应角色权限
const hasAuth = Array.isArray(requiredRoles)
- ? requiredRoles.some((role) => roles ? roles.map((role) => role.code).includes(role) : false)
- : roles ? roles.map((role) => role.code).includes(requiredRoles) : false;
+ ? requiredRoles.some((role) =>
+ roles ? roles.map((role) => role.code).includes(role) : false
+ )
+ : roles
+ ? roles.map((role) => role.code).includes(requiredRoles)
+ : false;
// 如果没有权限,移除元素
if (!hasAuth && el.parentNode) {
diff --git a/frontend/src/enums/api/result.enum.ts b/frontend/src/enums/api/result.enum.ts
index c95d2e27..57666691 100644
--- a/frontend/src/enums/api/result.enum.ts
+++ b/frontend/src/enums/api/result.enum.ts
@@ -24,5 +24,4 @@ export const enum ResultEnum {
* 令牌已过期
*/
TOKEN_EXPIRED = 10401,
-
}
diff --git a/frontend/src/lang/package/en.ts b/frontend/src/lang/package/en.ts
index a3e76049..4b7315b5 100644
--- a/frontend/src/lang/package/en.ts
+++ b/frontend/src/lang/package/en.ts
@@ -16,53 +16,53 @@ export default {
back: "Back",
close: "Close",
refresh: "Refresh",
- inputText: 'Please enter',
- selectText: 'Please select',
- startTimeText: 'Start Time',
- endTimeText: 'End Time',
- login: 'Login',
- required: 'This field is required',
- loginOut: 'Logout',
- document: 'Project Documentation',
- reminder: 'Reminder',
- loginOutMessage: 'Are you sure you want to log out?',
- ok: 'OK',
- reload: 'Reload',
- closeTab: 'Close Tab',
- closeTheLeftTab: 'Close Left Tabs',
- closeTheRightTab: 'Close Right Tabs',
- closeOther: 'Close Other Tabs',
- closeAll: 'Close All Tabs',
- prevLabel: 'Previous',
- nextLabel: 'Next',
- skipLabel: 'Skip',
- doneLabel: 'Done',
- menu: 'Menu',
- menuDes: 'Menu rendered based on route structure',
- collapse: 'Collapse/Expand',
- collapseDes: 'Expand and collapse the menu bar',
- tagsView: 'Tags View',
- tagsViewDes: 'Used to record route history',
- tool: 'Tool',
- toolDes: 'Used to customize system settings',
- shrink: 'Collapse',
- expand: 'Expand',
- delMessage: 'Delete selected data?',
- delWarning: 'Warning',
- delOk: 'OK',
- delCancel: 'Cancel',
- delNoData: 'Please select data to delete',
- delSuccess: 'Deleted successfully',
- fullscreen: 'Fullscreen',
- size: 'Size',
- columnSetting: 'Column Settings',
- lengthRange: 'Length must be between {min} and {max} characters',
- notSpace: 'Cannot contain spaces',
- notSpecialCharacters: 'Cannot contain special characters',
- isEqual: 'The two inputs are inconsistent',
- setting: 'Settings',
- copySuccess: 'Copy successfully',
- copyFailed: 'Copy failed',
+ inputText: "Please enter",
+ selectText: "Please select",
+ startTimeText: "Start Time",
+ endTimeText: "End Time",
+ login: "Login",
+ required: "This field is required",
+ loginOut: "Logout",
+ document: "Project Documentation",
+ reminder: "Reminder",
+ loginOutMessage: "Are you sure you want to log out?",
+ ok: "OK",
+ reload: "Reload",
+ closeTab: "Close Tab",
+ closeTheLeftTab: "Close Left Tabs",
+ closeTheRightTab: "Close Right Tabs",
+ closeOther: "Close Other Tabs",
+ closeAll: "Close All Tabs",
+ prevLabel: "Previous",
+ nextLabel: "Next",
+ skipLabel: "Skip",
+ doneLabel: "Done",
+ menu: "Menu",
+ menuDes: "Menu rendered based on route structure",
+ collapse: "Collapse/Expand",
+ collapseDes: "Expand and collapse the menu bar",
+ tagsView: "Tags View",
+ tagsViewDes: "Used to record route history",
+ tool: "Tool",
+ toolDes: "Used to customize system settings",
+ shrink: "Collapse",
+ expand: "Expand",
+ delMessage: "Delete selected data?",
+ delWarning: "Warning",
+ delOk: "OK",
+ delCancel: "Cancel",
+ delNoData: "Please select data to delete",
+ delSuccess: "Deleted successfully",
+ fullscreen: "Fullscreen",
+ size: "Size",
+ columnSetting: "Column Settings",
+ lengthRange: "Length must be between {min} and {max} characters",
+ notSpace: "Cannot contain spaces",
+ notSpecialCharacters: "Cannot contain special characters",
+ isEqual: "The two inputs are inconsistent",
+ setting: "Settings",
+ copySuccess: "Copy successfully",
+ copyFailed: "Copy failed",
},
// Menu internationalization
route: {
@@ -113,7 +113,7 @@ export default {
},
agree: {
required: "Please agree to the User Agreement",
- }
+ },
},
otherLoginMethods: "Other",
resetPassword: "Reset password",
@@ -132,8 +132,8 @@ export default {
logout: "Logout",
document: "Document",
gitee: "Gitee",
- lock: 'Lock',
- unlock: 'Unlock',
+ lock: "Lock",
+ unlock: "Unlock",
profile: "User Profile",
config: "Config Center",
tour: "Project Tour",
@@ -143,16 +143,16 @@ export default {
closeRight: "Close Right",
closeOther: "Close Other",
closeAll: "Close All",
- refreshCache: 'Refresh Cache',
+ refreshCache: "Refresh Cache",
},
- lock: {
- lockScreen: 'Lock screen',
- lockPassword: 'Lock screen password',
- unlock: 'Click to unlock',
- backToLogin: 'Back to login',
- entrySystem: 'Entry the system',
- placeholder: 'Please enter the lock screen password',
- message: 'Lock screen password error'
+ lock: {
+ lockScreen: "Lock screen",
+ lockPassword: "Lock screen password",
+ unlock: "Click to unlock",
+ backToLogin: "Back to login",
+ entrySystem: "Entry the system",
+ placeholder: "Please enter the lock screen password",
+ message: "Lock screen password error",
},
sizeSelect: {
tooltip: "Layout Size",
@@ -208,8 +208,8 @@ export default {
},
error: {
noPermission: `Sorry, you don't have permission to access this page.`,
- pageError: 'Sorry, the page you visited does not exist.',
- networkError: 'Sorry, the server reported an error.',
- returnToHome: 'Return to home'
+ pageError: "Sorry, the page you visited does not exist.",
+ networkError: "Sorry, the server reported an error.",
+ returnToHome: "Return to home",
},
};
diff --git a/frontend/src/lang/package/zh-cn.ts b/frontend/src/lang/package/zh-cn.ts
index 80db419e..7c76524b 100644
--- a/frontend/src/lang/package/zh-cn.ts
+++ b/frontend/src/lang/package/zh-cn.ts
@@ -16,53 +16,53 @@ export default {
back: "返回",
close: "关闭",
refresh: "刷新",
- inputText: '请输入',
- selectText: '请选择',
- startTimeText: '开始时间',
- endTimeText: '结束时间',
- login: '登录',
- required: '该项为必填项',
- loginOut: '退出系统',
- document: '项目文档',
- reminder: '温馨提示',
- loginOutMessage: '是否退出本系统?',
- ok: '确定',
- reload: '重新加载',
- closeTab: '关闭标签页',
- closeTheLeftTab: '关闭左侧标签页',
- closeTheRightTab: '关闭右侧标签页',
- closeOther: '关闭其它标签页',
- closeAll: '关闭全部标签页',
- prevLabel: '上一步',
- nextLabel: '下一步',
- skipLabel: '跳过',
- doneLabel: '结束',
- menu: '菜单',
- menuDes: '以路由的结构渲染的菜单栏',
- collapse: '展开缩收',
- collapseDes: '展开和缩放菜单栏',
- tagsView: '标签页',
- tagsViewDes: '用于记录路由历史记录',
- tool: '工具',
- toolDes: '用于设置定制系统',
- shrink: '收起',
- expand: '展开',
- delMessage: '是否删除所选中数据?',
- delWarning: '提示',
- delOk: '确定',
- delCancel: '取消',
- delNoData: '请选择需要删除的数据',
- delSuccess: '删除成功',
- fullscreen: '全屏',
- size: '尺寸',
- columnSetting: '列设置',
- lengthRange: '长度在 {min} 到 {max} 个字符',
- notSpace: '不能包含空格',
- notSpecialCharacters: '不能包含特殊字符',
- isEqual: '两次输入不一致',
- setting: '设置',
- copySuccess: '复制成功',
- copyFailed: '复制失败',
+ inputText: "请输入",
+ selectText: "请选择",
+ startTimeText: "开始时间",
+ endTimeText: "结束时间",
+ login: "登录",
+ required: "该项为必填项",
+ loginOut: "退出系统",
+ document: "项目文档",
+ reminder: "温馨提示",
+ loginOutMessage: "是否退出本系统?",
+ ok: "确定",
+ reload: "重新加载",
+ closeTab: "关闭标签页",
+ closeTheLeftTab: "关闭左侧标签页",
+ closeTheRightTab: "关闭右侧标签页",
+ closeOther: "关闭其它标签页",
+ closeAll: "关闭全部标签页",
+ prevLabel: "上一步",
+ nextLabel: "下一步",
+ skipLabel: "跳过",
+ doneLabel: "结束",
+ menu: "菜单",
+ menuDes: "以路由的结构渲染的菜单栏",
+ collapse: "展开缩收",
+ collapseDes: "展开和缩放菜单栏",
+ tagsView: "标签页",
+ tagsViewDes: "用于记录路由历史记录",
+ tool: "工具",
+ toolDes: "用于设置定制系统",
+ shrink: "收起",
+ expand: "展开",
+ delMessage: "是否删除所选中数据?",
+ delWarning: "提示",
+ delOk: "确定",
+ delCancel: "取消",
+ delNoData: "请选择需要删除的数据",
+ delSuccess: "删除成功",
+ fullscreen: "全屏",
+ size: "尺寸",
+ columnSetting: "列设置",
+ lengthRange: "长度在 {min} 到 {max} 个字符",
+ notSpace: "不能包含空格",
+ notSpecialCharacters: "不能包含特殊字符",
+ isEqual: "两次输入不一致",
+ setting: "设置",
+ copySuccess: "复制成功",
+ copyFailed: "复制失败",
},
// 菜单国际化
route: {
@@ -113,7 +113,7 @@ export default {
},
agree: {
required: "请先勾选同意用户协议",
- }
+ },
},
otherLoginMethods: "其他",
resetPassword: "重置密码",
@@ -132,8 +132,8 @@ export default {
logout: "退出登录",
document: "项目文档",
gitee: "项目地址",
- lock: '锁定屏幕',
- unlock: '解锁屏幕',
+ lock: "锁定屏幕",
+ unlock: "解锁屏幕",
profile: "个人中心",
config: "配置中心",
tour: "项目引导",
@@ -143,17 +143,17 @@ export default {
closeRight: "关闭右侧",
closeOther: "关闭其他",
closeAll: "关闭所有",
- refreshCache: '刷新缓存',
+ refreshCache: "刷新缓存",
},
lock: {
- lockScreen: '锁定屏幕',
- lockPassword: '锁屏密码',
- unlock: '点击解锁',
- backToLogin: '返回登录',
- entrySystem: '进入系统',
- placeholder: '请输入锁屏密码',
- required: '请输入必填项',
- message: '锁屏密码错误'
+ lockScreen: "锁定屏幕",
+ lockPassword: "锁屏密码",
+ unlock: "点击解锁",
+ backToLogin: "返回登录",
+ entrySystem: "进入系统",
+ placeholder: "请输入锁屏密码",
+ required: "请输入必填项",
+ message: "锁屏密码错误",
},
sizeSelect: {
tooltip: "布局大小",
@@ -210,8 +210,8 @@ export default {
},
error: {
noPermission: `抱歉,您无权访问此页面。`,
- pageError: '抱歉,您访问的页面不存在。',
- networkError: '抱歉,服务器报告错误。',
- returnToHome: '返回首页'
+ pageError: "抱歉,您访问的页面不存在。",
+ networkError: "抱歉,服务器报告错误。",
+ returnToHome: "返回首页",
},
};
diff --git a/frontend/src/layouts/components/Menu/MixTopMenu.vue b/frontend/src/layouts/components/Menu/MixTopMenu.vue
index ea471b7e..2b0d11b5 100644
--- a/frontend/src/layouts/components/Menu/MixTopMenu.vue
+++ b/frontend/src/layouts/components/Menu/MixTopMenu.vue
@@ -58,51 +58,53 @@ const topMenus = ref([]);
// 处理后的顶部菜单列表 - 智能显示唯一子菜单的标题
const processedTopMenus = computed(() => {
- return topMenus.value
- .map((route) => {
- // 如果路由本身设置了hidden=true,直接返回null
- if (route.meta?.hidden === true) {
- return null;
- }
-
- // 如果路由设置了 alwaysShow=true,或者没有子菜单,直接返回原路由
- if (route.meta?.alwaysShow || !route.children || route.children.length === 0) {
- return route;
- }
-
- // 过滤出非隐藏的子菜单
- const visibleChildren = route.children.filter((child) => !child.meta?.hidden);
-
- // 如果没有可见子菜单,返回null
- if (visibleChildren.length === 0) {
- return null;
- }
-
- // 如果只有一个非隐藏的子菜单,显示子菜单的信息
- if (visibleChildren.length === 1) {
- const onlyChild = visibleChildren[0];
- // 检查子菜单是否应该显示在顶部菜单
- if (onlyChild.meta?.hidden !== true) {
- return {
- ...route,
- meta: {
- ...route.meta,
- title: onlyChild.meta?.title || route.meta?.title,
- icon: onlyChild.meta?.icon || route.meta?.icon,
- },
- // 使用子菜单的path确保路由匹配正确
- path: onlyChild.path
- };
+ return (
+ topMenus.value
+ .map((route) => {
+ // 如果路由本身设置了hidden=true,直接返回null
+ if (route.meta?.hidden === true) {
+ return null;
}
- // 如果子菜单应该隐藏,则不显示该顶级菜单
- return null;
- }
- // 其他情况返回原路由
- return route;
- })
- // 过滤掉null值
- .filter((route) => route !== null);
+ // 如果路由设置了 alwaysShow=true,或者没有子菜单,直接返回原路由
+ if (route.meta?.alwaysShow || !route.children || route.children.length === 0) {
+ return route;
+ }
+
+ // 过滤出非隐藏的子菜单
+ const visibleChildren = route.children.filter((child) => !child.meta?.hidden);
+
+ // 如果没有可见子菜单,返回null
+ if (visibleChildren.length === 0) {
+ return null;
+ }
+
+ // 如果只有一个非隐藏的子菜单,显示子菜单的信息
+ if (visibleChildren.length === 1) {
+ const onlyChild = visibleChildren[0];
+ // 检查子菜单是否应该显示在顶部菜单
+ if (onlyChild.meta?.hidden !== true) {
+ return {
+ ...route,
+ meta: {
+ ...route.meta,
+ title: onlyChild.meta?.title || route.meta?.title,
+ icon: onlyChild.meta?.icon || route.meta?.icon,
+ },
+ // 使用子菜单的path确保路由匹配正确
+ path: onlyChild.path,
+ };
+ }
+ // 如果子菜单应该隐藏,则不显示该顶级菜单
+ return null;
+ }
+
+ // 其他情况返回原路由
+ return route;
+ })
+ // 过滤掉null值
+ .filter((route) => route !== null)
+ );
});
/**
diff --git a/frontend/src/layouts/components/NavBar/components/LockDialog.vue b/frontend/src/layouts/components/NavBar/components/LockDialog.vue
index a97b128d..d9aa34a3 100644
--- a/frontend/src/layouts/components/NavBar/components/LockDialog.vue
+++ b/frontend/src/layouts/components/NavBar/components/LockDialog.vue
@@ -13,7 +13,7 @@
{{ userStore.basicInfo.name }}
-
+
- {{ t('navbar.lock') }}
+ {{ t("navbar.lock") }}
-
diff --git a/frontend/src/layouts/components/TagsView/index.vue b/frontend/src/layouts/components/TagsView/index.vue
index 8a92d79b..83c7ebc1 100644
--- a/frontend/src/layouts/components/TagsView/index.vue
+++ b/frontend/src/layouts/components/TagsView/index.vue
@@ -10,16 +10,20 @@
+ @click.middle="handleMiddleClick(tag)"
+ >
- onContextMenuVisibleChange(visible, tag)"
- @click.stop>
+ @click.stop
+ >
{{ translateRouteTitle(tag.title) }}
@@ -32,7 +36,8 @@
+ @click="closeSelectedTag(tag)"
+ >
@@ -41,7 +46,8 @@
+ @click="closeLeftTags"
+ >
@@ -50,7 +56,8 @@
+ @click="closeRightTags"
+ >
@@ -59,16 +66,15 @@
+ @click="closeOtherTags"
+ >
{{ t("navbar.closeOther") }}
-
+
@@ -81,21 +87,24 @@
- {{ isQuickLinkExists(tag) ? '取消收藏' : '收藏' }}
+ {{ isQuickLinkExists(tag) ? "取消收藏" : "收藏" }}
{{ translateRouteTitle(tag.title) }}
-
-
+
+
-
@@ -146,7 +155,10 @@
{{ t("navbar.closeRight") }}
-
+
@@ -172,7 +184,7 @@ import { translateRouteTitle } from "@/utils/i18n";
import { refreshAppCaches, usePermissionStore, useTagsViewStore } from "@/store";
import { VueDraggable } from "vue-draggable-plus";
import { quickStartManager } from "@/utils/quickStartManager";
-import { ElMessage } from 'element-plus';
+import { ElMessage } from "element-plus";
const { t } = useI18n();
@@ -183,12 +195,11 @@ const route = useRoute();
const permissionStore = usePermissionStore();
const tagsViewStore = useTagsViewStore();
-
const { visitedViews } = storeToRefs(tagsViewStore);
// 简单的标签显示逻辑
const displayedViews = computed(() => {
- return visitedViews.value
+ return visitedViews.value;
});
// 当前选中的标签
@@ -203,7 +214,7 @@ const activeTag = computed(() => {
const scrollbarRef = ref();
// 标签切换来源跟踪
-const tagSwitchSource = ref<'menu' | 'tab' | null>(null);
+const tagSwitchSource = ref<"menu" | "tab" | null>(null);
// 路由映射缓存,提升查找性能
const routePathMap = computed(() => {
@@ -282,14 +293,14 @@ const addCurrentTag = () => {
if (!route.meta?.title) return;
// 检查标签是否已存在
- const existingTag = visitedViews.value.find(tag => tag.path === route.path);
+ const existingTag = visitedViews.value.find((tag) => tag.path === route.path);
if (existingTag) {
// 如果标签已存在,根据来源决定是否移动位置
if (!existingTag.affix) {
- if (tagSwitchSource.value === 'menu') {
+ if (tagSwitchSource.value === "menu") {
// 通过菜单点击:移动到最新位置
- const index = visitedViews.value.findIndex(tag => tag.path === route.path);
+ const index = visitedViews.value.findIndex((tag) => tag.path === route.path);
if (index !== -1) {
const tag = visitedViews.value.splice(index, 1)[0];
visitedViews.value.push(tag);
@@ -312,7 +323,7 @@ const addCurrentTag = () => {
}
// 根据来源决定是否滚动
- if (tagSwitchSource.value === 'menu') {
+ if (tagSwitchSource.value === "menu") {
// 通过菜单点击:滚动到最新标签
nextTick(() => {
autoScrollToLatestTag();
@@ -359,7 +370,6 @@ const handleMiddleClick = (tag: TagView) => {
* 处理滚轮事件(优化后)
*/
const handleScroll = (event: WheelEvent) => {
-
const scrollWrapper = scrollbarRef.value?.wrapRef;
if (!scrollWrapper) return;
@@ -373,8 +383,20 @@ const handleScroll = (event: WheelEvent) => {
const deltaX = event.deltaX || 0;
// 计算新的滚动位置
- const newScrollLeft = Math.max(0, Math.min(scrollWrapper.scrollWidth - scrollWrapper.clientWidth, scrollWrapper.scrollLeft + deltaX));
- const newScrollTop = Math.max(0, Math.min(scrollWrapper.scrollHeight - scrollWrapper.clientHeight, scrollWrapper.scrollTop + deltaY));
+ const newScrollLeft = Math.max(
+ 0,
+ Math.min(
+ scrollWrapper.scrollWidth - scrollWrapper.clientWidth,
+ scrollWrapper.scrollLeft + deltaX
+ )
+ );
+ const newScrollTop = Math.max(
+ 0,
+ Math.min(
+ scrollWrapper.scrollHeight - scrollWrapper.clientHeight,
+ scrollWrapper.scrollTop + deltaY
+ )
+ );
scrollbarRef.value.setScrollLeft(newScrollLeft);
scrollbarRef.value.setScrollTop(newScrollTop); // 新增垂直滚动支持
@@ -493,35 +515,35 @@ const handleAction = async (action: string) => {
if (!currentTag) return;
switch (action) {
- case 'refresh':
+ case "refresh":
refreshSelectedTag(currentTag);
break;
- case 'close':
+ case "close":
closeSelectedTag(currentTag);
break;
- case 'closeRight':
+ case "closeRight":
closeRightTags();
break;
- case 'closeLeft':
+ case "closeLeft":
closeLeftTags();
break;
- case 'closeOther':
+ case "closeOther":
closeOtherTags();
break;
- case 'closeAll':
+ case "closeAll":
closeAllTags(currentTag);
break;
- case 'refreshCache':
+ case "refreshCache":
try {
// 1) 刷新服务端缓存(用户/权限/配置/公告/按需字典)并重建路由
await refreshAppCaches();
// 2) 软刷新当前页面,重新加载组件
refreshSelectedTag(currentTag);
// 3) 提示成功
- ElMessage.success(t('navbar.refreshCache') + '完成');
+ ElMessage.success(t("navbar.refreshCache") + "完成");
} catch (error) {
- console.error('刷新缓存失败:', error);
- ElMessage.error('刷新失败');
+ console.error("刷新缓存失败:", error);
+ ElMessage.error("刷新失败");
}
break;
default:
@@ -553,7 +575,7 @@ const toggleQuickStart = (tag: TagView) => {
if (isExists) {
// 取消收藏:找到对应的链接并删除
const links = quickStartManager.getQuickLinks();
- const targetLink = links.find(link => link.href === href);
+ const targetLink = links.find((link) => link.href === href);
if (targetLink?.id) {
quickStartManager.removeQuickLink(targetLink.id);
ElMessage.success(`已取消收藏:${tag.title}`);
@@ -565,8 +587,8 @@ const toggleQuickStart = (tag: TagView) => {
ElMessage.success(`已收藏:${tag.title}`);
}
} catch (error) {
- console.error('Failed to toggle quick start:', error);
- ElMessage.error('操作失败');
+ console.error("Failed to toggle quick start:", error);
+ ElMessage.error("操作失败");
}
};
@@ -581,102 +603,105 @@ const isQuickLinkExists = (tag: TagView): boolean => {
* 向左滚动标签页
*/
const scrollLeft = () => {
- const scrollWrapper = scrollbarRef.value?.wrapRef
- if (!scrollWrapper) return
+ const scrollWrapper = scrollbarRef.value?.wrapRef;
+ if (!scrollWrapper) return;
- const newScrollLeft = Math.max(0, scrollWrapper.scrollLeft - 200)
- scrollbarRef.value.setScrollLeft(newScrollLeft)
-}
+ const newScrollLeft = Math.max(0, scrollWrapper.scrollLeft - 200);
+ scrollbarRef.value.setScrollLeft(newScrollLeft);
+};
/**
* 向右滚动标签页
*/
const scrollRight = () => {
- const scrollWrapper = scrollbarRef.value?.wrapRef
- if (!scrollWrapper) return
+ const scrollWrapper = scrollbarRef.value?.wrapRef;
+ if (!scrollWrapper) return;
- const maxScrollLeft = scrollWrapper.scrollWidth - scrollWrapper.clientWidth
- const newScrollLeft = Math.min(maxScrollLeft, scrollWrapper.scrollLeft + 200)
- scrollbarRef.value.setScrollLeft(newScrollLeft)
+ const maxScrollLeft = scrollWrapper.scrollWidth - scrollWrapper.clientWidth;
+ const newScrollLeft = Math.min(maxScrollLeft, scrollWrapper.scrollLeft + 200);
+ scrollbarRef.value.setScrollLeft(newScrollLeft);
// 如果滚动到最右边,重置滚动状态以允许下次自动滚动
if (newScrollLeft >= maxScrollLeft - 1) {
- scrollState.value.hasScrolledToLatest = false
+ scrollState.value.hasScrolledToLatest = false;
}
-}
+};
/**
* 重置滚动状态
*/
const resetScrollState = () => {
- scrollState.value.hasScrolledToLatest = false
- scrollState.value.isContainerFull = false
-}
+ scrollState.value.hasScrolledToLatest = false;
+ scrollState.value.isContainerFull = false;
+};
// 滚动状态跟踪
const scrollState = ref({
hasScrolledToLatest: false, // 是否已经滚动到最新标签
- isContainerFull: false // 容器是否已满
-})
+ isContainerFull: false, // 容器是否已满
+});
/**
* 自动滚动到最新标签或确保当前激活标签可见
*/
const autoScrollToLatestTag = () => {
- const scrollWrapper = scrollbarRef.value?.wrapRef
- if (!scrollWrapper) return
+ const scrollWrapper = scrollbarRef.value?.wrapRef;
+ if (!scrollWrapper) return;
// 获取容器宽度和内容宽度
- const containerWidth = scrollWrapper.clientWidth
- const contentWidth = scrollWrapper.scrollWidth
+ const containerWidth = scrollWrapper.clientWidth;
+ const contentWidth = scrollWrapper.scrollWidth;
// 判断容器是否已满(内容宽度是否超过容器宽度)
- const isContainerFull = contentWidth > containerWidth
+ const isContainerFull = contentWidth > containerWidth;
// 查找当前激活的标签元素
- const activeTagElement = document.querySelector('.tags-item.active')
-
+ const activeTagElement = document.querySelector(".tags-item.active");
+
if (activeTagElement) {
// 将Element类型断言为HTMLElement类型以访问offsetWidth属性
const activeHtmlElement = activeTagElement as HTMLElement;
// 计算激活标签的位置信息
- const activeTagRect = activeHtmlElement.getBoundingClientRect()
- const containerRect = scrollWrapper.getBoundingClientRect()
-
+ const activeTagRect = activeHtmlElement.getBoundingClientRect();
+ const containerRect = scrollWrapper.getBoundingClientRect();
+
// 计算标签相对于容器的位置
- const tagLeft = activeTagRect.left - containerRect.left + scrollWrapper.scrollLeft
- const tagRight = tagLeft + activeHtmlElement.offsetWidth
-
+ const tagLeft = activeTagRect.left - containerRect.left + scrollWrapper.scrollLeft;
+ const tagRight = tagLeft + activeHtmlElement.offsetWidth;
+
// 检查标签是否完全在可见区域内
- if (tagLeft < scrollWrapper.scrollLeft || tagRight > scrollWrapper.scrollLeft + containerWidth) {
+ if (
+ tagLeft < scrollWrapper.scrollLeft ||
+ tagRight > scrollWrapper.scrollLeft + containerWidth
+ ) {
// 如果标签不在可见区域内,滚动到使标签居中的位置
- const targetScrollLeft = tagLeft - (containerWidth - activeHtmlElement.offsetWidth) / 2
- const maxScrollLeft = contentWidth - containerWidth
- const minScrollLeft = 0
- const clampedScrollLeft = Math.max(minScrollLeft, Math.min(maxScrollLeft, targetScrollLeft))
-
- scrollbarRef.value.setScrollLeft(clampedScrollLeft)
- scrollState.value.hasScrolledToLatest = true
- scrollState.value.isContainerFull = isContainerFull
- return
+ const targetScrollLeft = tagLeft - (containerWidth - activeHtmlElement.offsetWidth) / 2;
+ const maxScrollLeft = contentWidth - containerWidth;
+ const minScrollLeft = 0;
+ const clampedScrollLeft = Math.max(minScrollLeft, Math.min(maxScrollLeft, targetScrollLeft));
+
+ scrollbarRef.value.setScrollLeft(clampedScrollLeft);
+ scrollState.value.hasScrolledToLatest = true;
+ scrollState.value.isContainerFull = isContainerFull;
+ return;
}
}
// 如果没有找到激活标签或激活标签已经在可见区域内,则使用原来的逻辑
if (isContainerFull && !scrollState.value.hasScrolledToLatest) {
// 计算需要滚动到的位置,确保最新标签在右侧可见
- const maxScrollLeft = contentWidth - containerWidth
- scrollbarRef.value.setScrollLeft(maxScrollLeft)
- scrollState.value.hasScrolledToLatest = true
- scrollState.value.isContainerFull = true
+ const maxScrollLeft = contentWidth - containerWidth;
+ scrollbarRef.value.setScrollLeft(maxScrollLeft);
+ scrollState.value.hasScrolledToLatest = true;
+ scrollState.value.isContainerFull = true;
} else if (!isContainerFull) {
// 如果内容宽度不超过容器宽度,滚动到最左边
- scrollbarRef.value.setScrollLeft(0)
+ scrollbarRef.value.setScrollLeft(0);
// 重置滚动状态
- scrollState.value.hasScrolledToLatest = false
- scrollState.value.isContainerFull = false
+ scrollState.value.hasScrolledToLatest = false;
+ scrollState.value.isContainerFull = false;
}
-}
+};
// 监听路由变化
watch(
@@ -684,7 +709,7 @@ watch(
() => {
// 如果没有设置来源,则默认为菜单点击
if (tagSwitchSource.value === null) {
- tagSwitchSource.value = 'menu';
+ tagSwitchSource.value = "menu";
}
addCurrentTag();
updateCurrentTag();
@@ -700,7 +725,7 @@ watch(
() => visitedViews.value.length,
() => {
// 只有在通过菜单添加新标签时才滚动
- if (tagSwitchSource.value === 'menu') {
+ if (tagSwitchSource.value === "menu") {
nextTick(() => {
autoScrollToLatestTag();
});
@@ -721,9 +746,9 @@ watch(
// 初始化
onMounted(() => {
initAffixTags();
-
+
// 监听容器大小变化
- const tagsContainer = document.querySelector('.tags-container');
+ const tagsContainer = document.querySelector(".tags-container");
if (tagsContainer && window.ResizeObserver) {
resizeObserver = new ResizeObserver(() => {
// 强制重新计算 displayedViews
@@ -741,7 +766,6 @@ onUnmounted(() => {
resizeObserver.disconnect();
}
});
-
diff --git a/frontend/src/views/current/profile.vue b/frontend/src/views/current/profile.vue
index f3af872b..f430ce94 100644
--- a/frontend/src/views/current/profile.vue
+++ b/frontend/src/views/current/profile.vue
@@ -10,114 +10,115 @@