fix: 秀修改bug

This commit is contained in:
whitechiina 2026-04-29 13:59:31 +08:00
parent b2c365ecbf
commit 322651e507
30 changed files with 1632 additions and 239 deletions

View File

@ -112,9 +112,23 @@ function normalizeBalances(data) {
}; };
} }
function toRawDisplayValue(value) {
if (value === undefined || value === null || value === "") {
return "0";
}
return String(value);
}
function buildHomeOverview(balanceData, tickerData) { function buildHomeOverview(balanceData, tickerData) {
const balances = normalizeBalances(balanceData); const balances = normalizeBalances(balanceData);
const ticker = normalizeTicker(tickerData); const ticker = normalizeTicker(tickerData);
const rawQuickAssets = {
points: toRawDisplayValue(balanceData && balanceData.point),
voucher: toRawDisplayValue(balanceData && balanceData.coin),
coupon: toRawDisplayValue(balanceData && balanceData.diamond_balance),
power: toRawDisplayValue(balanceData && balanceData.c_power),
};
return { return {
title: "数字资产", title: "数字资产",
@ -138,26 +152,26 @@ function buildHomeOverview(balanceData, tickerData) {
quickAssets: [ quickAssets: [
{ {
key: "points", key: "points",
title: "积分", title: "可用积分",
value: formatHomeNumber(balances.points, 0), value: rawQuickAssets.points,
accent: "gold", accent: "gold",
}, },
{ {
key: "voucher", key: "voucher",
title: "抵用券", title: "抵用券",
value: formatHomeNumber(balances.voucher, 2), value: rawQuickAssets.voucher,
accent: "rose", accent: "rose",
}, },
{ {
key: "coupon", key: "coupon",
title: "消费券", title: "消费券",
value: formatHomeNumber(balances.coupon, 0), value: rawQuickAssets.coupon,
accent: "teal", accent: "teal",
}, },
{ {
key: "power", key: "power",
title: "算力", title: "算力",
value: formatHomeNumber(balances.power, 0), value: rawQuickAssets.power,
accent: "violet", accent: "violet",
}, },
], ],
@ -274,6 +288,205 @@ function normalizeListData(data) {
return []; return [];
} }
const DEFAULT_LIST_PAGE = 1;
const DEFAULT_LIST_PAGE_SIZE = 10;
function hasPaginationKey(value) {
if (!value || typeof value !== "object") {
return false;
}
return (
value.page !== undefined ||
value.pageSize !== undefined ||
value.page_size !== undefined ||
value.pagesize !== undefined ||
value.size !== undefined ||
value.limit !== undefined
);
}
function normalizePaginationOptions(pagination) {
const source = pagination && typeof pagination === "object" ? pagination : {};
const page = Math.max(
1,
Math.floor(
toNumber(
pickFirstValue(source, ["page", "current_page", "currentPage", "p"]) ||
DEFAULT_LIST_PAGE,
),
),
);
const pageSize = Math.max(
1,
Math.floor(
toNumber(
pickFirstValue(source, [
"pageSize",
"page_size",
"pagesize",
"size",
"limit",
"per_page",
]) || DEFAULT_LIST_PAGE_SIZE,
),
),
);
return {
page: page,
pageSize: pageSize,
};
}
function resolvePagingArguments(paginationOrRequestOptions, requestOptions) {
if (requestOptions || hasPaginationKey(paginationOrRequestOptions)) {
return {
pagination: normalizePaginationOptions(paginationOrRequestOptions),
requestOptions: requestOptions || null,
};
}
return {
pagination: normalizePaginationOptions(null),
requestOptions: paginationOrRequestOptions || null,
};
}
function buildPagingRequestData(baseData, pagination) {
const normalizedPagination = normalizePaginationOptions(pagination);
return Object.assign({}, baseData || {}, {
page: normalizedPagination.page,
page_size: normalizedPagination.pageSize,
});
}
function parseBooleanLike(value) {
if (typeof value === "boolean") {
return value;
}
if (typeof value === "number") {
return value > 0;
}
if (typeof value === "string") {
const normalized = value.trim().toLowerCase();
if (!normalized) {
return null;
}
if (
normalized === "1" ||
normalized === "true" ||
normalized === "yes" ||
normalized === "y"
) {
return true;
}
if (
normalized === "0" ||
normalized === "false" ||
normalized === "no" ||
normalized === "n"
) {
return false;
}
}
return null;
}
function buildPaginationMeta(data, requestPagination) {
const normalizedData = data && typeof data === "object" ? data : {};
const normalizedRequestPagination = normalizePaginationOptions(requestPagination);
const list = normalizeListData(normalizedData);
const page =
Math.floor(
toNumber(
pickFirstValue(normalizedData, [
"page",
"current_page",
"currentPage",
"page_num",
]),
),
) || normalizedRequestPagination.page;
const pageSize =
Math.floor(
toNumber(
pickFirstValue(normalizedData, [
"page_size",
"pagesize",
"size",
"limit",
"per_page",
]),
),
) || normalizedRequestPagination.pageSize;
const total = Math.floor(
toNumber(
pickFirstValue(normalizedData, [
"total",
"count",
"total_count",
"totalCount",
"record_count",
"all_count",
]),
),
);
const lastPage = Math.floor(
toNumber(
pickFirstValue(normalizedData, [
"last_page",
"lastPage",
"page_total",
"total_page",
"pages",
"pageCount",
]),
),
);
const hasMoreRaw = pickFirstValue(normalizedData, [
"has_more",
"hasMore",
"more",
"is_more",
"next_page",
"nextPage",
]);
const hasMoreByRawValue = parseBooleanLike(hasMoreRaw);
let hasMore = hasMoreByRawValue;
if (hasMore === null && lastPage > 0) {
hasMore = page < lastPage;
}
if (hasMore === null && total > 0 && pageSize > 0) {
hasMore = page * pageSize < total;
}
if (hasMore === null) {
hasMore = list.length >= pageSize && list.length > 0;
}
if (!list.length) {
hasMore = false;
}
return {
page: page,
pageSize: pageSize,
total: total,
lastPage: lastPage,
hasMore: Boolean(hasMore),
};
}
function formatTransferRecordNumber(value) { function formatTransferRecordNumber(value) {
const number = toNumber(value); const number = toNumber(value);
return formatHomeNumber(number, Number.isInteger(number) ? 0 : 2); return formatHomeNumber(number, Number.isInteger(number) ? 0 : 2);
@ -352,6 +565,18 @@ function buildRecordId(item, fallbackPrefix) {
); );
} }
function normalizeAvailablePointsText(text) {
const safeText = String(text || "");
if (!safeText) {
return "";
}
return safeText
.replace(/可用积分/g, "__AVAILABLE_POINTS__")
.replace(/积分/g, "可用积分")
.replace(/__AVAILABLE_POINTS__/g, "可用积分");
}
function mapWalletFlowRecords(list, meta) { function mapWalletFlowRecords(list, meta) {
return normalizeListData(list).map(function (item) { return normalizeListData(list).map(function (item) {
const amountValue = pickFirstValue(item, [ const amountValue = pickFirstValue(item, [
@ -363,6 +588,7 @@ function mapWalletFlowRecords(list, meta) {
"money", "money",
"bmt_num", "bmt_num",
"power_num", "power_num",
"gold_num",
"point_num", "point_num",
]); ]);
const direction = resolveRecordDirection(item, amountValue, "收入", "支出"); const direction = resolveRecordDirection(item, amountValue, "收入", "支出");
@ -392,12 +618,27 @@ function mapWalletFlowRecords(list, meta) {
pickFirstValue(item, ["title", "name", "type_name"]) || pickFirstValue(item, ["title", "name", "type_name"]) ||
meta.title || meta.title ||
meta.unit + "记录"; meta.unit + "记录";
const isPointsLedger = String(meta && meta.key ? meta.key : "") === "points";
const displayTitle = isPointsLedger
? normalizeAvailablePointsText(title)
: title;
const displayNoteText = isPointsLedger
? normalizeAvailablePointsText(noteText)
: noteText;
const displayDirection = isPointsLedger
? normalizeAvailablePointsText(direction)
: direction;
const displaySubtitle = orderSn
? "单号 " + orderSn
: displayNoteText || displayDirection;
const displayFeeText =
displayNoteText || (orderSn ? "单号 " + orderSn : meta.subtitle);
return { return {
id: buildRecordId(item, meta.key || "flow"), id: buildRecordId(item, meta.key || "flow"),
sourceId: String(pickFirstValue(item, ["id", "log_id", "uid"]) || ""), sourceId: String(pickFirstValue(item, ["id", "log_id", "uid"]) || ""),
title: title, title: displayTitle,
subtitle: orderSn ? "单号 " + orderSn : noteText || direction, subtitle: displaySubtitle,
time: pickFirstValue(item, [ time: pickFirstValue(item, [
"add_time", "add_time",
"create_time", "create_time",
@ -417,11 +658,11 @@ function mapWalletFlowRecords(list, meta) {
? "剩余" + meta.unit + "" + formatTransferRecordNumber(balanceValue) ? "剩余" + meta.unit + "" + formatTransferRecordNumber(balanceValue)
: "当前" + meta.unit + "流水", : "当前" + meta.unit + "流水",
assetLabel: meta.unit, assetLabel: meta.unit,
feeText: noteText || (orderSn ? "单号 " + orderSn : meta.subtitle), feeText: displayFeeText,
directionLabel: direction, directionLabel: displayDirection,
actionSymbol: directionPresentation.actionSymbol, actionSymbol: directionPresentation.actionSymbol,
orderSn: orderSn, orderSn: orderSn,
noteText: noteText, noteText: displayNoteText,
tag: directionPresentation.income ? "收" : "支", tag: directionPresentation.income ? "收" : "支",
tone: directionPresentation.income ? "success" : "danger", tone: directionPresentation.income ? "success" : "danger",
cardTone: directionPresentation.income ? "success" : "danger", cardTone: directionPresentation.income ? "success" : "danger",
@ -469,7 +710,8 @@ function buildRedeemConsumeText(item, meta) {
]); ]);
if (pointValue !== "") { if (pointValue !== "") {
fragments.push("积分 " + formatTransferRecordNumber(pointValue)); const pointLabel = meta.type === 1 ? "可用积分" : "积分";
fragments.push(pointLabel + " " + formatTransferRecordNumber(pointValue));
} }
if (powerValue !== "") { if (powerValue !== "") {
@ -538,7 +780,19 @@ function mapRedeemRecords(list, meta) {
} }
function getTransferRecordUnit(item) { function getTransferRecordUnit(item) {
return Number(item && item.type) === 0 ? "算力" : "积分"; return Number(item && item.type) === 0 ? "算力" : "可用积分";
}
function normalizeTransferPointsText(text, item) {
if (Number(item && item.type) === 0) {
return String(text || "");
}
const safeText = String(text || "");
return safeText
.replace(/可用积分/g, "__POINTS_LABEL__")
.replace(/积分/g, "可用积分")
.replace(/__POINTS_LABEL__/g, "可用积分");
} }
function getTransferRecordTone(item) { function getTransferRecordTone(item) {
@ -612,9 +866,10 @@ function getTransferRecordBalanceLabel(item) {
function mapTransferRecords(list) { function mapTransferRecords(list) {
return (Array.isArray(list) ? list : []).map(function (item) { return (Array.isArray(list) ? list : []).map(function (item) {
const titleText = item.title || getTransferRecordTitle(item);
return { return {
id: item.order_sn || item.id || String(Math.random()), id: item.order_sn || item.id || String(Math.random()),
title: item.title || getTransferRecordTitle(item), title: normalizeTransferPointsText(titleText, item),
subtitle: item.order_sn ? "单号 " + item.order_sn : getTransferRecordDirection(item), subtitle: item.order_sn ? "单号 " + item.order_sn : getTransferRecordDirection(item),
time: item.add_time || "", time: item.add_time || "",
amount: getTransferRecordAmount(item), amount: getTransferRecordAmount(item),
@ -634,8 +889,18 @@ function mapTransferRecords(list) {
function mapPointsConvertRecords(list) { function mapPointsConvertRecords(list) {
return normalizeListData(list).map(function (item) { return normalizeListData(list).map(function (item) {
const rawTransferPointValue = pickFirstValue(item, [
"transfer_point_num",
"transfer_coin_num",
"point_num",
"coin_num",
"value",
]);
const hasTransferPointValue = rawTransferPointValue !== "";
const amountValue = pickFirstValue(item, [ const amountValue = pickFirstValue(item, [
"gold_num",
"number", "number",
"transfer_point_num",
"transfer_coin_num", "transfer_coin_num",
"point_num", "point_num",
"num", "num",
@ -675,6 +940,10 @@ function mapPointsConvertRecords(list) {
disabled = true; disabled = true;
} }
if (hasTransferPointValue && toNumber(rawTransferPointValue) <= 0) {
disabled = true;
}
return { return {
id: String( id: String(
pickFirstValue(item, ["id", "bill_id", "log_id", "sn"]) || pickFirstValue(item, ["id", "bill_id", "log_id", "sn"]) ||
@ -690,7 +959,7 @@ function mapPointsConvertRecords(list) {
subtitle: subtitle:
"可转数量 " + "可转数量 " +
formatTransferRecordNumber( formatTransferRecordNumber(
pickFirstValue(item, ["transfer_coin_num", "coin_num", "value"]) || 0, rawTransferPointValue || 0,
), ),
time: pickFirstValue(item, [ time: pickFirstValue(item, [
"add_time", "add_time",
@ -701,6 +970,7 @@ function mapPointsConvertRecords(list) {
]), ]),
amount: "+" + formatLedgerRecordNumber(amountValue), amount: "+" + formatLedgerRecordNumber(amountValue),
amountValue: toNumber(amountValue), amountValue: toNumber(amountValue),
transferPointValue: toNumber(rawTransferPointValue),
statusText: statusText, statusText: statusText,
disabled: disabled, disabled: disabled,
balance: balance:
@ -858,21 +1128,36 @@ function buildPointsConvertDetailPayload(data, fallbackRecord) {
const summarySource = const summarySource =
data && typeof data === "object" && !Array.isArray(data) ? data : {}; data && typeof data === "object" && !Array.isArray(data) ? data : {};
const fallback = fallbackRecord || {}; const fallback = fallbackRecord || {};
const totalValue = const summaryTotalValue = pickFirstValue(summarySource, [
pickFirstValue(summarySource, [ "gold_num",
"number", "number",
"num", "num",
"amount", "amount",
"point_num", "point_num",
"transfer_coin_num", "transfer_coin_num",
"total", "total",
]) || sumBy(detailItems, "number"); ]);
const detailFallbackTotal = detailItems.reduce(function (total, item) {
const value = pickFirstValue(item, [
"gold_num",
"number",
"num",
"amount",
"point_num",
"transfer_coin_num",
"value",
]);
return total + toNumber(value);
}, 0);
const totalValue =
summaryTotalValue !== "" ? summaryTotalValue : detailFallbackTotal;
const totalAmountText = const totalAmountText =
totalValue !== "" totalValue !== ""
? "+" + formatLedgerRecordNumber(totalValue) ? "+" + formatLedgerRecordNumber(totalValue)
: fallback.amount || "+0"; : fallback.amount || "+0";
const detailList = detailItems.map(function (item, index) { const detailList = detailItems.map(function (item, index) {
const amountValue = pickFirstValue(item, [ const amountValue = pickFirstValue(item, [
"gold_num",
"number", "number",
"num", "num",
"amount", "amount",
@ -1046,6 +1331,26 @@ async function fetchBmtPowerRateData(requestOptions) {
); );
} }
function resolveRedeemPowerRate(data) {
const sourceValue =
data && typeof data === "object"
? pickFirstValue(data, [
"r",
"rate",
"power_rate",
"powerRate",
"redeem_power_rate",
"redeemRate",
"value",
"num",
"number",
])
: data;
const normalizedRate = toNumber(sourceValue);
return normalizedRate > 0 ? normalizedRate : 0;
}
async function fetchTransferFeeData(requestOptions) { async function fetchTransferFeeData(requestOptions) {
return fetchPayload( return fetchPayload(
createRequestOptions({ createRequestOptions({
@ -1064,13 +1369,13 @@ async function fetchWalletAddressData(requestOptions) {
); );
} }
async function fetchPointsConvertList(requestOptions) { async function fetchPointsConvertList(pagination, requestOptions) {
return fetchPayload( return fetchPayload(
createRequestOptions({ createRequestOptions({
url: serviceConfig.ENDPOINTS.pointsConvertAvailableList, url: serviceConfig.ENDPOINTS.pointsConvertAvailableList,
data: { data: buildPagingRequestData({
interval: serviceConfig.POINTS_CONVERT_INTERVAL, interval: serviceConfig.POINTS_CONVERT_INTERVAL,
}, }, pagination),
}, requestOptions), }, requestOptions),
"积分转换列表加载失败", "积分转换列表加载失败",
); );
@ -1088,34 +1393,35 @@ async function fetchPointsConvertHistoryListData(month, requestOptions) {
); );
} }
async function fetchTransferLedgerData(requestOptions) { async function fetchTransferLedgerData(pagination, requestOptions) {
return fetchPayload( return fetchPayload(
createRequestOptions({ createRequestOptions({
url: serviceConfig.ENDPOINTS.transferLedger, url: serviceConfig.ENDPOINTS.transferLedger,
data: buildPagingRequestData({}, pagination),
}, requestOptions), }, requestOptions),
"转赠记录加载失败", "转赠记录加载失败",
); );
} }
async function fetchWalletFlowListData(flowType, requestOptions) { async function fetchWalletFlowListData(flowType, pagination, requestOptions) {
return fetchPayload( return fetchPayload(
createRequestOptions({ createRequestOptions({
url: serviceConfig.ENDPOINTS.walletFlowList, url: serviceConfig.ENDPOINTS.walletFlowList,
data: { data: buildPagingRequestData({
type: flowType, type: flowType,
}, }, pagination),
}, requestOptions), }, requestOptions),
"资产流水加载失败", "资产流水加载失败",
); );
} }
async function fetchRedeemRecordListData(redeemType, requestOptions) { async function fetchRedeemRecordListData(redeemType, pagination, requestOptions) {
return fetchPayload( return fetchPayload(
createRequestOptions({ createRequestOptions({
url: serviceConfig.ENDPOINTS.redeemRecordList, url: serviceConfig.ENDPOINTS.redeemRecordList,
data: { data: buildPagingRequestData({
type: redeemType, type: redeemType,
}, }, pagination),
}, requestOptions), }, requestOptions),
"兑换记录加载失败", "兑换记录加载失败",
); );
@ -1158,12 +1464,24 @@ export async function fetchPointsConvertHome(month, requestOptions) {
}; };
} }
export async function fetchPointsConvertSelection(requestOptions) { export async function fetchPointsConvertSelection(
const data = await fetchPointsConvertList(requestOptions); paginationOrRequestOptions,
requestOptions,
) {
const params = resolvePagingArguments(
paginationOrRequestOptions,
requestOptions,
);
const data = await fetchPointsConvertList(
params.pagination,
params.requestOptions,
);
const items = mapPointsConvertRecords(data); const items = mapPointsConvertRecords(data);
const pagination = buildPaginationMeta(data, params.pagination);
return { return {
items: items, items: items,
pagination: pagination,
}; };
} }
@ -1358,14 +1676,14 @@ export async function fetchBmtExchangeDetail(requestOptions) {
]); ]);
const ticker = normalizeTicker(result[0]); const ticker = normalizeTicker(result[0]);
const balances = normalizeBalances(result[1]); const balances = normalizeBalances(result[1]);
const powerRate = toNumber(result[2]); const powerRate = resolveRedeemPowerRate(result[2]);
return { return {
ticker: ticker, ticker: ticker,
powerRate: powerRate, powerRate: powerRate,
balances: { balances: {
points: toFixedNumber(balances.points, 0), points: toFixedNumber(balances.points, 0),
power: toFixedNumber(balances.power, 2), power: toFixedNumber(balances.power, 0),
bmt: toFixedNumber(balances.bmt, 2), bmt: toFixedNumber(balances.bmt, 2),
voucher: toFixedNumber(balances.voucher, 2), voucher: toFixedNumber(balances.voucher, 2),
coupon: toFixedNumber(balances.coupon, 2), coupon: toFixedNumber(balances.coupon, 2),
@ -1451,34 +1769,47 @@ export async function submitAssetWithdraw(payload, requestOptions) {
}; };
} }
export async function fetchLedgerDetail(type, requestOptions) { export async function fetchLedgerDetail(
type,
paginationOrRequestOptions,
requestOptions,
) {
const params = resolvePagingArguments(
paginationOrRequestOptions,
requestOptions,
);
const pagination = params.pagination;
const mergedRequestOptions = params.requestOptions;
if (type === "transfer") { if (type === "transfer") {
const data = await fetchTransferLedgerData(requestOptions); const data = await fetchTransferLedgerData(pagination, mergedRequestOptions);
return { return {
type: type, type: type,
title: "转赠记录", title: "转赠记录",
subtitle: "积分与算力转赠流水", subtitle: "可用积分与算力转赠流水",
records: mapTransferRecords(data && data.list), records: mapTransferRecords(normalizeListData(data)),
pagination: buildPaginationMeta(data, pagination),
}; };
} }
if (type === "points") { if (type === "points") {
const data = await fetchWalletFlowListData(0, requestOptions); const data = await fetchWalletFlowListData(0, pagination, mergedRequestOptions);
return { return {
type: type, type: type,
title: "我的积分", title: "可用积分记录",
subtitle: "可用积分收支记录", subtitle: "可用积分收支记录",
records: mapWalletFlowRecords(data, { records: mapWalletFlowRecords(data, {
key: "points", key: "points",
unit: "积分", unit: "可用积分",
title: "积分记录", title: "可用积分记录",
subtitle: "可用积分流水", subtitle: "可用积分流水",
}), }),
pagination: buildPaginationMeta(data, pagination),
}; };
} }
if (type === "power-flow") { if (type === "power-flow") {
const data = await fetchWalletFlowListData(1, requestOptions); const data = await fetchWalletFlowListData(1, pagination, mergedRequestOptions);
return { return {
type: type, type: type,
title: "算力记录", title: "算力记录",
@ -1489,11 +1820,12 @@ export async function fetchLedgerDetail(type, requestOptions) {
title: "算力记录", title: "算力记录",
subtitle: "算力流水", subtitle: "算力流水",
}), }),
pagination: buildPaginationMeta(data, pagination),
}; };
} }
if (type === "power") { if (type === "power") {
const data = await fetchRedeemRecordListData(0, requestOptions); const data = await fetchRedeemRecordListData(0, pagination, mergedRequestOptions);
return { return {
type: type, type: type,
title: "兑换记录", title: "兑换记录",
@ -1504,13 +1836,21 @@ export async function fetchLedgerDetail(type, requestOptions) {
unit: "算力", unit: "算力",
title: "兑换算力", title: "兑换算力",
subtitle: "算力兑换", subtitle: "算力兑换",
amountKeys: ["power", "power_num", "c_power", "redeem_power", "num", "number"], amountKeys: [
"num",
"number",
"power",
"power_num",
"c_power",
"redeem_power",
],
}), }),
pagination: buildPaginationMeta(data, pagination),
}; };
} }
if (type === "bmt") { if (type === "bmt") {
const data = await fetchRedeemRecordListData(1, requestOptions); const data = await fetchRedeemRecordListData(1, pagination, mergedRequestOptions);
return { return {
type: type, type: type,
title: "兑换记录", title: "兑换记录",
@ -1523,11 +1863,12 @@ export async function fetchLedgerDetail(type, requestOptions) {
subtitle: "BMT兑换", subtitle: "BMT兑换",
amountKeys: ["bmt", "bmt_num", "redeem_bmt", "num", "number"], amountKeys: ["bmt", "bmt_num", "redeem_bmt", "num", "number"],
}), }),
pagination: buildPaginationMeta(data, pagination),
}; };
} }
if (type === "withdraw") { if (type === "withdraw") {
const data = await fetchWalletFlowListData(2, requestOptions); const data = await fetchWalletFlowListData(2, pagination, mergedRequestOptions);
return { return {
type: type, type: type,
title: "提取记录", title: "提取记录",
@ -1538,12 +1879,21 @@ export async function fetchLedgerDetail(type, requestOptions) {
title: "BMT记录", title: "BMT记录",
subtitle: "BMT流水", subtitle: "BMT流水",
}), }),
pagination: buildPaginationMeta(data, pagination),
}; };
} }
const homeData = await fetchHomeBalanceData(requestOptions); const homeData = await fetchHomeBalanceData(mergedRequestOptions);
const balances = normalizeBalances(homeData); const balances = normalizeBalances(homeData);
return buildDefaultLedger(type, balances); return Object.assign({}, buildDefaultLedger(type, balances), {
pagination: {
page: pagination.page,
pageSize: pagination.pageSize,
total: 0,
lastPage: 0,
hasMore: false,
},
});
} }
export async function fetchWalletDetail(requestOptions) { export async function fetchWalletDetail(requestOptions) {

View File

@ -46,12 +46,14 @@
<view <view
v-if="showCancel" v-if="showCancel"
class="popup-panel__btn popup-panel__btn--ghost" class="popup-panel__btn popup-panel__btn--ghost"
style="height: 64rpx"
@click="$emit('cancel')" @click="$emit('cancel')"
> >
{{ cancelText }} {{ cancelText }}
</view> </view>
<view <view
class="popup-panel__btn popup-panel__btn--primary" class="popup-panel__btn popup-panel__btn--primary"
style="height: 64rpx"
@click="$emit('confirm')" @click="$emit('confirm')"
> >
{{ confirmText }} {{ confirmText }}
@ -163,18 +165,28 @@ export default {
} }
.popup-panel__head { .popup-panel__head {
position: relative;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: center;
min-height: 44rpx;
} }
.popup-panel__title { .popup-panel__title {
display: block;
width: 100%;
padding: 0 56rpx;
font-size: 32rpx; font-size: 32rpx;
font-weight: 500; font-weight: 500;
text-align: center;
color: #ffffff; color: #ffffff;
} }
.popup-panel__close { .popup-panel__close {
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@ -218,7 +230,7 @@ export default {
.popup-panel__description { .popup-panel__description {
display: block; display: block;
margin-top: 34rpx; margin-top: 34rpx;
font-size: 20rpx; font-size: 26rpx;
line-height: 1.7; line-height: 1.7;
text-align: center; text-align: center;
color: rgba(201, 209, 233, 0.86); color: rgba(201, 209, 233, 0.86);
@ -244,14 +256,14 @@ export default {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
flex: 1; flex: 1;
height: 72rpx; height: 64rpx;
border-radius: 12rpx; border-radius: 12rpx;
font-size: 24rpx; font-size: 28rpx;
font-weight: 700; font-weight: 700;
} }
.popup-panel__footer--single .popup-panel__btn { .popup-panel__footer--single .popup-panel__btn {
flex: 0 0 240rpx; flex: 0 0 232rpx;
} }
.popup-panel__btn--ghost { .popup-panel__btn--ghost {

View File

@ -94,7 +94,8 @@ export default {
.wallet-dialog__description { .wallet-dialog__description {
display: block; display: block;
margin-top: 20rpx; margin-top: 20rpx;
font-size: 22rpx; font-size: 26rpx !important;
font-weight: 400;
line-height: 1.7; line-height: 1.7;
color: rgba(201, 209, 233, 0.82); color: rgba(201, 209, 233, 0.82);
} }

View File

@ -72,7 +72,7 @@
"h5" : { "h5" : {
"router" : { "router" : {
"base" : "/bmt/", "base" : "/bmt/",
"mode" : "hash" "mode" : "history"
} }
} }
} }

View File

@ -28,7 +28,7 @@
:src="pageIcon('voucher')" :src="pageIcon('voucher')"
mode="aspectFit" mode="aspectFit"
></image> ></image>
<text class="info-row__label">我的抵用券</text> <text class="info-row__label">我的算力</text>
</view> </view>
<text class="info-row__value asset-number-font">{{ displayVoucher }}</text> <text class="info-row__value asset-number-font">{{ displayVoucher }}</text>
</view> </view>
@ -40,7 +40,7 @@
:src="pageIcon('coupon')" :src="pageIcon('coupon')"
mode="aspectFit" mode="aspectFit"
></image> ></image>
<text class="info-row__label">我的消费券</text> <text class="info-row__label">可用积分</text>
</view> </view>
<text class="info-row__value asset-number-font">{{ displayCoupon }}</text> <text class="info-row__value asset-number-font">{{ displayCoupon }}</text>
</view> </view>
@ -85,7 +85,7 @@
placeholder="请输入积分数量" placeholder="请输入积分数量"
placeholder-class="form-input__placeholder" placeholder-class="form-input__placeholder"
/> />
<text class="form-input__suffix">积分</text> <text class="form-input__suffix">可用积分</text>
</view> </view>
<view class="preview-card"> <view class="preview-card">
@ -138,15 +138,15 @@
@confirm="submit" @confirm="submit"
> >
<view class="popup-line"> <view class="popup-line">
<text class="meta-pair__label">输入积分</text> <text class="meta-pair__label">预估兑换BMT</text>
<text class="meta-pair__value asset-number-font">{{ formatAmount(pointsValue, 0) }}</text>
</view>
<view class="popup-line">
<text class="meta-pair__label">预估兑换BMT</text>
<text class="meta-pair__value asset-number-font">{{ estimateBmt }}</text> <text class="meta-pair__value asset-number-font">{{ estimateBmt }}</text>
</view> </view>
<view class="popup-line"> <view class="popup-line">
<text class="meta-pair__label">消耗算力</text> <text class="meta-pair__label">消耗可用积分</text>
<text class="meta-pair__value asset-number-font">{{ formatAmount(pointsValue, 0) }}</text>
</view>
<view class="popup-line">
<text class="meta-pair__label">消耗算力</text>
<text class="meta-pair__value asset-number-font">{{ powerCost }}</text> <text class="meta-pair__value asset-number-font">{{ powerCost }}</text>
</view> </view>
</asset-confirm-popup> </asset-confirm-popup>
@ -215,12 +215,16 @@ export default {
}, },
allEstimateBmt() { allEstimateBmt() {
const pointsLimit = Number(this.detail.balances.points || 0); const pointsLimit = Number(this.detail.balances.points || 0);
const powerLimit = this.powerRateNumber const powerLimit = Number(this.detail.balances.power || 0);
? Number(this.detail.balances.power || 0) / this.powerRateNumber
: pointsLimit;
const available = Math.max(0, Math.min(pointsLimit, powerLimit || 0));
return this.formatAmount(available, 0); if (!this.powerRateNumber) {
return "0";
}
const maxByPower = powerLimit / this.powerRateNumber;
const available = Math.max(0, Math.min(pointsLimit, maxByPower || 0));
return this.formatAmount(available, 2);
}, },
priceNumber() { priceNumber() {
return Number( return Number(
@ -228,13 +232,13 @@ export default {
); );
}, },
displayBmt() { displayBmt() {
return this.formatAmount(this.detail.balances.bmt, 2); return this.detail.balances.bmt;
}, },
displayVoucher() { displayVoucher() {
return this.formatAmount(this.detail.balances.voucher, 0); return this.detail.balances.power;
}, },
displayCoupon() { displayCoupon() {
return this.formatAmount(this.detail.balances.coupon, 2); return this.detail.balances.points;
}, },
displayPrice() { displayPrice() {
return this.priceNumber ? this.priceNumber.toFixed(2) : "0.00"; return this.priceNumber ? this.priceNumber.toFixed(2) : "0.00";

View File

@ -38,7 +38,7 @@
}}</text> }}</text>
<text <text
v-if="item.orderSn || item.subtitle" v-if="item.orderSn || item.subtitle"
class="transfer-record-card__order asset-number-font" class="transfer-record-card__order"
> >
{{ item.orderSn ? "单号 " + item.orderSn : item.subtitle }} {{ item.orderSn ? "单号 " + item.orderSn : item.subtitle }}
</text> </text>
@ -144,6 +144,10 @@
<asset-record-list :list="ledger.records || []" /> <asset-record-list :list="ledger.records || []" />
</view> </view>
</template> </template>
<view v-if="showLoadMoreState" class="load-more-state">
<text class="load-more-state__text asset-number-font">{{ loadMoreText }}</text>
</view>
</view> </view>
</view> </view>
</template> </template>
@ -168,6 +172,10 @@ export default {
tabs: [], tabs: [],
}, },
activeTab: "", activeTab: "",
page: 1,
pageSize: 20,
hasMore: false,
loadingMore: false,
}; };
}, },
computed: { computed: {
@ -192,6 +200,20 @@ export default {
transferRecords() { transferRecords() {
return Array.isArray(this.ledger.records) ? this.ledger.records : []; return Array.isArray(this.ledger.records) ? this.ledger.records : [];
}, },
showLoadMoreState() {
return this.isTransferLedger && this.transferRecords.length > 0;
},
loadMoreText() {
if (this.loadingMore) {
return "加载中...";
}
if (this.hasMore) {
return "上拉加载更多";
}
return "没有更多了";
},
pageMark() { pageMark() {
const markMap = { const markMap = {
transfer: "⇄", transfer: "⇄",
@ -207,30 +229,133 @@ export default {
}, },
onLoad(options) { onLoad(options) {
this.type = (options && options.type) || "transfer"; this.type = (options && options.type) || "transfer";
this.loadPage(true); this.resetPagingState();
this.loadPage(true, 1);
},
onReachBottom() {
this.loadMore();
}, },
methods: { methods: {
async loadPage(showLoading) { resetPagingState() {
this.page = 1;
this.hasMore = false;
this.loadingMore = false;
},
updatePaging(result, requestedPage, receivedLength) {
const pagination =
result && result.pagination && typeof result.pagination === "object"
? result.pagination
: null;
const nextPage =
Number(pagination && pagination.page) ||
Number(requestedPage) ||
this.page ||
1;
this.page = nextPage;
if (pagination && typeof pagination.hasMore === "boolean") {
this.hasMore = pagination.hasMore;
return;
}
this.hasMore = receivedLength >= this.pageSize;
},
mergeRecords(currentRecords, nextRecords) {
const mergedMap = {};
const mergedList = [];
const appendItem = (item) => {
const key =
String(item && item.id ? item.id : "").trim() ||
[
item && item.orderSn,
item && item.time,
item && item.title,
item && item.amount,
]
.map((value) => String(value || ""))
.join("-");
if (!key || mergedMap[key]) {
return;
}
mergedMap[key] = true;
mergedList.push(item);
};
(Array.isArray(currentRecords) ? currentRecords : []).forEach(appendItem);
(Array.isArray(nextRecords) ? nextRecords : []).forEach(appendItem);
return mergedList;
},
async loadPage(showLoading, targetPage) {
const page = Number(targetPage || 1);
const isLoadMore = page > 1;
if (isLoadMore && this.loadingMore) {
return;
}
if (isLoadMore) {
this.loadingMore = true;
}
try { try {
const result = await fetchLedgerDetail( const requestOptions =
this.type, !isLoadMore && showLoading
showLoading
? { ? {
showLoading: true, showLoading: true,
loadingText: "加载中", loadingText: "加载中",
} }
: null, : null;
const result = await fetchLedgerDetail(
this.type,
{
page: page,
pageSize: this.pageSize,
},
requestOptions,
); );
this.ledger = result;
if (result.tabs && result.tabs.length) { const incomingRecords = Array.isArray(result.records) ? result.records : [];
this.activeTab = result.tabs[0].key; const currentRecords = Array.isArray(this.ledger.records)
? this.ledger.records
: [];
const mergedRecords = isLoadMore
? this.mergeRecords(currentRecords, incomingRecords)
: incomingRecords;
this.ledger = Object.assign({}, this.ledger, result, {
records: mergedRecords,
});
if (!isLoadMore && result.tabs && result.tabs.length) {
this.activeTab = result.tabs[0].key || "";
}
this.updatePaging(result, page, incomingRecords.length);
if (isLoadMore && mergedRecords.length <= currentRecords.length) {
this.hasMore = false;
} }
} catch (error) { } catch (error) {
uni.showToast({ uni.showToast({
title: error.message || "记录加载失败", title: error.message || (isLoadMore ? "加载更多失败" : "记录加载失败"),
icon: "none", icon: "none",
}); });
} finally {
if (isLoadMore) {
this.loadingMore = false;
} }
}
},
loadMore() {
if (!this.isTransferLedger || this.loadingMore || !this.hasMore) {
return;
}
this.loadPage(false, this.page + 1);
}, },
}, },
}; };
@ -484,4 +609,15 @@ export default {
line-height: 1.6; line-height: 1.6;
color: rgba(188, 197, 223, 0.78); color: rgba(188, 197, 223, 0.78);
} }
.load-more-state {
display: flex;
justify-content: center;
padding: 26rpx 0 8rpx;
}
.load-more-state__text {
font-size: 22rpx;
color: rgba(173, 182, 211, 0.9);
}
</style> </style>

View File

@ -15,6 +15,15 @@
}}</text> }}</text>
积分 积分
</text> </text>
<text
v-if="showTransferPointEstimate"
class="summary-card__extra"
>
可用积分预估值
<text class="summary-card__accent asset-number-font">{{
displayTransferPointEstimate
}}</text>
</text>
</view> </view>
<view class="selection-card"> <view class="selection-card">
@ -73,8 +82,8 @@
}}</text> }}</text>
</view> </view>
<view class="selection-item__side"> <view class="selection-item__side" style="display: flex">
<text class="selection-item__label">转换积分</text> <text class="selection-item__label">积分</text>
<text class="selection-item__amount asset-number-font">{{ <text class="selection-item__amount asset-number-font">{{
item.amount item.amount
}}</text> }}</text>
@ -88,6 +97,10 @@
当前筛选条件下没有符合条件的积分订单 当前筛选条件下没有符合条件的积分订单
</text> </text>
</view> </view>
<view v-if="showLoadMoreState" class="list-load-more">
<text class="list-load-more__text asset-number-font">{{ loadMoreText }}</text>
</view>
</view> </view>
</view> </view>
@ -105,15 +118,45 @@
<view <view
class="bottom-bar__button" class="bottom-bar__button"
:class="{ 'bottom-bar__button--disabled': !canSubmit }" :class="{ 'bottom-bar__button--disabled': !canSubmit }"
@click="submit" @click="openConfirm"
> >
{{ submitting ? "提交中..." : "确认转换" }} {{ submitting ? "提交中..." : "确认转换" }}
</view> </view>
</view> </view>
<asset-confirm-popup
class="points-confirm-popup"
:visible="confirmVisible"
title="兑换可用积分"
:confirm-text="submitting ? '提交中...' : '确认'"
cancel-text="取消"
:show-close="false"
:close-on-mask="false"
@cancel="handleConfirmCancel"
@confirm="submit"
>
<view class="points-confirm">
<text class="points-confirm__line">
确认使用
<text class="points-confirm__number asset-number-font">{{
displaySelectedTotal
}}</text>
积分 兑换
<text class="points-confirm__number asset-number-font">{{
displaySelectedTotal
}}</text>
可用积分吗
</text>
<text class="points-confirm__note">
积分成功兑换可用积分不能再换回积分
</text>
</view>
</asset-confirm-popup>
</view> </view>
</template> </template>
<script> <script>
import AssetConfirmPopup from "../../components/asset-confirm-popup.vue";
import AssetPageShell from "../../components/asset-page-shell.vue"; import AssetPageShell from "../../components/asset-page-shell.vue";
import { import {
fetchPointsConvertSelection, fetchPointsConvertSelection,
@ -122,6 +165,7 @@ import {
export default { export default {
components: { components: {
AssetConfirmPopup,
AssetPageShell, AssetPageShell,
}, },
data() { data() {
@ -129,10 +173,15 @@ export default {
hasShown: false, hasShown: false,
activeRange: "", activeRange: "",
submitting: false, submitting: false,
confirmVisible: false,
selectedIds: [], selectedIds: [],
detail: { detail: {
items: [], items: [],
}, },
page: 1,
pageSize: 20,
hasMore: false,
loadingMore: false,
}; };
}, },
computed: { computed: {
@ -183,6 +232,18 @@ export default {
displaySelectedTotal() { displaySelectedTotal() {
return this.formatAmount(this.selectedTotal); return this.formatAmount(this.selectedTotal);
}, },
transferPointEstimate() {
return this.selectedItems.reduce(
(total, item) => total + Number(item.transferPointValue || 0),
0,
);
},
displayTransferPointEstimate() {
return this.formatAmount(this.transferPointEstimate);
},
showTransferPointEstimate() {
return this.transferPointEstimate > 0;
},
selectableFilteredItems() { selectableFilteredItems() {
return this.filteredItems.filter((item) => !item.disabled); return this.filteredItems.filter((item) => !item.disabled);
}, },
@ -198,19 +259,85 @@ export default {
canSubmit() { canSubmit() {
return this.selectedItems.length > 0 && !this.submitting; return this.selectedItems.length > 0 && !this.submitting;
}, },
showLoadMoreState() {
return this.items.length > 0;
},
loadMoreText() {
if (this.loadingMore) {
return "加载中...";
}
if (this.hasMore) {
return "上拉加载更多";
}
return "没有更多了";
},
}, },
onLoad() { onLoad() {
this.loadPage(true); this.resetPagingState();
this.loadPage(true, 1);
}, },
onShow() { onShow() {
if (this.hasShown) { if (this.hasShown) {
this.loadPage(); this.resetPagingState();
this.loadPage(false, 1);
return; return;
} }
this.hasShown = true; this.hasShown = true;
}, },
onReachBottom() {
this.loadMore();
},
methods: { methods: {
resetPagingState() {
this.page = 1;
this.hasMore = false;
this.loadingMore = false;
},
updatePaging(result, requestedPage, receivedLength) {
const pagination =
result && result.pagination && typeof result.pagination === "object"
? result.pagination
: null;
this.page =
Number(pagination && pagination.page) || Number(requestedPage) || this.page || 1;
if (pagination && typeof pagination.hasMore === "boolean") {
this.hasMore = pagination.hasMore;
return;
}
this.hasMore = receivedLength >= this.pageSize;
},
mergeItems(currentItems, nextItems) {
const mergedMap = {};
const mergedList = [];
const appendItem = (item) => {
const key =
String(item && item.id ? item.id : "").trim() ||
[
item && item.orderSn,
item && item.time,
item && item.title,
item && item.amount,
]
.map((value) => String(value || ""))
.join("-");
if (!key || mergedMap[key]) {
return;
}
mergedMap[key] = true;
mergedList.push(item);
};
(Array.isArray(currentItems) ? currentItems : []).forEach(appendItem);
(Array.isArray(nextItems) ? nextItems : []).forEach(appendItem);
return mergedList;
},
formatAmount(value) { formatAmount(value) {
const number = Number(value || 0); const number = Number(value || 0);
if (!Number.isFinite(number)) { if (!Number.isFinite(number)) {
@ -302,18 +429,43 @@ export default {
this.selectedIds = Array.from(new Set(this.selectedIds.concat(currentIds))); this.selectedIds = Array.from(new Set(this.selectedIds.concat(currentIds)));
}, },
async loadPage(showLoading) { async loadPage(showLoading, targetPage) {
const page = Number(targetPage || 1);
const isLoadMore = page > 1;
if (isLoadMore && this.loadingMore) {
return;
}
if (isLoadMore) {
this.loadingMore = true;
}
try { try {
const result = await fetchPointsConvertSelection( const requestOptions =
showLoading !isLoadMore && showLoading
? { ? {
showLoading: true, showLoading: true,
loadingText: "加载中", loadingText: "加载中",
} }
: null, : null;
const result = await fetchPointsConvertSelection(
{
page: page,
pageSize: this.pageSize,
},
requestOptions,
); );
this.detail = result; const incomingItems = Array.isArray(result.items) ? result.items : [];
const previousCount = this.items.length;
const mergedItems = isLoadMore
? this.mergeItems(this.items, incomingItems)
: incomingItems;
this.detail = Object.assign({}, this.detail, result, {
items: mergedItems,
});
const validIds = this.items const validIds = this.items
.filter((item) => !item.disabled) .filter((item) => !item.disabled)
.map((item) => item.id); .map((item) => item.id);
@ -328,14 +480,53 @@ export default {
? this.resolveRangeByAmount(firstItem.amountValue) ? this.resolveRangeByAmount(firstItem.amountValue)
: this.rangeTabs[0].key; : this.rangeTabs[0].key;
} }
this.updatePaging(result, page, incomingItems.length);
if (isLoadMore && mergedItems.length <= previousCount) {
this.hasMore = false;
}
} catch (error) { } catch (error) {
uni.showToast({ uni.showToast({
title: error.message || "页面加载失败", title: error.message || (isLoadMore ? "加载更多失败" : "页面加载失败"),
icon: "none", icon: "none",
}); });
} finally {
if (isLoadMore) {
this.loadingMore = false;
}
} }
}, },
loadMore() {
if (this.loadingMore || !this.hasMore) {
return;
}
this.loadPage(false, this.page + 1);
},
openConfirm() {
if (!this.canSubmit) {
uni.showToast({
title: "请选择可转换订单",
icon: "none",
});
return;
}
this.confirmVisible = true;
},
handleConfirmCancel() {
if (this.submitting) {
return;
}
this.confirmVisible = false;
},
async submit() { async submit() {
if (this.submitting) {
return;
}
if (!this.canSubmit) { if (!this.canSubmit) {
uni.showToast({ uni.showToast({
title: "请选择可转换订单", title: "请选择可转换订单",
@ -357,6 +548,7 @@ export default {
}, },
); );
this.confirmVisible = false;
uni.showToast({ uni.showToast({
title: "转换成功", title: "转换成功",
icon: "none", icon: "none",
@ -415,11 +607,11 @@ export default {
content: ""; content: "";
position: absolute; position: absolute;
top: -18rpx; top: -18rpx;
left: 42%; left: -260rpx;
width: 110rpx; width: 600rpx;
height: 180rpx; height: 180rpx;
background: rgba(123, 137, 190, 0.18); background: rgba(123, 137, 190, 0.18);
transform: rotate(30deg); transform: rotate(130deg);
} }
.summary-card__text { .summary-card__text {
@ -434,6 +626,16 @@ export default {
color: #ff8d62; color: #ff8d62;
} }
.summary-card__extra {
position: relative;
z-index: 1;
display: block;
margin-top: 12rpx;
font-size: 26rpx;
font-weight: 500;
color: rgba(236, 242, 255, 0.92);
}
.selection-card { .selection-card {
margin-top: 18rpx; margin-top: 18rpx;
padding: 28rpx 0 0; padding: 28rpx 0 0;
@ -613,6 +815,17 @@ export default {
color: rgba(158, 170, 204, 0.86); color: rgba(158, 170, 204, 0.86);
} }
.list-load-more {
display: flex;
justify-content: center;
padding: 12rpx 0 22rpx;
}
.list-load-more__text {
font-size: 22rpx;
color: rgba(173, 182, 211, 0.9);
}
.bottom-bar { .bottom-bar {
position: fixed; position: fixed;
right: 0; right: 0;
@ -655,4 +868,30 @@ export default {
.bottom-bar__button--disabled { .bottom-bar__button--disabled {
opacity: 0.5; opacity: 0.5;
} }
.points-confirm {
padding: 6rpx 2rpx 4rpx;
}
.points-confirm__line {
display: block;
font-size: 30rpx;
line-height: 1.9;
text-align: center;
color: #edf2ff;
}
.points-confirm__number {
margin: 0 6rpx;
color: #ff8d62;
font-weight: 700;
}
.points-confirm__note {
display: block;
margin-top: 18rpx;
font-size: 24rpx;
line-height: 1.7;
color: rgba(186, 194, 218, 0.9);
}
</style> </style>

View File

@ -71,7 +71,7 @@
<view class="input-bar recipient-search"> <view class="input-bar recipient-search">
<input <input
v-model="searchId" v-model="searchId"
class="input-bar__field asset-number-font" class="input-bar__field"
type="number" type="number"
placeholder="请输入被赠人ID" placeholder="请输入被赠人ID"
placeholder-class="input-bar__placeholder" placeholder-class="input-bar__placeholder"
@ -143,7 +143,7 @@
</text> </text>
<view class="confirm-detail__target"> <view class="confirm-detail__target">
<text class="confirm-detail__id asset-number-font">ID: {{ currentTarget.id }}</text> <text class="confirm-detail__id">ID: {{ currentTarget.id }}</text>
<view class="confirm-detail__line"></view> <view class="confirm-detail__line"></view>
<view class="confirm-detail__profile"> <view class="confirm-detail__profile">
<view class="target-card__profile"> <view class="target-card__profile">
@ -233,7 +233,7 @@ export default {
}, },
computed: { computed: {
currentTabLabel() { currentTabLabel() {
return this.activeTab === "power" ? "算力" : "积分"; return this.activeTab === "power" ? "算力" : "可用积分";
}, },
currentBalance() { currentBalance() {
return Number(this.balances[this.activeTab] || 0); return Number(this.balances[this.activeTab] || 0);
@ -684,6 +684,8 @@ export default {
.target-card__profile { .target-card__profile {
display: flex; display: flex;
align-items: center; align-items: center;
flex: 1;
min-width: 0;
} }
.target-card__avatar { .target-card__avatar {
@ -713,12 +715,21 @@ export default {
} }
.target-card__name { .target-card__name {
flex: 1;
min-width: 0;
font-weight: 400;
font-size: 28rpx;
margin-left: 16rpx; margin-left: 16rpx;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
color: #ffffff; color: #ffffff;
} }
.target-card__phone { .target-card__phone {
margin-left: 18rpx; margin-left: 18rpx;
font-size: 28rpx;
font-weight: 400;
color: rgba(244, 248, 255, 0.88); color: rgba(244, 248, 255, 0.88);
} }
@ -754,8 +765,8 @@ export default {
.confirm-detail__summary { .confirm-detail__summary {
display: block; display: block;
margin-top: 6rpx; margin-top: 6rpx;
font-size: 22rpx; font-size: 30rpx;
font-weight: 700; font-weight: 500;
text-align: center; text-align: center;
color: #ff8b5d; color: #ff8b5d;
} }
@ -772,8 +783,8 @@ export default {
} }
.confirm-detail__id { .confirm-detail__id {
font-size: 22rpx; font-size: 28rpx;
font-weight: 600; font-weight: 500;
color: #ffffff; color: #ffffff;
} }
@ -786,7 +797,7 @@ export default {
.confirm-detail__desc { .confirm-detail__desc {
display: block; display: block;
margin-top: 18rpx; margin-top: 18rpx;
font-size: 18rpx; font-size: 24rpx;
line-height: 1.65; line-height: 1.65;
color: rgba(186, 194, 218, 0.88); color: rgba(186, 194, 218, 0.88);
} }

View File

@ -29,17 +29,32 @@
</view> </view>
</view> </view>
<view v-else class="wallet-panel__empty"> <view v-else class="wallet-panel__new">
<text class="wallet-panel__empty-title">暂未设置钱包地址</text> <view class="wallet-input">
<text class="wallet-panel__empty-desc"> <input
新增后即可用于 BMT 提取和后续交易所对接 v-model="draftAddress"
class="wallet-input__field asset-number-font"
type="text"
placeholder="粘贴新钱包地址"
placeholder-style="color:rgba(154, 163, 197, 0.82);font-size:28rpx;"
/>
<text class="wallet-input__paste" @click="paste">粘贴</text>
</view>
<text class="wallet-panel__helper">
钱包地址请在海南农综APP中获取
</text> </text>
</view> </view>
</view> </view>
<view class="wallet-actions"> <view
<view class="wallet-button wallet-button--primary" @click="openForm()"> class="wallet-actions"
{{ currentWallet ? "修改钱包地址" : "保存" }} :class="{ 'wallet-actions--compact': !currentWallet }"
>
<view
class="wallet-button wallet-button--primary"
@click="currentWallet ? openEditDialog() : saveCurrentAddress()"
>
{{ currentWallet ? "修改钱包地址" : savingAddress ? "保存中..." : "保存" }}
</view> </view>
<view <view
class="wallet-button wallet-button--secondary" class="wallet-button wallet-button--secondary"
@ -48,6 +63,17 @@
返回 返回
</view> </view>
</view> </view>
<view v-if="!currentWallet" class="get-walleck">
<view class="title">获取操作流程</view>
<view
v-for="(imageUrl, index) in walletFlowImages"
:key="imageUrl"
class="img"
@click="previewWalletFlow(index)"
>
<image :src="imageUrl" mode="widthFix"></image>
</view>
</view>
<text <text
v-if="currentWallet" v-if="currentWallet"
@ -68,23 +94,83 @@
@cancel="deleteDialogVisible = false" @cancel="deleteDialogVisible = false"
@confirm="remove" @confirm="remove"
/> />
<asset-confirm-popup
class="wallet-edit-popup"
:visible="editDialogVisible"
title="修改钱包"
:confirm-text="editingAddress ? '确认中...' : '确认'"
cancel-text="取消"
:close-on-mask="false"
@cancel="closeEditDialog"
@confirm="confirmEditAddress"
>
<view class="wallet-edit-dialog">
<text class="wallet-edit-dialog__message">确认修改钱包新地址</text>
<view class="wallet-edit-dialog__box">
<input
v-model="editAddress"
class="wallet-edit-dialog__input asset-number-font"
type="text"
placeholder="粘贴新钱包地址"
placeholder-style="color:rgba(165,175,208,0.82);font-size:28rpx;"
/>
<text class="wallet-edit-dialog__paste" @click.stop="pasteEditAddress">
粘贴
</text>
</view>
<text class="wallet-edit-dialog__desc asset-number-font">
请仔细核对钱包地址以免造成财产损失
</text>
</view>
</asset-confirm-popup>
<view v-if="successTipVisible" class="wallet-success-tip">
<image
class="wallet-success-tip__icon"
src="https://imgs.agrimedia.cn/bm-bmt/success.png"
mode="aspectFit"
></image>
<text class="wallet-success-tip__text">{{ successTipText }}</text>
</view>
</view> </view>
</template> </template>
<script> <script>
import AssetPageShell from "../../components/asset-page-shell.vue"; import AssetPageShell from "../../components/asset-page-shell.vue";
import AssetConfirmPopup from "../../components/asset-confirm-popup.vue";
import WalletActionPopup from "../../components/wallet-action-popup.vue"; import WalletActionPopup from "../../components/wallet-action-popup.vue";
import { fetchWalletDetail, deleteAssetWallet } from "../../api/assets"; import {
fetchWalletDetail,
deleteAssetWallet,
saveAssetWallet,
} from "../../api/assets";
export default { export default {
components: { components: {
AssetPageShell, AssetPageShell,
AssetConfirmPopup,
WalletActionPopup, WalletActionPopup,
}, },
data() { data() {
return { return {
wallets: [], wallets: [],
draftAddress: "",
savingAddress: false,
editDialogVisible: false,
editAddress: "",
editingAddress: false,
deleteDialogVisible: false, deleteDialogVisible: false,
successTipVisible: false,
successTipText: "",
successTipTimer: null,
walletFlowImages: [
"https://imgs.agrimedia.cn/bm-bmt/wallect-1.png",
"https://imgs.agrimedia.cn/bm-bmt/wallect-2.png",
"https://imgs.agrimedia.cn/bm-bmt/wallect-3.png",
"https://imgs.agrimedia.cn/bm-bmt/wallect-4.png",
"https://imgs.agrimedia.cn/bm-bmt/wallect-5.png",
],
}; };
}, },
onLoad() { onLoad() {
@ -93,6 +179,9 @@ export default {
onShow() { onShow() {
this.loadPage(); this.loadPage();
}, },
onUnload() {
this.clearSuccessTipTimer();
},
computed: { computed: {
currentWallet() { currentWallet() {
for (let i = 0; i < this.wallets.length; i += 1) { for (let i = 0; i < this.wallets.length; i += 1) {
@ -103,12 +192,21 @@ export default {
return this.wallets[0] || null; return this.wallets[0] || null;
}, },
trimmedDraftAddress() {
return String(this.draftAddress || "").trim();
},
trimmedEditAddress() {
return String(this.editAddress || "").trim();
},
}, },
methods: { methods: {
async loadPage() { async loadPage() {
try { try {
const result = await fetchWalletDetail(); const result = await fetchWalletDetail();
this.wallets = result.wallets || []; this.wallets = result.wallets || [];
if (this.currentWallet) {
this.draftAddress = this.currentWallet.address || "";
}
} catch (error) { } catch (error) {
uni.showToast({ uni.showToast({
title: error.message || "钱包加载失败", title: error.message || "钱包加载失败",
@ -137,15 +235,151 @@ export default {
}, },
}); });
}, },
openForm() { paste() {
let url = "/pages/assets/wallet-form"; const that = this;
if (this.currentWallet && this.currentWallet.id) { uni.getClipboardData({
url += "?id=" + this.currentWallet.id; success(result) {
} that.draftAddress = result.data || "";
uni.navigateTo({ },
url: url,
}); });
}, },
previewWalletFlow(index) {
const urls = Array.isArray(this.walletFlowImages)
? this.walletFlowImages
: [];
if (!urls.length) {
return;
}
const currentIndex = Number(index) || 0;
const currentUrl = urls[currentIndex] || urls[0];
uni.previewImage({
urls: urls,
current: currentUrl,
});
},
openEditDialog() {
if (!this.currentWallet) {
return;
}
this.editAddress = this.currentWallet.address || "";
this.editDialogVisible = true;
},
closeEditDialog() {
this.editDialogVisible = false;
this.editAddress = "";
this.editingAddress = false;
},
pasteEditAddress() {
const that = this;
uni.getClipboardData({
success(result) {
that.editAddress = result.data || "";
},
});
},
clearSuccessTipTimer() {
if (this.successTipTimer) {
clearTimeout(this.successTipTimer);
this.successTipTimer = null;
}
},
showSuccessTip(text) {
const message = String(text || "").trim();
if (!message) {
return;
}
this.clearSuccessTipTimer();
this.successTipText = message;
this.successTipVisible = true;
this.successTipTimer = setTimeout(() => {
this.successTipVisible = false;
this.successTipTimer = null;
}, 1400);
},
async confirmEditAddress() {
if (!this.currentWallet || this.editingAddress) {
return;
}
if (!this.trimmedEditAddress) {
uni.showToast({
title: "请先粘贴钱包地址",
icon: "none",
});
return;
}
this.editingAddress = true;
try {
await saveAssetWallet(
{
id: this.currentWallet.id,
name: this.currentWallet.name,
address: this.trimmedEditAddress,
},
{
showLoading: true,
loadingText: "保存中",
},
);
this.closeEditDialog();
this.showSuccessTip("修改成功!");
this.loadPage();
} catch (error) {
uni.showToast({
title: error.message || "保存失败",
icon: "none",
});
} finally {
this.editingAddress = false;
}
},
async saveCurrentAddress() {
if (this.currentWallet || this.savingAddress) {
return;
}
if (!this.trimmedDraftAddress) {
uni.showToast({
title: "请先粘贴钱包地址",
icon: "none",
});
return;
}
this.savingAddress = true;
try {
await saveAssetWallet(
{
address: this.trimmedDraftAddress,
},
{
showLoading: true,
loadingText: "保存中",
},
);
this.showSuccessTip("保存成功!");
this.loadPage();
} catch (error) {
uni.showToast({
title: error.message || "保存失败",
icon: "none",
});
} finally {
this.savingAddress = false;
}
},
openDeleteDialog() { openDeleteDialog() {
if (!this.currentWallet) { if (!this.currentWallet) {
return; return;
@ -161,10 +395,8 @@ export default {
try { try {
await deleteAssetWallet(this.currentWallet.id); await deleteAssetWallet(this.currentWallet.id);
this.deleteDialogVisible = false; this.deleteDialogVisible = false;
uni.showToast({ this.draftAddress = '';
title: "删除成功", this.showSuccessTip("删除成功!");
icon: "none",
});
this.loadPage(); this.loadPage();
} catch (error) { } catch (error) {
uni.showToast({ uni.showToast({
@ -190,7 +422,7 @@ export default {
min-height: calc(100vh - env(safe-area-inset-top) - 104rpx); min-height: calc(100vh - env(safe-area-inset-top) - 104rpx);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding: 10rpx 14rpx calc(env(safe-area-inset-bottom) + 36rpx); padding: 10rpx 22rpx calc(env(safe-area-inset-bottom) + 36rpx);
} }
.wallet-panel { .wallet-panel {
@ -258,7 +490,7 @@ export default {
} }
.wallet-panel__content, .wallet-panel__content,
.wallet-panel__empty { .wallet-panel__new {
margin-top: 18rpx; margin-top: 18rpx;
} }
@ -290,18 +522,35 @@ export default {
color: #1fb7f9; color: #1fb7f9;
} }
.wallet-panel__empty-title { .wallet-input {
display: block; display: flex;
font-size: 26rpx; align-items: center;
font-weight: 700; padding: 0 20rpx;
height: 94rpx;
border-radius: 10rpx;
background: #191e32;
}
.wallet-input__field {
flex: 1;
min-width: 0;
height: 94rpx;
font-size: 28rpx;
color: #ffffff; color: #ffffff;
} }
.wallet-panel__empty-desc { .wallet-input__paste {
margin-left: 18rpx;
font-size: 28rpx;
font-weight: 500;
color: #1fb7f9;
}
.wallet-panel__helper {
display: block; display: block;
margin-top: 12rpx; margin-top: 18rpx;
font-size: 22rpx; font-size: 26rpx;
line-height: 1.7; line-height: 1.6;
color: rgba(174, 183, 211, 0.82); color: rgba(174, 183, 211, 0.82);
} }
@ -312,12 +561,117 @@ export default {
padding: 0 10rpx; padding: 0 10rpx;
} }
.wallet-actions--compact {
margin-top: 48rpx;
}
.get-walleck {
background: #20263e;
margin-top: 48rpx;
padding: 30rpx 28rpx;
border-radius: 8rpx;
}
.get-walleck .title {
font-weight: 500;
font-size: 28rpx;
color: #ffffff;
text-align: center;
margin-bottom: 28rpx;
}
.get-walleck .img {
margin: 12rpx auto 0;
text-align: center;
border-radius: 12rpx;
overflow: hidden;
}
.get-walleck .img:first-of-type {
margin-top: 0;
}
.get-walleck .img image {
width: 100%;
display: block;
}
.wallet-edit-dialog__message {
display: block;
margin-top: 4rpx;
font-size: 30rpx;
line-height: 1.8;
color: #edf2ff;
}
.wallet-edit-dialog__box {
display: flex;
align-items: center;
margin-top: 26rpx;
padding: 0 20rpx;
min-height: 112rpx;
border-radius: 12rpx;
background: rgba(70, 80, 122, 0.78);
}
.wallet-edit-dialog__input {
flex: 1;
min-width: 0;
min-height: 112rpx;
font-size: 28rpx;
line-height: 1.6;
color: #ffffff;
}
.wallet-edit-dialog__paste {
margin-left: 18rpx;
font-size: 28rpx;
font-weight: 500;
color: #1fb7f9;
}
.wallet-edit-dialog__desc {
display: block;
margin-top: 26rpx;
font-size: 22rpx;
line-height: 1.8;
color: rgba(178, 190, 224, 0.86);
}
.wallet-edit-popup ::v-deep .popup-panel {
max-width: 640rpx;
padding: 30rpx 30rpx 32rpx;
border-radius: 12rpx;
background: #20263e;
}
.wallet-edit-popup ::v-deep .popup-panel__close-image {
width: 38rpx;
height: 38rpx;
}
.wallet-edit-popup ::v-deep .popup-panel__footer {
margin-top: 34rpx;
gap: 16rpx;
}
.wallet-edit-popup ::v-deep .popup-panel__btn {
height: 82rpx;
border-radius: 12rpx;
font-size: 32rpx;
}
.wallet-edit-popup ::v-deep .popup-panel__btn--ghost {
border: 1px solid rgba(157, 173, 221, 0.64);
background: rgba(67, 77, 118, 0.92);
}
.wallet-button { .wallet-button {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
flex: 1; flex: 1;
height: 92rpx; height: 90rpx;
border-radius: 999rpx; border-radius: 999rpx;
font-size: 32rpx; font-size: 32rpx;
font-weight: 700; font-weight: 700;
@ -343,4 +697,34 @@ export default {
font-weight: 700; font-weight: 700;
color: #1fb7f9; color: #1fb7f9;
} }
.wallet-success-tip {
position: fixed;
top: calc(env(safe-area-inset-top) + 132rpx);
left: 50%;
transform: translateX(-50%);
z-index: 99;
display: flex;
align-items: center;
justify-content: center;
gap: 12rpx;
min-height: 76rpx;
padding: 14rpx 28rpx;
border-radius: 999rpx;
background: rgba(17, 23, 43, 0.96);
box-shadow: 0 10rpx 30rpx rgba(8, 13, 30, 0.38);
}
.wallet-success-tip__icon {
width: 34rpx;
height: 34rpx;
flex-shrink: 0;
}
.wallet-success-tip__text {
font-size: 28rpx;
font-weight: 700;
color: #ffffff;
white-space: nowrap;
}
</style> </style>

View File

@ -182,10 +182,17 @@ export default {
return this.formatAmount(this.detail.withdrawableBmt, 2); return this.formatAmount(this.detail.withdrawableBmt, 2);
}, },
displayPrice() { displayPrice() {
const price = Number( const ticker =
this.detail.ticker.cnyPrice || this.detail.ticker.close || 0, this.detail && this.detail.ticker && typeof this.detail.ticker === "object"
); ? this.detail.ticker
return price ? price.toFixed(2) : "0.00"; : {};
const price = Number(ticker.close || ticker.cnyPrice || 0);
if (!Number.isFinite(price) || price <= 0) {
return "0.000";
}
return price.toFixed(3);
}, },
walletDisplayAddress() { walletDisplayAddress() {
if (!this.detail.defaultWallet || !this.detail.defaultWallet.address) { if (!this.detail.defaultWallet || !this.detail.defaultWallet.address) {

View File

@ -15,6 +15,50 @@
</view> </view>
<view class="asset-scroll home-scroll"> <view class="asset-scroll home-scroll">
<view v-if="isHomeLoading" class="home-skeleton">
<view class="home-skeleton__hero">
<view class="home-skeleton__banner skeleton-block"></view>
<view class="home-skeleton__stat-panel">
<view v-for="index in 2" :key="'home-skeleton-stat-' + index" class="home-skeleton__stat-card skeleton-card">
<view class="home-skeleton__line home-skeleton__line--label skeleton-block"></view>
<view class="home-skeleton__line home-skeleton__line--value skeleton-block"></view>
</view>
</view>
</view>
<view class="home-skeleton__section">
<view class="home-skeleton__title skeleton-block"></view>
<view class="home-skeleton__asset-grid">
<view v-for="index in 4" :key="'home-skeleton-asset-' + index" class="home-skeleton__asset-card skeleton-card">
<view class="home-skeleton__line home-skeleton__line--asset-label skeleton-block"></view>
<view class="home-skeleton__line home-skeleton__line--asset-value skeleton-block"></view>
</view>
</view>
</view>
<view class="home-skeleton__section">
<view class="home-skeleton__title skeleton-block"></view>
<view class="home-skeleton__feature-list">
<view v-for="index in 5" :key="'home-skeleton-feature-' + index" class="home-skeleton__feature-item skeleton-card">
<view class="home-skeleton__feature-main">
<view class="home-skeleton__feature-icon skeleton-block"></view>
<view class="home-skeleton__line home-skeleton__line--feature skeleton-block"></view>
</view>
<view class="home-skeleton__feature-arrow skeleton-block"></view>
</view>
</view>
</view>
<view class="home-skeleton__notice">
<view class="home-skeleton__notice-icon skeleton-block"></view>
<view class="home-skeleton__notice-lines">
<view class="home-skeleton__line home-skeleton__line--notice skeleton-block"></view>
<view class="home-skeleton__line home-skeleton__line--notice-short skeleton-block"></view>
</view>
</view>
</view>
<view v-show="!isHomeLoading">
<view class="home-hero"> <view class="home-hero">
<view class="brand-banner"></view> <view class="brand-banner"></view>
@ -24,6 +68,7 @@
:key="item.key" :key="item.key"
class="stat-card" class="stat-card"
:class="'stat-card--' + item.accent" :class="'stat-card--' + item.accent"
@click="openTopStat(item)"
> >
<view class="stat-card__body"> <view class="stat-card__body">
<text class="stat-card__label">{{ item.title }}</text> <text class="stat-card__label">{{ item.title }}</text>
@ -97,6 +142,7 @@
</view> </view>
</view> </view>
</view> </view>
</view>
</template> </template>
<script> <script>
@ -105,6 +151,9 @@ import { fetchAssetHome } from "../../api/assets";
export default { export default {
data() { data() {
return { return {
isHomeLoading: true,
hasLoadedOnce: false,
isFetchingHome: false,
overview: { overview: {
title: "数字资产", title: "数字资产",
heroTitle: "BM数字资产管理", heroTitle: "BM数字资产管理",
@ -116,20 +165,43 @@ export default {
}; };
}, },
onLoad() { onLoad() {
this.loadPage(); this.loadPage({
showSkeleton: true,
});
}, },
onShow() { onShow() {
this.loadPage(); this.loadPage({
showSkeleton: !this.hasLoadedOnce,
});
}, },
methods: { methods: {
async loadPage() { async loadPage(options = {}) {
if (this.isFetchingHome) {
return;
}
const shouldShowSkeleton =
Boolean(options.showSkeleton) || !this.hasLoadedOnce;
if (shouldShowSkeleton) {
this.isHomeLoading = true;
}
this.isFetchingHome = true;
try { try {
this.overview = await fetchAssetHome(); const nextOverview = await fetchAssetHome();
this.overview = nextOverview;
this.hasLoadedOnce = true;
} catch (error) { } catch (error) {
uni.showToast({ uni.showToast({
title: error.message || "首页加载失败", title: error.message || "首页加载失败",
icon: "none", icon: "none",
}); });
} finally {
if (shouldShowSkeleton || !this.hasLoadedOnce) {
this.isHomeLoading = false;
}
this.isFetchingHome = false;
} }
}, },
quickAssetIcon(key) { quickAssetIcon(key) {
@ -185,12 +257,27 @@ export default {
url: targetUrl, url: targetUrl,
}); });
}, },
openTopStat(item) {
const key = item && item.key ? item.key : "";
const urlMap = {
"wallet-bmt": "/pages/assets/ledger?type=withdraw",
};
const targetUrl = urlMap[key];
if (!targetUrl) {
return;
}
uni.navigateTo({
url: targetUrl,
});
},
openQuickAsset(item) { openQuickAsset(item) {
const urlMap = { const urlMap = {
points: "/pages/assets/ledger?type=points", points: "/pages/assets/ledger?type=points",
voucher: "/pages/assets/ledger?type=voucher", voucher: "/pages/assets/ledger?type=voucher",
coupon: "/pages/assets/ledger?type=coupon", coupon: "/pages/assets/ledger?type=coupon",
power: "/pages/assets/ledger?type=power", power: "/pages/assets/ledger?type=power-flow",
}; };
const targetUrl = urlMap[item.key]; const targetUrl = urlMap[item.key];
@ -261,12 +348,200 @@ export default {
.home-wallet-btn__text { .home-wallet-btn__text {
font-size: 28rpx; font-size: 28rpx;
color: rgba(229, 235, 255, 0.82); color: rgba(229, 235, 255, 0.82);
padding-top: 10rpx;
} }
.home-scroll { .home-scroll {
padding: 12rpx 20rpx 24rpx; padding: 12rpx 20rpx 24rpx;
} }
.home-skeleton {
padding-bottom: 6rpx;
}
.skeleton-block {
position: relative;
overflow: hidden;
background: rgba(86, 98, 135, 0.32);
}
.skeleton-block::after {
content: "";
position: absolute;
top: 0;
bottom: 0;
left: -140rpx;
width: 140rpx;
background: linear-gradient(
90deg,
rgba(255, 255, 255, 0) 0%,
rgba(255, 255, 255, 0.24) 50%,
rgba(255, 255, 255, 0) 100%
);
animation: home-skeleton-shimmer 1.35s linear infinite;
}
.skeleton-card {
border-radius: 12rpx;
background: rgba(36, 41, 68, 0.92);
border: 1px solid rgba(129, 141, 183, 0.16);
}
.home-skeleton__hero {
position: relative;
margin-bottom: 26rpx;
}
.home-skeleton__banner {
height: 380rpx;
border-radius: 28rpx;
}
.home-skeleton__stat-panel {
position: absolute;
left: 20rpx;
right: 20rpx;
bottom: 24rpx;
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 20rpx;
}
.home-skeleton__stat-card {
padding: 22rpx 22rpx 20rpx;
min-height: 126rpx;
}
.home-skeleton__section {
margin-top: 28rpx;
}
.home-skeleton__title {
width: 154rpx;
height: 34rpx;
border-radius: 8rpx;
margin-bottom: 16rpx;
}
.home-skeleton__asset-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 16rpx;
}
.home-skeleton__asset-card {
height: 80rpx;
padding: 14rpx 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
.home-skeleton__feature-list {
display: grid;
gap: 16rpx;
}
.home-skeleton__feature-item {
min-height: 100rpx;
padding: 0 22rpx;
display: flex;
align-items: center;
justify-content: space-between;
}
.home-skeleton__feature-main {
display: flex;
align-items: center;
min-width: 0;
flex: 1;
}
.home-skeleton__feature-icon {
width: 36rpx;
height: 36rpx;
border-radius: 8rpx;
flex-shrink: 0;
}
.home-skeleton__feature-arrow {
width: 24rpx;
height: 24rpx;
border-radius: 999rpx;
margin-left: 20rpx;
}
.home-skeleton__notice {
display: flex;
align-items: flex-start;
margin-top: 20rpx;
padding: 0 2rpx;
}
.home-skeleton__notice-icon {
width: 28rpx;
height: 28rpx;
border-radius: 50%;
margin-right: 8rpx;
margin-top: 4rpx;
flex-shrink: 0;
}
.home-skeleton__notice-lines {
flex: 1;
}
.home-skeleton__line {
border-radius: 8rpx;
}
.home-skeleton__line--label {
width: 150rpx;
height: 24rpx;
}
.home-skeleton__line--value {
width: 200rpx;
height: 34rpx;
margin-top: 16rpx;
}
.home-skeleton__line--asset-label {
width: 86rpx;
height: 24rpx;
}
.home-skeleton__line--asset-value {
width: 118rpx;
height: 30rpx;
}
.home-skeleton__line--feature {
width: 200rpx;
height: 28rpx;
margin-left: 20rpx;
}
.home-skeleton__line--notice {
width: 100%;
height: 24rpx;
}
.home-skeleton__line--notice-short {
width: 72%;
height: 24rpx;
margin-top: 10rpx;
}
@keyframes home-skeleton-shimmer {
0% {
transform: translateX(0);
}
100% {
transform: translateX(900rpx);
}
}
.home-hero { .home-hero {
position: relative; position: relative;
margin-bottom: 26rpx; margin-bottom: 26rpx;

View File

@ -1,2 +0,0 @@
<!DOCTYPE html><html lang=zh-CN><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><title>白马交易所</title><script>var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') || CSS.supports('top: constant(a)'))
document.write('<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' + (coverSupport ? ', viewport-fit=cover' : '') + '" />')</script><link rel=stylesheet href=/bmt/static/index.883130ca.css></head><body><noscript><strong>Please enable JavaScript to continue.</strong></noscript><div id=app></div><script src=/bmt/static/js/chunk-vendors.98a0fd8f.js></script><script src=/bmt/static/js/index.fa34fa02.js></script></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB