|
@@ -0,0 +1,310 @@
|
|
|
|
|
+# 家庭成员渠道选择 Implementation Plan
|
|
|
|
|
+
|
|
|
|
|
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
|
|
|
+
|
|
|
|
|
+**Goal:** 在小程序新增/编辑家庭成员页面中,新增渠道单选项,从会员渠道列表中选择,保存时同步设置成员渠道。
|
|
|
|
|
+
|
|
|
|
|
+**Architecture:** 仅修改 `pagesMy/familyMemberVindicate.vue` 一个文件。页面加载时并行获取会员渠道列表和成员当前渠道(编辑时),使用原生 `<picker>` 组件实现单选,保存成员后调 `POST /app/member/setChannels` 同步渠道。
|
|
|
|
|
+
|
|
|
|
|
+**Tech Stack:** uni-app Vue 3 Composition API、微信小程序原生 picker、uview-plus
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 文件变更清单
|
|
|
|
|
+
|
|
|
|
|
+| 操作 | 文件 |
|
|
|
|
|
+|------|------|
|
|
|
|
|
+| Modify | `SxsnWechat/pagesMy/familyMemberVindicate.vue` |
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+### Task 1: 加载会员渠道列表 + 编辑时回显成员渠道
|
|
|
|
|
+
|
|
|
|
|
+**Files:**
|
|
|
|
|
+- Modify: `SxsnWechat/pagesMy/familyMemberVindicate.vue`
|
|
|
|
|
+
|
|
|
|
|
+背景:
|
|
|
|
|
+- `GET /app/channel/list` 返回 `{ code: 0, data: [{ id, channelName, type, code }] }`
|
|
|
|
|
+- `GET /app/member/channelList?memberId=xxx` 返回 `{ code: 0, data: [{ id, channelName }] }`,取第一个作为已选渠道(单选)
|
|
|
|
|
+- `proxy.$api.get(url, params)` 是项目封装的 GET 请求,返回 `{ data: res }`
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 1: 在 script 中新增渠道相关响应式变量**
|
|
|
|
|
+
|
|
|
|
|
+在 `familyMemberVindicate.vue` 的 `<script setup>` 中,在 `const qicShow = ref(false)` 之后插入:
|
|
|
|
|
+
|
|
|
|
|
+```js
|
|
|
|
|
+// 渠道相关
|
|
|
|
|
+const channelList = ref([]) // 会员渠道列表(可选项)
|
|
|
|
|
+const selectedChannelId = ref('') // 已选渠道 ID(字符串)
|
|
|
|
|
+const selectedChannelIndex = ref(-1) // picker 当前选中下标,-1 表示未选
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 2: 新增 loadChannelList 函数**
|
|
|
|
|
+
|
|
|
|
|
+在 `const changeMember = ...` 之前插入:
|
|
|
|
|
+
|
|
|
|
|
+```js
|
|
|
|
|
+const loadChannelList = () => {
|
|
|
|
|
+ proxy.$api.get('/app/channel/list', {}).then(({ data: res }) => {
|
|
|
|
|
+ if (res.code !== 0) return
|
|
|
|
|
+ channelList.value = res.data || []
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 3: 新增 loadMemberChannel 函数(编辑时回显)**
|
|
|
|
|
+
|
|
|
|
|
+在 `loadChannelList` 之后插入:
|
|
|
|
|
+
|
|
|
|
|
+```js
|
|
|
|
|
+const loadMemberChannel = (memberId) => {
|
|
|
|
|
+ proxy.$api.get('/app/member/channelList', { memberId }).then(({ data: res }) => {
|
|
|
|
|
+ if (res.code !== 0) return
|
|
|
|
|
+ const list = res.data || []
|
|
|
|
|
+ if (list.length > 0) {
|
|
|
|
|
+ selectedChannelId.value = String(list[0].id)
|
|
|
|
|
+ // 等 channelList 加载完后同步 index
|
|
|
|
|
+ const idx = channelList.value.findIndex(c => String(c.id) === selectedChannelId.value)
|
|
|
|
|
+ if (idx >= 0) selectedChannelIndex.value = idx
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 4: 在 onLoad 中调用**
|
|
|
|
|
+
|
|
|
|
|
+找到 `onLoad((options) => {` 块,修改为:
|
|
|
|
|
+
|
|
|
|
|
+```js
|
|
|
|
|
+onLoad((options) => {
|
|
|
|
|
+ turnType.value = options.type
|
|
|
|
|
+ loadChannelList()
|
|
|
|
|
+ const id = options?.id
|
|
|
|
|
+ if (id) {
|
|
|
|
|
+ title.value = '编辑家庭成员'
|
|
|
|
|
+ getMemberInfo(id)
|
|
|
|
|
+ loadMemberChannel(id)
|
|
|
|
|
+ }
|
|
|
|
|
+})
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 5: Commit**
|
|
|
|
|
+
|
|
|
|
|
+```bash
|
|
|
|
|
+cd /workspace/sxsn/SxsnWechat
|
|
|
|
|
+git add pagesMy/familyMemberVindicate.vue
|
|
|
|
|
+git commit -m "feat: 加载会员渠道列表,编辑时回显成员渠道"
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+### Task 2: 新增渠道选择 UI 行
|
|
|
|
|
+
|
|
|
|
|
+**Files:**
|
|
|
|
|
+- Modify: `SxsnWechat/pagesMy/familyMemberVindicate.vue`
|
|
|
|
|
+
|
|
|
|
|
+背景:
|
|
|
|
|
+- 现有 `pre` 行结构:`<view class="pre adfac">` 包含 `pre-title`(左,209rpx)和 `pre-content`(右,flex-end)
|
|
|
|
|
+- 使用微信小程序原生 `<picker>` 组件,`mode="selector"`,`:range="channelList"`,`:range-key="'channelName'"`
|
|
|
|
|
+- `picker` 的 `@change` 事件返回 `e.detail.value`(选中下标)
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 1: 在「就读学校」行之后插入渠道选择行**
|
|
|
|
|
+
|
|
|
|
|
+找到:
|
|
|
|
|
+```html
|
|
|
|
|
+<view class="pre adfac" v-if="memberInfo.personnelType==2">
|
|
|
|
|
+ <view class="pre-title"><span>*</span>就读学校</view>
|
|
|
|
|
+ <view class="pre-content">
|
|
|
|
|
+ <input v-model="memberInfo.currentSchool" ... />
|
|
|
|
|
+ </view>
|
|
|
|
|
+</view>
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+在其后插入:
|
|
|
|
|
+
|
|
|
|
|
+```html
|
|
|
|
|
+<view class="pre adfac">
|
|
|
|
|
+ <view class="pre-title"><span style="color: #FFFFFF;">*</span>渠道</view>
|
|
|
|
|
+ <view class="pre-content">
|
|
|
|
|
+ <picker
|
|
|
|
|
+ v-if="channelList.length > 0"
|
|
|
|
|
+ mode="selector"
|
|
|
|
|
+ :range="channelList"
|
|
|
|
|
+ range-key="channelName"
|
|
|
|
|
+ :value="selectedChannelIndex"
|
|
|
|
|
+ @change="onChannelChange"
|
|
|
|
|
+ >
|
|
|
|
|
+ <view style="font-size: 30rpx; color: #252525; text-align: right;">
|
|
|
|
|
+ {{ selectedChannelIndex >= 0 ? channelList[selectedChannelIndex].channelName : '请选择渠道' }}
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </picker>
|
|
|
|
|
+ <view v-else style="font-size: 28rpx; color: #A1A4A9; text-align: right;">暂无可用渠道</view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+</view>
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 2: 新增 onChannelChange 方法**
|
|
|
|
|
+
|
|
|
|
|
+在 `loadMemberChannel` 之后插入:
|
|
|
|
|
+
|
|
|
|
|
+```js
|
|
|
|
|
+const onChannelChange = (e) => {
|
|
|
|
|
+ const idx = e.detail.value
|
|
|
|
|
+ selectedChannelIndex.value = idx
|
|
|
|
|
+ selectedChannelId.value = String(channelList.value[idx].id)
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 3: 手动验证 UI**
|
|
|
|
|
+
|
|
|
|
|
+在微信开发者工具中打开「添加家庭成员」页面,确认:
|
|
|
|
|
+- 渠道行出现在「就读学校」之后
|
|
|
|
|
+- 点击渠道行弹出 picker,可选择渠道
|
|
|
|
|
+- 选中后右侧显示渠道名称
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 4: Commit**
|
|
|
|
|
+
|
|
|
|
|
+```bash
|
|
|
|
|
+cd /workspace/sxsn/SxsnWechat
|
|
|
|
|
+git add pagesMy/familyMemberVindicate.vue
|
|
|
|
|
+git commit -m "feat: 家庭成员表单新增渠道选择行"
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+### Task 3: 保存时同步设置成员渠道
|
|
|
|
|
+
|
|
|
|
|
+**Files:**
|
|
|
|
|
+- Modify: `SxsnWechat/pagesMy/familyMemberVindicate.vue`
|
|
|
|
|
+
|
|
|
|
|
+背景:
|
|
|
|
|
+- `POST /app/member/setChannels` 参数:`memberId`(Long)、`channelIds`(String,逗号分隔,单选时只有一个 ID)
|
|
|
|
|
+- 新增成员时,`POST /core/family/member` 成功后响应 `res.data` 包含新成员的 `id`
|
|
|
|
|
+- 编辑成员时,`memberInfo.value.id` 已有值
|
|
|
|
|
+- 若 `selectedChannelId` 为空字符串,`channelIds` 传 `''`(清空成员渠道,继承家长)
|
|
|
|
|
+- `setChannels` 失败只 Toast 提示,不阻断跳转
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 1: 新增 setMemberChannel 辅助函数**
|
|
|
|
|
+
|
|
|
|
|
+在 `onChannelChange` 之后插入:
|
|
|
|
|
+
|
|
|
|
|
+```js
|
|
|
|
|
+const setMemberChannel = (memberId) => {
|
|
|
|
|
+ proxy.$api.post('/app/member/setChannels', {
|
|
|
|
|
+ memberId,
|
|
|
|
|
+ channelIds: selectedChannelId.value
|
|
|
|
|
+ }).then(({ data: res }) => {
|
|
|
|
|
+ if (res.code !== 0) proxy.$showToast(res.msg)
|
|
|
|
|
+ }).catch(() => {
|
|
|
|
|
+ proxy.$showToast('渠道设置失败,请稍后重试')
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 2: 修改 handleSave 中的保存逻辑**
|
|
|
|
|
+
|
|
|
|
|
+找到 `handleSave` 中的接口调用部分:
|
|
|
|
|
+
|
|
|
|
|
+```js
|
|
|
|
|
+proxy.$api[memberInfo.value.id?'put':'post']('/core/family/member',memberInfo.value).then(({data:res})=>{
|
|
|
|
|
+ if(res.code!==0) return proxy.$showToast(res.msg)
|
|
|
|
|
+ proxy.$showToast(memberInfo.value.id?'编辑成功':'添加成功')
|
|
|
|
|
+ setTimeout(()=>{
|
|
|
|
|
+ if(turnType.value==='my'){
|
|
|
|
|
+ uni.reLaunch({ url:'/pages/my' })
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ uni.redirectTo({ url:'/pagesMy/familyMember' })
|
|
|
|
|
+ },1000)
|
|
|
|
|
+})
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+替换为:
|
|
|
|
|
+
|
|
|
|
|
+```js
|
|
|
|
|
+proxy.$api[memberInfo.value.id ? 'put' : 'post']('/core/family/member', memberInfo.value).then(({ data: res }) => {
|
|
|
|
|
+ if (res.code !== 0) return proxy.$showToast(res.msg)
|
|
|
|
|
+ // 设置成员渠道(新增时用返回的 id,编辑时用已有 id)
|
|
|
|
|
+ const memberId = memberInfo.value.id || res.data?.id
|
|
|
|
|
+ if (memberId) {
|
|
|
|
|
+ setMemberChannel(memberId)
|
|
|
|
|
+ }
|
|
|
|
|
+ proxy.$showToast(memberInfo.value.id ? '编辑成功' : '添加成功')
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ if (turnType.value === 'my') {
|
|
|
|
|
+ uni.reLaunch({ url: '/pages/my' })
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ uni.redirectTo({ url: '/pagesMy/familyMember' })
|
|
|
|
|
+ }, 1000)
|
|
|
|
|
+})
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 3: 手动验证保存流程**
|
|
|
|
|
+
|
|
|
|
|
+在微信开发者工具中:
|
|
|
|
|
+1. 新增家庭成员,选择渠道,保存 → 确认 Network 中有 `setChannels` 请求,参数正确
|
|
|
|
|
+2. 编辑已有成员,修改渠道,保存 → 确认 `setChannels` 请求发出
|
|
|
|
|
+3. 编辑成员,不选渠道,保存 → 确认 `setChannels` 请求中 `channelIds` 为空字符串
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 4: Commit**
|
|
|
|
|
+
|
|
|
|
|
+```bash
|
|
|
|
|
+cd /workspace/sxsn/SxsnWechat
|
|
|
|
|
+git add pagesMy/familyMemberVindicate.vue
|
|
|
|
|
+git commit -m "feat: 保存家庭成员时同步设置渠道"
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+### Task 4: 编辑时 channelList 加载后同步 selectedChannelIndex
|
|
|
|
|
+
|
|
|
|
|
+**Files:**
|
|
|
|
|
+- Modify: `SxsnWechat/pagesMy/familyMemberVindicate.vue`
|
|
|
|
|
+
|
|
|
|
|
+背景:`loadChannelList` 和 `loadMemberChannel` 是并行请求,`loadMemberChannel` 可能在 `channelList` 还未填充时就执行了 `findIndex`,导致 index 找不到(返回 -1)。需要在 `loadChannelList` 完成后,若 `selectedChannelId` 已有值,补充同步 index。
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 1: 修改 loadChannelList,加载完成后同步 index**
|
|
|
|
|
+
|
|
|
|
|
+将 `loadChannelList` 修改为:
|
|
|
|
|
+
|
|
|
|
|
+```js
|
|
|
|
|
+const loadChannelList = () => {
|
|
|
|
|
+ proxy.$api.get('/app/channel/list', {}).then(({ data: res }) => {
|
|
|
|
|
+ if (res.code !== 0) return
|
|
|
|
|
+ channelList.value = res.data || []
|
|
|
|
|
+ // 若 loadMemberChannel 已先完成并设置了 selectedChannelId,补充同步 index
|
|
|
|
|
+ if (selectedChannelId.value) {
|
|
|
|
|
+ const idx = channelList.value.findIndex(c => String(c.id) === selectedChannelId.value)
|
|
|
|
|
+ if (idx >= 0) selectedChannelIndex.value = idx
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 2: 手动验证编辑回显**
|
|
|
|
|
+
|
|
|
|
|
+在微信开发者工具中打开已有家庭成员的编辑页面,确认:
|
|
|
|
|
+- 渠道行正确显示该成员已设置的渠道名称(而非「请选择渠道」)
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Step 3: Commit**
|
|
|
|
|
+
|
|
|
|
|
+```bash
|
|
|
|
|
+cd /workspace/sxsn/SxsnWechat
|
|
|
|
|
+git add pagesMy/familyMemberVindicate.vue
|
|
|
|
|
+git commit -m "fix: 修复编辑时渠道回显竞态问题"
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 自检清单
|
|
|
|
|
+
|
|
|
|
|
+- [x] 规格「新增/编辑时显示渠道选择行」→ Task 2
|
|
|
|
|
+- [x] 规格「渠道单选,从会员渠道列表中选择」→ Task 1(loadChannelList)+ Task 2(picker)
|
|
|
|
|
+- [x] 规格「编辑时回显已设置渠道」→ Task 1(loadMemberChannel)+ Task 4(竞态修复)
|
|
|
|
|
+- [x] 规格「保存时调 setChannels」→ Task 3
|
|
|
|
|
+- [x] 规格「渠道非必填」→ Task 2(无必填校验)+ Task 3(memberId 为空时跳过)
|
|
|
|
|
+- [x] 规格「渠道列表为空时显示暂无可用渠道」→ Task 2(v-else)
|
|
|
|
|
+- [x] 规格「setChannels 失败只 Toast,不阻断跳转」→ Task 3 Step 1(catch 只 Toast)
|
|
|
|
|
+- [x] 无 placeholder,所有步骤含完整代码
|
|
|
|
|
+- [x] `selectedChannelId` 类型统一为字符串(String(c.id)),避免雪花 ID 精度问题
|