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.
- package/README.md +121 -0
- package/bin/create-vue-template.js +565 -0
- package/package.json +28 -0
- package/templates/vue-project/.browserslistrc +3 -0
- package/templates/vue-project/.editorconfig +28 -0
- package/templates/vue-project/.env.development +2 -0
- package/templates/vue-project/.env.production +2 -0
- package/templates/vue-project/.eslintrc.cjs +76 -0
- package/templates/vue-project/.keep +0 -0
- package/templates/vue-project/.node-version +1 -0
- package/templates/vue-project/.prettierignore +13 -0
- package/templates/vue-project/.prettierrc +20 -0
- package/templates/vue-project/.prettierrc.txt +130 -0
- package/templates/vue-project/.stylelintrc.json +94 -0
- package/templates/vue-project/README.md +24 -0
- package/templates/vue-project/babel.config.js +5 -0
- package/templates/vue-project/index.html +34 -0
- package/templates/vue-project/package.json +75 -0
- package/templates/vue-project/public/favicon.ico +0 -0
- package/templates/vue-project/public/static/img/ai-default.jpg +0 -0
- package/templates/vue-project/public/static/img/image.png +0 -0
- package/templates/vue-project/public/static/img/ppt1.png +0 -0
- package/templates/vue-project/public/static/img/ppt2.png +0 -0
- package/templates/vue-project/public/static/img/ppt3.png +0 -0
- package/templates/vue-project/public/static/js/config.js +11 -0
- package/templates/vue-project/public/static/js/dataConfig.js +1143 -0
- package/templates/vue-project/src/App.vue +10 -0
- package/templates/vue-project/src/api/error-handler.ts +60 -0
- package/templates/vue-project/src/api/http.ts +254 -0
- package/templates/vue-project/src/api/services/aicebd.ts +47 -0
- package/templates/vue-project/src/api/services/base.ts +18 -0
- package/templates/vue-project/src/api/services/umse.ts +17 -0
- package/templates/vue-project/src/assets/font/Alibaba-PuHuiTi-Medium.otf +0 -0
- package/templates/vue-project/src/assets/font/Alibaba-PuHuiTi-Regular.otf +0 -0
- package/templates/vue-project/src/assets/font/DOUYINSANSBOLD.OTF +0 -0
- package/templates/vue-project/src/assets/font/Pangmen-Title.TTF +0 -0
- package/templates/vue-project/src/assets/font/font.css +25 -0
- package/templates/vue-project/src/assets/iconfont/iconfont.css +402 -0
- package/templates/vue-project/src/assets/iconfont/iconfont.js +66 -0
- package/templates/vue-project/src/assets/iconfont/iconfont.json +688 -0
- package/templates/vue-project/src/assets/iconfont/iconfont.ttf +0 -0
- package/templates/vue-project/src/assets/iconfont/iconfont.woff +0 -0
- package/templates/vue-project/src/assets/iconfont/iconfont.woff2 +0 -0
- package/templates/vue-project/src/assets/images/Click-tap.png +0 -0
- package/templates/vue-project/src/assets/images/Effects.png +0 -0
- package/templates/vue-project/src/assets/images/bg.png +0 -0
- package/templates/vue-project/src/assets/images/erCode.png +0 -0
- package/templates/vue-project/src/assets/images/header-bg.png +0 -0
- package/templates/vue-project/src/assets/images/logo.png +0 -0
- package/templates/vue-project/src/assets/scss/common.scss +530 -0
- package/templates/vue-project/src/assets/styles/element-overrides.css +53 -0
- package/templates/vue-project/src/assets/styles/reset.css +186 -0
- package/templates/vue-project/src/assets/styles/theme.css +100 -0
- package/templates/vue-project/src/components/BarChart.vue +238 -0
- package/templates/vue-project/src/components/echarts/EChart.vue +140 -0
- package/templates/vue-project/src/composables/useTheme.ts +84 -0
- package/templates/vue-project/src/main.ts +111 -0
- package/templates/vue-project/src/mocks/base.ts +37 -0
- package/templates/vue-project/src/mocks/umse.ts +31 -0
- package/templates/vue-project/src/router/index.ts +32 -0
- package/templates/vue-project/src/shims-vue.d.ts +19 -0
- package/templates/vue-project/src/store/index.ts +18 -0
- package/templates/vue-project/src/store/modules/user.ts +85 -0
- package/templates/vue-project/src/types/DTO/aicebd.d.ts +60 -0
- package/templates/vue-project/src/types/DTO/base.d.ts +26 -0
- package/templates/vue-project/src/types/DTO/global.d.ts +48 -0
- package/templates/vue-project/src/types/VO/teachingLog.d.ts +15 -0
- package/templates/vue-project/src/types/auto-imports.d.ts +73 -0
- package/templates/vue-project/src/types/components.d.ts +17 -0
- package/templates/vue-project/src/types/element-plus.d.ts +15 -0
- package/templates/vue-project/src/types/js-cookie.d.ts +1 -0
- package/templates/vue-project/src/types/unocss.d.ts +2 -0
- package/templates/vue-project/src/types/vite-plugins.d.ts +3 -0
- package/templates/vue-project/src/types/vue-router.d.ts +1 -0
- package/templates/vue-project/src/types/window-config.d.ts +12 -0
- package/templates/vue-project/src/utils/com-methods.ts +307 -0
- package/templates/vue-project/src/utils/echarts.ts +111 -0
- package/templates/vue-project/src/utils/number.ts +99 -0
- package/templates/vue-project/src/utils/rem.ts +82 -0
- package/templates/vue-project/src/utils/responsive.ts +103 -0
- package/templates/vue-project/src/utils/time.ts +314 -0
- package/templates/vue-project/src/utils/tracker.ts +527 -0
- package/templates/vue-project/src/utils/validators.ts +85 -0
- package/templates/vue-project/src/utils/window.ts +132 -0
- package/templates/vue-project/src/views/home/Home.vue +60 -0
- package/templates/vue-project/src/views/home/composables/useUserAuth.ts +13 -0
- package/templates/vue-project/src/views/teachingLog/TeachingLog.vue +40 -0
- package/templates/vue-project/src/views/teachingLog/__tests__/TeachingEffect.test.ts +96 -0
- package/templates/vue-project/src/views/teachingLog/__tests__/TeachingHighlight.test.ts +66 -0
- package/templates/vue-project/src/views/teachingLog/__tests__/TeachingLog.test.ts +34 -0
- package/templates/vue-project/src/views/teachingLog/components/TeachingEffect.vue +458 -0
- package/templates/vue-project/src/views/teachingLog/components/TeachingHighlight.vue +181 -0
- package/templates/vue-project/src/views/teachingLog/composables/useEffectTooltip.ts +88 -0
- package/templates/vue-project/src/views/teachingLog/composables/useEffectTrendChart.ts +160 -0
- package/templates/vue-project/tests/setup.ts +27 -0
- package/templates/vue-project/tsconfig.json +24 -0
- package/templates/vue-project/tsconfig.node.json +41 -0
- package/templates/vue-project/uno.config.ts +84 -0
- package/templates/vue-project/vite.config.ts +216 -0
- package/templates/vue-project/vue3_ai_prompt.md +652 -0
- package/templates/vue-project/vue3_ai_prompt_basic.md +722 -0
- package/templates/vue-project/vue3_ai_prompt_full.md +1021 -0
- package/templates/vue-project/vue3_ai_prompt_unocss.md +768 -0
- 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
|
+
});
|