hookehuyr

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

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