hookehuyr

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

创建 src/common/constants.js 集中管理项目常量
替换多处硬编码值为导入的常量,包括时间、超时等配置
更新 ISSUES_TO_FIX.md 标记问题为已修复
......@@ -203,7 +203,14 @@ export const MIN_TIMEOUT = 800
export const DEFAULT_FETCH_TIMEOUT = 2000
```
**状态**: ⬜ 未修复
**状态**: ✅ 已修复 (2026-01-18)
**修复详情**: 已创建 `src/common/constants.js`,包含所有需要的常量
**已应用到以下文件**:
- `src/App.vue` - 已导入 `UPDATE_INTERVAL` 并替换 `time: 30000`
- `src/contexts/cart.js` - 已导入 `ONE_DAY_MS` 并替换 `24 * 60 * 60 * 1000`
- `src/utils/versionUpdater.js` - 已导入 `DEFAULT_TIMEOUT` 并替换 `timing(time = 10000)`
- `src/components/ui/SharePoster.vue` - 已导入 `MIN_TIMEOUT, DEFAULT_FETCH_TIMEOUT` 并替换 `Math.max(800, timeout_ms || 2000)`
---
......
......@@ -21,6 +21,7 @@ import { apiList } from "@/api/wx/jsApiList.js";
import { wxInfo } from "@/utils/tools";
import { Updater } from '@/utils/versionUpdater';
import { UPDATE_INTERVAL } from '@/common/constants';
import 'vant/es/toast/style'
......@@ -58,7 +59,7 @@ if (!import.meta.env.DEV && wxInfo().isWeiXin) {
let upDater = null;
if (import.meta.env.PROD) {
upDater = new Updater({
time: 30000
time: UPDATE_INTERVAL
})
upDater.on('no-update', () => {
// console.log('还没更新')
......
/**
* 项目通用常量定义
*
* @Date: 2026-01-18
* @Description: 统一管理项目中的魔法数字和字符串
*/
// ==================== 时间相关常量 ====================
/**
* 一天的毫秒数
*/
export const ONE_DAY_MS = 24 * 60 * 60 * 1000
/**
* 一小时的毫秒数
*/
export const ONE_HOUR_MS = 60 * 60 * 1000
/**
* 一分钟的毫秒数
*/
export const ONE_MINUTE_MS = 60 * 1000
// ==================== 更新检查间隔 ====================
/**
* 更新检查间隔(30秒)
*/
export const UPDATE_INTERVAL = 30000
// ==================== 超时时间 ====================
/**
* 默认 API 超时时间(10秒)
*/
export const DEFAULT_TIMEOUT = 10000
/**
* 最小超时时间(800毫秒)
*/
export const MIN_TIMEOUT = 800
/**
* 默认图片/资源加载超时时间(2秒)
*/
export const DEFAULT_FETCH_TIMEOUT = 2000
/**
* 图片加载超时时间(5秒)
*/
export const IMAGE_LOAD_TIMEOUT = 5000
// ==================== 文件相关常量 ====================
/**
* 最大上传文件数量
*/
export const MAX_UPLOAD_COUNT = 5
/**
* 最大文件大小(20MB)
*/
export const MAX_FILE_SIZE_MB = 20
/**
* 最大文件大小(字节)
*/
export const MAX_FILE_SIZE_BYTES = 20 * 1024 * 1024
// ==================== 默认值 ====================
/**
* 默认头像 URL
*/
export const DEFAULT_AVATAR = 'https://cdn.ipadbiz.cn/mlaj/images/default-avatar.jpeg'
/**
* 默认课程封面图 URL
*/
export const DEFAULT_COVER_IMAGE = 'https://cdn.ipadbiz.cn/mlaj/images/default_block.png'
/**
* 默认占位图 URL
*/
export const DEFAULT_PLACEHOLDER_IMAGE = '/assets/images/course-placeholder.jpg'
// ==================== 分页常量 ====================
/**
* 默认分页大小
*/
export const DEFAULT_PAGE_SIZE = 10
/**
* 打卡列表分页大小
*/
export const CHECKIN_PAGE_SIZE = 5
// ==================== 评论相关常量 ====================
/**
* 最大评论内容长度
*/
export const MAX_COMMENT_LENGTH = 500
/**
* 最短打卡内容长度(文字打卡)
*/
export const MIN_CHECKIN_CONTENT_LENGTH = 10
// ==================== API 响应码 ====================
/**
* API 成功响应码
*/
export const API_SUCCESS_CODE = 1
/**
* API 未登录响应码
*/
export const API_UNAUTHORIZED_CODE = 401
/**
* API 禁止访问响应码
*/
export const API_FORBIDDEN_CODE = 403
/**
* API 未找到响应码
*/
export const API_NOT_FOUND_CODE = 404
/**
* API 服务器错误响应码
*/
export const API_SERVER_ERROR_CODE = 500
// ==================== 正则表达式 ====================
/**
* 中国大陆手机号正则表达式
* 规则:以 1 开头,第二位为 3-9,后面接 9 位数字
*/
export const REGEX_PHONE_CN = /^1[3-9]\d{9}$/
/**
* 身份证号正则表达式(15位或18位)
*/
export const REGEX_ID_CARD = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/
// ==================== 消息提示 ====================
/**
* 默认成功提示消息
*/
export const MSG_SUCCESS = '操作成功'
/**
* 默认失败提示消息
*/
export const MSG_FAILED = '操作失败,请稍后重试'
/**
* 默认登录提示消息
*/
export const MSG_PLEASE_LOGIN = '请先登录'
/**
* 默认网络错误提示消息
*/
export const MSG_NETWORK_ERROR = '网络错误,请检查网络连接'
/**
* 默认服务器错误提示消息
*/
export const MSG_SERVER_ERROR = '服务器错误,请稍后重试'
......@@ -60,6 +60,7 @@ import { ref, computed, watch, nextTick } from 'vue'
import { toPng } from 'html-to-image'
import QRCode from 'qrcode'
import { showToast } from 'vant'
import { MIN_TIMEOUT, DEFAULT_FETCH_TIMEOUT } from '@/common/constants'
/**
* @typedef {Object} CourseLike
......@@ -488,7 +489,7 @@ async function fetch_to_data_url_with_timeout(url, timeout_ms) {
if (!url) return ''
try {
const ctrl = new AbortController()
const timer = setTimeout(() => ctrl.abort(), Math.max(800, timeout_ms || 2000))
const timer = setTimeout(() => ctrl.abort(), Math.max(MIN_TIMEOUT, timeout_ms || DEFAULT_FETCH_TIMEOUT))
const resp = await fetch(url, { mode: 'cors', cache: 'no-cache', credentials: 'omit', signal: ctrl.signal })
clearTimeout(timer)
if (!resp.ok) return ''
......
......@@ -12,6 +12,7 @@ import { logoutAPI, getUserInfoAPI } from '@/api/users'
import { getAuthInfoAPI } from '@/api/auth'
import { clearAuthHeaders, setAuthHeaders } from '@/utils/axios'
import { applyUserInfoAuth, getUserInfoFromStorage, removeUserInfoFromStorage } from '@/utils/auth_user_info'
import { ONE_DAY_MS } from '@/common/constants'
// 创建认证上下文的Symbol key,用于provide/inject依赖注入
// 使用Symbol确保key的唯一性,避免命名冲突
......
import { ref, provide, inject, watchEffect } from 'vue'
import { useRouter } from 'vue-router'
// 导入接口
import { addOrderAPI } from "@/api/order";
import { ONE_DAY_MS } from '@/common/constants'
const CartSymbol = Symbol()
......@@ -26,7 +25,7 @@ export function provideCart(mode = CartMode.MULTIPLE) {
// 检查是否为新格式(包含时间戳)
if (cartData && typeof cartData === 'object' && cartData.timestamp) {
const currentTime = Date.now()
const oneDay = 24 * 60 * 60 * 1000 // 一天的毫秒数
const oneDay = ONE_DAY_MS // 一天的毫秒数
// 检查缓存是否过期(超过一天)
if (currentTime - cartData.timestamp > oneDay) {
......
......@@ -11,6 +11,8 @@
* @param {*} time 阈值
* @return {*}
*/
import { DEFAULT_TIMEOUT } from '@/common/constants'
export class Updater {
constructor(options = {}) {
this.oldScript = [];
......@@ -59,7 +61,7 @@ export class Updater {
}
}
timing(time = 10000) {
timing(time = DEFAULT_TIMEOUT) {
//轮询
this.intervalId = setInterval(async () => {
const newHtml = await this.getHtml();
......