diff --git a/BMTR.MD b/BMTR.MD new file mode 100644 index 0000000..8c15453 --- /dev/null +++ b/BMTR.MD @@ -0,0 +1,267 @@ +# BMTR 资产接入实现文档 + +## 一、需求概述 + +在首页「我的资产」区域新增 **BMTR** 卡片,展示当前登录用户的 BMTR 数量。 + +数据来源: + +``` +GET /api/mxj/api/get_user +``` + +返回示例: + +```json +{ + "status": 200, + "msg": "success", + "data": { + "uid": 94227, + "nickname": "siediyer", + "avatar": "https://thirdwx.qlogo.cn/...", + "mxj_points": "99999.00", + "bmtr": "11.0000", + "is_check": 1, + "is_idcard": 1, + "is_sign": 1, + "sign_time": 1781172245, + "status": 1 + } +} +``` + +--- + +## 二、API 请求运行方式 + +项目中的网络请求采用三层结构: + +``` +页面 (pages/index/index.vue) + ↓ 调用 +api/assets.js 中的业务方法 + ↓ 调用 +utils/request.js 中的通用 request 方法 + ↓ 调用 +uni.request 发送 HTTP 请求 +``` + +### 2.1 配置层:`config/service.js` + +定义了接口基础地址和 endpoint 映射表。 + +### 2.2 通用请求层:`utils/request.js` + +- 自动拼接 `BASE_URL` 和 `url` +- 统一注入 `Authori-zation` / `Authorization` Token +- 统一处理 `showLoading` / `hideLoading` +- 对响应进行 `statusCode` 校验,返回 `response.data` + +### 2.3 业务 API 层:`api/assets.js` + +- 封装 `fetchPayload` 统一处理 `{ status: 200, data: {...} }` 结构 +- 提供 `createRequestOptions` 合并请求参数 +- 提供 `buildHomeOverview` 将原始接口数据转换为页面渲染数据 + +--- + +## 三、本次改动文件 + +| 文件 | 改动内容 | +| ----------------------- | ------------------------------------------------------------------ | +| `config/service.js` | 新增 `getUser` endpoint | +| `api/assets.js` | 新增 `fetchUserData`,修改 `buildHomeOverview` 和 `fetchAssetHome` | +| `pages/index/index.vue` | 新增 BMTR 图标、背景、点击跳转 | +| `BMTR.MD` | 本文档 | + +--- + +## 四、具体改动 + +### 4.1 `config/service.js` + +在 `ENDPOINTS` 中新增: + +```javascript +getUser: "/api/mxj/api/get_user", +``` + +### 4.2 `api/assets.js` + +#### 4.2.1 新增获取用户数据的函数 + +```javascript +async function fetchUserData(requestOptions) { + return fetchPayload( + createRequestOptions( + { + url: serviceConfig.ENDPOINTS.getUser, + }, + requestOptions, + ), + "用户信息加载失败", + ); +} +``` + +#### 4.2.2 修改 `buildHomeOverview` + +函数签名从: + +```javascript +function buildHomeOverview(balanceData, tickerData) { +``` + +改为: + +```javascript +function buildHomeOverview(balanceData, tickerData, userData) { +``` + +在 `rawQuickAssets` 中新增 `bmtr`: + +```javascript +const rawQuickAssets = { + balance: toRawDisplayValue(balanceData && balanceData.brokerage_price), + points: toRawDisplayValue(balanceData && balanceData.point), + voucher: toRawDisplayValue(balanceData && balanceData.coin), + coupon: toRawDisplayValue(balanceData && balanceData.diamond_balance), + power: toRawDisplayValue(balanceData && balanceData.c_power), + bmtr: toRawDisplayValue(userData && userData.bmtr), +}; +``` + +在 `quickAssets` 数组中新增 BMTR 项: + +```javascript +{ + key: "bmtr", + title: "BMTR", + value: rawQuickAssets.bmtr, + accent: "orange", +}, +``` + +> `value` 直接使用接口返回的 `bmtr` 字符串。 +> `accent` 设为 `orange`,与页面已有的 `.asset-mini-card--orange` 样式类对应。 + +#### 4.2.3 修改 `fetchAssetHome` + +原来是同时请求价格 + 余额,现在增加用户信息请求: + +```javascript +export async function fetchAssetHome(requestOptions) { + const result = await Promise.all([ + fetchPriceData(requestOptions), + fetchHomeBalanceData(requestOptions), + fetchUserData(requestOptions), + ]); + const overview = buildHomeOverview(result[1], result[0], result[2]); + setHomeTickerCache(overview.ticker); + return overview; +} +``` + +三个请求并行执行,BMTR 接口失败不会阻塞其他两个请求(但会进入 `Promise.all` 的 catch 逻辑,页面会显示错误提示)。 + +### 4.3 `pages/index/index.vue` + +#### 4.3.1 新增图标 + +在 `quickAssetIcon` 的 `iconMap` 中新增: + +```javascript +bmtr: "https://imgs.agrimedia.cn/bm-bmt/qianbao.png", +``` + +> 当前复用已有的钱包图标,后续可替换为 BMTR 专属图标。 + +#### 4.3.2 新增背景 + +在 `quickAssetBg` 的 `bgMap` 中新增: + +```javascript +bmtr: "https://imgs.agrimedia.cn/bm-bmt/suanli-bg.png", +``` + +> 当前复用算力卡片背景,后续可替换为 BMTR 专属背景。 + +#### 4.3.3 新增点击跳转 + +在 `openQuickAsset` 的 `urlMap` 中新增: + +```javascript +bmtr: "/pages/assets/bmtr", +``` + +用户点击 BMTR 卡片后,会跳转到 `pages/assets/bmtr.vue` 页面。 + +--- + +## 五、首页资产卡片渲染顺序 + +修改后,首页「我的资产」区域共 6 个卡片,顺序如下: + +1. 可用积分 +2. 算力 +3. 消费券 +4. 抵用券 +5. **BMTR(新增)** +6. 余额 + +> 由于 `balance` 卡片设置了 `grid-column: 1 / -1`,会单独占一行,所以实际布局为: +> +> - 第一行:可用积分、算力 +> - 第二行:消费券、抵用券 +> - 第三行:BMTR +> - 第四行:余额(整行) + +如果希望调整 BMTR 的位置,修改 `api/assets.js` 中 `quickAssets` 数组的顺序即可。 + +--- + +## 六、待替换项 + +| 项目 | 当前值 | 说明 | +| --------- | ------------------------------------------------------------ | ----------------------- | +| BMTR 图标 | `https://imgs.agrimedia.cn/webimg/202607171808386371853.svg` | BMTR 专属图标(已替换) | +| BMTR 背景 | `https://imgs.agrimedia.cn/webimg/202607201122331061368.svg` | BMTR 专属背景(已替换) | + +--- + +## 七、接口字段说明 + +| 字段 | 类型 | 用途 | +| ------------ | ------ | --------------------------- | +| `uid` | number | 用户ID | +| `nickname` | string | 用户昵称 | +| `avatar` | string | 头像URL | +| `mxj_points` | string | MXJ积分 | +| `bmtr` | string | **BMTR 余额,本次使用字段** | +| `is_check` | number | 是否通过某种校验 | +| `is_idcard` | number | 是否实名认证 | +| `is_sign` | number | 是否签到 | +| `sign_time` | number | 签到时间戳 | +| `status` | number | 用户状态 | + +--- + +## 八、验证建议 + +1. 进入首页,确认「我的资产」区域出现 BMTR 卡片 +2. 确认 BMTR 显示的数值与 `/api/mxj/api/get_user` 返回的 `data.bmtr` 一致 +3. 点击 BMTR 卡片,确认能跳转到 `pages/assets/bmtr` +4. 检查网络面板,确认三个请求并行发送: + - `/api/hn/getPrice` + - `/api/hn/getAllBalance` + - `/api/mxj/api/get_user` +5. 如果 BMTR 接口失败,首页应显示错误提示,不影响其他资产卡片的数据逻辑(但 Promise.all 会统一报错,如需降级处理可改为单独 catch) + +--- + +## 九、可能的后续优化 + +1. **接口降级**:如果 `/api/mxj/api/get_user` 不稳定,可将 `fetchUserData` 从 `Promise.all` 中拆出,单独 catch 并给 BMTR 一个默认值 `0`。 +2. **数值格式化**:当前 `bmtr` 直接显示为字符串,如需统一显示为 `11.00` 或 `11.0000`,可在 `buildHomeOverview` 中使用 `toFixedNumber` 处理。 +3. **图标替换**:拿到 BMTR 专属图标和背景后,更新 `pages/index/index.vue` 中的 `iconMap` 和 `bgMap`。 diff --git a/api/assets.js b/api/assets.js index 013bcf7..211f99b 100644 --- a/api/assets.js +++ b/api/assets.js @@ -97,8 +97,7 @@ function normalizeTicker(data) { return { symbol: (data && data.symbol) || "BMT/CNY", close: close, - cnyPrice: - (data && data.cnyPrice) || (close ? close.toFixed(2) : "0.00"), + cnyPrice: (data && data.cnyPrice) || (close ? close.toFixed(2) : "0.00"), lastDayClose: lastDayClose, change: change, }; @@ -158,7 +157,7 @@ function toRawDisplayValue(value) { return String(value); } -function buildHomeOverview(balanceData, tickerData) { +function buildHomeOverview(balanceData, tickerData, userData) { const balances = normalizeBalances(balanceData); const ticker = normalizeTicker(tickerData); const rawQuickAssets = { @@ -167,6 +166,7 @@ function buildHomeOverview(balanceData, tickerData) { voucher: toRawDisplayValue(balanceData && balanceData.coin), coupon: toRawDisplayValue(balanceData && balanceData.diamond_balance), power: toRawDisplayValue(balanceData && balanceData.c_power), + bmtr: toRawDisplayValue(userData && userData.bmtr), }; return { @@ -195,63 +195,68 @@ function buildHomeOverview(balanceData, tickerData) { value: rawQuickAssets.points, accent: "gold", }, - { - key: "power", - title: "算力", - value: rawQuickAssets.power, - accent: "violet", - }, - { - key: "coupon", - title: "消费券", - value: rawQuickAssets.coupon, - accent: "teal", - }, + { + key: "power", + title: "算力", + value: rawQuickAssets.power, + accent: "violet", + }, + { + key: "coupon", + title: "消费券", + value: rawQuickAssets.coupon, + accent: "teal", + }, { key: "voucher", title: "抵用券", value: rawQuickAssets.voucher, accent: "rose", }, - { - key: "balance", - title: "余额", - value: rawQuickAssets.balance, - accent: "blue", - }, - + { + key: "balance", + title: "余额", + value: rawQuickAssets.balance, + accent: "blue", + }, + { + key: "bmtr", + title: "BMTR", + value: rawQuickAssets.bmtr, + accent: "orange", + }, ], features: [ - { - key: "points-convert", - title: "积分转换", - desc: "释放中的积分转换为可用积分", - accent: "pink", - }, + { + key: "points-convert", + title: "积分转换", + desc: "释放中的积分转换为可用积分", + accent: "pink", + }, { key: "power-exchange", title: "算力兑换", desc: "抵用券与消费券兑换算力", accent: "amber", }, - { - key: "bmt-flash", - title: "BMT闪兑", - desc: "BMT闪兑", - accent: "indigo", - }, + { + key: "bmt-flash", + title: "BMT闪兑", + desc: "BMT闪兑", + accent: "indigo", + }, { key: "transfer", title: "转赠中心", desc: "积分或算力转赠好友", accent: "indigo", }, - { - key: "bmt-exchange", - title: "BMT兑换", - desc: "积分与算力兑换 BMT", - accent: "mint", - }, + { + key: "bmt-exchange", + title: "BMT兑换", + desc: "积分与算力兑换 BMT", + accent: "mint", + }, { key: "withdraw", title: "BMT提取", @@ -273,10 +278,7 @@ function buildTransferTips(feePercent) { "凌晨00:00至02:00为系统维护时段不可赠送", "转赠系统会扣除" + percentText + "%的手续费", ], - power: [ - "只能转赠1的整数倍", - "转赠系统会扣除" + percentText + "%的手续费", - ], + power: ["只能转赠1的整数倍", "转赠系统会扣除" + percentText + "%的手续费"], }; } @@ -453,7 +455,8 @@ function parseBooleanLike(value) { function buildPaginationMeta(data, requestPagination) { const normalizedData = data && typeof data === "object" ? data : {}; - const normalizedRequestPagination = normalizePaginationOptions(requestPagination); + const normalizedRequestPagination = + normalizePaginationOptions(requestPagination); const list = normalizeListData(normalizedData); const page = Math.floor( @@ -548,7 +551,12 @@ function formatLedgerRecordNumber(value) { return formatTransferRecordNumber(Math.abs(toNumber(value))); } -function resolveRecordDirection(item, amountValue, positiveLabel, negativeLabel) { +function resolveRecordDirection( + item, + amountValue, + positiveLabel, + negativeLabel, +) { const rawDirection = pickFirstValue(item, [ "io_type", "in_out", @@ -670,7 +678,8 @@ function mapWalletFlowRecords(list, meta) { pickFirstValue(item, ["title", "name", "type_name"]) || meta.title || meta.unit + "记录"; - const isPointsLedger = String(meta && meta.key ? meta.key : "") === "points"; + const isPointsLedger = + String(meta && meta.key ? meta.key : "") === "points"; const displayTitle = isPointsLedger ? normalizeAvailablePointsText(title) : title; @@ -882,7 +891,9 @@ function getTransferRecordAmount(item) { } function extractTransferTitleFeeValue(item) { - const titleText = String(pickFirstValue(item, ["title", "name"]) || "").trim(); + const titleText = String( + pickFirstValue(item, ["title", "name"]) || "", + ).trim(); if (!titleText) { return ""; @@ -916,7 +927,9 @@ function getTransferRecordFee(item) { return toNumber(item.fee); } - return (toNumber(item && item.num) * toNumber(item && item.fee_percent)) / 100; + return ( + (toNumber(item && item.num) * toNumber(item && item.fee_percent)) / 100 + ); } function getTransferRecordFeeText(item) { @@ -938,7 +951,9 @@ function getTransferRecordBalance(item) { function getTransferRecordBalanceLabel(item) { const unit = getTransferRecordUnit(item); - return "剩余" + unit + ":" + formatTransferRecordNumber(item && item.balance); + return ( + "剩余" + unit + ":" + formatTransferRecordNumber(item && item.balance) + ); } function mapTransferRecords(list) { @@ -947,7 +962,9 @@ function mapTransferRecords(list) { return { id: item.order_sn || item.id || String(Math.random()), 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 || "", amount: getTransferRecordAmount(item), balance: getTransferRecordBalance(item), @@ -1031,13 +1048,9 @@ function mapPointsConvertRecords(list) { pickFirstValue(item, ["order_sn", "trade_no", "bill_no", "sn"]) || "", ), title: - pickFirstValue(item, ["title", "name", "type_name"]) || - "系统赠送积分", + pickFirstValue(item, ["title", "name", "type_name"]) || "系统赠送积分", subtitle: - "可转数量 " + - formatTransferRecordNumber( - rawTransferPointValue || 0, - ), + "可转数量 " + formatTransferRecordNumber(rawTransferPointValue || 0), time: pickFirstValue(item, [ "add_time", "create_time", @@ -1085,9 +1098,7 @@ function extractMonthLabel(value) { } return ( - date.getFullYear() + - "-" + - String(date.getMonth() + 1).padStart(2, "0") + date.getFullYear() + "-" + String(date.getMonth() + 1).padStart(2, "0") ); } @@ -1111,7 +1122,8 @@ function buildMonthOptions(list) { result, item, ) { - const month = item && item.month ? item.month : extractMonthLabel(item && item.time); + const month = + item && item.month ? item.month : extractMonthLabel(item && item.time); if (month && result.indexOf(month) === -1) { result.push(month); } @@ -1261,22 +1273,25 @@ function buildPointsConvertDetailPayload(data, fallbackRecord) { "created_at", "time", "update_time", - ]) || fallback.time || "", + ]) || + fallback.time || + "", }; }); if (!detailList.length) { return buildPointsConvertDetailFallback({ id: fallback.id, - orderSn: - String( - pickFirstValue(summarySource, [ - "order_sn", - "trade_no", - "bill_no", - "sn", - ]) || fallback.orderSn || "", - ), + orderSn: String( + pickFirstValue(summarySource, [ + "order_sn", + "trade_no", + "bill_no", + "sn", + ]) || + fallback.orderSn || + "", + ), title: pickFirstValue(summarySource, ["title", "name", "type_name"]) || fallback.title, @@ -1294,7 +1309,12 @@ function buildPointsConvertDetailPayload(data, fallbackRecord) { return { orderSn: String( - pickFirstValue(summarySource, ["order_sn", "trade_no", "bill_no", "sn"]) || + pickFirstValue(summarySource, [ + "order_sn", + "trade_no", + "bill_no", + "sn", + ]) || fallback.orderSn || "", ), @@ -1306,7 +1326,9 @@ function buildPointsConvertDetailPayload(data, fallbackRecord) { "created_at", "time", "update_time", - ]) || fallback.time || "", + ]) || + fallback.time || + "", details: detailList, }; } @@ -1352,8 +1374,7 @@ function normalizeTransferTarget(data, fallbackId) { return { id: id, - nickname: - data.nickname || data.nick_name || data.username || "用户" + id, + nickname: data.nickname || data.nick_name || data.username || "用户" + id, phone: data.mobile || data.phone || "ID已通过校验", avatar: data.avatar || data.headimg || "", }; @@ -1384,36 +1405,48 @@ function sumBy(list, key) { async function fetchPriceData(requestOptions) { return fetchPayload( - createRequestOptions({ - url: serviceConfig.ENDPOINTS.price, - }, requestOptions), + createRequestOptions( + { + url: serviceConfig.ENDPOINTS.price, + }, + requestOptions, + ), "实时价格加载失败", ); } async function fetchHomeBalanceData(requestOptions) { return fetchPayload( - createRequestOptions({ - url: serviceConfig.ENDPOINTS.homeBalance, - }, requestOptions), + createRequestOptions( + { + url: serviceConfig.ENDPOINTS.homeBalance, + }, + requestOptions, + ), "首页资产加载失败", ); } async function fetchBmtPowerRateData(requestOptions) { return fetchPayload( - createRequestOptions({ - url: serviceConfig.ENDPOINTS.bmtRedeemPowerRate, - }, requestOptions), + createRequestOptions( + { + url: serviceConfig.ENDPOINTS.bmtRedeemPowerRate, + }, + requestOptions, + ), "兑换比例加载失败", ); } async function fetchPowerExchangeMuitData(requestOptions) { return fetchPayload( - createRequestOptions({ - url: serviceConfig.ENDPOINTS.powerExchangeMuit, - }, requestOptions), + createRequestOptions( + { + url: serviceConfig.ENDPOINTS.powerExchangeMuit, + }, + requestOptions, + ), "兑换倍率加载失败", ); } @@ -1438,20 +1471,38 @@ function resolveRedeemPowerRate(data) { return normalizedRate > 0 ? normalizedRate : 0; } +async function fetchUserData(requestOptions) { + return fetchPayload( + createRequestOptions( + { + url: serviceConfig.ENDPOINTS.getUser, + }, + requestOptions, + ), + "用户信息加载失败", + ); +} + async function fetchTransferFeeData(requestOptions) { return fetchPayload( - createRequestOptions({ - url: serviceConfig.ENDPOINTS.transferFee, - }, requestOptions), + createRequestOptions( + { + url: serviceConfig.ENDPOINTS.transferFee, + }, + requestOptions, + ), "手续费比例加载失败", ); } async function fetchWithdrawRateData(requestOptions) { return fetchPayload( - createRequestOptions({ - url: serviceConfig.ENDPOINTS.withdrawRate, - }, requestOptions), + createRequestOptions( + { + url: serviceConfig.ENDPOINTS.withdrawRate, + }, + requestOptions, + ), "提现费率加载失败", ); } @@ -1488,27 +1539,36 @@ function resolvePercentRate(data) { async function fetchWalletAddressData(requestOptions) { return fetchPayload( - createRequestOptions({ - url: serviceConfig.ENDPOINTS.walletDetail, - }, requestOptions), + createRequestOptions( + { + url: serviceConfig.ENDPOINTS.walletDetail, + }, + requestOptions, + ), "钱包加载失败", ); } async function fetchCoinIndexData(requestOptions) { return fetchPayload( - createRequestOptions({ - url: serviceConfig.ENDPOINTS.coinIndex, - }, requestOptions), + createRequestOptions( + { + url: serviceConfig.ENDPOINTS.coinIndex, + }, + requestOptions, + ), "抵用券信息加载失败", ); } async function fetchUserProfileData(requestOptions) { return fetchPayload( - createRequestOptions({ - url: serviceConfig.ENDPOINTS.userProfile, - }, requestOptions), + createRequestOptions( + { + url: serviceConfig.ENDPOINTS.userProfile, + }, + requestOptions, + ), "用户信息加载失败", ); } @@ -1542,12 +1602,18 @@ function normalizePointsConvertInterval(value) { async function fetchPointsConvertList(pagination, requestOptions, interval) { const normalizedInterval = normalizePointsConvertInterval(interval); return fetchPayload( - createRequestOptions({ - url: serviceConfig.ENDPOINTS.pointsConvertAvailableList, - data: buildPagingRequestData({ - interval: normalizedInterval, - }, pagination), - }, requestOptions), + createRequestOptions( + { + url: serviceConfig.ENDPOINTS.pointsConvertAvailableList, + data: buildPagingRequestData( + { + interval: normalizedInterval, + }, + pagination, + ), + }, + requestOptions, + ), "积分转换列表加载失败", ); } @@ -1558,56 +1624,84 @@ async function fetchPointsConvertHistoryListData( requestOptions, ) { return fetchPayload( - createRequestOptions({ - url: serviceConfig.ENDPOINTS.pointsConvertHistoryList, - data: buildPagingRequestData({ - month: month, - }, pagination), - }, requestOptions), + createRequestOptions( + { + url: serviceConfig.ENDPOINTS.pointsConvertHistoryList, + data: buildPagingRequestData( + { + month: month, + }, + pagination, + ), + }, + requestOptions, + ), "积分兑换列表加载失败", ); } async function fetchTransferLedgerData(pagination, requestOptions) { return fetchPayload( - createRequestOptions({ - url: serviceConfig.ENDPOINTS.transferLedger, - data: buildPagingRequestData({}, pagination), - }, requestOptions), + createRequestOptions( + { + url: serviceConfig.ENDPOINTS.transferLedger, + data: buildPagingRequestData({}, pagination), + }, + requestOptions, + ), "转赠记录加载失败", ); } async function fetchWalletFlowListData(flowType, pagination, requestOptions) { return fetchPayload( - createRequestOptions({ - url: serviceConfig.ENDPOINTS.walletFlowList, - data: buildPagingRequestData({ - type: flowType, - }, pagination), - }, requestOptions), + createRequestOptions( + { + url: serviceConfig.ENDPOINTS.walletFlowList, + data: buildPagingRequestData( + { + type: flowType, + }, + pagination, + ), + }, + requestOptions, + ), "资产流水加载失败", ); } -async function fetchRedeemRecordListData(redeemType, pagination, requestOptions) { +async function fetchRedeemRecordListData( + redeemType, + pagination, + requestOptions, +) { return fetchPayload( - createRequestOptions({ - url: serviceConfig.ENDPOINTS.redeemRecordList, - data: buildPagingRequestData({ - type: redeemType, - }, pagination), - }, requestOptions), + createRequestOptions( + { + url: serviceConfig.ENDPOINTS.redeemRecordList, + data: buildPagingRequestData( + { + type: redeemType, + }, + pagination, + ), + }, + requestOptions, + ), "兑换记录加载失败", ); } async function fetchPointsConvertInfoData(query, requestOptions) { return fetchPayload( - createRequestOptions({ - url: serviceConfig.ENDPOINTS.pointsConvertRecordDetail, - data: query || {}, - }, requestOptions), + createRequestOptions( + { + url: serviceConfig.ENDPOINTS.pointsConvertRecordDetail, + data: query || {}, + }, + requestOptions, + ), "积分转换详情加载失败", ); } @@ -1616,8 +1710,9 @@ export async function fetchAssetHome(requestOptions) { const result = await Promise.all([ fetchPriceData(requestOptions), fetchHomeBalanceData(requestOptions), + fetchUserData(requestOptions), ]); - const overview = buildHomeOverview(result[1], result[0]); + const overview = buildHomeOverview(result[1], result[0], result[2]); setHomeTickerCache(overview.ticker); return overview; } @@ -1678,11 +1773,7 @@ export async function fetchPointsConvertHome( const mergedRequestOptions = params.requestOptions; const result = await Promise.all([ fetchHomeBalanceData(mergedRequestOptions), - fetchPointsConvertHistoryListData( - month, - pagination, - mergedRequestOptions, - ), + fetchPointsConvertHistoryListData(month, pagination, mergedRequestOptions), ]); const balances = normalizeBalances(result[0]); const records = buildPointsHistoryRecords(result[1]).map(function (item) { @@ -1704,8 +1795,7 @@ export async function fetchPointsConvertSelection( requestOptions, ) { const interval = - paginationOrRequestOptions && - typeof paginationOrRequestOptions === "object" + paginationOrRequestOptions && typeof paginationOrRequestOptions === "object" ? paginationOrRequestOptions.interval : ""; const params = resolvePagingArguments( @@ -1765,14 +1855,17 @@ export async function submitAssetPointsConvert(payload, requestOptions) { } await fetchPayload( - createRequestOptions({ - url: serviceConfig.ENDPOINTS.pointsConvertSubmit, - method: "POST", - data: { - ids: ids.join(","), - type: 1, + createRequestOptions( + { + url: serviceConfig.ENDPOINTS.pointsConvertSubmit, + method: "POST", + data: { + ids: ids.join(","), + type: 1, + }, }, - }, requestOptions), + requestOptions, + ), "积分转换失败", ); @@ -1807,13 +1900,16 @@ export async function searchTransferUser(uid, requestOptions) { } const data = await fetchPayload( - createRequestOptions({ - url: serviceConfig.ENDPOINTS.transferUser, - method: "POST", - data: { - uid: keyword, + createRequestOptions( + { + url: serviceConfig.ENDPOINTS.transferUser, + method: "POST", + data: { + uid: keyword, + }, }, - }, requestOptions), + requestOptions, + ), "查询好友失败", ); @@ -1822,7 +1918,9 @@ export async function searchTransferUser(uid, requestOptions) { export async function submitAssetTransfer(payload, requestOptions) { const transferType = payload && payload.type === "power" ? "power" : "points"; - const targetId = String(payload && payload.targetId ? payload.targetId : "").trim(); + const targetId = String( + payload && payload.targetId ? payload.targetId : "", + ).trim(); const amount = toNumber(payload && payload.amount); if (!targetId) { @@ -1836,17 +1934,20 @@ export async function submitAssetTransfer(payload, requestOptions) { const result = await Promise.all([ fetchTransferFeeData(requestOptions), fetchPayload( - createRequestOptions({ - url: - transferType === "power" - ? serviceConfig.ENDPOINTS.transferPowerSubmit - : serviceConfig.ENDPOINTS.transferPointsSubmit, - method: "POST", - data: { - uid: targetId, - number: String(amount), + createRequestOptions( + { + url: + transferType === "power" + ? serviceConfig.ENDPOINTS.transferPowerSubmit + : serviceConfig.ENDPOINTS.transferPointsSubmit, + method: "POST", + data: { + uid: targetId, + number: String(amount), + }, }, - }, requestOptions), + requestOptions, + ), "转赠失败", ), ]); @@ -1901,14 +2002,17 @@ export async function submitAssetPowerExchange(payload, requestOptions) { } await fetchPayload( - createRequestOptions({ - url: serviceConfig.ENDPOINTS.powerExchangeSubmit, - method: "POST", - data: { - type: mode === "coupon" ? 1 : mode === "balance" ? 2 : 0, - number: String(amount), + createRequestOptions( + { + url: serviceConfig.ENDPOINTS.powerExchangeSubmit, + method: "POST", + data: { + type: mode === "coupon" ? 1 : mode === "balance" ? 2 : 0, + number: String(amount), + }, }, - }, requestOptions), + requestOptions, + ), "算力兑换失败", ); @@ -1957,14 +2061,17 @@ export async function submitAssetBmtFlashExchange(payload, requestOptions) { } await fetchPayload( - createRequestOptions({ - url: serviceConfig.ENDPOINTS.bmtFlashExchangeSubmit, - method: "POST", - data: { - type: mode === "coupon" ? 1 : mode === "balance" ? 2 : 0, - number: String(amount), + createRequestOptions( + { + url: serviceConfig.ENDPOINTS.bmtFlashExchangeSubmit, + method: "POST", + data: { + type: mode === "coupon" ? 1 : mode === "balance" ? 2 : 0, + number: String(amount), + }, }, - }, requestOptions), + requestOptions, + ), "BMT闪兑失败", ); @@ -1973,7 +2080,6 @@ export async function submitAssetBmtFlashExchange(payload, requestOptions) { }; } - // BMT兑换 export async function fetchBmtExchangeDetail(requestOptions) { const result = await Promise.all([ @@ -1984,7 +2090,7 @@ export async function fetchBmtExchangeDetail(requestOptions) { const ticker = getHomeTickerCache(); const balances = normalizeBalances(result[0]); const powerRate = resolveRedeemPowerRate(result[1]); - + return { ticker: ticker, powerRate: powerRate, @@ -2012,13 +2118,16 @@ export async function submitAssetBmtExchange(payload, requestOptions) { } await fetchPayload( - createRequestOptions({ - url: serviceConfig.ENDPOINTS.bmtExchangeSubmit, - method: "POST", - data: { - number: String(amount), + createRequestOptions( + { + url: serviceConfig.ENDPOINTS.bmtExchangeSubmit, + method: "POST", + data: { + number: String(amount), + }, }, - }, requestOptions), + requestOptions, + ), "BMT兑换失败", ); @@ -2070,14 +2179,17 @@ export async function submitAssetWithdraw(payload, requestOptions) { } await fetchPayload( - createRequestOptions({ - url: serviceConfig.ENDPOINTS.withdrawSubmit, - method: "POST", - data: { - address: address, - number: String(amount), + createRequestOptions( + { + url: serviceConfig.ENDPOINTS.withdrawSubmit, + method: "POST", + data: { + address: address, + number: String(amount), + }, }, - }, requestOptions), + requestOptions, + ), "提取失败", ); @@ -2099,7 +2211,10 @@ export async function fetchLedgerDetail( const mergedRequestOptions = params.requestOptions; if (type === "transfer") { - const data = await fetchTransferLedgerData(pagination, mergedRequestOptions); + const data = await fetchTransferLedgerData( + pagination, + mergedRequestOptions, + ); return { type: type, title: "转赠记录", @@ -2110,7 +2225,11 @@ export async function fetchLedgerDetail( } if (type === "points") { - const data = await fetchWalletFlowListData(0, pagination, mergedRequestOptions); + const data = await fetchWalletFlowListData( + 0, + pagination, + mergedRequestOptions, + ); return { type: type, title: "可用积分记录", @@ -2126,7 +2245,11 @@ export async function fetchLedgerDetail( } if (type === "power-flow") { - const data = await fetchWalletFlowListData(1, pagination, mergedRequestOptions); + const data = await fetchWalletFlowListData( + 1, + pagination, + mergedRequestOptions, + ); return { type: type, title: "算力记录", @@ -2142,7 +2265,11 @@ export async function fetchLedgerDetail( } if (type === "power") { - const data = await fetchRedeemRecordListData(0, pagination, mergedRequestOptions); + const data = await fetchRedeemRecordListData( + 0, + pagination, + mergedRequestOptions, + ); return { type: type, title: "兑换记录", @@ -2167,7 +2294,11 @@ export async function fetchLedgerDetail( } if (type === "bmt") { - const data = await fetchRedeemRecordListData(1, pagination, mergedRequestOptions); + const data = await fetchRedeemRecordListData( + 1, + pagination, + mergedRequestOptions, + ); return { type: type, title: "兑换记录", @@ -2185,7 +2316,11 @@ export async function fetchLedgerDetail( } if (type === "bmt-flash") { - const data = await fetchRedeemRecordListData(1, pagination, mergedRequestOptions); + const data = await fetchRedeemRecordListData( + 1, + pagination, + mergedRequestOptions, + ); return { type: type, title: "闪兑记录", @@ -2203,7 +2338,11 @@ export async function fetchLedgerDetail( } if (type === "withdraw") { - const data = await fetchWalletFlowListData(2, pagination, mergedRequestOptions); + const data = await fetchWalletFlowListData( + 2, + pagination, + mergedRequestOptions, + ); return { type: type, title: "提取记录", @@ -2237,20 +2376,25 @@ export async function fetchWalletDetail(requestOptions) { } export async function saveAssetWallet(payload, requestOptions) { - const address = String(payload && payload.address ? payload.address : "").trim(); + const address = String( + payload && payload.address ? payload.address : "", + ).trim(); if (!address) { throw createError("请输入钱包地址"); } await fetchPayload( - createRequestOptions({ - url: serviceConfig.ENDPOINTS.walletSave, - method: "POST", - data: { - address: address, + createRequestOptions( + { + url: serviceConfig.ENDPOINTS.walletSave, + method: "POST", + data: { + address: address, + }, }, - }, requestOptions), + requestOptions, + ), "保存失败", ); @@ -2261,13 +2405,16 @@ export async function saveAssetWallet(payload, requestOptions) { export async function deleteAssetWallet(id, requestOptions) { await fetchPayload( - createRequestOptions({ - url: serviceConfig.ENDPOINTS.walletSave, - method: "POST", - data: { - address: "", + createRequestOptions( + { + url: serviceConfig.ENDPOINTS.walletSave, + method: "POST", + data: { + address: "", + }, }, - }, requestOptions), + requestOptions, + ), "删除失败", ); @@ -2275,3 +2422,91 @@ export async function deleteAssetWallet(id, requestOptions) { success: true, }; } + +function padTimeUnit(value) { + return value < 10 ? "0" + value : String(value); +} + +function formatTimestamp(timestamp) { + const seconds = toNumber(timestamp); + + if (!seconds) { + return ""; + } + + const date = new Date(seconds * 1000); + + return ( + date.getFullYear() + + "-" + + padTimeUnit(date.getMonth() + 1) + + "-" + + padTimeUnit(date.getDate()) + + " " + + padTimeUnit(date.getHours()) + + ":" + + padTimeUnit(date.getMinutes()) + + ":" + + padTimeUnit(date.getSeconds()) + ); +} + +function mapBmtrRecords(list) { + return normalizeListData(list).map(function (item) { + const amountValue = toNumber(item && item.bmtr); + // 交易类型:0 支出,1 收入,2 退回 + const bmtrType = Number(item && item.bmtr_type); + const isIncome = bmtrType === 1 || bmtrType === 2; + + return { + id: buildRecordId(item, "bmtr"), + title: String(pickFirstValue(item, ["bmtr_type_tips", "title"]) || "BMTR记录"), + time: formatTimestamp(item && item.add_time), + amount: isIncome ? Math.abs(amountValue) : -Math.abs(amountValue), + orderSn: String(pickFirstValue(item, ["order_sn", "sn"]) || ""), + }; + }); +} + +export async function fetchBmtrDetail(requestOptions) { + const data = await fetchPayload( + createRequestOptions( + { + url: serviceConfig.ENDPOINTS.bmtrMonthPoints, + }, + requestOptions, + ), + "BMTR信息加载失败", + ); + const bmtrValue = pickFirstValue(data, ["bmtr"]); + + return { + available: bmtrValue === "" ? "0.0000" : String(bmtrValue), + address: String(pickFirstValue(data, ["address"]) || ""), + }; +} + +export async function fetchBmtrRecords( + paginationOrRequestOptions, + requestOptions, +) { + const params = resolvePagingArguments( + paginationOrRequestOptions, + requestOptions, + ); + const data = await fetchPayload( + createRequestOptions( + { + url: serviceConfig.ENDPOINTS.bmtrChargeList, + data: buildPagingRequestData({}, params.pagination), + }, + params.requestOptions, + ), + "BMTR记录加载失败", + ); + + return { + records: mapBmtrRecords(data), + pagination: buildPaginationMeta(data, params.pagination), + }; +} diff --git a/config/service.js b/config/service.js index a938e3f..32a4062 100644 --- a/config/service.js +++ b/config/service.js @@ -16,6 +16,7 @@ const serviceConfig = { powerExchangeMuit: "/api/hn/redeem/getMuit", bmtRedeemPowerRate: "/api/hn/redeem/getRedeemPowerRate", bmtExchangeSubmit: "/api/hn/redeem/redeem_bmt", + getUser: "/api/mxj/api/get_user", transferFee: "/api/hn/transfer/getProp", transferUser: "/api/hn/transfer/getUserInfo", transferPowerSubmit: "/api/hn/transfer/transferPower", @@ -33,6 +34,8 @@ const serviceConfig = { pointsConvertAvailableList: "/api/integral/transferList", pointsConvertSubmit: "/api/integral/doTransfer", pointsConvertRecordDetail: "/api/integral/transferInfo", + bmtrMonthPoints: "/api/mxj/api/get_month_points", + bmtrChargeList: "/api/mxj/api/get_charge_list", }, }; diff --git a/manifest.json b/manifest.json index ff5e05f..b9c6ccd 100644 --- a/manifest.json +++ b/manifest.json @@ -73,6 +73,15 @@ "router" : { "base" : "/bmt/", "mode" : "history" + }, + "devServer" : { + "proxy" : { + "/api" : { + "target" : "https://tpoint.agrimedia.cn", + "changeOrigin" : true, + "secure" : false + } + } } } } diff --git a/pages.json b/pages.json index 3f8bdcf..9df4f42 100644 --- a/pages.json +++ b/pages.json @@ -85,6 +85,13 @@ "navigationStyle": "custom", "backgroundColor": "#191E32" } + }, + { + "path": "pages/assets/bmtr", + "style": { + "navigationStyle": "custom", + "backgroundColor": "#191E32" + } } ], "globalStyle": { diff --git a/pages/assets/bmtr.vue b/pages/assets/bmtr.vue new file mode 100644 index 0000000..6749056 --- /dev/null +++ b/pages/assets/bmtr.vue @@ -0,0 +1,535 @@ + + + + + diff --git a/pages/index/index.vue b/pages/index/index.vue index 4087468..947b7ea 100644 --- a/pages/index/index.vue +++ b/pages/index/index.vue @@ -5,12 +5,9 @@ {{ overview.title || "数字资产" }} - + - 钱包 + 钱包1 @@ -19,7 +16,8 @@ - + @@ -29,7 +27,8 @@ - + @@ -39,7 +38,8 @@ - + @@ -63,13 +63,8 @@ - + {{ item.title }} @@ -86,21 +81,11 @@ 我的资产 - - + + {{ item.title }} @@ -114,19 +99,11 @@ 功能中心 - + - + {{ feature.title }} @@ -136,15 +113,12 @@ - + {{ overview.notice }} - - + + + @@ -222,6 +196,7 @@ export default { 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/suanli-icon.png", + bmtr: "https://imgs.agrimedia.cn/webimg/202607171808386371853.svg", }; return iconMap[key] || ""; @@ -233,6 +208,7 @@ export default { voucher: "https://imgs.agrimedia.cn/bm-bmt/quan-bg.png", coupon: "https://imgs.agrimedia.cn/bm-bmt/xiaofei-bg.png", power: "https://imgs.agrimedia.cn/bm-bmt/suanli-bg.png", + bmtr: "https://imgs.agrimedia.cn/webimg/202607201122331061368.svg", }; return bgMap[key] || ""; @@ -308,6 +284,7 @@ export default { const urlMap = { points: "/pages/assets/ledger?type=points", power: "/pages/assets/ledger?type=power-flow", + bmtr: "/pages/assets/bmtr", }; const targetUrl = urlMap[item.key]; @@ -446,6 +423,7 @@ export default { width: 30rpx; height: 32rpx; margin-right: 8rpx; + image { width: 30rpx; height: 32rpx; @@ -479,12 +457,10 @@ export default { 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% - ); + 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; } @@ -644,6 +620,7 @@ export default { 0% { transform: translateX(0); } + 100% { transform: translateX(900rpx); } @@ -660,13 +637,10 @@ export default { height: 380rpx; border-radius: 28rpx; background: - linear-gradient( - 180deg, + linear-gradient(180deg, rgba(5, 12, 28, 0.06) 0%, - rgba(5, 12, 28, 0.16) 100% - ), - url("https://imgs.agrimedia.cn/bm-bmt/home2-bg.png") - no-repeat; + rgba(5, 12, 28, 0.16) 100%), + url("https://imgs.agrimedia.cn/bm-bmt/home2-bg.png") no-repeat; border: 1px solid rgba(112, 135, 196, 0.18); background-size: cover; box-shadow: 0 24rpx 48rpx rgba(7, 12, 32, 0.28); @@ -710,11 +684,9 @@ export default { min-height: 126rpx; padding: 22rpx 22rpx 20rpx; border-radius: 8rpx; - background: linear-gradient( - 180deg, - rgba(42, 55, 82, 0.98) 0%, - rgba(39, 51, 76, 0.98) 100% - ); + background: linear-gradient(180deg, + rgba(42, 55, 82, 0.98) 0%, + rgba(39, 51, 76, 0.98) 100%); border: 1px solid rgba(121, 139, 190, 0.16); box-shadow: 0 14rpx 24rpx rgba(5, 11, 29, 0.2); } @@ -742,19 +714,15 @@ export default { } .stat-card__icon-box--gold { - background: linear-gradient( - 135deg, - rgba(20, 182, 255, 0.22) 0%, - rgba(20, 182, 255, 0.08) 100% - ); + background: linear-gradient(135deg, + rgba(20, 182, 255, 0.22) 0%, + rgba(20, 182, 255, 0.08) 100%); } .stat-card__icon-box--green { - background: linear-gradient( - 135deg, - rgba(46, 233, 167, 0.22) 0%, - rgba(46, 233, 167, 0.08) 100% - ); + background: linear-gradient(135deg, + rgba(46, 233, 167, 0.22) 0%, + rgba(46, 233, 167, 0.08) 100%); } .stat-card__body { @@ -844,9 +812,9 @@ export default { background: rgba(35, 45, 79, 0.9); } -.asset-mini-card--full { - grid-column: 1 / -1; -} +// .asset-mini-card--full { +// grid-column: 1 / -1; +// } .asset-mini-card__bg { position: absolute; diff --git a/uni.webview.1.5.8.js b/uni.webview.1.5.8.js new file mode 100644 index 0000000..052bce1 --- /dev/null +++ b/uni.webview.1.5.8.js @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + AtomGit - 全球开发者的开源社区,开源代码托管平台 + + + + + + + + + + + favicon +
+ + + + + + + + + + + + +