hookehuyr

docs: 为工具函数、API和组件添加JSDoc注释以提升代码可读性

- 为 utils、api、stores、composables 等模块的函数和变量添加详细的 @description 注释
- 统一接口返回类型为 Promise<{code,data,msg}> 格式说明
- 为组件方法添加参数和返回值类型标注
- 更新文件头部的 @Description 字段使其更具体
- 增强代码自文档化能力,便于团队维护和IDE智能提示
...@@ -15,33 +15,38 @@ const Api = { ...@@ -15,33 +15,38 @@ const Api = {
15 15
16 /** 16 /**
17 * @description: 发送验证码 17 * @description: 发送验证码
18 - * @param {*} phone 手机号码 18 + * @param {Object} params 请求参数
19 - * @returns 19 + * @param {string} params.phone 手机号
20 + * @returns {Promise<{code:number,data:any,msg:string}>} 标准返回
20 */ 21 */
21 export const smsAPI = (params) => fn(fetch.post(Api.SMS, params)); 22 export const smsAPI = (params) => fn(fetch.post(Api.SMS, params));
22 23
23 /** 24 /**
24 * @description: 获取七牛token 25 * @description: 获取七牛token
25 - * @param {*} filename 文件名 26 + * @param {Object} params 请求参数
26 - * @param {*} file 图片base64 27 + * @param {string} params.filename 文件名
27 - * @returns 28 + * @param {string} params.file 图片 base64
29 + * @returns {Promise<{code:number,data:any,msg:string}>} 标准返回(data 为上传 token 等信息)
28 */ 30 */
29 export const qiniuTokenAPI = (params) => fn(fetch.stringifyPost(Api.TOKEN, params)); 31 export const qiniuTokenAPI = (params) => fn(fetch.stringifyPost(Api.TOKEN, params));
30 32
31 /** 33 /**
32 * @description: 上传七牛 34 * @description: 上传七牛
33 - * @param {*} 35 + * @param {string} url 七牛上传地址
34 - * @returns 36 + * @param {any} data 上传数据
37 + * @param {Object} config axios 配置
38 + * @returns {Promise<any|false>} 成功返回七牛响应数据,失败返回 false
35 */ 39 */
36 export const qiniuUploadAPI = (url, data, config) => uploadFn(fetch.basePost(url, data, config)); 40 export const qiniuUploadAPI = (url, data, config) => uploadFn(fetch.basePost(url, data, config));
37 41
38 /** 42 /**
39 * @description: 保存图片 43 * @description: 保存图片
40 - * @param {*} format 44 + * @param {Object} params 请求参数
41 - * @param {*} hash 45 + * @param {string} params.format 文件格式
42 - * @param {*} height 46 + * @param {string} params.hash 文件 hash
43 - * @param {*} width 47 + * @param {string|number} params.height 图片高
44 - * @param {*} filekey 48 + * @param {string|number} params.width 图片宽
45 - * @returns 49 + * @param {string} params.filekey 文件 key
50 + * @returns {Promise<{code:number,data:any,msg:string}>} 标准返回
46 */ 51 */
47 export const saveFileAPI = (params) => fn(fetch.stringifyPost(Api.SAVE_FILE, params)); 52 export const saveFileAPI = (params) => fn(fetch.stringifyPost(Api.SAVE_FILE, params));
......
1 /* 1 /*
2 * @Date: 2022-05-18 22:56:08 2 * @Date: 2022-05-18 22:56:08
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2026-01-24 12:13:14 4 + * @LastEditTime: 2026-01-24 12:52:06
5 * @FilePath: /xyxBooking-weapp/src/api/fn.js 5 * @FilePath: /xyxBooking-weapp/src/api/fn.js
6 - * @Description: 文件描述 6 + * @Description: 统一后端返回格式(强制 { code, data, msg })
7 */ 7 */
8 import axios from '@/utils/request'; 8 import axios from '@/utils/request';
9 import Taro from '@tarojs/taro' 9 import Taro from '@tarojs/taro'
10 import qs from 'qs' 10 import qs from 'qs'
11 11
12 /** 12 /**
13 - * 网络请求功能函数 13 + * @description 统一后端返回格式(强制 { code, data, msg })
14 - * @param {*} api 请求axios接口 14 + * - code === 1 表示成功
15 - * @returns 请求成功后,获取数据 15 + * - 失败时统一 toast 提示(可通过 res.data.show=false 禁用提示)
16 + * @param {Promise<any>} api axios 请求 Promise
17 + * @returns {Promise<{code:number,data:any,msg:string,show?:boolean}>} 标准化后的返回对象
16 */ 18 */
17 export const fn = (api) => { 19 export const fn = (api) => {
18 return api 20 return api
19 .then(res => { 21 .then(res => {
20 - // 适配 H5 逻辑,code === 1 为成功 22 + // 约定:后端 code === 1 为成功
21 const res_data = res && res.data ? res.data : null 23 const res_data = res && res.data ? res.data : null
22 if (res_data && String(res_data.code) === '1') { 24 if (res_data && String(res_data.code) === '1') {
23 return res_data 25 return res_data
24 } 26 }
25 - // tslint:disable-next-line: no-console 27 + // 失败兜底:优先返回后端响应,同时做 toast 提示
26 - console.warn('API Error:', res) 28 + console.warn('接口请求失败:', res)
27 if (res_data && res_data.show === false) return res_data 29 if (res_data && res_data.show === false) return res_data
28 Taro.showToast({ 30 Taro.showToast({
29 title: (res_data && res_data.msg) ? res_data.msg : '请求失败', 31 title: (res_data && res_data.msg) ? res_data.msg : '请求失败',
...@@ -33,8 +35,7 @@ export const fn = (api) => { ...@@ -33,8 +35,7 @@ export const fn = (api) => {
33 return res_data || { code: 0, data: null, msg: '请求失败' } 35 return res_data || { code: 0, data: null, msg: '请求失败' }
34 }) 36 })
35 .catch(err => { 37 .catch(err => {
36 - // tslint:disable-next-line: no-console 38 + console.error('接口请求异常:', err);
37 - console.error(err);
38 return { code: 0, data: null, msg: (err && (err.msg || err.message || err.errMsg)) ? (err.msg || err.message || err.errMsg) : '网络异常' } 39 return { code: 0, data: null, msg: (err && (err.msg || err.message || err.errMsg)) ? (err.msg || err.message || err.errMsg) : '网络异常' }
39 }) 40 })
40 .finally(() => { // 最终执行 41 .finally(() => { // 最终执行
...@@ -42,9 +43,9 @@ export const fn = (api) => { ...@@ -42,9 +43,9 @@ export const fn = (api) => {
42 } 43 }
43 44
44 /** 45 /**
45 - * 七牛返回格式 46 + * @description 七牛上传返回格式适配
46 - * @param {*} api 47 + * @param {Promise<any>} api axios 请求 Promise
47 - * @returns 48 + * @returns {Promise<any|false>} 成功返回七牛响应数据,失败返回 false
48 */ 49 */
49 export const uploadFn = (api) => { 50 export const uploadFn = (api) => {
50 return api 51 return api
...@@ -52,8 +53,7 @@ export const uploadFn = (api) => { ...@@ -52,8 +53,7 @@ export const uploadFn = (api) => {
52 if (res.statusText === 'OK') { 53 if (res.statusText === 'OK') {
53 return res.data || true; 54 return res.data || true;
54 } else { 55 } else {
55 - // tslint:disable-next-line: no-console 56 + console.warn('七牛上传失败:', res);
56 - console.warn(res);
57 if (!res.data.show) return false; 57 if (!res.data.show) return false;
58 Taro.showToast({ 58 Taro.showToast({
59 title: res.data.msg, 59 title: res.data.msg,
...@@ -64,22 +64,42 @@ export const uploadFn = (api) => { ...@@ -64,22 +64,42 @@ export const uploadFn = (api) => {
64 } 64 }
65 }) 65 })
66 .catch(err => { 66 .catch(err => {
67 - // tslint:disable-next-line: no-console 67 + console.error('七牛上传异常:', err);
68 - console.error(err);
69 return false; 68 return false;
70 }) 69 })
71 } 70 }
72 71
73 /** 72 /**
74 - * 统一 GET/POST 不同传参形式 73 + * @description 统一 GET/POST 传参形式
74 + * - get/post:直接透传 axios
75 + * - stringifyPost:表单提交(x-www-form-urlencoded)
76 + * - basePost:保留自定义 config 的扩展位
75 */ 77 */
76 export const fetch = { 78 export const fetch = {
79 + /**
80 + * @description GET 请求
81 + * @param {string} api 接口地址
82 + * @param {Object} params 查询参数
83 + * @returns {Promise<any>} axios Promise
84 + */
77 get: function (api, params) { 85 get: function (api, params) {
78 return axios.get(api, params) 86 return axios.get(api, params)
79 }, 87 },
88 + /**
89 + * @description POST 请求(JSON)
90 + * @param {string} api 接口地址
91 + * @param {Object} params 请求体
92 + * @returns {Promise<any>} axios Promise
93 + */
80 post: function (api, params) { 94 post: function (api, params) {
81 return axios.post(api, params) 95 return axios.post(api, params)
82 }, 96 },
97 + /**
98 + * @description POST 请求(表单序列化)
99 + * @param {string} api 接口地址
100 + * @param {Object} params 请求体
101 + * @returns {Promise<any>} axios Promise
102 + */
83 stringifyPost: function (api, params) { 103 stringifyPost: function (api, params) {
84 return axios.post(api, qs.stringify(params), { 104 return axios.post(api, qs.stringify(params), {
85 headers: { 105 headers: {
...@@ -87,6 +107,13 @@ export const fetch = { ...@@ -87,6 +107,13 @@ export const fetch = {
87 } 107 }
88 }) 108 })
89 }, 109 },
110 + /**
111 + * @description POST 请求(自定义 config)
112 + * @param {string} url 接口地址
113 + * @param {any} data 请求体
114 + * @param {Object} config axios 配置
115 + * @returns {Promise<any>} axios Promise
116 + */
90 basePost: function (url, data, config) { 117 basePost: function (url, data, config) {
91 return axios.post(url, data, config) 118 return axios.post(url, data, config)
92 } 119 }
......
...@@ -7,6 +7,10 @@ ...@@ -7,6 +7,10 @@
7 */ 7 */
8 import { fn, fetch } from '@/api/fn'; 8 import { fn, fetch } from '@/api/fn';
9 9
10 +/**
11 + * @description 预约业务 API 聚合
12 + * - 所有接口统一走 fn(fetch.xxx) 保证返回 { code, data, msg }
13 + */
10 const Api = { 14 const Api = {
11 CAN_RESERVE_DATE_LIST: '/srv/?a=api&t=can_reserve_date_list', 15 CAN_RESERVE_DATE_LIST: '/srv/?a=api&t=can_reserve_date_list',
12 CAN_RESERVE_TIME_LIST: '/srv/?a=api&t=can_reserve_time_list', 16 CAN_RESERVE_TIME_LIST: '/srv/?a=api&t=can_reserve_time_list',
...@@ -171,14 +175,16 @@ export const billPersonAPI = (params) => fn(fetch.get(Api.BILL_PREPARE, params)) ...@@ -171,14 +175,16 @@ export const billPersonAPI = (params) => fn(fetch.get(Api.BILL_PREPARE, params))
171 175
172 /** 176 /**
173 * @description: 身份证查询预约码 177 * @description: 身份证查询预约码
174 - * @param {String} 178 + * @param {Object} params 请求参数
175 - * @returns {String} 179 + * @param {string} params.id_number 身份证号
180 + * @returns {Promise<{code:number,data:any,msg:string}>} 标准返回
176 */ 181 */
177 export const queryQrCodeAPI = (params) => fn(fetch.get(Api.QUERY_QR_CODE, params)); 182 export const queryQrCodeAPI = (params) => fn(fetch.get(Api.QUERY_QR_CODE, params));
178 183
179 /** 184 /**
180 * @description: 查询订单号 185 * @description: 查询订单号
181 - * @param {String} 186 + * @param {Object} params 请求参数
182 - * @returns {String} 187 + * @param {string} params.pay_id 支付凭证/订单号
188 + * @returns {Promise<{code:number,data:any,msg:string}>} 标准返回
183 */ 189 */
184 export const icbcOrderQryAPI = (params) => fn(fetch.get(Api.ICBC_ORDER_QRY, params)); 190 export const icbcOrderQryAPI = (params) => fn(fetch.get(Api.ICBC_ORDER_QRY, params));
......
...@@ -15,16 +15,21 @@ const Api = { ...@@ -15,16 +15,21 @@ const Api = {
15 15
16 /** 16 /**
17 * @description: 义工登录 17 * @description: 义工登录
18 + * @param {Object} params 请求参数
19 + * @returns {Promise<{code:number,data:any,msg:string}>} 标准返回
18 */ 20 */
19 export const volunteerLoginAPI = (params) => fn(fetch.post(Api.REDEEM_LOGIN, params)); 21 export const volunteerLoginAPI = (params) => fn(fetch.post(Api.REDEEM_LOGIN, params));
20 22
21 /** 23 /**
22 * @description: 检查核销权限 24 * @description: 检查核销权限
23 - * @returns {Object} { data.can_redeem: Boolean, msg: String} 25 + * @param {Object} params 请求参数
26 + * @returns {Promise<{code:number,data:{can_redeem:boolean},msg:string}>} 标准返回
24 */ 27 */
25 export const checkRedeemPermissionAPI = (params) => fn(fetch.get(Api.REDEEM_CHECK_AUTH, params)); 28 export const checkRedeemPermissionAPI = (params) => fn(fetch.get(Api.REDEEM_CHECK_AUTH, params));
26 29
27 /** 30 /**
28 * @description: 核销 31 * @description: 核销
32 + * @param {Object} params 请求参数
33 + * @returns {Promise<{code:number,data:any,msg:string}>} 标准返回
29 */ 34 */
30 export const verifyTicketAPI = (params) => fn(fetch.post(Api.REDEEM_REDEEM, params)); 35 export const verifyTicketAPI = (params) => fn(fetch.post(Api.REDEEM_REDEEM, params));
......
...@@ -13,8 +13,9 @@ const Api = { ...@@ -13,8 +13,9 @@ const Api = {
13 } 13 }
14 14
15 /** 15 /**
16 - * @description 获取微信CONFIG配置文件 16 + * @description 获取微信分享配置(wx.config 所需参数)
17 - * @param {*} url 17 + * @param {Object} params 请求参数
18 - * @returns {*} cfg 18 + * @param {string} params.url 当前页面 URL(用于签名)
19 + * @returns {Promise<{code:number,data:any,msg:string}>} 标准返回
19 */ 20 */
20 export const wxJsAPI = (params) => fn(fetch.get(Api.WX_JSAPI, params)); 21 export const wxJsAPI = (params) => fn(fetch.get(Api.WX_JSAPI, params));
......
...@@ -5,6 +5,11 @@ ...@@ -5,6 +5,11 @@
5 * @FilePath: /tswj/src/api/wx/jsApiList.js 5 * @FilePath: /tswj/src/api/wx/jsApiList.js
6 * @Description: 文件描述 6 * @Description: 文件描述
7 */ 7 */
8 +/**
9 + * @description 微信 JSSDK 需要注入的 API 列表
10 + * - 该列表用于 wx.config 的 jsApiList 字段
11 + * @type {Array<string>}
12 + */
8 export const apiList = [ 13 export const apiList = [
9 "updateAppMessageShareData", 14 "updateAppMessageShareData",
10 "updateTimelineShareData", 15 "updateTimelineShareData",
......
...@@ -14,7 +14,8 @@ const Api = { ...@@ -14,7 +14,8 @@ const Api = {
14 14
15 /** 15 /**
16 * @description: 微信支付接口 16 * @description: 微信支付接口
17 - * @param {*} pay_id 预约单支付凭证 17 + * @param {Object} params 请求参数
18 - * @returns {*} 微信支付参数 18 + * @param {string} params.pay_id 预约单支付凭证
19 + * @returns {Promise<{code:number,data:any,msg:string}>} 标准返回(data 为微信支付参数)
19 */ 20 */
20 export const wxPayAPI = (params) => fn(fetch.post(Api.WX_PAY, params)); 21 export const wxPayAPI = (params) => fn(fetch.post(Api.WX_PAY, params));
......
...@@ -38,9 +38,10 @@ const App = createApp({ ...@@ -38,9 +38,10 @@ const App = createApp({
38 } 38 }
39 39
40 /** 40 /**
41 - * 预加载离线预约记录数据(列表+详情) 41 + * @description 预加载离线预约记录数据(列表+详情)
42 * - 仅在有授权且网络可用时调用 42 * - 仅在有授权且网络可用时调用
43 * - 成功后将数据存储到本地缓存(key: OFFLINE_BOOKING_DATA) 43 * - 成功后将数据存储到本地缓存(key: OFFLINE_BOOKING_DATA)
44 + * @returns {Promise<void>} 无返回值
44 */ 45 */
45 const preloadBookingData = async () => { 46 const preloadBookingData = async () => {
46 try { 47 try {
...@@ -51,8 +52,9 @@ const App = createApp({ ...@@ -51,8 +52,9 @@ const App = createApp({
51 } 52 }
52 53
53 /** 54 /**
54 - * 判断是否应该跳过网络异常提示弹窗 55 + * @description 判断是否应该跳过网络异常提示弹窗
55 * - 仅在当前页面为离线预约列表/详情/核销码页时返回 true 56 * - 仅在当前页面为离线预约列表/详情/核销码页时返回 true
57 + * @returns {boolean} true=跳过提示,false=不跳过
56 */ 58 */
57 59
58 const should_skip_network_prompt = () => { 60 const should_skip_network_prompt = () => {
...@@ -67,10 +69,12 @@ const App = createApp({ ...@@ -67,10 +69,12 @@ const App = createApp({
67 } 69 }
68 70
69 /** 71 /**
70 - * 处理不良网络情况 72 + * @description 处理不良网络情况
71 * - 仅在当前页面未跳过提示弹窗时调用 73 * - 仅在当前页面未跳过提示弹窗时调用
72 * - 若有离线预约缓存,则展示弹窗询问是否使用缓存数据 74 * - 若有离线预约缓存,则展示弹窗询问是否使用缓存数据
73 - * - 否则展示简单提示弹窗 75 + * - 否则展示简单提示 toast
76 + * @param {string} network_type 网络类型(wifi/4g/5g/3g/none/unknown)
77 + * @returns {Promise<boolean>} true=需要中断后续启动流程,false=继续
74 */ 78 */
75 79
76 const handle_bad_network = async (network_type) => { 80 const handle_bad_network = async (network_type) => {
...@@ -129,10 +133,10 @@ const App = createApp({ ...@@ -129,10 +133,10 @@ const App = createApp({
129 }) 133 })
130 134
131 /** 135 /**
132 - * 处理在启动时出现的不良网络情况 136 + * @description 处理启动时的不良网络情况(只在启动阶段检查一次)
133 - * - 当网络连接不良且有离线预约记录缓存时,提示用户是否使用缓存进入离线模式 137 + * - 网络不可用且有离线缓存:询问是否进入离线模式
134 - * - 当网络连接不良且无缓存时,提示用户网络连接不畅 138 + * - 网络不可用且无缓存:toast 提示网络不佳
135 - * @returns {Promise<boolean>} 如果用户选择进入离线模式,则返回 true;否则返回 false 139 + * @returns {Promise<boolean>} true=进入离线模式并中断启动,false=继续启动
136 */ 140 */
137 const handle_bad_network_on_launch = async () => { 141 const handle_bad_network_on_launch = async () => {
138 /** 142 /**
...@@ -148,9 +152,9 @@ const App = createApp({ ...@@ -148,9 +152,9 @@ const App = createApp({
148 } 152 }
149 153
150 /** 154 /**
151 - * 尝试在网络可用时预加载离线预约记录数据 155 + * @description 尝试在网络可用时预加载离线预约记录数据
152 * - 仅在有授权时调用 156 * - 仅在有授权时调用
153 - * @returns {Promise<void>} 157 + * @returns {void} 无返回值
154 */ 158 */
155 const try_preload_when_online = () => { 159 const try_preload_when_online = () => {
156 if (!hasAuth()) return 160 if (!hasAuth()) return
...@@ -164,9 +168,10 @@ const App = createApp({ ...@@ -164,9 +168,10 @@ const App = createApp({
164 } 168 }
165 169
166 /** 170 /**
167 - * @description: 授权成功后的共用启动逻辑 171 + * @description 授权成功后的共用启动逻辑
168 * - 尝试在网络可用时预加载离线预约数据 172 * - 尝试在网络可用时预加载离线预约数据
169 * - 启动离线预约缓存轮询(会自行处理网络可用性与引用计数) 173 * - 启动离线预约缓存轮询(会自行处理网络可用性与引用计数)
174 + * @returns {void} 无返回值
170 */ 175 */
171 176
172 const bootstrap_after_auth = () => { 177 const bootstrap_after_auth = () => {
......
1 import Taro from '@tarojs/taro' 1 import Taro from '@tarojs/taro'
2 2
3 +/**
4 + * @description 生成随机字符串(递归补齐长度)
5 + * @param {number} length 目标长度
6 + * @returns {string} 随机字符串
7 + */
3 export function randomString(length) { 8 export function randomString(length) {
4 let str = Math.random().toString(36).substr(2) 9 let str = Math.random().toString(36).substr(2)
5 if (str.length >= length) { 10 if (str.length >= length) {
...@@ -9,10 +14,21 @@ export function randomString(length) { ...@@ -9,10 +14,21 @@ export function randomString(length) {
9 return str 14 return str
10 } 15 }
11 16
17 +/**
18 + * @description 生成随机 id(常用于 canvasId)
19 + * @param {string} prefix 前缀
20 + * @param {number} length 随机段长度
21 + * @returns {string} 随机 id
22 + */
12 export function getRandomId(prefix = 'canvas', length = 10) { 23 export function getRandomId(prefix = 'canvas', length = 10) {
13 return prefix + randomString(length) 24 return prefix + randomString(length)
14 } 25 }
15 26
27 +/**
28 + * @description 将 http 链接转换为 https(小程序部分场景要求 https)
29 + * @param {string} rawUrl 原始 url
30 + * @returns {string} 处理后的 url
31 + */
16 export function mapHttpToHttps(rawUrl) { 32 export function mapHttpToHttps(rawUrl) {
17 if (rawUrl.indexOf(':') < 0 || rawUrl.startsWith('http://tmp')) { 33 if (rawUrl.indexOf(':') < 0 || rawUrl.startsWith('http://tmp')) {
18 return rawUrl 34 return rawUrl
...@@ -27,18 +43,40 @@ export function mapHttpToHttps(rawUrl) { ...@@ -27,18 +43,40 @@ export function mapHttpToHttps(rawUrl) {
27 return rawUrl 43 return rawUrl
28 } 44 }
29 45
46 +/**
47 + * @description 获取 rpx 与 px 的换算系数(以 750 设计稿为基准)
48 + * @returns {number} 系数(screenWidth / 750)
49 + */
30 export const getFactor = () => { 50 export const getFactor = () => {
31 const sysInfo = Taro.getSystemInfoSync() 51 const sysInfo = Taro.getSystemInfoSync()
32 const { screenWidth } = sysInfo 52 const { screenWidth } = sysInfo
33 return screenWidth / 750 53 return screenWidth / 750
34 } 54 }
35 55
56 +/**
57 + * @description rpx 转 px
58 + * @param {number} rpx rpx 值
59 + * @param {number} factor 换算系数
60 + * @returns {number} px 值(整数)
61 + */
36 export const toPx = (rpx, factor = getFactor()) => 62 export const toPx = (rpx, factor = getFactor()) =>
37 parseInt(String(rpx * factor), 10) 63 parseInt(String(rpx * factor), 10)
38 64
65 +/**
66 + * @description px 转 rpx
67 + * @param {number} px px 值
68 + * @param {number} factor 换算系数
69 + * @returns {number} rpx 值(整数)
70 + */
39 export const toRpx = (px, factor = getFactor()) => 71 export const toRpx = (px, factor = getFactor()) =>
40 parseInt(String(px / factor), 10) 72 parseInt(String(px / factor), 10)
41 73
74 +/**
75 + * @description 下载图片到本地临时路径(避免跨域/协议限制)
76 + * - 已是本地路径/用户数据路径时直接返回
77 + * @param {string} url 图片地址
78 + * @returns {Promise<string>} 本地可用的图片路径
79 + */
42 export function downImage(url) { 80 export function downImage(url) {
43 return new Promise((resolve, reject) => { 81 return new Promise((resolve, reject) => {
44 const wx_user_data_path = 82 const wx_user_data_path =
...@@ -69,6 +107,12 @@ export function downImage(url) { ...@@ -69,6 +107,12 @@ export function downImage(url) {
69 }) 107 })
70 } 108 }
71 109
110 +/**
111 + * @description 获取图片信息并计算裁剪参数(居中裁剪)
112 + * @param {Object} item 图片配置
113 + * @param {number} index 渲染顺序(默认 zIndex)
114 + * @returns {Promise<Object>} 标准化后的图片绘制参数
115 + */
72 export const getImageInfo = (item, index) => 116 export const getImageInfo = (item, index) =>
73 new Promise((resolve, reject) => { 117 new Promise((resolve, reject) => {
74 const { x, y, width, height, url, zIndex } = item 118 const { x, y, width, height, url, zIndex } = item
...@@ -112,6 +156,16 @@ export const getImageInfo = (item, index) => ...@@ -112,6 +156,16 @@ export const getImageInfo = (item, index) =>
112 ) 156 )
113 }) 157 })
114 158
159 +/**
160 + * @description 解析 linear-gradient 字符串为 canvas 渐变色
161 + * @param {CanvasRenderingContext2D} ctx canvas 上下文
162 + * @param {string} color 颜色字符串(支持 linear-gradient(...))
163 + * @param {number} startX 起点 x
164 + * @param {number} startY 起点 y
165 + * @param {number} w 宽度
166 + * @param {number} h 高度
167 + * @returns {any} 普通颜色字符串或渐变对象
168 + */
115 export function getLinearColor(ctx, color, startX, startY, w, h) { 169 export function getLinearColor(ctx, color, startX, startY, w, h) {
116 if ( 170 if (
117 typeof startX !== 'number' || 171 typeof startX !== 'number' ||
...@@ -150,6 +204,13 @@ export function getLinearColor(ctx, color, startX, startY, w, h) { ...@@ -150,6 +204,13 @@ export function getLinearColor(ctx, color, startX, startY, w, h) {
150 return grd 204 return grd
151 } 205 }
152 206
207 +/**
208 + * @description 根据 textAlign 计算文本绘制起点 x
209 + * @param {'left'|'center'|'right'} textAlign 对齐方式
210 + * @param {number} x 原始 x
211 + * @param {number} width 容器宽
212 + * @returns {number} 计算后的 x
213 + */
153 export function getTextX(textAlign, x, width) { 214 export function getTextX(textAlign, x, width) {
154 let newX = x 215 let newX = x
155 if (textAlign === 'center') { 216 if (textAlign === 'center') {
......
...@@ -51,7 +51,7 @@ ...@@ -51,7 +51,7 @@
51 51
52 <script setup> 52 <script setup>
53 import { ref, computed, watch, onMounted, onUnmounted } from 'vue' 53 import { ref, computed, watch, onMounted, onUnmounted } from 'vue'
54 -import Taro, { useDidShow } from '@tarojs/taro' 54 +import Taro from '@tarojs/taro'
55 import { formatDatetime } from '@/utils/tools'; 55 import { formatDatetime } from '@/utils/tools';
56 import { qrcodeListAPI, qrcodeStatusAPI, billPersonAPI } from '@/api/index' 56 import { qrcodeListAPI, qrcodeStatusAPI, billPersonAPI } from '@/api/index'
57 import { useGo } from '@/hooks/useGo' 57 import { useGo } from '@/hooks/useGo'
...@@ -77,12 +77,21 @@ const props = defineProps({ ...@@ -77,12 +77,21 @@ const props = defineProps({
77 const select_index = ref(0); 77 const select_index = ref(0);
78 const userList = ref([]); 78 const userList = ref([]);
79 79
80 +/**
81 + * @description 切换到上一张二维码(循环)
82 + * @returns {void} 无返回值
83 + */
80 const prevCode = () => { 84 const prevCode = () => {
81 select_index.value = select_index.value - 1; 85 select_index.value = select_index.value - 1;
82 if (select_index.value < 0) { 86 if (select_index.value < 0) {
83 select_index.value = userList.value.length - 1; 87 select_index.value = userList.value.length - 1;
84 } 88 }
85 }; 89 };
90 +
91 +/**
92 + * @description 切换到下一张二维码(循环)
93 + * @returns {void} 无返回值
94 + */
86 const nextCode = () => { 95 const nextCode = () => {
87 select_index.value = select_index.value + 1; 96 select_index.value = select_index.value + 1;
88 if (select_index.value > userList.value.length - 1) { 97 if (select_index.value > userList.value.length - 1) {
...@@ -92,7 +101,7 @@ const nextCode = () => { ...@@ -92,7 +101,7 @@ const nextCode = () => {
92 101
93 watch( 102 watch(
94 () => select_index.value, 103 () => select_index.value,
95 - (index) => { 104 + () => {
96 refreshBtn(); 105 refreshBtn();
97 } 106 }
98 ) 107 )
...@@ -107,7 +116,12 @@ watch( ...@@ -107,7 +116,12 @@ watch(
107 { immediate: true } 116 { immediate: true }
108 ) 117 )
109 118
110 -function replaceMiddleCharacters(inputString) { 119 +/**
120 + * @description 身份证号脱敏:中间 8 位替换为 * 号
121 + * @param {string} inputString 原始身份证号
122 + * @returns {string} 脱敏后的身份证号
123 + */
124 +function replaceMiddleCharacters (inputString) {
111 if (!inputString || inputString.length < 15) { 125 if (!inputString || inputString.length < 15) {
112 return inputString; 126 return inputString;
113 } 127 }
...@@ -117,6 +131,11 @@ function replaceMiddleCharacters(inputString) { ...@@ -117,6 +131,11 @@ function replaceMiddleCharacters(inputString) {
117 return inputString.substring(0, start) + replacement + inputString.substring(end); 131 return inputString.substring(0, start) + replacement + inputString.substring(end);
118 } 132 }
119 133
134 +/**
135 + * @description 格式化证件号展示(脱敏)
136 + * @param {string} id 原始证件号
137 + * @returns {string} 脱敏后的证件号
138 + */
120 const formatId = (id) => replaceMiddleCharacters(id); 139 const formatId = (id) => replaceMiddleCharacters(id);
121 140
122 const userinfo = computed(() => { 141 const userinfo = computed(() => {
...@@ -151,6 +170,11 @@ const STATUS_CODE = { ...@@ -151,6 +170,11 @@ const STATUS_CODE = {
151 USED: '7', 170 USED: '7',
152 }; 171 };
153 172
173 +/**
174 + * @description 刷新当前选中二维码状态
175 + * - 仅在当前选中用户存在时请求
176 + * @returns {Promise<void>} 无返回值
177 + */
154 const refreshBtn = async () => { 178 const refreshBtn = async () => {
155 if (!userList.value[select_index.value]) return; 179 if (!userList.value[select_index.value]) return;
156 const { code, data } = await qrcodeStatusAPI({ qr_code: userList.value[select_index.value].qr_code }); 180 const { code, data } = await qrcodeStatusAPI({ qr_code: userList.value[select_index.value].qr_code });
...@@ -159,10 +183,21 @@ const refreshBtn = async () => { ...@@ -159,10 +183,21 @@ const refreshBtn = async () => {
159 } 183 }
160 } 184 }
161 185
186 +/**
187 + * @description 选择指定参观者的二维码
188 + * @param {number} index 下标
189 + * @returns {void} 无返回值
190 + */
162 const selectUser = (index) => { 191 const selectUser = (index) => {
163 select_index.value = index; 192 select_index.value = index;
164 } 193 }
165 194
195 +/**
196 + * @description 按 pay_id 分组标记(用于展示分隔线)
197 + * - 首个 pay_id 出现处标记 sort=1,其余为 sort=0
198 + * @param {Array<Object>} data 二维码列表
199 + * @returns {Array<Object>} 处理后的列表
200 + */
166 const formatGroup = (data) => { 201 const formatGroup = (data) => {
167 let lastPayId = null; 202 let lastPayId = null;
168 for (let i = 0; i < data.length; i++) { 203 for (let i = 0; i < data.length; i++) {
...@@ -176,6 +211,12 @@ const formatGroup = (data) => { ...@@ -176,6 +211,12 @@ const formatGroup = (data) => {
176 return data; 211 return data;
177 } 212 }
178 213
214 +/**
215 + * @description 初始化二维码列表
216 + * - 不传 type:拉取“我的当日二维码列表”
217 + * - 传入 type + payId:按订单查询二维码人员列表
218 + * @returns {Promise<void>} 无返回值
219 + */
179 const init = async () => { 220 const init = async () => {
180 if (!props.type) { 221 if (!props.type) {
181 try { 222 try {
...@@ -228,6 +269,11 @@ onMounted(() => { ...@@ -228,6 +269,11 @@ onMounted(() => {
228 init(); 269 init();
229 }); 270 });
230 271
272 +/**
273 + * @description 轮询刷新二维码状态
274 + * - 仅在“待使用”状态下轮询,避免无意义请求
275 + * @returns {Promise<void>} 无返回值
276 + */
231 const poll = async () => { 277 const poll = async () => {
232 if (userList.value.length && useStatus.value === STATUS_CODE.SUCCESS) { 278 if (userList.value.length && useStatus.value === STATUS_CODE.SUCCESS) {
233 if (userList.value[select_index.value]) { 279 if (userList.value[select_index.value]) {
...@@ -239,12 +285,17 @@ const poll = async () => { ...@@ -239,12 +285,17 @@ const poll = async () => {
239 } 285 }
240 }; 286 };
241 287
242 -const intervalId = setInterval(poll, 3000); // 3秒轮询一次,避免过于频繁 288 +// 3 秒轮询一次,避免过于频繁
289 +const intervalId = setInterval(poll, 3000);
243 290
244 onUnmounted(() => { 291 onUnmounted(() => {
245 clearInterval(intervalId); 292 clearInterval(intervalId);
246 }); 293 });
247 294
295 +/**
296 + * @description 跳转预约记录列表页
297 + * @returns {void} 无返回值
298 + */
248 const toRecord = () => { 299 const toRecord = () => {
249 go('/bookingList'); 300 go('/bookingList');
250 } 301 }
......
...@@ -58,6 +58,11 @@ const reserve_info = computed(() => props.data); ...@@ -58,6 +58,11 @@ const reserve_info = computed(() => props.data);
58 58
59 const is_offline = computed(() => props.is_offline); 59 const is_offline = computed(() => props.is_offline);
60 60
61 +/**
62 + * @description 预约码状态枚举(与后端约定)
63 + * - 1=待支付;2=支付中;3=预约成功;5/7=已取消;9=已使用;11=退款中
64 + * @readonly
65 + */
61 const CodeStatus = { 66 const CodeStatus = {
62 APPLY: '1', 67 APPLY: '1',
63 PAYING: '2', 68 PAYING: '2',
...@@ -69,7 +74,7 @@ const CodeStatus = { ...@@ -69,7 +74,7 @@ const CodeStatus = {
69 } 74 }
70 75
71 /** 76 /**
72 - * 是否支付待处理状态 77 + * @description 是否支付待处理状态
73 */ 78 */
74 79
75 const is_pay_pending = computed(() => { 80 const is_pay_pending = computed(() => {
...@@ -79,6 +84,11 @@ const is_pay_pending = computed(() => { ...@@ -79,6 +84,11 @@ const is_pay_pending = computed(() => {
79 84
80 const countdown_seconds = ref(0) 85 const countdown_seconds = ref(0)
81 86
87 +/**
88 + * @description 将数字格式化为两位字符串(不足补 0)
89 + * @param {number|string} n 数字
90 + * @returns {string} 两位字符串
91 + */
82 const format_two_digits = (n) => { 92 const format_two_digits = (n) => {
83 const num = Number(n) || 0 93 const num = Number(n) || 0
84 return num < 10 ? `0${num}` : String(num) 94 return num < 10 ? `0${num}` : String(num)
...@@ -91,6 +101,11 @@ const countdown_text = computed(() => { ...@@ -91,6 +101,11 @@ const countdown_text = computed(() => {
91 return `${format_two_digits(minutes)}:${format_two_digits(remain)}` 101 return `${format_two_digits(minutes)}:${format_two_digits(remain)}`
92 }) 102 })
93 103
104 +/**
105 + * @description 解析 created_time 为毫秒时间戳(兼容 iOS Date 解析)
106 + * @param {string} created_time 创建时间字符串
107 + * @returns {number} 毫秒时间戳;解析失败返回 0
108 + */
94 const parse_created_time_ms = (created_time) => { 109 const parse_created_time_ms = (created_time) => {
95 const raw = String(created_time || '') 110 const raw = String(created_time || '')
96 if (!raw) return 0 111 if (!raw) return 0
...@@ -102,7 +117,8 @@ const parse_created_time_ms = (created_time) => { ...@@ -102,7 +117,8 @@ const parse_created_time_ms = (created_time) => {
102 117
103 let countdown_timer = null 118 let countdown_timer = null
104 /** 119 /**
105 - * 停止倒计时 120 + * @description 停止倒计时(清理定时器)
121 + * @returns {void} 无返回值
106 */ 122 */
107 123
108 const stop_countdown = () => { 124 const stop_countdown = () => {
...@@ -112,6 +128,10 @@ const stop_countdown = () => { ...@@ -112,6 +128,10 @@ const stop_countdown = () => {
112 } 128 }
113 } 129 }
114 130
131 +/**
132 + * @description 更新倒计时剩余秒数(默认 10 分钟支付窗口)
133 + * @returns {void} 无返回值
134 + */
115 const update_countdown = () => { 135 const update_countdown = () => {
116 const start_ms = parse_created_time_ms(reserve_info.value?.created_time) 136 const start_ms = parse_created_time_ms(reserve_info.value?.created_time)
117 if (!start_ms) { 137 if (!start_ms) {
...@@ -130,6 +150,10 @@ const update_countdown = () => { ...@@ -130,6 +150,10 @@ const update_countdown = () => {
130 } 150 }
131 } 151 }
132 152
153 +/**
154 + * @description 启动倒计时(每秒更新)
155 + * @returns {void} 无返回值
156 + */
133 const start_countdown = () => { 157 const start_countdown = () => {
134 stop_countdown() 158 stop_countdown()
135 update_countdown() 159 update_countdown()
...@@ -138,6 +162,11 @@ const start_countdown = () => { ...@@ -138,6 +162,11 @@ const start_countdown = () => {
138 } 162 }
139 163
140 let is_showing_pay_modal = false 164 let is_showing_pay_modal = false
165 +/**
166 + * @description 支付失败提示弹窗(防并发)
167 + * @param {string} content 弹窗内容
168 + * @returns {Promise<boolean>} true=继续支付,false=取消
169 + */
141 const show_pay_modal = async (content) => { 170 const show_pay_modal = async (content) => {
142 if (is_showing_pay_modal) return false 171 if (is_showing_pay_modal) return false
143 is_showing_pay_modal = true 172 is_showing_pay_modal = true
...@@ -156,7 +185,8 @@ const show_pay_modal = async (content) => { ...@@ -156,7 +185,8 @@ const show_pay_modal = async (content) => {
156 } 185 }
157 186
158 /** 187 /**
159 - * 重新支付 188 + * @description 重新支付(循环支付直到成功或用户取消)
189 + * @returns {Promise<void>} 无返回值
160 */ 190 */
161 191
162 const onRepay = async () => { 192 const onRepay = async () => {
...@@ -178,6 +208,11 @@ const onRepay = async () => { ...@@ -178,6 +208,11 @@ const onRepay = async () => {
178 } 208 }
179 } 209 }
180 210
211 +/**
212 + * @description 订单状态展示映射
213 + * @param {string} status 订单状态码
214 + * @returns {{key:string,value:string}} 展示状态(key 用于 class)
215 + */
181 const formatStatus = (status) => { 216 const formatStatus = (status) => {
182 switch (status) { 217 switch (status) {
183 case CodeStatus.APPLY: 218 case CodeStatus.APPLY:
...@@ -224,6 +259,12 @@ const status_info = computed(() => { ...@@ -224,6 +259,12 @@ const status_info = computed(() => {
224 return formatStatus(reserve_info.value?.status) || { key: '', value: '' } 259 return formatStatus(reserve_info.value?.status) || { key: '', value: '' }
225 }) 260 })
226 261
262 +/**
263 + * @description 跳转预约详情
264 + * - 只有成功/已使用/已取消才允许进入详情
265 + * @param {Object} item 预约记录
266 + * @returns {void} 无返回值
267 + */
227 const goToDetail = (item) => { 268 const goToDetail = (item) => {
228 // 只有成功、已使用、已取消(退款成功)才跳转详情 269 // 只有成功、已使用、已取消(退款成功)才跳转详情
229 if (item.status === CodeStatus.SUCCESS || item.status === CodeStatus.USED || item.status === CodeStatus.CANCEL) { 270 if (item.status === CodeStatus.SUCCESS || item.status === CodeStatus.USED || item.status === CodeStatus.CANCEL) {
...@@ -232,7 +273,9 @@ const goToDetail = (item) => { ...@@ -232,7 +273,9 @@ const goToDetail = (item) => {
232 } 273 }
233 274
234 /** 275 /**
235 - * 监听支付待处理状态变化 276 + * @description 监听支付待处理状态变化
277 + * - 进入待支付:启动倒计时
278 + * - 退出待支付:清空倒计时
236 */ 279 */
237 280
238 watch(is_pay_pending, (val) => { 281 watch(is_pay_pending, (val) => {
......
...@@ -16,6 +16,12 @@ export const OFFLINE_BOOKING_CACHE_KEY = 'OFFLINE_BOOKING_DATA' ...@@ -16,6 +16,12 @@ export const OFFLINE_BOOKING_CACHE_KEY = 'OFFLINE_BOOKING_DATA'
16 16
17 let refresh_promise = null 17 let refresh_promise = null
18 18
19 +/**
20 + * @description 兼容不同后端结构:从预约记录中提取可用数据载荷
21 + * - 部分接口会把字段塞到 bill.list 对象里,这里做一次展开合并
22 + * @param {Object} bill 原始预约记录
23 + * @returns {Object} 扁平化后的预约记录对象
24 + */
19 const extract_bill_payload = (bill) => { 25 const extract_bill_payload = (bill) => {
20 if (!bill) return {} 26 if (!bill) return {}
21 27
...@@ -29,6 +35,13 @@ const extract_bill_payload = (bill) => { ...@@ -29,6 +35,13 @@ const extract_bill_payload = (bill) => {
29 return data 35 return data
30 } 36 }
31 37
38 +/**
39 + * @description 从预约记录中提取人员列表
40 + * - 兼容不同字段名(person_list/bill_person_list/persons/qrcode_list/qr_list/detail_list)
41 + * - 保证返回数组类型
42 + * @param {Object} bill 预约记录
43 + * @returns {Array} 人员列表
44 + */
32 const extract_person_list = (bill) => { 45 const extract_person_list = (bill) => {
33 if (!bill) return [] 46 if (!bill) return []
34 47
...@@ -52,8 +65,8 @@ const extract_person_list = (bill) => { ...@@ -52,8 +65,8 @@ const extract_person_list = (bill) => {
52 } 65 }
53 66
54 /** 67 /**
55 - * 格式化预约记录项 68 + * @description 格式化预约记录项(统一字段与展示用时间)
56 - * @param {Object} item - 原始预约记录项 69 + * @param {Object} item 原始预约记录项
57 * @returns {Object} 格式化后的预约记录项 70 * @returns {Object} 格式化后的预约记录项
58 */ 71 */
59 const normalize_bill_item = (item) => { 72 const normalize_bill_item = (item) => {
......
...@@ -8,14 +8,17 @@ ...@@ -8,14 +8,17 @@
8 import Taro from '@tarojs/taro'; 8 import Taro from '@tarojs/taro';
9 9
10 /** 10 /**
11 - * 封装路由跳转方便行内调用 11 + * @description 获取页面跳转方法(navigateTo)
12 - * @returns 12 + * - 支持短路径:notice / /notice
13 + * - 自动补全:pages/notice/index
14 + * @returns {(path:string, query?:Object)=>void} go 跳转函数
13 */ 15 */
14 export function useGo () { 16 export function useGo () {
15 /** 17 /**
16 - * 路由跳转 18 + * @description 路由跳转
17 - * @param {string} path - 目标页面路径,支持 / 开头 19 + * @param {string} path 目标页面路径,支持 / 开头与短路径
18 - * @param {Object} query - 查询参数,键值对形式 20 + * @param {Object} query 查询参数(键值对)
21 + * @returns {void} 无返回值
19 */ 22 */
20 function go (path, query = {}) { 23 function go (path, query = {}) {
21 // 补全路径,如果是 / 开头,去掉 / 24 // 补全路径,如果是 / 开头,去掉 /
...@@ -40,7 +43,7 @@ export function useGo () { ...@@ -40,7 +43,7 @@ export function useGo () {
40 if (err.errMsg && err.errMsg.indexOf('tabbar') !== -1) { 43 if (err.errMsg && err.errMsg.indexOf('tabbar') !== -1) {
41 Taro.switchTab({ url: '/' + url }); 44 Taro.switchTab({ url: '/' + url });
42 } else { 45 } else {
43 - console.error('Navigation failed', err); 46 + console.error('页面跳转失败:', err);
44 } 47 }
45 } 48 }
46 }) 49 })
...@@ -49,14 +52,17 @@ export function useGo () { ...@@ -49,14 +52,17 @@ export function useGo () {
49 } 52 }
50 53
51 /** 54 /**
52 - * 封装路由替换方便行内调用 55 + * @description 获取页面替换方法(redirectTo)
53 - * @returns 56 + * - 支持短路径:notice / /notice
57 + * - 自动补全:pages/notice/index
58 + * @returns {(path:string, query?:Object)=>void} replace 替换函数
54 */ 59 */
55 export function useReplace () { 60 export function useReplace () {
56 /** 61 /**
57 - * 路由替换 62 + * @description 路由替换
58 - * @param {string} path - 目标页面路径,支持 / 开头 63 + * @param {string} path 目标页面路径,支持 / 开头与短路径
59 - * @param {Object} query - 查询参数,键值对形式 64 + * @param {Object} query 查询参数(键值对)
65 + * @returns {void} 无返回值
60 */ 66 */
61 function replace (path, query = {}) { 67 function replace (path, query = {}) {
62 let url = path.startsWith('/') ? path.substring(1) : path; 68 let url = path.startsWith('/') ? path.substring(1) : path;
......
1 <!-- 1 <!--
2 * @Date: 2024-01-16 13:19:23 2 * @Date: 2024-01-16 13:19:23
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2026-01-13 18:36:58 4 + * @LastEditTime: 2026-01-24 12:46:06
5 * @FilePath: /xyxBooking-weapp/src/pages/bookingDetail/index.vue 5 * @FilePath: /xyxBooking-weapp/src/pages/bookingDetail/index.vue
6 * @Description: 预约记录详情 6 * @Description: 预约记录详情
7 --> 7 -->
...@@ -44,19 +44,21 @@ ...@@ -44,19 +44,21 @@
44 <script setup> 44 <script setup>
45 import { ref, computed } from 'vue' 45 import { ref, computed } from 'vue'
46 import Taro, { useDidShow, useRouter as useTaroRouter } from '@tarojs/taro' 46 import Taro, { useDidShow, useRouter as useTaroRouter } from '@tarojs/taro'
47 -import { useGo } from '@/hooks/useGo'
48 import qrCode from '@/components/qrCode'; 47 import qrCode from '@/components/qrCode';
49 import { billInfoAPI, icbcRefundAPI } from '@/api/index' 48 import { billInfoAPI, icbcRefundAPI } from '@/api/index'
50 import { formatDatetime } from '@/utils/tools'; 49 import { formatDatetime } from '@/utils/tools';
51 import { refresh_offline_booking_cache } from '@/composables/useOfflineBookingCache' 50 import { refresh_offline_booking_cache } from '@/composables/useOfflineBookingCache'
52 51
53 const router = useTaroRouter(); 52 const router = useTaroRouter();
54 -const go = useGo();
55 53
56 const pay_id = ref(''); 54 const pay_id = ref('');
57 const qrCodeStatus = ref(''); 55 const qrCodeStatus = ref('');
58 const billInfo = ref({}); 56 const billInfo = ref({});
59 57
58 +/**
59 + * @description 预约码状态枚举(与后端约定)
60 + * @readonly
61 + */
60 const CodeStatus = { 62 const CodeStatus = {
61 APPLY: '1', 63 APPLY: '1',
62 PAYING: '2', 64 PAYING: '2',
...@@ -67,6 +69,10 @@ const CodeStatus = { ...@@ -67,6 +69,10 @@ const CodeStatus = {
67 REFUNDING: '11' 69 REFUNDING: '11'
68 } 70 }
69 71
72 +/**
73 + * @description 订单状态文案
74 + * @returns {string} 状态文案
75 + */
70 const qrCodeStatusText = computed(() => { 76 const qrCodeStatusText = computed(() => {
71 const status = billInfo.value?.status; 77 const status = billInfo.value?.status;
72 switch (status) { 78 switch (status) {
...@@ -78,6 +84,11 @@ const qrCodeStatusText = computed(() => { ...@@ -78,6 +84,11 @@ const qrCodeStatusText = computed(() => {
78 } 84 }
79 }) 85 })
80 86
87 +/**
88 + * @description 取消预约
89 + * - 成功后刷新离线缓存(保证弱网/离线模式数据一致)
90 + * @returns {Promise<void>} 无返回值
91 + */
81 const cancelBooking = async () => { 92 const cancelBooking = async () => {
82 const { confirm } = await Taro.showModal({ 93 const { confirm } = await Taro.showModal({
83 title: '温馨提示', 94 title: '温馨提示',
......
1 <!-- 1 <!--
2 * @Date: 2024-01-16 11:37:10 2 * @Date: 2024-01-16 11:37:10
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2026-01-16 19:41:35 4 + * @LastEditTime: 2026-01-24 12:45:49
5 * @FilePath: /xyxBooking-weapp/src/pages/bookingList/index.vue 5 * @FilePath: /xyxBooking-weapp/src/pages/bookingList/index.vue
6 * @Description: 预约记录列表页 6 * @Description: 预约记录列表页
7 --> 7 -->
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
23 23
24 <script setup> 24 <script setup>
25 import { ref } from 'vue' 25 import { ref } from 'vue'
26 -import Taro, { useDidShow, useReachBottom } from '@tarojs/taro' 26 +import { useDidShow, useReachBottom } from '@tarojs/taro'
27 import { billListAPI } from '@/api/index' 27 import { billListAPI } from '@/api/index'
28 import { formatDatetime } from '@/utils/tools'; 28 import { formatDatetime } from '@/utils/tools';
29 import reserveCard from '@/components/reserveCard.vue' 29 import reserveCard from '@/components/reserveCard.vue'
...@@ -35,8 +35,9 @@ const loading = ref(false); ...@@ -35,8 +35,9 @@ const loading = ref(false);
35 const finished = ref(false); 35 const finished = ref(false);
36 36
37 /** 37 /**
38 - * 加载预约记录列表 38 + * @description 加载预约记录列表(分页)
39 - * @param isRefresh 是否刷新,默认 false 39 + * @param {boolean} isRefresh 是否刷新(刷新会重置 page 并覆盖列表)
40 + * @returns {Promise<void>} 无返回值
40 */ 41 */
41 const loadData = async (isRefresh = false) => { 42 const loadData = async (isRefresh = false) => {
42 if (loading.value || (finished.value && !isRefresh)) return; 43 if (loading.value || (finished.value && !isRefresh)) return;
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 * @LastEditTime: 2025-07-01 10:56:38 4 * @LastEditTime: 2025-07-01 10:56:38
5 * @FilePath: /myApp/src/pages/demo/index.vue 5 * @FilePath: /myApp/src/pages/demo/index.vue
6 - * @Description: 文件描述 6 + * @Description: demo 页面(开发调试用)
7 --> 7 -->
8 <template> 8 <template>
9 <div class="red">{{ str }}</div> 9 <div class="red">{{ str }}</div>
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 * @LastEditTime: 2026-01-08 20:16:02 4 * @LastEditTime: 2026-01-08 20:16:02
5 * @FilePath: /xyxBooking-weapp/src/pages/notice/index.vue 5 * @FilePath: /xyxBooking-weapp/src/pages/notice/index.vue
6 - * @Description: 文件描述 6 + * @Description: 参访须知确认页
7 --> 7 -->
8 <template> 8 <template>
9 <view class="notice-page"> 9 <view class="notice-page">
...@@ -46,6 +46,11 @@ const note_text = [ ...@@ -46,6 +46,11 @@ const note_text = [
46 ]; 46 ];
47 const checked = ref([]); 47 const checked = ref([]);
48 48
49 +/**
50 + * @description 点击确认进入下一步
51 + * - 必须勾选“我已阅读并同意”
52 + * @returns {void} 无返回值
53 + */
49 const confirmBtn = () => { 54 const confirmBtn = () => {
50 if (checked.value.includes("1")) { 55 if (checked.value.includes("1")) {
51 go("/booking"); 56 go("/booking");
......
1 <!-- 1 <!--
2 * @Date: 2024-01-15 16:25:51 2 * @Date: 2024-01-15 16:25:51
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2026-01-16 20:38:48 4 + * @LastEditTime: 2026-01-24 12:44:25
5 * @FilePath: /xyxBooking-weapp/src/pages/submit/index.vue 5 * @FilePath: /xyxBooking-weapp/src/pages/submit/index.vue
6 * @Description: 预约人员信息 6 * @Description: 预约人员信息
7 --> 7 -->
...@@ -76,8 +76,9 @@ const price = ref(0); ...@@ -76,8 +76,9 @@ const price = ref(0);
76 const period_type = ref(''); 76 const period_type = ref('');
77 77
78 /** 78 /**
79 - * 生成15位身份证号中间8位替换为*号 79 + * @description 身份证号脱敏:中间 8 位替换为 *
80 - * @param {*} inputString 80 + * @param {string} inputString 身份证号
81 + * @returns {string} 脱敏后的身份证号
81 */ 82 */
82 function replaceMiddleCharacters(inputString) { 83 function replaceMiddleCharacters(inputString) {
83 if (!inputString || inputString.length < 15) { 84 if (!inputString || inputString.length < 15) {
...@@ -93,16 +94,33 @@ function replaceMiddleCharacters(inputString) { ...@@ -93,16 +94,33 @@ function replaceMiddleCharacters(inputString) {
93 return replacedString; 94 return replacedString;
94 } 95 }
95 96
97 +/**
98 + * @description 格式化身份证号展示
99 + * @param {string} id 身份证号
100 + * @returns {string} 脱敏后的身份证号
101 + */
96 const formatId = (id) => { 102 const formatId = (id) => {
97 return replaceMiddleCharacters(id); 103 return replaceMiddleCharacters(id);
98 }; 104 };
99 105
106 +/**
107 + * @description 当天预约标记
108 + * - ENABLE 表示该参观者今天已预约,不允许重复预约
109 + * @readonly
110 + */
100 const RESERVE_STATUS = { 111 const RESERVE_STATUS = {
101 ENABLE: '1' 112 ENABLE: '1'
102 } 113 }
103 114
104 const checked_visitors = ref([]); 115 const checked_visitors = ref([]);
105 const is_submitting = ref(false); // 是否正在提交订单 116 const is_submitting = ref(false); // 是否正在提交订单
117 +
118 +/**
119 + * @description 选择/取消选择参观者
120 + * - 已预约过当天参观的参观者不允许选择
121 + * @param {Object} item 参观者数据
122 + * @returns {void} 无返回值
123 + */
106 const addVisitor = (item) => { 124 const addVisitor = (item) => {
107 if (item.is_reserve === RESERVE_STATUS.ENABLE) { // 今天已经预约 125 if (item.is_reserve === RESERVE_STATUS.ENABLE) { // 今天已经预约
108 Taro.showToast({ title: '已预约过参观,请不要重复预约', icon: 'none' }) 126 Taro.showToast({ title: '已预约过参观,请不要重复预约', icon: 'none' })
...@@ -119,9 +137,18 @@ const total = computed(() => { ...@@ -119,9 +137,18 @@ const total = computed(() => {
119 return price.value * checked_visitors.value.length; 137 return price.value * checked_visitors.value.length;
120 }) 138 })
121 139
140 +/**
141 + * @description 返回重新选择参访时间页
142 + * @returns {void} 无返回值
143 + */
122 const goToBooking = () => { 144 const goToBooking = () => {
123 go('/booking'); 145 go('/booking');
124 } 146 }
147 +
148 +/**
149 + * @description 跳转新增参观者页
150 + * @returns {void} 无返回值
151 + */
125 const goToVisitor = () => { 152 const goToVisitor = () => {
126 go('/addVisitor'); 153 go('/addVisitor');
127 } 154 }
...@@ -132,7 +159,10 @@ const pending_pay_id = ref(null); ...@@ -132,7 +159,10 @@ const pending_pay_id = ref(null);
132 const pending_need_pay = ref(null); 159 const pending_need_pay = ref(null);
133 160
134 /** 161 /**
135 - * 刷新参观者列表 162 + * @description 刷新参观者列表(并同步“当天已预约”标记)
163 + * @param {Object} options 选项
164 + * @param {boolean} options.reset_checked 是否清空已勾选参观者
165 + * @returns {Promise<void>} 无返回值
136 */ 166 */
137 167
138 const refreshVisitorList = async (options) => { 168 const refreshVisitorList = async (options) => {
...@@ -152,6 +182,12 @@ const refreshVisitorList = async (options) => { ...@@ -152,6 +182,12 @@ const refreshVisitorList = async (options) => {
152 } 182 }
153 183
154 let is_showing_pay_modal = false; 184 let is_showing_pay_modal = false;
185 +
186 +/**
187 + * @description 支付未完成弹窗(防并发)
188 + * @param {string} content 弹窗内容
189 + * @returns {Promise<boolean>} true=继续支付,false=离开
190 + */
155 const showPayErrorModal = async (content) => { 191 const showPayErrorModal = async (content) => {
156 if (is_showing_pay_modal) return; 192 if (is_showing_pay_modal) return;
157 is_showing_pay_modal = true; 193 is_showing_pay_modal = true;
...@@ -169,6 +205,12 @@ const showPayErrorModal = async (content) => { ...@@ -169,6 +205,12 @@ const showPayErrorModal = async (content) => {
169 } 205 }
170 } 206 }
171 207
208 +/**
209 + * @description 提交订单
210 + * - 先创建预约单拿 pay_id(支持“待支付订单”复用)
211 + * - need_pay=1 时循环拉起微信支付,直到成功或用户取消
212 + * @returns {Promise<void>} 无返回值
213 + */
172 const submitBtn = async () => { 214 const submitBtn = async () => {
173 if (is_submitting.value) return; 215 if (is_submitting.value) return;
174 if (!checked_visitors.value.length) { 216 if (!checked_visitors.value.length) {
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 * @LastEditTime: 2026-01-12 22:53:55 4 * @LastEditTime: 2026-01-12 22:53:55
5 * @FilePath: /xyxBooking-weapp/src/pages/tailwindTest/index.config.js 5 * @FilePath: /xyxBooking-weapp/src/pages/tailwindTest/index.config.js
6 - * @Description: 文件描述 6 + * @Description: Tailwind 测试页配置(仅开发环境)
7 */ 7 */
8 export default { 8 export default {
9 navigationBarTitleText: 'Tailwind 测试' 9 navigationBarTitleText: 'Tailwind 测试'
......
...@@ -70,7 +70,12 @@ const on_nav_select = (key) => { ...@@ -70,7 +70,12 @@ const on_nav_select = (key) => {
70 70
71 const visitorList = ref([]); 71 const visitorList = ref([]);
72 72
73 -function replaceMiddleCharacters(inputString) { 73 +/**
74 + * @description 身份证号脱敏:中间 8 位替换为 * 号
75 + * @param {string} inputString 原始身份证号
76 + * @returns {string} 脱敏后的身份证号
77 + */
78 +function replaceMiddleCharacters (inputString) {
74 if (!inputString || inputString.length < 15) { 79 if (!inputString || inputString.length < 15) {
75 return inputString; 80 return inputString;
76 } 81 }
...@@ -80,8 +85,17 @@ function replaceMiddleCharacters(inputString) { ...@@ -80,8 +85,17 @@ function replaceMiddleCharacters(inputString) {
80 return inputString.substring(0, start) + replacement + inputString.substring(end); 85 return inputString.substring(0, start) + replacement + inputString.substring(end);
81 } 86 }
82 87
88 +/**
89 + * @description 格式化证件号展示(脱敏)
90 + * @param {string} id 原始证件号
91 + * @returns {string} 脱敏后的证件号
92 + */
83 const formatId = (id) => replaceMiddleCharacters(id); 93 const formatId = (id) => replaceMiddleCharacters(id);
84 94
95 +/**
96 + * @description 加载参观者列表
97 + * @returns {Promise<void>} 无返回值
98 + */
85 const loadList = async () => { 99 const loadList = async () => {
86 try { 100 try {
87 const { code, data } = await personListAPI({}); 101 const { code, data } = await personListAPI({});
...@@ -94,6 +108,11 @@ const loadList = async () => { ...@@ -94,6 +108,11 @@ const loadList = async () => {
94 } 108 }
95 } 109 }
96 110
111 +/**
112 + * @description 删除参观者
113 + * @param {Object} item 参观者对象
114 + * @returns {Promise<void>} 无返回值
115 + */
97 const removeItem = async (item) => { 116 const removeItem = async (item) => {
98 const { confirm } = await Taro.showModal({ title: '提示', content: '确定删除该参观者吗?' }); 117 const { confirm } = await Taro.showModal({ title: '提示', content: '确定删除该参观者吗?' });
99 if (confirm) { 118 if (confirm) {
......
1 -// https://pinia.esm.dev/introduction.html 1 +// Pinia 入门文档:https://pinia.vuejs.org/introduction.html
2 import { defineStore } from 'pinia' 2 import { defineStore } from 'pinia'
3 3
4 +/**
5 + * @description 计数器示例 Store(模板保留)
6 + */
4 export const useCounterStore = defineStore('counter', { 7 export const useCounterStore = defineStore('counter', {
5 state: () => { 8 state: () => {
6 return { count: 0 } 9 return { count: 0 }
7 }, 10 },
8 - // could also be defined as 11 + // 也可以写成:state: () => ({ count: 0 })
9 - // state: () => ({ count: 0 })
10 actions: { 12 actions: {
13 + /**
14 + * @description 计数 +1
15 + * @returns {void} 无返回值
16 + */
11 increment() { 17 increment() {
12 this.count++ 18 this.count++
13 }, 19 },
14 }, 20 },
15 }) 21 })
16 22
17 -// You can even use a function (similar to a component setup()) to define a Store for more advanced use cases: 23 +// 也可以用函数式(类似组件 setup)定义 Store,适合更复杂场景:
18 // export const useCounterStore = defineStore('counter', () => { 24 // export const useCounterStore = defineStore('counter', () => {
19 // const count = ref(0) 25 // const count = ref(0)
20 // 26 //
...@@ -23,4 +29,4 @@ export const useCounterStore = defineStore('counter', { ...@@ -23,4 +29,4 @@ export const useCounterStore = defineStore('counter', {
23 // } 29 // }
24 // 30 //
25 // return {count, increment} 31 // return {count, increment}
26 -// })
...\ No newline at end of file ...\ No newline at end of file
32 +// })
......
...@@ -7,6 +7,10 @@ ...@@ -7,6 +7,10 @@
7 */ 7 */
8 import { defineStore } from 'pinia' 8 import { defineStore } from 'pinia'
9 9
10 +/**
11 + * @description 主办方相关缓存
12 + * - 用于保存主办方 id / join_id 等页面间共享参数
13 + */
10 export const hostStore = defineStore('host', { 14 export const hostStore = defineStore('host', {
11 state: () => { 15 state: () => {
12 return { 16 return {
...@@ -15,9 +19,19 @@ export const hostStore = defineStore('host', { ...@@ -15,9 +19,19 @@ export const hostStore = defineStore('host', {
15 } 19 }
16 }, 20 },
17 actions: { 21 actions: {
22 + /**
23 + * @description 设置主办方 id
24 + * @param {string} id 主办方 id
25 + * @returns {void} 无返回值
26 + */
18 add (id) { 27 add (id) {
19 this.id = id 28 this.id = id
20 }, 29 },
30 + /**
31 + * @description 设置 join_id
32 + * @param {string} id join_id
33 + * @returns {void} 无返回值
34 + */
21 addJoin (id) { 35 addJoin (id) {
22 this.join_id = id 36 this.join_id = id
23 }, 37 },
......
...@@ -7,6 +7,10 @@ ...@@ -7,6 +7,10 @@
7 */ 7 */
8 import { defineStore } from 'pinia'; 8 import { defineStore } from 'pinia';
9 9
10 +/**
11 + * @description 全局主状态
12 + * - 存储用户信息与授权态等全局数据
13 + */
10 export const mainStore = defineStore('main', { 14 export const mainStore = defineStore('main', {
11 state: () => { 15 state: () => {
12 return { 16 return {
...@@ -18,12 +22,20 @@ export const mainStore = defineStore('main', { ...@@ -18,12 +22,20 @@ export const mainStore = defineStore('main', {
18 }; 22 };
19 }, 23 },
20 getters: { 24 getters: {
21 - // 判断是否为义工 25 + /**
26 + * @description 是否具备义工核销权限
27 + * @returns {boolean} true=义工,false=非义工
28 + */
22 isVolunteer: (state) => { 29 isVolunteer: (state) => {
23 return !!(state.appUserInfo && (state.appUserInfo.can_redeem === true)); 30 return !!(state.appUserInfo && (state.appUserInfo.can_redeem === true));
24 }, 31 },
25 }, 32 },
26 actions: { 33 actions: {
34 + /**
35 + * @description 更新授权状态
36 + * @param {boolean} state 是否已授权
37 + * @returns {void} 无返回值
38 + */
27 changeState (state) { 39 changeState (state) {
28 this.auth = state; 40 this.auth = state;
29 }, 41 },
...@@ -38,6 +50,11 @@ export const mainStore = defineStore('main', { ...@@ -38,6 +50,11 @@ export const mainStore = defineStore('main', {
38 // }, 50 // },
39 // removeThisPage () { 51 // removeThisPage () {
40 // }, 52 // },
53 + /**
54 + * @description 更新全局用户信息
55 + * @param {Object|null} info 用户信息对象
56 + * @returns {void} 无返回值
57 + */
41 changeUserInfo (info) { 58 changeUserInfo (info) {
42 this.appUserInfo = info; 59 this.appUserInfo = info;
43 } 60 }
......
...@@ -7,6 +7,11 @@ ...@@ -7,6 +7,11 @@
7 */ 7 */
8 import { defineStore } from 'pinia' 8 import { defineStore } from 'pinia'
9 9
10 +/**
11 + * @description 路由回跳信息(用于授权完成后的返回)
12 + * - authRedirect.saveCurrentPagePath 会写入 url
13 + * - authRedirect.returnToOriginalPage 会消费并清空 url
14 + */
10 export const routerStore = defineStore('router', { 15 export const routerStore = defineStore('router', {
11 state: () => { 16 state: () => {
12 return { 17 return {
...@@ -14,9 +19,18 @@ export const routerStore = defineStore('router', { ...@@ -14,9 +19,18 @@ export const routerStore = defineStore('router', {
14 } 19 }
15 }, 20 },
16 actions: { 21 actions: {
22 + /**
23 + * @description 记录回跳路径
24 + * @param {string} path 页面路径(可带 query)
25 + * @returns {void} 无返回值
26 + */
17 add (path) { 27 add (path) {
18 this.url = path 28 this.url = path
19 }, 29 },
30 + /**
31 + * @description 清空回跳路径
32 + * @returns {void} 无返回值
33 + */
20 remove () { 34 remove () {
21 this.url = '' 35 this.url = ''
22 }, 36 },
......
1 /* 1 /*
2 * @Date: 2022-09-19 14:11:06 2 * @Date: 2022-09-19 14:11:06
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2026-01-24 12:28:14 4 + * @LastEditTime: 2026-01-24 12:40:28
5 * @FilePath: /xyxBooking-weapp/src/utils/config.js 5 * @FilePath: /xyxBooking-weapp/src/utils/config.js
6 * @Description: 服务器环境配置 6 * @Description: 服务器环境配置
7 */ 7 */
8 // TAG:服务器环境配置 8 // TAG:服务器环境配置
9 // const isH5Dev = process.env.TARO_ENV === 'h5' && process.env.NODE_ENV === 'development'; 9 // const isH5Dev = process.env.TARO_ENV === 'h5' && process.env.NODE_ENV === 'development';
10 10
11 +/**
12 + * @description 接口基础域名
13 + * - 小程序端由 taro 构建时注入 NODE_ENV
14 + * - 线上/测试环境按需切换
15 + * @type {string}
16 + */
11 const BASE_URL = process.env.NODE_ENV === 'production' 17 const BASE_URL = process.env.NODE_ENV === 'production'
12 // ? 'https://oa.onwall.cn' 18 // ? 'https://oa.onwall.cn'
13 ? 'https://oa-dev.onwall.cn' 19 ? 'https://oa-dev.onwall.cn'
......
1 /* 1 /*
2 * @Date: 2022-10-13 22:36:08 2 * @Date: 2022-10-13 22:36:08
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2023-12-22 18:23:08 4 + * @LastEditTime: 2026-01-24 12:53:30
5 - * @FilePath: /meihuaApp/src/utils/mixin.js 5 + * @FilePath: /xyxBooking-weapp/src/utils/mixin.js
6 - * @Description: 文件描述 6 + * @Description: 全局 mixin(兼容保留)
7 */ 7 */
8 import { getSessionId, setSessionId, clearSessionId } from './request'; 8 import { getSessionId, setSessionId, clearSessionId } from './request';
9 9
10 /** 10 /**
11 - * 全局mixin,提供sessionid管理功能 11 + * @description 全局 mixin(兼容保留)
12 - * 注意:sessionid现在由request.js自动管理,无需手动设置 12 + * - 早期版本用于手动管理 sessionid
13 + * - 当前项目 sessionid 由 request.js 拦截器自动注入
14 + * - 该文件主要作为“旧代码兼容层”保留
13 */ 15 */
14 export default { 16 export default {
15 - // 初始化设置 17 + // 初始化入口(如需全局混入逻辑可写在这里)
16 init: { 18 init: {
17 created () { 19 created () {
18 - // sessionid现在由request.js的拦截器自动管理 20 + // 说明:sessionid 现在由 request.js 的拦截器自动管理
19 - // 这里可以添加其他初始化逻辑 21 + // 如需在组件创建时做通用初始化,可在此补充
20 } 22 }
21 } 23 }
22 }; 24 };
23 25
24 -// 导出sessionid管理工具函数,供其他组件使用 26 +/**
27 + * @description 导出 sessionid 管理工具(供极端场景手动处理)
28 + * - 正常业务不建议直接调用
29 + */
25 export { getSessionId, setSessionId, clearSessionId } 30 export { getSessionId, setSessionId, clearSessionId }
......
1 +/*
2 + * @Date: 2026-01-07 21:19:46
3 + * @LastEditors: hookehuyr hookehuyr@gmail.com
4 + * @LastEditTime: 2026-01-24 12:44:01
5 + * @FilePath: /xyxBooking-weapp/src/utils/polyfill.js
6 + * @Description: 文件描述
7 + */
8 +
9 +/**
10 + * @description 小程序环境 Polyfill:TextEncoder/TextDecoder
11 + * - 部分运行时(尤其是低版本基础库)可能缺少 TextEncoder/TextDecoder
12 + * - NFC/二维码等场景会用到 TextDecoder/TextEncoder(例如解析 NDEF)
13 + */
1 14
2 if (typeof TextEncoder === 'undefined') { 15 if (typeof TextEncoder === 'undefined') {
3 class TextEncoder { 16 class TextEncoder {
17 + /**
18 + * @description 将字符串编码为 UTF-8 字节数组
19 + * @param {string} str 待编码字符串
20 + * @returns {Uint8Array} UTF-8 字节数组
21 + */
4 encode(str) { 22 encode(str) {
5 const len = str.length; 23 const len = str.length;
6 const res = []; 24 const res = [];
...@@ -40,7 +58,14 @@ if (typeof TextEncoder === 'undefined') { ...@@ -40,7 +58,14 @@ if (typeof TextEncoder === 'undefined') {
40 58
41 if (typeof TextDecoder === 'undefined') { 59 if (typeof TextDecoder === 'undefined') {
42 class TextDecoder { 60 class TextDecoder {
61 + /**
62 + * @description 将字节数据解码为字符串(简化 UTF-8 解码)
63 + * @param {ArrayBuffer|Uint8Array} view 字节数据
64 + * @param {Object} options 预留参数(与标准接口对齐)
65 + * @returns {string} 解码后的字符串
66 + */
43 decode(view, options) { 67 decode(view, options) {
68 + void options;
44 if (!view) { 69 if (!view) {
45 return ''; 70 return '';
46 } 71 }
......
...@@ -21,10 +21,12 @@ import { parseQueryString } from './tools' ...@@ -21,10 +21,12 @@ import { parseQueryString } from './tools'
21 import BASE_URL, { REQUEST_DEFAULT_PARAMS } from './config'; 21 import BASE_URL, { REQUEST_DEFAULT_PARAMS } from './config';
22 22
23 /** 23 /**
24 - * 获取sessionid的工具函数 24 + * @description 获取 sessionid 的工具函数
25 + * - sessionid 由 authRedirect.refreshSession 写入
26 + * - 每次请求前动态读取,避免旧会话导致的 401
25 * @returns {string|null} sessionid或null 27 * @returns {string|null} sessionid或null
26 */ 28 */
27 -const getSessionId = () => { 29 +export const getSessionId = () => {
28 try { 30 try {
29 return Taro.getStorageSync("sessionid") || null; 31 return Taro.getStorageSync("sessionid") || null;
30 } catch (error) { 32 } catch (error) {
...@@ -33,12 +35,44 @@ const getSessionId = () => { ...@@ -33,12 +35,44 @@ const getSessionId = () => {
33 } 35 }
34 }; 36 };
35 37
38 +/**
39 + * @description 设置 sessionid(一般不需要手动调用)
40 + * - 正常情况下由 authRedirect.refreshSession 写入
41 + * - 保留该方法用于极端场景的手动修复/兼容旧逻辑
42 + * @param {string} sessionid cookie 字符串
43 + * @returns {void} 无返回值
44 + */
45 +export const setSessionId = (sessionid) => {
46 + try {
47 + if (!sessionid) return
48 + Taro.setStorageSync('sessionid', sessionid)
49 + } catch (error) {
50 + console.error('设置sessionid失败:', error)
51 + }
52 +}
53 +
54 +/**
55 + * @description 清空 sessionid(一般不需要手动调用)
56 + * @returns {void} 无返回值
57 + */
58 +export const clearSessionId = () => {
59 + try {
60 + Taro.removeStorageSync('sessionid')
61 + } catch (error) {
62 + console.error('清空sessionid失败:', error)
63 + }
64 +}
65 +
36 // const isPlainObject = (value) => { 66 // const isPlainObject = (value) => {
37 // if (value === null || typeof value !== 'object') return false 67 // if (value === null || typeof value !== 'object') return false
38 // return Object.prototype.toString.call(value) === '[object Object]' 68 // return Object.prototype.toString.call(value) === '[object Object]'
39 // } 69 // }
40 70
41 -// create an axios instance 71 +/**
72 + * @description axios 实例(axios-miniprogram)
73 + * - 统一 baseURL / timeout
74 + * - 通过拦截器处理:默认参数、cookie 注入、401 自动续期、弱网降级
75 + */
42 const service = axios.create({ 76 const service = axios.create({
43 baseURL: BASE_URL, // url = base url + request url 77 baseURL: BASE_URL, // url = base url + request url
44 // withCredentials: true, // send cookies when cross-domain requests 78 // withCredentials: true, // send cookies when cross-domain requests
...@@ -52,9 +86,9 @@ const service = axios.create({ ...@@ -52,9 +86,9 @@ const service = axios.create({
52 let has_shown_timeout_modal = false 86 let has_shown_timeout_modal = false
53 87
54 /** 88 /**
55 - * 判断是否为超时错误 89 + * @description 判断是否为超时错误
56 - * @param {Error} error - 请求错误对象 90 + * @param {Error} error 请求错误对象
57 - * @returns {boolean} 是否为超时错误 91 + * @returns {boolean} true=超时,false=非超时
58 */ 92 */
59 93
60 const is_timeout_error = (error) => { 94 const is_timeout_error = (error) => {
...@@ -64,9 +98,9 @@ const is_timeout_error = (error) => { ...@@ -64,9 +98,9 @@ const is_timeout_error = (error) => {
64 } 98 }
65 99
66 /** 100 /**
67 - * 判断是否为网络错误(断网/弱网/请求失败等) 101 + * @description 判断是否为网络错误(断网/弱网/请求失败等)
68 - * @param {Error} error - 请求错误对象 102 + * @param {Error} error 请求错误对象
69 - * @returns {boolean} 是否为网络错误 103 + * @returns {boolean} true=网络错误,false=非网络错误
70 */ 104 */
71 const is_network_error = (error) => { 105 const is_network_error = (error) => {
72 const msg = String(error?.message || error?.errMsg || '') 106 const msg = String(error?.message || error?.errMsg || '')
...@@ -89,11 +123,11 @@ const is_network_error = (error) => { ...@@ -89,11 +123,11 @@ const is_network_error = (error) => {
89 } 123 }
90 124
91 /** 125 /**
92 - * 是否需要触发弱网/断网降级逻辑 126 + * @description 是否需要触发弱网/断网降级逻辑
93 * - 超时:直接触发 127 * - 超时:直接触发
94 * - 网络错误:直接触发(避免 wifi 但无网场景漏判) 128 * - 网络错误:直接触发(避免 wifi 但无网场景漏判)
95 - * @param {Error} error - 请求错误对象 129 + * @param {Error} error 请求错误对象
96 - * @returns {Promise<boolean>} 是否需要触发降级 130 + * @returns {Promise<boolean>} true=需要降级,false=不需要
97 */ 131 */
98 const should_handle_bad_network = async (error) => { 132 const should_handle_bad_network = async (error) => {
99 if (is_timeout_error(error)) return true 133 if (is_timeout_error(error)) return true
...@@ -101,10 +135,10 @@ const should_handle_bad_network = async (error) => { ...@@ -101,10 +135,10 @@ const should_handle_bad_network = async (error) => {
101 } 135 }
102 136
103 /** 137 /**
104 - * 处理请求超时错误 138 + * @description 处理请求超时/弱网错误
105 - * - 显示超时提示 modal 139 + * - 优先:若存在离线预约记录缓存,直接跳转离线预约列表页
106 - * - 若有离线预约记录缓存,则跳转至离线预约列表页 140 + * - 否则:弹出弱网提示(统一文案由 uiText 管理)
107 - * - 否则提示用户检查网络连接 141 + * @returns {Promise<void>} 无返回值
108 */ 142 */
109 const handle_request_timeout = async () => { 143 const handle_request_timeout = async () => {
110 if (has_shown_timeout_modal) return 144 if (has_shown_timeout_modal) return
...@@ -133,7 +167,7 @@ const handle_request_timeout = async () => { ...@@ -133,7 +167,7 @@ const handle_request_timeout = async () => {
133 } 167 }
134 } 168 }
135 169
136 -// request interceptor 170 +// 请求拦截器:合并默认参数 / 注入 cookie
137 service.interceptors.request.use( 171 service.interceptors.request.use(
138 config => { 172 config => {
139 // console.warn(config) 173 // console.warn(config)
...@@ -155,11 +189,13 @@ service.interceptors.request.use( ...@@ -155,11 +189,13 @@ service.interceptors.request.use(
155 } 189 }
156 190
157 /** 191 /**
158 - * 动态获取sessionid并设置到请求头 192 + * 动态获取 sessionid 并设置到请求头
159 - * 确保每个请求都带上最新的sessionid 193 + * - 确保每个请求都带上最新的 sessionid
194 + * - 注意:axios-miniprogram 的 headers 可能不存在,需要先兜底
160 */ 195 */
161 const sessionid = getSessionId(); 196 const sessionid = getSessionId();
162 if (sessionid) { 197 if (sessionid) {
198 + config.headers = config.headers || {}
163 config.headers.cookie = sessionid; 199 config.headers.cookie = sessionid;
164 } 200 }
165 201
...@@ -187,13 +223,12 @@ service.interceptors.request.use( ...@@ -187,13 +223,12 @@ service.interceptors.request.use(
187 return config 223 return config
188 }, 224 },
189 error => { 225 error => {
190 - // do something with request error 226 + console.error('请求拦截器异常:', error)
191 - console.error(error, 'err') // for debug
192 return Promise.reject(error) 227 return Promise.reject(error)
193 } 228 }
194 ) 229 )
195 230
196 -// response interceptor 231 +// 响应拦截器:401 自动续期 / 弱网降级
197 service.interceptors.response.use( 232 service.interceptors.response.use(
198 /** 233 /**
199 * 响应拦截器说明 234 * 响应拦截器说明
......
...@@ -2,7 +2,16 @@ ...@@ -2,7 +2,16 @@
2 * 系统参数 2 * 系统参数
3 */ 3 */
4 4
5 -const DEFAULT_HOST_TYPE = ['首次参与', '老用户']; // 主办方默认用户类型 5 +/**
6 -const DEFAULT_HOST_STATUS = ['跟踪', '引导']; // 主办方默认用户状态 6 + * @description 主办方默认用户类型
7 + * @type {Array<string>}
8 + */
9 +const DEFAULT_HOST_TYPE = ['首次参与', '老用户'];
10 +
11 +/**
12 + * @description 主办方默认用户状态
13 + * @type {Array<string>}
14 + */
15 +const DEFAULT_HOST_STATUS = ['跟踪', '引导'];
7 16
8 export { DEFAULT_HOST_TYPE, DEFAULT_HOST_STATUS } 17 export { DEFAULT_HOST_TYPE, DEFAULT_HOST_STATUS }
......
...@@ -9,7 +9,11 @@ import dayjs from 'dayjs'; ...@@ -9,7 +9,11 @@ import dayjs from 'dayjs';
9 import Taro from '@tarojs/taro'; 9 import Taro from '@tarojs/taro';
10 import BASE_URL, { REQUEST_DEFAULT_PARAMS } from './config' 10 import BASE_URL, { REQUEST_DEFAULT_PARAMS } from './config'
11 11
12 -// 格式化时间 12 +/**
13 + * @description 格式化时间
14 + * @param {string|number|Date} date 时间入参
15 + * @returns {string} 格式化后的时间字符串(YYYY-MM-DD HH:mm)
16 + */
13 const formatDate = (date) => { 17 const formatDate = (date) => {
14 return dayjs(date).format('YYYY-MM-DD HH:mm'); 18 return dayjs(date).format('YYYY-MM-DD HH:mm');
15 }; 19 };
...@@ -22,7 +26,7 @@ const wxInfo = () => { ...@@ -22,7 +26,7 @@ const wxInfo = () => {
22 const info = Taro.getSystemInfoSync(); 26 const info = Taro.getSystemInfoSync();
23 const isAndroid = info.platform === 'android'; 27 const isAndroid = info.platform === 'android';
24 const isiOS = info.platform === 'ios'; 28 const isiOS = info.platform === 'ios';
25 - // 简单模拟 29 + // 说明:当前项目只用到 Android/iOS 区分;平板能力按需补充
26 return { 30 return {
27 isAndroid, 31 isAndroid,
28 isiOS, 32 isiOS,
...@@ -31,9 +35,9 @@ const wxInfo = () => { ...@@ -31,9 +35,9 @@ const wxInfo = () => {
31 }; 35 };
32 36
33 /** 37 /**
34 - * @description 解析URL参数 38 + * @description 解析 URL 参数
35 - * @param {*} url 39 + * @param {string} url 完整 URL 或带 query 的路径
36 - * @returns {Object} URL参数对象,键值对形式存储参数名和参数值 40 + * @returns {Object} URL 参数对象(键值对)
37 */ 41 */
38 const parseQueryString = url => { 42 const parseQueryString = url => {
39 if (!url) return {}; 43 if (!url) return {};
...@@ -47,10 +51,10 @@ const parseQueryString = url => { ...@@ -47,10 +51,10 @@ const parseQueryString = url => {
47 } 51 }
48 52
49 /** 53 /**
50 - * 字符串包含字符数组中字符的状态 54 + * @description 判断字符串是否包含数组中的任意子串
51 - * @param {*} array 字符数组 55 + * @param {Array<string>} array 子串数组
52 - * @param {*} str 字符串 56 + * @param {string} str 目标字符串
53 - * @returns 包含状态 57 + * @returns {boolean} true=包含任意一个子串,false=都不包含
54 */ 58 */
55 const strExist = (array, str) => { 59 const strExist = (array, str) => {
56 if (!str) return false; 60 if (!str) return false;
...@@ -114,13 +118,10 @@ const formatDatetime = (data) => { ...@@ -114,13 +118,10 @@ const formatDatetime = (data) => {
114 }; 118 };
115 119
116 /** 120 /**
117 - * @description 构建 API 请求 URL 121 + * @description 构建 API 请求 URL(带默认公共参数)
118 - * @param {string} action - API 动作名称(如 'get_user_info') 122 + * @param {string} action 接口动作名称(例如:openid_wxapp)
119 - * @param {Object} [params={}] - 请求参数对象,默认空对象 123 + * @param {Object} [params={}] 额外 query 参数
120 - * @returns {string} 完整的 API 请求 URL,包含基础路径、动作参数、默认参数和查询字符串 124 + * @returns {string} 完整请求 URL(BASE_URL + /srv/?a=...&f=...&client_name=...)
121 - * @example
122 - * buildApiUrl('get_user_info', { user_id: 123 });
123 - * 返回 "/srv/?a=get_user_info&f=json&client_name=xyxBooking&user_id=123"
124 */ 125 */
125 const buildApiUrl = (action, params = {}) => { 126 const buildApiUrl = (action, params = {}) => {
126 const queryParams = new URLSearchParams({ 127 const queryParams = new URLSearchParams({
......
...@@ -5,6 +5,10 @@ ...@@ -5,6 +5,10 @@
5 * @FilePath: /xyxBooking-weapp/src/utils/uiText.js 5 * @FilePath: /xyxBooking-weapp/src/utils/uiText.js
6 * @Description: 弱网络提示文本 6 * @Description: 弱网络提示文本
7 */ 7 */
8 +/**
9 + * @description 弱网/断网统一文案
10 + * - toast/modal/banner 等入口统一引用,避免多处硬编码
11 + */
8 export const weak_network_text = { 12 export const weak_network_text = {
9 title: '网络连接不畅', 13 title: '网络连接不畅',
10 toast_title: '网络连接不畅', 14 toast_title: '网络连接不畅',
......
1 -/*获取当前页url*/ 1 +/**
2 + * @description 获取当前页路由(不含 query)
3 + * @returns {string} 当前页 route,例如:pages/index/index
4 + */
2 const getCurrentPageUrl = () => { 5 const getCurrentPageUrl = () => {
3 - let pages = getCurrentPages() //获取加载的页面 6 + // 获取加载的页面栈
4 - let currentPage = pages[pages.length - 1] //获取当前页面的对象 7 + let pages = getCurrentPages()
5 - let url = currentPage.route //当前页面url 8 + // 获取当前页面对象
9 + let currentPage = pages[pages.length - 1]
10 + // 当前页面 route(不含 query)
11 + let url = currentPage.route
6 return url 12 return url
7 } 13 }
8 -/*获取当前页参数*/ 14 +/**
15 + * @description 获取当前页 query 参数
16 + * @returns {Object} 当前页 options(query 参数对象)
17 + */
9 const getCurrentPageParam = () => { 18 const getCurrentPageParam = () => {
10 - let pages = getCurrentPages() //获取加载的页面 19 + // 获取加载的页面栈
11 - let currentPage = pages[pages.length - 1] //获取当前页面的对象 20 + let pages = getCurrentPages()
12 - let options = currentPage.options //如果要获取url中所带的参数可以查看options 21 + // 获取当前页面对象
22 + let currentPage = pages[pages.length - 1]
23 + // 当前页面 query 参数对象
24 + let options = currentPage.options
13 return options 25 return options
14 } 26 }
15 27
......