Index.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. <template>
  2. <!-- 我的 -->
  3. <div class="user-index">
  4. <!-- 导航栏 -->
  5. <div class="content">
  6. <div class="user-card">
  7. <!-- 左侧信息保持不变 -->
  8. <div class="card-left">
  9. <div class="avatar-box">
  10. <img :src="userInfo.avatar" alt="Avatar" class="avatar-img" />
  11. </div>
  12. <div class="info-box">
  13. <div class="name-row">
  14. <span class="user-name">{{ userInfo.nickname }}</span>
  15. <span class="vip-badge">{{ userInfo.level }}</span>
  16. </div>
  17. <div class="user-id">ID: {{ userInfo.id }}</div>
  18. </div>
  19. </div>
  20. <!-- 右侧:动态信用分仪表盘 -->
  21. <div class="card-right">
  22. <div class="score-chart">
  23. <svg viewBox="0 0 100 100" class="chart-svg">
  24. <!--
  25. 第一层:灰色底轨 (Background Segments)
  26. 遍历配置生成4段灰色轨迹,作为槽位
  27. -->
  28. <circle
  29. v-for="(item, index) in backgroundSegments"
  30. :key="`bg-${index}`"
  31. cx="50" cy="50" :r="radius"
  32. fill="none"
  33. stroke="#E5E9F2"
  34. stroke-width="6"
  35. :stroke-dasharray="item.dashArray"
  36. stroke-linecap="round"
  37. :transform="item.transform"
  38. />
  39. <!--
  40. 第二层:彩色进度 (Progress Segments)
  41. 根据分数覆盖在底轨之上
  42. -->
  43. <circle
  44. v-for="(item, index) in progressSegments"
  45. :key="`prog-${index}`"
  46. cx="50" cy="50" :r="radius"
  47. fill="none"
  48. :stroke="item.color"
  49. stroke-width="6"
  50. :stroke-dasharray="item.dashArray"
  51. stroke-linecap="round"
  52. :transform="item.transform"
  53. />
  54. </svg>
  55. <!-- 环内文字 -->
  56. <div class="score-text">
  57. <div class="score-num">{{ creditScore }}</div>
  58. <div class="score-label">信用分</div>
  59. </div>
  60. </div>
  61. </div>
  62. </div>
  63. <!-- 调试滑块 (实际使用可删除) -->
  64. <!-- <div class="debug-slider">-->
  65. <!-- <div class="slider-header">-->
  66. <!-- <span>当前分数: {{ creditScore }}</span>-->
  67. <!-- <span style="font-size:12px; color:#999">拖动查看未填满时的灰色效果</span>-->
  68. <!-- </div>-->
  69. <!-- <input type="range" min="0" max="100" v-model.number="creditScore" class="custom-range" />-->
  70. <!-- </div>-->
  71. </div>
  72. <!-- <div class="loan-info">-->
  73. <!-- <div class="info-left">-->
  74. <!-- <img src="../../assets/img/index/user/default-head.png" alt="" />-->
  75. <!-- <div class="info-name" @click="deleteAccount">-->
  76. <!-- <div class="name-area pf500 fs18 fc121212">-->
  77. <!-- 用户昵称-->
  78. <!-- <div class="level pf500 fs12 fcDF384C" @click.stop="router.push({name: 'VipCenter'})">V2</div>-->
  79. <!-- </div>-->
  80. <!-- <div class="pf400 fs14 fc6A7187" >ID: 658908</div>-->
  81. <!-- </div>-->
  82. <!-- </div>-->
  83. <!-- <div class="info-right"></div>-->
  84. <!-- </div>-->
  85. <!-- <div style="width: 375px; b"></div>-->
  86. <img src="@/assets/icon/user/yaoqi.svg" class="invite-friend" alt="" @click="router.push('/invite')"/>
  87. <div class="user-func">
  88. <div
  89. class="func-item"
  90. v-for="(item, index) in userMenu"
  91. :key="index"
  92. @click="goMenuRouter(index, item)">
  93. <div class="item-left pf500 fs14 fc333333">
  94. <div class="left-img">
  95. <img :src="item.img" class="anquan-set" alt="" />
  96. </div>
  97. {{ item.name }}
  98. </div>
  99. <img src="../../assets/icon/user/right-arrow.svg" class="right-arrow" alt="" />
  100. </div>
  101. </div>
  102. <div class="submit pf600 fs14 fcFFFFFF" @click="out=true">退出</div>
  103. </div>
  104. <logout v-model:visible="out"></logout>
  105. </template>
  106. <script setup>
  107. import logout from '@/views/user/Logout.vue'
  108. import { ref, computed } from 'vue';
  109. import { useRoute, useRouter } from "vue-router";
  110. const router = useRouter();
  111. const out = ref(false);
  112. const userInfo = ref({
  113. nickname: '用户昵称',
  114. id: '658908',
  115. level: 'V2',
  116. avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=Felix'
  117. });
  118. // --- 核心配置 ---
  119. const creditScore = ref(90); // 当前分数
  120. const maxScore = 100; // 满分
  121. const radius = 42;
  122. const circumference = 2 * Math.PI * radius; // 周长 ≈ 263.89
  123. // 修改 1: 起始角度改为 -90 度 (从顶部正中间开始,顺时针绘制)
  124. const startAngle = -270;
  125. const gapLength = 10; // 每一段之间的间隙 (像素)
  126. // 修改 2: 总有效长度改为完整圆周长 (不再乘 0.75,变成闭合圆环)
  127. const totalArcLength = circumference;
  128. // 颜色段配置 (总分需等于100)
  129. // 这里的顺序决定了圆环颜色的排列顺序
  130. const configSegments = [
  131. { max: 50, color: '#2B6BFF' }, // 蓝色
  132. { max: 20, color: '#FF4D6A' }, // 红色
  133. { max: 15, color: '#FFAA00' }, // 黄色
  134. { max: 15, color: '#00C087' }, // 绿色
  135. ];
  136. // --- 辅助函数:计算一段圆弧的几何信息 ---
  137. const calculateSegmentGeometry = (segmentMaxScore, currentStartAngle) => {
  138. // 1. 计算这一段理论上应该占多长 (像素)
  139. const ratio = segmentMaxScore / maxScore;
  140. let arcLength = ratio * totalArcLength;
  141. // 2. 减去间隙 (视觉上扣除,防止连在一起)
  142. // 实际绘制长度不能小于0
  143. const drawLength = Math.max(0, arcLength - gapLength);
  144. // 3. 计算这一段占用的角度 (包含被扣除的间隙,用于计算下一段的起点)
  145. const angleSpan = (arcLength / circumference) * 360;
  146. return { drawLength, angleSpan };
  147. };
  148. // --- 计算属性 1: 底轨片段 (灰色) ---
  149. const backgroundSegments = computed(() => {
  150. let currentAngle = startAngle;
  151. return configSegments.map(seg => {
  152. const { drawLength, angleSpan } = calculateSegmentGeometry(seg.max, currentAngle);
  153. const result = {
  154. dashArray: `${drawLength}, ${circumference}`,
  155. transform: `rotate(${currentAngle}, 50, 50)`
  156. };
  157. currentAngle += angleSpan; // 移动到下一段起点
  158. return result;
  159. });
  160. });
  161. // --- 计算属性 2: 进度片段 (彩色) ---
  162. const progressSegments = computed(() => {
  163. let currentAngle = startAngle;
  164. let remainingScore = creditScore.value;
  165. return configSegments.map(seg => {
  166. const { drawLength, angleSpan } = calculateSegmentGeometry(seg.max, currentAngle);
  167. // 计算这一段应该填满多少
  168. let fillRatio = 0;
  169. if (remainingScore >= seg.max) {
  170. fillRatio = 1; // 填满
  171. } else if (remainingScore > 0) {
  172. fillRatio = remainingScore / seg.max; // 填一部分
  173. } else {
  174. fillRatio = 0; // 不填
  175. }
  176. const currentDrawLength = drawLength * fillRatio;
  177. const result = {
  178. color: seg.color,
  179. dashArray: `${currentDrawLength}, ${circumference}`,
  180. transform: `rotate(${currentAngle}, 50, 50)`
  181. };
  182. currentAngle += angleSpan;
  183. remainingScore -= seg.max;
  184. return result;
  185. });
  186. });
  187. // const deleteAccount = () => {
  188. // router.push("/deleteAccount");
  189. // };
  190. const goMenuRouter = (index, item) => {
  191. if (index == 1) {
  192. router.push("/userLoanIndex");
  193. } else if (index == 4) {
  194. router.push("/userAsset");
  195. }else if (index === 2) {
  196. router.push({ name: 'AdvancedCertification' });
  197. }else if (index === 3) {
  198. router.push({ name: 'KycForm' });
  199. }else if (index === 5) {
  200. router.push({ name: 'LanguageSwitch' });
  201. }else if (index === 7) {
  202. router.push({ name: 'HelpCenter' });
  203. }else if (index === 8) {
  204. router.push({ name: 'AboutUs' });
  205. }else if (index === 0) {
  206. router.push({ name: 'SecuritySettings' });
  207. }
  208. };
  209. const userMenu = [
  210. {
  211. name: "安全设置",
  212. img: require("@/assets/icon/user/anquan-set.svg"),
  213. },
  214. {
  215. name: "我的贷款",
  216. img: require("@/assets/icon/user/my-loan.svg"),
  217. },
  218. {
  219. name: "高级认证",
  220. img: require("@/assets/icon/user/certification.svg"),
  221. },
  222. {
  223. name: "KYC认证",
  224. img: require("@/assets/icon/user/key-certification.svg"),
  225. },
  226. {
  227. name: "我的资产",
  228. img: require("@/assets/icon/user/my-money.svg"),
  229. },
  230. {
  231. name: "切换语言",
  232. img: require("@/assets/icon/user/language.svg"),
  233. },
  234. {
  235. name: "联系客服",
  236. img: require("@/assets/icon/user/customer-service.svg"),
  237. },
  238. {
  239. name: "帮助中心",
  240. img: require("@/assets/icon/user/help-center.svg"),
  241. },
  242. {
  243. name: "关于我们",
  244. img: require("@/assets/icon/user/about-we.svg"),
  245. },
  246. ];
  247. </script>
  248. <style lang="less" scoped>
  249. .user-index {
  250. display: flex;
  251. flex-direction: column;
  252. justify-content: flex-start;
  253. align-items: center;
  254. margin-bottom: 100px;
  255. width: 100%;
  256. .margin-top16 {
  257. margin-top: 16px;
  258. }
  259. .margin-top28 {
  260. margin-top: 28px;
  261. }
  262. .loan-info {
  263. display: flex;
  264. flex-direction: row;
  265. justify-content: space-between;
  266. align-items: center;
  267. margin-top: 20px;
  268. width: calc(349px - 22px);
  269. height: 90px;
  270. .info-left {
  271. display: flex;
  272. flex-direction: row;
  273. justify-content: flex-start;
  274. height: 56px;
  275. img {
  276. width: 56px;
  277. height: 56px;
  278. }
  279. .info-name {
  280. display: flex;
  281. flex-direction: column;
  282. justify-content: space-evenly;
  283. margin-left: 16px;
  284. height: 56px;
  285. .name-area {
  286. display: flex;
  287. flex-direction: row;
  288. justify-content: flex-start;
  289. align-items: center;
  290. .level {
  291. margin-left: 5px;
  292. width: 20px;
  293. height: 18px;
  294. line-height: 15px;
  295. text-align: center;
  296. border-radius: 3px;
  297. background: #df384c1a;
  298. }
  299. }
  300. }
  301. }
  302. .info-right {
  303. width: 90px;
  304. height: 90px;
  305. background: pink;
  306. }
  307. }
  308. .invite-friend {
  309. margin: 0 auto;
  310. //margin-top: 20px;
  311. width: 360px ;
  312. height: 68px;
  313. padding-left: 15px;
  314. }
  315. .user-func {
  316. width: 340px;
  317. .func-item {
  318. display: flex;
  319. flex-direction: row;
  320. justify-content: space-between;
  321. align-items: center;
  322. margin-top: 26.5px;
  323. width: 355px;
  324. height: 24px;
  325. &:first-child {
  326. margin-top: 18px;
  327. }
  328. .item-left {
  329. display: flex;
  330. flex-direction: row;
  331. justify-content: flex-start;
  332. align-items: center;
  333. height: 24px;
  334. .left-img {
  335. display: flex;
  336. flex-direction: row;
  337. justify-content: center;
  338. align-items: center;
  339. width: 24px;
  340. height: 24px;
  341. }
  342. .anquan-set {
  343. margin-right: 8px;
  344. }
  345. }
  346. .right-arrow {
  347. width: 7px;
  348. height: 15px;
  349. }
  350. }
  351. }
  352. .submit {
  353. margin-top: 43px;
  354. width: 311px;
  355. height: 40px;
  356. line-height: 40px;
  357. text-align: center;
  358. border-radius: 100px;
  359. background: #df384c;
  360. letter-spacing: 0.2px;
  361. }
  362. }
  363. //xin
  364. .nav-bar {
  365. display: flex; justify-content: space-between; align-items: center;
  366. height: 44px; padding: 0 16px; background: #fff;
  367. position: sticky; top: 0; z-index: 10;
  368. width: 100%;
  369. .icon1{ padding-left: 15px;}
  370. }
  371. .nav-title { font-size: 18px; font-weight: 500; color: #333; }
  372. .nav-left, .nav-right { width: 40px; }
  373. .content { width:100%;padding: 15px; }
  374. .user-card {
  375. background: #fff; border-radius: 16px; padding: 0px 0 0 15px;
  376. display: flex; justify-content: space-between; align-items: center;
  377. //box-shadow: 0 4px 12px rgba(0,0,0,0.03);
  378. }
  379. .card-left { display: flex; align-items: center; }
  380. .avatar-box {
  381. width: 56px; height: 56px; border-radius: 50%; overflow: hidden;
  382. background: #E0E6ED; margin-right: 12px; border: 1px solid #f0f0f0;
  383. }
  384. .avatar-img { width: 100%; height: 100%; object-fit: cover; }
  385. .info-box { display: flex; flex-direction: column; justify-content: center; }
  386. .name-row { display: flex; align-items: center; margin-bottom: 6px; }
  387. .user-name { font-size: 18px; font-weight: 600; color: #1A1A1A; margin-right: 8px; }
  388. .vip-badge {
  389. background-color: #FDEEEE; color: #E02F44; font-size: 10px; font-weight: 700;
  390. padding: 1px 5px; border-radius: 4px; line-height: 1.4; font-style: italic;
  391. }
  392. .user-id { font-size: 14px; color: #858B9C; }
  393. .card-right { position: relative; }
  394. .score-chart {
  395. width: 90px; height: 90px; position: relative;
  396. display: flex; align-items: center; justify-content: center;
  397. }
  398. .chart-svg {
  399. width: 100%; height: 100%;
  400. transform: rotate(0deg);
  401. }
  402. .score-text {
  403. position: absolute; text-align: center;
  404. top: 50%; left: 50%;
  405. transform: translate(-50%, -50%);
  406. }
  407. .score-num {
  408. font-size: 22px; font-weight: 700; color: #1A1A1A; line-height: 1; margin-bottom: 2px;
  409. }
  410. .score-label {
  411. font-size: 10px; color: #858B9C; transform: scale(0.9);
  412. }
  413. /* 调试滑块样式 */
  414. .debug-slider {
  415. margin-top: 20px; padding: 15px; background: #fff; border-radius: 12px;
  416. }
  417. .slider-header {
  418. display: flex; justify-content: space-between; margin-bottom: 10px; font-size: 14px; color: #333;
  419. }
  420. .custom-range {
  421. width: 100%; accent-color: #2B6BFF;
  422. }
  423. </style>