hookehuyr

feat(user): 添加用户性别字段支持

refactor(EditFamily): 添加familyId字段用于家庭信息更新
style(ShareButton): 调整按钮间距和样式
fix(auth): 修正测试环境openid配置
docs(PosterCheckin): 更新海报关卡描述显示
feat(profile): 在添加和编辑资料页面增加性别选择功能
...@@ -26,6 +26,7 @@ const Api = { ...@@ -26,6 +26,7 @@ const Api = {
26 * @returns {string} response.data.user.avatar_url - 用户头像URL 26 * @returns {string} response.data.user.avatar_url - 用户头像URL
27 * @returns {string} response.data.user.birth_date - 用户生日 27 * @returns {string} response.data.user.birth_date - 用户生日
28 * @returns {boolean} response.data.user.wheelchair_needed - 是否需要轮椅 28 * @returns {boolean} response.data.user.wheelchair_needed - 是否需要轮椅
29 + * @returns {number} response.data.user.gender - 用户性别 (0=女, 1=男)
29 */ 30 */
30 export const getUserProfileAPI = (params) => fn(fetch.get(Api.GET_PROFILE, params)); 31 export const getUserProfileAPI = (params) => fn(fetch.get(Api.GET_PROFILE, params));
31 32
...@@ -36,5 +37,6 @@ export const getUserProfileAPI = (params) => fn(fetch.get(Api.GET_PROFILE, param ...@@ -36,5 +37,6 @@ export const getUserProfileAPI = (params) => fn(fetch.get(Api.GET_PROFILE, param
36 * @param {string} params.birth_date - 用户生日 37 * @param {string} params.birth_date - 用户生日
37 * @param {string} params.avatar_url - 用户头像URL 38 * @param {string} params.avatar_url - 用户头像URL
38 * @param {boolean} params.wheelchair_needed - 是否需要轮椅 39 * @param {boolean} params.wheelchair_needed - 是否需要轮椅
40 + * @param {number} params.gender - 用户性别 (0=女, 1=男)
39 */ 41 */
40 export const updateUserProfileAPI = (params) => fn(fetch.post(Api.UPDATE_PROFILE, params)); 42 export const updateUserProfileAPI = (params) => fn(fetch.post(Api.UPDATE_PROFILE, params));
......
1 <!-- 1 <!--
2 * @Date: 2025-09-03 14:34:58 2 * @Date: 2025-09-03 14:34:58
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-09-08 11:16:47 4 + * @LastEditTime: 2025-09-08 11:50:44
5 * @FilePath: /lls_program/src/components/ShareButton/index.vue 5 * @FilePath: /lls_program/src/components/ShareButton/index.vue
6 * @Description: 文件描述 6 * @Description: 文件描述
7 --> 7 -->
...@@ -54,7 +54,7 @@ const handleSharePoster = () => { ...@@ -54,7 +54,7 @@ const handleSharePoster = () => {
54 z-index: 1000; 54 z-index: 1000;
55 display: flex; 55 display: flex;
56 flex-direction: column; 56 flex-direction: column;
57 - gap: 16rpx; 57 + gap: 28rpx;
58 align-items: center; 58 align-items: center;
59 } 59 }
60 60
...@@ -68,7 +68,7 @@ const handleSharePoster = () => { ...@@ -68,7 +68,7 @@ const handleSharePoster = () => {
68 font: inherit !important; 68 font: inherit !important;
69 position: relative !important; 69 position: relative !important;
70 display: inline-block !important; 70 display: inline-block !important;
71 - 71 +
72 // 应用自定义样式 72 // 应用自定义样式
73 color: white !important; 73 color: white !important;
74 padding: 12rpx 20rpx !important; 74 padding: 12rpx 20rpx !important;
...@@ -93,13 +93,13 @@ const handleSharePoster = () => { ...@@ -93,13 +93,13 @@ const handleSharePoster = () => {
93 transform: scale(0.95) !important; 93 transform: scale(0.95) !important;
94 background: rgba(0, 0, 0, 0.8) !important; 94 background: rgba(0, 0, 0, 0.8) !important;
95 } 95 }
96 - 96 +
97 // 去除微信小程序button的默认样式 97 // 去除微信小程序button的默认样式
98 &::after { 98 &::after {
99 border: none !important; 99 border: none !important;
100 content: none !important; 100 content: none !important;
101 } 101 }
102 - 102 +
103 // 去除button默认的点击效果 103 // 去除button默认的点击效果
104 &.button-hover { 104 &.button-hover {
105 background: rgba(0, 0, 0, 0.8) !important; 105 background: rgba(0, 0, 0, 0.8) !important;
......
...@@ -34,6 +34,20 @@ ...@@ -34,6 +34,20 @@
34 <DateIcon size="20" color="#888" /> 34 <DateIcon size="20" color="#888" />
35 </view> 35 </view>
36 </view> 36 </view>
37 + <text class="text-red-500 text-xs mt-1 block">注意:出生年月填写后不可修改,请仔细确认</text>
38 + </view>
39 +
40 + <!-- gender -->
41 + <view class="mb-6">
42 + <label class="block text-sm font-medium text-gray-700 mb-2">性别</label>
43 + <view class="bg-white rounded-xl p-4 border border-gray-200" @click="showGenderPicker = true">
44 + <view class="flex justify-between items-center">
45 + <text :class="{'text-gray-400': !formData.gender_text, 'text-gray-900': formData.gender_text}" class="text-base">
46 + {{ formData.gender_text || '请选择' }}
47 + </text>
48 + <Right size="16" color="#888" />
49 + </view>
50 + </view>
37 </view> 51 </view>
38 52
39 <!-- wheelchair_needed --> 53 <!-- wheelchair_needed -->
...@@ -77,6 +91,16 @@ ...@@ -77,6 +91,16 @@
77 ></nut-picker> 91 ></nut-picker>
78 </nut-popup> 92 </nut-popup>
79 93
94 + <nut-popup v-model:visible="showGenderPicker" position="bottom">
95 + <nut-picker
96 + v-model="genderValue"
97 + :columns="genderColumns"
98 + @confirm="onGenderConfirm"
99 + @cancel="showGenderPicker = false"
100 + title="选择性别"
101 + ></nut-picker>
102 + </nut-popup>
103 +
80 <nut-image-preview v-model:show="previewVisible" :images="previewImages" /> 104 <nut-image-preview v-model:show="previewVisible" :images="previewImages" />
81 </view> 105 </view>
82 </template> 106 </template>
...@@ -100,13 +124,15 @@ const formData = reactive({ ...@@ -100,13 +124,15 @@ const formData = reactive({
100 birth_date: '', 124 birth_date: '',
101 wheelchair_needed: null, // 0 for No, 1 for Yes 125 wheelchair_needed: null, // 0 for No, 1 for Yes
102 wheelchair_text: '', 126 wheelchair_text: '',
127 + gender: null, // 0 for 女, 1 for 男
128 + gender_text: '',
103 }); 129 });
104 130
105 /** 131 /**
106 * @description 检查表单是否有效 132 * @description 检查表单是否有效
107 */ 133 */
108 const isFormValid = computed(() => { 134 const isFormValid = computed(() => {
109 - return formData.nickname && formData.birth_date && formData.wheelchair_needed !== null; 135 + return formData.nickname && formData.birth_date && formData.wheelchair_needed !== null && formData.gender !== null;
110 }); 136 });
111 137
112 // --- Date Picker --- 138 // --- Date Picker ---
...@@ -153,6 +179,33 @@ const wheelchairColumns = ref([ ...@@ -153,6 +179,33 @@ const wheelchairColumns = ref([
153 { text: '否', value: 0 }, 179 { text: '否', value: 0 },
154 ]); 180 ]);
155 181
182 +// --- Gender Picker ---
183 +/**
184 + * @description 控制性别选择器显示
185 + */
186 +const showGenderPicker = ref(false);
187 +/**
188 + * @description 当前选中的性别选项
189 + */
190 +const genderValue = ref([]);
191 +/**
192 + * @description 性别选择器选项
193 + */
194 +const genderColumns = ref([
195 + { text: '女', value: 0 },
196 + { text: '男', value: 1 },
197 +]);
198 +
199 +/**
200 + * @description 确认选择性别选项
201 + * @param {object} param0 - 包含 selectedValue 和 selectedOptions 的对象
202 + */
203 +const onGenderConfirm = ({ selectedValue, selectedOptions }) => {
204 + formData.gender = selectedValue[0];
205 + formData.gender_text = selectedOptions.map((option) => option.text).join('');
206 + showGenderPicker.value = false;
207 +};
208 +
156 /** 209 /**
157 * @description 确认选择轮椅选项 210 * @description 确认选择轮椅选项
158 * @param {object} param0 - 包含 selectedValue 和 selectedOptions 的对象 211 * @param {object} param0 - 包含 selectedValue 和 selectedOptions 的对象
......
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-04 17:39:21 4 + * @LastEditTime: 2025-09-08 11:55:03
5 * @FilePath: /lls_program/src/pages/EditFamily/index.vue 5 * @FilePath: /lls_program/src/pages/EditFamily/index.vue
6 * @Description: 文件描述 6 * @Description: 文件描述
7 --> 7 -->
...@@ -176,6 +176,7 @@ const defaultFamilyCoverSvg = 'https://cdn.ipadbiz.cn/lls_prog/images/default-fa ...@@ -176,6 +176,7 @@ const defaultFamilyCoverSvg = 'https://cdn.ipadbiz.cn/lls_prog/images/default-fa
176 // 获取接口信息 176 // 获取接口信息
177 import { editFamilyAPI, getFamilyInfoAPI } from '@/api/family'; 177 import { editFamilyAPI, getFamilyInfoAPI } from '@/api/family';
178 178
179 +const familyId = ref('');
179 const familyName = ref(''); 180 const familyName = ref('');
180 const familyIntro = ref(''); 181 const familyIntro = ref('');
181 const selectedDistrict = ref(null); 182 const selectedDistrict = ref(null);
...@@ -221,6 +222,7 @@ onMounted(async () => { ...@@ -221,6 +222,7 @@ onMounted(async () => {
221 Taro.setNavigationBarTitle({ title: '编辑家庭' }); 222 Taro.setNavigationBarTitle({ title: '编辑家庭' });
222 const { code, data } = await getFamilyInfoAPI(); 223 const { code, data } = await getFamilyInfoAPI();
223 if (code) { 224 if (code) {
225 + familyId.value = data.id;
224 familyName.value = data.name; 226 familyName.value = data.name;
225 familyIntro.value = data.note; 227 familyIntro.value = data.note;
226 districtValue.value = [+data.county]; 228 districtValue.value = [+data.county];
...@@ -474,6 +476,7 @@ const handleSaveFamily = async () => { ...@@ -474,6 +476,7 @@ const handleSaveFamily = async () => {
474 476
475 // 调用API保存家庭信息 477 // 调用API保存家庭信息
476 const { code } = await editFamilyAPI({ 478 const { code } = await editFamilyAPI({
479 + id: familyId.value,
477 name: familyName.value, 480 name: familyName.value,
478 note: familyIntro.value, 481 note: familyIntro.value,
479 county: selectedDistrict.value, 482 county: selectedDistrict.value,
...@@ -481,7 +484,7 @@ const handleSaveFamily = async () => { ...@@ -481,7 +484,7 @@ const handleSaveFamily = async () => {
481 avatar_url: familyAvatar.value, 484 avatar_url: familyAvatar.value,
482 }); 485 });
483 if (code) { 486 if (code) {
484 - showToast('家庭信息保存成功', 'success'); 487 + showToast('保存成功', 'success');
485 488
486 setTimeout(() => { 489 setTimeout(() => {
487 Taro.navigateBack(); 490 Taro.navigateBack();
......
...@@ -26,12 +26,26 @@ ...@@ -26,12 +26,26 @@
26 <!-- birth_date --> 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-gray-100 rounded-xl p-4 border border-gray-200">
30 <view class="flex justify-between items-center"> 30 <view class="flex justify-between items-center">
31 - <text :class="{'text-gray-400': !formData.birth_date, 'text-gray-900': formData.birth_date}" class="text-base"> 31 + <text class="text-gray-500 text-base">
32 {{ formData.birth_date || '-/-/-/' }} 32 {{ formData.birth_date || '-/-/-/' }}
33 </text> 33 </text>
34 - <DateIcon size="20" color="#888" /> 34 + <DateIcon size="20" color="#ccc" />
35 + </view>
36 + </view>
37 + <text class="text-gray-400 text-xs mt-1 block">出生年月不可修改</text>
38 + </view>
39 +
40 + <!-- gender -->
41 + <view class="mb-6">
42 + <label class="block text-sm font-medium text-gray-700 mb-2">性别</label>
43 + <view class="bg-white rounded-xl p-4 border border-gray-200" @click="showGenderPicker = true">
44 + <view class="flex justify-between items-center">
45 + <text :class="{'text-gray-400': !formData.gender_text, 'text-gray-900': formData.gender_text}" class="text-base">
46 + {{ formData.gender_text || '请选择' }}
47 + </text>
48 + <Right size="16" color="#888" />
35 </view> 49 </view>
36 </view> 50 </view>
37 </view> 51 </view>
...@@ -77,6 +91,16 @@ ...@@ -77,6 +91,16 @@
77 ></nut-picker> 91 ></nut-picker>
78 </nut-popup> 92 </nut-popup>
79 93
94 + <nut-popup v-model:visible="showGenderPicker" position="bottom">
95 + <nut-picker
96 + v-model="genderValue"
97 + :columns="genderColumns"
98 + @confirm="onGenderConfirm"
99 + @cancel="showGenderPicker = false"
100 + title="选择性别"
101 + ></nut-picker>
102 + </nut-popup>
103 +
80 <nut-image-preview v-model:show="previewVisible" :images="previewImages" /> 104 <nut-image-preview v-model:show="previewVisible" :images="previewImages" />
81 </view> 105 </view>
82 </template> 106 </template>
...@@ -100,6 +124,8 @@ const formData = reactive({ ...@@ -100,6 +124,8 @@ const formData = reactive({
100 birth_date: '', 124 birth_date: '',
101 wheelchair_needed: null, // 0 for No, 1 for Yes 125 wheelchair_needed: null, // 0 for No, 1 for Yes
102 wheelchair_text: '', 126 wheelchair_text: '',
127 + gender: null, // 0 for 女, 1 for 男
128 + gender_text: '',
103 }); 129 });
104 130
105 // --- Date Picker --- 131 // --- Date Picker ---
...@@ -146,6 +172,33 @@ const wheelchairColumns = ref([ ...@@ -146,6 +172,33 @@ const wheelchairColumns = ref([
146 { text: '否', value: false }, 172 { text: '否', value: false },
147 ]); 173 ]);
148 174
175 +// --- Gender Picker ---
176 +/**
177 + * @description 控制性别选择器显示
178 + */
179 +const showGenderPicker = ref(false);
180 +/**
181 + * @description 当前选中的性别选项
182 + */
183 +const genderValue = ref([]);
184 +/**
185 + * @description 性别选择器选项
186 + */
187 +const genderColumns = ref([
188 + { text: '女', value: 0 },
189 + { text: '男', value: 1 },
190 +]);
191 +
192 +/**
193 + * @description 确认选择性别选项
194 + * @param {object} param0 - 包含 selectedValue 和 selectedOptions 的对象
195 + */
196 +const onGenderConfirm = ({ selectedValue, selectedOptions }) => {
197 + formData.gender = selectedValue[0];
198 + formData.gender_text = selectedOptions.map((option) => option.text).join('');
199 + showGenderPicker.value = false;
200 +};
201 +
149 /** 202 /**
150 * @description 确认选择轮椅选项 203 * @description 确认选择轮椅选项
151 * @param {object} param0 - 包含 selectedValue 和 selectedOptions 的对象 204 * @param {object} param0 - 包含 selectedValue 和 selectedOptions 的对象
...@@ -289,6 +342,11 @@ onMounted(async () => { ...@@ -289,6 +342,11 @@ onMounted(async () => {
289 wheelchairValue.value = [formData.wheelchair_needed]; 342 wheelchairValue.value = [formData.wheelchair_needed];
290 formData.wheelchair_text = formData.wheelchair_needed ? '是' : '否'; 343 formData.wheelchair_text = formData.wheelchair_needed ? '是' : '否';
291 } 344 }
345 + // 设置性别选择器的值和显示文本
346 + if (formData.gender !== null && formData.gender !== undefined) {
347 + genderValue.value = [formData.gender];
348 + formData.gender_text = formData.gender === 0 ? '女' : '男';
349 + }
292 } 350 }
293 }); 351 });
294 </script> 352 </script>
......
...@@ -207,7 +207,7 @@ const posterList = ref([ ...@@ -207,7 +207,7 @@ const posterList = ref([
207 }, 207 },
208 level: { 208 level: {
209 logo: 'https://cdn.ipadbiz.cn/lls_prog/images/%E6%B5%B7%E6%8A%A5%E5%8F%B3%E4%B8%8B%E8%A7%92icon.png', 209 logo: 'https://cdn.ipadbiz.cn/lls_prog/images/%E6%B5%B7%E6%8A%A5%E5%8F%B3%E4%B8%8B%E8%A7%92icon.png',
210 - name: '第一关卡' 210 + name: '卡点: 上海市第一百货商店'
211 }, 211 },
212 qrcode: 'https://cdn.ipadbiz.cn/space/068a790496c87cb8d2ed6e551401c544.png', 212 qrcode: 'https://cdn.ipadbiz.cn/space/068a790496c87cb8d2ed6e551401c544.png',
213 qrcodeDesc: '长按识别,来,我们一起打卡!' 213 qrcodeDesc: '长按识别,来,我们一起打卡!'
...@@ -364,25 +364,25 @@ const posterConfig = computed(() => { ...@@ -364,25 +364,25 @@ const posterConfig = computed(() => {
364 zIndex: 2 364 zIndex: 2
365 }, 365 },
366 // 家庭描述 366 // 家庭描述
367 - { 367 + // {
368 - x: 40, 368 + // x: 40,
369 - y: 250, 369 + // y: 250,
370 - text: currentMockData.value.family.description, 370 + // text: currentMockData.value.family.description,
371 - fontSize: 28, 371 + // fontSize: 28,
372 - color: '#ffffff', 372 + // color: '#ffffff',
373 - textAlign: 'left', 373 + // textAlign: 'left',
374 - shadowColor: 'rgba(0, 0, 0, 0.6)', 374 + // shadowColor: 'rgba(0, 0, 0, 0.6)',
375 - shadowOffsetX: 2, 375 + // shadowOffsetX: 2,
376 - shadowOffsetY: 2, 376 + // shadowOffsetY: 2,
377 - shadowBlur: 4, 377 + // shadowBlur: 4,
378 - zIndex: 2 378 + // zIndex: 2
379 - }, 379 + // },
380 - // 关卡描述 380 + // 小程序码描述
381 { 381 {
382 x: 280, 382 x: 280,
383 y: 1125, 383 y: 1125,
384 - text: currentMockData.value.level.name, 384 + text: currentMockData.value.qrcodeDesc,
385 - fontSize: 35, 385 + fontSize: 28,
386 color: '#333333', 386 color: '#333333',
387 lineHeight: 40, 387 lineHeight: 40,
388 lineNum: 2, 388 lineNum: 2,
...@@ -390,19 +390,19 @@ const posterConfig = computed(() => { ...@@ -390,19 +390,19 @@ const posterConfig = computed(() => {
390 textAlign: 'left', 390 textAlign: 'left',
391 zIndex: 1 391 zIndex: 1
392 }, 392 },
393 - // 小程序码描述 393 + // 关卡描述
394 { 394 {
395 x: 280, 395 x: 280,
396 - y: 1200, 396 + y: 1180,
397 - text: currentMockData.value.qrcodeDesc, 397 + text: currentMockData.value.level.name,
398 - fontSize: 28, 398 + fontSize: 32,
399 color: '#333333', 399 color: '#333333',
400 lineHeight: 40, 400 lineHeight: 40,
401 lineNum: 2, 401 lineNum: 2,
402 width: 400, 402 width: 400,
403 textAlign: 'left', 403 textAlign: 'left',
404 zIndex: 1 404 zIndex: 1
405 - } 405 + },
406 ], 406 ],
407 blocks: [ 407 blocks: [
408 // 下半部分白色背景 408 // 下半部分白色背景
......
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-06 22:13:30 4 + * @LastEditTime: 2025-09-07 01:37:03
5 * @FilePath: /lls_program/src/pages/auth/index.vue 5 * @FilePath: /lls_program/src/pages/auth/index.vue
6 * @Description: 文件描述 6 * @Description: 文件描述
7 --> 7 -->
...@@ -42,12 +42,12 @@ export default { ...@@ -42,12 +42,12 @@ export default {
42 42
43 // 测试环境下传递openid,正式环境不传递 43 // 测试环境下传递openid,正式环境不传递
44 if (process.env.NODE_ENV === 'development') { 44 if (process.env.NODE_ENV === 'development') {
45 - // requestData.openid = 'h-008'; 45 + requestData.openid = 'h-008';
46 // requestData.openid = 'h-009'; 46 // requestData.openid = 'h-009';
47 // requestData.openid = 'h-010'; 47 // requestData.openid = 'h-010';
48 // requestData.openid = 'h-011'; 48 // requestData.openid = 'h-011';
49 // requestData.openid = 'h-012'; 49 // requestData.openid = 'h-012';
50 - requestData.openid = 'h-013'; 50 + // requestData.openid = 'h-013';
51 // requestData.openid = 'oWbdFvkD5VtloC50wSNR9IWiU2q8'; 51 // requestData.openid = 'oWbdFvkD5VtloC50wSNR9IWiU2q8';
52 // requestData.openid = 'oex8h5QZnZJto3ttvO6swSvylAQo'; 52 // requestData.openid = 'oex8h5QZnZJto3ttvO6swSvylAQo';
53 } 53 }
......