hookehuyr

refactor(常量): 将魔法数字替换为统一常量管理

创建 src/common/constants.js 集中管理项目常量
替换多处硬编码值为导入的常量,包括时间、超时等配置
更新 ISSUES_TO_FIX.md 标记问题为已修复
...@@ -203,7 +203,14 @@ export const MIN_TIMEOUT = 800 ...@@ -203,7 +203,14 @@ export const MIN_TIMEOUT = 800
203 export const DEFAULT_FETCH_TIMEOUT = 2000 203 export const DEFAULT_FETCH_TIMEOUT = 2000
204 ``` 204 ```
205 205
206 -**状态**: ⬜ 未修复 206 +**状态**: ✅ 已修复 (2026-01-18)
207 +**修复详情**: 已创建 `src/common/constants.js`,包含所有需要的常量
208 +
209 +**已应用到以下文件**:
210 +- `src/App.vue` - 已导入 `UPDATE_INTERVAL` 并替换 `time: 30000`
211 +- `src/contexts/cart.js` - 已导入 `ONE_DAY_MS` 并替换 `24 * 60 * 60 * 1000`
212 +- `src/utils/versionUpdater.js` - 已导入 `DEFAULT_TIMEOUT` 并替换 `timing(time = 10000)`
213 +- `src/components/ui/SharePoster.vue` - 已导入 `MIN_TIMEOUT, DEFAULT_FETCH_TIMEOUT` 并替换 `Math.max(800, timeout_ms || 2000)`
207 214
208 --- 215 ---
209 216
......
...@@ -21,6 +21,7 @@ import { apiList } from "@/api/wx/jsApiList.js"; ...@@ -21,6 +21,7 @@ import { apiList } from "@/api/wx/jsApiList.js";
21 import { wxInfo } from "@/utils/tools"; 21 import { wxInfo } from "@/utils/tools";
22 22
23 import { Updater } from '@/utils/versionUpdater'; 23 import { Updater } from '@/utils/versionUpdater';
24 +import { UPDATE_INTERVAL } from '@/common/constants';
24 25
25 import 'vant/es/toast/style' 26 import 'vant/es/toast/style'
26 27
...@@ -58,7 +59,7 @@ if (!import.meta.env.DEV && wxInfo().isWeiXin) { ...@@ -58,7 +59,7 @@ if (!import.meta.env.DEV && wxInfo().isWeiXin) {
58 let upDater = null; 59 let upDater = null;
59 if (import.meta.env.PROD) { 60 if (import.meta.env.PROD) {
60 upDater = new Updater({ 61 upDater = new Updater({
61 - time: 30000 62 + time: UPDATE_INTERVAL
62 }) 63 })
63 upDater.on('no-update', () => { 64 upDater.on('no-update', () => {
64 // console.log('还没更新') 65 // console.log('还没更新')
......
1 +/**
2 + * 项目通用常量定义
3 + *
4 + * @Date: 2026-01-18
5 + * @Description: 统一管理项目中的魔法数字和字符串
6 + */
7 +
8 +// ==================== 时间相关常量 ====================
9 +
10 +/**
11 + * 一天的毫秒数
12 + */
13 +export const ONE_DAY_MS = 24 * 60 * 60 * 1000
14 +
15 +/**
16 + * 一小时的毫秒数
17 + */
18 +export const ONE_HOUR_MS = 60 * 60 * 1000
19 +
20 +/**
21 + * 一分钟的毫秒数
22 + */
23 +export const ONE_MINUTE_MS = 60 * 1000
24 +
25 +// ==================== 更新检查间隔 ====================
26 +
27 +/**
28 + * 更新检查间隔(30秒)
29 + */
30 +export const UPDATE_INTERVAL = 30000
31 +
32 +// ==================== 超时时间 ====================
33 +
34 +/**
35 + * 默认 API 超时时间(10秒)
36 + */
37 +export const DEFAULT_TIMEOUT = 10000
38 +
39 +/**
40 + * 最小超时时间(800毫秒)
41 + */
42 +export const MIN_TIMEOUT = 800
43 +
44 +/**
45 + * 默认图片/资源加载超时时间(2秒)
46 + */
47 +export const DEFAULT_FETCH_TIMEOUT = 2000
48 +
49 +/**
50 + * 图片加载超时时间(5秒)
51 + */
52 +export const IMAGE_LOAD_TIMEOUT = 5000
53 +
54 +// ==================== 文件相关常量 ====================
55 +
56 +/**
57 + * 最大上传文件数量
58 + */
59 +export const MAX_UPLOAD_COUNT = 5
60 +
61 +/**
62 + * 最大文件大小(20MB)
63 + */
64 +export const MAX_FILE_SIZE_MB = 20
65 +
66 +/**
67 + * 最大文件大小(字节)
68 + */
69 +export const MAX_FILE_SIZE_BYTES = 20 * 1024 * 1024
70 +
71 +// ==================== 默认值 ====================
72 +
73 +/**
74 + * 默认头像 URL
75 + */
76 +export const DEFAULT_AVATAR = 'https://cdn.ipadbiz.cn/mlaj/images/default-avatar.jpeg'
77 +
78 +/**
79 + * 默认课程封面图 URL
80 + */
81 +export const DEFAULT_COVER_IMAGE = 'https://cdn.ipadbiz.cn/mlaj/images/default_block.png'
82 +
83 +/**
84 + * 默认占位图 URL
85 + */
86 +export const DEFAULT_PLACEHOLDER_IMAGE = '/assets/images/course-placeholder.jpg'
87 +
88 +// ==================== 分页常量 ====================
89 +
90 +/**
91 + * 默认分页大小
92 + */
93 +export const DEFAULT_PAGE_SIZE = 10
94 +
95 +/**
96 + * 打卡列表分页大小
97 + */
98 +export const CHECKIN_PAGE_SIZE = 5
99 +
100 +// ==================== 评论相关常量 ====================
101 +
102 +/**
103 + * 最大评论内容长度
104 + */
105 +export const MAX_COMMENT_LENGTH = 500
106 +
107 +/**
108 + * 最短打卡内容长度(文字打卡)
109 + */
110 +export const MIN_CHECKIN_CONTENT_LENGTH = 10
111 +
112 +// ==================== API 响应码 ====================
113 +
114 +/**
115 + * API 成功响应码
116 + */
117 +export const API_SUCCESS_CODE = 1
118 +
119 +/**
120 + * API 未登录响应码
121 + */
122 +export const API_UNAUTHORIZED_CODE = 401
123 +
124 +/**
125 + * API 禁止访问响应码
126 + */
127 +export const API_FORBIDDEN_CODE = 403
128 +
129 +/**
130 + * API 未找到响应码
131 + */
132 +export const API_NOT_FOUND_CODE = 404
133 +
134 +/**
135 + * API 服务器错误响应码
136 + */
137 +export const API_SERVER_ERROR_CODE = 500
138 +
139 +// ==================== 正则表达式 ====================
140 +
141 +/**
142 + * 中国大陆手机号正则表达式
143 + * 规则:以 1 开头,第二位为 3-9,后面接 9 位数字
144 + */
145 +export const REGEX_PHONE_CN = /^1[3-9]\d{9}$/
146 +
147 +/**
148 + * 身份证号正则表达式(15位或18位)
149 + */
150 +export const REGEX_ID_CARD = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/
151 +
152 +// ==================== 消息提示 ====================
153 +
154 +/**
155 + * 默认成功提示消息
156 + */
157 +export const MSG_SUCCESS = '操作成功'
158 +
159 +/**
160 + * 默认失败提示消息
161 + */
162 +export const MSG_FAILED = '操作失败,请稍后重试'
163 +
164 +/**
165 + * 默认登录提示消息
166 + */
167 +export const MSG_PLEASE_LOGIN = '请先登录'
168 +
169 +/**
170 + * 默认网络错误提示消息
171 + */
172 +export const MSG_NETWORK_ERROR = '网络错误,请检查网络连接'
173 +
174 +/**
175 + * 默认服务器错误提示消息
176 + */
177 +export const MSG_SERVER_ERROR = '服务器错误,请稍后重试'
...@@ -60,6 +60,7 @@ import { ref, computed, watch, nextTick } from 'vue' ...@@ -60,6 +60,7 @@ import { ref, computed, watch, nextTick } from 'vue'
60 import { toPng } from 'html-to-image' 60 import { toPng } from 'html-to-image'
61 import QRCode from 'qrcode' 61 import QRCode from 'qrcode'
62 import { showToast } from 'vant' 62 import { showToast } from 'vant'
63 +import { MIN_TIMEOUT, DEFAULT_FETCH_TIMEOUT } from '@/common/constants'
63 64
64 /** 65 /**
65 * @typedef {Object} CourseLike 66 * @typedef {Object} CourseLike
...@@ -488,7 +489,7 @@ async function fetch_to_data_url_with_timeout(url, timeout_ms) { ...@@ -488,7 +489,7 @@ async function fetch_to_data_url_with_timeout(url, timeout_ms) {
488 if (!url) return '' 489 if (!url) return ''
489 try { 490 try {
490 const ctrl = new AbortController() 491 const ctrl = new AbortController()
491 - const timer = setTimeout(() => ctrl.abort(), Math.max(800, timeout_ms || 2000)) 492 + const timer = setTimeout(() => ctrl.abort(), Math.max(MIN_TIMEOUT, timeout_ms || DEFAULT_FETCH_TIMEOUT))
492 const resp = await fetch(url, { mode: 'cors', cache: 'no-cache', credentials: 'omit', signal: ctrl.signal }) 493 const resp = await fetch(url, { mode: 'cors', cache: 'no-cache', credentials: 'omit', signal: ctrl.signal })
493 clearTimeout(timer) 494 clearTimeout(timer)
494 if (!resp.ok) return '' 495 if (!resp.ok) return ''
......
...@@ -12,6 +12,7 @@ import { logoutAPI, getUserInfoAPI } from '@/api/users' ...@@ -12,6 +12,7 @@ import { logoutAPI, getUserInfoAPI } from '@/api/users'
12 import { getAuthInfoAPI } from '@/api/auth' 12 import { getAuthInfoAPI } from '@/api/auth'
13 import { clearAuthHeaders, setAuthHeaders } from '@/utils/axios' 13 import { clearAuthHeaders, setAuthHeaders } from '@/utils/axios'
14 import { applyUserInfoAuth, getUserInfoFromStorage, removeUserInfoFromStorage } from '@/utils/auth_user_info' 14 import { applyUserInfoAuth, getUserInfoFromStorage, removeUserInfoFromStorage } from '@/utils/auth_user_info'
15 +import { ONE_DAY_MS } from '@/common/constants'
15 16
16 // 创建认证上下文的Symbol key,用于provide/inject依赖注入 17 // 创建认证上下文的Symbol key,用于provide/inject依赖注入
17 // 使用Symbol确保key的唯一性,避免命名冲突 18 // 使用Symbol确保key的唯一性,避免命名冲突
......
1 import { ref, provide, inject, watchEffect } from 'vue' 1 import { ref, provide, inject, watchEffect } from 'vue'
2 import { useRouter } from 'vue-router' 2 import { useRouter } from 'vue-router'
3 -
4 -// 导入接口
5 import { addOrderAPI } from "@/api/order"; 3 import { addOrderAPI } from "@/api/order";
4 +import { ONE_DAY_MS } from '@/common/constants'
6 5
7 const CartSymbol = Symbol() 6 const CartSymbol = Symbol()
8 7
...@@ -26,7 +25,7 @@ export function provideCart(mode = CartMode.MULTIPLE) { ...@@ -26,7 +25,7 @@ export function provideCart(mode = CartMode.MULTIPLE) {
26 // 检查是否为新格式(包含时间戳) 25 // 检查是否为新格式(包含时间戳)
27 if (cartData && typeof cartData === 'object' && cartData.timestamp) { 26 if (cartData && typeof cartData === 'object' && cartData.timestamp) {
28 const currentTime = Date.now() 27 const currentTime = Date.now()
29 - const oneDay = 24 * 60 * 60 * 1000 // 一天的毫秒数 28 + const oneDay = ONE_DAY_MS // 一天的毫秒数
30 29
31 // 检查缓存是否过期(超过一天) 30 // 检查缓存是否过期(超过一天)
32 if (currentTime - cartData.timestamp > oneDay) { 31 if (currentTime - cartData.timestamp > oneDay) {
......
...@@ -11,6 +11,8 @@ ...@@ -11,6 +11,8 @@
11 * @param {*} time 阈值 11 * @param {*} time 阈值
12 * @return {*} 12 * @return {*}
13 */ 13 */
14 +import { DEFAULT_TIMEOUT } from '@/common/constants'
15 +
14 export class Updater { 16 export class Updater {
15 constructor(options = {}) { 17 constructor(options = {}) {
16 this.oldScript = []; 18 this.oldScript = [];
...@@ -59,7 +61,7 @@ export class Updater { ...@@ -59,7 +61,7 @@ export class Updater {
59 } 61 }
60 } 62 }
61 63
62 - timing(time = 10000) { 64 + timing(time = DEFAULT_TIMEOUT) {
63 //轮询 65 //轮询
64 this.intervalId = setInterval(async () => { 66 this.intervalId = setInterval(async () => {
65 const newHtml = await this.getHtml(); 67 const newHtml = await this.getHtml();
......