hookehuyr

feat: 实现用户和家庭相关接口集成及功能优化

- 集成用户信息获取和更新接口
- 实现家庭创建、加入和查询功能
- 优化微信运动授权逻辑
- 统一上传接口响应处理
- 修复多个页面表单验证逻辑
...@@ -13,7 +13,6 @@ declare module 'vue' { ...@@ -13,7 +13,6 @@ declare module 'vue' {
13 GlassCard: typeof import('./src/components/GlassCard.vue')['default'] 13 GlassCard: typeof import('./src/components/GlassCard.vue')['default']
14 NavBar: typeof import('./src/components/navBar.vue')['default'] 14 NavBar: typeof import('./src/components/navBar.vue')['default']
15 NutActionSheet: typeof import('@nutui/nutui-taro')['ActionSheet'] 15 NutActionSheet: typeof import('@nutui/nutui-taro')['ActionSheet']
16 - NutBacktop: typeof import('@nutui/nutui-taro')['Backtop']
17 NutButton: typeof import('@nutui/nutui-taro')['Button'] 16 NutButton: typeof import('@nutui/nutui-taro')['Button']
18 NutDatePicker: typeof import('@nutui/nutui-taro')['DatePicker'] 17 NutDatePicker: typeof import('@nutui/nutui-taro')['DatePicker']
19 NutImagePreview: typeof import('@nutui/nutui-taro')['ImagePreview'] 18 NutImagePreview: typeof import('@nutui/nutui-taro')['ImagePreview']
......
1 +/*
2 + * @Date: 2024-01-01 00:00:00
3 + * @LastEditors: hookehuyr hookehuyr@gmail.com
4 + * @LastEditTime: 2025-09-02 20:09:52
5 + * @FilePath: /lls_program/src/api/family.js
6 + * @Description: 家庭相关接口
7 + */
8 +import { fn, fetch } from './fn';
9 +
10 +const Api = {
11 + SEARCH_BY_PASSPHRASE: '/srv/?a=family&t=search_by_passphrase',
12 + LIST_MY_FAMILIES: '/srv/?a=family&t=list_my_families',
13 + GET_DASHBOARD: '/srv/?a=family&t=get_dashboard',
14 + ADD_FAMILY: '/srv/?a=family&t=add',
15 + JOIN_FAMILY: '/srv/?a=family&t=join',
16 + DEL_MEMBER: '/srv/?a=family&t=del_member',
17 +}
18 +
19 +/**
20 + * @description: 根据口令搜索家庭
21 + * @param {Object} params - 请求参数
22 + * @param {string} params.passphrase - 家训口令
23 + * @param {number} [params.page=0] - 页码,从0开始
24 + * @param {number} [params.limit=10] - 每页数量
25 + * @returns {Promise} 返回匹配的家庭列表
26 + * @returns {Object} response - 响应对象
27 + * @returns {number} response.code - 响应状态码
28 + * @returns {string} response.msg - 响应消息
29 + * @returns {Array} response.data - 家庭列表
30 + * @returns {number} response.data[].id - 家庭ID
31 + * @returns {string} response.data[].name - 家庭名称
32 + * @returns {string} response.data[].avatar_url - 家庭头像
33 + */
34 +export const searchFamilyByPassphraseAPI = (params) => fn(fetch.get(Api.SEARCH_BY_PASSPHRASE, params));
35 +
36 +/**
37 + * @description: 获取我的家庭列表
38 + * @returns {Promise} 返回当前用户创建和加入的所有家庭列表
39 + * @returns {Object} response - 响应对象
40 + * @returns {string} response.code - 响应状态码
41 + * @returns {string} response.msg - 响应消息
42 + * @returns {Array} response.data - 家庭列表
43 + * @returns {number} response.data[].id - 家庭ID
44 + * @returns {string} response.data[].name - 家庭名称
45 + * @returns {string} response.data[].avatar_url - 家庭头像
46 + * @returns {boolean} response.data[].is_my - 是否是我创建的家庭
47 + */
48 +export const getMyFamiliesAPI = () => fn(fetch.get(Api.LIST_MY_FAMILIES));
49 +
50 +/**
51 + * @description: 获取家庭首页数据
52 + * @param {Object} [params] - 请求参数
53 + * @param {string} [params.family_id] - 家庭ID,默认是上次选中的家庭
54 + * @returns {Promise} 返回家庭首页聚合数据
55 + * @returns {Object} response - 响应对象
56 + * @returns {number} response.code - 响应状态码
57 + * @returns {string} response.msg - 响应消息
58 + * @returns {Object} response.data - 家庭首页数据
59 + * @returns {Object} response.data.family - 家庭信息
60 + * @returns {number} response.data.family.id - 家庭ID
61 + * @returns {string} response.data.family.name - 家庭名称
62 + * @returns {string} response.data.family.note - 家庭描述
63 + * @returns {string} response.data.family.avatar_url - 家庭头像
64 + * @returns {number} response.data.family.total_points - 家庭总积分
65 + * @returns {number} response.data.family_today_step - 家庭今日总步数
66 + * @returns {number} response.data.my_today_step - 我今天的步数
67 + * @returns {Array} response.data.pending_points - 等待汇总到家庭的积分
68 + * @returns {number} response.data.pending_points[].id - 积分ID
69 + * @returns {string} response.data.pending_points[].title - 积分标题
70 + * @returns {string} response.data.pending_points[].points - 积分数量
71 + * @returns {string} response.data.pending_points[].source_type - 积分来源
72 + * @returns {string} response.data.pending_points[].note - 积分说明
73 + * @returns {Array} response.data.step_ranking - 步数排名
74 + * @returns {string} response.data.step_ranking[].user_id - 用户ID
75 + * @returns {string} response.data.step_ranking[].role - 角色
76 + * @returns {string} response.data.step_ranking[].avatar_url - 头像
77 + * @returns {string} response.data.step_ranking[].today_step - 用户今日步数
78 + */
79 +export const getFamilyDashboardAPI = (params) => fn(fetch.get(Api.GET_DASHBOARD, params));
80 +
81 +/**
82 + * @description: 创建家庭
83 + * @param {Object} params - 请求参数
84 + * @param {string} params.name - 家庭名称
85 + * @param {string} params.county - 所在区县
86 + * @param {string} params.passphrase - 家训口令
87 + * @param {string} params.avatar_url - 家庭头像
88 + * @param {string} params.note - 家庭介绍
89 + * @returns {Promise} 返回创建的家庭ID
90 + * @returns {Object} response - 响应对象
91 + * @returns {string} response.code - 响应状态码
92 + * @returns {string} response.msg - 响应消息
93 + * @returns {Object} response.data - 响应数据
94 + * @returns {number} response.data.family_id - 家庭ID
95 + */
96 +export const createFamilyAPI = (params) => fn(fetch.post(Api.ADD_FAMILY, params));
97 +
98 +/**
99 + * @description: 加入家庭
100 + * @param {Object} params - 请求参数
101 + * @param {number} params.family_id - 家庭ID
102 + * @param {string} params.role - 角色(如:儿子、女儿等)
103 + * @returns {Promise} 返回加入结果
104 + * @returns {Object} response - 响应对象
105 + * @returns {string} response.code - 响应状态码
106 + * @returns {string} response.msg - 响应消息
107 + */
108 +export const joinFamilyAPI = (params) => fn(fetch.post(Api.JOIN_FAMILY, params));
109 +
110 +/**
111 + * @description: 退出或移出家庭成员
112 + * @param {Object} params - 请求参数
113 + * @param {number} params.family_id - 家庭ID
114 + * @param {number} [params.member_user_id] - 成员用户ID(主动退出时不传,创建者移出时传递)
115 + * @returns {Promise} 返回操作结果
116 + * @returns {Object} response - 响应对象
117 + * @returns {string} response.code - 响应状态码
118 + * @returns {string} response.msg - 响应消息
119 + */
120 +export const deleteFamilyMemberAPI = (params) => fn(fetch.post(Api.DEL_MEMBER, params));
1 +/*
2 + * @Date: 2024-01-01 00:00:00
3 + * @LastEditors: hookehuyr hookehuyr@gmail.com
4 + * @LastEditTime: 2025-09-02 20:33:00
5 + * @FilePath: /lls_program/src/api/user.js
6 + * @Description: 用户相关接口
7 + */
8 +import { fn, fetch } from './fn';
9 +
10 +const Api = {
11 + GET_PROFILE: '/srv/?a=user&t=get_profile',
12 + UPDATE_PROFILE: '/srv/?a=user&t=update_profile',
13 +}
14 +
15 +/**
16 + * @description: 获取个人信息
17 + * @returns {Promise} 返回用户详细个人资料
18 + * @returns {Object} response - 响应对象
19 + * @returns {number} response.code - 响应状态码
20 + * @returns {string} response.msg - 响应消息
21 + * @returns {Object} response.data - 响应数据
22 + * @returns {Object} response.data.user - 用户信息对象
23 + * @returns {number} response.data.user.id - 用户ID
24 + * @returns {string} response.data.user.openid - 微信openid
25 + * @returns {string} response.data.user.nickname - 用户昵称
26 + * @returns {string} response.data.user.avatar_url - 用户头像URL
27 + * @returns {string} response.data.user.birth_date - 用户生日
28 + * @returns {boolean} response.data.user.wheelchair_needed - 是否需要轮椅
29 + */
30 +export const getUserProfileAPI = (params) => fn(fetch.get(Api.GET_PROFILE, params));
31 +
32 +/**
33 + * @description: 更新个人信息
34 + * @param {Object} params - 请求参数
35 + * @param {string} params.nickname - 用户昵称
36 + * @param {string} params.birth_date - 用户生日
37 + * @param {string} params.avatar_url - 用户头像URL
38 + * @param {boolean} params.wheelchair_needed - 是否需要轮椅
39 + */
40 +export const updateUserProfileAPI = (params) => fn(fetch.post(Api.UPDATE_PROFILE, params));
...@@ -47,8 +47,9 @@ const isLoading = ref(false) ...@@ -47,8 +47,9 @@ const isLoading = ref(false)
47 47
48 /** 48 /**
49 * @description 检查微信运动授权状态 49 * @description 检查微信运动授权状态
50 + * @param {boolean} shouldGetData - 是否在授权后获取步数数据
50 */ 51 */
51 -const checkAuthStatus = async () => { 52 +const checkAuthStatus = async (shouldGetData = false) => {
52 try { 53 try {
53 const authSetting = await Taro.getSetting() 54 const authSetting = await Taro.getSetting()
54 const hasWeRunAuth = authSetting.authSetting['scope.werun'] 55 const hasWeRunAuth = authSetting.authSetting['scope.werun']
...@@ -58,8 +59,8 @@ const checkAuthStatus = async () => { ...@@ -58,8 +59,8 @@ const checkAuthStatus = async () => {
58 // 通知父组件授权状态变化 59 // 通知父组件授权状态变化
59 emit('auth-change', isAuthorized.value) 60 emit('auth-change', isAuthorized.value)
60 61
61 - // 如果已授权,获取步数数据 62 + // 根据参数决定是否获取步数数据
62 - if (isAuthorized.value) { 63 + if (isAuthorized.value && shouldGetData) {
63 await getWeRunData() 64 await getWeRunData()
64 } 65 }
65 } catch (error) { 66 } catch (error) {
...@@ -147,10 +148,10 @@ const getWeRunData = async () => { ...@@ -147,10 +148,10 @@ const getWeRunData = async () => {
147 const { code, data } = await syncWxStepAPI({ encryptedData: weRunRes.encryptedData, iv: weRunRes.iv }); 148 const { code, data } = await syncWxStepAPI({ encryptedData: weRunRes.encryptedData, iv: weRunRes.iv });
148 if (code) { 149 if (code) {
149 // 提示获取步数成功 150 // 提示获取步数成功
150 - Taro.showToast({ 151 + // Taro.showToast({
151 - title: `获取步数成功`, 152 + // title: `获取步数成功`,
152 - icon: 'none' 153 + // icon: 'none'
153 - }) 154 + // })
154 console.warn('同步微信步数成功', data); 155 console.warn('同步微信步数成功', data);
155 // todaySteps.value = decryptedData.todaySteps 156 // todaySteps.value = decryptedData.todaySteps
156 isLoading.value = false 157 isLoading.value = false
...@@ -187,9 +188,9 @@ defineExpose({ ...@@ -187,9 +188,9 @@ defineExpose({
187 checkAuthStatus 188 checkAuthStatus
188 }) 189 })
189 190
190 -// 组件挂载时检查授权状态 191 +// 组件挂载时检查授权状态,但不获取步数数据
191 onMounted(() => { 192 onMounted(() => {
192 - checkAuthStatus() 193 + checkAuthStatus(false)
193 }) 194 })
194 </script> 195 </script>
195 196
......
...@@ -23,20 +23,20 @@ ...@@ -23,20 +23,20 @@
23 </view> 23 </view>
24 </view> 24 </view>
25 25
26 - <!-- Birthday --> 26 + <!-- birth_date -->
27 <view class="mb-6"> 27 <view class="mb-6">
28 <label class="block text-sm font-medium text-gray-700 mb-2">出生年月</label> 28 <label class="block text-sm font-medium text-gray-700 mb-2">出生年月</label>
29 <view class="bg-white rounded-xl p-4 border border-gray-200" @click="showDatePicker = true"> 29 <view class="bg-white rounded-xl p-4 border border-gray-200" @click="showDatePicker = true">
30 <view class="flex justify-between items-center"> 30 <view class="flex justify-between items-center">
31 - <text :class="{'text-gray-400': !formData.birthday, 'text-gray-900': formData.birthday}" class="text-base"> 31 + <text :class="{'text-gray-400': !formData.birth_date, 'text-gray-900': formData.birth_date}" class="text-base">
32 - {{ formData.birthday || '-/-/-/' }} 32 + {{ formData.birth_date || '-/-/-/' }}
33 </text> 33 </text>
34 <DateIcon size="20" color="#888" /> 34 <DateIcon size="20" color="#888" />
35 </view> 35 </view>
36 </view> 36 </view>
37 </view> 37 </view>
38 38
39 - <!-- Wheelchair --> 39 + <!-- wheelchair_needed -->
40 <view class="mb-6"> 40 <view class="mb-6">
41 <label class="block text-sm font-medium text-gray-700 mb-2">是否需要轮椅出行</label> 41 <label class="block text-sm font-medium text-gray-700 mb-2">是否需要轮椅出行</label>
42 <view class="bg-white rounded-xl p-4 border border-gray-200" @click="showWheelchairPicker = true"> 42 <view class="bg-white rounded-xl p-4 border border-gray-200" @click="showWheelchairPicker = true">
...@@ -88,6 +88,8 @@ import { Date as DateIcon, Right } from '@nutui/icons-vue-taro'; ...@@ -88,6 +88,8 @@ import { Date as DateIcon, Right } from '@nutui/icons-vue-taro';
88 import BASE_URL from '@/utils/config'; 88 import BASE_URL from '@/utils/config';
89 // 默认头像 89 // 默认头像
90 const defaultAvatar = 'https://cdn.ipadbiz.cn/mlaj/images/icon_1.jpeg' 90 const defaultAvatar = 'https://cdn.ipadbiz.cn/mlaj/images/icon_1.jpeg'
91 +// 获取接口信息
92 +import { updateUserProfileAPI } from '@/api/user';
91 93
92 /** 94 /**
93 * @description 表单数据 95 * @description 表单数据
...@@ -95,8 +97,8 @@ const defaultAvatar = 'https://cdn.ipadbiz.cn/mlaj/images/icon_1.jpeg' ...@@ -95,8 +97,8 @@ const defaultAvatar = 'https://cdn.ipadbiz.cn/mlaj/images/icon_1.jpeg'
95 const formData = reactive({ 97 const formData = reactive({
96 avatar_url: '', 98 avatar_url: '',
97 nickname: '', 99 nickname: '',
98 - birthday: '', 100 + birth_date: '',
99 - wheelchair: null, // 0 for No, 1 for Yes 101 + wheelchair_needed: null, // 0 for No, 1 for Yes
100 wheelchair_text: '', 102 wheelchair_text: '',
101 }); 103 });
102 104
...@@ -104,7 +106,7 @@ const formData = reactive({ ...@@ -104,7 +106,7 @@ const formData = reactive({
104 * @description 检查表单是否有效 106 * @description 检查表单是否有效
105 */ 107 */
106 const isFormValid = computed(() => { 108 const isFormValid = computed(() => {
107 - return formData.nickname && formData.birthday && formData.wheelchair !== null; 109 + return formData.nickname && formData.birth_date && formData.wheelchair_needed !== null;
108 }); 110 });
109 111
110 // --- Date Picker --- 112 // --- Date Picker ---
...@@ -130,11 +132,11 @@ const currentDate = ref(new Date()); ...@@ -130,11 +132,11 @@ const currentDate = ref(new Date());
130 * @param {object} param0 - 包含 selectedValue 的对象 132 * @param {object} param0 - 包含 selectedValue 的对象
131 */ 133 */
132 const onDateConfirm = ({ selectedValue }) => { 134 const onDateConfirm = ({ selectedValue }) => {
133 - formData.birthday = selectedValue.join('-'); 135 + formData.birth_date = selectedValue.join('-');
134 showDatePicker.value = false; 136 showDatePicker.value = false;
135 }; 137 };
136 138
137 -// --- Wheelchair Picker --- 139 +// --- wheelchair_needed Picker ---
138 /** 140 /**
139 * @description 控制轮椅选择器显示 141 * @description 控制轮椅选择器显示
140 */ 142 */
...@@ -156,7 +158,7 @@ const wheelchairColumns = ref([ ...@@ -156,7 +158,7 @@ const wheelchairColumns = ref([
156 * @param {object} param0 - 包含 selectedValue 和 selectedOptions 的对象 158 * @param {object} param0 - 包含 selectedValue 和 selectedOptions 的对象
157 */ 159 */
158 const onWheelchairConfirm = ({ selectedValue, selectedOptions }) => { 160 const onWheelchairConfirm = ({ selectedValue, selectedOptions }) => {
159 - formData.wheelchair = selectedValue[0]; 161 + formData.wheelchair_needed = selectedValue[0];
160 formData.wheelchair_text = selectedOptions.map((option) => option.text).join(''); 162 formData.wheelchair_text = selectedOptions.map((option) => option.text).join('');
161 showWheelchairPicker.value = false; 163 showWheelchairPicker.value = false;
162 }; 164 };
...@@ -198,8 +200,8 @@ const changeAvatar = () => { ...@@ -198,8 +200,8 @@ const changeAvatar = () => {
198 success: (uploadRes) => { 200 success: (uploadRes) => {
199 Taro.hideLoading(); 201 Taro.hideLoading();
200 const data = JSON.parse(uploadRes.data); 202 const data = JSON.parse(uploadRes.data);
201 - if (data.code === 0) { 203 + if (data.code == 0) {
202 - formData.avatar_url = data.data.url; 204 + formData.avatar_url = data.data.src;
203 Taro.showToast({ title: '上传成功', icon: 'success' }); 205 Taro.showToast({ title: '上传成功', icon: 'success' });
204 } else { 206 } else {
205 Taro.showToast({ title: data.msg || '上传失败', icon: 'none' }); 207 Taro.showToast({ title: data.msg || '上传失败', icon: 'none' });
...@@ -221,7 +223,7 @@ const changeAvatar = () => { ...@@ -221,7 +223,7 @@ const changeAvatar = () => {
221 /** 223 /**
222 * @description 保存用户信息 224 * @description 保存用户信息
223 */ 225 */
224 -const handleSave = () => { 226 +const handleSave = async () => {
225 if (!isFormValid.value) { 227 if (!isFormValid.value) {
226 Taro.showToast({ 228 Taro.showToast({
227 title: '请填写所有必填项', 229 title: '请填写所有必填项',
...@@ -229,39 +231,21 @@ const handleSave = () => { ...@@ -229,39 +231,21 @@ const handleSave = () => {
229 }); 231 });
230 return; 232 return;
231 } 233 }
232 - console.log('Saving data:', formData); 234 +
233 - Taro.showLoading({ title: '保存中...' }); 235 + // 保存用户信息
234 - // Mock save process 236 + const { code, data } = await updateUserProfileAPI(formData);
235 - setTimeout(() => { 237 + if (code) {
236 - Taro.hideLoading();
237 Taro.showToast({ 238 Taro.showToast({
238 title: '保存成功', 239 title: '保存成功',
239 icon: 'success', 240 icon: 'success',
240 - duration: 1500,
241 - complete: () => {
242 - Taro.navigateBack();
243 - },
244 }); 241 });
245 - }, 1000); 242 + setTimeout(() => {
243 + Taro.navigateBack();
244 + }, 1500);
245 + }
246 }; 246 };
247 247
248 -/**
249 - * @description 页面加载时获取初始数据
250 - */
251 onMounted(() => { 248 onMounted(() => {
252 - // Mock fetching user data, in a real app, you would call an API
253 - // const mockUserData = {
254 - // avatar_url: 'https://img.yzcdn.cn/vant/cat.jpeg',
255 - // nickname: '张三',
256 - // birthday: '1990-05-15',
257 - // wheelchair: 0,
258 - // wheelchair_text: '否',
259 - // };
260 - // Object.assign(formData, mockUserData);
261 -
262 - // Initialize pickers with current data
263 - // currentDate.value = new Date(mockUserData.birthday);
264 - // wheelchairValue.value = [mockUserData.wheelchair];
265 }); 249 });
266 </script> 250 </script>
267 251
......
1 <!-- 1 <!--
2 * @Date: 2025-08-27 17:44:53 2 * @Date: 2025-08-27 17:44:53
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-09-02 16:29:48 4 + * @LastEditTime: 2025-09-02 17:52:24
5 * @FilePath: /lls_program/src/pages/CreateFamily/index.vue 5 * @FilePath: /lls_program/src/pages/CreateFamily/index.vue
6 * @Description: 文件描述 6 * @Description: 文件描述
7 --> 7 -->
...@@ -23,7 +23,6 @@ ...@@ -23,7 +23,6 @@
23 class="w-full text-gray-600 focus:outline-none" 23 class="w-full text-gray-600 focus:outline-none"
24 placeholder="请输入家庭名称(最多10个字)" 24 placeholder="请输入家庭名称(最多10个字)"
25 @blur="validateFamilyName" 25 @blur="validateFamilyName"
26 - maxlength="10"
27 /> 26 />
28 <view v-if="familyNameError" class="text-red-500 text-sm mt-2">{{ familyNameError }}</view> 27 <view v-if="familyNameError" class="text-red-500 text-sm mt-2">{{ familyNameError }}</view>
29 </view> 28 </view>
...@@ -38,7 +37,6 @@ ...@@ -38,7 +37,6 @@
38 placeholder="请输入您家庭的特色、成员特点等家庭标签(最多100个字)" 37 placeholder="请输入您家庭的特色、成员特点等家庭标签(最多100个字)"
39 :rows="2" 38 :rows="2"
40 @blur="validateFamilyIntro" 39 @blur="validateFamilyIntro"
41 - maxlength="100"
42 /> 40 />
43 <view v-if="familyIntroError" class="text-red-500 text-sm mt-2">{{ familyIntroError }}</view> 41 <view v-if="familyIntroError" class="text-red-500 text-sm mt-2">{{ familyIntroError }}</view>
44 </view> 42 </view>
...@@ -138,7 +136,7 @@ ...@@ -138,7 +136,7 @@
138 </view> 136 </view>
139 <!-- Submit Button --> 137 <!-- Submit Button -->
140 <view 138 <view
141 - @tap="isFormValid ? handleCreateFamily : null" 139 + @tap="handleCreateFamily"
142 :class="[ 140 :class="[
143 'w-full py-3 text-white text-lg font-medium rounded-lg flex items-center justify-center', 141 'w-full py-3 text-white text-lg font-medium rounded-lg flex items-center justify-center',
144 isFormValid ? 'bg-blue-500' : 'bg-gray-300' 142 isFormValid ? 'bg-blue-500' : 'bg-gray-300'
...@@ -174,10 +172,11 @@ ...@@ -174,10 +172,11 @@
174 <script setup> 172 <script setup>
175 import { ref, nextTick, computed } from 'vue'; 173 import { ref, nextTick, computed } from 'vue';
176 import Taro from '@tarojs/taro'; 174 import Taro from '@tarojs/taro';
177 -import { Edit, Tips, Photograph, Right } from '@nutui/icons-vue-taro'; 175 +import { Tips, Photograph, Right } from '@nutui/icons-vue-taro';
178 -// import AppHeader from '../../components/AppHeader.vue';
179 import BASE_URL from '@/utils/config'; 176 import BASE_URL from '@/utils/config';
180 import defaultFamilyCoverSvg from '@/assets/images/default-family-cover.png'; 177 import defaultFamilyCoverSvg from '@/assets/images/default-family-cover.png';
178 +// 接口信息
179 +import { createFamilyAPI } from '@/api/family';
181 180
182 const familyName = ref(''); 181 const familyName = ref('');
183 const familyIntro = ref(''); 182 const familyIntro = ref('');
...@@ -342,7 +341,7 @@ const uploadImage = (filePath) => { ...@@ -342,7 +341,7 @@ const uploadImage = (filePath) => {
342 let upload_data = JSON.parse(res.data); 341 let upload_data = JSON.parse(res.data);
343 Taro.hideLoading({ 342 Taro.hideLoading({
344 success: () => { 343 success: () => {
345 - if (res.statusCode === 200) { 344 + if (data.code == 0) {
346 familyAvatar.value = upload_data.data.src; 345 familyAvatar.value = upload_data.data.src;
347 showToast('上传成功', 'success'); 346 showToast('上传成功', 'success');
348 } else { 347 } else {
...@@ -460,19 +459,45 @@ const validateForm = () => { ...@@ -460,19 +459,45 @@ const validateForm = () => {
460 /** 459 /**
461 * 创建家庭 460 * 创建家庭
462 */ 461 */
463 -const handleCreateFamily = () => { 462 +const handleCreateFamily = async () => {
464 if (!validateForm()) { 463 if (!validateForm()) {
465 return; 464 return;
466 } 465 }
467 466
468 - // 在实际应用中,这里会调用API创建家庭 467 + try {
469 - // 目前仅作为演示跳转到仪表盘页面 468 + // 显示加载中
470 - showToast('家庭创建成功', 'success'); 469 + Taro.showLoading({
470 + title: '创建中...',
471 + mask: true
472 + });
471 473
472 - setTimeout(() => { 474 + const { code, data, msg } = await createFamilyAPI({
473 - Taro.navigateTo({ 475 + name: familyName.value,
474 - url: '/pages/Dashboard/index' 476 + note: familyIntro.value,
477 + county: selectedDistrict.value,
478 + passphrase: familyMotto.value.join(''),
479 + avatar_url: familyAvatar.value,
475 }); 480 });
476 - }, 1500); 481 +
482 + Taro.hideLoading();
483 +
484 + // 判断API调用是否成功
485 + if (code) {
486 + showToast('创建成功', 'success');
487 +
488 + setTimeout(() => {
489 + Taro.navigateTo({
490 + url: '/pages/Dashboard/index'
491 + });
492 + }, 1500);
493 + } else {
494 + // 显示错误信息
495 + showToast(msg || '创建失败,请重试', 'none');
496 + }
497 + } catch (error) {
498 + Taro.hideLoading();
499 + console.error('创建家庭失败:', error);
500 + showToast('网络错误,请重试', 'none');
501 + }
477 }; 502 };
478 </script> 503 </script>
......
...@@ -205,6 +205,8 @@ import { useMediaPreview } from '@/composables/useMediaPreview'; ...@@ -205,6 +205,8 @@ import { useMediaPreview } from '@/composables/useMediaPreview';
205 import defaultFamilyCover from '@/assets/images/default-family-cover.png'; 205 import defaultFamilyCover from '@/assets/images/default-family-cover.png';
206 // 默认头像 206 // 默认头像
207 const defaultAvatar = 'https://cdn.ipadbiz.cn/mlaj/images/icon_1.jpeg' 207 const defaultAvatar = 'https://cdn.ipadbiz.cn/mlaj/images/icon_1.jpeg'
208 +// 接口信息
209 +import { getMyFamiliesAPI } from '@/api/family'
208 210
209 const todaySteps = ref(0); 211 const todaySteps = ref(0);
210 const isWeRunAuthorized = ref(false); 212 const isWeRunAuthorized = ref(false);
...@@ -381,15 +383,25 @@ const initPageData = async () => { ...@@ -381,15 +383,25 @@ const initPageData = async () => {
381 familyCover.value = defaultFamilyCover 383 familyCover.value = defaultFamilyCover
382 } 384 }
383 385
384 -useDidShow(() => { 386 +useDidShow(async () => {
385 - // TODO: 等待真实接口获取用户是否加入家庭 387 + // 获取用户是否加入家庭
386 - const hasJoinedFamily = Math.random() > 0.5; // Change to true to simulate having a family 388 + const { code, data } = await getMyFamiliesAPI()
387 - 389 + if (code) {
388 - if (!hasJoinedFamily) { 390 + // 如果没有加入家庭
389 - Taro.redirectTo({ url: '/pages/Welcome/index' }); 391 + if (!data.length) {
392 + Taro.redirectTo({ url: '/pages/Welcome/index' });
393 + return; // 直接返回,不执行后续逻辑
394 + }
390 } 395 }
396 +
397 + // 只有在用户已加入家庭的情况下才初始化页面数据
391 initPageData(); 398 initPageData();
392 399
400 + // 刷新微信步数数据(只有在已加入家庭时才获取)
401 + if (weRunAuthRef.value) {
402 + weRunAuthRef.value.checkAuthStatus(true);
403 + }
404 +
393 // TODO: 真实情况下 需要重新获取积分列表, 测试随机积分获取状态, 接口有积分列表就显示出来, 没有就不显示 405 // TODO: 真实情况下 需要重新获取积分列表, 测试随机积分获取状态, 接口有积分列表就显示出来, 没有就不显示
394 showTotalPointsOnly.value = Math.random() > 0.5 ? true : false; 406 showTotalPointsOnly.value = Math.random() > 0.5 ? true : false;
395 // 总积分数量也要从接口获取传进去 407 // 总积分数量也要从接口获取传进去
......
1 <!-- 1 <!--
2 * @Date: 2025-08-27 17:44:53 2 * @Date: 2025-08-27 17:44:53
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-09-02 16:30:22 4 + * @LastEditTime: 2025-09-02 20:00:30
5 * @FilePath: /lls_program/src/pages/EditFamily/index.vue 5 * @FilePath: /lls_program/src/pages/EditFamily/index.vue
6 * @Description: 文件描述 6 * @Description: 文件描述
7 --> 7 -->
...@@ -324,7 +324,7 @@ const uploadImage = (filePath) => { ...@@ -324,7 +324,7 @@ const uploadImage = (filePath) => {
324 let upload_data = JSON.parse(res.data); 324 let upload_data = JSON.parse(res.data);
325 Taro.hideLoading({ 325 Taro.hideLoading({
326 success: () => { 326 success: () => {
327 - if (res.statusCode === 200) { 327 + if (data.code == 0) {
328 familyAvatar.value = upload_data.data.src; 328 familyAvatar.value = upload_data.data.src;
329 showToast('上传成功', 'success'); 329 showToast('上传成功', 'success');
330 } else { 330 } else {
......
...@@ -23,20 +23,20 @@ ...@@ -23,20 +23,20 @@
23 </view> 23 </view>
24 </view> 24 </view>
25 25
26 - <!-- Birthday --> 26 + <!-- birth_date -->
27 <view class="mb-6"> 27 <view class="mb-6">
28 <label class="block text-sm font-medium text-gray-700 mb-2">出生年月</label> 28 <label class="block text-sm font-medium text-gray-700 mb-2">出生年月</label>
29 <view class="bg-white rounded-xl p-4 border border-gray-200" @click="showDatePicker = true"> 29 <view class="bg-white rounded-xl p-4 border border-gray-200" @click="showDatePicker = true">
30 <view class="flex justify-between items-center"> 30 <view class="flex justify-between items-center">
31 - <text :class="{'text-gray-400': !formData.birthday, 'text-gray-900': formData.birthday}" class="text-base"> 31 + <text :class="{'text-gray-400': !formData.birth_date, 'text-gray-900': formData.birth_date}" class="text-base">
32 - {{ formData.birthday || '-/-/-/' }} 32 + {{ formData.birth_date || '-/-/-/' }}
33 </text> 33 </text>
34 <DateIcon size="20" color="#888" /> 34 <DateIcon size="20" color="#888" />
35 </view> 35 </view>
36 </view> 36 </view>
37 </view> 37 </view>
38 38
39 - <!-- Wheelchair --> 39 + <!-- wheelchair_needed -->
40 <view class="mb-6"> 40 <view class="mb-6">
41 <label class="block text-sm font-medium text-gray-700 mb-2">是否需要轮椅出行</label> 41 <label class="block text-sm font-medium text-gray-700 mb-2">是否需要轮椅出行</label>
42 <view class="bg-white rounded-xl p-4 border border-gray-200" @click="showWheelchairPicker = true"> 42 <view class="bg-white rounded-xl p-4 border border-gray-200" @click="showWheelchairPicker = true">
...@@ -88,6 +88,8 @@ import { My, Date as DateIcon, Right } from '@nutui/icons-vue-taro'; ...@@ -88,6 +88,8 @@ import { My, Date as DateIcon, Right } from '@nutui/icons-vue-taro';
88 import BASE_URL from '@/utils/config'; 88 import BASE_URL from '@/utils/config';
89 // 默认头像 89 // 默认头像
90 const defaultAvatar = 'https://cdn.ipadbiz.cn/mlaj/images/icon_1.jpeg' 90 const defaultAvatar = 'https://cdn.ipadbiz.cn/mlaj/images/icon_1.jpeg'
91 +// 获取接口信息
92 +import { getUserProfileAPI, updateUserProfileAPI } from '@/api/user';
91 93
92 /** 94 /**
93 * @description 表单数据 95 * @description 表单数据
...@@ -95,8 +97,8 @@ const defaultAvatar = 'https://cdn.ipadbiz.cn/mlaj/images/icon_1.jpeg' ...@@ -95,8 +97,8 @@ const defaultAvatar = 'https://cdn.ipadbiz.cn/mlaj/images/icon_1.jpeg'
95 const formData = reactive({ 97 const formData = reactive({
96 avatar_url: '', 98 avatar_url: '',
97 nickname: '', 99 nickname: '',
98 - birthday: '', 100 + birth_date: '',
99 - wheelchair: null, // 0 for No, 1 for Yes 101 + wheelchair_needed: null, // 0 for No, 1 for Yes
100 wheelchair_text: '', 102 wheelchair_text: '',
101 }); 103 });
102 104
...@@ -123,11 +125,11 @@ const currentDate = ref(new Date()); ...@@ -123,11 +125,11 @@ const currentDate = ref(new Date());
123 * @param {object} param0 - 包含 selectedValue 的对象 125 * @param {object} param0 - 包含 selectedValue 的对象
124 */ 126 */
125 const onDateConfirm = ({ selectedValue }) => { 127 const onDateConfirm = ({ selectedValue }) => {
126 - formData.birthday = selectedValue.join('-'); 128 + formData.birth_date = selectedValue.join('-');
127 showDatePicker.value = false; 129 showDatePicker.value = false;
128 }; 130 };
129 131
130 -// --- Wheelchair Picker --- 132 +// --- wheelchair_needed Picker ---
131 /** 133 /**
132 * @description 控制轮椅选择器显示 134 * @description 控制轮椅选择器显示
133 */ 135 */
...@@ -140,8 +142,8 @@ const wheelchairValue = ref([]); ...@@ -140,8 +142,8 @@ const wheelchairValue = ref([]);
140 * @description 轮椅选择器选项 142 * @description 轮椅选择器选项
141 */ 143 */
142 const wheelchairColumns = ref([ 144 const wheelchairColumns = ref([
143 - { text: '是', value: 1 }, 145 + { text: '是', value: true },
144 - { text: '否', value: 0 }, 146 + { text: '否', value: false },
145 ]); 147 ]);
146 148
147 /** 149 /**
...@@ -149,7 +151,7 @@ const wheelchairColumns = ref([ ...@@ -149,7 +151,7 @@ const wheelchairColumns = ref([
149 * @param {object} param0 - 包含 selectedValue 和 selectedOptions 的对象 151 * @param {object} param0 - 包含 selectedValue 和 selectedOptions 的对象
150 */ 152 */
151 const onWheelchairConfirm = ({ selectedValue, selectedOptions }) => { 153 const onWheelchairConfirm = ({ selectedValue, selectedOptions }) => {
152 - formData.wheelchair = selectedValue[0]; 154 + formData.wheelchair_needed = selectedValue[0];
153 formData.wheelchair_text = selectedOptions.map((option) => option.text).join(''); 155 formData.wheelchair_text = selectedOptions.map((option) => option.text).join('');
154 showWheelchairPicker.value = false; 156 showWheelchairPicker.value = false;
155 }; 157 };
...@@ -192,7 +194,7 @@ const changeAvatar = () => { ...@@ -192,7 +194,7 @@ const changeAvatar = () => {
192 Taro.hideLoading(); 194 Taro.hideLoading();
193 const data = JSON.parse(uploadRes.data); 195 const data = JSON.parse(uploadRes.data);
194 if (data.code === 0) { 196 if (data.code === 0) {
195 - formData.avatar_url = data.data.url; 197 + formData.avatar_url = data.data.src;
196 Taro.showToast({ title: '上传成功', icon: 'success' }); 198 Taro.showToast({ title: '上传成功', icon: 'success' });
197 } else { 199 } else {
198 Taro.showToast({ title: data.msg || '上传失败', icon: 'none' }); 200 Taro.showToast({ title: data.msg || '上传失败', icon: 'none' });
...@@ -224,7 +226,7 @@ const validateForm = () => { ...@@ -224,7 +226,7 @@ const validateForm = () => {
224 return false; 226 return false;
225 } 227 }
226 228
227 - if (!formData.birthday) { 229 + if (!formData.birth_date) {
228 Taro.showToast({ 230 Taro.showToast({
229 title: '请选择出生年月', 231 title: '请选择出生年月',
230 icon: 'none' 232 icon: 'none'
...@@ -232,7 +234,7 @@ const validateForm = () => { ...@@ -232,7 +234,7 @@ const validateForm = () => {
232 return false; 234 return false;
233 } 235 }
234 236
235 - if (formData.wheelchair === null || formData.wheelchair === undefined) { 237 + if (formData.wheelchair_needed === null || formData.wheelchair_needed === undefined) {
236 Taro.showToast({ 238 Taro.showToast({
237 title: '请选择是否需要轮椅出行', 239 title: '请选择是否需要轮椅出行',
238 icon: 'none' 240 icon: 'none'
...@@ -246,7 +248,7 @@ const validateForm = () => { ...@@ -246,7 +248,7 @@ const validateForm = () => {
246 /** 248 /**
247 * @description 保存用户信息 249 * @description 保存用户信息
248 */ 250 */
249 -const handleSave = () => { 251 +const handleSave = async () => {
250 // 验证表单 252 // 验证表单
251 if (!validateForm()) { 253 if (!validateForm()) {
252 return; 254 return;
...@@ -257,39 +259,37 @@ const handleSave = () => { ...@@ -257,39 +259,37 @@ const handleSave = () => {
257 formData.avatar_url = defaultAvatar; 259 formData.avatar_url = defaultAvatar;
258 } 260 }
259 261
260 - console.log('Saving data:', formData); 262 + // 保存数据
261 - Taro.showLoading({ title: '保存中...' }); 263 + const { code, data } = await updateUserProfileAPI(formData);
262 - // Mock save process 264 + if (code) {
263 - setTimeout(() => {
264 - Taro.hideLoading();
265 Taro.showToast({ 265 Taro.showToast({
266 title: '保存成功', 266 title: '保存成功',
267 icon: 'success', 267 icon: 'success',
268 - duration: 1500,
269 - complete: () => {
270 - Taro.navigateBack();
271 - },
272 }); 268 });
273 - }, 1000); 269 + setTimeout(() => {
270 + Taro.navigateBack();
271 + }, 1500);
272 + }
274 }; 273 };
275 274
276 /** 275 /**
277 * @description 页面加载时获取初始数据 276 * @description 页面加载时获取初始数据
278 */ 277 */
279 -onMounted(() => { 278 +onMounted(async () => {
280 - // Mock fetching user data, in a real app, you would call an API 279 + // 获取用户信息
281 - const mockUserData = { 280 + const { code, data } = await getUserProfileAPI();
282 - avatar_url: 'https://img.yzcdn.cn/vant/cat.jpeg', 281 + if (code && data?.user) {
283 - nickname: '张三', 282 + Object.assign(formData, data.user);
284 - birthday: '1990-05-15', 283 + // 初始化勾选数据
285 - wheelchair: 0, 284 + if (formData.birth_date) {
286 - wheelchair_text: '否', 285 + currentDate.value = new Date(formData.birth_date);
287 - }; 286 + }
288 - Object.assign(formData, mockUserData); 287 + // 设置轮椅选择器的值和显示文本
289 - 288 + if (formData.wheelchair_needed !== null && formData.wheelchair_needed !== undefined) {
290 - // Initialize pickers with current data 289 + wheelchairValue.value = [formData.wheelchair_needed];
291 - currentDate.value = new Date(mockUserData.birthday); 290 + formData.wheelchair_text = formData.wheelchair_needed ? '是' : '否';
292 - wheelchairValue.value = [mockUserData.wheelchair]; 291 + }
292 + }
293 }); 293 });
294 </script> 294 </script>
295 295
......
...@@ -191,7 +191,7 @@ const uploadImage = (filePath) => { ...@@ -191,7 +191,7 @@ const uploadImage = (filePath) => {
191 let upload_data = JSON.parse(res.data); 191 let upload_data = JSON.parse(res.data);
192 Taro.hideLoading({ 192 Taro.hideLoading({
193 success: () => { 193 success: () => {
194 - if (res.statusCode === 200) { 194 + if (data.code == 0) {
195 screenshots.value.push({ 195 screenshots.value.push({
196 url: upload_data.data.src, 196 url: upload_data.data.src,
197 localPath: filePath 197 localPath: filePath
......
...@@ -117,17 +117,15 @@ ...@@ -117,17 +117,15 @@
117 <!-- 家庭头像 --> 117 <!-- 家庭头像 -->
118 <view class="w-12 h-12 rounded-full bg-gray-200 flex items-center justify-center overflow-hidden"> 118 <view class="w-12 h-12 rounded-full bg-gray-200 flex items-center justify-center overflow-hidden">
119 <image 119 <image
120 - v-if="family.avatar" 120 + :src="family.avatar_url || defaultAvatar"
121 - :src="family.avatar"
122 class="w-full h-full object-cover" 121 class="w-full h-full object-cover"
123 /> 122 />
124 - <view v-else class="text-gray-500 text-xs">头像</view>
125 </view> 123 </view>
126 124
127 <!-- 家庭信息 --> 125 <!-- 家庭信息 -->
128 <view class="flex-1"> 126 <view class="flex-1">
129 <view class="font-medium text-gray-900 mb-1">{{ family.name }}</view> 127 <view class="font-medium text-gray-900 mb-1">{{ family.name }}</view>
130 - <view class="text-sm text-gray-600 line-clamp-2">{{ family.description }}</view> 128 + <view class="text-sm text-gray-600 line-clamp-2">{{ family.note }}</view>
131 </view> 129 </view>
132 130
133 <!-- 选中状态 --> 131 <!-- 选中状态 -->
...@@ -168,6 +166,10 @@ ...@@ -168,6 +166,10 @@
168 import { ref, computed, nextTick, onMounted, watch } from 'vue'; 166 import { ref, computed, nextTick, onMounted, watch } from 'vue';
169 import Taro from '@tarojs/taro'; 167 import Taro from '@tarojs/taro';
170 import { My, Check } from '@nutui/icons-vue-taro'; 168 import { My, Check } from '@nutui/icons-vue-taro';
169 +// 获取接口信息
170 +import { searchFamilyByPassphraseAPI, joinFamilyAPI } from '@/api/family';
171 +// 默认头像
172 +const defaultAvatar = 'https://cdn.ipadbiz.cn/mlaj/images/icon_1.jpeg'
171 173
172 const mottoChars = ref(['', '', '', '']); 174 const mottoChars = ref(['', '', '', '']);
173 const selectedRole = ref(''); 175 const selectedRole = ref('');
...@@ -239,42 +241,6 @@ const familyRoles = [ ...@@ -239,42 +241,6 @@ const familyRoles = [
239 { id: 'maternal-granddaughter', label: '外孙女' } 241 { id: 'maternal-granddaughter', label: '外孙女' }
240 ]; 242 ];
241 243
242 -// Mock家庭数据
243 -const generateMockFamilies = () => {
244 - return [
245 - {
246 - id: '1',
247 - name: '幸福之家',
248 - description: '我们是一个温馨和睦的大家庭,每天一起运动健身,分享生活的美好时光。',
249 - avatar: 'https://placehold.co/100x100/e2f3ff/0369a1?text=幸福&font=roboto'
250 - },
251 - {
252 - id: '2',
253 - name: '健康家园',
254 - description: '注重健康生活,坚持每日步行锻炼,用积分兑换健康好礼。',
255 - avatar: 'https://placehold.co/100x100/f0f9ff/0284c7?text=健康&font=roboto'
256 - },
257 - {
258 - id: '3',
259 - name: '快乐一家人',
260 - description: '快乐是我们家的主旋律,一起运动,一起成长,一起享受生活。',
261 - avatar: 'https://placehold.co/100x100/fef3c7/d97706?text=快乐&font=roboto'
262 - },
263 - {
264 - id: '4',
265 - name: '幸福一家人',
266 - description: '快乐是我们家的主旋律,一起运动,一起成长,一起享受生活。',
267 - avatar: 'https://placehold.co/100x100/fef3c7/d97706?text=快乐&font=roboto'
268 - },
269 - {
270 - id: '5',
271 - name: '幸福一家人',
272 - description: '快乐是我们家的主旋律,一起运动,一起成长,一起享受生活。',
273 - avatar: 'https://placehold.co/100x100/fef3c7/d97706?text=快乐&font=roboto'
274 - },
275 - ]
276 -};
277 -
278 const isComplete = computed(() => { 244 const isComplete = computed(() => {
279 return mottoChars.value.every((char) => char) && selectedRole.value; 245 return mottoChars.value.every((char) => char) && selectedRole.value;
280 }); 246 });
...@@ -359,7 +325,7 @@ watch(searchKeyword, () => { ...@@ -359,7 +325,7 @@ watch(searchKeyword, () => {
359 }) 325 })
360 326
361 // 确认加入家庭 327 // 确认加入家庭
362 -const confirmJoinFamily = () => { 328 +const confirmJoinFamily = async () => {
363 if (!selectedFamilyId.value) { 329 if (!selectedFamilyId.value) {
364 Taro.showToast({ 330 Taro.showToast({
365 title: '请选择一个家庭', 331 title: '请选择一个家庭',
...@@ -370,14 +336,25 @@ const confirmJoinFamily = () => { ...@@ -370,14 +336,25 @@ const confirmJoinFamily = () => {
370 336
371 const selectedFamily = mockFamilies.value.find(f => f.id === selectedFamilyId.value) 337 const selectedFamily = mockFamilies.value.find(f => f.id === selectedFamilyId.value)
372 console.log('确认加入家庭:', selectedFamily) 338 console.log('确认加入家庭:', selectedFamily)
339 + const joinFamily = await joinFamilyAPI({
340 + family_id: selectedFamily.id,
341 + role: selectedRole.value
342 + })
343 + if (joinFamily.code) {
344 + // 关闭弹窗
345 + closeFamilySelector()
373 346
374 - // 关闭弹窗 347 + Taro.showToast({
375 - closeFamilySelector() 348 + title: '加入成功',
349 + icon: 'success'
350 + })
376 351
377 - // 跳转到Dashboard 352 + setTimeout(() => {
378 - Taro.redirectTo({ 353 + Taro.redirectTo({
379 - url: '/pages/Dashboard/index' 354 + url: '/pages/Dashboard/index'
380 - }) 355 + })
356 + }, 1500)
357 + }
381 }; 358 };
382 359
383 const handleJoinFamily = async () => { 360 const handleJoinFamily = async () => {
...@@ -386,32 +363,50 @@ const handleJoinFamily = async () => { ...@@ -386,32 +363,50 @@ const handleJoinFamily = async () => {
386 const motto = mottoChars.value.join('') 363 const motto = mottoChars.value.join('')
387 364
388 try { 365 try {
389 - // TODO: 调用API查询家庭 366 + // 调用API查询家庭
390 - // const families = await api.queryFamiliesByMotto(motto) 367 + const { code, data } = await searchFamilyByPassphraseAPI({
391 - 368 + passphrase: motto,
392 - // 模拟API响应 - 生成mock数据 369 + page: 0,
393 - const families = generateMockFamilies() 370 + limit: 9999
394 - 371 + })
395 - console.log('查询家庭:', { motto, role: selectedRole.value, families })
396 -
397 - if (families.length === 0) {
398 - Taro.showToast({
399 - title: '未找到匹配的家庭',
400 - icon: 'none'
401 - })
402 - return
403 - }
404 372
405 - if (families.length === 1) { 373 + let families = [];
406 - // 只有一个家庭,直接跳转 374 +
407 - console.log('直接加入家庭:', families[0]) 375 + if (code) {
408 - Taro.redirectTo({ 376 + families = data;
409 - url: '/pages/Dashboard/index' 377 + console.log('查询家庭:', { motto, role: selectedRole.value, families })
410 - }) 378 +
411 - } else { 379 + if (families.length === 0) {
412 - // 多个家庭,显示选择弹窗 380 + Taro.showToast({
413 - mockFamilies.value = families 381 + title: '未找到匹配的家庭',
414 - showFamilySelector.value = true 382 + icon: 'none'
383 + })
384 + return
385 + }
386 +
387 + if (families.length === 1) {
388 + // 只有一个家庭,直接加入
389 + const joinFamily = await joinFamilyAPI({
390 + family_id: families[0].id,
391 + role: selectedRole.value
392 + })
393 + if (joinFamily.code) {
394 + Taro.showToast({
395 + title: '加入成功',
396 + icon: 'success'
397 + })
398 +
399 + setTimeout(() => {
400 + Taro.redirectTo({
401 + url: '/pages/Dashboard/index'
402 + })
403 + }, 1500)
404 + }
405 + } else {
406 + // 多个家庭,显示选择弹窗
407 + showFamilySelector.value = true
408 + mockFamilies.value = families
409 + }
415 } 410 }
416 } catch (error) { 411 } catch (error) {
417 console.error('加入家庭失败:', error) 412 console.error('加入家庭失败:', error)
......
1 <!-- 1 <!--
2 * @Date: 2025-08-27 17:47:46 2 * @Date: 2025-08-27 17:47:46
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-09-02 14:55:52 4 + * @LastEditTime: 2025-09-02 20:41:29
5 * @FilePath: /lls_program/src/pages/Profile/index.vue 5 * @FilePath: /lls_program/src/pages/Profile/index.vue
6 * @Description: 文件描述 6 * @Description: 文件描述
7 --> 7 -->
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
14 <!-- User profile section --> 14 <!-- User profile section -->
15 <view class="px-4 pt-8 pb-6 flex items-center justify-between"> 15 <view class="px-4 pt-8 pb-6 flex items-center justify-between">
16 <view class="flex items-center"> 16 <view class="flex items-center">
17 - <image :src="defaultAvatar" alt="User avatar" class="w-16 h-16 rounded-full border-2 border-white" /> 17 + <image :src="userInfo.avatarUrl || defaultAvatar" alt="User avatar" class="w-16 h-16 rounded-full border-2 border-white" />
18 <view class="ml-4"> 18 <view class="ml-4">
19 <h1 class="text-xl font-bold text-white">{{ userInfo.nickName }}</h1> 19 <h1 class="text-xl font-bold text-white">{{ userInfo.nickName }}</h1>
20 </view> 20 </view>
...@@ -50,6 +50,8 @@ import BottomNav from '../../components/BottomNav.vue'; ...@@ -50,6 +50,8 @@ import BottomNav from '../../components/BottomNav.vue';
50 import { My, Shop3, Cart, Message, Tips, Right } from '@nutui/icons-vue-taro'; 50 import { My, Shop3, Cart, Message, Tips, Right } from '@nutui/icons-vue-taro';
51 // 默认头像 51 // 默认头像
52 const defaultAvatar = 'https://cdn.ipadbiz.cn/mlaj/images/icon_1.jpeg' 52 const defaultAvatar = 'https://cdn.ipadbiz.cn/mlaj/images/icon_1.jpeg'
53 +// 获取接口信息
54 +import { getUserProfileAPI } from '@/api/user'
53 55
54 const menuItems = shallowRef([ 56 const menuItems = shallowRef([
55 { 57 {
...@@ -105,11 +107,14 @@ const goToEditProfile = () => { ...@@ -105,11 +107,14 @@ const goToEditProfile = () => {
105 Taro.navigateTo({ url: '/pages/EditProfile/index' }); 107 Taro.navigateTo({ url: '/pages/EditProfile/index' });
106 }; 108 };
107 109
108 -const initPageData = () => { 110 +const initPageData = async () => {
109 // 获取用户信息 111 // 获取用户信息
110 - userInfo.value = { 112 + const { code, data } = await getUserProfileAPI()
111 - nickName: '用户昵称', 113 + if (code) {
112 - avatarUrl: defaultAvatar, 114 + userInfo.value = {
115 + nickName: data?.user?.nickname || '',
116 + avatarUrl: data?.user?.avatar_url || '',
117 + }
113 } 118 }
114 } 119 }
115 120
......
...@@ -368,7 +368,7 @@ const uploadFileToServer = (filePath) => { ...@@ -368,7 +368,7 @@ const uploadFileToServer = (filePath) => {
368 success: function (res) { 368 success: function (res) {
369 try { 369 try {
370 let upload_data = JSON.parse(res.data); 370 let upload_data = JSON.parse(res.data);
371 - if (res.statusCode === 200 && upload_data.data) { 371 + if (data.code == 0 && upload_data.data) {
372 resolve(upload_data.data.src); 372 resolve(upload_data.data.src);
373 } else { 373 } else {
374 reject(new Error('服务器错误')); 374 reject(new Error('服务器错误'));
......
1 <!-- 1 <!--
2 * @Date: 2025-08-27 17:43:45 2 * @Date: 2025-08-27 17:43:45
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-09-02 11:52:57 4 + * @LastEditTime: 2025-09-02 18:11:12
5 * @FilePath: /lls_program/src/pages/Welcome/index.vue 5 * @FilePath: /lls_program/src/pages/Welcome/index.vue
6 * @Description: 文件描述 6 * @Description: 文件描述
7 --> 7 -->
...@@ -61,7 +61,7 @@ ...@@ -61,7 +61,7 @@
61 </view> 61 </view>
62 <!-- Action Buttons --> 62 <!-- Action Buttons -->
63 <view class="space-y-4 mt-auto"> 63 <view class="space-y-4 mt-auto">
64 - <view v-if="canCreateFamily" @tap="handleNavigate('/pages/CreateFamily/index')" class="w-full py-3.5 bg-blue-500 text-white text-lg font-medium rounded-full text-center"> 64 + <view @tap="handleNavigate('/pages/CreateFamily/index')" class="w-full py-3.5 bg-blue-500 text-white text-lg font-medium rounded-full text-center">
65 创建家庭 65 创建家庭
66 </view> 66 </view>
67 <view @tap="handleNavigate('/pages/JoinFamily/index')" class="w-full py-3.5 bg-white text-gray-800 text-lg font-medium rounded-full border border-gray-300 text-center" style="margin-bottom: 1rem;"> 67 <view @tap="handleNavigate('/pages/JoinFamily/index')" class="w-full py-3.5 bg-white text-gray-800 text-lg font-medium rounded-full border border-gray-300 text-center" style="margin-bottom: 1rem;">
...@@ -75,44 +75,45 @@ ...@@ -75,44 +75,45 @@
75 </template> 75 </template>
76 76
77 <script setup> 77 <script setup>
78 -import { ref, computed, onMounted } from 'vue'; 78 +import { ref } from 'vue';
79 -import Taro from '@tarojs/taro'; 79 +import Taro, { useDidShow } from '@tarojs/taro';
80 import BottomNav from '../../components/BottomNav.vue'; // 假设BottomNav组件已转换 80 import BottomNav from '../../components/BottomNav.vue'; // 假设BottomNav组件已转换
81 import welcomeHomeImg from '../../assets/images/welcome_home.png'; 81 import welcomeHomeImg from '../../assets/images/welcome_home.png';
82 +// 获取接口信息
83 +import { getUserProfileAPI } from '@/api/user';
82 84
83 -// TODO: 等待真实接口获取用户年龄
84 const userAge = ref(null); 85 const userAge = ref(null);
85 -const userInfo = ref({ 86 +const userInfo = ref({});
86 - age: null, 87 +const canCreateFamily = ref(true);
87 - name: null, 88 +const hasProfile = ref(false);
88 - phone: null,
89 -});
90 -const canCreateFamily = computed(() => userAge.value >= 60);
91 89
92 const navigateTo = (url) => { 90 const navigateTo = (url) => {
93 Taro.navigateTo({ url }); 91 Taro.navigateTo({ url });
94 }; 92 };
95 93
96 -onMounted(() => { 94 +useDidShow(async () => {
97 - // 模拟获取用户的个人信息 95 + // 获取用户的个人信息
98 - const userInfo = { 96 + const { code, data } = await getUserProfileAPI();
99 - age: '1890-01-01', 97 + if (code) {
100 - name: '张三', 98 + userInfo.value = data?.user?.nickname ? data.user : {
101 - phone: '13800000000', 99 + avatar_url: null,
102 - }; 100 + nickname: null,
103 - 101 + birthday: null,
104 - userInfo.value = userInfo; 102 + wheelchair: null,
105 - // userInfo.age 是年月日的形式需要转成年龄 103 + wheelchair_text: null,
106 - userInfo.value.age = new Date().getFullYear() - new Date(userInfo.value.age).getFullYear(); 104 + phone: null,
107 - userAge.value = userInfo.value.age; 105 + };
108 - console.warn(userAge.value); 106 + // userInfo.birthday 是年月日的形式需要转成年龄
107 + userInfo.value.birthday = new Date().getFullYear() - new Date(userInfo.value.birth_date).getFullYear();
108 + // 检查用户是否完善了个人信息
109 + hasProfile.value = userInfo.value.nickname && userInfo.value.birth_date && userInfo.value.wheelchair_needed !== null;
110 + userAge.value = userInfo.value.birthday;
111 + }
109 }); 112 });
110 113
111 const handleNavigate = (url) => { 114 const handleNavigate = (url) => {
112 - // TODO: 模拟检查个人信息是否完善 115 + // 检查个人信息是否完善
113 - const hasProfile = true; // 假设未完善 116 + if (!hasProfile.value) {
114 -
115 - if (!hasProfile) {
116 Taro.showModal({ 117 Taro.showModal({
117 title: '提示', 118 title: '提示',
118 content: '参加活动需要完善个人信息', 119 content: '参加活动需要完善个人信息',
...@@ -125,7 +126,17 @@ const handleNavigate = (url) => { ...@@ -125,7 +126,17 @@ const handleNavigate = (url) => {
125 }, 126 },
126 }); 127 });
127 } else { 128 } else {
128 - navigateTo(url); 129 + // 检查用户是否年满60岁
130 + if (canCreateFamily.value) {
131 + navigateTo(url);
132 + } else {
133 + Taro.showModal({
134 + title: '提示',
135 + content: '您需要年满60岁才能创建家庭',
136 + cancelText: '关闭',
137 + confirmText: '了解',
138 + });
139 + }
129 } 140 }
130 }; 141 };
131 </script> 142 </script>
......
1 <!-- 1 <!--
2 * @Date: 2022-09-19 14:11:06 2 * @Date: 2022-09-19 14:11:06
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-09-01 13:57:13 4 + * @LastEditTime: 2025-09-02 19:46:17
5 * @FilePath: /lls_program/src/pages/auth/index.vue 5 * @FilePath: /lls_program/src/pages/auth/index.vue
6 * @Description: 文件描述 6 * @Description: 文件描述
7 --> 7 -->
...@@ -34,13 +34,24 @@ export default { ...@@ -34,13 +34,24 @@ export default {
34 Taro.showLoading({ 34 Taro.showLoading({
35 title: '授权中', 35 title: '授权中',
36 }) 36 })
37 - request.post('/srv/?a=openid', { 37 + // 根据环境判断是否传递openid参数
38 + const requestData = {
38 code: res.code 39 code: res.code
39 - // openid: '0002' 40 + };
40 - // openid: 'o5NFZ5cFQtLRy3aVHaZMLkjHFusI' 41 +
41 - // openid: 'o5NFZ5TpgG4FwYursGCLjcUJH2ak' 42 + // 测试环境下传递openid,正式环境不传递
42 - // openid: 'o5NFZ5cqroPYwawCp8FEOxewtgnw' 43 + if (process.env.NODE_ENV === 'development') {
43 - }) 44 + // requestData.openid = 'h-008';
45 + requestData.openid = 'h-009';
46 + // requestData.openid = 'h-010';
47 + // requestData.openid = 'h-011';
48 + // requestData.openid = 'h-012';
49 + // requestData.openid = 'h-013';
50 + // requestData.openid = 'oWbdFvkD5VtloC50wSNR9IWiU2q8';
51 + // requestData.openid = 'oex8h5QZnZJto3ttvO6swSvylAQo';
52 + }
53 +
54 + request.post('/srv/?a=openid', requestData)
44 .then(res => { 55 .then(res => {
45 if (res.data.code) { 56 if (res.data.code) {
46 var cookie = res.cookies[0]; 57 var cookie = res.cookies[0];
......