my.uvue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572
  1. <template>
  2. <view class="page-container">
  3. <!-- 1. 用户信息头部 -->
  4. <view class="user-header">
  5. <image :src="userInfo['avatar']" class="avatar" mode="aspectFill" />
  6. <view class="user-info">
  7. <text class="username">
  8. {{ userInfo['name'] }}
  9. </text>
  10. <view class="badge-row">
  11. <text class="badge vip">
  12. 王牌
  13. </text>
  14. </view>
  15. </view>
  16. <view class="header-actions">
  17. <view class="action-item" @click="contactService">
  18. <u-icon type="customer-service" size="24" color="#666">
  19. </u-icon>
  20. <text>
  21. 客服
  22. </text>
  23. </view>
  24. <view class="action-item" @click="openSettings">
  25. <u-icon type="gear" size="24" color="#666">
  26. </u-icon>
  27. <text>
  28. 设置
  29. </text>
  30. </view>
  31. </view>
  32. </view>
  33. <!-- 2. 等级卡片 -->
  34. <view class="level-card">
  35. <view class="level-main">
  36. <text class="level-title">
  37. V{{ userInfo['level'] }}
  38. </text>
  39. <text class="level-sub">
  40. 成长值 {{ userInfo['growthValue'] }}
  41. </text>
  42. </view>
  43. <view class="level-progress">
  44. <text class="progress-text">
  45. 还差{{ userInfo['nextLevelGap'] }}成长值可升至V{{ userInfo['nextLevel'] }}
  46. </text>
  47. <view class="progress-bar">
  48. <view class="progress-fill" :style="{ width: progressWidth }">
  49. </view>
  50. </view>
  51. <view class="progress-labels">
  52. <text>
  53. V1
  54. </text>
  55. <text>
  56. V2
  57. </text>
  58. </view>
  59. </view>
  60. <view class="level-badge" @click="viewLevelDetail">
  61. <u-icon type="vip" size="40" color="#ffffff">
  62. </u-icon>
  63. <text>
  64. 我的等级
  65. </text>
  66. </view>
  67. </view>
  68. <!-- 3. 我的档案 -->
  69. <view class="section-title">
  70. 我的档案
  71. </view>
  72. <view class="profile-stats">
  73. <view class="stat-item">
  74. <text class="stat-value">
  75. 60
  76. </text>
  77. <text class="stat-label">
  78. 签约
  79. </text>
  80. </view>
  81. <view class="stat-item">
  82. <text class="stat-value">
  83. 21
  84. </text>
  85. <text class="stat-label">
  86. 解约
  87. </text>
  88. </view>
  89. <view class="stat-item">
  90. <u-icon type="location" size="24" color="#666">
  91. </u-icon>
  92. <text class="stat-label">
  93. 异地签到
  94. </text>
  95. </view>
  96. <view class="stat-item">
  97. <u-icon type="cart" size="24" color="#666">
  98. </u-icon>
  99. <text class="stat-label">
  100. 购买物料
  101. </text>
  102. </view>
  103. </view>
  104. <!-- 4. 我的工具 -->
  105. <view class="section-title">
  106. 我的工具
  107. </view>
  108. <view class="tools-grid">
  109. <view class="tool-item" v-for="(tool, idx) in tools" :key="idx" @click="handleToolClick(tool)">
  110. <u-icon :type="tool.icon" size="32" :color="tool.color">
  111. </u-icon>
  112. <text class="tool-name">
  113. {{ tool.name }}
  114. </text>
  115. </view>
  116. </view>
  117. <!-- 5. 推广卡片 -->
  118. <view class="promo-cards">
  119. <!-- 邀请好友 -->
  120. <view class="promo-card invite" @click="inviteFriends">
  121. <view class="promo-content">
  122. <text class="promo-title">
  123. 邀请好友赚钱
  124. </text>
  125. <text class="promo-subtitle">
  126. 单次最高可奖200元
  127. </text>
  128. </view>
  129. <u-icon type="gift" size="40" color="#ffd700">
  130. </u-icon>
  131. </view>
  132. <!-- 我的团队 -->
  133. <view class="promo-card team" @click="viewTeam">
  134. <view class="promo-content">
  135. <text class="promo-title">
  136. 我的团队
  137. </text>
  138. <text class="promo-subtitle">
  139. 团队成员100人
  140. </text>
  141. </view>
  142. <u-icon type="person" size="40" color="#4a90e2">
  143. </u-icon>
  144. </view>
  145. </view>
  146. <!-- 6. 城市合伙人 banner -->
  147. <view class="partner-banner" @click="joinPartner">
  148. <view class="partner-content">
  149. <text class="partner-title">
  150. 寻找城市合伙人
  151. </text>
  152. <text class="partner-subtitle">
  153. 全新盈利模式助你创业
  154. </text>
  155. </view>
  156. <view class="partner-icon">
  157. <u-icon type="person" size="50" color="#ffffff">
  158. </u-icon>
  159. <u-icon type="person" size="30" color="#ffffff" style="margin-left: -20rpx;">
  160. </u-icon>
  161. </view>
  162. </view>
  163. </view>
  164. </template>
  165. <script setup lang="ts">
  166. import { ref, reactive } from 'vue';
  167. // --- 用户信息 ---
  168. type UserInfo = {
  169. name : string;
  170. avatar : string;
  171. level : number;
  172. growthValue : number;
  173. nextLevel : number;
  174. nextLevelGap : number;
  175. progressPercent : number;
  176. }
  177. const userInfo = reactive<UserInfo>({
  178. name: '刘大锤',
  179. avatar: 'https://via.placeholder.com/100x100/4a90e2/ffffff?text=LD', // 替换为实际头像
  180. level: 30,
  181. growthValue: 50,
  182. nextLevel: 2,
  183. nextLevelGap: 15,
  184. progressPercent: 75 // (50 / (50+15)) * 100 ≈ 75%
  185. });
  186. // computed helpers to satisfy UTS inference rules
  187. // convert percent to a fixed rpx width (assuming 750rpx full width)
  188. const progressWidth = computed(() => `${userInfo.progressPercent * 7.5}rpx`);
  189. // --- 工具列表 ---
  190. type ToolItem = {
  191. name : string;
  192. icon : string;
  193. color : string;
  194. };
  195. // avoid generic parameter on ref which earlier triggered an "interface does not
  196. // have constructors" error; cast the initial value instead.
  197. const tools = ref([
  198. { name: '学习园地', icon: 'book', color: '#ff9900' },
  199. { name: '问题反馈', icon: 'chat', color: '#52c41a' },
  200. { name: '定制优惠', icon: 'wallet', color: '#1890ff' },
  201. { name: 'VIP俱乐部', icon: 'vip', color: '#faad14' }
  202. ] as ToolItem[]);
  203. // --- 方法 ---
  204. const contactService = () => {
  205. uni.showToast({ title: '联系客服', icon: 'none' });
  206. };
  207. const openSettings = () => {
  208. uni.showToast({ title: '打开设置', icon: 'none' });
  209. };
  210. const viewLevelDetail = () => {
  211. uni.showToast({ title: '查看等级详情', icon: 'none' });
  212. };
  213. const handleToolClick = (tool : ToolItem) => {
  214. uni.showToast({ title: `点击${tool.name}`, icon: 'none' });
  215. };
  216. const inviteFriends = () => {
  217. uni.showModal({
  218. title: '邀请好友',
  219. content: '分享链接邀请好友加入,单次最高可获奖励200元!',
  220. success: (res) => {
  221. if (res.confirm) {
  222. uni.showToast({ title: '已生成邀请链接', icon: 'success' });
  223. }
  224. }
  225. });
  226. };
  227. const viewTeam = () => {
  228. uni.showToast({ title: '查看团队成员', icon: 'none' });
  229. };
  230. const joinPartner = () => {
  231. uni.showModal({
  232. title: '城市合伙人',
  233. content: '全新盈利模式助你创业,立即申请成为城市合伙人!',
  234. success: (res) => {
  235. if (res.confirm) {
  236. uni.showToast({ title: '申请已提交', icon: 'success' });
  237. }
  238. }
  239. });
  240. };
  241. </script>
  242. <style scoped>
  243. /*
  244. UniApp X 默认 page 是 flex-direction: column
  245. 所以 .page-container 会自动垂直排列子元素
  246. */
  247. .page-container {
  248. background-color: #f5f6f8;
  249. /* min-height: 100vh unsupported */
  250. min-height: 1000rpx;
  251. /* width: 100%; default block behavior */
  252. box-sizing: border-box;
  253. padding: 20rpx;
  254. /* gap: 20rpx; */
  255. /* 各模块间间距 */
  256. }
  257. /* --- 用户头部 --- */
  258. .user-header {
  259. display: flex;
  260. flex-direction: row;
  261. align-items: center;
  262. /* gap: 20rpx; */
  263. padding: 20rpx;
  264. background: #ffffff;
  265. border-radius: 16rpx;
  266. box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
  267. }
  268. .avatar {
  269. width: 100rpx;
  270. height: 100rpx;
  271. border-radius: 50rpx;
  272. border: 2rpx solid #eee;
  273. }
  274. .user-info {
  275. flex: 1;
  276. display: flex;
  277. flex-direction: column;
  278. /* */
  279. }
  280. .username {
  281. font-size: 32rpx;
  282. font-weight: bold;
  283. color: #333;
  284. }
  285. .badge-row {
  286. display: flex;
  287. flex-direction: row;
  288. /* gap: 10rpx; */
  289. }
  290. .badge {
  291. font-size: 22rpx;
  292. padding: 4rpx 12rpx;
  293. border-radius: 20rpx;
  294. font-weight: bold;
  295. }
  296. .badge.vip {
  297. background: linear-gradient(90deg, #4a90e2, #67b26f);
  298. color: #ffffff;
  299. }
  300. .header-actions {
  301. display: flex;
  302. flex-direction: row;
  303. /* gap: 30rpx; */
  304. }
  305. .action-item {
  306. display: flex;
  307. flex-direction: column;
  308. align-items: center;
  309. /* gap: 4rpx; unsupported */
  310. font-size: 22rpx;
  311. color: #666;
  312. }
  313. /* --- 等级卡片 --- */
  314. .level-card {
  315. background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  316. border-radius: 16rpx;
  317. padding: 30rpx;
  318. color: #ffffff;
  319. position: relative;
  320. overflow: hidden;
  321. }
  322. .level-main {
  323. display: flex;
  324. flex-direction: row;
  325. align-items: flex-start;
  326. /* baseline unsupported */
  327. /* gap: 15rpx; not supported by uvue */
  328. margin-bottom: 20rpx;
  329. }
  330. .level-title {
  331. font-size: 48rpx;
  332. font-weight: bold;
  333. }
  334. .level-sub {
  335. font-size: 28rpx;
  336. opacity: 0.9;
  337. }
  338. .level-progress {
  339. margin-bottom: 20rpx;
  340. }
  341. .progress-text {
  342. font-size: 24rpx;
  343. opacity: 0.8;
  344. margin-bottom: 10rpx;
  345. /* display:block removed; inline text is fine */
  346. }
  347. .progress-bar {
  348. /* width: 100%; */
  349. /* 全宽默认,无需指定百分比 */
  350. height: 12rpx;
  351. background: rgba(255, 255, 255, 0.3);
  352. border-radius: 6rpx;
  353. overflow: hidden;
  354. margin-bottom: 8rpx;
  355. }
  356. .progress-fill {
  357. /* 使用与父容器相同的固定高度 */
  358. height: 12rpx;
  359. background: #ffffff;
  360. border-radius: 6rpx;
  361. transition: width 0.3s ease;
  362. }
  363. .progress-labels {
  364. display: flex;
  365. flex-direction: row;
  366. justify-content: space-between;
  367. font-size: 22rpx;
  368. opacity: 0.7;
  369. }
  370. .level-badge {
  371. position: absolute;
  372. top: 20rpx;
  373. right: 20rpx;
  374. display: flex;
  375. flex-direction: column;
  376. align-items: center;
  377. /* */
  378. font-size: 24rpx;
  379. opacity: 0.9;
  380. }
  381. /* --- 章节标题 --- */
  382. .section-title {
  383. font-size: 30rpx;
  384. font-weight: bold;
  385. color: #333;
  386. margin-top: 10rpx;
  387. margin-bottom: 15rpx;
  388. padding-left: 10rpx;
  389. border-left: 4rpx solid #4a90e2;
  390. }
  391. /* --- 档案统计 --- */
  392. .profile-stats {
  393. display: flex;
  394. flex-direction: row;
  395. justify-content: space-around;
  396. background: #ffffff;
  397. border-radius: 16rpx;
  398. padding: 30rpx 20rpx;
  399. box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
  400. }
  401. .stat-item {
  402. display: flex;
  403. flex-direction: column;
  404. align-items: center;
  405. }
  406. .stat-value {
  407. font-size: 32rpx;
  408. font-weight: bold;
  409. color: #333;
  410. }
  411. .stat-label {
  412. font-size: 24rpx;
  413. color: #666;
  414. }
  415. /* --- 工具网格 --- */
  416. .tools-grid {
  417. display: flex;
  418. flex-direction: row;
  419. flex-wrap: wrap;
  420. /* gap: 20rpx; */
  421. background: #ffffff;
  422. border-radius: 16rpx;
  423. padding: 30rpx;
  424. box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
  425. }
  426. .tool-item {
  427. /* width: calc(25% - 15rpx); not supported */
  428. width: 168rpx;
  429. /* approximate quarter width */
  430. /* 4列,减去gap */
  431. display: flex;
  432. flex-direction: column;
  433. align-items: center;
  434. /* gap: 10rpx; */
  435. padding: 20rpx 0;
  436. }
  437. .tool-name {
  438. font-size: 24rpx;
  439. color: #666;
  440. text-align: center;
  441. }
  442. /* --- 推广卡片 --- */
  443. .promo-cards {
  444. display: flex;
  445. flex-direction: row;
  446. /* gap: 20rpx; */
  447. }
  448. .promo-card {
  449. flex: 1;
  450. border-radius: 16rpx;
  451. padding: 25rpx;
  452. display: flex;
  453. flex-direction: row;
  454. justify-content: space-between;
  455. align-items: center;
  456. box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
  457. }
  458. .promo-card.invite {
  459. background: linear-gradient(135deg, #fff9e6 0%, #ffeaa7 100%);
  460. }
  461. .promo-card.team {
  462. background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%);
  463. }
  464. .promo-content {
  465. display: flex;
  466. flex-direction: column;
  467. /* */
  468. }
  469. .promo-title {
  470. font-size: 28rpx;
  471. font-weight: bold;
  472. color: #333;
  473. }
  474. .promo-subtitle {
  475. font-size: 24rpx;
  476. color: #666;
  477. }
  478. /* --- 合伙人 Banner --- */
  479. .partner-banner {
  480. background: linear-gradient(135deg, #ff9a9e 0%, #fad0c4 100%);
  481. border-radius: 16rpx;
  482. padding: 30rpx;
  483. display: flex;
  484. flex-direction: row;
  485. justify-content: space-between;
  486. align-items: center;
  487. color: #ffffff;
  488. box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
  489. }
  490. .partner-content {
  491. display: flex;
  492. flex-direction: column;
  493. /* gap: 10rpx; */
  494. }
  495. .partner-title {
  496. font-size: 32rpx;
  497. font-weight: bold;
  498. }
  499. .partner-subtitle {
  500. font-size: 26rpx;
  501. opacity: 0.9;
  502. }
  503. .partner-icon {
  504. display: flex;
  505. flex-direction: row;
  506. align-items: center;
  507. }
  508. </style>