index.vue 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972
  1. <template>
  2. <div class="container">
  3. <svg-icon name="wallet-bg" class="bg-img" />
  4. <van-pull-refresh v-model="loading" @refresh="onRefresh" style="height:100%">
  5. <div class="user-info-body">
  6. <div class="user-info-lf">
  7. <van-image width="28px" height="28px" round :src="walletStore.avatar" />
  8. <div class="user-info">
  9. <div class="user-info-name" @click="showWallet = true">
  10. <div>{{ walletStore.username }}</div>
  11. <svg-icon
  12. style="width: 16px; height: 16px; margin-left: 3px;margin-top: -3px;"
  13. name="down"
  14. />
  15. </div>
  16. <div class="user-info-key-body">
  17. <span class="user-info-key-body-text">{{
  18. formatAddress(walletStore.account)
  19. }}</span>
  20. <svg-icon
  21. class="im-copy-btn"
  22. style="width: 16px; height: 16px"
  23. name="copy"
  24. :data-clipboard-text="walletStore.account"
  25. />
  26. </div>
  27. </div>
  28. </div>
  29. <div class="network">
  30. <van-image
  31. width="20px"
  32. height="20px"
  33. round
  34. :src="walletStore.accountIcon"
  35. />
  36. <div class="network-name">{{ walletStore.accountName }}</div>
  37. </div>
  38. </div>
  39. <div class="amount">
  40. <div class="amount-item-box">
  41. <div>Total assets</div>
  42. <div class="amount-item">
  43. <div class="item-text">
  44. <svg-icon class="my" style="width: 28px; height: 28px" name="my" />
  45. <div>{{ isShow ? totalMoney : "****" }}</div>
  46. </div>
  47. <svg-icon
  48. style="width: 16px; height: 16px"
  49. :name="isShow ? 'eyes' : 'seyes'"
  50. @click="isShow = !isShow"
  51. />
  52. </div>
  53. </div>
  54. <svg-icon
  55. style="width: 24px; height: 24px; margin-right: 27px"
  56. name="right"
  57. @click="goToPageDetail"
  58. />
  59. </div>
  60. <div class="card-box">
  61. <div class="box-cont" @click="goToPage('exchange')">
  62. <svg-icon
  63. class="box-cont-icon"
  64. style="width: 30px; height: 30px"
  65. name="1"
  66. />
  67. <div>{{ $t("wallet.GoldCoinExchange") }}</div>
  68. </div>
  69. <div class="box-cont" @click="goToPage('proceeds')">
  70. <svg-icon
  71. class="box-cont-icon"
  72. style="width: 30px; height: 30px"
  73. name="2"
  74. />
  75. <div>{{ $t("wallet.Collection") }}</div>
  76. </div>
  77. <div class="box-cont" @click="goToPage('transferDetail')">
  78. <svg-icon
  79. class="box-cont-icon"
  80. style="width: 30px; height: 30px"
  81. name="3"
  82. />
  83. <div>{{ $t("wallet.Transfer") }}</div>
  84. </div>
  85. <div class="box-cont" @click="goToDapp(1)">
  86. <svg-icon
  87. class="box-cont-icon"
  88. style="width: 30px; height: 30px"
  89. name="4"
  90. />
  91. <div>{{ $t("wallet.Competition") }}</div>
  92. </div>
  93. <div class="box-cont" @click="goToDapp(2)">
  94. <svg-icon
  95. class="box-cont-icon"
  96. style="width: 30px; height: 30px"
  97. name="5"
  98. />
  99. <div>{{ $t("wallet.BlockBrowser") }}</div>
  100. </div>
  101. </div>
  102. <div class="list-box">
  103. <div class="list-title">
  104. <div>{{ $t("wallet.Token") }}</div>
  105. <svg-icon
  106. style="width: 24px; height: 24px"
  107. name="add"
  108. @click="changePop(1)"
  109. />
  110. </div>
  111. <div class="list-ul">
  112. <template v-for="(item, i) in walletStore.tokenList" :key="i">
  113. <div
  114. class="list-li"
  115. v-if="item.name == 'ACC' || !item.show"
  116. @click="changePop(2, item)"
  117. >
  118. <div class="list-li-lf">
  119. <van-image width="30px" height="30px" round :src="item.logo" />
  120. <div style="margin-left: 8px">{{ item.name }}</div>
  121. </div>
  122. <div class="list-li-ri">
  123. <div>{{ item.balance }}</div>
  124. <div class="list-li-ri-num">${{ item.money }}</div>
  125. </div>
  126. </div>
  127. </template>
  128. </div>
  129. </div>
  130. </van-pull-refresh>
  131. <van-popup v-model:show="showWallet" position="bottom" round>
  132. <div class="pop-content">
  133. <div class="pop-title">
  134. <svg-icon
  135. style="width: 24px; height: 24px"
  136. name="left-arrow"
  137. @click="showWallet = false"
  138. />
  139. <div class="title">{{ $t("wallet.SelectWallet") }}</div>
  140. </div>
  141. <div class="tree">
  142. <div class="tree-lf">
  143. <div
  144. class="tree-lf-icon"
  145. v-for="(item, i) in networkList"
  146. :class="netWorkIndex == i ? 'active-bg' : ''"
  147. @click="changeNetwork(i)"
  148. >
  149. <van-image lazy-load class="network-item-icon" :src="item.icon" />
  150. </div>
  151. </div>
  152. <div class="tree-ri">
  153. <div class="tree-ri-title">
  154. {{ networkList[netWorkIndex]?.name }}
  155. </div>
  156. <div class="tree-ul">
  157. <div
  158. class="tree-li"
  159. v-for="(item, i) in currentWallets"
  160. :class="item.id == walletStore.id ? 'tree-active' : ''"
  161. @click="changeWallet(item)"
  162. >
  163. <van-image
  164. width="37px"
  165. height="37px"
  166. round
  167. :src="item.avatar"
  168. />
  169. <div class="tree-li-cont">
  170. <div class="tree-li-text">{{ item.username }}</div>
  171. <div class="tree-li-address">
  172. <span>{{ formatAddress(item.account) }}</span>
  173. <svg-icon
  174. class="im-copy-btn"
  175. style="width: 16px; height: 16px; margin-left: 2px"
  176. name="copy"
  177. :data-clipboard-text="item.account"
  178. />
  179. </div>
  180. </div>
  181. </div>
  182. </div>
  183. </div>
  184. </div>
  185. <div class="pop-btn" @click="addWallet">
  186. <van-button class="btn" type="primary" size="large" color="#4765DD">
  187. <svg-icon
  188. style="width: 24px; height: 24px; margin-right: 4px"
  189. name="add1"
  190. />{{ $t("wallet.AddWallet") }}
  191. </van-button>
  192. </div>
  193. </div>
  194. </van-popup>
  195. <van-popup
  196. v-model:show="showHistory"
  197. position="bottom"
  198. round
  199. style="height: 500px"
  200. >
  201. <div class="pop-content" style="height: 500px">
  202. <div class="pop-title">
  203. <svg-icon
  204. style="width: 24px; height: 24px"
  205. name="left-arrow"
  206. @click="showHistory = false"
  207. />
  208. <div class="title">
  209. {{
  210. type == 1
  211. ? $t("wallet.AddTokens")
  212. : $t("wallet.HistoricalRecords")
  213. }}
  214. </div>
  215. </div>
  216. <!-- 1 -->
  217. <div class="pop-ul" v-if="type == 1">
  218. <div class="pop-li" v-for="(item, i) in walletStore.tokenList">
  219. <div class="pop-li-lf" v-if="item.name != 'ACC'">
  220. <van-image width="32px" height="32px" round :src="item.logo" />
  221. <div style="margin-left: 6px">
  222. <div>{{ item.name }}</div>
  223. <div class="pop-address">{{ formatAddress(item.address) }}</div>
  224. </div>
  225. </div>
  226. <svg-icon
  227. v-if="item.name != 'ACC'"
  228. style="width: 20px; height: 20px"
  229. :name="item.show ? 'add-icon' : 'del-icon'"
  230. @click="tokensHandle(item)"
  231. />
  232. </div>
  233. </div>
  234. <!-- 2 -->
  235. <div class="pop-list" v-if="type == 2">
  236. <template v-if="historyList.length > 0">
  237. <div class="pop-list-box" v-for="(item, i) in historyList">
  238. <svg-icon
  239. style="width: 24px; height: 24px; color: #fff"
  240. :name="item.from?.hash == walletStore.account ? 'fs' : 'js'"
  241. />
  242. <div class="pop-list-ul">
  243. <div class="pop-list-li">
  244. <div class="pop-list-li-title">
  245. {{
  246. item.from?.hash == walletStore.account
  247. ? $t("wallet.Send")
  248. : $t("wallet.Receive")
  249. }}
  250. </div>
  251. <div class="pop-list-li-status">{{ $t("wallet.Success") }}</div>
  252. </div>
  253. <div class="pop-list-li">
  254. <div>{{ $t("wallet.TransferAmount") }}</div>
  255. <div>{{ item.value }}{{ selectUrl.name }}</div>
  256. </div>
  257. <div class="pop-list-li">
  258. <div>
  259. {{
  260. item.from?.hash == walletStore.account
  261. ? $t("wallet.Send")
  262. : $t("wallet.Receive")
  263. }}{{ $t("wallet.Time") }}
  264. </div>
  265. <div>{{ item.timestamp }}</div>
  266. </div>
  267. <div class="pop-list-li">
  268. <div>{{ $t("wallet.ReceivingAddress") }}</div>
  269. <div>
  270. {{ formatAddress(item.to?.hash) }}
  271. <svg-icon
  272. class="im-copy-btn"
  273. style="width: 20px; height: 20px"
  274. name="copy"
  275. :data-clipboard-text="item.to?.hash"
  276. />
  277. </div>
  278. </div>
  279. <div class="pop-list-li">
  280. <div>hash</div>
  281. <div>
  282. {{ formatAddress(item.hash) }}
  283. <svg-icon
  284. class="im-copy-btn"
  285. style="width: 20px; height: 20px"
  286. name="copy"
  287. :data-clipboard-text="item.hash"
  288. />
  289. </div>
  290. </div>
  291. <div class="pop-list-li" v-if="selectUrl.name == 'ACC'">
  292. <div>{{ $t("wallet.MinerIsFee") }}</div>
  293. <div>{{ item.gas_price }}ACC</div>
  294. </div>
  295. </div>
  296. </div>
  297. </template>
  298. <template v-if="DBhistoryList.length > 0">
  299. <div class="pop-list-box" v-for="(item, i) in DBhistoryList">
  300. <svg-icon
  301. style="width: 24px; height: 24px; color: #fff"
  302. :name="item.from.toLowerCase() == walletStore.account.toLowerCase() ? 'fs' : 'js'"
  303. />
  304. <div class="pop-list-ul">
  305. <div class="pop-list-li">
  306. <div class="pop-list-li-title">
  307. {{
  308. item.from.toLowerCase() == walletStore.account.toLowerCase()
  309. ? $t("wallet.Send")
  310. : $t("wallet.Receive")
  311. }}
  312. </div>
  313. <div class="pop-list-li-status">{{ $t("wallet.Success") }}</div>
  314. </div>
  315. <div class="pop-list-li">
  316. <div>{{ $t("wallet.TransferAmount") }}</div>
  317. <div>{{ item.value }}{{ selectUrl.name }}</div>
  318. </div>
  319. <div class="pop-list-li">
  320. <div>
  321. {{
  322. item.from.toLowerCase() == walletStore.account.toLowerCase()
  323. ? $t("wallet.Send")
  324. : $t("wallet.Receive")
  325. }}{{ $t("wallet.Time") }}
  326. </div>
  327. <div>{{ item.timeStamp }}</div>
  328. </div>
  329. <div class="pop-list-li">
  330. <div>{{ $t("wallet.ReceivingAddress") }}</div>
  331. <div>
  332. {{ formatAddress(item.to) }}
  333. <svg-icon
  334. class="im-copy-btn"
  335. style="width: 20px; height: 20px"
  336. name="copy"
  337. :data-clipboard-text="item.to"
  338. />
  339. </div>
  340. </div>
  341. <div class="pop-list-li">
  342. <div>hash</div>
  343. <div>
  344. {{ formatAddress(item.hash) }}
  345. <svg-icon
  346. class="im-copy-btn"
  347. style="width: 20px; height: 20px"
  348. name="copy"
  349. :data-clipboard-text="item.hash"
  350. />
  351. </div>
  352. </div>
  353. <div class="pop-list-li" v-if="selectUrl.name == 'ACC'">
  354. <div>{{ $t("wallet.MinerIsFee") }}</div>
  355. <div>{{ item.gas_price }}ACC</div>
  356. </div>
  357. </div>
  358. </div>
  359. </template>
  360. </div>
  361. </div>
  362. </van-popup>
  363. </div>
  364. </template>
  365. <script setup>
  366. import { getNetwork } from "@/api/path/login.api";
  367. import { useWalletStore } from "@/stores/modules/walletStore";
  368. import { useSystemStore } from "@/stores/modules/systemStore";
  369. import { transactions, tokenTransfers } from "@/api/path/backend.api";
  370. import { openDapp } from "@/composables/dAppView";
  371. import { cryptoEncode } from "@/utils/crypto";
  372. import { useCopy } from "@/hooks/use-copy.js";
  373. import { Device } from "@capacitor/device";
  374. useCopy();
  375. const router = useRouter();
  376. const walletStore = useWalletStore();
  377. const systemStore = useSystemStore();
  378. const loading = ref(false);
  379. const isShow = ref(true);
  380. const showWallet = ref(false);
  381. const showHistory = ref(false);
  382. const networkList = ref([]);
  383. const netWorkIndex = ref(0);
  384. const type = ref("");
  385. const historyList = ref([]);
  386. const DBhistoryList = ref([]);
  387. const selectUrl = ref({});
  388. const goToPage = (url) => {
  389. router.push(url);
  390. };
  391. const onRefresh = () => {
  392. setTimeout(() => {
  393. gethotTokens();
  394. loading.value = false;
  395. }, 1000);
  396. };
  397. // 跳转详情
  398. const goToPageDetail = () => {
  399. router.push({
  400. path: "walletDetail",
  401. query: {
  402. id: walletStore.id,
  403. },
  404. });
  405. };
  406. const changePop = (val, item) => {
  407. type.value = val;
  408. if (val == 2) {
  409. getDetail(item);
  410. selectUrl.value = item;
  411. return;
  412. }
  413. showHistory.value = true;
  414. };
  415. // 查看历史记录
  416. const getDetail = (item) => {
  417. historyList.value = [];
  418. DBhistoryList.value = [];
  419. if (item.name == "ACC") {
  420. getList();
  421. } else {
  422. getDBList(item.address);
  423. }
  424. showHistory.value = true;
  425. };
  426. const getList = async () => {
  427. const res = await transactions(walletStore.account);
  428. const items = res.items || [];
  429. if (items.length > 0) {
  430. items.forEach((val) => {
  431. val.timestamp = getDateTime(val.timestamp);
  432. val.gas_price = getPrice(val.fee.value).toFixed(9);
  433. if (val.value !== "0") {
  434. val.value = getPrice(val.value).toFixed(6);
  435. }
  436. });
  437. historyList.value = items;
  438. }
  439. };
  440. const getDBList = async (DBaddress) => {
  441. const res = await tokenTransfers(walletStore.account,DBaddress);
  442. const items = res.result || [];
  443. if (items.length > 0) {
  444. items.forEach((val) => {
  445. val.timeStamp = formatTime(val.timeStamp);
  446. val.value = getPrice(val.value).toFixed(6);
  447. val.hash = val.hash;
  448. });
  449. DBhistoryList.value = items;
  450. }
  451. };
  452. // 计算总和
  453. const totalMoney = computed(() => {
  454. const list = walletStore.tokenList;
  455. if (!Array.isArray(list) || list.length === 0) return 0;
  456. let total = Number(list[0].money || 0);
  457. for (let i = 1; i < list.length; i++) {
  458. if (!list[i].show) {
  459. total += Number(list[i].money || 0);
  460. }
  461. }
  462. return Number(total.toFixed(4));
  463. });
  464. // 添加钱包
  465. const addWallet = () => {
  466. walletStore.accountName = networkList.value[netWorkIndex.value].name;
  467. walletStore.accountIcon = networkList.value[netWorkIndex.value].icon;
  468. walletStore.rpcUrl = networkList.value[netWorkIndex.value].url;
  469. router.push({
  470. path: "/login",
  471. query: {
  472. isFirst: false,
  473. },
  474. });
  475. };
  476. // 网络数据
  477. const initNetwork = async () => {
  478. const { data } = await getNetwork({});
  479. networkList.value = data;
  480. };
  481. const currentChainName = computed(
  482. () => networkList.value[netWorkIndex.value]?.name
  483. );
  484. // 当前链对应的钱包数组
  485. const currentWallets = computed(() => {
  486. return walletStore.walletList.filter(
  487. (w) => w.accountName === currentChainName.value
  488. );
  489. });
  490. // 切换网络
  491. const changeNetwork = (i) => {
  492. netWorkIndex.value = i;
  493. };
  494. // 切换钱包
  495. const changeWallet = (item) => {
  496. walletStore.switchWallet(item.id, item.account);
  497. showWallet.value = false;
  498. };
  499. // 代币显示隐藏
  500. const tokensHandle = (item) => {
  501. walletStore.changeToten(item.name);
  502. };
  503. // 更新代币信息
  504. const gethotTokens = async () => {
  505. await walletStore.updateTokenVal();
  506. };
  507. // 跳转竞赛
  508. const goToDapp = (val) => {
  509. const dapp = cryptoEncode(
  510. JSON.stringify({
  511. address: walletStore.account,
  512. privateKey: walletStore.privateKey,
  513. })
  514. );
  515. if(val == 1){
  516. openDapp("https://accgame.angeltokens.io/", { dapp });
  517. }else{
  518. openDapp("https://backend.angeltoken.net/",{}, true);
  519. }
  520. };
  521. const formatAddress = (address) => {
  522. if (!address) return "";
  523. return address.slice(0, 8) + "..." + address.slice(-6);
  524. };
  525. const getPrice = (price) => {
  526. const num = parseFloat(price) / Math.pow(10, 18);
  527. return num;
  528. };
  529. const getDateTime = (isoString) => {
  530. const date = new Date(isoString);
  531. const formattedDate = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")} ${String(date.getHours()).padStart(2, "0")}:${String(date.getMinutes()).padStart(2, "0")}:${String(date.getSeconds()).padStart(2, "0")}`;
  532. return formattedDate;
  533. };
  534. function formatTime(ts) {
  535. const date = new Date(ts * 1000);
  536. const y = date.getFullYear();
  537. const m = String(date.getMonth() + 1).padStart(2, '0');
  538. const d = String(date.getDate()).padStart(2, '0');
  539. const hh = String(date.getHours()).padStart(2, '0');
  540. const mm = String(date.getMinutes()).padStart(2, '0');
  541. const ss = String(date.getSeconds()).padStart(2, '0');
  542. return `${y}-${m}-${d} ${hh}:${mm}:${ss}`;
  543. }
  544. onMounted(async () => {
  545. initNetwork();
  546. gethotTokens();
  547. const deviceId = await Device.getId();
  548. systemStore.DeviceId = deviceId.identifier;
  549. });
  550. </script>
  551. <style lang="less" scoped>
  552. .container {
  553. position: relative;
  554. display: flex;
  555. flex-direction: column;
  556. height: calc(100vh - 50px);
  557. .bg-img {
  558. height: 18.8rem;
  559. min-width: 100%;
  560. width: 100vw;
  561. position: absolute;
  562. top: -0.8rem;
  563. left: 0;
  564. z-index: -1;
  565. }
  566. :deep(.van-pull-refresh__text){
  567. color: #fff !important;
  568. }
  569. :deep(.van-loading__text){
  570. color: #fff !important;
  571. }
  572. .user-info-body {
  573. padding: 60px 17px 25px;
  574. display: flex;
  575. align-items: center;
  576. justify-content: space-between;
  577. .user-info-lf {
  578. display: flex;
  579. align-items: center;
  580. .user-info {
  581. margin-left: 11px;
  582. font-family:
  583. PingFang SC,
  584. PingFang SC;
  585. font-size: 10px;
  586. .user-info-name {
  587. font-weight: 500;
  588. color: #ffffff;
  589. display: flex;
  590. align-items: center;
  591. margin-bottom: 2px;
  592. }
  593. .user-info-key-body {
  594. font-weight: 400;
  595. color: rgba(255, 255, 255, 0.6);
  596. display: flex;
  597. align-items: center;
  598. .user-info-key-body-text {
  599. margin-right: 4px;
  600. }
  601. }
  602. }
  603. }
  604. .network {
  605. display: flex;
  606. align-items: center;
  607. border-radius: 27px;
  608. border: 1px solid rgba(255, 255, 255, 0.6);
  609. padding: 0 5px;
  610. font-family:
  611. PingFang SC,
  612. PingFang SC;
  613. font-weight: 400;
  614. font-size: 15px;
  615. color: #ffffff;
  616. height: 25px;
  617. line-height: 25px;
  618. box-sizing: border-box;
  619. .network-name {
  620. margin-left: 5px;
  621. }
  622. }
  623. }
  624. .amount {
  625. height: 102px;
  626. background-color: rgba(242, 242, 242, 0.2);
  627. backdrop-filter: blur(3px);
  628. -webkit-backdrop-filter: blur(3px);
  629. border-radius: 15px;
  630. margin: 0 17px 20px 17px;
  631. box-shadow: 0 7px 10px #15277031;
  632. box-sizing: border-box;
  633. display: flex;
  634. align-items: center;
  635. justify-content: space-between;
  636. font-family:
  637. PingFang SC,
  638. PingFang SC;
  639. font-weight: 400;
  640. font-size: 15px;
  641. color: #ffffff;
  642. &:before {
  643. height: calc(100% - 4px);
  644. width: calc(100% - 4px);
  645. content: "";
  646. position: absolute;
  647. inset: 0;
  648. z-index: -1;
  649. padding: 2px; /* 边框宽度 */
  650. background: linear-gradient(
  651. 135deg,
  652. rgba(255, 255, 255, 0.6) 0%,
  653. rgba(255, 255, 255, 0.2) 25%,
  654. rgba(255, 255, 255, 0.1) 50%,
  655. rgba(255, 255, 255, 0.2) 75%,
  656. rgba(255, 255, 255, 0.6) 100%
  657. ); //linear-gradient(135deg, #ffffff64, #ffffff2e); /* 渐变方向可调 */
  658. -webkit-mask:
  659. linear-gradient(#fff 0 0) content-box,
  660. linear-gradient(#fff 0 0);
  661. -webkit-mask-composite: xor;
  662. mask-composite: exclude;
  663. border-radius: 15px; /* 必须和父元素一致 */
  664. pointer-events: none; /* 避免遮挡点击 */
  665. }
  666. .amount-item-box {
  667. margin: 21px 21px 27px 27px;
  668. }
  669. .amount-item {
  670. margin-top: 7px;
  671. font-weight: 600;
  672. font-size: 25px;
  673. display: flex;
  674. position: relative;
  675. .item-text {
  676. display: flex;
  677. align-items: center;
  678. margin-right: 2px;
  679. .my {
  680. position: relative;
  681. top: 1px;
  682. }
  683. }
  684. }
  685. }
  686. .card-box {
  687. background: #ffffff;
  688. box-shadow: 0px 4px 8px -2px rgba(25, 75, 137, 0.25);
  689. border-radius: 17px;
  690. height: 78px;
  691. margin: 0 17px;
  692. padding: 15px 10px;
  693. box-sizing: border-box;
  694. display: flex;
  695. align-items: center;
  696. font-family:
  697. PingFang SC,
  698. PingFang SC;
  699. font-weight: 400;
  700. font-size: 12px;
  701. color: #000000;
  702. .box-cont {
  703. width: calc(100% / 5);
  704. display: flex;
  705. flex-direction: column;
  706. align-items: center;
  707. .box-cont-icon {
  708. margin-bottom: 4px;
  709. color: #fff;
  710. }
  711. }
  712. }
  713. .list-box {
  714. background: #ffffff;
  715. border-radius: 12px;
  716. margin: 25px 17px;
  717. padding: 16px;
  718. box-sizing: border-box;
  719. display: flex;
  720. flex-direction: column;
  721. flex: 1;
  722. overflow: hidden;
  723. .list-title {
  724. display: flex;
  725. justify-content: space-between;
  726. align-items: center;
  727. font-family:
  728. PingFang SC,
  729. PingFang SC;
  730. font-weight: 500;
  731. font-size: 17px;
  732. color: #000000;
  733. margin-bottom: 23px;
  734. }
  735. .list-ul {
  736. flex: 1;
  737. display: flex;
  738. flex-direction: column;
  739. overflow: auto;
  740. .list-li {
  741. display: flex;
  742. align-items: center;
  743. justify-content: space-between;
  744. font-family:
  745. PingFang SC,
  746. PingFang SC;
  747. font-weight: 500;
  748. margin-bottom: 12px;
  749. .list-li-lf {
  750. display: flex;
  751. align-items: center;
  752. font-size: 15px;
  753. color: #000000;
  754. }
  755. .list-li-ri {
  756. font-size: 12px;
  757. color: @font-color2;
  758. text-align: right;
  759. .list-li-ri-num {
  760. font-weight: 400;
  761. font-size: 10px;
  762. color: #8d8d8d;
  763. }
  764. }
  765. }
  766. .list-li:last-child {
  767. margin-bottom: 0;
  768. }
  769. }
  770. .list-ul::-webkit-scrollbar {
  771. width: 0;
  772. }
  773. }
  774. .pop-content {
  775. display: flex;
  776. flex-direction: column;
  777. .pop-title {
  778. padding: 17px;
  779. border-bottom: 1px solid #f2f2f2;
  780. display: flex;
  781. align-items: center;
  782. font-family:
  783. PingFang SC,
  784. PingFang SC;
  785. font-weight: 500;
  786. font-size: 17px;
  787. color: #000000;
  788. .title {
  789. flex: 1;
  790. display: flex;
  791. justify-content: center;
  792. }
  793. }
  794. .tree {
  795. display: flex;
  796. height: 300px;
  797. overflow: hidden;
  798. .tree-lf {
  799. display: flex;
  800. flex-direction: column;
  801. width: 67px;
  802. overflow: auto;
  803. .tree-lf-icon {
  804. height: 67px;
  805. padding: 16px;
  806. box-sizing: border-box;
  807. display: flex;
  808. align-items: center;
  809. justify-content: center;
  810. }
  811. .active-bg {
  812. background: #eceffc;
  813. }
  814. }
  815. .tree-lf::-webkit-scrollbar {
  816. width: 0;
  817. }
  818. .tree-ri {
  819. margin-left: 10px;
  820. margin-right: 17px;
  821. flex: 1;
  822. overflow: auto;
  823. .tree-ri-title {
  824. margin: 10px 0 4px;
  825. font-family:
  826. PingFang SC,
  827. PingFang SC;
  828. font-weight: 500;
  829. font-size: 15px;
  830. color: #000000;
  831. }
  832. .tree-ul {
  833. .tree-li {
  834. background: #f2f2f2;
  835. border-radius: 8px;
  836. border: 1px solid #f2f2f2;
  837. padding: 14px 16px;
  838. display: flex;
  839. align-items: center;
  840. margin-bottom: 12px;
  841. .tree-li-cont {
  842. margin-left: 12px;
  843. font-family:
  844. PingFang SC,
  845. PingFang SC;
  846. font-weight: 400;
  847. font-size: 12px;
  848. color: #8d8d8d;
  849. .tree-li-text {
  850. font-weight: 500;
  851. font-size: 15px;
  852. color: @theme-color1;
  853. }
  854. .tree-li-address {
  855. display: flex;
  856. align-items: center;
  857. }
  858. }
  859. }
  860. .tree-active {
  861. background: #fafbff;
  862. border: 1px solid @theme-color1;
  863. }
  864. }
  865. }
  866. .tree-ri::-webkit-scrollbar {
  867. width: 0;
  868. }
  869. }
  870. .pop-btn {
  871. margin: 17px;
  872. .btn {
  873. height: 40px;
  874. border-radius: 50px;
  875. font-family:
  876. PingFang SC,
  877. PingFang SC;
  878. font-weight: 500;
  879. font-size: 15px;
  880. color: #ffffff;
  881. }
  882. :deep(.van-button__text) {
  883. display: flex;
  884. align-items: center;
  885. }
  886. }
  887. .pop-list {
  888. flex: 1;
  889. display: flex;
  890. flex-direction: column;
  891. overflow: auto;
  892. .pop-list-box {
  893. display: flex;
  894. padding: 17px 17px 0;
  895. .pop-list-ul {
  896. padding-bottom: 12px;
  897. flex: 1;
  898. margin-left: 8px;
  899. border-bottom: 1px solid #f2f2f2;
  900. box-sizing: border-box;
  901. .pop-list-li {
  902. display: flex;
  903. align-items: center;
  904. justify-content: space-between;
  905. font-family:
  906. PingFang SC,
  907. PingFang SC;
  908. margin-bottom: 4px;
  909. font-weight: 400;
  910. font-size: 12px;
  911. color: @font-color2;
  912. .pop-list-li-title {
  913. font-weight: 500;
  914. font-size: 15px;
  915. color: #000000;
  916. }
  917. .pop-list-li-status {
  918. font-weight: 500;
  919. font-size: 15px;
  920. color: @theme-color1 !important;
  921. }
  922. }
  923. .pop-list-li div:last-child {
  924. color: #8d8d8d;
  925. display: flex;
  926. align-items: center;
  927. }
  928. }
  929. }
  930. }
  931. .pop-list::-webkit-scrollbar {
  932. width: 0;
  933. }
  934. .pop-ul {
  935. flex: 1;
  936. display: flex;
  937. flex-direction: column;
  938. overflow: auto;
  939. margin: 16px 16px 0;
  940. .pop-li {
  941. display: flex;
  942. align-items: center;
  943. justify-content: space-between;
  944. margin-bottom: 16px;
  945. .pop-li-lf {
  946. display: flex;
  947. align-items: center;
  948. font-family:
  949. PingFang SC,
  950. PingFang SC;
  951. font-weight: 400;
  952. font-size: 15px;
  953. color: #4f4f4f;
  954. .pop-address {
  955. font-size: 12px;
  956. color: #8d8d8d;
  957. }
  958. }
  959. }
  960. }
  961. }
  962. }
  963. </style>