周五定时更新
This commit is contained in:
@@ -7,6 +7,7 @@ ENV = 'development'
|
||||
#开发环境
|
||||
VUE_APP_BASE_API = 'https://test.m2pool.com/api/'
|
||||
# VUE_APP_BASE_API = 'http://18.183.240.108:8080/api/'
|
||||
# VUE_APP_BASE_API = 'http://10.168.2.220:8888'
|
||||
VUE_APP_BASE_URL = 'https://test.m2pool.com/'
|
||||
# 路由懒加载
|
||||
VUE_CLI_BABEL_TRANSPILE_MODULES = true
|
||||
|
||||
@@ -5,7 +5,8 @@ VUE_APP_TITLE = m2pool
|
||||
ENV = 'production'
|
||||
|
||||
# 生产环境
|
||||
VUE_APP_BASE_API = 'https://m2pool.com/api/'
|
||||
# VUE_APP_BASE_API = 'https://m2pool.com/api/'
|
||||
VUE_APP_BASE_API = 'http://10.168.2.220:8888'
|
||||
VUE_APP_BASE_URL = 'https://m2pool.com/'
|
||||
|
||||
# 路由懒加载
|
||||
|
||||
@@ -7,9 +7,10 @@ NODE_ENV = production
|
||||
ENV = 'staging'
|
||||
|
||||
# 测试环境
|
||||
# VUE_APP_BASE_API = 'http://18.183.240.108:8080/api/'
|
||||
VUE_APP_BASE_API = 'https://test.m2pool.com/api/'
|
||||
VUE_APP_BASE_API = 'http://10.168.2.220:8888'
|
||||
# VUE_APP_BASE_API = 'https://test.m2pool.com/api/'
|
||||
VUE_APP_BASE_URL = 'https://test.m2pool.com/'
|
||||
|
||||
|
||||
# 路由懒加载
|
||||
VUE_CLI_BABEL_TRANSPILE_MODULES = true
|
||||
@@ -18,6 +18,9 @@ body{
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
text-align: center;
|
||||
color: #2c3e50;
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
nav {
|
||||
|
||||
@@ -25,4 +25,10 @@ body,html,*{
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.el-main{
|
||||
// background: palegoldenrod;
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
// overflow-x: hidden;
|
||||
}
|
||||
</style>
|
||||
@@ -91,12 +91,11 @@ export function deleteShopConfig(data) {
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 批量删除购物车中已下架商品
|
||||
export function deleteBatchGoodsForIsDelete(data) {
|
||||
// 钱包配置(用于修改卖家钱包地址)----获取链(一级)和币(二级) 下拉列表(获取本系统支持的链和币种)
|
||||
export function getChainAndCoin(data) {
|
||||
return request({
|
||||
url: `/lease/shopping/cart/deleteBatchGoodsForIsDelete`,
|
||||
url: `/lease/shop/getChainAndCoin`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
@@ -107,3 +106,8 @@ export function deleteBatchGoodsForIsDelete(data) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
BIN
power_leasing/src/assets/imgs/commodity.png
Normal file
BIN
power_leasing/src/assets/imgs/commodity.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 57 KiB |
@@ -124,8 +124,8 @@ export const accountRoutes = [
|
||||
name: 'accountShopConfig',
|
||||
component: () => import('../views/account/shopConfig.vue'),
|
||||
meta: {
|
||||
title: '店铺配置',
|
||||
description: '配置店铺收款和支付方式',
|
||||
title: '钱包绑定',
|
||||
description: '绑定店铺收款钱包',
|
||||
allAuthority: ['all']
|
||||
}
|
||||
},
|
||||
@@ -180,7 +180,7 @@ export const accountRoutes = [
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'purchased-detail/:orderItemId',
|
||||
path: 'purchased-detail/:id',
|
||||
name: 'PurchasedDetail',
|
||||
component: () => import('../views/account/purchasedDetail.vue'),
|
||||
meta: {
|
||||
|
||||
@@ -46,7 +46,7 @@ function decryptData(encryptedText, secretKey) {
|
||||
return {
|
||||
// 敏感数据(已解密)
|
||||
token: sensitiveData?.token || '',
|
||||
userEmail: sensitiveData?.userEmail || '',
|
||||
leasEmail: sensitiveData?.leasEmail || '',
|
||||
userId: sensitiveData?.userId || '',
|
||||
timestamp: sensitiveData?.timestamp || null,
|
||||
|
||||
@@ -61,8 +61,8 @@ function decryptData(encryptedText, secretKey) {
|
||||
/**
|
||||
* 执行自动登录
|
||||
*/
|
||||
function performAutoLogin(token, userId, userEmail) {
|
||||
console.log('执行自动登录:', { userId, userEmail: userEmail ? '***' : '' });
|
||||
function performAutoLogin(token, userId, leasEmail) {
|
||||
console.log('执行自动登录:', { userId, leasEmail: leasEmail ? '***' : '' });
|
||||
// 这里可以添加自动登录的逻辑
|
||||
// 例如:设置全局状态、跳转页面等
|
||||
}
|
||||
@@ -83,7 +83,7 @@ function decryptData(encryptedText, secretKey) {
|
||||
console.log(params.token,"params.token 存入");
|
||||
|
||||
localStorage.setItem('token', params.token);
|
||||
localStorage.setItem('userEmail', params.userEmail);
|
||||
localStorage.setItem('leasEmail', params.leasEmail);
|
||||
localStorage.setItem('userId', params.userId);
|
||||
localStorage.setItem('language', params.language);
|
||||
localStorage.setItem('username', params.username);
|
||||
@@ -93,7 +93,7 @@ function decryptData(encryptedText, secretKey) {
|
||||
|
||||
console.log('接收到的参数:', {
|
||||
userId: params.userId ? '***' : '',
|
||||
userEmail: params.userEmail ? '***' : '',
|
||||
leasEmail: params.leasEmail ? '***' : '',
|
||||
token: params.token ? '***' : '',
|
||||
language: params.language,
|
||||
username: params.username,
|
||||
@@ -103,7 +103,7 @@ function decryptData(encryptedText, secretKey) {
|
||||
// 根据参数执行相应操作
|
||||
if (params.token && params.userId) {
|
||||
// 执行自动登录
|
||||
performAutoLogin(params.token, params.userId, params.userEmail);
|
||||
performAutoLogin(params.token, params.userId, params.leasEmail);
|
||||
}
|
||||
|
||||
if (params.language) {
|
||||
|
||||
@@ -28,8 +28,23 @@
|
||||
<el-table-column label="总金额(USDT)" min-width="140">
|
||||
<template #default="scope"><span class="value strong">{{ (scope.row && scope.row.totalPrice) != null ? scope.row.totalPrice : '—' }}</span></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="已支付金额(USDT)" min-width="140">
|
||||
<template #default="scope"><span class="value strong">{{ (scope.row && scope.row.payAmount) != null ? scope.row.payAmount : '—' }}</span></template>
|
||||
<el-table-column min-width="180">
|
||||
<template #header>
|
||||
<el-tooltip placement="top" effect="dark">
|
||||
<div slot="content">
|
||||
实际支付金额/理论支付金额:<br/>
|
||||
1. 实际支付金额是按照矿机实际算力计算支付金额<br/>
|
||||
2. 理论支付金额是卖家定义出售价格
|
||||
</div>
|
||||
<span style="display:inline-flex;align-items:center;gap:6px;">
|
||||
<i class="el-icon-question" style="color:#909399;" aria-label="说明" role="img"></i>
|
||||
已支付金额(USDT)
|
||||
</span>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<span class="value strong">{{ (scope.row && scope.row.payAmount) != null ? scope.row.payAmount : '—' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="待支付金额(USDT)" min-width="140">
|
||||
<template #default="scope"><span class="value strong">{{ (scope.row && scope.row.noPayAmount) != null ? scope.row.noPayAmount : '—' }}</span></template>
|
||||
@@ -65,7 +80,18 @@
|
||||
<el-dialog :visible.sync="dialogVisible" width="520px" title="请扫码支付">
|
||||
<div style="text-align:left; margin-bottom:12px; color:#666;">
|
||||
<div style="margin-bottom:6px;">总金额(USDT):<b>{{ paymentDialog.totalPrice }}</b></div>
|
||||
<div style="margin-bottom:6px;">已支付金额(USDT):<b class="value strong">{{ paymentDialog.payAmount }}</b></div>
|
||||
<div style="margin-bottom:6px;display:flex;align-items:center;gap:6px;">
|
||||
<el-tooltip placement="top" effect="dark">
|
||||
<div slot="content">
|
||||
实际支付金额/理论支付金额:<br/>
|
||||
1. 实际支付金额是按照矿机实际算力计算支付金额<br/>
|
||||
2. 理论支付金额是卖家定义出售价格
|
||||
</div>
|
||||
<i class="el-icon-question" style="color:#909399;" aria-label="说明" role="img"></i>
|
||||
</el-tooltip>
|
||||
<span>已支付金额(USDT):</span>
|
||||
<b class="value strong">{{ paymentDialog.payAmount }}</b>
|
||||
</div>
|
||||
<div style="margin-bottom:6px;">待支付金额(USDT):<b class="value strong">{{ paymentDialog.noPayAmount }}</b></div>
|
||||
<!-- <div style="word-break:break-all;">收款地址:<code>{{ orderDialog.address }}</code></div> -->
|
||||
</div>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<div v-for="(row, idx) in rechargeRows" :key="getRowKey(row, idx)" class="record-item" :class="statusClass(row.status)" @click="toggleExpand('recharge', row, idx)">
|
||||
<div class="item-main">
|
||||
<div class="item-left">
|
||||
<div class="amount">+ {{ formatTrunc(row.amount, 2) }} {{ row.fromSymbol || 'USDT' }}</div>
|
||||
<div class="amount">+ {{ formatDec6(row.amount) }} {{ (row.fromSymbol || 'USDT').toUpperCase() }}</div>
|
||||
<div class="chain">{{ formatChain(row.fromChain) }}</div>
|
||||
</div>
|
||||
<div class="item-right">
|
||||
@@ -23,8 +23,20 @@
|
||||
</div>
|
||||
<div v-show="isExpanded('recharge', row, idx)" class="expand-panel">
|
||||
<div class="expand-grid">
|
||||
<div class="expand-item"><span class="label">充值地址</span><span class="value mono-ellipsis" :title="row.fromAddress">{{ row.fromAddress }}</span></div>
|
||||
<div class="expand-item" v-if="row.txHash"><span class="label">交易哈希</span><span class="value mono-ellipsis" :title="row.txHash">{{ row.txHash }}</span></div>
|
||||
<div class="expand-item">
|
||||
<span class="label">充值地址</span>
|
||||
<div class="value value-row">
|
||||
<span class="mono-ellipsis" :title="row.fromAddress">{{ row.fromAddress }}</span>
|
||||
<el-button type="text" size="mini" icon="el-icon-document-copy" @click.stop="handleCopy(row.fromAddress, '充值地址')">复制</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="expand-item" v-if="row.txHash">
|
||||
<span class="label">交易哈希</span>
|
||||
<div class="value value-row">
|
||||
<span class="mono-ellipsis" :title="row.txHash">{{ row.txHash }}</span>
|
||||
<el-button type="text" size="mini" icon="el-icon-document-copy" @click.stop="handleCopy(row.txHash, '交易哈希')">复制</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -42,7 +54,7 @@
|
||||
<div v-for="(row, idx) in withdrawRows" :key="getRowKey(row, idx)" class="record-item" :class="statusClass(row.status)" @click="toggleExpand('withdraw', row, idx)">
|
||||
<div class="item-main">
|
||||
<div class="item-left">
|
||||
<div class="amount">- {{ formatTrunc(row.amount, 2) }} {{ row.toSymbol || 'USDT' }}</div>
|
||||
<div class="amount">- {{ formatDec6(row.amount) }} {{ (row.toSymbol || 'USDT').toUpperCase() }}</div>
|
||||
<div class="chain">{{ formatChain(row.toChain) }}</div>
|
||||
</div>
|
||||
<div class="item-right">
|
||||
@@ -52,8 +64,20 @@
|
||||
</div>
|
||||
<div v-show="isExpanded('withdraw', row, idx)" class="expand-panel">
|
||||
<div class="expand-grid">
|
||||
<div class="expand-item"><span class="label">收款地址</span><span class="value mono-ellipsis" :title="row.toAddress">{{ row.toAddress }}</span></div>
|
||||
<div class="expand-item" v-if="row.txHash"><span class="label">交易哈希</span><span class="value mono-ellipsis" :title="row.txHash">{{ row.txHash }}</span></div>
|
||||
<div class="expand-item">
|
||||
<span class="label">收款地址</span>
|
||||
<div class="value value-row">
|
||||
<span class="mono-ellipsis" :title="row.toAddress">{{ row.toAddress }}</span>
|
||||
<el-button type="text" size="mini" icon="el-icon-document-copy" @click.stop="handleCopy(row.toAddress, '收款地址')">复制</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="expand-item" v-if="row.txHash">
|
||||
<span class="label">交易哈希</span>
|
||||
<div class="value value-row">
|
||||
<span class="mono-ellipsis" :title="row.txHash">{{ row.txHash }}</span>
|
||||
<el-button type="text" size="mini" icon="el-icon-document-copy" @click.stop="handleCopy(row.txHash, '交易哈希')">复制</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -71,7 +95,7 @@
|
||||
<div v-for="(row, idx) in consumeRows" :key="getRowKey(row, idx)" class="record-item" :class="statusClass(row.status)" @click="toggleExpand('consume', row, idx)">
|
||||
<div class="item-main">
|
||||
<div class="item-left">
|
||||
<div class="amount">- {{ formatTrunc(row.realAmount, 2) }} {{ (row.fromSymbol || 'USDT').toUpperCase() }}</div>
|
||||
<div class="amount">- {{ formatDec6(row.realAmount) }} {{ (row.fromSymbol || 'USDT').toUpperCase() }}</div>
|
||||
<div class="chain">{{ formatChain(row.fromChain) }}</div>
|
||||
</div>
|
||||
<div class="item-right">
|
||||
@@ -138,7 +162,7 @@ export default {
|
||||
// },
|
||||
// {
|
||||
// createTime: '2024-01-15 14:30:25',
|
||||
// amount: 100,
|
||||
// amount: 100.656578965,
|
||||
// fromAddress: 'djdddksfhsfj',
|
||||
// fromChain: 'tron',
|
||||
// fromSymbol: 'USDT',
|
||||
@@ -345,9 +369,16 @@ export default {
|
||||
getPayStatusType(s) { return ({ 0: 'danger', 1: 'success', 2: 'warning', 3: 'danger' })[s] || 'info' },
|
||||
getPayStatusText(s) { return ({ 0: '支付失败', 1: '支付成功', 2: '待校验', 3: '证书校验失败' })[s] || '未知' },
|
||||
|
||||
/**
|
||||
* 将链名称标准化为大写简称
|
||||
* @param {string} chain - 后端返回的链标识,如 tron/eth/ethereum/bsc/polygon
|
||||
* @returns {string} 大写显示,如 TRON/ETH/BSC/POLYGON
|
||||
*/
|
||||
formatChain(chain) {
|
||||
const map = { tron: 'Tron (TRC20)', ethereum: 'Ethereum (ERC20)', bsc: 'BSC (BEP20)', polygon: 'Polygon (MATIC)' }
|
||||
return map[chain] || chain
|
||||
if (!chain) return ''
|
||||
const key = String(chain).toLowerCase()
|
||||
const map = { tron: 'TRON', trx: 'TRON', eth: 'ETH', ethereum: 'ETH', bsc: 'BSC', polygon: 'POLYGON', matic: 'POLYGON' }
|
||||
return (map[key] || String(chain)).toUpperCase()
|
||||
},
|
||||
formatFullTime(time) { if (!time) return ''; try { return new Date(time).toLocaleString('zh-CN') } catch (e) { return String(time) } },
|
||||
formatTime(time) { return this.formatFullTime(time) },
|
||||
@@ -363,6 +394,27 @@ export default {
|
||||
const padded = decPart.padEnd(d, '0')
|
||||
return `${intPart}.${padded}`
|
||||
},
|
||||
/**
|
||||
* 金额显示:保留最多6位小数,直接截断不四舍五入;不补尾随0;始终返回非负字符串
|
||||
* @param {number|string} value
|
||||
* @returns {string}
|
||||
*/
|
||||
formatDec6(value) {
|
||||
if (value === null || value === undefined || value === '') return '0'
|
||||
let s = String(value)
|
||||
// 展开科学计数法为普通小数,避免 1e-7 之类展示
|
||||
if (/e/i.test(s)) {
|
||||
const n = Number(value)
|
||||
if (!Number.isFinite(n)) return '0'
|
||||
s = n.toFixed(20).replace(/\.0+$/, '').replace(/(\.\d*?)0+$/, '$1')
|
||||
}
|
||||
const m = s.match(/^(-?)(\d+)(?:\.(\d+))?$/)
|
||||
if (!m) return s
|
||||
let intPart = m[2]
|
||||
let decPart = m[3] || ''
|
||||
if (decPart.length > 6) decPart = decPart.slice(0, 6)
|
||||
return decPart ? `${intPart}.${decPart}` : intPart
|
||||
},
|
||||
handleSizeChange(val) {
|
||||
console.log(`每页 ${val} 条`);
|
||||
this.pagination.pageSize = val;
|
||||
@@ -376,6 +428,33 @@ export default {
|
||||
this.loadList();
|
||||
},
|
||||
|
||||
/**
|
||||
* 复制文本到剪贴板
|
||||
* @param {string} text - 需要复制的内容
|
||||
* @param {string} [label] - 语义标签,用于提示文案
|
||||
*/
|
||||
async handleCopy(text, label = '内容') {
|
||||
try {
|
||||
const value = String(text || '')
|
||||
if (navigator && navigator.clipboard && navigator.clipboard.writeText) {
|
||||
await navigator.clipboard.writeText(value)
|
||||
} else {
|
||||
const ta = document.createElement('textarea')
|
||||
ta.value = value
|
||||
ta.style.position = 'fixed'
|
||||
ta.style.left = '-9999px'
|
||||
document.body.appendChild(ta)
|
||||
ta.focus()
|
||||
ta.select()
|
||||
document.execCommand('copy')
|
||||
document.body.removeChild(ta)
|
||||
}
|
||||
this.$message.success(`${label}已复制`)
|
||||
} catch (e) {
|
||||
this.$message.error('复制失败,请手动选择复制')
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Tab 名称转状态码
|
||||
* @param {string} tabName - 'recharge' | 'withdraw' | 'consume'
|
||||
@@ -434,6 +513,7 @@ export default {
|
||||
.expand-item { display: grid; grid-template-columns: 80px 1fr; gap: 6px; align-items: center; }
|
||||
.label { color: #666; font-size: 13px; text-align: right; }
|
||||
.value { color: #333; font-size: 13px; text-align: left; }
|
||||
.value-row { display: inline-flex; align-items: center; gap: 6px; }
|
||||
.mono { font-family: "Monaco", "Menlo", monospace; }
|
||||
.mono-ellipsis { font-family: "Monaco", "Menlo", monospace; max-width: 480px; display: inline-block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.empty { text-align: center; color: #999; padding: 20px 0; }
|
||||
|
||||
@@ -38,8 +38,7 @@
|
||||
v-for="item in displayedLinks"
|
||||
:key="item.to"
|
||||
:to="item.to"
|
||||
class="side-link"
|
||||
active-class="active"
|
||||
:class="['side-link', isActiveLink(item.to) ? 'active' : '']"
|
||||
>{{ item.label }}</router-link>
|
||||
</nav>
|
||||
</aside>
|
||||
@@ -104,13 +103,16 @@ export default {
|
||||
if (raw == null) return null
|
||||
try { return JSON.parse(raw) } catch (e) { return raw }
|
||||
}
|
||||
const val = getVal('userName') || getVal('userEmail') || ''
|
||||
const val =getVal('leasEmail') || ''
|
||||
this.userEmail = typeof val === 'string' ? val : String(val)
|
||||
// 恢复上次选择的导航分组(如无则默认 seller)
|
||||
const savedRole = getVal('accountActiveRole')
|
||||
if (savedRole === 'buyer' || savedRole === 'seller') {
|
||||
this.activeRole = savedRole
|
||||
this.activeRole = savedRole
|
||||
|
||||
}
|
||||
// 根据当前路由自动匹配分组,确保左侧导航高亮正确
|
||||
this.setActiveRoleByRoute()
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
@@ -122,8 +124,78 @@ export default {
|
||||
if (role !== 'buyer' && role !== 'seller') return
|
||||
this.activeRole = role
|
||||
try { localStorage.setItem('accountActiveRole', JSON.stringify(role)) } catch (e) {}
|
||||
// 切换分组后,立即跳转到该分组的第一个导航页面
|
||||
try {
|
||||
const firstPath = role === 'buyer'
|
||||
? (this.buyerLinks && this.buyerLinks[0] && this.buyerLinks[0].to)
|
||||
: (this.sellerLinks && this.sellerLinks[0] && this.sellerLinks[0].to)
|
||||
if (firstPath && this.$route && this.$route.path !== firstPath) {
|
||||
this.$router.push(firstPath)
|
||||
}
|
||||
} catch (e) { /* noop */ }
|
||||
},
|
||||
/**
|
||||
* 根据当前路由自动选择导航分组,保证进入“我的店铺”等卖家页面时左侧高亮正确
|
||||
*/
|
||||
setActiveRoleByRoute() {
|
||||
const path = (this.$route && this.$route.path) || ''
|
||||
// 买家前缀优先匹配,确保“已购详情”等页面归属买家侧
|
||||
const buyerPrefixes = [
|
||||
'/account/wallet',
|
||||
'/account/purchased',
|
||||
'/account/purchased-detail',
|
||||
'/account/orders',
|
||||
'/account/funds-flow'
|
||||
]
|
||||
const sellerPrefixes = [
|
||||
'/account/shops',
|
||||
'/account/shop-new',
|
||||
'/account/product-new',
|
||||
'/account/products',
|
||||
'/account/product-detail',
|
||||
'/account/product-machine-add',
|
||||
'/account/seller-orders',
|
||||
'/account/order-detail',
|
||||
'/account/receipt-record',
|
||||
'/account/shop-config'
|
||||
]
|
||||
const shouldBuyer = buyerPrefixes.some(p => path.indexOf(p) === 0)
|
||||
const shouldSeller = sellerPrefixes.some(p => path.indexOf(p) === 0)
|
||||
const role = shouldBuyer ? 'buyer' : (shouldSeller ? 'seller' : this.activeRole)
|
||||
if (this.activeRole !== role) {
|
||||
this.activeRole = role
|
||||
try { localStorage.setItem('accountActiveRole', JSON.stringify(role)) } catch (e) {}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 判断左侧导航项是否高亮
|
||||
* - 普通路径完全匹配
|
||||
* - “已售出订单”需同时匹配详情页 /account/order-detail/:id
|
||||
*/
|
||||
isActiveLink(pathLike) {
|
||||
const current = (this.$route && this.$route.path) || ''
|
||||
if (!pathLike) return false
|
||||
// 列表-详情联动高亮映射
|
||||
const map = {
|
||||
'/account/seller-orders': ['/account/seller-orders', '/account/order-detail'],
|
||||
'/account/products': ['/account/products', '/account/product-detail'],
|
||||
'/account/purchased': ['/account/purchased', '/account/purchased-detail']
|
||||
}
|
||||
const prefixes = map[pathLike]
|
||||
if (Array.isArray(prefixes)) {
|
||||
return prefixes.some(p => current.indexOf(p) === 0)
|
||||
}
|
||||
return current.indexOf(pathLike) === 0
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'$route.path': {
|
||||
immediate: true,
|
||||
handler() {
|
||||
this.setActiveRoleByRoute()
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -172,6 +244,7 @@ export default {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 8px;
|
||||
margin-top: 18px;
|
||||
}
|
||||
.role-button {
|
||||
appearance: none;
|
||||
|
||||
@@ -134,17 +134,17 @@
|
||||
<el-dialog title="修改配置" :visible.sync="visibleConfigEdit" width="560px">
|
||||
<div class="row">
|
||||
<label class="label">支付链</label>
|
||||
<el-select v-model="configForm.chain" placeholder="请选择链">
|
||||
<el-option v-for="c in chainOptions" :key="c.value" :value="c.value" :label="c.label" />
|
||||
</el-select>
|
||||
<el-input v-model="configForm.chainLabel" placeholder="-" disabled />
|
||||
</div>
|
||||
<div class="row">
|
||||
<label class="label">支付币种</label>
|
||||
<el-select
|
||||
class="input"
|
||||
size="middle"
|
||||
ref="screen"
|
||||
v-model="configForm.payCoin"
|
||||
v-model="configForm.payCoins"
|
||||
multiple
|
||||
collapse-tags
|
||||
filterable
|
||||
placeholder="请选择币种"
|
||||
>
|
||||
<el-option
|
||||
@@ -152,20 +152,22 @@
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
>
|
||||
<div style="display: flex; align-items: center">
|
||||
<img v-if="item.imgUrl" :src="item.imgUrl" style="float: left; width: 20px" />
|
||||
<span style="float: left; margin-left: 5px">{{ item.label }}</span>
|
||||
</div>
|
||||
</el-option>
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label class="label">币种类型</label>
|
||||
<el-radio-group v-model="configForm.payType">
|
||||
<el-radio :label="0">虚拟币</el-radio>
|
||||
<el-radio :label="1">稳定币</el-radio>
|
||||
</el-radio-group>
|
||||
<label class="label">已选择币种</label>
|
||||
<div class="selected-coin-list">
|
||||
<el-tag
|
||||
v-for="c in selectedCoinLabels"
|
||||
:key="c"
|
||||
type="warning"
|
||||
effect="light"
|
||||
closable
|
||||
@close="removeSelectedCoin(c)"
|
||||
>{{ c }}</el-tag>
|
||||
<span v-if="!selectedCoinLabels.length" style="color:#c0c4cc">未选择</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label class="label">钱包地址</label>
|
||||
@@ -173,7 +175,7 @@
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="visibleConfigEdit=false">取消</el-button>
|
||||
<el-button type="primary" @click="submitConfigEdit">保存</el-button>
|
||||
<el-button type="primary" @click="submitConfigEdit">确认修改</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
@@ -181,7 +183,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getMyShop, updateShop, deleteShop, queryShop, closeShop ,updateShopConfig,deleteShopConfig} from '@/api/shops'
|
||||
import { getMyShop, updateShop, deleteShop, queryShop, closeShop ,updateShopConfig,deleteShopConfig,getChainAndCoin} from '@/api/shops'
|
||||
|
||||
import { coinList } from '@/utils/coinList'
|
||||
import { getShopConfig } from '@/api/wallet'
|
||||
@@ -206,9 +208,10 @@ export default {
|
||||
// 店铺配置列表
|
||||
shopConfigs: [],
|
||||
visibleConfigEdit: false,
|
||||
configForm: { id: '', chain: '', payAddress: '', payCoin: '', payType: 0 },
|
||||
configForm: { id: '', chainLabel: '', chainValue: '', payAddress: '', payCoins: [], payCoin: '' },
|
||||
productOptions: [],
|
||||
coinOptions: coinList || [],
|
||||
editCoinOptionsApi: [],
|
||||
// 支付链选项(可与后端接口对齐后替换为动态)
|
||||
chainOptions: [
|
||||
{ label: 'Tron (TRC20)', value: 'tron' },
|
||||
@@ -244,14 +247,12 @@ export default {
|
||||
* 弹窗可选币种:稳定币/虚拟币分流
|
||||
*/
|
||||
editCoinOptions() {
|
||||
if (Number(this.configForm.payType) === 1) {
|
||||
return [
|
||||
{ label: 'USDT', value: 'usdt' },
|
||||
{ label: 'USDC', value: 'usdc' },
|
||||
{ label: 'BUSD', value: 'busd' },
|
||||
]
|
||||
}
|
||||
if (Array.isArray(this.editCoinOptionsApi) && this.editCoinOptionsApi.length) return this.editCoinOptionsApi
|
||||
return this.coinOptions
|
||||
},
|
||||
selectedCoinLabels() {
|
||||
const map = new Map((this.editCoinOptions || []).map(o => [String(o.value), String(o.label).toUpperCase()]))
|
||||
return (this.configForm.payCoins || []).map(v => map.get(String(v)) || String(v).toUpperCase())
|
||||
}
|
||||
},
|
||||
created() {
|
||||
@@ -338,8 +339,6 @@ export default {
|
||||
this.$message.success('保存成功')
|
||||
this.visibleConfigEdit = false
|
||||
this.fetchShopConfigs(this.shop.id)
|
||||
} else {
|
||||
this.$message.error(res && res.msg ? res.msg : '保存失败')
|
||||
}
|
||||
},
|
||||
async deleteShopConfig(params) {
|
||||
@@ -349,16 +348,41 @@ export default {
|
||||
this.fetchShopConfigs(this.shop.id)
|
||||
}
|
||||
},
|
||||
handleEditConfig(row) {
|
||||
this.configForm = {
|
||||
id: row.id,
|
||||
chain: row.chain || '',
|
||||
payCoin: row.payCoin || '',
|
||||
payType: typeof row.payType === 'number' ? row.payType : Number(row.payType || 0),
|
||||
payAddress: row.payAddress || '',
|
||||
async handleEditConfig(row) {
|
||||
try {
|
||||
const res = await getChainAndCoin({ id: row.id })
|
||||
if (res && (res.code === 0 || res.code === 200) && res.data) {
|
||||
const d = res.data || {}
|
||||
const children = Array.isArray(d.children) ? d.children : []
|
||||
this.editCoinOptionsApi = children.map(c => ({ label: c.label, value: c.value }))
|
||||
const preSelected = children.filter(c => Number(c.hasBind) === 1).map(c => c.value)
|
||||
this.configForm = {
|
||||
id: row.id,
|
||||
chainLabel: d.label || '',
|
||||
chainValue: d.value || '',
|
||||
payAddress: d.address || '',
|
||||
payCoins: preSelected,
|
||||
payCoin: preSelected.join(',')
|
||||
}
|
||||
} else {
|
||||
// 回退:使用行内已有数据
|
||||
this.editCoinOptionsApi = []
|
||||
const chainLabel = row.chain || ''
|
||||
const payCoinStr = String(row.payCoin || '')
|
||||
const payCoins = payCoinStr ? payCoinStr.split(',') : []
|
||||
this.configForm = {
|
||||
id: row.id,
|
||||
chainLabel,
|
||||
chainValue: row.chain || '',
|
||||
payAddress: row.payAddress || '',
|
||||
payCoins,
|
||||
payCoin: payCoins.join(',')
|
||||
}
|
||||
}
|
||||
this.visibleConfigEdit = true
|
||||
} catch (e) {
|
||||
this.visibleConfigEdit = true
|
||||
}
|
||||
this.visibleConfigEdit = true
|
||||
|
||||
},
|
||||
async handleDeleteConfig(row) {
|
||||
this.deleteShopConfig({id:row.id})
|
||||
@@ -366,11 +390,11 @@ export default {
|
||||
|
||||
submitConfigEdit() {
|
||||
// 基础校验
|
||||
if (!this.configForm.chain) {
|
||||
if (!this.configForm.chainLabel && !this.configForm.chainValue) {
|
||||
this.$message.warning('请选择支付链')
|
||||
return
|
||||
}
|
||||
if (!this.configForm.payCoin) {
|
||||
if (!this.configForm.payCoins || this.configForm.payCoins.length === 0) {
|
||||
this.$message.warning('请选择支付币种')
|
||||
return
|
||||
}
|
||||
@@ -379,10 +403,21 @@ export default {
|
||||
this.$message.warning('请输入钱包地址')
|
||||
return
|
||||
}
|
||||
const { productId, ...rest } = this.configForm
|
||||
const payload = { ...rest, payType: Number(this.configForm.payType || 0) }
|
||||
const payload = {
|
||||
id: this.configForm.id,
|
||||
chain: this.configForm.chainValue || this.configForm.chainLabel,
|
||||
payCoin: (this.configForm.payCoins || []).join(','),
|
||||
payAddress: this.configForm.payAddress
|
||||
}
|
||||
this.updateShopConfig(payload)
|
||||
},
|
||||
removeSelectedCoin(labelUpper) {
|
||||
const label = String(labelUpper || '').toLowerCase()
|
||||
const map = new Map((this.editCoinOptions || []).map(o => [String(o.label).toLowerCase(), String(o.value)]))
|
||||
const value = map.get(label)
|
||||
if (!value) return
|
||||
this.configForm.payCoins = (this.configForm.payCoins || []).filter(v => String(v) !== String(value))
|
||||
},
|
||||
async handleOpenEdit() {
|
||||
try {
|
||||
// 先打开弹窗,提供更快的视觉反馈
|
||||
@@ -627,5 +662,9 @@ export default {
|
||||
.el-dialog__footer {
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
/* 已选择币种 - 靠左对齐且换行友好 */
|
||||
.selected-coin-list { display: flex; flex-wrap: wrap; gap: 6px; justify-content: flex-start; }
|
||||
.selected-coin-list .el-tag { margin-right: 0; }
|
||||
</style>
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<el-card class="section">
|
||||
<div class="row"><span class="label">订单ID:</span><span class="value mono">{{ order.id || '—' }}</span></div>
|
||||
<div class="row"><span class="label">订单号:</span><span class="value mono">{{ order.orderNumber || '—' }}</span></div>
|
||||
<div class="row"><span class="label">状态:</span><span class="value">{{ order.status }}</span></div>
|
||||
<div class="row"><span class="label">状态:</span><span class="value">{{ getOrderStatusText(order.status) }}</span></div>
|
||||
<div class="row"><span class="label">金额(USDT):</span><span class="value strong">{{ order.totalPrice }}</span></div>
|
||||
<div class="row"><span class="label">创建时间:</span><span class="value">{{ formatDateTime(order.createTime) }}</span></div>
|
||||
</el-card>
|
||||
@@ -72,8 +72,17 @@ export default {
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
}
|
||||
,
|
||||
},
|
||||
/**
|
||||
* 订单状态文案映射
|
||||
* 7: 进行中;8: 已完成;其他:原样返回
|
||||
*/
|
||||
getOrderStatusText(value) {
|
||||
const n = Number(value)
|
||||
if (n === 7) return '进行中'
|
||||
if (n === 8) return '已完成'
|
||||
return String(value == null ? '' : value)
|
||||
},
|
||||
formatDateTime(value) {
|
||||
if (!value) return '—'
|
||||
try {
|
||||
|
||||
@@ -916,7 +916,13 @@ export default {
|
||||
|
||||
const res = await addSingleOrBatchMachine(payload)
|
||||
if (res && (res.code === 0 || res.code === 200)) {
|
||||
this.$message.success('添加成功')
|
||||
|
||||
this.$message({
|
||||
message: '添加成功',
|
||||
duration: 3000,
|
||||
showClose: true,
|
||||
type: 'success'
|
||||
})
|
||||
this.confirmVisible = false
|
||||
this.$router.back()
|
||||
}
|
||||
|
||||
@@ -190,11 +190,24 @@ export default {
|
||||
}
|
||||
}
|
||||
try {
|
||||
this.userEmail=JSON.parse(localStorage.getItem('userEmail'))
|
||||
this.userEmail=JSON.parse(localStorage.getItem('leasEmail'))
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
}
|
||||
if ( !this.userEmail) {
|
||||
this.$alert('登录信息异常,请重新返回 m2poll 矿池进入该系统', '提示', {
|
||||
confirmButtonText: '确认',
|
||||
center: true,
|
||||
closeOnClickModal: false,
|
||||
closeOnPressEscape: false,
|
||||
showClose: false,
|
||||
callback: () => {
|
||||
window.location.href = 'https://m2pool.com'
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
const params = {
|
||||
pageNum: this.pagination.pageNum,
|
||||
pageSize: this.pagination.pageSize,
|
||||
|
||||
@@ -287,8 +287,9 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.receipt-page { padding: 4px; }
|
||||
.card { background: #fff; border: 1px solid #eee; border-radius: 10px; padding: 12px; box-shadow: 0 4px 18px rgba(0,0,0,0.04); }
|
||||
|
||||
.receipt-page { margin: 0; box-sizing: border-box; overflow-x: hidden; }
|
||||
.card { background: #fff; border: 1px solid #eee; border-radius: 10px; padding: 12px; box-shadow: 0 4px 18px rgba(0,0,0,0.04); overflow-x: auto; }
|
||||
.card-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
|
||||
.card-title { margin: 0; font-size: 18px; font-weight: 700; color: #2c3e50; }
|
||||
.card-actions { display: flex; align-items: center; gap: 8px; }
|
||||
|
||||
@@ -3,16 +3,43 @@
|
||||
<h2 class="panel-title page-title">钱包绑定</h2>
|
||||
<div class="panel-body" v-loading="loading">
|
||||
<el-form :model="form" label-width="120px" class="config-form">
|
||||
<el-form-item label="选择链">
|
||||
<el-cascader style="width: 420px;" @change="handleChange" v-model="value" :options="options"> </el-cascader>
|
||||
<el-form-item label="选择链/币种">
|
||||
<el-cascader
|
||||
style="width: 420px;"
|
||||
v-model="value"
|
||||
:options="options"
|
||||
:props="cascaderProps"
|
||||
:show-all-levels="false"
|
||||
clearable
|
||||
filterable
|
||||
@change="handleChange"
|
||||
@expand-change="handleExpandChange"
|
||||
>
|
||||
<template slot-scope="{ node, data }">
|
||||
<span class="custom-node" aria-label="cascader-item" tabindex="0" @click.stop="handleItemClick(node, data)">
|
||||
<span class="node-label">{{ data.label }}</span>
|
||||
<span v-if="node.isLeaf && node.checked" class="leaf-checked" aria-hidden="true">✓</span>
|
||||
</span>
|
||||
</template>
|
||||
</el-cascader>
|
||||
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="币种类型">
|
||||
<el-radio-group v-model="form.payType" class="radio-group">
|
||||
<!-- <el-radio :label="0">虚拟币</el-radio> -->
|
||||
<el-radio :label="1">稳定币</el-radio>
|
||||
</el-radio-group>
|
||||
|
||||
<el-form-item label="已选择币种">
|
||||
<div class="selected-coins" aria-label="selected-coins" tabindex="0">
|
||||
<el-tag
|
||||
v-for="coin in selectedCoins"
|
||||
:key="coin"
|
||||
type="warning"
|
||||
effect="light"
|
||||
closable
|
||||
disable-transitions
|
||||
@close="handleRemoveSelectedCoin(coin)"
|
||||
>
|
||||
{{ coin }}
|
||||
</el-tag>
|
||||
<span v-if="selectedCoins.length === 0" class="placeholder">未选择</span>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
@@ -39,32 +66,15 @@
|
||||
import { getMyShop } from "@/api/shops";
|
||||
import { getChainAndList, addWalletShopConfig } from "../../api/wallet";
|
||||
|
||||
// 币种集合
|
||||
const VIRTUAL_COINS = [
|
||||
"nexa",
|
||||
"rxd",
|
||||
"dgbo",
|
||||
"dgbq",
|
||||
"dgbs",
|
||||
"alph",
|
||||
"enx",
|
||||
"grs",
|
||||
"mona",
|
||||
];
|
||||
const STABLE_COINS = ["usdt", "usdc", "busd"];
|
||||
|
||||
export default {
|
||||
name: "AccountShopConfig",
|
||||
data() {
|
||||
return {
|
||||
VIRTUAL_COINS,
|
||||
STABLE_COINS,
|
||||
productOptions: [],
|
||||
form: {
|
||||
chain: "",
|
||||
payAddress: "",
|
||||
payCoin: "",
|
||||
payType: 1, // 0 虚拟币 1 稳定币
|
||||
|
||||
},
|
||||
shop: {
|
||||
@@ -75,7 +85,10 @@ export default {
|
||||
del: true,
|
||||
state: 0,
|
||||
},
|
||||
value: "",
|
||||
// 级联多选值:形如 [[chain, coin], [chain, coin2]]
|
||||
value: [],
|
||||
currentChain: '',
|
||||
cascaderProps: { multiple: true, checkStrictly: false, emitPath: true, value: 'value', label: 'label', children: 'children' },
|
||||
options: [
|
||||
// {
|
||||
// value: "Tron (TRC20)",
|
||||
@@ -110,6 +123,49 @@ export default {
|
||||
this.getChainAndList();
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 关闭标签 -> 移除对应币种
|
||||
*/
|
||||
handleRemoveSelectedCoin(coinUpper) {
|
||||
const coin = String(coinUpper || '').toLowerCase()
|
||||
const next = (this.value || []).filter(v => Array.isArray(v) && String(v[1]).toLowerCase() !== coin)
|
||||
this.handleChange(next)
|
||||
},
|
||||
// 允许点击整行触发选择/展开,提升“可点文字即可选”的体验
|
||||
handleItemClick(node, data) {
|
||||
if (!node) return
|
||||
if (node.isLeaf) {
|
||||
// 叶子:切换选中
|
||||
const path = node.path.map(n => n.value)
|
||||
const chain = path[0]
|
||||
const coin = path[1]
|
||||
this.currentChain = String(chain || '')
|
||||
let next = Array.isArray(this.value) ? this.value.slice() : []
|
||||
// 始终以最后点击的链为准:如果链不同,清空旧链
|
||||
const last = next.length ? next[next.length - 1] : null
|
||||
const lastChain = Array.isArray(last) ? last[0] : null
|
||||
if (lastChain && lastChain !== chain) next = []
|
||||
const idx = next.findIndex(v => Array.isArray(v) && v[0] === chain && v[1] === coin)
|
||||
if (idx >= 0) next.splice(idx, 1)
|
||||
else next.push([chain, coin])
|
||||
this.handleChange(next)
|
||||
} else {
|
||||
// 父级:以最后点击为准,清空旧链选择并切换到当前链
|
||||
const chain = data && data.value
|
||||
if (!node.expanded) node.expand()
|
||||
if (chain) {
|
||||
this.currentChain = String(chain)
|
||||
this.value = []
|
||||
this.form.chain = String(chain)
|
||||
this.form.payCoin = ''
|
||||
}
|
||||
}
|
||||
},
|
||||
handleExpandChange(nodes) {
|
||||
// 记录当前展开的链,供 @change 过滤依据
|
||||
const chain = Array.isArray(nodes) ? (nodes[0] || '') : ''
|
||||
if (chain) this.currentChain = String(chain)
|
||||
},
|
||||
/**
|
||||
* 根据选择的链校验钱包地址格式(参考钱包页面规则)
|
||||
* - tron: 以 T 开头的 34 位字符
|
||||
@@ -123,7 +179,9 @@ export default {
|
||||
|
||||
if (c.includes('tron') || c === 'tron') {
|
||||
const ok = /^T[A-Za-z1-9]{33}$/.test(addr)
|
||||
return ok ? { ok: true } : { ok: false, message: '请输入正确的收款地址格式(TRON)' }
|
||||
return ok
|
||||
? { ok: true }
|
||||
: { ok: false, message: '请输入正确的 TRON 地址:以 T 开头的 34 位字符' }
|
||||
}
|
||||
|
||||
if (
|
||||
@@ -134,7 +192,9 @@ export default {
|
||||
c.includes('erc') || c.includes('bep')
|
||||
) {
|
||||
const ok = /^0x[a-fA-F0-9]{40}$/.test(addr)
|
||||
return ok ? { ok: true } : { ok: false, message: '请输入正确的收款地址格式(EVM)' }
|
||||
return ok
|
||||
? { ok: true }
|
||||
: { ok: false, message: '请输入正确的以太坊/EVM 兼容链地址:以 0x 开头 + 40 位十六进制' }
|
||||
}
|
||||
|
||||
if (addr.length <= 10) {
|
||||
@@ -179,17 +239,28 @@ export default {
|
||||
},
|
||||
|
||||
handleChange(value) {
|
||||
console.log(value);
|
||||
|
||||
this.form.payCoin = value[1];
|
||||
this.form.chain = value[0];
|
||||
|
||||
// 仅允许同一链下多选;无提示,始终以“最后一次点击”的链为准
|
||||
const selections = Array.isArray(value) ? value : []
|
||||
if (selections.length === 0) {
|
||||
this.form.chain = ""
|
||||
this.form.payCoin = ""
|
||||
this.value = []
|
||||
return
|
||||
}
|
||||
const last = selections[selections.length - 1]
|
||||
const lastChain = Array.isArray(last) ? last[0] : ''
|
||||
const targetChain = this.currentChain || lastChain
|
||||
const filtered = selections.filter(v => Array.isArray(v) && v[0] === targetChain)
|
||||
this.value = filtered
|
||||
this.form.chain = targetChain || ""
|
||||
this.form.payCoin = filtered.map(v => v[1]).filter(Boolean).join(',')
|
||||
},
|
||||
|
||||
handleSave() {
|
||||
|
||||
this.form.chain =this.value[0]
|
||||
this.form.payCoin = this.value[1]
|
||||
// 从多选值同步 chain 与 payCoin(英文逗号隔开)
|
||||
const selections = Array.isArray(this.value) ? this.value : []
|
||||
this.form.chain = selections.length ? (selections[0] && selections[0][0]) : ''
|
||||
this.form.payCoin = selections.map(v => v && v[1]).filter(Boolean).join(',')
|
||||
|
||||
if (!this.form.chain) {
|
||||
this.$message.warning("请选择链");
|
||||
@@ -214,19 +285,25 @@ export default {
|
||||
this.FetchAddWalletShopConfig(this.form);
|
||||
},
|
||||
handleReset() {
|
||||
this.form = { chain: "", payAddress: "", payCoin: "", payType: 0 };
|
||||
this.form = { chain: "", payAddress: "", payCoin: "" };
|
||||
this.value = []
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
// 根据币种类型动态展示可选币种
|
||||
coinOptions() {
|
||||
return this.form.payType === 1 ? STABLE_COINS : VIRTUAL_COINS;
|
||||
/**
|
||||
* 已选择币种的可读展示(中文顿号分隔)
|
||||
*/
|
||||
selectedCoinsDisplay() {
|
||||
const arr = Array.isArray(this.value) ? this.value : []
|
||||
const coins = arr.map(v => v && v[1]).filter(Boolean).map(s => String(s).toUpperCase())
|
||||
return coins.join('、')
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
"form.payType"(val) {
|
||||
// 切换类型时清空已选币种,避免类型与币种不匹配
|
||||
this.form.payCoin = "";
|
||||
/**
|
||||
* 标签列表数据
|
||||
*/
|
||||
selectedCoins() {
|
||||
const arr = Array.isArray(this.value) ? this.value : []
|
||||
return arr.map(v => v && v[1]).filter(Boolean).map(s => String(s).toUpperCase())
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -266,5 +343,15 @@ export default {
|
||||
font-size: 12px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
/* 自定义级联节点样式:叶子被选中时显示勾选 */
|
||||
.custom-node { display: inline-flex; align-items: center; gap: 8px; }
|
||||
.leaf-checked { color: #409EFF; font-weight: 700; }
|
||||
.node-label { line-height: 20px; }
|
||||
|
||||
/* 已选择币种标签样式 */
|
||||
.selected-coins { display: flex; flex-wrap: wrap; gap: 8px; min-height: 32px; align-items: center; margin-left: 79px;}
|
||||
.selected-coins .el-tag { border-radius: 4px; }
|
||||
.selected-coins .placeholder { color: #c0c4cc; }
|
||||
</style>
|
||||
|
||||
|
||||
@@ -26,6 +26,14 @@
|
||||
<span class="balance-amount">{{ (w.walletBalance || w.balance || 0) }} {{ displaySymbol(w) }}</span>
|
||||
</div>
|
||||
<div class="balance-item">
|
||||
<el-tooltip placement="top" effect="dark">
|
||||
<div slot="content">
|
||||
冻结金额不能使用或提现,以下情况会冻结钱包余额:<br/>
|
||||
1. 下单机器后会冻结订单对应金额<br/>
|
||||
2. 提交提现后,金额正在提现中
|
||||
</div>
|
||||
<i class="el-icon-question balance-tip-icon"></i>
|
||||
</el-tooltip>
|
||||
<span class="balance-label">冻结余额</span>
|
||||
<span class="balance-amount frozen">{{ (w.blockedBalance || 0) }} {{ displaySymbol(w) }}</span>
|
||||
</div>
|
||||
@@ -53,9 +61,16 @@
|
||||
<div class="transaction-info">
|
||||
<span class="transaction-type">{{ transaction.type }}</span>
|
||||
<span class="transaction-time">{{ transaction.time }}</span>
|
||||
<el-tag
|
||||
size="mini"
|
||||
class="transaction-status"
|
||||
:type="transaction.statusTagType || 'info'"
|
||||
>
|
||||
{{ transaction.statusText || '-' }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<div class="transaction-amount" :class="transaction.amount > 0 ? 'positive' : 'negative'">
|
||||
{{ transaction.amount > 0 ? '+' : '' }}{{ transaction.amount }} USDT
|
||||
{{ transaction.amount > 0 ? '+' : '' }}{{ transaction.amountText }} USDT
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="recentTransactions.length === 0" class="empty-state">
|
||||
@@ -136,8 +151,9 @@
|
||||
<el-dialog
|
||||
title="USDT提现"
|
||||
:visible.sync="withdrawDialogVisible"
|
||||
width="600px"
|
||||
width="720px"
|
||||
@close="resetWithdrawForm"
|
||||
:close-on-click-modal="false" :close-on-press-escape="false"
|
||||
>
|
||||
<el-form :model="withdrawForm" :rules="withdrawRules" ref="withdrawForm" label-width="120px">
|
||||
<!-- 提现链(只读展示当前钱包链) -->
|
||||
@@ -170,12 +186,22 @@
|
||||
<template slot="append">{{ displayWithdrawSymbol }}</template>
|
||||
</el-input>
|
||||
<div class="balance-info">
|
||||
<div class="balance-detail">
|
||||
<span>可用余额:{{ (WalletData.walletBalance || WalletData.balance || 0) }} {{ displayWithdrawSymbol }}</span>
|
||||
</div>
|
||||
<div class="balance-detail frozen-info">
|
||||
<span>冻结余额:{{ (WalletData.blockedBalance || 0) }} {{ displayWithdrawSymbol }}</span>
|
||||
<span class="frozen-tip">(购买机器下单后冻结,不可提现)</span>
|
||||
<div class="balance-total">钱包总余额:{{ totalBalance }} {{ displayWithdrawSymbol }}</div>
|
||||
<div class="balance-row">
|
||||
<span>可用余额:{{ availableWithdrawBalance }} {{ displayWithdrawSymbol }}</span>
|
||||
<span class="divider">|</span>
|
||||
<span class="frozen-info">
|
||||
<el-tooltip placement="top" effect="dark">
|
||||
<div slot="content">
|
||||
冻结金额不能使用或提现,以下情况会冻结钱包余额:<br/>
|
||||
1. 下单机器后会冻结订单对应金额<br/>
|
||||
2. 提交提现后,金额正在提现中
|
||||
</div>
|
||||
<i class="el-icon-question frozen-tip-icon"></i>
|
||||
</el-tooltip>
|
||||
冻结余额:{{ (WalletData.blockedBalance || 0) }} {{ displayWithdrawSymbol }}
|
||||
<span class="frozen-tip">(购买机器下单后冻结,不可提现)</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
@@ -255,6 +281,7 @@
|
||||
<el-dialog
|
||||
title="链上充值"
|
||||
:visible.sync="createDialogVisible"
|
||||
:close-on-click-modal="false" :close-on-press-escape="false"
|
||||
width="520px"
|
||||
>
|
||||
<el-form label-width="120px">
|
||||
@@ -405,22 +432,29 @@ export default {
|
||||
* 计算实际到账金额
|
||||
*/
|
||||
actualAmount() {
|
||||
// 使用“分”为单位进行整数计算,避免浮点误差
|
||||
const amountCents = this.toCents(this.withdrawForm.amount)
|
||||
const feeCents = this.toCents(this.withdrawForm.fee)
|
||||
if (!Number.isFinite(amountCents) || !Number.isFinite(feeCents)) return '0.00'
|
||||
const resultCents = amountCents - feeCents
|
||||
return resultCents > 0 ? this.centsToAmountString(resultCents) : '0.00'
|
||||
// 使用 10^6 精度进行整数计算,避免浮点误差;展示时最多6位小数、去尾零
|
||||
const amountInt = this.toScaledInt(this.withdrawForm.amount)
|
||||
const feeInt = this.toScaledInt(this.withdrawForm.fee)
|
||||
if (!Number.isFinite(amountInt) || !Number.isFinite(feeInt)) return '0'
|
||||
const result = amountInt - feeInt
|
||||
if (result <= 0) return '0'
|
||||
return this.formatDec6FromInt(result)
|
||||
},
|
||||
|
||||
/**
|
||||
* 计算总余额(可用余额 + 冻结余额)
|
||||
*/
|
||||
totalBalance() {
|
||||
const available = parseFloat(this.walletBalance) || 0
|
||||
const blocked = parseFloat(this.blockedBalance) || 0
|
||||
const available = parseFloat(this.WalletData.walletBalance || this.WalletData.balance || this.walletBalance || 0) || 0
|
||||
const blocked = parseFloat(this.WalletData.blockedBalance || this.blockedBalance || 0) || 0
|
||||
return (available + blocked).toFixed(2)
|
||||
},
|
||||
/**
|
||||
* 可用余额(提现对话框时以当前卡片的钱包为准)
|
||||
*/
|
||||
availableWithdrawBalance() {
|
||||
return (this.WalletData.walletBalance || this.WalletData.balance || 0)
|
||||
},
|
||||
/**
|
||||
* 提现展示单位(始终大写):优先当前 WalletData 的 fromSymbol/coin
|
||||
*/
|
||||
@@ -511,11 +545,18 @@ export default {
|
||||
const type = Number(r && r.type)
|
||||
const signAmt = (type === 1) ? Math.abs(amt) : -Math.abs(amt) // 1 充值为正,0 支付/2 提现为负
|
||||
const typeLabel = type === 1 ? '充值' : (type === 2 ? '提现' : '支付')
|
||||
const status = Number(r && r.status)
|
||||
const statusTextMap = { 0: '失败', 1: '成功', 2: '处理中', 3: '校验失败' }
|
||||
const statusTagTypeMap = { 0: 'danger', 1: 'success', 2: 'warning', 3: 'danger' }
|
||||
return {
|
||||
id: `${r && r.updateTime || ''}-${idx}`,
|
||||
type: typeLabel,
|
||||
amount: Number(signAmt.toFixed(2)),
|
||||
time: this.formatApiTime(r && r.updateTime)
|
||||
amount: signAmt,
|
||||
amountText: this.formatDec6(Math.abs(signAmt)),
|
||||
time: this.formatApiTime(r && r.updateTime),
|
||||
status,
|
||||
statusText: statusTextMap[status] || '-',
|
||||
statusTagType: statusTagTypeMap[status] || 'info'
|
||||
}
|
||||
})
|
||||
this.recentTransactions = mapped
|
||||
@@ -533,35 +574,69 @@ export default {
|
||||
if (!s) return ''
|
||||
return s.replace('T', ' ').replace('Z', '')
|
||||
},
|
||||
/**
|
||||
* 金额显示:保留最多6位小数,直接截断不四舍五入;不补尾随0
|
||||
*/
|
||||
formatDec6(value) {
|
||||
if (value === null || value === undefined || value === '') return '0'
|
||||
let s = String(value)
|
||||
if (/e/i.test(s)) {
|
||||
const n = Number(value)
|
||||
if (!Number.isFinite(n)) return '0'
|
||||
s = n.toFixed(20).replace(/\.0+$/, '').replace(/(\.\d*?)0+$/, '$1')
|
||||
}
|
||||
const m = s.match(/^(-?)(\d+)(?:\.(\d+))?$/)
|
||||
if (!m) return s
|
||||
let intPart = m[2]
|
||||
let decPart = m[3] || ''
|
||||
if (decPart.length > 6) decPart = decPart.slice(0, 6)
|
||||
return decPart ? `${intPart}.${decPart}` : intPart
|
||||
},
|
||||
/**
|
||||
* 将金额字符串转换为“分”为单位的整数
|
||||
*/
|
||||
toCents(amountStr) {
|
||||
/**
|
||||
* 将金额字符串转为按 10^6 精度的整数
|
||||
*/
|
||||
toScaledInt(amountStr, decimals = 6) {
|
||||
if (amountStr === null || amountStr === undefined) return 0
|
||||
const normalized = String(amountStr).trim()
|
||||
if (normalized === '') return 0
|
||||
// 使用正则确保是最多两位小数的数字格式
|
||||
const match = normalized.match(/^\d+(?:\.(\d{0,2}))?$/)
|
||||
const re = new RegExp(`^\\d+(?:\\.(\\d{0,${decimals}}))?$`)
|
||||
const match = normalized.match(re)
|
||||
if (!match) {
|
||||
const n = Number(normalized)
|
||||
if (!Number.isFinite(n)) return 0
|
||||
return Math.round(n * 100)
|
||||
const scale = Math.pow(10, decimals)
|
||||
return Math.round(n * scale)
|
||||
}
|
||||
const [intPart, decPartRaw] = normalized.split('.')
|
||||
const decPart = (decPartRaw || '').padEnd(2, '0').slice(0, 2)
|
||||
return Number(intPart) * 100 + Number(decPart)
|
||||
const decPart = (decPartRaw || '').padEnd(decimals, '0').slice(0, decimals)
|
||||
const scale = Math.pow(10, decimals)
|
||||
return Number(intPart) * scale + Number(decPart)
|
||||
},
|
||||
|
||||
/**
|
||||
* 将“分”为单位的整数转为字符串金额,固定两位小数
|
||||
*/
|
||||
centsToAmountString(cents) {
|
||||
const sign = cents < 0 ? '-' : ''
|
||||
const abs = Math.abs(cents)
|
||||
const intPart = Math.floor(abs / 100)
|
||||
const decPart = String(abs % 100).padStart(2, '0')
|
||||
/**
|
||||
* 将按 10^6 精度的整数转为字符串金额,固定六位小数
|
||||
*/
|
||||
scaledIntToString(intVal, decimals = 6) {
|
||||
const sign = intVal < 0 ? '-' : ''
|
||||
const abs = Math.abs(intVal)
|
||||
const scale = Math.pow(10, decimals)
|
||||
const intPart = Math.floor(abs / scale)
|
||||
const decPart = String(abs % scale).padStart(decimals, '0')
|
||||
return `${sign}${intPart}.${decPart}`
|
||||
},
|
||||
/**
|
||||
* 将按10^6精度的整数转为最多6位小数字符串(不补零,不四舍五入)
|
||||
*/
|
||||
formatDec6FromInt(intVal) {
|
||||
const s = this.scaledIntToString(intVal, 6)
|
||||
return s.replace(/\.0+$/, '').replace(/(\.\d*?)0+$/, '$1')
|
||||
},
|
||||
async fetchWalletInfo(params) {
|
||||
try {
|
||||
const res = await getWalletInfo(params)
|
||||
@@ -786,6 +861,14 @@ export default {
|
||||
* 根据链更新手续费
|
||||
*/
|
||||
updateFeeByChain() {
|
||||
// 优先从当前钱包信息中读取提现手续费(后端字段:charge)
|
||||
const walletCharge = (this.WalletData && (this.WalletData.charge != null ? this.WalletData.charge : this.WalletData.fee))
|
||||
if (walletCharge != null && walletCharge !== '') {
|
||||
const n = Number(walletCharge)
|
||||
this.withdrawForm.fee = Number.isFinite(n) ? n.toFixed(2) : String(walletCharge)
|
||||
return
|
||||
}
|
||||
// 兜底:按链类型给一个默认手续费
|
||||
const feeMap = {
|
||||
tron: '1.00', // TRC20 手续费较低
|
||||
ethereum: '5.00', // ERC20 手续费较高
|
||||
@@ -811,6 +894,7 @@ export default {
|
||||
toSymbol: (this.WalletData && (this.WalletData.fromSymbol || this.WalletData.coin)) || this.withdrawForm.toSymbol,
|
||||
amount: parseFloat(this.withdrawForm.amount),
|
||||
toAddress: this.withdrawForm.toAddress,
|
||||
fromAddress: (this.WalletData && this.WalletData.fromAddress) || '',
|
||||
code: this.withdrawForm.googleCode // 添加谷歌验证码
|
||||
})
|
||||
|
||||
@@ -872,31 +956,32 @@ export default {
|
||||
}
|
||||
|
||||
// 使用“分”为单位的整数校验
|
||||
const amountCents = this.toCents(value)
|
||||
if (!Number.isFinite(amountCents) || amountCents <= 0) {
|
||||
const amountInt = this.toScaledInt(value)
|
||||
if (!Number.isFinite(amountInt) || amountInt <= 0) {
|
||||
callback(new Error('请输入有效的金额'))
|
||||
return
|
||||
}
|
||||
|
||||
const feeCents = this.toCents(this.withdrawForm.fee)
|
||||
const totalRequired = amountCents + feeCents
|
||||
// 手续费与总需求按相同精度计算
|
||||
const feeInt = this.toScaledInt(this.withdrawForm.fee)
|
||||
const totalRequired = amountInt + feeInt
|
||||
|
||||
// 钱包余额转分
|
||||
const balanceCents = this.toCents(this.walletBalance)
|
||||
if (totalRequired > balanceCents) {
|
||||
const totalText = this.centsToAmountString(totalRequired)
|
||||
const balanceInt = this.toScaledInt(this.walletBalance)
|
||||
if (totalRequired > balanceInt) {
|
||||
const totalText = this.scaledIntToString(totalRequired)
|
||||
callback(new Error(`提现金额加上手续费(${totalText} USDT)不能超过钱包余额`))
|
||||
return
|
||||
}
|
||||
|
||||
// 检查最小提现金额
|
||||
if (amountCents < 1000) { // 10 USDT = 1000 分
|
||||
callback(new Error('最小提现金额为10 USDT'))
|
||||
// 最小提现金额 1 USDT
|
||||
if (amountInt < 1000000) {
|
||||
callback(new Error('最小提现金额为1 USDT'))
|
||||
return
|
||||
}
|
||||
|
||||
// 检查实际到账金额是否为正数
|
||||
if (amountCents <= feeCents) {
|
||||
// 检查实际到账金额是否为正数(提现金额必须大于手续费)
|
||||
if (amountInt <= feeInt) {
|
||||
callback(new Error('提现金额必须大于手续费'))
|
||||
return
|
||||
}
|
||||
@@ -924,10 +1009,10 @@ export default {
|
||||
if (firstDot !== -1) {
|
||||
v = v.slice(0, firstDot + 1) + v.slice(firstDot + 1).replace(/\./g, '')
|
||||
}
|
||||
// 最多两位小数
|
||||
// 最多六位小数
|
||||
if (firstDot !== -1) {
|
||||
const [intPart, decPart] = v.split('.')
|
||||
v = intPart + '.' + (decPart ? decPart.slice(0, 2) : '')
|
||||
v = intPart + '.' + (decPart ? decPart.slice(0, 6) : '')
|
||||
}
|
||||
// 限制整数部分长度,避免过大导致显示或后端处理异常,这里限制到 12 位(万亿级)
|
||||
const parts = v.split('.')
|
||||
@@ -1182,26 +1267,30 @@ export default {
|
||||
}
|
||||
|
||||
.transaction-list {
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
|
||||
/* API 仅返回 <=5 条,使用自适应高度,避免滚动条 */
|
||||
max-height: none;
|
||||
overflow-y: visible;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.transaction-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px 0;
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
transition: background-color 0.2s ease;
|
||||
|
||||
}
|
||||
|
||||
.transaction-item:hover {
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 6px;
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
padding-left: 6px;
|
||||
padding-right: 6px;
|
||||
}
|
||||
|
||||
.transaction-item:last-child {
|
||||
@@ -1213,6 +1302,7 @@ export default {
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
.transaction-status { align-self: flex-start; }
|
||||
|
||||
.transaction-type {
|
||||
font-weight: 500;
|
||||
@@ -1407,13 +1497,17 @@ export default {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.balance-detail {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.balance-total { margin-bottom: 4px; font-weight: 600; }
|
||||
.balance-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
||||
.divider { color: #ccc; }
|
||||
.frozen-info {
|
||||
color: #e6a23c;
|
||||
}
|
||||
.balance-tip-icon, .frozen-tip-icon {
|
||||
margin-right: 4px;
|
||||
color: #ffd666;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.frozen-tip {
|
||||
font-size: 11px;
|
||||
|
||||
@@ -102,9 +102,12 @@
|
||||
<!-- <el-table-column label="机器总数" min-width="120">
|
||||
<template #default="scope">{{ countMachines(scope.row) }}</template>
|
||||
</el-table-column> -->
|
||||
<el-table-column label="总价(USDT)" >
|
||||
<template #default="scope"><span class="price-strong">{{ formatTrunc(computeShopTotal(scope.row), 2) }}</span></template>
|
||||
<el-table-column prop="totalPrice" label="总价(USDT)">
|
||||
<template #default="scope">
|
||||
<span class="price-strong">{{ computeShopTotalDisplay(scope.row) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
|
||||
<el-table-column label="支付方式">
|
||||
<template #default="scope">
|
||||
@@ -128,20 +131,25 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-dialog :visible.sync="confirmDialog.visible" width="720px" :close-on-click-modal="false" :title="`确认结算该店铺订单(共 ${confirmDialog.count} 台机器)`">
|
||||
<el-dialog :visible.sync="confirmDialog.visible" width="80vw" :close-on-click-modal="false" :title="`确认结算该店铺订单(共 ${confirmDialog.count} 台机器)`">
|
||||
<div>
|
||||
<el-table :data="confirmDialog.items" height="360" border stripe
|
||||
:header-cell-style="{ textAlign: 'left' }" :cell-style="{ textAlign: 'left' }">
|
||||
<el-table-column prop="product" label="商品" min-width="160" />
|
||||
<el-table-column prop="coin" label="币种" min-width="100" />
|
||||
<el-table-column prop="machineId" label="机器ID" min-width="100" />
|
||||
<el-table-column prop="user" label="账户" min-width="120" />
|
||||
<el-table-column prop="miner" label="机器编号" min-width="160" />
|
||||
<el-table-column prop="price" min-width="120">
|
||||
<el-table-column prop="unitPrice" min-width="140">
|
||||
<template #header>单价({{ payCoinSymbol || 'USDT' }})</template>
|
||||
<template #default="scope"><span class="price-strong">{{ formatTrunc(scope.row.unitPrice, 2) }}</span></template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="leaseTime" label="租赁天数" min-width="120" />
|
||||
<el-table-column prop="subtotal" min-width="140">
|
||||
<template #header>小计({{ payCoinSymbol || 'USDT' }})</template>
|
||||
<template #default="scope"><span class="price-strong">{{ formatTrunc(scope.row.subtotal, 2) }}</span></template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div style="margin-top:12px;text-align:right;">总金额({{ payCoinSymbol || 'USDT' }}):<b>{{ formatTrunc(confirmDialog.total, 2) }}</b></div>
|
||||
<div style="margin-top:12px;text-align:right;">总金额({{ payCoinSymbol || 'USDT' }}):<span class="price-strong">{{ formatTrunc(confirmDialog.total, 2) }}</span></div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="confirmDialog.visible=false">取消</el-button>
|
||||
@@ -166,20 +174,7 @@
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 结算成功提示弹窗 -->
|
||||
<el-dialog :visible.sync="successDialog.visible" width="480px" :close-on-click-modal="false" :close-on-press-escape="false" @close="handleCloseSuccessDialog">
|
||||
<div style="text-align:center; padding: 20px 0;">
|
||||
<div style="font-size: 48px; color: #52c41a; margin-bottom: 16px;">✓</div>
|
||||
<div style="font-size: 18px; color: #333; margin-bottom: 12px;">请求结算处理成功</div>
|
||||
<div style="color: #666; line-height: 1.6;">
|
||||
请在订单列表页面查看结算状态<br>
|
||||
结算成功会自动更新钱包余额
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button type="primary" @click="handleCloseSuccessDialog">已知晓</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
|
||||
<!-- 购物须知(测试版) - 必须勾选并等待5秒后才可关闭 -->
|
||||
<el-dialog :visible.sync="noticeDialog.visible" width="680px" title="下单须知" :show-close="false" :close-on-click-modal="false" :close-on-press-escape="false">
|
||||
@@ -252,7 +247,22 @@
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
<!-- 结算成功提示弹窗(全局,避免被父级条件渲染影响) -->
|
||||
<el-dialog :visible.sync="settlementSuccessfulVisible" width="480px" append-to-body :close-on-click-modal="false" :close-on-press-escape="false" @close="handleCloseSuccessDialog">
|
||||
<div style="text-align:center; padding: 20px 0;">
|
||||
<div style="font-size: 48px; color: #52c41a; margin-bottom: 16px;">✓</div>
|
||||
<div style="font-size: 18px; color: #333; margin-bottom: 12px;">请求结算处理成功</div>
|
||||
<div style="color: #666; line-height: 1.6;">
|
||||
请在订单列表页面查看结算状态<br>
|
||||
结算成功会自动更新钱包余额
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button type="primary" @click="handleCloseSuccessDialog">已知晓</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -292,7 +302,8 @@ export default {
|
||||
selectedChain: '',
|
||||
selectedCoin: '',
|
||||
selectedPrice: 0
|
||||
,clearOffLoading: false
|
||||
,clearOffLoading: false,
|
||||
settlementSuccessfulVisible: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -368,10 +379,45 @@ export default {
|
||||
this.noticeTimer = null
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 将金额转为整分整数,避免浮点误差
|
||||
* @param {number|string} v
|
||||
* @returns {number}
|
||||
*/
|
||||
toCents(v) {
|
||||
// 直接截取两位小数(不四舍五入),并转为“分”的整数
|
||||
if (v === null || v === undefined) return 0
|
||||
let s = String(v).trim()
|
||||
if (s === '') return 0
|
||||
let sign = 1
|
||||
if (s[0] === '-') { sign = -1; s = s.slice(1) }
|
||||
const parts = s.split('.')
|
||||
const intPart = parseInt(parts[0] || '0', 10) || 0
|
||||
const decRaw = (parts[1] || '').replace(/[^0-9]/g, '')
|
||||
const decTwo = (decRaw.length >= 2 ? decRaw.slice(0, 2) : decRaw.padEnd(2, '0'))
|
||||
const cents = intPart * 100 + (parseInt(decTwo || '0', 10) || 0)
|
||||
return sign * cents
|
||||
},
|
||||
/**
|
||||
* 整分转字符串,固定两位小数
|
||||
* @param {number} cents
|
||||
* @returns {string}
|
||||
*/
|
||||
centsToText(cents) {
|
||||
const sign = cents < 0 ? '-' : ''
|
||||
const abs = Math.abs(Number(cents) || 0)
|
||||
const intPart = Math.floor(abs / 100)
|
||||
const decPart = String(abs % 100).padStart(2, '0')
|
||||
return `${sign}${intPart}.${decPart}`
|
||||
},
|
||||
// 选择框可选逻辑:下架(或删除)机器不可选择
|
||||
isRowSelectable(row, index) {
|
||||
return !(Number(row && row.del) === 1 || Number(row && row.state) === 1)
|
||||
},
|
||||
// 判断机器是否“上架”(用于未展开时的自动筛选)
|
||||
isOnShelf(row) {
|
||||
return !(Number(row && row.del) === 1 || Number(row && row.state) === 1)
|
||||
},
|
||||
// 获取本地最大可租赁天数,默认 365
|
||||
getRowMaxLeaseDaysLocal(row) {
|
||||
const raw = row && row.maxLeaseDays
|
||||
@@ -402,6 +448,11 @@ export default {
|
||||
},
|
||||
//获取支持的链和币种
|
||||
async fetchChainAndListForSeller(shopId) {
|
||||
if (!shopId) {
|
||||
this.options = []
|
||||
this.loading = false
|
||||
return
|
||||
}
|
||||
this.loading = true;
|
||||
const res = await getChainAndListForSeller({ id: shopId });
|
||||
if (res && (res.code === 0 || res.code === 200) && res.data) {
|
||||
@@ -425,10 +476,42 @@ export default {
|
||||
},
|
||||
// 获取所有商品分组(兼容保留,现直接返回空,因已移除中间商品层)
|
||||
getAllGroups() { return [] },
|
||||
// 店铺总价 = 累加其所有商品的 (机器单价 × 租赁天数)
|
||||
// 店铺总价(精确到分):优先用后端 totalPrice;若用户改动租期则实时按分计算
|
||||
computeShopTotal(shop) {
|
||||
if (!shop) return 0
|
||||
const list = Array.isArray(shop.productMachineDtoList) ? shop.productMachineDtoList : []
|
||||
// 若没有子项,直接返回后端值
|
||||
if (!list.length) return Number(shop.totalPrice || 0)
|
||||
let totalCents = 0
|
||||
for (const m of list) {
|
||||
const priceCents = this.toCents(m && m.price)
|
||||
const days = Math.max(1, Math.floor(Number(m && m.leaseTime) || 1))
|
||||
totalCents += priceCents * days
|
||||
}
|
||||
return totalCents / 100
|
||||
},
|
||||
/**
|
||||
* 返回展示用字符串:两位小数且无精度丢失
|
||||
*/
|
||||
computeShopTotalDisplay(shop) {
|
||||
const list = Array.isArray(shop && shop.productMachineDtoList) ? shop.productMachineDtoList : []
|
||||
return list.reduce((sum, m) => sum + Number(m.price || 0) * Number(m.leaseTime || 1), 0)
|
||||
const backendVal = Number(shop && shop.totalPrice)
|
||||
const hasBackend = Number.isFinite(backendVal)
|
||||
// 若未修改任一机器租期,优先展示后端总价
|
||||
let modified = false
|
||||
for (const m of list) {
|
||||
const orig = (m && m._origLeaseTime != null) ? Number(m._origLeaseTime) : Number(m && m.leaseTime)
|
||||
const cur = Math.max(1, Math.floor(Number(m && m.leaseTime) || 1))
|
||||
if (orig !== cur) { modified = true; break }
|
||||
}
|
||||
if ((hasBackend && !modified) || (!list.length && hasBackend)) return this.formatTrunc(backendVal, 2)
|
||||
let totalCents = 0
|
||||
for (const m of list) {
|
||||
const priceCents = this.toCents(m && m.price)
|
||||
const days = Math.max(1, Math.floor(Number(m && m.leaseTime) || 1))
|
||||
totalCents += priceCents * days
|
||||
}
|
||||
return this.centsToText(totalCents)
|
||||
},
|
||||
// 组装批量删除的请求体:数组 [{ machineId, productId }]
|
||||
buildDeletePayload() {
|
||||
@@ -548,6 +631,13 @@ export default {
|
||||
...shop,
|
||||
id: shop.id != null ? String(shop.id) : `shop-${sIdx}`
|
||||
}))
|
||||
// 记录每台机器的原始租期,便于判断是否被修改
|
||||
try {
|
||||
withShopKeys.forEach(sp => {
|
||||
const list = Array.isArray(sp.productMachineDtoList) ? sp.productMachineDtoList : []
|
||||
list.forEach(m => { if (m && m._origLeaseTime == null) m._origLeaseTime = Number(m.leaseTime || 1) })
|
||||
})
|
||||
} catch (e) { /* noop */ }
|
||||
this.shops = withShopKeys
|
||||
this.groups = []
|
||||
this.expandedGroupKeys = []
|
||||
@@ -681,7 +771,7 @@ export default {
|
||||
return
|
||||
}
|
||||
machines.forEach(m => {
|
||||
if (selectedSet.has(m.id)) {
|
||||
if (selectedSet.has(m.id) && this.isOnShelf(m)) {
|
||||
payload.push({
|
||||
leaseTime: Number(m.leaseTime || 1),
|
||||
machineId: m.id,
|
||||
@@ -690,9 +780,18 @@ export default {
|
||||
})
|
||||
}
|
||||
})
|
||||
if (!payload.length) {
|
||||
this.$message({ message: '所选机器均已下架,无法结算', type: 'warning', showClose: true })
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// 未展开:默认使用全部机器
|
||||
machines.forEach(m => {
|
||||
// 未展开:仅结算“上架”机器
|
||||
const onShelfMachines = machines.filter(m => this.isOnShelf(m))
|
||||
if (!onShelfMachines.length) {
|
||||
this.$message({ message: '该店铺暂无上架机器可结算', type: 'warning', showClose: true })
|
||||
return
|
||||
}
|
||||
onShelfMachines.forEach(m => {
|
||||
payload.push({
|
||||
leaseTime: Number(m.leaseTime || 1),
|
||||
machineId: m.id,
|
||||
@@ -723,11 +822,18 @@ export default {
|
||||
if (res && Number(res.code) === 200) {
|
||||
const dataStr = String(res.data || '')
|
||||
ok = dataStr.includes('成功')
|
||||
this.$message({
|
||||
message: `结算成功,订单状态请在订单列表中查看`,
|
||||
type: 'success',
|
||||
duration: 3000,
|
||||
showClose: true,
|
||||
})
|
||||
this.settlementSuccessfulVisible = true
|
||||
}
|
||||
if (ok) {
|
||||
// 结算成功后重新获取购物车数据,更新购物车数量
|
||||
await this.fetchGetGoodsList()
|
||||
this.successDialog.visible = true
|
||||
// this.settlementSuccessfulVisible = true
|
||||
} else {
|
||||
// 失败或未成功,恢复之前的勾选 UI
|
||||
this.reapplySelectionsForPendingShop()
|
||||
@@ -823,7 +929,7 @@ export default {
|
||||
},
|
||||
// 关闭成功弹窗:跳转到订单列表的订单进行中状态
|
||||
handleCloseSuccessDialog() {
|
||||
try { this.successDialog.visible = false } catch (e) { /* noop */ }
|
||||
try { this.settlementSuccessfulVisible = false } catch (e) { /* noop */ }
|
||||
this.$router.push({ path: '/account/orders', query: { status: '7' } })
|
||||
},
|
||||
// 注意事项:启动 5 秒倒计时
|
||||
@@ -910,24 +1016,28 @@ export default {
|
||||
|
||||
const items = []
|
||||
list.forEach(m => {
|
||||
if (selectedIds.has(m.id)) {
|
||||
const usdtPrice = Number(m.price || 0) * Number(m.leaseTime || 1)
|
||||
if (selectedIds.has(m.id) && this.isOnShelf(m)) {
|
||||
// 单价(同币种显示,若非USDT按实时价换算)
|
||||
const baseUnit = Number(m.price || 0)
|
||||
const leaseDays = Math.max(1, Math.floor(Number(m.leaseTime || 1)))
|
||||
const isUSDT = String(this.selectedCoin).toUpperCase() === 'USDT'
|
||||
const displayPrice = !isUSDT && this.selectedPrice > 0 ? (usdtPrice / this.selectedPrice) : usdtPrice
|
||||
const unitPrice = !isUSDT && this.selectedPrice > 0 ? (baseUnit / this.selectedPrice) : baseUnit
|
||||
const subtotal = unitPrice * leaseDays
|
||||
items.push({
|
||||
product: shop.name || '',
|
||||
coin: this.toUpperText(m.coin),
|
||||
machineId: m.id,
|
||||
user: m.user,
|
||||
miner: m.miner,
|
||||
price: Number(displayPrice || 0)
|
||||
unitPrice: Number(unitPrice || 0),
|
||||
leaseTime: leaseDays,
|
||||
subtotal: Number(subtotal || 0)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
this.confirmDialog.items = items
|
||||
this.confirmDialog.count = items.length
|
||||
this.confirmDialog.total = items.reduce((s, i) => s + i.price, 0)
|
||||
this.confirmDialog.total = items.reduce((s, i) => s + i.subtotal, 0)
|
||||
this.confirmDialog.visible = true
|
||||
},
|
||||
// 显示谷歌验证码输入框
|
||||
|
||||
@@ -108,7 +108,7 @@
|
||||
|
||||
<!-- 外层列宽同样收紧,避免横向滚动 -->
|
||||
<el-table-column label="价格 (USDT)" header-align="left" align="left" min-width="120">
|
||||
<template slot-scope="scope">{{ scope.row.productMachineRangeGroupDto && scope.row.productMachineRangeGroupDto.price }} </template>
|
||||
<template slot-scope="scope"><span class="price-strong">{{ scope.row.productMachineRangeGroupDto && scope.row.productMachineRangeGroupDto.price }}</span></template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="理论算力范围" min-width="220" header-align="left" align="left" show-overflow-tooltip>
|
||||
@@ -132,20 +132,21 @@
|
||||
</div>
|
||||
|
||||
<!-- 确认加入购物车弹窗 -->
|
||||
<el-dialog :visible.sync="confirmAddDialog.visible" width="60vw" :title="`确认加入购物车(共 ${confirmAddDialog.items.length} 台)`">
|
||||
<el-dialog :visible.sync="confirmAddDialog.visible" width="80vw" :title="`确认加入购物车(共 ${confirmAddDialog.items.length} 台)`">
|
||||
<div>
|
||||
<el-table :data="confirmAddDialog.items" height="360" border stripe :header-cell-style="{ textAlign: 'left' }" :cell-style="{ textAlign: 'left' }">
|
||||
<el-table-column prop="type" label="型号" width="160" header-align="left" align="left" />
|
||||
<el-table-column prop="theoryPower" label="理论算力" width="160" header-align="left" align="left" />
|
||||
<el-table-column label="算力" width="160" header-align="left" align="left">
|
||||
<el-table-column prop="theoryPower" label="理论算力" header-align="left" align="left" />
|
||||
<el-table-column label="实际算力" header-align="left" align="left">
|
||||
<template #default="scope">{{ scope.row.computingPower }} {{ scope.row.unit }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="algorithm" label="算法" width="120" header-align="left" align="left" />
|
||||
<el-table-column prop="powerDissipation" label="功耗(kw/h)" width="160" header-align="left" align="left" />
|
||||
<el-table-column label="租赁天数(天)" width="160" header-align="left" align="left">
|
||||
<el-table-column prop="powerDissipation" label="功耗(kw/h)" header-align="left" align="left" />
|
||||
<el-table-column label="租赁天数(天)" header-align="left" align="left">
|
||||
<template #default="scope">{{ Number(scope.row.leaseTime || 1) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="price" label="单价(USDT)" width="160" header-align="left" align="left" />
|
||||
<el-table-column prop="price" label="单价(USDT)" header-align="left" align="left">
|
||||
<template #default="scope"><span class="price-strong">{{ scope.row.price }}</span></template>
|
||||
</el-table-column>
|
||||
|
||||
</el-table>
|
||||
</div>
|
||||
@@ -350,6 +351,11 @@ export default {
|
||||
color: #e74c3c;
|
||||
}
|
||||
|
||||
.price-strong {
|
||||
font-weight: 700;
|
||||
color: #e74c3c;
|
||||
}
|
||||
|
||||
/* 支付方式区域(视觉更友好 + 可达性) */
|
||||
.pay-methods {
|
||||
display: flex;
|
||||
|
||||
@@ -35,44 +35,44 @@ export default {
|
||||
],
|
||||
loading: false,
|
||||
powerList: [
|
||||
{
|
||||
value: 1,
|
||||
label: "NexaPow",
|
||||
children: [
|
||||
{
|
||||
value: 1 - 1,
|
||||
label: "挖矿账户1",
|
||||
},
|
||||
{
|
||||
value: 1 - 2,
|
||||
label: "挖矿账户2",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
label: "Grepow",
|
||||
children: [
|
||||
{
|
||||
value: 2 - 1,
|
||||
label: "挖矿账户1",
|
||||
},
|
||||
{
|
||||
value: 2 - 2,
|
||||
label: "挖矿账户2",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: 3,
|
||||
label: "mofang",
|
||||
children: [
|
||||
{
|
||||
value: 3 - 1,
|
||||
label: "挖矿账户1",
|
||||
},
|
||||
],
|
||||
},
|
||||
// {
|
||||
// value: 1,
|
||||
// label: "NexaPow",
|
||||
// children: [
|
||||
// {
|
||||
// value: 1 - 1,
|
||||
// label: "挖矿账户1",
|
||||
// },
|
||||
// {
|
||||
// value: 1 - 2,
|
||||
// label: "挖矿账户2",
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// {
|
||||
// value: 2,
|
||||
// label: "Grepow",
|
||||
// children: [
|
||||
// {
|
||||
// value: 2 - 1,
|
||||
// label: "挖矿账户1",
|
||||
// },
|
||||
// {
|
||||
// value: 2 - 2,
|
||||
// label: "挖矿账户2",
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// {
|
||||
// value: 3,
|
||||
// label: "mofang",
|
||||
// children: [
|
||||
// {
|
||||
// value: 3 - 1,
|
||||
// label: "挖矿账户1",
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
],
|
||||
currencyList: [
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="product-list" v-loading="productListLoading">
|
||||
<section class="container">
|
||||
<section class="container">
|
||||
<h1 class="page-title">商品列表</h1>
|
||||
<section class="filter-section">
|
||||
<label class="required" style="margin-bottom: 10px">币种选择:</label>
|
||||
@@ -54,7 +54,8 @@
|
||||
tabindex="0"
|
||||
aria-label="查看详情"
|
||||
>
|
||||
<img :src="product.image || 'https://img.yzcdn.cn/vant/apple-1.jpg'" :alt="product.name" class="product-image" />
|
||||
<!-- <img :src="product.image || 'https://img.yzcdn.cn/vant/apple-1.jpg'" :alt="product.name" class="product-image" /> -->
|
||||
<img src="../../assets/imgs/commodity.png" :alt="product.name" class="product-image" />
|
||||
<div class="product-info">
|
||||
<h4>商品: {{ product.name }}</h4>
|
||||
<p style="font-size: 16px;margin-top: 10px;font-weight: bold;">算法: {{ product.algorithm }}</p>
|
||||
@@ -166,10 +167,10 @@ export default {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
height: 35vh;
|
||||
height: 40vh;
|
||||
}
|
||||
.product-image {
|
||||
width: 90%;
|
||||
width: 68%;
|
||||
height:65%;
|
||||
object-fit: cover;
|
||||
margin-bottom: 12px;
|
||||
|
||||
Binary file not shown.
1
power_leasing/test/css/app.9d232bce.css
Normal file
1
power_leasing/test/css/app.9d232bce.css
Normal file
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
<!doctype html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/favicon.ico"><title>power_leasing</title><script defer="defer" src="/js/chunk-vendors.f4da7ffe.js"></script><script defer="defer" src="/js/app.e19a35c5.js"></script><link href="/css/chunk-vendors.10dd4e95.css" rel="stylesheet"><link href="/css/app.95fce3d4.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but power_leasing doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>
|
||||
<!doctype html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/favicon.ico"><title>power_leasing</title><script defer="defer" src="/js/chunk-vendors.f4da7ffe.js"></script><script defer="defer" src="/js/app.7b715e42.js"></script><link href="/css/chunk-vendors.10dd4e95.css" rel="stylesheet"><link href="/css/app.9d232bce.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but power_leasing doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>
|
||||
2
power_leasing/test/js/app.7b715e42.js
Normal file
2
power_leasing/test/js/app.7b715e42.js
Normal file
File diff suppressed because one or more lines are too long
1
power_leasing/test/js/app.7b715e42.js.map
Normal file
1
power_leasing/test/js/app.7b715e42.js.map
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user