feat(web): 升级至 v3.0 并重构组件与引导流程

- 提取应用引导逻辑至 useAppBootstrap 组合式函数
- 增强日期选择器组件,支持全类型与格式自动推导
- 修复资源泄漏:清理 RAF、定时器、事件与 mitt 监听
- 修复退出登录逻辑与异步导出流程
- 迁移 ::v-deep 至 Vue 3 标准 :deep 语法
- 移除调试日志并改进类型定义与注释文档
This commit is contained in:
zhangtao
2026-05-11 23:37:11 +08:00
parent 7e7a5c97cb
commit 9a231aa58c
120 changed files with 2282 additions and 1931 deletions
@@ -25,28 +25,12 @@
<script setup lang="ts">
import { useAuth } from "@/hooks/core/useAuth";
import type { ButtonMoreItem } from "./types";
defineOptions({ name: "FaButtonMore" });
const { hasAuth } = useAuth();
export interface ButtonMoreItem {
/** 按钮标识,可用于点击事件 */
key: string | number;
/** 按钮文本 */
label: string;
/** 是否禁用 */
disabled?: boolean;
/** 权限标识 */
auth?: string;
/** 图标组件 */
icon?: string;
/** 文本颜色 */
color?: string;
/** 图标颜色(优先级高于 color) */
iconColor?: string;
}
interface Props {
/** 下拉项列表 */
list: ButtonMoreItem[];
@@ -0,0 +1,18 @@
/** FaButtonMore 组件相关类型 */
export interface ButtonMoreItem {
/** 按钮标识,可用于点击事件 */
key: string | number;
/** 按钮文本 */
label: string;
/** 是否禁用 */
disabled?: boolean;
/** 权限标识 */
auth?: string;
/** 图标组件 */
icon?: string;
/** 文本颜色 */
color?: string;
/** 图标颜色(优先级高于 color) */
iconColor?: string;
}
@@ -147,10 +147,6 @@ const onTouchMove = (e: any) => {
}
};
// 全局事件监听器添加
document.addEventListener("touchstart", onTouchStart);
document.addEventListener("touchmove", onTouchMove, { passive: false });
// 获取数值形式的宽度
const getNumericWidth = (): number => {
if (typeof props.width === "string") {
@@ -180,7 +176,7 @@ onMounted(() => {
dragVerify.value?.style.setProperty("--pwidth", -Math.floor(numericWidth / 2) + "px");
});
// 重复添加事件监听器(确保事件绑定)
// 注册 touch 事件监听器,由 onBeforeUnmount 统一清理
document.addEventListener("touchstart", onTouchStart);
document.addEventListener("touchmove", onTouchMove, { passive: false });
});
@@ -97,14 +97,22 @@
</template>
<script setup lang="ts">
/**
* FaForm —— 动态表单组件。
*
* 渲染策略:items 配置项数组 → componentMap[type] 映射组件 → 动态 :is 渲染。
* 支持所有 Element Plus 表单组件类型、自定义 render、插槽插写。
*
* @see SearchFormItem 接口定义
*/
import { useWindowSize } from "@vueuse/core";
import { useI18n } from "vue-i18n";
import { toRaw, type Component } from "vue";
import DatePicker from "@/components/DatePicker/index.vue";
import {
ElCascader,
ElCheckbox,
ElCheckboxGroup,
ElDatePicker,
ElInput,
ElInputTag,
ElInputNumber,
@@ -131,10 +139,10 @@ const componentMap = {
checkbox: ElCheckbox, // 复选框
checkboxgroup: ElCheckboxGroup, // 复选框组
radiogroup: ElRadioGroup, // 单选框组
date: ElDatePicker, // 日期选择器
daterange: ElDatePicker, // 日期范围选择器
datetime: ElDatePicker, // 日期时间选择器
datetimerange: ElDatePicker, // 日期时间范围选择器
date: DatePicker, // 日期选择器
daterange: DatePicker, // 日期范围选择器
datetime: DatePicker, // 日期时间选择器
datetimerange: DatePicker, // 日期时间范围选择器
rate: ElRate, // 评分
slider: ElSlider, // 滑块
cascader: ElCascader, // 级联选择器
@@ -113,11 +113,11 @@ import { ArrowUpBold, ArrowDownBold, Refresh, Search } from "@element-plus/icons
import { useWindowSize } from "@vueuse/core";
import { useI18n } from "vue-i18n";
import { toRaw, type Component } from "vue";
import DatePicker from "@/components/DatePicker/index.vue";
import {
ElCascader,
ElCheckbox,
ElCheckboxGroup,
ElDatePicker,
ElInput,
ElInputTag,
ElInputNumber,
@@ -144,10 +144,10 @@ const componentMap = {
checkbox: ElCheckbox, // 复选框
checkboxgroup: ElCheckboxGroup, // 复选框组
radiogroup: ElRadioGroup, // 单选框组
date: ElDatePicker, // 日期选择器
daterange: ElDatePicker, // 日期范围选择器
datetime: ElDatePicker, // 日期时间选择器
datetimerange: ElDatePicker, // 日期时间范围选择器
date: DatePicker, // 日期选择器
daterange: DatePicker, // 日期范围选择器
datetime: DatePicker, // 日期时间选择器
datetimerange: DatePicker, // 日期时间范围选择器
rate: ElRate, // 评分
slider: ElSlider, // 滑块
cascader: ElCascader, // 级联选择器
@@ -156,7 +156,7 @@ if (uploadConfig?.isCustomUpload && uploadConfig.server && editorConfig.MENU_CON
},
});
const { url, alt = "", href = "" } = response.data.data ?? ({} as any);
const { url, alt = "", href = "" } = response.data.data ?? ({} as Record<string, string>);
if (!url) {
throw new Error("上传失败,请检查服务端配置");
@@ -177,7 +177,7 @@ const onCreateEditor = (editor: IDomEditor) => {
// 监听全屏事件
editor.on("fullScreen", () => {
console.log("编辑器进入全屏模式");
// 全屏状态由 wangEditor 内部管理
});
// 确保在编辑器创建后应用自定义图标