bohui-vue 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/README.md +121 -0
  2. package/bin/create-vue-template.js +565 -0
  3. package/package.json +28 -0
  4. package/templates/vue-project/.browserslistrc +3 -0
  5. package/templates/vue-project/.editorconfig +28 -0
  6. package/templates/vue-project/.env.development +2 -0
  7. package/templates/vue-project/.env.production +2 -0
  8. package/templates/vue-project/.eslintrc.cjs +76 -0
  9. package/templates/vue-project/.keep +0 -0
  10. package/templates/vue-project/.node-version +1 -0
  11. package/templates/vue-project/.prettierignore +13 -0
  12. package/templates/vue-project/.prettierrc +20 -0
  13. package/templates/vue-project/.prettierrc.txt +130 -0
  14. package/templates/vue-project/.stylelintrc.json +94 -0
  15. package/templates/vue-project/README.md +24 -0
  16. package/templates/vue-project/babel.config.js +5 -0
  17. package/templates/vue-project/index.html +34 -0
  18. package/templates/vue-project/package.json +75 -0
  19. package/templates/vue-project/public/favicon.ico +0 -0
  20. package/templates/vue-project/public/static/img/ai-default.jpg +0 -0
  21. package/templates/vue-project/public/static/img/image.png +0 -0
  22. package/templates/vue-project/public/static/img/ppt1.png +0 -0
  23. package/templates/vue-project/public/static/img/ppt2.png +0 -0
  24. package/templates/vue-project/public/static/img/ppt3.png +0 -0
  25. package/templates/vue-project/public/static/js/config.js +11 -0
  26. package/templates/vue-project/public/static/js/dataConfig.js +1143 -0
  27. package/templates/vue-project/src/App.vue +10 -0
  28. package/templates/vue-project/src/api/error-handler.ts +60 -0
  29. package/templates/vue-project/src/api/http.ts +254 -0
  30. package/templates/vue-project/src/api/services/aicebd.ts +47 -0
  31. package/templates/vue-project/src/api/services/base.ts +18 -0
  32. package/templates/vue-project/src/api/services/umse.ts +17 -0
  33. package/templates/vue-project/src/assets/font/Alibaba-PuHuiTi-Medium.otf +0 -0
  34. package/templates/vue-project/src/assets/font/Alibaba-PuHuiTi-Regular.otf +0 -0
  35. package/templates/vue-project/src/assets/font/DOUYINSANSBOLD.OTF +0 -0
  36. package/templates/vue-project/src/assets/font/Pangmen-Title.TTF +0 -0
  37. package/templates/vue-project/src/assets/font/font.css +25 -0
  38. package/templates/vue-project/src/assets/iconfont/iconfont.css +402 -0
  39. package/templates/vue-project/src/assets/iconfont/iconfont.js +66 -0
  40. package/templates/vue-project/src/assets/iconfont/iconfont.json +688 -0
  41. package/templates/vue-project/src/assets/iconfont/iconfont.ttf +0 -0
  42. package/templates/vue-project/src/assets/iconfont/iconfont.woff +0 -0
  43. package/templates/vue-project/src/assets/iconfont/iconfont.woff2 +0 -0
  44. package/templates/vue-project/src/assets/images/Click-tap.png +0 -0
  45. package/templates/vue-project/src/assets/images/Effects.png +0 -0
  46. package/templates/vue-project/src/assets/images/bg.png +0 -0
  47. package/templates/vue-project/src/assets/images/erCode.png +0 -0
  48. package/templates/vue-project/src/assets/images/header-bg.png +0 -0
  49. package/templates/vue-project/src/assets/images/logo.png +0 -0
  50. package/templates/vue-project/src/assets/scss/common.scss +530 -0
  51. package/templates/vue-project/src/assets/styles/element-overrides.css +53 -0
  52. package/templates/vue-project/src/assets/styles/reset.css +186 -0
  53. package/templates/vue-project/src/assets/styles/theme.css +100 -0
  54. package/templates/vue-project/src/components/BarChart.vue +238 -0
  55. package/templates/vue-project/src/components/echarts/EChart.vue +140 -0
  56. package/templates/vue-project/src/composables/useTheme.ts +84 -0
  57. package/templates/vue-project/src/main.ts +111 -0
  58. package/templates/vue-project/src/mocks/base.ts +37 -0
  59. package/templates/vue-project/src/mocks/umse.ts +31 -0
  60. package/templates/vue-project/src/router/index.ts +32 -0
  61. package/templates/vue-project/src/shims-vue.d.ts +19 -0
  62. package/templates/vue-project/src/store/index.ts +18 -0
  63. package/templates/vue-project/src/store/modules/user.ts +85 -0
  64. package/templates/vue-project/src/types/DTO/aicebd.d.ts +60 -0
  65. package/templates/vue-project/src/types/DTO/base.d.ts +26 -0
  66. package/templates/vue-project/src/types/DTO/global.d.ts +48 -0
  67. package/templates/vue-project/src/types/VO/teachingLog.d.ts +15 -0
  68. package/templates/vue-project/src/types/auto-imports.d.ts +73 -0
  69. package/templates/vue-project/src/types/components.d.ts +17 -0
  70. package/templates/vue-project/src/types/element-plus.d.ts +15 -0
  71. package/templates/vue-project/src/types/js-cookie.d.ts +1 -0
  72. package/templates/vue-project/src/types/unocss.d.ts +2 -0
  73. package/templates/vue-project/src/types/vite-plugins.d.ts +3 -0
  74. package/templates/vue-project/src/types/vue-router.d.ts +1 -0
  75. package/templates/vue-project/src/types/window-config.d.ts +12 -0
  76. package/templates/vue-project/src/utils/com-methods.ts +307 -0
  77. package/templates/vue-project/src/utils/echarts.ts +111 -0
  78. package/templates/vue-project/src/utils/number.ts +99 -0
  79. package/templates/vue-project/src/utils/rem.ts +82 -0
  80. package/templates/vue-project/src/utils/responsive.ts +103 -0
  81. package/templates/vue-project/src/utils/time.ts +314 -0
  82. package/templates/vue-project/src/utils/tracker.ts +527 -0
  83. package/templates/vue-project/src/utils/validators.ts +85 -0
  84. package/templates/vue-project/src/utils/window.ts +132 -0
  85. package/templates/vue-project/src/views/home/Home.vue +60 -0
  86. package/templates/vue-project/src/views/home/composables/useUserAuth.ts +13 -0
  87. package/templates/vue-project/src/views/teachingLog/TeachingLog.vue +40 -0
  88. package/templates/vue-project/src/views/teachingLog/__tests__/TeachingEffect.test.ts +96 -0
  89. package/templates/vue-project/src/views/teachingLog/__tests__/TeachingHighlight.test.ts +66 -0
  90. package/templates/vue-project/src/views/teachingLog/__tests__/TeachingLog.test.ts +34 -0
  91. package/templates/vue-project/src/views/teachingLog/components/TeachingEffect.vue +458 -0
  92. package/templates/vue-project/src/views/teachingLog/components/TeachingHighlight.vue +181 -0
  93. package/templates/vue-project/src/views/teachingLog/composables/useEffectTooltip.ts +88 -0
  94. package/templates/vue-project/src/views/teachingLog/composables/useEffectTrendChart.ts +160 -0
  95. package/templates/vue-project/tests/setup.ts +27 -0
  96. package/templates/vue-project/tsconfig.json +24 -0
  97. package/templates/vue-project/tsconfig.node.json +41 -0
  98. package/templates/vue-project/uno.config.ts +84 -0
  99. package/templates/vue-project/vite.config.ts +216 -0
  100. package/templates/vue-project/vue3_ai_prompt.md +652 -0
  101. package/templates/vue-project/vue3_ai_prompt_basic.md +722 -0
  102. package/templates/vue-project/vue3_ai_prompt_full.md +1021 -0
  103. package/templates/vue-project/vue3_ai_prompt_unocss.md +768 -0
  104. package/templates/vue-project//345/267/245/347/250/213/345/214/226/346/250/241/346/235/277/344/273/213/347/273/215.md +463 -0
