edit-profile.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543
  1. <template>
  2. <view class="edit-profile">
  3. <!-- 顶部导航栏 -->
  4. <view class="header">
  5. <view class="back-btn" @click="handleBack">
  6. <text class="back-icon">←</text>
  7. </view>
  8. <text class="header-title">编辑资料</text>
  9. <view class="right-empty"></view>
  10. </view>
  11. <scroll-view scroll-y class="content">
  12. <!-- 头像上传区域 -->
  13. <view class="avatar-section">
  14. <view class="avatar-container">
  15. <image class="avatar" :src="userInfo.avatar || defaultAvatar" mode="aspectFill"></image>
  16. <view class="avatar-upload-btn" @click="handleAvatarUpload">
  17. <text class="upload-icon">📷</text>
  18. </view>
  19. </view>
  20. <text class="avatar-text">更换头像</text>
  21. </view>
  22. <!-- 表单区域 -->
  23. <view class="form-section">
  24. <!-- 姓名 -->
  25. <view class="form-item">
  26. <view class="form-label">
  27. <text class="label-icon">👤</text>
  28. <text class="label-text">姓名</text>
  29. </view>
  30. <input
  31. class="form-input"
  32. v-model="userInfo.name"
  33. placeholder="请输入姓名"
  34. placeholder-style="color: #999"
  35. >
  36. </view>
  37. <!-- 性别 -->
  38. <view class="form-item">
  39. <view class="form-label">
  40. <text class="label-icon">⚧️</text>
  41. <text class="label-text">性别</text>
  42. </view>
  43. <picker class="form-picker gender-picker" @change="handleGenderChange" :value="genderIndex"
  44. :range="genderOptions">
  45. <view class="picker-content">
  46. {{ userInfo.gender || '请选择性别' }}
  47. </view>
  48. </picker>
  49. </view>
  50. <!-- 个人简介 -->
  51. <view class="form-item">
  52. <view class="form-label">
  53. <text class="label-icon">📝</text>
  54. <text class="label-text">个人简介</text>
  55. </view>
  56. <textarea
  57. class="form-textarea"
  58. v-model="userInfo.bio"
  59. placeholder="请输入个人简介"
  60. placeholder-style="color: #999"
  61. maxlength="200"
  62. auto-height
  63. ></textarea>
  64. </view>
  65. <!-- 邮箱 -->
  66. <view class="form-item">
  67. <view class="form-label">
  68. <text class="label-icon">📧</text>
  69. <text class="label-text">邮箱</text>
  70. </view>
  71. <input
  72. class="form-input"
  73. v-model="userInfo.email"
  74. placeholder="请输入邮箱"
  75. placeholder-style="color: #999"
  76. type="email"
  77. >
  78. </view>
  79. <!-- 省市区选择 -->
  80. <view class="form-item">
  81. <view class="form-label">
  82. <text class="label-icon">📍</text>
  83. <text class="label-text">所在地区</text>
  84. </view>
  85. <picker
  86. class="form-picker"
  87. mode="multiSelector"
  88. :value="multiIndex"
  89. :range="multiArray"
  90. @columnchange="handleColumnChange"
  91. @change="handleMultiPickerChange"
  92. >
  93. <view class="picker-content">{{ selectedArea || '请选择地区' }}</view>
  94. </picker>
  95. </view>
  96. <!-- 详细地址 -->
  97. <view class="form-item">
  98. <view class="form-label">
  99. <text class="label-icon">🏠</text>
  100. <text class="label-text">详细地址</text>
  101. </view>
  102. <textarea
  103. class="form-textarea"
  104. v-model="userInfo.address_detail"
  105. placeholder="请输入详细地址"
  106. placeholder-style="color: #999"
  107. maxlength="255"
  108. auto-height
  109. ></textarea>
  110. </view>
  111. </view>
  112. <!-- 保存按钮 -->
  113. <view class="save-btn-section">
  114. <button class="save-btn" @click="handleSave">保存</button>
  115. </view>
  116. </scroll-view>
  117. </view>
  118. </template>
  119. <script>
  120. export default {
  121. data() {
  122. return {
  123. // 默认头像
  124. defaultAvatar: 'https://q.qlogo.cn/qqapp/1105591438/05E13358B43B3D39D6AC2D6888828201/100',
  125. // 用户信息
  126. userInfo: {
  127. avatar: '',
  128. name: '',
  129. gender: '',
  130. bio: '我是一名专业的红娘,拥有丰富的婚恋服务经验,已成功帮助1200对单身男女找到幸福。',
  131. email: '',
  132. province_id: '',
  133. city_id: '',
  134. area_id: '',
  135. address_detail: '',
  136. province_name: '',
  137. city_name: '',
  138. area_name: ''
  139. },
  140. // 省市区多列选择器数据
  141. multiArray: [
  142. ['北京市', '天津市', '河北省', '山西省', '内蒙古自治区'],
  143. ['石家庄市', '唐山市', '秦皇岛市', '邯郸市', '邢台市', '保定市'],
  144. ['竞秀区', '莲池区', '满城区', '清苑区', '徐水区', '涞水县']
  145. ],
  146. multiIndex: [0, 0, 0],
  147. selectedArea: '',
  148. // 省市区详细数据(用于动态加载)
  149. areaData: {
  150. '北京市': {
  151. '北京市': ['东城区', '西城区', '朝阳区', '海淀区', '丰台区', '石景山区']
  152. },
  153. '天津市': {
  154. '天津市': ['和平区', '河东区', '河西区', '南开区', '河北区', '红桥区']
  155. },
  156. '河北省': {
  157. '石家庄市': ['长安区', '桥西区', '新华区', '井陉矿区', '裕华区', '藁城区'],
  158. '唐山市': ['路南区', '路北区', '古冶区', '开平区', '丰南区', '丰润区'],
  159. '秦皇岛市': ['海港区', '山海关区', '北戴河区', '抚宁区', '青龙满族自治县'],
  160. '邯郸市': ['邯山区', '丛台区', '复兴区', '峰峰矿区', '肥乡区', '永年区'],
  161. '邢台市': ['桥东区', '桥西区', '邢台县', '临城县', '内丘县', '柏乡县'],
  162. '保定市': ['竞秀区', '莲池区', '满城区', '清苑区', '徐水区', '涞水县']
  163. },
  164. '山西省': {
  165. '太原市': ['小店区', '迎泽区', '杏花岭区', '尖草坪区', '万柏林区', '晋源区']
  166. },
  167. '内蒙古自治区': {
  168. '呼和浩特市': ['新城区', '回民区', '玉泉区', '赛罕区', '土默特左旗']
  169. }
  170. },
  171. // 性别选项
  172. genderOptions: ['男', '女'],
  173. genderIndex: 0
  174. }
  175. },
  176. onLoad() {
  177. // 从本地存储获取用户信息
  178. this.loadUserInfo()
  179. },
  180. methods: {
  181. // 加载用户信息
  182. loadUserInfo() {
  183. const userInfo = uni.getStorageSync('matchmakerUserInfo')
  184. if (userInfo) {
  185. this.userInfo = {...this.userInfo, ...userInfo}
  186. // 设置性别索引
  187. this.genderIndex = this.genderOptions.indexOf(this.userInfo.gender) || 0
  188. // 如果有省市区信息,设置选中的区域文本
  189. if (this.userInfo.province_name && this.userInfo.city_name && this.userInfo.area_name) {
  190. this.selectedArea = `${this.userInfo.province_name} ${this.userInfo.city_name} ${this.userInfo.area_name}`
  191. // 设置对应的索引
  192. const provinceIndex = this.multiArray[0].indexOf(this.userInfo.province_name)
  193. if (provinceIndex !== -1) {
  194. this.multiIndex[0] = provinceIndex
  195. // 更新城市列表
  196. this.updateCityList(provinceIndex)
  197. const cityIndex = this.multiArray[1].indexOf(this.userInfo.city_name)
  198. if (cityIndex !== -1) {
  199. this.multiIndex[1] = cityIndex
  200. // 更新区县列表
  201. this.updateAreaList(provinceIndex, cityIndex)
  202. const areaIndex = this.multiArray[2].indexOf(this.userInfo.area_name)
  203. if (areaIndex !== -1) {
  204. this.multiIndex[2] = areaIndex
  205. }
  206. }
  207. }
  208. }
  209. }
  210. },
  211. // 更新城市列表
  212. updateCityList(provinceIndex) {
  213. const province = this.multiArray[0][provinceIndex]
  214. const cities = Object.keys(this.areaData[province] || {})
  215. this.multiArray[1] = cities
  216. this.multiIndex[1] = 0
  217. this.updateAreaList(provinceIndex, 0)
  218. },
  219. // 更新区县列表
  220. updateAreaList(provinceIndex, cityIndex) {
  221. const province = this.multiArray[0][provinceIndex]
  222. const city = this.multiArray[1][cityIndex]
  223. const areas = this.areaData[province]?.[city] || []
  224. this.multiArray[2] = areas
  225. this.multiIndex[2] = 0
  226. },
  227. // 处理列变化
  228. handleColumnChange(e) {
  229. const column = e.detail.column
  230. const value = e.detail.value
  231. this.multiIndex[column] = value
  232. // 根据列索引更新对应的数据
  233. if (column === 0) {
  234. // 省份变化,更新城市和区县
  235. this.updateCityList(value)
  236. } else if (column === 1) {
  237. // 城市变化,更新区县
  238. this.updateAreaList(this.multiIndex[0], value)
  239. }
  240. },
  241. // 处理多列选择器确认
  242. handleMultiPickerChange(e) {
  243. this.multiIndex = e.detail.value
  244. const province = this.multiArray[0][this.multiIndex[0]]
  245. const city = this.multiArray[1][this.multiIndex[1]]
  246. const area = this.multiArray[2][this.multiIndex[2]]
  247. this.selectedArea = `${province} ${city} ${area}`
  248. // 更新用户信息
  249. this.userInfo.province_id = this.multiIndex[0] + 1
  250. this.userInfo.city_id = this.multiIndex[1] + 1
  251. this.userInfo.area_id = this.multiIndex[2] + 1
  252. this.userInfo.province_name = province
  253. this.userInfo.city_name = city
  254. this.userInfo.area_name = area
  255. },
  256. // 返回上一页
  257. async handleBack() {
  258. uni.navigateBack({
  259. delta: 1
  260. })
  261. },
  262. // 更换头像
  263. handleAvatarUpload() {
  264. uni.chooseImage({
  265. count: 1,
  266. sizeType: ['compressed'],
  267. sourceType: ['album', 'camera'],
  268. success: (res) => {
  269. const tempFilePath = res.tempFilePaths[0]
  270. // 这里可以添加上传图片到服务器的逻辑
  271. this.userInfo.avatar = tempFilePath
  272. uni.showToast({
  273. title: '头像更换成功',
  274. icon: 'success'
  275. })
  276. }
  277. })
  278. },
  279. // 性别选择变化
  280. handleGenderChange(e) {
  281. const index = e.detail.value
  282. this.genderIndex = index
  283. this.userInfo.gender = this.genderOptions[index]
  284. },
  285. // 保存资料
  286. handleSave() {
  287. // 验证表单
  288. if (!this.userInfo.name) {
  289. uni.showToast({
  290. title: '请输入姓名',
  291. icon: 'none'
  292. })
  293. return
  294. }
  295. if (!this.userInfo.gender) {
  296. uni.showToast({
  297. title: '请选择性别',
  298. icon: 'none'
  299. })
  300. return
  301. }
  302. // 保存到本地存储
  303. uni.setStorageSync('matchmakerUserInfo', this.userInfo)
  304. // 提示保存成功
  305. uni.showToast({
  306. title: '资料保存成功',
  307. icon: 'success'
  308. })
  309. // 返回上一页
  310. setTimeout(() => {
  311. this.handleBack()
  312. }, 1500)
  313. }
  314. }
  315. }
  316. </script>
  317. <style lang="scss" scoped>
  318. .edit-profile {
  319. min-height: 100vh;
  320. background: #FFFFFF;
  321. display: flex;
  322. flex-direction: column;
  323. }
  324. /* 顶部导航栏 */
  325. .header {
  326. display: flex;
  327. align-items: center;
  328. justify-content: space-between;
  329. padding: 25rpx 30rpx;
  330. padding-top: calc(25rpx + env(safe-area-inset-top));
  331. background: #FFFFFF;
  332. border-bottom: 1rpx solid #F0F0F0;
  333. .back-btn {
  334. width: 44rpx;
  335. height: 44rpx;
  336. display: flex;
  337. align-items: center;
  338. justify-content: center;
  339. .back-icon {
  340. font-size: 40rpx;
  341. color: #333;
  342. }
  343. }
  344. .header-title {
  345. font-size: 38rpx;
  346. font-weight: bold;
  347. color: #333;
  348. }
  349. .right-empty {
  350. width: 44rpx;
  351. }
  352. }
  353. .content {
  354. flex: 1;
  355. padding-bottom: 40rpx;
  356. }
  357. /* 头像上传区域 */
  358. .avatar-section {
  359. display: flex;
  360. flex-direction: column;
  361. align-items: center;
  362. padding: 40rpx 0;
  363. border-bottom: 1rpx solid #F0F0F0;
  364. .avatar-container {
  365. position: relative;
  366. width: 160rpx;
  367. height: 160rpx;
  368. margin-bottom: 20rpx;
  369. .avatar {
  370. width: 100%;
  371. height: 100%;
  372. border-radius: 50%;
  373. background: #F5F5F5;
  374. }
  375. .avatar-upload-btn {
  376. position: absolute;
  377. bottom: 0;
  378. right: 0;
  379. width: 48rpx;
  380. height: 48rpx;
  381. display: flex;
  382. align-items: center;
  383. justify-content: center;
  384. background: #FFFFFF;
  385. border: 2rpx solid #E91E63;
  386. border-radius: 50%;
  387. box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
  388. .upload-icon {
  389. font-size: 28rpx;
  390. color: #E91E63;
  391. }
  392. }
  393. }
  394. .avatar-text {
  395. font-size: 28rpx;
  396. color: #E91E63;
  397. font-weight: 500;
  398. }
  399. }
  400. /* 表单区域 */
  401. .form-section {
  402. padding: 0 30rpx;
  403. .form-item {
  404. display: flex;
  405. flex-direction: column;
  406. padding: 25rpx 0;
  407. border-bottom: 1rpx solid #F5F5F5;
  408. &:last-child {
  409. border-bottom: none;
  410. }
  411. .form-label {
  412. display: flex;
  413. align-items: center;
  414. margin-bottom: 15rpx;
  415. .label-icon {
  416. font-size: 28rpx;
  417. margin-right: 12rpx;
  418. }
  419. .label-text {
  420. font-size: 28rpx;
  421. color: #333;
  422. font-weight: 500;
  423. }
  424. }
  425. .form-input,
  426. .form-picker {
  427. width: 90%;
  428. font-size: 28rpx;
  429. color: #333;
  430. padding: 18rpx 20rpx;
  431. background: #F9F9F9;
  432. border-radius: 12rpx;
  433. border: 1rpx solid #E0E0E0;
  434. &.readonly {
  435. background: #F5F5F5;
  436. color: #999;
  437. }
  438. }
  439. .form-picker {
  440. display: flex;
  441. align-items: center;
  442. .picker-content {
  443. flex: 1;
  444. }
  445. }
  446. /* 性别选择器特定样式 */
  447. .gender-picker {
  448. width: 25%;
  449. }
  450. .form-textarea {
  451. width: 90%;
  452. font-size: 28rpx;
  453. color: #333;
  454. padding: 20rpx;
  455. background: #F9F9F9;
  456. border-radius: 12rpx;
  457. border: 1rpx solid #E0E0E0;
  458. min-height: 160rpx;
  459. line-height: 1.5;
  460. }
  461. }
  462. }
  463. /* 保存按钮 */
  464. .save-btn-section {
  465. padding: 40rpx 30rpx;
  466. .save-btn {
  467. width: 100%;
  468. height: 90rpx;
  469. background: linear-gradient(135deg, #E91E63 0%, #FF6B8A 100%);
  470. color: #FFFFFF;
  471. font-size: 32rpx;
  472. font-weight: bold;
  473. border-radius: 45rpx;
  474. border: none;
  475. box-shadow: 0 6rpx 20rpx rgba(233, 30, 99, 0.3);
  476. transition: all 0.3s ease;
  477. &:active {
  478. transform: scale(0.98);
  479. box-shadow: 0 3rpx 10rpx rgba(233, 30, 99, 0.2);
  480. }
  481. }
  482. }
  483. </style>