This commit is contained in:
whitechiina 2026-04-27 18:46:25 +08:00
parent d593b51ff9
commit 2ae4aa2d17
9 changed files with 1627 additions and 295 deletions

View File

@ -395,6 +395,7 @@ function mapWalletFlowRecords(list, meta) {
return {
id: buildRecordId(item, meta.key || "flow"),
sourceId: String(pickFirstValue(item, ["id", "log_id", "uid"]) || ""),
title: title,
subtitle: orderSn ? "单号 " + orderSn : noteText || direction,
time: pickFirstValue(item, [
@ -420,6 +421,7 @@ function mapWalletFlowRecords(list, meta) {
directionLabel: direction,
actionSymbol: directionPresentation.actionSymbol,
orderSn: orderSn,
noteText: noteText,
tag: directionPresentation.income ? "收" : "支",
tone: directionPresentation.income ? "success" : "danger",
cardTone: directionPresentation.income ? "success" : "danger",
@ -631,30 +633,321 @@ function mapTransferRecords(list) {
}
function mapPointsConvertRecords(list) {
return (Array.isArray(list) ? list : []).map(function (item) {
const numberText = toFixedNumber(item.number, 2);
const transferCoinText = toFixedNumber(item.transfer_coin_num, 0);
const releaseTotal =
return normalizeListData(list).map(function (item) {
const amountValue = pickFirstValue(item, [
"number",
"transfer_coin_num",
"point_num",
"num",
"amount",
"value",
]);
const rawStatusText = String(
pickFirstValue(item, [
"status_text",
"status_name",
"status_desc",
"transfer_status_text",
]) || "",
);
const rawStatusCode = String(
pickFirstValue(item, ["status", "state", "transfer_status"]) || "",
);
let statusText = rawStatusText;
let disabled = false;
if (!statusText) {
if (rawStatusCode === "1") {
statusText = "兑换中";
} else if (rawStatusCode === "2") {
statusText = "已转换";
} else if (rawStatusCode === "3") {
statusText = "转换失败";
}
}
if (
/中/.test(statusText) ||
/完成/.test(statusText) ||
/成功/.test(statusText) ||
/失败/.test(statusText)
) {
disabled = true;
}
return {
id: String(
pickFirstValue(item, ["id", "bill_id", "log_id", "sn"]) ||
Math.random(),
),
sourceId: String(pickFirstValue(item, ["id", "bill_id", "log_id"]) || ""),
orderSn: String(
pickFirstValue(item, ["order_sn", "trade_no", "bill_no", "sn"]) || "",
),
title:
pickFirstValue(item, ["title", "name", "type_name"]) ||
"系统赠送积分",
subtitle:
"可转数量 " +
formatTransferRecordNumber(
pickFirstValue(item, ["transfer_coin_num", "coin_num", "value"]) || 0,
),
time: pickFirstValue(item, [
"add_time",
"create_time",
"created_at",
"time",
"update_time",
]),
amount: "+" + formatLedgerRecordNumber(amountValue),
amountValue: toNumber(amountValue),
statusText: statusText,
disabled: disabled,
balance:
item &&
item.userBillRelease &&
item.userBillRelease.total !== undefined &&
item.userBillRelease.total !== null
? String(item.userBillRelease.total)
: "";
return {
id: String(item.id || ""),
title: item.title || "积分记录",
subtitle: "可转数量 " + transferCoinText,
time: item.add_time || "",
amount: "+" + numberText,
balance: releaseTotal ? "释放总量 " + releaseTotal : "",
? "释放总量 " + formatTransferRecordNumber(item.userBillRelease.total)
: "",
tag: "积",
tone: "success",
tone: disabled ? "warning" : "success",
};
});
}
function extractMonthLabel(value) {
const raw = String(value || "").trim();
if (!raw) {
const currentDate = new Date();
const year = currentDate.getFullYear();
const month = String(currentDate.getMonth() + 1).padStart(2, "0");
return year + "-" + month;
}
const matcher = raw.match(/(\d{4})[-/.](\d{1,2})/);
if (matcher) {
return matcher[1] + "-" + String(matcher[2]).padStart(2, "0");
}
const date = new Date(raw.replace(/-/g, "/"));
if (Number.isNaN(date.getTime())) {
return raw.slice(0, 7);
}
return (
date.getFullYear() +
"-" +
String(date.getMonth() + 1).padStart(2, "0")
);
}
function buildPointsHistoryRecords(list) {
return mapWalletFlowRecords(list, {
key: "points-convert-history",
unit: "积分",
title: "积分记录",
subtitle: "可用积分流水",
}).map(function (item) {
return Object.assign({}, item, {
month: extractMonthLabel(item.time),
detailAvailable: canOpenPointsConvertRecordDetail(item),
amountTone: item.actionSymbol === "+" ? "increase" : "decrease",
});
});
}
function buildMonthOptions(list) {
const months = (Array.isArray(list) ? list : []).reduce(function (
result,
item,
) {
const month = item && item.month ? item.month : extractMonthLabel(item && item.time);
if (month && result.indexOf(month) === -1) {
result.push(month);
}
return result;
}, []);
months.sort(function (left, right) {
return right.localeCompare(left);
});
if (!months.length) {
months.push(extractMonthLabel(""));
}
return months;
}
function canOpenPointsConvertRecordDetail(item) {
const target = item && typeof item === "object" ? item : {};
const contentText = [
target.title,
target.subtitle,
target.noteText,
target.feeText,
target.directionLabel,
]
.join(" ")
.toLowerCase();
return (
Boolean(target.orderSn || target.sourceId) &&
/转换/.test(contentText) &&
!/bmt/.test(contentText)
);
}
function extractPointsConvertDetailItems(data) {
if (Array.isArray(data)) {
return data;
}
if (!data || typeof data !== "object") {
return [];
}
if (Array.isArray(data.list)) {
return data.list;
}
if (Array.isArray(data.items)) {
return data.items;
}
if (Array.isArray(data.details)) {
return data.details;
}
if (Array.isArray(data.records)) {
return data.records;
}
return [];
}
function buildPointsConvertDetailFallback(record) {
const fallback = record || {};
const amountValue = String(fallback.amount || "").replace(/[^\d.-]/g, "");
return {
orderSn: fallback.orderSn || "",
totalAmount:
fallback.amount && String(fallback.amount).trim()
? String(fallback.amount)
: "+" + formatLedgerRecordNumber(amountValue),
time: fallback.time || "",
details: [
{
id: fallback.id || fallback.orderSn || "fallback-record",
title: fallback.title || "积分转换",
amount:
fallback.amount && String(fallback.amount).trim()
? String(fallback.amount)
: "+" + formatLedgerRecordNumber(amountValue),
time: fallback.time || "",
},
],
};
}
function buildPointsConvertDetailPayload(data, fallbackRecord) {
const detailItems = extractPointsConvertDetailItems(data);
const summarySource =
data && typeof data === "object" && !Array.isArray(data) ? data : {};
const fallback = fallbackRecord || {};
const totalValue =
pickFirstValue(summarySource, [
"number",
"num",
"amount",
"point_num",
"transfer_coin_num",
"total",
]) || sumBy(detailItems, "number");
const totalAmountText =
totalValue !== ""
? "+" + formatLedgerRecordNumber(totalValue)
: fallback.amount || "+0";
const detailList = detailItems.map(function (item, index) {
const amountValue = pickFirstValue(item, [
"number",
"num",
"amount",
"point_num",
"transfer_coin_num",
"value",
]);
return {
id: String(
pickFirstValue(item, ["id", "bill_id", "log_id", "sn"]) ||
"detail-" + index,
),
title:
pickFirstValue(item, ["title", "name", "type_name"]) ||
fallback.title ||
"系统赠送积分",
amount: "+" + formatLedgerRecordNumber(amountValue),
time:
pickFirstValue(item, [
"add_time",
"create_time",
"created_at",
"time",
"update_time",
]) || fallback.time || "",
};
});
if (!detailList.length) {
return buildPointsConvertDetailFallback({
id: fallback.id,
orderSn:
String(
pickFirstValue(summarySource, [
"order_sn",
"trade_no",
"bill_no",
"sn",
]) || fallback.orderSn || "",
),
title:
pickFirstValue(summarySource, ["title", "name", "type_name"]) ||
fallback.title,
amount: totalAmountText,
time:
pickFirstValue(summarySource, [
"add_time",
"create_time",
"created_at",
"time",
"update_time",
]) || fallback.time,
});
}
return {
orderSn: String(
pickFirstValue(summarySource, ["order_sn", "trade_no", "bill_no", "sn"]) ||
fallback.orderSn ||
"",
),
totalAmount: totalAmountText,
time:
pickFirstValue(summarySource, [
"add_time",
"create_time",
"created_at",
"time",
"update_time",
]) || fallback.time || "",
details: detailList,
};
}
function buildDefaultLedger(type, balances) {
const map = {
power: {
@ -774,7 +1067,7 @@ async function fetchWalletAddressData(requestOptions) {
async function fetchPointsConvertList(requestOptions) {
return fetchPayload(
createRequestOptions({
url: serviceConfig.ENDPOINTS.pointsConvertList,
url: serviceConfig.ENDPOINTS.pointsConvertAvailableList,
data: {
interval: serviceConfig.POINTS_CONVERT_INTERVAL,
},
@ -783,6 +1076,18 @@ async function fetchPointsConvertList(requestOptions) {
);
}
async function fetchPointsConvertHistoryListData(month, requestOptions) {
return fetchPayload(
createRequestOptions({
url: serviceConfig.ENDPOINTS.pointsConvertHistoryList,
data: {
month: month,
},
}, requestOptions),
"积分兑换列表加载失败",
);
}
async function fetchTransferLedgerData(requestOptions) {
return fetchPayload(
createRequestOptions({
@ -816,6 +1121,16 @@ async function fetchRedeemRecordListData(redeemType, requestOptions) {
);
}
async function fetchPointsConvertInfoData(query, requestOptions) {
return fetchPayload(
createRequestOptions({
url: serviceConfig.ENDPOINTS.pointsConvertRecordDetail,
data: query || {},
}, requestOptions),
"积分转换详情加载失败",
);
}
export async function fetchAssetHome(requestOptions) {
const result = await Promise.all([
fetchPriceData(requestOptions),
@ -824,31 +1139,65 @@ export async function fetchAssetHome(requestOptions) {
return buildHomeOverview(result[1], result[0]);
}
export async function fetchPointsConvertDetail(requestOptions) {
export async function fetchPointsConvertHome(month, requestOptions) {
const result = await Promise.all([
fetchHomeBalanceData(requestOptions),
fetchPointsConvertList(requestOptions),
fetchPointsConvertHistoryListData(month, requestOptions),
]);
const balances = normalizeBalances(result[0]);
const pointList = Array.isArray(result[1] && result[1].list)
? result[1].list
: [];
const records = buildPointsHistoryRecords(result[1]).map(function (item) {
return Object.assign({}, item, {
month: extractMonthLabel(item.time) || month,
detailAvailable: canOpenPointsConvertRecordDetail(item),
});
});
return {
availablePoints: toFixedNumber(balances.points, 2),
pendingPoints: toFixedNumber(sumBy(pointList, "number"), 0),
ids: pointList
.map(function (item) {
return item && item.id;
})
.filter(Boolean),
tips: [
"释放中的积分转换成可用积分后方可兑换BMT",
"凌晨0点-凌晨1点积分系统维护不可兑换。",
],
availablePoints: formatHomeNumber(balances.points, 0),
records: records,
};
}
export async function fetchPointsConvertSelection(requestOptions) {
const data = await fetchPointsConvertList(requestOptions);
const items = mapPointsConvertRecords(data);
return {
items: items,
};
}
export async function fetchPointsConvertRecordDetail(record, requestOptions) {
const fallbackRecord = record || {};
const query = {};
if (fallbackRecord.sourceId) {
query.id = fallbackRecord.sourceId;
} else if (
fallbackRecord.id &&
fallbackRecord.orderSn &&
fallbackRecord.id !== fallbackRecord.orderSn
) {
query.id = fallbackRecord.id;
}
if (fallbackRecord.orderSn) {
query.order_sn = fallbackRecord.orderSn;
query.sn = fallbackRecord.orderSn;
}
if (!Object.keys(query).length) {
return buildPointsConvertDetailFallback(fallbackRecord);
}
try {
const data = await fetchPointsConvertInfoData(query, requestOptions);
return buildPointsConvertDetailPayload(data, fallbackRecord);
} catch (error) {
return buildPointsConvertDetailFallback(fallbackRecord);
}
}
export async function submitAssetPointsConvert(payload, requestOptions) {
const ids = Array.isArray(payload && payload.ids) ? payload.ids : [];

View File

@ -19,9 +19,10 @@ const serviceConfig = {
walletDetail: "/api/hn/wallet/getWalletAddress",
walletSave: "/api/hn/wallet/saveAddress",
withdrawSubmit: "/api/hn/wallet/withdraw",
pointsConvertList: "/api/integral/transferList",
pointsConvertHistoryList: "/api/integral/getTransferList",
pointsConvertAvailableList: "/api/integral/transferList",
pointsConvertSubmit: "/api/integral/doTransfer",
pointsConvertInfo: "/api/integral/transferInfo",
pointsConvertRecordDetail: "/api/integral/transferInfo",
},
};

View File

@ -68,5 +68,10 @@
"uniStatistics" : {
"enable" : false
},
"vueVersion" : "2"
"vueVersion" : "2",
"h5" : {
"router" : {
"base" : "/bmt/"
}
}
}

View File

@ -43,6 +43,20 @@
"backgroundColor": "#191E32"
}
},
{
"path": "pages/assets/points-convert-list",
"style": {
"navigationStyle": "custom",
"backgroundColor": "#191E32"
}
},
{
"path": "pages/assets/points-convert-detail",
"style": {
"navigationStyle": "custom",
"backgroundColor": "#191E32"
}
},
{
"path": "pages/assets/ledger",
"style": {

View File

@ -0,0 +1,262 @@
<template>
<view class="asset-page asset-theme points-detail-page">
<asset-page-shell title="积分转换" />
<view class="asset-scroll points-detail-scroll">
<text class="detail-section-title">转换记录</text>
<view class="summary-card">
<view class="summary-card__top">
<text class="summary-card__order">
单号
<text class="asset-number-font">{{ detail.orderSn || "--" }}</text>
</text>
<view class="summary-card__amount-box">
<text class="summary-card__amount-label">转换积分</text>
<text class="summary-card__amount asset-number-font">{{
detail.totalAmount
}}</text>
</view>
</view>
<text class="summary-card__time asset-number-font">{{
detail.time || "--"
}}</text>
</view>
<view class="detail-card">
<text class="detail-card__title">转换明细</text>
<view
v-for="item in detail.details"
:key="item.id + '-' + item.time"
class="detail-item"
>
<view class="detail-item__main">
<text class="detail-item__title">{{ item.title }}</text>
<text class="detail-item__time asset-number-font">{{
item.time
}}</text>
</view>
<view class="detail-item__side">
<text class="detail-item__label">转换积分</text>
<text class="detail-item__amount asset-number-font">{{
item.amount
}}</text>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import AssetPageShell from "../../components/asset-page-shell.vue";
import { fetchPointsConvertRecordDetail } from "../../api/assets";
function parsePayload(value) {
if (!value) {
return {};
}
try {
return JSON.parse(decodeURIComponent(value));
} catch (error) {
return {};
}
}
export default {
components: {
AssetPageShell,
},
data() {
return {
recordPayload: {},
detail: {
orderSn: "",
totalAmount: "+0",
time: "",
details: [],
},
};
},
onLoad(options) {
this.recordPayload = parsePayload(options && options.payload);
this.loadPage(true);
},
methods: {
async loadPage(showLoading) {
try {
this.detail = await fetchPointsConvertRecordDetail(
this.recordPayload,
showLoading
? {
showLoading: true,
loadingText: "加载中",
}
: null,
);
} catch (error) {
uni.showToast({
title: error.message || "详情加载失败",
icon: "none",
});
}
},
},
};
</script>
<style lang="scss" scoped>
@import "../../styles/tokens.scss";
@import "../../styles/common.scss";
.points-detail-page {
min-height: 100vh;
background: #191e32;
}
.points-detail-scroll {
min-height: calc(100vh - env(safe-area-inset-top) - 104rpx);
padding: 14rpx 14rpx calc(env(safe-area-inset-bottom) + 36rpx);
}
.detail-section-title {
display: block;
margin: 8rpx 2rpx 20rpx;
font-size: 28rpx;
font-weight: 600;
color: #ffffff;
}
.summary-card,
.detail-card {
position: relative;
overflow: hidden;
border-radius: 10rpx;
background: #242944;
box-shadow: 0 12rpx 24rpx rgba(8, 13, 30, 0.14);
}
.summary-card::after {
content: "";
position: absolute;
top: -24rpx;
left: 42%;
width: 118rpx;
height: 210rpx;
background: rgba(123, 137, 190, 0.18);
transform: rotate(30deg);
}
.summary-card__top {
position: relative;
z-index: 1;
display: flex;
align-items: center;
justify-content: space-between;
padding: 28rpx 26rpx;
border-bottom: 1px solid rgba(129, 141, 183, 0.18);
}
.summary-card__order {
flex: 1;
min-width: 0;
font-size: 28rpx;
font-weight: 600;
color: #ffffff;
}
.summary-card__amount-box {
display: flex;
align-items: baseline;
justify-content: flex-end;
margin-left: 18rpx;
}
.summary-card__amount-label {
font-size: 26rpx;
color: rgba(242, 245, 255, 0.94);
}
.summary-card__amount {
margin-left: 8rpx;
font-size: 28rpx;
font-weight: 700;
color: #ff8d62;
}
.summary-card__time {
position: relative;
z-index: 1;
display: block;
padding: 20rpx 26rpx 22rpx;
font-size: 24rpx;
color: rgba(228, 233, 250, 0.9);
}
.detail-card {
margin-top: 2rpx;
padding: 28rpx 26rpx 18rpx;
}
.detail-card__title {
display: block;
font-size: 28rpx;
font-weight: 600;
color: #ffffff;
}
.detail-item {
display: flex;
align-items: flex-start;
justify-content: space-between;
padding: 28rpx 0;
border-top: 1px solid rgba(129, 141, 183, 0.18);
}
.detail-item:first-of-type {
margin-top: 8rpx;
}
.detail-item__main {
flex: 1;
min-width: 0;
}
.detail-item__title {
display: block;
font-size: 28rpx;
font-weight: 500;
color: #ffffff;
}
.detail-item__time {
display: block;
margin-top: 14rpx;
font-size: 22rpx;
color: rgba(158, 170, 204, 0.9);
}
.detail-item__side {
margin-left: 18rpx;
text-align: right;
}
.detail-item__label {
display: block;
font-size: 26rpx;
color: rgba(240, 243, 255, 0.92);
}
.detail-item__amount {
display: block;
margin-top: 10rpx;
font-size: 28rpx;
font-weight: 700;
color: #ff8d62;
}
</style>

View File

@ -0,0 +1,658 @@
<template>
<view class="asset-page asset-theme points-list-page">
<asset-page-shell title="积分转换" />
<view class="asset-scroll points-list-scroll">
<view class="summary-card">
<text class="summary-card__text">
已选择订单
<text class="summary-card__accent asset-number-font">{{
selectedCount
}}</text>
<text class="summary-card__accent asset-number-font">{{
displaySelectedTotal
}}</text>
积分
</text>
</view>
<view class="selection-card">
<text class="selection-card__title">按订单积分区间筛选</text>
<view class="range-tabs">
<view
v-for="tab in rangeTabs"
:key="tab.key"
class="range-tabs__item"
:class="{ 'range-tabs__item--active': activeRange === tab.key }"
@click="activeRange = tab.key"
>
{{ tab.label }}
</view>
</view>
<view v-if="filteredItems.length" class="selection-list">
<view
v-for="item in filteredItems"
:key="item.id"
class="selection-item"
:class="{ 'selection-item--disabled': item.disabled }"
@click="toggleItem(item)"
>
<view
class="selection-item__checkbox"
:class="{
'selection-item__checkbox--checked': isChecked(item),
'selection-item__checkbox--disabled': item.disabled,
}"
>
<text
v-if="isChecked(item)"
class="selection-item__checkbox-mark"
></text
>
</view>
<view class="selection-item__main">
<view class="selection-item__title-row">
<text class="selection-item__title">{{ item.title }}</text>
<view
v-if="item.statusText"
class="selection-item__status"
:class="{
'selection-item__status--warning': item.disabled,
}"
>
{{ item.statusText }}
</view>
</view>
<text class="selection-item__time asset-number-font">{{
item.time
}}</text>
</view>
<view class="selection-item__side">
<text class="selection-item__label">转换积分</text>
<text class="selection-item__amount asset-number-font">{{
item.amount
}}</text>
</view>
</view>
</view>
<view v-else class="selection-empty">
<text class="selection-empty__title">暂无可转换订单</text>
<text class="selection-empty__desc">
当前筛选条件下没有符合条件的积分订单
</text>
</view>
</view>
</view>
<view class="bottom-bar">
<view class="bottom-bar__check-all" @click="toggleAll">
<view
class="bottom-bar__checkbox"
:class="{ 'bottom-bar__checkbox--checked': allChecked }"
>
<text v-if="allChecked" class="bottom-bar__checkbox-mark"></text>
</view>
<text class="bottom-bar__check-all-text">全选</text>
</view>
<view
class="bottom-bar__button"
:class="{ 'bottom-bar__button--disabled': !canSubmit }"
@click="submit"
>
{{ submitting ? "提交中..." : "确认转换" }}
</view>
</view>
</view>
</template>
<script>
import AssetPageShell from "../../components/asset-page-shell.vue";
import {
fetchPointsConvertSelection,
submitAssetPointsConvert,
} from "../../api/assets";
export default {
components: {
AssetPageShell,
},
data() {
return {
hasShown: false,
activeRange: "",
submitting: false,
selectedIds: [],
detail: {
items: [],
},
};
},
computed: {
rangeTabs() {
return [
{
key: "below-20",
label: "20以下",
},
{
key: "20-100",
label: "20-100",
},
{
key: "100-200",
label: "100-200",
},
{
key: "200-500",
label: "200-500",
},
{
key: "500-plus",
label: "500以上",
},
];
},
items() {
return Array.isArray(this.detail.items) ? this.detail.items : [];
},
filteredItems() {
return this.items.filter((item) => this.matchRange(item));
},
selectedItems() {
return this.items.filter(
(item) => !item.disabled && this.selectedIds.indexOf(item.id) > -1,
);
},
selectedCount() {
return this.selectedItems.length;
},
selectedTotal() {
return this.selectedItems.reduce(
(total, item) => total + Number(item.amountValue || 0),
0,
);
},
displaySelectedTotal() {
return this.formatAmount(this.selectedTotal);
},
selectableFilteredItems() {
return this.filteredItems.filter((item) => !item.disabled);
},
allChecked() {
if (!this.selectableFilteredItems.length) {
return false;
}
return this.selectableFilteredItems.every(
(item) => this.selectedIds.indexOf(item.id) > -1,
);
},
canSubmit() {
return this.selectedItems.length > 0 && !this.submitting;
},
},
onLoad() {
this.loadPage(true);
},
onShow() {
if (this.hasShown) {
this.loadPage();
return;
}
this.hasShown = true;
},
methods: {
formatAmount(value) {
const number = Number(value || 0);
if (!Number.isFinite(number)) {
return "0";
}
return Math.round(number)
.toString()
.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
},
matchRange(item) {
const amount = Number(item && item.amountValue ? item.amountValue : 0);
if (!this.activeRange) {
return true;
}
if (this.activeRange === "below-20") {
return amount < 20;
}
if (this.activeRange === "20-100") {
return amount >= 20 && amount < 100;
}
if (this.activeRange === "100-200") {
return amount >= 100 && amount < 200;
}
if (this.activeRange === "200-500") {
return amount >= 200 && amount < 500;
}
if (this.activeRange === "500-plus") {
return amount >= 500;
}
return false;
},
resolveRangeByAmount(amount) {
const numericAmount = Number(amount || 0);
if (numericAmount < 20) {
return "below-20";
}
if (numericAmount < 100) {
return "20-100";
}
if (numericAmount < 200) {
return "100-200";
}
if (numericAmount < 500) {
return "200-500";
}
return "500-plus";
},
isChecked(item) {
return this.selectedIds.indexOf(item.id) > -1;
},
toggleItem(item) {
if (!item || item.disabled) {
return;
}
if (this.isChecked(item)) {
this.selectedIds = this.selectedIds.filter((id) => id !== item.id);
return;
}
this.selectedIds = this.selectedIds.concat(item.id);
},
toggleAll() {
const currentIds = this.selectableFilteredItems.map((item) => item.id);
if (!currentIds.length) {
return;
}
if (this.allChecked) {
this.selectedIds = this.selectedIds.filter(
(id) => currentIds.indexOf(id) === -1,
);
return;
}
this.selectedIds = Array.from(new Set(this.selectedIds.concat(currentIds)));
},
async loadPage(showLoading) {
try {
const result = await fetchPointsConvertSelection(
showLoading
? {
showLoading: true,
loadingText: "加载中",
}
: null,
);
this.detail = result;
const validIds = this.items
.filter((item) => !item.disabled)
.map((item) => item.id);
this.selectedIds = this.selectedIds.filter(
(id) => validIds.indexOf(id) > -1,
);
const rangeKeys = this.rangeTabs.map((item) => item.key);
if (rangeKeys.indexOf(this.activeRange) === -1) {
const firstItem = this.items[0];
this.activeRange = firstItem
? this.resolveRangeByAmount(firstItem.amountValue)
: this.rangeTabs[0].key;
}
} catch (error) {
uni.showToast({
title: error.message || "页面加载失败",
icon: "none",
});
}
},
async submit() {
if (!this.canSubmit) {
uni.showToast({
title: "请选择可转换订单",
icon: "none",
});
return;
}
this.submitting = true;
try {
await submitAssetPointsConvert(
{
ids: this.selectedItems.map((item) => item.sourceId || item.id),
},
{
showLoading: true,
loadingText: "转换中",
},
);
uni.showToast({
title: "转换成功",
icon: "none",
});
setTimeout(() => {
uni.navigateBack();
}, 400);
} catch (error) {
uni.showToast({
title: error.message || "转换失败",
icon: "none",
});
} finally {
this.submitting = false;
}
},
},
};
</script>
<style lang="scss" scoped>
@import "../../styles/tokens.scss";
@import "../../styles/common.scss";
.points-list-page {
min-height: 100vh;
background: #191e32;
}
.points-list-scroll {
min-height: calc(100vh - env(safe-area-inset-top) - 104rpx);
padding: 12rpx 14rpx calc(env(safe-area-inset-bottom) + 150rpx);
}
.summary-card,
.selection-card,
.bottom-bar {
background: #242944;
}
.summary-card,
.selection-card,
.selection-empty {
border-radius: 10rpx;
box-shadow: 0 12rpx 24rpx rgba(8, 13, 30, 0.14);
}
.summary-card {
position: relative;
overflow: hidden;
padding: 28rpx 26rpx;
}
.summary-card::after {
content: "";
position: absolute;
top: -18rpx;
left: 42%;
width: 110rpx;
height: 180rpx;
background: rgba(123, 137, 190, 0.18);
transform: rotate(30deg);
}
.summary-card__text {
position: relative;
z-index: 1;
font-size: 28rpx;
font-weight: 500;
color: #ffffff;
}
.summary-card__accent {
color: #ff8d62;
}
.selection-card {
margin-top: 18rpx;
padding: 28rpx 0 0;
overflow: hidden;
}
.selection-card__title {
display: block;
padding: 0 20rpx;
font-size: 28rpx;
font-weight: 500;
color: #ffffff;
}
.range-tabs {
display: flex;
align-items: center;
margin-top: 26rpx;
padding: 0 18rpx;
border-bottom: 1px solid rgba(129, 141, 183, 0.18);
}
.range-tabs__item {
position: relative;
flex: 1;
padding: 0 4rpx 18rpx;
text-align: center;
font-size: 24rpx;
color: rgba(173, 182, 211, 0.92);
}
.range-tabs__item--active {
color: #ff8d62;
font-weight: 600;
}
.range-tabs__item--active::after {
content: "";
position: absolute;
right: 18rpx;
bottom: 0;
left: 18rpx;
height: 4rpx;
border-radius: 999rpx;
background: #ff8d62;
}
.selection-list {
padding: 0 20rpx;
}
.selection-item {
display: flex;
align-items: flex-start;
padding: 28rpx 0;
border-top: 1px solid rgba(129, 141, 183, 0.18);
}
.selection-item:first-child {
border-top: 0;
}
.selection-item--disabled {
opacity: 0.78;
}
.selection-item__checkbox,
.bottom-bar__checkbox {
display: flex;
align-items: center;
justify-content: center;
width: 32rpx;
height: 32rpx;
border-radius: 6rpx;
border: 2rpx solid rgba(255, 255, 255, 0.9);
background: #ffffff;
flex-shrink: 0;
}
.selection-item__checkbox {
margin-top: 6rpx;
}
.selection-item__checkbox--checked,
.bottom-bar__checkbox--checked {
border-color: #29d39c;
background: #29d39c;
}
.selection-item__checkbox--disabled {
border-color: rgba(205, 212, 235, 0.5);
background: rgba(255, 255, 255, 0.92);
}
.selection-item__checkbox-mark,
.bottom-bar__checkbox-mark {
font-size: 22rpx;
line-height: 1;
color: #ffffff;
}
.selection-item__main {
flex: 1;
min-width: 0;
margin-left: 16rpx;
}
.selection-item__title-row {
display: flex;
align-items: center;
flex-wrap: wrap;
}
.selection-item__title {
font-size: 28rpx;
font-weight: 500;
color: #ffffff;
}
.selection-item__status {
margin-left: 14rpx;
padding: 4rpx 14rpx;
border-radius: 999rpx;
background: rgba(255, 141, 98, 0.14);
font-size: 22rpx;
color: #ff8d62;
}
.selection-item__status--warning {
background: rgba(255, 141, 98, 0.18);
}
.selection-item__time {
display: block;
margin-top: 14rpx;
font-size: 22rpx;
color: rgba(158, 170, 204, 0.9);
}
.selection-item__side {
margin-left: 18rpx;
padding-top: 2rpx;
text-align: right;
}
.selection-item__label {
display: block;
font-size: 26rpx;
color: rgba(240, 243, 255, 0.92);
}
.selection-item__amount {
display: block;
margin-top: 10rpx;
font-size: 28rpx;
font-weight: 700;
color: #ff8d62;
}
.selection-empty {
margin: 22rpx 20rpx 30rpx;
padding: 70rpx 24rpx;
text-align: center;
background: rgba(38, 45, 73, 0.72);
}
.selection-empty__title {
display: block;
font-size: 28rpx;
color: #ffffff;
}
.selection-empty__desc {
display: block;
margin-top: 14rpx;
font-size: 22rpx;
color: rgba(158, 170, 204, 0.86);
}
.bottom-bar {
position: fixed;
right: 0;
bottom: 0;
left: 0;
z-index: 10;
display: flex;
align-items: center;
justify-content: space-between;
padding: 18rpx 20rpx calc(env(safe-area-inset-bottom) + 18rpx);
border-top: 1px solid rgba(129, 141, 183, 0.16);
}
.bottom-bar__check-all {
display: flex;
align-items: center;
}
.bottom-bar__check-all-text {
margin-left: 14rpx;
font-size: 28rpx;
color: #ffffff;
}
.bottom-bar__button {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 170rpx;
height: 78rpx;
padding: 0 34rpx;
border-radius: 999rpx;
background: linear-gradient(135deg, #1ec5ff 0%, #179fe8 100%);
box-shadow: 0 14rpx 24rpx rgba(27, 179, 242, 0.22);
font-size: 32rpx;
font-weight: 600;
color: #ffffff;
}
.bottom-bar__button--disabled {
opacity: 0.5;
}
</style>

View File

@ -1,49 +1,83 @@
<template>
<view class="asset-page asset-theme points-page">
<view class="asset-page asset-theme points-home-page">
<asset-page-shell title="积分转换" />
<view class="asset-scroll points-scroll">
<view class="asset-scroll points-home-scroll">
<view class="hero-card">
<view class="hero-card__content">
<view class="hero-card__coin-row">
<view class="hero-card__coin">
<image
class="hero-card__coin-image"
:src="pageIcon('points')"
src="https://imgs.agrimedia.cn/bm-bmt/j-w.png"
mode="aspectFit"
></image>
</view>
<text class="hero-card__coin-text">积分</text>
</view>
<text class="hero-card__label">可用积分:</text>
<text class="hero-card__value asset-number-font">{{ displayAvailablePoints }}</text>
<text class="hero-card__label">当前可用积分</text>
<view class="hero-card__bottom">
<text class="hero-card__value asset-number-font">{{
detail.availablePoints
}}</text>
<view class="hero-card__button" @click="openConvertList">
积分转换
</view>
</view>
</view>
</view>
<view class="pending-card">
<text class="pending-card__label">未转换积分释放中的积分</text>
<text class="pending-card__value asset-number-font">{{ displayPendingPoints }}</text>
</view>
<view class="tips-block">
<view
v-for="(tip, index) in detail.tips"
:key="tip"
class="tips-block__item"
<picker
class="month-picker"
mode="selector"
:range="monthOptions"
:value="monthIndex"
@change="handleMonthChange"
>
<text class="tips-block__index asset-number-font">{{ index + 1 }}.</text>
<text class="tips-block__text asset-number-font">{{ tip }}</text>
<view class="month-picker__inner">
<text class="month-picker__text">{{
activeMonth
}}</text>
<text class="month-picker__arrow"></text>
</view>
</picker>
<view v-if="filteredRecords.length" class="record-list">
<view
v-for="item in filteredRecords"
:key="item.id + '-' + item.time"
class="record-card"
:class="{ 'record-card--clickable': item.detailAvailable }"
@click="openRecord(item)"
>
<view class="record-card__main">
<text class="record-card__title">{{ item.title }}</text>
<text class="record-card__time asset-number-font">{{
item.time
}}</text>
</view>
<view class="record-card__side">
<view class="record-card__amount-row">
<text
class="record-card__amount asset-number-font"
:class="'record-card__amount--' + item.amountTone"
>
{{ item.amount }}
</text>
<text v-if="item.detailAvailable" class="record-card__arrow"
>&gt;</text
>
</view>
</view>
</view>
</view>
<view class="action-wrap">
<view
class="action-button"
:class="{ 'action-button--disabled': !canSubmit || submitting }"
@click="submit"
>
{{ submitting ? "转换中..." : "转换可用积分" }}
</view>
<view v-else class="empty-card">
<text class="empty-card__title">暂无转换记录</text>
<text class="empty-card__desc">当前月份还没有积分转换明细</text>
</view>
</view>
</view>
@ -51,10 +85,7 @@
<script>
import AssetPageShell from "../../components/asset-page-shell.vue";
import {
fetchPointsConvertDetail,
submitAssetPointsConvert,
} from "../../api/assets";
import { fetchPointsConvertHome } from "../../api/assets";
export default {
components: {
@ -63,16 +94,30 @@ export default {
data() {
return {
hasShown: false,
selectedMonth: "",
detail: {
availablePoints: "0.00",
pendingPoints: "0",
ids: [],
tips: [],
availablePoints: "0",
records: [],
},
submitting: false,
};
},
computed: {
monthOptions() {
return this.buildMonthOptions();
},
monthIndex() {
const currentIndex = this.monthOptions.indexOf(this.activeMonth);
return currentIndex > -1 ? currentIndex : 0;
},
activeMonth() {
return this.selectedMonth || this.currentMonth();
},
filteredRecords() {
return Array.isArray(this.detail.records) ? this.detail.records : [];
},
},
onLoad() {
this.selectedMonth = this.currentMonth();
this.loadPage(true);
},
onShow() {
@ -83,52 +128,45 @@ export default {
this.hasShown = true;
},
computed: {
pendingPointsValue() {
return Number(this.detail.pendingPoints || 0);
},
canSubmit() {
return this.pendingPointsValue > 0;
},
displayAvailablePoints() {
return this.formatAmount(this.detail.availablePoints, 2);
},
displayPendingPoints() {
return this.formatAmount(this.detail.pendingPoints, 0);
},
},
methods: {
pageIcon(type) {
const iconMap = {
points: "https://imgs.agrimedia.cn/bm-bmt/j-w.png",
voucher: "https://imgs.agrimedia.cn/bm-bmt/quan-icon.png",
coupon: "https://imgs.agrimedia.cn/bm-bmt/xiaofei-icon.png",
power: "https://imgs.agrimedia.cn/bm-bmt/s.png",
bmt: "https://imgs.agrimedia.cn/bm-bmt/b.png",
};
return iconMap[type] || "";
currentMonth() {
const currentDate = new Date();
return (
currentDate.getFullYear() +
"-" +
String(currentDate.getMonth() + 1).padStart(2, "0")
);
},
formatAmount(value, digits) {
const number = Number(value || 0);
if (!Number.isFinite(number)) {
return digits > 0 ? Number(0).toFixed(digits) : "0";
buildMonthOptions() {
const currentDate = new Date();
const monthList = [];
for (let index = 0; index < 12; index += 1) {
const cursor = new Date(
currentDate.getFullYear(),
currentDate.getMonth() - index,
1,
);
monthList.push(
cursor.getFullYear() +
"-" +
String(cursor.getMonth() + 1).padStart(2, "0"),
);
}
if (digits > 0) {
const fixed = number.toFixed(digits);
const parts = fixed.split(".");
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
return parts.join(".");
}
return Math.floor(number)
.toString()
.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
return monthList;
},
handleMonthChange(event) {
const nextIndex = Number(
event && event.detail ? event.detail.value : 0,
);
this.selectedMonth = this.monthOptions[nextIndex] || this.activeMonth;
this.loadPage();
},
async loadPage(showLoading) {
try {
this.detail = await fetchPointsConvertDetail(
const result = await fetchPointsConvertHome(
this.activeMonth,
showLoading
? {
showLoading: true,
@ -136,6 +174,8 @@ export default {
}
: null,
);
this.detail = result;
} catch (error) {
uni.showToast({
title: error.message || "页面加载失败",
@ -143,43 +183,30 @@ export default {
});
}
},
async submit() {
if (this.submitting) {
openConvertList() {
uni.navigateTo({
url: "/pages/assets/points-convert-list",
});
},
openRecord(item) {
if (!item || !item.detailAvailable) {
return;
}
if (!this.canSubmit) {
uni.showToast({
title: "暂无可转换积分",
icon: "none",
});
return;
}
const payload = encodeURIComponent(
JSON.stringify({
id: item.id,
sourceId: item.sourceId,
orderSn: item.orderSn,
title: item.title,
amount: item.amount,
time: item.time,
}),
);
this.submitting = true;
try {
const result = await submitAssetPointsConvert({
pendingPoints: this.pendingPointsValue,
ids: this.detail.ids,
}, {
showLoading: true,
loadingText: "转换中",
uni.navigateTo({
url: "/pages/assets/points-convert-detail?payload=" + payload,
});
uni.showToast({
title: result.message || "转换成功",
icon: "none",
});
this.loadPage();
} catch (error) {
uni.showToast({
title: error.message || "转换失败",
icon: "none",
});
} finally {
this.submitting = false;
}
},
},
};
@ -189,41 +216,37 @@ export default {
@import "../../styles/tokens.scss";
@import "../../styles/common.scss";
.points-page {
.points-home-page {
min-height: 100vh;
background: #191e32;
}
.points-scroll {
.points-home-scroll {
min-height: calc(100vh - env(safe-area-inset-top) - 104rpx);
display: flex;
flex-direction: column;
padding: 10rpx 14rpx calc(env(safe-area-inset-bottom) + 36rpx);
}
.hero-card,
.pending-card {
border-radius: 8rpx;
box-shadow: 0 12rpx 24rpx rgba(8, 13, 30, 0.12);
}
.hero-card {
position: relative;
min-height: 240rpx;
padding: 24rpx 28rpx;
overflow: hidden;
min-height: 248rpx;
padding: 24rpx 28rpx;
border-radius: 10rpx;
background: #242944 url("https://imgs.agrimedia.cn/bm-bmt/jifen-header.png")
no-repeat center top / 100% 240rpx;
no-repeat center top / 100% 248rpx;
box-shadow: 0 12rpx 24rpx rgba(8, 13, 30, 0.16);
}
.hero-card__content {
position: relative;
z-index: 1;
display: flex;
flex-direction: column;
}
.hero-card__coin-row {
.hero-card__coin-row,
.hero-card__bottom,
.record-card,
.record-card__amount-row,
.month-picker__inner {
display: flex;
align-items: center;
}
@ -252,86 +275,153 @@ export default {
}
.hero-card__label {
display: block;
margin-top: 38rpx;
font-size: 28rpx;
color: #9ba7ce;
color: rgba(233, 239, 255, 0.9);
}
.hero-card__value {
margin-top: 8rpx;
font-size: 48rpx;
font-weight: 800;
line-height: 1.08;
color: #ffffff;
}
.pending-card {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 20rpx;
padding: 30rpx 26rpx;
background: rgba(59, 49, 67, 0.92);
}
.pending-card__label,
.pending-card__value {
font-size: 26rpx;
font-weight: 600;
color: #ff885d;
}
.pending-card__value {
font-size: 28rpx;
}
.tips-block {
margin-top: 20rpx;
padding: 0 2rpx;
}
.tips-block__item {
display: flex;
align-items: flex-start;
.hero-card__bottom {
margin-top: 10rpx;
}
.tips-block__item:first-child {
margin-top: 0;
.hero-card__value {
max-width: 360rpx;
font-size: 58rpx;
font-weight: 800;
line-height: 1.05;
color: #ffffff;
margin-right: 20rpx;
}
.tips-block__index,
.tips-block__text {
font-size: 24rpx;
font-weight: 400;
line-height: 1.75;
color: rgba(173, 184, 216, 0.82);
}
.tips-block__index {
flex-shrink: 0;
margin-right: 6rpx;
}
.action-wrap {
margin-top: auto;
padding: 120rpx 24rpx 0;
}
.action-button {
display: flex;
.hero-card__button {
display: inline-flex;
align-items: center;
justify-content: center;
height: 90rpx;
min-width: 166rpx;
height: 48rpx;
border-radius: 999rpx;
background: linear-gradient(135deg, #22c2ff 0%, #159de6 100%);
box-shadow: 0 16rpx 30rpx rgba(31, 169, 243, 0.2);
font-size: 32rpx;
font-weight: 700;
background: linear-gradient(135deg, #1ec5ff 0%, #179fe8 100%);
box-shadow: 0 14rpx 24rpx rgba(27, 179, 242, 0.22);
font-size: 26rpx;
font-weight: 500;
color: #ffffff;
}
.action-button--disabled {
opacity: 0.56;
.month-picker {
display: inline-block;
margin-top: 22rpx;
}
.month-picker__inner {
justify-content: space-between;
width: 210rpx;
height: 64rpx;
padding: 0 18rpx;
border-radius: 6rpx;
background: #4a5174;
}
.month-picker__text {
font-size: 26rpx;
color: #ffffff;
}
.month-picker__arrow {
font-size: 18rpx;
color: rgba(255, 255, 255, 0.92);
}
.record-list {
margin-top: 22rpx;
}
.record-card,
.empty-card {
border-radius: 10rpx;
background: #242944;
box-shadow: 0 12rpx 24rpx rgba(8, 13, 30, 0.14);
}
.record-card {
justify-content: space-between;
padding: 26rpx 28rpx;
margin-top: 18rpx;
}
.record-card:first-child {
margin-top: 0;
}
.record-card--clickable {
cursor: pointer;
}
.record-card__main {
flex: 1;
min-width: 0;
}
.record-card__title {
display: block;
font-size: 28rpx;
font-weight: 500;
color: #ffffff;
}
.record-card__time {
display: block;
margin-top: 14rpx;
font-size: 22rpx;
color: rgba(158, 170, 204, 0.9);
}
.record-card__side {
display: flex;
justify-content: flex-end;
margin-left: 20rpx;
}
.record-card__amount-row {
justify-content: flex-end;
}
.record-card__amount {
font-size: 28rpx;
font-weight: 700;
}
.record-card__amount--increase {
color: #ff8b63;
}
.record-card__amount--decrease {
color: #20ddb0;
}
.record-card__arrow {
margin-left: 16rpx;
font-size: 38rpx;
line-height: 1;
color: rgba(255, 255, 255, 0.96);
}
.empty-card {
margin-top: 22rpx;
padding: 60rpx 28rpx;
text-align: center;
}
.empty-card__title {
display: block;
font-size: 28rpx;
color: #ffffff;
}
.empty-card__desc {
display: block;
margin-top: 14rpx;
font-size: 22rpx;
color: rgba(158, 170, 204, 0.86);
}
</style>

View File

@ -1,14 +1,6 @@
<template>
<view class="asset-page asset-theme wallet-page">
<view class="wallet-nav">
<view class="wallet-nav__side">
<view class="wallet-nav__back" @click="handleBack">
<text class="wallet-nav__back-icon"></text>
</view>
</view>
<text class="wallet-nav__title">钱包地址</text>
<view class="wallet-nav__side"></view>
</view>
<asset-page-shell title="钱包地址" />
<view class="asset-scroll wallet-scroll">
<view class="wallet-panel">
@ -80,11 +72,13 @@
</template>
<script>
import AssetPageShell from "../../components/asset-page-shell.vue";
import WalletActionPopup from "../../components/wallet-action-popup.vue";
import { fetchWalletDetail, deleteAssetWallet } from "../../api/assets";
export default {
components: {
AssetPageShell,
WalletActionPopup,
},
data() {
@ -192,40 +186,6 @@ export default {
background: #191e32;
}
.wallet-nav {
display: grid;
grid-template-columns: 120rpx 1fr 120rpx;
align-items: center;
padding: calc(env(safe-area-inset-top) + 12rpx) 14rpx 12rpx;
}
.wallet-nav__side {
display: flex;
align-items: center;
}
.wallet-nav__back {
display: flex;
align-items: center;
justify-content: center;
width: 56rpx;
height: 56rpx;
}
.wallet-nav__back-icon {
margin-top: -4rpx;
font-size: 56rpx;
line-height: 1;
color: #ffffff;
}
.wallet-nav__title {
text-align: center;
font-size: 36rpx;
font-weight: 700;
color: #ffffff;
}
.wallet-scroll {
min-height: calc(100vh - env(safe-area-inset-top) - 104rpx);
display: flex;

View File

@ -25,16 +25,6 @@
class="stat-card"
:class="'stat-card--' + item.accent"
>
<view
class="stat-card__icon-box"
:class="'stat-card__icon-box--' + item.accent"
>
<image
class="stat-card__icon-image"
:src="statIcon(item.key)"
mode="aspectFit"
></image>
</view>
<view class="stat-card__body">
<text class="stat-card__label">{{ item.title }}</text>
<view class="stat-card__value-row">
@ -55,6 +45,7 @@
v-for="item in overview.quickAssets"
:key="item.key"
class="asset-mini-card"
:class="'asset-mini-card--' + item.accent"
@click="openQuickAsset(item)"
>
<image
@ -63,13 +54,6 @@
mode="aspectFill"
></image>
<view class="asset-mini-card__head">
<view class="asset-mini-card__icon">
<image
class="asset-mini-card__icon-image"
:src="quickAssetIcon(item.key)"
mode="aspectFit"
></image>
</view>
<text class="asset-mini-card__label">{{ item.title }}</text>
</view>
<text class="asset-mini-card__value asset-number-font">{{ item.value }}</text>
@ -148,14 +132,6 @@ export default {
});
}
},
statIcon(key) {
const iconMap = {
"wallet-bmt": "https://imgs.agrimedia.cn/bm-bmt/hong-l.png",
ticker: "https://imgs.agrimedia.cn/bm-bmt/hong-r.png",
};
return iconMap[key] || "";
},
quickAssetIcon(key) {
const iconMap = {
points: "https://imgs.agrimedia.cn/bm-bmt/jifen-icon.png",
@ -214,7 +190,7 @@ export default {
points: "/pages/assets/ledger?type=points",
voucher: "/pages/assets/ledger?type=voucher",
coupon: "/pages/assets/ledger?type=coupon",
power: "/pages/assets/ledger?type=power-flow",
power: "/pages/assets/ledger?type=power",
};
const targetUrl = urlMap[item.key];
@ -478,7 +454,9 @@ export default {
align-items: center;
justify-content: space-between;
min-width: 0;
padding: 18rpx 20rpx;
height: 80rpx;
line-height: 80rpx;
padding: 0rpx 20rpx;
border-radius: 12rpx;
box-shadow: 0 12rpx 24rpx rgba(10, 16, 37, 0.16);
background: rgba(35, 45, 79, 0.9);
@ -491,6 +469,22 @@ export default {
height: 100%;
}
.asset-mini-card--blue {
background: linear-gradient(135deg, #10acee 0%, #1aa4ff 100%);
}
.asset-mini-card--green {
background: linear-gradient(135deg, #25d8a4 0%, #36e08f 100%);
}
.asset-mini-card--orange {
background: linear-gradient(135deg, #ff8d58 0%, #ff7d7d 100%);
}
.asset-mini-card--violet {
background: linear-gradient(135deg, #a667f3 0%, #b27cff 100%);
}
.asset-mini-card__head,
.asset-mini-card__value {
position: relative;
@ -499,9 +493,8 @@ export default {
.asset-mini-card__head {
display: flex;
align-items: center;
min-width: 0;
font-size: 26rpx;
align-items: center;
}
.asset-mini-card__icon {