每周更新

This commit is contained in:
2025-12-12 15:33:23 +08:00
parent cbefb964d4
commit 5945ab5588
11 changed files with 2172 additions and 908 deletions

View File

@@ -81,4 +81,26 @@ export function addAsicMachine(data) {
})
}
///添加出售机器的币种获取ASIC
export function getSupportCoin() {
return request({
url: `/lease/v2/product/machine/getSupportCoin`,
method: 'get',
})
}
///添加出售机器的对应币种的算法获取ASIC
/**
* 根据币种获取支持的算法列表
* @param {string} coin - 币种名称,例如 'BTC', 'ETH' 等
* @returns {Promise} 返回算法列表
*/
export function getSupportAlgo(coin) {
return request({
url: `/lease/v2/product/machine/getSupportAlgo`,
method: 'get',
params: { coin } // 使用 paramsrequest 拦截器会自动将参数拼接到 URL 查询字符串中
})
}

View File

@@ -98,3 +98,15 @@ export function addOrdersV2(data) {
//已购矿机配置
export function getPurchasedItems(data) {
return request({
url: `/lease/v2/order/info/getPurchasedItems`,
method: 'post',
data
})
}

View File

@@ -34,7 +34,7 @@
<script>
import { readCart } from '../utils/cartManager'
import { mainNavigation, getBreadcrumb } from '../utils/navigation'
import { getGoodsList } from '../api/shoppingCart'
import { getGoodsListV2 } from '../api/shoppingCart'
export default {
name: 'Header',
@@ -74,47 +74,37 @@ export default {
loadCart() {
this.cart = readCart()
},
/**
* 加载服务器购物车数量
* 根据新接口结构res.rows[].cartMachineInfoDtoList.length 累加
*/
async loadServerCartCount() {
try {
const res = await getGoodsList()
// 统一提取 rows/数组
const primary = Array.isArray(res && res.rows)
? res.rows
: Array.isArray(res && res.data && res.data.rows)
? res.data.rows
: Array.isArray(res && res.data)
? res.data
: (Array.isArray(res) ? res : [])
let groups = []
if (Array.isArray(primary) && primary.length) {
// 情况Ashop -> shoppingCartInfoDtoList -> productMachineDtoList
if (Array.isArray(primary[0] && primary[0].shoppingCartInfoDtoList)) {
primary.forEach(shop => {
if (Array.isArray(shop && shop.shoppingCartInfoDtoList)) {
groups.push(...shop.shoppingCartInfoDtoList)
}
})
} else {
// 情况B直接就是商品分组数组
groups = primary
}
} else if (Array.isArray(res && res.shoppingCartInfoDtoList)) {
// 情况C返回对象直接有 shoppingCartInfoDtoList
groups = res.shoppingCartInfoDtoList
}
const res = await getGoodsListV2()
let total = 0
if (groups.length) {
total = groups.reduce((sum, g) => sum + (Array.isArray(g && g.productMachineDtoList) ? g.productMachineDtoList.length : 0), 0)
} else if (Array.isArray(res && res.productMachineDtoList)) {
// 情况D根对象直接是机器列表
total = res.productMachineDtoList.length
// 新接口结构res.rows 是店铺数组,每个店铺有 cartMachineInfoDtoList
if (Array.isArray(res && res.rows)) {
total = res.rows.reduce((sum, shop) => {
const machineList = Array.isArray(shop && shop.cartMachineInfoDtoList)
? shop.cartMachineInfoDtoList
: []
return sum + machineList.length
}, 0)
} else if (Array.isArray(res && res.data && res.data.rows)) {
// 兼容:如果数据在 res.data.rows 中
total = res.data.rows.reduce((sum, shop) => {
const machineList = Array.isArray(shop && shop.cartMachineInfoDtoList)
? shop.cartMachineInfoDtoList
: []
return sum + machineList.length
}, 0)
}
this.cartServerCount = Number.isFinite(total) ? total : 0
} catch (e) {
// 忽略错误,保持当前显示
console.error('加载购物车数量失败:', e)
}
},
handleStorageChange(event) {

View File

@@ -179,6 +179,16 @@ export const accountRoutes = [
allAuthority: ['all']
}
},
{
path: 'purchased-machine-config',
name: 'accountPurchasedMachineConfig',
component: () => import('../views/account/purchasedMachineConfig.vue'),
meta: {
title: '已购矿机配置',
description: '查看已购买矿机的配置信息',
allAuthority: ['all']
}
},
{
path: 'funds-flow',
name: 'accountFundsFlow',

View File

@@ -6,11 +6,16 @@
<template #default="outer">
<el-table :data="outer.row.orderItemDtoList || []" size="small" border :header-cell-style="{ textAlign: 'left' }" :cell-style="{ textAlign: 'left' }" row-key="productMachineId">
<el-table-column prop="productMachineId" label="机器ID" min-width="120" />
<el-table-column prop="name" label="名称" min-width="160" />
<el-table-column label="矿机类型" min-width="100">
<template #default="scope">{{ formatMachineType(scope.row && scope.row.type) }}</template>
</el-table-column>
<el-table-column prop="payCoin" label="币种" min-width="100" />
<el-table-column prop="address" label="收款地址" min-width="240" />
<el-table-column prop="leaseTime" label="租赁天数" min-width="100" />
<el-table-column prop="price" label="售价(USDT)" min-width="240">
<el-table-column label="购买数量" min-width="100">
<template #default="scope">{{ scope.row && scope.row.numbers != null ? scope.row.numbers : '—' }}</template>
</el-table-column>
<el-table-column prop="price" label="单价(USDT)" min-width="240">
<template #default="scope">
<span class="value strong">
<el-tooltip
@@ -31,17 +36,20 @@
</template>
</el-table-column>
<el-table-column label="店铺" width="120">
<template #default="scope">{{ scope.row && scope.row.shopName || '—' }}</template>
</el-table-column>
<el-table-column label="订单号" min-width="220">
<template #default="scope"><span class="value mono">{{ scope.row && scope.row.orderNumber || '—' }}</span></template>
</el-table-column>
<el-table-column label="创建时间" min-width="180">
<el-table-column label="创建时间" width="160">
<template #default="scope">{{ formatDateTime(scope.row && scope.row.createTime) }}</template>
</el-table-column>
<el-table-column label="商品数" min-width="100">
<el-table-column label="商品数" min-width="70">
<template #default="scope">{{ Array.isArray(scope.row && scope.row.orderItemDtoList) ? scope.row.orderItemDtoList.length : 0 }}</template>
</el-table-column>
<el-table-column label="总金额(USDT)" min-width="140">
<el-table-column label="总金额(USDT)" width="120">
<template #default="scope">
<span class="value strong">
<el-tooltip
@@ -58,7 +66,7 @@
</span>
</template>
</el-table-column>
<el-table-column min-width="180">
<el-table-column width="160">
<template #header>
<el-tooltip placement="top" effect="dark">
<div slot="content">
@@ -88,24 +96,10 @@
</span>
</template>
</el-table-column>
<el-table-column label="待支付金额(USDT)" min-width="140">
<template #default="scope">
<span class="value strong">
<el-tooltip
v-if="formatAmount(scope.row && scope.row.noPayAmount, 'USDT').truncated"
:content="formatAmount(scope.row && scope.row.noPayAmount, 'USDT').full"
placement="top"
>
<span>
{{ formatAmount(scope.row && scope.row.noPayAmount, 'USDT').text }}
<i class="el-icon-more amount-more"></i>
</span>
</el-tooltip>
<span v-else>{{ formatAmount(scope.row && scope.row.noPayAmount, 'USDT').text }}</span>
</span>
</template>
<el-table-column label="订单完成时间" width="160">
<template #default="scope">{{ formatDateTime(scope.row && scope.row.endTime) }}</template>
</el-table-column>
<el-table-column label="操作" min-width="280" fixed="right">
<el-table-column label="操作" min-width="60" fixed="right">
<template #default="scope">
<el-button size="mini" @click="handleGoDetail(scope.row)" style="margin-right:8px;">详情</el-button>
<template v-if="shouldShowActions(scope.row)">
@@ -241,6 +235,18 @@ export default {
if (!value) return '—'
try { const str = String(value); return str.includes('T') ? str.replace('T', ' ') : str } catch (e) { return String(value) }
},
/**
* 格式化矿机类型
* @param {number} type - 矿机类型0=ASIC, 1=GPU
* @returns {string} 类型文本
*/
formatMachineType(type) {
if (type === null || type === undefined) return '—'
const typeNum = Number(type)
if (typeNum === 0) return 'ASIC'
if (typeNum === 1) return 'GPU'
return '—'
},
async handleCheckout(row) {
if (!row) return
try {

View File

@@ -64,6 +64,7 @@ export default {
buyerLinks: [
{ label: '我的钱包', to: '/account/wallet' },
{ label: '已购商品', to: '/account/purchased' },
{ label: '已购矿机配置', to: '/account/purchased-machine-config' },
{ label: '订单列表', to: '/account/orders' },
// { label: '充值记录', to: '/account/rechargeRecord' },
// { label: '提现记录', to: '/account/withdrawalHistory' },
@@ -154,11 +155,12 @@ export default {
}
return
}
// 买家前缀优先匹配,确保已购详情等页面归属买家侧
// 买家前缀优先匹配,确保"已购详情"等页面归属买家侧
const buyerPrefixes = [
'/account/wallet',
'/account/purchased',
'/account/purchased-detail',
'/account/purchased-machine-config',
'/account/orders',
'/account/funds-flow'
]
@@ -206,13 +208,26 @@ export default {
const map = {
'/account/seller-orders': ['/account/seller-orders'],
'/account/products': ['/account/products', '/account/product-detail'],
'/account/purchased': ['/account/purchased', '/account/purchased-detail']
'/account/purchased': ['/account/purchased', '/account/purchased-detail'],
'/account/purchased-machine-config': ['/account/purchased-machine-config']
}
const prefixes = map[pathLike]
if (Array.isArray(prefixes)) {
return prefixes.some(p => current.indexOf(p) === 0)
return prefixes.some(p => {
// 精确匹配
if (current === p) return true
// 对于详情页路径,使用前缀匹配(支持 /account/purchased-detail/:id
if (p === '/account/purchased-detail' || p === '/account/product-detail') {
return current.indexOf(p) === 0
}
// 列表页已经在上面精确匹配了,这里不需要额外处理
return false
})
}
return current.indexOf(pathLike) === 0
// 不在 map 中的路径,精确匹配
// 特别注意:避免 /account/purchased 匹配到 /account/purchased-machine-config
// 使用精确匹配而不是前缀匹配
return current === pathLike
}
},
watch: {
@@ -344,6 +359,7 @@ export default {
border-radius: 8px;
padding: 16px;
min-height: 420px;
width: 82vw;
}
@media (max-width: 768px) {

View File

@@ -7,8 +7,10 @@
<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">{{ getOrderStatusText(order.status) }}</span></div>
<div class="row"><span class="label">店铺</span><span class="value">{{ order.shopName || '—' }}</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>
<div class="row"><span class="label">订单完成时间</span><span class="value">{{ formatDateTime(order.endTime) }}</span></div>
</el-card>
<el-card class="section" style="margin-top:12px;">
@@ -16,9 +18,14 @@
<el-table :data="items" border size="small" style="width:100%"
:header-cell-style="{ textAlign: 'left' }" :cell-style="{ textAlign: 'left' }">
<el-table-column prop="productMachineId" label="机器ID" min-width="120" />
<el-table-column prop="name" label="名称" min-width="160" />
<el-table-column label="矿机类型" min-width="100">
<template #default="scope">{{ formatMachineType(scope.row && scope.row.type) }}</template>
</el-table-column>
<el-table-column prop="payCoin" label="币种" min-width="100" />
<el-table-column prop="leaseTime" label="租赁天数" min-width="100" />
<el-table-column label="购买数量" min-width="100">
<template #default="scope">{{ scope.row && scope.row.numbers != null ? scope.row.numbers : '—' }}</template>
</el-table-column>
<el-table-column prop="price" label="单价(USDT)" min-width="120" />
<el-table-column prop="address" label="收款地址" min-width="240" />
</el-table>
@@ -91,6 +98,18 @@ export default {
} catch (e) {
return String(value)
}
},
/**
* 格式化矿机类型
* @param {number} type - 矿机类型0=ASIC, 1=GPU
* @returns {string} 类型文本
*/
formatMachineType(type) {
if (type === null || type === undefined) return '—'
const typeNum = Number(type)
if (typeNum === 0) return 'ASIC'
if (typeNum === 1) return 'GPU'
return '—'
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,421 @@
<template>
<div class="account-purchased-machine-config">
<div class="toolbar">
<div class="left-area">
<h2 class="page-title">已购矿机配置</h2>
</div>
</div>
<el-table
:data="tableData"
v-loading="loading"
border
stripe
style="width: 100%"
:header-cell-style="{ textAlign: 'left' }"
:cell-style="{ textAlign: 'left' }"
>
<el-table-column type="expand" width="46">
<template #default="scope">
<div class="expand-content">
<div class="expand-row" v-if="scope.row.walletAddress">
<div class="expand-label">钱包地址</div>
<div class="expand-value">
<span class="mono-ellipsis" style="font-family: monospace;">{{ scope.row.walletAddress }}</span>
<el-button
type="text"
size="mini"
icon="el-icon-document-copy"
@click="handleCopy(scope.row.walletAddress, '钱包地址')"
class="copy-btn"
>
复制
</el-button>
</div>
</div>
<div class="expand-row" v-if="scope.row.poolUrl">
<div class="expand-label">矿池地址</div>
<div class="expand-value">
<span class="mono-ellipsis">{{ scope.row.poolUrl }}</span>
<el-button
type="text"
size="mini"
icon="el-icon-document-copy"
@click="handleCopy(scope.row.poolUrl, '矿池地址')"
class="copy-btn"
>
复制
</el-button>
</div>
</div>
<div class="expand-row" v-if="scope.row.watchUrl">
<div class="expand-label">挖矿信息页面地址</div>
<div class="expand-value">
<span class="mono-ellipsis">{{ scope.row.watchUrl }}</span>
<el-button
type="text"
size="mini"
icon="el-icon-document-copy"
@click="handleCopy(scope.row.watchUrl, '挖矿信息页面地址')"
class="copy-btn"
>
复制
</el-button>
</div>
</div>
<div v-if="!scope.row.walletAddress && !scope.row.poolUrl && !scope.row.watchUrl" class="expand-empty">
暂无地址信息
</div>
</div>
</template>
</el-table-column>
<!-- <el-table-column prop="id" label="ID" width="80" /> -->
<el-table-column prop="coin" label="币种" width="100">
<template #default="scope">
<span>{{ scope.row.coin || '—' }}</span>
</template>
</el-table-column>
<el-table-column prop="algorithm" label="算法" min-width="120">
<template #default="scope">
<span>{{ scope.row.algorithm || '—' }}</span>
</template>
</el-table-column>
<el-table-column prop="pool" label="矿池" min-width="140">
<template #default="scope">
<span>{{ scope.row.pool || '—' }}</span>
</template>
</el-table-column>
<el-table-column prop="poolUser" label="矿池用户" min-width="140">
<template #default="scope">
<span>{{ scope.row.poolUser || '—' }}</span>
</template>
</el-table-column>
<el-table-column prop="startTime" label="开始时间" min-width="160">
<template #default="scope">
<span>{{ formatDateTime(scope.row.startTime) }}</span>
</template>
</el-table-column>
<el-table-column prop="endTime" label="结束时间" min-width="160">
<template #default="scope">
<span>{{ formatDateTime(scope.row.endTime) }}</span>
</template>
</el-table-column>
<el-table-column prop="status" label="状态" width="140">
<template #default="scope">
<el-tag :type="getStatusType(scope.row.status)">
{{ getStatusText(scope.row.status) }}
</el-tag>
</template>
</el-table-column>
</el-table>
<div class="pagination">
<el-pagination
background
layout="total, sizes, prev, pager, next, jumper"
:total="total"
:current-page.sync="pagination.pageNum"
:page-sizes="[10, 20, 50, 100]"
:page-size.sync="pagination.pageSize"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
</template>
<script>
import { getPurchasedItems } from '../../api/order'
/**
* 已购矿机配置页面
* - 展示已购买矿机的配置信息
* - 支持分页查询
*/
export default {
name: 'AccountPurchasedMachineConfig',
data() {
return {
loading: false,
tableData: [],
pagination: {
pageNum: 1,
pageSize: 10
},
total: 0,
totalPage: 0
}
},
created() {
this.fetchTableData()
},
methods: {
/**
* 获取已购矿机配置列表
*/
async fetchTableData() {
this.loading = true
try {
const params = {
pageNum: this.pagination.pageNum,
pageSize: this.pagination.pageSize
}
const res = await getPurchasedItems(params)
if (res && (res.code === 0 || res.code === 200)) {
this.tableData = Array.isArray(res.rows) ? res.rows : []
this.total = Number(res.total || 0)
this.totalPage = Number(res.totalPage || 0)
} else {
this.tableData = []
this.total = 0
this.totalPage = 0
}
} catch (e) {
console.error('获取已购矿机配置失败', e)
this.$message({
message: '获取已购矿机配置失败,请重试',
type: 'error',
showClose: true
})
this.tableData = []
this.total = 0
this.totalPage = 0
} finally {
this.loading = false
}
},
/**
* 处理分页大小变化
* @param {number} size - 新的分页大小
*/
handleSizeChange(size) {
this.pagination.pageSize = size
this.pagination.pageNum = 1
this.fetchTableData()
},
/**
* 处理当前页变化
* @param {number} page - 新的页码
*/
handleCurrentChange(page) {
this.pagination.pageNum = page
this.fetchTableData()
},
/**
* 格式化日期时间
* @param {string|number} value - 日期时间值
* @returns {string} 格式化后的日期时间字符串
*/
formatDateTime(value) {
if (!value) return '—'
try {
const str = String(value)
return str.includes('T') ? str.replace('T', ' ') : str
} catch (e) {
return String(value)
}
},
/**
* 复制文本到剪贴板
* @param {string} text - 需要复制的内容
* @param {string} label - 语义标签,用于提示文案
*/
async handleCopy(text, label = '内容') {
if (!text) {
this.$message({
message: `${label}为空,无法复制`,
type: 'warning',
showClose: true
})
return
}
try {
const value = String(text).trim()
if (navigator && navigator.clipboard && navigator.clipboard.writeText) {
await navigator.clipboard.writeText(value)
this.$message({
message: `${label}已复制到剪贴板`,
type: 'success',
showClose: true
})
} else {
// 备用复制方法
const textArea = document.createElement('textarea')
textArea.value = value
textArea.style.position = 'fixed'
textArea.style.left = '-9999px'
document.body.appendChild(textArea)
textArea.focus()
textArea.select()
try {
document.execCommand('copy')
this.$message({
message: `${label}已复制到剪贴板`,
type: 'success',
showClose: true
})
} catch (err) {
this.$message({
message: '复制失败,请手动复制',
type: 'error',
showClose: true
})
}
document.body.removeChild(textArea)
}
} catch (e) {
console.error('复制失败', e)
this.$message({
message: '复制失败,请手动复制',
type: 'error',
showClose: true
})
}
},
/**
* 获取状态文本
* @param {number|boolean} status - 状态值
* @returns {string} 状态文本
*/
getStatusText(status) {
const statusNum = Number(status)
if (statusNum === 0) return '租约已到期'
if (statusNum === 1) return '挖矿中'
if (statusNum === 2) return '卖家矿机启动中'
// 兼容旧数据boolean类型
if (status === true) return '挖矿中'
return '未知状态'
},
/**
* 获取状态标签类型
* @param {number|boolean} status - 状态值
* @returns {string} el-tag 的类型
*/
getStatusType(status) {
const statusNum = Number(status)
if (statusNum === 0) return 'info' // 租约已到期 - 灰色
if (statusNum === 1) return 'success' // 挖矿中 - 绿色
if (statusNum === 2) return 'warning' // 卖家矿机启动中 - 黄色
// 兼容旧数据boolean类型
if (status === true) return 'success'
return 'info'
}
}
}
</script>
<style scoped>
.account-purchased-machine-config {
padding: 4px;
}
.toolbar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.left-area {
display: flex;
align-items: center;
}
.page-title {
margin: 0;
font-size: 18px;
font-weight: 600;
color: #2c3e50;
}
.pagination {
display: flex;
justify-content: flex-end;
margin-top: 12px;
}
/* 展开内容样式 */
.expand-content {
padding: 16px;
background-color: #fafafa;
}
.expand-row {
display: flex;
align-items: flex-start;
margin-bottom: 16px;
padding: 12px;
background-color: #fff;
border-radius: 4px;
border: 1px solid #e4e7ed;
}
.expand-row:last-child {
margin-bottom: 0;
}
.expand-label {
min-width: 100px;
font-weight: 600;
color: #606266;
margin-right: 12px;
flex-shrink: 0;
}
.expand-value {
flex: 1;
display: flex;
align-items: flex-start;
gap: 8px;
flex-wrap: wrap;
}
.expand-value .mono-ellipsis {
font-family: monospace;
color: #303133;
line-height: 1.5;
word-break: break-all;
flex: 0 1 auto;
margin-right: 8px;
}
.copy-btn {
flex-shrink: 0;
padding: 0 8px;
color: #409eff;
}
.copy-btn:hover {
color: #66b1ff;
}
.expand-empty {
padding: 20px;
text-align: center;
color: #909399;
font-size: 14px;
}
/* 钱包地址和URL格式化显示样式 */
.mono-ellipsis {
font-family: monospace;
display: inline-block;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
vertical-align: middle;
}
</style>

View File

@@ -51,7 +51,7 @@
</template>
</el-table-column>
<el-table-column prop="id" label="机器ID" width="80" />
<el-table-column prop="algorithm" label="算法" />
<el-table-column prop="algorithm" label="最大收益算法" />
<!-- <el-table-column prop="powerDissipation" label="功耗(kw/h)">
@@ -343,7 +343,7 @@
</template>
</el-table-column>
<el-table-column prop="id" label="机器ID" min-width="160" />
<el-table-column prop="algorithm" label="算法" min-width="140" />
<el-table-column prop="algorithm" label="最大收益算法" min-width="140" />
<el-table-column prop="unitPrice" min-width="140">
<template #header>单价({{ grp.coinSymbol || 'USDT' }}</template>
<template #default="scope">
@@ -408,6 +408,7 @@
</div>
</div>
<template #footer>
<el-button @click="handleConfirmDialogPrev">上一步</el-button>
<el-button @click="confirmDialog.visible=false">取消</el-button>
<el-button type="primary" @click="confirmPay">确认结算</el-button>
</template>
@@ -416,7 +417,7 @@
<!-- 购物须知(测试版) - 必须勾选并等待5秒后才可关闭 -->
<el-dialog :visible.sync="noticeDialog.visible" width="680px" title="下单须知" :show-close="false" :close-on-click-modal="false" :close-on-press-escape="false">
<el-dialog :visible.sync="noticeDialog.visible" width="60vw" title="下单须知" :show-close="false" :close-on-click-modal="false" :close-on-press-escape="false">
<div class="notice-content">
<p class="notice-title">尊敬的客户,感谢您选择我们的服务。在您下单前,请务必仔细阅读并完全理解以下须知条款。一旦您点击" 同意并下单"或完成支付流程,即视为您已充分阅读、理解并同意接受本须知的全部内容约束。</p>
<ol class="notice-list">
@@ -445,7 +446,7 @@
:close-on-click-modal="false" :close-on-press-escape="false">
<div>
<!-- 未配置的机器配置区域 -->
<div v-if="unconfiguredMachinesList.length > 0">
<div v-if="configDialog.selectedMachines && configDialog.selectedMachines.length > 0">
<div style="display:flex;align-items:center;gap:12px;margin-bottom:12px;">
<div style="font-weight:600;color:#2c3e50;">选择币种/算法</div>
<el-cascader
@@ -468,65 +469,121 @@
style="width: 240px;"
size="small"
@change="handlePoolValueChange"
:disabled="!configDialog.coinAlgoValue || configDialog.coinAlgoValue.length !== 2"
/>
</div>
<el-table
:data="unconfiguredMachinesList"
max-height="300"
border
stripe
ref="configTable"
row-key="id"
reserve-selection
@selection-change="handleConfigSelectionChange"
:header-cell-style="{ textAlign: 'left', fontSize: '14px' }"
:cell-style="{ textAlign: 'left', fontSize: '14px' }">
<el-table-column type="selection" width="46" />
<el-table-column label="矿机种类" width="120">
<template #default="scope">
<el-tag
disable-transitions
:class="['type-tag', typeClass(scope.row.type)]"
style="font-size: 14px;"
>
{{ formatMachineType(scope.row.type) }}
</el-tag>
<!-- 机器列表 -->
<div v-if="unconfiguredMachinesList.length > 0">
<div style="font-weight:600;color:#2c3e50;margin-bottom:8px;font-size:14px;">
<template v-if="!configDialog.coinAlgoValue || configDialog.coinAlgoValue.length !== 2">
待配置机器(共 {{ unconfiguredMachinesList.length }} 台)
</template>
</el-table-column>
<el-table-column prop="id" label="机器ID" width="120" />
<el-table-column prop="algorithm" label="算法" min-width="140" />
</el-table>
<div style="display:flex;align-items:center;gap:12px;margin-top:12px;flex-wrap: nowrap;">
<div style="font-weight:600;color:#2c3e50;white-space: nowrap;">钱包地址</div>
<el-input
v-model="configDialog.walletAddress"
placeholder="请输入钱包地址26-95个字符仅支持英文数字"
size="small"
style="width: 380px;"
clearable
maxlength="95"
@input="handleWalletAddressInput"
/>
<div style="font-weight:600;color:#2c3e50;margin-left:8px;white-space: nowrap;">挖矿账户</div>
<el-input
v-model="configDialog.miningAccount"
placeholder="挖矿账户仅支持英文数字"
size="small"
style="width: 220px;"
clearable
maxlength="50"
@input="handleMiningAccountInput"
/>
<div style="font-weight:600;color:#2c3e50;margin-left:8px;white-space: nowrap;">矿工号</div>
<el-input
v-model="configDialog.workerId"
placeholder="矿工号最多24个英文字符"
size="small"
style="width: 220px;"
clearable
maxlength="24"
@input="handleWorkerIdInput"
/>
<template v-else>
支持该币种/算法的机器(共 {{ unconfiguredMachinesList.length }} 台)
</template>
</div>
<el-table
:data="unconfiguredMachinesList"
max-height="300"
border
stripe
ref="configTable"
row-key="id"
reserve-selection
@selection-change="handleConfigSelectionChange"
:header-cell-style="{ textAlign: 'left', fontSize: '14px' }"
:cell-style="{ textAlign: 'left', fontSize: '14px' }">
<el-table-column type="selection" width="46" />
<el-table-column label="矿机种类" width="120">
<template #default="scope">
<el-tag
disable-transitions
:class="['type-tag', typeClass(scope.row.type)]"
style="font-size: 14px;"
>
{{ formatMachineType(scope.row.type) }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="id" label="机器ID" width="120" />
<el-table-column prop="algorithm" label="最大收益算法" min-width="140" />
<!-- 矿工号列始终显示(只要选择了矿池/模型) -->
<el-table-column
v-if="getCurrentWalletMining() !== null"
label="矿工号"
min-width="160">
<template #default="scope">
<el-input
v-model="scope.row._workerId"
placeholder="矿工号"
size="mini"
clearable
maxlength="40"
@input="val => handleMachineWorkerIdInput(scope.row, val)"
/>
</template>
</el-table-column>
</el-table>
</div>
<!-- 统一输入区域:根据 walletMining 显示钱包地址或挖矿账户 -->
<div v-if="getCurrentWalletMining() !== null" style="margin-top: 16px;">
<!-- 钱包地址输入(当 walletMining 为 true 时显示) -->
<div v-if="getCurrentWalletMining() === true" style="display: flex; align-items: center; gap: 12px;">
<label style="font-weight: 600; color: #2c3e50; min-width: 100px;">钱包地址:</label>
<el-input
v-model="configDialog.walletAddress"
placeholder="请输入钱包地址"
clearable
maxlength="95"
style="flex: 1; max-width: 500px;"
@input="handleWalletAddressInput"
/>
</div>
<!-- 挖矿账户输入(当 walletMining 为 false 时显示) -->
<div v-if="getCurrentWalletMining() === false" style="display: flex; align-items: center; gap: 12px;">
<label style="font-weight: 600; color: #2c3e50; min-width: 100px;">挖矿账户:</label>
<el-input
v-model="configDialog.miningAccount"
placeholder="请输入挖矿账户"
clearable
maxlength="40"
style="flex: 1; max-width: 500px;"
@input="handleMiningAccountInput"
/>
</div>
</div>
<div v-else-if="configDialog.coinAlgoValue && configDialog.coinAlgoValue.length === 2 && getCurrentWalletMining() === null" style="margin-top: 16px; color: #909399; font-size: 14px;">
请先选择矿池/模型
</div>
<div v-else-if="configDialog.coinAlgoValue && configDialog.coinAlgoValue.length === 2 && unconfiguredMachinesList.length === 0" style="margin-bottom:12px;color:#909399;font-size:14px;">
当前选择的币种/算法下没有可配置的机器
</div>
<!-- 不支持的机器列表 -->
<div v-if="unsupportedMachinesList.length > 0" style="margin-top: 16px;">
<div style="font-weight:600;color:#f56c6c;margin-bottom:8px;font-size:14px;">
不支持该币种/算法的机器(共 {{ unsupportedMachinesList.length }} 台)
</div>
<el-table
:data="unsupportedMachinesList"
max-height="200"
border
stripe
:header-cell-style="{ textAlign: 'left', fontSize: '14px' }"
:cell-style="{ textAlign: 'left', fontSize: '14px' }">
<el-table-column label="矿机种类" width="120">
<template #default="scope">
<el-tag
disable-transitions
:class="['type-tag', typeClass(scope.row.type)]"
style="font-size: 14px;"
>
{{ formatMachineType(scope.row.type) }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="id" label="机器ID" width="120" />
<el-table-column prop="algorithm" label="最大收益算法" min-width="140" />
</el-table>
</div>
</div>
@@ -560,12 +617,21 @@
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column prop="miningAccount" label="挖矿账户" min-width="140" />
<el-table-column prop="workerId" label="矿工号" min-width="140" />
<el-table-column
v-if="hasConfiguredMiningAccount"
prop="miningAccount"
label="挖矿账户"
min-width="140" />
<el-table-column
v-if="hasConfiguredWorkerId"
prop="workerId"
label="矿工号"
min-width="140" />
</el-table>
</div>
</div>
<template #footer>
<el-button @click="handleConfigPrev">上一步</el-button>
<el-button @click="handleConfigCancel">取消</el-button>
<el-button
type="primary"
@@ -609,6 +675,7 @@
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="handleGoogleCodePrev">上一步</el-button>
<el-button @click="handleGoogleCodeCancel">取消</el-button>
<el-button
type="primary"
@@ -781,11 +848,67 @@ export default {
payCoinSymbol() {
return (this.selectedCoin || '').toUpperCase()
},
// 未配置的机器列表(计算属性,避免每次渲染都重新计算导致选择状态丢失
// 未配置的机器列表(计算属性,根据选择的币种/算法和machineIds过滤
unconfiguredMachinesList() {
const allMachines = this.configDialog.selectedMachines || []
const configuredIds = new Set((this.configDialog.configuredMachines || []).map(m => m.id))
return allMachines.filter(m => !configuredIds.has(m.id))
const unconfigured = allMachines.filter(m => !configuredIds.has(m.id))
// 如果未选择币种/算法,返回所有未配置的机器
const coinAlgo = Array.isArray(this.configDialog.coinAlgoValue) ? this.configDialog.coinAlgoValue : []
if (coinAlgo.length !== 2) {
return unconfigured
}
const coin = coinAlgo[0]
const algorithm = coinAlgo[1]
// 从machineSupportData中查找对应的machineIds
const coinData = (this.machineSupportData || []).find(it => String(it && it.coin) === String(coin))
if (!coinData) {
return []
}
const algoData = (coinData.algorithmList || []).find(a => String(a && a.algorithm) === String(algorithm))
if (!algoData) {
return []
}
const supportedMachineIds = new Set((algoData.machineIds || []).map(id => Number(id)))
// 只返回在machineIds中的机器
return unconfigured.filter(m => supportedMachineIds.has(Number(m.id)))
},
// 不支持的机器列表(不在当前选择的币种/算法的machineIds中
unsupportedMachinesList() {
const allMachines = this.configDialog.selectedMachines || []
const configuredIds = new Set((this.configDialog.configuredMachines || []).map(m => m.id))
const unconfigured = allMachines.filter(m => !configuredIds.has(m.id))
// 如果未选择币种/算法,返回空列表
const coinAlgo = Array.isArray(this.configDialog.coinAlgoValue) ? this.configDialog.coinAlgoValue : []
if (coinAlgo.length !== 2) {
return []
}
const coin = coinAlgo[0]
const algorithm = coinAlgo[1]
// 从machineSupportData中查找对应的machineIds
const coinData = (this.machineSupportData || []).find(it => String(it && it.coin) === String(coin))
if (!coinData) {
return []
}
const algoData = (coinData.algorithmList || []).find(a => String(a && a.algorithm) === String(algorithm))
if (!algoData) {
return []
}
const supportedMachineIds = new Set((algoData.machineIds || []).map(id => Number(id)))
// 返回不在machineIds中的机器
return unconfigured.filter(m => !supportedMachineIds.has(Number(m.id)))
},
/**
* 检查是否可以点击按钮(计算属性,确保响应式更新)
@@ -844,6 +967,16 @@ export default {
const allConfigured = Array.from(allMachineIds).every(id => configuredIds.has(id))
return allConfigured
},
// 判断已配置机器中是否有挖矿账户数据
hasConfiguredMiningAccount() {
const configured = this.configDialog.configuredMachines || []
return configured.some(m => m.miningAccount && String(m.miningAccount).trim().length > 0)
},
// 判断已配置机器中是否有矿工号数据
hasConfiguredWorkerId() {
const configured = this.configDialog.configuredMachines || []
return configured.some(m => m.workerId && String(m.workerId).trim().length > 0)
}
},
mounted() {
@@ -1573,8 +1706,8 @@ export default {
// 构建订单信息列表 orderInfoVoList
const orderInfoVoList = []
// 构建挖矿配置列表 orderMiningInfoDtoList按 coinConfigId 分组
const miningConfigMap = new Map() // key: coinConfigId, value: { coinConfigId, poolName, poolUser, walletAddress, walletMining, workerId, machineIds: [] }
// 构建挖矿配置列表 orderMiningInfoDtoList每台机器一个配置项,使用 machineId 字符串
const orderMiningInfoDtoList = []
// 处理单店铺或多店铺的机器
const allMachines = []
@@ -1595,7 +1728,7 @@ export default {
})
}
// 构建订单信息列表和挖矿配置映射
// 构建订单信息列表和挖矿配置列表
allMachines.forEach(({ shop, machine }) => {
const sel = this.paySelectionMap[shop.id] || ''
const [chain, coin] = String(sel).split('|')
@@ -1618,32 +1751,29 @@ export default {
shopId: shop.id
})
// 按 coinConfigId 分组构建挖矿配置
// 为每台机器单独创建挖矿配置项,使用 machineIdstring而不是 machineIdsarray
const coinConfigId = config.coinConfigId
if (!coinConfigId) {
console.warn(`机器 ${machine.id} 的 coinConfigId 为空`)
return
}
if (!miningConfigMap.has(coinConfigId)) {
miningConfigMap.set(coinConfigId, {
coinConfigId: coinConfigId,
poolName: config.poolName || '',
poolUser: config.miningAccount || '',
walletAddress: config.walletAddress || '',
walletMining: config.walletMining || '',
workerId: config.workerId || '',
machineIds: []
})
}
const walletAddress = String(config.walletAddress || '').trim()
const poolUser = String(config.miningAccount || '').trim()
const workerId = String(config.workerId || '').trim()
const miningConfig = miningConfigMap.get(coinConfigId)
miningConfig.machineIds.push(machine.id)
// 为每台机器创建独立的配置项
orderMiningInfoDtoList.push({
coinConfigId: coinConfigId,
poolName: config.poolName || '',
poolUser: poolUser,
walletAddress: walletAddress,
walletMining: config.walletMining || false,
workerId: workerId,
machineId: String(machine.id) // 使用 machineIdstring而不是 machineIdsarray
})
})
// 转换为数组
const orderMiningInfoDtoList = Array.from(miningConfigMap.values())
this.creatingOrder = true
try {
const res = await this.fetchAddOrdersV2(orderInfoVoList, orderMiningInfoDtoList, googleCode)
@@ -1710,7 +1840,7 @@ export default {
this.$message({ message: '未找到可结算的上架机器', type: 'warning', showClose: true })
return
}
// 在点击结算选中机器时请求币种/算法支持数据
// 在点击"结算选中机器"时请求币种/算法支持数据
try {
const ids = []
picked.forEach(({ items }) => {
@@ -1720,8 +1850,24 @@ export default {
})
if (ids.length) {
await this.fetchGetMachineSupportCoinAndAlgorithm(ids)
// 检查是否获取到数据
if (!this.machineSupportData || this.machineSupportData.length === 0) {
this.$message({
message: '加载配置信息失败,请重试',
type: 'error',
showClose: true
})
return
}
}
} catch (e) { /* noop */ }
} catch (e) {
this.$message({
message: '加载配置信息失败,请重试',
type: 'error',
showClose: true
})
return
}
this.pendingCheckoutAll = picked
// 打开须知弹窗
this.noticeDialog.visible = true
@@ -1774,6 +1920,12 @@ export default {
this.confirmDialog.visible = false
this.showGoogleCodeDialog()
},
// 确认结算弹窗的上一步:返回到配置弹窗
handleConfirmDialogPrev() {
this.confirmDialog.visible = false
// 重新打开配置弹窗
this.configDialog.visible = true
},
// 关闭成功弹窗:跳转到订单列表的订单进行中状态
handleCloseSuccessDialog() {
try { this.settlementSuccessfulVisible = false } catch (e) { /* noop */ }
@@ -1813,6 +1965,15 @@ export default {
},
// 打开配置弹窗并初始化选项与机器列表
openConfigDialog() {
// 检查是否获取到配置数据
if (!this.machineSupportData || this.machineSupportData.length === 0) {
this.$message({
message: '加载配置信息失败请重试',
type: 'error',
showClose: true
})
return
}
// 重置已配置列表
this.configDialog.configuredMachines = []
// 初始化币种选项
@@ -1831,21 +1992,10 @@ export default {
value: a && a.algorithm ? String(a.algorithm) : ''
})) : []
}))
// 默认选中第一个币种
if (!this.configDialog.coin && coinOptions.length) {
this.configDialog.coin = coinOptions[0].value
}
// 默认算法为该币种下第一个
try {
const firstCoin = (coins && coins.length) ? coins[0] : null
const firstAlgo = firstCoin && Array.isArray(firstCoin.algorithmList) && firstCoin.algorithmList.length
? (firstCoin.algorithmList[0].algorithm || '')
: ''
if (!this.configDialog.algorithm) this.configDialog.algorithm = firstAlgo
this.configDialog.coinAlgoValue = [this.configDialog.coin, this.configDialog.algorithm].filter(Boolean)
} catch (e) { /* noop */ }
// 根据币种初始化算法选项
this.refreshAlgorithmOptions()
// 默认选择币种和算法,等用户自己选择
this.$set(this.configDialog, 'coin', '')
this.$set(this.configDialog, 'algorithm', '')
this.$set(this.configDialog, 'coinAlgoValue', [])
// 构建展示用机器列表
this.configDialog.selectedMachines = this.buildSelectedMachinesForConfig()
// 重置选择状态
@@ -1855,12 +2005,9 @@ export default {
this.$set(this.configDialog, 'miningAccount', '')
this.$set(this.configDialog, 'workerId', '')
this.$set(this.configDialog, 'poolValue', [])
// 同步加载矿池/模型下拉
try {
if (this.configDialog.coin && this.configDialog.algorithm) {
this.fetchGetMachineSupportPool(this.configDialog.coin, this.configDialog.algorithm)
}
} catch (e) { /* noop */ }
// 重置矿池选项
this.$set(this.configDialog, 'poolOptions', [])
this.$set(this.configDialog, 'poolRawData', [])
// 打开弹窗
this.configDialog.visible = true
},
@@ -1915,10 +2062,20 @@ export default {
this.$set(this.configDialog, 'coin', coin)
this.$set(this.configDialog, 'algorithm', algo)
this.$set(this.configDialog, 'coinAlgoValue', arr)
// 强制触发视图更新
// 清空表格选择状态
this.$set(this.configDialog, 'selectedMachineIds', [])
this.$nextTick(() => {
try {
const tb = this.$refs.configTable
if (tb && typeof tb.clearSelection === 'function') {
tb.clearSelection()
}
} catch (e) { /* noop */ }
this.$forceUpdate()
})
// 清空统一输入框和矿池/模型选择
this.$set(this.configDialog, 'walletAddress', '')
this.$set(this.configDialog, 'miningAccount', '')
if (coin && algo) {
this.fetchGetMachineSupportPool(coin, algo)
} else {
@@ -1930,11 +2087,95 @@ export default {
// 矿池/模型选择变化
handlePoolValueChange(val) {
this.$set(this.configDialog, 'poolValue', val || [])
// 获取当前选择的矿池/模型的 walletMining 值
const poolValue = Array.isArray(val) ? val : []
if (poolValue.length === 2) {
const poolId = poolValue[0]
const coinConfigId = poolValue[1]
const poolOption = (this.configDialog.poolOptions || []).find(p => p.value === poolId)
if (poolOption) {
const modelOption = (poolOption.children || []).find(m => m.value === coinConfigId)
if (modelOption && modelOption.raw) {
const walletMining = modelOption.raw.walletMining || false
// 清空统一输入框
this.$set(this.configDialog, 'walletAddress', '')
this.$set(this.configDialog, 'miningAccount', '')
// 清空所有未配置机器的矿工号
const unconfigured = this.unconfiguredMachinesList
unconfigured.forEach(machine => {
this.$set(machine, '_walletMining', walletMining)
this.$set(machine, '_workerId', '')
})
}
}
} else {
// 如果未选择矿池/模型,清空统一输入框
this.$set(this.configDialog, 'walletAddress', '')
this.$set(this.configDialog, 'miningAccount', '')
}
// 强制触发视图更新
this.$nextTick(() => {
this.$forceUpdate()
})
},
// 获取当前选择的矿池/模型的 walletMining 值
getCurrentWalletMining() {
const poolValue = Array.isArray(this.configDialog.poolValue) ? this.configDialog.poolValue : []
if (poolValue.length !== 2) {
return null
}
const poolId = poolValue[0]
const coinConfigId = poolValue[1]
const poolOption = (this.configDialog.poolOptions || []).find(p => p.value === poolId)
if (poolOption) {
const modelOption = (poolOption.children || []).find(m => m.value === coinConfigId)
if (modelOption && modelOption.raw) {
return modelOption.raw.walletMining || false
}
}
return null
},
// 处理机器钱包地址输入
handleMachineWalletAddressInput(machine, val) {
if (!val) {
this.$set(machine, '_walletAddress', '')
return
}
// 只过滤中文字符,其他字符都可以输入
// 匹配中文字符范围:\u4e00-\u9fa5 (基本汉字), \u3400-\u4dbf (扩展A), \u3000-\u303f (CJK符号和标点), \uff00-\uffef (全角字符)
let filtered = val.replace(/[\u4e00-\u9fa5\u3400-\u4dbf\u3000-\u303f\uff00-\uffef]/g, '')
// 限制最大长度95
if (filtered.length > 95) {
filtered = filtered.substring(0, 95)
}
this.$set(machine, '_walletAddress', filtered)
},
// 处理机器挖矿账户输入
handleMachineMiningAccountInput(machine, val) {
if (!val) {
this.$set(machine, '_miningAccount', '')
return
}
// 只限制最大长度40不限制其他输入
let filtered = String(val)
if (filtered.length > 40) {
filtered = filtered.substring(0, 40)
}
this.$set(machine, '_miningAccount', filtered)
},
// 处理机器矿工号输入
handleMachineWorkerIdInput(machine, val) {
if (!val) {
this.$set(machine, '_workerId', '')
return
}
// 只限制最大长度40不限制其他输入
let filtered = String(val)
if (filtered.length > 40) {
filtered = filtered.substring(0, 40)
}
this.$set(machine, '_workerId', filtered)
},
// 钱包地址输入过滤中文限制长度26-95
handleWalletAddressInput(val) {
if (!val) {
@@ -2095,8 +2336,6 @@ export default {
// 验证表单完整性
const coinAlgo = Array.isArray(this.configDialog.coinAlgoValue) ? this.configDialog.coinAlgoValue : []
const pool = Array.isArray(this.configDialog.poolValue) ? this.configDialog.poolValue : []
const wallet = String(this.configDialog.walletAddress || '').trim()
const account = String(this.configDialog.miningAccount || '').trim()
if (coinAlgo.length !== 2) {
this.$message.warning('请选择币种和算法')
@@ -2108,22 +2347,6 @@ export default {
return
}
if (!wallet || wallet.length === 0) {
this.$message.warning('请输入钱包地址')
return
}
// 验证钱包地址长度26-95个字符
if (wallet.length < 26 || wallet.length > 95) {
this.$message.warning('钱包地址长度应在26-95个字符之间')
return
}
if (!account || account.length === 0) {
this.$message.warning('请输入挖矿账户')
return
}
// 获取选中的机器(使用上面验证时获取的 selectedIds
const selectedMachines = unconfigured.filter(m =>
selectedIds.includes(m.id)
@@ -2134,7 +2357,7 @@ export default {
return
}
// 获取矿池和模型名称
// 获取矿池和模型名称以及 walletMining
const poolValue = this.configDialog.poolValue || []
const poolId = poolValue[0]
const coinConfigId = poolValue[1]
@@ -2142,7 +2365,7 @@ export default {
// 从 poolOptions 中查找名称和原始数据
let poolName = ''
let modelName = ''
let walletMining = ''
let walletMining = false
const poolOption = (this.configDialog.poolOptions || []).find(p => p.value === poolId)
if (poolOption) {
poolName = poolOption.label || ''
@@ -2150,13 +2373,47 @@ export default {
if (modelOption && modelOption.raw) {
modelName = modelOption.label || ''
// 从原始数据中获取 walletMining
walletMining = modelOption.raw.walletMining || ''
walletMining = modelOption.raw.walletMining || false
}
}
// 验证统一输入字段
if (walletMining) {
// 支持钱包地址挖矿,需要验证钱包地址
const wallet = String(this.configDialog.walletAddress || '').trim()
if (!wallet || wallet.length === 0) {
this.$message.warning('请输入钱包地址')
return
}
// 验证钱包地址长度26-95个字符
if (wallet.length < 26 || wallet.length > 95) {
this.$message.warning('钱包地址长度应在26-95个字符之间')
return
}
} else {
// 不支持钱包地址挖矿,需要验证挖矿账户
const account = String(this.configDialog.miningAccount || '').trim()
if (!account || account.length === 0) {
this.$message.warning('请输入挖矿账户')
return
}
}
// 验证每台机器的矿工号
for (const machine of selectedMachines) {
const workerId = String(machine._workerId || '').trim()
if (!workerId || workerId.length === 0) {
this.$message.warning(`机器ID ${machine.id} 请输入矿工号`)
return
}
}
// 保存配置(使用上面已经验证过的 coinAlgo
const coin = this.toUpperText(coinAlgo[0] || '')
const algorithm = String(coinAlgo[1] || '')
// 统一输入框的值
const walletAddress = walletMining ? String(this.configDialog.walletAddress || '').trim() : ''
const miningAccount = walletMining ? '' : String(this.configDialog.miningAccount || '').trim()
selectedMachines.forEach(machine => {
this.configDialog.configuredMachines.push({
@@ -2165,9 +2422,9 @@ export default {
algorithm: algorithm,
poolName: poolName,
modelName: modelName,
walletAddress: String(this.configDialog.walletAddress || '').trim(),
miningAccount: String(this.configDialog.miningAccount || '').trim(),
workerId: String(this.configDialog.workerId || '').trim(),
walletAddress: walletAddress,
miningAccount: miningAccount,
workerId: String(machine._workerId || '').trim(), // 矿工号从每行获取
walletMining: walletMining,
type: machine.type,
// 保存原始数据用于后续提交
@@ -2179,15 +2436,66 @@ export default {
})
})
// 计算剩下待配置的机器ID
const allMachineIds = new Set((this.configDialog.selectedMachines || []).map(m => m.id))
const configuredIds = new Set((this.configDialog.configuredMachines || []).map(m => m.id))
const remainingMachineIds = Array.from(allMachineIds).filter(id => !configuredIds.has(id))
// 如果有待配置的机器,重新请求币种/算法支持数据
if (remainingMachineIds.length > 0) {
this.fetchGetMachineSupportCoinAndAlgorithm(remainingMachineIds).then(() => {
// 检查是否获取到数据
if (!this.machineSupportData || this.machineSupportData.length === 0) {
this.$message({
message: '加载配置信息失败请重试',
type: 'error',
showClose: true
})
return
}
// 更新币种/算法选项
const coins = Array.isArray(this.machineSupportData) ? this.machineSupportData : []
this.configDialog.coinAlgoOptions = (coins || []).map(c => ({
label: this.toUpperText(c && c.coin),
value: c && c.coin,
children: Array.isArray(c && c.algorithmList) ? c.algorithmList.map(a => ({
label: a && a.algorithm ? String(a.algorithm) : '',
value: a && a.algorithm ? String(a.algorithm) : ''
})) : []
}))
// 如果更新后没有选项,清空选项
if (this.configDialog.coinAlgoOptions.length === 0) {
this.configDialog.coinAlgoOptions = []
}
}).catch(e => {
console.error('重新获取币种/算法支持数据失败:', e)
this.$message({
message: '加载配置信息失败请重试',
type: 'error',
showClose: true
})
})
} else {
// 如果没有待配置的机器,清空币种/算法选项
this.configDialog.coinAlgoOptions = []
}
// 清空当前输入,准备下一批配置
this.$set(this.configDialog, 'selectedMachineIds', [])
this.$set(this.configDialog, 'walletAddress', '')
this.$set(this.configDialog, 'miningAccount', '')
this.$set(this.configDialog, 'workerId', '')
this.$set(this.configDialog, 'poolValue', [])
this.$set(this.configDialog, 'coinAlgoValue', [])
this.$set(this.configDialog, 'coin', '')
this.$set(this.configDialog, 'algorithm', '')
// 清空统一输入框
this.$set(this.configDialog, 'walletAddress', '')
this.$set(this.configDialog, 'miningAccount', '')
// 清空所有未配置机器的矿工号
const remainingUnconfigured = this.unconfiguredMachinesList
remainingUnconfigured.forEach(machine => {
this.$set(machine, '_workerId', '')
this.$set(machine, '_walletMining', null)
})
// 清空表格选择
this.$nextTick(() => {
@@ -2254,6 +2562,14 @@ export default {
// 恢复勾选状态
this.reapplySelectionsForPendingShop()
},
// 配置弹窗的上一步:返回到下单须知弹窗
handleConfigPrev() {
this.configDialog.visible = false
// 重新打开下单须知弹窗
this.noticeDialog.visible = true
this.noticeDialog.checked = false
this.startNoticeCountdown()
},
openPaySelectDialog() {
// 已取消支付方式选择步骤,此函数保留空实现以兼容旧调用
return
@@ -2466,6 +2782,19 @@ export default {
this.reapplySelectionsForPendingShop()
this.pendingCheckoutShop = null
},
// 谷歌验证码弹窗的上一步:返回到确认结算弹窗
handleGoogleCodePrev() {
this.googleCodeDialog.visible = false
this.googleCodeDialog.code = ''
this.googleCodeDialog.error = ''
this.googleCodeDialog.loading = false
// 重新打开确认结算弹窗
if (this.pendingCheckoutAll && this.pendingCheckoutAll.length) {
this.showConfirmDialogAll()
} else {
this.showConfirmDialog()
}
},
// 处理租赁天数输入变化
handleLeaseTimeChange(machine) {
// 确保租赁天数是大于等于1的整数

View File

@@ -435,23 +435,54 @@ export default {
*/
hasAnyPrice(row) {
try {
if (!row) return false
if (!row) {
console.log('[hasAnyPrice] 行数据为空')
return false
}
if (Array.isArray(row.priceList) && row.priceList.length) {
return row.priceList.some(it => it && it.price !== null && it.price !== undefined)
const hasValidPrice = row.priceList.some(it => it && it.price !== null && it.price !== undefined)
if (!hasValidPrice) {
console.log(`[hasAnyPrice] 机器ID ${row.id || 'unknown'} priceList 中无有效价格:`, row.priceList)
}
return hasValidPrice
}
const v = row.price
return v !== null && v !== undefined && v !== ''
} catch (e) { return false }
const hasPrice = v !== null && v !== undefined && v !== ''
if (!hasPrice) {
console.log(`[hasAnyPrice] 机器ID ${row.id || 'unknown'} row.price 无效:`, v)
}
return hasPrice
} catch (e) {
console.error('[hasAnyPrice] 异常:', e, row)
return false
}
},
/**
* 该行是否应禁用选择:已售出/售出中 或 无价格
*/
isRowDisabled(row) {
try {
if (!row) return true
if (row.saleState === 1 || row.saleState === 2) return true
return !this.hasAnyPrice(row)
} catch (e) { return true }
if (!row) {
console.log('[isRowDisabled] 行数据为空')
return true
}
if (row.saleState === 1 || row.saleState === 2) {
console.log(`[isRowDisabled] 机器ID ${row.id || 'unknown'} 已售出或售出中, saleState:`, row.saleState)
return true
}
const hasPrice = this.hasAnyPrice(row)
if (!hasPrice) {
console.log(`[isRowDisabled] 机器ID ${row.id || 'unknown'} 无价格:`, {
priceList: row.priceList,
price: row.price,
hasPriceList: Array.isArray(row.priceList) && row.priceList.length > 0
})
}
return !hasPrice
} catch (e) {
console.error('[isRowDisabled] 异常:', e, row)
return true
}
},
/**
* 获取行的最大可租赁天数