@@ -0,0 +1,88 @@
1
+ import { ref } from "vue";
2
+ import type { Ref } from "vue";
3
+
4
+ /**
5
+ * 学生专注度下降点图片 tooltip hooks
6
+ * - 管理 tooltip 显示/隐藏与文本内容
7
+ * - 根据图片在「教学效果」卡片中的位置计算 tooltip 坐标
8
+ * - 保证 tooltip 最小宽度为 30rem,并随卡片一起滚动
9
+ */
10
+ export function useEffectTooltip(): {
11
+ showTooltipContent: Ref<boolean>;
12
+ tooltipDescription: Ref<string>;
13
+ tooltipX: Ref<number>;
14
+ tooltipY: Ref<number>;
15
+ showTooltip: (desc: string, e: MouseEvent) => void;
16
+ moveTooltip: (e: MouseEvent) => void;
17
+ hideTooltip: () => void;
18
+ } {
19
+ // tooltip 最小宽度(30rem)与右侧预留间距(16px)
20
+ // 通过读取根元素 font-size,保证「30」的语义与 30rem 一致
21
+ const minWidthPx = (() => {
22
+ if (typeof window === "undefined") return 280 * 16;
23
+ const fontSize = window.getComputedStyle(document.documentElement).fontSize || "16";
24
+ const root = parseFloat(fontSize);
25
+ const base = Number.isFinite(root) && root > 0 ? root : 16;
26
+ return 30 * base;
27
+ })();
28
+ const marginPx = 16;
29
+ const showTooltipContent = ref(false);
30
+ const tooltipDescription = ref("");
31
+ const tooltipX = ref(0);
32
+ const tooltipY = ref(0);
33
+ /**
34
+ * 计算 tooltip 在「教学效果」卡片内的相对位置
35
+ * - 优先靠图片左侧对齐
36
+ * - 若右侧空间不足,则向左收缩,保证最小宽度
37
+ */
38
+ function updatePosition(target: HTMLElement) {
39
+ const container = target.closest(".teaching-effect") as HTMLElement | null;
40
+ const containerRect = container?.getBoundingClientRect();
41
+ const targetRect = target.getBoundingClientRect();
42
+ if (!containerRect) {
43
+ tooltipX.value = targetRect.left;
44
+ tooltipY.value = targetRect.bottom + 8;
45
+ return;
46
+ }
47
+ const containerWidth = containerRect.width;
48
+ const maxLeft = Math.max(0, containerWidth - minWidthPx - marginPx);
49
+ const baseLeft = targetRect.left - containerRect.left;
50
+ tooltipX.value = Math.min(baseLeft, maxLeft);
51
+ tooltipY.value = targetRect.bottom - containerRect.top + 8;
52
+ }
53
+ /**
54
+ * 鼠标移入图片时展示 tooltip,并根据当前图片位置计算坐标
55
+ */
56
+ function showTooltip(desc: string, e: MouseEvent) {
57
+ if (!desc) return;
58
+ const target = e.currentTarget as HTMLElement | null;
59
+ if (!target) return;
60
+ showTooltipContent.value = true;
61
+ tooltipDescription.value = desc;
62
+ updatePosition(target);
63
+ }
64
+ /**
65
+ * 鼠标在图片上移动时实时更新 tooltip 位置
66
+ */
67
+ function moveTooltip(e: MouseEvent) {
68
+ if (!showTooltipContent.value) return;
69
+ const target = e.currentTarget as HTMLElement | null;
70
+ if (!target) return;
71
+ updatePosition(target);
72
+ }
73
+ /**
74
+ * 鼠标移出图片时隐藏 tooltip
75
+ */
76
+ function hideTooltip() {
77
+ showTooltipContent.value = false;
78
+ }
79
+ return {
80
+ showTooltipContent,
81
+ tooltipDescription,
82
+ tooltipX,
83
+ tooltipY,
84
+ showTooltip,
85
+ moveTooltip,
86
+ hideTooltip,
87
+ };
88
+ }
@@ -0,0 +1,160 @@
1
+ import * as echarts from "echarts";
2
+ import { onMounted, onUnmounted } from "vue";
3
+ import { formatSeconds } from "@/utils/time";
4
+ import { getResponsivePx } from "@/utils/responsive";
5
+
6
+ /**
7
+ * 教学效果趋势图 hooks
8
+ * - 接收 [time, focus] 数组并渲染折线面积图
9
+ * - 对坐标轴、tooltip、渐变填充等配置进行统一封装
10
+ * - 返回 initTrendChart,组件中调用后可获得 dispose 方法用于销毁图表
11
+ */
12
+ export function useEffectTrendChart(): {
13
+ initTrendChart: (el: HTMLElement, trend: Array<[number, number]>) => { dispose: () => void };
14
+ } {
15
+ /**
16
+ * 线性渐变颜色类型定义
17
+ * 用于 areaStyle 中描述从上到下的渐变填充
18
+ */
19
+ type LinearGradientColor = {
20
+ type: "linear";
21
+ x: number;
22
+ y: number;
23
+ x2: number;
24
+ y2: number;
25
+ colorStops: Array<{ offset: number; color: string }>;
26
+ global?: boolean;
27
+ };
28
+ /**
29
+ * 初始化趋势图
30
+ * - el:ECharts 容器元素
31
+ * - trend:[[time, focus], ...],time 为秒,focus 为百分比 0-100
32
+ */
33
+ function initTrendChart(el: HTMLElement, trend: Array<[number, number]>) {
34
+ const chart = echarts.init(el);
35
+ const maxTime = trend.length ? Math.max(...trend.map((d) => d[0])) : 0;
36
+ const data = trend;
37
+ const maxFocus = 100;
38
+ const fontSize = Math.min(getResponsivePx(28, "vw"), 12);
39
+ // X 轴刻度格式化为 HH:mm:ss 或 mm:ss
40
+ const formatAxisTime = (v: number) => {
41
+ const s = Math.round(v);
42
+ const h = Math.floor(s / 3600);
43
+ const m = Math.floor((s % 3600) / 60);
44
+ const sec = s % 60;
45
+ const pad = (n: number) => (n < 10 ? `0${n}` : `${n}`);
46
+ return h > 0 ? `${h}:${pad(m)}:${pad(sec)}` : `${m}:${pad(sec)}`;
47
+ };
48
+ // 动态计算最小专注度,保证曲线贴合数据下沿
49
+ const minFocus = trend.length ? Math.floor(Math.min(...trend.map((d) => d[1]))) : 0;
50
+ const calcSplitNumber = () => {
51
+ const width = el.clientWidth || 0;
52
+ const labelWidth = 60;
53
+ const maxLabels = Math.max(2, Math.floor(width / labelWidth));
54
+ return Math.max(1, maxLabels - 1);
55
+ };
56
+ // 根据容器宽度计算初始刻度数量,避免刻度标签重叠
57
+ const initialSplitNumber = calcSplitNumber();
58
+ chart.setOption({
59
+ grid: { left: 0, right: 0, top: 40, bottom: 0 },
60
+ xAxis: {
61
+ type: "value",
62
+ min: 0,
63
+ max: maxTime,
64
+ splitNumber: initialSplitNumber,
65
+ name: "",
66
+ nameLocation: "center",
67
+ nameGap: 30,
68
+ nameTextStyle: { color: "#9aa4af" },
69
+ axisLine: { show: false },
70
+ axisTick: { show: false },
71
+ axisLabel: {
72
+ color: "#9aa4af",
73
+ fontSize: fontSize,
74
+ formatter: (v: number) => formatAxisTime(v),
75
+ interval: "auto",
76
+ hideOverlap: true,
77
+ },
78
+ splitLine: { show: false },
79
+ },
80
+ yAxis: {
81
+ type: "value",
82
+ min: minFocus,
83
+ max: maxFocus,
84
+ name: "百分比(%)",
85
+ nameLocation: "end",
86
+ nameGap: 20,
87
+ nameTextStyle: { color: "#9aa4af" },
88
+ axisLine: { show: false },
89
+ axisTick: { show: false },
90
+ axisLabel: {
91
+ color: "#9aa4af",
92
+ fontSize: fontSize,
93
+ formatter: (v: number) => {
94
+ if (maxFocus - v <= 2 && v !== maxFocus) return "";
95
+ return `${v}%`;
96
+ },
97
+ },
98
+ splitLine: { show: false },
99
+ },
100
+ tooltip: {
101
+ trigger: "axis",
102
+ backgroundColor: "rgba(0,0,0,0.86)",
103
+ borderWidth: 0,
104
+ textStyle: { color: "#fff", fontSize: fontSize },
105
+ formatter: (params: unknown[]) => {
106
+ const p = Array.isArray(params)
107
+ ? (params[0] as { value?: [number, number] })
108
+ : null;
109
+ if (!p?.value) return "";
110
+ const t = p.value[0];
111
+ const f = p.value[1];
112
+ return `时间:${formatSeconds(t)}<br/>学生专注度:${f.toFixed(2)}%`;
113
+ },
114
+ axisPointer: {
115
+ type: "cross",
116
+ label: { show: false },
117
+ crossStyle: { color: "rgba(255,255,255,0.25)", type: "dashed" },
118
+ },
119
+ },
120
+ series: [
121
+ {
122
+ type: "line",
123
+ smooth: true,
124
+ symbol: "circle",
125
+ symbolSize: 15,
126
+ showSymbol: false,
127
+ showAllSymbol: false,
128
+ data,
129
+ lineStyle: { color: "#3f7af2", width: 0 },
130
+ emphasis: {
131
+ itemStyle: { color: "rgb(0,27,254)", borderColor: "#fff", borderWidth: 4 },
132
+ },
133
+ areaStyle: {
134
+ color: {
135
+ type: "linear",
136
+ x: 0,
137
+ y: 0,
138
+ x2: 0,
139
+ y2: 1,
140
+ colorStops: [
141
+ { offset: 0, color: "rgba(0,27,254,1)" },
142
+ { offset: 1, color: "rgba(217,217,217,0)" },
143
+ ],
144
+ } as LinearGradientColor,
145
+ },
146
+ },
147
+ ],
148
+ });
149
+ // 监听窗口尺寸变化,保持趋势图自适应
150
+ const onResize = () => {
151
+ const sn = calcSplitNumber();
152
+ chart.setOption({ xAxis: { splitNumber: sn } });
153
+ chart.resize();
154
+ };
155
+ onMounted(() => window.addEventListener("resize", onResize));
156
+ onUnmounted(() => window.removeEventListener("resize", onResize));
157
+ return { dispose: () => chart.dispose() };
158
+ }
159
+ return { initTrendChart };
160
+ }
@@ -0,0 +1,27 @@
1
+ import { config } from "@vue/test-utils";
2
+ import ElementPlus from "element-plus";
3
+
4
+ // 全局注册 ElementUI(如果你们用的是 ElementPlus)
5
+ config.global.plugins = [...(config.global.plugins || []), ElementPlus];
6
+
7
+ // 模拟浏览器 API(如果需要)
8
+ if (!window.matchMedia) {
9
+ const matchMedia = () => {
10
+ return {
11
+ matches: false,
12
+ media: "",
13
+ onchange: null,
14
+ addListener: () => {},
15
+ removeListener: () => {},
16
+ addEventListener: () => {},
17
+ removeEventListener: () => {},
18
+ dispatchEvent: () => false,
19
+ };
20
+ };
21
+ Object.defineProperty(window, "matchMedia", { writable: true, value: matchMedia });
22
+ Object.defineProperty(globalThis, "matchMedia", { writable: true, value: matchMedia });
23
+ }
24
+
25
+ // 未来集成测试时,这里可以初始化 MSW
26
+ // import { setupServer } from 'msw/node'
27
+ // export const server = setupServer()
@@ -0,0 +1,24 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "useDefineForClassFields": true,
5
+ "module": "ESNext",
6
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
7
+ "skipLibCheck": true,
8
+ "moduleResolution": "bundler",
9
+ "resolveJsonModule": true,
10
+ "isolatedModules": true,
11
+ "noEmit": true,
12
+ "jsx": "preserve",
13
+ "strict": true,
14
+ "allowJs": false,
15
+ "types": ["node"],
16
+ "baseUrl": ".",
17
+ "paths": {
18
+ "@/*": ["src/*"]
19
+ },
20
+ "typeRoots": ["./node_modules/@types", "./src/types"]
21
+ },
22
+ "include": ["src", "vite.config.ts"],
23
+ "exclude": ["node_modules"]
24
+ }
@@ -0,0 +1,41 @@
1
+ {
2
+ "compilerOptions": {
3
+ "composite": true,
4
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
5
+ "target": "ES2023",
6
+ "lib": ["ES2023"],
7
+ "module": "ESNext",
8
+ "types": ["node"],
9
+ "skipLibCheck": true,
10
+
11
+ /* 核心修复:解决 ts(1259) 报错 */
12
+ "esModuleInterop": true, // 允许 ESM 和 CommonJS 模块之间的互操作
13
+ "allowSyntheticDefaultImports": true, // 允许从没有默认导出的模块中进行默认导入
14
+
15
+ /* Bundler 模式与现代路径解析 */
16
+ "moduleResolution": "bundler",
17
+ "allowImportingTsExtensions": true,
18
+ "verbatimModuleSyntax": true,
19
+ "moduleDetection": "force",
20
+ "noEmit": true,
21
+
22
+ /* 路径解析增强:解决 vite.config.ts 中的别名识别(可选建议) */
23
+ "baseUrl": ".",
24
+ "paths": {
25
+ "@/*": ["src/*"]
26
+ },
27
+
28
+ /* Linting 严格检查 */
29
+ "strict": true,
30
+ "noUnusedLocals": true,
31
+ "noUnusedParameters": true,
32
+ "erasableSyntaxOnly": true,
33
+ "noFallthroughCasesInSwitch": true,
34
+ "noUncheckedSideEffectImports": true
35
+ },
36
+ "include": [
37
+ "vite.config.ts",
38
+ "uno.config.ts",
39
+ "src/types/**/*.d.ts" // 建议包含类型声明文件目录
40
+ ]
41
+ }
@@ -0,0 +1,84 @@
1
+ import {
2
+ defineConfig,
3
+ presetAttributify,
4
+ presetIcons,
5
+ presetUno,
6
+ transformerDirectives,
7
+ transformerVariantGroup,
8
+ } from "unocss";
9
+
10
+ export default defineConfig({
11
+ presets: [
12
+ presetUno(),
13
+ presetAttributify(),
14
+ presetIcons({
15
+ scale: 1.2,
16
+ warn: true,
17
+ }),
18
+ ],
19
+ transformers: [transformerDirectives(), transformerVariantGroup()],
20
+ theme: {
21
+ colors: {
22
+ brand: "var(--color-brand)", // 品牌色
23
+ success: "var(--color-success)", // 成功色
24
+ warning: "var(--color-warning)", // 警告色
25
+ danger: "var(--color-danger)", // 危险色
26
+ info: "var(--color-info)", // 信息色
27
+
28
+ "brand-50": "var(--color-brand-50)", // 品牌色50
29
+ "brand-100": "var(--color-brand-100)", // 品牌色100
30
+ "brand-200": "var(--color-brand-200)", // 品牌色200
31
+ "brand-300": "var(--color-brand-300)", // 品牌色300
32
+ "brand-400": "var(--color-brand-400)", // 品牌色400
33
+ "brand-500": "var(--color-brand-500)", // 品牌色500
34
+ "brand-600": "var(--color-brand-600)", // 品牌色600
35
+ "brand-700": "var(--color-brand-700)", // 品牌色700
36
+
37
+ // 语义背景底色
38
+ "b-base": "var(--bg-base)", // 基础底色
39
+ "b-muted": "var(--bg-muted)", // 次级底色
40
+ "b-soft": "var(--bg-soft)", // 柔和底色
41
+ "b-overlay": "var(--bg-overlay)", // 遮罩
42
+ "b-brand": "var(--bg-brand)", // 品牌色底色
43
+ "b-success": "var(--bg-success)", // 成功色底色
44
+ "b-warning": "var(--bg-warning)", // 警告色底色
45
+ "b-danger": "var(--bg-danger)", // 危险色底色
46
+ "b-info": "var(--bg-info-soft)",
47
+ "b-brand-soft": "var(--bg-brand-soft)", // 品牌色浅色背景
48
+ "b-success-soft": "var(--bg-success-soft)", // 成功色浅色背景
49
+ "b-warning-soft": "var(--bg-warning-soft)", // 警告色浅色背景
50
+ "b-danger-soft": "var(--bg-danger-soft)", // 危险色浅色背景
51
+ "b-info-soft": "var(--bg-info-soft)", // 信息色浅色背景
52
+
53
+ // 边框/分割线
54
+ border: "var(--border-color)", // 基础边框
55
+ "border-light": "var(--border-color-light)", // 浅色边框
56
+ "border-dark": "var(--border-color-dark)", // 深色边框
57
+ divider: "var(--divider-color)", // 分割线
58
+
59
+ // 文字颜色(以 t-* 命名,配合 text-t-primary 等类使用)
60
+ "t-brand": "var(--text-brand)", // 品牌色
61
+ "t-primary": "var(--text-primary)", // 主文字
62
+ "t-regular": "var(--text-regular)", // 常规文字
63
+ "t-secondary": "var(--text-secondary)", // 次要文字
64
+ "t-placeholder": "var(--text-placeholder)", // 占位/禁用
65
+ "t-inverse": "var(--text-inverse)", // 反色文字(深底色上)
66
+ "t-link": "var(--text-link)", // 链接文字
67
+ "t-danger": "var(--text-danger)", // 危险文字
68
+ "t-warning": "var(--text-warning)", // 警告文字
69
+ "t-success": "var(--text-success)", // 成功文字
70
+ },
71
+ // 让数值刻度与 rem 一一对应:1 -> 1rem, 2 -> 2rem ...
72
+ // 影响:m-/p-/w-/h-/gap-/inset-/space- 等使用 spacing 的工具类,以及 text- 的数值刻度
73
+ spacing: (() => {
74
+ const map: Record<string, string> = { "0": "0rem" };
75
+ for (let i = 1; i <= 200; i++) map[String(i)] = `${i}rem`;
76
+ return map;
77
+ })(),
78
+ fontSize: (() => {
79
+ const map: Record<string, string> = { "0": "0rem" };
80
+ for (let i = 1; i <= 200; i++) map[String(i)] = `${i}rem`;
81
+ return map;
82
+ })(),
83
+ },
84
+ });
@@ -0,0 +1,216 @@
1
+ import { loadEnv } from "vite";
2
+ import { defineConfig } from "vitest/config";
3
+ import vue from "@vitejs/plugin-vue";
4
+ import { viteMockServe } from "vite-plugin-mock";
5
+ import { resolve } from "path";
6
+ import viteCompression from "vite-plugin-compression";
7
+ import AutoImport from "unplugin-auto-import/vite";
8
+ import Components from "unplugin-vue-components/vite";
9
+ import { ElementPlusResolver } from "unplugin-vue-components/resolvers";
10
+ import UnoCSS from "unocss/vite";
11
+
12
+ const devServerUrl1 = "https://bhlecrms.bhlec.com:10023/";
13
+ const devServerUrl2 = "https://bhlecrms.bhlec.com:10023/";
14
+
15
+ // https://vitejs.dev/config/
16
+ export default defineConfig(({ command, mode }) => {
17
+ const isDev = command === "serve";
18
+ const env = loadEnv(mode, process.cwd());
19
+ const useMock = env.VITE_USE_MOCK === "true";
20
+ return {
21
+ // 基础路径
22
+ base: "/teaching",
23
+
24
+ // 插件配置
25
+ plugins: [
26
+ vue(),
27
+ UnoCSS(),
28
+ AutoImport({
29
+ imports: [
30
+ "vue",
31
+ {
32
+ pinia: ["defineStore", "storeToRefs"],
33
+ },
34
+ ],
35
+ resolvers: [ElementPlusResolver()],
36
+ dts: "src/types/auto-imports.d.ts",
37
+ }),
38
+ Components({
39
+ resolvers: [
40
+ ElementPlusResolver({
41
+ importStyle: "css",
42
+ }),
43
+ ],
44
+ dts: "src/types/components.d.ts",
45
+ }),
46
+ // gzip 压缩插件(仅生产环境)
47
+ viteCompression({
48
+ verbose: true, // 是否在控制台输出压缩结果
49
+ disable: false, // 是否禁用
50
+ threshold: 10240, // 文件大小超过 10KB 才压缩
51
+ algorithm: "gzip", // 压缩算法
52
+ ext: ".gz", // 生成的压缩文件扩展名
53
+ deleteOriginFile: false, // 是否删除源文件
54
+ }),
55
+ // 本地 mock 能力(仅开发环境启用,生产禁用)
56
+ viteMockServe({
57
+ mockPath: "src/mocks",
58
+ enable: isDev && useMock,
59
+ logger: true,
60
+ watchFiles: true,
61
+ }),
62
+ ],
63
+
64
+ // 路径解析别名
65
+ resolve: {
66
+ alias: {
67
+ "@": resolve(__dirname, "src"),
68
+ },
69
+ // 省略的扩展名
70
+ extensions: [".ts", ".tsx", ".js", ".jsx", ".vue", ".json"],
71
+ },
72
+
73
+ // CSS 预处理器配置
74
+ css: {
75
+ preprocessorOptions: {
76
+ scss: {
77
+ // 自动导入全局变量文件和 sass:color 模块
78
+ additionalData: `@use "sass:color";`,
79
+ },
80
+ },
81
+ },
82
+
83
+ // 开发服务器配置
84
+ server: {
85
+ port: 3008,
86
+ open: true, // 启动时自动打开浏览器
87
+ cors: true, // 允许跨域
88
+ host: "0.0.0.0", // 设置0.0.0.0可以同时监听localhost和ip,同时防止和webpack端口冲突的问题
89
+ // 核心配置:确保热更新正常工作
90
+ hmr: {
91
+ overlay: true, // 错误时显示遮罩层
92
+ },
93
+ // 代理配置
94
+ proxy: {
95
+ "/api/base": {
96
+ target: devServerUrl1,
97
+ changeOrigin: true,
98
+ ws: true, // 支持 WebSocket
99
+ },
100
+ "/api/aice": {
101
+ target: devServerUrl2,
102
+ changeOrigin: true,
103
+ ws: true,
104
+ },
105
+ "/api/cas": {
106
+ target: devServerUrl2,
107
+ changeOrigin: true,
108
+ ws: true,
109
+ },
110
+ "/api/tsms": {
111
+ target: devServerUrl2,
112
+ changeOrigin: true,
113
+ ws: true,
114
+ },
115
+ "/api/rms": {
116
+ target: devServerUrl2,
117
+ changeOrigin: true,
118
+ ws: true,
119
+ },
120
+ "/api/aidas": {
121
+ target: devServerUrl2,
122
+ changeOrigin: true,
123
+ ws: true,
124
+ },
125
+ "/analysis": {
126
+ target: "http://172.17.31.80:8011",
127
+ changeOrigin: true,
128
+ ws: true,
129
+ },
130
+ "/stream": {
131
+ target: devServerUrl2,
132
+ ws: true,
133
+ changeOrigin: true,
134
+ },
135
+ "/ppt": {
136
+ target: devServerUrl2,
137
+ ws: true,
138
+ changeOrigin: true,
139
+ },
140
+ "/api": {
141
+ target: devServerUrl1,
142
+ ws: true,
143
+ changeOrigin: true,
144
+ },
145
+ },
146
+ },
147
+
148
+ // 构建配置
149
+ build: {
150
+ // 输出目录
151
+ outDir: "dist",
152
+ // 静态资源目录
153
+ assetsDir: "static",
154
+ // 生产环境不生成 source map
155
+ sourcemap: false,
156
+ // chunk 大小警告的限制(单位 KB)
157
+ chunkSizeWarningLimit: 1500,
158
+ // rollup 打包配置
159
+ rollupOptions: {
160
+ output: {
161
+ // 代码分割策略
162
+ manualChunks: {
163
+ // 将 Vue 相关库单独打包
164
+ vue: ["vue", "vue-router"],
165
+ // 将 Element Plus 单独打包
166
+ "element-plus": ["element-plus"],
167
+ },
168
+ // 静态资源命名
169
+ chunkFileNames: "static/js/[name]-[hash].js",
170
+ entryFileNames: "static/js/[name]-[hash].js",
171
+ assetFileNames: "static/[ext]/[name]-[hash].[ext]",
172
+ },
173
+ },
174
+ // 压缩配置
175
+ minify: "terser",
176
+ terserOptions: {
177
+ compress: {
178
+ // 生产环境移除 console
179
+ drop_console: true,
180
+ drop_debugger: true,
181
+ pure_funcs: ["console.log"],
182
+ },
183
+ },
184
+ },
185
+
186
+ // 全局常量定义
187
+ define: {
188
+ __VUE_OPTIONS_API__: JSON.stringify(true),
189
+ __VUE_PROD_DEVTOOLS__: JSON.stringify(false),
190
+ __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: JSON.stringify(false),
191
+ },
192
+
193
+ // 优化依赖预构建
194
+ optimizeDeps: {
195
+ include: ["vue", "vue-router", "element-plus"],
196
+ },
197
+
198
+ test: {
199
+ // 单元测试配置
200
+ globals: true,
201
+ environment: "jsdom",
202
+ setupFiles: "./tests/setup.ts", // 测试前的全局配置
203
+
204
+ // 覆盖率配置
205
+ coverage: {
206
+ provider: "v8",
207
+ reporter: ["text", "html", "json"],
208
+ include: ["src/**/*.{js,ts,vue}"],
209
+ exclude: ["src/main.ts", "src/**/*.d.ts", "src/**/*.spec.ts", "src/**/*.test.ts"],
210
+ },
211
+
212
+ // 为未来集成测试预留的配置
213
+ testTimeout: 10000,
214
+ },
215
+ };
216
+ });