hookehuyr

docs: 重写 AGENTS.md 和 README.md 项目文档

- AGENTS.md 精简 Codex 协作说明,聚焦扫码打卡链路等既有约定
- README.md 补充核心功能、目录结构、开发命令等项目描述

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
# AGENTS.md
本文件为 Codex (Codex.ai/code) 在此代码库中工作时提供指导
本文件为 Codex `lls_program` 仓库中的协作说明。目标不是介绍 Taro 通用知识,而是帮助后续修改尽量贴合这个项目当前的真实实现
## 项目概述
**lls_program** 是一个基于 Taro 4 + Vue 3 + NutUI 的微信小程序,名为"老来赛"。这是一个家庭活动和积分奖励管理系统。
`lls_program` 是一个基于 Taro 4 + Vue 3 + NutUI 的微信小程序,当前核心业务包括:
- 家庭创建、加入、成员资料维护
- 活动页、广告页、海报打卡、扫码打卡
- 积分、奖励、优惠券相关页面
- 基于微信授权和 `sessionid` 的登录态流转
最近一轮实现里,扫码打卡链路已经接入真实接口,并补上了注册来源归因、回跳续扫、地理围栏判断等逻辑。后续改动请优先遵守这些既有约定,不要在页面里重新长出一套平行逻辑。
## 技术栈
- **框架**: Taro 4.1.7 - 跨平台小程序框架
- **UI**: Vue 3.3 + Composition API (`<script setup>`)
- **UI 组件库**: NutUI Taro 4.3.13 (自动导入,无需手动引入)
- **样式**: TailwindCSS 3.4 + Less (组件特定样式)
- **状态管理**: Pinia 3.0 + taro-plugin-pinia
- **HTTP 请求**: axios-miniprogram 2.7.2
- **构建工具**: Webpack 5
- 框架:Taro `4.1.7`
- UI:Vue `3.3`,统一使用 `<script setup>`
- UI 组件:NutUI Taro `4.3.13`
- 样式:TailwindCSS `3.4` + Less
- 状态管理:Pinia `3.0` + `taro-plugin-pinia`
- 请求库:`axios-miniprogram`
- 构建:Webpack 5
- 测试:Vitest
## 常用命令
## 开发命令
项目当前 `package.json` 里是标准 npm scripts,使用 `npm``pnpm` 都可以,但文档和命令示例优先按仓库现状写 `npm`
```bash
# 安装依赖
pnpm install
npm install
# 开发(微信小程序)
pnpm run dev:weapp
# 微信小程序开发
npm run dev:weapp
# 生产构建
pnpm run build:weapp
# 微信小程序打包
npm run build:weapp
# 其他平台
pnpm run dev:h5 # H5 开发
pnpm run dev:alipay # 支付宝小程序
pnpm run dev:tt # 抖音小程序
```
## 架构设计
# H5 / 支付宝 / 抖音
npm run dev:h5
npm run dev:alipay
npm run dev:tt
### 核心目录结构
# 代码质量
npm run lint
npm run format
# 测试
npm run test
npm run test:run
npm run test:coverage
```
## 目录结构
```text
src/
├── api/ # 按业务领域组织的 API 接口
├── assets/ # 静态资源(图片、样式)
├── components/ # 可复用的 Vue 组件
├── composables/ # Vue 3 组合式函数 (useXxx)
├── pages/ # Taro 页面(每个页面包含 index.vue + index.config.js)
├── stores/ # Pinia 状态管理
├── utils/ # 工具函数
├── app.config.js # Taro 应用配置(页面列表、窗口、权限)
├── api/ # 只放接口调用
├── assets/ # 静态资源
├── components/ # 通用组件
├── pages/ # 页面
├── stores/ # Pinia 状态
├── utils/ # 纯工具逻辑、流程辅助、请求封装
├── app.config.js # 页面注册、权限声明
└── app.less # 全局样式
```
### 路径别名 (config/index.js:30-38)
当前和扫码打卡强相关的目录:
- `src/api/map.js`
- `src/pages/ScanCheckinList/`
- `src/pages/ScanCheckinDetail/`
- `src/pages/Welcome/`
- `src/pages/AddProfile/`
- `src/pages/CreateFamily/`
- `src/pages/JoinFamily/`
- `src/utils/checkinLocation.js`
- `src/utils/scanCheckin.js`
- `src/utils/returnUrl.js`
- `src/utils/userProfile.js`
- `src/components/RichTextRenderer.vue`
## 路径别名
项目已经配置好以下别名,新增代码优先使用别名,不要堆相对路径:
```javascript
@/utils src/utils
@/components src/components
@/images src/assets/images
@/assets src/assets
@/composables src/composables
@/api src/api
@/stores src/stores
@/hooks src/hooks
@/utils
@/components
@/assets
@/api
@/stores
@/composables
@/hooks
```
### 设计宽度配置
- **NutUI 组件**: 375px (自动处理)
- **其他所有内容**: 750px (Taro 标准)
- `config/index.js` 中的 `designWidth` 函数根据文件路径自动切换
## API 与请求约定
## 核心 API 模式
### 响应判断
### API 响应格式
所有接口统一按下面结构处理:
所有 API 响应遵循以下结构:
```javascript
{
code: 1, // 1 = 成功,其他值 = 失败
data: {...}, // 响应数据
msg: "message" // 错误/成功消息
code: 1,
data: {},
msg: '...'
}
```
**始终检查** `res.code === 1`(而不是 `res.code`)来判断成功
必须显式判断 `res.code === 1`,不要写成 `if (res.code)`
### 认证机制 (sessionid)
### sessionid 机制
**关键**: 项目使用 `sessionid` 进行认证(存储在 `wx.storage` 中):
项目通过 `sessionid` 做服务端认证:
1. **获取**: `src/utils/request.js:23-30` - `getSessionId()``wx.getStorageSync("sessionid")` 读取
2. **设置**: 在 `miniProgramAuthAPI``loginAPI` 成功后设置
3. **使用**: 请求拦截器 (`request.js:75-78`) 设置 `config.headers.cookie = sessionid`
4. **清除**: 收到 401 响应或用户登出时
1. `src/utils/request.js` 每次请求前动态从 storage 读取 `sessionid`
2. 请求头通过 `config.headers.cookie = sessionid` 透传
3. 401 由现有登录/静默授权流程接管
⚠️ **重要**: sessionid **不**由前端用于判断登录状态(后端通过 401 响应来判断)。它只是传递给服务器的凭证。
重要约定:
### 请求拦截器 (src/utils/request.js:66-80)
- 前端不要把 `sessionid` 当成“能否继续业务流程”的唯一判断条件
- 是否能继续业务流程,应看接口结果和页面自己的业务条件
```javascript
service.interceptors.request.use(config => {
// 动态获取 sessionid 并设置到请求头
const sessionid = getSessionId();
if (sessionid) {
config.headers.cookie = sessionid;
}
return config;
})
```
### API 文件职责边界
### API 模块模式 (src/api/)
`src/api/*.js` 只放接口定义和请求函数,不要把参数拼装、流程判断、字段归一化 helper 塞进去。
每个 API 文件导出调用中央 `fn()` 辅助函数的函数
正确做法
```javascript
// src/api/common.js
export const smsAPI = (params) => fn(fetch.post(Api.SMS, params));
```
- API 请求函数放 `src/api/`
- payload 构建、字段解析、return_url 处理、地理围栏计算等 helper 放 `src/utils/`
关键 API 模块:
- `common.js` - 短信验证码、上传凭证
- `user.js` - 用户认证和个人信息
- `family.js` - 家庭管理
- `points.js` - 积分/奖励系统
- `photo.js` - 照片/媒体处理
- `organization.js` - 组织管理
当前已经落地的例子:
## Taro 小程序限制
- `src/utils/userProfile.js`
- `buildUpdateUserProfilePayload`
- `isUserProfileComplete`
- `src/utils/scanCheckin.js`
- 负责从二维码内容里解析 `activity_id` / `detail_id`
- `src/utils/returnUrl.js`
- 负责 `return_url` 解码和拼接
- `src/utils/checkinLocation.js`
- 负责地理围栏判断
### ❌ 禁止使用 Web API
## 当前扫码打卡链路
```javascript
// 禁止 - 在小程序中会崩溃
window.document.getElementById()
localStorage
window.location.href
fetch()
```
这是现在最需要保持稳定的一条业务链路,后续改动请先看清楚,不要只盯某一个页面。
### ✅ 必须使用 Taro API
### 页面与接口
```javascript
// 正确 - 使用 Taro 等价 API
Taro.createSelectorQuery()
Taro.getStorage() / Taro.setStorage()
Taro.navigateTo()
Taro.request()
```
- 列表页:`src/pages/ScanCheckinList/index.vue`
- 使用 `getScanStageListAPI`
- 详情页:`src/pages/ScanCheckinDetail/index.vue`
- 使用 `getScanStageDetailAPI`
- 使用 `submitScanCheckinAPI`
- 接口定义集中在 `src/api/map.js`
### 页面生命周期(使用 Taro Hooks)
### 当前流程
```javascript
import { useLoad, useShow, useReady } from '@tarojs/taro'
1. 用户从活动或二维码入口进入扫码打卡相关页面
2. 扫码链接会带 `activityId`,详情页自身还可能带 `reg_source``reg_stage_id`
3. `ScanCheckinDetail` 点击“扫码打卡”时,先按现有路线检查“是否已有家庭”
4. 没有家庭时,不直接扫码,先弹提示,再跳 `Welcome`
5. `Welcome` 再决定是否先补资料、再创建家庭或加入家庭
6. 完成资料和家庭链路后,通过 `return_url` 回到原扫码详情页
7. 用户再次点击“扫码打卡”时:
- 重新静默获取当前位置
- 如果该关卡开启地理围栏,则先判断是否在范围内
- 调起微信扫码
- 从二维码结果里解析真实的 `activity_id` / `detail_id`
- 调用打卡接口
8. 打卡成功后跳转到 `ScanCheckinList`
useLoad((options) => {
// 页面加载(仅触发一次)- 适合获取路由参数
})
### 关键业务约束
useShow(() => {
// 页面显示(每次显示都触发)- 适合刷新数据
})
- 打卡提交参数来自二维码内容,不来自详情页当前路由参数
- 详情页路由参数主要用于展示、回跳和列表跳转
- 注册来源归因字段当前只保留:
- `reg_source`
- `reg_stage_id`
- `reg_activity_id` 已经不再需要,不要再传
- “是否补全资料”的判断交给 `Welcome` 链路,不要在 `ScanCheckinDetail` 再复制一套资料完整性分支
useReady(() => {
// 页面首次渲染完成
})
```
## return_url 回跳约定
### ❌ 页面中避免使用 Vue 生命周期
扫码打卡目前依赖多页串联回跳,任何一个页面处理不对,都会出现跳错页、路径双重编码、甚至 `redirectTo` 找不到页面。
```javascript
// 不要使用 - 可能无法正常工作
onMounted(() => { ... })
onUnmounted(() => { ... })
```
当前约定:
## 组件指南
- 统一使用 `src/utils/returnUrl.js`
- 页面收到 `options.return_url` 后,先走 `normalizeReturnUrl`
- 页面拼接下一个页面地址时,优先走 `appendReturnUrlParam`
- 不要在页面里到处手写 `decodeURIComponent(options.return_url || '')`
### 页面结构
当前受这个约束影响较大的页面:
每个页面目录包含:
- `index.vue` - 页面组件(必须使用 `<script setup>`
- `index.config.js` - 页面特定配置(navigationBarTitleText 等)
- `index.less` - 页面特定样式(scoped)
- `src/pages/Welcome/index.vue`
- `src/pages/AddProfile/index.vue`
- `src/pages/CreateFamily/index.vue`
- `src/pages/JoinFamily/index.vue`
### 组件命名规范
## 注册资料与来源归因
- **页面**: 目录名(如 `pages/Dashboard/`
- **组件**: PascalCase 多单词命名(如 `PointsCollector.vue``FamilyAlbum.vue`
- **API 文件**: camelCase(如 `miniProgramAuthAPI`
`buildUpdateUserProfilePayload` 目前已经抽到 `src/utils/userProfile.js`,专门负责把页面表单转成接口 payload。
### NutUI 自动导入
当前约定:
NutUI 组件通过 `unplugin-vue-components` 自动导入。**不要**手动导入:
- `user.js` 里只保留接口调用
- `buildUpdateUserProfilePayload` 不回迁到 API 文件
- 注册来源字段在补资料接口里继续透传:
- `reg_source`
- `reg_stage_id`
- `reg_stage_id` 需要按数值类型传给后端
```vue
<!-- ✅ 正确 - 自动导入 -->
<template>
<nut-button type="primary">点击</nut-button>
</template>
## 地理围栏约定
<!-- ❌ 错误 - 不要导入 -->
<script setup>
import { Button } from '@nutui/nutui-taro'
</script>
```
扫码详情页如果 `geo_enabled === true`,必须做范围判断。
## 样式
当前实现约定:
### TailwindCSS + Less 混合使用
- 判断逻辑统一放 `src/utils/checkinLocation.js`
- 页面层只负责调用,不要在页面里自己算经纬度距离
- 点击“扫码打卡”时重新调用 `Taro.getLocation()`,不依赖旧缓存
- 关卡详情接口字段:
- `geo_enabled`
- `center_lng`
- `center_lat`
- `radius_meters`
- **TailwindCSS**: 用于布局、间距、颜色、排版(80% 的样式)
- **Less**: 用于组件特定样式、动画、深度选择器(20%)
## 富文本与轮播图约定
### Tailwind 配置
扫码详情页当前已经不是单图和原生富文本直出:
- **Content**: `./src/**/*.{html,js,ts,jsx,tsx,vue}` (tailwind.config.js:13)
- **Preflight**: 禁用(小程序不需要)
- **rem → rpx**: 由 `weapp-tailwindcss` 插件处理 (rem2rpx: true)
- 顶部 banner 使用 NutUI `nut-swiper`
- 富文本显示统一走 `src/components/RichTextRenderer.vue`
### 样式指南
后续如果再改扫码详情页:
```vue
<style lang="less" scoped>
/* ✅ 组件必须使用 scoped */
.page-container {
padding: 30px;
}
- 不要再改回单张 `<image>`
- 不要再临时拼一个简化版富文本组件替代现有 `RichTextRenderer`
/* ✅ 使用 Less 处理深度选择器 */
.custom-element :deep(.nut-popup) {
background-color: #fff;
}
</style>
```
## 页面开发约定
## 状态管理 (Pinia)
### 生命周期
### Store 模式
页面优先使用 Taro 生命周期:
```javascript
// src/stores/host.js
import { defineStore } from 'pinia'
export const hostStore = defineStore('host', {
state: () => ({
id: '',
join_id: ''
}),
actions: {
add(id) {
this.id = id
}
}
})
import { useLoad, useShow, useDidShow, useReady } from '@tarojs/taro'
```
### 在组件中使用
尽量避免把页面主流程写到 Vue 的 `onMounted` / `onUnmounted` 里。
```vue
<script setup>
import { hostStore } from '@/stores/host'
### 页面结构
const host = hostStore()
host.add('123')
</script>
```
每个页面目录通常包含:
## 常用模式
- `index.vue`
- `index.config.js`
- `index.less`
### 页面导航
新增页面后必须同步注册到 `src/app.config.js`
```javascript
import Taro from '@tarojs/taro'
// 跳转到页面
Taro.navigateTo({
url: '/pages/Detail/index?id=123'
})
// 重定向(无返回)
Taro.redirectTo({
url: '/pages/Login/index'
})
// 切换 Tab
Taro.switchTab({
url: '/pages/Dashboard/index'
})
// 获取路由参数
useLoad((options) => {
const { id } = options
})
```
### 小程序 API 约束
### 本地存储
不要在页面或工具函数里使用浏览器 API,例如:
```javascript
// 异步(推荐)
await Taro.setStorage({ key: 'user', data: userInfo })
const { data } = await Taro.getStorage({ key: 'user' })
- `window`
- `document`
- `localStorage`
- 原生 `fetch`
// 同步(谨慎使用)
Taro.setStorageSync('token', 'xxxx')
const token = Taro.getStorageSync('token')
```
统一使用 Taro API,例如:
### 提示/弹窗
- `Taro.navigateTo`
- `Taro.redirectTo`
- `Taro.switchTab`
- `Taro.getStorage`
- `Taro.scanCode`
- `Taro.getLocation`
```javascript
// Toast 提示
Taro.showToast({
title: '操作成功',
icon: 'success',
duration: 2000
})
// Modal 弹窗
Taro.showModal({
title: '提示',
content: '确定删除吗?',
success: (res) => {
if (res.confirm) {
// 用户点击了确定
}
}
})
```
## 样式约定
## 页面注册
- NutUI 组件自动导入,不要手动 import
- 页面样式继续保持 Tailwind + Less 混合方式
- Less 样式默认 `scoped`
- 深层覆盖 NutUI 时使用 `:deep(...)`
页面在 `src/app.config.js` 中注册:
```javascript
export default {
pages: [
'pages/Dashboard/index',
'pages/MyFamily/index',
'pages/Activities/index',
// ... 更多页面
]
}
```
这个仓库已经有一个和扫码页相关的稳定样式经验:
**创建新页面时**: 必须将其添加到此数组中。
- `ScanCheckinDetail` 底部按钮采用固定定位和安全区留白
- 如果只是调整按钮视觉位置,优先做 CSS 修改,不要顺手改业务逻辑
## 构建输出
## 权限与配置
- **开发环境**: `dist/` 目录
- **微信开发者工具**: 打开 `dist/` 作为项目根目录
`src/app.config.js` 当前已经声明了位置权限:
## 重要文件说明
- `requiredPrivateInfos: ['getLocation']`
- `permission['scope.userLocation']`
### `src/utils/request.js`
所以如果你在扫码打卡里继续用定位能力,优先复用现有授权前提,不要再设计一套重复授权流程。
核心 HTTP 客户端,包含:
- SessionID 注入
- 401 响应处理
- 401 时静默授权重定向
- 错误处理
## 修改建议
### `src/utils/authRedirect.js`
### 适合抽到 utils 的逻辑
处理小程序登录流程的静默授权。
- 二维码参数解析
- 距离计算和范围判断
- return_url 编解码
- 用户资料 payload 构建
- 纯字段映射或判空逻辑
### `src/utils/tools.js`
### 不适合放进 API 文件的逻辑
通用工具函数:
- `formatDate()` - 使用 moment.js 格式化日期
- `wxInfo()` - 平台检测(Android/iOS/微信)
- `hasEllipsis()` - 文本溢出检测
- 表单转 payload
- 页面跳转流程判断
- 二维码内容解析
- 路由参数兼容处理
- 页面专用字段映射
## 开发注意事项
### 做改动前先检查
1. **始终使用 Taro API** 而非 Web API
2. **检查 `res.code === 1`** 判断 API 成功(不是 `res.code`
3. **NutUI 组件已自动导入** - 不要手动导入
4. **页面中使用 Taro 生命周期钩子**`useLoad``useShow`
5. **SessionID 动态获取** - 每次请求从存储中读取
6. **已配置路径别名** - 使用 `@/components` 代替相对路径
7. **设计宽度双模式**: NutUI 使用 375px,其他使用 750px
- 这是页面职责,还是工具职责
- 这是接口请求定义,还是接口参数拼装
- 这是详情页展示参数,还是打卡提交真实参数
- 这是当前页下一跳,还是完整链路里的回跳页面
## 平台差异
## 交付标准
项目通过 Taro 支持多平台:
- **微信 (weapp)**: 主要目标平台
- **H5**: Web 浏览器版本
- **支付宝 (alipay)**: 支付宝小程序
- **抖音 (tt)**: 字节跳动小程序
在这个仓库里完成修改时,优先做到:
平台特定代码可使用:
```javascript
if (process.env.TARO_ENV === 'weapp') {
// 微信特定代码
}
```
- 与当前真实业务链路一致
- 不在页面里复制已有 helper
- 不在 API 文件里塞 helper
- 不破坏 `return_url` 回跳
- 不破坏扫码打卡的地理围栏与注册来源归因
- 不把 Taro 小程序页面写成浏览器页面
......
## 项目介绍
# lls_program
基于Taro4的微信小程序模版,集成了常用的功能,如登录、注册、列表、详情、购物车等。
`lls_program` 是一个基于 Taro 4 + Vue 3 + NutUI 的微信小程序项目,当前业务围绕家庭管理、活动参与、积分奖励,以及海报/扫码打卡展开。
这不是一个通用模板仓库。当前代码里已经有比较明确的业务结构,尤其是扫码打卡链路、资料补全链路、家庭创建/加入链路,后续开发建议直接沿用现有实现方式,而不是重新搭一套平行流程。
## 技术栈
- Taro4
- Vue3
- TypeScript
- Pinia
- Taro `4.1.7`
- Vue `3.3`
- NutUI Taro `4.3.13`
- Pinia `3.0`
- TailwindCSS `3.4`
- Less
- axios-miniprogram
- Vitest
## 核心功能
- 微信小程序登录与静默授权
- 家庭创建、加入、家庭资料维护
- 用户资料补全与注册来源归因
- 活动详情、海报打卡、扫码打卡
- 积分、奖励、优惠券相关页面
- 地理位置相关能力与位置权限接入
## 当前重点业务链路
### 扫码打卡
当前扫码打卡已接入真实接口,主要页面和工具如下:
- 列表页:`src/pages/ScanCheckinList/index.vue`
- 详情页:`src/pages/ScanCheckinDetail/index.vue`
- API:`src/api/map.js`
- 地理围栏:`src/utils/checkinLocation.js`
- 扫码参数解析:`src/utils/scanCheckin.js`
- 回跳参数处理:`src/utils/returnUrl.js`
- 富文本渲染:`src/components/RichTextRenderer.vue`
当前流程:
1. 用户进入扫码打卡详情页
2. 点击“扫码打卡”时先检查是否已有家庭
3. 没有家庭则提示后跳转 `Welcome`
4. `Welcome -> AddProfile -> Welcome -> CreateFamily/JoinFamily -> 原详情页`
5. 返回详情页后再次扫码
6. 若关卡启用地理围栏,先重新静默获取当前位置并判断范围
7. 调起微信扫码
8. 从二维码结果中解析真实的 `activity_id` / `detail_id`
9. 调用真实打卡接口
10. 成功后跳到扫码关卡列表页
补充约定:
- 打卡参数来自二维码内容,不来自当前页面路由参数
- 注册来源归因目前通过 `reg_source``reg_stage_id` 透传
- `reg_activity_id` 已经不再使用
- `return_url` 回跳统一通过 `src/utils/returnUrl.js` 处理
## 项目结构
- src
- api:请求接口
- assets:静态资源
- components:全局组件
- config:项目配置
- pages:页面
- stores:状态管理
- utils:工具函数
- app.config.js:项目配置
- app.js:应用入口
- app.less:全局样式
- taro.config.js:Taro配置
- tsconfig.json:TypeScript配置
- package.json:依赖配置
## 项目运行
1. 安装依赖
```text
src/
├── api/ # 接口定义,只放请求相关代码
├── assets/ # 静态资源
├── components/ # 通用组件
├── pages/ # 页面
├── stores/ # Pinia 状态管理
├── utils/ # 工具函数、流程辅助
├── app.config.js # 页面注册、权限声明
└── app.less # 全局样式
```
几个常用目录说明:
- `src/api/user.js`:用户认证、资料接口
- `src/api/family.js`:家庭相关接口
- `src/api/map.js`:地图、扫码关卡、扫码打卡相关接口
- `src/utils/request.js`:请求拦截器、`sessionid` 注入
- `src/utils/userProfile.js`:资料相关 helper
- `src/components/RichTextRenderer.vue`:富文本渲染组件
## 开发约定
### API 与 helper 分层
- `src/api/*.js` 只放接口调用
- 参数拼装、字段解析、距离计算、回跳处理等逻辑放 `src/utils/`
例如:
- `buildUpdateUserProfilePayload``src/utils/userProfile.js`
- 扫码结果解析在 `src/utils/scanCheckin.js`
- 地理围栏判断在 `src/utils/checkinLocation.js`
### 请求返回判断
所有接口都要显式判断:
```javascript
if (res.code === 1) {
// success
}
```
不要写成:
```javascript
if (res.code) {
// 不推荐
}
```
### 小程序环境约束
页面里不要使用浏览器 API:
- `window`
- `document`
- `localStorage`
- 原生 `fetch`
统一使用 Taro API:
- `Taro.navigateTo`
- `Taro.redirectTo`
- `Taro.switchTab`
- `Taro.scanCode`
- `Taro.getLocation`
### 页面生命周期
页面优先使用 Taro 提供的生命周期:
- `useLoad`
- `useShow`
- `useDidShow`
- `useReady`
## 登录与认证
项目当前通过 `sessionid` 维持服务端登录态:
- 从 storage 读取 `sessionid`
- 在请求拦截器中注入到 `cookie`
- 401 交给现有授权/跳转逻辑处理
需要注意:
- `sessionid` 只是认证凭证
- 它不等于“用户一定能继续后续业务流程”
- 业务上是否可继续,仍要结合资料、家庭、活动规则等条件判断
## 本地开发
### 安装依赖
```bash
npm install
```
2. 运行项目
### 微信小程序开发
```bash
npm run dev:weapp
```
3. 打包项目
### 微信小程序构建
```bash
npm run build:weapp
```
### 其他常用命令
```bash
npm run dev:h5
npm run dev:alipay
npm run dev:tt
npm run lint
npm run format
npm run test:run
```
## 页面注册与权限
页面统一在 `src/app.config.js` 中注册。
当前已经声明位置相关能力:
- `requiredPrivateInfos: ['getLocation']`
- `permission.scope.userLocation`
这也是扫码打卡地理围栏能力能直接接上的基础配置。
## 备注
- NutUI 组件是自动导入的,不需要手动 import
- `ScanCheckinDetail` 顶部 banner 现在使用 NutUI `nut-swiper`
- `ScanCheckinDetail` 富文本展示统一使用 `RichTextRenderer`
- 如果只是调整扫码详情页底部按钮视觉位置,优先改样式,不要顺手改业务逻辑
......