hookehuyr

docs: 新增头像修改页面接口记录

- 更新接口联调工作记录文档,新增通用模块接口
  - qiniuTokenAPI: 获取七牛上传 Token
  - saveFileAPI: 保存文件信息到服务器
  - uploadImageToQiniuAPI: 完整的图片上传流程(封装接口)
- 更新 CHANGELOG.md,记录头像页面实现情况
- 更新总体进度:14个接口(5个已完成,9个后端开发中)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
...@@ -42,11 +42,34 @@ ...@@ -42,11 +42,34 @@
42 42
43 ## 📊 快速统计 43 ## 📊 快速统计
44 44
45 -- **总变更数**: 58+ 45 +- **总变更数**: 59+
46 - **新增功能**: 26+ 46 - **新增功能**: 26+
47 - **优化改进**: 28+ 47 - **优化改进**: 28+
48 - **问题修复**: 13+ 48 - **问题修复**: 13+
49 -- **文档更新**: 12+ 49 +- **文档更新**: 13+
50 +
51 +---
52 +
53 +## [2026-02-03] - 新增头像修改页面接口记录
54 +
55 +### 文档
56 +- 更新接口联调工作记录文档 (`docs/api-integration-log.md`)
57 +- 新增通用模块接口记录(3个):
58 + - 获取七牛上传 Token (`qiniuTokenAPI`) - ⏳ 后端开发中
59 + - 保存文件信息 (`saveFileAPI`) - ⏳ 后端开发中
60 + - 上传图片到七牛云 (`uploadImageToQiniuAPI`) - ⏳ 后端开发中
61 +- 更新总体进度:14个接口(5个已完成,9个后端开发中)
62 +
63 +---
64 +
65 +**详细信息**
66 +- **影响文件**: docs/api-integration-log.md, src/pages/avatar/index.vue, src/api/common.js
67 +- **技术栈**: Vue 3, Taro, 七牛云
68 +- **测试状态**: ⏳ 后端开发中
69 +- **备注**:
70 + - 头像修改页面已实现前端逻辑
71 + - 图片上传流程:选择图片 → 七牛云上传 → 保存到服务器
72 + - 等待后端接口开发完成后联调
50 73
51 --- 74 ---
52 75
......
...@@ -4,10 +4,10 @@ ...@@ -4,10 +4,10 @@
4 4
5 ## 📊 总体进度 5 ## 📊 总体进度
6 6
7 -- **总接口数**: 10 7 +- **总接口数**: 14
8 -- **已完成**: 5 (50%) 8 +- **已完成**: 5 (35.7%)
9 - **联调中**: 0 (0%) 9 - **联调中**: 0 (0%)
10 -- **后端开发中**: 5 (50%) 10 +- **后端开发中**: 9 (64.3%)
11 - **有阻塞**: 0 11 - **有阻塞**: 0
12 12
13 --- 13 ---
...@@ -200,6 +200,104 @@ ...@@ -200,6 +200,104 @@
200 200
201 --- 201 ---
202 202
203 +### 通用模块
204 +
205 +#### 接口 1: 获取七牛上传 Token
206 +
207 +**接口信息**
208 +- **接口名称**: `qiniuTokenAPI`
209 +- **接口路径**: `/srv/?a=upload`
210 +- **请求方法**: POST
211 +- **负责页面**: `src/pages/avatar/index.vue`(待确认)
212 +- **负责人**: 后端团队
213 +
214 +**接口文档更新记录**
215 +
216 +| 日期 | 版本 | 变更内容 | 变更原因 | 文档链接 |
217 +|------|------|---------|---------|---------|
218 +| 2026-02-03 | v1.0 | 初始版本 | - | [查看](#) |
219 +
220 +**页面调试情况**
221 +
222 +| 日期 | 调试页面 | 问题记录 | 解决方案 | 状态 |
223 +|------|---------|---------|---------|------|
224 +| 2026-02-03 | - | 后端开发中 | - | ⏳ 后端开发中 |
225 +
226 +**接口状态**: ⏳ 后端开发中
227 +
228 +**备注**:
229 +- 参数:filename(文件名)、file(图片 base64)
230 +- 返回七牛上传 token、upload_url、filekey
231 +- 后端接口开发中
232 +
233 +---
234 +
235 +#### 接口 2: 保存文件信息
236 +
237 +**接口信息**
238 +- **接口名称**: `saveFileAPI`
239 +- **接口路径**: `/srv/?a=upload&t=save_file`
240 +- **请求方法**: POST
241 +- **负责页面**: `src/pages/avatar/index.vue`(待确认)
242 +- **负责人**: 后端团队
243 +
244 +**接口文档更新记录**
245 +
246 +| 日期 | 版本 | 变更内容 | 变更原因 | 文档链接 |
247 +|------|------|---------|---------|---------|
248 +| 2026-02-03 | v1.0 | 初始版本 | - | [查看](#) |
249 +
250 +**页面调试情况**
251 +
252 +| 日期 | 调试页面 | 问题记录 | 解决方案 | 状态 |
253 +|------|---------|---------|---------|------|
254 +| 2026-02-03 | - | 后端开发中 | - | ⏳ 后端开发中 |
255 +
256 +**接口状态**: ⏳ 后端开发中
257 +
258 +**备注**:
259 +- 参数:format(文件格式)、hash(文件 hash)、height(图片高)、width(图片宽)、filekey(文件 key)
260 +- 七牛上传成功后,将文件信息保存到服务器
261 +- 后端接口开发中
262 +
263 +---
264 +
265 +#### 接口 3: 上传图片到七牛云(完整流程)
266 +
267 +**接口信息**
268 +- **接口名称**: `uploadImageToQiniuAPI`
269 +- **接口路径**: 封装接口(内部调用 `qiniuTokenAPI`、七牛上传、`saveFileAPI`
270 +- **请求方法**: 封装函数
271 +- **负责页面**: `src/pages/avatar/index.vue`
272 +- **负责人**: 后端团队
273 +
274 +**接口文档更新记录**
275 +
276 +| 日期 | 版本 | 变更内容 | 变更原因 | 文档链接 |
277 +|------|------|---------|---------|---------|
278 +| 2026-02-03 | v1.0 | 初始版本 | - | [查看](#) |
279 +
280 +**页面调试情况**
281 +
282 +| 日期 | 调试页面 | 问题记录 | 解决方案 | 状态 |
283 +|------|---------|---------|---------|------|
284 +| 2026-02-03 | `src/pages/avatar/index.vue` | 后端开发中 | - | ⏳ 后端开发中 |
285 +
286 +**接口状态**: ⏳ 后端开发中
287 +
288 +**备注**:
289 +- 完整的上传流程:
290 + 1. 读取本地文件为 base64
291 + 2. 获取七牛上传 token(调用 `qiniuTokenAPI`
292 + 3. 上传到七牛云(调用 `qiniuUploadAPI`
293 + 4. 保存文件信息到服务器(调用 `saveFileAPI`
294 +- 参数:filePath(本地文件路径)
295 +- 返回:`{ code: 1, data: { src, hash, format, width, height } }`
296 +- 后端接口开发中
297 +- 实现位置:`src/api/common.js:77-150`
298 +
299 +---
300 +
203 ### 意见反馈模块 301 ### 意见反馈模块
204 302
205 #### 接口 1: 意见反馈列表 303 #### 接口 1: 意见反馈列表
...@@ -382,12 +480,12 @@ ...@@ -382,12 +480,12 @@
382 480
383 ## 📈 进度追踪 481 ## 📈 进度追踪
384 482
385 -### 本周进度 (2026-01-27 ~ 2026-02-02) 483 +### 本周进度 (2026-01-27 ~ 2026-02-03)
386 484
387 -- **新增接口**: 10 485 +- **新增接口**: 14
388 - **完成联调**: 5 486 - **完成联调**: 5
389 - **联调中**: 0 487 - **联调中**: 0
390 -- **后端开发中**: 5 488 +- **后端开发中**: 9
391 - **发现问题**: 0 489 - **发现问题**: 0
392 - **解决问题**: 0 490 - **解决问题**: 0
393 491
...@@ -395,7 +493,7 @@ ...@@ -395,7 +493,7 @@
395 493
396 | 周 | 完成数 | 新增数 | 问题数 | 494 | 周 | 完成数 | 新增数 | 问题数 |
397 |----|--------|--------|--------| 495 |----|--------|--------|--------|
398 -| 2026-01-27 ~ 2026-02-02 | 5 | 10 | 0 | 496 +| 2026-01-27 ~ 2026-02-03 | 5 | 14 | 0 |
399 497
400 --- 498 ---
401 499
...@@ -403,15 +501,18 @@ ...@@ -403,15 +501,18 @@
403 501
404 ### 按状态查看 502 ### 按状态查看
405 - [✅ 已完成](#用户中心模块) - 5个接口 503 - [✅ 已完成](#用户中心模块) - 5个接口
504 +- [⏳ 后端开发中](#用户中心模块) - 1个接口
505 +- [⏳ 后端开发中](#通用模块) - 3个接口
406 - [⏳ 后端开发中](#意见反馈模块) - 2个接口 506 - [⏳ 后端开发中](#意见反馈模块) - 2个接口
407 - [⏳ 后端开发中](#消息模块) - 2个接口 507 - [⏳ 后端开发中](#消息模块) - 2个接口
408 -- [⏳ 后端开发中](#用户中心模块) - 1个接口 508 +- [⏳ 后端开发中](#首页模块) - 1个接口
409 509
410 ### 按模块查看 510 ### 按模块查看
411 - [用户中心](#用户中心模块) - ✅ 5个已完成, ⏳ 1个后端开发中 511 - [用户中心](#用户中心模块) - ✅ 5个已完成, ⏳ 1个后端开发中
512 +- [通用](#通用模块) - ⏳ 3个后端开发中
412 - [意见反馈](#意见反馈模块) - ⏳ 2个后端开发中 513 - [意见反馈](#意见反馈模块) - ⏳ 2个后端开发中
413 - [消息](#消息模块) - ⏳ 2个后端开发中 514 - [消息](#消息模块) - ⏳ 2个后端开发中
414 -- [首页](#首页模块) - ⏳ 未开始 515 +- [首页](#首页模块) - ⏳ 1个后端开发中
415 - [产品详情](#产品模块) - ⏳ 未开始 516 - [产品详情](#产品模块) - ⏳ 未开始
416 - [知识库](#知识库模块) - ⏳ 未开始 517 - [知识库](#知识库模块) - ⏳ 未开始
417 - [家办](#家办模块) - ⏳ 未开始 518 - [家办](#家办模块) - ⏳ 未开始
...@@ -449,8 +550,8 @@ ...@@ -449,8 +550,8 @@
449 550
450 --- 551 ---
451 552
452 -**最后更新时间**: 2026-02-03 15:30 553 +**最后更新时间**: 2026-02-03 18:00
453 -**文档版本**: v1.3 554 +**文档版本**: v1.4
454 **更新内容**: 555 **更新内容**:
455 -- 登录接口联调完成,更新状态为 ✅ 已完成 556 +- 新增通用模块接口:获取七牛上传 Token、保存文件信息、上传图片到七牛云(完整流程)
456 -- 更新总体进度:10个接口(5个已完成,5个后端开发中) 557 +- 更新总体进度:14个接口(5个已完成,9个后端开发中)
......
...@@ -11,6 +11,7 @@ const Api = { ...@@ -11,6 +11,7 @@ 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 + UPLOAD_IMAGE: '/admin/?m=srv&a=upload', // 上传图片(不带审核)
14 } 15 }
15 16
16 /** 17 /**
...@@ -50,3 +51,101 @@ export const qiniuUploadAPI = (url, data, config) => uploadFn(fetch.basePost(url ...@@ -50,3 +51,101 @@ export const qiniuUploadAPI = (url, data, config) => uploadFn(fetch.basePost(url
50 * @returns {Promise<{code:number,data:any,msg:string}>} 标准返回 51 * @returns {Promise<{code:number,data:any,msg:string}>} 标准返回
51 */ 52 */
52 export const saveFileAPI = (params) => fn(fetch.stringifyPost(Api.SAVE_FILE, params)); 53 export const saveFileAPI = (params) => fn(fetch.stringifyPost(Api.SAVE_FILE, params));
54 +
55 +/**
56 + * @description: 上传图片到七牛云(完整流程)
57 + * @param {Object} params 请求参数
58 + * @param {string} params.filePath 本地文件路径
59 + * @returns {Promise<{code:number,data:{src:string},msg:string}>} 返回上传结果,data.src 为图片URL
60 + *
61 + * @example
62 + * // 在页面中使用
63 + * import { uploadImageToQiniuAPI } from '@/api/common'
64 + *
65 + * Taro.chooseImage({
66 + * count: 1,
67 + * success: async (res) => {
68 + * const result = await uploadImageToQiniuAPI({
69 + * filePath: res.tempFilePaths[0]
70 + * })
71 + * if (result.code === 1) {
72 + * console.log('图片URL:', result.data.src)
73 + * }
74 + * }
75 + * })
76 + */
77 +export const uploadImageToQiniuAPI = async (params) => {
78 + try {
79 + // 1. 读取文件为 base64
80 + const base64Data = await new Promise((resolve, reject) => {
81 + Taro.getFileSystemManager().readFile({
82 + filePath: params.filePath,
83 + encoding: 'base64',
84 + success: (res) => resolve(res.data),
85 + fail: (err) => reject(err)
86 + });
87 + });
88 +
89 + // 2. 获取七牛上传token
90 + const tokenRes = await qiniuTokenAPI({
91 + filename: params.filePath.split('/').pop(),
92 + file: base64Data
93 + });
94 +
95 + if (tokenRes.code !== 1 || !tokenRes.data) {
96 + throw new Error(tokenRes.msg || '获取上传凭证失败');
97 + }
98 +
99 + const { token, upload_url, filekey } = tokenRes.data;
100 +
101 + // 3. 上传到七牛云
102 + const formData = new FormData();
103 + formData.append('file', base64Data);
104 + formData.append('token', token);
105 + formData.append('key', filekey);
106 +
107 + const uploadRes = await qiniuUploadAPI(upload_url, formData, {
108 + headers: {
109 + 'Content-Type': 'multipart/form-data'
110 + }
111 + });
112 +
113 + if (!uploadRes) {
114 + throw new Error('七牛上传失败');
115 + }
116 +
117 + // 4. 保存文件信息到服务器
118 + const saveRes = await saveFileAPI({
119 + format: uploadRes.format,
120 + hash: uploadRes.hash,
121 + height: uploadRes.height || 0,
122 + width: uploadRes.width || 0,
123 + filekey: uploadRes.key
124 + });
125 +
126 + if (saveRes.code !== 1) {
127 + throw new Error(saveRes.msg || '保存文件信息失败');
128 + }
129 +
130 + return {
131 + code: 1,
132 + data: {
133 + src: uploadRes.src || saveRes.data.src, // 优先使用七牛返回的src,否则使用服务器返回的
134 + hash: uploadRes.hash,
135 + format: uploadRes.format,
136 + width: uploadRes.width,
137 + height: uploadRes.height
138 + },
139 + msg: '上传成功'
140 + };
141 +
142 + } catch (err) {
143 + console.error('上传图片失败:', err);
144 + return {
145 + code: 0,
146 + data: null,
147 + msg: err.message || err.msg || '上传失败'
148 + };
149 + }
150 +};
151 +
......
...@@ -40,34 +40,125 @@ import IconFont from '@/components/IconFont.vue' ...@@ -40,34 +40,125 @@ import IconFont from '@/components/IconFont.vue'
40 import NavHeader from '@/components/NavHeader.vue' 40 import NavHeader from '@/components/NavHeader.vue'
41 import Taro from '@tarojs/taro' 41 import Taro from '@tarojs/taro'
42 import defaultAvatar from '@/assets/images/icon/avatar.svg' 42 import defaultAvatar from '@/assets/images/icon/avatar.svg'
43 +import { uploadImageToQiniuAPI } from '@/api/common'
44 +import { updateProfileAPI } from '@/api/user'
43 45
44 const go = useGo() 46 const go = useGo()
45 const avatarUrl = ref(defaultAvatar) 47 const avatarUrl = ref(defaultAvatar)
48 +const tempAvatarUrl = ref('') // 临时存储上传后的头像URL
49 +const uploading = ref(false) // 上传状态
46 50
51 +/**
52 + * @description 更换头像
53 + */
47 const onChangeAvatar = () => { 54 const onChangeAvatar = () => {
48 Taro.chooseImage({ 55 Taro.chooseImage({
49 count: 1, 56 count: 1,
50 - sizeType: ['original', 'compressed'], 57 + sizeType: ['compressed'], // 使用压缩图,减少上传时间
51 sourceType: ['album', 'camera'], 58 sourceType: ['album', 'camera'],
52 - success: function (res) { 59 + success: async (res) => {
53 - // tempFilePath can be used as image src 60 + const tempFile = res.tempFiles[0]
54 - avatarUrl.value = res.tempFilePaths[0] 61 +
62 + // 检查文件大小(5MB限制)
63 + if (tempFile.size > 5 * 1024 * 1024) {
64 + Taro.showToast({
65 + title: '图片大小不能超过5MB',
66 + icon: 'none',
67 + })
68 + return
69 + }
70 +
71 + // 显示上传进度
72 + Taro.showLoading({ title: '上传中...', mask: true })
73 + uploading.value = true
74 +
75 + try {
76 + // 上传到七牛云
77 + const uploadRes = await uploadImageToQiniuAPI({
78 + filePath: tempFile.path
79 + })
80 +
81 + Taro.hideLoading()
82 + uploading.value = false
83 +
84 + if (uploadRes.code === 1) {
85 + // 上传成功,更新头像显示
86 + avatarUrl.value = uploadRes.data.src
87 + tempAvatarUrl.value = uploadRes.data.src
88 + Taro.showToast({
89 + title: '上传成功',
90 + icon: 'success'
91 + })
92 + } else {
93 + Taro.showToast({
94 + title: uploadRes.msg || '上传失败',
95 + icon: 'none'
96 + })
97 + }
98 + } catch (err) {
99 + Taro.hideLoading()
100 + uploading.value = false
101 + console.error('上传头像失败:', err)
102 + Taro.showToast({
103 + title: '上传失败,请稍后重试',
104 + icon: 'none'
105 + })
106 + }
55 } 107 }
56 }) 108 })
57 } 109 }
58 110
111 +/**
112 + * @description 取消修改
113 + */
59 const onCancel = () => { 114 const onCancel = () => {
60 Taro.navigateBack() 115 Taro.navigateBack()
61 } 116 }
62 117
63 -const onSave = () => { 118 +/**
64 - Taro.showLoading({ title: '保存中...' }) 119 + * @description 保存头像
65 - setTimeout(() => { 120 + */
121 +const onSave = async () => {
122 + // 如果没有上传新头像,直接返回
123 + if (!tempAvatarUrl.value) {
124 + Taro.showToast({
125 + title: '请先选择头像',
126 + icon: 'none'
127 + })
128 + return
129 + }
130 +
131 + // 保存到服务器
132 + Taro.showLoading({ title: '保存中...', mask: true })
133 +
134 + try {
135 + const res = await updateProfileAPI({
136 + avatar_url: tempAvatarUrl.value
137 + })
138 +
66 Taro.hideLoading() 139 Taro.hideLoading()
67 - Taro.showToast({ title: '保存成功', icon: 'success' }) 140 +
141 + if (res.code === 1) {
142 + Taro.showToast({
143 + title: '保存成功',
144 + icon: 'success'
145 + })
68 setTimeout(() => { 146 setTimeout(() => {
69 Taro.navigateBack() 147 Taro.navigateBack()
70 }, 1500) 148 }, 1500)
71 - }, 1000) 149 + } else {
150 + Taro.showToast({
151 + title: res.msg || '保存失败',
152 + icon: 'none'
153 + })
154 + }
155 + } catch (err) {
156 + Taro.hideLoading()
157 + console.error('保存头像失败:', err)
158 + Taro.showToast({
159 + title: '保存失败,请稍后重试',
160 + icon: 'none'
161 + })
162 + }
72 } 163 }
73 </script> 164 </script>
......