hookehuyr

feat(EditProfile): 新增编辑个人信息页面

添加编辑个人信息页面,包含头像上传、昵称修改、出生年月选择和轮椅出行需求选择功能
...@@ -12,7 +12,11 @@ declare module 'vue' { ...@@ -12,7 +12,11 @@ declare module 'vue' {
12 GlassCard: typeof import('./src/components/GlassCard.vue')['default'] 12 GlassCard: typeof import('./src/components/GlassCard.vue')['default']
13 NavBar: typeof import('./src/components/navBar.vue')['default'] 13 NavBar: typeof import('./src/components/navBar.vue')['default']
14 NutButton: typeof import('@nutui/nutui-taro')['Button'] 14 NutButton: typeof import('@nutui/nutui-taro')['Button']
15 + NutDatePicker: typeof import('@nutui/nutui-taro')['DatePicker']
15 NutImagePreview: typeof import('@nutui/nutui-taro')['ImagePreview'] 16 NutImagePreview: typeof import('@nutui/nutui-taro')['ImagePreview']
17 + NutInput: typeof import('@nutui/nutui-taro')['Input']
18 + NutPicker: typeof import('@nutui/nutui-taro')['Picker']
19 + NutPopup: typeof import('@nutui/nutui-taro')['Popup']
16 Picker: typeof import('./src/components/time-picker-data/picker.vue')['default'] 20 Picker: typeof import('./src/components/time-picker-data/picker.vue')['default']
17 PointsCollector: typeof import('./src/components/PointsCollector.vue')['default'] 21 PointsCollector: typeof import('./src/components/PointsCollector.vue')['default']
18 PosterBuilder: typeof import('./src/components/PosterBuilder/index.vue')['default'] 22 PosterBuilder: typeof import('./src/components/PosterBuilder/index.vue')['default']
......
1 /* 1 /*
2 * @Date: 2025-06-28 10:33:00 2 * @Date: 2025-06-28 10:33:00
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-08-28 10:21:44 4 + * @LastEditTime: 2025-08-28 11:22:42
5 * @FilePath: /lls_program/src/app.config.js 5 * @FilePath: /lls_program/src/app.config.js
6 * @Description: 文件描述 6 * @Description: 文件描述
7 */ 7 */
...@@ -24,6 +24,7 @@ export default { ...@@ -24,6 +24,7 @@ export default {
24 'pages/PrivacyPolicy/index', 24 'pages/PrivacyPolicy/index',
25 'pages/UserAgreement/index', 25 'pages/UserAgreement/index',
26 'pages/CouponDetail/index', 26 'pages/CouponDetail/index',
27 + 'pages/EditProfile/index',
27 ], 28 ],
28 window: { 29 window: {
29 backgroundTextStyle: 'light', 30 backgroundTextStyle: 'light',
......
...@@ -146,11 +146,7 @@ const goToProfile = () => { ...@@ -146,11 +146,7 @@ const goToProfile = () => {
146 Taro.navigateTo({ url: '/pages/Profile/index' }); 146 Taro.navigateTo({ url: '/pages/Profile/index' });
147 }; 147 };
148 148
149 -const goToRewards = () => {
150 - Taro.navigateTo({ url: '/pages/RewardCategories/index' });
151 -};
152 -
153 const goToActivities = () => { 149 const goToActivities = () => {
154 - Taro.switchTab({ url: '/pages/Activities/index' }); 150 +
155 }; 151 };
156 </script> 152 </script>
......
1 +/*
2 + * @Date: 2025-08-28 11:22:13
3 + * @LastEditors: hookehuyr hookehuyr@gmail.com
4 + * @LastEditTime: 2025-08-28 11:22:19
5 + * @FilePath: /lls_program/src/pages/EditProfile/index.config.js
6 + * @Description: 文件描述
7 + */
8 +export default {
9 + navigationBarTitleText: '编辑个人信息'
10 +}
1 +<template>
2 + <view class="min-h-screen bg-gray-50 p-5 pb-24">
3 + <!-- Avatar -->
4 + <view class="flex flex-col items-center py-8" @click="changeAvatar">
5 + <view class="w-24 h-24 rounded-full bg-gray-100 flex items-center justify-center mb-2 overflow-hidden">
6 + <image v-if="formData.avatar_url" :src="formData.avatar_url" class="w-full h-full" mode="aspectFill" />
7 + <My size="40" color="#888" v-else />
8 + </view>
9 + <text class="text-gray-500 text-sm">上传头像</text>
10 + </view>
11 +
12 + <!-- Form -->
13 + <view class="space-y-6">
14 + <!-- Nickname -->
15 + <view class="mb-6">
16 + <label class="block text-sm font-medium text-gray-700 mb-2">昵称</label>
17 + <view class="bg-white rounded-xl p-2 border border-gray-200">
18 + <nut-input
19 + v-model="formData.nickname"
20 + placeholder="请输入昵称"
21 + :border="false"
22 + class="!bg-transparent !border-none !p-0 text-base"
23 + />
24 + </view>
25 + </view>
26 +
27 + <!-- Birthday -->
28 + <view class="mb-6">
29 + <label class="block text-sm font-medium text-gray-700 mb-2">出生年月</label>
30 + <view class="bg-white rounded-xl p-4 border border-gray-200" @click="showDatePicker = true">
31 + <view class="flex justify-between items-center">
32 + <text :class="{'text-gray-400': !formData.birthday, 'text-gray-900': formData.birthday}" class="text-base">
33 + {{ formData.birthday || '-/-/-/' }}
34 + </text>
35 + <DateIcon size="20" color="#888" />
36 + </view>
37 + </view>
38 + </view>
39 +
40 + <!-- Wheelchair -->
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="showWheelchairPicker = true">
44 + <view class="flex justify-between items-center">
45 + <text :class="{'text-gray-400': !formData.wheelchair_text, 'text-gray-900': formData.wheelchair_text}" class="text-base">
46 + {{ formData.wheelchair_text || '请选择' }}
47 + </text>
48 + <Right size="16" color="#888" />
49 + </view>
50 + </view>
51 + </view>
52 + </view>
53 +
54 + <!-- Save Button -->
55 + <view class="fixed bottom-0 left-0 right-0 p-4 bg-white border-t border-gray-100">
56 + <nut-button type="primary" size="large" color="#4A90E2" block @click="handleSave">保存</nut-button>
57 + </view>
58 +
59 + <!-- Popups -->
60 + <nut-popup v-model:visible="showDatePicker" position="bottom">
61 + <nut-date-picker
62 + v-model="currentDate"
63 + @confirm="onDateConfirm"
64 + @cancel="showDatePicker = false"
65 + :min-date="minDate"
66 + :max-date="maxDate"
67 + title="选择出生年月"
68 + ></nut-date-picker>
69 + </nut-popup>
70 +
71 + <nut-popup v-model:visible="showWheelchairPicker" position="bottom">
72 + <nut-picker
73 + v-model="wheelchairValue"
74 + :columns="wheelchairColumns"
75 + @confirm="onWheelchairConfirm"
76 + @cancel="showWheelchairPicker = false"
77 + title="是否需要轮椅出行"
78 + ></nut-picker>
79 + </nut-popup>
80 +
81 + <nut-image-preview v-model:show="previewVisible" :images="previewImages" />
82 + </view>
83 +</template>
84 +
85 +<script setup>
86 +import { ref, reactive, onMounted } from 'vue';
87 +import Taro from '@tarojs/taro';
88 +import { My, Date as DateIcon, Right } from '@nutui/icons-vue-taro';
89 +
90 +/**
91 + * @description 表单数据
92 + */
93 +const formData = reactive({
94 + avatar_url: '',
95 + nickname: '',
96 + birthday: '',
97 + wheelchair: null, // 0 for No, 1 for Yes
98 + wheelchair_text: '',
99 +});
100 +
101 +// --- Date Picker ---
102 +/**
103 + * @description 控制日期选择器显示
104 + */
105 +const showDatePicker = ref(false);
106 +/**
107 + * @description 最小可选日期
108 + */
109 +const minDate = new Date(1920, 0, 1);
110 +/**
111 + * @description 最大可选日期
112 + */
113 +const maxDate = new Date();
114 +/**
115 + * @description 当前选中的日期
116 + */
117 +const currentDate = ref(new Date());
118 +
119 +/**
120 + * @description 确认选择日期
121 + * @param {object} param0 - 包含 selectedValue 的对象
122 + */
123 +const onDateConfirm = ({ selectedValue }) => {
124 + formData.birthday = selectedValue.join('-');
125 + showDatePicker.value = false;
126 +};
127 +
128 +// --- Wheelchair Picker ---
129 +/**
130 + * @description 控制轮椅选择器显示
131 + */
132 +const showWheelchairPicker = ref(false);
133 +/**
134 + * @description 当前选中的轮椅选项
135 + */
136 +const wheelchairValue = ref([]);
137 +/**
138 + * @description 轮椅选择器选项
139 + */
140 +const wheelchairColumns = ref([
141 + { text: '是', value: 1 },
142 + { text: '否', value: 0 },
143 +]);
144 +
145 +/**
146 + * @description 确认选择轮椅选项
147 + * @param {object} param0 - 包含 selectedValue 和 selectedOptions 的对象
148 + */
149 +const onWheelchairConfirm = ({ selectedValue, selectedOptions }) => {
150 + formData.wheelchair = selectedValue[0];
151 + formData.wheelchair_text = selectedOptions.map((option) => option.text).join('');
152 + showWheelchairPicker.value = false;
153 +};
154 +
155 +// --- Avatar ---
156 +/**
157 + * @description 控制图片预览显示
158 + */
159 +const previewVisible = ref(false);
160 +/**
161 + * @description 预览的图片列表
162 + */
163 +const previewImages = ref([]);
164 +
165 +/**
166 + * @description 更换头像
167 + */
168 +const changeAvatar = () => {
169 + Taro.chooseImage({
170 + count: 1,
171 + sizeType: ['compressed'],
172 + sourceType: ['album', 'camera'],
173 + success: (res) => {
174 + const tempFilePath = res.tempFilePaths[0];
175 + // In a real app, you would upload this file to your server
176 + // and get back a URL. For this demo, we'll just use the local path.
177 + Taro.showLoading({ title: '上传中...' });
178 + // Mock upload process
179 + setTimeout(() => {
180 + formData.avatar_url = tempFilePath;
181 + Taro.hideLoading();
182 + Taro.showToast({ title: '上传成功', icon: 'success' });
183 + }, 1000);
184 + },
185 + fail: () => {
186 + Taro.showToast({ title: '选择图片失败', icon: 'none' });
187 + }
188 + });
189 +};
190 +
191 +// --- Save ---
192 +/**
193 + * @description 保存用户信息
194 + */
195 +const handleSave = () => {
196 + console.log('Saving data:', formData);
197 + Taro.showLoading({ title: '保存中...' });
198 + // Mock save process
199 + setTimeout(() => {
200 + Taro.hideLoading();
201 + Taro.showToast({
202 + title: '保存成功',
203 + icon: 'success',
204 + duration: 1500,
205 + complete: () => {
206 + Taro.navigateBack();
207 + },
208 + });
209 + }, 1000);
210 +};
211 +
212 +/**
213 + * @description 页面加载时获取初始数据
214 + */
215 +onMounted(() => {
216 + // Mock fetching user data, in a real app, you would call an API
217 + const mockUserData = {
218 + avatar_url: 'https://img.yzcdn.cn/vant/cat.jpeg',
219 + nickname: '张三',
220 + birthday: '1990-05-15',
221 + wheelchair: 0,
222 + wheelchair_text: '否',
223 + };
224 + Object.assign(formData, mockUserData);
225 +
226 + // Initialize pickers with current data
227 + currentDate.value = new Date(mockUserData.birthday);
228 + wheelchairValue.value = [mockUserData.wheelchair];
229 +});
230 +</script>
231 +
232 +<style>
233 +.nut-input.mt-2 {
234 + padding: 12px 0 !important;
235 + border-bottom: 1px solid #e5e7eb;
236 +}
237 +</style>
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-08-28 09:57:23 4 + * @LastEditTime: 2025-08-28 11:16:24
5 * @FilePath: /lls_program/src/pages/Profile/index.vue 5 * @FilePath: /lls_program/src/pages/Profile/index.vue
6 * @Description: 文件描述 6 * @Description: 文件描述
7 --> 7 -->
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
19 <h1 class="text-xl font-bold text-white">张爷爷</h1> 19 <h1 class="text-xl font-bold text-white">张爷爷</h1>
20 </view> 20 </view>
21 </view> 21 </view>
22 - <view class="text-white"> 22 + <view @tap="goToEditProfile" class="text-white">
23 <span>编辑</span> 23 <span>编辑</span>
24 </view> 24 </view>
25 </view> 25 </view>
...@@ -86,4 +86,8 @@ const menuItems = shallowRef([ ...@@ -86,4 +86,8 @@ const menuItems = shallowRef([
86 onClick: () => Taro.navigateTo({ url: '/pages/PrivacyPolicy/index' }) 86 onClick: () => Taro.navigateTo({ url: '/pages/PrivacyPolicy/index' })
87 } 87 }
88 ]); 88 ]);
89 +
90 +const goToEditProfile = () => {
91 + Taro.navigateTo({ url: '/pages/EditProfile/index' });
92 +};
89 </script> 93 </script>
......