爱简收旧衣按件回收前端代码仓库
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.

348 lines
7.6 KiB

1 week ago
5 days ago
1 week ago
1 week ago
1 week ago
  1. <template>
  2. <view class="edit-profile">
  3. <!-- 顶部导航栏 -->
  4. <view class="nav-bar">
  5. <view class="back-icon" @click="goBack">
  6. <uni-icons type="left" size="20"></uni-icons>
  7. </view>
  8. <view class="title">修改信息</view>
  9. </view>
  10. <!-- 个人信息表单 -->
  11. <view class="info-container">
  12. <view class="info-card">
  13. <text class="section-title">个人信息</text>
  14. <!-- 昵称 -->
  15. <view class="info-item">
  16. <text class="label">昵称</text>
  17. <input type="text" v-model="userInfo.nickname" placeholder="请输入昵称" />
  18. </view>
  19. <!-- 电话 -->
  20. <view class="info-item">
  21. <text class="label">电话</text>
  22. <view class="phone-input">
  23. <input type="number" v-model="userInfo.phone" placeholder="请输入手机号" />
  24. <uni-icons type="eye" size="20" color="#999"></uni-icons>
  25. </view>
  26. </view>
  27. <!-- 头像 -->
  28. <view class="info-item avatar-section">
  29. <text class="label">头像</text>
  30. <view class="avatar-container">
  31. <view class="avatar-box" @tap="chooseImage">
  32. <uni-icons v-if="!userInfo.avatar" type="reload" size="24" color="#999"></uni-icons>
  33. <image v-else :src="userInfo.avatar" mode="aspectFill"></image>
  34. </view>
  35. <image v-if="userInfo.newAvatar" :src="userInfo.newAvatar" mode="aspectFill" class="new-avatar"></image>
  36. </view>
  37. </view>
  38. </view>
  39. </view>
  40. <!-- 头像上传状态提示 -->
  41. <view class="upload-status" v-if="uploadStatus.show">
  42. <view class="status-mask"></view>
  43. <view class="status-content">
  44. <!-- 加载中 -->
  45. <template v-if="uploadStatus.type === 'loading'">
  46. <view class="loading-icon"></view>
  47. <text>正在加载修改</text>
  48. </template>
  49. <!-- 成功 -->
  50. <template v-else-if="uploadStatus.type === 'success'">
  51. <uni-icons type="checkmark" size="24" color="#fff"></uni-icons>
  52. <text>修改成功</text>
  53. </template>
  54. <!-- 失败 -->
  55. <template v-else-if="uploadStatus.type === 'error'">
  56. <uni-icons type="closeempty" size="24" color="#fff"></uni-icons>
  57. <text>修改失败</text>
  58. </template>
  59. </view>
  60. </view>
  61. <!-- 保存按钮 -->
  62. <view class="save-button" @tap="saveProfile">
  63. <text>保存</text>
  64. </view>
  65. </view>
  66. </template>
  67. <script>
  68. import pullRefreshMixin from '@/pages/mixins/pullRefreshMixin.js'
  69. export default {
  70. mixins: [pullRefreshMixin],
  71. data() {
  72. return {
  73. userInfo: {
  74. nickname: '吴彦谋',
  75. phone: '15888977617',
  76. avatar: '',
  77. newAvatar: '/static/logo.png'
  78. },
  79. uploadStatus: {
  80. show: false,
  81. type: 'loading' // loading, success, error
  82. }
  83. }
  84. },
  85. methods: {
  86. async onRefresh() {
  87. // 模拟刷新数据
  88. await new Promise(resolve => setTimeout(resolve, 1000))
  89. uni.stopPullRefresh()
  90. },
  91. goBack() {
  92. uni.navigateBack()
  93. },
  94. async chooseImage() {
  95. try {
  96. const [tempFile] = await new Promise((resolve, reject) => {
  97. uni.chooseImage({
  98. count: 1,
  99. sizeType: ['compressed'],
  100. sourceType: ['album', 'camera'],
  101. success: (res) => resolve(res.tempFilePaths),
  102. fail: reject
  103. })
  104. })
  105. // 显示加载状态
  106. this.uploadStatus = {
  107. show: true,
  108. type: 'loading'
  109. }
  110. // 模拟上传过程
  111. await new Promise(resolve => setTimeout(resolve, 1500))
  112. // 随机模拟成功或失败
  113. const isSuccess = Math.random() > 0.3
  114. if (isSuccess) {
  115. this.userInfo.newAvatar = tempFile
  116. this.uploadStatus.type = 'success'
  117. } else {
  118. throw new Error('Upload failed')
  119. }
  120. // 成功状态显示1秒后关闭
  121. await new Promise(resolve => setTimeout(resolve, 1000))
  122. this.uploadStatus.show = false
  123. } catch (error) {
  124. console.error('Upload failed:', error)
  125. this.uploadStatus = {
  126. show: true,
  127. type: 'error'
  128. }
  129. // 失败状态显示1秒后关闭
  130. setTimeout(() => {
  131. this.uploadStatus.show = false
  132. }, 1000)
  133. }
  134. },
  135. saveProfile() {
  136. uni.showToast({
  137. title: '保存成功',
  138. icon: 'success'
  139. })
  140. setTimeout(() => {
  141. uni.navigateBack()
  142. }, 1500)
  143. }
  144. }
  145. }
  146. </script>
  147. <style lang="scss" scoped>
  148. .edit-profile {
  149. min-height: 100vh;
  150. background-color: #f5f5f5;
  151. padding-bottom: calc(env(safe-area-inset-bottom) + 120rpx);
  152. }
  153. .nav-bar {
  154. display: flex;
  155. align-items: center;
  156. height: 88rpx;
  157. padding-top: var(--status-bar-height);
  158. .title {
  159. font-family: PingFang SC;
  160. font-weight: 500;
  161. font-size: 16px;
  162. line-height: 140%;
  163. letter-spacing: 0%;
  164. text-align: center;
  165. vertical-align: middle;
  166. color: #333;
  167. margin-left: 30%;
  168. }
  169. .back-icon {
  170. width: 88rpx;
  171. height: 88rpx;
  172. display: flex;
  173. align-items: center;
  174. justify-content: center;
  175. }
  176. }
  177. .info-container {
  178. margin-top: calc(88rpx + env(safe-area-inset-top));
  179. padding: 30rpx;
  180. }
  181. .info-card {
  182. background: #fff;
  183. border-radius: 20rpx;
  184. padding: 30rpx;
  185. .section-title {
  186. font-size: 32rpx;
  187. color: #333;
  188. font-weight: 500;
  189. margin-bottom: 40rpx;
  190. }
  191. .info-item {
  192. margin-bottom: 40rpx;
  193. .label {
  194. font-size: 28rpx;
  195. color: #333;
  196. margin-bottom: 20rpx;
  197. display: block;
  198. }
  199. input {
  200. font-size: 32rpx;
  201. color: #333;
  202. width: 100%;
  203. padding: 20rpx 0;
  204. border-bottom: 2rpx solid #f5f5f5;
  205. }
  206. .phone-input {
  207. display: flex;
  208. align-items: center;
  209. border-bottom: 2rpx solid #f5f5f5;
  210. input {
  211. flex: 1;
  212. border-bottom: none;
  213. }
  214. }
  215. }
  216. .avatar-section {
  217. .avatar-container {
  218. display: flex;
  219. gap: 20rpx;
  220. }
  221. .avatar-box {
  222. width: 160rpx;
  223. height: 160rpx;
  224. background: #f5f5f5;
  225. border-radius: 10rpx;
  226. display: flex;
  227. align-items: center;
  228. justify-content: center;
  229. overflow: hidden;
  230. image {
  231. width: 100%;
  232. height: 100%;
  233. }
  234. }
  235. .new-avatar {
  236. width: 160rpx;
  237. height: 160rpx;
  238. border-radius: 10rpx;
  239. }
  240. }
  241. }
  242. .save-button {
  243. position: fixed;
  244. left: 30rpx;
  245. right: 30rpx;
  246. bottom: calc(env(safe-area-inset-bottom) + 30rpx);
  247. height: 90rpx;
  248. background: linear-gradient(to right,#17f261,#13d755);
  249. border-radius: 45rpx;
  250. display: flex;
  251. align-items: center;
  252. justify-content: center;
  253. text {
  254. color: #fff;
  255. font-size: 32rpx;
  256. font-weight: 500;
  257. }
  258. }
  259. .upload-status {
  260. position: fixed;
  261. top: 0;
  262. left: 0;
  263. right: 0;
  264. bottom: 0;
  265. z-index: 999;
  266. display: flex;
  267. align-items: center;
  268. justify-content: center;
  269. .status-mask {
  270. position: absolute;
  271. top: 0;
  272. left: 0;
  273. right: 0;
  274. bottom: 0;
  275. background: rgba(0, 0, 0, 0.6);
  276. }
  277. .status-content {
  278. position: relative;
  279. width: 240rpx;
  280. height: 240rpx;
  281. background: rgba(0, 0, 0, 0.8);
  282. border-radius: 20rpx;
  283. display: flex;
  284. flex-direction: column;
  285. align-items: center;
  286. justify-content: center;
  287. gap: 20rpx;
  288. text {
  289. color: #fff;
  290. font-size: 28rpx;
  291. }
  292. .loading-icon {
  293. width: 60rpx;
  294. height: 60rpx;
  295. border: 4rpx solid #fff;
  296. border-top-color: transparent;
  297. border-radius: 50%;
  298. animation: spin 1s linear infinite;
  299. }
  300. }
  301. }
  302. @keyframes spin {
  303. from {
  304. transform: rotate(0deg);
  305. }
  306. to {
  307. transform: rotate(360deg);
  308. }
  309. }
  310. </style>