hookehuyr

chore: 补充 Taro 小程序开发配置体系并格式化代码

- 新增全局规则:
  - taro-patterns.md(Taro 开发规范)
  - miniprogram-checklist.md(小程序检查清单)
  - taro-cross-platform.md(多端兼容指南)

- 新增项目配置:
  - .eslintrc.js(ESLint 规则)
  - .prettierrc(Prettier 格式化)
  - .lintstagedrc.js(Git 暂存检查)
  - .editorconfig(编辑器统一配置)
  - scripts/setup-husky.sh(Husky 自动安装)

- 新增文档:
  - docs/development-guide.md(完整开发指南)
  - docs/config-quick-reference.md(配置快速参考)

- 更新 package.json:
  - 添加代码质量相关命令(lint、format等)

- 使用 Prettier 格式化所有代码

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Showing 84 changed files with 5400 additions and 3353 deletions
1 { 1 {
2 "permissions": { 2 "permissions": {
3 "allow": [ 3 "allow": [
4 - "Skill(glm-plan-usage:usage-query-skill)" 4 + "Skill(glm-plan-usage:usage-query-skill)",
5 + "Bash(pnpm add:*)",
6 + "Bash(chmod:*)",
7 + "Bash(bash:*)",
8 + "Bash(pnpm lint:no-fix:*)",
9 + "Bash(pnpm lint:*)",
10 + "Bash(pnpm list:*)",
11 + "Bash(pnpm format:*)",
12 + "Bash(git add:*)"
5 ] 13 ]
6 } 14 }
7 } 15 }
......
1 -# http://editorconfig.org 1 +# EditorConfig 配置
2 +# https://editorconfig.org
3 +
2 root = true 4 root = true
3 5
6 +# 所有文件
4 [*] 7 [*]
8 +charset = utf-8
5 indent_style = space 9 indent_style = space
6 indent_size = 2 10 indent_size = 2
7 -charset = utf-8 11 +end_of_line = lf
8 -trim_trailing_whitespace = true
9 insert_final_newline = true 12 insert_final_newline = true
13 +trim_trailing_whitespace = true
14 +
15 +# Vue 文件
16 +[*.{vue,js,jsx,ts,tsx}]
17 +indent_style = space
18 +indent_size = 2
19 +
20 +# Less/SCSS 文件
21 +[*.{less,scss,css}]
22 +indent_style = space
23 +indent_size = 2
10 24
25 +# JSON 文件
26 +[*.json]
27 +indent_style = space
28 +indent_size = 2
29 +
30 +# Markdown 文件
11 [*.md] 31 [*.md]
12 trim_trailing_whitespace = false 32 trim_trailing_whitespace = false
33 +indent_style = space
34 +indent_size = 2
35 +
36 +# 配置文件
37 +[*.{yml,yaml}]
38 +indent_style = space
39 +indent_size = 2
40 +
41 +# Shell 脚本
42 +[*.sh]
43 +end_of_line = lf
44 +
45 +# Windows 特定
46 +[*.{bat,cmd}]
47 +end_of_line = crlf
......
1 +module.exports = {
2 + root: true,
3 + env: {
4 + browser: true,
5 + node: true,
6 + es2021: true
7 + },
8 + extends: [
9 + 'eslint:recommended'
10 + // 暂时不使用 '@vue/eslint-config-prettier',手动配置规则避免冲突
11 + ],
12 + plugins: ['vue'],
13 + parser: 'vue-eslint-parser', // 使用 Vue parser
14 + parserOptions: {
15 + parser: 'espree', // JavaScript parser
16 + ecmaVersion: 2021,
17 + sourceType: 'module',
18 + ecmaFeatures: {
19 + jsx: true
20 + }
21 + },
22 + rules: {
23 + // Vue 规则(手动配置)
24 + 'vue/multi-word-component-names': 'off', // 允许单词组件名
25 + 'vue/no-v-html': 'warn', // 警告使用 v-html(XSS 风险)
26 + 'vue/require-default-prop': 'off', // 不强制 prop 默认值
27 + 'vue/require-prop-types': 'off', // 不强制 prop 类型(使用 JSDoc)
28 + 'vue/no-unused-vars': 'warn', // 警告未使用的变量
29 + 'vue/no-unused-components': 'warn', // 警告未使用的组件
30 +
31 + // 通用规则
32 + 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
33 + 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
34 + 'no-unused-vars': ['warn', { argsIgnorePattern: '^_' }], // 警告未使用的变量
35 + 'prefer-const': 'error', // 优先使用 const
36 + 'no-var': 'error', // 禁止使用 var
37 + 'eqeqeq': ['error', 'always'], // 强制使用 === 和 !==
38 + 'curly': ['error', 'all'], // 强制使用大括号
39 + 'brace-style': ['error', '1tbs'], // 大括号风格
40 + 'quotes': ['error', 'single', { avoidEscape: true }], // 单引号
41 + 'semi': ['error', 'never'], // 不使用分号
42 + 'comma-dangle': ['error', 'never'], // 不允许尾随逗号
43 + 'arrow-parens': ['error', 'as-needed'], // 箭头函数参数括号
44 + 'object-curly-spacing': ['error', 'always'], // 对象大括号空格
45 + 'array-bracket-spacing': ['error', 'never'], // 数组方括号无空格
46 +
47 + // 代码质量
48 + 'no-duplicate-imports': 'error', // 禁止重复导入
49 + 'no-useless-return': 'error', // 禁止无用的 return
50 + 'no-else-return': 'error', // 禁止 else return(提前 return)
51 + 'prefer-template': 'error', // 优先使用模板字符串
52 + 'template-curly-spacing': 'error', // 模板字符串大括号内无空格
53 + 'object-shorthand': ['error', 'always'], // 对象属性简写
54 +
55 + // 放宽一些规则,避免过多警告
56 + 'no-prototype-builtins': 'off', // 允许使用原型方法
57 + 'no-nested-ternary': 'off', // 允许嵌套三元表达式
58 + 'no-param-reassign': 'off', // 允许修改参数
59 + 'consistent-return': 'off' // 不要求一致的 return
60 + },
61 + globals: {
62 + // Taro 全局变量
63 + wx: 'readonly',
64 + getCurrentPages: 'readonly',
65 + getApp: 'readonly',
66 +
67 + // NutUI 全局变量
68 + NutUI: 'readonly'
69 + },
70 + overrides: [
71 + // 测试文件规则
72 + {
73 + files: ['**/__tests__/**/*', '**/*.test.js', '**/*.spec.js'],
74 + env: {
75 + jest: true,
76 + node: true
77 + },
78 + rules: {
79 + 'no-console': 'off'
80 + }
81 + }
82 + ]
83 +}
1 +#!/bin/sh
2 +. "$(dirname "$0")/_/husky.sh"
3 +
4 +# 验证 commit 信息格式(可选)
5 +# commit_regex='^(feat|fix|docs|style|refactor|test|chore|build|ci|perf|revert)(\(.+\))?: .{1,50}'
6 +#
7 +#if ! grep -qE "$commit_regex" "$1"; then
8 +# echo "❌ Commit 信息格式不正确"
9 +# echo "✅ 正确格式: type(scope): subject"
10 +# echo "📝 类型: feat, fix, docs, style, refactor, test, chore, etc."
11 +# exit 1
12 +#fi
13 +
14 +echo "✅ Commit 信息验证通过"
1 +#!/bin/sh
2 +. "$(dirname "$0")/_/husky.sh"
3 +
4 +echo "🔍 运行代码检查..."
5 +pnpm lint-staged
1 +module.exports = {
2 + // Vue 文件
3 + '*.{vue,js,jsx,ts,tsx}': [
4 + 'eslint --fix',
5 + 'prettier --write'
6 + ],
7 +
8 + // 样式文件
9 + '*.{less,scss,css}': [
10 + 'prettier --write'
11 + ],
12 +
13 + // JSON/配置文件
14 + '*.{json,md,yml,yaml}': [
15 + 'prettier --write'
16 + ]
17 +}
1 +# 忽略构建输出
2 +dist/
3 +build/
4 +
5 +# 忽略依赖
6 +node_modules/
7 +
8 +# 忽略配置文件
9 +.prettierignore
10 +.eslintrc.js
11 +
12 +# 忽略锁文件
13 +package-lock.json
14 +pnpm-lock.yaml
15 +yarn.lock
16 +
17 +# 忽略日志
18 +*.log
19 +logs/
20 +
21 +# 忽略临时文件
22 +*.tmp
23 +.cache/
24 +
25 +# 忽略覆盖率报告
26 +coverage/
27 +
28 +# 忽略 Taro 配置
29 +config/prod.config.js
30 +config/dev.config.js
31 +config/index.js
32 +
33 +# 忽略静态资源
34 +src/assets/fonts/
35 +src/assets/images/
36 +
37 +# 忽略特定文件
38 +miniprogram_npm/
1 +{
2 + "semi": false,
3 + "singleQuote": true,
4 + "quoteProps": "as-needed",
5 + "trailingComma": "none",
6 + "bracketSpacing": true,
7 + "bracketSameLine": false,
8 + "arrowParens": "avoid",
9 + "printWidth": 100,
10 + "tabWidth": 2,
11 + "useTabs": false,
12 + "endOfLine": "lf",
13 + "vueIndentScriptAndStyle": false,
14 + "singleAttributePerLine": false,
15 + "overrides": [
16 + {
17 + "files": "*.vue",
18 + "options": {
19 + "parser": "vue"
20 + }
21 + },
22 + {
23 + "files": "*.less",
24 + "options": {
25 + "parser": "less"
26 + }
27 + }
28 + ]
29 +}
...@@ -23,12 +23,70 @@ ...@@ -23,12 +23,70 @@
23 ## 开发命令 23 ## 开发命令
24 24
25 ```bash 25 ```bash
26 +# 安装依赖
26 pnpm install 27 pnpm install
27 -pnpm dev:weapp 28 +
28 -pnpm build:weapp 29 +# 开发模式
29 -pnpm lint 30 +pnpm dev:weapp # 微信小程序
31 +pnpm dev:h5 # H5 端
32 +
33 +# 构建
34 +pnpm build:weapp # 微信小程序生产包
35 +pnpm build:h5 # H5 端生产包
36 +
37 +# 代码质量
38 +pnpm lint # ESLint 检查并修复
39 +pnpm format # Prettier 格式化
40 +pnpm lint:no-fix # 仅检查不修复
41 +```
42 +
43 +## 代码质量保障
44 +
45 +项目已配置完整的代码质量保障体系:
46 +
47 +### ✅ 已配置工具
48 +
49 +- **ESLint**:代码风格检查和潜在错误检测
50 +- **Prettier**:代码格式化统一
51 +- **EditorConfig**:编辑器配置统一
52 +- **Husky**:Git Hooks 自动化
53 +- **lint-staged**:提交前仅检查本次修改文件
54 +
55 +### 📋 初始配置步骤
56 +
57 +```bash
58 +# 1. 安装代码质量相关依赖
59 +pnpm add -D eslint prettier eslint-plugin-vue husky lint-staged
60 +
61 +# 2. 初始化 Husky(自动设置 Git Hooks)
62 +bash scripts/setup-husky.sh
63 +
64 +# 3. 提交代码时会自动运行检查
65 +git add .
66 +git commit -m "feat: 添加新功能"
30 ``` 67 ```
31 68
69 +### 📚 详细配置文档
70 +
71 +查看 `docs/development-guide.md` 获取完整的配置使用说明。
72 +
73 +### 🎯 Claude Code 开发配置
74 +
75 +项目配置了完善的 Claude Code 全局规则,支持:
76 +- Vue 3 最佳实践
77 +- Taro 开发规范
78 +- 小程序特性适配
79 +- 多端兼容处理
80 +- 代码审查清单
81 +
82 +全局规则位于 `~/.claude/rules/`
83 +- `taro-patterns.md` - Taro 开发规范
84 +- `miniprogram-checklist.md` - 小程序检查清单
85 +- `taro-cross-platform.md` - 多端兼容指南
86 +- `vue-patterns.md` - Vue 3 最佳实践
87 +- `frontend-testing.md` - 前端测试指南
88 +- `code-review.md` - 代码审查清单
89 +
32 ## 项目结构 90 ## 项目结构
33 91
34 ```text 92 ```text
......
1 +# 配置快速参考
2 +
3 +## 🚀 快速开始
4 +
5 +### 1. 安装依赖
6 +
7 +```bash
8 +# 安装项目依赖
9 +pnpm install
10 +
11 +# 安装代码质量工具(首次)
12 +pnpm add -D eslint prettier eslint-plugin-vue husky lint-staged @vue/eslint-config-prettier
13 +
14 +# 初始化 Git Hooks
15 +bash scripts/setup-husky.sh
16 +```
17 +
18 +### 2. 日常开发
19 +
20 +```bash
21 +# 启动开发服务器
22 +pnpm dev:weapp
23 +
24 +# 代码检查与格式化
25 +pnpm lint # ESLint 检查并修复
26 +pnpm format # Prettier 格式化
27 +
28 +# 提交代码(自动检查)
29 +git add .
30 +git commit -m "feat: 添加新功能"
31 +```
32 +
33 +## 📁 配置文件说明
34 +
35 +### 项目配置
36 +
37 +| 文件 | 用途 |
38 +|------|------|
39 +| `.eslintrc.js` | ESLint 规则配置 |
40 +| `.prettierrc` | Prettier 格式化配置 |
41 +| `.editorconfig` | 编辑器统一配置 |
42 +| `.lintstagedrc.js` | Git 暂存文件检查配置 |
43 +| `.husky/pre-commit` | Git 提交前钩子 |
44 +
45 +### 全局配置(`~/.claude/rules/`)
46 +
47 +| 文件 | 用途 |
48 +|------|------|
49 +| `taro-patterns.md` | Taro 开发规范 |
50 +| `miniprogram-checklist.md` | 小程序检查清单 |
51 +| `taro-cross-platform.md` | 多端兼容指南 |
52 +| `vue-patterns.md` | Vue 3 最佳实践 |
53 +| `frontend-testing.md` | 前端测试指南 |
54 +| `code-review.md` | 代码审查清单 |
55 +| `tailwindcss-guide.md` | TailwindCSS 使用规范 |
56 +| `frontend-performance.md` | 前端性能优化 |
57 +
58 +## 🎯 常用命令
59 +
60 +### 开发命令
61 +
62 +```bash
63 +pnpm dev:weapp # 微信小程序开发模式
64 +pnpm dev:h5 # H5 端开发模式
65 +pnpm build:weapp # 微信小程序构建
66 +pnpm build:h5 # H5 端构建
67 +```
68 +
69 +### 代码质量命令
70 +
71 +```bash
72 +pnpm lint # ESLint 检查并修复
73 +pnpm lint:no-fix # ESLint 仅检查
74 +pnpm format # Prettier 格式化
75 +pnpm format:check # Prettier 检查
76 +```
77 +
78 +### Git 命令
79 +
80 +```bash
81 +git add . # 添加所有文件
82 +git commit -m "feat: xxx" # 提交(自动运行检查)
83 +git commit --no-verify -m "xxx" # 跳过检查(不推荐)
84 +```
85 +
86 +## 📝 Commit 规范
87 +
88 +推荐使用 Conventional Commits 格式:
89 +
90 +```
91 +feat: 新功能
92 +fix: 修复 bug
93 +docs: 文档更新
94 +style: 代码格式
95 +refactor: 重构
96 +test: 测试相关
97 +chore: 构建过程或辅助工具
98 +```
99 +
100 +### 示例
101 +
102 +```bash
103 +git commit -m "feat(booking): 添加预约日期选择功能"
104 +git commit -m "fix(auth): 修复登录时 token 未持久化的问题"
105 +git commit -m "docs: 更新开发文档"
106 +```
107 +
108 +## ⚙️ VS Code 配置
109 +
110 +### 推荐插件
111 +
112 +```bash
113 +# 安装推荐插件
114 +code --install-extension dbaeumer.vscode-eslint
115 +code --install-extension esbenp.prettier-vscode
116 +code --install-extension EditorConfig.EditorConfig
117 +code --install-extension Vue.volar
118 +code --install-extension taro.vscode-tarojs
119 +```
120 +
121 +### 工作区配置
122 +
123 +创建 `.vscode/settings.json`
124 +
125 +```json
126 +{
127 + "editor.formatOnSave": false,
128 + "editor.codeActionsOnSave": {
129 + "source.fixAll.eslint": true
130 + },
131 + "[javascript]": {
132 + "editor.defaultFormatter": "esbenp.prettier-vscode"
133 + },
134 + "[vue]": {
135 + "editor.defaultFormatter": "esbenp.prettier-vscode"
136 + },
137 + "files.eol": "\n",
138 + "files.trimTrailingWhitespace": true
139 +}
140 +```
141 +
142 +## 🔍 常见问题
143 +
144 +### Q: Husky 钩子不生效?
145 +
146 +```bash
147 +# 重新初始化 Husky
148 +npx husky install
149 +npx husky add .husky/pre-commit "pnpm lint-staged"
150 +chmod +x .husky/pre-commit
151 +```
152 +
153 +### Q: ESLint 和 Prettier 冲突?
154 +
155 +```bash
156 +# 安装 @vue/eslint-config-prettier
157 +pnpm add -D @vue/eslint-config-prettier
158 +
159 +# 在 .eslintrc.js 的 extends 最后添加
160 +'@vue/eslint-config-prettier'
161 +```
162 +
163 +### Q: 想跳过检查临时提交?
164 +
165 +```bash
166 +# ⚠️ 不推荐,仅用于紧急情况
167 +git commit --no-verify -m "feat: 临时提交"
168 +```
169 +
170 +## 📋 代码审查清单
171 +
172 +提交代码前检查:
173 +
174 +- [ ] 代码已通过 `pnpm lint`
175 +- [ ] 代码已通过 `pnpm format`
176 +- [ ]`console.log``debugger`
177 +- [ ] 无注释掉的代码
178 +- [ ] 代码有必要的注释
179 +- [ ] 功能测试通过
180 +- [ ] 文档已更新(如需要)
181 +
182 +## 🎨 代码风格
183 +
184 +### Vue 组件
185 +
186 +```vue
187 +<script setup>
188 +// 1. 导入
189 +import { ref, computed } from 'vue'
190 +
191 +// 2. Props/Emits
192 +const props = defineProps({})
193 +const emit = defineEmits({})
194 +
195 +// 3. 响应式状态
196 +const count = ref(0)
197 +
198 +// 4. Computed
199 +const double = computed(() => count.value * 2)
200 +
201 +// 5. 方法
202 +const increment = () => { count.value++ }
203 +
204 +// 6. 生命周期
205 +onMounted(() => { init() })
206 +
207 +// 7. Watch
208 +watch(count, (val) => { track(val) })
209 +</script>
210 +```
211 +
212 +### 命名规范
213 +
214 +```javascript
215 +// 组件:PascalCase
216 +UserCard.vue
217 +BookingList.vue
218 +
219 +// 函数/变量:camelCase
220 +getUserInfo
221 +handleSubmit
222 +
223 +// 常量:UPPER_SNAKE_CASE
224 +API_BASE_URL
225 +MAX_RETRY_COUNT
226 +
227 +// 文件夹:kebab-case
228 +use-offline-booking.js
229 +auth-redirect.js
230 +```
231 +
232 +## 🔐 安全检查
233 +
234 +提交前确认:
235 +
236 +- [ ] 无硬编码密钥/Token
237 +- [ ] 用户输入已验证
238 +- [ ] API 错误信息不泄露敏感数据
239 +- [ ] XSS 防护(避免 `v-html` 或净化)
240 +- [ ] 敏感数据不存储在 localStorage
241 +
242 +## 🚀 性能检查
243 +
244 +部署前确认:
245 +
246 +- [ ] 主包体积 < 2MB
247 +- [ ] 单个分包 < 2MB
248 +- [ ] 首屏渲染 < 2s
249 +- [ ] 图片已优化(CDN 参数)
250 +- [ ] 长列表使用虚拟滚动
251 +- [ ] 路由懒加载已配置
252 +
253 +## 📚 参考资源
254 +
255 +- [完整开发指南](./development-guide.md)
256 +- [项目 CLAUDE.md](../CLAUDE.md)
257 +- [Taro 官方文档](https://docs.taro.zone/)
258 +- [Vue 3 官方文档](https://cn.vuejs.org/)
259 +- [NutUI 官方文档](https://nutui.jd.com/4/taro/)
260 +
261 +## 🎯 下一步
262 +
263 +1. ✅ 安装依赖并初始化 Husky
264 +2. ✅ 配置 VS Code
265 +3. ✅ 运行 `pnpm dev:weapp` 启动开发
266 +4. ✅ 开始编码,享受自动代码检查
267 +5. ✅ 查看全局配置了解最佳实践
268 +
269 +祝开发愉快!🎉
1 +# Taro 小程序开发配置指南
2 +
3 +## 配置概览
4 +
5 +项目已配置完整的代码质量保障体系:
6 +
7 +### ✅ 已创建的配置文件
8 +
9 +#### 1. ESLint 配置(`.eslintrc.js`)
10 +- Vue 3 推荐规则
11 +- Taro 小程序适配规则
12 +- 代码风格检查
13 +- 潜在错误检测
14 +
15 +#### 2. Prettier 配置(`.prettierrc`)
16 +- 代码格式化规则
17 +- 统一代码风格
18 +- 与 ESLint 无冲突集成
19 +
20 +#### 3. EditorConfig 配置(`.editorconfig`)
21 +- 编辑器统一配置
22 +- 缩进、换行符等
23 +
24 +#### 4. lint-staged 配置(`.lintstagedrc.js`)
25 +- Git 暂存文件检查
26 +- 仅检查本次修改的文件
27 +
28 +#### 5. Husky 配置(`scripts/setup-husky.sh`)
29 +- Git Hooks 自动化
30 +- 提交前自动检查
31 +
32 +## 快速开始
33 +
34 +### 步骤 1:安装依赖
35 +
36 +```bash
37 +# 安装 ESLint 相关依赖
38 +pnpm add -D eslint prettier eslint-plugin-vue @vue/eslint-config-prettier
39 +
40 +# 安装 Husky 和 lint-staged
41 +pnpm add -D husky lint-staged
42 +```
43 +
44 +### 步骤 2:初始化 Husky
45 +
46 +```bash
47 +# 方式 1:使用自动安装脚本
48 +bash scripts/setup-husky.sh
49 +
50 +# 方式 2:手动安装
51 +npx husky install
52 +npx husky add .husky/pre-commit "pnpm lint-staged"
53 +npx husky add .husky/commit-msg "npx commitlint --edit \$1"
54 +```
55 +
56 +### 步骤 3:更新 package.json
57 +
58 +`package.json``scripts` 中添加:
59 +
60 +```json
61 +{
62 + "scripts": {
63 + "dev:weapp": "npm run build:weapp -- --watch",
64 + "build:weapp": "taro build --type weapp",
65 + "lint": "eslint \"src/**/*.{js,jsx,vue,ts,tsx}\" --fix",
66 + "lint:no-fix": "eslint \"src/**/*.{js,jsx,vue,ts,tsx}\"",
67 + "format": "prettier --write \"src/**/*.{js,jsx,vue,ts,tsx,less,css,json,md}\"",
68 + "format:check": "prettier --check \"src/**/*.{js,jsx,vue,ts,tsx,less,css,json,md}\"",
69 + "prepare": "husky install"
70 + }
71 +}
72 +```
73 +
74 +## 使用说明
75 +
76 +### 日常开发
77 +
78 +#### 1. 开发前检查
79 +
80 +```bash
81 +# 启动开发服务器
82 +pnpm dev:weapp
83 +
84 +# 代码会自动检查,但有错误时不会阻止编译
85 +```
86 +
87 +#### 2. 提交代码
88 +
89 +```bash
90 +# 添加文件到暂存区
91 +git add .
92 +
93 +# 提交代码(会自动运行 lint-staged)
94 +git commit -m "feat: 添加新功能"
95 +
96 +# 如果检查失败,修复后再次提交
97 +git add .
98 +git commit -m "feat: 添加新功能"
99 +```
100 +
101 +#### 3. 手动检查代码
102 +
103 +```bash
104 +# 检查并自动修复
105 +pnpm lint
106 +
107 +# 仅检查不修复
108 +pnpm lint:no-fix
109 +
110 +# 格式化代码
111 +pnpm format
112 +
113 +# 检查代码格式
114 +pnpm format:check
115 +```
116 +
117 +### 跳过检查(不推荐)
118 +
119 +```bash
120 +# 跳过 lint-staged 检查
121 +git commit --no-verify -m "feat: 临时提交"
122 +
123 +# ⚠️ 注意:仅用于紧急情况,平时不要使用
124 +```
125 +
126 +## 编辑器集成
127 +
128 +### VS Code
129 +
130 +#### 1. 安装插件
131 +
132 +推荐安装以下 VS Code 插件:
133 +
134 +```json
135 +{
136 + "recommendations": [
137 + "dbaeumer.vscode-eslint", // ESLint
138 + "esbenp.prettier-vscode", // Prettier
139 + "EditorConfig.EditorConfig", // EditorConfig
140 + "Vue.volar", // Vue 语言支持
141 + "taro.vscode-tarojs" // Taro 开发工具
142 + ]
143 +}
144 +```
145 +
146 +#### 2. 配置 VS Code
147 +
148 +创建 `.vscode/settings.json`
149 +
150 +```json
151 +{
152 + // ESLint
153 + "editor.formatOnSave": false,
154 + "editor.codeActionsOnSave": {
155 + "source.fixAll.eslint": true
156 + },
157 +
158 + // Prettier
159 + "[javascript]": {
160 + "editor.defaultFormatter": "esbenp.prettier-vscode"
161 + },
162 + "[vue]": {
163 + "editor.defaultFormatter": "esbenp.prettier-vscode"
164 + },
165 + "[json]": {
166 + "editor.defaultFormatter": "esbenp.prettier-vscode"
167 + },
168 +
169 + // Vue
170 + "volar.autoCompleteRefs": true,
171 + "volar.codeLens.pugTools": false,
172 + "volar.completion.autoImportComponent": true,
173 +
174 + // 文件
175 + "files.eol": "\n",
176 + "files.trimTrailingWhitespace": true,
177 + "files.insertFinalNewline": true
178 +}
179 +```
180 +
181 +#### 3. 配置 VS Code 任务
182 +
183 +创建 `.vscode/tasks.json`
184 +
185 +```json
186 +{
187 + "version": "2.0.0",
188 + "tasks": [
189 + {
190 + "label": "Lint: 检查并修复",
191 + "type": "npm",
192 + "script": "lint",
193 + "problemMatcher": []
194 + },
195 + {
196 + "label": "Format: 格式化代码",
197 + "type": "npm",
198 + "script": "format",
199 + "problemMatcher": []
200 + },
201 + {
202 + "label": "Dev: 启动微信小程序",
203 + "type": "npm",
204 + "script": "dev:weapp",
205 + "problemMatcher": []
206 + }
207 + ]
208 +}
209 +```
210 +
211 +### WebStorm / IntelliJ IDEA
212 +
213 +1. **启用 ESLint**
214 + - Settings → Languages & Frameworks → JavaScript → Code Quality Tools → ESLint
215 + - 选择 "Automatic ESLint configuration"
216 + - 勾选 "Run eslint --fix on save"
217 +
218 +2. **启用 Prettier**
219 + - Settings → Languages & Frameworks → JavaScript → Prettier
220 + - 选择 "Run on save for files"
221 + - 勾选 "On code reformat"
222 +
223 +3. **启用 EditorConfig**
224 + - Settings → Editor → Code Style → EditorConfig
225 + - 勾选 "Enable EditorConfig support"
226 +
227 +## 配置详解
228 +
229 +### ESLint 规则说明
230 +
231 +#### Vue 相关规则
232 +
233 +```javascript
234 +'vue/multi-word-component-names': 'off', // 允许单词组件名(如 Home.vue)
235 +'vue/no-v-html': 'warn', // 警告使用 v-html(XSS 风险)
236 +'vue/require-default-prop': 'off', // 不强制 prop 默认值
237 +'vue/no-unused-vars': 'warn', // 警告未使用的变量
238 +```
239 +
240 +#### 通用规则
241 +
242 +```javascript
243 +'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', // 生产环境警告 console
244 +'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', // 生产环境禁止 debugger
245 +'prefer-const': 'error', // 优先使用 const
246 +'no-var': 'error', // 禁止使用 var
247 +'eqeqeq': ['error', 'always'], // 强制使用 ===
248 +'semi': ['error', 'never'], // 不使用分号
249 +'quotes': ['error', 'single'], // 使用单引号
250 +```
251 +
252 +### Prettier 规则说明
253 +
254 +```json
255 +{
256 + "semi": false, // 不使用分号
257 + "singleQuote": true, // 使用单引号
258 + "trailingComma": "none", // 不使用尾随逗号
259 + "printWidth": 100, // 每行最大 100 字符
260 + "tabWidth": 2, // 缩进 2 空格
261 + "endOfLine": "lf" // 使用 LF 换行符
262 +}
263 +```
264 +
265 +## 常见问题
266 +
267 +### Q1: Husky 钩子不生效
268 +
269 +**问题**:提交代码时没有自动运行检查
270 +
271 +**解决方案**
272 +
273 +```bash
274 +# 1. 检查 Husky 是否安装
275 +ls -la .husky/
276 +
277 +# 2. 重新安装 Husky
278 +pnpm add -D husky
279 +npx husky install
280 +
281 +# 3. 添加 pre-commit 钩子
282 +npx husky add .husky/pre-commit "pnpm lint-staged"
283 +
284 +# 4. 检查钩子文件
285 +cat .husky/pre-commit
286 +```
287 +
288 +### Q2: ESLint 和 Prettier 冲突
289 +
290 +**问题**:ESLint 和 Prettier 对同一处代码有不同规则
291 +
292 +**解决方案**
293 +
294 +```bash
295 +# 安装 @vue/eslint-config-prettier
296 +pnpm add -D @vue/eslint-config-prettier
297 +
298 +# 在 .eslintrc.js 中添加
299 +{
300 + "extends": [
301 + 'plugin:vue/vue3-recommended',
302 + '@vue/eslint-config-prettier' // 放在最后
303 + ]
304 +}
305 +```
306 +
307 +### Q3: lint-staged 检查所有文件
308 +
309 +**问题**:每次提交都检查所有文件,很慢
310 +
311 +**解决方案**
312 +
313 +```bash
314 +# 确保 lint-staged 配置正确
315 +cat .lintstagedrc.js
316 +
317 +# 应该配置为:
318 +{
319 + "*.{js,jsx,vue}": ["eslint --fix", "prettier --write"]
320 +}
321 +```
322 +
323 +### Q4: 提交被阻止,但不想修复
324 +
325 +**问题**:代码有问题但想临时提交
326 +
327 +**解决方案**
328 +
329 +```bash
330 +# ⚠️ 不推荐,仅用于紧急情况
331 +git commit --no-verify -m "feat: 临时提交"
332 +```
333 +
334 +### Q5: Taro API 报 ESLint 错误
335 +
336 +**问题**:使用了 `wx` 全局变量报错
337 +
338 +**解决方案**
339 +
340 +```javascript
341 +// .eslintrc.js 中已配置
342 +globals: {
343 + wx: 'readonly',
344 + getCurrentPages: 'readonly',
345 + getApp: 'readonly'
346 +}
347 +```
348 +
349 +## 最佳实践
350 +
351 +### 1. 提交前自检
352 +
353 +```bash
354 +# 1. 拉取最新代码
355 +git pull origin develop
356 +
357 +# 2. 运行检查
358 +pnpm lint
359 +pnpm format:check
360 +
361 +# 3. 运行测试(如果有)
362 +pnpm test
363 +
364 +# 4. 提交代码
365 +git add .
366 +git commit -m "feat: 添加新功能"
367 +```
368 +
369 +### 2. Commit 信息规范
370 +
371 +推荐使用 Conventional Commits 格式:
372 +
373 +```
374 +feat: 新功能
375 +fix: 修复 bug
376 +docs: 文档更新
377 +style: 代码格式(不影响代码运行的变动)
378 +refactor: 重构(既不是新增功能,也不是修改 bug 的代码变动)
379 +test: 测试相关
380 +chore: 构建过程或辅助工具的变动
381 +```
382 +
383 +示例:
384 +
385 +```bash
386 +git commit -m "feat(booking): 添加预约日期选择功能"
387 +git commit -m "fix(auth): 修复登录时 token 未持久化的问题"
388 +git commit -m "docs: 更新开发文档"
389 +```
390 +
391 +### 3. 代码审查清单
392 +
393 +在提交 PR 前检查:
394 +
395 +- [ ] 代码已通过 ESLint 检查
396 +- [ ] 代码已通过 Prettier 格式化
397 +- [ ]`console.log``debugger`
398 +- [ ] 无注释掉的代码
399 +- [ ] 代码有必要的注释
400 +- [ ] 测试已通过(如果有)
401 +- [ ] 文档已更新(如需要)
402 +
403 +## 团队协作
404 +
405 +### 统一开发环境
406 +
407 +确保团队成员使用相同的配置:
408 +
409 +```bash
410 +# 1. 克隆项目
411 +git clone <repo-url>
412 +
413 +# 2. 安装依赖
414 +pnpm install
415 +
416 +# 3. 初始化 Husky
417 +pnpm prepare
418 +
419 +# 4. 安装 VS Code 插件
420 +code --install-extension dbaeumer.vscode-eslint
421 +code --install-extension esbenp.prettier-vscode
422 +code --install-extension Vue.volar
423 +```
424 +
425 +### 代码审查流程
426 +
427 +1. **开发者**:提交代码前运行 `pnpm lint`
428 +2. **Husky**:自动运行 `lint-staged`
429 +3. **CI/CD**:运行完整的 `pnpm lint``pnpm test`
430 +4. **审查者**:检查代码质量和规范
431 +5. **合并**:通过所有检查后合并
432 +
433 +## 持续改进
434 +
435 +### 定期更新依赖
436 +
437 +```bash
438 +# 检查过时的依赖
439 +pnpm outdated
440 +
441 +# 更新依赖
442 +pnpm update
443 +
444 +# 更新主要版本
445 +pnpm upgrade --latest
446 +```
447 +
448 +### 自定义规则
449 +
450 +根据团队需求调整规则:
451 +
452 +```javascript
453 +// .eslintrc.js
454 +rules: {
455 + // 添加团队特定规则
456 + 'custom-rule-name': 'error'
457 +}
458 +```
459 +
460 +## 参考资源
461 +
462 +- [ESlint 官方文档](https://eslint.org/)
463 +- [Prettier 官方文档](https://prettier.io/)
464 +- [EditorConfig 官方文档](https://editorconfig.org/)
465 +- [Husky 官方文档](https://typicode.github.io/husky/)
466 +- [lint-staged 官方文档](https://github.com/okonet/lint-staged)
467 +- [Vue ESLint 官方插件](https://eslint.vuejs.org/)
468 +- [Conventional Commits](https://www.conventionalcommits.org/)
...@@ -27,7 +27,11 @@ ...@@ -27,7 +27,11 @@
27 "dev:qq": "NODE_ENV=development taro build --type qq --watch", 27 "dev:qq": "NODE_ENV=development taro build --type qq --watch",
28 "dev:quickapp": "NODE_ENV=development taro build --type quickapp --watch", 28 "dev:quickapp": "NODE_ENV=development taro build --type quickapp --watch",
29 "postinstall": "weapp-tw patch", 29 "postinstall": "weapp-tw patch",
30 - "lint": "eslint --ext .js,.vue src" 30 + "lint": "eslint \"src/**/*.{js,jsx,vue}\" --fix",
31 + "lint:no-fix": "eslint \"src/**/*.{js,jsx,vue}\"",
32 + "format": "prettier --write \"src/**/*.{js,jsx,vue,less,css,json,md}\"",
33 + "format:check": "prettier --check \"src/**/*.{js,jsx,vue,less,css,json,md}\"",
34 + "prepare": "husky install"
31 }, 35 },
32 "browserslist": [ 36 "browserslist": [
33 "last 3 versions", 37 "last 3 versions",
...@@ -72,17 +76,23 @@ ...@@ -72,17 +76,23 @@
72 "@types/webpack-env": "^1.13.6", 76 "@types/webpack-env": "^1.13.6",
73 "@vue/babel-plugin-jsx": "^1.0.6", 77 "@vue/babel-plugin-jsx": "^1.0.6",
74 "@vue/compiler-sfc": "^3.0.0", 78 "@vue/compiler-sfc": "^3.0.0",
79 + "@vue/eslint-config-prettier": "^10.2.0",
75 "autoprefixer": "^10.4.21", 80 "autoprefixer": "^10.4.21",
76 "babel-preset-taro": "4.1.9", 81 "babel-preset-taro": "4.1.9",
77 "css-loader": "3.4.2", 82 "css-loader": "3.4.2",
78 "eslint": "^8.12.0", 83 "eslint": "^8.12.0",
79 "eslint-config-taro": "4.1.9", 84 "eslint-config-taro": "4.1.9",
85 + "eslint-plugin-vue": "^10.7.0",
86 + "husky": "^9.1.7",
80 "less": "^4.2.0", 87 "less": "^4.2.0",
88 + "lint-staged": "^16.2.7",
81 "postcss": "^8.5.6", 89 "postcss": "^8.5.6",
90 + "prettier": "^3.8.1",
82 "sass": "^1.78.0", 91 "sass": "^1.78.0",
83 "style-loader": "1.3.0", 92 "style-loader": "1.3.0",
84 "tailwindcss": "^3.4.0", 93 "tailwindcss": "^3.4.0",
85 "unplugin-vue-components": "^0.26.0", 94 "unplugin-vue-components": "^0.26.0",
95 + "vue-eslint-parser": "^10.2.0",
86 "vue-loader": "^17.0.0", 96 "vue-loader": "^17.0.0",
87 "weapp-tailwindcss": "^4.1.10", 97 "weapp-tailwindcss": "^4.1.10",
88 "webpack": "5.91.0" 98 "webpack": "5.91.0"
......
...@@ -108,6 +108,9 @@ importers: ...@@ -108,6 +108,9 @@ importers:
108 '@vue/compiler-sfc': 108 '@vue/compiler-sfc':
109 specifier: ^3.0.0 109 specifier: ^3.0.0
110 version: 3.5.26 110 version: 3.5.26
111 + '@vue/eslint-config-prettier':
112 + specifier: ^10.2.0
113 + version: 10.2.0(@types/eslint@9.6.1)(eslint@8.57.1)(prettier@3.8.1)
111 autoprefixer: 114 autoprefixer:
112 specifier: ^10.4.21 115 specifier: ^10.4.21
113 version: 10.4.23(postcss@8.5.6) 116 version: 10.4.23(postcss@8.5.6)
...@@ -122,13 +125,25 @@ importers: ...@@ -122,13 +125,25 @@ importers:
122 version: 8.57.1 125 version: 8.57.1
123 eslint-config-taro: 126 eslint-config-taro:
124 specifier: 4.1.9 127 specifier: 4.1.9
125 - version: 4.1.9(@babel/core@7.28.5)(eslint@8.57.1)(typescript@5.9.3) 128 + version: 4.1.9(@babel/core@7.28.5)(eslint-plugin-vue@10.7.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(vue-eslint-parser@10.2.0(eslint@8.57.1)))(eslint@8.57.1)(typescript@5.9.3)
129 + eslint-plugin-vue:
130 + specifier: ^10.7.0
131 + version: 10.7.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(vue-eslint-parser@10.2.0(eslint@8.57.1))
132 + husky:
133 + specifier: ^9.1.7
134 + version: 9.1.7
126 less: 135 less:
127 specifier: ^4.2.0 136 specifier: ^4.2.0
128 version: 4.5.1 137 version: 4.5.1
138 + lint-staged:
139 + specifier: ^16.2.7
140 + version: 16.2.7
129 postcss: 141 postcss:
130 specifier: ^8.5.6 142 specifier: ^8.5.6
131 version: 8.5.6 143 version: 8.5.6
144 + prettier:
145 + specifier: ^3.8.1
146 + version: 3.8.1
132 sass: 147 sass:
133 specifier: ^1.78.0 148 specifier: ^1.78.0
134 version: 1.97.2 149 version: 1.97.2
...@@ -141,6 +156,9 @@ importers: ...@@ -141,6 +156,9 @@ importers:
141 unplugin-vue-components: 156 unplugin-vue-components:
142 specifier: ^0.26.0 157 specifier: ^0.26.0
143 version: 0.26.0(@babel/parser@7.28.5)(rollup@3.29.5)(vue@3.5.26(typescript@5.9.3)) 158 version: 0.26.0(@babel/parser@7.28.5)(rollup@3.29.5)(vue@3.5.26(typescript@5.9.3))
159 + vue-eslint-parser:
160 + specifier: ^10.2.0
161 + version: 10.2.0(eslint@8.57.1)
144 vue-loader: 162 vue-loader:
145 specifier: ^17.0.0 163 specifier: ^17.0.0
146 version: 17.4.2(@vue/compiler-sfc@3.5.26)(vue@3.5.26(typescript@5.9.3))(webpack@5.91.0(@swc/core@1.3.96)) 164 version: 17.4.2(@vue/compiler-sfc@3.5.26)(vue@3.5.26(typescript@5.9.3))(webpack@5.91.0(@swc/core@1.3.96))
...@@ -1535,6 +1553,10 @@ packages: ...@@ -1535,6 +1553,10 @@ packages:
1535 resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} 1553 resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
1536 engines: {node: '>=14'} 1554 engines: {node: '>=14'}
1537 1555
1556 + '@pkgr/core@0.2.9':
1557 + resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==}
1558 + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
1559 +
1538 '@rnx-kit/babel-preset-metro-react-native@1.1.8': 1560 '@rnx-kit/babel-preset-metro-react-native@1.1.8':
1539 resolution: {integrity: sha512-8DotuBK1ZgV0H/tmCmtW/3ofA7JR/8aPqSu9lKnuqwBfq4bxz+w1sMyfFl89m4teWlkhgyczWBGD6NCLqTgi9A==} 1561 resolution: {integrity: sha512-8DotuBK1ZgV0H/tmCmtW/3ofA7JR/8aPqSu9lKnuqwBfq4bxz+w1sMyfFl89m4teWlkhgyczWBGD6NCLqTgi9A==}
1540 peerDependencies: 1562 peerDependencies:
...@@ -2225,6 +2247,12 @@ packages: ...@@ -2225,6 +2247,12 @@ packages:
2225 '@vue/devtools-shared@7.7.9': 2247 '@vue/devtools-shared@7.7.9':
2226 resolution: {integrity: sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==} 2248 resolution: {integrity: sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==}
2227 2249
2250 + '@vue/eslint-config-prettier@10.2.0':
2251 + resolution: {integrity: sha512-GL3YBLwv/+b86yHcNNfPJxOTtVFJ4Mbc9UU3zR+KVoG7SwGTjPT+32fXamscNumElhcpXW3mT0DgzS9w32S7Bw==}
2252 + peerDependencies:
2253 + eslint: '>= 8.21.0'
2254 + prettier: '>= 3.0.0'
2255 +
2228 '@vue/reactivity@3.5.26': 2256 '@vue/reactivity@3.5.26':
2229 resolution: {integrity: sha512-9EnYB1/DIiUYYnzlnUBgwU32NNvLp/nhxLXeWRhHUEeWNTn1ECxX8aGO7RTXeX6PPcxe3LLuNBFoJbV4QZ+CFQ==} 2257 resolution: {integrity: sha512-9EnYB1/DIiUYYnzlnUBgwU32NNvLp/nhxLXeWRhHUEeWNTn1ECxX8aGO7RTXeX6PPcxe3LLuNBFoJbV4QZ+CFQ==}
2230 2258
...@@ -2385,6 +2413,10 @@ packages: ...@@ -2385,6 +2413,10 @@ packages:
2385 resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} 2413 resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==}
2386 engines: {node: '>=8'} 2414 engines: {node: '>=8'}
2387 2415
2416 + ansi-escapes@7.2.0:
2417 + resolution: {integrity: sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw==}
2418 + engines: {node: '>=18'}
2419 +
2388 ansi-html-community@0.0.8: 2420 ansi-html-community@0.0.8:
2389 resolution: {integrity: sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==} 2421 resolution: {integrity: sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==}
2390 engines: {'0': node >= 0.8.0} 2422 engines: {'0': node >= 0.8.0}
...@@ -2741,6 +2773,10 @@ packages: ...@@ -2741,6 +2773,10 @@ packages:
2741 resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} 2773 resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==}
2742 engines: {node: '>=8'} 2774 engines: {node: '>=8'}
2743 2775
2776 + cli-cursor@5.0.0:
2777 + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==}
2778 + engines: {node: '>=18'}
2779 +
2744 cli-highlight@2.1.11: 2780 cli-highlight@2.1.11:
2745 resolution: {integrity: sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==} 2781 resolution: {integrity: sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==}
2746 engines: {node: '>=8.0.0', npm: '>=5.0.0'} 2782 engines: {node: '>=8.0.0', npm: '>=5.0.0'}
...@@ -2750,6 +2786,10 @@ packages: ...@@ -2750,6 +2786,10 @@ packages:
2750 resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} 2786 resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==}
2751 engines: {node: '>=6'} 2787 engines: {node: '>=6'}
2752 2788
2789 + cli-truncate@5.1.1:
2790 + resolution: {integrity: sha512-SroPvNHxUnk+vIW/dOSfNqdy1sPEFkrTk6TUtqLCnBlo3N7TNYYkzzN7uSD6+jVjrdO4+p8nH7JzH6cIvUem6A==}
2791 + engines: {node: '>=20'}
2792 +
2753 cli-width@3.0.0: 2793 cli-width@3.0.0:
2754 resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==} 2794 resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==}
2755 engines: {node: '>= 10'} 2795 engines: {node: '>= 10'}
...@@ -2791,6 +2831,10 @@ packages: ...@@ -2791,6 +2831,10 @@ packages:
2791 resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} 2831 resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
2792 engines: {node: '>= 0.8'} 2832 engines: {node: '>= 0.8'}
2793 2833
2834 + commander@14.0.2:
2835 + resolution: {integrity: sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==}
2836 + engines: {node: '>=20'}
2837 +
2794 commander@2.20.3: 2838 commander@2.20.3:
2795 resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} 2839 resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
2796 2840
...@@ -3285,6 +3329,9 @@ packages: ...@@ -3285,6 +3329,9 @@ packages:
3285 electron-to-chromium@1.5.267: 3329 electron-to-chromium@1.5.267:
3286 resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} 3330 resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==}
3287 3331
3332 + emoji-regex@10.6.0:
3333 + resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==}
3334 +
3288 emoji-regex@8.0.0: 3335 emoji-regex@8.0.0:
3289 resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} 3336 resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
3290 3337
...@@ -3330,6 +3377,10 @@ packages: ...@@ -3330,6 +3377,10 @@ packages:
3330 engines: {node: '>=4'} 3377 engines: {node: '>=4'}
3331 hasBin: true 3378 hasBin: true
3332 3379
3380 + environment@1.1.0:
3381 + resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==}
3382 + engines: {node: '>=18'}
3383 +
3333 errno@0.1.8: 3384 errno@0.1.8:
3334 resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==} 3385 resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==}
3335 hasBin: true 3386 hasBin: true
...@@ -3401,6 +3452,12 @@ packages: ...@@ -3401,6 +3452,12 @@ packages:
3401 resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} 3452 resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
3402 engines: {node: '>=10'} 3453 engines: {node: '>=10'}
3403 3454
3455 + eslint-config-prettier@10.1.8:
3456 + resolution: {integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==}
3457 + hasBin: true
3458 + peerDependencies:
3459 + eslint: '>=7.0.0'
3460 +
3404 eslint-config-taro@4.1.9: 3461 eslint-config-taro@4.1.9:
3405 resolution: {integrity: sha512-vPtiWIIb4P2RxuGpTQvRiWNrxuKZvhVl/eSHCyxa6IXo26bTOLcg3Hv5imaDi8NRshqLT0jqcf85dWWBYxBxpw==} 3462 resolution: {integrity: sha512-vPtiWIIb4P2RxuGpTQvRiWNrxuKZvhVl/eSHCyxa6IXo26bTOLcg3Hv5imaDi8NRshqLT0jqcf85dWWBYxBxpw==}
3406 engines: {node: '>= 18'} 3463 engines: {node: '>= 18'}
...@@ -3451,6 +3508,34 @@ packages: ...@@ -3451,6 +3508,34 @@ packages:
3451 '@typescript-eslint/parser': 3508 '@typescript-eslint/parser':
3452 optional: true 3509 optional: true
3453 3510
3511 + eslint-plugin-prettier@5.5.5:
3512 + resolution: {integrity: sha512-hscXkbqUZ2sPithAuLm5MXL+Wph+U7wHngPBv9OMWwlP8iaflyxpjTYZkmdgB4/vPIhemRlBEoLrH7UC1n7aUw==}
3513 + engines: {node: ^14.18.0 || >=16.0.0}
3514 + peerDependencies:
3515 + '@types/eslint': '>=8.0.0'
3516 + eslint: '>=8.0.0'
3517 + eslint-config-prettier: '>= 7.0.0 <10.0.0 || >=10.1.0'
3518 + prettier: '>=3.0.0'
3519 + peerDependenciesMeta:
3520 + '@types/eslint':
3521 + optional: true
3522 + eslint-config-prettier:
3523 + optional: true
3524 +
3525 + eslint-plugin-vue@10.7.0:
3526 + resolution: {integrity: sha512-r2XFCK4qlo1sxEoAMIoTTX0PZAdla0JJDt1fmYiworZUX67WeEGqm+JbyAg3M+pGiJ5U6Mp5WQbontXWtIW7TA==}
3527 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
3528 + peerDependencies:
3529 + '@stylistic/eslint-plugin': ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0
3530 + '@typescript-eslint/parser': ^7.0.0 || ^8.0.0
3531 + eslint: ^8.57.0 || ^9.0.0
3532 + vue-eslint-parser: ^10.0.0
3533 + peerDependenciesMeta:
3534 + '@stylistic/eslint-plugin':
3535 + optional: true
3536 + '@typescript-eslint/parser':
3537 + optional: true
3538 +
3454 eslint-scope@5.1.1: 3539 eslint-scope@5.1.1:
3455 resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} 3540 resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==}
3456 engines: {node: '>=8.0.0'} 3541 engines: {node: '>=8.0.0'}
...@@ -3459,6 +3544,10 @@ packages: ...@@ -3459,6 +3544,10 @@ packages:
3459 resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} 3544 resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==}
3460 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 3545 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
3461 3546
3547 + eslint-scope@8.4.0:
3548 + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==}
3549 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
3550 +
3462 eslint-visitor-keys@2.1.0: 3551 eslint-visitor-keys@2.1.0:
3463 resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} 3552 resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==}
3464 engines: {node: '>=10'} 3553 engines: {node: '>=10'}
...@@ -3467,6 +3556,10 @@ packages: ...@@ -3467,6 +3556,10 @@ packages:
3467 resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} 3556 resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
3468 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 3557 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
3469 3558
3559 + eslint-visitor-keys@4.2.1:
3560 + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==}
3561 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
3562 +
3470 eslint@8.41.0: 3563 eslint@8.41.0:
3471 resolution: {integrity: sha512-WQDQpzGBOP5IrXPo4Hc0814r4/v2rrIsB0rhT7jtunIalgg6gYXWhRMOejVO8yH21T/FGaxjmFjBMNqcIlmH1Q==} 3564 resolution: {integrity: sha512-WQDQpzGBOP5IrXPo4Hc0814r4/v2rrIsB0rhT7jtunIalgg6gYXWhRMOejVO8yH21T/FGaxjmFjBMNqcIlmH1Q==}
3472 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 3565 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
...@@ -3479,6 +3572,10 @@ packages: ...@@ -3479,6 +3572,10 @@ packages:
3479 deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. 3572 deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options.
3480 hasBin: true 3573 hasBin: true
3481 3574
3575 + espree@10.4.0:
3576 + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==}
3577 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
3578 +
3482 espree@9.6.1: 3579 espree@9.6.1:
3483 resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} 3580 resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==}
3484 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 3581 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
...@@ -3513,6 +3610,9 @@ packages: ...@@ -3513,6 +3610,9 @@ packages:
3513 eventemitter3@4.0.7: 3610 eventemitter3@4.0.7:
3514 resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} 3611 resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
3515 3612
3613 + eventemitter3@5.0.4:
3614 + resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==}
3615 +
3516 events@3.3.0: 3616 events@3.3.0:
3517 resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} 3617 resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
3518 engines: {node: '>=0.8.x'} 3618 engines: {node: '>=0.8.x'}
...@@ -3542,6 +3642,9 @@ packages: ...@@ -3542,6 +3642,9 @@ packages:
3542 fast-deep-equal@3.1.3: 3642 fast-deep-equal@3.1.3:
3543 resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} 3643 resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
3544 3644
3645 + fast-diff@1.3.0:
3646 + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==}
3647 +
3545 fast-glob@3.3.3: 3648 fast-glob@3.3.3:
3546 resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} 3649 resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
3547 engines: {node: '>=8.6.0'} 3650 engines: {node: '>=8.6.0'}
...@@ -3731,6 +3834,10 @@ packages: ...@@ -3731,6 +3834,10 @@ packages:
3731 resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} 3834 resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
3732 engines: {node: 6.* || 8.* || >= 10.*} 3835 engines: {node: 6.* || 8.* || >= 10.*}
3733 3836
3837 + get-east-asian-width@1.4.0:
3838 + resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==}
3839 + engines: {node: '>=18'}
3840 +
3734 get-intrinsic@1.3.0: 3841 get-intrinsic@1.3.0:
3735 resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} 3842 resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
3736 engines: {node: '>= 0.4'} 3843 engines: {node: '>= 0.4'}
...@@ -3989,6 +4096,11 @@ packages: ...@@ -3989,6 +4096,11 @@ packages:
3989 resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} 4096 resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
3990 engines: {node: '>=10.17.0'} 4097 engines: {node: '>=10.17.0'}
3991 4098
4099 + husky@9.1.7:
4100 + resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==}
4101 + engines: {node: '>=18'}
4102 + hasBin: true
4103 +
3992 iconv-lite@0.4.24: 4104 iconv-lite@0.4.24:
3993 resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} 4105 resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
3994 engines: {node: '>=0.10.0'} 4106 engines: {node: '>=0.10.0'}
...@@ -4140,6 +4252,10 @@ packages: ...@@ -4140,6 +4252,10 @@ packages:
4140 resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} 4252 resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
4141 engines: {node: '>=8'} 4253 engines: {node: '>=8'}
4142 4254
4255 + is-fullwidth-code-point@5.1.0:
4256 + resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==}
4257 + engines: {node: '>=18'}
4258 +
4143 is-generator-function@1.1.2: 4259 is-generator-function@1.1.2:
4144 resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} 4260 resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==}
4145 engines: {node: '>= 0.4'} 4261 engines: {node: '>= 0.4'}
...@@ -4501,6 +4617,15 @@ packages: ...@@ -4501,6 +4617,15 @@ packages:
4501 lines-and-columns@1.2.4: 4617 lines-and-columns@1.2.4:
4502 resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} 4618 resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
4503 4619
4620 + lint-staged@16.2.7:
4621 + resolution: {integrity: sha512-lDIj4RnYmK7/kXMya+qJsmkRFkGolciXjrsZ6PC25GdTfWOAWetR0ZbsNXRAj1EHHImRSalc+whZFg56F5DVow==}
4622 + engines: {node: '>=20.17'}
4623 + hasBin: true
4624 +
4625 + listr2@9.0.5:
4626 + resolution: {integrity: sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==}
4627 + engines: {node: '>=20.0.0'}
4628 +
4504 loader-runner@4.3.1: 4629 loader-runner@4.3.1:
4505 resolution: {integrity: sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==} 4630 resolution: {integrity: sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==}
4506 engines: {node: '>=6.11.5'} 4631 engines: {node: '>=6.11.5'}
...@@ -4562,6 +4687,10 @@ packages: ...@@ -4562,6 +4687,10 @@ packages:
4562 resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} 4687 resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==}
4563 engines: {node: '>=10'} 4688 engines: {node: '>=10'}
4564 4689
4690 + log-update@6.1.0:
4691 + resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==}
4692 + engines: {node: '>=18'}
4693 +
4565 loglevel-plugin-prefix@0.8.4: 4694 loglevel-plugin-prefix@0.8.4:
4566 resolution: {integrity: sha512-WpG9CcFAOjz/FtNht+QJeGpvVl/cdR6P0z6OcXSkr8wFJOsV2GRj2j10JLfjuA4aYkcKCNIEqRGCyTife9R8/g==} 4695 resolution: {integrity: sha512-WpG9CcFAOjz/FtNht+QJeGpvVl/cdR6P0z6OcXSkr8wFJOsV2GRj2j10JLfjuA4aYkcKCNIEqRGCyTife9R8/g==}
4567 4696
...@@ -4676,6 +4805,10 @@ packages: ...@@ -4676,6 +4805,10 @@ packages:
4676 resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} 4805 resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
4677 engines: {node: '>=6'} 4806 engines: {node: '>=6'}
4678 4807
4808 + mimic-function@5.0.1:
4809 + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==}
4810 + engines: {node: '>=18'}
4811 +
4679 mimic-response@1.0.1: 4812 mimic-response@1.0.1:
4680 resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} 4813 resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==}
4681 engines: {node: '>=4'} 4814 engines: {node: '>=4'}
...@@ -4755,6 +4888,10 @@ packages: ...@@ -4755,6 +4888,10 @@ packages:
4755 mz@2.7.0: 4888 mz@2.7.0:
4756 resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} 4889 resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
4757 4890
4891 + nano-spawn@2.0.0:
4892 + resolution: {integrity: sha512-tacvGzUY5o2D8CBh2rrwxyNojUsZNU2zjNTzKQrkgGJQTbGAfArVWXSKMBokBeeg6C7OLRGUEyoFlYbfeWQIqw==}
4893 + engines: {node: '>=20.17'}
4894 +
4758 nanoid@3.3.11: 4895 nanoid@3.3.11:
4759 resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} 4896 resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
4760 engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 4897 engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
...@@ -4885,6 +5022,10 @@ packages: ...@@ -4885,6 +5022,10 @@ packages:
4885 resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} 5022 resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
4886 engines: {node: '>=6'} 5023 engines: {node: '>=6'}
4887 5024
5025 + onetime@7.0.0:
5026 + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==}
5027 + engines: {node: '>=18'}
5028 +
4888 open@8.4.2: 5029 open@8.4.2:
4889 resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} 5030 resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==}
4890 engines: {node: '>=12'} 5031 engines: {node: '>=12'}
...@@ -5063,6 +5204,11 @@ packages: ...@@ -5063,6 +5204,11 @@ packages:
5063 resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} 5204 resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
5064 engines: {node: '>=12'} 5205 engines: {node: '>=12'}
5065 5206
5207 + pidtree@0.6.0:
5208 + resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==}
5209 + engines: {node: '>=0.10'}
5210 + hasBin: true
5211 +
5066 pify@2.3.0: 5212 pify@2.3.0:
5067 resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} 5213 resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
5068 engines: {node: '>=0.10.0'} 5214 engines: {node: '>=0.10.0'}
...@@ -5593,6 +5739,15 @@ packages: ...@@ -5593,6 +5739,15 @@ packages:
5593 resolution: {integrity: sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==} 5739 resolution: {integrity: sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==}
5594 engines: {node: '>=4'} 5740 engines: {node: '>=4'}
5595 5741
5742 + prettier-linter-helpers@1.0.1:
5743 + resolution: {integrity: sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==}
5744 + engines: {node: '>=6.0.0'}
5745 +
5746 + prettier@3.8.1:
5747 + resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==}
5748 + engines: {node: '>=14'}
5749 + hasBin: true
5750 +
5596 pretty-bytes@5.6.0: 5751 pretty-bytes@5.6.0:
5597 resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==} 5752 resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==}
5598 engines: {node: '>=6'} 5753 engines: {node: '>=6'}
...@@ -5800,6 +5955,10 @@ packages: ...@@ -5800,6 +5955,10 @@ packages:
5800 resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} 5955 resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==}
5801 engines: {node: '>=8'} 5956 engines: {node: '>=8'}
5802 5957
5958 + restore-cursor@5.1.0:
5959 + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==}
5960 + engines: {node: '>=18'}
5961 +
5803 retry@0.13.1: 5962 retry@0.13.1:
5804 resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} 5963 resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==}
5805 engines: {node: '>= 4'} 5964 engines: {node: '>= 4'}
...@@ -6033,6 +6192,10 @@ packages: ...@@ -6033,6 +6192,10 @@ packages:
6033 resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} 6192 resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==}
6034 engines: {node: '>=14.16'} 6193 engines: {node: '>=14.16'}
6035 6194
6195 + slice-ansi@7.1.2:
6196 + resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==}
6197 + engines: {node: '>=18'}
6198 +
6036 snake-case@3.0.4: 6199 snake-case@3.0.4:
6037 resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} 6200 resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==}
6038 6201
...@@ -6110,6 +6273,10 @@ packages: ...@@ -6110,6 +6273,10 @@ packages:
6110 resolution: {integrity: sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==} 6273 resolution: {integrity: sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==}
6111 engines: {node: '>=0.10.0'} 6274 engines: {node: '>=0.10.0'}
6112 6275
6276 + string-argv@0.3.2:
6277 + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==}
6278 + engines: {node: '>=0.6.19'}
6279 +
6113 string-width@4.2.3: 6280 string-width@4.2.3:
6114 resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} 6281 resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
6115 engines: {node: '>=8'} 6282 engines: {node: '>=8'}
...@@ -6118,6 +6285,14 @@ packages: ...@@ -6118,6 +6285,14 @@ packages:
6118 resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} 6285 resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
6119 engines: {node: '>=12'} 6286 engines: {node: '>=12'}
6120 6287
6288 + string-width@7.2.0:
6289 + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==}
6290 + engines: {node: '>=18'}
6291 +
6292 + string-width@8.1.0:
6293 + resolution: {integrity: sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==}
6294 + engines: {node: '>=20'}
6295 +
6121 string.fromcodepoint@0.2.1: 6296 string.fromcodepoint@0.2.1:
6122 resolution: {integrity: sha512-n69H31OnxSGSZyZbgBlvYIXlrMhJQ0dQAX1js1QDhpaUH6zmU3QYlj07bCwCNlPOu3oRXIubGPl2gDGnHsiCqg==} 6297 resolution: {integrity: sha512-n69H31OnxSGSZyZbgBlvYIXlrMhJQ0dQAX1js1QDhpaUH6zmU3QYlj07bCwCNlPOu3oRXIubGPl2gDGnHsiCqg==}
6123 6298
...@@ -6243,6 +6418,10 @@ packages: ...@@ -6243,6 +6418,10 @@ packages:
6243 symbol-tree@3.2.4: 6418 symbol-tree@3.2.4:
6244 resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} 6419 resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
6245 6420
6421 + synckit@0.11.12:
6422 + resolution: {integrity: sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==}
6423 + engines: {node: ^14.18.0 || >=16.0.0}
6424 +
6246 tailwindcss-config@1.1.3: 6425 tailwindcss-config@1.1.3:
6247 resolution: {integrity: sha512-7AN01Cwz2vd/vl6nKwoR0y/KlgpbKBREp1Q+MHJw8QF53AueVRFgU2Cqq0yhIQ4nC2wGvEJtlBCTeKYPX3TCXQ==} 6426 resolution: {integrity: sha512-7AN01Cwz2vd/vl6nKwoR0y/KlgpbKBREp1Q+MHJw8QF53AueVRFgU2Cqq0yhIQ4nC2wGvEJtlBCTeKYPX3TCXQ==}
6248 6427
...@@ -6558,6 +6737,12 @@ packages: ...@@ -6558,6 +6737,12 @@ packages:
6558 engines: {node: '>=6.0'} 6737 engines: {node: '>=6.0'}
6559 hasBin: true 6738 hasBin: true
6560 6739
6740 + vue-eslint-parser@10.2.0:
6741 + resolution: {integrity: sha512-CydUvFOQKD928UzZhTp4pr2vWz1L+H99t7Pkln2QSPdvmURT0MoC4wUccfCnuEaihNsu9aYYyk+bep8rlfkUXw==}
6742 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
6743 + peerDependencies:
6744 + eslint: ^8.57.0 || ^9.0.0
6745 +
6561 vue-loader@17.4.2: 6746 vue-loader@17.4.2:
6562 resolution: {integrity: sha512-yTKOA4R/VN4jqjw4y5HrynFL8AK0Z3/Jt7eOJXEitsm0GMRHDBjCfCiuTiLP7OESvsZYo2pATCWhDqxC5ZrM6w==} 6747 resolution: {integrity: sha512-yTKOA4R/VN4jqjw4y5HrynFL8AK0Z3/Jt7eOJXEitsm0GMRHDBjCfCiuTiLP7OESvsZYo2pATCWhDqxC5ZrM6w==}
6563 peerDependencies: 6748 peerDependencies:
...@@ -6726,6 +6911,10 @@ packages: ...@@ -6726,6 +6911,10 @@ packages:
6726 resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} 6911 resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
6727 engines: {node: '>=12'} 6912 engines: {node: '>=12'}
6728 6913
6914 + wrap-ansi@9.0.2:
6915 + resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==}
6916 + engines: {node: '>=18'}
6917 +
6729 wrappy@1.0.2: 6918 wrappy@1.0.2:
6730 resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} 6919 resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
6731 6920
...@@ -6741,6 +6930,10 @@ packages: ...@@ -6741,6 +6930,10 @@ packages:
6741 utf-8-validate: 6930 utf-8-validate:
6742 optional: true 6931 optional: true
6743 6932
6933 + xml-name-validator@4.0.0:
6934 + resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==}
6935 + engines: {node: '>=12'}
6936 +
6744 xml-name-validator@5.0.0: 6937 xml-name-validator@5.0.0:
6745 resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} 6938 resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
6746 engines: {node: '>=18'} 6939 engines: {node: '>=18'}
...@@ -8220,6 +8413,8 @@ snapshots: ...@@ -8220,6 +8413,8 @@ snapshots:
8220 '@pkgjs/parseargs@0.11.0': 8413 '@pkgjs/parseargs@0.11.0':
8221 optional: true 8414 optional: true
8222 8415
8416 + '@pkgr/core@0.2.9': {}
8417 +
8223 '@rnx-kit/babel-preset-metro-react-native@1.1.8(@babel/core@7.28.5)(@babel/plugin-transform-typescript@7.28.5(@babel/core@7.28.5))(@babel/runtime@7.28.4)': 8418 '@rnx-kit/babel-preset-metro-react-native@1.1.8(@babel/core@7.28.5)(@babel/plugin-transform-typescript@7.28.5(@babel/core@7.28.5))(@babel/runtime@7.28.4)':
8224 dependencies: 8419 dependencies:
8225 '@babel/core': 7.28.5 8420 '@babel/core': 7.28.5
...@@ -9113,6 +9308,15 @@ snapshots: ...@@ -9113,6 +9308,15 @@ snapshots:
9113 dependencies: 9308 dependencies:
9114 rfdc: 1.4.1 9309 rfdc: 1.4.1
9115 9310
9311 + '@vue/eslint-config-prettier@10.2.0(@types/eslint@9.6.1)(eslint@8.57.1)(prettier@3.8.1)':
9312 + dependencies:
9313 + eslint: 8.57.1
9314 + eslint-config-prettier: 10.1.8(eslint@8.57.1)
9315 + eslint-plugin-prettier: 5.5.5(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@8.57.1))(eslint@8.57.1)(prettier@3.8.1)
9316 + prettier: 3.8.1
9317 + transitivePeerDependencies:
9318 + - '@types/eslint'
9319 +
9116 '@vue/reactivity@3.5.26': 9320 '@vue/reactivity@3.5.26':
9117 dependencies: 9321 dependencies:
9118 '@vue/shared': 3.5.26 9322 '@vue/shared': 3.5.26
...@@ -9311,6 +9515,10 @@ snapshots: ...@@ -9311,6 +9515,10 @@ snapshots:
9311 dependencies: 9515 dependencies:
9312 type-fest: 0.21.3 9516 type-fest: 0.21.3
9313 9517
9518 + ansi-escapes@7.2.0:
9519 + dependencies:
9520 + environment: 1.1.0
9521 +
9314 ansi-html-community@0.0.8: {} 9522 ansi-html-community@0.0.8: {}
9315 9523
9316 ansi-regex@5.0.1: {} 9524 ansi-regex@5.0.1: {}
...@@ -9765,6 +9973,10 @@ snapshots: ...@@ -9765,6 +9973,10 @@ snapshots:
9765 dependencies: 9973 dependencies:
9766 restore-cursor: 3.1.0 9974 restore-cursor: 3.1.0
9767 9975
9976 + cli-cursor@5.0.0:
9977 + dependencies:
9978 + restore-cursor: 5.1.0
9979 +
9768 cli-highlight@2.1.11: 9980 cli-highlight@2.1.11:
9769 dependencies: 9981 dependencies:
9770 chalk: 4.1.2 9982 chalk: 4.1.2
...@@ -9776,6 +9988,11 @@ snapshots: ...@@ -9776,6 +9988,11 @@ snapshots:
9776 9988
9777 cli-spinners@2.9.2: {} 9989 cli-spinners@2.9.2: {}
9778 9990
9991 + cli-truncate@5.1.1:
9992 + dependencies:
9993 + slice-ansi: 7.1.2
9994 + string-width: 8.1.0
9995 +
9779 cli-width@3.0.0: {} 9996 cli-width@3.0.0: {}
9780 9997
9781 cliui@6.0.0: 9998 cliui@6.0.0:
...@@ -9820,6 +10037,8 @@ snapshots: ...@@ -9820,6 +10037,8 @@ snapshots:
9820 dependencies: 10037 dependencies:
9821 delayed-stream: 1.0.0 10038 delayed-stream: 1.0.0
9822 10039
10040 + commander@14.0.2: {}
10041 +
9823 commander@2.20.3: {} 10042 commander@2.20.3: {}
9824 10043
9825 commander@4.1.1: {} 10044 commander@4.1.1: {}
...@@ -10343,6 +10562,8 @@ snapshots: ...@@ -10343,6 +10562,8 @@ snapshots:
10343 10562
10344 electron-to-chromium@1.5.267: {} 10563 electron-to-chromium@1.5.267: {}
10345 10564
10565 + emoji-regex@10.6.0: {}
10566 +
10346 emoji-regex@8.0.0: {} 10567 emoji-regex@8.0.0: {}
10347 10568
10348 emoji-regex@9.2.2: {} 10569 emoji-regex@9.2.2: {}
...@@ -10372,6 +10593,8 @@ snapshots: ...@@ -10372,6 +10593,8 @@ snapshots:
10372 10593
10373 envinfo@7.21.0: {} 10594 envinfo@7.21.0: {}
10374 10595
10596 + environment@1.1.0: {}
10597 +
10375 errno@0.1.8: 10598 errno@0.1.8:
10376 dependencies: 10599 dependencies:
10377 prr: 1.0.1 10600 prr: 1.0.1
...@@ -10538,13 +10761,19 @@ snapshots: ...@@ -10538,13 +10761,19 @@ snapshots:
10538 10761
10539 escape-string-regexp@4.0.0: {} 10762 escape-string-regexp@4.0.0: {}
10540 10763
10541 - eslint-config-taro@4.1.9(@babel/core@7.28.5)(eslint@8.57.1)(typescript@5.9.3): 10764 + eslint-config-prettier@10.1.8(eslint@8.57.1):
10765 + dependencies:
10766 + eslint: 8.57.1
10767 +
10768 + eslint-config-taro@4.1.9(@babel/core@7.28.5)(eslint-plugin-vue@10.7.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(vue-eslint-parser@10.2.0(eslint@8.57.1)))(eslint@8.57.1)(typescript@5.9.3):
10542 dependencies: 10769 dependencies:
10543 '@babel/eslint-parser': 7.28.5(@babel/core@7.28.5)(eslint@8.57.1) 10770 '@babel/eslint-parser': 7.28.5(@babel/core@7.28.5)(eslint@8.57.1)
10544 '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) 10771 '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3)
10545 '@typescript-eslint/parser': 6.21.0(eslint@8.57.1)(typescript@5.9.3) 10772 '@typescript-eslint/parser': 6.21.0(eslint@8.57.1)(typescript@5.9.3)
10546 eslint: 8.57.1 10773 eslint: 8.57.1
10547 eslint-plugin-import: 2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1) 10774 eslint-plugin-import: 2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)
10775 + optionalDependencies:
10776 + eslint-plugin-vue: 10.7.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(vue-eslint-parser@10.2.0(eslint@8.57.1))
10548 transitivePeerDependencies: 10777 transitivePeerDependencies:
10549 - '@babel/core' 10778 - '@babel/core'
10550 - eslint-import-resolver-typescript 10779 - eslint-import-resolver-typescript
...@@ -10599,6 +10828,29 @@ snapshots: ...@@ -10599,6 +10828,29 @@ snapshots:
10599 - eslint-import-resolver-webpack 10828 - eslint-import-resolver-webpack
10600 - supports-color 10829 - supports-color
10601 10830
10831 + eslint-plugin-prettier@5.5.5(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@8.57.1))(eslint@8.57.1)(prettier@3.8.1):
10832 + dependencies:
10833 + eslint: 8.57.1
10834 + prettier: 3.8.1
10835 + prettier-linter-helpers: 1.0.1
10836 + synckit: 0.11.12
10837 + optionalDependencies:
10838 + '@types/eslint': 9.6.1
10839 + eslint-config-prettier: 10.1.8(eslint@8.57.1)
10840 +
10841 + eslint-plugin-vue@10.7.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(vue-eslint-parser@10.2.0(eslint@8.57.1)):
10842 + dependencies:
10843 + '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1)
10844 + eslint: 8.57.1
10845 + natural-compare: 1.4.0
10846 + nth-check: 2.1.1
10847 + postcss-selector-parser: 7.1.1
10848 + semver: 7.7.3
10849 + vue-eslint-parser: 10.2.0(eslint@8.57.1)
10850 + xml-name-validator: 4.0.0
10851 + optionalDependencies:
10852 + '@typescript-eslint/parser': 6.21.0(eslint@8.57.1)(typescript@5.9.3)
10853 +
10602 eslint-scope@5.1.1: 10854 eslint-scope@5.1.1:
10603 dependencies: 10855 dependencies:
10604 esrecurse: 4.3.0 10856 esrecurse: 4.3.0
...@@ -10609,10 +10861,17 @@ snapshots: ...@@ -10609,10 +10861,17 @@ snapshots:
10609 esrecurse: 4.3.0 10861 esrecurse: 4.3.0
10610 estraverse: 5.3.0 10862 estraverse: 5.3.0
10611 10863
10864 + eslint-scope@8.4.0:
10865 + dependencies:
10866 + esrecurse: 4.3.0
10867 + estraverse: 5.3.0
10868 +
10612 eslint-visitor-keys@2.1.0: {} 10869 eslint-visitor-keys@2.1.0: {}
10613 10870
10614 eslint-visitor-keys@3.4.3: {} 10871 eslint-visitor-keys@3.4.3: {}
10615 10872
10873 + eslint-visitor-keys@4.2.1: {}
10874 +
10616 eslint@8.41.0: 10875 eslint@8.41.0:
10617 dependencies: 10876 dependencies:
10618 '@eslint-community/eslint-utils': 4.9.1(eslint@8.41.0) 10877 '@eslint-community/eslint-utils': 4.9.1(eslint@8.41.0)
...@@ -10700,6 +10959,12 @@ snapshots: ...@@ -10700,6 +10959,12 @@ snapshots:
10700 transitivePeerDependencies: 10959 transitivePeerDependencies:
10701 - supports-color 10960 - supports-color
10702 10961
10962 + espree@10.4.0:
10963 + dependencies:
10964 + acorn: 8.15.0
10965 + acorn-jsx: 5.3.2(acorn@8.15.0)
10966 + eslint-visitor-keys: 4.2.1
10967 +
10703 espree@9.6.1: 10968 espree@9.6.1:
10704 dependencies: 10969 dependencies:
10705 acorn: 8.15.0 10970 acorn: 8.15.0
...@@ -10726,6 +10991,8 @@ snapshots: ...@@ -10726,6 +10991,8 @@ snapshots:
10726 10991
10727 eventemitter3@4.0.7: {} 10992 eventemitter3@4.0.7: {}
10728 10993
10994 + eventemitter3@5.0.4: {}
10995 +
10729 events@3.3.0: {} 10996 events@3.3.0: {}
10730 10997
10731 execa@5.1.1: 10998 execa@5.1.1:
...@@ -10791,6 +11058,8 @@ snapshots: ...@@ -10791,6 +11058,8 @@ snapshots:
10791 11058
10792 fast-deep-equal@3.1.3: {} 11059 fast-deep-equal@3.1.3: {}
10793 11060
11061 + fast-diff@1.3.0: {}
11062 +
10794 fast-glob@3.3.3: 11063 fast-glob@3.3.3:
10795 dependencies: 11064 dependencies:
10796 '@nodelib/fs.stat': 2.0.5 11065 '@nodelib/fs.stat': 2.0.5
...@@ -10969,6 +11238,8 @@ snapshots: ...@@ -10969,6 +11238,8 @@ snapshots:
10969 11238
10970 get-caller-file@2.0.5: {} 11239 get-caller-file@2.0.5: {}
10971 11240
11241 + get-east-asian-width@1.4.0: {}
11242 +
10972 get-intrinsic@1.3.0: 11243 get-intrinsic@1.3.0:
10973 dependencies: 11244 dependencies:
10974 call-bind-apply-helpers: 1.0.2 11245 call-bind-apply-helpers: 1.0.2
...@@ -11311,6 +11582,8 @@ snapshots: ...@@ -11311,6 +11582,8 @@ snapshots:
11311 11582
11312 human-signals@2.1.0: {} 11583 human-signals@2.1.0: {}
11313 11584
11585 + husky@9.1.7: {}
11586 +
11314 iconv-lite@0.4.24: 11587 iconv-lite@0.4.24:
11315 dependencies: 11588 dependencies:
11316 safer-buffer: 2.1.2 11589 safer-buffer: 2.1.2
...@@ -11465,6 +11738,10 @@ snapshots: ...@@ -11465,6 +11738,10 @@ snapshots:
11465 11738
11466 is-fullwidth-code-point@3.0.0: {} 11739 is-fullwidth-code-point@3.0.0: {}
11467 11740
11741 + is-fullwidth-code-point@5.1.0:
11742 + dependencies:
11743 + get-east-asian-width: 1.4.0
11744 +
11468 is-generator-function@1.1.2: 11745 is-generator-function@1.1.2:
11469 dependencies: 11746 dependencies:
11470 call-bound: 1.0.4 11747 call-bound: 1.0.4
...@@ -11816,6 +12093,25 @@ snapshots: ...@@ -11816,6 +12093,25 @@ snapshots:
11816 12093
11817 lines-and-columns@1.2.4: {} 12094 lines-and-columns@1.2.4: {}
11818 12095
12096 + lint-staged@16.2.7:
12097 + dependencies:
12098 + commander: 14.0.2
12099 + listr2: 9.0.5
12100 + micromatch: 4.0.8
12101 + nano-spawn: 2.0.0
12102 + pidtree: 0.6.0
12103 + string-argv: 0.3.2
12104 + yaml: 2.8.2
12105 +
12106 + listr2@9.0.5:
12107 + dependencies:
12108 + cli-truncate: 5.1.1
12109 + colorette: 2.0.20
12110 + eventemitter3: 5.0.4
12111 + log-update: 6.1.0
12112 + rfdc: 1.4.1
12113 + wrap-ansi: 9.0.2
12114 +
11819 loader-runner@4.3.1: {} 12115 loader-runner@4.3.1: {}
11820 12116
11821 loader-utils@1.4.2: 12117 loader-utils@1.4.2:
...@@ -11872,6 +12168,14 @@ snapshots: ...@@ -11872,6 +12168,14 @@ snapshots:
11872 chalk: 4.1.2 12168 chalk: 4.1.2
11873 is-unicode-supported: 0.1.0 12169 is-unicode-supported: 0.1.0
11874 12170
12171 + log-update@6.1.0:
12172 + dependencies:
12173 + ansi-escapes: 7.2.0
12174 + cli-cursor: 5.0.0
12175 + slice-ansi: 7.1.2
12176 + strip-ansi: 7.1.2
12177 + wrap-ansi: 9.0.2
12178 +
11875 loglevel-plugin-prefix@0.8.4: {} 12179 loglevel-plugin-prefix@0.8.4: {}
11876 12180
11877 loglevel@1.9.2: {} 12181 loglevel@1.9.2: {}
...@@ -11958,6 +12262,8 @@ snapshots: ...@@ -11958,6 +12262,8 @@ snapshots:
11958 12262
11959 mimic-fn@2.1.0: {} 12263 mimic-fn@2.1.0: {}
11960 12264
12265 + mimic-function@5.0.1: {}
12266 +
11961 mimic-response@1.0.1: {} 12267 mimic-response@1.0.1: {}
11962 12268
11963 mini-css-extract-plugin@2.9.4(webpack@5.91.0(@swc/core@1.3.96)): 12269 mini-css-extract-plugin@2.9.4(webpack@5.91.0(@swc/core@1.3.96)):
...@@ -12040,6 +12346,8 @@ snapshots: ...@@ -12040,6 +12346,8 @@ snapshots:
12040 object-assign: 4.1.1 12346 object-assign: 4.1.1
12041 thenify-all: 1.6.0 12347 thenify-all: 1.6.0
12042 12348
12349 + nano-spawn@2.0.0: {}
12350 +
12043 nanoid@3.3.11: {} 12351 nanoid@3.3.11: {}
12044 12352
12045 native-request@1.1.2: 12353 native-request@1.1.2:
...@@ -12165,6 +12473,10 @@ snapshots: ...@@ -12165,6 +12473,10 @@ snapshots:
12165 dependencies: 12473 dependencies:
12166 mimic-fn: 2.1.0 12474 mimic-fn: 2.1.0
12167 12475
12476 + onetime@7.0.0:
12477 + dependencies:
12478 + mimic-function: 5.0.1
12479 +
12168 open@8.4.2: 12480 open@8.4.2:
12169 dependencies: 12481 dependencies:
12170 define-lazy-prop: 2.0.0 12482 define-lazy-prop: 2.0.0
...@@ -12335,6 +12647,8 @@ snapshots: ...@@ -12335,6 +12647,8 @@ snapshots:
12335 12647
12336 picomatch@4.0.3: {} 12648 picomatch@4.0.3: {}
12337 12649
12650 + pidtree@0.6.0: {}
12651 +
12338 pify@2.3.0: {} 12652 pify@2.3.0: {}
12339 12653
12340 pify@3.0.0: {} 12654 pify@3.0.0: {}
...@@ -12892,6 +13206,12 @@ snapshots: ...@@ -12892,6 +13206,12 @@ snapshots:
12892 13206
12893 prepend-http@2.0.0: {} 13207 prepend-http@2.0.0: {}
12894 13208
13209 + prettier-linter-helpers@1.0.1:
13210 + dependencies:
13211 + fast-diff: 1.3.0
13212 +
13213 + prettier@3.8.1: {}
13214 +
12895 pretty-bytes@5.6.0: {} 13215 pretty-bytes@5.6.0: {}
12896 13216
12897 pretty-error@4.0.0: 13217 pretty-error@4.0.0:
...@@ -13122,6 +13442,11 @@ snapshots: ...@@ -13122,6 +13442,11 @@ snapshots:
13122 onetime: 5.1.2 13442 onetime: 5.1.2
13123 signal-exit: 3.0.7 13443 signal-exit: 3.0.7
13124 13444
13445 + restore-cursor@5.1.0:
13446 + dependencies:
13447 + onetime: 7.0.0
13448 + signal-exit: 4.1.0
13449 +
13125 retry@0.13.1: {} 13450 retry@0.13.1: {}
13126 13451
13127 reusify@1.1.0: {} 13452 reusify@1.1.0: {}
...@@ -13388,6 +13713,11 @@ snapshots: ...@@ -13388,6 +13713,11 @@ snapshots:
13388 13713
13389 slash@5.1.0: {} 13714 slash@5.1.0: {}
13390 13715
13716 + slice-ansi@7.1.2:
13717 + dependencies:
13718 + ansi-styles: 6.2.3
13719 + is-fullwidth-code-point: 5.1.0
13720 +
13391 snake-case@3.0.4: 13721 snake-case@3.0.4:
13392 dependencies: 13722 dependencies:
13393 dot-case: 3.0.4 13723 dot-case: 3.0.4
...@@ -13470,6 +13800,8 @@ snapshots: ...@@ -13470,6 +13800,8 @@ snapshots:
13470 13800
13471 strict-uri-encode@1.1.0: {} 13801 strict-uri-encode@1.1.0: {}
13472 13802
13803 + string-argv@0.3.2: {}
13804 +
13473 string-width@4.2.3: 13805 string-width@4.2.3:
13474 dependencies: 13806 dependencies:
13475 emoji-regex: 8.0.0 13807 emoji-regex: 8.0.0
...@@ -13482,6 +13814,17 @@ snapshots: ...@@ -13482,6 +13814,17 @@ snapshots:
13482 emoji-regex: 9.2.2 13814 emoji-regex: 9.2.2
13483 strip-ansi: 7.1.2 13815 strip-ansi: 7.1.2
13484 13816
13817 + string-width@7.2.0:
13818 + dependencies:
13819 + emoji-regex: 10.6.0
13820 + get-east-asian-width: 1.4.0
13821 + strip-ansi: 7.1.2
13822 +
13823 + string-width@8.1.0:
13824 + dependencies:
13825 + get-east-asian-width: 1.4.0
13826 + strip-ansi: 7.1.2
13827 +
13485 string.fromcodepoint@0.2.1: {} 13828 string.fromcodepoint@0.2.1: {}
13486 13829
13487 string.prototype.trim@1.2.10: 13830 string.prototype.trim@1.2.10:
...@@ -13616,6 +13959,10 @@ snapshots: ...@@ -13616,6 +13959,10 @@ snapshots:
13616 13959
13617 symbol-tree@3.2.4: {} 13960 symbol-tree@3.2.4: {}
13618 13961
13962 + synckit@0.11.12:
13963 + dependencies:
13964 + '@pkgr/core': 0.2.9
13965 +
13619 tailwindcss-config@1.1.3: 13966 tailwindcss-config@1.1.3:
13620 dependencies: 13967 dependencies:
13621 '@weapp-tailwindcss/shared': 1.1.1 13968 '@weapp-tailwindcss/shared': 1.1.1
...@@ -13957,6 +14304,18 @@ snapshots: ...@@ -13957,6 +14304,18 @@ snapshots:
13957 acorn: 8.15.0 14304 acorn: 8.15.0
13958 acorn-walk: 8.3.4 14305 acorn-walk: 8.3.4
13959 14306
14307 + vue-eslint-parser@10.2.0(eslint@8.57.1):
14308 + dependencies:
14309 + debug: 4.4.3
14310 + eslint: 8.57.1
14311 + eslint-scope: 8.4.0
14312 + eslint-visitor-keys: 4.2.1
14313 + espree: 10.4.0
14314 + esquery: 1.7.0
14315 + semver: 7.7.3
14316 + transitivePeerDependencies:
14317 + - supports-color
14318 +
13960 vue-loader@17.4.2(@vue/compiler-sfc@3.5.26)(vue@3.5.26(typescript@5.9.3))(webpack@5.91.0(@swc/core@1.3.96)): 14319 vue-loader@17.4.2(@vue/compiler-sfc@3.5.26)(vue@3.5.26(typescript@5.9.3))(webpack@5.91.0(@swc/core@1.3.96)):
13961 dependencies: 14320 dependencies:
13962 chalk: 4.1.2 14321 chalk: 4.1.2
...@@ -14229,10 +14588,18 @@ snapshots: ...@@ -14229,10 +14588,18 @@ snapshots:
14229 string-width: 5.1.2 14588 string-width: 5.1.2
14230 strip-ansi: 7.1.2 14589 strip-ansi: 7.1.2
14231 14590
14591 + wrap-ansi@9.0.2:
14592 + dependencies:
14593 + ansi-styles: 6.2.3
14594 + string-width: 7.2.0
14595 + strip-ansi: 7.1.2
14596 +
14232 wrappy@1.0.2: {} 14597 wrappy@1.0.2: {}
14233 14598
14234 ws@8.19.0: {} 14599 ws@8.19.0: {}
14235 14600
14601 + xml-name-validator@4.0.0: {}
14602 +
14236 xml-name-validator@5.0.0: {} 14603 xml-name-validator@5.0.0: {}
14237 14604
14238 xmlchars@2.2.0: {} 14605 xmlchars@2.2.0: {}
......
1 +#!/bin/bash
2 +
3 +# Husky 初始化脚本
4 +# 使用方法: bash scripts/setup-husky.sh
5 +
6 +set -e
7 +
8 +echo "🔧 开始设置 Husky Git Hooks..."
9 +
10 +# 检查是否在项目根目录
11 +if [ ! -f "package.json" ]; then
12 + echo "❌ 错误:请在项目根目录运行此脚本"
13 + exit 1
14 +fi
15 +
16 +# 安装依赖(如果未安装)
17 +echo "📦 安装依赖..."
18 +pnpm add -D husky lint-staged prettier || {
19 + echo "❌ 依赖安装失败"
20 + exit 1
21 +}
22 +
23 +# 初始化 Husky
24 +echo "🪝 初始化 Husky..."
25 +npx husky install || {
26 + echo "❌ Husky 初始化失败"
27 + exit 1
28 +}
29 +
30 +# 创建 pre-commit 钩子
31 +echo "📝 创建 pre-commit 钩子..."
32 +cat > .husky/pre-commit << 'EOF'
33 +#!/bin/sh
34 +. "$(dirname "$0")/_/husky.sh"
35 +
36 +echo "🔍 运行代码检查..."
37 +pnpm lint-staged
38 +EOF
39 +
40 +# 添加执行权限
41 +chmod +x .husky/pre-commit
42 +
43 +# 创建 commit-msg 钩子(可选,用于验证 commit 信息)
44 +cat > .husky/commit-msg << 'EOF'
45 +#!/bin/sh
46 +. "$(dirname "$0")/_/husky.sh"
47 +
48 +# 验证 commit 信息格式(可选)
49 +# commit_regex='^(feat|fix|docs|style|refactor|test|chore|build|ci|perf|revert)(\(.+\))?: .{1,50}'
50 +#
51 +#if ! grep -qE "$commit_regex" "$1"; then
52 +# echo "❌ Commit 信息格式不正确"
53 +# echo "✅ 正确格式: type(scope): subject"
54 +# echo "📝 类型: feat, fix, docs, style, refactor, test, chore, etc."
55 +# exit 1
56 +#fi
57 +
58 +echo "✅ Commit 信息验证通过"
59 +EOF
60 +
61 +chmod +x .husky/commit-msg
62 +
63 +# 更新 package.json scripts
64 +echo "📦 更新 package.json scripts..."
65 +if command -v jq > /dev/null 2>&1; then
66 + # 如果系统有 jq,使用它更新 package.json
67 + jq '.scripts.prepare = "husky install"' package.json > package.json.tmp && mv package.json.tmp package.json
68 +else
69 + echo "⚠️ 请手动在 package.json 的 scripts 中添加: \"prepare\": \"husky install\""
70 +fi
71 +
72 +echo "✅ Git Hooks 设置完成!"
73 +echo ""
74 +echo "📝 使用说明:"
75 +echo " - 每次 commit 前会自动运行 ESLint 和 Prettier"
76 +echo " - 如果检查失败,commit 将被中止"
77 +echo " - 使用 'git commit --no-verify' 跳过检查(不推荐)"
78 +echo ""
79 +echo "🎯 现在可以开始开发了!"
...@@ -5,12 +5,12 @@ ...@@ -5,12 +5,12 @@
5 * @FilePath: /tswj/src/api/common.js 5 * @FilePath: /tswj/src/api/common.js
6 * @Description: 通用接口 6 * @Description: 通用接口
7 */ 7 */
8 -import { fn, fetch, uploadFn } from '@/api/fn'; 8 +import { fn, fetch, uploadFn } from '@/api/fn'
9 9
10 const Api = { 10 const Api = {
11 SMS: '/srv/?a=sms', 11 SMS: '/srv/?a=sms',
12 TOKEN: '/srv/?a=upload', 12 TOKEN: '/srv/?a=upload',
13 - SAVE_FILE: '/srv/?a=upload&t=save_file', 13 + SAVE_FILE: '/srv/?a=upload&t=save_file'
14 } 14 }
15 15
16 /** 16 /**
...@@ -19,7 +19,7 @@ const Api = { ...@@ -19,7 +19,7 @@ const Api = {
19 * @param {string} params.phone 手机号 19 * @param {string} params.phone 手机号
20 * @returns {Promise<{code:number,data:any,msg:string}>} 标准返回 20 * @returns {Promise<{code:number,data:any,msg:string}>} 标准返回
21 */ 21 */
22 -export const smsAPI = (params) => fn(fetch.post(Api.SMS, params)); 22 +export const smsAPI = params => fn(fetch.post(Api.SMS, params))
23 23
24 /** 24 /**
25 * @description: 获取七牛token 25 * @description: 获取七牛token
...@@ -28,7 +28,7 @@ export const smsAPI = (params) => fn(fetch.post(Api.SMS, params)); ...@@ -28,7 +28,7 @@ export const smsAPI = (params) => fn(fetch.post(Api.SMS, params));
28 * @param {string} params.file 图片 base64 28 * @param {string} params.file 图片 base64
29 * @returns {Promise<{code:number,data:any,msg:string}>} 标准返回(data 为上传 token 等信息) 29 * @returns {Promise<{code:number,data:any,msg:string}>} 标准返回(data 为上传 token 等信息)
30 */ 30 */
31 -export const qiniuTokenAPI = (params) => fn(fetch.stringifyPost(Api.TOKEN, params)); 31 +export const qiniuTokenAPI = params => fn(fetch.stringifyPost(Api.TOKEN, params))
32 32
33 /** 33 /**
34 * @description: 上传七牛 34 * @description: 上传七牛
...@@ -37,7 +37,7 @@ export const qiniuTokenAPI = (params) => fn(fetch.stringifyPost(Api.TOKEN, param ...@@ -37,7 +37,7 @@ export const qiniuTokenAPI = (params) => fn(fetch.stringifyPost(Api.TOKEN, param
37 * @param {Object} config axios 配置 37 * @param {Object} config axios 配置
38 * @returns {Promise<any|false>} 成功返回七牛响应数据,失败返回 false 38 * @returns {Promise<any|false>} 成功返回七牛响应数据,失败返回 false
39 */ 39 */
40 -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))
41 41
42 /** 42 /**
43 * @description: 保存图片 43 * @description: 保存图片
...@@ -49,4 +49,4 @@ export const qiniuUploadAPI = (url, data, config) => uploadFn(fetch.basePost(url ...@@ -49,4 +49,4 @@ export const qiniuUploadAPI = (url, data, config) => uploadFn(fetch.basePost(url
49 * @param {string} params.filekey 文件 key 49 * @param {string} params.filekey 文件 key
50 * @returns {Promise<{code:number,data:any,msg:string}>} 标准返回 50 * @returns {Promise<{code:number,data:any,msg:string}>} 标准返回
51 */ 51 */
52 -export const saveFileAPI = (params) => fn(fetch.stringifyPost(Api.SAVE_FILE, params)); 52 +export const saveFileAPI = params => fn(fetch.stringifyPost(Api.SAVE_FILE, params))
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
5 * @FilePath: /xyxBooking-weapp/src/api/fn.js 5 * @FilePath: /xyxBooking-weapp/src/api/fn.js
6 * @Description: 统一后端返回格式(强制 { code, data, msg }) 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
...@@ -16,7 +16,7 @@ import qs from 'qs' ...@@ -16,7 +16,7 @@ import qs from 'qs'
16 * @param {Promise<any>} api axios 请求 Promise 16 * @param {Promise<any>} api axios 请求 Promise
17 * @returns {Promise<{code:number,data:any,msg:string,show?:boolean}>} 标准化后的返回对象 17 * @returns {Promise<{code:number,data:any,msg:string,show?:boolean}>} 标准化后的返回对象
18 */ 18 */
19 -export const fn = (api) => { 19 +export const fn = api => {
20 return api 20 return api
21 .then(res => { 21 .then(res => {
22 // 约定:后端 code === 1 为成功 22 // 约定:后端 code === 1 为成功
...@@ -26,19 +26,29 @@ export const fn = (api) => { ...@@ -26,19 +26,29 @@ export const fn = (api) => {
26 } 26 }
27 // 失败兜底:优先返回后端响应,同时做 toast 提示 27 // 失败兜底:优先返回后端响应,同时做 toast 提示
28 console.warn('接口请求失败:', res) 28 console.warn('接口请求失败:', res)
29 - if (res_data && res_data.show === false) return res_data 29 + if (res_data && res_data.show === false) {
30 + return res_data
31 + }
30 Taro.showToast({ 32 Taro.showToast({
31 - title: (res_data && res_data.msg) ? res_data.msg : '请求失败', 33 + title: res_data && res_data.msg ? res_data.msg : '请求失败',
32 icon: 'none', 34 icon: 'none',
33 duration: 2000 35 duration: 2000
34 }) 36 })
35 return res_data || { code: 0, data: null, msg: '请求失败' } 37 return res_data || { code: 0, data: null, msg: '请求失败' }
36 }) 38 })
37 .catch(err => { 39 .catch(err => {
38 - console.error('接口请求异常:', err); 40 + console.error('接口请求异常:', err)
39 - return { code: 0, data: null, msg: (err && (err.msg || err.message || err.errMsg)) ? (err.msg || err.message || err.errMsg) : '网络异常' } 41 + return {
42 + code: 0,
43 + data: null,
44 + msg:
45 + err && (err.msg || err.message || err.errMsg)
46 + ? err.msg || err.message || err.errMsg
47 + : '网络异常'
48 + }
40 }) 49 })
41 - .finally(() => { // 最终执行 50 + .finally(() => {
51 + // 最终执行
42 }) 52 })
43 } 53 }
44 54
...@@ -47,25 +57,26 @@ export const fn = (api) => { ...@@ -47,25 +57,26 @@ export const fn = (api) => {
47 * @param {Promise<any>} api axios 请求 Promise 57 * @param {Promise<any>} api axios 请求 Promise
48 * @returns {Promise<any|false>} 成功返回七牛响应数据,失败返回 false 58 * @returns {Promise<any|false>} 成功返回七牛响应数据,失败返回 false
49 */ 59 */
50 -export const uploadFn = (api) => { 60 +export const uploadFn = api => {
51 return api 61 return api
52 .then(res => { 62 .then(res => {
53 if (res.statusText === 'OK') { 63 if (res.statusText === 'OK') {
54 - return res.data || true; 64 + return res.data || true
55 - } else { 65 + }
56 - console.warn('七牛上传失败:', res); 66 + console.warn('七牛上传失败:', res)
57 - if (!res.data.show) return false; 67 + if (!res.data.show) {
58 - Taro.showToast({ 68 + return false
59 - title: res.data.msg,
60 - icon: 'none',
61 - duration: 2000
62 - });
63 - return false;
64 } 69 }
70 + Taro.showToast({
71 + title: res.data.msg,
72 + icon: 'none',
73 + duration: 2000
74 + })
75 + return false
65 }) 76 })
66 .catch(err => { 77 .catch(err => {
67 - console.error('七牛上传异常:', err); 78 + console.error('七牛上传异常:', err)
68 - return false; 79 + return false
69 }) 80 })
70 } 81 }
71 82
...@@ -82,7 +93,7 @@ export const fetch = { ...@@ -82,7 +93,7 @@ export const fetch = {
82 * @param {Object} params 查询参数 93 * @param {Object} params 查询参数
83 * @returns {Promise<any>} axios Promise 94 * @returns {Promise<any>} axios Promise
84 */ 95 */
85 - get: function (api, params) { 96 + get(api, params) {
86 return axios.get(api, params) 97 return axios.get(api, params)
87 }, 98 },
88 /** 99 /**
...@@ -91,7 +102,7 @@ export const fetch = { ...@@ -91,7 +102,7 @@ export const fetch = {
91 * @param {Object} params 请求体 102 * @param {Object} params 请求体
92 * @returns {Promise<any>} axios Promise 103 * @returns {Promise<any>} axios Promise
93 */ 104 */
94 - post: function (api, params) { 105 + post(api, params) {
95 return axios.post(api, params) 106 return axios.post(api, params)
96 }, 107 },
97 /** 108 /**
...@@ -100,7 +111,7 @@ export const fetch = { ...@@ -100,7 +111,7 @@ export const fetch = {
100 * @param {Object} params 请求体 111 * @param {Object} params 请求体
101 * @returns {Promise<any>} axios Promise 112 * @returns {Promise<any>} axios Promise
102 */ 113 */
103 - stringifyPost: function (api, params) { 114 + stringifyPost(api, params) {
104 return axios.post(api, qs.stringify(params), { 115 return axios.post(api, qs.stringify(params), {
105 headers: { 116 headers: {
106 'content-type': 'application/x-www-form-urlencoded' 117 'content-type': 'application/x-www-form-urlencoded'
...@@ -114,7 +125,7 @@ export const fetch = { ...@@ -114,7 +125,7 @@ export const fetch = {
114 * @param {Object} config axios 配置 125 * @param {Object} config axios 配置
115 * @returns {Promise<any>} axios Promise 126 * @returns {Promise<any>} axios Promise
116 */ 127 */
117 - basePost: function (url, data, config) { 128 + basePost(url, data, config) {
118 return axios.post(url, data, config) 129 return axios.post(url, data, config)
119 } 130 }
120 } 131 }
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
5 * @FilePath: /xyxBooking-weapp/src/api/index.js 5 * @FilePath: /xyxBooking-weapp/src/api/index.js
6 * @Description: 文件描述 6 * @Description: 文件描述
7 */ 7 */
8 -import { fn, fetch } from '@/api/fn'; 8 +import { fn, fetch } from '@/api/fn'
9 9
10 /** 10 /**
11 * @description 预约业务 API 聚合 11 * @description 预约业务 API 聚合
...@@ -30,22 +30,22 @@ const Api = { ...@@ -30,22 +30,22 @@ const Api = {
30 BILL_PREPARE: '/srv/?a=api&t=bill_person', 30 BILL_PREPARE: '/srv/?a=api&t=bill_person',
31 // BILL_PAY_STATUS: '/srv/?a=api&t=bill_pay_status', 31 // BILL_PAY_STATUS: '/srv/?a=api&t=bill_pay_status',
32 QUERY_QR_CODE: '/srv/?a=api&t=id_number_query_qr_code', 32 QUERY_QR_CODE: '/srv/?a=api&t=id_number_query_qr_code',
33 - ICBC_ORDER_QRY: '/srv/?a=icbc_orderqry', 33 + ICBC_ORDER_QRY: '/srv/?a=icbc_orderqry'
34 -}; 34 +}
35 35
36 /** 36 /**
37 * @description: 可预约日期列表 37 * @description: 可预约日期列表
38 * @param {Array} month 月份,格式yyyy-mm, reserve_full 是否可约 1=可约,0=约满,-1=没有配置预约时段, open_time 在今天,开放预约最晚可预约日期的后一天的时间点, tips 不可预约的提示信息 38 * @param {Array} month 月份,格式yyyy-mm, reserve_full 是否可约 1=可约,0=约满,-1=没有配置预约时段, open_time 在今天,开放预约最晚可预约日期的后一天的时间点, tips 不可预约的提示信息
39 * @returns 39 * @returns
40 */ 40 */
41 -export const canReserveDateListAPI = (params) => fn(fetch.get(Api.CAN_RESERVE_DATE_LIST, params)); 41 +export const canReserveDateListAPI = params => fn(fetch.get(Api.CAN_RESERVE_DATE_LIST, params))
42 42
43 /** 43 /**
44 * @description: 可预约时段列表 44 * @description: 可预约时段列表
45 * @param {Array} month_date 日期,格式yyyy-mm-dd 45 * @param {Array} month_date 日期,格式yyyy-mm-dd
46 * @returns 46 * @returns
47 */ 47 */
48 -export const canReserveTimeListAPI = (params) => fn(fetch.get(Api.CAN_RESERVE_TIME_LIST, params)); 48 +export const canReserveTimeListAPI = params => fn(fetch.get(Api.CAN_RESERVE_TIME_LIST, params))
49 49
50 /** 50 /**
51 * @description: 参观者列表 51 * @description: 参观者列表
...@@ -54,7 +54,7 @@ export const canReserveTimeListAPI = (params) => fn(fetch.get(Api.CAN_RESERVE_TI ...@@ -54,7 +54,7 @@ export const canReserveTimeListAPI = (params) => fn(fetch.get(Api.CAN_RESERVE_TI
54 * @param {String} end_time 时段结束时间,格式 hh:mm 54 * @param {String} end_time 时段结束时间,格式 hh:mm
55 * @returns 55 * @returns
56 */ 56 */
57 -export const personListAPI = (params) => fn(fetch.get(Api.PERSON_LIST, params)); 57 +export const personListAPI = params => fn(fetch.get(Api.PERSON_LIST, params))
58 58
59 /** 59 /**
60 * @description: 添加参观者 60 * @description: 添加参观者
...@@ -63,14 +63,14 @@ export const personListAPI = (params) => fn(fetch.get(Api.PERSON_LIST, params)); ...@@ -63,14 +63,14 @@ export const personListAPI = (params) => fn(fetch.get(Api.PERSON_LIST, params));
63 * @param {String} id_number 证件号 63 * @param {String} id_number 证件号
64 * @returns 64 * @returns
65 */ 65 */
66 -export const addPersonAPI = (params) => fn(fetch.post(Api.ADD_PERSON, params)); 66 +export const addPersonAPI = params => fn(fetch.post(Api.ADD_PERSON, params))
67 67
68 /** 68 /**
69 * @description: 删除参观者 69 * @description: 删除参观者
70 * @param {String} person_id 参观者id 70 * @param {String} person_id 参观者id
71 * @returns 71 * @returns
72 */ 72 */
73 -export const delPersonAPI = (params) => fn(fetch.post(Api.DEL_PERSON, params)); 73 +export const delPersonAPI = params => fn(fetch.post(Api.DEL_PERSON, params))
74 74
75 /** 75 /**
76 * @description: 提交预约 76 * @description: 提交预约
...@@ -80,7 +80,7 @@ export const delPersonAPI = (params) => fn(fetch.post(Api.DEL_PERSON, params)); ...@@ -80,7 +80,7 @@ export const delPersonAPI = (params) => fn(fetch.post(Api.DEL_PERSON, params));
80 * @param {String} person_id_list 80 * @param {String} person_id_list
81 * @returns {String} bill_id 预约单id 81 * @returns {String} bill_id 预约单id
82 */ 82 */
83 -export const addReserveAPI = (params) => fn(fetch.post(Api.ADD_RESERVE, params)); 83 +export const addReserveAPI = params => fn(fetch.post(Api.ADD_RESERVE, params))
84 84
85 /** 85 /**
86 * @description: 支付准备(模拟) 86 * @description: 支付准备(模拟)
...@@ -102,34 +102,34 @@ export const addReserveAPI = (params) => fn(fetch.post(Api.ADD_RESERVE, params)) ...@@ -102,34 +102,34 @@ export const addReserveAPI = (params) => fn(fetch.post(Api.ADD_RESERVE, params))
102 * @param {String} bill_id 预约单id 102 * @param {String} bill_id 预约单id
103 * @returns {String} 103 * @returns {String}
104 */ 104 */
105 -export const billInfoAPI = (params) => fn(fetch.get(Api.BILL_INFO, params)); 105 +export const billInfoAPI = params => fn(fetch.get(Api.BILL_INFO, params))
106 106
107 /** 107 /**
108 * @description: 预约单详情,参观者列表 - 免授权接口 108 * @description: 预约单详情,参观者列表 - 免授权接口
109 * @param {String} pay_id 订单id 109 * @param {String} pay_id 订单id
110 * @returns {String} 110 * @returns {String}
111 */ 111 */
112 -export const onAuthBillInfoAPI = (params) => fn(fetch.get(Api.ON_AUTH_BILL_INFO, params)); 112 +export const onAuthBillInfoAPI = params => fn(fetch.get(Api.ON_AUTH_BILL_INFO, params))
113 113
114 /** 114 /**
115 * @description: 预约码列表 115 * @description: 预约码列表
116 * @returns {String} 116 * @returns {String}
117 */ 117 */
118 -export const qrcodeListAPI = (params) => fn(fetch.get(Api.QRCODE_LIST, params)); 118 +export const qrcodeListAPI = params => fn(fetch.get(Api.QRCODE_LIST, params))
119 119
120 /** 120 /**
121 * @description: 二维码使用状态 121 * @description: 二维码使用状态
122 * @param {String} qr_code 二维码编号 122 * @param {String} qr_code 二维码编号
123 * @returns {String} status 二维码状态 1=未激活(未支付),3=待使用(已支付),5=被取消,7=已使用 123 * @returns {String} status 二维码状态 1=未激活(未支付),3=待使用(已支付),5=被取消,7=已使用
124 */ 124 */
125 -export const qrcodeStatusAPI = (params) => fn(fetch.get(Api.QRCODE_STATUS, params)); 125 +export const qrcodeStatusAPI = params => fn(fetch.get(Api.QRCODE_STATUS, params))
126 126
127 /** 127 /**
128 * @description: 预约单列表 128 * @description: 预约单列表
129 * @param {String} 129 * @param {String}
130 * @returns {String} 130 * @returns {String}
131 */ 131 */
132 -export const billListAPI = (params) => fn(fetch.get(Api.BILL_LIST, params)); 132 +export const billListAPI = params => fn(fetch.get(Api.BILL_LIST, params))
133 133
134 /** 134 /**
135 * @description: 所有预约单的详情(用于离线缓存:列表+详情) 135 * @description: 所有预约单的详情(用于离线缓存:列表+详情)
...@@ -149,21 +149,21 @@ export const billListAPI = (params) => fn(fetch.get(Api.BILL_LIST, params)); ...@@ -149,21 +149,21 @@ export const billListAPI = (params) => fn(fetch.get(Api.BILL_LIST, params));
149 * @returns: {Object} data[].list 列表字段集合 149 * @returns: {Object} data[].list 列表字段集合
150 * @returns: {Number} data[].list.show_cancel_reserve 显示“取消预约”按钮(1=显示) 150 * @returns: {Number} data[].list.show_cancel_reserve 显示“取消预约”按钮(1=显示)
151 */ 151 */
152 -export const billOfflineAllAPI = (params) => fn(fetch.get(Api.BILL_OFFLINE_ALL, params)); 152 +export const billOfflineAllAPI = params => fn(fetch.get(Api.BILL_OFFLINE_ALL, params))
153 153
154 /** 154 /**
155 * @description: 取消预约 155 * @description: 取消预约
156 * @param {String} pay_id 156 * @param {String} pay_id
157 * @returns {String} 157 * @returns {String}
158 */ 158 */
159 -export const icbcRefundAPI = (params) => fn(fetch.post(Api.ICBC_REFUND, params)); 159 +export const icbcRefundAPI = params => fn(fetch.post(Api.ICBC_REFUND, params))
160 160
161 /** 161 /**
162 * @description: 预约单的参观者列表 162 * @description: 预约单的参观者列表
163 * @param {String} 163 * @param {String}
164 * @returns {String} 164 * @returns {String}
165 */ 165 */
166 -export const billPersonAPI = (params) => fn(fetch.get(Api.BILL_PREPARE, params)); 166 +export const billPersonAPI = params => fn(fetch.get(Api.BILL_PREPARE, params))
167 167
168 /** 168 /**
169 * 接口废弃 169 * 接口废弃
...@@ -179,7 +179,7 @@ export const billPersonAPI = (params) => fn(fetch.get(Api.BILL_PREPARE, params)) ...@@ -179,7 +179,7 @@ export const billPersonAPI = (params) => fn(fetch.get(Api.BILL_PREPARE, params))
179 * @param {string} params.id_number 身份证号 179 * @param {string} params.id_number 身份证号
180 * @returns {Promise<{code:number,data:any,msg:string}>} 标准返回 180 * @returns {Promise<{code:number,data:any,msg:string}>} 标准返回
181 */ 181 */
182 -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))
183 183
184 /** 184 /**
185 * @description: 查询订单号 185 * @description: 查询订单号
...@@ -187,4 +187,4 @@ export const queryQrCodeAPI = (params) => fn(fetch.get(Api.QUERY_QR_CODE, params ...@@ -187,4 +187,4 @@ export const queryQrCodeAPI = (params) => fn(fetch.get(Api.QUERY_QR_CODE, params
187 * @param {string} params.pay_id 支付凭证/订单号 187 * @param {string} params.pay_id 支付凭证/订单号
188 * @returns {Promise<{code:number,data:any,msg:string}>} 标准返回 188 * @returns {Promise<{code:number,data:any,msg:string}>} 标准返回
189 */ 189 */
190 -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))
......
...@@ -5,12 +5,12 @@ ...@@ -5,12 +5,12 @@
5 * @FilePath: /xyxBooking-weapp/src/api/redeem.js 5 * @FilePath: /xyxBooking-weapp/src/api/redeem.js
6 * @Description: 义工核销端接口 6 * @Description: 义工核销端接口
7 */ 7 */
8 -import { fn, fetch } from '@/api/fn'; 8 +import { fn, fetch } from '@/api/fn'
9 9
10 const Api = { 10 const Api = {
11 REDEEM_LOGIN: '/srv/?f=reserve_admin&a=login', 11 REDEEM_LOGIN: '/srv/?f=reserve_admin&a=login',
12 REDEEM_CHECK_AUTH: '/srv/?f=reserve_admin&a=user&t=check_auth', 12 REDEEM_CHECK_AUTH: '/srv/?f=reserve_admin&a=user&t=check_auth',
13 - REDEEM_REDEEM: '/srv/?f=reserve_admin&a=bill&t=redeem', 13 + REDEEM_REDEEM: '/srv/?f=reserve_admin&a=bill&t=redeem'
14 } 14 }
15 15
16 /** 16 /**
...@@ -18,18 +18,18 @@ const Api = { ...@@ -18,18 +18,18 @@ const Api = {
18 * @param {Object} params 请求参数 18 * @param {Object} params 请求参数
19 * @returns {Promise<{code:number,data:any,msg:string}>} 标准返回 19 * @returns {Promise<{code:number,data:any,msg:string}>} 标准返回
20 */ 20 */
21 -export const volunteerLoginAPI = (params) => fn(fetch.post(Api.REDEEM_LOGIN, params)); 21 +export const volunteerLoginAPI = params => fn(fetch.post(Api.REDEEM_LOGIN, params))
22 22
23 /** 23 /**
24 * @description: 检查核销权限 24 * @description: 检查核销权限
25 * @param {Object} params 请求参数 25 * @param {Object} params 请求参数
26 * @returns {Promise<{code:number,data:{can_redeem:boolean},msg:string}>} 标准返回 26 * @returns {Promise<{code:number,data:{can_redeem:boolean},msg:string}>} 标准返回
27 */ 27 */
28 -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))
29 29
30 /** 30 /**
31 * @description: 核销 31 * @description: 核销
32 * @param {Object} params 请求参数 32 * @param {Object} params 请求参数
33 * @returns {Promise<{code:number,data:any,msg:string}>} 标准返回 33 * @returns {Promise<{code:number,data:any,msg:string}>} 标准返回
34 */ 34 */
35 -export const verifyTicketAPI = (params) => fn(fetch.post(Api.REDEEM_REDEEM, params)); 35 +export const verifyTicketAPI = params => fn(fetch.post(Api.REDEEM_REDEEM, params))
......
...@@ -2,12 +2,12 @@ ...@@ -2,12 +2,12 @@
2 * @Date: 2026-01-10 2 * @Date: 2026-01-10
3 * @Description: 从原始文档转换生成的 API 接口文件 3 * @Description: 从原始文档转换生成的 API 接口文件
4 */ 4 */
5 -import { fn, fetch } from '@/api/fn'; 5 +import { fn, fetch } from '@/api/fn'
6 6
7 const Api = { 7 const Api = {
8 CAN_RESERVE_DATE_LIST: '/srv/?a=api&t=can_reserve_date_list', 8 CAN_RESERVE_DATE_LIST: '/srv/?a=api&t=can_reserve_date_list',
9 - CAN_RESERVE_TIME_LIST: '/srv/?a=api&t=can_reserve_time_list', 9 + CAN_RESERVE_TIME_LIST: '/srv/?a=api&t=can_reserve_time_list'
10 -}; 10 +}
11 11
12 /** 12 /**
13 * @description: 可预约日期列表 13 * @description: 可预约日期列表
...@@ -22,7 +22,7 @@ const Api = { ...@@ -22,7 +22,7 @@ const Api = {
22 * }> 22 * }>
23 * }>} 23 * }>}
24 */ 24 */
25 -export const canReserveDateListAPI = (params) => fn(fetch.get(Api.CAN_RESERVE_DATE_LIST, params)); 25 +export const canReserveDateListAPI = params => fn(fetch.get(Api.CAN_RESERVE_DATE_LIST, params))
26 26
27 /** 27 /**
28 * @description: 可预约时段列表 28 * @description: 可预约时段列表
...@@ -39,4 +39,4 @@ export const canReserveDateListAPI = (params) => fn(fetch.get(Api.CAN_RESERVE_DA ...@@ -39,4 +39,4 @@ export const canReserveDateListAPI = (params) => fn(fetch.get(Api.CAN_RESERVE_DA
39 * }> 39 * }>
40 * }>} 40 * }>}
41 */ 41 */
42 -export const canReserveTimeListAPI = (params) => fn(fetch.get(Api.CAN_RESERVE_TIME_LIST, params)); 42 +export const canReserveTimeListAPI = params => fn(fetch.get(Api.CAN_RESERVE_TIME_LIST, params))
......
...@@ -6,10 +6,10 @@ ...@@ -6,10 +6,10 @@
6 * @FilePath: /tswj/src/api/wx/config.js 6 * @FilePath: /tswj/src/api/wx/config.js
7 * @Description: 7 * @Description:
8 */ 8 */
9 -import { fn, fetch } from '@/api/fn'; 9 +import { fn, fetch } from '@/api/fn'
10 10
11 const Api = { 11 const Api = {
12 - WX_JSAPI: '/srv/?a=wx_share', 12 + WX_JSAPI: '/srv/?a=wx_share'
13 } 13 }
14 14
15 /** 15 /**
...@@ -18,4 +18,4 @@ const Api = { ...@@ -18,4 +18,4 @@ const Api = {
18 * @param {string} params.url 当前页面 URL(用于签名) 18 * @param {string} params.url 当前页面 URL(用于签名)
19 * @returns {Promise<{code:number,data:any,msg:string}>} 标准返回 19 * @returns {Promise<{code:number,data:any,msg:string}>} 标准返回
20 */ 20 */
21 -export const wxJsAPI = (params) => fn(fetch.get(Api.WX_JSAPI, params)); 21 +export const wxJsAPI = params => fn(fetch.get(Api.WX_JSAPI, params))
......
...@@ -11,41 +11,41 @@ ...@@ -11,41 +11,41 @@
11 * @type {Array<string>} 11 * @type {Array<string>}
12 */ 12 */
13 export const apiList = [ 13 export const apiList = [
14 - "updateAppMessageShareData", 14 + 'updateAppMessageShareData',
15 - "updateTimelineShareData", 15 + 'updateTimelineShareData',
16 - "onMenuShareTimeline", 16 + 'onMenuShareTimeline',
17 - "onMenuShareAppMessage", 17 + 'onMenuShareAppMessage',
18 - "onMenuShareQQ", 18 + 'onMenuShareQQ',
19 - "onMenuShareWeibo", 19 + 'onMenuShareWeibo',
20 - "onMenuShareQZone", 20 + 'onMenuShareQZone',
21 - "startRecord", 21 + 'startRecord',
22 - "stopRecord", 22 + 'stopRecord',
23 - "onVoiceRecordEnd", 23 + 'onVoiceRecordEnd',
24 - "playVoice", 24 + 'playVoice',
25 - "pauseVoice", 25 + 'pauseVoice',
26 - "stopVoice", 26 + 'stopVoice',
27 - "onVoicePlayEnd", 27 + 'onVoicePlayEnd',
28 - "uploadVoice", 28 + 'uploadVoice',
29 - "downloadVoice", 29 + 'downloadVoice',
30 - "chooseImage", 30 + 'chooseImage',
31 - "previewImage", 31 + 'previewImage',
32 - "uploadImage", 32 + 'uploadImage',
33 - "downloadImage", 33 + 'downloadImage',
34 - "translateVoice", 34 + 'translateVoice',
35 - "getNetworkType", 35 + 'getNetworkType',
36 - "openLocation", 36 + 'openLocation',
37 - "getLocation", 37 + 'getLocation',
38 - "hideOptionMenu", 38 + 'hideOptionMenu',
39 - "showOptionMenu", 39 + 'showOptionMenu',
40 - "hideMenuItems", 40 + 'hideMenuItems',
41 - "showMenuItems", 41 + 'showMenuItems',
42 - "hideAllNonBaseMenuItem", 42 + 'hideAllNonBaseMenuItem',
43 - "showAllNonBaseMenuItem", 43 + 'showAllNonBaseMenuItem',
44 - "closeWindow", 44 + 'closeWindow',
45 - "scanQRCode", 45 + 'scanQRCode',
46 - "chooseWXPay", 46 + 'chooseWXPay',
47 - "openProductSpecificView", 47 + 'openProductSpecificView',
48 - "addCard", 48 + 'addCard',
49 - "chooseCard", 49 + 'chooseCard',
50 - "openCard" 50 + 'openCard'
51 ] 51 ]
......
...@@ -6,10 +6,10 @@ ...@@ -6,10 +6,10 @@
6 * @FilePath: /tswj/src/api/wx/config.js 6 * @FilePath: /tswj/src/api/wx/config.js
7 * @Description: 7 * @Description:
8 */ 8 */
9 -import { fn, fetch } from '@/api/fn'; 9 +import { fn, fetch } from '@/api/fn'
10 10
11 const Api = { 11 const Api = {
12 - WX_PAY: '/srv/?a=icbc_pay_wxamp', 12 + WX_PAY: '/srv/?a=icbc_pay_wxamp'
13 } 13 }
14 14
15 /** 15 /**
...@@ -18,4 +18,4 @@ const Api = { ...@@ -18,4 +18,4 @@ const Api = {
18 * @param {string} params.pay_id 预约单支付凭证 18 * @param {string} params.pay_id 预约单支付凭证
19 * @returns {Promise<{code:number,data:any,msg:string}>} 标准返回(data 为微信支付参数) 19 * @returns {Promise<{code:number,data:any,msg:string}>} 标准返回(data 为微信支付参数)
20 */ 20 */
21 -export const wxPayAPI = (params) => fn(fetch.post(Api.WX_PAY, params)); 21 +export const wxPayAPI = params => fn(fetch.post(Api.WX_PAY, params))
......
...@@ -24,7 +24,7 @@ const pages = [ ...@@ -24,7 +24,7 @@ const pages = [
24 'pages/weakNetwork/index', 24 'pages/weakNetwork/index',
25 'pages/offlineBookingCode/index', 25 'pages/offlineBookingCode/index',
26 'pages/offlineBookingList/index', 26 'pages/offlineBookingList/index',
27 - 'pages/offlineBookingDetail/index', 27 + 'pages/offlineBookingDetail/index'
28 ] 28 ]
29 29
30 if (process.env.NODE_ENV === 'development') { 30 if (process.env.NODE_ENV === 'development') {
...@@ -32,14 +32,15 @@ if (process.env.NODE_ENV === 'development') { ...@@ -32,14 +32,15 @@ if (process.env.NODE_ENV === 'development') {
32 pages.push('pages/tailwindTest/index') 32 pages.push('pages/tailwindTest/index')
33 } 33 }
34 34
35 -const subpackages = process.env.NODE_ENV === 'development' 35 +const subpackages =
36 - ? [ 36 + process.env.NODE_ENV === 'development'
37 - { 37 + ? [
38 - root: 'pages/demo', 38 + {
39 - pages: ['index'], 39 + root: 'pages/demo',
40 - }, 40 + pages: ['index']
41 - ] 41 + }
42 - : [] 42 + ]
43 + : []
43 44
44 export default { 45 export default {
45 pages, 46 pages,
...@@ -48,6 +49,6 @@ export default { ...@@ -48,6 +49,6 @@ export default {
48 backgroundTextStyle: 'light', 49 backgroundTextStyle: 'light',
49 navigationBarBackgroundColor: '#fff', 50 navigationBarBackgroundColor: '#fff',
50 navigationBarTitleText: '西园寺预约', 51 navigationBarTitleText: '西园寺预约',
51 - navigationBarTextStyle: 'black', 52 + navigationBarTextStyle: 'black'
52 - }, 53 + }
53 } 54 }
......
...@@ -11,7 +11,10 @@ import './utils/polyfill' ...@@ -11,7 +11,10 @@ import './utils/polyfill'
11 import './app.less' 11 import './app.less'
12 import { saveCurrentPagePath, hasAuth, silentAuth, navigateToAuth } from '@/utils/authRedirect' 12 import { saveCurrentPagePath, hasAuth, silentAuth, navigateToAuth } from '@/utils/authRedirect'
13 import Taro from '@tarojs/taro' 13 import Taro from '@tarojs/taro'
14 -import { refresh_offline_booking_cache, has_offline_booking_cache } from '@/composables/useOfflineBookingCache' 14 +import {
15 + refresh_offline_booking_cache,
16 + has_offline_booking_cache
17 +} from '@/composables/useOfflineBookingCache'
15 import { is_usable_network, get_network_type } from '@/utils/network' 18 import { is_usable_network, get_network_type } from '@/utils/network'
16 import { enable_offline_booking_cache_polling } from '@/composables/useOfflineBookingCachePolling' 19 import { enable_offline_booking_cache_polling } from '@/composables/useOfflineBookingCachePolling'
17 import { weak_network_text, get_weak_network_modal_use_cache_options } from '@/utils/uiText' 20 import { weak_network_text, get_weak_network_modal_use_cache_options } from '@/utils/uiText'
...@@ -22,199 +25,223 @@ let has_shown_network_modal = false ...@@ -22,199 +25,223 @@ let has_shown_network_modal = false
22 let last_network_usable = null 25 let last_network_usable = null
23 26
24 const App = createApp({ 27 const App = createApp({
25 - // 对应 onLaunch 28 + // 对应 onLaunch
26 - async onLaunch(options) { 29 + async onLaunch(options) {
27 - const path = options?.path || '' 30 + const path = options?.path || ''
28 - const query = options?.query || {} 31 + const query = options?.query || {}
29 - 32 +
30 - const query_string = Object.keys(query) 33 + const query_string = Object.keys(query)
31 - .map((key) => `${key}=${encodeURIComponent(query[key])}`) 34 + .map(key => `${key}=${encodeURIComponent(query[key])}`)
32 - .join('&') 35 + .join('&')
33 - const full_path = query_string ? `${path}?${query_string}` : path 36 + const full_path = query_string ? `${path}?${query_string}` : path
34 - 37 +
35 - // 保存当前页面路径,用于授权后跳转回原页面 38 + // 保存当前页面路径,用于授权后跳转回原页面
36 - if (full_path) { 39 + if (full_path) {
37 - saveCurrentPagePath(full_path) 40 + saveCurrentPagePath(full_path)
38 - } 41 + }
39 - 42 +
40 - /** 43 + /**
41 - * @description 预加载离线预约记录数据(列表+详情) 44 + * @description 预加载离线预约记录数据(列表+详情)
42 - * - 仅在有授权且网络可用时调用 45 + * - 仅在有授权且网络可用时调用
43 - * - 成功后将数据存储到本地缓存(key: OFFLINE_BOOKING_DATA) 46 + * - 成功后将数据存储到本地缓存(key: OFFLINE_BOOKING_DATA)
44 - * @returns {Promise<void>} 无返回值 47 + * @returns {Promise<void>} 无返回值
45 - */ 48 + */
46 - const preloadBookingData = async () => { 49 + const preloadBookingData = async () => {
47 - try { 50 + try {
48 - await refresh_offline_booking_cache() 51 + await refresh_offline_booking_cache()
49 - } catch (e) { 52 + } catch (e) {
50 - console.error('Preload booking cache failed', e) 53 + console.error('Preload booking cache failed', e)
51 - } 54 + }
52 - } 55 + }
53 - 56 +
54 - /** 57 + /**
55 - * @description 判断是否应该跳过网络异常提示弹窗 58 + * @description 判断是否应该跳过网络异常提示弹窗
56 - * - 仅在当前页面为离线预约列表/详情/核销码页时返回 true 59 + * - 仅在当前页面为离线预约列表/详情/核销码页时返回 true
57 - * @returns {boolean} true=跳过提示,false=不跳过 60 + * @returns {boolean} true=跳过提示,false=不跳过
58 - */ 61 + */
59 - 62 +
60 - const should_skip_network_prompt = () => { 63 + const should_skip_network_prompt = () => {
61 - const pages = Taro.getCurrentPages ? Taro.getCurrentPages() : [] 64 + const pages = Taro.getCurrentPages ? Taro.getCurrentPages() : []
62 - const current_page = pages && pages.length ? pages[pages.length - 1] : null 65 + const current_page = pages && pages.length ? pages[pages.length - 1] : null
63 - const current_route = String(current_page?.route || '') 66 + const current_route = String(current_page?.route || '')
64 - if (!current_route) return false 67 + if (!current_route) {
65 - if (current_route.includes('pages/offlineBookingList/index')) return true 68 + return false
66 - if (current_route.includes('pages/offlineBookingDetail/index')) return true 69 + }
67 - if (current_route.includes('pages/offlineBookingCode/index')) return true 70 + if (current_route.includes('pages/offlineBookingList/index')) {
68 - return false 71 + return true
69 - } 72 + }
70 - 73 + if (current_route.includes('pages/offlineBookingDetail/index')) {
71 - /** 74 + return true
72 - * @description 处理不良网络情况 75 + }
73 - * - 仅在当前页面未跳过提示弹窗时调用 76 + if (current_route.includes('pages/offlineBookingCode/index')) {
74 - * - 若有离线预约缓存,则展示弹窗询问是否使用缓存数据 77 + return true
75 - * - 否则展示简单提示 toast 78 + }
76 - * @param {string} network_type 网络类型(wifi/4g/5g/3g/none/unknown) 79 + return false
77 - * @returns {Promise<boolean>} true=需要中断后续启动流程,false=继续 80 + }
78 - */ 81 +
79 - 82 + /**
80 - const handle_bad_network = async (network_type) => { 83 + * @description 处理不良网络情况
81 - if (has_shown_network_modal) return false 84 + * - 仅在当前页面未跳过提示弹窗时调用
82 - if (should_skip_network_prompt()) return false 85 + * - 若有离线预约缓存,则展示弹窗询问是否使用缓存数据
83 - 86 + * - 否则展示简单提示 toast
84 - const is_none_network = network_type === 'none' 87 + * @param {string} network_type 网络类型(wifi/4g/5g/3g/none/unknown)
85 - const is_weak_network = !is_usable_network(network_type) 88 + * @returns {Promise<boolean>} true=需要中断后续启动流程,false=继续
86 - if (!is_weak_network) return false 89 + */
87 - 90 +
88 - has_shown_network_modal = true 91 + const handle_bad_network = async network_type => {
89 - 92 + if (has_shown_network_modal) {
90 - if (has_offline_booking_cache()) { 93 + return false
91 - try { 94 + }
92 - const modal_res = await Taro.showModal(get_weak_network_modal_use_cache_options()) 95 + if (should_skip_network_prompt()) {
93 - if (modal_res?.confirm) { 96 + return false
94 - await Taro.reLaunch({ url: '/pages/offlineBookingList/index' }) 97 + }
95 - return true 98 +
96 - } 99 + const is_none_network = network_type === 'none'
97 - } catch (e) { 100 + const is_weak_network = !is_usable_network(network_type)
98 - return is_none_network 101 + if (!is_weak_network) {
99 - } 102 + return false
100 - } else { 103 + }
101 - try { 104 +
102 - await Taro.showToast({ title: weak_network_text.toast_title, icon: 'none', duration: 2000 }) 105 + has_shown_network_modal = true
103 - } catch (e) { 106 +
104 - return is_none_network 107 + if (has_offline_booking_cache()) {
105 - } 108 + try {
106 - } 109 + const modal_res = await Taro.showModal(get_weak_network_modal_use_cache_options())
107 - 110 + if (modal_res?.confirm) {
108 - return is_none_network 111 + await Taro.reLaunch({ url: '/pages/offlineBookingList/index' })
109 - } 112 + return true
110 - 113 + }
111 - /** 114 + } catch (e) {
112 - * 监听网络状态变化 115 + return is_none_network
113 - * - 当网络连接且有授权时,预加载离线预约记录数据
114 - */
115 - Taro.onNetworkStatusChange((res) => {
116 - const is_connected = res?.isConnected !== false
117 - const network_type = res?.networkType || 'none'
118 - const network_usable = is_connected && is_usable_network(network_type)
119 -
120 - if (network_usable) {
121 - has_shown_network_modal = false
122 - last_network_usable = true
123 - if (hasAuth()) preloadBookingData()
124 - return
125 - }
126 -
127 - const should_prompt = last_network_usable === true || last_network_usable === null
128 - last_network_usable = false
129 - if (should_prompt) {
130 - handle_bad_network(network_type)
131 - }
132 - return
133 - })
134 -
135 - /**
136 - * @description 处理启动时的不良网络情况(只在启动阶段检查一次)
137 - * - 网络不可用且有离线缓存:询问是否进入离线模式
138 - * - 网络不可用且无缓存:toast 提示网络不佳
139 - * @returns {Promise<boolean>} true=进入离线模式并中断启动,false=继续启动
140 - */
141 - const handle_bad_network_on_launch = async () => {
142 - /**
143 - * 避免重复提示用户
144 - * - 仅在首次启动时检查网络情况
145 - * - 如果用户已展示过提示弹窗,则直接返回 false
146 - */
147 - if (has_shown_network_modal) return false
148 -
149 - const network_type = await get_network_type()
150 - last_network_usable = is_usable_network(network_type)
151 - return handle_bad_network(network_type)
152 - }
153 -
154 - /**
155 - * @description 尝试在网络可用时预加载离线预约记录数据
156 - * - 仅在有授权时调用
157 - * @returns {void} 无返回值
158 - */
159 - const try_preload_when_online = () => {
160 - if (!hasAuth()) return
161 - Taro.getNetworkType({
162 - success: (res) => {
163 - if (is_usable_network(res.networkType)) {
164 - preloadBookingData()
165 - }
166 - }
167 - })
168 } 116 }
169 - 117 + } else {
170 - /** 118 + try {
171 - * @description 授权成功后的共用启动逻辑 119 + await Taro.showToast({
172 - * - 尝试在网络可用时预加载离线预约数据 120 + title: weak_network_text.toast_title,
173 - * - 启动离线预约缓存轮询(会自行处理网络可用性与引用计数) 121 + icon: 'none',
174 - * @returns {void} 无返回值 122 + duration: 2000
175 - */ 123 + })
176 - 124 + } catch (e) {
177 - const bootstrap_after_auth = () => { 125 + return is_none_network
178 - try_preload_when_online()
179 - enable_offline_booking_cache_polling({ interval_ms: 2 * 1000 * 60 })
180 } 126 }
181 - 127 + }
182 - // 处理在启动时出现的不良网络情况 128 +
183 - const should_stop = await handle_bad_network_on_launch() 129 + return is_none_network
184 - // 如果用户选择进入离线模式,则直接返回 130 + }
185 - if (should_stop) return 131 +
186 - 132 + /**
187 - /** 133 + * 监听网络状态变化
188 - * 尝试在有授权时预加载离线预约记录数据 134 + * - 当网络连接且有授权时,预加载离线预约记录数据
189 - * - 若无授权,则尝试静默授权 135 + */
190 - * - 授权成功后调用 bootstrap_after_auth 启动共用逻辑 136 + Taro.onNetworkStatusChange(res => {
191 - * - 授权失败则跳转至授权页面 137 + const is_connected = res?.isConnected !== false
192 - */ 138 + const network_type = res?.networkType || 'none'
193 - 139 + const network_usable = is_connected && is_usable_network(network_type)
194 - // 如果用户已授权,则直接调用 bootstrap_after_auth 启动共用逻辑 140 +
141 + if (network_usable) {
142 + has_shown_network_modal = false
143 + last_network_usable = true
195 if (hasAuth()) { 144 if (hasAuth()) {
196 - bootstrap_after_auth() 145 + preloadBookingData()
197 - return
198 - }
199 -
200 - if (path === 'pages/auth/index') return
201 -
202 - try {
203 - // 尝试静默授权
204 - await silentAuth()
205 - // 授权成功后调用 bootstrap_after_auth 启动共用逻辑
206 - bootstrap_after_auth()
207 - } catch (error) {
208 - console.error('静默授权失败:', error)
209 - // 授权失败则跳转至授权页面
210 - navigateToAuth(full_path || undefined)
211 } 146 }
212 -
213 return 147 return
214 - }, 148 + }
215 - onShow() { 149 +
216 - }, 150 + const should_prompt = last_network_usable === true || last_network_usable === null
217 -}); 151 + last_network_usable = false
152 + if (should_prompt) {
153 + handle_bad_network(network_type)
154 + }
155 + })
156 +
157 + /**
158 + * @description 处理启动时的不良网络情况(只在启动阶段检查一次)
159 + * - 网络不可用且有离线缓存:询问是否进入离线模式
160 + * - 网络不可用且无缓存:toast 提示网络不佳
161 + * @returns {Promise<boolean>} true=进入离线模式并中断启动,false=继续启动
162 + */
163 + const handle_bad_network_on_launch = async () => {
164 + /**
165 + * 避免重复提示用户
166 + * - 仅在首次启动时检查网络情况
167 + * - 如果用户已展示过提示弹窗,则直接返回 false
168 + */
169 + if (has_shown_network_modal) {
170 + return false
171 + }
172 +
173 + const network_type = await get_network_type()
174 + last_network_usable = is_usable_network(network_type)
175 + return handle_bad_network(network_type)
176 + }
177 +
178 + /**
179 + * @description 尝试在网络可用时预加载离线预约记录数据
180 + * - 仅在有授权时调用
181 + * @returns {void} 无返回值
182 + */
183 + const try_preload_when_online = () => {
184 + if (!hasAuth()) {
185 + return
186 + }
187 + Taro.getNetworkType({
188 + success: res => {
189 + if (is_usable_network(res.networkType)) {
190 + preloadBookingData()
191 + }
192 + }
193 + })
194 + }
195 +
196 + /**
197 + * @description 授权成功后的共用启动逻辑
198 + * - 尝试在网络可用时预加载离线预约数据
199 + * - 启动离线预约缓存轮询(会自行处理网络可用性与引用计数)
200 + * @returns {void} 无返回值
201 + */
202 +
203 + const bootstrap_after_auth = () => {
204 + try_preload_when_online()
205 + enable_offline_booking_cache_polling({ interval_ms: 2 * 1000 * 60 })
206 + }
207 +
208 + // 处理在启动时出现的不良网络情况
209 + const should_stop = await handle_bad_network_on_launch()
210 + // 如果用户选择进入离线模式,则直接返回
211 + if (should_stop) {
212 + return
213 + }
214 +
215 + /**
216 + * 尝试在有授权时预加载离线预约记录数据
217 + * - 若无授权,则尝试静默授权
218 + * - 授权成功后调用 bootstrap_after_auth 启动共用逻辑
219 + * - 授权失败则跳转至授权页面
220 + */
221 +
222 + // 如果用户已授权,则直接调用 bootstrap_after_auth 启动共用逻辑
223 + if (hasAuth()) {
224 + bootstrap_after_auth()
225 + return
226 + }
227 +
228 + if (path === 'pages/auth/index') {
229 + return
230 + }
231 +
232 + try {
233 + // 尝试静默授权
234 + await silentAuth()
235 + // 授权成功后调用 bootstrap_after_auth 启动共用逻辑
236 + bootstrap_after_auth()
237 + } catch (error) {
238 + console.error('静默授权失败:', error)
239 + // 授权失败则跳转至授权页面
240 + navigateToAuth(full_path || undefined)
241 + }
242 + },
243 + onShow() {}
244 +})
218 245
219 App.use(createPinia()) 246 App.use(createPinia())
220 247
......
...@@ -11,5 +11,5 @@ ...@@ -11,5 +11,5 @@
11 @tailwind utilities; 11 @tailwind utilities;
12 12
13 :root { 13 :root {
14 - --nut-primary-color: #A67939; 14 + --nut-primary-color: #a67939;
15 } 15 }
......
1 -
2 .modify-top { 1 .modify-top {
3 z-index: 36; 2 z-index: 36;
4 position: absolute; 3 position: absolute;
......
...@@ -3,9 +3,9 @@ ...@@ -3,9 +3,9 @@
3 /* ============ 颜色 ============ */ 3 /* ============ 颜色 ============ */
4 4
5 // 主色调 5 // 主色调
6 -@base-color: #11D2B1; 6 +@base-color: #11d2b1;
7 // 文字颜色 7 // 文字颜色
8 -@base-font-color: #FFFFFF; 8 +@base-font-color: #ffffff;
9 9
10 // 定义一个映射 10 // 定义一个映射
11 #colors() { 11 #colors() {
......
...@@ -8,30 +8,24 @@ ...@@ -8,30 +8,24 @@
8 /> 8 />
9 </template> 9 </template>
10 <script> 10 <script>
11 -import Taro from "@tarojs/taro" 11 +import Taro from '@tarojs/taro'
12 -import { defineComponent, onMounted, ref } from "vue" 12 +import { defineComponent, onMounted, ref } from 'vue'
13 -import { drawImage, drawText, drawBlock, drawLine } from "./utils/draw.js" 13 +import { drawImage, drawText, drawBlock, drawLine } from './utils/draw.js'
14 -import { 14 +import { toPx, toRpx, getRandomId, getImageInfo, getLinearColor } from './utils/tools.js'
15 - toPx,
16 - toRpx,
17 - getRandomId,
18 - getImageInfo,
19 - getLinearColor,
20 -} from "./utils/tools.js"
21 15
22 export default defineComponent({ 16 export default defineComponent({
23 - name: "PosterBuilder", 17 + name: 'PosterBuilder',
24 props: { 18 props: {
25 showLoading: { 19 showLoading: {
26 type: Boolean, 20 type: Boolean,
27 - default: false, 21 + default: false
28 }, 22 },
29 config: { 23 config: {
30 type: Object, 24 type: Object,
31 - default: () => ({}), 25 + default: () => ({})
32 - }, 26 + }
33 }, 27 },
34 - emits: ["success", "fail"], 28 + emits: ['success', 'fail'],
35 setup(props, context) { 29 setup(props, context) {
36 const count = ref(1) 30 const count = ref(1)
37 const { 31 const {
...@@ -41,7 +35,7 @@ export default defineComponent({ ...@@ -41,7 +35,7 @@ export default defineComponent({
41 texts = [], 35 texts = [],
42 blocks = [], 36 blocks = [],
43 lines = [], 37 lines = [],
44 - debug = false, 38 + debug = false
45 } = props.config || {} 39 } = props.config || {}
46 40
47 const canvasId = getRandomId() 41 const canvasId = getRandomId()
...@@ -51,11 +45,9 @@ export default defineComponent({ ...@@ -51,11 +45,9 @@ export default defineComponent({
51 * @param {Array} images = imgTask 45 * @param {Array} images = imgTask
52 * @return {Promise} downloadImagePromise 46 * @return {Promise} downloadImagePromise
53 */ 47 */
54 - const initImages = (images) => { 48 + const initImages = images => {
55 - const imagesTemp = images.filter((item) => item.url) 49 + const imagesTemp = images.filter(item => item.url)
56 - const drawList = imagesTemp.map((item, index) => 50 + const drawList = imagesTemp.map((item, index) => getImageInfo(item, index))
57 - getImageInfo(item, index)
58 - )
59 return Promise.all(drawList) 51 return Promise.all(drawList)
60 } 52 }
61 53
...@@ -64,15 +56,15 @@ export default defineComponent({ ...@@ -64,15 +56,15 @@ export default defineComponent({
64 * @return {Promise} resolve 里返回其 dom 和实例 56 * @return {Promise} resolve 里返回其 dom 和实例
65 */ 57 */
66 const initCanvas = () => 58 const initCanvas = () =>
67 - new Promise((resolve) => { 59 + new Promise(resolve => {
68 setTimeout(() => { 60 setTimeout(() => {
69 const pageInstance = Taro.getCurrentInstance()?.page || {} // 拿到当前页面实例 61 const pageInstance = Taro.getCurrentInstance()?.page || {} // 拿到当前页面实例
70 const query = Taro.createSelectorQuery().in(pageInstance) // 确定在当前页面内匹配子元素 62 const query = Taro.createSelectorQuery().in(pageInstance) // 确定在当前页面内匹配子元素
71 query 63 query
72 .select(`#${canvasId}`) 64 .select(`#${canvasId}`)
73 - .fields({ node: true, size: true, context: true }, (res) => { 65 + .fields({ node: true, size: true, context: true }, res => {
74 const canvas = res.node 66 const canvas = res.node
75 - const ctx = canvas.getContext("2d") 67 + const ctx = canvas.getContext('2d')
76 resolve({ ctx, canvas }) 68 resolve({ ctx, canvas })
77 }) 69 })
78 .exec() 70 .exec()
...@@ -83,30 +75,30 @@ export default defineComponent({ ...@@ -83,30 +75,30 @@ export default defineComponent({
83 * @description 保存绘制的图片 75 * @description 保存绘制的图片
84 * @param { object } config 76 * @param { object } config
85 */ 77 */
86 - const getTempFile = (canvas) => { 78 + const getTempFile = canvas => {
87 Taro.canvasToTempFilePath( 79 Taro.canvasToTempFilePath(
88 { 80 {
89 canvas, 81 canvas,
90 - success: (result) => { 82 + success: result => {
91 Taro.hideLoading() 83 Taro.hideLoading()
92 - context.emit("success", result) 84 + context.emit('success', result)
93 }, 85 },
94 - fail: (error) => { 86 + fail: error => {
95 const { errMsg } = error 87 const { errMsg } = error
96 - if (errMsg === "canvasToTempFilePath:fail:create bitmap failed") { 88 + if (errMsg === 'canvasToTempFilePath:fail:create bitmap failed') {
97 count.value += 1 89 count.value += 1
98 if (count.value <= 3) { 90 if (count.value <= 3) {
99 getTempFile(canvas) 91 getTempFile(canvas)
100 } else { 92 } else {
101 Taro.hideLoading() 93 Taro.hideLoading()
102 Taro.showToast({ 94 Taro.showToast({
103 - icon: "none", 95 + icon: 'none',
104 - title: errMsg || "绘制海报失败", 96 + title: errMsg || '绘制海报失败'
105 }) 97 })
106 - context.emit("fail", errMsg) 98 + context.emit('fail', errMsg)
107 } 99 }
108 } 100 }
109 - }, 101 + }
110 }, 102 },
111 context 103 context
112 ) 104 )
...@@ -116,7 +108,7 @@ export default defineComponent({ ...@@ -116,7 +108,7 @@ export default defineComponent({
116 * step2: 开始绘制任务 108 * step2: 开始绘制任务
117 * @param { Array } drawTasks 待绘制任务 109 * @param { Array } drawTasks 待绘制任务
118 */ 110 */
119 - const startDrawing = async (drawTasks) => { 111 + const startDrawing = async drawTasks => {
120 // TODO: check 112 // TODO: check
121 // const configHeight = getHeight(config) 113 // const configHeight = getHeight(config)
122 const { ctx, canvas } = await initCanvas() 114 const { ctx, canvas } = await initCanvas()
...@@ -135,22 +127,22 @@ export default defineComponent({ ...@@ -135,22 +127,22 @@ export default defineComponent({
135 // 将要画的方块、文字、线条放进队列数组 127 // 将要画的方块、文字、线条放进队列数组
136 const queue = drawTasks 128 const queue = drawTasks
137 .concat( 129 .concat(
138 - texts.map((item) => { 130 + texts.map(item => {
139 - item.type = "text" 131 + item.type = 'text'
140 item.zIndex = item.zIndex || 0 132 item.zIndex = item.zIndex || 0
141 return item 133 return item
142 }) 134 })
143 ) 135 )
144 .concat( 136 .concat(
145 - blocks.map((item) => { 137 + blocks.map(item => {
146 - item.type = "block" 138 + item.type = 'block'
147 item.zIndex = item.zIndex || 0 139 item.zIndex = item.zIndex || 0
148 return item 140 return item
149 }) 141 })
150 ) 142 )
151 .concat( 143 .concat(
152 - lines.map((item) => { 144 + lines.map(item => {
153 - item.type = "line" 145 + item.type = 'line'
154 item.zIndex = item.zIndex || 0 146 item.zIndex = item.zIndex || 0
155 return item 147 return item
156 }) 148 })
...@@ -162,15 +154,15 @@ export default defineComponent({ ...@@ -162,15 +154,15 @@ export default defineComponent({
162 canvas, 154 canvas,
163 ctx, 155 ctx,
164 toPx, 156 toPx,
165 - toRpx, 157 + toRpx
166 } 158 }
167 - if (queue[i].type === "image") { 159 + if (queue[i].type === 'image') {
168 await drawImage(queue[i], drawOptions) 160 await drawImage(queue[i], drawOptions)
169 - } else if (queue[i].type === "text") { 161 + } else if (queue[i].type === 'text') {
170 drawText(queue[i], drawOptions) 162 drawText(queue[i], drawOptions)
171 - } else if (queue[i].type === "block") { 163 + } else if (queue[i].type === 'block') {
172 drawBlock(queue[i], drawOptions) 164 drawBlock(queue[i], drawOptions)
173 - } else if (queue[i].type === "line") { 165 + } else if (queue[i].type === 'line') {
174 drawLine(queue[i], drawOptions) 166 drawLine(queue[i], drawOptions)
175 } 167 }
176 } 168 }
...@@ -182,21 +174,22 @@ export default defineComponent({ ...@@ -182,21 +174,22 @@ export default defineComponent({
182 174
183 // start: 初始化 canvas 实例 && 下载图片资源 175 // start: 初始化 canvas 实例 && 下载图片资源
184 const init = () => { 176 const init = () => {
185 - if (props.showLoading) 177 + if (props.showLoading) {
186 - Taro.showLoading({ mask: true, title: "生成中..." }) 178 + Taro.showLoading({ mask: true, title: '生成中...' })
179 + }
187 if (props.config?.images?.length) { 180 if (props.config?.images?.length) {
188 initImages(props.config.images) 181 initImages(props.config.images)
189 - .then((result) => { 182 + .then(result => {
190 // 1. 下载图片资源 183 // 1. 下载图片资源
191 startDrawing(result) 184 startDrawing(result)
192 }) 185 })
193 - .catch((err) => { 186 + .catch(err => {
194 Taro.hideLoading() 187 Taro.hideLoading()
195 Taro.showToast({ 188 Taro.showToast({
196 - icon: "none", 189 + icon: 'none',
197 - title: err.errMsg || "下载图片失败", 190 + title: err.errMsg || '下载图片失败'
198 }) 191 })
199 - context.emit("fail", err) 192 + context.emit('fail', err)
200 }) 193 })
201 } else { 194 } else {
202 startDrawing([]) 195 startDrawing([])
...@@ -211,8 +204,8 @@ export default defineComponent({ ...@@ -211,8 +204,8 @@ export default defineComponent({
211 canvasId, 204 canvasId,
212 debug, 205 debug,
213 width, 206 width,
214 - height, 207 + height
215 } 208 }
216 - }, 209 + }
217 }) 210 })
218 </script> 211 </script>
......
...@@ -2,7 +2,9 @@ import { getLinearColor, getTextX, toPx } from './tools' ...@@ -2,7 +2,9 @@ import { getLinearColor, getTextX, toPx } from './tools'
2 2
3 const drawRadiusRect = ({ x, y, w, h, r }, { ctx }) => { 3 const drawRadiusRect = ({ x, y, w, h, r }, { ctx }) => {
4 const minSize = Math.min(w, h) 4 const minSize = Math.min(w, h)
5 - if (r > minSize / 2) r = minSize / 2 5 + if (r > minSize / 2) {
6 + r = minSize / 2
7 + }
6 ctx.beginPath() 8 ctx.beginPath()
7 ctx.moveTo(x + r, y) 9 ctx.moveTo(x + r, y)
8 ctx.arcTo(x + w, y, x + w, y + h, r) 10 ctx.arcTo(x + w, y, x + w, y + h, r)
...@@ -120,10 +122,7 @@ const drawSingleText = (drawData, drawOptions) => { ...@@ -120,10 +122,7 @@ const drawSingleText = (drawData, drawOptions) => {
120 122
121 if (restWidth < 0) { 123 if (restWidth < 0) {
122 if (line === lineNum) { 124 if (line === lineNum) {
123 - if ( 125 + if (restWidth + ctx.measureText(text[i + 1]).width > ctx.measureText('...').width) {
124 - restWidth + ctx.measureText(text[i + 1]).width >
125 - ctx.measureText('...').width
126 - ) {
127 fillText = `${fillText}...` 126 fillText = `${fillText}...`
128 } else { 127 } else {
129 fillText = `${fillText.substr(0, fillText.length - 1)}...` 128 fillText = `${fillText.substr(0, fillText.length - 1)}...`
...@@ -145,11 +144,7 @@ const drawSingleText = (drawData, drawOptions) => { ...@@ -145,11 +144,7 @@ const drawSingleText = (drawData, drawOptions) => {
145 } 144 }
146 145
147 textArr.forEach((item, index) => 146 textArr.forEach((item, index) =>
148 - ctx.fillText( 147 + ctx.fillText(item, getTextX(textAlign, x, width), y + (lineHeight || fontSize) * index)
149 - item,
150 - getTextX(textAlign, x, width),
151 - y + (lineHeight || fontSize) * index
152 - )
153 ) 148 )
154 ctx.restore() 149 ctx.restore()
155 150
...@@ -172,7 +167,7 @@ export function drawText(params, drawOptions) { ...@@ -172,7 +167,7 @@ export function drawText(params, drawOptions) {
172 const { x = 0, y = 0, text, baseLine } = params 167 const { x = 0, y = 0, text, baseLine } = params
173 if (Object.prototype.toString.call(text) === '[object Array]') { 168 if (Object.prototype.toString.call(text) === '[object Array]') {
174 const preText = { x, y, baseLine } 169 const preText = { x, y, baseLine }
175 - text.forEach((item) => { 170 + text.forEach(item => {
176 preText.x += item.marginLeft || 0 171 preText.x += item.marginLeft || 0
177 const textWidth = drawSingleText( 172 const textWidth = drawSingleText(
178 Object.assign(item, { ...preText, y: y + (item.marginTop || 0) }), 173 Object.assign(item, { ...preText, y: y + (item.marginTop || 0) }),
...@@ -188,7 +183,9 @@ export function drawText(params, drawOptions) { ...@@ -188,7 +183,9 @@ export function drawText(params, drawOptions) {
188 export function drawLine(drawData, drawOptions) { 183 export function drawLine(drawData, drawOptions) {
189 const { startX, startY, endX, endY, color, width } = drawData 184 const { startX, startY, endX, endY, color, width } = drawData
190 const { ctx } = drawOptions 185 const { ctx } = drawOptions
191 - if (!width) return 186 + if (!width) {
187 + return
188 + }
192 ctx.save() 189 ctx.save()
193 ctx.beginPath() 190 ctx.beginPath()
194 ctx.strokeStyle = color 191 ctx.strokeStyle = color
...@@ -225,10 +222,7 @@ export function drawBlock(data, drawOptions) { ...@@ -225,10 +222,7 @@ export function drawBlock(data, drawOptions) {
225 let textY = 0 222 let textY = 0
226 223
227 if (text) { 224 if (text) {
228 - const textWidth = getTextWidth( 225 + const textWidth = getTextWidth(typeof text.text === 'string' ? text : text.text, drawOptions)
229 - typeof text.text === 'string' ? text : text.text,
230 - drawOptions
231 - )
232 blockWidth = textWidth > width ? textWidth : width 226 blockWidth = textWidth > width ? textWidth : width
233 blockWidth += paddingLeft + paddingLeft 227 blockWidth += paddingLeft + paddingLeft
234 228
...@@ -296,7 +290,7 @@ export function drawBlock(data, drawOptions) { ...@@ -296,7 +290,7 @@ export function drawBlock(data, drawOptions) {
296 } 290 }
297 291
298 export const drawImage = (data, drawOptions) => 292 export const drawImage = (data, drawOptions) =>
299 - new Promise((resolve) => { 293 + new Promise(resolve => {
300 const { canvas, ctx } = drawOptions 294 const { canvas, ctx } = drawOptions
301 const { 295 const {
302 x, 296 x,
......
...@@ -59,8 +59,7 @@ export const getFactor = () => { ...@@ -59,8 +59,7 @@ export const getFactor = () => {
59 * @param {number} factor 换算系数 59 * @param {number} factor 换算系数
60 * @returns {number} px 值(整数) 60 * @returns {number} px 值(整数)
61 */ 61 */
62 -export const toPx = (rpx, factor = getFactor()) => 62 +export const toPx = (rpx, factor = getFactor()) => parseInt(String(rpx * factor), 10)
63 - parseInt(String(rpx * factor), 10)
64 63
65 /** 64 /**
66 * @description px 转 rpx 65 * @description px 转 rpx
...@@ -68,8 +67,7 @@ export const toPx = (rpx, factor = getFactor()) => ...@@ -68,8 +67,7 @@ export const toPx = (rpx, factor = getFactor()) =>
68 * @param {number} factor 换算系数 67 * @param {number} factor 换算系数
69 * @returns {number} rpx 值(整数) 68 * @returns {number} rpx 值(整数)
70 */ 69 */
71 -export const toRpx = (px, factor = getFactor()) => 70 +export const toRpx = (px, factor = getFactor()) => parseInt(String(px / factor), 10)
72 - parseInt(String(px / factor), 10)
73 71
74 /** 72 /**
75 * @description 下载图片到本地临时路径(避免跨域/协议限制) 73 * @description 下载图片到本地临时路径(避免跨域/协议限制)
...@@ -80,17 +78,15 @@ export const toRpx = (px, factor = getFactor()) => ...@@ -80,17 +78,15 @@ export const toRpx = (px, factor = getFactor()) =>
80 export function downImage(url) { 78 export function downImage(url) {
81 return new Promise((resolve, reject) => { 79 return new Promise((resolve, reject) => {
82 const wx_user_data_path = 80 const wx_user_data_path =
83 - (typeof wx !== 'undefined' && wx && wx.env && wx.env.USER_DATA_PATH) 81 + typeof wx !== 'undefined' && wx && wx.env && wx.env.USER_DATA_PATH
84 ? wx.env.USER_DATA_PATH 82 ? wx.env.USER_DATA_PATH
85 : '' 83 : ''
86 - const is_local_user_path = wx_user_data_path 84 + const is_local_user_path = wx_user_data_path ? new RegExp(wx_user_data_path).test(url) : false
87 - ? new RegExp(wx_user_data_path).test(url)
88 - : false
89 85
90 if (/^http/.test(url) && !is_local_user_path) { 86 if (/^http/.test(url) && !is_local_user_path) {
91 Taro.downloadFile({ 87 Taro.downloadFile({
92 url: mapHttpToHttps(url), 88 url: mapHttpToHttps(url),
93 - success: (res) => { 89 + success: res => {
94 if (res.statusCode === 200) { 90 if (res.statusCode === 200) {
95 resolve(res.tempFilePath) 91 resolve(res.tempFilePath)
96 } else { 92 } else {
...@@ -116,9 +112,9 @@ export function downImage(url) { ...@@ -116,9 +112,9 @@ export function downImage(url) {
116 export const getImageInfo = (item, index) => 112 export const getImageInfo = (item, index) =>
117 new Promise((resolve, reject) => { 113 new Promise((resolve, reject) => {
118 const { x, y, width, height, url, zIndex } = item 114 const { x, y, width, height, url, zIndex } = item
119 - downImage(url).then((imgPath) => 115 + downImage(url).then(imgPath =>
120 Taro.getImageInfo({ src: imgPath }) 116 Taro.getImageInfo({ src: imgPath })
121 - .then((imgInfo) => { 117 + .then(imgInfo => {
122 let sx 118 let sx
123 let sy 119 let sy
124 const borderRadius = item.borderRadius || 0 120 const borderRadius = item.borderRadius || 0
...@@ -150,7 +146,7 @@ export const getImageInfo = (item, index) => ...@@ -150,7 +146,7 @@ export const getImageInfo = (item, index) =>
150 } 146 }
151 resolve(result) 147 resolve(result)
152 }) 148 })
153 - .catch((err) => { 149 + .catch(err => {
154 reject(err) 150 reject(err)
155 }) 151 })
156 ) 152 )
......
1 <template> 1 <template>
2 <view class="index-nav" :class="[`is-${position}`]"> 2 <view class="index-nav" :class="[`is-${position}`]">
3 - <view class="nav-logo is-home" :class="{ 'is-active': active === 'home' }" @tap="() => on_select('home')"> 3 + <view
4 + class="nav-logo is-home"
5 + :class="{ 'is-active': active === 'home' }"
6 + @tap="() => on_select('home')"
7 + >
4 <view class="nav-icon-wrap"> 8 <view class="nav-icon-wrap">
5 <image class="nav-icon" :src="icons?.home" mode="aspectFit" /> 9 <image class="nav-icon" :src="icons?.home" mode="aspectFit" />
6 </view> 10 </view>
...@@ -9,7 +13,10 @@ ...@@ -9,7 +13,10 @@
9 13
10 <view 14 <view
11 class="nav-logo is-code" 15 class="nav-logo is-code"
12 - :class="[{ 'is-active': active === 'code' }, { 'is-center-raised': center_variant === 'raised' }]" 16 + :class="[
17 + { 'is-active': active === 'code' },
18 + { 'is-center-raised': center_variant === 'raised' }
19 + ]"
13 @tap="() => on_select('code')" 20 @tap="() => on_select('code')"
14 > 21 >
15 <view class="nav-icon-wrap"> 22 <view class="nav-icon-wrap">
...@@ -23,7 +30,11 @@ ...@@ -23,7 +30,11 @@
23 <text class="nav-text">预约码</text> 30 <text class="nav-text">预约码</text>
24 </view> 31 </view>
25 32
26 - <view class="nav-logo is-me" :class="{ 'is-active': active === 'me' }" @tap="() => on_select('me')"> 33 + <view
34 + class="nav-logo is-me"
35 + :class="{ 'is-active': active === 'me' }"
36 + @tap="() => on_select('me')"
37 + >
27 <view class="nav-icon-wrap"> 38 <view class="nav-icon-wrap">
28 <image class="nav-icon" :src="icons?.me" mode="aspectFit" /> 39 <image class="nav-icon" :src="icons?.me" mode="aspectFit" />
29 </view> 40 </view>
...@@ -58,8 +69,10 @@ const props = defineProps({ ...@@ -58,8 +69,10 @@ const props = defineProps({
58 69
59 const emit = defineEmits(['select']) 70 const emit = defineEmits(['select'])
60 71
61 -const on_select = (key) => { 72 +const on_select = key => {
62 - if (!props.allow_active_tap && props.active && key === props.active) return 73 + if (!props.allow_active_tap && props.active && key === props.active) {
74 + return
75 + }
63 emit('select', key) 76 emit('select', key)
64 } 77 }
65 </script> 78 </script>
...@@ -74,12 +87,12 @@ const on_select = (key) => { ...@@ -74,12 +87,12 @@ const on_select = (key) => {
74 padding-bottom: calc(0rpx + constant(safe-area-inset-bottom)); 87 padding-bottom: calc(0rpx + constant(safe-area-inset-bottom));
75 padding-bottom: calc(0rpx + env(safe-area-inset-bottom)); 88 padding-bottom: calc(0rpx + env(safe-area-inset-bottom));
76 box-sizing: border-box; 89 box-sizing: border-box;
77 - background: #FFFFFF; 90 + background: #ffffff;
78 box-shadow: 0 -8rpx 8rpx 0 rgba(0, 0, 0, 0.1); 91 box-shadow: 0 -8rpx 8rpx 0 rgba(0, 0, 0, 0.1);
79 display: flex; 92 display: flex;
80 align-items: flex-end; 93 align-items: flex-end;
81 justify-content: space-around; 94 justify-content: space-around;
82 - color: #A67939; 95 + color: #a67939;
83 z-index: 99; 96 z-index: 99;
84 97
85 &.is-fixed { 98 &.is-fixed {
......
...@@ -16,8 +16,8 @@ ...@@ -16,8 +16,8 @@
16 <image :src="icon_2" /> 16 <image :src="icon_2" />
17 </view> 17 </view>
18 </view> 18 </view>
19 - <view style="color: red; margin-top: 32rpx;">{{ userinfo.datetime }}</view> 19 + <view style="color: red; margin-top: 32rpx">{{ userinfo.datetime }}</view>
20 - <view style="color: #999; font-size: 24rpx; margin-top: 10rpx;">(离线模式)</view> 20 + <view style="color: #999; font-size: 24rpx; margin-top: 10rpx">(离线模式)</view>
21 </view> 21 </view>
22 <view class="user-list"> 22 <view class="user-list">
23 <view 23 <view
...@@ -27,14 +27,15 @@ ...@@ -27,14 +27,15 @@
27 :class="[ 27 :class="[
28 'user-item', 28 'user-item',
29 select_index === index ? 'checked' : '', 29 select_index === index ? 'checked' : '',
30 - userList.length > 1 && item.sort ? 'border' : '', 30 + userList.length > 1 && item.sort ? 'border' : ''
31 - ]"> 31 + ]"
32 + >
32 {{ item.name }} 33 {{ item.name }}
33 </view> 34 </view>
34 </view> 35 </view>
35 </view> 36 </view>
36 <view v-else class="no-qrcode"> 37 <view v-else class="no-qrcode">
37 - <image :src="icon_3" style="width: 320rpx; height: 320rpx;" /> 38 + <image :src="icon_3" style="width: 320rpx; height: 320rpx" />
38 <view class="no-qrcode-title">本地无缓存预约记录</view> 39 <view class="no-qrcode-title">本地无缓存预约记录</view>
39 </view> 40 </view>
40 </view> 41 </view>
...@@ -54,101 +55,104 @@ const props = defineProps({ ...@@ -54,101 +55,104 @@ const props = defineProps({
54 type: Array, 55 type: Array,
55 default: () => [] 56 default: () => []
56 } 57 }
57 -}); 58 +})
58 59
59 -const select_index = ref(0); 60 +const select_index = ref(0)
60 -const userList = ref([]); 61 +const userList = ref([])
61 -const qrCodeImages = ref({}); // 存储生成的二维码图片 base64 62 +const qrCodeImages = ref({}) // 存储生成的二维码图片 base64
62 63
63 const prevCode = () => { 64 const prevCode = () => {
64 - select_index.value = select_index.value - 1; 65 + select_index.value = select_index.value - 1
65 if (select_index.value < 0) { 66 if (select_index.value < 0) {
66 - select_index.value = userList.value.length - 1; 67 + select_index.value = userList.value.length - 1
67 } 68 }
68 -}; 69 +}
69 const nextCode = () => { 70 const nextCode = () => {
70 - select_index.value = select_index.value + 1; 71 + select_index.value = select_index.value + 1
71 if (select_index.value > userList.value.length - 1) { 72 if (select_index.value > userList.value.length - 1) {
72 - select_index.value = 0; 73 + select_index.value = 0
73 } 74 }
74 -}; 75 +}
75 76
76 function replaceMiddleCharacters(inputString) { 77 function replaceMiddleCharacters(inputString) {
77 if (!inputString || inputString.length < 15) { 78 if (!inputString || inputString.length < 15) {
78 - return inputString; 79 + return inputString
79 } 80 }
80 - const start = Math.floor((inputString.length - 8) / 2); 81 + const start = Math.floor((inputString.length - 8) / 2)
81 - const end = start + 8; 82 + const end = start + 8
82 - const replacement = '*'.repeat(8); 83 + const replacement = '*'.repeat(8)
83 - return inputString.substring(0, start) + replacement + inputString.substring(end); 84 + return inputString.substring(0, start) + replacement + inputString.substring(end)
84 } 85 }
85 86
86 -const formatId = (id) => replaceMiddleCharacters(id); 87 +const formatId = id => replaceMiddleCharacters(id)
87 88
88 const userinfo = computed(() => { 89 const userinfo = computed(() => {
89 return { 90 return {
90 name: userList.value[select_index.value]?.name, 91 name: userList.value[select_index.value]?.name,
91 id: formatId(userList.value[select_index.value]?.id_number), 92 id: formatId(userList.value[select_index.value]?.id_number),
92 - datetime: userList.value[select_index.value]?.datetime, 93 + datetime: userList.value[select_index.value]?.datetime
93 - }; 94 + }
94 -}); 95 +})
95 96
96 const currentQrCodeUrl = computed(() => { 97 const currentQrCodeUrl = computed(() => {
97 - const key = userList.value[select_index.value]?.qr_code; 98 + const key = userList.value[select_index.value]?.qr_code
98 - return qrCodeImages.value[key] || ''; 99 + return qrCodeImages.value[key] || ''
99 }) 100 })
100 101
101 -const selectUser = (index) => { 102 +const selectUser = index => {
102 - select_index.value = index; 103 + select_index.value = index
103 } 104 }
104 105
105 const generateQrCodes = () => { 106 const generateQrCodes = () => {
106 - for (const item of userList.value) { 107 + for (const item of userList.value) {
107 - if (item.qr_code && !qrCodeImages.value[item.qr_code]) { 108 + if (item.qr_code && !qrCodeImages.value[item.qr_code]) {
108 - try { 109 + try {
109 - // 使用 create + SVG 手动生成,避免 Taro 中 Canvas 依赖问题 110 + // 使用 create + SVG 手动生成,避免 Taro 中 Canvas 依赖问题
110 - const qr = QRCode.create(item.qr_code, { errorCorrectionLevel: 'M' }); 111 + const qr = QRCode.create(item.qr_code, { errorCorrectionLevel: 'M' })
111 - const size = qr.modules.size; 112 + const size = qr.modules.size
112 - let d = ''; 113 + let d = ''
113 - for (let row = 0; row < size; row++) { 114 + for (let row = 0; row < size; row++) {
114 - for (let col = 0; col < size; col++) { 115 + for (let col = 0; col < size; col++) {
115 - if (qr.modules.get(col, row)) { 116 + if (qr.modules.get(col, row)) {
116 - d += `M${col},${row}h1v1h-1z`; 117 + d += `M${col},${row}h1v1h-1z`
117 - }
118 - }
119 - }
120 - const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${size} ${size}"><path d="${d}" fill="#000"/></svg>`;
121 -
122 - // 转 Base64
123 - const buffer = new ArrayBuffer(svg.length);
124 - const view = new Uint8Array(buffer);
125 - for (let i = 0; i < svg.length; i++) {
126 - view[i] = svg.charCodeAt(i);
127 - }
128 - const b64 = Taro.arrayBufferToBase64(buffer);
129 -
130 - qrCodeImages.value[item.qr_code] = `data:image/svg+xml;base64,${b64}`;
131 - } catch (err) {
132 - console.error('QR Gen Error', err);
133 } 118 }
119 + }
120 + }
121 + const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${size} ${size}"><path d="${d}" fill="#000"/></svg>`
122 +
123 + // 转 Base64
124 + const buffer = new ArrayBuffer(svg.length)
125 + const view = new Uint8Array(buffer)
126 + for (let i = 0; i < svg.length; i++) {
127 + view[i] = svg.charCodeAt(i)
134 } 128 }
129 + const b64 = Taro.arrayBufferToBase64(buffer)
130 +
131 + qrCodeImages.value[item.qr_code] = `data:image/svg+xml;base64,${b64}`
132 + } catch (err) {
133 + console.error('QR Gen Error', err)
134 + }
135 } 135 }
136 + }
136 } 137 }
137 138
138 onMounted(() => { 139 onMounted(() => {
139 - if (props.list && props.list.length > 0) { 140 + if (props.list && props.list.length > 0) {
140 - userList.value = props.list; 141 + userList.value = props.list
141 - generateQrCodes(); 142 + generateQrCodes()
142 - } 143 + }
143 -}); 144 +})
144 145
145 -watch(() => props.list, (newVal) => { 146 +watch(
147 + () => props.list,
148 + newVal => {
146 if (newVal && newVal.length > 0) { 149 if (newVal && newVal.length > 0) {
147 - userList.value = newVal; 150 + userList.value = newVal
148 - generateQrCodes(); 151 + generateQrCodes()
149 } 152 }
150 -}, { deep: true }); 153 + },
151 - 154 + { deep: true }
155 +)
152 </script> 156 </script>
153 157
154 <style lang="less"> 158 <style lang="less">
...@@ -159,12 +163,12 @@ watch(() => props.list, (newVal) => { ...@@ -159,12 +163,12 @@ watch(() => props.list, (newVal) => {
159 flex-direction: column; 163 flex-direction: column;
160 justify-content: center; 164 justify-content: center;
161 align-items: center; 165 align-items: center;
162 - background-color: #FFF; 166 + background-color: #fff;
163 border-radius: 16rpx; 167 border-radius: 16rpx;
164 - box-shadow: 0 0 29rpx 0 rgba(106,106,106,0.27); 168 + box-shadow: 0 0 29rpx 0 rgba(106, 106, 106, 0.27);
165 169
166 .user-info { 170 .user-info {
167 - color: #A6A6A6; 171 + color: #a6a6a6;
168 font-size: 37rpx; 172 font-size: 37rpx;
169 margin-top: 16rpx; 173 margin-top: 16rpx;
170 margin-bottom: 16rpx; 174 margin-bottom: 16rpx;
...@@ -180,21 +184,25 @@ watch(() => props.list, (newVal) => { ...@@ -180,21 +184,25 @@ watch(() => props.list, (newVal) => {
180 align-items: center; 184 align-items: center;
181 .left { 185 .left {
182 image { 186 image {
183 - width: 56rpx; height: 56rpx; margin-right: 16rpx; 187 + width: 56rpx;
188 + height: 56rpx;
189 + margin-right: 16rpx;
184 } 190 }
185 } 191 }
186 .center { 192 .center {
187 - border: 2rpx solid #D1D1D1; 193 + border: 2rpx solid #d1d1d1;
188 border-radius: 40rpx; 194 border-radius: 40rpx;
189 padding: 46rpx; 195 padding: 46rpx;
190 position: relative; 196 position: relative;
191 image { 197 image {
192 - width: 400rpx; height: 400rpx; 198 + width: 400rpx;
199 + height: 400rpx;
193 } 200 }
194 } 201 }
195 .right { 202 .right {
196 image { 203 image {
197 - width: 56rpx; height: 56rpx; 204 + width: 56rpx;
205 + height: 56rpx;
198 margin-left: 16rpx; 206 margin-left: 16rpx;
199 } 207 }
200 } 208 }
...@@ -208,13 +216,13 @@ watch(() => props.list, (newVal) => { ...@@ -208,13 +216,13 @@ watch(() => props.list, (newVal) => {
208 .user-item { 216 .user-item {
209 position: relative; 217 position: relative;
210 padding: 8rpx 16rpx; 218 padding: 8rpx 16rpx;
211 - border: 2rpx solid #A67939; 219 + border: 2rpx solid #a67939;
212 margin: 8rpx; 220 margin: 8rpx;
213 border-radius: 10rpx; 221 border-radius: 10rpx;
214 - color: #A67939; 222 + color: #a67939;
215 &.checked { 223 &.checked {
216 - color: #FFF; 224 + color: #fff;
217 - background-color: #A67939; 225 + background-color: #a67939;
218 } 226 }
219 &.border { 227 &.border {
220 margin-right: 16rpx; 228 margin-right: 16rpx;
...@@ -224,7 +232,7 @@ watch(() => props.list, (newVal) => { ...@@ -224,7 +232,7 @@ watch(() => props.list, (newVal) => {
224 top: calc(50% - 16rpx); 232 top: calc(50% - 16rpx);
225 content: ''; 233 content: '';
226 height: 32rpx; 234 height: 32rpx;
227 - border-right: 2rpx solid #A67939; 235 + border-right: 2rpx solid #a67939;
228 } 236 }
229 } 237 }
230 } 238 }
...@@ -238,7 +246,7 @@ watch(() => props.list, (newVal) => { ...@@ -238,7 +246,7 @@ watch(() => props.list, (newVal) => {
238 margin-bottom: 32rpx; 246 margin-bottom: 32rpx;
239 247
240 .no-qrcode-title { 248 .no-qrcode-title {
241 - color: #A67939; 249 + color: #a67939;
242 font-size: 34rpx; 250 font-size: 34rpx;
243 } 251 }
244 } 252 }
......
...@@ -16,7 +16,10 @@ ...@@ -16,7 +16,10 @@
16 </view> 16 </view>
17 <view class="center"> 17 <view class="center">
18 <image :src="currentQrCodeUrl" mode="aspectFit" /> 18 <image :src="currentQrCodeUrl" mode="aspectFit" />
19 - <view v-if="useStatus === STATUS_CODE.CANCELED || useStatus === STATUS_CODE.USED" class="qrcode-used"> 19 + <view
20 + v-if="useStatus === STATUS_CODE.CANCELED || useStatus === STATUS_CODE.USED"
21 + class="qrcode-used"
22 + >
20 <view class="overlay"></view> 23 <view class="overlay"></view>
21 <text class="status-text">二维码{{ get_qrcode_status_text(useStatus) }}</text> 24 <text class="status-text">二维码{{ get_qrcode_status_text(useStatus) }}</text>
22 </view> 25 </view>
...@@ -25,7 +28,7 @@ ...@@ -25,7 +28,7 @@
25 <image src="https://cdn.ipadbiz.cn/xys/booking/%E5%8F%B3@2x.png" /> 28 <image src="https://cdn.ipadbiz.cn/xys/booking/%E5%8F%B3@2x.png" />
26 </view> 29 </view>
27 </view> 30 </view>
28 - <view style="color: red; margin-top: 32rpx;">{{ userinfo.datetime }}</view> 31 + <view style="color: red; margin-top: 32rpx">{{ userinfo.datetime }}</view>
29 </view> 32 </view>
30 <view class="user-list"> 33 <view class="user-list">
31 <view 34 <view
...@@ -35,16 +38,24 @@ ...@@ -35,16 +38,24 @@
35 :class="[ 38 :class="[
36 'user-item', 39 'user-item',
37 select_index === index ? 'checked' : '', 40 select_index === index ? 'checked' : '',
38 - userList.length > 1 && item.sort ? 'border' : '', 41 + userList.length > 1 && item.sort ? 'border' : ''
39 - ]"> 42 + ]"
43 + >
40 {{ item.name }} 44 {{ item.name }}
41 </view> 45 </view>
42 </view> 46 </view>
43 </view> 47 </view>
44 <view v-else class="no-qrcode"> 48 <view v-else class="no-qrcode">
45 - <image src="https://cdn.ipadbiz.cn/xys/booking/%E6%9A%82%E6%97%A0@2x.png" style="width: 320rpx; height: 320rpx;" /> 49 + <image
50 + src="https://cdn.ipadbiz.cn/xys/booking/%E6%9A%82%E6%97%A0@2x.png"
51 + style="width: 320rpx; height: 320rpx"
52 + />
46 <view class="no-qrcode-title">今天没有预约记录</view> 53 <view class="no-qrcode-title">今天没有预约记录</view>
47 - <view style="text-align: center; color: #A67939; margin-top: 16rpx;">查看我的“<text @tap="toRecord" style="text-decoration: underline; color: #ED9820;">预约记录</text>”</view> 54 + <view style="text-align: center; color: #a67939; margin-top: 16rpx"
55 + >查看我的“<text @tap="toRecord" style="text-decoration: underline; color: #ed9820"
56 + >预约记录</text
57 + >”</view
58 + >
48 </view> 59 </view>
49 </view> 60 </view>
50 </template> 61 </template>
...@@ -52,12 +63,12 @@ ...@@ -52,12 +63,12 @@
52 <script setup> 63 <script setup>
53 import { ref, computed, watch, onMounted, onUnmounted } from 'vue' 64 import { ref, computed, watch, onMounted, onUnmounted } from 'vue'
54 import Taro from '@tarojs/taro' 65 import Taro from '@tarojs/taro'
55 -import { formatDatetime, mask_id_number, get_qrcode_status_text } from '@/utils/tools'; 66 +import { formatDatetime, mask_id_number, get_qrcode_status_text } from '@/utils/tools'
56 import { qrcodeListAPI, qrcodeStatusAPI, billPersonAPI } from '@/api/index' 67 import { qrcodeListAPI, qrcodeStatusAPI, billPersonAPI } from '@/api/index'
57 import { useGo } from '@/hooks/useGo' 68 import { useGo } from '@/hooks/useGo'
58 -import BASE_URL from '@/utils/config'; 69 +import BASE_URL from '@/utils/config'
59 70
60 -const go = useGo(); 71 +const go = useGo()
61 72
62 const props = defineProps({ 73 const props = defineProps({
63 status: { 74 status: {
...@@ -68,80 +79,81 @@ const props = defineProps({ ...@@ -68,80 +79,81 @@ const props = defineProps({
68 type: String, 79 type: String,
69 default: '' 80 default: ''
70 }, 81 },
71 - payId: { // 接收 payId 82 + payId: {
83 + // 接收 payId
72 type: String, 84 type: String,
73 default: '' 85 default: ''
74 } 86 }
75 -}); 87 +})
76 88
77 -const select_index = ref(0); 89 +const select_index = ref(0)
78 -const userList = ref([]); 90 +const userList = ref([])
79 91
80 /** 92 /**
81 * @description 切换到上一张二维码(循环) 93 * @description 切换到上一张二维码(循环)
82 * @returns {void} 无返回值 94 * @returns {void} 无返回值
83 */ 95 */
84 const prevCode = () => { 96 const prevCode = () => {
85 - select_index.value = select_index.value - 1; 97 + select_index.value = select_index.value - 1
86 if (select_index.value < 0) { 98 if (select_index.value < 0) {
87 - select_index.value = userList.value.length - 1; 99 + select_index.value = userList.value.length - 1
88 } 100 }
89 -}; 101 +}
90 102
91 /** 103 /**
92 * @description 切换到下一张二维码(循环) 104 * @description 切换到下一张二维码(循环)
93 * @returns {void} 无返回值 105 * @returns {void} 无返回值
94 */ 106 */
95 const nextCode = () => { 107 const nextCode = () => {
96 - select_index.value = select_index.value + 1; 108 + select_index.value = select_index.value + 1
97 if (select_index.value > userList.value.length - 1) { 109 if (select_index.value > userList.value.length - 1) {
98 - select_index.value = 0; 110 + select_index.value = 0
99 } 111 }
100 -}; 112 +}
101 113
102 watch( 114 watch(
103 () => select_index.value, 115 () => select_index.value,
104 () => { 116 () => {
105 - refreshBtn(); 117 + refreshBtn()
106 } 118 }
107 ) 119 )
108 120
109 watch( 121 watch(
110 () => props.payId, 122 () => props.payId,
111 - (val) => { 123 + val => {
112 if (val) { 124 if (val) {
113 - init(); 125 + init()
114 } 126 }
115 }, 127 },
116 { immediate: true } 128 { immediate: true }
117 ) 129 )
118 130
119 -const formatId = (id) => mask_id_number(id) 131 +const formatId = id => mask_id_number(id)
120 132
121 const userinfo = computed(() => { 133 const userinfo = computed(() => {
122 return { 134 return {
123 name: userList.value[select_index.value]?.name, 135 name: userList.value[select_index.value]?.name,
124 id: formatId(userList.value[select_index.value]?.id_number), 136 id: formatId(userList.value[select_index.value]?.id_number),
125 - datetime: userList.value[select_index.value]?.datetime, 137 + datetime: userList.value[select_index.value]?.datetime
126 - }; 138 + }
127 -}); 139 +})
128 140
129 const currentQrCodeUrl = computed(() => { 141 const currentQrCodeUrl = computed(() => {
130 - const url = userList.value[select_index.value]?.qr_code_url; 142 + const url = userList.value[select_index.value]?.qr_code_url
131 - if (url && url.startsWith('/')) { 143 + if (url && url.startsWith('/')) {
132 - return BASE_URL + url; 144 + return BASE_URL + url
133 - } 145 + }
134 - return url; 146 + return url
135 }) 147 })
136 148
137 -const useStatus = ref('0'); 149 +const useStatus = ref('0')
138 150
139 const STATUS_CODE = { 151 const STATUS_CODE = {
140 APPLY: '1', 152 APPLY: '1',
141 SUCCESS: '3', 153 SUCCESS: '3',
142 CANCELED: '5', 154 CANCELED: '5',
143 - USED: '7', 155 + USED: '7'
144 -}; 156 +}
145 157
146 /** 158 /**
147 * @description 刷新当前选中二维码状态 159 * @description 刷新当前选中二维码状态
...@@ -149,10 +161,14 @@ const STATUS_CODE = { ...@@ -149,10 +161,14 @@ const STATUS_CODE = {
149 * @returns {Promise<void>} 无返回值 161 * @returns {Promise<void>} 无返回值
150 */ 162 */
151 const refreshBtn = async () => { 163 const refreshBtn = async () => {
152 - if (!userList.value[select_index.value]) return; 164 + if (!userList.value[select_index.value]) {
153 - const { code, data } = await qrcodeStatusAPI({ qr_code: userList.value[select_index.value].qr_code }); 165 + return
166 + }
167 + const { code, data } = await qrcodeStatusAPI({
168 + qr_code: userList.value[select_index.value].qr_code
169 + })
154 if (code) { 170 if (code) {
155 - useStatus.value = data.status; 171 + useStatus.value = data.status
156 } 172 }
157 } 173 }
158 174
...@@ -161,8 +177,8 @@ const refreshBtn = async () => { ...@@ -161,8 +177,8 @@ const refreshBtn = async () => {
161 * @param {number} index 下标 177 * @param {number} index 下标
162 * @returns {void} 无返回值 178 * @returns {void} 无返回值
163 */ 179 */
164 -const selectUser = (index) => { 180 +const selectUser = index => {
165 - select_index.value = index; 181 + select_index.value = index
166 } 182 }
167 183
168 /** 184 /**
...@@ -171,17 +187,17 @@ const selectUser = (index) => { ...@@ -171,17 +187,17 @@ const selectUser = (index) => {
171 * @param {Array<Object>} data 二维码列表 187 * @param {Array<Object>} data 二维码列表
172 * @returns {Array<Object>} 处理后的列表 188 * @returns {Array<Object>} 处理后的列表
173 */ 189 */
174 -const formatGroup = (data) => { 190 +const formatGroup = data => {
175 - let lastPayId = null; 191 + let lastPayId = null
176 for (let i = 0; i < data.length; i++) { 192 for (let i = 0; i < data.length; i++) {
177 if (data[i].pay_id !== lastPayId) { 193 if (data[i].pay_id !== lastPayId) {
178 - data[i].sort = 1; 194 + data[i].sort = 1
179 - lastPayId = data[i].pay_id; 195 + lastPayId = data[i].pay_id
180 } else { 196 } else {
181 - data[i].sort = 0; 197 + data[i].sort = 0
182 } 198 }
183 } 199 }
184 - return data; 200 + return data
185 } 201 }
186 202
187 /** 203 /**
...@@ -193,55 +209,55 @@ const formatGroup = (data) => { ...@@ -193,55 +209,55 @@ const formatGroup = (data) => {
193 const init = async () => { 209 const init = async () => {
194 if (!props.type) { 210 if (!props.type) {
195 try { 211 try {
196 - const { code, data } = await qrcodeListAPI(); 212 + const { code, data } = await qrcodeListAPI()
197 - 213 +
198 - if (code) { 214 + if (code) {
199 - data.forEach(item => { 215 + data.forEach(item => {
200 - item.qr_code_url = '/admin?m=srv&a=get_qrcode&key=' + item.qr_code; 216 + item.qr_code_url = `/admin?m=srv&a=get_qrcode&key=${item.qr_code}`
201 - item.datetime = formatDatetime({ begin_time: item.begin_time, end_time: item.end_time }) 217 + item.datetime = formatDatetime({ begin_time: item.begin_time, end_time: item.end_time })
202 - item.sort = 0; 218 + item.sort = 0
203 - }); 219 + })
204 - // 剔除qr_code为空的二维码 220 + // 剔除qr_code为空的二维码
205 - const validData = data.filter(item => item.qr_code !== ''); 221 + const validData = data.filter(item => item.qr_code !== '')
206 - 222 +
207 - if (validData.length > 0) { 223 + if (validData.length > 0) {
208 - userList.value = formatGroup(validData); 224 + userList.value = formatGroup(validData)
209 - refreshBtn(); 225 + refreshBtn()
210 - } else { 226 + } else {
211 - userList.value = []; 227 + userList.value = []
212 - }
213 } 228 }
229 + }
214 } catch (err) { 230 } catch (err) {
215 - console.error('Fetch QR List Failed:', err); 231 + console.error('Fetch QR List Failed:', err)
216 } 232 }
217 } else { 233 } else {
218 if (props.payId) { 234 if (props.payId) {
219 - const { code, data } = await billPersonAPI({ pay_id: props.payId }); 235 + const { code, data } = await billPersonAPI({ pay_id: props.payId })
220 - if (code) { 236 + if (code) {
221 - data.forEach(item => { 237 + data.forEach(item => {
222 - item.qr_code_url = '/admin?m=srv&a=get_qrcode&key=' + item.qr_code; 238 + item.qr_code_url = `/admin?m=srv&a=get_qrcode&key=${item.qr_code}`
223 - item.sort = 0; 239 + item.sort = 0
224 - // billPersonAPI 返回的数据可能没有 datetime 字段,需要检查 240 + // billPersonAPI 返回的数据可能没有 datetime 字段,需要检查
225 - // 如果没有,可能需要从外部传入或者假设是当天的? 241 + // 如果没有,可能需要从外部传入或者假设是当天的?
226 - // H5 代码没有处理 datetime,但在 template 里用了。 242 + // H5 代码没有处理 datetime,但在 template 里用了。
227 - // 这里暂且不做处理,如果没有 datetime 就不显示 243 + // 这里暂且不做处理,如果没有 datetime 就不显示
228 - }); 244 + })
229 - const validData = data.filter(item => item.qr_code !== ''); 245 + const validData = data.filter(item => item.qr_code !== '')
230 - if (validData.length > 0) { 246 + if (validData.length > 0) {
231 - userList.value = validData; 247 + userList.value = validData
232 - refreshBtn(); 248 + refreshBtn()
233 - } else { 249 + } else {
234 - userList.value = []; 250 + userList.value = []
235 - }
236 } 251 }
252 + }
237 } 253 }
238 } 254 }
239 -}; 255 +}
240 256
241 onMounted(() => { 257 onMounted(() => {
242 - init(); 258 + init()
243 - start_polling(); 259 + start_polling()
244 -}); 260 +})
245 261
246 /** 262 /**
247 * @description 轮询刷新二维码状态 263 * @description 轮询刷新二维码状态
...@@ -251,13 +267,15 @@ onMounted(() => { ...@@ -251,13 +267,15 @@ onMounted(() => {
251 const poll = async () => { 267 const poll = async () => {
252 if (userList.value.length && useStatus.value === STATUS_CODE.SUCCESS) { 268 if (userList.value.length && useStatus.value === STATUS_CODE.SUCCESS) {
253 if (userList.value[select_index.value]) { 269 if (userList.value[select_index.value]) {
254 - const { code, data } = await qrcodeStatusAPI({ qr_code: userList.value[select_index.value].qr_code }); 270 + const { code, data } = await qrcodeStatusAPI({
255 - if (code) { 271 + qr_code: userList.value[select_index.value].qr_code
256 - useStatus.value = data.status; 272 + })
257 - } 273 + if (code) {
274 + useStatus.value = data.status
275 + }
258 } 276 }
259 } 277 }
260 -}; 278 +}
261 279
262 const interval_id = ref(null) 280 const interval_id = ref(null)
263 /** 281 /**
...@@ -267,8 +285,10 @@ const interval_id = ref(null) ...@@ -267,8 +285,10 @@ const interval_id = ref(null)
267 */ 285 */
268 286
269 const start_polling = () => { 287 const start_polling = () => {
270 - if (interval_id.value) return 288 + if (interval_id.value) {
271 - interval_id.value = setInterval(poll, 3000) 289 + return
290 + }
291 + interval_id.value = setInterval(poll, 3000)
272 } 292 }
273 293
274 /** 294 /**
...@@ -278,14 +298,16 @@ const start_polling = () => { ...@@ -278,14 +298,16 @@ const start_polling = () => {
278 */ 298 */
279 299
280 const stop_polling = () => { 300 const stop_polling = () => {
281 - if (!interval_id.value) return 301 + if (!interval_id.value) {
282 - clearInterval(interval_id.value) 302 + return
283 - interval_id.value = null 303 + }
304 + clearInterval(interval_id.value)
305 + interval_id.value = null
284 } 306 }
285 307
286 onUnmounted(() => { 308 onUnmounted(() => {
287 - stop_polling(); 309 + stop_polling()
288 -}); 310 +})
289 311
290 defineExpose({ start_polling, stop_polling }) 312 defineExpose({ start_polling, stop_polling })
291 313
...@@ -294,7 +316,7 @@ defineExpose({ start_polling, stop_polling }) ...@@ -294,7 +316,7 @@ defineExpose({ start_polling, stop_polling })
294 * @returns {void} 无返回值 316 * @returns {void} 无返回值
295 */ 317 */
296 const toRecord = () => { 318 const toRecord = () => {
297 - go('/bookingList'); 319 + go('/bookingList')
298 } 320 }
299 </script> 321 </script>
300 322
...@@ -306,12 +328,12 @@ const toRecord = () => { ...@@ -306,12 +328,12 @@ const toRecord = () => {
306 flex-direction: column; 328 flex-direction: column;
307 justify-content: center; 329 justify-content: center;
308 align-items: center; 330 align-items: center;
309 - background-color: #FFF; 331 + background-color: #fff;
310 border-radius: 16rpx; 332 border-radius: 16rpx;
311 - box-shadow: 0 0 29rpx 0 rgba(106,106,106,0.27); 333 + box-shadow: 0 0 29rpx 0 rgba(106, 106, 106, 0.27);
312 334
313 .user-info { 335 .user-info {
314 - color: #A6A6A6; 336 + color: #a6a6a6;
315 font-size: 37rpx; 337 font-size: 37rpx;
316 margin-top: 16rpx; 338 margin-top: 16rpx;
317 margin-bottom: 16rpx; 339 margin-bottom: 16rpx;
...@@ -321,50 +343,54 @@ const toRecord = () => { ...@@ -321,50 +343,54 @@ const toRecord = () => {
321 align-items: center; 343 align-items: center;
322 .left { 344 .left {
323 image { 345 image {
324 - width: 56rpx; height: 56rpx; margin-right: 16rpx; 346 + width: 56rpx;
347 + height: 56rpx;
348 + margin-right: 16rpx;
325 } 349 }
326 } 350 }
327 .center { 351 .center {
328 - border: 2rpx solid #D1D1D1; 352 + border: 2rpx solid #d1d1d1;
329 border-radius: 40rpx; 353 border-radius: 40rpx;
330 padding: 46rpx; 354 padding: 46rpx;
331 position: relative; 355 position: relative;
332 image { 356 image {
333 - width: 400rpx; height: 400rpx; 357 + width: 400rpx;
358 + height: 400rpx;
334 } 359 }
335 .qrcode-used { 360 .qrcode-used {
361 + position: absolute;
362 + top: 0;
363 + left: 0;
364 + right: 0;
365 + bottom: 0;
366 + border-radius: 40rpx;
367 + overflow: hidden;
368 +
369 + .overlay {
370 + width: 100%;
371 + height: 100%;
372 + background-image: url('https://cdn.ipadbiz.cn/xys/booking/southeast.jpeg');
373 + background-size: contain;
374 + opacity: 0.9;
375 + }
376 +
377 + .status-text {
378 + color: #a67939;
336 position: absolute; 379 position: absolute;
337 - top: 0; 380 + top: 50%;
338 - left: 0; 381 + left: 50%;
339 - right: 0; 382 + transform: translate(-50%, -50%);
340 - bottom: 0; 383 + font-size: 38rpx;
341 - border-radius: 40rpx; 384 + white-space: nowrap;
342 - overflow: hidden; 385 + font-weight: bold;
343 - 386 + z-index: 10;
344 - .overlay { 387 + }
345 - width: 100%;
346 - height: 100%;
347 - background-image: url('https://cdn.ipadbiz.cn/xys/booking/southeast.jpeg');
348 - background-size: contain;
349 - opacity: 0.9;
350 - }
351 -
352 - .status-text {
353 - color: #A67939;
354 - position: absolute;
355 - top: 50%;
356 - left: 50%;
357 - transform: translate(-50%, -50%);
358 - font-size: 38rpx;
359 - white-space: nowrap;
360 - font-weight: bold;
361 - z-index: 10;
362 - }
363 } 388 }
364 } 389 }
365 .right { 390 .right {
366 image { 391 image {
367 - width: 56rpx; height: 56rpx; 392 + width: 56rpx;
393 + height: 56rpx;
368 margin-left: 16rpx; 394 margin-left: 16rpx;
369 } 395 }
370 } 396 }
...@@ -378,13 +404,13 @@ const toRecord = () => { ...@@ -378,13 +404,13 @@ const toRecord = () => {
378 .user-item { 404 .user-item {
379 position: relative; 405 position: relative;
380 padding: 8rpx 16rpx; 406 padding: 8rpx 16rpx;
381 - border: 2rpx solid #A67939; 407 + border: 2rpx solid #a67939;
382 margin: 8rpx; 408 margin: 8rpx;
383 border-radius: 10rpx; 409 border-radius: 10rpx;
384 - color: #A67939; 410 + color: #a67939;
385 &.checked { 411 &.checked {
386 - color: #FFF; 412 + color: #fff;
387 - background-color: #A67939; 413 + background-color: #a67939;
388 } 414 }
389 &.border { 415 &.border {
390 margin-right: 16rpx; 416 margin-right: 16rpx;
...@@ -394,7 +420,7 @@ const toRecord = () => { ...@@ -394,7 +420,7 @@ const toRecord = () => {
394 top: calc(50% - 16rpx); 420 top: calc(50% - 16rpx);
395 content: ''; 421 content: '';
396 height: 32rpx; 422 height: 32rpx;
397 - border-right: 2rpx solid #A67939; 423 + border-right: 2rpx solid #a67939;
398 } 424 }
399 } 425 }
400 } 426 }
...@@ -408,7 +434,7 @@ const toRecord = () => { ...@@ -408,7 +434,7 @@ const toRecord = () => {
408 margin-bottom: 32rpx; 434 margin-bottom: 32rpx;
409 435
410 .no-qrcode-title { 436 .no-qrcode-title {
411 - color: #A67939; 437 + color: #a67939;
412 font-size: 34rpx; 438 font-size: 34rpx;
413 } 439 }
414 } 440 }
......
...@@ -16,7 +16,10 @@ ...@@ -16,7 +16,10 @@
16 </view> 16 </view>
17 <view class="center"> 17 <view class="center">
18 <image :src="userinfo.qr_code_url" mode="aspectFit" /> 18 <image :src="userinfo.qr_code_url" mode="aspectFit" />
19 - <view v-if="useStatus === STATUS_CODE.CANCELED || useStatus === STATUS_CODE.USED" class="qrcode-used"> 19 + <view
20 + v-if="useStatus === STATUS_CODE.CANCELED || useStatus === STATUS_CODE.USED"
21 + class="qrcode-used"
22 + >
20 <view class="overlay"></view> 23 <view class="overlay"></view>
21 <text class="status-text">二维码{{ qr_code_status[useStatus] }}</text> 24 <text class="status-text">二维码{{ qr_code_status[useStatus] }}</text>
22 </view> 25 </view>
...@@ -25,11 +28,14 @@ ...@@ -25,11 +28,14 @@
25 <!-- <image src="https://cdn.ipadbiz.cn/xys/booking/%E5%8F%B3@2x.png"> --> 28 <!-- <image src="https://cdn.ipadbiz.cn/xys/booking/%E5%8F%B3@2x.png"> -->
26 </view> 29 </view>
27 </view> 30 </view>
28 - <view style="color: red; margin-top: 32rpx;">{{ userinfo.datetime }}</view> 31 + <view style="color: red; margin-top: 32rpx">{{ userinfo.datetime }}</view>
29 </view> 32 </view>
30 </view> 33 </view>
31 <view v-else class="no-qrcode"> 34 <view v-else class="no-qrcode">
32 - <image src="https://cdn.ipadbiz.cn/xys/booking/%E6%9A%82%E6%97%A0@2x.png" style="width: 320rpx; height: 320rpx;" /> 35 + <image
36 + src="https://cdn.ipadbiz.cn/xys/booking/%E6%9A%82%E6%97%A0@2x.png"
37 + style="width: 320rpx; height: 320rpx"
38 + />
33 <view class="no-qrcode-title">您还没有预约过今天参观</view> 39 <view class="no-qrcode-title">您还没有预约过今天参观</view>
34 </view> 40 </view>
35 </view> 41 </view>
...@@ -37,9 +43,9 @@ ...@@ -37,9 +43,9 @@
37 43
38 <script setup> 44 <script setup>
39 import { ref, onMounted, watch, onUnmounted } from 'vue' 45 import { ref, onMounted, watch, onUnmounted } from 'vue'
40 -import { formatDatetime } from '@/utils/tools'; 46 +import { formatDatetime } from '@/utils/tools'
41 import { qrcodeStatusAPI, queryQrCodeAPI } from '@/api/index' 47 import { qrcodeStatusAPI, queryQrCodeAPI } from '@/api/index'
42 -import BASE_URL from '@/utils/config'; 48 +import BASE_URL from '@/utils/config'
43 49
44 const props = defineProps({ 50 const props = defineProps({
45 id: { 51 id: {
...@@ -50,42 +56,44 @@ const props = defineProps({ ...@@ -50,42 +56,44 @@ const props = defineProps({
50 type: Number, 56 type: Number,
51 default: 1 57 default: 1
52 } 58 }
53 -}); 59 +})
54 60
55 -const userinfo = ref({}); 61 +const userinfo = ref({})
56 62
57 -const replaceMiddleCharacters = (input_string) => { 63 +const replaceMiddleCharacters = input_string => {
58 if (!input_string || input_string.length < 15) { 64 if (!input_string || input_string.length < 15) {
59 - return input_string; 65 + return input_string
60 } 66 }
61 - const start = Math.floor((input_string.length - 8) / 2); 67 + const start = Math.floor((input_string.length - 8) / 2)
62 - const end = start + 8; 68 + const end = start + 8
63 - const replacement = '*'.repeat(8); 69 + const replacement = '*'.repeat(8)
64 - return input_string.substring(0, start) + replacement + input_string.substring(end); 70 + return input_string.substring(0, start) + replacement + input_string.substring(end)
65 } 71 }
66 72
67 -const formatId = (id) => replaceMiddleCharacters(id); 73 +const formatId = id => replaceMiddleCharacters(id)
68 74
69 -const useStatus = ref('0'); 75 +const useStatus = ref('0')
70 const is_loading = ref(false) 76 const is_loading = ref(false)
71 let is_destroyed = false 77 let is_destroyed = false
72 78
73 const qr_code_status = { 79 const qr_code_status = {
74 - '1': '未激活', 80 + 1: '未激活',
75 - '3': '待使用', 81 + 3: '待使用',
76 - '5': '被取消', 82 + 5: '被取消',
77 - '7': '已使用', 83 + 7: '已使用'
78 -}; 84 +}
79 85
80 const STATUS_CODE = { 86 const STATUS_CODE = {
81 APPLY: '1', 87 APPLY: '1',
82 SUCCESS: '3', 88 SUCCESS: '3',
83 CANCELED: '5', 89 CANCELED: '5',
84 - USED: '7', 90 + USED: '7'
85 -}; 91 +}
86 92
87 -const build_qr_code_url = (qr_code) => { 93 +const build_qr_code_url = qr_code => {
88 - if (!qr_code) return '' 94 + if (!qr_code) {
95 + return ''
96 + }
89 return `${BASE_URL}/admin?m=srv&a=get_qrcode&key=${encodeURIComponent(String(qr_code))}` 97 return `${BASE_URL}/admin?m=srv&a=get_qrcode&key=${encodeURIComponent(String(qr_code))}`
90 } 98 }
91 99
...@@ -95,8 +103,10 @@ const build_qr_code_url = (qr_code) => { ...@@ -95,8 +103,10 @@ const build_qr_code_url = (qr_code) => {
95 * @return {*} 格式化后的数据 103 * @return {*} 格式化后的数据
96 */ 104 */
97 105
98 -const normalize_item = (raw) => { 106 +const normalize_item = raw => {
99 - if (!raw || typeof raw !== 'object') return null 107 + if (!raw || typeof raw !== 'object') {
108 + return null
109 + }
100 const qr_code = raw.qr_code ? String(raw.qr_code) : '' 110 const qr_code = raw.qr_code ? String(raw.qr_code) : ''
101 const id_number = raw.id_number ? String(raw.id_number) : '' 111 const id_number = raw.id_number ? String(raw.id_number) : ''
102 return { 112 return {
...@@ -104,7 +114,7 @@ const normalize_item = (raw) => { ...@@ -104,7 +114,7 @@ const normalize_item = (raw) => {
104 qr_code, 114 qr_code,
105 qr_code_url: build_qr_code_url(qr_code), 115 qr_code_url: build_qr_code_url(qr_code),
106 datetime: formatDatetime({ begin_time: raw.begin_time, end_time: raw.end_time }), 116 datetime: formatDatetime({ begin_time: raw.begin_time, end_time: raw.end_time }),
107 - id: formatId(id_number), 117 + id: formatId(id_number)
108 } 118 }
109 } 119 }
110 120
...@@ -123,13 +133,21 @@ const reset_state = () => { ...@@ -123,13 +133,21 @@ const reset_state = () => {
123 * @return {*} 状态码 133 * @return {*} 状态码
124 */ 134 */
125 135
126 -const load_qr_code_status = async (qr_code) => { 136 +const load_qr_code_status = async qr_code => {
127 - if (!qr_code) return 137 + if (!qr_code) {
138 + return
139 + }
128 const res = await qrcodeStatusAPI({ qr_code }) 140 const res = await qrcodeStatusAPI({ qr_code })
129 - if (is_destroyed) return 141 + if (is_destroyed) {
130 - if (!res || res.code !== 1) return 142 + return
143 + }
144 + if (!res || res.code !== 1) {
145 + return
146 + }
131 const status = res?.data?.status 147 const status = res?.data?.status
132 - if (status === undefined || status === null) return 148 + if (status === undefined || status === null) {
149 + return
150 + }
133 useStatus.value = String(status) 151 useStatus.value = String(status)
134 } 152 }
135 153
...@@ -139,7 +157,7 @@ const load_qr_code_status = async (qr_code) => { ...@@ -139,7 +157,7 @@ const load_qr_code_status = async (qr_code) => {
139 * @return {*} 预约码卡信息 157 * @return {*} 预约码卡信息
140 */ 158 */
141 159
142 -const load_qr_code_info = async (id_number) => { 160 +const load_qr_code_info = async id_number => {
143 const id = String(id_number || '').trim() 161 const id = String(id_number || '').trim()
144 if (!id) { 162 if (!id) {
145 reset_state() 163 reset_state()
...@@ -148,9 +166,13 @@ const load_qr_code_info = async (id_number) => { ...@@ -148,9 +166,13 @@ const load_qr_code_info = async (id_number) => {
148 166
149 is_loading.value = true 167 is_loading.value = true
150 const params = { id_number: id } 168 const params = { id_number: id }
151 - if (props.id_type) params.id_type = props.id_type 169 + if (props.id_type) {
170 + params.id_type = props.id_type
171 + }
152 const res = await queryQrCodeAPI(params) 172 const res = await queryQrCodeAPI(params)
153 - if (is_destroyed) return 173 + if (is_destroyed) {
174 + return
175 + }
154 is_loading.value = false 176 is_loading.value = false
155 177
156 if (!res || res.code !== 1 || !res.data) { 178 if (!res || res.code !== 1 || !res.data) {
...@@ -180,7 +202,9 @@ onMounted(() => { ...@@ -180,7 +202,9 @@ onMounted(() => {
180 watch( 202 watch(
181 () => [props.id, props.id_type], 203 () => [props.id, props.id_type],
182 ([val]) => { 204 ([val]) => {
183 - if (is_loading.value) return 205 + if (is_loading.value) {
206 + return
207 + }
184 load_qr_code_info(val) 208 load_qr_code_info(val)
185 } 209 }
186 ) 210 )
...@@ -194,12 +218,12 @@ watch( ...@@ -194,12 +218,12 @@ watch(
194 flex-direction: column; 218 flex-direction: column;
195 justify-content: center; 219 justify-content: center;
196 align-items: center; 220 align-items: center;
197 - background-color: #FFF; 221 + background-color: #fff;
198 border-radius: 16rpx; 222 border-radius: 16rpx;
199 - box-shadow: 0 0 29rpx 0 rgba(106,106,106,0.27); 223 + box-shadow: 0 0 29rpx 0 rgba(106, 106, 106, 0.27);
200 224
201 .user-info { 225 .user-info {
202 - color: #A6A6A6; 226 + color: #a6a6a6;
203 font-size: 37rpx; 227 font-size: 37rpx;
204 margin-top: 16rpx; 228 margin-top: 16rpx;
205 margin-bottom: 16rpx; 229 margin-bottom: 16rpx;
...@@ -208,41 +232,42 @@ watch( ...@@ -208,41 +232,42 @@ watch(
208 display: flex; 232 display: flex;
209 align-items: center; 233 align-items: center;
210 .center { 234 .center {
211 - border: 2rpx solid #D1D1D1; 235 + border: 2rpx solid #d1d1d1;
212 border-radius: 40rpx; 236 border-radius: 40rpx;
213 padding: 16rpx; 237 padding: 16rpx;
214 position: relative; 238 position: relative;
215 image { 239 image {
216 - width: 480rpx; height: 480rpx; 240 + width: 480rpx;
241 + height: 480rpx;
217 } 242 }
218 .qrcode-used { 243 .qrcode-used {
219 - position: absolute; 244 + position: absolute;
220 - top: 0; 245 + top: 0;
221 - left: 0; 246 + left: 0;
222 - right: 0; 247 + right: 0;
223 - bottom: 0; 248 + bottom: 0;
224 - border-radius: 40rpx; 249 + border-radius: 40rpx;
225 - overflow: hidden; 250 + overflow: hidden;
226 251
227 - .overlay { 252 + .overlay {
228 - width: 100%; 253 + width: 100%;
229 - height: 100%; 254 + height: 100%;
230 - background-image: url('https://cdn.ipadbiz.cn/xys/booking/southeast.jpeg'); 255 + background-image: url('https://cdn.ipadbiz.cn/xys/booking/southeast.jpeg');
231 - background-size: contain; 256 + background-size: contain;
232 - opacity: 0.9; 257 + opacity: 0.9;
233 - } 258 + }
234 259
235 - .status-text { 260 + .status-text {
236 - color: #A67939; 261 + color: #a67939;
237 - position: absolute; 262 + position: absolute;
238 - top: 50%; 263 + top: 50%;
239 - left: 50%; 264 + left: 50%;
240 - transform: translate(-50%, -50%); 265 + transform: translate(-50%, -50%);
241 - font-size: 38rpx; 266 + font-size: 38rpx;
242 - white-space: nowrap; 267 + white-space: nowrap;
243 - font-weight: bold; 268 + font-weight: bold;
244 - z-index: 10; 269 + z-index: 10;
245 - } 270 + }
246 } 271 }
247 } 272 }
248 } 273 }
...@@ -256,7 +281,7 @@ watch( ...@@ -256,7 +281,7 @@ watch(
256 margin-bottom: 32rpx; 281 margin-bottom: 32rpx;
257 282
258 .no-qrcode-title { 283 .no-qrcode-title {
259 - color: #A67939; 284 + color: #a67939;
260 font-size: 34rpx; 285 font-size: 34rpx;
261 } 286 }
262 } 287 }
......
...@@ -14,13 +14,27 @@ ...@@ -14,13 +14,27 @@
14 </view> 14 </view>
15 <view class="booking-list-item-body"> 15 <view class="booking-list-item-body">
16 <view class="booking-num"> 16 <view class="booking-num">
17 - <view class="num-body van-ellipsis">预约人数:<text>{{ reserve_info.total_qty }} 人</text>&nbsp;<text>({{ reserve_info.person_name }})</text></view> 17 + <view class="num-body van-ellipsis"
18 - <view v-if="(reserve_info.status === CodeStatus.SUCCESS || reserve_info.status === CodeStatus.USED || reserve_info.status === CodeStatus.CANCEL)"> 18 + >预约人数:<text>{{ reserve_info.total_qty }} 人</text>&nbsp;<text
19 + >({{ reserve_info.person_name }})</text
20 + ></view
21 + >
22 + <view
23 + v-if="
24 + reserve_info.status === CodeStatus.SUCCESS ||
25 + reserve_info.status === CodeStatus.USED ||
26 + reserve_info.status === CodeStatus.CANCEL
27 + "
28 + >
19 <IconFont name="rect-right" /> 29 <IconFont name="rect-right" />
20 </view> 30 </view>
21 </view> 31 </view>
22 - <view class="booking-price">支付金额:<text>¥ {{ reserve_info.total_amt }}</text></view> 32 + <view class="booking-price"
23 - <view class="booking-time">下单时间:<text>{{ reserve_info.order_time }}</text></view> 33 + >支付金额:<text>¥ {{ reserve_info.total_amt }}</text></view
34 + >
35 + <view class="booking-time"
36 + >下单时间:<text>{{ reserve_info.order_time }}</text></view
37 + >
24 </view> 38 </view>
25 <view v-if="is_pay_pending" class="booking-list-item-footer" @tap.stop> 39 <view v-if="is_pay_pending" class="booking-list-item-footer" @tap.stop>
26 <view v-if="countdown_seconds > 0" class="countdown">剩余支付时间:{{ countdown_text }}</view> 40 <view v-if="countdown_seconds > 0" class="countdown">剩余支付时间:{{ countdown_text }}</view>
...@@ -37,26 +51,26 @@ import { IconFont } from '@nutui/icons-vue-taro' ...@@ -37,26 +51,26 @@ import { IconFont } from '@nutui/icons-vue-taro'
37 import { useGo } from '@/hooks/useGo' 51 import { useGo } from '@/hooks/useGo'
38 import { wechat_pay } from '@/utils/wechatPay' 52 import { wechat_pay } from '@/utils/wechatPay'
39 53
40 -const go = useGo(); 54 +const go = useGo()
41 55
42 const props = defineProps({ 56 const props = defineProps({
43 data: { 57 data: {
44 type: Object, 58 type: Object,
45 - default: () => ({}), 59 + default: () => ({})
46 }, 60 },
47 detail_path: { 61 detail_path: {
48 type: String, 62 type: String,
49 - default: '/bookingDetail', 63 + default: '/bookingDetail'
50 }, 64 },
51 is_offline: { 65 is_offline: {
52 type: Boolean, 66 type: Boolean,
53 - default: false, 67 + default: false
54 - }, 68 + }
55 -}); 69 +})
56 70
57 -const reserve_info = computed(() => props.data); 71 +const reserve_info = computed(() => props.data)
58 72
59 -const is_offline = computed(() => props.is_offline); 73 +const is_offline = computed(() => props.is_offline)
60 74
61 /** 75 /**
62 * @description 预约码状态枚举(与后端约定) 76 * @description 预约码状态枚举(与后端约定)
...@@ -78,7 +92,9 @@ const CodeStatus = { ...@@ -78,7 +92,9 @@ const CodeStatus = {
78 */ 92 */
79 93
80 const is_pay_pending = computed(() => { 94 const is_pay_pending = computed(() => {
81 - if (is_offline.value) return false 95 + if (is_offline.value) {
96 + return false
97 + }
82 return reserve_info.value?.status === CodeStatus.APPLY && !!reserve_info.value?.pay_id 98 return reserve_info.value?.status === CodeStatus.APPLY && !!reserve_info.value?.pay_id
83 }) 99 })
84 100
...@@ -89,7 +105,7 @@ const countdown_seconds = ref(0) ...@@ -89,7 +105,7 @@ const countdown_seconds = ref(0)
89 * @param {number|string} n 数字 105 * @param {number|string} n 数字
90 * @returns {string} 两位字符串 106 * @returns {string} 两位字符串
91 */ 107 */
92 -const format_two_digits = (n) => { 108 +const format_two_digits = n => {
93 const num = Number(n) || 0 109 const num = Number(n) || 0
94 return num < 10 ? `0${num}` : String(num) 110 return num < 10 ? `0${num}` : String(num)
95 } 111 }
...@@ -106,9 +122,11 @@ const countdown_text = computed(() => { ...@@ -106,9 +122,11 @@ const countdown_text = computed(() => {
106 * @param {string} created_time 创建时间字符串 122 * @param {string} created_time 创建时间字符串
107 * @returns {number} 毫秒时间戳;解析失败返回 0 123 * @returns {number} 毫秒时间戳;解析失败返回 0
108 */ 124 */
109 -const parse_created_time_ms = (created_time) => { 125 +const parse_created_time_ms = created_time => {
110 const raw = String(created_time || '') 126 const raw = String(created_time || '')
111 - if (!raw) return 0 127 + if (!raw) {
128 + return 0
129 + }
112 const fixed = raw.replace(/-/g, '/') 130 const fixed = raw.replace(/-/g, '/')
113 const date = new Date(fixed) 131 const date = new Date(fixed)
114 const time = date.getTime() 132 const time = date.getTime()
...@@ -157,7 +175,9 @@ const update_countdown = () => { ...@@ -157,7 +175,9 @@ const update_countdown = () => {
157 const start_countdown = () => { 175 const start_countdown = () => {
158 stop_countdown() 176 stop_countdown()
159 update_countdown() 177 update_countdown()
160 - if (countdown_seconds.value <= 0) return 178 + if (countdown_seconds.value <= 0) {
179 + return
180 + }
161 countdown_timer = setInterval(update_countdown, 1000) 181 countdown_timer = setInterval(update_countdown, 1000)
162 } 182 }
163 183
...@@ -167,8 +187,10 @@ let is_showing_pay_modal = false ...@@ -167,8 +187,10 @@ let is_showing_pay_modal = false
167 * @param {string} content 弹窗内容 187 * @param {string} content 弹窗内容
168 * @returns {Promise<boolean>} true=继续支付,false=取消 188 * @returns {Promise<boolean>} true=继续支付,false=取消
169 */ 189 */
170 -const show_pay_modal = async (content) => { 190 +const show_pay_modal = async content => {
171 - if (is_showing_pay_modal) return false 191 + if (is_showing_pay_modal) {
192 + return false
193 + }
172 is_showing_pay_modal = true 194 is_showing_pay_modal = true
173 try { 195 try {
174 const res = await Taro.showModal({ 196 const res = await Taro.showModal({
...@@ -176,7 +198,7 @@ const show_pay_modal = async (content) => { ...@@ -176,7 +198,7 @@ const show_pay_modal = async (content) => {
176 content: content || '支付未完成', 198 content: content || '支付未完成',
177 showCancel: true, 199 showCancel: true,
178 cancelText: '取消', 200 cancelText: '取消',
179 - confirmText: '继续支付', 201 + confirmText: '继续支付'
180 }) 202 })
181 return !!res?.confirm 203 return !!res?.confirm
182 } finally { 204 } finally {
...@@ -190,7 +212,9 @@ const show_pay_modal = async (content) => { ...@@ -190,7 +212,9 @@ const show_pay_modal = async (content) => {
190 */ 212 */
191 213
192 const onRepay = async () => { 214 const onRepay = async () => {
193 - if (!is_pay_pending.value) return 215 + if (!is_pay_pending.value) {
216 + return
217 + }
194 if (countdown_seconds.value <= 0) { 218 if (countdown_seconds.value <= 0) {
195 Taro.showToast({ title: '支付已超时', icon: 'none' }) 219 Taro.showToast({ title: '支付已超时', icon: 'none' })
196 return 220 return
...@@ -213,7 +237,7 @@ const onRepay = async () => { ...@@ -213,7 +237,7 @@ const onRepay = async () => {
213 * @param {string} status 订单状态码 237 * @param {string} status 订单状态码
214 * @returns {{key:string,value:string}} 展示状态(key 用于 class) 238 * @returns {{key:string,value:string}} 展示状态(key 用于 class)
215 */ 239 */
216 -const formatStatus = (status) => { 240 +const formatStatus = status => {
217 switch (status) { 241 switch (status) {
218 case CodeStatus.APPLY: 242 case CodeStatus.APPLY:
219 return { 243 return {
...@@ -251,12 +275,12 @@ const formatStatus = (status) => { ...@@ -251,12 +275,12 @@ const formatStatus = (status) => {
251 value: '退款中' 275 value: '退款中'
252 } 276 }
253 default: 277 default:
254 - return { key: '', value: '' } 278 + return { key: '', value: '' }
255 } 279 }
256 } 280 }
257 281
258 const status_info = computed(() => { 282 const status_info = computed(() => {
259 - return formatStatus(reserve_info.value?.status) || { key: '', value: '' } 283 + return formatStatus(reserve_info.value?.status) || { key: '', value: '' }
260 }) 284 })
261 285
262 /** 286 /**
...@@ -265,10 +289,14 @@ const status_info = computed(() => { ...@@ -265,10 +289,14 @@ const status_info = computed(() => {
265 * @param {Object} item 预约记录 289 * @param {Object} item 预约记录
266 * @returns {void} 无返回值 290 * @returns {void} 无返回值
267 */ 291 */
268 -const goToDetail = (item) => { 292 +const goToDetail = item => {
269 - // 只有成功、已使用、已取消(退款成功)才跳转详情 293 + // 只有成功、已使用、已取消(退款成功)才跳转详情
270 - if (item.status === CodeStatus.SUCCESS || item.status === CodeStatus.USED || item.status === CodeStatus.CANCEL) { 294 + if (
271 - go(props.detail_path, { pay_id: item.pay_id }); 295 + item.status === CodeStatus.SUCCESS ||
296 + item.status === CodeStatus.USED ||
297 + item.status === CodeStatus.CANCEL
298 + ) {
299 + go(props.detail_path, { pay_id: item.pay_id })
272 } 300 }
273 } 301 }
274 302
...@@ -278,14 +306,18 @@ const goToDetail = (item) => { ...@@ -278,14 +306,18 @@ const goToDetail = (item) => {
278 * - 退出待支付:清空倒计时 306 * - 退出待支付:清空倒计时
279 */ 307 */
280 308
281 -watch(is_pay_pending, (val) => { 309 +watch(
282 - if (val) { 310 + is_pay_pending,
283 - start_countdown() 311 + val => {
284 - } else { 312 + if (val) {
285 - countdown_seconds.value = 0 313 + start_countdown()
286 - stop_countdown() 314 + } else {
287 - } 315 + countdown_seconds.value = 0
288 -}, { immediate: true }) 316 + stop_countdown()
317 + }
318 + },
319 + { immediate: true }
320 +)
289 321
290 onUnmounted(() => { 322 onUnmounted(() => {
291 stop_countdown() 323 stop_countdown()
...@@ -294,71 +326,73 @@ onUnmounted(() => { ...@@ -294,71 +326,73 @@ onUnmounted(() => {
294 326
295 <style lang="less"> 327 <style lang="less">
296 .booking-list-item { 328 .booking-list-item {
297 - background-color: #FFF; 329 + background-color: #fff;
298 border-radius: 16rpx; 330 border-radius: 16rpx;
299 padding: 32rpx; 331 padding: 32rpx;
300 margin-bottom: 32rpx; 332 margin-bottom: 32rpx;
301 - box-shadow: 0 0 30rpx 0 rgba(106,106,106,0.1); 333 + box-shadow: 0 0 30rpx 0 rgba(106, 106, 106, 0.1);
302 334
303 .booking-list-item-header { 335 .booking-list-item-header {
304 display: flex; 336 display: flex;
305 justify-content: space-between; 337 justify-content: space-between;
306 align-items: center; 338 align-items: center;
307 padding-bottom: 16rpx; 339 padding-bottom: 16rpx;
308 - border-bottom: 2rpx dashed #E6E6E6; 340 + border-bottom: 2rpx dashed #e6e6e6;
309 margin-bottom: 16rpx; 341 margin-bottom: 16rpx;
310 font-size: 32rpx; 342 font-size: 32rpx;
311 font-weight: bold; 343 font-weight: bold;
312 color: #333; 344 color: #333;
313 345
314 .status { 346 .status {
315 - font-size: 27rpx; 347 + font-size: 27rpx;
316 - font-weight: normal; 348 + font-weight: normal;
317 - padding: 4rpx 12rpx; 349 + padding: 4rpx 12rpx;
318 - border-radius: 8rpx; 350 + border-radius: 8rpx;
319 -
320 - &.offline {
321 - color: #999;
322 - background-color: #EEE;
323 - }
324 351
325 - &.success { 352 + &.offline {
326 - color: #A67939; 353 + color: #999;
327 - background-color: #FBEEDC; 354 + background-color: #eee;
328 - } 355 + }
329 - &.cancel { 356 +
330 - color: #999; 357 + &.success {
331 - background-color: #EEE; 358 + color: #a67939;
332 - } 359 + background-color: #fbeedc;
333 - &.used { 360 + }
334 - color: #477F3D; 361 + &.cancel {
335 - background-color: #E5EFE3; 362 + color: #999;
336 - } 363 + background-color: #eee;
364 + }
365 + &.used {
366 + color: #477f3d;
367 + background-color: #e5efe3;
368 + }
337 } 369 }
338 } 370 }
339 371
340 .booking-list-item-body { 372 .booking-list-item-body {
341 .booking-num { 373 .booking-num {
342 - display: flex; 374 + display: flex;
343 - justify-content: space-between; 375 + justify-content: space-between;
344 - align-items: center; 376 + align-items: center;
345 - margin-bottom: 16rpx; 377 + margin-bottom: 16rpx;
346 - color: #666; 378 + color: #666;
347 - 379 +
348 - .num-body { 380 + .num-body {
349 - span, text { 381 + span,
350 - color: #A67939;
351 - font-weight: bold;
352 - }
353 - }
354 - }
355 - .booking-price, .booking-time {
356 - color: #999;
357 - font-size: 29rpx;
358 - margin-bottom: 10rpx;
359 text { 382 text {
360 - color: #333; 383 + color: #a67939;
384 + font-weight: bold;
361 } 385 }
386 + }
387 + }
388 + .booking-price,
389 + .booking-time {
390 + color: #999;
391 + font-size: 29rpx;
392 + margin-bottom: 10rpx;
393 + text {
394 + color: #333;
395 + }
362 } 396 }
363 } 397 }
364 398
...@@ -369,7 +403,7 @@ onUnmounted(() => { ...@@ -369,7 +403,7 @@ onUnmounted(() => {
369 margin-top: 16rpx; 403 margin-top: 16rpx;
370 404
371 .countdown { 405 .countdown {
372 - color: #A67939; 406 + color: #a67939;
373 font-size: 28rpx; 407 font-size: 28rpx;
374 408
375 &.timeout { 409 &.timeout {
...@@ -380,8 +414,8 @@ onUnmounted(() => { ...@@ -380,8 +414,8 @@ onUnmounted(() => {
380 .repay-btn { 414 .repay-btn {
381 padding: 8rpx 20rpx; 415 padding: 8rpx 20rpx;
382 border-radius: 12rpx; 416 border-radius: 12rpx;
383 - background-color: #A67939; 417 + background-color: #a67939;
384 - color: #FFF; 418 + color: #fff;
385 font-size: 28rpx; 419 font-size: 28rpx;
386 } 420 }
387 } 421 }
......
1 -var getDaysInOneMonth = function (year, month) { 1 +const getDaysInOneMonth = function (year, month) {
2 - let _month = parseInt(month, 10); 2 + const _month = parseInt(month, 10)
3 - let d = new Date(year, _month, 0); 3 + const d = new Date(year, _month, 0)
4 - return d.getDate(); 4 + return d.getDate()
5 } 5 }
6 -var dateDate = function (date) { 6 +const dateDate = function (date) {
7 - let year = date && date.getFullYear(); 7 + const year = date && date.getFullYear()
8 - let month = date && date.getMonth() + 1; 8 + const month = date && date.getMonth() + 1
9 - let day = date && date.getDate(); 9 + const day = date && date.getDate()
10 - let hours = date && date.getHours(); 10 + const hours = date && date.getHours()
11 - let minutes = date && date.getMinutes(); 11 + const minutes = date && date.getMinutes()
12 return { 12 return {
13 - year, month, day, hours, minutes 13 + year,
14 + month,
15 + day,
16 + hours,
17 + minutes
14 } 18 }
15 } 19 }
16 -var dateTimePicker = function (startyear, endyear) { 20 +const dateTimePicker = function (startyear, endyear) {
17 // 获取date time 年份,月份,天数,小时,分钟推后30分 21 // 获取date time 年份,月份,天数,小时,分钟推后30分
18 - const years = []; 22 + const years = []
19 - const months = []; 23 + const months = []
20 - const hours = []; 24 + const hours = []
21 - const minutes = []; 25 + const minutes = []
22 for (let i = startyear; i <= endyear; i++) { 26 for (let i = startyear; i <= endyear; i++) {
23 years.push({ 27 years.push({
24 - name: i + '年', 28 + name: `${i}年`,
25 id: i 29 id: i
26 - }); 30 + })
27 } 31 }
28 //获取月份 32 //获取月份
29 for (let i = 1; i <= 12; i++) { 33 for (let i = 1; i <= 12; i++) {
30 if (i < 10) { 34 if (i < 10) {
31 - i = "0" + i; 35 + i = `0${i}`
32 } 36 }
33 months.push({ 37 months.push({
34 - name: i + '月', 38 + name: `${i}月`,
35 id: i 39 id: i
36 - }); 40 + })
37 } 41 }
38 //获取小时 42 //获取小时
39 for (let i = 0; i < 24; i++) { 43 for (let i = 0; i < 24; i++) {
40 if (i < 10) { 44 if (i < 10) {
41 - i = "0" + i; 45 + i = `0${i}`
42 } 46 }
43 hours.push({ 47 hours.push({
44 - name: i + '时', 48 + name: `${i}时`,
45 id: i 49 id: i
46 - }); 50 + })
47 } 51 }
48 //获取分钟 52 //获取分钟
49 for (let i = 0; i < 60; i++) { 53 for (let i = 0; i < 60; i++) {
50 if (i < 10) { 54 if (i < 10) {
51 - i = "0" + i; 55 + i = `0${i}`
52 } 56 }
53 minutes.push({ 57 minutes.push({
54 - name: i + '分', 58 + name: `${i}分`,
55 id: i 59 id: i
56 - }); 60 + })
57 } 61 }
58 return function (_year, _month) { 62 return function (_year, _month) {
59 - const days = []; 63 + const days = []
60 - _year = parseInt(_year); 64 + _year = parseInt(_year)
61 - _month = parseInt(_month); 65 + _month = parseInt(_month)
62 //获取日期 66 //获取日期
63 for (let i = 1; i <= getDaysInOneMonth(_year, _month); i++) { 67 for (let i = 1; i <= getDaysInOneMonth(_year, _month); i++) {
64 if (i < 10) { 68 if (i < 10) {
65 - i = "0" + i; 69 + i = `0${i}`
66 } 70 }
67 days.push({ 71 days.push({
68 - name: i + '日', 72 + name: `${i}日`,
69 id: i 73 id: i
70 - }); 74 + })
71 } 75 }
72 - return [years, months, days, hours, minutes]; 76 + return [years, months, days, hours, minutes]
73 } 77 }
74 } 78 }
75 -export { 79 +export { dateTimePicker, getDaysInOneMonth, dateDate }
76 - dateTimePicker,
77 - getDaysInOneMonth,
78 - dateDate
79 -}
......
1 <template> 1 <template>
2 - <picker mode="multiSelector" :range-key="'name'" :value="timeIndex" :range="activityArray" :disabled="disabled" 2 + <picker
3 - @change="bindMultiPickerChange" @columnChange="bindMultiPickerColumnChange"> 3 + mode="multiSelector"
4 + :range-key="'name'"
5 + :value="timeIndex"
6 + :range="activityArray"
7 + :disabled="disabled"
8 + @change="bindMultiPickerChange"
9 + @columnChange="bindMultiPickerColumnChange"
10 + >
4 <slot /> 11 <slot />
5 </picker> 12 </picker>
6 </template> 13 </template>
7 <script> 14 <script>
8 -import { dateTimePicker, dateDate } from "./dateTimePicker.js"; 15 +import { dateTimePicker, dateDate } from './dateTimePicker.js'
9 export default { 16 export default {
10 - name: "TimePickerDataPicker", 17 + name: 'TimePickerDataPicker',
11 props: { 18 props: {
12 startTime: { 19 startTime: {
13 type: [Object, Date], 20 type: [Object, Date],
14 - default: new Date(), 21 + default: new Date()
15 }, 22 },
16 endTime: { 23 endTime: {
17 type: [Object, Date], 24 type: [Object, Date],
18 - default: new Date(), 25 + default: new Date()
19 }, 26 },
20 defaultTime: { 27 defaultTime: {
21 type: [Object, Date], 28 type: [Object, Date],
22 - default: new Date(), 29 + default: new Date()
23 }, 30 },
24 disabled: { 31 disabled: {
25 type: Boolean, 32 type: Boolean,
26 - default: false, 33 + default: false
27 - }, 34 + }
28 }, 35 },
29 data() { 36 data() {
30 return { 37 return {
...@@ -35,150 +42,139 @@ export default { ...@@ -35,150 +42,139 @@ export default {
35 day: 1, 42 day: 1,
36 hour: 0, 43 hour: 0,
37 minute: 0, 44 minute: 0,
38 - datePicker: "", 45 + datePicker: '',
39 defaultIndex: [0, 0, 0, 0, 0], 46 defaultIndex: [0, 0, 0, 0, 0],
40 startIndex: [0, 0, 0, 0, 0], 47 startIndex: [0, 0, 0, 0, 0],
41 - endIndex: [0, 0, 0, 0, 0], 48 + endIndex: [0, 0, 0, 0, 0]
42 - }; 49 + }
43 }, 50 },
44 computed: { 51 computed: {
45 timeDate() { 52 timeDate() {
46 - const { startTime, endTime } = this; 53 + const { startTime, endTime } = this
47 - return { startTime, endTime }; 54 + return { startTime, endTime }
48 - }, 55 + }
49 }, 56 },
50 watch: { 57 watch: {
51 timeDate() { 58 timeDate() {
52 - this.initData(); 59 + this.initData()
53 }, 60 },
54 - defaultTime () { 61 + defaultTime() {
55 - this.initData(); 62 + this.initData()
56 } 63 }
57 }, 64 },
58 created() { 65 created() {
59 - this.initData(); 66 + this.initData()
60 }, 67 },
61 methods: { 68 methods: {
62 initData() { 69 initData() {
63 - let startTime = this.startTime; 70 + const startTime = this.startTime
64 - let endTime = this.endTime; 71 + const endTime = this.endTime
65 - this.datePicker = dateTimePicker( 72 + this.datePicker = dateTimePicker(startTime.getFullYear(), endTime.getFullYear())
66 - startTime.getFullYear(), 73 + this.setDateData(this.defaultTime)
67 - endTime.getFullYear() 74 + this.getKeyIndex(this.startTime, 'startIndex')
68 - );
69 - this.setDateData(this.defaultTime);
70 - this.getKeyIndex(this.startTime, "startIndex");
71 // 截止时间索引 75 // 截止时间索引
72 - this.getKeyIndex(this.endTime, "endIndex"); 76 + this.getKeyIndex(this.endTime, 'endIndex')
73 // 默认索引 77 // 默认索引
74 - this.getKeyIndex(this.defaultTime, "defaultIndex"); 78 + this.getKeyIndex(this.defaultTime, 'defaultIndex')
75 - this.timeIndex = this.defaultIndex; 79 + this.timeIndex = this.defaultIndex
76 // 初始时间 80 // 初始时间
77 - this.initTime(); 81 + this.initTime()
78 }, 82 },
79 getKeyIndex(time, key) { 83 getKeyIndex(time, key) {
80 - let Arr = dateDate(time); 84 + const Arr = dateDate(time)
81 - let _index = this.getIndex(Arr); 85 + const _index = this.getIndex(Arr)
82 - this[key] = _index; 86 + this[key] = _index
83 }, 87 },
84 getIndex(arr) { 88 getIndex(arr) {
85 - let timeIndex = []; 89 + const timeIndex = []
86 - let indexKey = ["year", "month", "day", "hours", "minutes"]; 90 + const indexKey = ['year', 'month', 'day', 'hours', 'minutes']
87 this.activityArray.forEach((element, index) => { 91 this.activityArray.forEach((element, index) => {
88 - let _index = element.findIndex( 92 + const _index = element.findIndex(
89 - (item) => parseInt(item.id) === parseInt(arr[indexKey[index]]) 93 + item => parseInt(item.id) === parseInt(arr[indexKey[index]])
90 - ); 94 + )
91 - timeIndex[index] = _index >= 0 ? _index : 0; 95 + timeIndex[index] = _index >= 0 ? _index : 0
92 - }); 96 + })
93 - return timeIndex; 97 + return timeIndex
94 }, 98 },
95 initTime() { 99 initTime() {
96 - let _index = this.timeIndex; 100 + const _index = this.timeIndex
97 - this.year = this.activityArray[0][_index[0]].id; 101 + this.year = this.activityArray[0][_index[0]].id
98 - this.month = this.activityArray[1].length && this.activityArray[1][_index[1]].id; 102 + this.month = this.activityArray[1].length && this.activityArray[1][_index[1]].id
99 - this.day = this.activityArray[2].length && this.activityArray[2][_index[2]].id; 103 + this.day = this.activityArray[2].length && this.activityArray[2][_index[2]].id
100 - this.hour = this.activityArray[3].length && this.activityArray[3][_index[3]].id; 104 + this.hour = this.activityArray[3].length && this.activityArray[3][_index[3]].id
101 - this.minute = this.activityArray[4].length && this.activityArray[4][_index[4]].id; 105 + this.minute = this.activityArray[4].length && this.activityArray[4][_index[4]].id
102 }, 106 },
103 setDateData(_date) { 107 setDateData(_date) {
104 - let _data = dateDate(_date); 108 + const _data = dateDate(_date)
105 - this.activityArray = this.datePicker(_data.year, _data.month); 109 + this.activityArray = this.datePicker(_data.year, _data.month)
106 }, 110 },
107 bindMultiPickerChange(e) { 111 bindMultiPickerChange(e) {
108 - console.log("picker发送选择改变,携带值为", e.detail.value); 112 + console.log('picker发送选择改变,携带值为', e.detail.value)
109 - let activityArray = JSON.parse(JSON.stringify(this.activityArray)), 113 + const activityArray = JSON.parse(JSON.stringify(this.activityArray)),
110 { value } = e.detail, 114 { value } = e.detail,
111 - _result = []; 115 + _result = []
112 for (let i = 0; i < value.length; i++) { 116 for (let i = 0; i < value.length; i++) {
113 - _result[i] = activityArray[i][value[i]].id; 117 + _result[i] = activityArray[i][value[i]].id
114 } 118 }
115 - this.$emit("result", _result); 119 + this.$emit('result', _result)
116 }, 120 },
117 bindMultiPickerColumnChange(e) { 121 bindMultiPickerColumnChange(e) {
118 - console.log("修改的列为", e.detail.column, ",值为", e.detail.value); 122 + console.log('修改的列为', e.detail.column, ',值为', e.detail.value)
119 let _data = JSON.parse(JSON.stringify(this.activityArray)), 123 let _data = JSON.parse(JSON.stringify(this.activityArray)),
120 timeIndex = JSON.parse(JSON.stringify(this.timeIndex)), 124 timeIndex = JSON.parse(JSON.stringify(this.timeIndex)),
121 { startIndex, endIndex } = this, 125 { startIndex, endIndex } = this,
122 { column, value } = e.detail, 126 { column, value } = e.detail,
123 _value = _data[column][value].id, 127 _value = _data[column][value].id,
124 _start = dateDate(this.startTime), 128 _start = dateDate(this.startTime),
125 - _end = dateDate(this.endTime); 129 + _end = dateDate(this.endTime)
126 switch (e.detail.column) { 130 switch (e.detail.column) {
127 case 0: 131 case 0:
128 if (_value <= _start.year) { 132 if (_value <= _start.year) {
129 - timeIndex = startIndex; 133 + timeIndex = startIndex
130 - this.year = _start.year; 134 + this.year = _start.year
131 - this.setDateData(this.startTime); 135 + this.setDateData(this.startTime)
132 } else if (_value >= _end.year) { 136 } else if (_value >= _end.year) {
133 - this.year = _end.year; 137 + this.year = _end.year
134 - timeIndex = [endIndex[0], 0, 0, 0, 0]; 138 + timeIndex = [endIndex[0], 0, 0, 0, 0]
135 - this.setDateData(this.endTime); 139 + this.setDateData(this.endTime)
136 } else { 140 } else {
137 - this.year = _value; 141 + this.year = _value
138 - timeIndex = [value, 0, 0, 0, 0]; 142 + timeIndex = [value, 0, 0, 0, 0]
139 - this.activityArray = this.datePicker(_value, 1); 143 + this.activityArray = this.datePicker(_value, 1)
140 } 144 }
141 - timeIndex = this.timeIndex = JSON.parse(JSON.stringify(timeIndex)); 145 + timeIndex = this.timeIndex = JSON.parse(JSON.stringify(timeIndex))
142 - this.timeIndex = timeIndex; 146 + this.timeIndex = timeIndex
143 - break; 147 + break
144 case 1: 148 case 1:
145 if (this.year == _start.year && value <= startIndex[1]) { 149 if (this.year == _start.year && value <= startIndex[1]) {
146 - timeIndex = startIndex; 150 + timeIndex = startIndex
147 - this.month = _start.month; 151 + this.month = _start.month
148 - this.setDateData(this.startTime); 152 + this.setDateData(this.startTime)
149 } else if (this.year == _end.year && value >= endIndex[1]) { 153 } else if (this.year == _end.year && value >= endIndex[1]) {
150 - timeIndex = endIndex; 154 + timeIndex = endIndex
151 - this.month = _end.month; 155 + this.month = _end.month
152 - this.setDateData(this.endTime); 156 + this.setDateData(this.endTime)
153 } else { 157 } else {
154 - this.month = _value; 158 + this.month = _value
155 - _data[2] = this.datePicker(this.year, this.month)[2]; 159 + _data[2] = this.datePicker(this.year, this.month)[2]
156 - timeIndex = [timeIndex[0], value, 0, 0, 0]; 160 + timeIndex = [timeIndex[0], value, 0, 0, 0]
157 - this.activityArray = _data; 161 + this.activityArray = _data
158 } 162 }
159 - this.timeIndex = JSON.parse(JSON.stringify(timeIndex)); 163 + this.timeIndex = JSON.parse(JSON.stringify(timeIndex))
160 - break; 164 + break
161 case 2: 165 case 2:
162 - if ( 166 + if (this.year == _start.year && this.month == _start.month && value <= startIndex[2]) {
163 - this.year == _start.year && 167 + this.day = _start.day
164 - this.month == _start.month && 168 + timeIndex = startIndex
165 - value <= startIndex[2] 169 + } else if (this.year == _end.year && this.month == _end.month && value >= endIndex[2]) {
166 - ) { 170 + this.day = _end.day
167 - this.day = _start.day; 171 + timeIndex = endIndex
168 - timeIndex = startIndex;
169 - } else if (
170 - this.year == _end.year &&
171 - this.month == _end.month &&
172 - value >= endIndex[2]
173 - ) {
174 - this.day = _end.day;
175 - timeIndex = endIndex;
176 } else { 172 } else {
177 - this.day = _value; 173 + this.day = _value
178 - timeIndex = [timeIndex[0], timeIndex[1], value, 0, 0]; 174 + timeIndex = [timeIndex[0], timeIndex[1], value, 0, 0]
179 } 175 }
180 - this.timeIndex = JSON.parse(JSON.stringify(timeIndex)); 176 + this.timeIndex = JSON.parse(JSON.stringify(timeIndex))
181 - break; 177 + break
182 case 3: 178 case 3:
183 if ( 179 if (
184 this.year == _start.year && 180 this.year == _start.year &&
...@@ -186,25 +182,25 @@ export default { ...@@ -186,25 +182,25 @@ export default {
186 this.day == _start.day && 182 this.day == _start.day &&
187 value <= startIndex[3] 183 value <= startIndex[3]
188 ) { 184 ) {
189 - this.hour = _start.hours; 185 + this.hour = _start.hours
190 - timeIndex = startIndex; 186 + timeIndex = startIndex
191 } else if ( 187 } else if (
192 this.year == _end.year && 188 this.year == _end.year &&
193 this.month == _end.month && 189 this.month == _end.month &&
194 this.day == _end.day && 190 this.day == _end.day &&
195 value >= endIndex[3] 191 value >= endIndex[3]
196 ) { 192 ) {
197 - this.hour = _end.hours; 193 + this.hour = _end.hours
198 - timeIndex = endIndex; 194 + timeIndex = endIndex
199 } else { 195 } else {
200 - this.hour = _value; 196 + this.hour = _value
201 - timeIndex[3] = value; 197 + timeIndex[3] = value
202 - timeIndex[4] = 0; 198 + timeIndex[4] = 0
203 } 199 }
204 - this.timeIndex = JSON.parse(JSON.stringify(timeIndex)); 200 + this.timeIndex = JSON.parse(JSON.stringify(timeIndex))
205 - break; 201 + break
206 case 4: 202 case 4:
207 - timeIndex[4] = value; 203 + timeIndex[4] = value
208 if ( 204 if (
209 this.year == _start.year && 205 this.year == _start.year &&
210 this.month == _start.month && 206 this.month == _start.month &&
...@@ -212,7 +208,7 @@ export default { ...@@ -212,7 +208,7 @@ export default {
212 this.hour == _start.hours && 208 this.hour == _start.hours &&
213 value <= startIndex[4] 209 value <= startIndex[4]
214 ) { 210 ) {
215 - timeIndex = startIndex; 211 + timeIndex = startIndex
216 } else if ( 212 } else if (
217 this.year == _end.year && 213 this.year == _end.year &&
218 this.month == _end.month && 214 this.month == _end.month &&
...@@ -220,12 +216,12 @@ export default { ...@@ -220,12 +216,12 @@ export default {
220 this.hour == _end.hours && 216 this.hour == _end.hours &&
221 value >= endIndex[4] 217 value >= endIndex[4]
222 ) { 218 ) {
223 - timeIndex = endIndex; 219 + timeIndex = endIndex
224 } 220 }
225 - this.timeIndex = JSON.parse(JSON.stringify(timeIndex)); 221 + this.timeIndex = JSON.parse(JSON.stringify(timeIndex))
226 - break; 222 + break
227 } 223 }
228 - }, 224 + }
229 - }, 225 + }
230 -}; 226 +}
231 </script> 227 </script>
......
...@@ -22,17 +22,19 @@ let refresh_promise = null ...@@ -22,17 +22,19 @@ let refresh_promise = null
22 * @param {Object} bill 原始预约记录 22 * @param {Object} bill 原始预约记录
23 * @returns {Object} 扁平化后的预约记录对象 23 * @returns {Object} 扁平化后的预约记录对象
24 */ 24 */
25 -const extract_bill_payload = (bill) => { 25 +const extract_bill_payload = bill => {
26 - if (!bill) return {} 26 + if (!bill) {
27 + return {}
28 + }
27 29
28 - const data = { ...bill } 30 + const data = { ...bill }
29 - const list = data.list 31 + const list = data.list
30 32
31 - if (list && typeof list === 'object' && !Array.isArray(list)) { 33 + if (list && typeof list === 'object' && !Array.isArray(list)) {
32 - return { ...list, ...data } 34 + return { ...list, ...data }
33 - } 35 + }
34 36
35 - return data 37 + return data
36 } 38 }
37 39
38 /** 40 /**
...@@ -42,26 +44,28 @@ const extract_bill_payload = (bill) => { ...@@ -42,26 +44,28 @@ const extract_bill_payload = (bill) => {
42 * @param {Object} bill 预约记录 44 * @param {Object} bill 预约记录
43 * @returns {Array} 人员列表 45 * @returns {Array} 人员列表
44 */ 46 */
45 -const extract_person_list = (bill) => { 47 +const extract_person_list = bill => {
46 - if (!bill) return [] 48 + if (!bill) {
47 - 49 + return []
48 - /** 50 + }
49 - * 从预约记录中提取人员列表 51 +
50 - * - 考虑不同字段名的情况(如 person_list, bill_person_list, persons, qrcode_list, qr_list, detail_list) 52 + /**
51 - * - 确保返回的是数组类型 53 + * 从预约记录中提取人员列表
52 - */ 54 + * - 考虑不同字段名的情况(如 person_list, bill_person_list, persons, qrcode_list, qr_list, detail_list)
53 - const candidate = 55 + * - 确保返回的是数组类型
54 - (Array.isArray(bill.list) ? bill.list : null) || 56 + */
55 - (Array.isArray(bill?.list?.list) ? bill.list.list : null) || 57 + const candidate =
56 - bill.person_list || 58 + (Array.isArray(bill.list) ? bill.list : null) ||
57 - bill.bill_person_list || 59 + (Array.isArray(bill?.list?.list) ? bill.list.list : null) ||
58 - bill.persons || 60 + bill.person_list ||
59 - bill.qrcode_list || 61 + bill.bill_person_list ||
60 - bill.qr_list || 62 + bill.persons ||
61 - bill.detail_list || 63 + bill.qrcode_list ||
62 - [] 64 + bill.qr_list ||
63 - 65 + bill.detail_list ||
64 - return Array.isArray(candidate) ? candidate : [] 66 + []
67 +
68 + return Array.isArray(candidate) ? candidate : []
65 } 69 }
66 70
67 /** 71 /**
...@@ -69,21 +73,23 @@ const extract_person_list = (bill) => { ...@@ -69,21 +73,23 @@ const extract_person_list = (bill) => {
69 * @param {Object} item 原始预约记录项 73 * @param {Object} item 原始预约记录项
70 * @returns {Object} 格式化后的预约记录项 74 * @returns {Object} 格式化后的预约记录项
71 */ 75 */
72 -const normalize_bill_item = (item) => { 76 +const normalize_bill_item = item => {
73 - const data = extract_bill_payload(item) 77 + const data = extract_bill_payload(item)
74 - 78 +
75 - data.datetime = data.datetime || formatDatetime(data) 79 + data.datetime = data.datetime || formatDatetime(data)
76 - data.booking_time = data.booking_time || data.datetime 80 + data.booking_time = data.booking_time || data.datetime
77 - data.order_time = data.order_time || (data.created_time ? data.created_time.slice(0, -3) : '') 81 + data.order_time = data.order_time || (data.created_time ? data.created_time.slice(0, -3) : '')
78 - 82 +
79 - if (!data.person_name) { 83 + if (!data.person_name) {
80 - const person_list = extract_person_list(item) 84 + const person_list = extract_person_list(item)
81 - const first = person_list[0] 85 + const first = person_list[0]
82 - const name = first?.name || first?.person_name 86 + const name = first?.name || first?.person_name
83 - if (name) data.person_name = name 87 + if (name) {
88 + data.person_name = name
84 } 89 }
90 + }
85 91
86 - return data 92 + return data
87 } 93 }
88 94
89 /** 95 /**
...@@ -91,12 +97,12 @@ const normalize_bill_item = (item) => { ...@@ -91,12 +97,12 @@ const normalize_bill_item = (item) => {
91 * @returns {Array} 格式化后的预约记录项列表 97 * @returns {Array} 格式化后的预约记录项列表
92 */ 98 */
93 export const get_offline_booking_cache = () => { 99 export const get_offline_booking_cache = () => {
94 - try { 100 + try {
95 - const data = Taro.getStorageSync(OFFLINE_BOOKING_CACHE_KEY) 101 + const data = Taro.getStorageSync(OFFLINE_BOOKING_CACHE_KEY)
96 - return Array.isArray(data) ? data : [] 102 + return Array.isArray(data) ? data : []
97 - } catch (e) { 103 + } catch (e) {
98 - return [] 104 + return []
99 - } 105 + }
100 } 106 }
101 107
102 /** 108 /**
...@@ -104,8 +110,8 @@ export const get_offline_booking_cache = () => { ...@@ -104,8 +110,8 @@ export const get_offline_booking_cache = () => {
104 * @returns {boolean} 是否存在缓存且非空 110 * @returns {boolean} 是否存在缓存且非空
105 */ 111 */
106 export const has_offline_booking_cache = () => { 112 export const has_offline_booking_cache = () => {
107 - const list = get_offline_booking_cache() 113 + const list = get_offline_booking_cache()
108 - return Array.isArray(list) && list.length > 0 114 + return Array.isArray(list) && list.length > 0
109 } 115 }
110 116
111 /** 117 /**
...@@ -113,10 +119,10 @@ export const has_offline_booking_cache = () => { ...@@ -113,10 +119,10 @@ export const has_offline_booking_cache = () => {
113 * @param {*} pay_id 支付ID 119 * @param {*} pay_id 支付ID
114 * @returns {Object|null} 匹配的预约记录项或 null 120 * @returns {Object|null} 匹配的预约记录项或 null
115 */ 121 */
116 -export const get_offline_booking_by_pay_id = (pay_id) => { 122 +export const get_offline_booking_by_pay_id = pay_id => {
117 - const list = get_offline_booking_cache() 123 + const list = get_offline_booking_cache()
118 - const target_pay_id = String(pay_id || '') 124 + const target_pay_id = String(pay_id || '')
119 - return list.find((item) => String(item?.pay_id || '') === target_pay_id) || null 125 + return list.find(item => String(item?.pay_id || '') === target_pay_id) || null
120 } 126 }
121 127
122 /** 128 /**
...@@ -124,8 +130,8 @@ export const get_offline_booking_by_pay_id = (pay_id) => { ...@@ -124,8 +130,8 @@ export const get_offline_booking_by_pay_id = (pay_id) => {
124 * @param {Object} bill - 预约记录项 130 * @param {Object} bill - 预约记录项
125 * @returns {Array} 人员列表(包含姓名、身份证号、二维码等信息) 131 * @returns {Array} 人员列表(包含姓名、身份证号、二维码等信息)
126 */ 132 */
127 -export const get_offline_bill_person_list = (bill) => { 133 +export const get_offline_bill_person_list = bill => {
128 - return extract_person_list(bill) 134 + return extract_person_list(bill)
129 } 135 }
130 136
131 /** 137 /**
...@@ -133,29 +139,36 @@ export const get_offline_bill_person_list = (bill) => { ...@@ -133,29 +139,36 @@ export const get_offline_bill_person_list = (bill) => {
133 * @param {Object} bill - 预约记录项 139 * @param {Object} bill - 预约记录项
134 * @returns {Array} 二维码列表(包含姓名、身份证号、二维码、预约时间等信息) 140 * @returns {Array} 二维码列表(包含姓名、身份证号、二维码、预约时间等信息)
135 */ 141 */
136 -export const build_offline_qr_list = (bill) => { 142 +export const build_offline_qr_list = bill => {
137 - const list = get_offline_bill_person_list(bill) 143 + const list = get_offline_bill_person_list(bill)
138 - const datetime = bill?.datetime || formatDatetime(bill || {}) 144 + const datetime = bill?.datetime || formatDatetime(bill || {})
139 - 145 +
140 - return list 146 + return list
141 - .filter((item) => item && (item.qr_code || item.qrcode || item.qrCode) && (item.qr_code || item.qrcode || item.qrCode) !== '') 147 + .filter(
142 - .map((item) => { 148 + item =>
143 - const begin_time = item.begin_time || bill?.begin_time 149 + item &&
144 - const end_time = item.end_time || bill?.end_time 150 + (item.qr_code || item.qrcode || item.qrCode) &&
145 - const qr_code = item.qr_code || item.qrcode || item.qrCode 151 + (item.qr_code || item.qrcode || item.qrCode) !== ''
146 - const name = item.name || item.person_name || item.real_name 152 + )
147 - const id_number = item.id_number || item.idcard || item.idCard || item.id 153 + .map(item => {
148 - return { 154 + const begin_time = item.begin_time || bill?.begin_time
149 - name, 155 + const end_time = item.end_time || bill?.end_time
150 - id_number, 156 + const qr_code = item.qr_code || item.qrcode || item.qrCode
151 - qr_code, 157 + const name = item.name || item.person_name || item.real_name
152 - begin_time, 158 + const id_number = item.id_number || item.idcard || item.idCard || item.id
153 - end_time, 159 + return {
154 - datetime: item.datetime || (begin_time && end_time ? formatDatetime({ begin_time, end_time }) : datetime), 160 + name,
155 - pay_id: bill?.pay_id, 161 + id_number,
156 - sort: 0, 162 + qr_code,
157 - } 163 + begin_time,
158 - }) 164 + end_time,
165 + datetime:
166 + item.datetime ||
167 + (begin_time && end_time ? formatDatetime({ begin_time, end_time }) : datetime),
168 + pay_id: bill?.pay_id,
169 + sort: 0
170 + }
171 + })
159 } 172 }
160 173
161 /** 174 /**
...@@ -169,42 +182,48 @@ export const build_offline_qr_list = (bill) => { ...@@ -169,42 +182,48 @@ export const build_offline_qr_list = (bill) => {
169 */ 182 */
170 183
171 export const refresh_offline_booking_cache = async ({ force = false } = {}) => { 184 export const refresh_offline_booking_cache = async ({ force = false } = {}) => {
172 - // 1. 检查是否有正在进行的刷新请求 185 + // 1. 检查是否有正在进行的刷新请求
173 - // 2. 如果有,且 force 为 false,则直接返回该 Promise 186 + // 2. 如果有,且 force 为 false,则直接返回该 Promise
174 - // 3. 如果没有,或 force 为 true,则继续执行刷新逻辑 187 + // 3. 如果没有,或 force 为 true,则继续执行刷新逻辑
175 - // 4. 刷新完成后,将结果存储到本地缓存(key: OFFLINE_BOOKING_CACHE_KEY) 188 + // 4. 刷新完成后,将结果存储到本地缓存(key: OFFLINE_BOOKING_CACHE_KEY)
176 - // 5. 返回刷新结果 Promise 189 + // 5. 返回刷新结果 Promise
177 - 190 +
178 - if (!hasAuth()) return { code: 0, data: null, msg: '未授权' } 191 + if (!hasAuth()) {
179 - 192 + return { code: 0, data: null, msg: '未授权' }
180 - if (refresh_promise && !force) return refresh_promise 193 + }
181 - 194 +
182 - // 核心逻辑: 195 + if (refresh_promise && !force) {
183 - // 1. 立刻触发异步逻辑,同时捕获 Promise 状态 196 + return refresh_promise
184 - // 2. 保证 refresh_promise 始终是 Promise 类型,适配 await 197 + }
185 - // 3. 隔离作用域,避免变量污染 198 +
186 - // 加 () 是为了 “让异步逻辑立刻跑起来”,并把 “跑的结果(Promise)” 存起来,供后续复用和等待。 199 + // 核心逻辑:
187 - refresh_promise = (async () => { 200 + // 1. 立刻触发异步逻辑,同时捕获 Promise 状态
188 - const network_type = await get_network_type() 201 + // 2. 保证 refresh_promise 始终是 Promise 类型,适配 await
189 - if (!is_usable_network(network_type)) { 202 + // 3. 隔离作用域,避免变量污染
190 - return { code: 0, data: null, msg: '网络不可用' } 203 + // 加 () 是为了 “让异步逻辑立刻跑起来”,并把 “跑的结果(Promise)” 存起来,供后续复用和等待。
191 - } 204 + refresh_promise = (async () => {
192 - 205 + const network_type = await get_network_type()
193 - const { code, data, msg } = await billOfflineAllAPI() 206 + if (!is_usable_network(network_type)) {
194 - if (code && Array.isArray(data)) { 207 + return { code: 0, data: null, msg: '网络不可用' }
195 - // 过滤出状态为3(已完成)的记录 208 + }
196 - const normalized = data.map(normalize_bill_item).filter((item) => item && item.pay_id && item.status == 3) 209 +
197 - if (normalized.length > 0) { 210 + const { code, data, msg } = await billOfflineAllAPI()
198 - // TAG: 核心逻辑:将过滤后的记录存储到本地缓存 211 + if (code && Array.isArray(data)) {
199 - Taro.setStorageSync(OFFLINE_BOOKING_CACHE_KEY, normalized) 212 + // 过滤出状态为3(已完成)的记录
200 - } 213 + const normalized = data
201 - } 214 + .map(normalize_bill_item)
202 - return { code, data, msg } 215 + .filter(item => item && item.pay_id && item.status == 3)
203 - })() 216 + if (normalized.length > 0) {
204 - 217 + // TAG: 核心逻辑:将过滤后的记录存储到本地缓存
205 - try { 218 + Taro.setStorageSync(OFFLINE_BOOKING_CACHE_KEY, normalized)
206 - return await refresh_promise 219 + }
207 - } finally {
208 - refresh_promise = null
209 } 220 }
221 + return { code, data, msg }
222 + })()
223 +
224 + try {
225 + return await refresh_promise
226 + } finally {
227 + refresh_promise = null
228 + }
210 } 229 }
......
...@@ -31,16 +31,16 @@ import { get_network_type, is_usable_network } from '@/utils/network' ...@@ -31,16 +31,16 @@ import { get_network_type, is_usable_network } from '@/utils/network'
31 31
32 /** @type {PollingState} */ 32 /** @type {PollingState} */
33 const polling_state = { 33 const polling_state = {
34 - timer_id: null, // 轮询定时器id 34 + timer_id: null, // 轮询定时器id
35 - running: false, // 是否正在轮询 35 + running: false, // 是否正在轮询
36 - in_flight: false, // 是否正在刷新 36 + in_flight: false, // 是否正在刷新
37 - ref_count: 0, // 引用计数 37 + ref_count: 0, // 引用计数
38 - app_enabled: false, // 是否启用应用 38 + app_enabled: false, // 是否启用应用
39 - last_options: null, // 最后一次选项 39 + last_options: null, // 最后一次选项
40 - network_usable: null, // 网络可用性 40 + network_usable: null, // 网络可用性
41 - has_network_listener: false, // 是否已注册网络监听器 41 + has_network_listener: false, // 是否已注册网络监听器
42 - network_listener: null, // 网络监听器 42 + network_listener: null, // 网络监听器
43 - network_listener_promise: null, // 网络监听器Promise 43 + network_listener_promise: null // 网络监听器Promise
44 } 44 }
45 45
46 /** 46 /**
...@@ -48,8 +48,8 @@ const polling_state = { ...@@ -48,8 +48,8 @@ const polling_state = {
48 * @param {Object} options 选项 48 * @param {Object} options 选项
49 * @return {Object} 规范化后的选项 49 * @return {Object} 规范化后的选项
50 */ 50 */
51 -const normalize_options = (options) => { 51 +const normalize_options = options => {
52 - return options || {} 52 + return options || {}
53 } 53 }
54 54
55 /** 55 /**
...@@ -57,9 +57,11 @@ const normalize_options = (options) => { ...@@ -57,9 +57,11 @@ const normalize_options = (options) => {
57 * @param {Object} options 选项 57 * @param {Object} options 选项
58 * @return {Object} 保存后的选项 58 * @return {Object} 保存后的选项
59 */ 59 */
60 -const save_last_options = (options) => { 60 +const save_last_options = options => {
61 - if (options) polling_state.last_options = options 61 + if (options) {
62 - return polling_state.last_options 62 + polling_state.last_options = options
63 + }
64 + return polling_state.last_options
63 } 65 }
64 66
65 /** 67 /**
...@@ -73,19 +75,23 @@ const save_last_options = (options) => { ...@@ -73,19 +75,23 @@ const save_last_options = (options) => {
73 * @param {Object} options 选项 75 * @param {Object} options 选项
74 * @param {Boolean} options.force 是否强制刷新 76 * @param {Boolean} options.force 是否强制刷新
75 */ 77 */
76 -const run_refresh_once = async (options) => { 78 +const run_refresh_once = async options => {
77 - // 前置检查:不满足轮询条件时直接返回(网络不可用或无引用) 79 + // 前置检查:不满足轮询条件时直接返回(网络不可用或无引用)
78 - if (!should_run_polling()) return 80 + if (!should_run_polling()) {
79 - // 核心防重复——如果正在刷新,直接返回 81 + return
80 - if (polling_state.in_flight) return 82 + }
81 - // 标记为"正在刷新" 83 + // 核心防重复——如果正在刷新,直接返回
82 - polling_state.in_flight = true 84 + if (polling_state.in_flight) {
83 - try { 85 + return
84 - await refresh_offline_booking_cache({ force: !!options?.force }) 86 + }
85 - } finally { 87 + // 标记为"正在刷新"
86 - // 刷新完成后,标记为"刷新完成" 88 + polling_state.in_flight = true
87 - polling_state.in_flight = false 89 + try {
88 - } 90 + await refresh_offline_booking_cache({ force: !!options?.force })
91 + } finally {
92 + // 刷新完成后,标记为"刷新完成"
93 + polling_state.in_flight = false
94 + }
89 } 95 }
90 96
91 /** 97 /**
...@@ -93,8 +99,8 @@ const run_refresh_once = async (options) => { ...@@ -93,8 +99,8 @@ const run_refresh_once = async (options) => {
93 */ 99 */
94 100
95 const update_network_usable = async () => { 101 const update_network_usable = async () => {
96 - const type = await get_network_type() 102 + const type = await get_network_type()
97 - polling_state.network_usable = is_usable_network(type) 103 + polling_state.network_usable = is_usable_network(type)
98 } 104 }
99 105
100 /** 106 /**
...@@ -111,10 +117,16 @@ const update_network_usable = async () => { ...@@ -111,10 +117,16 @@ const update_network_usable = async () => {
111 * 2. network_usable === true:网络可用 117 * 2. network_usable === true:网络可用
112 */ 118 */
113 const should_run_polling = () => { 119 const should_run_polling = () => {
114 - if (polling_state.ref_count <= 0) return false 120 + if (polling_state.ref_count <= 0) {
115 - if (polling_state.network_usable === false) return false 121 + return false
116 - if (polling_state.network_usable === null) return false 122 + }
117 - return true 123 + if (polling_state.network_usable === false) {
124 + return false
125 + }
126 + if (polling_state.network_usable === null) {
127 + return false
128 + }
129 + return true
118 } 130 }
119 131
120 /** 132 /**
...@@ -122,74 +134,74 @@ const should_run_polling = () => { ...@@ -122,74 +134,74 @@ const should_run_polling = () => {
122 * @return {Promise<Boolean>} 是否注册成功(true=成功,false=失败) 134 * @return {Promise<Boolean>} 是否注册成功(true=成功,false=失败)
123 */ 135 */
124 const ensure_network_listener = async () => { 136 const ensure_network_listener = async () => {
125 - /** 137 + /**
126 - * 代码优先通过两个条件判断避免重复执行监听器逻辑 138 + * 代码优先通过两个条件判断避免重复执行监听器逻辑
127 - * 1. 有已注册的监听器直接返回 139 + * 1. 有已注册的监听器直接返回
128 - * 2. 有未完成的注册 Promise 则直接返回 140 + * 2. 有未完成的注册 Promise 则直接返回
129 - */ 141 + */
130 - 142 +
131 - if (polling_state.has_network_listener) { 143 + if (polling_state.has_network_listener) {
132 - await update_network_usable() 144 + await update_network_usable()
133 - return true 145 + return true
134 - } 146 + }
135 147
136 - if (polling_state.network_listener_promise) { 148 + if (polling_state.network_listener_promise) {
137 - await polling_state.network_listener_promise 149 + await polling_state.network_listener_promise
138 - // 等待注册完成后检查是否成功 150 + // 等待注册完成后检查是否成功
139 - return polling_state.has_network_listener 151 + return polling_state.has_network_listener
152 + }
153 +
154 + // 立即执行异步的监听器注册流程(标记状态→更新网络可用性→定义回调→注册监听)
155 + polling_state.network_listener_promise = (async () => {
156 + // 标记已注册网络监听器
157 + polling_state.has_network_listener = true
158 + // 初始化时更新网络可用性
159 + await update_network_usable()
160 +
161 + // 网络状态变化监听器, 网络状态变化时的处理逻辑,此时只是定义,不会立即执行
162 + polling_state.network_listener = res => {
163 + const is_connected = res?.isConnected !== false
164 + const type = res?.networkType || 'unknown'
165 + polling_state.network_usable = is_connected && is_usable_network(type)
166 +
167 + // 改进:不再主动停止轮询,由 run_refresh_once 中的 should_run_polling() 前置检查控制
168 + // 优势:
169 + // 1. 避免网络恢复时需要额外的重启逻辑
170 + // 2. 保持定时器稳定,避免频繁启动/停止
171 + // 3. 网络不可用时,刷新操作会在 run_refresh_once 中被前置检查过滤掉
172 +
173 + // 网络恢复时,确保轮询正在运行(处理之前因网络不可用而可能停止的轮询)
174 + if (polling_state.network_usable && should_run_polling()) {
175 + // 传入 restart: true,支持重启逻辑
176 + // 使用 normalize_options 显式处理 null/undefined,语义更清晰
177 + start_offline_booking_cache_polling({
178 + ...normalize_options(polling_state.last_options),
179 + restart: true
180 + })
181 + }
140 } 182 }
141 183
142 - // 立即执行异步的监听器注册流程(标记状态→更新网络可用性→定义回调→注册监听)
143 - polling_state.network_listener_promise = (async () => {
144 - // 标记已注册网络监听器
145 - polling_state.has_network_listener = true
146 - // 初始化时更新网络可用性
147 - await update_network_usable()
148 -
149 - // 网络状态变化监听器, 网络状态变化时的处理逻辑,此时只是定义,不会立即执行
150 - polling_state.network_listener = (res) => {
151 - const is_connected = res?.isConnected !== false
152 - const type = res?.networkType || 'unknown'
153 - polling_state.network_usable = is_connected && is_usable_network(type)
154 -
155 - // 改进:不再主动停止轮询,由 run_refresh_once 中的 should_run_polling() 前置检查控制
156 - // 优势:
157 - // 1. 避免网络恢复时需要额外的重启逻辑
158 - // 2. 保持定时器稳定,避免频繁启动/停止
159 - // 3. 网络不可用时,刷新操作会在 run_refresh_once 中被前置检查过滤掉
160 -
161 - // 网络恢复时,确保轮询正在运行(处理之前因网络不可用而可能停止的轮询)
162 - if (polling_state.network_usable && should_run_polling()) {
163 - // 传入 restart: true,支持重启逻辑
164 - // 使用 normalize_options 显式处理 null/undefined,语义更清晰
165 - start_offline_booking_cache_polling({
166 - ...normalize_options(polling_state.last_options),
167 - restart: true
168 - })
169 - }
170 - }
171 -
172 - try {
173 - // 注册网络状态变化监听器
174 - Taro.onNetworkStatusChange(polling_state.network_listener)
175 - } catch (e) {
176 - polling_state.has_network_listener = false
177 - polling_state.network_listener = null
178 - polling_state.network_usable = null
179 - console.error('注册网络监听失败:', e)
180 - }
181 - })()
182 -
183 try { 184 try {
184 - // 等待网络监听器初始化完成 185 + // 注册网络状态变化监听器
185 - await polling_state.network_listener_promise 186 + Taro.onNetworkStatusChange(polling_state.network_listener)
186 - } finally { 187 + } catch (e) {
187 - // 等待注册流程完成后,强制清空 Promise 缓存(finally 块),保证下次执行逻辑时状态干净 188 + polling_state.has_network_listener = false
188 - polling_state.network_listener_promise = null 189 + polling_state.network_listener = null
190 + polling_state.network_usable = null
191 + console.error('注册网络监听失败:', e)
189 } 192 }
190 - 193 + })()
191 - // 返回注册是否成功 194 +
192 - return polling_state.has_network_listener 195 + try {
196 + // 等待网络监听器初始化完成
197 + await polling_state.network_listener_promise
198 + } finally {
199 + // 等待注册流程完成后,强制清空 Promise 缓存(finally 块),保证下次执行逻辑时状态干净
200 + polling_state.network_listener_promise = null
201 + }
202 +
203 + // 返回注册是否成功
204 + return polling_state.has_network_listener
193 } 205 }
194 206
195 /** 207 /**
...@@ -202,33 +214,37 @@ const ensure_network_listener = async () => { ...@@ -202,33 +214,37 @@ const ensure_network_listener = async () => {
202 */ 214 */
203 215
204 const teardown_network_listener = () => { 216 const teardown_network_listener = () => {
205 - // 1. 前置校验:避免无效执行 217 + // 1. 前置校验:避免无效执行
206 - // 如果没有注册网络监听器,直接返回 218 + // 如果没有注册网络监听器,直接返回
207 - if (!polling_state.has_network_listener) return 219 + if (!polling_state.has_network_listener) {
208 - // 如果有引用计数,说明有其他地方在使用轮询,不能注销监听器 220 + return
209 - if (polling_state.ref_count > 0) return 221 + }
210 - // 标记监听器已注销(核心状态更新) 222 + // 如果有引用计数,说明有其他地方在使用轮询,不能注销监听器
211 - polling_state.has_network_listener = false 223 + if (polling_state.ref_count > 0) {
212 - // 解绑框架层面的监听器 224 + return
213 - if (polling_state.network_listener && typeof Taro.offNetworkStatusChange === 'function') { 225 + }
214 - try { 226 + // 标记监听器已注销(核心状态更新)
215 - Taro.offNetworkStatusChange(polling_state.network_listener) 227 + polling_state.has_network_listener = false
216 - } catch (e) { 228 + // 解绑框架层面的监听器
217 - // 捕获解绑失败的异常(比如监听器已被手动解绑) 229 + if (polling_state.network_listener && typeof Taro.offNetworkStatusChange === 'function') {
218 - console.warn('注销网络监听器失败:', e) 230 + try {
219 - } 231 + Taro.offNetworkStatusChange(polling_state.network_listener)
232 + } catch (e) {
233 + // 捕获解绑失败的异常(比如监听器已被手动解绑)
234 + console.warn('注销网络监听器失败:', e)
220 } 235 }
221 - // 手动清空本地引用(关键!无论解绑成功/失败都要做) 236 + }
222 - // 注销后,清空网络监听器引用,确保后续调用能正常工作 237 + // 手动清空本地引用(关键!无论解绑成功/失败都要做)
223 - polling_state.network_listener = null 238 + // 注销后,清空网络监听器引用,确保后续调用能正常工作
224 - /** 239 + polling_state.network_listener = null
225 - * 核心目的:清空 network_usable = null 是为了让状态和监听器的生命周期完全同步 —— 监听器注销后,其产生的网络状态也必须失效,避免 “无监听器却有状态” 的矛盾; 240 + /**
226 - * 关键作用:通过让 should_run_polling() 直接返回 false,杜绝基于过期状态启动轮询的可能; 241 + * 核心目的:清空 network_usable = null 是为了让状态和监听器的生命周期完全同步 —— 监听器注销后,其产生的网络状态也必须失效,避免 “无监听器却有状态” 的矛盾;
227 - * 设计思维:体现了 “状态闭环” 的工程化思想 —— 任何状态都要有明确的产生、更新、销毁逻辑,不残留 “脏数据” 干扰后续流程。 242 + * 关键作用:通过让 should_run_polling() 直接返回 false,杜绝基于过期状态启动轮询的可能;
228 - */ 243 + * 设计思维:体现了 “状态闭环” 的工程化思想 —— 任何状态都要有明确的产生、更新、销毁逻辑,不残留 “脏数据” 干扰后续流程。
229 - // 清空网络可用性状态,确保后续判断逻辑能正常工作 244 + */
230 - // 清空衍生状态,避免脏数据 245 + // 清空网络可用性状态,确保后续判断逻辑能正常工作
231 - polling_state.network_usable = null 246 + // 清空衍生状态,避免脏数据
247 + polling_state.network_usable = null
232 } 248 }
233 249
234 /** 250 /**
...@@ -239,35 +255,39 @@ const teardown_network_listener = () => { ...@@ -239,35 +255,39 @@ const teardown_network_listener = () => {
239 * @param {Boolean} options.force 是否强制刷新(透传给 refresh_offline_booking_cache) 255 * @param {Boolean} options.force 是否强制刷新(透传给 refresh_offline_booking_cache)
240 * @param {Boolean} options.restart 是否为重启操作(网络恢复时调用) 256 * @param {Boolean} options.restart 是否为重启操作(网络恢复时调用)
241 */ 257 */
242 -const start_offline_booking_cache_polling = (options) => { 258 +const start_offline_booking_cache_polling = options => {
243 - options = normalize_options(options) 259 + options = normalize_options(options)
244 - if (!should_run_polling()) return // 不满足轮询条件直接返回 260 + if (!should_run_polling()) {
245 - 261 + return
246 - const interval_ms = Number(options?.interval_ms || 60000) 262 + } // 不满足轮询条件直接返回
247 - const is_restart = options?.restart === true 263 +
248 - 264 + const interval_ms = Number(options?.interval_ms || 60000)
249 - // 改进:区分首次启动和重启的防重逻辑 265 + const is_restart = options?.restart === true
250 - // 首次启动时,如果已经在轮询则直接返回(防重复启动) 266 +
251 - // 重启时,需要清除旧定时器并重新建立(支持网络恢复时重启) 267 + // 改进:区分首次启动和重启的防重逻辑
252 - if (polling_state.running && !is_restart) return 268 + // 首次启动时,如果已经在轮询则直接返回(防重复启动)
253 - 269 + // 重启时,需要清除旧定时器并重新建立(支持网络恢复时重启)
254 - // 如果是重启或定时器已存在,先清除旧定时器 270 + if (polling_state.running && !is_restart) {
255 - if (is_restart && polling_state.timer_id) { 271 + return
256 - clearInterval(polling_state.timer_id) 272 + }
257 - polling_state.timer_id = null 273 +
258 - } 274 + // 如果是重启或定时器已存在,先清除旧定时器
259 - 275 + if (is_restart && polling_state.timer_id) {
260 - polling_state.running = true // 标记为"正在轮询" 276 + clearInterval(polling_state.timer_id)
261 - 277 + polling_state.timer_id = null
262 - // 立即刷新一次,确保轮询开始时数据是最新的 278 + }
263 - if (options?.immediate !== false) { 279 +
264 - run_refresh_once(options) 280 + polling_state.running = true // 标记为"正在轮询"
265 - } 281 +
266 - 282 + // 立即刷新一次,确保轮询开始时数据是最新的
267 - // 启动轮询定时器,按照指定间隔执行刷新操作 283 + if (options?.immediate !== false) {
268 - polling_state.timer_id = setInterval(() => { 284 + run_refresh_once(options)
269 - run_refresh_once(options) 285 + }
270 - }, interval_ms) 286 +
287 + // 启动轮询定时器,按照指定间隔执行刷新操作
288 + polling_state.timer_id = setInterval(() => {
289 + run_refresh_once(options)
290 + }, interval_ms)
271 } 291 }
272 292
273 /** 293 /**
...@@ -275,11 +295,11 @@ const start_offline_booking_cache_polling = (options) => { ...@@ -275,11 +295,11 @@ const start_offline_booking_cache_polling = (options) => {
275 */ 295 */
276 296
277 const stop_offline_booking_cache_polling = () => { 297 const stop_offline_booking_cache_polling = () => {
278 - if (polling_state.timer_id) { 298 + if (polling_state.timer_id) {
279 - clearInterval(polling_state.timer_id) 299 + clearInterval(polling_state.timer_id)
280 - polling_state.timer_id = null 300 + polling_state.timer_id = null
281 - } 301 + }
282 - polling_state.running = false 302 + polling_state.running = false
283 } 303 }
284 304
285 /** 305 /**
...@@ -296,15 +316,15 @@ const stop_offline_booking_cache_polling = () => { ...@@ -296,15 +316,15 @@ const stop_offline_booking_cache_polling = () => {
296 * 核心动作:将全局的 ref_count 加 1,代表 "又多了一个场景需要使用轮询功能"。 316 * 核心动作:将全局的 ref_count 加 1,代表 "又多了一个场景需要使用轮询功能"。
297 * @param {Object} options 选项 317 * @param {Object} options 选项
298 */ 318 */
299 -const acquire_polling_ref = (options) => { 319 +const acquire_polling_ref = options => {
300 - save_last_options(options) 320 + save_last_options(options)
301 - polling_state.ref_count += 1 321 + polling_state.ref_count += 1
302 - // 改进:检查网络监听器注册结果,只有成功后才启动轮询 322 + // 改进:检查网络监听器注册结果,只有成功后才启动轮询
303 - ensure_network_listener().then((success) => { 323 + ensure_network_listener().then(success => {
304 - if (success && polling_state.last_options) { 324 + if (success && polling_state.last_options) {
305 - start_offline_booking_cache_polling(polling_state.last_options) 325 + start_offline_booking_cache_polling(polling_state.last_options)
306 - } 326 + }
307 - }) 327 + })
308 } 328 }
309 329
310 /** 330 /**
...@@ -313,12 +333,12 @@ const acquire_polling_ref = (options) => { ...@@ -313,12 +333,12 @@ const acquire_polling_ref = (options) => {
313 */ 333 */
314 334
315 const release_polling_ref = () => { 335 const release_polling_ref = () => {
316 - polling_state.ref_count = Math.max(0, polling_state.ref_count - 1) 336 + polling_state.ref_count = Math.max(0, polling_state.ref_count - 1)
317 - if (polling_state.ref_count === 0) { 337 + if (polling_state.ref_count === 0) {
318 - // 引用计数降为0时,停止轮询并注销网络监听器 338 + // 引用计数降为0时,停止轮询并注销网络监听器
319 - stop_offline_booking_cache_polling() 339 + stop_offline_booking_cache_polling()
320 - teardown_network_listener() 340 + teardown_network_listener()
321 - } 341 + }
322 } 342 }
323 343
324 /** 344 /**
...@@ -328,23 +348,23 @@ const release_polling_ref = () => { ...@@ -328,23 +348,23 @@ const release_polling_ref = () => {
328 * @param {Boolean} options.immediate 是否立即刷新一次 348 * @param {Boolean} options.immediate 是否立即刷新一次
329 * @param {Boolean} options.force 是否强制刷新(透传给 refresh_offline_booking_cache) 349 * @param {Boolean} options.force 是否强制刷新(透传给 refresh_offline_booking_cache)
330 */ 350 */
331 -export const enable_offline_booking_cache_polling = (options) => { 351 +export const enable_offline_booking_cache_polling = options => {
332 - save_last_options(options) 352 + save_last_options(options)
333 - /** 353 + /**
334 - * 核心目的:对 app_enabled=true 的场景做兜底,确保轮询在 "已启用但异常停止" 时能被主动恢复,而非被动等待网络变化; 354 + * 核心目的:对 app_enabled=true 的场景做兜底,确保轮询在 "已启用但异常停止" 时能被主动恢复,而非被动等待网络变化;
335 - * 执行逻辑:先保证网络监听器(轮询的依赖)就绪,再尝试启动轮询,且利用 start_offline_booking_cache_polling 的幂等性避免重复; 355 + * 执行逻辑:先保证网络监听器(轮询的依赖)就绪,再尝试启动轮询,且利用 start_offline_booking_cache_polling 的幂等性避免重复;
336 - * 设计思维:体现了 "主动调用需即时生效" 的用户体验考量,以及 "依赖前置检查" 的工程化思维 —— 先保证依赖(监听器)就绪,再执行核心操作(启动轮询)。 356 + * 设计思维:体现了 "主动调用需即时生效" 的用户体验考量,以及 "依赖前置检查" 的工程化思维 —— 先保证依赖(监听器)就绪,再执行核心操作(启动轮询)。
337 - */ 357 + */
338 - if (polling_state.app_enabled) { 358 + if (polling_state.app_enabled) {
339 - ensure_network_listener().then((success) => { 359 + ensure_network_listener().then(success => {
340 - if (success && polling_state.last_options) { 360 + if (success && polling_state.last_options) {
341 - start_offline_booking_cache_polling(polling_state.last_options) 361 + start_offline_booking_cache_polling(polling_state.last_options)
342 - } 362 + }
343 - }) 363 + })
344 - return 364 + return
345 - } 365 + }
346 - polling_state.app_enabled = true 366 + polling_state.app_enabled = true
347 - acquire_polling_ref(polling_state.last_options || {}) 367 + acquire_polling_ref(polling_state.last_options || {})
348 } 368 }
349 369
350 /** 370 /**
...@@ -352,7 +372,9 @@ export const enable_offline_booking_cache_polling = (options) => { ...@@ -352,7 +372,9 @@ export const enable_offline_booking_cache_polling = (options) => {
352 */ 372 */
353 373
354 export const disable_offline_booking_cache_polling = () => { 374 export const disable_offline_booking_cache_polling = () => {
355 - if (!polling_state.app_enabled) return 375 + if (!polling_state.app_enabled) {
356 - polling_state.app_enabled = false 376 + return
357 - release_polling_ref() 377 + }
378 + polling_state.app_enabled = false
379 + release_polling_ref()
358 } 380 }
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
5 * @FilePath: /xyxBooking-weapp/src/hooks/useGo.js 5 * @FilePath: /xyxBooking-weapp/src/hooks/useGo.js
6 * @Description: 封装路由跳转方便行内调用 6 * @Description: 封装路由跳转方便行内调用
7 */ 7 */
8 -import Taro from '@tarojs/taro'; 8 +import Taro from '@tarojs/taro'
9 9
10 /** 10 /**
11 * @description 获取页面跳转方法(navigateTo) 11 * @description 获取页面跳转方法(navigateTo)
...@@ -13,37 +13,39 @@ import Taro from '@tarojs/taro'; ...@@ -13,37 +13,39 @@ import Taro from '@tarojs/taro';
13 * - 自动补全:pages/notice/index 13 * - 自动补全:pages/notice/index
14 * @returns {(path:string, query?:Object)=>void} go 跳转函数 14 * @returns {(path:string, query?:Object)=>void} go 跳转函数
15 */ 15 */
16 -export function useGo () { 16 +export function useGo() {
17 /** 17 /**
18 * @description 路由跳转 18 * @description 路由跳转
19 * @param {string} path 目标页面路径,支持 / 开头与短路径 19 * @param {string} path 目标页面路径,支持 / 开头与短路径
20 * @param {Object} query 查询参数(键值对) 20 * @param {Object} query 查询参数(键值对)
21 * @returns {void} 无返回值 21 * @returns {void} 无返回值
22 */ 22 */
23 - function go (path, query = {}) { 23 + function go(path, query = {}) {
24 // 补全路径,如果是 / 开头,去掉 / 24 // 补全路径,如果是 / 开头,去掉 /
25 - let url = path.startsWith('/') ? path.substring(1) : path; 25 + let url = path.startsWith('/') ? path.substring(1) : path
26 // 检查是否是 tabbar 页面 (目前没有配置 tabbar,所以都是普通跳转) 26 // 检查是否是 tabbar 页面 (目前没有配置 tabbar,所以都是普通跳转)
27 // 如果是页面,加上 pages/ 前缀 (假设都在 pages 下,且目录名和 path 一致) 27 // 如果是页面,加上 pages/ 前缀 (假设都在 pages 下,且目录名和 path 一致)
28 // H5 path 是 /notice,小程序是 pages/notice/index 28 // H5 path 是 /notice,小程序是 pages/notice/index
29 if (!url.startsWith('pages/')) { 29 if (!url.startsWith('pages/')) {
30 - url = `pages/${url}/index`; // 适配 pages/notice/index 结构 30 + url = `pages/${url}/index` // 适配 pages/notice/index 结构
31 } 31 }
32 32
33 // 构建 query string 33 // 构建 query string
34 - let queryString = Object.keys(query).map(key => key + '=' + query[key]).join('&'); 34 + const queryString = Object.keys(query)
35 + .map(key => `${key}=${query[key]}`)
36 + .join('&')
35 if (queryString) { 37 if (queryString) {
36 - url += '?' + queryString; 38 + url += `?${queryString}`
37 } 39 }
38 40
39 Taro.navigateTo({ 41 Taro.navigateTo({
40 - url: '/' + url, 42 + url: `/${url}`,
41 - fail: (err) => { 43 + fail: err => {
42 // 如果是 tabbar 页面,尝试 switchTab 44 // 如果是 tabbar 页面,尝试 switchTab
43 if (err.errMsg && err.errMsg.indexOf('tabbar') !== -1) { 45 if (err.errMsg && err.errMsg.indexOf('tabbar') !== -1) {
44 - Taro.switchTab({ url: '/' + url }); 46 + Taro.switchTab({ url: `/${url}` })
45 } else { 47 } else {
46 - console.error('页面跳转失败:', err); 48 + console.error('页面跳转失败:', err)
47 } 49 }
48 } 50 }
49 }) 51 })
...@@ -57,26 +59,28 @@ export function useGo () { ...@@ -57,26 +59,28 @@ export function useGo () {
57 * - 自动补全:pages/notice/index 59 * - 自动补全:pages/notice/index
58 * @returns {(path:string, query?:Object)=>void} replace 替换函数 60 * @returns {(path:string, query?:Object)=>void} replace 替换函数
59 */ 61 */
60 -export function useReplace () { 62 +export function useReplace() {
61 /** 63 /**
62 * @description 路由替换 64 * @description 路由替换
63 * @param {string} path 目标页面路径,支持 / 开头与短路径 65 * @param {string} path 目标页面路径,支持 / 开头与短路径
64 * @param {Object} query 查询参数(键值对) 66 * @param {Object} query 查询参数(键值对)
65 * @returns {void} 无返回值 67 * @returns {void} 无返回值
66 */ 68 */
67 - function replace (path, query = {}) { 69 + function replace(path, query = {}) {
68 - let url = path.startsWith('/') ? path.substring(1) : path; 70 + let url = path.startsWith('/') ? path.substring(1) : path
69 if (!url.startsWith('pages/')) { 71 if (!url.startsWith('pages/')) {
70 - url = `pages/${url}/index`; 72 + url = `pages/${url}/index`
71 } 73 }
72 74
73 - let queryString = Object.keys(query).map(key => key + '=' + query[key]).join('&'); 75 + const queryString = Object.keys(query)
76 + .map(key => `${key}=${query[key]}`)
77 + .join('&')
74 if (queryString) { 78 if (queryString) {
75 - url += '?' + queryString; 79 + url += `?${queryString}`
76 } 80 }
77 81
78 Taro.redirectTo({ 82 Taro.redirectTo({
79 - url: '/' + url 83 + url: `/${url}`
80 }) 84 })
81 } 85 }
82 return replace 86 return replace
......
...@@ -6,46 +6,60 @@ ...@@ -6,46 +6,60 @@
6 * @Description: 添加参观者 6 * @Description: 添加参观者
7 --> 7 -->
8 <template> 8 <template>
9 - <view class="add-visitor-page"> 9 + <view class="add-visitor-page">
10 - <view class="content"> 10 + <view class="content">
11 - <view class="form-card"> 11 + <view class="form-card">
12 - <view class="form-row"> 12 + <view class="form-row">
13 - <view class="label">姓名</view> 13 + <view class="label">姓名</view>
14 - <nut-input v-model="name" class="field-input" placeholder="请输入参观者真实姓名" type="text" input-align="right" :border="false" /> 14 + <nut-input
15 - </view> 15 + v-model="name"
16 - <view class="form-row"> 16 + class="field-input"
17 - <view class="label">证件类型</view> 17 + placeholder="请输入参观者真实姓名"
18 - <view class="field-value picker-value" @tap="open_id_type_picker"> 18 + type="text"
19 - <text>{{ id_type_label }}</text> 19 + input-align="right"
20 - <text class="picker-arrow">›</text> 20 + :border="false"
21 - </view> 21 + />
22 - </view>
23 - <view class="form-row">
24 - <view class="label">证件号码</view>
25 - <nut-input v-model="id_number" class="field-input" placeholder="请输入证件号码" :type="id_number_type" input-align="right" :border="false" />
26 - </view>
27 - </view>
28 -
29 - <view class="tip">
30 - <IconFont name="tips" size="14" color="#C7A46D" />
31 - <text class="tip-text">温馨提示:账号实名认证信息一经填写将无法修改</text>
32 - </view>
33 </view> 22 </view>
34 - 23 + <view class="form-row">
35 - <view class="footer"> 24 + <view class="label">证件类型</view>
36 - <view class="save-btn" @tap="save">保存</view> 25 + <view class="field-value picker-value" @tap="open_id_type_picker">
26 + <text>{{ id_type_label }}</text>
27 + <text class="picker-arrow">›</text>
28 + </view>
29 + </view>
30 + <view class="form-row">
31 + <view class="label">证件号码</view>
32 + <nut-input
33 + v-model="id_number"
34 + class="field-input"
35 + placeholder="请输入证件号码"
36 + :type="id_number_type"
37 + input-align="right"
38 + :border="false"
39 + />
37 </view> 40 </view>
41 + </view>
42 +
43 + <view class="tip">
44 + <IconFont name="tips" size="14" color="#C7A46D" />
45 + <text class="tip-text">温馨提示:账号实名认证信息一经填写将无法修改</text>
46 + </view>
47 + </view>
38 48
39 - <nut-popup v-model:visible="show_id_type_picker" position="bottom" safe-area-inset-bottom> 49 + <view class="footer">
40 - <nut-picker 50 + <view class="save-btn" @tap="save">保存</view>
41 - v-model="id_type_picker_value"
42 - :columns="id_type_columns"
43 - title="选择证件类型"
44 - @confirm="on_id_type_confirm"
45 - @cancel="show_id_type_picker = false"
46 - ></nut-picker>
47 - </nut-popup>
48 </view> 51 </view>
52 +
53 + <nut-popup v-model:visible="show_id_type_picker" position="bottom" safe-area-inset-bottom>
54 + <nut-picker
55 + v-model="id_type_picker_value"
56 + :columns="id_type_columns"
57 + title="选择证件类型"
58 + @confirm="on_id_type_confirm"
59 + @cancel="show_id_type_picker = false"
60 + ></nut-picker>
61 + </nut-popup>
62 + </view>
49 </template> 63 </template>
50 64
51 <script setup> 65 <script setup>
...@@ -54,219 +68,254 @@ import Taro from '@tarojs/taro' ...@@ -54,219 +68,254 @@ import Taro from '@tarojs/taro'
54 import { addPersonAPI } from '@/api/index' 68 import { addPersonAPI } from '@/api/index'
55 import { IconFont } from '@nutui/icons-vue-taro' 69 import { IconFont } from '@nutui/icons-vue-taro'
56 70
57 -const name = ref(''); 71 +const name = ref('')
58 -const id_number = ref(''); 72 +const id_number = ref('')
59 -const show_id_type_picker = ref(false); 73 +const show_id_type_picker = ref(false)
60 const id_type_options = [ 74 const id_type_options = [
61 - { label: '身份证', value: 1 }, 75 + { label: '身份证', value: 1 },
62 - { label: '其他', value: 3 } 76 + { label: '其他', value: 3 }
63 -]; 77 +]
64 -const id_type = ref(id_type_options[0].value); 78 +const id_type = ref(id_type_options[0].value)
65 -const id_type_picker_value = ref([String(id_type.value)]); 79 +const id_type_picker_value = ref([String(id_type.value)])
66 80
67 const id_type_columns = computed(() => { 81 const id_type_columns = computed(() => {
68 - return id_type_options.map(item => ({ 82 + return id_type_options.map(item => ({
69 - text: item.label, 83 + text: item.label,
70 - value: String(item.value) 84 + value: String(item.value)
71 - })); 85 + }))
72 -}); 86 +})
73 const id_type_label = computed(() => { 87 const id_type_label = computed(() => {
74 - return id_type_options.find(item => item.value === id_type.value)?.label || id_type_options[0].label; 88 + return (
75 -}); 89 + id_type_options.find(item => item.value === id_type.value)?.label || id_type_options[0].label
76 -const id_number_type = computed(() => (id_type.value === 1 ? 'idcard' : 'text')); 90 + )
91 +})
92 +const id_number_type = computed(() => (id_type.value === 1 ? 'idcard' : 'text'))
77 93
78 const open_id_type_picker = () => { 94 const open_id_type_picker = () => {
79 - id_type_picker_value.value = [String(id_type.value)]; 95 + id_type_picker_value.value = [String(id_type.value)]
80 - show_id_type_picker.value = true; 96 + show_id_type_picker.value = true
81 } 97 }
82 98
83 const on_id_type_confirm = ({ selectedValue }) => { 99 const on_id_type_confirm = ({ selectedValue }) => {
84 - const value = selectedValue?.[0]; 100 + const value = selectedValue?.[0]
85 - id_type.value = Number(value) || 1; 101 + id_type.value = Number(value) || 1
86 - show_id_type_picker.value = false; 102 + show_id_type_picker.value = false
87 } 103 }
88 104
89 // 身份证校验 105 // 身份证校验
90 -const checkIDCard = (idcode) => { 106 +const checkIDCard = idcode => {
91 - // 1. 基础格式校验 (18位) 107 + // 1. 基础格式校验 (18位)
92 - if (!idcode || !/^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/.test(idcode)) { 108 + if (
93 - return false; 109 + !idcode ||
94 - } 110 + !/^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/.test(
95 - 111 + idcode
96 - // 2. 地区码校验 112 + )
97 - const cityMap = { 113 + ) {
98 - 11: "北京", 12: "天津", 13: "河北", 14: "山西", 15: "内蒙古", 114 + return false
99 - 21: "辽宁", 22: "吉林", 23: "黑龙江", 115 + }
100 - 31: "上海", 32: "江苏", 33: "浙江", 34: "安徽", 35: "福建", 36: "江西", 37: "山东", 116 +
101 - 41: "河南", 42: "湖北", 43: "湖南", 44: "广东", 45: "广西", 46: "海南", 117 + // 2. 地区码校验
102 - 50: "重庆", 51: "四川", 52: "贵州", 53: "云南", 54: "西藏", 118 + const cityMap = {
103 - 61: "陕西", 62: "甘肃", 63: "青海", 64: "宁夏", 65: "新疆", 119 + 11: '北京',
104 - 71: "台湾", 81: "香港", 82: "澳门", 91: "国外" 120 + 12: '天津',
105 - }; 121 + 13: '河北',
106 - if (!cityMap[idcode.substr(0, 2)]) { 122 + 14: '山西',
107 - return false; 123 + 15: '内蒙古',
108 - } 124 + 21: '辽宁',
109 - 125 + 22: '吉林',
110 - // 3. 出生日期校验 126 + 23: '黑龙江',
111 - const birthday = idcode.substr(6, 8); 127 + 31: '上海',
112 - const year = parseInt(birthday.substr(0, 4)); 128 + 32: '江苏',
113 - const month = parseInt(birthday.substr(4, 2)); 129 + 33: '浙江',
114 - const day = parseInt(birthday.substr(6, 2)); 130 + 34: '安徽',
115 - const date = new Date(year, month - 1, day); 131 + 35: '福建',
116 - 132 + 36: '江西',
117 - if (date.getFullYear() !== year || date.getMonth() + 1 !== month || date.getDate() !== day) { 133 + 37: '山东',
118 - return false; 134 + 41: '河南',
119 - } 135 + 42: '湖北',
120 - 136 + 43: '湖南',
121 - // 校验日期不能超过当前时间 137 + 44: '广东',
122 - if (date > new Date()) { 138 + 45: '广西',
123 - return false; 139 + 46: '海南',
124 - } 140 + 50: '重庆',
125 - 141 + 51: '四川',
126 - // 4. 校验码计算 142 + 52: '贵州',
127 - // 加权因子 143 + 53: '云南',
128 - const factor = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]; 144 + 54: '西藏',
129 - // 校验位对应值 145 + 61: '陕西',
130 - const parity = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2']; 146 + 62: '甘肃',
131 - 147 + 63: '青海',
132 - let sum = 0; 148 + 64: '宁夏',
133 - const codeArr = idcode.split(""); 149 + 65: '新疆',
134 - // 计算加权和 150 + 71: '台湾',
135 - for (let i = 0; i < 17; i++) { 151 + 81: '香港',
136 - sum += codeArr[i] * factor[i]; 152 + 82: '澳门',
137 - } 153 + 91: '国外'
138 - // 取模 154 + }
139 - const mod = sum % 11; 155 + if (!cityMap[idcode.substr(0, 2)]) {
140 - // 获取校验位 156 + return false
141 - const last = parity[mod]; 157 + }
142 - 158 +
143 - // 对比最后一位 (统一转大写比较) 159 + // 3. 出生日期校验
144 - return last === codeArr[17].toUpperCase(); 160 + const birthday = idcode.substr(6, 8)
161 + const year = parseInt(birthday.substr(0, 4))
162 + const month = parseInt(birthday.substr(4, 2))
163 + const day = parseInt(birthday.substr(6, 2))
164 + const date = new Date(year, month - 1, day)
165 +
166 + if (date.getFullYear() !== year || date.getMonth() + 1 !== month || date.getDate() !== day) {
167 + return false
168 + }
169 +
170 + // 校验日期不能超过当前时间
171 + if (date > new Date()) {
172 + return false
173 + }
174 +
175 + // 4. 校验码计算
176 + // 加权因子
177 + const factor = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
178 + // 校验位对应值
179 + const parity = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2']
180 +
181 + let sum = 0
182 + const codeArr = idcode.split('')
183 + // 计算加权和
184 + for (let i = 0; i < 17; i++) {
185 + sum += codeArr[i] * factor[i]
186 + }
187 + // 取模
188 + const mod = sum % 11
189 + // 获取校验位
190 + const last = parity[mod]
191 +
192 + // 对比最后一位 (统一转大写比较)
193 + return last === codeArr[17].toUpperCase()
145 } 194 }
146 195
147 const save = async () => { 196 const save = async () => {
148 - if (!name.value) { 197 + if (!name.value) {
149 - Taro.showToast({ title: '请输入姓名', icon: 'none' }); 198 + Taro.showToast({ title: '请输入姓名', icon: 'none' })
150 - return; 199 + return
151 - } 200 + }
152 - if (!id_number.value) { 201 + if (!id_number.value) {
153 - Taro.showToast({ title: '请输入证件号码', icon: 'none' }); 202 + Taro.showToast({ title: '请输入证件号码', icon: 'none' })
154 - return; 203 + return
155 - } 204 + }
156 - if (id_type.value === 1 && !checkIDCard(id_number.value)) { 205 + if (id_type.value === 1 && !checkIDCard(id_number.value)) {
157 - Taro.showToast({ title: '请输入正确的身份证号', icon: 'none' }); 206 + Taro.showToast({ title: '请输入正确的身份证号', icon: 'none' })
158 - return; 207 + return
159 - } 208 + }
160 - 209 +
161 - Taro.showLoading({ title: '保存中' }); 210 + Taro.showLoading({ title: '保存中' })
162 - const { code, msg } = await addPersonAPI({ 211 + const { code, msg } = await addPersonAPI({
163 - name: name.value, 212 + name: name.value,
164 - id_type: id_type.value, 213 + id_type: id_type.value,
165 - id_number: id_number.value 214 + id_number: id_number.value
166 - }); 215 + })
167 - Taro.hideLoading(); 216 + Taro.hideLoading()
168 - 217 +
169 - if (code) { 218 + if (code) {
170 - Taro.showToast({ title: '添加成功' }); 219 + Taro.showToast({ title: '添加成功' })
171 - name.value = ''; 220 + name.value = ''
172 - id_number.value = ''; 221 + id_number.value = ''
173 - Taro.navigateBack(); 222 + Taro.navigateBack()
174 - } else { 223 + } else {
175 - Taro.showToast({ title: msg || '添加失败', icon: 'none' }); 224 + Taro.showToast({ title: msg || '添加失败', icon: 'none' })
176 - } 225 + }
177 } 226 }
178 </script> 227 </script>
179 228
180 <style lang="less"> 229 <style lang="less">
181 .add-visitor-page { 230 .add-visitor-page {
182 - min-height: 100vh; 231 + min-height: 100vh;
183 - background-color: #F6F6F6; 232 + background-color: #f6f6f6;
184 - padding-top: 2rpx; 233 + padding-top: 2rpx;
185 - 234 +
186 - .content { 235 + .content {
187 - padding: 32rpx; 236 + padding: 32rpx;
188 - padding-bottom: 220rpx; 237 + padding-bottom: 220rpx;
238 + }
239 +
240 + .form-card {
241 + background-color: #fff;
242 + border-radius: 16rpx;
243 + overflow: hidden;
244 + }
245 +
246 + .form-row {
247 + display: flex;
248 + align-items: center;
249 + padding-left: 32rpx;
250 + height: 112rpx;
251 +
252 + &:not(:last-child) {
253 + border-bottom: 2rpx solid #f2f2f2;
189 } 254 }
190 255
191 - .form-card { 256 + .label {
192 - background-color: #FFF; 257 + width: 160rpx;
193 - border-radius: 16rpx; 258 + color: #333;
194 - overflow: hidden; 259 + font-size: 30rpx;
195 } 260 }
196 261
197 - .form-row { 262 + .field-value {
198 - display: flex; 263 + flex: 1;
199 - align-items: center; 264 + text-align: right;
200 - padding-left: 32rpx; 265 + color: #333;
201 - height: 112rpx; 266 + font-size: 30rpx;
202 -
203 - &:not(:last-child) {
204 - border-bottom: 2rpx solid #F2F2F2;
205 - }
206 -
207 - .label {
208 - width: 160rpx;
209 - color: #333;
210 - font-size: 30rpx;
211 - }
212 -
213 - .field-value {
214 - flex: 1;
215 - text-align: right;
216 - color: #333;
217 - font-size: 30rpx;
218 - }
219 -
220 - .field-input {
221 - flex: 1;
222 - }
223 -
224 - .picker-value {
225 - display: flex;
226 - align-items: center;
227 - justify-content: flex-end;
228 - padding-right: 50rpx;
229 - }
230 -
231 - .picker-arrow {
232 - margin-left: 10rpx;
233 - color: #BBB;
234 - font-size: 28rpx;
235 - }
236 } 267 }
237 268
238 - .tip { 269 + .field-input {
239 - margin-top: 28rpx; 270 + flex: 1;
240 - display: flex; 271 + }
241 - align-items: center;
242 - color: #C7A46D;
243 - font-size: 24rpx;
244 272
245 - .tip-text { 273 + .picker-value {
246 - margin-left: 10rpx; 274 + display: flex;
247 - } 275 + align-items: center;
276 + justify-content: flex-end;
277 + padding-right: 50rpx;
248 } 278 }
249 279
250 - .footer { 280 + .picker-arrow {
251 - position: fixed; 281 + margin-left: 10rpx;
252 - left: 0; 282 + color: #bbb;
253 - right: 0; 283 + font-size: 28rpx;
254 - bottom: 0;
255 - padding: 24rpx 32rpx calc(24rpx + env(safe-area-inset-bottom));
256 - background-color: #F6F6F6;
257 } 284 }
285 + }
286 +
287 + .tip {
288 + margin-top: 28rpx;
289 + display: flex;
290 + align-items: center;
291 + color: #c7a46d;
292 + font-size: 24rpx;
258 293
259 - .save-btn { 294 + .tip-text {
260 - width: 686rpx; 295 + margin-left: 10rpx;
261 - height: 96rpx;
262 - background-color: #A67939;
263 - border-radius: 12rpx;
264 - display: flex;
265 - align-items: center;
266 - justify-content: center;
267 - color: #FFF;
268 - font-size: 34rpx;
269 - font-weight: 600;
270 } 296 }
297 + }
298 +
299 + .footer {
300 + position: fixed;
301 + left: 0;
302 + right: 0;
303 + bottom: 0;
304 + padding: 24rpx 32rpx calc(24rpx + env(safe-area-inset-bottom));
305 + background-color: #f6f6f6;
306 + }
307 +
308 + .save-btn {
309 + width: 686rpx;
310 + height: 96rpx;
311 + background-color: #a67939;
312 + border-radius: 12rpx;
313 + display: flex;
314 + align-items: center;
315 + justify-content: center;
316 + color: #fff;
317 + font-size: 34rpx;
318 + font-weight: 600;
319 + }
271 } 320 }
272 </style> 321 </style>
......
1 export default { 1 export default {
2 navigationBarTitleText: '授权页', 2 navigationBarTitleText: '授权页',
3 - usingComponents: { 3 + usingComponents: {}
4 - },
5 } 4 }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
8 <template> 8 <template>
9 <view class="auth-page"> 9 <view class="auth-page">
10 <view class="loading"> 10 <view class="loading">
11 - <view>正在授权登录...</view> 11 + <view>正在授权登录...</view>
12 </view> 12 </view>
13 </view> 13 </view>
14 </template> 14 </template>
...@@ -22,41 +22,47 @@ let has_shown_fail_modal = false ...@@ -22,41 +22,47 @@ let has_shown_fail_modal = false
22 let has_failed = false 22 let has_failed = false
23 23
24 useDidShow(() => { 24 useDidShow(() => {
25 - if (has_failed) return 25 + if (has_failed) {
26 - const now = Date.now() 26 + return
27 - if (now - last_try_at < 1200) return 27 + }
28 - last_try_at = now 28 + const now = Date.now()
29 + if (now - last_try_at < 1200) {
30 + return
31 + }
32 + last_try_at = now
29 33
30 - /** 34 + /**
31 - * 尝试静默授权 35 + * 尝试静默授权
32 - * - 授权成功后回跳到来源页 36 + * - 授权成功后回跳到来源页
33 - * - 授权失败则跳转至授权页面 37 + * - 授权失败则跳转至授权页面
34 - */ 38 + */
35 - silentAuth() 39 + silentAuth()
36 - .then(() => returnToOriginalPage()) 40 + .then(() => returnToOriginalPage())
37 - .catch(async (error) => { 41 + .catch(async error => {
38 - has_failed = true 42 + has_failed = true
39 - if (has_shown_fail_modal) return 43 + if (has_shown_fail_modal) {
40 - has_shown_fail_modal = true 44 + return
41 - await Taro.showModal({ 45 + }
42 - title: '提示', 46 + has_shown_fail_modal = true
43 - content: error?.message || '授权失败,请稍后再尝试', 47 + await Taro.showModal({
44 - showCancel: false, 48 + title: '提示',
45 - confirmText: '我知道了', 49 + content: error?.message || '授权失败,请稍后再尝试',
46 - }) 50 + showCancel: false,
51 + confirmText: '我知道了'
47 }) 52 })
53 + })
48 }) 54 })
49 </script> 55 </script>
50 56
51 <style lang="less"> 57 <style lang="less">
52 .auth-page { 58 .auth-page {
53 - min-height: 100vh; 59 + min-height: 100vh;
54 - display: flex; 60 + display: flex;
55 - align-items: center; 61 + align-items: center;
56 - justify-content: center; 62 + justify-content: center;
57 - .loading { 63 + .loading {
58 - text-align: center; 64 + text-align: center;
59 - color: #999; 65 + color: #999;
60 - } 66 + }
61 } 67 }
62 </style> 68 </style>
......
...@@ -18,24 +18,44 @@ ...@@ -18,24 +18,44 @@
18 </view> 18 </view>
19 <view class="weeks-wrapper"> 19 <view class="weeks-wrapper">
20 <view v-for="(week, index) in weeks" :key="index" class="weeks"> 20 <view v-for="(week, index) in weeks" :key="index" class="weeks">
21 - <view v-for="(date, dateIndex) in week" :key="dateIndex" 21 + <view
22 + v-for="(date, dateIndex) in week"
23 + :key="dateIndex"
22 @tap="chooseDay(date)" 24 @tap="chooseDay(date)"
23 - :class="[ 'item', 25 + :class="[
26 + 'item',
24 date && checked_day === findDatesInfo(date).date ? 'checked' : '', 27 date && checked_day === findDatesInfo(date).date ? 'checked' : '',
25 - date && (findDatesInfo(date).reserve_full === ReserveStatus.FULL || findDatesInfo(date).reserve_full === ReserveStatus.OVERDUE) ? 'disabled' : '' 28 + date &&
29 + (findDatesInfo(date).reserve_full === ReserveStatus.FULL ||
30 + findDatesInfo(date).reserve_full === ReserveStatus.OVERDUE)
31 + ? 'disabled'
32 + : ''
26 ]" 33 ]"
27 > 34 >
28 <view v-if="findDatesInfo(date).date"> 35 <view v-if="findDatesInfo(date).date">
29 <view class="day-lunar">{{ findDatesInfo(date).lunar_date.IDayCn }}</view> 36 <view class="day-lunar">{{ findDatesInfo(date).lunar_date.IDayCn }}</view>
30 <view class="day-text">{{ findDatesInfo(date).text }}</view> 37 <view class="day-text">{{ findDatesInfo(date).text }}</view>
31 - <view v-if="findDatesInfo(date).reserve_full === ReserveStatus.INFINITY || findDatesInfo(date).reserve_full === ReserveStatus.OVERDUE" class="day-price"></view> 38 + <view
32 - <view v-else-if="findDatesInfo(date).reserve_full === ReserveStatus.FULL" class="day-no-booking">约满</view> 39 + v-if="
40 + findDatesInfo(date).reserve_full === ReserveStatus.INFINITY ||
41 + findDatesInfo(date).reserve_full === ReserveStatus.OVERDUE
42 + "
43 + class="day-price"
44 + ></view>
45 + <view
46 + v-else-if="findDatesInfo(date).reserve_full === ReserveStatus.FULL"
47 + class="day-no-booking"
48 + >约满</view
49 + >
33 </view> 50 </view>
34 </view> 51 </view>
35 </view> 52 </view>
36 </view> 53 </view>
37 </view> 54 </view>
38 - <view v-if="checked_day && checked_day_reserve_full === ReserveStatus.AVAILABLE" class="choose-time"> 55 + <view
56 + v-if="checked_day && checked_day_reserve_full === ReserveStatus.AVAILABLE"
57 + class="choose-time"
58 + >
39 <view class="title"> 59 <view class="title">
40 <view class="text">选择参访时间段</view> 60 <view class="text">选择参访时间段</view>
41 </view> 61 </view>
...@@ -64,27 +84,27 @@ ...@@ -64,27 +84,27 @@
64 <view class="title"> 84 <view class="title">
65 <view class="text">选择参访时间段</view> 85 <view class="text">选择参访时间段</view>
66 </view> 86 </view>
67 - <view style="padding: 48rpx 24rpx; color: #A67939; text-align: center;"> 87 + <view style="padding: 48rpx 24rpx; color: #a67939; text-align: center">
68 {{ infinity_tips_text }} 88 {{ infinity_tips_text }}
69 </view> 89 </view>
70 </view> 90 </view>
71 </view> 91 </view>
72 - <view style="height: 160rpx;"></view> 92 + <view style="height: 160rpx"></view>
73 <view v-if="checked_day && checked_day_reserve_full === ReserveStatus.AVAILABLE" class="next"> 93 <view v-if="checked_day && checked_day_reserve_full === ReserveStatus.AVAILABLE" class="next">
74 - <view @tap="nextBtn" class="button" style="background-color: #A67939;">下一步</view> 94 + <view @tap="nextBtn" class="button" style="background-color: #a67939">下一步</view>
75 </view> 95 </view>
76 96
77 <!-- NutUI Popup + DatePicker --> 97 <!-- NutUI Popup + DatePicker -->
78 <nut-popup v-model:visible="showPicker" position="bottom"> 98 <nut-popup v-model:visible="showPicker" position="bottom">
79 - <nut-date-picker 99 + <nut-date-picker
80 - v-model="currentDate" 100 + v-model="currentDate"
81 - type="year-month" 101 + type="year-month"
82 - title="选择年月" 102 + title="选择年月"
83 - :min-date="minDate" 103 + :min-date="minDate"
84 - :max-date="maxDate" 104 + :max-date="maxDate"
85 - @confirm="onConfirm" 105 + @confirm="onConfirm"
86 - @cancel="onCancel" 106 + @cancel="onCancel"
87 - ></nut-date-picker> 107 + ></nut-date-picker>
88 </nut-popup> 108 </nut-popup>
89 </view> 109 </view>
90 </template> 110 </template>
...@@ -92,31 +112,33 @@ ...@@ -92,31 +112,33 @@
92 <script setup> 112 <script setup>
93 import { ref, computed } from 'vue' 113 import { ref, computed } from 'vue'
94 import Taro, { useDidShow } from '@tarojs/taro' 114 import Taro, { useDidShow } from '@tarojs/taro'
95 -import dayjs from 'dayjs'; 115 +import dayjs from 'dayjs'
96 import { useGo } from '@/hooks/useGo' 116 import { useGo } from '@/hooks/useGo'
97 import icon_select1 from '@/assets/images/单选01@2x.png' 117 import icon_select1 from '@/assets/images/单选01@2x.png'
98 import icon_select2 from '@/assets/images/单选02@2x.png' 118 import icon_select2 from '@/assets/images/单选02@2x.png'
99 import { canReserveDateListAPI, canReserveTimeListAPI } from '@/api/index' 119 import { canReserveDateListAPI, canReserveTimeListAPI } from '@/api/index'
100 import calendar from 'xst-solar2lunar' 120 import calendar from 'xst-solar2lunar'
101 121
102 -const go = useGo(); 122 +const go = useGo()
103 123
104 -const dates_list = ref([]); // 当月日期列表信息 124 +const dates_list = ref([]) // 当月日期列表信息
105 -const dates = ref([]); // 当月日期集合 125 +const dates = ref([]) // 当月日期集合
106 126
107 useDidShow(async () => { 127 useDidShow(async () => {
108 - const raw_date = new Date(); 128 + const raw_date = new Date()
109 - const { code, data } = await canReserveDateListAPI({ month: `${raw_date.getFullYear()}-${(raw_date.getMonth() + 1).toString().padStart(2, '0')}` }); 129 + const { code, data } = await canReserveDateListAPI({
130 + month: `${raw_date.getFullYear()}-${(raw_date.getMonth() + 1).toString().padStart(2, '0')}`
131 + })
110 if (code) { 132 if (code) {
111 // 日期列表 133 // 日期列表
112 - dates_list.value = data || []; 134 + dates_list.value = data || []
113 // 今日之前都不可约 135 // 今日之前都不可约
114 - dates_list.value.forEach((date) => { 136 + dates_list.value.forEach(date => {
115 if (dayjs(date.month_date).isBefore(dayjs())) { 137 if (dayjs(date.month_date).isBefore(dayjs())) {
116 - date.reserve_full = ReserveStatus.OVERDUE; 138 + date.reserve_full = ReserveStatus.OVERDUE
117 } 139 }
118 - }); 140 + })
119 - dates.value = dates_list.value.map(item => item.month_date); 141 + dates.value = dates_list.value.map(item => item.month_date)
120 } 142 }
121 }) 143 })
122 144
...@@ -125,22 +147,24 @@ useDidShow(async () => { ...@@ -125,22 +147,24 @@ useDidShow(async () => {
125 * @param {string} date 147 * @param {string} date
126 * @return {object} {text: 日期, date: 日期, reserve_full: 是否可约 1=可约,0=约满,-1=无需预约 overdue=过期日期 } 148 * @return {object} {text: 日期, date: 日期, reserve_full: 是否可约 1=可约,0=约满,-1=无需预约 overdue=过期日期 }
127 */ 149 */
128 -const findDatesInfo = (date) => { 150 +const findDatesInfo = date => {
129 - if (!date) return { text: '', date: '', reserve_full: '', lunar_date: {} }; 151 + if (!date) {
130 - const result = dates_list.value.find((item) => item.month_date === date); 152 + return { text: '', date: '', reserve_full: '', lunar_date: {} }
131 - const currentDate = new Date(date); 153 + }
154 + const result = dates_list.value.find(item => item.month_date === date)
155 + const currentDate = new Date(date)
132 // calendar.solar2lunar 需要年,月,日 (数字) 156 // calendar.solar2lunar 需要年,月,日 (数字)
133 // dayjs(date).format('YYYY-MM-DD') -> 2024-01-01 157 // dayjs(date).format('YYYY-MM-DD') -> 2024-01-01
134 - const d = dayjs(date); 158 + const d = dayjs(date)
135 - const lunarDate = calendar.solar2lunar(d.year(), d.month() + 1, d.date()); 159 + const lunarDate = calendar.solar2lunar(d.year(), d.month() + 1, d.date())
136 return { 160 return {
137 text: currentDate.getDate().toString().padStart(2, '0'), 161 text: currentDate.getDate().toString().padStart(2, '0'),
138 date: result?.month_date, 162 date: result?.month_date,
139 reserve_full: result?.reserve_full, 163 reserve_full: result?.reserve_full,
140 tips: result?.tips || '', 164 tips: result?.tips || '',
141 lunar_date: lunarDate 165 lunar_date: lunarDate
142 - }; 166 + }
143 -}; 167 +}
144 168
145 /** 169 /**
146 * @description: 预约状态 170 * @description: 预约状态
...@@ -150,59 +174,61 @@ const ReserveStatus = { ...@@ -150,59 +174,61 @@ const ReserveStatus = {
150 INFINITY: -1, // 无需预约 174 INFINITY: -1, // 无需预约
151 FULL: 0, // 约满 175 FULL: 0, // 约满
152 AVAILABLE: 1, // 可约 176 AVAILABLE: 1, // 可约
153 - OVERDUE: 'overdue', // 过期日期 177 + OVERDUE: 'overdue' // 过期日期
154 } 178 }
155 179
156 -const daysOfWeek = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]; 180 +const daysOfWeek = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
157 181
158 /** 182 /**
159 * @description: 每周日期列表 183 * @description: 每周日期列表
160 * @return {array} [array] 184 * @return {array} [array]
161 */ 185 */
162 const weeks = computed(() => { 186 const weeks = computed(() => {
163 - if (dates.value.length === 0) return []; 187 + if (dates.value.length === 0) {
164 - const result = []; 188 + return []
165 - let currentWeek = []; 189 + }
166 - let currentDate = new Date(dates.value[0]); 190 + const result = []
191 + let currentWeek = []
192 + let currentDate = new Date(dates.value[0])
167 193
168 // 确定第一个日期是星期几 194 // 确定第一个日期是星期几
169 - const firstDayOfWeek = currentDate.getDay() === 0 ? 7 : currentDate.getDay(); 195 + const firstDayOfWeek = currentDate.getDay() === 0 ? 7 : currentDate.getDay()
170 196
171 // 添加空白的日期,直到第一个日期的星期一 197 // 添加空白的日期,直到第一个日期的星期一
172 for (let i = 1; i < firstDayOfWeek; i++) { 198 for (let i = 1; i < firstDayOfWeek; i++) {
173 - currentWeek.push(''); 199 + currentWeek.push('')
174 } 200 }
175 201
176 // 添加日期 202 // 添加日期
177 for (const date of dates.value) { 203 for (const date of dates.value) {
178 - currentDate = new Date(date); 204 + currentDate = new Date(date)
179 - const dayOfWeek = currentDate.getDay() === 0 ? 7 : currentDate.getDay(); 205 + const dayOfWeek = currentDate.getDay() === 0 ? 7 : currentDate.getDay()
180 206
181 // 如果当前星期一,开始新的一行 207 // 如果当前星期一,开始新的一行
182 if (dayOfWeek === 1 && currentWeek.length > 0) { 208 if (dayOfWeek === 1 && currentWeek.length > 0) {
183 - result.push(currentWeek); 209 + result.push(currentWeek)
184 - currentWeek = []; 210 + currentWeek = []
185 } 211 }
186 212
187 - currentWeek.push(date); // 仅将日期部分作为字符串添加到当前星期数组 213 + currentWeek.push(date) // 仅将日期部分作为字符串添加到当前星期数组
188 } 214 }
189 215
190 // 添加最后一行 216 // 添加最后一行
191 if (currentWeek.length > 0) { 217 if (currentWeek.length > 0) {
192 while (currentWeek.length < 7) { 218 while (currentWeek.length < 7) {
193 - currentWeek.push(''); 219 + currentWeek.push('')
194 } 220 }
195 - result.push(currentWeek); 221 + result.push(currentWeek)
196 } 222 }
197 223
198 - return result; 224 + return result
199 -}); 225 +})
200 226
201 -const checked_day = ref(''); 227 +const checked_day = ref('')
202 -const checked_day_price = ref(0); 228 +const checked_day_price = ref(0)
203 -const checked_day_reserve_full = ref(null); 229 +const checked_day_reserve_full = ref(null)
204 -const checked_time = ref(-1); 230 +const checked_time = ref(-1)
205 -const timePeriod = ref([]); // 当前时间段信息 231 +const timePeriod = ref([]) // 当前时间段信息
206 232
207 /** 233 /**
208 * @description: 无预约提示 234 * @description: 无预约提示
...@@ -210,21 +236,27 @@ const timePeriod = ref([]); // 当前时间段信息 ...@@ -210,21 +236,27 @@ const timePeriod = ref([]); // 当前时间段信息
210 */ 236 */
211 237
212 const infinity_tips_text = computed(() => { 238 const infinity_tips_text = computed(() => {
213 - if (!checked_day.value || checked_day_reserve_full.value !== ReserveStatus.INFINITY) return ''; 239 + if (!checked_day.value || checked_day_reserve_full.value !== ReserveStatus.INFINITY) {
214 - const info = findDatesInfo(checked_day.value); 240 + return ''
241 + }
242 + const info = findDatesInfo(checked_day.value)
215 if (dayjs(checked_day.value).isAfter(dayjs(), 'day')) { 243 if (dayjs(checked_day.value).isAfter(dayjs(), 'day')) {
216 - const tips = (info.tips || '').trim(); 244 + const tips = (info.tips || '').trim()
217 - if (tips) return tips; 245 + if (tips) {
246 + return tips
247 + }
218 } 248 }
219 - return '暂未开启预约'; 249 + return '暂未开启预约'
220 -}); 250 +})
221 251
222 -const chooseTime = (item, index) => { // 选择时间段回调 252 +const chooseTime = (item, index) => {
223 - if (item.rest_qty || item.rest_qty === QtyStatus.INFINITY) { // 余量等于-1为不限制数量 253 + // 选择时间段回调
224 - checked_time.value = index; 254 + if (item.rest_qty || item.rest_qty === QtyStatus.INFINITY) {
225 - checked_day_price.value = item.price; // 当前价格 255 + // 余量等于-1为不限制数量
256 + checked_time.value = index
257 + checked_day_price.value = item.price // 当前价格
226 } 258 }
227 -}; 259 +}
228 260
229 /** 261 /**
230 * @description: 数量状态 262 * @description: 数量状态
...@@ -232,7 +264,7 @@ const chooseTime = (item, index) => { // 选择时间段回调 ...@@ -232,7 +264,7 @@ const chooseTime = (item, index) => { // 选择时间段回调
232 */ 264 */
233 const QtyStatus = { 265 const QtyStatus = {
234 FULL: 0, // 无余量 266 FULL: 0, // 无余量
235 - INFINITY: -1, // 无限制 267 + INFINITY: -1 // 无限制
236 } 268 }
237 269
238 /** 270 /**
...@@ -240,77 +272,90 @@ const QtyStatus = { ...@@ -240,77 +272,90 @@ const QtyStatus = {
240 * @param {string} date 272 * @param {string} date
241 * @return {void} 273 * @return {void}
242 */ 274 */
243 -const chooseDay = async (date) => { // 点击日期回调 275 +const chooseDay = async date => {
244 - if (!date) return; 276 + // 点击日期回调
245 - const info = findDatesInfo(date); 277 + if (!date) {
278 + return
279 + }
280 + const info = findDatesInfo(date)
246 // 281 //
247 - if (info.reserve_full === ReserveStatus.AVAILABLE || info.reserve_full === ReserveStatus.INFINITY) { // 状态 1可约 || -1不限制 282 + if (
248 - checked_day.value = date; // 当前日期 283 + info.reserve_full === ReserveStatus.AVAILABLE ||
249 - checked_day_reserve_full.value = info.reserve_full; // 当前状态 284 + info.reserve_full === ReserveStatus.INFINITY
285 + ) {
286 + // 状态 1可约 || -1不限制
287 + checked_day.value = date // 当前日期
288 + checked_day_reserve_full.value = info.reserve_full // 当前状态
250 // 如果可约,查询时间段信息 289 // 如果可约,查询时间段信息
251 if (info.reserve_full === ReserveStatus.AVAILABLE) { 290 if (info.reserve_full === ReserveStatus.AVAILABLE) {
252 // 选择日期后,查询时间段信息 291 // 选择日期后,查询时间段信息
253 - const { code, data } = await canReserveTimeListAPI({ month_date: checked_day.value }); 292 + const { code, data } = await canReserveTimeListAPI({ month_date: checked_day.value })
254 if (code) { 293 if (code) {
255 // rest_qty >0表示有余量,可约;=0表示没有余量,不可约;<0表示不限,可约; 294 // rest_qty >0表示有余量,可约;=0表示没有余量,不可约;<0表示不限,可约;
256 // period_type 时段类型 REGULAR=日常预约,SPRING_FESTIVAL=春节预约 295 // period_type 时段类型 REGULAR=日常预约,SPRING_FESTIVAL=春节预约
257 - timePeriod.value = data; 296 + timePeriod.value = data
258 - checked_time.value = -1; // 重置已选择的时间段 297 + checked_time.value = -1 // 重置已选择的时间段
259 } 298 }
260 } 299 }
261 } 300 }
262 -}; 301 +}
263 302
264 -const showPicker = ref(false); 303 +const showPicker = ref(false)
265 const chooseDate = () => { 304 const chooseDate = () => {
266 - showPicker.value = true; 305 + showPicker.value = true
267 } 306 }
268 307
269 -const raw_date = new Date(); 308 +const raw_date = new Date()
270 -const currentDate = ref(new Date()); // NutUI DatePicker v-model 绑定的是 Date 对象 309 +const currentDate = ref(new Date()) // NutUI DatePicker v-model 绑定的是 Date 对象
271 -const minDate = new Date(); 310 +const minDate = new Date()
272 -const maxDate = new Date(2050, 11, 1); 311 +const maxDate = new Date(2050, 11, 1)
273 -const currentDateText = ref((raw_date.getMonth() + 1).toString().padStart(2, '0')); 312 +const currentDateText = ref((raw_date.getMonth() + 1).toString().padStart(2, '0'))
274 313
275 -const onConfirm = async ({ selectedValue, selectedOptions }) => { // 选择日期回调 314 +const onConfirm = async ({ selectedValue, selectedOptions }) => {
315 + // 选择日期回调
276 // selectedValue 可能是数组或对象,NutUI 文档 316 // selectedValue 可能是数组或对象,NutUI 文档
277 // selectedOptions 是选项对象数组 317 // selectedOptions 是选项对象数组
278 // year-month 模式下 selectedValue 可能是 [year, month] 318 // year-month 模式下 selectedValue 可能是 [year, month]
279 // 实际上 NutUI DatePicker confirm 事件参数:{ selectedValue, selectedOptions } 319 // 实际上 NutUI DatePicker confirm 事件参数:{ selectedValue, selectedOptions }
280 320
281 - showPicker.value = false; 321 + showPicker.value = false
282 // selectedValue: ['2024', '02'] 322 // selectedValue: ['2024', '02']
283 - const [year, month] = selectedValue; 323 + const [year, month] = selectedValue
284 - currentDateText.value = month; 324 + currentDateText.value = month
285 325
286 // 清空选择 326 // 清空选择
287 - checked_day.value = ''; 327 + checked_day.value = ''
288 - checked_time.value = -1; 328 + checked_time.value = -1
289 - checked_day_reserve_full.value = null; 329 + checked_day_reserve_full.value = null
290 // 选择日期后,查询月份信息 330 // 选择日期后,查询月份信息
291 - const { code, data } = await canReserveDateListAPI({ month: `${year}-${month}` }); 331 + const { code, data } = await canReserveDateListAPI({ month: `${year}-${month}` })
292 if (code) { 332 if (code) {
293 // 日期列表 333 // 日期列表
294 - dates_list.value = data || []; 334 + dates_list.value = data || []
295 // 今日之前都不可约 335 // 今日之前都不可约
296 - dates_list.value.forEach((date) => { 336 + dates_list.value.forEach(date => {
297 if (dayjs(date.month_date).isBefore(dayjs())) { 337 if (dayjs(date.month_date).isBefore(dayjs())) {
298 - date.reserve_full = ReserveStatus.OVERDUE; 338 + date.reserve_full = ReserveStatus.OVERDUE
299 } 339 }
300 - }); 340 + })
301 - dates.value = dates_list.value.map(item => item.month_date); 341 + dates.value = dates_list.value.map(item => item.month_date)
302 } 342 }
303 } 343 }
304 344
305 const onCancel = () => { 345 const onCancel = () => {
306 - showPicker.value = false; 346 + showPicker.value = false
307 } 347 }
308 348
309 const nextBtn = () => { 349 const nextBtn = () => {
310 if (!checked_day.value || checked_time.value === -1) { 350 if (!checked_day.value || checked_time.value === -1) {
311 - Taro.showToast({ title: '请选择日期和时间段', icon: 'none' }); 351 + Taro.showToast({ title: '请选择日期和时间段', icon: 'none' })
312 } else { 352 } else {
313 - go('/submit', { date: checked_day.value, time: `${timePeriod.value[checked_time.value]['begin_time'].slice(0, -3)}-${timePeriod.value[checked_time.value]['end_time'].slice(0, -3)}`, price: checked_day_price.value, period_type: timePeriod.value[checked_time.value].period_type }); 353 + go('/submit', {
354 + date: checked_day.value,
355 + time: `${timePeriod.value[checked_time.value]['begin_time'].slice(0, -3)}-${timePeriod.value[checked_time.value]['end_time'].slice(0, -3)}`,
356 + price: checked_day_price.value,
357 + period_type: timePeriod.value[checked_time.value].period_type
358 + })
314 } 359 }
315 } 360 }
316 </script> 361 </script>
...@@ -319,13 +364,12 @@ const nextBtn = () => { ...@@ -319,13 +364,12 @@ const nextBtn = () => {
319 .booking-page { 364 .booking-page {
320 position: relative; 365 position: relative;
321 min-height: 100vh; 366 min-height: 100vh;
322 - background-color: #F6F6F6; 367 + background-color: #f6f6f6;
323 .calendar { 368 .calendar {
324 padding: 32rpx 16rpx; 369 padding: 32rpx 16rpx;
325 .choose-date { 370 .choose-date {
326 border-radius: 10rpx; 371 border-radius: 10rpx;
327 - background-color: #FFFFFF; 372 + background-color: #ffffff;
328 -
329 373
330 .title { 374 .title {
331 padding: 16rpx 24rpx; 375 padding: 16rpx 24rpx;
...@@ -335,20 +379,20 @@ const nextBtn = () => { ...@@ -335,20 +379,20 @@ const nextBtn = () => {
335 .text { 379 .text {
336 &::before { 380 &::before {
337 content: ''; 381 content: '';
338 - border: 4rpx solid #A67939; 382 + border: 4rpx solid #a67939;
339 margin-right: 16rpx; 383 margin-right: 16rpx;
340 } 384 }
341 } 385 }
342 .day { 386 .day {
343 - background-color: #FFFBF3; 387 + background-color: #fffbf3;
344 border-radius: 14rpx; 388 border-radius: 14rpx;
345 - border: 2rpx solid #A67939; 389 + border: 2rpx solid #a67939;
346 padding: 6rpx 16rpx; 390 padding: 6rpx 16rpx;
347 - color: #A67939; 391 + color: #a67939;
348 } 392 }
349 } 393 }
350 .days-of-week { 394 .days-of-week {
351 - background-color: #F6F6F6; 395 + background-color: #f6f6f6;
352 display: flex; 396 display: flex;
353 padding: 24rpx 1%; 397 padding: 24rpx 1%;
354 font-size: 27rpx; 398 font-size: 27rpx;
...@@ -364,44 +408,44 @@ const nextBtn = () => { ...@@ -364,44 +408,44 @@ const nextBtn = () => {
364 display: flex; 408 display: flex;
365 padding: 0 1%; 409 padding: 0 1%;
366 .item { 410 .item {
367 - width: 14.28%; // 100 / 7 411 + width: 14.28%; // 100 / 7
368 // width: 11.5%; 412 // width: 11.5%;
369 text-align: center; 413 text-align: center;
370 margin: 0 10rpx; 414 margin: 0 10rpx;
371 padding: 16rpx 0; 415 padding: 16rpx 0;
372 - border: 2rpx solid #FFF; 416 + border: 2rpx solid #fff;
373 .day-lunar { 417 .day-lunar {
374 - color: #1E1E1E; 418 + color: #1e1e1e;
375 font-size: 27rpx; 419 font-size: 27rpx;
376 margin-bottom: 10rpx; 420 margin-bottom: 10rpx;
377 } 421 }
378 .day-text { 422 .day-text {
379 - color: #1E1E1E; 423 + color: #1e1e1e;
380 font-weight: bold; 424 font-weight: bold;
381 font-size: 34rpx; 425 font-size: 34rpx;
382 } 426 }
383 .day-price { 427 .day-price {
384 - color: #A67939; 428 + color: #a67939;
385 font-size: 27rpx; 429 font-size: 27rpx;
386 } 430 }
387 &.checked { 431 &.checked {
388 - border: 2rpx solid #A67939; 432 + border: 2rpx solid #a67939;
389 border-radius: 10rpx; 433 border-radius: 10rpx;
390 - background-color: #FFFBF3; 434 + background-color: #fffbf3;
391 } 435 }
392 &.disabled { 436 &.disabled {
393 .day-lunar { 437 .day-lunar {
394 - color: #C7C7C7; 438 + color: #c7c7c7;
395 margin-bottom: 10rpx; 439 margin-bottom: 10rpx;
396 } 440 }
397 .day-text { 441 .day-text {
398 - color: #C7C7C7; 442 + color: #c7c7c7;
399 } 443 }
400 .day-price { 444 .day-price {
401 - color: #C7C7C7; 445 + color: #c7c7c7;
402 } 446 }
403 .day-no-booking { 447 .day-no-booking {
404 - color: #C7C7C7; 448 + color: #c7c7c7;
405 font-size: 24rpx; 449 font-size: 24rpx;
406 } 450 }
407 } 451 }
...@@ -418,7 +462,7 @@ const nextBtn = () => { ...@@ -418,7 +462,7 @@ const nextBtn = () => {
418 .text { 462 .text {
419 &::before { 463 &::before {
420 content: ''; 464 content: '';
421 - border: 4rpx solid #A67939; 465 + border: 4rpx solid #a67939;
422 margin-right: 16rpx; 466 margin-right: 16rpx;
423 } 467 }
424 } 468 }
...@@ -428,37 +472,37 @@ const nextBtn = () => { ...@@ -428,37 +472,37 @@ const nextBtn = () => {
428 display: flex; 472 display: flex;
429 align-items: center; 473 align-items: center;
430 justify-content: space-between; 474 justify-content: space-between;
431 - background-color: #FFF; 475 + background-color: #fff;
432 border-radius: 10rpx; 476 border-radius: 10rpx;
433 padding: 27rpx; 477 padding: 27rpx;
434 margin: 32rpx 0; 478 margin: 32rpx 0;
435 .left { 479 .left {
436 display: flex; 480 display: flex;
437 align-items: center; 481 align-items: center;
438 - color: #1E1E1E; 482 + color: #1e1e1e;
439 .icon { 483 .icon {
440 - width: 38rpx; 484 + width: 38rpx;
441 - height: 38rpx; 485 + height: 38rpx;
442 - margin-right: 16rpx; 486 + margin-right: 16rpx;
443 } 487 }
444 .price { 488 .price {
445 - color:#A67939; 489 + color: #a67939;
446 margin-left: 16rpx; 490 margin-left: 16rpx;
447 } 491 }
448 } 492 }
449 .right { 493 .right {
450 - color: #A67939; 494 + color: #a67939;
451 } 495 }
452 &.disabled { 496 &.disabled {
453 - background-color: #E0E0E0; 497 + background-color: #e0e0e0;
454 .left { 498 .left {
455 - color: #C7C7C7; 499 + color: #c7c7c7;
456 .price { 500 .price {
457 - color:#C7C7C7; 501 + color: #c7c7c7;
458 } 502 }
459 } 503 }
460 .right { 504 .right {
461 - color: #C7C7C7; 505 + color: #c7c7c7;
462 } 506 }
463 } 507 }
464 } 508 }
...@@ -472,12 +516,12 @@ const nextBtn = () => { ...@@ -472,12 +516,12 @@ const nextBtn = () => {
472 width: 750rpx; 516 width: 750rpx;
473 display: flex; 517 display: flex;
474 left: 0; 518 left: 0;
475 - background-color: #FFF; 519 + background-color: #fff;
476 align-items: center; 520 align-items: center;
477 justify-content: center; 521 justify-content: center;
478 - box-shadow: 0 -10rpx 8rpx 0 rgba(0,0,0,0.12); 522 + box-shadow: 0 -10rpx 8rpx 0 rgba(0, 0, 0, 0.12);
479 .button { 523 .button {
480 - color: #FFF; 524 + color: #fff;
481 padding: 27rpx 0; 525 padding: 27rpx 0;
482 border-radius: 16rpx; 526 border-radius: 16rpx;
483 font-size: 35rpx; 527 font-size: 35rpx;
......
...@@ -7,12 +7,14 @@ ...@@ -7,12 +7,14 @@
7 --> 7 -->
8 <template> 8 <template>
9 <view class="booking-code-page"> 9 <view class="booking-code-page">
10 - <view style="padding: 32rpx;"> 10 + <view style="padding: 32rpx">
11 <qrCode ref="qr_code_ref"></qrCode> 11 <qrCode ref="qr_code_ref"></qrCode>
12 <view class="warning"> 12 <view class="warning">
13 - <view style="display: flex; align-items: center; justify-content: center;"><IconFont name="tips" /><text style="margin-left: 10rpx;">温馨提示</text></view> 13 + <view style="display: flex; align-items: center; justify-content: center"
14 - <view style="margin-top: 16rpx;">一人一码,扫码或识别身份证成功后进入</view> 14 + ><IconFont name="tips" /><text style="margin-left: 10rpx">温馨提示</text></view
15 - <view style="height: 256rpx;"></view> 15 + >
16 + <view style="margin-top: 16rpx">一人一码,扫码或识别身份证成功后进入</view>
17 + <view style="height: 256rpx"></view>
16 </view> 18 </view>
17 </view> 19 </view>
18 <indexNav 20 <indexNav
...@@ -28,7 +30,7 @@ ...@@ -28,7 +30,7 @@
28 <script setup> 30 <script setup>
29 import { ref } from 'vue' 31 import { ref } from 'vue'
30 import Taro, { useDidShow, useDidHide } from '@tarojs/taro' 32 import Taro, { useDidShow, useDidHide } from '@tarojs/taro'
31 -import qrCode from '@/components/qrCode'; 33 +import qrCode from '@/components/qrCode'
32 import { IconFont } from '@nutui/icons-vue-taro' 34 import { IconFont } from '@nutui/icons-vue-taro'
33 import indexNav from '@/components/indexNav.vue' 35 import indexNav from '@/components/indexNav.vue'
34 import icon_3 from '@/assets/images/首页01@2x.png' 36 import icon_3 from '@/assets/images/首页01@2x.png'
...@@ -42,49 +44,53 @@ import { get_weak_network_modal_no_cache_options } from '@/utils/uiText' ...@@ -42,49 +44,53 @@ import { get_weak_network_modal_no_cache_options } from '@/utils/uiText'
42 const qr_code_ref = ref(null) 44 const qr_code_ref = ref(null)
43 45
44 useDidShow(() => { 46 useDidShow(() => {
45 - qr_code_ref.value?.start_polling?.() 47 + qr_code_ref.value?.start_polling?.()
46 - Taro.getNetworkType({ 48 + Taro.getNetworkType({
47 - success: async (res) => { 49 + success: async res => {
48 - const isConnected = is_usable_network(res.networkType); 50 + const isConnected = is_usable_network(res.networkType)
49 - if (isConnected) return 51 + if (isConnected) {
52 + return
53 + }
50 54
51 - if (has_offline_booking_cache()) { 55 + if (has_offline_booking_cache()) {
52 - Taro.redirectTo({ url: '/pages/offlineBookingList/index' }) 56 + Taro.redirectTo({ url: '/pages/offlineBookingList/index' })
53 - return 57 + return
54 - } 58 + }
55 59
56 - try { 60 + try {
57 - await Taro.showModal(get_weak_network_modal_no_cache_options()) 61 + await Taro.showModal(get_weak_network_modal_no_cache_options())
58 - } catch (e) { 62 + } catch (e) {
59 - console.error('show weak network modal failed:', e) 63 + console.error('show weak network modal failed:', e)
60 - } 64 + }
61 - Taro.redirectTo({ url: '/pages/index/index' }) 65 + Taro.redirectTo({ url: '/pages/index/index' })
62 - }, 66 + },
63 - fail: async () => { 67 + fail: async () => {
64 - if (has_offline_booking_cache()) { 68 + if (has_offline_booking_cache()) {
65 - Taro.redirectTo({ url: '/pages/offlineBookingList/index' }) 69 + Taro.redirectTo({ url: '/pages/offlineBookingList/index' })
66 - return 70 + return
67 - } 71 + }
68 - try { 72 + try {
69 - await Taro.showModal(get_weak_network_modal_no_cache_options()) 73 + await Taro.showModal(get_weak_network_modal_no_cache_options())
70 - } catch (e) { 74 + } catch (e) {
71 - console.error('show weak network modal failed:', e) 75 + console.error('show weak network modal failed:', e)
72 - } 76 + }
73 - Taro.redirectTo({ url: '/pages/index/index' }) 77 + Taro.redirectTo({ url: '/pages/index/index' })
74 - } 78 + }
75 - }); 79 + })
76 }) 80 })
77 81
78 useDidHide(() => { 82 useDidHide(() => {
79 - qr_code_ref.value?.stop_polling?.() 83 + qr_code_ref.value?.stop_polling?.()
80 }) 84 })
81 85
82 -const toMy = () => { // 跳转到我的 86 +const toMy = () => {
87 + // 跳转到我的
83 Taro.redirectTo({ 88 Taro.redirectTo({
84 url: '/pages/me/index' 89 url: '/pages/me/index'
85 }) 90 })
86 } 91 }
87 -const toHome = () => { // 跳转到首页 92 +const toHome = () => {
93 + // 跳转到首页
88 Taro.redirectTo({ 94 Taro.redirectTo({
89 url: '/pages/index/index' 95 url: '/pages/index/index'
90 }) 96 })
...@@ -92,22 +98,25 @@ const toHome = () => { // 跳转到首页 ...@@ -92,22 +98,25 @@ const toHome = () => { // 跳转到首页
92 98
93 const nav_icons = { home: icon_3, code: icon_4, me: icon_5 } 99 const nav_icons = { home: icon_3, code: icon_4, me: icon_5 }
94 100
95 -const on_nav_select = (key) => { 101 +const on_nav_select = key => {
96 - if (key === 'home') return toHome() 102 + if (key === 'home') {
97 - if (key === 'me') return toMy() 103 + return toHome()
104 + }
105 + if (key === 'me') {
106 + return toMy()
107 + }
98 } 108 }
99 -
100 </script> 109 </script>
101 110
102 <style lang="less"> 111 <style lang="less">
103 .booking-code-page { 112 .booking-code-page {
104 position: relative; 113 position: relative;
105 min-height: 100vh; 114 min-height: 100vh;
106 - background-color: #F6F6F6; 115 + background-color: #f6f6f6;
107 116
108 .warning { 117 .warning {
109 text-align: center; 118 text-align: center;
110 - color: #A67939; 119 + color: #a67939;
111 margin-top: 32rpx; 120 margin-top: 32rpx;
112 } 121 }
113 } 122 }
......
...@@ -34,9 +34,12 @@ ...@@ -34,9 +34,12 @@
34 <view>{{ qrCodeStatusText }}</view> 34 <view>{{ qrCodeStatusText }}</view>
35 </view> 35 </view>
36 </view> 36 </view>
37 - <view style="height: 160rpx;"></view> 37 + <view style="height: 160rpx"></view>
38 - <view v-if="billInfo.status === CodeStatus.SUCCESS && billInfo.show_cancel_reserve === 1" class="cancel-wrapper"> 38 + <view
39 - <view @tap="cancelBooking" class="cancel-btn ">取消预约</view> 39 + v-if="billInfo.status === CodeStatus.SUCCESS && billInfo.show_cancel_reserve === 1"
40 + class="cancel-wrapper"
41 + >
42 + <view @tap="cancelBooking" class="cancel-btn">取消预约</view>
40 </view> 43 </view>
41 </view> 44 </view>
42 </template> 45 </template>
...@@ -44,16 +47,16 @@ ...@@ -44,16 +47,16 @@
44 <script setup> 47 <script setup>
45 import { ref, computed } from 'vue' 48 import { ref, computed } from 'vue'
46 import Taro, { useDidShow, useDidHide, useRouter as useTaroRouter } from '@tarojs/taro' 49 import Taro, { useDidShow, useDidHide, useRouter as useTaroRouter } from '@tarojs/taro'
47 -import qrCode from '@/components/qrCode'; 50 +import qrCode from '@/components/qrCode'
48 import { billInfoAPI, icbcRefundAPI } from '@/api/index' 51 import { billInfoAPI, icbcRefundAPI } from '@/api/index'
49 -import { formatDatetime, get_bill_status_text } from '@/utils/tools'; 52 +import { formatDatetime, get_bill_status_text } from '@/utils/tools'
50 import { refresh_offline_booking_cache } from '@/composables/useOfflineBookingCache' 53 import { refresh_offline_booking_cache } from '@/composables/useOfflineBookingCache'
51 54
52 -const router = useTaroRouter(); 55 +const router = useTaroRouter()
53 56
54 -const pay_id = ref(''); 57 +const pay_id = ref('')
55 -const qrCodeStatus = ref(''); 58 +const qrCodeStatus = ref('')
56 -const billInfo = ref({}); 59 +const billInfo = ref({})
57 const qr_code_ref = ref(null) 60 const qr_code_ref = ref(null)
58 61
59 /** 62 /**
...@@ -75,7 +78,7 @@ const CodeStatus = { ...@@ -75,7 +78,7 @@ const CodeStatus = {
75 * @returns {string} 状态文案 78 * @returns {string} 状态文案
76 */ 79 */
77 const qrCodeStatusText = computed(() => { 80 const qrCodeStatusText = computed(() => {
78 - return get_bill_status_text(billInfo.value?.status) 81 + return get_bill_status_text(billInfo.value?.status)
79 }) 82 })
80 83
81 /** 84 /**
...@@ -84,99 +87,99 @@ const qrCodeStatusText = computed(() => { ...@@ -84,99 +87,99 @@ const qrCodeStatusText = computed(() => {
84 * @returns {Promise<void>} 无返回值 87 * @returns {Promise<void>} 无返回值
85 */ 88 */
86 const cancelBooking = async () => { 89 const cancelBooking = async () => {
87 - const { confirm } = await Taro.showModal({ 90 + const { confirm } = await Taro.showModal({
88 - title: '温馨提示', 91 + title: '温馨提示',
89 - content: '是否取消预约?', 92 + content: '是否取消预约?',
90 - confirmColor: '#A67939' 93 + confirmColor: '#A67939'
91 - }); 94 + })
92 - 95 +
93 - if (confirm) { 96 + if (confirm) {
94 - Taro.showLoading({ title: '取消中...' }); 97 + Taro.showLoading({ title: '取消中...' })
95 - const { code, data } = await icbcRefundAPI({ pay_id: pay_id.value }); 98 + const { code, data } = await icbcRefundAPI({ pay_id: pay_id.value })
96 - Taro.hideLoading(); 99 + Taro.hideLoading()
97 - if (code) { 100 + if (code) {
98 - Taro.showToast({ title: '取消成功' }); 101 + Taro.showToast({ title: '取消成功' })
99 - try { 102 + try {
100 - await refresh_offline_booking_cache({ force: true }) 103 + await refresh_offline_booking_cache({ force: true })
101 - } catch (e) {} 104 + } catch (e) {}
102 - Taro.navigateBack(); 105 + Taro.navigateBack()
103 - } else { 106 + } else {
104 - Taro.showToast({ title: '取消失败', icon: 'none' }); 107 + Taro.showToast({ title: '取消失败', icon: 'none' })
105 - }
106 } 108 }
109 + }
107 } 110 }
108 111
109 useDidShow(async () => { 112 useDidShow(async () => {
110 - qr_code_ref.value?.start_polling?.() 113 + qr_code_ref.value?.start_polling?.()
111 - pay_id.value = router.params.pay_id; 114 + pay_id.value = router.params.pay_id
112 - if (pay_id.value) { 115 + if (pay_id.value) {
113 - const { code, data } = await billInfoAPI({ pay_id: pay_id.value }); 116 + const { code, data } = await billInfoAPI({ pay_id: pay_id.value })
114 - if (code) { 117 + if (code) {
115 - data.datetime = data && formatDatetime(data); 118 + data.datetime = data && formatDatetime(data)
116 - data.order_time = data.created_time ? data.created_time.slice(0, -3) : ''; 119 + data.order_time = data.created_time ? data.created_time.slice(0, -3) : ''
117 - billInfo.value = data; 120 + billInfo.value = data
118 - }
119 } 121 }
122 + }
120 }) 123 })
121 124
122 useDidHide(() => { 125 useDidHide(() => {
123 - qr_code_ref.value?.stop_polling?.() 126 + qr_code_ref.value?.stop_polling?.()
124 }) 127 })
125 </script> 128 </script>
126 129
127 <style lang="less"> 130 <style lang="less">
128 .booking-detail-page { 131 .booking-detail-page {
129 - min-height: 100vh; 132 + min-height: 100vh;
130 - background-color: #F6F6F6; 133 + background-color: #f6f6f6;
131 - padding: 32rpx; 134 + padding: 32rpx;
132 135
133 - .detail-wrapper { 136 + .detail-wrapper {
134 - background-color: #FFF; 137 + background-color: #fff;
135 - border-radius: 16rpx; 138 + border-radius: 16rpx;
136 - padding: 32rpx; 139 + padding: 32rpx;
137 - margin-top: 32rpx; 140 + margin-top: 32rpx;
138 - box-shadow: 0 0 29rpx 0 rgba(106,106,106,0.1); 141 + box-shadow: 0 0 29rpx 0 rgba(106, 106, 106, 0.1);
139 - 142 +
140 - .detail-item { 143 + .detail-item {
141 - display: flex; 144 + display: flex;
142 - justify-content: space-between; 145 + justify-content: space-between;
143 - margin-bottom: 26rpx; 146 + margin-bottom: 26rpx;
144 - color: #333; 147 + color: #333;
145 - font-size: 30rpx; 148 + font-size: 30rpx;
146 - 149 +
147 - &:last-child { 150 + &:last-child {
148 - margin-bottom: 0; 151 + margin-bottom: 0;
149 - } 152 + }
150 - 153 +
151 - view:first-child { 154 + view:first-child {
152 - color: #999; 155 + color: #999;
153 - width: 160rpx; 156 + width: 160rpx;
154 - } 157 + }
155 - view:last-child { 158 + view:last-child {
156 - flex: 1; 159 + flex: 1;
157 - text-align: right; 160 + text-align: right;
158 - } 161 + }
159 - }
160 } 162 }
161 - 163 + }
162 - .cancel-wrapper { 164 +
163 - position: fixed; 165 + .cancel-wrapper {
164 - bottom: 0; 166 + position: fixed;
165 - left: 0; 167 + bottom: 0;
166 - width: 750rpx; 168 + left: 0;
167 - background-color: #FFF; 169 + width: 750rpx;
168 - padding: 32rpx; 170 + background-color: #fff;
169 - box-sizing: border-box; 171 + padding: 32rpx;
170 - 172 + box-sizing: border-box;
171 - .cancel-btn { 173 +
172 - background-color: #FFF; 174 + .cancel-btn {
173 - color: #A67939; 175 + background-color: #fff;
174 - border: 2rpx solid #A67939; 176 + color: #a67939;
175 - text-align: center; 177 + border: 2rpx solid #a67939;
176 - padding: 26rpx 0; 178 + text-align: center;
177 - border-radius: 16rpx; 179 + padding: 26rpx 0;
178 - font-size: 35rpx; 180 + border-radius: 16rpx;
179 - } 181 + font-size: 35rpx;
180 } 182 }
183 + }
181 } 184 }
182 </style> 185 </style>
......
...@@ -11,11 +11,18 @@ ...@@ -11,11 +11,18 @@
11 <reserveCard :data="item" /> 11 <reserveCard :data="item" />
12 </view> 12 </view>
13 13
14 - <view v-if="loading" style="text-align: center; color: #999; padding: 20rpx;">加载中...</view> 14 + <view v-if="loading" style="text-align: center; color: #999; padding: 20rpx">加载中...</view>
15 - <view v-if="finished && bookingList.length > 0" style="text-align: center; color: #999; padding: 20rpx;">没有更多了</view> 15 + <view
16 + v-if="finished && bookingList.length > 0"
17 + style="text-align: center; color: #999; padding: 20rpx"
18 + >没有更多了</view
19 + >
16 20
17 <view v-if="!bookingList.length && finished" class="no-qrcode"> 21 <view v-if="!bookingList.length && finished" class="no-qrcode">
18 - <image src="https://cdn.ipadbiz.cn/xys/booking/%E6%9A%82%E6%97%A0@2x.png" style="width: 320rpx; height: 320rpx;" /> 22 + <image
23 + src="https://cdn.ipadbiz.cn/xys/booking/%E6%9A%82%E6%97%A0@2x.png"
24 + style="width: 320rpx; height: 320rpx"
25 + />
19 <view class="no-qrcode-title">您还没有预约过参观</view> 26 <view class="no-qrcode-title">您还没有预约过参观</view>
20 </view> 27 </view>
21 </view> 28 </view>
...@@ -25,14 +32,14 @@ ...@@ -25,14 +32,14 @@
25 import { ref } from 'vue' 32 import { ref } from 'vue'
26 import { useDidShow, useReachBottom } from '@tarojs/taro' 33 import { useDidShow, useReachBottom } from '@tarojs/taro'
27 import { billListAPI } from '@/api/index' 34 import { billListAPI } from '@/api/index'
28 -import { formatDatetime } from '@/utils/tools'; 35 +import { formatDatetime } from '@/utils/tools'
29 import reserveCard from '@/components/reserveCard.vue' 36 import reserveCard from '@/components/reserveCard.vue'
30 37
31 -const page = ref(1); 38 +const page = ref(1)
32 -const limit = ref(5); 39 +const limit = ref(5)
33 -const bookingList = ref([]); 40 +const bookingList = ref([])
34 -const loading = ref(false); 41 +const loading = ref(false)
35 -const finished = ref(false); 42 +const finished = ref(false)
36 43
37 /** 44 /**
38 * @description 加载预约记录列表(分页) 45 * @description 加载预约记录列表(分页)
...@@ -40,52 +47,54 @@ const finished = ref(false); ...@@ -40,52 +47,54 @@ const finished = ref(false);
40 * @returns {Promise<void>} 无返回值 47 * @returns {Promise<void>} 无返回值
41 */ 48 */
42 const loadData = async (isRefresh = false) => { 49 const loadData = async (isRefresh = false) => {
43 - if (loading.value || (finished.value && !isRefresh)) return; 50 + if (loading.value || (finished.value && !isRefresh)) {
51 + return
52 + }
44 53
45 - loading.value = true; 54 + loading.value = true
46 - if (isRefresh) { 55 + if (isRefresh) {
47 - page.value = 1; 56 + page.value = 1
48 - finished.value = false; 57 + finished.value = false
49 - } 58 + }
50 59
51 - const { code, data } = await billListAPI({ page: page.value, row_num: limit.value }); 60 + const { code, data } = await billListAPI({ page: page.value, row_num: limit.value })
52 - loading.value = false; 61 + loading.value = false
53 62
54 - if (code) { 63 + if (code) {
55 - const list = data || []; 64 + const list = data || []
56 - list.forEach(item => { 65 + list.forEach(item => {
57 - item.booking_time = item && formatDatetime(item); 66 + item.booking_time = item && formatDatetime(item)
58 - item.order_time = item.created_time ? item.created_time.slice(0, -3) : ''; 67 + item.order_time = item.created_time ? item.created_time.slice(0, -3) : ''
59 - }); 68 + })
60 69
61 - if (isRefresh) { 70 + if (isRefresh) {
62 - bookingList.value = list; 71 + bookingList.value = list
63 - } else { 72 + } else {
64 - bookingList.value = bookingList.value.concat(list); 73 + bookingList.value = bookingList.value.concat(list)
65 - } 74 + }
66 75
67 - if (list.length < limit.value) { 76 + if (list.length < limit.value) {
68 - finished.value = true; 77 + finished.value = true
69 - } else { 78 + } else {
70 - page.value++; 79 + page.value++
71 - }
72 } 80 }
81 + }
73 } 82 }
74 83
75 useDidShow(() => { 84 useDidShow(() => {
76 - loadData(true); 85 + loadData(true)
77 -}); 86 +})
78 87
79 useReachBottom(() => { 88 useReachBottom(() => {
80 - loadData(); 89 + loadData()
81 -}); 90 +})
82 </script> 91 </script>
83 92
84 <style lang="less"> 93 <style lang="less">
85 .booking-list-page { 94 .booking-list-page {
86 padding: 32rpx; 95 padding: 32rpx;
87 min-height: 100vh; 96 min-height: 100vh;
88 - background-color: #F6F6F6; 97 + background-color: #f6f6f6;
89 98
90 .no-qrcode { 99 .no-qrcode {
91 display: flex; 100 display: flex;
...@@ -95,9 +104,9 @@ useReachBottom(() => { ...@@ -95,9 +104,9 @@ useReachBottom(() => {
95 padding-top: 160rpx; 104 padding-top: 160rpx;
96 105
97 .no-qrcode-title { 106 .no-qrcode-title {
98 - color: #A67939; 107 + color: #a67939;
99 - font-size: 34rpx; 108 + font-size: 34rpx;
100 - margin-top: 32rpx; 109 + margin-top: 32rpx;
101 } 110 }
102 } 111 }
103 } 112 }
......
...@@ -2,19 +2,33 @@ ...@@ -2,19 +2,33 @@
2 <view class="callback-page"> 2 <view class="callback-page">
3 <view> 3 <view>
4 <view v-if="pay_status === PAY_STATUS.FAIL" class="text-prompts"> 4 <view v-if="pay_status === PAY_STATUS.FAIL" class="text-prompts">
5 - <image src="https://cdn.ipadbiz.cn/xys/booking/shibai.png" mode="widthFix" class="status-icon"/> 5 + <image
6 + src="https://cdn.ipadbiz.cn/xys/booking/shibai.png"
7 + mode="widthFix"
8 + class="status-icon"
9 + />
6 <view class="text">支付失败</view> 10 <view class="text">支付失败</view>
7 </view> 11 </view>
8 <view v-else class="text-prompts"> 12 <view v-else class="text-prompts">
9 - <image src="https://cdn.ipadbiz.cn/xys/booking/%E6%88%90%E5%8A%9F@2x.png?imageMogr2/thumbnail/200x/strip/quality/70" mode="widthFix" class="status-icon"/> 13 + <image
14 + src="https://cdn.ipadbiz.cn/xys/booking/%E6%88%90%E5%8A%9F@2x.png?imageMogr2/thumbnail/200x/strip/quality/70"
15 + mode="widthFix"
16 + class="status-icon"
17 + />
10 <!-- <view class="text">支付完成</view> --> 18 <!-- <view class="text">支付完成</view> -->
11 </view> 19 </view>
12 <view class="appointment-information"> 20 <view class="appointment-information">
13 - <view class="info-item">参观人数:<text>{{ billInfo?.total_qty || 0 }} 人</text></view> 21 + <view class="info-item"
14 - <view class="info-item">参访时间:<text>{{ billInfo?.datetime || '--' }}</text></view> 22 + >参观人数:<text>{{ billInfo?.total_qty || 0 }} 人</text></view
15 - <view class="info-item">支付金额:<text>¥ {{ billInfo?.total_amt || 0 }}</text></view> 23 + >
24 + <view class="info-item"
25 + >参访时间:<text>{{ billInfo?.datetime || '--' }}</text></view
26 + >
27 + <view class="info-item"
28 + >支付金额:<text>¥ {{ billInfo?.total_amt || 0 }}</text></view
29 + >
16 </view> 30 </view>
17 - <view style="padding: 16rpx; display: flex; justify-content: center; margin-top: 32rpx;"> 31 + <view style="padding: 16rpx; display: flex; justify-content: center; margin-top: 32rpx">
18 <nut-button color="#A67939" size="small" @click="returnMerchant">返回首页</nut-button> 32 <nut-button color="#A67939" size="small" @click="returnMerchant">返回首页</nut-button>
19 </view> 33 </view>
20 </view> 34 </view>
...@@ -35,26 +49,28 @@ const billInfo = ref({}) ...@@ -35,26 +49,28 @@ const billInfo = ref({})
35 const PAY_STATUS = { 49 const PAY_STATUS = {
36 SUCCESS: '0', 50 SUCCESS: '0',
37 FAIL: '1', 51 FAIL: '1',
38 - UNKNOWN: '2', 52 + UNKNOWN: '2'
39 } 53 }
40 const pay_status = ref('0') // Default to success as per logic 54 const pay_status = ref('0') // Default to success as per logic
41 55
42 const out_trade_no = router.params.out_trade_no 56 const out_trade_no = router.params.out_trade_no
43 57
44 const getBillInfo = async () => { 58 const getBillInfo = async () => {
45 - if (!out_trade_no) return 59 + if (!out_trade_no) {
60 + return
61 + }
46 62
47 try { 63 try {
48 - // Get order details 64 + // Get order details
49 - const { code, data } = await onAuthBillInfoAPI({ order_id: out_trade_no }) 65 + const { code, data } = await onAuthBillInfoAPI({ order_id: out_trade_no })
50 - if (code && data) { 66 + if (code && data) {
51 - data.datetime = data && formatDatetime(data) 67 + data.datetime = data && formatDatetime(data)
52 - billInfo.value = data 68 + billInfo.value = data
53 - } else { 69 + } else {
54 - // Handle error if needed 70 + // Handle error if needed
55 - } 71 + }
56 } catch (e) { 72 } catch (e) {
57 - console.error(e) 73 + console.error(e)
58 } 74 }
59 } 75 }
60 76
...@@ -63,51 +79,51 @@ const returnMerchant = () => { ...@@ -63,51 +79,51 @@ const returnMerchant = () => {
63 } 79 }
64 80
65 onMounted(() => { 81 onMounted(() => {
66 - getBillInfo() 82 + getBillInfo()
67 }) 83 })
68 </script> 84 </script>
69 85
70 <style lang="less"> 86 <style lang="less">
71 .callback-page { 87 .callback-page {
72 - padding: 32rpx; 88 + padding: 32rpx;
73 - background: #fff; 89 + background: #fff;
74 - min-height: 100vh; 90 + min-height: 100vh;
75 91
76 - .text-prompts { 92 + .text-prompts {
77 - display: flex; 93 + display: flex;
78 - flex-direction: column; 94 + flex-direction: column;
79 - align-items: center; 95 + align-items: center;
80 - margin-bottom: 64rpx; 96 + margin-bottom: 64rpx;
81 - padding-top: 64rpx; 97 + padding-top: 64rpx;
82 98
83 - .status-icon { 99 + .status-icon {
84 - width: 200rpx; 100 + width: 200rpx;
85 - height: 200rpx; 101 + height: 200rpx;
86 - } 102 + }
87 103
88 - .text { 104 + .text {
89 - margin-top: 32rpx; 105 + margin-top: 32rpx;
90 - font-size: 38rpx; 106 + font-size: 38rpx;
91 - color: #333; 107 + color: #333;
92 - }
93 } 108 }
109 + }
94 110
95 - .appointment-information { 111 + .appointment-information {
96 - background: #f8f8f8; 112 + background: #f8f8f8;
97 - padding: 32rpx; 113 + padding: 32rpx;
98 - border-radius: 16rpx; 114 + border-radius: 16rpx;
99 115
100 - .info-item { 116 + .info-item {
101 - margin-bottom: 16rpx; 117 + margin-bottom: 16rpx;
102 - font-size: 29rpx; 118 + font-size: 29rpx;
103 - color: #666; 119 + color: #666;
104 120
105 - text { 121 + text {
106 - color: #333; 122 + color: #333;
107 - font-weight: 500; 123 + font-weight: 500;
108 - margin-left: 16rpx; 124 + margin-left: 16rpx;
109 - } 125 + }
110 - }
111 } 126 }
127 + }
112 } 128 }
113 </style> 129 </style>
......
1 export default { 1 export default {
2 navigationBarTitleText: 'demo', 2 navigationBarTitleText: 'demo',
3 - usingComponents: { 3 + usingComponents: {}
4 - },
5 } 4 }
......
...@@ -11,8 +11,8 @@ ...@@ -11,8 +11,8 @@
11 11
12 <script setup> 12 <script setup>
13 import '@tarojs/taro/html.css' 13 import '@tarojs/taro/html.css'
14 -import { ref } from "vue"; 14 +import { ref } from 'vue'
15 -import "./index.less"; 15 +import './index.less'
16 16
17 // 定义响应式数据 17 // 定义响应式数据
18 const str = ref('Demo页面') 18 const str = ref('Demo页面')
...@@ -20,6 +20,6 @@ const str = ref('Demo页面') ...@@ -20,6 +20,6 @@ const str = ref('Demo页面')
20 20
21 <script> 21 <script>
22 export default { 22 export default {
23 - name: "demoPage", 23 + name: 'demoPage'
24 -}; 24 +}
25 </script> 25 </script>
......
...@@ -2,9 +2,9 @@ ...@@ -2,9 +2,9 @@
2 * index页面样式 2 * index页面样式
3 */ 3 */
4 .index { 4 .index {
5 - padding: 40rpx; 5 + padding: 40rpx;
6 - 6 +
7 - .nut-button { 7 + .nut-button {
8 - margin-bottom: 40rpx; 8 + margin-bottom: 40rpx;
9 - } 9 + }
10 } 10 }
......
...@@ -15,10 +15,17 @@ ...@@ -15,10 +15,17 @@
15 <view class="mt-1 text-sm opacity-80">{{ weak_network_banner_desc }}</view> 15 <view class="mt-1 text-sm opacity-80">{{ weak_network_banner_desc }}</view>
16 </view> 16 </view>
17 <view class="index-content"> 17 <view class="index-content">
18 - <view style="height: 28vh;"> 18 + <view style="height: 28vh">
19 - <swiper class="my-swipe" :autoplay="true" :interval="3000" indicator-dots indicator-color="white" :circular="true"> 19 + <swiper
20 + class="my-swipe"
21 + :autoplay="true"
22 + :interval="3000"
23 + indicator-dots
24 + indicator-color="white"
25 + :circular="true"
26 + >
20 <swiper-item> 27 <swiper-item>
21 - <image style="height: 28vh; width: 100vw;" :src="banner_url" /> 28 + <image style="height: 28vh; width: 100vw" :src="banner_url" />
22 </swiper-item> 29 </swiper-item>
23 </swiper> 30 </swiper>
24 </view> 31 </view>
...@@ -26,8 +33,8 @@ ...@@ -26,8 +33,8 @@
26 <view ref="root" class="index-circular"> 33 <view ref="root" class="index-circular">
27 <view class="booking-wrapper"> 34 <view class="booking-wrapper">
28 <view class="booking" @tap="toBooking"> 35 <view class="booking" @tap="toBooking">
29 - <view><image :src="icon_1" style="width: 96rpx; height: 96rpx;" /></view> 36 + <view><image :src="icon_1" style="width: 96rpx; height: 96rpx" /></view>
30 - <view style="color: #FFF;">开始预约</view> 37 + <view style="color: #fff">开始预约</view>
31 </view> 38 </view>
32 </view> 39 </view>
33 </view> 40 </view>
...@@ -57,7 +64,7 @@ import icon_3 from '@/assets/images/首页02@2x.png' ...@@ -57,7 +64,7 @@ import icon_3 from '@/assets/images/首页02@2x.png'
57 import icon_4 from '@/assets/images/二维码icon.png' 64 import icon_4 from '@/assets/images/二维码icon.png'
58 import icon_5 from '@/assets/images/我的01@2x.png' 65 import icon_5 from '@/assets/images/我的01@2x.png'
59 66
60 -const go = useGo(); 67 +const go = useGo()
61 const is_offline = ref(false) 68 const is_offline = ref(false)
62 const weak_network_banner_desc = weak_network_text.banner_desc 69 const weak_network_banner_desc = weak_network_text.banner_desc
63 // 背景图版本号, 用于刷新背景图 70 // 背景图版本号, 用于刷新背景图
...@@ -66,10 +73,12 @@ const bg_version = ref(Number.isFinite(initial_t) ? initial_t : 0) ...@@ -66,10 +73,12 @@ const bg_version = ref(Number.isFinite(initial_t) ? initial_t : 0)
66 let is_reloading = false 73 let is_reloading = false
67 74
68 const reload_page = () => { 75 const reload_page = () => {
69 - if (is_reloading) return 76 + if (is_reloading) {
77 + return
78 + }
70 is_reloading = true 79 is_reloading = true
71 Taro.reLaunch({ 80 Taro.reLaunch({
72 - url: `/pages/index/index?_t=${Date.now()}`, 81 + url: `/pages/index/index?_t=${Date.now()}`
73 }) 82 })
74 } 83 }
75 84
...@@ -93,18 +102,18 @@ const page_style = computed(() => { ...@@ -93,18 +102,18 @@ const page_style = computed(() => {
93 if (is_offline.value) { 102 if (is_offline.value) {
94 return { 103 return {
95 backgroundColor: '#F3EEE3', 104 backgroundColor: '#F3EEE3',
96 - backgroundImage: `linear-gradient(180deg, rgba(166, 121, 57, 0.10) 0%, rgba(255, 255, 255, 0.90) 60%, rgba(243, 238, 227, 1) 100%), url('${normal_bg_url.value}')`, 105 + backgroundImage: `linear-gradient(180deg, rgba(166, 121, 57, 0.10) 0%, rgba(255, 255, 255, 0.90) 60%, rgba(243, 238, 227, 1) 100%), url('${normal_bg_url.value}')`
97 } 106 }
98 } 107 }
99 return { 108 return {
100 backgroundColor: '#F3EEE3', 109 backgroundColor: '#F3EEE3',
101 - backgroundImage: `url('${normal_bg_url.value}')`, 110 + backgroundImage: `url('${normal_bg_url.value}')`
102 } 111 }
103 }) 112 })
104 113
105 const logo_style = computed(() => { 114 const logo_style = computed(() => {
106 return { 115 return {
107 - backgroundImage: `url('${logo_url.value}')`, 116 + backgroundImage: `url('${logo_url.value}')`
108 } 117 }
109 }) 118 })
110 119
...@@ -114,7 +123,7 @@ const logo_style = computed(() => { ...@@ -114,7 +123,7 @@ const logo_style = computed(() => {
114 * - 更新 is_offline 状态 123 * - 更新 is_offline 状态
115 */ 124 */
116 125
117 -const apply_offline_state = (next_offline) => { 126 +const apply_offline_state = next_offline => {
118 if (is_offline.value === true && next_offline === false) { 127 if (is_offline.value === true && next_offline === false) {
119 reload_page() 128 reload_page()
120 return true 129 return true
...@@ -147,9 +156,11 @@ let network_listener = null ...@@ -147,9 +156,11 @@ let network_listener = null
147 */ 156 */
148 157
149 const setup_network_listener = () => { 158 const setup_network_listener = () => {
150 - if (has_network_listener) return 159 + if (has_network_listener) {
160 + return
161 + }
151 has_network_listener = true 162 has_network_listener = true
152 - network_listener = (res) => { 163 + network_listener = res => {
153 try { 164 try {
154 const is_connected = res?.isConnected !== false 165 const is_connected = res?.isConnected !== false
155 const network_type = res?.networkType 166 const network_type = res?.networkType
...@@ -158,7 +169,9 @@ const setup_network_listener = () => { ...@@ -158,7 +169,9 @@ const setup_network_listener = () => {
158 const next_offline = !(is_connected && is_usable_network(network_type)) 169 const next_offline = !(is_connected && is_usable_network(network_type))
159 // 检查是否需要刷新 170 // 检查是否需要刷新
160 const is_handled = apply_offline_state(next_offline) 171 const is_handled = apply_offline_state(next_offline)
161 - if (is_handled) return 172 + if (is_handled) {
173 + return
174 + }
162 } 175 }
163 // 还没有网, 再次刷新 176 // 还没有网, 再次刷新
164 refresh_offline_state() 177 refresh_offline_state()
...@@ -175,7 +188,9 @@ const setup_network_listener = () => { ...@@ -175,7 +188,9 @@ const setup_network_listener = () => {
175 */ 188 */
176 189
177 const teardown_network_listener = () => { 190 const teardown_network_listener = () => {
178 - if (!has_network_listener) return 191 + if (!has_network_listener) {
192 + return
193 + }
179 has_network_listener = false 194 has_network_listener = false
180 if (network_listener && typeof Taro.offNetworkStatusChange === 'function') { 195 if (network_listener && typeof Taro.offNetworkStatusChange === 'function') {
181 try { 196 try {
...@@ -199,7 +214,8 @@ onUnmounted(() => { ...@@ -199,7 +214,8 @@ onUnmounted(() => {
199 teardown_network_listener() 214 teardown_network_listener()
200 }) 215 })
201 216
202 -const toBooking = () => { // 跳转到预约须知 217 +const toBooking = () => {
218 + // 跳转到预约须知
203 // 如果是离线模式,不跳转 219 // 如果是离线模式,不跳转
204 if (is_offline.value) { 220 if (is_offline.value) {
205 Taro.showToast({ 221 Taro.showToast({
...@@ -208,16 +224,18 @@ const toBooking = () => { // 跳转到预约须知 ...@@ -208,16 +224,18 @@ const toBooking = () => { // 跳转到预约须知
208 }) 224 })
209 return 225 return
210 } 226 }
211 - go('/notice'); 227 + go('/notice')
212 } 228 }
213 229
214 -const toCode = () => { // 跳转到预约码 230 +const toCode = () => {
231 + // 跳转到预约码
215 Taro.redirectTo({ 232 Taro.redirectTo({
216 url: '/pages/bookingCode/index' 233 url: '/pages/bookingCode/index'
217 }) 234 })
218 } 235 }
219 236
220 -const toMy = () => { // 跳转到我的 237 +const toMy = () => {
238 + // 跳转到我的
221 Taro.redirectTo({ 239 Taro.redirectTo({
222 url: '/pages/me/index' 240 url: '/pages/me/index'
223 }) 241 })
...@@ -225,9 +243,13 @@ const toMy = () => { // 跳转到我的 ...@@ -225,9 +243,13 @@ const toMy = () => { // 跳转到我的
225 243
226 const nav_icons = { home: icon_3, code: icon_4, me: icon_5 } 244 const nav_icons = { home: icon_3, code: icon_4, me: icon_5 }
227 245
228 -const on_nav_select = (key) => { 246 +const on_nav_select = key => {
229 - if (key === 'code') return toCode() 247 + if (key === 'code') {
230 - if (key === 'me') return toMy() 248 + return toCode()
249 + }
250 + if (key === 'me') {
251 + return toMy()
252 + }
231 } 253 }
232 254
233 useShareAppMessage(() => { 255 useShareAppMessage(() => {
...@@ -236,7 +258,6 @@ useShareAppMessage(() => { ...@@ -236,7 +258,6 @@ useShareAppMessage(() => {
236 path: '/pages/index/index' 258 path: '/pages/index/index'
237 } 259 }
238 }) 260 })
239 -
240 </script> 261 </script>
241 262
242 <style lang="less"> 263 <style lang="less">
...@@ -248,7 +269,7 @@ useShareAppMessage(() => { ...@@ -248,7 +269,7 @@ useShareAppMessage(() => {
248 background-size: cover; /* 确保背景覆盖 */ 269 background-size: cover; /* 确保背景覆盖 */
249 270
250 &.is-offline { 271 &.is-offline {
251 - background-color: #F3EEE3; 272 + background-color: #f3eee3;
252 } 273 }
253 274
254 .offline-banner { 275 .offline-banner {
...@@ -258,7 +279,7 @@ useShareAppMessage(() => { ...@@ -258,7 +279,7 @@ useShareAppMessage(() => {
258 right: 24rpx; 279 right: 24rpx;
259 z-index: 10; 280 z-index: 10;
260 background: rgba(255, 255, 255, 0.88); 281 background: rgba(255, 255, 255, 0.88);
261 - color: #A67939; 282 + color: #a67939;
262 border: 2rpx solid rgba(166, 121, 57, 0.25); 283 border: 2rpx solid rgba(166, 121, 57, 0.25);
263 box-shadow: 0 12rpx 30rpx rgba(166, 121, 57, 0.12); 284 box-shadow: 0 12rpx 30rpx rgba(166, 121, 57, 0.12);
264 backdrop-filter: blur(6px); 285 backdrop-filter: blur(6px);
...@@ -279,30 +300,30 @@ useShareAppMessage(() => { ...@@ -279,30 +300,30 @@ useShareAppMessage(() => {
279 display: flex; 300 display: flex;
280 justify-content: center; 301 justify-content: center;
281 align-items: center; 302 align-items: center;
282 - background-color: #A67939; 303 + background-color: #a67939;
283 border-radius: 14rpx; 304 border-radius: 14rpx;
284 - color: #FFFFFF; 305 + color: #ffffff;
285 padding: 22rpx 128rpx; 306 padding: 22rpx 128rpx;
286 - border: 2rpx solid #A67939; 307 + border: 2rpx solid #a67939;
287 } 308 }
288 .record { 309 .record {
289 display: flex; 310 display: flex;
290 justify-content: center; 311 justify-content: center;
291 align-items: center; 312 align-items: center;
292 - color: #A67939; 313 + color: #a67939;
293 border-radius: 14rpx; 314 border-radius: 14rpx;
294 padding: 22rpx 128rpx; 315 padding: 22rpx 128rpx;
295 - border: 2rpx solid #A67939; 316 + border: 2rpx solid #a67939;
296 margin-top: 48rpx; 317 margin-top: 48rpx;
297 } 318 }
298 .search { 319 .search {
299 display: flex; 320 display: flex;
300 justify-content: center; 321 justify-content: center;
301 align-items: center; 322 align-items: center;
302 - color: #A67939; 323 + color: #a67939;
303 border-radius: 14rpx; 324 border-radius: 14rpx;
304 padding: 22rpx 128rpx; 325 padding: 22rpx 128rpx;
305 - border: 2rpx solid #A67939; 326 + border: 2rpx solid #a67939;
306 margin-top: 48rpx; 327 margin-top: 48rpx;
307 } 328 }
308 } 329 }
...@@ -327,7 +348,7 @@ useShareAppMessage(() => { ...@@ -327,7 +348,7 @@ useShareAppMessage(() => {
327 height: 230rpx; 348 height: 230rpx;
328 width: 230rpx; 349 width: 230rpx;
329 border-radius: 50%; 350 border-radius: 50%;
330 - background-color: #A67939; 351 + background-color: #a67939;
331 display: flex; 352 display: flex;
332 align-items: center; 353 align-items: center;
333 justify-content: center; 354 justify-content: center;
...@@ -348,7 +369,8 @@ useShareAppMessage(() => { ...@@ -348,7 +369,8 @@ useShareAppMessage(() => {
348 } 369 }
349 .my-swipe { 370 .my-swipe {
350 height: 400rpx; 371 height: 400rpx;
351 - swiper-item { /* Taro swiper-item 编译后 */ 372 + swiper-item {
373 + /* Taro swiper-item 编译后 */
352 height: 400rpx; 374 height: 400rpx;
353 width: 750rpx; 375 width: 750rpx;
354 background-size: cover; 376 background-size: cover;
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
2 <view class="my-page"> 2 <view class="my-page">
3 <view v-for="(item, index) in menu_list" :key="index" class="my-item" @tap="on_menu_tap(item)"> 3 <view v-for="(item, index) in menu_list" :key="index" class="my-item" @tap="on_menu_tap(item)">
4 <view class="left"> 4 <view class="left">
5 - <image :src="item.icon" style="width: 38rpx; height: 38rpx; margin-right: 16rpx;" /> 5 + <image :src="item.icon" style="width: 38rpx; height: 38rpx; margin-right: 16rpx" />
6 {{ item.name }} 6 {{ item.name }}
7 </view> 7 </view>
8 <view> 8 <view>
...@@ -34,12 +34,17 @@ import { is_usable_network, get_network_type } from '@/utils/network' ...@@ -34,12 +34,17 @@ import { is_usable_network, get_network_type } from '@/utils/network'
34 import icon_booking from '@/assets/images/预约记录@2x.png' 34 import icon_booking from '@/assets/images/预约记录@2x.png'
35 import icon_visitor from '@/assets/images/我的01@2x.png' 35 import icon_visitor from '@/assets/images/我的01@2x.png'
36 import icon_invite from '@/assets/images/二维码@2x2.png' 36 import icon_invite from '@/assets/images/二维码@2x2.png'
37 -import { weak_network_text, get_weak_network_modal_go_offline_records_options } from '@/utils/uiText' 37 +import {
38 + weak_network_text,
39 + get_weak_network_modal_go_offline_records_options
40 +} from '@/utils/uiText'
38 41
39 -const go = useGo(); 42 +const go = useGo()
40 43
41 -const on_menu_tap = async (item) => { 44 +const on_menu_tap = async item => {
42 - if (!item?.to) return 45 + if (!item?.to) {
46 + return
47 + }
43 48
44 if (item.to === '/pages/bookingList/index') { 49 if (item.to === '/pages/bookingList/index') {
45 const network_type = await get_network_type() 50 const network_type = await get_network_type()
...@@ -60,12 +65,14 @@ const on_menu_tap = async (item) => { ...@@ -60,12 +65,14 @@ const on_menu_tap = async (item) => {
60 go(item.to) 65 go(item.to)
61 } 66 }
62 67
63 -const toCode = () => { // 跳转到预约码 68 +const toCode = () => {
69 + // 跳转到预约码
64 Taro.redirectTo({ 70 Taro.redirectTo({
65 url: '/pages/bookingCode/index' 71 url: '/pages/bookingCode/index'
66 }) 72 })
67 } 73 }
68 -const toHome = () => { // 跳转到首页 74 +const toHome = () => {
75 + // 跳转到首页
69 Taro.redirectTo({ 76 Taro.redirectTo({
70 url: '/pages/index/index' 77 url: '/pages/index/index'
71 }) 78 })
...@@ -73,43 +80,51 @@ const toHome = () => { // 跳转到首页 ...@@ -73,43 +80,51 @@ const toHome = () => { // 跳转到首页
73 80
74 const nav_icons = { home: icon_3, code: icon_4, me: icon_5 } 81 const nav_icons = { home: icon_3, code: icon_4, me: icon_5 }
75 82
76 -const on_nav_select = (key) => { 83 +const on_nav_select = key => {
77 - if (key === 'home') return toHome() 84 + if (key === 'home') {
78 - if (key === 'code') return toCode() 85 + return toHome()
86 + }
87 + if (key === 'code') {
88 + return toCode()
89 + }
79 } 90 }
80 91
81 -const menu_list = [{ 92 +const menu_list = [
82 - icon: icon_booking, 93 + {
83 - name: '预约记录', 94 + icon: icon_booking,
84 - to: '/pages/bookingList/index' 95 + name: '预约记录',
85 -}, { 96 + to: '/pages/bookingList/index'
86 - icon: icon_visitor, 97 + },
87 - name: '参观者', 98 + {
88 - to: '/pages/visitorList/index' 99 + icon: icon_visitor,
89 -}, { 100 + name: '参观者',
90 - icon: icon_invite, 101 + to: '/pages/visitorList/index'
91 - name: '邀请码', 102 + },
92 - to: '/pages/search/index' 103 + {
93 -}] 104 + icon: icon_invite,
105 + name: '邀请码',
106 + to: '/pages/search/index'
107 + }
108 +]
94 </script> 109 </script>
95 110
96 <style lang="less"> 111 <style lang="less">
97 .my-page { 112 .my-page {
98 position: relative; 113 position: relative;
99 min-height: 100vh; 114 min-height: 100vh;
100 - background-color: #F6F6F6; 115 + background-color: #f6f6f6;
101 padding: 32rpx; 116 padding: 32rpx;
102 117
103 .my-item { 118 .my-item {
104 padding: 32rpx; 119 padding: 32rpx;
105 display: flex; 120 display: flex;
106 - justify-content:space-between; 121 + justify-content: space-between;
107 align-items: center; 122 align-items: center;
108 margin-bottom: 32rpx; 123 margin-bottom: 32rpx;
109 - background-color: #FFF; 124 + background-color: #fff;
110 border-radius: 10rpx; 125 border-radius: 10rpx;
111 .left { 126 .left {
112 - color: #A67939; 127 + color: #a67939;
113 display: flex; 128 display: flex;
114 align-items: center; 129 align-items: center;
115 } 130 }
......
1 export default { 1 export default {
2 - navigationBarTitleText: 'NFC测试', 2 + navigationBarTitleText: 'NFC测试'
3 } 3 }
......
...@@ -6,18 +6,32 @@ ...@@ -6,18 +6,32 @@
6 </view> 6 </view>
7 7
8 <view class="action-area"> 8 <view class="action-area">
9 - <nut-button type="primary" size="large" @click="startScan" :disabled="isScanning" :loading="isScanning"> 9 + <nut-button
10 + type="primary"
11 + size="large"
12 + @click="startScan"
13 + :disabled="isScanning"
14 + :loading="isScanning"
15 + >
10 {{ isScanning ? '正在扫描...' : '开始 NFC 扫描' }} 16 {{ isScanning ? '正在扫描...' : '开始 NFC 扫描' }}
11 </nut-button> 17 </nut-button>
12 18
13 - <nut-button v-if="isScanning" type="danger" size="large" @click="stopScan" style="margin-top: 40rpx;"> 19 + <nut-button
20 + v-if="isScanning"
21 + type="danger"
22 + size="large"
23 + @click="stopScan"
24 + style="margin-top: 40rpx"
25 + >
14 停止扫描 26 停止扫描
15 </nut-button> 27 </nut-button>
16 </view> 28 </view>
17 29
18 <view class="status-area"> 30 <view class="status-area">
19 <text class="status-label">当前状态:</text> 31 <text class="status-label">当前状态:</text>
20 - <text class="status-text" :class="{ 'scanning': isScanning, 'error': !!error }">{{ status }}</text> 32 + <text class="status-text" :class="{ scanning: isScanning, error: !!error }">{{
33 + status
34 + }}</text>
21 </view> 35 </view>
22 36
23 <view class="result-area" v-if="result"> 37 <view class="result-area" v-if="result">
...@@ -41,18 +55,18 @@ ...@@ -41,18 +55,18 @@
41 </template> 55 </template>
42 56
43 <script setup> 57 <script setup>
44 -import { ref, onUnmounted } from 'vue'; 58 +import { ref, onUnmounted } from 'vue'
45 -import Taro from '@tarojs/taro'; 59 +import Taro from '@tarojs/taro'
46 60
47 -const isScanning = ref(false); 61 +const isScanning = ref(false)
48 -const status = ref('等待操作'); 62 +const status = ref('等待操作')
49 -const result = ref(''); 63 +const result = ref('')
50 -const error = ref(''); 64 +const error = ref('')
51 -const debugInfo = ref(''); 65 +const debugInfo = ref('')
52 66
53 -let nfcAdapter = null; 67 +let nfcAdapter = null
54 68
55 -const safe_stringify = (data) => { 69 +const safe_stringify = data => {
56 try { 70 try {
57 return JSON.stringify( 71 return JSON.stringify(
58 data, 72 data,
...@@ -60,63 +74,63 @@ const safe_stringify = (data) => { ...@@ -60,63 +74,63 @@ const safe_stringify = (data) => {
60 if (value instanceof ArrayBuffer) { 74 if (value instanceof ArrayBuffer) {
61 return { 75 return {
62 __type: 'ArrayBuffer', 76 __type: 'ArrayBuffer',
63 - hex: bufferToHex(value), 77 + hex: bufferToHex(value)
64 - }; 78 + }
65 } 79 }
66 if (value && value.buffer instanceof ArrayBuffer) { 80 if (value && value.buffer instanceof ArrayBuffer) {
67 return { 81 return {
68 __type: 'TypedArray', 82 __type: 'TypedArray',
69 - hex: bufferToHex(value.buffer), 83 + hex: bufferToHex(value.buffer)
70 - }; 84 + }
71 } 85 }
72 - return value; 86 + return value
73 }, 87 },
74 2 88 2
75 - ); 89 + )
76 } catch (e) { 90 } catch (e) {
77 - return String(data); 91 + return String(data)
78 } 92 }
79 -}; 93 +}
80 94
81 const startScan = async () => { 95 const startScan = async () => {
82 - error.value = ''; 96 + error.value = ''
83 - result.value = ''; 97 + result.value = ''
84 - debugInfo.value = ''; 98 + debugInfo.value = ''
85 99
86 - let systemInfo = null; 100 + let systemInfo = null
87 try { 101 try {
88 - systemInfo = Taro.getSystemInfoSync(); 102 + systemInfo = Taro.getSystemInfoSync()
89 } catch (e) {} 103 } catch (e) {}
90 - const envType = Taro.getEnv && Taro.getEnv(); 104 + const envType = Taro.getEnv && Taro.getEnv()
91 - const platform = systemInfo && systemInfo.platform ? systemInfo.platform : ''; 105 + const platform = systemInfo && systemInfo.platform ? systemInfo.platform : ''
92 - const system = systemInfo && systemInfo.system ? systemInfo.system : ''; 106 + const system = systemInfo && systemInfo.system ? systemInfo.system : ''
93 - const model = systemInfo && systemInfo.model ? systemInfo.model : ''; 107 + const model = systemInfo && systemInfo.model ? systemInfo.model : ''
94 - const SDKVersion = systemInfo && systemInfo.SDKVersion ? systemInfo.SDKVersion : ''; 108 + const SDKVersion = systemInfo && systemInfo.SDKVersion ? systemInfo.SDKVersion : ''
95 - const version = systemInfo && systemInfo.version ? systemInfo.version : ''; 109 + const version = systemInfo && systemInfo.version ? systemInfo.version : ''
96 - debugInfo.value = `env: ${envType}\nplatform: ${platform}\nsystem: ${system}\nmodel: ${model}\nSDKVersion: ${SDKVersion}\nversion: ${version}`; 110 + debugInfo.value = `env: ${envType}\nplatform: ${platform}\nsystem: ${system}\nmodel: ${model}\nSDKVersion: ${SDKVersion}\nversion: ${version}`
97 111
98 if (platform === 'ios') { 112 if (platform === 'ios') {
99 - error.value = 'iOS 端微信小程序通常不支持 NFC(该能力主要在 Android 可用)'; 113 + error.value = 'iOS 端微信小程序通常不支持 NFC(该能力主要在 Android 可用)'
100 - status.value = '启动失败'; 114 + status.value = '启动失败'
101 - return; 115 + return
102 } 116 }
103 117
104 if (!Taro.getNFCAdapter) { 118 if (!Taro.getNFCAdapter) {
105 - error.value = '当前环境不支持 NFC 接口'; 119 + error.value = '当前环境不支持 NFC 接口'
106 - status.value = '启动失败'; 120 + status.value = '启动失败'
107 - return; 121 + return
108 } 122 }
109 123
110 if (envType && Taro.ENV_TYPE && envType !== Taro.ENV_TYPE.WEAPP) { 124 if (envType && Taro.ENV_TYPE && envType !== Taro.ENV_TYPE.WEAPP) {
111 - error.value = '当前不是微信小程序环境,无法使用 NFC'; 125 + error.value = '当前不是微信小程序环境,无法使用 NFC'
112 - status.value = '启动失败'; 126 + status.value = '启动失败'
113 - return; 127 + return
114 } 128 }
115 129
116 try { 130 try {
117 - nfcAdapter = Taro.getNFCAdapter(); 131 + nfcAdapter = Taro.getNFCAdapter()
118 132
119 - status.value = '正在初始化 NFC...'; 133 + status.value = '正在初始化 NFC...'
120 134
121 await nfcAdapter.startDiscovery({ 135 await nfcAdapter.startDiscovery({
122 techs: [ 136 techs: [
...@@ -127,102 +141,101 @@ const startScan = async () => { ...@@ -127,102 +141,101 @@ const startScan = async () => {
127 'ISO-DEP', 141 'ISO-DEP',
128 'MIFARE-CLASSIC', 142 'MIFARE-CLASSIC',
129 'MIFARE-ULTRALIGHT', 143 'MIFARE-ULTRALIGHT',
130 - 'NDEF', 144 + 'NDEF'
131 ], 145 ],
132 success: () => { 146 success: () => {
133 - status.value = '请将手机背面靠近 NFC 标签'; 147 + status.value = '请将手机背面靠近 NFC 标签'
134 - isScanning.value = true; 148 + isScanning.value = true
135 }, 149 },
136 - fail: (err) => { 150 + fail: err => {
137 - console.error('NFC start error:', err); 151 + console.error('NFC start error:', err)
138 // 错误码参考微信文档 152 // 错误码参考微信文档
139 - debugInfo.value = `${debugInfo.value}\n\nstartDiscovery fail:\n${err && (err.errMsg || JSON.stringify(err))}`; 153 + debugInfo.value = `${debugInfo.value}\n\nstartDiscovery fail:\n${err && (err.errMsg || JSON.stringify(err))}`
140 if (err.errCode === 13000) { 154 if (err.errCode === 13000) {
141 - error.value = '设备不支持 NFC'; 155 + error.value = '设备不支持 NFC'
142 } else if (err.errCode === 13001) { 156 } else if (err.errCode === 13001) {
143 - error.value = '系统 NFC 开关未开启'; 157 + error.value = '系统 NFC 开关未开启'
144 } else if (err.errMsg && err.errMsg.includes('platform is not supported')) { 158 } else if (err.errMsg && err.errMsg.includes('platform is not supported')) {
145 - error.value = '开发者工具不支持 NFC,请使用真机调试'; 159 + error.value = '开发者工具不支持 NFC,请使用真机调试'
146 } else { 160 } else {
147 - error.value = 'NFC 启动失败: ' + (err.errMsg || JSON.stringify(err)); 161 + error.value = `NFC 启动失败: ${err.errMsg || JSON.stringify(err)}`
148 } 162 }
149 - status.value = '启动失败'; 163 + status.value = '启动失败'
150 } 164 }
151 - }); 165 + })
152 166
153 - nfcAdapter.onDiscovered((res) => { 167 + nfcAdapter.onDiscovered(res => {
154 - console.log('NFC Discovered:', res); 168 + console.log('NFC Discovered:', res)
155 - status.value = '发现标签,正在读取...'; 169 + status.value = '发现标签,正在读取...'
156 - Taro.vibrateShort(); 170 + Taro.vibrateShort()
157 171
158 - handleNfcMessage(res); 172 + handleNfcMessage(res)
159 173
160 // 扫描成功后,通常可以选择是否停止。这里我们保持扫描状态,或者提供停止按钮。 174 // 扫描成功后,通常可以选择是否停止。这里我们保持扫描状态,或者提供停止按钮。
161 // 用户需求是“扫描成功后显示信息内容”,为了防止重复读取造成刷屏,可以考虑读取成功后自动停止。 175 // 用户需求是“扫描成功后显示信息内容”,为了防止重复读取造成刷屏,可以考虑读取成功后自动停止。
162 // 但如果是测试页,可能想连续测多个。我选择不自动停止,但更新结果。 176 // 但如果是测试页,可能想连续测多个。我选择不自动停止,但更新结果。
163 - }); 177 + })
164 -
165 } catch (e) { 178 } catch (e) {
166 - console.error('NFC Adapter error:', e); 179 + console.error('NFC Adapter error:', e)
167 - debugInfo.value = `${debugInfo.value}\n\ngetNFCAdapter error:\n${e && (e.errMsg || e.message || JSON.stringify(e))}`; 180 + debugInfo.value = `${debugInfo.value}\n\ngetNFCAdapter error:\n${e && (e.errMsg || e.message || JSON.stringify(e))}`
168 - error.value = 'NFC 初始化失败(可能是设备/系统不支持,或不在可用环境)'; 181 + error.value = 'NFC 初始化失败(可能是设备/系统不支持,或不在可用环境)'
169 - status.value = '错误'; 182 + status.value = '错误'
170 } 183 }
171 -}; 184 +}
172 185
173 const stopScan = () => { 186 const stopScan = () => {
174 if (nfcAdapter) { 187 if (nfcAdapter) {
175 nfcAdapter.stopDiscovery({ 188 nfcAdapter.stopDiscovery({
176 success: () => { 189 success: () => {
177 - status.value = '已停止扫描'; 190 + status.value = '已停止扫描'
178 - isScanning.value = false; 191 + isScanning.value = false
179 }, 192 },
180 - fail: (err) => { 193 + fail: err => {
181 - console.error('Stop NFC fail', err); 194 + console.error('Stop NFC fail', err)
182 }, 195 },
183 complete: () => { 196 complete: () => {
184 // 确保状态更新 197 // 确保状态更新
185 - isScanning.value = false; 198 + isScanning.value = false
186 if (nfcAdapter && nfcAdapter.offDiscovered) { 199 if (nfcAdapter && nfcAdapter.offDiscovered) {
187 - nfcAdapter.offDiscovered(); 200 + nfcAdapter.offDiscovered()
188 } 201 }
189 } 202 }
190 - }); 203 + })
191 } else { 204 } else {
192 - isScanning.value = false; 205 + isScanning.value = false
193 } 206 }
194 -}; 207 +}
195 208
196 // 辅助函数:将 ArrayBuffer 转为 16 进制字符串 209 // 辅助函数:将 ArrayBuffer 转为 16 进制字符串
197 -const bufferToHex = (buffer) => { 210 +const bufferToHex = buffer => {
198 return Array.from(new Uint8Array(buffer)) 211 return Array.from(new Uint8Array(buffer))
199 .map(b => b.toString(16).padStart(2, '0')) 212 .map(b => b.toString(16).padStart(2, '0'))
200 .join(':') 213 .join(':')
201 - .toUpperCase(); 214 + .toUpperCase()
202 -}; 215 +}
203 216
204 -const handleNfcMessage = (res) => { 217 +const handleNfcMessage = res => {
205 - let content = ''; 218 + let content = ''
206 219
207 // 1. 获取 UID 220 // 1. 获取 UID
208 if (res.id) { 221 if (res.id) {
209 - content += `UID: ${bufferToHex(res.id)}\n`; 222 + content += `UID: ${bufferToHex(res.id)}\n`
210 } 223 }
211 224
212 // 2. 获取 Tech Types 225 // 2. 获取 Tech Types
213 if (res.techs && res.techs.length) { 226 if (res.techs && res.techs.length) {
214 - content += `Techs: ${res.techs.join(', ')}\n`; 227 + content += `Techs: ${res.techs.join(', ')}\n`
215 } 228 }
216 229
217 // 3. 解析 NDEF 消息 230 // 3. 解析 NDEF 消息
218 if (res.messages && res.messages.length > 0) { 231 if (res.messages && res.messages.length > 0) {
219 - content += '\n--- NDEF Records ---\n'; 232 + content += '\n--- NDEF Records ---\n'
220 try { 233 try {
221 // res.messages 是一个数组,通常取第一个 NDEF Message 234 // res.messages 是一个数组,通常取第一个 NDEF Message
222 - const records = res.messages[0].records || []; 235 + const records = res.messages[0].records || []
223 236
224 records.forEach((record, index) => { 237 records.forEach((record, index) => {
225 - content += `[Record ${index + 1}]\n`; 238 + content += `[Record ${index + 1}]\n`
226 239
227 // Type Name Format (TNF) - bits 0-2 of first byte (not directly exposed in simple objects usually, 240 // Type Name Format (TNF) - bits 0-2 of first byte (not directly exposed in simple objects usually,
228 // but we might need to parse payload if it's raw. 241 // but we might need to parse payload if it's raw.
...@@ -230,49 +243,48 @@ const handleNfcMessage = (res) => { ...@@ -230,49 +243,48 @@ const handleNfcMessage = (res) => {
230 243
231 if (record.type) { 244 if (record.type) {
232 // record.type is ArrayBuffer 245 // record.type is ArrayBuffer
233 - const typeStr = new TextDecoder().decode(record.type); 246 + const typeStr = new TextDecoder().decode(record.type)
234 - content += `Type: ${typeStr}\n`; 247 + content += `Type: ${typeStr}\n`
235 248
236 // Text Record Parsing (Type = 'T') 249 // Text Record Parsing (Type = 'T')
237 if (typeStr === 'T') { 250 if (typeStr === 'T') {
238 - const payload = new Uint8Array(record.payload); 251 + const payload = new Uint8Array(record.payload)
239 if (payload.length > 0) { 252 if (payload.length > 0) {
240 - const statusByte = payload[0]; 253 + const statusByte = payload[0]
241 - const langCodeLen = statusByte & 0x3F; 254 + const langCodeLen = statusByte & 0x3f
242 // const isUtf16 = (statusByte & 0x80) !== 0; // bit 7 255 // const isUtf16 = (statusByte & 0x80) !== 0; // bit 7
243 256
244 // 提取文本内容 257 // 提取文本内容
245 - const textBytes = payload.slice(1 + langCodeLen); 258 + const textBytes = payload.slice(1 + langCodeLen)
246 - const text = new TextDecoder().decode(textBytes); 259 + const text = new TextDecoder().decode(textBytes)
247 - content += `Content: ${text}\n`; 260 + content += `Content: ${text}\n`
248 } 261 }
249 } else { 262 } else {
250 // 其他类型,尝试直接转码显示,或者显示 HEX 263 // 其他类型,尝试直接转码显示,或者显示 HEX
251 - const text = new TextDecoder().decode(record.payload); 264 + const text = new TextDecoder().decode(record.payload)
252 // 简单的过滤,如果看起来像乱码则显示 Hex 265 // 简单的过滤,如果看起来像乱码则显示 Hex
253 if (/[\x00-\x08\x0E-\x1F]/.test(text)) { 266 if (/[\x00-\x08\x0E-\x1F]/.test(text)) {
254 - content += `Payload (Hex): ${bufferToHex(record.payload)}\n`; 267 + content += `Payload (Hex): ${bufferToHex(record.payload)}\n`
255 } else { 268 } else {
256 - content += `Payload: ${text}\n`; 269 + content += `Payload: ${text}\n`
257 } 270 }
258 } 271 }
259 } 272 }
260 - }); 273 + })
261 -
262 } catch (parseErr) { 274 } catch (parseErr) {
263 - console.error(parseErr); 275 + console.error(parseErr)
264 - content += '解析 NDEF 数据出错\n'; 276 + content += '解析 NDEF 数据出错\n'
265 } 277 }
266 } else { 278 } else {
267 - content += '\n(无 NDEF 消息)\n'; 279 + content += '\n(无 NDEF 消息)\n'
268 } 280 }
269 281
270 - result.value = `--- 原始数据 ---\n${safe_stringify(res)}\n\n--- 解析结果 ---\n${content}`; 282 + result.value = `--- 原始数据 ---\n${safe_stringify(res)}\n\n--- 解析结果 ---\n${content}`
271 -}; 283 +}
272 284
273 onUnmounted(() => { 285 onUnmounted(() => {
274 - stopScan(); 286 + stopScan()
275 -}); 287 +})
276 </script> 288 </script>
277 289
278 <style lang="less"> 290 <style lang="less">
......
...@@ -8,18 +8,20 @@ ...@@ -8,18 +8,20 @@
8 <template> 8 <template>
9 <view class="notice-page"> 9 <view class="notice-page">
10 <view class="content"> 10 <view class="content">
11 - <view style="text-align: center; font-size: 35rpx; margin-bottom: 16rpx;">温馨提示</view> 11 + <view style="text-align: center; font-size: 35rpx; margin-bottom: 16rpx">温馨提示</view>
12 <view> 12 <view>
13 为了您和他人的健康与安全,维护清净庄严的寺院环境,营造一个喜悦而祥和的节日氛围,请您留意并遵守以下注意事项: 13 为了您和他人的健康与安全,维护清净庄严的寺院环境,营造一个喜悦而祥和的节日氛围,请您留意并遵守以下注意事项:
14 </view> 14 </view>
15 - <view v-for="(item, index) in note_text" :key="index" style="margin-top: 16rpx;">{{ item }}</view> 15 + <view v-for="(item, index) in note_text" :key="index" style="margin-top: 16rpx">{{
16 - <view style="margin-top: 16rpx;">谢谢您的支持与配合。祝您新春吉祥、万事如意。</view> 16 + item
17 + }}</view>
18 + <view style="margin-top: 16rpx">谢谢您的支持与配合。祝您新春吉祥、万事如意。</view>
17 </view> 19 </view>
18 <view style="height: 256rpx"></view> 20 <view style="height: 256rpx"></view>
19 <view class="footer"> 21 <view class="footer">
20 <nut-checkbox-group v-model="checked"> 22 <nut-checkbox-group v-model="checked">
21 <nut-checkbox label="1" icon-size="32rpx"> 23 <nut-checkbox label="1" icon-size="32rpx">
22 - <text style="color: #a67939; font-size: 32rpx">我已阅读并同意以上内容</text> 24 + <text style="color: #a67939; font-size: 32rpx">我已阅读并同意以上内容</text>
23 </nut-checkbox> 25 </nut-checkbox>
24 </nut-checkbox-group> 26 </nut-checkbox-group>
25 <view @tap="confirmBtn" class="confirm-btn">确认,下一步</view> 27 <view @tap="confirmBtn" class="confirm-btn">确认,下一步</view>
...@@ -28,11 +30,11 @@ ...@@ -28,11 +30,11 @@
28 </template> 30 </template>
29 31
30 <script setup> 32 <script setup>
31 -import { ref } from "vue"; 33 +import { ref } from 'vue'
32 import Taro, { useDidShow } from '@tarojs/taro' 34 import Taro, { useDidShow } from '@tarojs/taro'
33 -import { useGo } from "@/hooks/useGo"; 35 +import { useGo } from '@/hooks/useGo'
34 36
35 -const go = useGo(); 37 +const go = useGo()
36 const note_text = [ 38 const note_text = [
37 '1、敬香贵在心诚,不在数量多少。三支清香,可表心诚。请带着虔诚心、恭敬心和清净心敬香礼佛。', 39 '1、敬香贵在心诚,不在数量多少。三支清香,可表心诚。请带着虔诚心、恭敬心和清净心敬香礼佛。',
38 '2、请不要自带香烛进寺院。山门殿两侧设有赠香处,凭香花券可免费领取三支清香。', 40 '2、请不要自带香烛进寺院。山门殿两侧设有赠香处,凭香花券可免费领取三支清香。',
...@@ -43,8 +45,8 @@ const note_text = [ ...@@ -43,8 +45,8 @@ const note_text = [
43 '7、请保管好自己随身携带的钱物,以免丢失给您带来麻烦。', 45 '7、请保管好自己随身携带的钱物,以免丢失给您带来麻烦。',
44 '8、您若有任何问题和困难,请向身边的法师或义工咨询、求助,或直接与客堂联系。电话:0512-65349545。', 46 '8、您若有任何问题和困难,请向身边的法师或义工咨询、求助,或直接与客堂联系。电话:0512-65349545。',
45 '9、预约如需退款,请在初七之后,到客堂办理。' 47 '9、预约如需退款,请在初七之后,到客堂办理。'
46 -]; 48 +]
47 -const checked = ref([]); 49 +const checked = ref([])
48 50
49 /** 51 /**
50 * @description 点击确认进入下一步 52 * @description 点击确认进入下一步
...@@ -52,19 +54,19 @@ const checked = ref([]); ...@@ -52,19 +54,19 @@ const checked = ref([]);
52 * @returns {void} 无返回值 54 * @returns {void} 无返回值
53 */ 55 */
54 const confirmBtn = () => { 56 const confirmBtn = () => {
55 - if (checked.value.includes("1")) { 57 + if (checked.value.includes('1')) {
56 - go("/booking"); 58 + go('/booking')
57 } else { 59 } else {
58 - Taro.showToast({ title: "请勾选同意须知", icon: "none" }); 60 + Taro.showToast({ title: '请勾选同意须知', icon: 'none' })
59 } 61 }
60 -}; 62 +}
61 </script> 63 </script>
62 64
63 <style lang="less"> 65 <style lang="less">
64 .notice-page { 66 .notice-page {
65 position: relative; 67 position: relative;
66 min-height: 100vh; 68 min-height: 100vh;
67 - background-color: #F6F6F6; 69 + background-color: #f6f6f6;
68 padding-top: 2rpx; // 防止 margin collapse 70 padding-top: 2rpx; // 防止 margin collapse
69 .content { 71 .content {
70 margin: 32rpx; 72 margin: 32rpx;
...@@ -79,16 +81,16 @@ const confirmBtn = () => { ...@@ -79,16 +81,16 @@ const confirmBtn = () => {
79 position: fixed; 81 position: fixed;
80 bottom: 0; 82 bottom: 0;
81 width: 750rpx; 83 width: 750rpx;
82 - background-color: #FFF; 84 + background-color: #fff;
83 display: flex; 85 display: flex;
84 flex-direction: column; 86 flex-direction: column;
85 padding: 32rpx; 87 padding: 32rpx;
86 box-sizing: border-box; 88 box-sizing: border-box;
87 - box-shadow: 0 -10rpx 8rpx 0 rgba(0,0,0,0.12); 89 + box-shadow: 0 -10rpx 8rpx 0 rgba(0, 0, 0, 0.12);
88 90
89 .confirm-btn { 91 .confirm-btn {
90 - background-color: #A67939; 92 + background-color: #a67939;
91 - color: #FFF; 93 + color: #fff;
92 text-align: center; 94 text-align: center;
93 padding: 26rpx 0; 95 padding: 26rpx 0;
94 border-radius: 16rpx; 96 border-radius: 16rpx;
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
6 * @Description: 线下预约码页面 6 * @Description: 线下预约码页面
7 --> 7 -->
8 <template> 8 <template>
9 - <view class="offline-booking-code-page"></view> 9 + <view class="offline-booking-code-page"></view>
10 </template> 10 </template>
11 11
12 <script setup> 12 <script setup>
...@@ -14,18 +14,17 @@ import { onMounted } from 'vue' ...@@ -14,18 +14,17 @@ import { onMounted } from 'vue'
14 import Taro from '@tarojs/taro' 14 import Taro from '@tarojs/taro'
15 import { useGo } from '@/hooks/useGo' 15 import { useGo } from '@/hooks/useGo'
16 16
17 -const go = useGo(); 17 +const go = useGo()
18 18
19 onMounted(() => { 19 onMounted(() => {
20 - Taro.nextTick(() => { 20 + Taro.nextTick(() => {
21 - go('/pages/offlineBookingList/index') 21 + go('/pages/offlineBookingList/index')
22 - }) 22 + })
23 -}); 23 +})
24 -
25 </script> 24 </script>
26 25
27 <style lang="less"> 26 <style lang="less">
28 .offline-booking-code-page { 27 .offline-booking-code-page {
29 - min-height: 100vh; 28 + min-height: 100vh;
30 } 29 }
31 </style> 30 </style>
......
1 export default { 1 export default {
2 - navigationBarTitleText: '离线预约详情' 2 + navigationBarTitleText: '离线预约详情'
3 } 3 }
4 -
......
1 <template> 1 <template>
2 - <view class="offline-booking-detail-page"> 2 + <view class="offline-booking-detail-page">
3 - <view class="header-tip"> 3 + <view class="header-tip">
4 - <IconFont name="tips" size="15" color="#A67939" />&nbsp; 4 + <IconFont name="tips" size="15" color="#A67939" />&nbsp;
5 - <text>您当前处于离线模式,仅展示本地缓存的数据</text> 5 + <text>您当前处于离线模式,仅展示本地缓存的数据</text>
6 - </view> 6 + </view>
7 +
8 + <view class="content">
9 + <offlineQrCode :list="qr_list" />
7 10
8 - <view class="content"> 11 + <view v-if="bill_info && bill_info.pay_id" class="detail-wrapper">
9 - <offlineQrCode :list="qr_list" /> 12 + <view class="detail-item">
10 - 13 + <view>参访时间:</view>
11 - <view v-if="bill_info && bill_info.pay_id" class="detail-wrapper"> 14 + <view>{{ bill_info.datetime }}</view>
12 - <view class="detail-item"> 15 + </view>
13 - <view>参访时间:</view> 16 + <view class="detail-item">
14 - <view>{{ bill_info.datetime }}</view> 17 + <view>参访人数:</view>
15 - </view> 18 + <view>{{ bill_info.total_qty }} 人</view>
16 - <view class="detail-item"> 19 + </view>
17 - <view>参访人数:</view> 20 + <view class="detail-item">
18 - <view>{{ bill_info.total_qty }} 人</view> 21 + <view>支付金额:</view>
19 - </view> 22 + <view>¥ {{ bill_info.total_amt }}</view>
20 - <view class="detail-item"> 23 + </view>
21 - <view>支付金额:</view> 24 + <view class="detail-item">
22 - <view>¥ {{ bill_info.total_amt }}</view> 25 + <view>下单时间:</view>
23 - </view> 26 + <view>{{ bill_info.order_time }}</view>
24 - <view class="detail-item"> 27 + </view>
25 - <view>下单时间:</view> 28 + <view class="detail-item">
26 - <view>{{ bill_info.order_time }}</view> 29 + <view>订单编号:</view>
27 - </view> 30 + <view>{{ bill_info.pay_id }}</view>
28 - <view class="detail-item"> 31 + </view>
29 - <view>订单编号:</view> 32 + <!-- <view class="detail-item">
30 - <view>{{ bill_info.pay_id }}</view>
31 - </view>
32 - <!-- <view class="detail-item">
33 <view>订单状态:</view> 33 <view>订单状态:</view>
34 <view>{{ status_text }}</view> 34 <view>{{ status_text }}</view>
35 </view> --> 35 </view> -->
36 - </view> 36 + </view>
37 - </view> 37 + </view>
38 38
39 - <view class="action-area"> 39 + <view class="action-area">
40 - <button class="back-btn" @tap="toList">返回列表</button> 40 + <button class="back-btn" @tap="toList">返回列表</button>
41 - </view>
42 </view> 41 </view>
42 + </view>
43 </template> 43 </template>
44 44
45 <script setup> 45 <script setup>
...@@ -47,7 +47,10 @@ import { ref, computed } from 'vue' ...@@ -47,7 +47,10 @@ import { ref, computed } from 'vue'
47 import Taro, { useDidShow, useRouter as useTaroRouter } from '@tarojs/taro' 47 import Taro, { useDidShow, useRouter as useTaroRouter } from '@tarojs/taro'
48 import { IconFont } from '@nutui/icons-vue-taro' 48 import { IconFont } from '@nutui/icons-vue-taro'
49 import offlineQrCode from '@/components/offlineQrCode.vue' 49 import offlineQrCode from '@/components/offlineQrCode.vue'
50 -import { get_offline_booking_by_pay_id, build_offline_qr_list } from '@/composables/useOfflineBookingCache' 50 +import {
51 + get_offline_booking_by_pay_id,
52 + build_offline_qr_list
53 +} from '@/composables/useOfflineBookingCache'
51 54
52 const router = useTaroRouter() 55 const router = useTaroRouter()
53 const bill_info = ref(null) 56 const bill_info = ref(null)
...@@ -78,95 +81,95 @@ const qr_list = ref([]) ...@@ -78,95 +81,95 @@ const qr_list = ref([])
78 // }) 81 // })
79 82
80 const toList = () => { 83 const toList = () => {
81 - Taro.navigateBack({ 84 + Taro.navigateBack({
82 - fail: () => { 85 + fail: () => {
83 - Taro.reLaunch({ url: '/pages/offlineBookingList/index' }) 86 + Taro.reLaunch({ url: '/pages/offlineBookingList/index' })
84 - } 87 + }
85 - }) 88 + })
86 } 89 }
87 90
88 const load_cache = () => { 91 const load_cache = () => {
89 - const pay_id = router.params.pay_id 92 + const pay_id = router.params.pay_id
90 - const data = get_offline_booking_by_pay_id(pay_id) 93 + const data = get_offline_booking_by_pay_id(pay_id)
91 - if (!data) { 94 + if (!data) {
92 - Taro.showToast({ title: '本地无该订单缓存', icon: 'none' }) 95 + Taro.showToast({ title: '本地无该订单缓存', icon: 'none' })
93 - Taro.reLaunch({ url: '/pages/offlineBookingList/index' }) 96 + Taro.reLaunch({ url: '/pages/offlineBookingList/index' })
94 - return 97 + return
95 - } 98 + }
96 - bill_info.value = data 99 + bill_info.value = data
97 - qr_list.value = build_offline_qr_list(data) 100 + qr_list.value = build_offline_qr_list(data)
98 } 101 }
99 102
100 useDidShow(() => { 103 useDidShow(() => {
101 - load_cache() 104 + load_cache()
102 }) 105 })
103 </script> 106 </script>
104 107
105 <style lang="less"> 108 <style lang="less">
106 .offline-booking-detail-page { 109 .offline-booking-detail-page {
107 - min-height: 100vh; 110 + min-height: 100vh;
108 - background-color: #F6F6F6; 111 + background-color: #f6f6f6;
109 - 112 +
110 - .header-tip { 113 + .header-tip {
111 - display: flex; 114 + display: flex;
112 - align-items: center; 115 + align-items: center;
113 - padding: 20rpx 32rpx; 116 + padding: 20rpx 32rpx;
114 - color: #A67939; 117 + color: #a67939;
115 - font-size: 26rpx; 118 + font-size: 26rpx;
116 - background: #FFF; 119 + background: #fff;
120 + }
121 +
122 + .content {
123 + padding: 32rpx;
124 + padding-bottom: 180rpx;
125 + }
126 +
127 + .detail-wrapper {
128 + background-color: #fff;
129 + border-radius: 16rpx;
130 + padding: 32rpx;
131 + margin-top: 32rpx;
132 + box-shadow: 0 0 29rpx 0 rgba(106, 106, 106, 0.1);
133 +
134 + .detail-item {
135 + display: flex;
136 + justify-content: space-between;
137 + margin-bottom: 26rpx;
138 + color: #333;
139 + font-size: 30rpx;
140 +
141 + &:last-child {
142 + margin-bottom: 0;
143 + }
144 +
145 + view:first-child {
146 + color: #999;
147 + width: 160rpx;
148 + }
149 + view:last-child {
150 + flex: 1;
151 + text-align: right;
152 + }
117 } 153 }
118 - 154 + }
119 - .content { 155 +
120 - padding: 32rpx; 156 + .action-area {
121 - padding-bottom: 180rpx; 157 + position: fixed;
122 - } 158 + bottom: 0;
123 - 159 + left: 0;
124 - .detail-wrapper { 160 + width: 750rpx;
125 - background-color: #FFF; 161 + padding: 24rpx 32rpx;
126 - border-radius: 16rpx; 162 + background: #fff;
127 - padding: 32rpx; 163 + box-sizing: border-box;
128 - margin-top: 32rpx; 164 + box-shadow: 0 -10rpx 8rpx 0 rgba(0, 0, 0, 0.06);
129 - box-shadow: 0 0 29rpx 0 rgba(106,106,106,0.1); 165 +
130 - 166 + .back-btn {
131 - .detail-item { 167 + background-color: #a67939;
132 - display: flex; 168 + color: #fff;
133 - justify-content: space-between; 169 + border-radius: 16rpx;
134 - margin-bottom: 26rpx; 170 + font-size: 32rpx;
135 - color: #333; 171 + padding: 12rpx 0;
136 - font-size: 30rpx;
137 -
138 - &:last-child {
139 - margin-bottom: 0;
140 - }
141 -
142 - view:first-child {
143 - color: #999;
144 - width: 160rpx;
145 - }
146 - view:last-child {
147 - flex: 1;
148 - text-align: right;
149 - }
150 - }
151 - }
152 -
153 - .action-area {
154 - position: fixed;
155 - bottom: 0;
156 - left: 0;
157 - width: 750rpx;
158 - padding: 24rpx 32rpx;
159 - background: #FFF;
160 - box-sizing: border-box;
161 - box-shadow: 0 -10rpx 8rpx 0 rgba(0,0,0,0.06);
162 -
163 - .back-btn {
164 - background-color: #A67939;
165 - color: #FFF;
166 - border-radius: 16rpx;
167 - font-size: 32rpx;
168 - padding: 12rpx 0;
169 - }
170 } 172 }
173 + }
171 } 174 }
172 </style> 175 </style>
......
1 export default { 1 export default {
2 - navigationBarTitleText: '离线预约记录' 2 + navigationBarTitleText: '离线预约记录'
3 } 3 }
4 -
......
...@@ -6,26 +6,26 @@ ...@@ -6,26 +6,26 @@
6 * @Description: 离线预约记录页面 6 * @Description: 离线预约记录页面
7 --> 7 -->
8 <template> 8 <template>
9 - <view class="offline-booking-list-page"> 9 + <view class="offline-booking-list-page">
10 - <view class="header-tip"> 10 + <view class="header-tip">
11 - <IconFont name="tips" size="15" color="#A67939" /> &nbsp; 11 + <IconFont name="tips" size="15" color="#A67939" /> &nbsp;
12 - <text>您当前处于离线模式,仅展示本地缓存的预约记录</text> 12 + <text>您当前处于离线模式,仅展示本地缓存的预约记录</text>
13 - </view> 13 + </view>
14 14
15 - <view class="list-wrapper"> 15 + <view class="list-wrapper">
16 - <view v-for="(item, index) in booking_list" :key="index"> 16 + <view v-for="(item, index) in booking_list" :key="index">
17 - <reserveCard :data="item" detail_path="/offlineBookingDetail" is_offline /> 17 + <reserveCard :data="item" detail_path="/offlineBookingDetail" is_offline />
18 - </view> 18 + </view>
19 19
20 - <view v-if="!booking_list.length" class="empty"> 20 + <view v-if="!booking_list.length" class="empty">
21 - <text>本地无缓存预约记录</text> 21 + <text>本地无缓存预约记录</text>
22 - </view> 22 + </view>
23 - </view> 23 + </view>
24 24
25 - <view class="action-area"> 25 + <view class="action-area">
26 - <button class="home-btn" @tap="toHome">返回首页</button> 26 + <button class="home-btn" @tap="toHome">返回首页</button>
27 - </view>
28 </view> 27 </view>
28 + </view>
29 </template> 29 </template>
30 30
31 <script setup> 31 <script setup>
...@@ -38,60 +38,60 @@ import { get_offline_booking_cache } from '@/composables/useOfflineBookingCache' ...@@ -38,60 +38,60 @@ import { get_offline_booking_cache } from '@/composables/useOfflineBookingCache'
38 const booking_list = ref([]) 38 const booking_list = ref([])
39 39
40 const toHome = () => { 40 const toHome = () => {
41 - Taro.reLaunch({ url: '/pages/index/index' }) 41 + Taro.reLaunch({ url: '/pages/index/index' })
42 } 42 }
43 43
44 const load_cache = () => { 44 const load_cache = () => {
45 - booking_list.value = get_offline_booking_cache() 45 + booking_list.value = get_offline_booking_cache()
46 } 46 }
47 47
48 useDidShow(() => { 48 useDidShow(() => {
49 - load_cache() 49 + load_cache()
50 }) 50 })
51 </script> 51 </script>
52 52
53 <style lang="less"> 53 <style lang="less">
54 .offline-booking-list-page { 54 .offline-booking-list-page {
55 - min-height: 100vh; 55 + min-height: 100vh;
56 - background-color: #F6F6F6; 56 + background-color: #f6f6f6;
57 57
58 - .header-tip { 58 + .header-tip {
59 - display: flex; 59 + display: flex;
60 - align-items: center; 60 + align-items: center;
61 - padding: 20rpx 32rpx; 61 + padding: 20rpx 32rpx;
62 - color: #A67939; 62 + color: #a67939;
63 - font-size: 26rpx; 63 + font-size: 26rpx;
64 - background: #FFF; 64 + background: #fff;
65 - } 65 + }
66 66
67 - .list-wrapper { 67 + .list-wrapper {
68 - padding: 32rpx; 68 + padding: 32rpx;
69 - } 69 + }
70 70
71 - .empty { 71 + .empty {
72 - padding: 120rpx 0; 72 + padding: 120rpx 0;
73 - text-align: center; 73 + text-align: center;
74 - color: #A67939; 74 + color: #a67939;
75 - font-size: 32rpx; 75 + font-size: 32rpx;
76 - } 76 + }
77 77
78 - .action-area { 78 + .action-area {
79 - position: fixed; 79 + position: fixed;
80 - bottom: 0; 80 + bottom: 0;
81 - left: 0; 81 + left: 0;
82 - width: 750rpx; 82 + width: 750rpx;
83 - padding: 24rpx 32rpx; 83 + padding: 24rpx 32rpx;
84 - background: #FFF; 84 + background: #fff;
85 - box-sizing: border-box; 85 + box-sizing: border-box;
86 - box-shadow: 0 -10rpx 8rpx 0 rgba(0,0,0,0.06); 86 + box-shadow: 0 -10rpx 8rpx 0 rgba(0, 0, 0, 0.06);
87 87
88 - .home-btn { 88 + .home-btn {
89 - background-color: #A67939; 89 + background-color: #a67939;
90 - color: #FFF; 90 + color: #fff;
91 - border-radius: 16rpx; 91 + border-radius: 16rpx;
92 - font-size: 32rpx; 92 + font-size: 32rpx;
93 - padding: 12rpx 0; 93 + padding: 12rpx 0;
94 - }
95 } 94 }
95 + }
96 } 96 }
97 </style> 97 </style>
......
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
25 placeholder="请输入证件号码" 25 placeholder="请输入证件号码"
26 @blur="checkIdCode" 26 @blur="checkIdCode"
27 :maxlength="id_type === 1 ? 18 : 30" 27 :maxlength="id_type === 1 ? 18 : 30"
28 - > 28 + />
29 </view> 29 </view>
30 <view class="tip-block"> 30 <view class="tip-block">
31 <view class="tip-title"> 31 <view class="tip-title">
...@@ -65,72 +65,78 @@ ...@@ -65,72 +65,78 @@
65 import { ref, computed } from 'vue' 65 import { ref, computed } from 'vue'
66 import Taro from '@tarojs/taro' 66 import Taro from '@tarojs/taro'
67 import { IconFont } from '@nutui/icons-vue-taro' 67 import { IconFont } from '@nutui/icons-vue-taro'
68 -import qrCodeSearch from '@/components/qrCodeSearch'; 68 +import qrCodeSearch from '@/components/qrCodeSearch'
69 import { useGo } from '@/hooks/useGo' 69 import { useGo } from '@/hooks/useGo'
70 70
71 -const go = useGo(); 71 +const go = useGo()
72 -const is_search = ref(false); 72 +const is_search = ref(false)
73 -const idCode = ref(''); 73 +const idCode = ref('')
74 -const id_number = ref(''); 74 +const id_number = ref('')
75 -const show_id_type_picker = ref(false); 75 +const show_id_type_picker = ref(false)
76 const id_type_options = [ 76 const id_type_options = [
77 { label: '身份证', value: 1 }, 77 { label: '身份证', value: 1 },
78 { label: '其他', value: 3 } 78 { label: '其他', value: 3 }
79 -]; 79 +]
80 -const id_type = ref(id_type_options[0].value); 80 +const id_type = ref(id_type_options[0].value)
81 -const id_type_picker_value = ref([String(id_type.value)]); 81 +const id_type_picker_value = ref([String(id_type.value)])
82 82
83 const id_type_columns = computed(() => { 83 const id_type_columns = computed(() => {
84 return id_type_options.map(item => ({ 84 return id_type_options.map(item => ({
85 text: item.label, 85 text: item.label,
86 value: String(item.value) 86 value: String(item.value)
87 - })); 87 + }))
88 -}); 88 +})
89 const id_type_label = computed(() => { 89 const id_type_label = computed(() => {
90 - return id_type_options.find(item => item.value === id_type.value)?.label || id_type_options[0].label; 90 + return (
91 -}); 91 + id_type_options.find(item => item.value === id_type.value)?.label || id_type_options[0].label
92 + )
93 +})
92 94
93 const open_id_type_picker = () => { 95 const open_id_type_picker = () => {
94 - id_type_picker_value.value = [String(id_type.value)]; 96 + id_type_picker_value.value = [String(id_type.value)]
95 - show_id_type_picker.value = true; 97 + show_id_type_picker.value = true
96 } 98 }
97 99
98 const on_id_type_confirm = ({ selectedValue }) => { 100 const on_id_type_confirm = ({ selectedValue }) => {
99 - const value = selectedValue?.[0]; 101 + const value = selectedValue?.[0]
100 - id_type.value = Number(value) || 1; 102 + id_type.value = Number(value) || 1
101 - show_id_type_picker.value = false; 103 + show_id_type_picker.value = false
102 } 104 }
103 105
104 // 简单的身份证校验 106 // 简单的身份证校验
105 -const validateCIN = (id) => { 107 +const validateCIN = id => {
106 - return /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/.test(id); 108 + return /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/.test(id)
107 } 109 }
108 110
109 -const checkIdCode = () => { // 检查身份证号是否为空 111 +const checkIdCode = () => {
110 - if (id_type.value !== 1) return true; 112 + // 检查身份证号是否为空
113 + if (id_type.value !== 1) {
114 + return true
115 + }
111 116
112 - let flag = true; 117 + let flag = true
113 - if (idCode.value.length === 15) { // 15位身份证号码不校验 118 + if (idCode.value.length === 15) {
114 - flag = true; 119 + // 15位身份证号码不校验
120 + flag = true
115 } else { 121 } else {
116 if (!validateCIN(idCode.value)) { 122 if (!validateCIN(idCode.value)) {
117 - Taro.showToast({ title: '请检查身份证号码', icon: 'none' }); 123 + Taro.showToast({ title: '请检查身份证号码', icon: 'none' })
118 - flag = false; 124 + flag = false
119 } 125 }
120 } 126 }
121 - return flag; 127 + return flag
122 } 128 }
123 129
124 const searchBtn = async () => { 130 const searchBtn = async () => {
125 // 查询用户信息 131 // 查询用户信息
126 if (checkIdCode() && idCode.value) { 132 if (checkIdCode() && idCode.value) {
127 - is_search.value = true; 133 + is_search.value = true
128 - id_number.value = idCode.value; 134 + id_number.value = idCode.value
129 idCode.value = '' 135 idCode.value = ''
130 } 136 }
131 } 137 }
132 const goBack = () => { 138 const goBack = () => {
133 - is_search.value = false; 139 + is_search.value = false
134 } 140 }
135 const goToHome = () => { 141 const goToHome = () => {
136 go('/index') 142 go('/index')
...@@ -139,146 +145,146 @@ const goToHome = () => { ...@@ -139,146 +145,146 @@ const goToHome = () => {
139 145
140 <style lang="less"> 146 <style lang="less">
141 .search-page { 147 .search-page {
142 - position: relative; 148 + position: relative;
143 - min-height: 100vh; 149 + min-height: 100vh;
144 - padding: 32rpx; 150 + padding: 32rpx;
145 - padding-bottom: 220rpx; 151 + padding-bottom: 220rpx;
146 - background-image: url('https://cdn.ipadbiz.cn/xys/booking/bg.jpg'); 152 + background-image: url('https://cdn.ipadbiz.cn/xys/booking/bg.jpg');
147 - background-repeat: no-repeat; 153 + background-repeat: no-repeat;
148 - background-position: center; 154 + background-position: center;
149 - background-size: cover; 155 + background-size: cover;
150 156
151 - .input-card { 157 + .input-card {
152 - background-color: #FFF; 158 + background-color: #fff;
153 - border-radius: 16rpx; 159 + border-radius: 16rpx;
154 - border: 2rpx solid rgba(166, 121, 57, 0.45); 160 + border: 2rpx solid rgba(166, 121, 57, 0.45);
155 - margin-bottom: 32rpx; 161 + margin-bottom: 32rpx;
156 - height: 112rpx; 162 + height: 112rpx;
157 - display: flex; 163 + display: flex;
158 - align-items: center; 164 + align-items: center;
159 - padding: 0 32rpx; 165 + padding: 0 32rpx;
160 166
161 - .input-label { 167 + .input-label {
162 - width: 160rpx; 168 + width: 160rpx;
163 - color: #333; 169 + color: #333;
164 - font-size: 30rpx; 170 + font-size: 30rpx;
165 - font-weight: 600; 171 + font-weight: 600;
166 - } 172 + }
167 -
168 - .id-input {
169 - flex: 1;
170 - width: 100%;
171 - text-align: right;
172 - font-size: 30rpx;
173 - color: #333;
174 - }
175 -
176 - .picker-value {
177 - flex: 1;
178 - display: flex;
179 - align-items: center;
180 - justify-content: flex-end;
181 - text-align: right;
182 - font-size: 30rpx;
183 - color: #333;
184 - }
185 173
186 - .picker-arrow { 174 + .id-input {
187 - margin-left: 10rpx; 175 + flex: 1;
188 - color: #BBB; 176 + width: 100%;
189 - font-size: 28rpx; 177 + text-align: right;
190 - } 178 + font-size: 30rpx;
179 + color: #333;
191 } 180 }
192 181
193 - .tip-block { 182 + .picker-value {
194 - margin-top: 64rpx; 183 + flex: 1;
195 - color: #A67939; 184 + display: flex;
196 - text-align: center; 185 + align-items: center;
197 - font-size: 30rpx; 186 + justify-content: flex-end;
187 + text-align: right;
188 + font-size: 30rpx;
189 + color: #333;
190 + }
198 191
199 - .tip-title { 192 + .picker-arrow {
200 - display: flex; 193 + margin-left: 10rpx;
201 - align-items: center; 194 + color: #bbb;
202 - justify-content: center; 195 + font-size: 28rpx;
203 - } 196 + }
197 + }
204 198
205 - .tip-title-text { 199 + .tip-block {
206 - margin-left: 10rpx; 200 + margin-top: 64rpx;
207 - font-weight: 600; 201 + color: #a67939;
208 - } 202 + text-align: center;
203 + font-size: 30rpx;
209 204
210 - .tip-desc { 205 + .tip-title {
211 - margin-top: 16rpx; 206 + display: flex;
212 - font-weight: 600; 207 + align-items: center;
213 - } 208 + justify-content: center;
214 } 209 }
215 210
216 - .footer { 211 + .tip-title-text {
217 - position: fixed; 212 + margin-left: 10rpx;
218 - left: 0; 213 + font-weight: 600;
219 - right: 0;
220 - bottom: 0;
221 - padding: 24rpx 32rpx calc(24rpx + env(safe-area-inset-bottom));
222 } 214 }
223 215
224 - .footer-btn { 216 + .tip-desc {
225 - background-color: #A67939; 217 + margin-top: 16rpx;
226 - color: #FFF; 218 + font-weight: 600;
227 - text-align: center;
228 - height: 96rpx;
229 - display: flex;
230 - align-items: center;
231 - justify-content: center;
232 - border-radius: 12rpx;
233 - font-size: 34rpx;
234 - font-weight: 600;
235 } 219 }
220 + }
236 221
237 - .success-footer { 222 + .footer {
238 - position: fixed; 223 + position: fixed;
239 - left: 0; 224 + left: 0;
240 - right: 0; 225 + right: 0;
241 - bottom: 0; 226 + bottom: 0;
242 - padding: 24rpx 32rpx calc(24rpx + env(safe-area-inset-bottom)); 227 + padding: 24rpx 32rpx calc(24rpx + env(safe-area-inset-bottom));
243 - display: flex; 228 + }
244 - gap: 24rpx;
245 229
246 - .btn-item { 230 + .footer-btn {
247 - flex: 1; 231 + background-color: #a67939;
248 - text-align: center; 232 + color: #fff;
249 - height: 96rpx; 233 + text-align: center;
250 - display: flex; 234 + height: 96rpx;
251 - align-items: center; 235 + display: flex;
252 - justify-content: center; 236 + align-items: center;
253 - border-radius: 12rpx; 237 + justify-content: center;
254 - font-size: 34rpx; 238 + border-radius: 12rpx;
255 - font-weight: 600; 239 + font-size: 34rpx;
256 - } 240 + font-weight: 600;
241 + }
257 242
258 - .btn-left { 243 + .success-footer {
259 - border: 2rpx solid #A67939; 244 + position: fixed;
260 - color: #A67939; 245 + left: 0;
261 - background-color: #FFF; 246 + right: 0;
262 - } 247 + bottom: 0;
248 + padding: 24rpx 32rpx calc(24rpx + env(safe-area-inset-bottom));
249 + display: flex;
250 + gap: 24rpx;
263 251
264 - .btn-right { 252 + .btn-item {
265 - background-color: #A67939; 253 + flex: 1;
266 - color: #FFF; 254 + text-align: center;
267 - } 255 + height: 96rpx;
256 + display: flex;
257 + align-items: center;
258 + justify-content: center;
259 + border-radius: 12rpx;
260 + font-size: 34rpx;
261 + font-weight: 600;
268 } 262 }
269 263
270 - .logo { 264 + .btn-left {
271 - position: absolute; 265 + border: 2rpx solid #a67939;
272 - right: 0; 266 + color: #a67939;
273 - bottom: 240rpx; 267 + background-color: #fff;
274 - height: 400rpx;
275 - width: 150rpx;
276 - background-image: url('https://cdn.ipadbiz.cn/xys/booking/logo.png');
277 - background-repeat: no-repeat;
278 - background-size: contain;
279 - background-position: center;
280 - opacity: 0.5;
281 - pointer-events: none;
282 } 268 }
269 +
270 + .btn-right {
271 + background-color: #a67939;
272 + color: #fff;
273 + }
274 + }
275 +
276 + .logo {
277 + position: absolute;
278 + right: 0;
279 + bottom: 240rpx;
280 + height: 400rpx;
281 + width: 150rpx;
282 + background-image: url('https://cdn.ipadbiz.cn/xys/booking/logo.png');
283 + background-repeat: no-repeat;
284 + background-size: contain;
285 + background-position: center;
286 + opacity: 0.5;
287 + pointer-events: none;
288 + }
283 } 289 }
284 </style> 290 </style>
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
10 <view @tap="goToBooking" class="visit-time"> 10 <view @tap="goToBooking" class="visit-time">
11 <view>参访时间</view> 11 <view>参访时间</view>
12 <view class="flex items-center"> 12 <view class="flex items-center">
13 - <text style="font-size: 30rpx;">{{ date }} {{ time }}</text> 13 + <text style="font-size: 30rpx">{{ date }} {{ time }}</text>
14 <IconFont name="rect-right" class="ml-1" /> 14 <IconFont name="rect-right" class="ml-1" />
15 </view> 15 </view>
16 </view> 16 </view>
...@@ -20,37 +20,53 @@ ...@@ -20,37 +20,53 @@
20 </view> 20 </view>
21 </view> 21 </view>
22 <view v-if="visitorList.length" class="visitors-list"> 22 <view v-if="visitorList.length" class="visitors-list">
23 - <view v-for="(item, index) in visitorList" :key="index" @tap="addVisitor(item)" class="visitor-item"> 23 + <view
24 - <view style="margin-right: 32rpx;"> 24 + v-for="(item, index) in visitorList"
25 - <image v-if="!checked_visitors.includes(item.id)" :src="icon_check1" style="width: 38rpx; height: 38rpx;" /> 25 + :key="index"
26 - <image v-else :src="icon_check2" style="width: 38rpx; height: 38rpx;" /> 26 + @tap="addVisitor(item)"
27 + class="visitor-item"
28 + >
29 + <view style="margin-right: 32rpx">
30 + <image
31 + v-if="!checked_visitors.includes(item.id)"
32 + :src="icon_check1"
33 + style="width: 38rpx; height: 38rpx"
34 + />
35 + <image v-else :src="icon_check2" style="width: 38rpx; height: 38rpx" />
27 </view> 36 </view>
28 <view> 37 <view>
29 - <view style="color: #A67939;">{{ item.name }}</view> 38 + <view style="color: #a67939">{{ item.name }}</view>
30 <view>证件号:{{ formatId(item.id_number) }}</view> 39 <view>证件号:{{ formatId(item.id_number) }}</view>
31 - <view v-if="item.is_reserve === RESERVE_STATUS.ENABLE" style="color: #9C9A9A; font-size: 26rpx;">*已预约过{{ date 40 + <view
32 - }}参观,请不要重复预约</view> 41 + v-if="item.is_reserve === RESERVE_STATUS.ENABLE"
42 + style="color: #9c9a9a; font-size: 26rpx"
43 + >*已预约过{{ date }}参观,请不要重复预约</view
44 + >
33 </view> 45 </view>
34 </view> 46 </view>
35 </view> 47 </view>
36 <view v-else class="no-visitors-list"> 48 <view v-else class="no-visitors-list">
37 - <image src="https://cdn.ipadbiz.cn/xys/booking/%E6%9A%82%E6%97%A0@2x.png" 49 + <image
38 - style="width: 320rpx; height: 320rpx;" /> 50 + src="https://cdn.ipadbiz.cn/xys/booking/%E6%9A%82%E6%97%A0@2x.png"
51 + style="width: 320rpx; height: 320rpx"
52 + />
39 <view class="no-visitors-list-title">您还没有添加过参观者</view> 53 <view class="no-visitors-list-title">您还没有添加过参观者</view>
40 </view> 54 </view>
41 - <view style="height: 160rpx;"></view> 55 + <view style="height: 160rpx"></view>
42 <view class="submit-wrapper"> 56 <view class="submit-wrapper">
43 <view class="control-wrapper"> 57 <view class="control-wrapper">
44 <view class="left"> 58 <view class="left">
45 - <view style="margin-left: 32rpx; display: flex;align-items: center;"> 59 + <view style="margin-left: 32rpx; display: flex; align-items: center">
46 - 订单金额&nbsp;&nbsp;<view style="color: #FF1919;display: inline-block;">¥<view 60 + 订单金额&nbsp;&nbsp;<view style="color: #ff1919; display: inline-block"
47 - style="font-size: 48rpx;display: inline-block;">&nbsp;{{ total }}</view> 61 + >¥<view style="font-size: 48rpx; display: inline-block">&nbsp;{{ total }}</view>
48 </view> 62 </view>
49 </view> 63 </view>
50 </view> 64 </view>
51 <view @tap="submitBtn" class="right">提交订单</view> 65 <view @tap="submitBtn" class="right">提交订单</view>
52 </view> 66 </view>
53 - <view style="font-size: 27rpx;margin-left: 32rpx;color: #FF1919; margin-bottom: 32rpx;">提交后请在10分钟内完成支付</view> 67 + <view style="font-size: 27rpx; margin-left: 32rpx; color: #ff1919; margin-bottom: 32rpx"
68 + >提交后请在10分钟内完成支付</view
69 + >
54 </view> 70 </view>
55 </view> 71 </view>
56 </template> 72 </template>
...@@ -66,16 +82,16 @@ import { personListAPI, addReserveAPI } from '@/api/index' ...@@ -66,16 +82,16 @@ import { personListAPI, addReserveAPI } from '@/api/index'
66 import { wechat_pay } from '@/utils/wechatPay' 82 import { wechat_pay } from '@/utils/wechatPay'
67 import { mask_id_number } from '@/utils/tools' 83 import { mask_id_number } from '@/utils/tools'
68 84
69 -const router = useTaroRouter(); 85 +const router = useTaroRouter()
70 -const go = useGo(); 86 +const go = useGo()
71 -const replace = useReplace(); 87 +const replace = useReplace()
72 88
73 -const visitorList = ref([]); 89 +const visitorList = ref([])
74 -const date = ref(''); 90 +const date = ref('')
75 -const time = ref(''); 91 +const time = ref('')
76 -const price = ref(0); 92 +const price = ref(0)
77 -const period_type = ref(''); 93 +const period_type = ref('')
78 -const formatId = (id) => mask_id_number(id) 94 +const formatId = id => mask_id_number(id)
79 95
80 /** 96 /**
81 * @description 当天预约标记 97 * @description 当天预约标记
...@@ -86,8 +102,8 @@ const RESERVE_STATUS = { ...@@ -86,8 +102,8 @@ const RESERVE_STATUS = {
86 ENABLE: '1' 102 ENABLE: '1'
87 } 103 }
88 104
89 -const checked_visitors = ref([]); 105 +const checked_visitors = ref([])
90 -const is_submitting = ref(false); // 是否正在提交订单 106 +const is_submitting = ref(false) // 是否正在提交订单
91 107
92 /** 108 /**
93 * @description 选择/取消选择参观者 109 * @description 选择/取消选择参观者
...@@ -95,20 +111,21 @@ const is_submitting = ref(false); // 是否正在提交订单 ...@@ -95,20 +111,21 @@ const is_submitting = ref(false); // 是否正在提交订单
95 * @param {Object} item 参观者数据 111 * @param {Object} item 参观者数据
96 * @returns {void} 无返回值 112 * @returns {void} 无返回值
97 */ 113 */
98 -const addVisitor = (item) => { 114 +const addVisitor = item => {
99 - if (item.is_reserve === RESERVE_STATUS.ENABLE) { // 今天已经预约 115 + if (item.is_reserve === RESERVE_STATUS.ENABLE) {
116 + // 今天已经预约
100 Taro.showToast({ title: '已预约过参观,请不要重复预约', icon: 'none' }) 117 Taro.showToast({ title: '已预约过参观,请不要重复预约', icon: 'none' })
101 - return; 118 + return
102 } 119 }
103 if (checked_visitors.value.includes(item.id)) { 120 if (checked_visitors.value.includes(item.id)) {
104 - checked_visitors.value = checked_visitors.value.filter((v) => v !== item.id); 121 + checked_visitors.value = checked_visitors.value.filter(v => v !== item.id)
105 } else { 122 } else {
106 - checked_visitors.value.push(item.id); 123 + checked_visitors.value.push(item.id)
107 } 124 }
108 } 125 }
109 126
110 const total = computed(() => { 127 const total = computed(() => {
111 - return price.value * checked_visitors.value.length; 128 + return price.value * checked_visitors.value.length
112 }) 129 })
113 130
114 /** 131 /**
...@@ -116,7 +133,7 @@ const total = computed(() => { ...@@ -116,7 +133,7 @@ const total = computed(() => {
116 * @returns {void} 无返回值 133 * @returns {void} 无返回值
117 */ 134 */
118 const goToBooking = () => { 135 const goToBooking = () => {
119 - go('/booking'); 136 + go('/booking')
120 } 137 }
121 138
122 /** 139 /**
...@@ -124,13 +141,13 @@ const goToBooking = () => { ...@@ -124,13 +141,13 @@ const goToBooking = () => {
124 * @returns {void} 无返回值 141 * @returns {void} 无返回值
125 */ 142 */
126 const goToVisitor = () => { 143 const goToVisitor = () => {
127 - go('/addVisitor'); 144 + go('/addVisitor')
128 } 145 }
129 146
130 // 待支付订单ID 147 // 待支付订单ID
131 -const pending_pay_id = ref(null); 148 +const pending_pay_id = ref(null)
132 // 待支付订单是否需要支付 149 // 待支付订单是否需要支付
133 -const pending_need_pay = ref(null); 150 +const pending_need_pay = ref(null)
134 151
135 /** 152 /**
136 * @description 刷新参观者列表(并同步“当天已预约”标记) 153 * @description 刷新参观者列表(并同步“当天已预约”标记)
...@@ -139,43 +156,47 @@ const pending_need_pay = ref(null); ...@@ -139,43 +156,47 @@ const pending_need_pay = ref(null);
139 * @returns {Promise<void>} 无返回值 156 * @returns {Promise<void>} 无返回值
140 */ 157 */
141 158
142 -const refreshVisitorList = async (options) => { 159 +const refreshVisitorList = async options => {
143 - if (!date.value || !time.value) return; 160 + if (!date.value || !time.value) {
161 + return
162 + }
144 const res = await personListAPI({ 163 const res = await personListAPI({
145 reserve_date: date.value, 164 reserve_date: date.value,
146 begin_time: time.value.split('-')[0], 165 begin_time: time.value.split('-')[0],
147 end_time: time.value.split('-')[1], 166 end_time: time.value.split('-')[1],
148 period_type: period_type.value 167 period_type: period_type.value
149 - }); 168 + })
150 if (res && res.code) { 169 if (res && res.code) {
151 - visitorList.value = res.data || []; 170 + visitorList.value = res.data || []
152 if (options?.reset_checked) { 171 if (options?.reset_checked) {
153 - checked_visitors.value = []; 172 + checked_visitors.value = []
154 } 173 }
155 } 174 }
156 } 175 }
157 176
158 -let is_showing_pay_modal = false; 177 +let is_showing_pay_modal = false
159 178
160 /** 179 /**
161 * @description 支付未完成弹窗(防并发) 180 * @description 支付未完成弹窗(防并发)
162 * @param {string} content 弹窗内容 181 * @param {string} content 弹窗内容
163 * @returns {Promise<boolean>} true=继续支付,false=离开 182 * @returns {Promise<boolean>} true=继续支付,false=离开
164 */ 183 */
165 -const showPayErrorModal = async (content) => { 184 +const showPayErrorModal = async content => {
166 - if (is_showing_pay_modal) return; 185 + if (is_showing_pay_modal) {
167 - is_showing_pay_modal = true; 186 + return
187 + }
188 + is_showing_pay_modal = true
168 try { 189 try {
169 const res = await Taro.showModal({ 190 const res = await Taro.showModal({
170 title: '提示', 191 title: '提示',
171 content: content || '支付失败,请稍后再试', 192 content: content || '支付失败,请稍后再试',
172 showCancel: true, 193 showCancel: true,
173 cancelText: '离开', 194 cancelText: '离开',
174 - confirmText: '继续支付', 195 + confirmText: '继续支付'
175 - }); 196 + })
176 - return !!res?.confirm; 197 + return !!res?.confirm
177 } finally { 198 } finally {
178 - is_showing_pay_modal = false; 199 + is_showing_pay_modal = false
179 } 200 }
180 } 201 }
181 202
...@@ -186,20 +207,23 @@ const showPayErrorModal = async (content) => { ...@@ -186,20 +207,23 @@ const showPayErrorModal = async (content) => {
186 * @returns {Promise<void>} 无返回值 207 * @returns {Promise<void>} 无返回值
187 */ 208 */
188 const submitBtn = async () => { 209 const submitBtn = async () => {
189 - if (is_submitting.value) return; 210 + if (is_submitting.value) {
211 + return
212 + }
190 if (!checked_visitors.value.length) { 213 if (!checked_visitors.value.length) {
191 Taro.showToast({ title: '请先添加参观者', icon: 'none' }) 214 Taro.showToast({ title: '请先添加参观者', icon: 'none' })
192 - return; 215 + return
193 } 216 }
194 217
195 - is_submitting.value = true; 218 + is_submitting.value = true
196 try { 219 try {
197 - let pay_id = pending_pay_id.value; 220 + let pay_id = pending_pay_id.value
198 - let need_pay = pending_need_pay.value; 221 + let need_pay = pending_need_pay.value
199 222
200 - if (!pay_id) { // TAG: 提交订单, 如果没有待支付订单ID, 则创建一个新的订单 223 + if (!pay_id) {
201 - Taro.showLoading({ title: '提交中...' }); 224 + // TAG: 提交订单, 如果没有待支付订单ID, 则创建一个新的订单
202 - let reserve_res = null; 225 + Taro.showLoading({ title: '提交中...' })
226 + let reserve_res = null
203 try { 227 try {
204 reserve_res = await addReserveAPI({ 228 reserve_res = await addReserveAPI({
205 reserve_date: date.value, 229 reserve_date: date.value,
...@@ -207,60 +231,61 @@ const submitBtn = async () => { ...@@ -207,60 +231,61 @@ const submitBtn = async () => {
207 end_time: time.value.split('-')[1], 231 end_time: time.value.split('-')[1],
208 person_id_list: JSON.stringify(checked_visitors.value), 232 person_id_list: JSON.stringify(checked_visitors.value),
209 period_type: period_type.value 233 period_type: period_type.value
210 - }); 234 + })
211 } finally { 235 } finally {
212 - Taro.hideLoading(); 236 + Taro.hideLoading()
213 } 237 }
214 238
215 if (!reserve_res || reserve_res.code != 1) { 239 if (!reserve_res || reserve_res.code != 1) {
216 - return; 240 + return
217 } 241 }
218 - pay_id = reserve_res.data.pay_id; 242 + pay_id = reserve_res.data.pay_id
219 - pending_pay_id.value = pay_id; 243 + pending_pay_id.value = pay_id
220 - need_pay = reserve_res.data?.need_pay; 244 + need_pay = reserve_res.data?.need_pay
221 - pending_need_pay.value = need_pay; 245 + pending_need_pay.value = need_pay
222 - await refreshVisitorList({ reset_checked: true }); 246 + await refreshVisitorList({ reset_checked: true })
223 } 247 }
224 248
225 // 以接口返回的 need_pay 为准:1=需要支付,0=不需要支付 249 // 以接口返回的 need_pay 为准:1=需要支付,0=不需要支付
226 if (Number(need_pay) === 1 || need_pay === true) { 250 if (Number(need_pay) === 1 || need_pay === true) {
227 // 初始化循环 251 // 初始化循环
228 - let should_continue = true; 252 + let should_continue = true
229 // 循环支付直到支付成功或用户取消支付 253 // 循环支付直到支付成功或用户取消支付
230 while (should_continue) { 254 while (should_continue) {
231 const pay_res = await wechat_pay({ pay_id }) 255 const pay_res = await wechat_pay({ pay_id })
232 if (pay_res && pay_res.code == 1) { 256 if (pay_res && pay_res.code == 1) {
233 - pending_pay_id.value = null; 257 + pending_pay_id.value = null
234 - pending_need_pay.value = null; 258 + pending_need_pay.value = null
235 - go('/success', { pay_id }); 259 + go('/success', { pay_id })
236 return 260 return
237 } 261 }
238 // 刷新参观者列表, 清除已预约标记 262 // 刷新参观者列表, 清除已预约标记
239 - refreshVisitorList({ reset_checked: true }).catch(() => {}); 263 + refreshVisitorList({ reset_checked: true }).catch(() => {})
240 - should_continue = await showPayErrorModal(pay_res?.msg || '支付未完成,可再次点击提交订单继续支付') 264 + should_continue = await showPayErrorModal(
265 + pay_res?.msg || '支付未完成,可再次点击提交订单继续支付'
266 + )
241 } 267 }
242 268
243 replace('/bookingList') 269 replace('/bookingList')
244 - return
245 } else { 270 } else {
246 - pending_pay_id.value = null; 271 + pending_pay_id.value = null
247 - pending_need_pay.value = null; 272 + pending_need_pay.value = null
248 - go('/success', { pay_id }); 273 + go('/success', { pay_id })
249 } 274 }
250 } finally { 275 } finally {
251 - is_submitting.value = false; 276 + is_submitting.value = false
252 } 277 }
253 } 278 }
254 279
255 useDidShow(async () => { 280 useDidShow(async () => {
256 - const params = router.params; 281 + const params = router.params
257 - date.value = params.date || ''; 282 + date.value = params.date || ''
258 - time.value = params.time || ''; 283 + time.value = params.time || ''
259 - price.value = params.price || 0; 284 + price.value = params.price || 0
260 - period_type.value = params.period_type || ''; 285 + period_type.value = params.period_type || ''
261 - 286 +
262 - await refreshVisitorList(); 287 + await refreshVisitorList()
263 -}); 288 +})
264 </script> 289 </script>
265 290
266 <style lang="less"> 291 <style lang="less">
...@@ -269,7 +294,7 @@ useDidShow(async () => { ...@@ -269,7 +294,7 @@ useDidShow(async () => {
269 position: relative; 294 position: relative;
270 295
271 .visit-time { 296 .visit-time {
272 - background-color: #FFF; 297 + background-color: #fff;
273 display: flex; 298 display: flex;
274 align-items: center; 299 align-items: center;
275 justify-content: space-between; 300 justify-content: space-between;
...@@ -278,8 +303,8 @@ useDidShow(async () => { ...@@ -278,8 +303,8 @@ useDidShow(async () => {
278 } 303 }
279 304
280 .add-visitors { 305 .add-visitors {
281 - border: 2rpx dashed #A67939; 306 + border: 2rpx dashed #a67939;
282 - color: #A67939; 307 + color: #a67939;
283 border-radius: 10rpx; 308 border-radius: 10rpx;
284 text-align: center; 309 text-align: center;
285 padding: 21rpx 0; 310 padding: 21rpx 0;
...@@ -289,7 +314,7 @@ useDidShow(async () => { ...@@ -289,7 +314,7 @@ useDidShow(async () => {
289 314
290 .visitors-list { 315 .visitors-list {
291 .visitor-item { 316 .visitor-item {
292 - background-color: #FFF; 317 + background-color: #fff;
293 border-radius: 16rpx; 318 border-radius: 16rpx;
294 padding: 32rpx; 319 padding: 32rpx;
295 margin-bottom: 32rpx; 320 margin-bottom: 32rpx;
...@@ -311,7 +336,7 @@ useDidShow(async () => { ...@@ -311,7 +336,7 @@ useDidShow(async () => {
311 } 336 }
312 337
313 .no-visitors-list-title { 338 .no-visitors-list-title {
314 - color: #A67939; 339 + color: #a67939;
315 font-size: 34rpx; 340 font-size: 34rpx;
316 } 341 }
317 } 342 }
...@@ -322,7 +347,7 @@ useDidShow(async () => { ...@@ -322,7 +347,7 @@ useDidShow(async () => {
322 left: 0; 347 left: 0;
323 width: 750rpx; 348 width: 750rpx;
324 display: flex; 349 display: flex;
325 - background-color: #FFF; 350 + background-color: #fff;
326 // padding: 32rpx; 351 // padding: 32rpx;
327 justify-content: space-between; 352 justify-content: space-between;
328 flex-direction: column; 353 flex-direction: column;
...@@ -342,8 +367,8 @@ useDidShow(async () => { ...@@ -342,8 +367,8 @@ useDidShow(async () => {
342 } 367 }
343 368
344 .right { 369 .right {
345 - background-color: #A67939; 370 + background-color: #a67939;
346 - color: #FFF; 371 + color: #fff;
347 margin: 32rpx; 372 margin: 32rpx;
348 padding: 26rpx 96rpx; 373 padding: 26rpx 96rpx;
349 border-radius: 5px; 374 border-radius: 5px;
......
...@@ -13,14 +13,23 @@ ...@@ -13,14 +13,23 @@
13 <view class="text">预约成功</view> 13 <view class="text">预约成功</view>
14 </view> 14 </view>
15 <view class="appointment-information"> 15 <view class="appointment-information">
16 - <view class="number-of-visitors">参观人数:<text>{{ billInfo?.total_qty }} 人</text></view> 16 + <view class="number-of-visitors"
17 - <view class="visit-time">参访时间:<text>{{ billInfo?.datetime }}</text></view> 17 + >参观人数:<text>{{ billInfo?.total_qty }} 人</text></view
18 - <view class="payment-amount">支付金额:<text>¥ {{ billInfo?.total_amt }}</text></view> 18 + >
19 + <view class="visit-time"
20 + >参访时间:<text>{{ billInfo?.datetime }}</text></view
21 + >
22 + <view class="payment-amount"
23 + >支付金额:<text>¥ {{ billInfo?.total_amt }}</text></view
24 + >
19 </view> 25 </view>
20 <view class="appointment-notice"> 26 <view class="appointment-notice">
21 - <view style="margin-bottom: 8rpx; display: flex; align-items: center; justify-content: center;"><IconFont name="tips" />&nbsp;温馨提示</view> 27 + <view
22 - <view style="font-size: 27rpx;">1. 一人一码,或拿身份证,扫码或识别身份证成功后进入</view> 28 + style="margin-bottom: 8rpx; display: flex; align-items: center; justify-content: center"
23 - <view style="font-size: 27rpx;">2. 若您无法按时参观,请提前在预约记录中取消您的预约</view> 29 + ><IconFont name="tips" />&nbsp;温馨提示</view
30 + >
31 + <view style="font-size: 27rpx">1. 一人一码,或拿身份证,扫码或识别身份证成功后进入</view>
32 + <view style="font-size: 27rpx">2. 若您无法按时参观,请提前在预约记录中取消您的预约</view>
24 </view> 33 </view>
25 </view> 34 </view>
26 <view class="success-btn"> 35 <view class="success-btn">
...@@ -36,27 +45,27 @@ import Taro, { useDidShow, useRouter as useTaroRouter } from '@tarojs/taro' ...@@ -36,27 +45,27 @@ import Taro, { useDidShow, useRouter as useTaroRouter } from '@tarojs/taro'
36 import { IconFont } from '@nutui/icons-vue-taro' 45 import { IconFont } from '@nutui/icons-vue-taro'
37 import { useGo } from '@/hooks/useGo' 46 import { useGo } from '@/hooks/useGo'
38 import { billInfoAPI } from '@/api/index' 47 import { billInfoAPI } from '@/api/index'
39 -import { formatDatetime } from '@/utils/tools'; 48 +import { formatDatetime } from '@/utils/tools'
40 import { refresh_offline_booking_cache } from '@/composables/useOfflineBookingCache' 49 import { refresh_offline_booking_cache } from '@/composables/useOfflineBookingCache'
41 50
42 -const router = useTaroRouter(); 51 +const router = useTaroRouter()
43 -const go = useGo(); 52 +const go = useGo()
44 53
45 const goToHome = () => { 54 const goToHome = () => {
46 go('/index') 55 go('/index')
47 } 56 }
48 const goToDetail = () => { 57 const goToDetail = () => {
49 - go('/bookingDetail', { pay_id: router.params.pay_id }); 58 + go('/bookingDetail', { pay_id: router.params.pay_id })
50 } 59 }
51 60
52 -const billInfo = ref({}); 61 +const billInfo = ref({})
53 62
54 useDidShow(async () => { 63 useDidShow(async () => {
55 // 获取订单详情 64 // 获取订单详情
56 - const { code, data } = await billInfoAPI({ pay_id: router.params.pay_id }); 65 + const { code, data } = await billInfoAPI({ pay_id: router.params.pay_id })
57 if (code) { 66 if (code) {
58 - data.datetime = data && formatDatetime(data); 67 + data.datetime = data && formatDatetime(data)
59 - billInfo.value = data; 68 + billInfo.value = data
60 } 69 }
61 // 刷新离线预约缓存 70 // 刷新离线预约缓存
62 refresh_offline_booking_cache({ force: true }) 71 refresh_offline_booking_cache({ force: true })
...@@ -66,7 +75,7 @@ useDidShow(async () => { ...@@ -66,7 +75,7 @@ useDidShow(async () => {
66 <style lang="less"> 75 <style lang="less">
67 .success-page { 76 .success-page {
68 position: relative; 77 position: relative;
69 - background-color: #FFF; 78 + background-color: #fff;
70 min-height: 100vh; 79 min-height: 100vh;
71 80
72 .text-prompts { 81 .text-prompts {
...@@ -79,61 +88,61 @@ useDidShow(async () => { ...@@ -79,61 +88,61 @@ useDidShow(async () => {
79 width: 60vw; 88 width: 60vw;
80 } 89 }
81 .text { 90 .text {
82 - color: #A67939; 91 + color: #a67939;
83 font-size: 40rpx; 92 font-size: 40rpx;
84 margin-top: 32rpx; 93 margin-top: 32rpx;
85 } 94 }
86 } 95 }
87 .appointment-information { 96 .appointment-information {
88 padding: 64rpx 32rpx; 97 padding: 64rpx 32rpx;
89 - border-bottom: 2rpx dashed #A67939; 98 + border-bottom: 2rpx dashed #a67939;
90 line-height: 2; 99 line-height: 2;
91 .number-of-visitors { 100 .number-of-visitors {
92 text { 101 text {
93 - color: #A67939; 102 + color: #a67939;
94 } 103 }
95 } 104 }
96 .visit-time { 105 .visit-time {
97 text { 106 text {
98 - color: #A67939; 107 + color: #a67939;
99 } 108 }
100 } 109 }
101 .payment-amount { 110 .payment-amount {
102 text { 111 text {
103 - color: #A67939; 112 + color: #a67939;
104 } 113 }
105 } 114 }
106 } 115 }
107 116
108 .appointment-notice { 117 .appointment-notice {
109 - padding: 32rpx; 118 + padding: 32rpx;
110 - color: #666; 119 + color: #666;
111 } 120 }
112 121
113 .success-btn { 122 .success-btn {
114 - position: fixed; 123 + position: fixed;
115 - bottom: 64rpx; 124 + bottom: 64rpx;
116 - width: 750rpx; 125 + width: 750rpx;
117 - display: flex; 126 + display: flex;
118 - justify-content: space-around; 127 + justify-content: space-around;
119 128
120 - .btn-item { 129 + .btn-item {
121 - width: 40%; 130 + width: 40%;
122 - text-align: center; 131 + text-align: center;
123 - padding: 26rpx 0; 132 + padding: 26rpx 0;
124 - border-radius: 16rpx; 133 + border-radius: 16rpx;
125 - font-size: 35rpx; 134 + font-size: 35rpx;
126 - } 135 + }
127 136
128 - .btn-left { 137 + .btn-left {
129 - border: 2rpx solid #A67939; 138 + border: 2rpx solid #a67939;
130 - color: #A67939; 139 + color: #a67939;
131 - } 140 + }
132 141
133 - .btn-right { 142 + .btn-right {
134 - background-color: #A67939; 143 + background-color: #a67939;
135 - color: #FFF; 144 + color: #fff;
136 - } 145 + }
137 } 146 }
138 } 147 }
139 </style> 148 </style>
......
...@@ -6,75 +6,89 @@ ...@@ -6,75 +6,89 @@
6 * @Description: 核销结果页面 6 * @Description: 核销结果页面
7 --> 7 -->
8 <template> 8 <template>
9 - <view class="min-h-screen bg-gray-100 p-4 verify-page"> 9 + <view class="min-h-screen bg-gray-100 p-4 verify-page">
10 - <view class="rounded-2xl bg-white p-4 shadow-sm"> 10 + <view class="rounded-2xl bg-white p-4 shadow-sm">
11 - <view class="flex items-center"> 11 + <view class="flex items-center">
12 - <view class="flex h-14 w-14 items-center justify-center rounded-full bg-amber-50"> 12 + <view class="flex h-14 w-14 items-center justify-center rounded-full bg-amber-50">
13 - <icon :type="status_icon_type" size="30" :color="status_icon_color" /> 13 + <icon :type="status_icon_type" size="30" :color="status_icon_color" />
14 - </view>
15 - <view class="ml-4 flex-1">
16 - <view class="text-lg font-semibold text-gray-900">{{ status_title }}</view>
17 - <!-- <view class="mt-1 text-sm text-gray-500">{{ msg }}</view> -->
18 - </view>
19 - </view>
20 </view> 14 </view>
15 + <view class="ml-4 flex-1">
16 + <view class="text-lg font-semibold text-gray-900">{{ status_title }}</view>
17 + <!-- <view class="mt-1 text-sm text-gray-500">{{ msg }}</view> -->
18 + </view>
19 + </view>
20 + </view>
21 21
22 - <!-- <view class="mt-4 rounded-2xl bg-white p-5 shadow-sm"> 22 + <!-- <view class="mt-4 rounded-2xl bg-white p-5 shadow-sm">
23 <view class="text-xs text-gray-500">核销权限</view> 23 <view class="text-xs text-gray-500">核销权限</view>
24 <view class="mt-2 text-sm font-medium text-gray-900">{{ can_redeem_text }}</view> 24 <view class="mt-2 text-sm font-medium text-gray-900">{{ can_redeem_text }}</view>
25 </view> --> 25 </view> -->
26 26
27 - <view class="mt-4 rounded-2xl bg-white p-5 shadow-sm"> 27 + <view class="mt-4 rounded-2xl bg-white p-5 shadow-sm">
28 - <view class="text-sm text-gray-500 mb-4">核销记录信息</view> 28 + <view class="text-sm text-gray-500 mb-4">核销记录信息</view>
29 - 29 +
30 - <template v-if="verify_info && Object.keys(verify_info).length > 0"> 30 + <template v-if="verify_info && Object.keys(verify_info).length > 0">
31 - <view class="flex justify-between items-center py-3 border-b border-gray-50 border-solid last:border-0"> 31 + <view
32 - <view class="text-gray-500 text-base">姓名</view> 32 + class="flex justify-between items-center py-3 border-b border-gray-50 border-solid last:border-0"
33 - <view class="text-gray-900 text-lg font-medium">{{ verify_info.person_name || '-' }}</view> 33 + >
34 - </view> 34 + <view class="text-gray-500 text-base">姓名</view>
35 - 35 + <view class="text-gray-900 text-lg font-medium">{{
36 - <view class="flex justify-between items-center py-3 border-b border-gray-50 border-solid last:border-0"> 36 + verify_info.person_name || '-'
37 - <view class="text-gray-500 text-base">证件号码</view> 37 + }}</view>
38 - <view class="text-gray-900 text-lg font-medium">{{ formatIdNumber(verify_info.id_number) }}</view> 38 + </view>
39 - </view> 39 +
40 - 40 + <view
41 - <view class="flex justify-between items-center py-3 border-b border-gray-50 border-solid last:border-0"> 41 + class="flex justify-between items-center py-3 border-b border-gray-50 border-solid last:border-0"
42 - <view class="text-gray-500 text-base">状态</view> 42 + >
43 - <view class="text-amber-600 text-lg font-medium">{{ verify_info.status || '-' }}</view> 43 + <view class="text-gray-500 text-base">证件号码</view>
44 - </view> 44 + <view class="text-gray-900 text-lg font-medium">{{
45 - 45 + formatIdNumber(verify_info.id_number)
46 - <view class="flex justify-between items-center py-3 border-b border-gray-50 border-solid last:border-0"> 46 + }}</view>
47 - <view class="text-gray-500 text-base">预约开始</view> 47 + </view>
48 - <view class="text-gray-900 text-lg font-medium">{{ verify_info.begin_time || '-' }}</view> 48 +
49 - </view> 49 + <view
50 - 50 + class="flex justify-between items-center py-3 border-b border-gray-50 border-solid last:border-0"
51 - <view class="flex justify-between items-center py-3 border-b border-gray-50 border-solid last:border-0"> 51 + >
52 - <view class="text-gray-500 text-base">预约结束</view> 52 + <view class="text-gray-500 text-base">状态</view>
53 - <view class="text-gray-900 text-lg font-medium">{{ verify_info.end_time || '-' }}</view> 53 + <view class="text-amber-600 text-lg font-medium">{{ verify_info.status || '-' }}</view>
54 - </view> 54 + </view>
55 - </template> 55 +
56 - <view v-else-if="verify_status === 'fail'" class="py-2"> 56 + <view
57 - <view class="text-gray-500 text-base mb-2">失败原因</view> 57 + class="flex justify-between items-center py-3 border-b border-gray-50 border-solid last:border-0"
58 - <view class="text-red-500 text-lg font-medium break-all">{{ msg }}</view> 58 + >
59 - </view> 59 + <view class="text-gray-500 text-base">预约开始</view>
60 - <!-- <view v-else-if="verify_code" class="mt-2 break-all whitespace-pre-wrap text-lg font-medium text-gray-900">{{ verify_code }}</view> --> 60 + <view class="text-gray-900 text-lg font-medium">{{ verify_info.begin_time || '-' }}</view>
61 - <view v-else class="mt-2 text-base text-gray-400">暂无核销信息</view>
62 </view> 61 </view>
63 62
64 - <view class="verify-footer"> 63 + <view
65 - <nut-button 64 + class="flex justify-between items-center py-3 border-b border-gray-50 border-solid last:border-0"
66 - block 65 + >
67 - color="#A67939" 66 + <view class="text-gray-500 text-base">预约结束</view>
68 - :loading="verify_status === 'verifying'" 67 + <view class="text-gray-900 text-lg font-medium">{{ verify_info.end_time || '-' }}</view>
69 - :disabled="verify_status === 'verifying'"
70 - class="verify-btn"
71 - @tap="start_scan_and_verify"
72 - >
73 - <view class="text-lg">核销</view>
74 - </nut-button>
75 - <view class="mt-4 text-center text-sm text-gray-400">扫描预约码二维码进行核销</view>
76 </view> 68 </view>
69 + </template>
70 + <view v-else-if="verify_status === 'fail'" class="py-2">
71 + <view class="text-gray-500 text-base mb-2">失败原因</view>
72 + <view class="text-red-500 text-lg font-medium break-all">{{ msg }}</view>
73 + </view>
74 + <!-- <view v-else-if="verify_code" class="mt-2 break-all whitespace-pre-wrap text-lg font-medium text-gray-900">{{ verify_code }}</view> -->
75 + <view v-else class="mt-2 text-base text-gray-400">暂无核销信息</view>
77 </view> 76 </view>
77 +
78 + <view class="verify-footer">
79 + <nut-button
80 + block
81 + color="#A67939"
82 + :loading="verify_status === 'verifying'"
83 + :disabled="verify_status === 'verifying'"
84 + class="verify-btn"
85 + @tap="start_scan_and_verify"
86 + >
87 + <view class="text-lg">核销</view>
88 + </nut-button>
89 + <view class="mt-4 text-center text-sm text-gray-400">扫描预约码二维码进行核销</view>
90 + </view>
91 + </view>
78 </template> 92 </template>
79 93
80 <script setup> 94 <script setup>
...@@ -94,25 +108,39 @@ const msg = ref('请点击下方按钮进行核销') ...@@ -94,25 +108,39 @@ const msg = ref('请点击下方按钮进行核销')
94 const store = mainStore() 108 const store = mainStore()
95 const replace = useReplace() 109 const replace = useReplace()
96 110
97 -const formatIdNumber = (id) => mask_id_number(id, { keep_start: 6, keep_end: 4 }) 111 +const formatIdNumber = id => mask_id_number(id, { keep_start: 6, keep_end: 4 })
98 112
99 const status_title = computed(() => { 113 const status_title = computed(() => {
100 - if (verify_status.value === 'verifying') return '核销中' 114 + if (verify_status.value === 'verifying') {
101 - if (verify_status.value === 'success') return '核销成功' 115 + return '核销中'
102 - if (verify_status.value === 'fail') return '核销失败' 116 + }
103 - return '核销' 117 + if (verify_status.value === 'success') {
118 + return '核销成功'
119 + }
120 + if (verify_status.value === 'fail') {
121 + return '核销失败'
122 + }
123 + return '核销'
104 }) 124 })
105 125
106 const status_icon_type = computed(() => { 126 const status_icon_type = computed(() => {
107 - if (verify_status.value === 'verifying') return 'waiting' 127 + if (verify_status.value === 'verifying') {
108 - if (verify_status.value === 'success') return 'success' 128 + return 'waiting'
109 - if (verify_status.value === 'fail') return 'cancel' 129 + }
110 - return 'info' 130 + if (verify_status.value === 'success') {
131 + return 'success'
132 + }
133 + if (verify_status.value === 'fail') {
134 + return 'cancel'
135 + }
136 + return 'info'
111 }) 137 })
112 138
113 const status_icon_color = computed(() => { 139 const status_icon_color = computed(() => {
114 - if (verify_status.value === 'fail') return '#E24A4A' 140 + if (verify_status.value === 'fail') {
115 - return '#A67939' 141 + return '#E24A4A'
142 + }
143 + return '#A67939'
116 }) 144 })
117 145
118 // const can_redeem_text = computed(() => { 146 // const can_redeem_text = computed(() => {
...@@ -126,89 +154,94 @@ const status_icon_color = computed(() => { ...@@ -126,89 +154,94 @@ const status_icon_color = computed(() => {
126 * @return {void} 154 * @return {void}
127 */ 155 */
128 156
129 -const verify_ticket = async (code) => { 157 +const verify_ticket = async code => {
130 - if (!code) return 158 + if (!code) {
131 - if (verify_status.value === 'verifying') return 159 + return
132 - 160 + }
133 - verify_code.value = code 161 + if (verify_status.value === 'verifying') {
134 - verify_status.value = 'verifying' 162 + return
135 - msg.value = '核销中...' 163 + }
136 - 164 +
137 - Taro.showLoading({ title: '核销中...' }) 165 + verify_code.value = code
138 - try { 166 + verify_status.value = 'verifying'
139 - const res = await verifyTicketAPI({ qr_code: code }) 167 + msg.value = '核销中...'
140 - if (res?.code === 1) { 168 +
141 - verify_status.value = 'success' 169 + Taro.showLoading({ title: '核销中...' })
142 - msg.value = res?.msg || '核销成功' 170 + try {
143 - // 保存核销返回的详细信息 171 + const res = await verifyTicketAPI({ qr_code: code })
144 - verify_info.value = res?.data || {} 172 + if (res?.code === 1) {
145 - return 173 + verify_status.value = 'success'
146 - } 174 + msg.value = res?.msg || '核销成功'
147 - verify_status.value = 'fail' 175 + // 保存核销返回的详细信息
148 - msg.value = res?.msg || '核销失败' 176 + verify_info.value = res?.data || {}
149 - verify_info.value = {} 177 + return
150 - } catch (e) {
151 - verify_status.value = 'fail'
152 - msg.value = '核销失败'
153 - verify_info.value = {}
154 - Taro.showToast({ title: msg.value, icon: 'none' })
155 - } finally {
156 - Taro.hideLoading()
157 } 178 }
179 + verify_status.value = 'fail'
180 + msg.value = res?.msg || '核销失败'
181 + verify_info.value = {}
182 + } catch (e) {
183 + verify_status.value = 'fail'
184 + msg.value = '核销失败'
185 + verify_info.value = {}
186 + Taro.showToast({ title: msg.value, icon: 'none' })
187 + } finally {
188 + Taro.hideLoading()
189 + }
158 } 190 }
159 191
160 useDidShow(async () => { 192 useDidShow(async () => {
161 - const permission_res = await checkRedeemPermissionAPI() 193 + const permission_res = await checkRedeemPermissionAPI()
162 - if (permission_res?.code !== 1) { 194 + if (permission_res?.code !== 1) {
163 - replace('volunteerLogin') 195 + replace('volunteerLogin')
164 - return 196 + return
165 - } 197 + }
166 - 198 +
167 - if (permission_res?.data) store.changeUserInfo(permission_res.data) 199 + if (permission_res?.data) {
168 - if (permission_res?.data?.can_redeem !== true) { 200 + store.changeUserInfo(permission_res.data)
169 - replace('volunteerLogin') 201 + }
170 - return 202 + if (permission_res?.data?.can_redeem !== true) {
171 - } 203 + replace('volunteerLogin')
172 - 204 + return
173 - const code = router?.params?.result || '' 205 + }
174 - if (code && verify_code.value !== code) { 206 +
175 - await verify_ticket(code) 207 + const code = router?.params?.result || ''
176 - } 208 + if (code && verify_code.value !== code) {
209 + await verify_ticket(code)
210 + }
177 }) 211 })
178 212
179 const start_scan_and_verify = () => { 213 const start_scan_and_verify = () => {
180 - Taro.scanCode({ 214 + Taro.scanCode({
181 - success: (res) => { 215 + success: res => {
182 - verify_ticket(res?.result || '') 216 + verify_ticket(res?.result || '')
183 - }, 217 + },
184 - fail: () => { 218 + fail: () => {}
185 - } 219 + })
186 - })
187 } 220 }
188 </script> 221 </script>
189 222
190 <style lang="less"> 223 <style lang="less">
191 .verify-page { 224 .verify-page {
192 - padding-bottom: 220rpx; 225 + padding-bottom: 220rpx;
193 } 226 }
194 227
195 .verify-footer { 228 .verify-footer {
196 - position: fixed; 229 + position: fixed;
197 - left: 0; 230 + left: 0;
198 - right: 0; 231 + right: 0;
199 - bottom: 0; 232 + bottom: 0;
200 - width: 750rpx; 233 + width: 750rpx;
201 - padding: 24rpx 32rpx calc(24rpx + env(safe-area-inset-bottom)); 234 + padding: 24rpx 32rpx calc(24rpx + env(safe-area-inset-bottom));
202 - background-color: #FFFFFF; 235 + background-color: #ffffff;
203 - box-sizing: border-box; 236 + box-sizing: border-box;
204 - box-shadow: 0 -10rpx 8rpx 0 rgba(0,0,0,0.08); 237 + box-shadow: 0 -10rpx 8rpx 0 rgba(0, 0, 0, 0.08);
205 } 238 }
206 239
207 .verify-btn { 240 .verify-btn {
208 - font-size: 36rpx; 241 + font-size: 36rpx;
209 - height: 104rpx; 242 + height: 104rpx;
210 - line-height: 104rpx; 243 + line-height: 104rpx;
211 - border-radius: 16rpx; 244 + border-radius: 16rpx;
212 - font-weight: 600; 245 + font-weight: 600;
213 } 246 }
214 </style> 247 </style>
......
...@@ -4,7 +4,14 @@ ...@@ -4,7 +4,14 @@
4 <view class="title"> 4 <view class="title">
5 <view class="text">参观者信息</view> 5 <view class="text">参观者信息</view>
6 </view> 6 </view>
7 - <view @tap="() => { go('/pages/addVisitor/index') }" class="add-visitors"> 7 + <view
8 + @tap="
9 + () => {
10 + go('/pages/addVisitor/index')
11 + }
12 + "
13 + class="add-visitors"
14 + >
8 <view class="add-btn flex items-center justify-center"> 15 <view class="add-btn flex items-center justify-center">
9 <IconFont name="plus" class="mr-1" /> 添加参观者 16 <IconFont name="plus" class="mr-1" /> 添加参观者
10 </view> 17 </view>
...@@ -12,20 +19,26 @@ ...@@ -12,20 +19,26 @@
12 <view v-if="visitorList.length" class="visitors-list"> 19 <view v-if="visitorList.length" class="visitors-list">
13 <view v-for="(item, index) in visitorList" :key="index" class="visitor-item"> 20 <view v-for="(item, index) in visitorList" :key="index" class="visitor-item">
14 <view> 21 <view>
15 - <view style="color: #A67939;">{{ item.name }}</view> 22 + <view style="color: #a67939">{{ item.name }}</view>
16 <view>证件号:{{ formatId(item.id_number) }}</view> 23 <view>证件号:{{ formatId(item.id_number) }}</view>
17 </view> 24 </view>
18 - <view @tap="removeItem(item)" style="margin-left: 32rpx;"> 25 + <view @tap="removeItem(item)" style="margin-left: 32rpx">
19 - <image src="https://cdn.ipadbiz.cn/xys/booking/%E5%88%A0%E9%99%A4@2x.png" style="width: 38rpx; height: 38rpx;" /> 26 + <image
27 + src="https://cdn.ipadbiz.cn/xys/booking/%E5%88%A0%E9%99%A4@2x.png"
28 + style="width: 38rpx; height: 38rpx"
29 + />
20 </view> 30 </view>
21 </view> 31 </view>
22 </view> 32 </view>
23 <view v-else class="no-visitors-list"> 33 <view v-else class="no-visitors-list">
24 - <image src="https://cdn.ipadbiz.cn/xys/booking/%E6%9A%82%E6%97%A0@2x.png" style="width: 320rpx; height: 320rpx;" /> 34 + <image
35 + src="https://cdn.ipadbiz.cn/xys/booking/%E6%9A%82%E6%97%A0@2x.png"
36 + style="width: 320rpx; height: 320rpx"
37 + />
25 <view class="no-visitors-list-title">您还没有添加过参观者</view> 38 <view class="no-visitors-list-title">您还没有添加过参观者</view>
26 </view> 39 </view>
27 </view> 40 </view>
28 - <view style="height: 256rpx;"></view> 41 + <view style="height: 256rpx"></view>
29 <indexNav 42 <indexNav
30 :icons="nav_icons" 43 :icons="nav_icons"
31 active="me" 44 active="me"
...@@ -49,28 +62,37 @@ import icon_4 from '@/assets/images/二维码icon.png' ...@@ -49,28 +62,37 @@ import icon_4 from '@/assets/images/二维码icon.png'
49 import icon_5 from '@/assets/images/我的02@2x.png' 62 import icon_5 from '@/assets/images/我的02@2x.png'
50 import { mask_id_number } from '@/utils/tools' 63 import { mask_id_number } from '@/utils/tools'
51 64
52 -const go = useGo(); 65 +const go = useGo()
53 66
54 -const toCode = () => { // 跳转到预约码 67 +const toCode = () => {
55 - go('/pages/bookingCode/index'); 68 + // 跳转到预约码
69 + go('/pages/bookingCode/index')
56 } 70 }
57 -const toHome = () => { // 跳转到首页 71 +const toHome = () => {
58 - go('/pages/index/index'); 72 + // 跳转到首页
73 + go('/pages/index/index')
59 } 74 }
60 -const toMy = () => { // 跳转到我的 75 +const toMy = () => {
61 - go('/pages/me/index'); 76 + // 跳转到我的
77 + go('/pages/me/index')
62 } 78 }
63 79
64 const nav_icons = { home: icon_3, code: icon_4, me: icon_5 } 80 const nav_icons = { home: icon_3, code: icon_4, me: icon_5 }
65 81
66 -const on_nav_select = (key) => { 82 +const on_nav_select = key => {
67 - if (key === 'home') return toHome() 83 + if (key === 'home') {
68 - if (key === 'code') return toCode() 84 + return toHome()
69 - if (key === 'me') return toMy() 85 + }
86 + if (key === 'code') {
87 + return toCode()
88 + }
89 + if (key === 'me') {
90 + return toMy()
91 + }
70 } 92 }
71 93
72 -const visitorList = ref([]); 94 +const visitorList = ref([])
73 -const formatId = (id) => mask_id_number(id) 95 +const formatId = id => mask_id_number(id)
74 96
75 /** 97 /**
76 * @description 加载参观者列表 98 * @description 加载参观者列表
...@@ -78,13 +100,13 @@ const formatId = (id) => mask_id_number(id) ...@@ -78,13 +100,13 @@ const formatId = (id) => mask_id_number(id)
78 */ 100 */
79 const loadList = async () => { 101 const loadList = async () => {
80 try { 102 try {
81 - const { code, data } = await personListAPI({}); 103 + const { code, data } = await personListAPI({})
82 if (code) { 104 if (code) {
83 - visitorList.value = data || []; 105 + visitorList.value = data || []
84 } 106 }
85 } catch (err) { 107 } catch (err) {
86 - console.error(err); 108 + console.error(err)
87 - Taro.showToast({ title: '加载失败', icon: 'none' }); 109 + Taro.showToast({ title: '加载失败', icon: 'none' })
88 } 110 }
89 } 111 }
90 112
...@@ -93,83 +115,83 @@ const loadList = async () => { ...@@ -93,83 +115,83 @@ const loadList = async () => {
93 * @param {Object} item 参观者对象 115 * @param {Object} item 参观者对象
94 * @returns {Promise<void>} 无返回值 116 * @returns {Promise<void>} 无返回值
95 */ 117 */
96 -const removeItem = async (item) => { 118 +const removeItem = async item => {
97 - const { confirm } = await Taro.showModal({ title: '提示', content: '确定删除该参观者吗?' }); 119 + const { confirm } = await Taro.showModal({ title: '提示', content: '确定删除该参观者吗?' })
98 - if (confirm) { 120 + if (confirm) {
99 - try { 121 + try {
100 - const res = await delPersonAPI({ person_id: item.id }); 122 + const res = await delPersonAPI({ person_id: item.id })
101 - if (res && res.code) { 123 + if (res && res.code) {
102 - Taro.showToast({ title: '删除成功' }); 124 + Taro.showToast({ title: '删除成功' })
103 - loadList(); 125 + loadList()
104 - } 126 + }
105 - } catch (error) { 127 + } catch (error) {
106 - console.error(error); 128 + console.error(error)
107 - Taro.showToast({ title: '删除出错', icon: 'none' }); 129 + Taro.showToast({ title: '删除出错', icon: 'none' })
108 - }
109 } 130 }
131 + }
110 } 132 }
111 133
112 useDidShow(() => { 134 useDidShow(() => {
113 - loadList(); 135 + loadList()
114 }) 136 })
115 </script> 137 </script>
116 138
117 <style lang="less"> 139 <style lang="less">
118 .visitor-list-page { 140 .visitor-list-page {
119 - min-height: 100vh; 141 + min-height: 100vh;
120 - background-color: #F6F6F6; 142 + background-color: #f6f6f6;
121 - padding: 32rpx; 143 + padding: 32rpx;
122 144
123 - .visitor-content { 145 + .visitor-content {
124 - .title { 146 + .title {
125 - .text { 147 + .text {
126 - font-size: 35rpx; 148 + font-size: 35rpx;
127 - font-weight: bold; 149 + font-weight: bold;
128 - margin-bottom: 32rpx; 150 + margin-bottom: 32rpx;
129 - border-left: 6rpx solid #A67939; 151 + border-left: 6rpx solid #a67939;
130 - padding-left: 16rpx; 152 + padding-left: 16rpx;
131 - } 153 + }
132 - } 154 + }
133 155
134 - .add-visitors { 156 + .add-visitors {
135 - border: 2rpx dashed #A67939; 157 + border: 2rpx dashed #a67939;
136 - color: #A67939; 158 + color: #a67939;
137 - border-radius: 10rpx; 159 + border-radius: 10rpx;
138 - text-align: center; 160 + text-align: center;
139 - padding: 21rpx 0; 161 + padding: 21rpx 0;
140 - margin: 32rpx 0; 162 + margin: 32rpx 0;
141 - font-size: 37rpx; 163 + font-size: 37rpx;
142 - .add-btn { 164 + .add-btn {
143 - display: flex; 165 + display: flex;
144 - align-items: center; 166 + align-items: center;
145 - justify-content: center; 167 + justify-content: center;
146 - } 168 + }
147 - } 169 + }
148 170
149 - .visitors-list { 171 + .visitors-list {
150 - .visitor-item { 172 + .visitor-item {
151 - background-color: #FFF; 173 + background-color: #fff;
152 - border-radius: 16rpx; 174 + border-radius: 16rpx;
153 - padding: 32rpx; 175 + padding: 32rpx;
154 - margin-bottom: 32rpx; 176 + margin-bottom: 32rpx;
155 - display: flex; 177 + display: flex;
156 - align-items: center; 178 + align-items: center;
157 - justify-content: space-between; 179 + justify-content: space-between;
158 - } 180 + }
159 - } 181 + }
160 182
161 - .no-visitors-list { 183 + .no-visitors-list {
162 - display: flex; 184 + display: flex;
163 - justify-content: center; 185 + justify-content: center;
164 - align-items: center; 186 + align-items: center;
165 - flex-direction: column; 187 + flex-direction: column;
166 188
167 - .no-visitors-list-title { 189 + .no-visitors-list-title {
168 - color: #A67939; 190 + color: #a67939;
169 - font-size: 34rpx; 191 + font-size: 34rpx;
170 - margin-top: 32rpx; 192 + margin-top: 32rpx;
171 - } 193 + }
172 - }
173 } 194 }
195 + }
174 } 196 }
175 </style> 197 </style>
......
...@@ -17,13 +17,23 @@ ...@@ -17,13 +17,23 @@
17 17
18 <view class="input-group"> 18 <view class="input-group">
19 <text class="label">账号</text> 19 <text class="label">账号</text>
20 - <input v-model="username" placeholder="请输入账号" placeholder-class="input-placeholder" cursorSpacing="40rpx" /> 20 + <input
21 + v-model="username"
22 + placeholder="请输入账号"
23 + placeholder-class="input-placeholder"
24 + cursorSpacing="40rpx"
25 + />
21 </view> 26 </view>
22 27
23 <view class="input-group"> 28 <view class="input-group">
24 <text class="label">密码</text> 29 <text class="label">密码</text>
25 - <input v-model="password" password placeholder="请输入密码" placeholder-class="input-placeholder" 30 + <input
26 - cursorSpacing="40rpx" /> 31 + v-model="password"
32 + password
33 + placeholder="请输入密码"
34 + placeholder-class="input-placeholder"
35 + cursorSpacing="40rpx"
36 + />
27 </view> 37 </view>
28 38
29 <button class="login-btn" @tap="handleLogin">立即登录</button> 39 <button class="login-btn" @tap="handleLogin">立即登录</button>
...@@ -51,10 +61,16 @@ const password = ref('') ...@@ -51,10 +61,16 @@ const password = ref('')
51 const check_permission_and_redirect = async () => { 61 const check_permission_and_redirect = async () => {
52 try { 62 try {
53 const permission_res = await checkRedeemPermissionAPI() 63 const permission_res = await checkRedeemPermissionAPI()
54 - if (permission_res?.code !== 1) return 64 + if (permission_res?.code !== 1) {
55 - if (permission_res?.data) store.changeUserInfo(permission_res.data) 65 + return
56 - if (permission_res?.data?.can_redeem === true) replace('verificationResult') 66 + }
57 - } catch (e) { } 67 + if (permission_res?.data) {
68 + store.changeUserInfo(permission_res.data)
69 + }
70 + if (permission_res?.data?.can_redeem === true) {
71 + replace('verificationResult')
72 + }
73 + } catch (e) {}
58 } 74 }
59 75
60 useDidShow(() => { 76 useDidShow(() => {
...@@ -85,7 +101,9 @@ const handleLogin = async () => { ...@@ -85,7 +101,9 @@ const handleLogin = async () => {
85 return 101 return
86 } 102 }
87 103
88 - if (permission_res?.data) store.changeUserInfo(permission_res.data) 104 + if (permission_res?.data) {
105 + store.changeUserInfo(permission_res.data)
106 + }
89 107
90 if (permission_res?.data?.can_redeem === true) { 108 if (permission_res?.data?.can_redeem === true) {
91 Taro.showToast({ title: permission_res?.msg || login_res?.msg || '登录成功', icon: 'success' }) 109 Taro.showToast({ title: permission_res?.msg || login_res?.msg || '登录成功', icon: 'success' })
...@@ -100,7 +118,7 @@ const handleLogin = async () => { ...@@ -100,7 +118,7 @@ const handleLogin = async () => {
100 <style lang="less"> 118 <style lang="less">
101 .login-page { 119 .login-page {
102 min-height: 100vh; 120 min-height: 100vh;
103 - background-color: #F6F6F6; 121 + background-color: #f6f6f6;
104 display: flex; 122 display: flex;
105 flex-direction: column; 123 flex-direction: column;
106 align-items: center; 124 align-items: center;
...@@ -146,7 +164,7 @@ const handleLogin = async () => { ...@@ -146,7 +164,7 @@ const handleLogin = async () => {
146 } 164 }
147 165
148 .input-group { 166 .input-group {
149 - background-color: #F7F8FA; 167 + background-color: #f7f8fa;
150 border-radius: 12rpx; 168 border-radius: 12rpx;
151 padding: 28rpx 30rpx; 169 padding: 28rpx 30rpx;
152 margin-bottom: 32rpx; 170 margin-bottom: 32rpx;
...@@ -171,13 +189,13 @@ const handleLogin = async () => { ...@@ -171,13 +189,13 @@ const handleLogin = async () => {
171 } 189 }
172 190
173 .input-placeholder { 191 .input-placeholder {
174 - color: #C0C4CC; 192 + color: #c0c4cc;
175 } 193 }
176 } 194 }
177 195
178 .login-btn { 196 .login-btn {
179 margin-top: 80rpx; 197 margin-top: 80rpx;
180 - background: #A67939; 198 + background: #a67939;
181 color: #fff; 199 color: #fff;
182 height: 96rpx; 200 height: 96rpx;
183 line-height: 96rpx; 201 line-height: 96rpx;
......
...@@ -4,9 +4,17 @@ ...@@ -4,9 +4,17 @@
4 <view> 4 <view>
5 <IconFont name="clock" size="80rpx" color="#A67939" /> 5 <IconFont name="clock" size="80rpx" color="#A67939" />
6 </view> 6 </view>
7 - <view style="margin: 32rpx 0;">支付中</view> 7 + <view style="margin: 32rpx 0">支付中</view>
8 <view>{{ current.seconds }} s</view> 8 <view>{{ current.seconds }} s</view>
9 - <view style="margin: 48rpx 0; font-size: 27rpx; color: #A67939; text-align: center; line-height: 2;"> 9 + <view
10 + style="
11 + margin: 48rpx 0;
12 + font-size: 27rpx;
13 + color: #a67939;
14 + text-align: center;
15 + line-height: 2;
16 + "
17 + >
10 温馨提示:{{ pay_msg }}<br /> 18 温馨提示:{{ pay_msg }}<br />
11 </view> 19 </view>
12 </view> 20 </view>
...@@ -56,30 +64,32 @@ const startCountdown = () => { ...@@ -56,30 +64,32 @@ const startCountdown = () => {
56 } 64 }
57 65
58 const checkStatus = async () => { 66 const checkStatus = async () => {
59 - if (!pay_id) return 67 + if (!pay_id) {
68 + return
69 + }
60 try { 70 try {
61 const { code, data } = await billPayStatusAPI({ pay_id }) 71 const { code, data } = await billPayStatusAPI({ pay_id })
62 // TAG:轮询支付回调 72 // TAG:轮询支付回调
63 if (data) { 73 if (data) {
64 - switch (data.status) { 74 + switch (data.status) {
65 - case PAY_STATUS.PAY: 75 + case PAY_STATUS.PAY:
66 - pay_msg.value = '订单待支付' 76 + pay_msg.value = '订单待支付'
67 - break 77 + break
68 - case PAY_STATUS.PAYING: 78 + case PAY_STATUS.PAYING:
69 - pay_msg.value = '订单支付中' 79 + pay_msg.value = '订单支付中'
70 - break 80 + break
71 - case PAY_STATUS.SUCCESS: 81 + case PAY_STATUS.SUCCESS:
72 - // 预约成功页面 82 + // 预约成功页面
73 - // Replace to avoid back button loop 83 + // Replace to avoid back button loop
74 - go(`/pages/success/index?pay_id=${pay_id}`, 'replace') 84 + go(`/pages/success/index?pay_id=${pay_id}`, 'replace')
75 - break 85 + break
76 - case PAY_STATUS.FAIL: 86 + case PAY_STATUS.FAIL:
77 - pay_msg.value = '订单支付失败' 87 + pay_msg.value = '订单支付失败'
78 - break 88 + break
79 - } 89 + }
80 } 90 }
81 } catch (error) { 91 } catch (error) {
82 - console.error('Check status error:', error) 92 + console.error('Check status error:', error)
83 } 93 }
84 } 94 }
85 95
...@@ -96,8 +106,12 @@ onMounted(() => { ...@@ -96,8 +106,12 @@ onMounted(() => {
96 }) 106 })
97 107
98 onUnmounted(() => { 108 onUnmounted(() => {
99 - if(timer) clearInterval(timer) 109 + if (timer) {
100 - if(countdownTimer) clearInterval(countdownTimer) 110 + clearInterval(timer)
111 + }
112 + if (countdownTimer) {
113 + clearInterval(countdownTimer)
114 + }
101 }) 115 })
102 116
103 const goBackBtn = () => { 117 const goBackBtn = () => {
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
16 16
17 <view class="offline-entry" @tap="toOfflineCode"> 17 <view class="offline-entry" @tap="toOfflineCode">
18 <view class="circle-btn"> 18 <view class="circle-btn">
19 - <image :src="icon_invite" style="width: 60rpx; height: 60rpx; margin-bottom: 16rpx;" /> 19 + <image :src="icon_invite" style="width: 60rpx; height: 60rpx; margin-bottom: 16rpx" />
20 <text>预约记录</text> 20 <text>预约记录</text>
21 </view> 21 </view>
22 </view> 22 </view>
...@@ -38,12 +38,14 @@ import { weak_network_text, get_weak_network_modal_no_cache_options } from '@/ut ...@@ -38,12 +38,14 @@ import { weak_network_text, get_weak_network_modal_no_cache_options } from '@/ut
38 38
39 import icon_invite from '@/assets/images/二维码@2x2.png' 39 import icon_invite from '@/assets/images/二维码@2x2.png'
40 40
41 -const go = useGo(); 41 +const go = useGo()
42 const weak_network_title = weak_network_text.title 42 const weak_network_title = weak_network_text.title
43 const weak_network_desc = weak_network_text.offline_page_desc 43 const weak_network_desc = weak_network_text.offline_page_desc
44 44
45 onMounted(async () => { 45 onMounted(async () => {
46 - if (has_offline_booking_cache()) return 46 + if (has_offline_booking_cache()) {
47 + return
48 + }
47 try { 49 try {
48 await Taro.showModal(get_weak_network_modal_no_cache_options()) 50 await Taro.showModal(get_weak_network_modal_no_cache_options())
49 } catch (e) { 51 } catch (e) {
...@@ -53,13 +55,13 @@ onMounted(async () => { ...@@ -53,13 +55,13 @@ onMounted(async () => {
53 }) 55 })
54 56
55 const toOfflineCode = () => { 57 const toOfflineCode = () => {
56 - go('/pages/offlineBookingList/index'); 58 + go('/pages/offlineBookingList/index')
57 } 59 }
58 60
59 const retry = () => { 61 const retry = () => {
60 - // 尝试重新加载当前页或者是返回上一页重试 62 + // 尝试重新加载当前页或者是返回上一页重试
61 - // 这里简单做成返回首页 63 + // 这里简单做成返回首页
62 - Taro.reLaunch({ url: '/pages/index/index' }); 64 + Taro.reLaunch({ url: '/pages/index/index' })
63 } 65 }
64 </script> 66 </script>
65 67
...@@ -79,57 +81,57 @@ const retry = () => { ...@@ -79,57 +81,57 @@ const retry = () => {
79 margin-top: -100rpx; 81 margin-top: -100rpx;
80 82
81 .icon-wrapper { 83 .icon-wrapper {
82 - margin-bottom: 40rpx; 84 + margin-bottom: 40rpx;
83 } 85 }
84 86
85 .title { 87 .title {
86 - font-size: 40rpx; 88 + font-size: 40rpx;
87 - color: #333; 89 + color: #333;
88 - font-weight: bold; 90 + font-weight: bold;
89 - margin-bottom: 20rpx; 91 + margin-bottom: 20rpx;
90 } 92 }
91 93
92 .desc { 94 .desc {
93 - font-size: 28rpx; 95 + font-size: 28rpx;
94 - color: #999; 96 + color: #999;
95 - margin-bottom: 80rpx; 97 + margin-bottom: 80rpx;
96 - text-align: center; 98 + text-align: center;
97 - padding: 0 60rpx; 99 + padding: 0 60rpx;
98 } 100 }
99 101
100 .offline-entry { 102 .offline-entry {
101 - margin-bottom: 60rpx; 103 + margin-bottom: 60rpx;
102 - .circle-btn { 104 + .circle-btn {
103 - width: 240rpx; 105 + width: 240rpx;
104 - height: 240rpx; 106 + height: 240rpx;
105 - border-radius: 50%; 107 + border-radius: 50%;
106 - background: #FFFFFF; 108 + background: #ffffff;
107 - display: flex; 109 + display: flex;
108 - flex-direction: column; 110 + flex-direction: column;
109 - align-items: center; 111 + align-items: center;
110 - justify-content: center; 112 + justify-content: center;
111 - box-shadow: 0 10rpx 30rpx rgba(166, 121, 57, 0.4); 113 + box-shadow: 0 10rpx 30rpx rgba(166, 121, 57, 0.4);
112 - 114 +
113 - text { 115 + text {
114 - color: #A67939; 116 + color: #a67939;
115 - font-size: 32rpx; 117 + font-size: 32rpx;
116 - font-weight: bold; 118 + font-weight: bold;
117 - letter-spacing: 2rpx; 119 + letter-spacing: 2rpx;
118 - }
119 -
120 - &:active {
121 - transform: scale(0.95);
122 - }
123 } 120 }
121 +
122 + &:active {
123 + transform: scale(0.95);
124 + }
125 + }
124 } 126 }
125 127
126 .sub-action { 128 .sub-action {
127 - padding: 20rpx; 129 + padding: 20rpx;
128 - text { 130 + text {
129 - color: #A67939; 131 + color: #a67939;
130 - font-size: 28rpx; 132 + font-size: 28rpx;
131 - text-decoration: underline; 133 + text-decoration: underline;
132 - } 134 + }
133 } 135 }
134 } 136 }
135 } 137 }
......
...@@ -16,8 +16,8 @@ export const useCounterStore = defineStore('counter', { ...@@ -16,8 +16,8 @@ export const useCounterStore = defineStore('counter', {
16 */ 16 */
17 increment() { 17 increment() {
18 this.count++ 18 this.count++
19 - }, 19 + }
20 - }, 20 + }
21 }) 21 })
22 22
23 // 也可以用函数式(类似组件 setup)定义 Store,适合更复杂场景: 23 // 也可以用函数式(类似组件 setup)定义 Store,适合更复杂场景:
......
...@@ -24,7 +24,7 @@ export const hostStore = defineStore('host', { ...@@ -24,7 +24,7 @@ export const hostStore = defineStore('host', {
24 * @param {string} id 主办方 id 24 * @param {string} id 主办方 id
25 * @returns {void} 无返回值 25 * @returns {void} 无返回值
26 */ 26 */
27 - add (id) { 27 + add(id) {
28 this.id = id 28 this.id = id
29 }, 29 },
30 /** 30 /**
...@@ -32,8 +32,8 @@ export const hostStore = defineStore('host', { ...@@ -32,8 +32,8 @@ export const hostStore = defineStore('host', {
32 * @param {string} id join_id 32 * @param {string} id join_id
33 * @returns {void} 无返回值 33 * @returns {void} 无返回值
34 */ 34 */
35 - addJoin (id) { 35 + addJoin(id) {
36 this.join_id = id 36 this.join_id = id
37 - }, 37 + }
38 - }, 38 + }
39 }) 39 })
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
5 * @FilePath: /xyxBooking-weapp/src/stores/main.js 5 * @FilePath: /xyxBooking-weapp/src/stores/main.js
6 * @Description: 文件描述 6 * @Description: 文件描述
7 */ 7 */
8 -import { defineStore } from 'pinia'; 8 +import { defineStore } from 'pinia'
9 9
10 /** 10 /**
11 * @description 全局主状态 11 * @description 全局主状态
...@@ -18,17 +18,17 @@ export const mainStore = defineStore('main', { ...@@ -18,17 +18,17 @@ export const mainStore = defineStore('main', {
18 count: 0, 18 count: 0,
19 auth: false, 19 auth: false,
20 // keepPages: ['default'], // 小程序不支持这种 keep-alive 机制 20 // keepPages: ['default'], // 小程序不支持这种 keep-alive 机制
21 - appUserInfo: null, // 用户信息 21 + appUserInfo: null // 用户信息
22 - }; 22 + }
23 }, 23 },
24 getters: { 24 getters: {
25 /** 25 /**
26 * @description 是否具备义工核销权限 26 * @description 是否具备义工核销权限
27 * @returns {boolean} true=义工,false=非义工 27 * @returns {boolean} true=义工,false=非义工
28 */ 28 */
29 - isVolunteer: (state) => { 29 + isVolunteer: state => {
30 - return !!(state.appUserInfo && (state.appUserInfo.can_redeem === true)); 30 + return !!(state.appUserInfo && state.appUserInfo.can_redeem === true)
31 - }, 31 + }
32 }, 32 },
33 actions: { 33 actions: {
34 /** 34 /**
...@@ -36,8 +36,8 @@ export const mainStore = defineStore('main', { ...@@ -36,8 +36,8 @@ export const mainStore = defineStore('main', {
36 * @param {boolean} state 是否已授权 36 * @param {boolean} state 是否已授权
37 * @returns {void} 无返回值 37 * @returns {void} 无返回值
38 */ 38 */
39 - changeState (state) { 39 + changeState(state) {
40 - this.auth = state; 40 + this.auth = state
41 }, 41 },
42 // setVolunteerStatus(status) { 42 // setVolunteerStatus(status) {
43 // this.isVolunteer = status; 43 // this.isVolunteer = status;
...@@ -55,8 +55,8 @@ export const mainStore = defineStore('main', { ...@@ -55,8 +55,8 @@ export const mainStore = defineStore('main', {
55 * @param {Object|null} info 用户信息对象 55 * @param {Object|null} info 用户信息对象
56 * @returns {void} 无返回值 56 * @returns {void} 无返回值
57 */ 57 */
58 - changeUserInfo (info) { 58 + changeUserInfo(info) {
59 - this.appUserInfo = info; 59 + this.appUserInfo = info
60 } 60 }
61 - }, 61 + }
62 -}); 62 +})
......
...@@ -15,7 +15,7 @@ import { defineStore } from 'pinia' ...@@ -15,7 +15,7 @@ import { defineStore } from 'pinia'
15 export const routerStore = defineStore('router', { 15 export const routerStore = defineStore('router', {
16 state: () => { 16 state: () => {
17 return { 17 return {
18 - url: '', 18 + url: ''
19 } 19 }
20 }, 20 },
21 actions: { 21 actions: {
...@@ -24,15 +24,15 @@ export const routerStore = defineStore('router', { ...@@ -24,15 +24,15 @@ export const routerStore = defineStore('router', {
24 * @param {string} path 页面路径(可带 query) 24 * @param {string} path 页面路径(可带 query)
25 * @returns {void} 无返回值 25 * @returns {void} 无返回值
26 */ 26 */
27 - add (path) { 27 + add(path) {
28 this.url = path 28 this.url = path
29 }, 29 },
30 /** 30 /**
31 * @description 清空回跳路径 31 * @description 清空回跳路径
32 * @returns {void} 无返回值 32 * @returns {void} 无返回值
33 */ 33 */
34 - remove () { 34 + remove() {
35 this.url = '' 35 this.url = ''
36 - }, 36 + }
37 - }, 37 + }
38 }) 38 })
......
...@@ -27,19 +27,21 @@ let navigating_to_auth = false ...@@ -27,19 +27,21 @@ let navigating_to_auth = false
27 * @returns {string} 当前页路径,示例:pages/index/index?a=1;获取失败返回空字符串 27 * @returns {string} 当前页路径,示例:pages/index/index?a=1;获取失败返回空字符串
28 */ 28 */
29 export const getCurrentPageFullPath = () => { 29 export const getCurrentPageFullPath = () => {
30 - const pages = Taro.getCurrentPages() 30 + const pages = Taro.getCurrentPages()
31 - if (!pages || pages.length === 0) return '' 31 + if (!pages || pages.length === 0) {
32 + return ''
33 + }
32 34
33 - const current_page = pages[pages.length - 1] 35 + const current_page = pages[pages.length - 1]
34 - const route = current_page.route 36 + const route = current_page.route
35 - const options = current_page.options || {} 37 + const options = current_page.options || {}
36 38
37 - // 改进:key 也需要编码,避免特殊字符导致 URL 解析错误 39 + // 改进:key 也需要编码,避免特殊字符导致 URL 解析错误
38 - const query_params = Object.keys(options) 40 + const query_params = Object.keys(options)
39 - .map((key) => `${encodeURIComponent(key)}=${encodeURIComponent(options[key])}`) 41 + .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(options[key])}`)
40 - .join('&') 42 + .join('&')
41 43
42 - return query_params ? `${route}?${query_params}` : route 44 + return query_params ? `${route}?${query_params}` : route
43 } 45 }
44 46
45 /** 47 /**
...@@ -47,10 +49,10 @@ export const getCurrentPageFullPath = () => { ...@@ -47,10 +49,10 @@ export const getCurrentPageFullPath = () => {
47 * @param {string} custom_path 自定义路径,不传则取当前页完整路径 49 * @param {string} custom_path 自定义路径,不传则取当前页完整路径
48 * @returns {void} 无返回值 50 * @returns {void} 无返回值
49 */ 51 */
50 -export const saveCurrentPagePath = (custom_path) => { 52 +export const saveCurrentPagePath = custom_path => {
51 - const router = routerStore() 53 + const router = routerStore()
52 - const path = custom_path || getCurrentPageFullPath() 54 + const path = custom_path || getCurrentPageFullPath()
53 - router.add(path) 55 + router.add(path)
54 } 56 }
55 57
56 /** 58 /**
...@@ -58,13 +60,13 @@ export const saveCurrentPagePath = (custom_path) => { ...@@ -58,13 +60,13 @@ export const saveCurrentPagePath = (custom_path) => {
58 * @returns {boolean} true=已存在 sessionid,false=需要授权 60 * @returns {boolean} true=已存在 sessionid,false=需要授权
59 */ 61 */
60 export const hasAuth = () => { 62 export const hasAuth = () => {
61 - try { 63 + try {
62 - const sessionid = Taro.getStorageSync('sessionid') 64 + const sessionid = Taro.getStorageSync('sessionid')
63 - return !!sessionid && sessionid !== '' 65 + return !!sessionid && sessionid !== ''
64 - } catch (error) { 66 + } catch (error) {
65 - console.error('检查授权状态失败:', error) 67 + console.error('检查授权状态失败:', error)
66 - return false 68 + return false
67 - } 69 + }
68 } 70 }
69 71
70 let auth_promise = null 72 let auth_promise = null
...@@ -75,13 +77,17 @@ let auth_promise = null ...@@ -75,13 +77,17 @@ let auth_promise = null
75 * @param {object} response Taro.request 响应对象 77 * @param {object} response Taro.request 响应对象
76 * @returns {string|null} cookie 字符串或 null 78 * @returns {string|null} cookie 字符串或 null
77 */ 79 */
78 -const extractCookie = (response) => { 80 +const extractCookie = response => {
79 - // 小程序端优先从 response.cookies 取 81 + // 小程序端优先从 response.cookies 取
80 - if (response.cookies?.[0]) return response.cookies[0] 82 + if (response.cookies?.[0]) {
81 - // H5 端从 header 取(兼容不同大小写) 83 + return response.cookies[0]
82 - const cookie = response.header?.['Set-Cookie'] || response.header?.['set-cookie'] 84 + }
83 - if (Array.isArray(cookie)) return cookie[0] 85 + // H5 端从 header 取(兼容不同大小写)
84 - return cookie || null 86 + const cookie = response.header?.['Set-Cookie'] || response.header?.['set-cookie']
87 + if (Array.isArray(cookie)) {
88 + return cookie[0]
89 + }
90 + return cookie || null
85 } 91 }
86 92
87 /** 93 /**
...@@ -92,76 +98,78 @@ const extractCookie = (response) => { ...@@ -92,76 +98,78 @@ const extractCookie = (response) => {
92 * @param {boolean} options.show_loading 是否展示 loading,默认 true 98 * @param {boolean} options.show_loading 是否展示 loading,默认 true
93 * @returns {Promise<{code:number,msg?:string,data?:any,cookie?:string}>} 授权结果(会把 cookie 写入 storage 的 sessionid) 99 * @returns {Promise<{code:number,msg?:string,data?:any,cookie?:string}>} 授权结果(会把 cookie 写入 storage 的 sessionid)
94 */ 100 */
95 -export const refreshSession = async (options) => { 101 +export const refreshSession = async options => {
96 - const show_loading = options?.show_loading !== false 102 + const show_loading = options?.show_loading !== false
97 -
98 - // 已有授权进行中时,直接复用同一个 Promise
99 - if (auth_promise) return auth_promise
100 -
101 - auth_promise = (async () => {
102 - try {
103 - if (show_loading) {
104 - Taro.showLoading({
105 - title: '加载中...',
106 - mask: true,
107 - })
108 - }
109 -
110 - // 调用微信登录获取临时 code
111 - const login_result = await new Promise((resolve, reject) => {
112 - Taro.login({
113 - success: resolve,
114 - fail: reject,
115 - })
116 - })
117 -
118 - if (!login_result || !login_result.code) {
119 - throw new Error('获取微信登录code失败')
120 - }
121 -
122 - const request_data = {
123 - code: login_result.code,
124 - }
125 -
126 - // 换取后端会话(服务端通过 Set-Cookie 返回会话信息)
127 - const response = await Taro.request({
128 - url: buildApiUrl('openid_wxapp'),
129 - method: 'POST',
130 - data: request_data,
131 - })
132 -
133 - if (!response?.data || response.data.code !== 1) {
134 - throw new Error(response?.data?.msg || '授权失败')
135 - }
136 -
137 - // 改进:使用 extractCookie 函数统一处理 cookie 提取逻辑
138 - const cookie = extractCookie(response)
139 - if (!cookie) {
140 - throw new Error('授权失败:没有获取到有效的会话信息')
141 - }
142 -
143 - // NOTE: 写入本地缓存:后续请求会从缓存取 sessionid 并带到请求头
144 - Taro.setStorageSync('sessionid', cookie)
145 -
146 - /**
147 - * refreshSession() 的返回值当前没有任何业务消费点:在 request.js 里只是 await refreshSession() ,不解构、不使用;其他地方也没直接调用它
148 - * 所以 return { ...response.data, cookie } 目前属于“严谨保留”:方便未来需要拿 cookie / code / msg 做埋点、提示、分支处理时直接用(例如授权页显示更细错误、统计刷新成功率等)。
149 - */
150 -
151 - return {
152 - ...response.data,
153 - cookie,
154 - }
155 - } finally {
156 - if (show_loading) {
157 - Taro.hideLoading()
158 - }
159 - }
160 - })().finally(() => {
161 - auth_promise = null
162 - })
163 103
104 + // 已有授权进行中时,直接复用同一个 Promise
105 + if (auth_promise) {
164 return auth_promise 106 return auth_promise
107 + }
108 +
109 + auth_promise = (async () => {
110 + try {
111 + if (show_loading) {
112 + Taro.showLoading({
113 + title: '加载中...',
114 + mask: true
115 + })
116 + }
117 +
118 + // 调用微信登录获取临时 code
119 + const login_result = await new Promise((resolve, reject) => {
120 + Taro.login({
121 + success: resolve,
122 + fail: reject
123 + })
124 + })
125 +
126 + if (!login_result || !login_result.code) {
127 + throw new Error('获取微信登录code失败')
128 + }
129 +
130 + const request_data = {
131 + code: login_result.code
132 + }
133 +
134 + // 换取后端会话(服务端通过 Set-Cookie 返回会话信息)
135 + const response = await Taro.request({
136 + url: buildApiUrl('openid_wxapp'),
137 + method: 'POST',
138 + data: request_data
139 + })
140 +
141 + if (!response?.data || response.data.code !== 1) {
142 + throw new Error(response?.data?.msg || '授权失败')
143 + }
144 +
145 + // 改进:使用 extractCookie 函数统一处理 cookie 提取逻辑
146 + const cookie = extractCookie(response)
147 + if (!cookie) {
148 + throw new Error('授权失败:没有获取到有效的会话信息')
149 + }
150 +
151 + // NOTE: 写入本地缓存:后续请求会从缓存取 sessionid 并带到请求头
152 + Taro.setStorageSync('sessionid', cookie)
153 +
154 + /**
155 + * refreshSession() 的返回值当前没有任何业务消费点:在 request.js 里只是 await refreshSession() ,不解构、不使用;其他地方也没直接调用它
156 + * 所以 return { ...response.data, cookie } 目前属于“严谨保留”:方便未来需要拿 cookie / code / msg 做埋点、提示、分支处理时直接用(例如授权页显示更细错误、统计刷新成功率等)。
157 + */
158 +
159 + return {
160 + ...response.data,
161 + cookie
162 + }
163 + } finally {
164 + if (show_loading) {
165 + Taro.hideLoading()
166 + }
167 + }
168 + })().finally(() => {
169 + auth_promise = null
170 + })
171 +
172 + return auth_promise
165 } 173 }
166 174
167 /** 175 /**
...@@ -171,14 +179,14 @@ export const refreshSession = async (options) => { ...@@ -171,14 +179,14 @@ export const refreshSession = async (options) => {
171 * 179 *
172 * 改进:使用下划线前缀表示私有函数,仅供 silentAuth 内部使用 180 * 改进:使用下划线前缀表示私有函数,仅供 silentAuth 内部使用
173 */ 181 */
174 -const _do_silent_auth = async (show_loading) => { 182 +const _do_silent_auth = async show_loading => {
175 - // 已有 sessionid 时直接视为已授权 183 + // 已有 sessionid 时直接视为已授权
176 - if (hasAuth()) { 184 + if (hasAuth()) {
177 - return { code: 1, msg: '已授权' } 185 + return { code: 1, msg: '已授权' }
178 - } 186 + }
179 - 187 +
180 - // 需要授权时,走刷新会话逻辑 188 + // 需要授权时,走刷新会话逻辑
181 - return await refreshSession({ show_loading }) 189 + return await refreshSession({ show_loading })
182 } 190 }
183 191
184 /** 192 /**
...@@ -191,48 +199,51 @@ const _do_silent_auth = async (show_loading) => { ...@@ -191,48 +199,51 @@ const _do_silent_auth = async (show_loading) => {
191 * @returns {Promise<any>} 授权结果(成功 resolve,失败 reject) 199 * @returns {Promise<any>} 授权结果(成功 resolve,失败 reject)
192 */ 200 */
193 export const silentAuth = async (on_success, on_error, options) => { 201 export const silentAuth = async (on_success, on_error, options) => {
194 - const show_loading = options?.show_loading !== false 202 + const show_loading = options?.show_loading !== false
203 +
204 + try {
205 + // 未有授权进行中时才发起一次授权,并复用 Promise
206 + if (!auth_promise) {
207 + /**
208 + * 用 auth_promise 做"单例锁",把同一时刻并发触发的多次授权合并成一次。
209 + * 把正在执行的授权 Promise 存起来;后面如果又有人调用 silentAuth() ,
210 + * 看到 auth_promise 不为空,就直接 await 同一个 Promise,避免同时发起多次 Taro.login / 换会话请求
211 + * ---------------------------------------------------------------------------------------
212 + * .finally(() => { auth_promise = null }) 不管授权成功还是失败(resolve/reject),都把"锁"释放掉。
213 + * 不用 finally 的问题:如果授权失败抛错了,而你只在 .then 里清空,那么 auth_promise 会一直卡着旧的 rejected Promise;
214 + * 后续再调用 silentAuth() 会复用这个失败的 Promise,导致永远失败、且永远不会重新发起授权。
215 + * 用 finally :保证成功/失败都会清空,下一次调用才有机会重新走授权流程。
216 + */
217 + auth_promise = _do_silent_auth(show_loading).finally(() => {
218 + auth_promise = null
219 + })
220 + }
221 + const result = await auth_promise
222 + if (on_success) {
223 + on_success(result)
224 + }
195 225
196 - try { 226 + /**
197 - // 未有授权进行中时才发起一次授权,并复用 Promise 227 + * 当前返回值 没有实际消费点 :全项目只在 3 处调用,全部都 不使用返回值 。
198 - if (!auth_promise) { 228 + * - 启动预加载: await silentAuth() 仅等待,不用结果, app.js
199 - /** 229 + * - 授权页: silentAuth().then(() => returnToOriginalPage()) then 里也没接 res , auth/index.vue
200 - * 用 auth_promise 做"单例锁",把同一时刻并发触发的多次授权合并成一次。 230 + * - 分享场景: await silentAuth(successCb, errorCb) 只看成功/失败分支,不用返回值, handleSharePageAuth
201 - * 把正在执行的授权 Promise 存起来;后面如果又有人调用 silentAuth() , 231 + * 所以这行 return result 的作用目前是 语义完整 + 未来扩展位 :
202 - * 看到 auth_promise 不为空,就直接 await 同一个 Promise,避免同时发起多次 Taro.login / 换会话请求 232 + * 如果以后要在调用处根据 code/msg/cookie 做分支或埋点,返回值就能直接用;现在等价于"只用 resolve/reject 表达成功失败"。
203 - * --------------------------------------------------------------------------------------- 233 + */
204 - * .finally(() => { auth_promise = null }) 不管授权成功还是失败(resolve/reject),都把"锁"释放掉。 234 +
205 - * 不用 finally 的问题:如果授权失败抛错了,而你只在 .then 里清空,那么 auth_promise 会一直卡着旧的 rejected Promise; 235 + return result
206 - * 后续再调用 silentAuth() 会复用这个失败的 Promise,导致永远失败、且永远不会重新发起授权。 236 + } catch (error) {
207 - * 用 finally :保证成功/失败都会清空,下一次调用才有机会重新走授权流程。 237 + // 改进:统一传递完整错误对象,包含 message 和 original error
208 - */ 238 + const error_obj = {
209 - auth_promise = _do_silent_auth(show_loading) 239 + message: error?.message || '授权失败,请稍后重试',
210 - .finally(() => { 240 + original: error
211 - auth_promise = null
212 - })
213 - }
214 - const result = await auth_promise
215 - if (on_success) on_success(result)
216 -
217 - /**
218 - * 当前返回值 没有实际消费点 :全项目只在 3 处调用,全部都 不使用返回值 。
219 - * - 启动预加载: await silentAuth() 仅等待,不用结果, app.js
220 - * - 授权页: silentAuth().then(() => returnToOriginalPage()) then 里也没接 res , auth/index.vue
221 - * - 分享场景: await silentAuth(successCb, errorCb) 只看成功/失败分支,不用返回值, handleSharePageAuth
222 - * 所以这行 return result 的作用目前是 语义完整 + 未来扩展位 :
223 - * 如果以后要在调用处根据 code/msg/cookie 做分支或埋点,返回值就能直接用;现在等价于"只用 resolve/reject 表达成功失败"。
224 - */
225 -
226 - return result
227 - } catch (error) {
228 - // 改进:统一传递完整错误对象,包含 message 和 original error
229 - const error_obj = {
230 - message: error?.message || '授权失败,请稍后重试',
231 - original: error
232 - }
233 - if (on_error) on_error(error_obj)
234 - throw error
235 } 241 }
242 + if (on_error) {
243 + on_error(error_obj)
244 + }
245 + throw error
246 + }
236 } 247 }
237 248
238 /** 249 /**
...@@ -253,39 +264,43 @@ const NAVIGATING_RESET_DELAY_MS = 300 ...@@ -253,39 +264,43 @@ const NAVIGATING_RESET_DELAY_MS = 300
253 * @param {string} return_path 指定回跳路径(可选) 264 * @param {string} return_path 指定回跳路径(可选)
254 * @returns {Promise<void>} 无返回值 265 * @returns {Promise<void>} 无返回值
255 */ 266 */
256 -export const navigateToAuth = async (return_path) => { 267 +export const navigateToAuth = async return_path => {
257 - const pages = Taro.getCurrentPages() 268 + const pages = Taro.getCurrentPages()
258 - const current_page = pages[pages.length - 1] 269 + const current_page = pages[pages.length - 1]
259 - const current_route = current_page?.route 270 + const current_route = current_page?.route
260 - if (current_route === 'pages/auth/index') { 271 + if (current_route === 'pages/auth/index') {
261 - return 272 + return
262 - } 273 + }
263 - 274 +
264 - const now = Date.now() 275 + const now = Date.now()
265 - if (navigating_to_auth) return 276 + if (navigating_to_auth) {
266 - if (now - last_navigate_auth_at < NAVIGATE_AUTH_COOLDOWN_MS) return 277 + return
267 - 278 + }
268 - last_navigate_auth_at = now 279 + if (now - last_navigate_auth_at < NAVIGATE_AUTH_COOLDOWN_MS) {
269 - navigating_to_auth = true 280 + return
270 - 281 + }
271 - if (return_path) { 282 +
272 - saveCurrentPagePath(return_path) 283 + last_navigate_auth_at = now
273 - } else { 284 + navigating_to_auth = true
274 - saveCurrentPagePath() 285 +
275 - } 286 + if (return_path) {
276 - 287 + saveCurrentPagePath(return_path)
277 - // 改进:使用 try-finally 明确状态恢复逻辑,确保无论成功失败都会重置状态 288 + } else {
278 - try { 289 + saveCurrentPagePath()
279 - await Taro.navigateTo({ url: '/pages/auth/index' }) 290 + }
280 - } catch (error) { 291 +
281 - // 改进:添加错误日志,方便追踪降级场景 292 + // 改进:使用 try-finally 明确状态恢复逻辑,确保无论成功失败都会重置状态
282 - console.warn('navigateTo 失败,降级使用 redirectTo:', error) 293 + try {
283 - await Taro.redirectTo({ url: '/pages/auth/index' }) 294 + await Taro.navigateTo({ url: '/pages/auth/index' })
284 - } finally { 295 + } catch (error) {
285 - setTimeout(() => { 296 + // 改进:添加错误日志,方便追踪降级场景
286 - navigating_to_auth = false 297 + console.warn('navigateTo 失败,降级使用 redirectTo:', error)
287 - }, NAVIGATING_RESET_DELAY_MS) 298 + await Taro.redirectTo({ url: '/pages/auth/index' })
288 - } 299 + } finally {
300 + setTimeout(() => {
301 + navigating_to_auth = false
302 + }, NAVIGATING_RESET_DELAY_MS)
303 + }
289 } 304 }
290 305
291 /** 306 /**
...@@ -296,42 +311,42 @@ export const navigateToAuth = async (return_path) => { ...@@ -296,42 +311,42 @@ export const navigateToAuth = async (return_path) => {
296 * @returns {Promise<void>} 回跳完成 311 * @returns {Promise<void>} 回跳完成
297 */ 312 */
298 export const returnToOriginalPage = async (default_path = '/pages/index/index') => { 313 export const returnToOriginalPage = async (default_path = '/pages/index/index') => {
299 - const router = routerStore() 314 + const router = routerStore()
300 - const saved_path = router.url 315 + const saved_path = router.url
301 316
302 - try { 317 + try {
303 - router.remove() 318 + router.remove()
304 319
305 - const pages = Taro.getCurrentPages() 320 + const pages = Taro.getCurrentPages()
306 - const current_page = pages[pages.length - 1] 321 + const current_page = pages[pages.length - 1]
307 - const current_route = current_page?.route 322 + const current_route = current_page?.route
308 323
309 - let target_path = default_path 324 + let target_path = default_path
310 - if (saved_path && saved_path !== '') { 325 + if (saved_path && saved_path !== '') {
311 - target_path = saved_path.startsWith('/') ? saved_path : `/${saved_path}` 326 + target_path = saved_path.startsWith('/') ? saved_path : `/${saved_path}`
312 - } 327 + }
313 328
314 - const target_route = target_path.split('?')[0].replace(/^\//, '') 329 + const target_route = target_path.split('?')[0].replace(/^\//, '')
315 330
316 - if (current_route === target_route) { 331 + if (current_route === target_route) {
317 - return 332 + return
318 - } 333 + }
319 334
320 - try { 335 + try {
321 - await Taro.redirectTo({ url: target_path }) 336 + await Taro.redirectTo({ url: target_path })
322 - } catch (error) {
323 - // 改进:添加错误日志,方便追踪降级场景
324 - console.warn('redirectTo 失败,降级使用 reLaunch:', error)
325 - await Taro.reLaunch({ url: target_path })
326 - }
327 } catch (error) { 337 } catch (error) {
328 - console.error('returnToOriginalPage 执行出错:', error) 338 + // 改进:添加错误日志,方便追踪降级场景
329 - try { 339 + console.warn('redirectTo 失败,降级使用 reLaunch:', error)
330 - await Taro.reLaunch({ url: default_path }) 340 + await Taro.reLaunch({ url: target_path })
331 - } catch (final_error) {
332 - console.error('最终降级方案也失败了:', final_error)
333 - }
334 } 341 }
342 + } catch (error) {
343 + console.error('returnToOriginalPage 执行出错:', error)
344 + try {
345 + await Taro.reLaunch({ url: default_path })
346 + } catch (final_error) {
347 + console.error('最终降级方案也失败了:', final_error)
348 + }
349 + }
335 } 350 }
336 351
337 /** 352 /**
...@@ -339,8 +354,8 @@ export const returnToOriginalPage = async (default_path = '/pages/index/index') ...@@ -339,8 +354,8 @@ export const returnToOriginalPage = async (default_path = '/pages/index/index')
339 * @param {object} options 页面 options 354 * @param {object} options 页面 options
340 * @returns {boolean} true=来自分享场景,false=非分享场景 355 * @returns {boolean} true=来自分享场景,false=非分享场景
341 */ 356 */
342 -export const isFromShare = (options) => { 357 +export const isFromShare = options => {
343 - return options && (options.from_share === '1' || options.scene) 358 + return options && (options.from_share === '1' || options.scene)
344 } 359 }
345 360
346 /** 361 /**
...@@ -352,29 +367,33 @@ export const isFromShare = (options) => { ...@@ -352,29 +367,33 @@ export const isFromShare = (options) => {
352 * @returns {Promise<boolean>} true=授权已完成/无需授权,false=授权失败 367 * @returns {Promise<boolean>} true=授权已完成/无需授权,false=授权失败
353 */ 368 */
354 export const handleSharePageAuth = async (options, callback) => { 369 export const handleSharePageAuth = async (options, callback) => {
355 - if (hasAuth()) { 370 + if (hasAuth()) {
356 - if (typeof callback === 'function') callback() 371 + if (typeof callback === 'function') {
357 - return true 372 + callback()
358 } 373 }
359 - 374 + return true
360 - if (isFromShare(options)) { 375 + }
361 - saveCurrentPagePath() 376 +
362 - } 377 + if (isFromShare(options)) {
363 - 378 + saveCurrentPagePath()
364 - try { 379 + }
365 - await silentAuth( 380 +
366 - () => { 381 + try {
367 - if (typeof callback === 'function') callback() 382 + await silentAuth(
368 - }, 383 + () => {
369 - () => { 384 + if (typeof callback === 'function') {
370 - navigateToAuth() 385 + callback()
371 - } 386 + }
372 - ) 387 + },
373 - return true 388 + () => {
374 - } catch (error) {
375 navigateToAuth() 389 navigateToAuth()
376 - return false 390 + }
377 - } 391 + )
392 + return true
393 + } catch (error) {
394 + navigateToAuth()
395 + return false
396 + }
378 } 397 }
379 398
380 /** 399 /**
...@@ -382,7 +401,7 @@ export const handleSharePageAuth = async (options, callback) => { ...@@ -382,7 +401,7 @@ export const handleSharePageAuth = async (options, callback) => {
382 * @param {string} path 原路径 401 * @param {string} path 原路径
383 * @returns {string} 追加后的路径 402 * @returns {string} 追加后的路径
384 */ 403 */
385 -export const addShareFlag = (path) => { 404 +export const addShareFlag = path => {
386 - const separator = path.includes('?') ? '&' : '?' 405 + const separator = path.includes('?') ? '&' : '?'
387 - return `${path}${separator}from_share=1` 406 + return `${path}${separator}from_share=1`
388 } 407 }
......
...@@ -14,21 +14,22 @@ ...@@ -14,21 +14,22 @@
14 * - 线上/测试环境按需切换 14 * - 线上/测试环境按需切换
15 * @type {string} 15 * @type {string}
16 */ 16 */
17 -const BASE_URL = process.env.NODE_ENV === 'production' 17 +const BASE_URL =
18 - // ? 'https://oa.onwall.cn' 18 + process.env.NODE_ENV === 'production'
19 - ? 'https://oa-dev.onwall.cn' 19 + ? // ? 'https://oa.onwall.cn'
20 - // ?'https://oa.jcedu.org' 20 + 'https://oa-dev.onwall.cn'
21 - : 'https://oa-dev.onwall.cn' 21 + : // ?'https://oa.jcedu.org'
22 - // : 'https://oa.jcedu.org' 22 + 'https://oa-dev.onwall.cn'
23 -; 23 +// : 'https://oa.jcedu.org'
24 +
24 /** 25 /**
25 * 接口默认公共参数(避免在多个文件里硬编码) 26 * 接口默认公共参数(避免在多个文件里硬编码)
26 * - f:业务模块标识 27 * - f:业务模块标识
27 * - client_name:客户端标识(后端用于识别来源) 28 * - client_name:客户端标识(后端用于识别来源)
28 */ 29 */
29 export const REQUEST_DEFAULT_PARAMS = { 30 export const REQUEST_DEFAULT_PARAMS = {
30 - f: 'reserve', 31 + f: 'reserve',
31 - client_name: '智慧西园寺', 32 + client_name: '智慧西园寺'
32 } 33 }
33 34
34 export default BASE_URL 35 export default BASE_URL
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
5 * @FilePath: /xyxBooking-weapp/src/utils/mixin.js 5 * @FilePath: /xyxBooking-weapp/src/utils/mixin.js
6 * @Description: 全局 mixin(兼容保留) 6 * @Description: 全局 mixin(兼容保留)
7 */ 7 */
8 -import { getSessionId, setSessionId, clearSessionId } from './request'; 8 +import { getSessionId, setSessionId, clearSessionId } from './request'
9 9
10 /** 10 /**
11 * @description 全局 mixin(兼容保留) 11 * @description 全局 mixin(兼容保留)
...@@ -16,12 +16,12 @@ import { getSessionId, setSessionId, clearSessionId } from './request'; ...@@ -16,12 +16,12 @@ import { getSessionId, setSessionId, clearSessionId } from './request';
16 export default { 16 export default {
17 // 初始化入口(如需全局混入逻辑可写在这里) 17 // 初始化入口(如需全局混入逻辑可写在这里)
18 init: { 18 init: {
19 - created () { 19 + created() {
20 // 说明:sessionid 现在由 request.js 的拦截器自动管理 20 // 说明:sessionid 现在由 request.js 的拦截器自动管理
21 // 如需在组件创建时做通用初始化,可在此补充 21 // 如需在组件创建时做通用初始化,可在此补充
22 } 22 }
23 } 23 }
24 -}; 24 +}
25 25
26 /** 26 /**
27 * @description 导出 sessionid 管理工具(供极端场景手动处理) 27 * @description 导出 sessionid 管理工具(供极端场景手动处理)
......
...@@ -12,8 +12,8 @@ import Taro from '@tarojs/taro' ...@@ -12,8 +12,8 @@ import Taro from '@tarojs/taro'
12 * @param {string} network_type - 网络类型 12 * @param {string} network_type - 网络类型
13 * @returns {boolean} 是否可用 13 * @returns {boolean} 是否可用
14 */ 14 */
15 -export const is_usable_network = (network_type) => { 15 +export const is_usable_network = network_type => {
16 - return ['wifi', '4g', '5g', '3g'].includes(network_type) 16 + return ['wifi', '4g', '5g', '3g'].includes(network_type)
17 } 17 }
18 18
19 /** 19 /**
...@@ -21,15 +21,15 @@ export const is_usable_network = (network_type) => { ...@@ -21,15 +21,15 @@ export const is_usable_network = (network_type) => {
21 * @returns {Promise<string>} 网络类型(wifi, 4g, 5g, 3g, unknown) 21 * @returns {Promise<string>} 网络类型(wifi, 4g, 5g, 3g, unknown)
22 */ 22 */
23 export const get_network_type = async () => { 23 export const get_network_type = async () => {
24 - try { 24 + try {
25 - const result = await new Promise((resolve, reject) => { 25 + const result = await new Promise((resolve, reject) => {
26 - Taro.getNetworkType({ 26 + Taro.getNetworkType({
27 - success: resolve, 27 + success: resolve,
28 - fail: reject, 28 + fail: reject
29 - }) 29 + })
30 - }) 30 + })
31 - return result?.networkType || 'unknown' 31 + return result?.networkType || 'unknown'
32 - } catch (e) { 32 + } catch (e) {
33 - return 'unknown' 33 + return 'unknown'
34 - } 34 + }
35 } 35 }
......
...@@ -20,39 +20,39 @@ if (typeof TextEncoder === 'undefined') { ...@@ -20,39 +20,39 @@ if (typeof TextEncoder === 'undefined') {
20 * @returns {Uint8Array} UTF-8 字节数组 20 * @returns {Uint8Array} UTF-8 字节数组
21 */ 21 */
22 encode(str) { 22 encode(str) {
23 - const len = str.length; 23 + const len = str.length
24 - const res = []; 24 + const res = []
25 for (let i = 0; i < len; i++) { 25 for (let i = 0; i < len; i++) {
26 - let point = str.charCodeAt(i); 26 + let point = str.charCodeAt(i)
27 if (point <= 0x007f) { 27 if (point <= 0x007f) {
28 - res.push(point); 28 + res.push(point)
29 } else if (point <= 0x07ff) { 29 } else if (point <= 0x07ff) {
30 - res.push(0xc0 | (point >>> 6)); 30 + res.push(0xc0 | (point >>> 6))
31 - res.push(0x80 | (0x3f & point)); 31 + res.push(0x80 | (0x3f & point))
32 } else if (point <= 0xffff) { 32 } else if (point <= 0xffff) {
33 - res.push(0xe0 | (point >>> 12)); 33 + res.push(0xe0 | (point >>> 12))
34 - res.push(0x80 | (0x3f & (point >>> 6))); 34 + res.push(0x80 | (0x3f & (point >>> 6)))
35 - res.push(0x80 | (0x3f & point)); 35 + res.push(0x80 | (0x3f & point))
36 } else { 36 } else {
37 - point = 0x10000 + ((point - 0xd800) << 10) + (str.charCodeAt(++i) - 0xdc00); 37 + point = 0x10000 + ((point - 0xd800) << 10) + (str.charCodeAt(++i) - 0xdc00)
38 - res.push(0xf0 | (point >>> 18)); 38 + res.push(0xf0 | (point >>> 18))
39 - res.push(0x80 | (0x3f & (point >>> 12))); 39 + res.push(0x80 | (0x3f & (point >>> 12)))
40 - res.push(0x80 | (0x3f & (point >>> 6))); 40 + res.push(0x80 | (0x3f & (point >>> 6)))
41 - res.push(0x80 | (0x3f & point)); 41 + res.push(0x80 | (0x3f & point))
42 } 42 }
43 } 43 }
44 - return new Uint8Array(res); 44 + return new Uint8Array(res)
45 } 45 }
46 } 46 }
47 47
48 if (typeof globalThis !== 'undefined') { 48 if (typeof globalThis !== 'undefined') {
49 - globalThis.TextEncoder = TextEncoder; 49 + globalThis.TextEncoder = TextEncoder
50 } 50 }
51 if (typeof global !== 'undefined') { 51 if (typeof global !== 'undefined') {
52 - global.TextEncoder = TextEncoder; 52 + global.TextEncoder = TextEncoder
53 } 53 }
54 if (typeof window !== 'undefined') { 54 if (typeof window !== 'undefined') {
55 - window.TextEncoder = TextEncoder; 55 + window.TextEncoder = TextEncoder
56 } 56 }
57 } 57 }
58 58
...@@ -65,30 +65,30 @@ if (typeof TextDecoder === 'undefined') { ...@@ -65,30 +65,30 @@ if (typeof TextDecoder === 'undefined') {
65 * @returns {string} 解码后的字符串 65 * @returns {string} 解码后的字符串
66 */ 66 */
67 decode(view, options) { 67 decode(view, options) {
68 - void options; 68 + void options
69 if (!view) { 69 if (!view) {
70 - return ''; 70 + return ''
71 } 71 }
72 - let string = ''; 72 + let string = ''
73 - const arr = new Uint8Array(view); 73 + const arr = new Uint8Array(view)
74 for (let i = 0; i < arr.length; i++) { 74 for (let i = 0; i < arr.length; i++) {
75 - string += String.fromCharCode(arr[i]); 75 + string += String.fromCharCode(arr[i])
76 } 76 }
77 try { 77 try {
78 - // 简单的 UTF-8 解码尝试 78 + // 简单的 UTF-8 解码尝试
79 - return decodeURIComponent(escape(string)); 79 + return decodeURIComponent(escape(string))
80 } catch (e) { 80 } catch (e) {
81 - return string; 81 + return string
82 } 82 }
83 } 83 }
84 } 84 }
85 if (typeof globalThis !== 'undefined') { 85 if (typeof globalThis !== 'undefined') {
86 - globalThis.TextDecoder = TextDecoder; 86 + globalThis.TextDecoder = TextDecoder
87 } 87 }
88 if (typeof global !== 'undefined') { 88 if (typeof global !== 'undefined') {
89 - global.TextDecoder = TextDecoder; 89 + global.TextDecoder = TextDecoder
90 } 90 }
91 if (typeof window !== 'undefined') { 91 if (typeof window !== 'undefined') {
92 - window.TextDecoder = TextDecoder; 92 + window.TextDecoder = TextDecoder
93 } 93 }
94 } 94 }
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
6 * @Description: 简单axios封装,后续按实际处理 6 * @Description: 简单axios封装,后续按实际处理
7 */ 7 */
8 // import axios from 'axios' 8 // import axios from 'axios'
9 -import axios from 'axios-miniprogram'; 9 +import axios from 'axios-miniprogram'
10 import Taro from '@tarojs/taro' 10 import Taro from '@tarojs/taro'
11 // import qs from 'qs' 11 // import qs from 'qs'
12 // import { strExist } from './tools' 12 // import { strExist } from './tools'
...@@ -18,7 +18,7 @@ import { parseQueryString } from './tools' ...@@ -18,7 +18,7 @@ import { parseQueryString } from './tools'
18 // import { ProgressStart, ProgressEnd } from '@/components/axios-progress/progress'; 18 // import { ProgressStart, ProgressEnd } from '@/components/axios-progress/progress';
19 // import store from '@/store' 19 // import store from '@/store'
20 // import { getToken } from '@/utils/auth' 20 // import { getToken } from '@/utils/auth'
21 -import BASE_URL, { REQUEST_DEFAULT_PARAMS } from './config'; 21 +import BASE_URL, { REQUEST_DEFAULT_PARAMS } from './config'
22 22
23 /** 23 /**
24 * @description 获取 sessionid 的工具函数 24 * @description 获取 sessionid 的工具函数
...@@ -28,12 +28,12 @@ import BASE_URL, { REQUEST_DEFAULT_PARAMS } from './config'; ...@@ -28,12 +28,12 @@ import BASE_URL, { REQUEST_DEFAULT_PARAMS } from './config';
28 */ 28 */
29 export const getSessionId = () => { 29 export const getSessionId = () => {
30 try { 30 try {
31 - return Taro.getStorageSync("sessionid") || null; 31 + return Taro.getStorageSync('sessionid') || null
32 } catch (error) { 32 } catch (error) {
33 - console.error('获取sessionid失败:', error); 33 + console.error('获取sessionid失败:', error)
34 - return null; 34 + return null
35 } 35 }
36 -}; 36 +}
37 37
38 /** 38 /**
39 * @description 设置 sessionid(一般不需要手动调用) 39 * @description 设置 sessionid(一般不需要手动调用)
...@@ -42,9 +42,11 @@ export const getSessionId = () => { ...@@ -42,9 +42,11 @@ export const getSessionId = () => {
42 * @param {string} sessionid cookie 字符串 42 * @param {string} sessionid cookie 字符串
43 * @returns {void} 无返回值 43 * @returns {void} 无返回值
44 */ 44 */
45 -export const setSessionId = (sessionid) => { 45 +export const setSessionId = sessionid => {
46 try { 46 try {
47 - if (!sessionid) return 47 + if (!sessionid) {
48 + return
49 + }
48 Taro.setStorageSync('sessionid', sessionid) 50 Taro.setStorageSync('sessionid', sessionid)
49 } catch (error) { 51 } catch (error) {
50 console.error('设置sessionid失败:', error) 52 console.error('设置sessionid失败:', error)
...@@ -76,7 +78,7 @@ export const clearSessionId = () => { ...@@ -76,7 +78,7 @@ export const clearSessionId = () => {
76 const service = axios.create({ 78 const service = axios.create({
77 baseURL: BASE_URL, // url = base url + request url 79 baseURL: BASE_URL, // url = base url + request url
78 // withCredentials: true, // send cookies when cross-domain requests 80 // withCredentials: true, // send cookies when cross-domain requests
79 - timeout: 5000, // request timeout 81 + timeout: 5000 // request timeout
80 }) 82 })
81 83
82 // service.defaults.params = { 84 // service.defaults.params = {
...@@ -91,9 +93,11 @@ let has_shown_timeout_modal = false ...@@ -91,9 +93,11 @@ let has_shown_timeout_modal = false
91 * @returns {boolean} true=超时,false=非超时 93 * @returns {boolean} true=超时,false=非超时
92 */ 94 */
93 95
94 -const is_timeout_error = (error) => { 96 +const is_timeout_error = error => {
95 const msg = String(error?.message || error?.errMsg || '') 97 const msg = String(error?.message || error?.errMsg || '')
96 - if (error?.code === 'ECONNABORTED') return true 98 + if (error?.code === 'ECONNABORTED') {
99 + return true
100 + }
97 return msg.toLowerCase().includes('timeout') 101 return msg.toLowerCase().includes('timeout')
98 } 102 }
99 103
...@@ -102,7 +106,7 @@ const is_timeout_error = (error) => { ...@@ -102,7 +106,7 @@ const is_timeout_error = (error) => {
102 * @param {Error} error 请求错误对象 106 * @param {Error} error 请求错误对象
103 * @returns {boolean} true=网络错误,false=非网络错误 107 * @returns {boolean} true=网络错误,false=非网络错误
104 */ 108 */
105 -const is_network_error = (error) => { 109 +const is_network_error = error => {
106 const msg = String(error?.message || error?.errMsg || '') 110 const msg = String(error?.message || error?.errMsg || '')
107 const raw = (() => { 111 const raw = (() => {
108 try { 112 try {
...@@ -111,14 +115,28 @@ const is_network_error = (error) => { ...@@ -111,14 +115,28 @@ const is_network_error = (error) => {
111 return '' 115 return ''
112 } 116 }
113 })() 117 })()
114 - const lower = (msg + ' ' + raw).toLowerCase() 118 + const lower = `${msg} ${raw}`.toLowerCase()
115 - if (lower.includes('request:fail')) return true 119 + if (lower.includes('request:fail')) {
116 - if (lower.includes('request fail')) return true 120 + return true
117 - if (lower.includes('network error')) return true 121 + }
118 - if (lower.includes('failed to fetch')) return true 122 + if (lower.includes('request fail')) {
119 - if (lower.includes('the internet connection appears to be offline')) return true 123 + return true
120 - if (lower.includes('err_blocked_by_client')) return true 124 + }
121 - if (lower.includes('blocked_by_client')) return true 125 + if (lower.includes('network error')) {
126 + return true
127 + }
128 + if (lower.includes('failed to fetch')) {
129 + return true
130 + }
131 + if (lower.includes('the internet connection appears to be offline')) {
132 + return true
133 + }
134 + if (lower.includes('err_blocked_by_client')) {
135 + return true
136 + }
137 + if (lower.includes('blocked_by_client')) {
138 + return true
139 + }
122 return false 140 return false
123 } 141 }
124 142
...@@ -129,8 +147,10 @@ const is_network_error = (error) => { ...@@ -129,8 +147,10 @@ const is_network_error = (error) => {
129 * @param {Error} error 请求错误对象 147 * @param {Error} error 请求错误对象
130 * @returns {Promise<boolean>} true=需要降级,false=不需要 148 * @returns {Promise<boolean>} true=需要降级,false=不需要
131 */ 149 */
132 -const should_handle_bad_network = async (error) => { 150 +const should_handle_bad_network = async error => {
133 - if (is_timeout_error(error)) return true 151 + if (is_timeout_error(error)) {
152 + return true
153 + }
134 return is_network_error(error) 154 return is_network_error(error)
135 } 155 }
136 156
...@@ -141,13 +161,17 @@ const should_handle_bad_network = async (error) => { ...@@ -141,13 +161,17 @@ const should_handle_bad_network = async (error) => {
141 * @returns {Promise<void>} 无返回值 161 * @returns {Promise<void>} 无返回值
142 */ 162 */
143 const handle_request_timeout = async () => { 163 const handle_request_timeout = async () => {
144 - if (has_shown_timeout_modal) return 164 + if (has_shown_timeout_modal) {
165 + return
166 + }
145 has_shown_timeout_modal = true 167 has_shown_timeout_modal = true
146 168
147 const pages = Taro.getCurrentPages ? Taro.getCurrentPages() : [] 169 const pages = Taro.getCurrentPages ? Taro.getCurrentPages() : []
148 const current_page = pages && pages.length ? pages[pages.length - 1] : null 170 const current_page = pages && pages.length ? pages[pages.length - 1] : null
149 const current_route = current_page?.route || '' 171 const current_route = current_page?.route || ''
150 - if (String(current_route).includes('pages/offlineBookingList/index')) return 172 + if (String(current_route).includes('pages/offlineBookingList/index')) {
173 + return
174 + }
151 175
152 // 若有离线预约记录缓存,则跳转至离线预约列表页 176 // 若有离线预约记录缓存,则跳转至离线预约列表页
153 if (has_offline_booking_cache()) { 177 if (has_offline_booking_cache()) {
...@@ -177,15 +201,15 @@ service.interceptors.request.use( ...@@ -177,15 +201,15 @@ service.interceptors.request.use(
177 const url = config.url || '' 201 const url = config.url || ''
178 let url_params = {} 202 let url_params = {}
179 if (url.includes('?')) { 203 if (url.includes('?')) {
180 - url_params = parseQueryString(url) 204 + url_params = parseQueryString(url)
181 - config.url = url.split('?')[0] 205 + config.url = url.split('?')[0]
182 } 206 }
183 207
184 // 优先级:调用传参 > URL参数 > 默认参数 208 // 优先级:调用传参 > URL参数 > 默认参数
185 config.params = { 209 config.params = {
186 - ...REQUEST_DEFAULT_PARAMS, 210 + ...REQUEST_DEFAULT_PARAMS,
187 - ...url_params, 211 + ...url_params,
188 - ...(config.params || {}) 212 + ...(config.params || {})
189 } 213 }
190 214
191 /** 215 /**
...@@ -193,15 +217,15 @@ service.interceptors.request.use( ...@@ -193,15 +217,15 @@ service.interceptors.request.use(
193 * - 确保每个请求都带上最新的 sessionid 217 * - 确保每个请求都带上最新的 sessionid
194 * - 注意:axios-miniprogram 的 headers 可能不存在,需要先兜底 218 * - 注意:axios-miniprogram 的 headers 可能不存在,需要先兜底
195 */ 219 */
196 - const sessionid = getSessionId(); 220 + const sessionid = getSessionId()
197 if (sessionid) { 221 if (sessionid) {
198 config.headers = config.headers || {} 222 config.headers = config.headers || {}
199 - config.headers.cookie = sessionid; 223 + config.headers.cookie = sessionid
200 } 224 }
201 225
202 // 增加时间戳 226 // 增加时间戳
203 if (config.method === 'get') { 227 if (config.method === 'get') {
204 - config.params = { ...config.params, timestamp: (new Date()).valueOf() } 228 + config.params = { ...config.params, timestamp: new Date().valueOf() }
205 } 229 }
206 230
207 // if ((config.method || '').toLowerCase() === 'post') { 231 // if ((config.method || '').toLowerCase() === 'post') {
...@@ -240,43 +264,43 @@ service.interceptors.response.use( ...@@ -240,43 +264,43 @@ service.interceptors.response.use(
240 264
241 // 401 未授权处理 265 // 401 未授权处理
242 if (res.code === 401) { 266 if (res.code === 401) {
243 - const config = response?.config || {} 267 + const config = response?.config || {}
244 - /** 268 + /**
245 - * 避免死循环/重复重试: 269 + * 避免死循环/重复重试:
246 - * - __is_retry:本次请求是 401 后的重试请求,如果仍 401,不再继续重试 270 + * - __is_retry:本次请求是 401 后的重试请求,如果仍 401,不再继续重试
247 - */ 271 + */
248 - if (config.__is_retry) { 272 + if (config.__is_retry) {
249 - return response 273 + return response
250 - } 274 + }
251 275
252 - /** 276 + /**
253 - * 记录来源页:用于授权成功后回跳 277 + * 记录来源页:用于授权成功后回跳
254 - * - 避免死循环:如果已经在 auth 页则不重复记录/跳转 278 + * - 避免死循环:如果已经在 auth 页则不重复记录/跳转
255 - */ 279 + */
256 - const pages = Taro.getCurrentPages(); 280 + const pages = Taro.getCurrentPages()
257 - const currentPage = pages[pages.length - 1]; 281 + const currentPage = pages[pages.length - 1]
258 - if (currentPage && currentPage.route !== 'pages/auth/index') { 282 + if (currentPage && currentPage.route !== 'pages/auth/index') {
259 - saveCurrentPagePath() 283 + saveCurrentPagePath()
260 - } 284 + }
261 285
262 - try { 286 + try {
263 - // 优先走静默续期:成功后重放原请求 287 + // 优先走静默续期:成功后重放原请求
264 - await refreshSession() 288 + await refreshSession()
265 - const retry_config = { ...config, __is_retry: true } 289 + const retry_config = { ...config, __is_retry: true }
266 - return await service(retry_config) 290 + return await service(retry_config)
267 - } catch (error) { 291 + } catch (error) {
268 - // 静默续期失败:降级跳转到授权页(由授权页完成授权并回跳) 292 + // 静默续期失败:降级跳转到授权页(由授权页完成授权并回跳)
269 - const pages_retry = Taro.getCurrentPages(); 293 + const pages_retry = Taro.getCurrentPages()
270 - const current_page_retry = pages_retry[pages_retry.length - 1]; 294 + const current_page_retry = pages_retry[pages_retry.length - 1]
271 - if (current_page_retry && current_page_retry.route !== 'pages/auth/index') { 295 + if (current_page_retry && current_page_retry.route !== 'pages/auth/index') {
272 - navigateToAuth() 296 + navigateToAuth()
273 - }
274 - return response
275 } 297 }
298 + return response
299 + }
276 } 300 }
277 301
278 if (['预约ID不存在'].includes(res.msg)) { 302 if (['预约ID不存在'].includes(res.msg)) {
279 - res.show = false; 303 + res.show = false
280 } 304 }
281 305
282 return response 306 return response
......
...@@ -6,12 +6,12 @@ ...@@ -6,12 +6,12 @@
6 * @description 主办方默认用户类型 6 * @description 主办方默认用户类型
7 * @type {Array<string>} 7 * @type {Array<string>}
8 */ 8 */
9 -const DEFAULT_HOST_TYPE = ['首次参与', '老用户']; 9 +const DEFAULT_HOST_TYPE = ['首次参与', '老用户']
10 10
11 /** 11 /**
12 * @description 主办方默认用户状态 12 * @description 主办方默认用户状态
13 * @type {Array<string>} 13 * @type {Array<string>}
14 */ 14 */
15 -const DEFAULT_HOST_STATUS = ['跟踪', '引导']; 15 +const DEFAULT_HOST_STATUS = ['跟踪', '引导']
16 16
17 export { DEFAULT_HOST_TYPE, DEFAULT_HOST_STATUS } 17 export { DEFAULT_HOST_TYPE, DEFAULT_HOST_STATUS }
......
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
5 * @FilePath: /git/xyxBooking-weapp/src/utils/tools.js 5 * @FilePath: /git/xyxBooking-weapp/src/utils/tools.js
6 * @Description: 工具函数库 6 * @Description: 工具函数库
7 */ 7 */
8 -import dayjs from 'dayjs'; 8 +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 /**
...@@ -14,25 +14,25 @@ import BASE_URL, { REQUEST_DEFAULT_PARAMS } from './config' ...@@ -14,25 +14,25 @@ import BASE_URL, { REQUEST_DEFAULT_PARAMS } from './config'
14 * @param {string|number|Date} date 时间入参 14 * @param {string|number|Date} date 时间入参
15 * @returns {string} 格式化后的时间字符串(YYYY-MM-DD HH:mm) 15 * @returns {string} 格式化后的时间字符串(YYYY-MM-DD HH:mm)
16 */ 16 */
17 -const formatDate = (date) => { 17 +const formatDate = date => {
18 - return dayjs(date).format('YYYY-MM-DD HH:mm'); 18 + return dayjs(date).format('YYYY-MM-DD HH:mm')
19 -}; 19 +}
20 20
21 /** 21 /**
22 * @description 判断设备信息 22 * @description 判断设备信息
23 * @returns {Object} 设备信息对象,包含是否为 Android、iOS、是否为平板等属性 23 * @returns {Object} 设备信息对象,包含是否为 Android、iOS、是否为平板等属性
24 */ 24 */
25 const wxInfo = () => { 25 const wxInfo = () => {
26 - const info = Taro.getSystemInfoSync(); 26 + const info = Taro.getSystemInfoSync()
27 - const isAndroid = info.platform === 'android'; 27 + const isAndroid = info.platform === 'android'
28 - const isiOS = info.platform === 'ios'; 28 + const isiOS = info.platform === 'ios'
29 // 说明:当前项目只用到 Android/iOS 区分;平板能力按需补充 29 // 说明:当前项目只用到 Android/iOS 区分;平板能力按需补充
30 return { 30 return {
31 isAndroid, 31 isAndroid,
32 isiOS, 32 isiOS,
33 isTable: false // 小程序通常不是 tablet 模式,或者可以根据 screenWidth 判断 33 isTable: false // 小程序通常不是 tablet 模式,或者可以根据 screenWidth 判断
34 - }; 34 + }
35 -}; 35 +}
36 36
37 /** 37 /**
38 * @description 解析 URL 参数 38 * @description 解析 URL 参数
...@@ -40,14 +40,16 @@ const wxInfo = () => { ...@@ -40,14 +40,16 @@ const wxInfo = () => {
40 * @returns {Object} URL 参数对象(键值对) 40 * @returns {Object} URL 参数对象(键值对)
41 */ 41 */
42 const parseQueryString = url => { 42 const parseQueryString = url => {
43 - if (!url) return {}; 43 + if (!url) {
44 - var json = {}; 44 + return {}
45 - var arr = url.indexOf('?') >= 0 ? url.substr(url.indexOf('?') + 1).split('&') : []; 45 + }
46 + const json = {}
47 + const arr = url.indexOf('?') >= 0 ? url.substr(url.indexOf('?') + 1).split('&') : []
46 arr.forEach(item => { 48 arr.forEach(item => {
47 - var tmp = item.split('='); 49 + const tmp = item.split('=')
48 - json[tmp[0]] = tmp[1]; 50 + json[tmp[0]] = tmp[1]
49 - }); 51 + })
50 - return json; 52 + return json
51 } 53 }
52 54
53 /** 55 /**
...@@ -57,9 +59,13 @@ const parseQueryString = url => { ...@@ -57,9 +59,13 @@ const parseQueryString = url => {
57 * @returns {boolean} true=包含任意一个子串,false=都不包含 59 * @returns {boolean} true=包含任意一个子串,false=都不包含
58 */ 60 */
59 const strExist = (array, str) => { 61 const strExist = (array, str) => {
60 - if (!str) return false; 62 + if (!str) {
63 + return false
64 + }
61 const exist = array.filter(arr => { 65 const exist = array.filter(arr => {
62 - if (str.indexOf(arr) >= 0) return str; 66 + if (str.indexOf(arr) >= 0) {
67 + return str
68 + }
63 }) 69 })
64 return exist.length > 0 70 return exist.length > 0
65 } 71 }
...@@ -90,32 +96,35 @@ const strExist = (array, str) => { ...@@ -90,32 +96,35 @@ const strExist = (array, str) => {
90 * 2. 若 begin_time/end_time 缺失,拼接后可能出现 "undefined-undefined" 等异常,需保证入参完整性; 96 * 2. 若 begin_time/end_time 缺失,拼接后可能出现 "undefined-undefined" 等异常,需保证入参完整性;
91 * 3. 该函数默认截取时间字符串前19位(slice(0, -6)),需根据实际时间格式调整截取长度 97 * 3. 该函数默认截取时间字符串前19位(slice(0, -6)),需根据实际时间格式调整截取长度
92 */ 98 */
93 -const formatDatetime = (data) => { 99 +const formatDatetime = data => {
94 - if (!data || !data.begin_time || !data.end_time) return ''; 100 + if (!data || !data.begin_time || !data.end_time) {
95 - 101 + return ''
96 - const normalize = (timeStr) => { 102 + }
97 - if (!timeStr) return ''; 103 +
98 - let clean = timeStr.split('+')[0]; 104 + const normalize = timeStr => {
99 - clean = clean.split('Z')[0]; 105 + if (!timeStr) {
100 - clean = clean.trim().replace(/\s+/, 'T'); 106 + return ''
101 - return clean; 107 + }
102 - }; 108 + let clean = timeStr.split('+')[0]
109 + clean = clean.split('Z')[0]
110 + clean = clean.trim().replace(/\s+/, 'T')
111 + return clean
112 + }
103 113
104 - const start = dayjs(normalize(data.begin_time)); 114 + const start = dayjs(normalize(data.begin_time))
105 - const end = dayjs(normalize(data.end_time)); 115 + const end = dayjs(normalize(data.end_time))
106 116
107 - if (!start.isValid() || !end.isValid()) return ''; 117 + if (!start.isValid() || !end.isValid()) {
118 + return ''
119 + }
108 120
109 const isNextDayMidnight = 121 const isNextDayMidnight =
110 - end.diff(start, 'day') === 1 && 122 + end.diff(start, 'day') === 1 && end.hour() === 0 && end.minute() === 0 && end.second() === 0
111 - end.hour() === 0 &&
112 - end.minute() === 0 &&
113 - end.second() === 0;
114 123
115 - const endTimeText = isNextDayMidnight ? '24:00' : end.format('HH:mm'); 124 + const endTimeText = isNextDayMidnight ? '24:00' : end.format('HH:mm')
116 125
117 - return `${start.format('YYYY-MM-DD')} ${start.format('HH:mm')}-${endTimeText}`; 126 + return `${start.format('YYYY-MM-DD')} ${start.format('HH:mm')}-${endTimeText}`
118 -}; 127 +}
119 128
120 /** 129 /**
121 * @description 证件号脱敏 130 * @description 证件号脱敏
...@@ -127,31 +136,39 @@ const formatDatetime = (data) => { ...@@ -127,31 +136,39 @@ const formatDatetime = (data) => {
127 * @returns {string} 脱敏后的证件号 136 * @returns {string} 脱敏后的证件号
128 */ 137 */
129 const mask_id_number = (id_number, options = {}) => { 138 const mask_id_number = (id_number, options = {}) => {
130 - const raw = String(id_number || '') 139 + const raw = String(id_number || '')
131 - if (!raw) return '' 140 + if (!raw) {
132 - 141 + return ''
133 - const has_keep_start = Number.isFinite(options.keep_start) 142 + }
134 - const has_keep_end = Number.isFinite(options.keep_end) 143 +
135 - const keep_start = has_keep_start ? options.keep_start : 0 144 + const has_keep_start = Number.isFinite(options.keep_start)
136 - const keep_end = has_keep_end ? options.keep_end : 0 145 + const has_keep_end = Number.isFinite(options.keep_end)
137 - const mask_count = Number.isFinite(options.mask_count) ? options.mask_count : 8 146 + const keep_start = has_keep_start ? options.keep_start : 0
138 - 147 + const keep_end = has_keep_end ? options.keep_end : 0
139 - if (has_keep_start && has_keep_end) { 148 + const mask_count = Number.isFinite(options.mask_count) ? options.mask_count : 8
140 - if (raw.length <= keep_start + keep_end) return raw 149 +
141 - const prefix = raw.slice(0, keep_start) 150 + if (has_keep_start && has_keep_end) {
142 - const suffix = raw.slice(raw.length - keep_end) 151 + if (raw.length <= keep_start + keep_end) {
143 - const middle_len = Math.max(1, raw.length - keep_start - keep_end) 152 + return raw
144 - return `${prefix}${'*'.repeat(middle_len)}${suffix}`
145 } 153 }
146 - 154 + const prefix = raw.slice(0, keep_start)
147 - if (raw.length < 15) return raw 155 + const suffix = raw.slice(raw.length - keep_end)
148 - 156 + const middle_len = Math.max(1, raw.length - keep_start - keep_end)
149 - const safe_mask_count = Math.min(Math.max(1, mask_count), raw.length) 157 + return `${prefix}${'*'.repeat(middle_len)}${suffix}`
150 - const start = Math.floor((raw.length - safe_mask_count) / 2) 158 + }
151 - const end = start + safe_mask_count 159 +
152 - if (start < 0 || end > raw.length) return raw 160 + if (raw.length < 15) {
153 - 161 + return raw
154 - return raw.substring(0, start) + '*'.repeat(safe_mask_count) + raw.substring(end) 162 + }
163 +
164 + const safe_mask_count = Math.min(Math.max(1, mask_count), raw.length)
165 + const start = Math.floor((raw.length - safe_mask_count) / 2)
166 + const end = start + safe_mask_count
167 + if (start < 0 || end > raw.length) {
168 + return raw
169 + }
170 +
171 + return raw.substring(0, start) + '*'.repeat(safe_mask_count) + raw.substring(end)
155 } 172 }
156 173
157 /** 174 /**
...@@ -159,13 +176,21 @@ const mask_id_number = (id_number, options = {}) => { ...@@ -159,13 +176,21 @@ const mask_id_number = (id_number, options = {}) => {
159 * @param {string|number} status 状态值 176 * @param {string|number} status 状态值
160 * @returns {string} 状态文案 177 * @returns {string} 状态文案
161 */ 178 */
162 -const get_qrcode_status_text = (status) => { 179 +const get_qrcode_status_text = status => {
163 - const key = String(status || '') 180 + const key = String(status || '')
164 - if (key === '1') return '未激活' 181 + if (key === '1') {
165 - if (key === '3') return '待使用' 182 + return '未激活'
166 - if (key === '5') return '被取消' 183 + }
167 - if (key === '7') return '已使用' 184 + if (key === '3') {
168 - return '未知状态' 185 + return '待使用'
186 + }
187 + if (key === '5') {
188 + return '被取消'
189 + }
190 + if (key === '7') {
191 + return '已使用'
192 + }
193 + return '未知状态'
169 } 194 }
170 195
171 /** 196 /**
...@@ -173,13 +198,21 @@ const get_qrcode_status_text = (status) => { ...@@ -173,13 +198,21 @@ const get_qrcode_status_text = (status) => {
173 * @param {string|number} status 状态值 198 * @param {string|number} status 状态值
174 * @returns {string} 状态文案 199 * @returns {string} 状态文案
175 */ 200 */
176 -const get_bill_status_text = (status) => { 201 +const get_bill_status_text = status => {
177 - const key = String(status || '') 202 + const key = String(status || '')
178 - if (key === '3') return '预约成功' 203 + if (key === '3') {
179 - if (key === '5') return '已取消' 204 + return '预约成功'
180 - if (key === '9') return '已使用' 205 + }
181 - if (key === '11') return '退款中' 206 + if (key === '5') {
182 - return '未知状态' 207 + return '已取消'
208 + }
209 + if (key === '9') {
210 + return '已使用'
211 + }
212 + if (key === '11') {
213 + return '退款中'
214 + }
215 + return '未知状态'
183 } 216 }
184 217
185 /** 218 /**
...@@ -189,13 +222,23 @@ const get_bill_status_text = (status) => { ...@@ -189,13 +222,23 @@ const get_bill_status_text = (status) => {
189 * @returns {string} 完整请求 URL(BASE_URL + /srv/?a=...&f=...&client_name=...) 222 * @returns {string} 完整请求 URL(BASE_URL + /srv/?a=...&f=...&client_name=...)
190 */ 223 */
191 const buildApiUrl = (action, params = {}) => { 224 const buildApiUrl = (action, params = {}) => {
192 - const queryParams = new URLSearchParams({ 225 + const queryParams = new URLSearchParams({
193 - a: action, 226 + a: action,
194 - f: REQUEST_DEFAULT_PARAMS.f, 227 + f: REQUEST_DEFAULT_PARAMS.f,
195 - client_name: REQUEST_DEFAULT_PARAMS.client_name, 228 + client_name: REQUEST_DEFAULT_PARAMS.client_name,
196 - ...params, 229 + ...params
197 - }) 230 + })
198 - return `${BASE_URL}/srv/?${queryParams.toString()}` 231 + return `${BASE_URL}/srv/?${queryParams.toString()}`
199 } 232 }
200 233
201 -export { formatDate, wxInfo, parseQueryString, strExist, formatDatetime, mask_id_number, get_qrcode_status_text, get_bill_status_text, buildApiUrl }; 234 +export {
235 + formatDate,
236 + wxInfo,
237 + parseQueryString,
238 + strExist,
239 + formatDatetime,
240 + mask_id_number,
241 + get_qrcode_status_text,
242 + get_bill_status_text,
243 + buildApiUrl
244 +}
......
...@@ -10,17 +10,18 @@ ...@@ -10,17 +10,18 @@
10 * - toast/modal/banner 等入口统一引用,避免多处硬编码 10 * - toast/modal/banner 等入口统一引用,避免多处硬编码
11 */ 11 */
12 export const weak_network_text = { 12 export const weak_network_text = {
13 - title: '网络连接不畅', 13 + title: '网络连接不畅',
14 - toast_title: '网络连接不畅', 14 + toast_title: '网络连接不畅',
15 - banner_desc: '网络开小差啦!请检查网络设置,或更换位置后重新进入小程序~', 15 + banner_desc: '网络开小差啦!请检查网络设置,或更换位置后重新进入小程序~',
16 - offline_page_desc: '当前网络信号较弱,已自动为您切换至离线模式', 16 + offline_page_desc: '当前网络信号较弱,已自动为您切换至离线模式',
17 - modal_no_cache_content: '当前网络信号较弱,暂无法使用小程序,请检查网络设置,或更换位置后重新进入小程序~', 17 + modal_no_cache_content:
18 - modal_use_cache_content: '当前网络信号较弱,可使用已缓存的预约记录进入离线模式', 18 + '当前网络信号较弱,暂无法使用小程序,请检查网络设置,或更换位置后重新进入小程序~',
19 - modal_go_offline_records_content: '当前网络信号较弱,是否进入离线预约记录?', 19 + modal_use_cache_content: '当前网络信号较弱,可使用已缓存的预约记录进入离线模式',
20 - offline_mode_no_booking_toast: '当前为离线模式,无法预约', 20 + modal_go_offline_records_content: '当前网络信号较弱,是否进入离线预约记录?',
21 - confirm_ok: '知道了', 21 + offline_mode_no_booking_toast: '当前为离线模式,无法预约',
22 - confirm_booking_records: '预约记录', 22 + confirm_ok: '知道了',
23 - confirm_offline_records: '离线记录', 23 + confirm_booking_records: '预约记录',
24 + confirm_offline_records: '离线记录'
24 } 25 }
25 26
26 /** 27 /**
...@@ -28,12 +29,12 @@ export const weak_network_text = { ...@@ -28,12 +29,12 @@ export const weak_network_text = {
28 * @returns {Object} 29 * @returns {Object}
29 */ 30 */
30 export const get_weak_network_modal_no_cache_options = () => { 31 export const get_weak_network_modal_no_cache_options = () => {
31 - return { 32 + return {
32 - title: weak_network_text.title, 33 + title: weak_network_text.title,
33 - content: weak_network_text.modal_no_cache_content, 34 + content: weak_network_text.modal_no_cache_content,
34 - confirmText: weak_network_text.confirm_ok, 35 + confirmText: weak_network_text.confirm_ok,
35 - showCancel: false, 36 + showCancel: false
36 - } 37 + }
37 } 38 }
38 39
39 /** 40 /**
...@@ -41,12 +42,12 @@ export const get_weak_network_modal_no_cache_options = () => { ...@@ -41,12 +42,12 @@ export const get_weak_network_modal_no_cache_options = () => {
41 * @returns {Object} 42 * @returns {Object}
42 */ 43 */
43 export const get_weak_network_modal_use_cache_options = () => { 44 export const get_weak_network_modal_use_cache_options = () => {
44 - return { 45 + return {
45 - title: weak_network_text.title, 46 + title: weak_network_text.title,
46 - content: weak_network_text.modal_use_cache_content, 47 + content: weak_network_text.modal_use_cache_content,
47 - confirmText: weak_network_text.confirm_booking_records, 48 + confirmText: weak_network_text.confirm_booking_records,
48 - cancelText: weak_network_text.confirm_ok, 49 + cancelText: weak_network_text.confirm_ok
49 - } 50 + }
50 } 51 }
51 52
52 /** 53 /**
...@@ -54,10 +55,10 @@ export const get_weak_network_modal_use_cache_options = () => { ...@@ -54,10 +55,10 @@ export const get_weak_network_modal_use_cache_options = () => {
54 * @returns {Object} 55 * @returns {Object}
55 */ 56 */
56 export const get_weak_network_modal_go_offline_records_options = () => { 57 export const get_weak_network_modal_go_offline_records_options = () => {
57 - return { 58 + return {
58 - title: weak_network_text.title, 59 + title: weak_network_text.title,
59 - content: weak_network_text.modal_go_offline_records_content, 60 + content: weak_network_text.modal_go_offline_records_content,
60 - confirmText: weak_network_text.confirm_offline_records, 61 + confirmText: weak_network_text.confirm_offline_records,
61 - cancelText: weak_network_text.confirm_ok, 62 + cancelText: weak_network_text.confirm_ok
62 - } 63 + }
63 } 64 }
......
...@@ -4,11 +4,11 @@ ...@@ -4,11 +4,11 @@
4 */ 4 */
5 const getCurrentPageUrl = () => { 5 const getCurrentPageUrl = () => {
6 // 获取加载的页面栈 6 // 获取加载的页面栈
7 - let pages = getCurrentPages() 7 + const pages = getCurrentPages()
8 // 获取当前页面对象 8 // 获取当前页面对象
9 - let currentPage = pages[pages.length - 1] 9 + const currentPage = pages[pages.length - 1]
10 // 当前页面 route(不含 query) 10 // 当前页面 route(不含 query)
11 - let url = currentPage.route 11 + const url = currentPage.route
12 return url 12 return url
13 } 13 }
14 /** 14 /**
...@@ -17,15 +17,12 @@ const getCurrentPageUrl = () => { ...@@ -17,15 +17,12 @@ const getCurrentPageUrl = () => {
17 */ 17 */
18 const getCurrentPageParam = () => { 18 const getCurrentPageParam = () => {
19 // 获取加载的页面栈 19 // 获取加载的页面栈
20 - let pages = getCurrentPages() 20 + const pages = getCurrentPages()
21 // 获取当前页面对象 21 // 获取当前页面对象
22 - let currentPage = pages[pages.length - 1] 22 + const currentPage = pages[pages.length - 1]
23 // 当前页面 query 参数对象 23 // 当前页面 query 参数对象
24 - let options = currentPage.options 24 + const options = currentPage.options
25 return options 25 return options
26 } 26 }
27 27
28 -export { 28 +export { getCurrentPageUrl, getCurrentPageParam }
29 - getCurrentPageUrl,
30 - getCurrentPageParam
31 -}
......
...@@ -15,40 +15,40 @@ import { wxPayAPI } from '@/api/wx/pay' ...@@ -15,40 +15,40 @@ import { wxPayAPI } from '@/api/wx/pay'
15 */ 15 */
16 16
17 export const wechat_pay = async ({ pay_id }) => { 17 export const wechat_pay = async ({ pay_id }) => {
18 - const normalized_pay_id = String(pay_id || '') 18 + const normalized_pay_id = String(pay_id || '')
19 - if (!normalized_pay_id) { 19 + if (!normalized_pay_id) {
20 - return { code: 0, data: null, msg: '缺少订单号' } 20 + return { code: 0, data: null, msg: '缺少订单号' }
21 - } 21 + }
22 - 22 +
23 - Taro.showLoading({ title: '支付准备中...' }) 23 + Taro.showLoading({ title: '支付准备中...' })
24 - let pay_params_res = null 24 + let pay_params_res = null
25 - try { 25 + try {
26 - pay_params_res = await wxPayAPI({ pay_id: normalized_pay_id }) 26 + pay_params_res = await wxPayAPI({ pay_id: normalized_pay_id })
27 - } finally { 27 + } finally {
28 - Taro.hideLoading() 28 + Taro.hideLoading()
29 - } 29 + }
30 - 30 +
31 - if (!pay_params_res || pay_params_res.code != 1) { 31 + if (!pay_params_res || pay_params_res.code != 1) {
32 - return { code: 0, data: null, msg: pay_params_res?.msg || '获取支付信息失败,请稍后再试' } 32 + return { code: 0, data: null, msg: pay_params_res?.msg || '获取支付信息失败,请稍后再试' }
33 - } 33 + }
34 - 34 +
35 - const pay_params = pay_params_res?.data || {} 35 + const pay_params = pay_params_res?.data || {}
36 - 36 +
37 - const pay_result = await new Promise((resolve) => { 37 + const pay_result = await new Promise(resolve => {
38 - Taro.requestPayment({ 38 + Taro.requestPayment({
39 - timeStamp: pay_params.timeStamp, 39 + timeStamp: pay_params.timeStamp,
40 - nonceStr: pay_params.nonceStr, 40 + nonceStr: pay_params.nonceStr,
41 - package: pay_params.package, 41 + package: pay_params.package,
42 - signType: pay_params.signType, 42 + signType: pay_params.signType,
43 - paySign: pay_params.paySign, 43 + paySign: pay_params.paySign,
44 - success: (res) => resolve({ ok: true, res }), 44 + success: res => resolve({ ok: true, res }),
45 - fail: (err) => resolve({ ok: false, err }), 45 + fail: err => resolve({ ok: false, err })
46 - })
47 }) 46 })
47 + })
48 48
49 - if (pay_result?.ok) { 49 + if (pay_result?.ok) {
50 - return { code: 1, data: pay_result.res || null, msg: '支付成功' } 50 + return { code: 1, data: pay_result.res || null, msg: '支付成功' }
51 - } 51 + }
52 52
53 - return { code: 0, data: pay_result?.err || null, msg: pay_result?.err?.errMsg || '支付未完成' } 53 + return { code: 0, data: pay_result?.err || null, msg: pay_result?.err?.errMsg || '支付未完成' }
54 } 54 }
......