展品维保小程序前端代码接口
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.

365 lines
7.4 KiB

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