木邻有你前端代码仓库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

378 lines
7.7 KiB

10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
  1. <template>
  2. <view class="profile-container">
  3. <!-- 基本资料 -->
  4. <view class="section">
  5. <view class="section-title">
  6. <!-- 小竖线 -->
  7. <view class="vertical-line"></view>
  8. <view>
  9. <text class="title-text">基本资料</text>
  10. </view>
  11. </view>
  12. <!-- 头像 -->
  13. <view class="avatar-section">
  14. <button
  15. class="avatar-button"
  16. open-type="chooseAvatar"
  17. @chooseavatar="onChooseAvatar"
  18. >
  19. <image
  20. class="avatar"
  21. :src="userInfo.headImage || '/static/待上传头像.png'"
  22. mode="aspectFill"
  23. ></image>
  24. </button>
  25. <text class="avatar-tip">点击更换头像</text>
  26. </view>
  27. <!-- 昵称 -->
  28. <view class="info-item">
  29. <text class="label">昵称</text>
  30. <view class="value-container">
  31. <input
  32. class="nickname-input"
  33. v-model="userInfo.nickName"
  34. placeholder="请输入"
  35. type="nickname"
  36. @blur="onNicknameBlur"
  37. />
  38. </view>
  39. </view>
  40. <!-- 手机号 -->
  41. <view class="info-item">
  42. <text class="label">手机号</text>
  43. <view class="value-container">
  44. <input
  45. class="phone-input"
  46. v-model="userInfo.phone"
  47. placeholder="请输入"
  48. type="number"
  49. maxlength="11"
  50. />
  51. </view>
  52. </view>
  53. <!-- 地址 -->
  54. <view class="info-item">
  55. <text class="label">地址</text>
  56. <view class="value-container" @click="openMap">
  57. <text class="address-input">
  58. {{ userInfo.address || '请选择地址' }}
  59. </text>
  60. <!-- <text class="address-input" v-else>
  61. 请选择地址
  62. </text> -->
  63. </view>
  64. </view>
  65. </view>
  66. <!-- 保存按钮 -->
  67. <view class="save-section">
  68. <button class="save-button" @click="saveProfile">
  69. 保存
  70. </button>
  71. </view>
  72. </view>
  73. </template>
  74. <script>
  75. export default {
  76. name: 'MyProfile',
  77. data() {
  78. return {
  79. userInfo: {
  80. headImage: '',
  81. nickName: '',
  82. phone: '',
  83. address: ''
  84. }
  85. }
  86. },
  87. methods: {
  88. // 返回上一页
  89. goBack() {
  90. uni.navigateBack();
  91. },
  92. // 选择微信头像
  93. async onChooseAvatar(e) {
  94. console.log('选择头像回调', e);
  95. if (e.detail.avatarUrl) {
  96. const file = {
  97. path: e.detail.avatarUrl
  98. }
  99. const res = await this.$utils.uploadImage(file)
  100. this.userInfo.headImage = res.url
  101. uni.showToast({
  102. title: '头像更新成功',
  103. icon: 'success'
  104. });
  105. } else {
  106. uni.showToast({
  107. title: '头像选择失败',
  108. icon: 'none'
  109. });
  110. }
  111. },
  112. // 昵称输入失焦验证
  113. onNicknameBlur() {
  114. if (!this.userInfo.nickName.trim()) {
  115. uni.showToast({
  116. title: '请输入昵称',
  117. icon: 'none'
  118. });
  119. }
  120. },
  121. // 保存资料
  122. async saveProfile() {
  123. // 验证昵称
  124. if (!this.userInfo.nickName.trim()) {
  125. uni.showToast({
  126. title: '请输入昵称',
  127. icon: 'none'
  128. });
  129. return;
  130. }
  131. // 验证手机号(如果填写了)
  132. if (this.userInfo.phone && !/^1[3-9]\d{9}$/.test(this.userInfo.phone)) {
  133. uni.showToast({
  134. title: '请输入正确的手机号',
  135. icon: 'none'
  136. });
  137. return;
  138. }
  139. const res = await this.$api.user.updateUser({
  140. nickName: this.userInfo.nickName,
  141. phone: this.userInfo.phone,
  142. headImage: this.userInfo.headImage,
  143. address: this.userInfo.address
  144. })
  145. if (res.code === 200) {
  146. uni.showToast({
  147. title: `${res.message}`,
  148. icon: 'success'
  149. });
  150. setTimeout(() => {
  151. uni.navigateBack()
  152. }, 1000);
  153. }
  154. },
  155. // 获取个人信息
  156. async getUserInfo() {
  157. const res = await this.$api.user.queryUser()
  158. this.userInfo = {...this.userInfo, ...res.result}
  159. // this.userInfo = res.result
  160. },
  161. // 打开地图
  162. openMap() {
  163. uni.chooseLocation({
  164. success: (res) => {
  165. console.log('位置名称:' + res.name)
  166. console.log('详细地址:' + res.address)
  167. console.log('纬度:' + res.latitude)
  168. console.log('经度:' + res.longitude)
  169. this.userInfo.address = res.address || res.name
  170. uni.showToast({
  171. title: '地址更新成功',
  172. icon: 'success'
  173. });
  174. },
  175. fail: (err) => {
  176. console.log(err);
  177. uni.showToast({
  178. title: `获取位置失败`,
  179. icon: 'none'
  180. })
  181. }
  182. })
  183. }
  184. },
  185. onLoad() {
  186. this.getUserInfo();
  187. }
  188. }
  189. </script>
  190. <style lang="scss" scoped>
  191. .profile-container {
  192. min-height: 100vh;
  193. background-color: #f5f5f5;
  194. }
  195. .vertical-line {
  196. width: 9rpx;
  197. height: 33rpx;
  198. background-color: #1488DB;
  199. margin-right: 20rpx;
  200. }
  201. // 自定义导航栏
  202. .custom-navbar {
  203. background: linear-gradient(90deg, #1488db 0%, #1488db 100%);
  204. padding-top: var(--status-bar-height, 44rpx);
  205. .navbar-content {
  206. height: 88rpx;
  207. display: flex;
  208. align-items: center;
  209. justify-content: space-between;
  210. padding: 0 30rpx;
  211. .navbar-left {
  212. .back-icon {
  213. font-size: 40rpx;
  214. color: #ffffff;
  215. font-weight: bold;
  216. }
  217. }
  218. .navbar-title {
  219. font-size: 36rpx;
  220. color: #ffffff;
  221. font-weight: 500;
  222. }
  223. .navbar-right {
  224. display: flex;
  225. gap: 20rpx;
  226. .more-icon,
  227. .settings-icon {
  228. font-size: 32rpx;
  229. color: #ffffff;
  230. }
  231. }
  232. }
  233. }
  234. .section {
  235. margin: 20rpx;
  236. background-color: #ffffff;
  237. border-radius: 20rpx;
  238. overflow: hidden;
  239. .section-title {
  240. display: flex;
  241. align-items: center;
  242. padding: 30rpx;
  243. border-bottom: 1rpx solid #f0f0f0;
  244. .title-text {
  245. font-size: 32rpx;
  246. font-weight: 500;
  247. color: #333333;
  248. }
  249. }
  250. }
  251. .avatar-section {
  252. display: flex;
  253. flex-direction: column;
  254. align-items: center;
  255. padding: 40rpx 30rpx;
  256. border-bottom: 1rpx solid #f0f0f0;
  257. .avatar-button {
  258. padding: 0;
  259. margin: 0;
  260. background: transparent;
  261. border: none;
  262. outline: none;
  263. margin-bottom: 20rpx;
  264. &::after {
  265. border: none;
  266. }
  267. .avatar {
  268. width: 160rpx;
  269. height: 160rpx;
  270. border-radius: 80rpx;
  271. border: 4rpx dashed #cccccc;
  272. }
  273. }
  274. .avatar-tip {
  275. font-size: 26rpx;
  276. color: #999999;
  277. }
  278. }
  279. .info-item {
  280. display: flex;
  281. align-items: center;
  282. justify-content: space-between;
  283. padding: 30rpx;
  284. border-bottom: 1rpx solid #f0f0f0;
  285. &:last-child {
  286. border-bottom: none;
  287. }
  288. .label {
  289. font-size: 30rpx;
  290. color: #333333;
  291. font-weight: 500;
  292. width: 120rpx;
  293. }
  294. .value-container {
  295. flex: 1;
  296. display: flex;
  297. align-items: center;
  298. justify-content: flex-end;
  299. .nickname-input,
  300. .phone-input,
  301. .address-input {
  302. width: 100%;
  303. text-align: right;
  304. font-size: 30rpx;
  305. color: #666666;
  306. border: none;
  307. outline: none;
  308. background: transparent;
  309. &::placeholder {
  310. color: #cccccc;
  311. }
  312. }
  313. }
  314. }
  315. .save-section {
  316. padding: 40rpx 20rpx;
  317. .save-button {
  318. width: 100%;
  319. height: 88rpx;
  320. background-color: #1488db;
  321. border-radius: 44rpx;
  322. border: none;
  323. outline: none;
  324. font-size: 32rpx;
  325. font-weight: 500;
  326. color: #ffffff;
  327. display: flex;
  328. align-items: center;
  329. justify-content: center;
  330. &::after {
  331. border: none;
  332. }
  333. }
  334. }
  335. </style>