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

409 lines
10 KiB

2 weeks ago
5 days ago
2 weeks ago
5 days ago
2 weeks ago
2 weeks ago
2 weeks ago
5 days ago
2 weeks ago
5 days ago
2 weeks ago
5 days ago
2 weeks ago
5 days ago
2 weeks ago
5 days ago
2 weeks ago
2 weeks ago
2 weeks ago
5 days ago
2 weeks ago
5 days ago
2 weeks ago
5 days ago
2 weeks ago
5 days ago
2 weeks ago
5 days ago
2 weeks ago
5 days ago
2 weeks ago
5 days ago
2 weeks ago
5 days ago
2 weeks ago
5 days ago
2 weeks ago
  1. <template>
  2. <view class="user-detail-container">
  3. <!-- 顶部导航栏 -->
  4. <view class="nav-bar">
  5. <view class="back" @tap="goBack">
  6. <uni-icons type="left" size="20"></uni-icons>
  7. </view>
  8. <text class="title">用户管理详情</text>
  9. </view>
  10. <view class="main-content">
  11. <!-- 个人信息卡片 -->
  12. <view class="info-card info-card-personal">
  13. <view class="card-title-row">
  14. <view class="card-title">个人信息</view>
  15. <text v-if="user.role==='推广官'" class="role-tag">推广官</text>
  16. </view>
  17. <view v-if="user.blocked" class="blocked-tag">已拉黑</view>
  18. <view class="info-row">
  19. <text class="label">姓名</text>
  20. <text class="value">{{ user.name }}</text>
  21. </view>
  22. <view class="divider"></view>
  23. <view class="info-row">
  24. <text class="label">电话</text>
  25. <text class="value">{{ user.phone }}</text>
  26. </view>
  27. <view class="divider"></view>
  28. <view class="info-row avatar-row">
  29. <text class="label">头像</text>
  30. <image class="avatar" :src="user.avatar" mode="aspectFill" />
  31. </view>
  32. </view>
  33. <!-- 平台信息卡片 -->
  34. <view class="info-card">
  35. <view class="card-title">平台信息</view>
  36. <view class="info-row">
  37. <text class="label">回收总次数</text>
  38. <text class="value">{{ user.recycleCount }}</text>
  39. </view>
  40. <view class="divider"></view>
  41. <view class="info-row">
  42. <text class="label">总件数</text>
  43. <text class="value">{{ user.totalItems }}</text>
  44. </view>
  45. <view class="divider"></view>
  46. <view class="info-row">
  47. <text class="label">回收总额</text>
  48. <text class="value">{{ user.totalAmount }}</text>
  49. </view>
  50. <view class="divider"></view>
  51. <view class="info-row address-row">
  52. <text class="label">用户下单地址</text>
  53. <text class="value address">{{ user.address }}</text>
  54. </view>
  55. <view class="divider"></view>
  56. <view class="info-row order-row">
  57. <text class="label">历史订单</text>
  58. <text class="value link" @tap="viewOrders">点击查看</text>
  59. </view>
  60. </view>
  61. </view>
  62. <!-- 底部操作栏 -->
  63. <view class="bottom-bar">
  64. <button v-if="user.blocked" class="action-btn danger" @tap="unblockUser">解除拉黑</button>
  65. <template v-else>
  66. <button class="action-btn danger" @tap="blockUser">拉黑</button>
  67. <button v-if="user.role!=='推广官'" class="action-btn primary" @tap="upgradeUser">升级推广官</button>
  68. </template>
  69. </view>
  70. </view>
  71. </template>
  72. <script>
  73. import pullRefreshMixin from '../mixins/pullRefreshMixin.js'
  74. export default {
  75. mixins: [pullRefreshMixin],
  76. data() {
  77. return {
  78. user: {
  79. name: '赵莫艳',
  80. phone: '15888977617',
  81. avatar: 'https://img1.baidu.com/it/u=1234567890,1234567890&fm=253&fmt=auto&app=138&f=JPEG?w=400&h=400',
  82. recycleCount: 2412,
  83. totalItems: 32,
  84. totalAmount: '8273.99',
  85. address: '海南省海口市秀英区秀英街道5单元183室',
  86. },
  87. statusBarHeight: 0,
  88. }
  89. },
  90. computed: {
  91. navbarStyle() {
  92. return `padding-top: ${this.statusBarHeight}px;`;
  93. }
  94. },
  95. onLoad(options) {
  96. // 这里可以通过 options 传递用户id并请求详情
  97. uni.getSystemInfo({
  98. success: (res) => {
  99. this.statusBarHeight = res.statusBarHeight || 20;
  100. }
  101. });
  102. // 获取上个页面传递的用户信息
  103. const eventChannel = this.getOpenerEventChannel && this.getOpenerEventChannel();
  104. if (eventChannel) {
  105. eventChannel.on('userDetail', (user) => {
  106. this.user = Object.assign({}, this.user, user);
  107. // 获取平台统计信息
  108. if (user.id) {
  109. this.$api && this.$api('getUserOrderNum', { userId: user.id }, res => {
  110. if (res && res.code === 200 && res.result) {
  111. this.user.recycleCount = res.result.unit_num;
  112. this.user.totalItems = res.result.sum;
  113. this.user.totalAmount = res.result.order_money;
  114. }
  115. });
  116. // 获取默认地址
  117. this.$api && this.$api('getAddressList', { userId: user.id }, res => {
  118. if (res && res.code === 200 && Array.isArray(res.result)) {
  119. const defaultAddr = res.result.find(addr => addr.defaultFlag === 'Y');
  120. if (defaultAddr) {
  121. this.user.address = defaultAddr.address;
  122. }
  123. }
  124. });
  125. }
  126. });
  127. }
  128. },
  129. methods: {
  130. goBack() {
  131. uni.navigateBack();
  132. },
  133. blockUser() {
  134. if (!this.user.id) return;
  135. this.$api('blackUser', { userId: this.user.id }, res => {
  136. if (res && res.code === 200) {
  137. uni.showToast({ title: '已拉黑', icon: 'success' });
  138. this.user.blocked = true;
  139. } else {
  140. uni.showToast({ title: res.msg || '拉黑失败', icon: 'none' });
  141. }
  142. })
  143. },
  144. upgradeUser() {
  145. if (!this.user.id) return;
  146. this.$api('upgrade', { userId: this.user.id }, res => {
  147. if (res && res.code === 200) {
  148. uni.showToast({ title: '已升级为推广官', icon: 'success' });
  149. this.user.role = '推广官';
  150. } else {
  151. uni.showToast({ title: res.msg || '升级失败', icon: 'none' });
  152. }
  153. })
  154. },
  155. viewOrders() {
  156. uni.navigateTo({
  157. url: '/pages/manager/order?historyOrder=true&userId=' + this.user.id
  158. });
  159. },
  160. unblockUser() {
  161. uni.showToast({ title: '已解除拉黑', icon: 'none' });
  162. this.user.blocked = false;
  163. },
  164. refreshData() {
  165. // TODO: 实现用户详情刷新逻辑,如重新请求接口
  166. },
  167. async onRefresh() {
  168. await this.refreshData && this.refreshData()
  169. }
  170. },
  171. onPullDownRefresh() {
  172. this.refreshData && this.refreshData()
  173. uni.stopPullDownRefresh()
  174. }
  175. }
  176. </script>
  177. <style lang="scss" scoped>
  178. .user-detail-container {
  179. min-height: 100vh;
  180. background: #f7f7f7;
  181. padding-bottom: 160rpx;
  182. }
  183. // 新增主流小程序风格导航栏
  184. .nav-bar {
  185. display: flex;
  186. align-items: center;
  187. height: calc(150rpx + var(--status-bar-height));
  188. padding: 0 32rpx;
  189. padding-top: var(--status-bar-height);
  190. background: #fff;
  191. position: fixed;
  192. top: 0;
  193. left: 0;
  194. right: 0;
  195. z-index: 999;
  196. box-sizing: border-box;
  197. box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.03);
  198. .back {
  199. padding: 20rpx;
  200. margin-left: -20rpx;
  201. }
  202. .title {
  203. flex: 1;
  204. text-align: center;
  205. font-size: 34rpx;
  206. font-weight: 500;
  207. color: #222;
  208. }
  209. }
  210. .main-content {
  211. margin-top: calc(150rpx + var(--status-bar-height));
  212. margin-bottom: 40rpx;
  213. }
  214. .info-card {
  215. background: #fff;
  216. border-radius: 40rpx;
  217. margin: 0 32rpx 32rpx 32rpx;
  218. padding: 40rpx 36rpx 36rpx 36rpx;
  219. box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.03);
  220. .card-title {
  221. font-size: 36rpx;
  222. font-weight: bold;
  223. color: #222;
  224. margin-bottom: 32rpx;
  225. }
  226. .info-row {
  227. display: flex;
  228. flex-direction: column;
  229. align-items: flex-start;
  230. min-height: 60rpx;
  231. margin-bottom: 0;
  232. padding: 18rpx 0 10rpx 0;
  233. .label {
  234. font-size: 26rpx;
  235. color: #b3b3b3;
  236. font-weight: 400;
  237. margin-bottom: 6rpx;
  238. }
  239. .value {
  240. font-size: 32rpx;
  241. color: #222;
  242. font-weight: 500;
  243. flex: 1;
  244. word-break: break-all;
  245. margin-bottom: 0;
  246. }
  247. .address {
  248. font-size: 30rpx;
  249. color: #222;
  250. font-weight: 400;
  251. margin-top: 0;
  252. line-height: 1.5;
  253. }
  254. .link {
  255. color: #ff8917;
  256. font-size: 32rpx;
  257. font-weight: 500;
  258. text-decoration: underline;
  259. margin-left: 0;
  260. }
  261. }
  262. .divider {
  263. height: 2rpx;
  264. background: #f3f3f3;
  265. margin: 10rpx 0 10rpx 0;
  266. border: none;
  267. }
  268. }
  269. .card-title-row {
  270. display: flex;
  271. align-items: center;
  272. margin-bottom: 32rpx;
  273. }
  274. .role-tag {
  275. font-size: 26rpx;
  276. color: #ff8917;
  277. border: 2rpx solid #ff8917;
  278. border-radius: 12rpx;
  279. padding: 2rpx 18rpx;
  280. font-weight: 400;
  281. background: #fff7f0;
  282. display: inline-block;
  283. vertical-align: middle;
  284. }
  285. .avatar-row {
  286. align-items: flex-start;
  287. .avatar {
  288. width: 120rpx;
  289. height: 120rpx;
  290. border-radius: 20rpx;
  291. margin-top: 8rpx;
  292. margin-left: 0;
  293. object-fit: cover;
  294. }
  295. }
  296. .order-row {
  297. .link {
  298. color: #ff8917;
  299. font-size: 30rpx;
  300. font-weight: 500;
  301. text-decoration: underline;
  302. margin-left: 0;
  303. }
  304. }
  305. .bottom-bar {
  306. position: fixed;
  307. left: 0;
  308. right: 0;
  309. bottom: 0;
  310. display: flex;
  311. justify-content: space-around;
  312. align-items: center;
  313. background: #f7f7f7;
  314. padding-bottom: env(safe-area-inset-bottom);
  315. padding-top: 24rpx;
  316. padding-bottom: 24rpx;
  317. z-index: 20;
  318. }
  319. .action-btn {
  320. flex: 1;
  321. margin: 0 32rpx;
  322. height: 80rpx;
  323. border-radius: 40rpx;
  324. font-size: 30rpx;
  325. font-weight: 500;
  326. border: 2rpx solid #ffd36d;
  327. background: #fffbe6;
  328. color: #ffb800;
  329. box-shadow: 0 2rpx 8rpx rgba(255, 156, 0, 0.03);
  330. }
  331. .action-btn.primary {
  332. color: #ffb800;
  333. border: 2rpx solid #ffd36d;
  334. background: #fffbe6;
  335. }
  336. .action-btn.danger {
  337. color: #ffb800;
  338. border: 2rpx solid #ffd36d;
  339. background: #fffbe6;
  340. }
  341. .info-card-personal {
  342. position: relative;
  343. .card-title-row {
  344. margin-bottom: 32rpx;
  345. .card-title {
  346. font-size: 36rpx;
  347. font-weight: bold;
  348. color: #222;
  349. margin-right: 18rpx;
  350. }
  351. }
  352. .info-row {
  353. display: flex;
  354. flex-direction: column;
  355. align-items: flex-start;
  356. min-height: 60rpx;
  357. margin-bottom: 0;
  358. padding: 18rpx 0 10rpx 0;
  359. .label {
  360. font-size: 26rpx;
  361. color: #b3b3b3;
  362. font-weight: 400;
  363. margin-bottom: 6rpx;
  364. }
  365. .value {
  366. font-size: 32rpx;
  367. color: #222;
  368. font-weight: 500;
  369. flex: 1;
  370. word-break: break-all;
  371. margin-bottom: 0;
  372. }
  373. }
  374. .avatar-row {
  375. align-items: flex-start;
  376. .avatar {
  377. width: 120rpx;
  378. height: 120rpx;
  379. border-radius: 24rpx;
  380. margin-top: 8rpx;
  381. margin-left: 0;
  382. object-fit: cover;
  383. display: block;
  384. }
  385. }
  386. .divider {
  387. height: 2rpx;
  388. background: #f3f3f3;
  389. margin: 10rpx 0 10rpx 0;
  390. border: none;
  391. }
  392. }
  393. .blocked-tag {
  394. position: absolute;
  395. right: 0;
  396. top: 0;
  397. background: #ffeaea;
  398. color: #ff5b5b;
  399. font-size: 28rpx;
  400. border-radius: 0 0 0 20rpx;
  401. padding: 12rpx 32rpx 12rpx 32rpx;
  402. font-weight: 400;
  403. z-index: 2;
  404. }
  405. </style>