周五固定更新
This commit is contained in:
@@ -87,3 +87,13 @@ export function getMachineInfoById(data) {
|
||||
}
|
||||
|
||||
|
||||
// 查获取商城商品支持的支付方式
|
||||
export function getPayTypes(data) {
|
||||
return request({
|
||||
url: `/lease/product/getPayTypes`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -56,43 +56,43 @@ export function closeShop(id) {
|
||||
|
||||
// 根据 店铺id 查询店铺商品配置信息列表
|
||||
export function getShopConfig(id) {
|
||||
return request({
|
||||
url: `/lease/shop/getShopConfig`,
|
||||
method: 'post',
|
||||
data: { id }
|
||||
})
|
||||
}
|
||||
return request({
|
||||
url: `/lease/shop/getShopConfig`,
|
||||
method: 'post',
|
||||
data: { id }
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 新增商铺配置
|
||||
// 新增商铺配置
|
||||
export function addShopConfig(data) {
|
||||
return request({
|
||||
url: `/lease/shop/addShopConfig`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
return request({
|
||||
url: `/lease/shop/addShopConfig`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 根据配置id 修改配置
|
||||
// 根据配置id 修改配置
|
||||
export function updateShopConfig(data) {
|
||||
return request({
|
||||
url: `/lease/shop/updateShopConfig`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
return request({
|
||||
url: `/lease/shop/updateShopConfig`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 根据配置id 删除配置
|
||||
// 根据配置id 删除配置
|
||||
export function deleteShopConfig(data) {
|
||||
return request({
|
||||
url: `/lease/shop/deleteShopConfig`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
return request({
|
||||
url: `/lease/shop/deleteShopConfig`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 钱包配置(用于修改卖家钱包地址)----获取链(一级)和币(二级) 下拉列表(获取本系统支持的链和币种)
|
||||
|
||||
// 钱包配置(用于修改卖家钱包地址)----获取链(一级)和币(二级) 下拉列表(获取本系统支持的链和币种)
|
||||
export function getChainAndCoin(data) {
|
||||
return request({
|
||||
url: `/lease/shop/getChainAndCoin`,
|
||||
|
||||
@@ -106,6 +106,28 @@ export function getRecentlyTransaction(data) {
|
||||
})
|
||||
}
|
||||
|
||||
//绑定钱包前查询商品列表
|
||||
export function getProductListForShopWalletConfig(data) {
|
||||
return request({
|
||||
url: `/lease/product/getProductListForShopWalletConfig`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
//设置之前商品列表的新链的机器价格
|
||||
export function updateProductListForShopWalletConfig(data) {
|
||||
return request({
|
||||
url: `/lease/product/updateProductListForShopWalletConfig`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -204,16 +204,7 @@ export default {
|
||||
});
|
||||
return
|
||||
}
|
||||
try {
|
||||
const curPath = (this.$route && this.$route.path) || ''
|
||||
const from = curPath.indexOf('/account/orders') === 0 ? 'buyer' : (curPath.indexOf('/account/seller-orders') === 0 ? 'seller' : '')
|
||||
try { if (from) sessionStorage.setItem('orderDetailFrom', from) } catch (e) {}
|
||||
if (from) {
|
||||
this.$router.push({ path: `/account/order-detail/${id}`, query: { from } })
|
||||
} else {
|
||||
this.$router.push(`/account/order-detail/${id}`)
|
||||
}
|
||||
} catch (e) {
|
||||
try { this.$router.push(`/account/order-detail/${id}`) } catch (e) {
|
||||
this.$message({
|
||||
message: '无法跳转到详情页',
|
||||
type: 'error',
|
||||
|
||||
@@ -37,21 +37,22 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="价格范围">
|
||||
<!-- <el-form-item label="价格范围">
|
||||
<el-input :value="product && product.priceRange" disabled />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12">
|
||||
</el-form-item> -->
|
||||
<el-form-item label="类型">
|
||||
<el-input :value="product && (product.type === 1 ? '算力套餐' : '挖矿机器')" disabled />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12">
|
||||
<el-form-item label="状态">
|
||||
<el-input :value="product && (product.state === 1 ? '下架' : '上架')" disabled />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
|
||||
</el-col>
|
||||
|
||||
<!-- <el-col :span="24">
|
||||
<el-form-item label="图片">
|
||||
@@ -64,7 +65,7 @@
|
||||
|
||||
<el-col :span="24">
|
||||
<el-form-item label="描述">
|
||||
<el-input type="textarea" :rows="3" :value="product && product.description" disabled />
|
||||
<el-input type="textarea" :rows="3" :value="product && product.description" disabled />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -76,9 +77,9 @@
|
||||
<div slot="header" class="section-title">机器组合</div>
|
||||
<div v-if="machineList && machineList.length">
|
||||
<el-table :data="machineList" border stripe style="width: 100%">
|
||||
<el-table-column prop="user" label="挖矿账户" min-width="80" />
|
||||
<el-table-column prop="id" label="矿机ID" min-width="60" />
|
||||
<el-table-column prop="miner" label="机器编号" min-width="100" />
|
||||
<el-table-column prop="user" label="挖矿账户" />
|
||||
<el-table-column prop="id" label="矿机ID" />
|
||||
<el-table-column prop="miner" label="机器编号" />
|
||||
<el-table-column label="实际算力" width="100">
|
||||
<template slot="header">
|
||||
<el-tooltip content="实际算力为该机器在本矿池过去24H的平均算力" effect="dark" placement="top">
|
||||
@@ -100,11 +101,15 @@
|
||||
:class="{ 'changed-input': isCellChanged(scope.row, 'theoryPower') }"
|
||||
style="max-width: 260px;"
|
||||
>
|
||||
<template slot="append">{{ scope.row.unit || '' }}</template>
|
||||
<template slot="append">
|
||||
<el-select v-model="scope.row.unit" size="mini" :disabled="isRowDisabled(scope.row)" class="append-select append-select--unit" style="width: 90px;">
|
||||
<el-option v-for="u in unitOptions" :key="u" :label="u" :value="u" />
|
||||
</el-select>
|
||||
</template>
|
||||
</el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="功耗(kw/h)" min-width="140">
|
||||
<el-table-column label="功耗(kw/h)" >
|
||||
<template #default="scope">
|
||||
<el-input
|
||||
v-model="scope.row.powerDissipation"
|
||||
@@ -116,16 +121,16 @@
|
||||
:class="{ 'changed-input': isCellChanged(scope.row, 'powerDissipation') }"
|
||||
style="max-width: 260px;"
|
||||
>
|
||||
<template slot="append">kw/h</template>
|
||||
<!-- <template slot="append">kw/h</template> -->
|
||||
</el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="型号" min-width="140">
|
||||
<el-table-column label="型号" >
|
||||
<template #default="scope">
|
||||
<el-input
|
||||
v-model="scope.row.type"
|
||||
size="small"
|
||||
placeholder="矿机型号"
|
||||
|
||||
:maxlength="20"
|
||||
:disabled="isRowDisabled(scope.row)"
|
||||
@input="handleTypeCell(scope.$index)"
|
||||
@@ -134,7 +139,7 @@
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="售价(USDT)" min-width="140">
|
||||
<el-table-column label="售价" width="188">
|
||||
<template slot="header">
|
||||
<el-tooltip effect="dark" placement="top">
|
||||
<div slot="content">
|
||||
@@ -145,11 +150,11 @@
|
||||
</div>
|
||||
<i class="el-icon-question label-help" aria-label="帮助" tabindex="0"></i>
|
||||
</el-tooltip>
|
||||
<span>售价(USDT)</span>
|
||||
<span>售价(按结算币种)</span>
|
||||
</template>
|
||||
<template slot-scope="scope">
|
||||
<el-input
|
||||
v-model="scope.row.price"
|
||||
v-model="scope.row._priceEditing"
|
||||
size="small"
|
||||
inputmode="decimal"
|
||||
:disabled="isRowDisabled(scope.row)"
|
||||
@@ -158,11 +163,20 @@
|
||||
:class="{ 'changed-input': isCellChanged(scope.row, 'price') }"
|
||||
style="max-width: 260px;"
|
||||
>
|
||||
<template slot="append">USDT</template>
|
||||
<template slot="append">
|
||||
<el-select v-model="scope.row._selectedPayIndex" size="mini" @change="handlePayTypeChange(scope.$index)" class="append-select append-select--coin" style="width:120px;">
|
||||
<el-option
|
||||
v-for="(pt, i) in (scope.row.priceList || [])"
|
||||
:key="pt.payTypeId || i"
|
||||
:label="[String(pt.chain||'').toUpperCase(), String(pt.coin||'').toUpperCase()].filter(Boolean).join('-')"
|
||||
:value="i"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="最大租赁天数(天)" min-width="140">
|
||||
<el-table-column label="最大租赁天数(天)" width="100">
|
||||
<template #default="scope">
|
||||
<el-input
|
||||
v-model="scope.row.maxLeaseDays"
|
||||
@@ -248,6 +262,8 @@ export default {
|
||||
// 可编辑字段快照(用于变更高亮)
|
||||
fieldSnapshot: {},
|
||||
updateLoading:false,
|
||||
// 算力单位选项(与新增出售机器页面保持一致)
|
||||
unitOptions: ['KH/S','MH/S','GH/S','TH/S','PH/S'],
|
||||
}
|
||||
},
|
||||
|
||||
@@ -269,6 +285,15 @@ export default {
|
||||
|
||||
},
|
||||
methods: {
|
||||
/** 结算币种切换时,更新当前编辑价格 */
|
||||
handlePayTypeChange(index) {
|
||||
const row = this.machineList && this.machineList[index]
|
||||
if (!row) return
|
||||
const sel = Number(row._selectedPayIndex || 0)
|
||||
const list = Array.isArray(row.priceList) ? row.priceList : []
|
||||
const target = list[sel] || {}
|
||||
this.$set(this.machineList, index, { ...row, _priceEditing: String(target.price ?? '') })
|
||||
},
|
||||
/**
|
||||
* 判断行是否不可编辑(已售出则禁用)
|
||||
* @param {Object} row - 当前行数据
|
||||
@@ -304,7 +329,13 @@ export default {
|
||||
|
||||
|
||||
if (res && res.code === 200) {
|
||||
this.machineList =res.rows
|
||||
const rows = Array.isArray(res.rows) ? res.rows : []
|
||||
this.machineList = rows.map(r => {
|
||||
const list = Array.isArray(r.priceList) ? r.priceList : []
|
||||
const sel = 0
|
||||
const first = list[sel] || {}
|
||||
return { ...r, _selectedPayIndex: sel, _priceEditing: String(first.price ?? '') }
|
||||
})
|
||||
this.refreshStateSnapshot()
|
||||
this.refreshFieldSnapshot()
|
||||
}
|
||||
@@ -338,11 +369,15 @@ export default {
|
||||
for (let i = 0; i < list.length; i += 1) {
|
||||
const row = list[i]
|
||||
if (!row || typeof row.id === 'undefined') continue
|
||||
const priceMap = {}
|
||||
if (Array.isArray(row.priceList)) {
|
||||
row.priceList.forEach(p => { if (p) priceMap[String(p.payTypeId ?? '')] = String(p.price ?? '') })
|
||||
}
|
||||
snapshot[row.id] = {
|
||||
theoryPower: String(row.theoryPower ?? ''),
|
||||
powerDissipation: String(row.powerDissipation ?? ''),
|
||||
type: String(row.type ?? ''),
|
||||
price: String(row.price ?? ''),
|
||||
priceMap,
|
||||
maxLeaseDays: String(row.maxLeaseDays ?? ''),
|
||||
}
|
||||
}
|
||||
@@ -358,6 +393,14 @@ export default {
|
||||
isCellChanged(row, key) {
|
||||
if (!row || typeof row.id === 'undefined') return false
|
||||
const snap = this.fieldSnapshot[row.id] || {}
|
||||
if (key === 'price') {
|
||||
const sel = Number(row._selectedPayIndex || 0)
|
||||
const pt = Array.isArray(row.priceList) && row.priceList[sel] ? row.priceList[sel] : null
|
||||
const pid = String(pt && pt.payTypeId ? pt.payTypeId : sel)
|
||||
const cur = String(pt && pt.price != null ? pt.price : '')
|
||||
const ori = String((snap.priceMap && snap.priceMap[pid]) || '')
|
||||
return cur !== ori
|
||||
}
|
||||
const current = String(row[key] ?? '')
|
||||
const original = String(snap[key] ?? '')
|
||||
return current !== original
|
||||
@@ -438,7 +481,7 @@ export default {
|
||||
// - 功耗:6 位整数 + 4 位小数
|
||||
// - 价格:12 位整数 + 2 位小数
|
||||
// - 其他:保持原逻辑(6 位小数)
|
||||
let v = String(this.machineList[index][key] ?? '')
|
||||
let v = String(key === 'price' ? (this.machineList[index]._priceEditing ?? '') : (this.machineList[index][key] ?? ''))
|
||||
v = v.replace(/[^0-9.]/g, '')
|
||||
const firstDot = v.indexOf('.')
|
||||
if (firstDot !== -1) {
|
||||
@@ -460,22 +503,35 @@ export default {
|
||||
if (intPart.length > 12) { intPart = intPart.slice(0, 12) }
|
||||
if (decPart) { decPart = decPart.slice(0, 2) }
|
||||
v = decPart.length ? `${intPart}.${decPart}` : (endsWithDot ? `${intPart}.` : intPart)
|
||||
// 同步到当前选中结算币种的价格
|
||||
this.$set(this.machineList[index], '_priceEditing', v)
|
||||
const row = this.machineList[index]
|
||||
const sel = Number(row._selectedPayIndex || 0)
|
||||
if (Array.isArray(row.priceList) && row.priceList[sel]) {
|
||||
this.$set(row.priceList[sel], 'price', v)
|
||||
}
|
||||
} else {
|
||||
if (firstDot !== -1) {
|
||||
const [i, d] = v.split('.')
|
||||
v = i + '.' + (d ? d.slice(0, 6) : '')
|
||||
}
|
||||
}
|
||||
const row = { ...this.machineList[index], [key]: v }
|
||||
this.$set(this.machineList, index, row)
|
||||
if (key !== 'price') {
|
||||
const row = { ...this.machineList[index], [key]: v }
|
||||
this.$set(this.machineList, index, row)
|
||||
}
|
||||
},
|
||||
handlePriceBlur(index) {
|
||||
const raw = String(this.machineList[index].price ?? '')
|
||||
const raw = String(this.machineList[index]._priceEditing ?? '')
|
||||
const pattern = /^\d{1,12}(\.\d{1,2})?$/
|
||||
if (!raw || Number(raw) <= 0 || !pattern.test(raw)) {
|
||||
this.$message.warning('单价必须大于0,整数最多12位,小数最多2位')
|
||||
const row = { ...this.machineList[index], price: '' }
|
||||
this.$set(this.machineList, index, row)
|
||||
this.$set(this.machineList[index], '_priceEditing', '')
|
||||
const row = this.machineList[index]
|
||||
const sel = Number(row._selectedPayIndex || 0)
|
||||
if (Array.isArray(row.priceList) && row.priceList[sel]) {
|
||||
this.$set(row.priceList[sel], 'price', '')
|
||||
}
|
||||
}
|
||||
},
|
||||
handleMaxLeaseDaysInput(index) {
|
||||
@@ -567,7 +623,7 @@ export default {
|
||||
const row = this.machineList[i]
|
||||
const rowLabel = row && (row.miner || row.id || i + 1)
|
||||
const theoryRaw = String(row.theoryPower ?? '')
|
||||
const priceRaw = String(row.price ?? '')
|
||||
const priceRaw = String(row._priceEditing ?? '')
|
||||
const typeRaw = String(row.type ?? '')
|
||||
const dissRaw = String(row.powerDissipation ?? '')
|
||||
const daysRaw = String(row.maxLeaseDays ?? '')
|
||||
@@ -598,7 +654,7 @@ export default {
|
||||
const payload = this.machineList.map(m => ({
|
||||
id: m.id,
|
||||
powerDissipation: Number(m.powerDissipation ?? 0),
|
||||
price: Number(m.price ?? 0),
|
||||
priceList: Array.isArray(m.priceList) ? m.priceList.map(p => ({ ...p, price: Number(p && p.price != null && p.price !== '' ? p.price : 0) })) : [],
|
||||
state: Number(m.state ?? 0),
|
||||
theoryPower: Number(m.theoryPower ?? 0),
|
||||
type: m.type || '',
|
||||
@@ -641,17 +697,51 @@ export default {
|
||||
.empty-text { color: #909399; text-align: center; padding: 12px 0; }
|
||||
|
||||
.label-help { margin-left: 4px; color: #909399; cursor: help; }
|
||||
|
||||
/* ::v-deep .el-form-item__content{
|
||||
margin-left: 52px !important;
|
||||
} */
|
||||
|
||||
</style>
|
||||
<style>
|
||||
.el-input-group__append, .el-input-group__prepend{
|
||||
padding: 0 5px !important;
|
||||
}
|
||||
.account-product-detail .el-table .el-input,
|
||||
.account-product-detail .el-table .el-textarea{
|
||||
width: 94% !important; /* 仅限制表格内输入宽度,避免影响上面的基础信息区域 */
|
||||
}
|
||||
/* 基础信息表单保持满宽,确保“描述”与上方输入左侧对齐 */
|
||||
.account-product-detail .detail-form .el-input,
|
||||
.account-product-detail .detail-form .el-textarea{
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
/* 让追加区裁剪内部元素,避免 el-select 下拉箭头溢出到单元格外 */
|
||||
.el-input-group__append,
|
||||
.el-input-group__prepend{
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 追加在输入框右侧的下拉(单位/结算币种)细节优化 */
|
||||
.append-select .el-input__inner{
|
||||
/* 预留更多箭头空间,避免被右侧裁剪 */
|
||||
padding-right: 28px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
.append-select .el-select__caret{
|
||||
right: 10px; /* 箭头往内侧移动,防止被裁切 */
|
||||
transform: scale(.85); /* 缩小箭头,保证完全显示 */
|
||||
}
|
||||
.append-select .el-input__icon{
|
||||
line-height: 30px; /* 垂直居中,避免上下被裁切 */
|
||||
}
|
||||
|
||||
/* 变化高亮:为输入框外层添加红色边框,视觉醒目但不改变布局 */
|
||||
.changed-input .el-input__inner,
|
||||
.changed-input input.el-input__inner {
|
||||
.changed-input input.el-input__inner,
|
||||
/* 带有 append 时,同步高亮右侧追加区的边框,保证整体连贯 */
|
||||
.changed-input .el-input-group__append {
|
||||
border-color: #f56c6c !important;
|
||||
}
|
||||
|
||||
|
||||
@@ -56,8 +56,7 @@
|
||||
</el-form-item>
|
||||
<el-form-item label="功耗" prop="powerDissipation">
|
||||
<el-input
|
||||
v-model="form.powerDissipation"
|
||||
placeholder="示例:0.01"
|
||||
v-model="form.powerDissipation"
|
||||
inputmode="decimal"
|
||||
@input="handleNumeric('powerDissipation')"
|
||||
style="width: 50%;"
|
||||
@@ -79,7 +78,22 @@
|
||||
<i class="el-icon-question label-help" aria-label="帮助" tabindex="0"></i>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
<!-- 若商品定义了多个结算币种,则按链-币种动态生成多个售价输入;否则回退为旧的 USDT 单价 -->
|
||||
<div v-if="payTypeDefs && payTypeDefs.length" class="cost-multi">
|
||||
<div v-for="pt in payTypeDefs" :key="pt.key" class="cost-item">
|
||||
<el-input
|
||||
v-model="form.costMap[pt.key]"
|
||||
placeholder="请输入价格"
|
||||
inputmode="decimal"
|
||||
@input="val => handleCostMapInput(pt.key, val)"
|
||||
style="width: 50%;"
|
||||
>
|
||||
<template slot="append">{{ pt.label }}</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
<el-input
|
||||
v-else
|
||||
v-model="form.cost"
|
||||
placeholder="请输入成本(USDT)"
|
||||
inputmode="decimal"
|
||||
@@ -158,7 +172,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="售价(USDT)" min-width="160">
|
||||
<el-table-column label="售价(按结算币种)" min-width="220">
|
||||
<template slot="header">
|
||||
<el-tooltip effect="dark" placement="top">
|
||||
<div slot="content">
|
||||
@@ -169,20 +183,35 @@
|
||||
</div>
|
||||
<i class="el-icon-question label-help" aria-label="帮助" tabindex="0"></i>
|
||||
</el-tooltip>
|
||||
<span>售价(USDT)</span>
|
||||
<span>售价(按结算币种)</span>
|
||||
</template>
|
||||
<template slot-scope="scope">
|
||||
<el-input
|
||||
v-model="scope.row.price"
|
||||
placeholder="价格"
|
||||
inputmode="decimal"
|
||||
@input="handleRowPriceInput(scope.$index)"
|
||||
@blur="handleRowPriceBlur(scope.$index)"
|
||||
style="width: 100%;"
|
||||
>
|
||||
<template slot="append">USDT</template>
|
||||
</el-input>
|
||||
|
||||
<div class="price-multi">
|
||||
<div v-if="payTypeDefs && payTypeDefs.length" class="price-items">
|
||||
<div v-for="pt in payTypeDefs" :key="pt.key" class="price-item">
|
||||
<el-input
|
||||
v-model="scope.row.priceMap[pt.key]"
|
||||
placeholder="价格"
|
||||
inputmode="decimal"
|
||||
@input="() => handleRowPriceMapInput(scope.$index, pt.key)"
|
||||
@blur="() => handleRowPriceMapBlur(scope.$index, pt.key)"
|
||||
>
|
||||
<template slot="append">{{ pt.label }}</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
<el-input
|
||||
v-else
|
||||
v-model="scope.row.price"
|
||||
placeholder="价格"
|
||||
inputmode="decimal"
|
||||
@input="handleRowPriceInput(scope.$index)"
|
||||
@blur="handleRowPriceBlur(scope.$index)"
|
||||
style="width: 100%;"
|
||||
>
|
||||
<template slot="append">USDT</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
</el-table-column>
|
||||
@@ -265,6 +294,7 @@ export default {
|
||||
type: '',
|
||||
unit: 'TH/S',
|
||||
cost: '',
|
||||
costMap: {}, // { 'CHAIN-COIN': '123.45' }
|
||||
maxLeaseDays: ''
|
||||
},
|
||||
confirmVisible: false,
|
||||
@@ -301,15 +331,18 @@ export default {
|
||||
],
|
||||
unit: [ { required: true, message: '请选择算力单位', trigger: 'change' } ],
|
||||
cost: [
|
||||
{ required: true, message: '请填写机器成本(USDT)', trigger: 'blur' },
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
validator(rule, value, callback) {
|
||||
// 若为多结算币种模式,跳过此校验(统一售价由每种币种的输入框承担)
|
||||
if (Array.isArray(this.payTypeDefs) && this.payTypeDefs.length > 0) {
|
||||
callback()
|
||||
return
|
||||
}
|
||||
const str = String(value || '')
|
||||
if (!str) {
|
||||
callback(new Error('请填写机器成本(USDT)'))
|
||||
return
|
||||
}
|
||||
// 整数最多12位,小数最多2位
|
||||
const pattern = /^\d{1,12}(\.\d{1,2})?$/
|
||||
if (!pattern.test(str)) {
|
||||
callback(new Error('成本整数最多12位,小数最多2位'))
|
||||
@@ -421,6 +454,7 @@ export default {
|
||||
selectedMachineRows: [],
|
||||
saving: false,
|
||||
lastCostBaseline: 0,
|
||||
lastCostMapBaseline: {}, // { key: number }
|
||||
lastTypeBaseline: '',
|
||||
lastMaxLeaseDaysBaseline: 0,
|
||||
lastPowerDissipationBaseline: 0,
|
||||
@@ -455,10 +489,58 @@ export default {
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.initPayTypesFromRoute()
|
||||
this.fetchMiners()
|
||||
this.lastTypeBaseline = this.form.type
|
||||
// 绑定基于组件实例的校验器,避免 this 丢失
|
||||
if (this.rules && this.rules.cost) {
|
||||
this.$set(this.rules, 'cost', [{ validator: this.validateCost, trigger: 'blur' }])
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/** 统一售价校验:多结算币种时跳过,单价时按 USDT 校验 */
|
||||
validateCost(rule, value, callback) {
|
||||
if (Array.isArray(this.payTypeDefs) && this.payTypeDefs.length > 0) {
|
||||
callback()
|
||||
return
|
||||
}
|
||||
const str = String(value || '')
|
||||
if (!str) { callback(new Error('请填写机器成本(USDT)')); return }
|
||||
const pattern = /^\d{1,12}(\.\d{1,2})?$/
|
||||
if (!pattern.test(str)) { callback(new Error('成本整数最多12位,小数最多2位')); return }
|
||||
if (Number(str) <= 0) { callback(new Error('成本必须大于 0')); return }
|
||||
callback()
|
||||
},
|
||||
/** 解析路由参数中的支付方式,生成标准定义 */
|
||||
initPayTypesFromRoute() {
|
||||
this.payTypeDefs = []
|
||||
try {
|
||||
const raw = this.$route.query.payTypes
|
||||
if (!raw) return
|
||||
const arr = JSON.parse(decodeURIComponent(raw))
|
||||
if (!Array.isArray(arr)) return
|
||||
const defs = []
|
||||
arr.forEach(it => {
|
||||
const chain = String(it && it.chain ? it.chain : '').toUpperCase()
|
||||
const coin = String(it && it.coin ? it.coin : '').toUpperCase()
|
||||
if (!chain && !coin) return
|
||||
const key = [chain, coin].filter(Boolean).join('-')
|
||||
const label = key
|
||||
defs.push({ chain, coin, key, label })
|
||||
})
|
||||
// 去重
|
||||
const map = new Map()
|
||||
defs.forEach(d => { if (!map.has(d.key)) map.set(d.key, d) })
|
||||
this.payTypeDefs = Array.from(map.values())
|
||||
// 初始化统一售价映射
|
||||
const initCostMap = {}
|
||||
this.payTypeDefs.forEach(d => { initCostMap[d.key] = '' })
|
||||
this.form.costMap = initCostMap
|
||||
this.lastCostMapBaseline = { ...initCostMap }
|
||||
} catch (e) {
|
||||
this.payTypeDefs = []
|
||||
}
|
||||
},
|
||||
handleBack() {
|
||||
this.$router.back()
|
||||
},
|
||||
@@ -516,6 +598,36 @@ export default {
|
||||
this.syncCostToRows()
|
||||
}
|
||||
},
|
||||
/** 顶部多结算币种统一售价输入 */
|
||||
handleCostMapInput(key, val) {
|
||||
// 价格输入:整数最多12位,小数最多2位;允许尾随小数点
|
||||
let v = String(val ?? this.form.costMap[key] ?? '')
|
||||
v = v.replace(/[^0-9.]/g, '')
|
||||
const firstDot = v.indexOf('.')
|
||||
if (firstDot !== -1) {
|
||||
v = v.slice(0, firstDot + 1) + v.slice(firstDot + 1).replace(/\./g, '')
|
||||
}
|
||||
const endsWithDot = v.endsWith('.')
|
||||
const parts = v.split('.')
|
||||
let intPart = parts[0] || ''
|
||||
let decPart = parts[1] || ''
|
||||
if (intPart.length > 12) intPart = intPart.slice(0, 12)
|
||||
if (decPart) decPart = decPart.slice(0, 2)
|
||||
v = decPart.length ? `${intPart}.${decPart}` : (endsWithDot ? `${intPart}.` : intPart)
|
||||
this.$set(this.form.costMap, key, v)
|
||||
|
||||
// 同步到行:仅当行对应价格未设置或等于旧基线
|
||||
const oldBaseline = Number(this.lastCostMapBaseline[key] ?? NaN)
|
||||
this.selectedMachineRows = this.selectedMachineRows.map(row => {
|
||||
const cur = Number((row.priceMap && row.priceMap[key]) ?? NaN)
|
||||
const shouldFollow = !Number.isFinite(cur) || cur === oldBaseline
|
||||
const nextPriceMap = { ...(row.priceMap || {}) }
|
||||
if (shouldFollow) nextPriceMap[key] = v
|
||||
return { ...row, priceMap: nextPriceMap }
|
||||
})
|
||||
const num = Number(v)
|
||||
if (Number.isFinite(num)) this.$set(this.lastCostMapBaseline, key, num)
|
||||
},
|
||||
/**
|
||||
* 顶部矿机型号输入:限制20字符
|
||||
*/
|
||||
@@ -563,18 +675,24 @@ export default {
|
||||
if (m) {
|
||||
// 若已存在,沿用已编辑的价格、型号和状态
|
||||
const existed = this.selectedMachineRows.find(r => r.miner === minerId)
|
||||
const existedPriceMap = existed && existed.priceMap ? existed.priceMap : null
|
||||
const defaultPriceMap = {}
|
||||
if (this.payTypeDefs && this.payTypeDefs.length) {
|
||||
this.payTypeDefs.forEach(d => { defaultPriceMap[d.key] = this.form.costMap[d.key] })
|
||||
}
|
||||
nextRows.push({
|
||||
user: m.user,
|
||||
coin: m.coin,
|
||||
miner: m.miner,
|
||||
realPower: m.realPower,
|
||||
price: existed ? existed.price : this.form.cost,
|
||||
price: existed ? existed.price : this.form.cost, // 兼容单价模式
|
||||
powerDissipation: existed && existed.powerDissipation !== undefined ? existed.powerDissipation : this.form.powerDissipation,
|
||||
theoryPower: existed && existed.theoryPower !== undefined ? existed.theoryPower : this.form.theoryPower,
|
||||
unit: existed && existed.unit ? existed.unit : this.form.unit,
|
||||
type: existed ? existed.type : this.form.type,
|
||||
state: existed ? existed.state : 0, // 默认上架
|
||||
maxLeaseDays: existed && existed.maxLeaseDays !== undefined ? existed.maxLeaseDays : this.form.maxLeaseDays
|
||||
maxLeaseDays: existed && existed.maxLeaseDays !== undefined ? existed.maxLeaseDays : this.form.maxLeaseDays,
|
||||
priceMap: existedPriceMap || defaultPriceMap
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -742,6 +860,38 @@ export default {
|
||||
v = decPart.length ? `${intPart}.${decPart}` : (endsWithDot ? `${intPart}.` : intPart)
|
||||
this.$set(this.selectedMachineRows[index], 'price', v)
|
||||
},
|
||||
/** 行内多结算币种价格输入 */
|
||||
handleRowPriceMapInput(index, key) {
|
||||
// 价格输入:整数最多12位,小数最多2位;允许尾随小数点
|
||||
const row = this.selectedMachineRows[index]
|
||||
const map = { ...(row.priceMap || {}) }
|
||||
let v = String(map[key] ?? '')
|
||||
v = v.replace(/[^0-9.]/g, '')
|
||||
const firstDot = v.indexOf('.')
|
||||
if (firstDot !== -1) {
|
||||
v = v.slice(0, firstDot + 1) + v.slice(firstDot + 1).replace(/\./g, '')
|
||||
}
|
||||
const endsWithDot = v.endsWith('.')
|
||||
const parts = v.split('.')
|
||||
let intPart = parts[0] || ''
|
||||
let decPart = parts[1] || ''
|
||||
if (intPart.length > 12) intPart = intPart.slice(0, 12)
|
||||
if (decPart) decPart = decPart.slice(0, 2)
|
||||
v = decPart.length ? `${intPart}.${decPart}` : (endsWithDot ? `${intPart}.` : intPart)
|
||||
map[key] = v
|
||||
this.$set(this.selectedMachineRows[index], 'priceMap', map)
|
||||
},
|
||||
handleRowPriceMapBlur(index, key) {
|
||||
const row = this.selectedMachineRows[index]
|
||||
const raw = String((row.priceMap && row.priceMap[key]) ?? '')
|
||||
const pattern = /^\d{1,12}(\.\d{1,2})?$/
|
||||
if (!raw || Number(raw) <= 0 || !pattern.test(raw)) {
|
||||
this.$message.warning('价格必须大于0,整数最多12位,小数最多2位')
|
||||
const map = { ...(row.priceMap || {}) }
|
||||
map[key] = ''
|
||||
this.$set(this.selectedMachineRows[index], 'priceMap', map)
|
||||
}
|
||||
},
|
||||
handleRowPriceBlur(index) {
|
||||
const raw = String(this.selectedMachineRows[index].price ?? '')
|
||||
const pattern = /^\d{1,12}(\.\d{1,2})?$/
|
||||
@@ -865,14 +1015,27 @@ export default {
|
||||
this.$message.warning('存在行的矿机型号全是空格,请修正后再试')
|
||||
return
|
||||
}
|
||||
// 校验:已选择机器的价格必须大于0
|
||||
// 校验:价格与最大租赁天数
|
||||
for (let i = 0; i < this.selectedMachineRows.length; i += 1) {
|
||||
const row = this.selectedMachineRows[i]
|
||||
const priceNum = Number(row && row.price)
|
||||
if (!Number.isFinite(priceNum) || priceNum <= 0) {
|
||||
const label = (row && (row.miner || row.user)) || i + 1
|
||||
this.$message.warning(`第${i + 1}行(机器:${label}) 价格必须大于0`)
|
||||
return
|
||||
if (this.payTypeDefs && this.payTypeDefs.length) {
|
||||
for (let j = 0; j < this.payTypeDefs.length; j += 1) {
|
||||
const def = this.payTypeDefs[j]
|
||||
const raw = String(row && row.priceMap ? row.priceMap[def.key] : '')
|
||||
const num = Number(raw)
|
||||
if (!/^\d{1,12}(\.\d{1,2})?$/.test(raw) || !Number.isFinite(num) || num <= 0) {
|
||||
const label = (row && (row.miner || row.user)) || i + 1
|
||||
this.$message.warning(`第${i + 1}行(机器:${label}) 价格(${def.label})必须大于0,整数最多12位,小数最多2位`)
|
||||
return
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const priceNum = Number(row && row.price)
|
||||
if (!Number.isFinite(priceNum) || priceNum <= 0) {
|
||||
const label = (row && (row.miner || row.user)) || i + 1
|
||||
this.$message.warning(`第${i + 1}行(机器:${label}) 价格必须大于0`)
|
||||
return
|
||||
}
|
||||
}
|
||||
// 校验:逐行最大租赁天数 1-365
|
||||
const rawDays = String((row && row.maxLeaseDays) ?? '')
|
||||
@@ -891,17 +1054,23 @@ export default {
|
||||
const [user, coin] = this.selectedMiner.split('|')
|
||||
this.saving = true
|
||||
try {
|
||||
// 若是多结算币种,组装 priceList;否则沿用单价字段
|
||||
const payload = {
|
||||
productId: this.form.productId,
|
||||
powerDissipation: this.form.powerDissipation,
|
||||
theoryPower: this.form.theoryPower,
|
||||
type: this.form.type,
|
||||
unit: this.form.unit,
|
||||
cost: this.form.cost,
|
||||
cost: this.payTypeDefs && this.payTypeDefs.length ? Number(this.form.costMap && this.form.costMap[this.payTypeDefs[0].key]) || 0 : this.form.cost,
|
||||
maxLeaseDays: this.form.maxLeaseDays,
|
||||
productMachineURDVos: this.selectedMachineRows.map(r => ({
|
||||
miner: r.miner,
|
||||
price: Number(r.price) || 0,
|
||||
price: this.payTypeDefs && this.payTypeDefs.length ? undefined : (Number(r.price) || 0),
|
||||
priceList: this.payTypeDefs && this.payTypeDefs.length ? this.payTypeDefs.map(d => ({
|
||||
chain: d.chain,
|
||||
coin: d.coin,
|
||||
price: Number(r.priceMap && r.priceMap[d.key]) || 0
|
||||
})) : undefined,
|
||||
state: r.state || 0,
|
||||
type: r.type || this.form.type,
|
||||
user: r.user,
|
||||
@@ -937,6 +1106,20 @@ export default {
|
||||
,
|
||||
watch: {
|
||||
'form.cost': function() { this.syncCostToRows() },
|
||||
// 当统一售价映射变化时手动同步(深度监听)
|
||||
form: {
|
||||
deep: true,
|
||||
handler(val, oldVal) {
|
||||
// 仅在 costMap 深度变化且有多支付类型时,做轻量同步(不覆盖用户已手改的值)
|
||||
if (!this.payTypeDefs || !this.payTypeDefs.length) return
|
||||
if (!val || !val.costMap) return
|
||||
Object.keys(val.costMap).forEach(k => {
|
||||
if (oldVal && oldVal.costMap && val.costMap[k] !== oldVal.costMap[k]) {
|
||||
this.handleCostMapInput(k, val.costMap[k])
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
'form.type': function() { this.updateMachineType() },
|
||||
'form.maxLeaseDays': function() { this.syncMaxLeaseDaysToRows() },
|
||||
'form.powerDissipation': function() { this.syncPowerDissipationToRows() },
|
||||
@@ -980,5 +1163,37 @@ export default {
|
||||
text-align: left;
|
||||
padding-left: 18px !important;
|
||||
}
|
||||
|
||||
/* 多结算币种价格输入的布局优化 */
|
||||
.cost-multi { display: grid; gap: 8px; }
|
||||
.cost-item { display: flex; align-items: center; }
|
||||
.price-multi { display: grid; gap: 8px; }
|
||||
.price-items { display: grid; gap: 8px; }
|
||||
/* 让 链-币种 附加区同宽、居中显示,整体对齐 */
|
||||
.price-item :deep(.el-input-group__append),
|
||||
.cost-item :deep(.el-input-group__append){
|
||||
width: 110px;
|
||||
min-width: 110px;
|
||||
text-align: center;
|
||||
padding: 0 8px;
|
||||
background: #f8fafc;
|
||||
color: #606266;
|
||||
}
|
||||
/* 缩小输入高度并保持垂直居中 */
|
||||
.price-item :deep(.el-input__inner),
|
||||
.cost-item :deep(.el-input__inner){
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
/* 让组内附加区高度与输入一致 */
|
||||
.price-item :deep(.el-input-group__append),
|
||||
.cost-item :deep(.el-input-group__append){
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
/* 略微收紧间距,让整体更紧凑 */
|
||||
.price-multi { gap: 6px; }
|
||||
.price-items { gap: 6px; }
|
||||
.cost-multi { gap: 6px; }
|
||||
</style>
|
||||
|
||||
|
||||
@@ -30,18 +30,32 @@
|
||||
style="width: 100%"
|
||||
>
|
||||
<!-- <el-table-column prop="id" label="ID" width="80" /> -->
|
||||
<el-table-column prop="name" label="名称" min-width="100" />
|
||||
<el-table-column prop="coin" label="币种" width="100" />
|
||||
<el-table-column prop="priceRange" label="价格范围" width="150" />
|
||||
<el-table-column prop="name" label="名称" />
|
||||
<el-table-column prop="coin" label="币种" />
|
||||
<el-table-column label="支持结算方式" >
|
||||
<template #default="scope">
|
||||
<div class="paytypes">
|
||||
<el-tooltip
|
||||
v-for="(pt, idx) in (scope.row.payTypes || [])"
|
||||
:key="idx"
|
||||
:content="formatPayType(pt)"
|
||||
placement="top"
|
||||
:open-delay="80"
|
||||
>
|
||||
<img :src="pt.image" :alt="formatPayType(pt)" class="paytype-icon" />
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column label="算力" min-width="140">
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.power }} {{ scope.row.unit }}</span>
|
||||
</template>
|
||||
</el-table-column> -->
|
||||
<el-table-column prop="algorithm" label="算法" min-width="120" />
|
||||
<el-table-column prop="algorithm" label="算法" />
|
||||
<!-- <el-table-column prop="electricityBill" label="电费" width="100" /> -->
|
||||
<!-- <el-table-column prop="incomeRate" label="收益率" width="100" /> -->
|
||||
<el-table-column prop="type" label="商品类型" width="130">
|
||||
<el-table-column prop="type" label="商品类型" >
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.type === 1 ? 'success' : 'warning'">
|
||||
{{ scope.row.type === 1 ? '算力套餐' : '挖矿机器' }}
|
||||
@@ -49,9 +63,9 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="saleNumber" label="已售数量" min-width="60" />
|
||||
<el-table-column prop="totalMachineNumber" label="该商品总机器数量" min-width="60" />
|
||||
<el-table-column prop="state" label="状态" width="100">
|
||||
<el-table-column prop="saleNumber" label="已售数量" />
|
||||
<el-table-column prop="totalMachineNumber" label="该商品总机器数量" />
|
||||
<el-table-column prop="state" label="状态" >
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.state === 1 ? 'info' : 'success'">
|
||||
{{ scope.row.state === 1 ? '下架' : '上架' }}
|
||||
@@ -149,6 +163,17 @@ export default {
|
||||
this.fetchTableData()
|
||||
},
|
||||
methods: {
|
||||
/** 格式化支持结算币种的展示,如 TRON-USDT */
|
||||
formatPayType(item) {
|
||||
try {
|
||||
const chain = (item && item.chain ? String(item.chain) : '').toUpperCase()
|
||||
const coin = (item && item.coin ? String(item.coin) : '').toUpperCase()
|
||||
if (chain && coin) return `${chain}-${coin}`
|
||||
return chain || coin || ''
|
||||
} catch (e) {
|
||||
return ''
|
||||
}
|
||||
},
|
||||
/** 初始化筛选选项 */
|
||||
initOptions() {
|
||||
try {
|
||||
@@ -372,7 +397,22 @@ export default {
|
||||
this.$message.warning('缺少商品ID')
|
||||
return
|
||||
}
|
||||
this.$router.push({ path: '/account/product-machine-add', query: { productId: row.id, coin: row.coin, name: row.name } })
|
||||
let payTypesParam = ''
|
||||
try {
|
||||
const pts = Array.isArray(row.payTypes) ? row.payTypes : []
|
||||
payTypesParam = encodeURIComponent(JSON.stringify(pts))
|
||||
} catch (e) {
|
||||
payTypesParam = ''
|
||||
}
|
||||
this.$router.push({
|
||||
path: '/account/product-machine-add',
|
||||
query: {
|
||||
productId: row.id,
|
||||
coin: row.coin,
|
||||
name: row.name,
|
||||
payTypes: payTypesParam
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -416,7 +456,12 @@ export default {
|
||||
}
|
||||
|
||||
::v-deep .el-form-item__content{
|
||||
text-align: left;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/* 支持结算币种的小图标样式 */
|
||||
.paytypes { display: inline-flex; align-items: center; gap: 8px; }
|
||||
.paytype-icon { width: 22px; height: 22px; border-radius: 4px; display: inline-block; }
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
@@ -58,13 +58,61 @@
|
||||
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<!-- 绑定前预检测弹窗:若存在关联商品,先提示用户再继续绑定 -->
|
||||
<el-dialog :visible.sync="preCheck.visible" width="80vw" :close-on-click-modal="false" title="检测到关联商品" @close="handlePreCheckClose">
|
||||
<div style="margin-bottom:10px;">
|
||||
<el-alert
|
||||
type="warning"
|
||||
:closable="false"
|
||||
show-icon
|
||||
description="检测到以下商品与本次绑定的链/币相关。继续绑定后,可能需要为这些商品配置该新链下的价格。是否继续?"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<p style="color: red; font-size: 12px; margin-top: 6px;text-align: right;">* 请填写每个商品对应币种的价格,商品包含机器统一设置价格,如需单台修改请在商品列表-详情页操作</p>
|
||||
<el-table :data="preCheck.rows" height="360" border :header-cell-style="{ textAlign: 'left' }" :cell-style="{ textAlign: 'left' }">
|
||||
<el-table-column label="商品名称" min-width="160">
|
||||
<template #default="scope">{{ scope.row.name || scope.row.productName || scope.row.title || scope.row.product || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="链" min-width="120">
|
||||
<template #default> {{ (form.chain || '').toUpperCase() }} </template>
|
||||
</el-table-column>
|
||||
<el-table-column label="币种" min-width="120">
|
||||
<template #default> {{ form.payCoin.split(',').map(s=>s.trim().toUpperCase()).join(',') }} </template>
|
||||
</el-table-column>
|
||||
<el-table-column label="总矿机数" min-width="100">
|
||||
<template #default="scope">{{ scope.row.totalMachineNumber != null ? scope.row.totalMachineNumber : (scope.row.total || scope.row.totalMachines || '-') }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="商品状态" min-width="100">
|
||||
<template #default="scope">{{ Number(scope.row.state) === 1 ? '下架' : '上架' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-for="sym in coinsForBind" :key="'price-'+sym" :label="sym + ' 价格'" min-width="160">
|
||||
<template #default="scope">
|
||||
<el-input
|
||||
v-model="preCheck.rowPrices[getRowKey(scope.row, scope.$index)][sym]"
|
||||
size="mini"
|
||||
class="price-input"
|
||||
placeholder="请输入"
|
||||
inputmode="decimal"
|
||||
>
|
||||
<template #append>{{ sym }}</template>
|
||||
</el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="preCheck.visible = false">取消</el-button>
|
||||
<el-button type="primary" :disabled="!canSubmitPreCheck" @click="handleConfirmBindAfterPreview">继续绑定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getMyShop } from "@/api/shops";
|
||||
import { getChainAndList, addWalletShopConfig } from "../../api/wallet";
|
||||
import { getChainAndList, addWalletShopConfig,getProductListForShopWalletConfig,updateProductListForShopWalletConfig } from "../../api/wallet";
|
||||
|
||||
export default {
|
||||
name: "AccountShopConfig",
|
||||
@@ -116,6 +164,8 @@ export default {
|
||||
// },
|
||||
],
|
||||
loading: false,
|
||||
// 绑定前预检测弹窗数据
|
||||
preCheck: { visible: false, rows: [], prices: {}, rowPrices: {} },
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
@@ -282,14 +332,159 @@ export default {
|
||||
return
|
||||
}
|
||||
|
||||
this.FetchAddWalletShopConfig(this.form);
|
||||
// 新增步骤:绑定前预检测商品列表
|
||||
this.preCheckBeforeBind()
|
||||
},
|
||||
/**
|
||||
* 绑定前预检测:若接口返回有关联商品,则弹窗展示;否则直接走绑定流程
|
||||
*/
|
||||
async preCheckBeforeBind() {
|
||||
try {
|
||||
this.loading = true
|
||||
const params = { chain: this.form.chain, payCoin: this.form.payCoin }
|
||||
const res = await getProductListForShopWalletConfig(params)
|
||||
const rows = Array.isArray(res && res.data) ? res.data : (Array.isArray(res && res.rows) ? res.rows : [])
|
||||
if (rows && rows.length) {
|
||||
this.preCheck.rows = rows
|
||||
// 初始化各币种价格输入
|
||||
const coins = (this.form.payCoin || '').split(',').map(s => s.trim().toUpperCase()).filter(Boolean)
|
||||
const map = {}
|
||||
coins.forEach(c => { if (!(c in this.preCheck.prices)) map[c] = '' })
|
||||
this.preCheck.prices = { ...map, ...this.preCheck.prices }
|
||||
// 初始化每行的价格容器
|
||||
this.preCheck.rowPrices = this.preCheck.rowPrices || {}
|
||||
this.preCheck.rows.forEach((r, idx) => {
|
||||
const key = this.getRowKey(r, idx)
|
||||
if (!this.preCheck.rowPrices[key]) this.$set(this.preCheck.rowPrices, key, {})
|
||||
coins.forEach(c => { if (!(c in this.preCheck.rowPrices[key])) this.$set(this.preCheck.rowPrices[key], c, '') })
|
||||
})
|
||||
this.preCheck.visible = true
|
||||
} else {
|
||||
// 无关联商品,直接绑定并设置(机器列表为空)
|
||||
await this.submitBindWithPrice([])
|
||||
}
|
||||
} catch (e) {
|
||||
// 接口异常不阻塞绑定流程
|
||||
await this.submitBindWithPrice([])
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
handleConfirmBindAfterPreview() {
|
||||
// 校验价格必填
|
||||
const coins = (this.form.payCoin || '').split(',').map(s => s.trim().toUpperCase()).filter(Boolean)
|
||||
// 逐行校验
|
||||
for (let i = 0; i < this.preCheck.rows.length; i++) {
|
||||
const row = this.preCheck.rows[i]
|
||||
const key = this.getRowKey(row, i)
|
||||
const priceMap = (this.preCheck.rowPrices && this.preCheck.rowPrices[key]) || {}
|
||||
for (const c of coins) {
|
||||
const v = priceMap[c]
|
||||
if (!v || Number(v) <= 0) {
|
||||
this.$message.warning(`请填写第 ${i + 1} 行 ${c} 的价格`)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
const groups = this.collectMachineGroups(this.preCheck.rows)
|
||||
this.preCheck.visible = false
|
||||
this.submitBindWithPrice(groups)
|
||||
},
|
||||
/** 收集每一行对应的机器ID分组(兼容不同返回结构) */
|
||||
collectMachineGroups(rows) {
|
||||
const groups = []
|
||||
const pushId = (arr, id) => { if (id != null && id !== '') arr.push(id) };
|
||||
(rows || []).forEach((r, idx) => {
|
||||
const ids = []
|
||||
// 兼容多种返回结构,优先使用接口包含的 machineList(每台矿机对象含 productMachineId)
|
||||
if (Array.isArray(r && r.machineList)) r.machineList.forEach(m => pushId(ids, m && (m.productMachineId != null ? m.productMachineId : m.id)))
|
||||
if (Array.isArray(r && r.productMachineIdList)) r.productMachineIdList.forEach(id => pushId(ids, id))
|
||||
if (r && r.productMachineId != null) pushId(ids, r.productMachineId)
|
||||
if (Array.isArray(r && r.productMachineDtoList)) r.productMachineDtoList.forEach(m => pushId(ids, (m && (m.productMachineId != null ? m.productMachineId : m.id))))
|
||||
if (Array.isArray(r && r.machines)) r.machines.forEach(m => pushId(ids, (m && (m.productMachineId != null ? m.productMachineId : m.id))))
|
||||
if (Array.isArray(r && r.items)) r.items.forEach(m => pushId(ids, (m && (m.productMachineId != null ? m.productMachineId : m.id))))
|
||||
const key = this.getRowKey(r, idx)
|
||||
groups.push({ key, machineIds: ids })
|
||||
})
|
||||
return groups
|
||||
},
|
||||
/** 生成某一行的 key(优先 productId/id) */
|
||||
getRowKey(row, index) {
|
||||
if (row && row.productId != null) return String(row.productId)
|
||||
if (row && row.id != null) return `p-${row.id}`
|
||||
return `idx-${index}`
|
||||
},
|
||||
/** 提交绑定:使用 updateProductListForShopWalletConfig 完成绑定与价格设置 */
|
||||
async submitBindWithPrice(machineGroups) {
|
||||
try {
|
||||
this.loading = true
|
||||
const coins = (this.form.payCoin || '').split(',').map(s => s.trim().toUpperCase()).filter(Boolean)
|
||||
const list = []
|
||||
if (Array.isArray(machineGroups) && machineGroups.length) {
|
||||
machineGroups.forEach(g => {
|
||||
const priceMap = (this.preCheck.rowPrices && this.preCheck.rowPrices[g.key]) || {}
|
||||
const priceStr = coins.map(c => priceMap[c] || '').join(',');
|
||||
(g.machineIds || []).forEach(id => { list.push({ productMachineId: id, price: priceStr }) })
|
||||
})
|
||||
}
|
||||
const payload = {
|
||||
chain: this.form.chain,
|
||||
symbol: this.form.payCoin,
|
||||
payAddress: this.form.payAddress,
|
||||
productMachineForWalletConfigVoList: list
|
||||
}
|
||||
const res = await updateProductListForShopWalletConfig(payload)
|
||||
if (res && (res.code === 0 || res.code === 200)) {
|
||||
|
||||
this.preCheck.visible = false
|
||||
this.resetPreCheckPrices()
|
||||
this.$message.success('绑定成功')
|
||||
this.$router.push('/account/shops')
|
||||
}else{
|
||||
this.preCheck.visible = true
|
||||
}
|
||||
} catch (e) {
|
||||
// 错误交由全局拦截或简单提示
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
handleReset() {
|
||||
this.form = { chain: "", payAddress: "", payCoin: "" };
|
||||
this.value = []
|
||||
},
|
||||
// 清空预检测中的价格输入
|
||||
resetPreCheckPrices() {
|
||||
try {
|
||||
this.preCheck.prices = {}
|
||||
this.preCheck.rowPrices = {}
|
||||
} catch (e) { /* noop */ }
|
||||
},
|
||||
// 弹窗关闭时清空价格输入
|
||||
handlePreCheckClose() {
|
||||
this.resetPreCheckPrices()
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
coinsForBind() {
|
||||
return (this.form.payCoin || '').split(',').map(s => s.trim().toUpperCase()).filter(Boolean)
|
||||
},
|
||||
canSubmitPreCheck() {
|
||||
if (!this.preCheck || !this.preCheck.visible) return false
|
||||
const coins = this.coinsForBind
|
||||
if (!coins.length) return false
|
||||
// 所有行都需要填写
|
||||
for (let i = 0; i < (this.preCheck.rows || []).length; i++) {
|
||||
const row = this.preCheck.rows[i]
|
||||
const key = this.getRowKey(row, i)
|
||||
const priceMap = (this.preCheck.rowPrices && this.preCheck.rowPrices[key]) || {}
|
||||
for (const c of coins) {
|
||||
const v = priceMap[c]
|
||||
if (!v || Number(v) <= 0) return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
},
|
||||
/**
|
||||
* 已选择币种的可读展示(中文顿号分隔)
|
||||
*/
|
||||
@@ -353,5 +548,11 @@ export default {
|
||||
.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; }
|
||||
|
||||
/* 价格输入框获得焦点时高亮为红色,提示用户输入 */
|
||||
.price-input :deep(.el-input__inner:focus) {
|
||||
border-color: #f56c6c !important;
|
||||
box-shadow: 0 0 0 1px #f56c6c inset;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -1036,7 +1036,7 @@ export default {
|
||||
items.push({
|
||||
product: shop.name || '',
|
||||
coin: this.toUpperText(m.coin),
|
||||
user: m.user,
|
||||
user: m.user,
|
||||
miner: m.miner,
|
||||
unitPrice: Number(unitPrice || 0),
|
||||
leaseTime: leaseDays,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
import { getProductById } from '../../utils/productService'
|
||||
import { addToCart } from '../../utils/cartManager'
|
||||
import { getMachineInfo } from '../../api/products'
|
||||
import { getMachineInfo, getPayTypes } from '../../api/products'
|
||||
import { addCart, getGoodsList } from '../../api/shoppingCart'
|
||||
|
||||
export default {
|
||||
@@ -13,6 +13,21 @@ export default {
|
||||
// 默认展开的行keys
|
||||
expandedRowKeys: [],
|
||||
selectedMap: {},
|
||||
// 新接口:单层矿机列表 & 支付方式
|
||||
machineList: [],
|
||||
paymentMethodList: [],
|
||||
// 筛选状态
|
||||
selectedPayKey: null,
|
||||
filters: {
|
||||
chain: '',
|
||||
coin: '',
|
||||
minPrice: null,
|
||||
maxPrice: null,
|
||||
minPower: null,
|
||||
maxPower: null,
|
||||
minPowerDissipation: null,
|
||||
maxPowerDissipation: null
|
||||
},
|
||||
params: {
|
||||
id: "",
|
||||
|
||||
@@ -120,6 +135,7 @@ export default {
|
||||
this.expandedRowKeys = [this.productListData[0].id]
|
||||
}
|
||||
this.fetchGetMachineInfo(this.params)
|
||||
this.fetchPayTypes()
|
||||
} else {
|
||||
this.$message.error('商品不存在')
|
||||
this.product = false
|
||||
@@ -127,6 +143,41 @@ export default {
|
||||
this.fetchGetGoodsList()
|
||||
},
|
||||
methods: {
|
||||
// 组合查询参数(带上商品 id 与筛选条件)
|
||||
buildQueryParams() {
|
||||
const q = { id: this.params.id }
|
||||
// 仅当用户真实填写(>0)时才传参;默认/空值不传
|
||||
const addNum = (obj, key, name) => {
|
||||
const raw = obj[key]
|
||||
if (raw === null || raw === undefined || raw === '') return
|
||||
const n = Number(raw)
|
||||
if (Number.isFinite(n) && n > 0) q[name] = n
|
||||
}
|
||||
// 支付方式条件:有值才传
|
||||
if (this.filters.chain && String(this.filters.chain).trim()) q.chain = String(this.filters.chain).trim()
|
||||
if (this.filters.coin && String(this.filters.coin).trim()) q.coin = String(this.filters.coin).trim()
|
||||
addNum(this.filters, 'minPrice', 'minPrice')
|
||||
addNum(this.filters, 'maxPrice', 'maxPrice')
|
||||
addNum(this.filters, 'minPower', 'minPower')
|
||||
addNum(this.filters, 'maxPower', 'maxPower')
|
||||
addNum(this.filters, 'minPowerDissipation', 'minPowerDissipation')
|
||||
addNum(this.filters, 'maxPowerDissipation', 'maxPowerDissipation')
|
||||
return q
|
||||
},
|
||||
// 拉取支付方式
|
||||
async fetchPayTypes() {
|
||||
try {
|
||||
const res = await getPayTypes({ productId: this.params.id })
|
||||
// 接口示例:{ code: 0, data: [ { payChain, payCoin, payCoinImage, shopId } ], msg: '' }
|
||||
if (res && (res.code === 0 || res.code === 200)) {
|
||||
const list = Array.isArray(res.data) ? res.data : []
|
||||
this.paymentMethodList = list
|
||||
}
|
||||
} catch (e) {
|
||||
// 忽略错误,保持页面可用
|
||||
this.paymentMethodList = []
|
||||
}
|
||||
},
|
||||
|
||||
async fetchGetMachineInfo(params) {
|
||||
this.productDetailLoading = true
|
||||
@@ -134,31 +185,26 @@ export default {
|
||||
console.log(res)
|
||||
if (res && res.code === 200) {
|
||||
console.log(res.data, 'res.rows');
|
||||
this.paymentMethodList = res.data.payConfigList || []
|
||||
const list =res.data.machineRangeInfoList || []
|
||||
const withKeys = list.map((group, idx) => {
|
||||
const fallbackId = `grp-${idx}`
|
||||
const groupId = group.id || group.onlyKey || (group.productMachineRangeGroupDto && group.productMachineRangeGroupDto.id)
|
||||
const firstMachineId = Array.isArray(group.productMachines) && group.productMachines.length > 0 ? group.productMachines[0].id : undefined
|
||||
// 为机器行设置默认租赁天数为1,并确保未选中状态
|
||||
const normalizedMachines = Array.isArray(group.productMachines)
|
||||
? group.productMachines.map(m => ({
|
||||
...m,
|
||||
leaseTime: (m && m.leaseTime && Number(m.leaseTime) > 0) ? Number(m.leaseTime) : 1,
|
||||
_selected: false // 确保所有机器行初始状态为未选中
|
||||
}))
|
||||
: []
|
||||
return { ...group, id: groupId || (firstMachineId ? `m-${firstMachineId}` : fallbackId), productMachines: normalizedMachines }
|
||||
})
|
||||
|
||||
this.productListData = withKeys
|
||||
if (this.productListData.length && (!this.expandedRowKeys || !this.expandedRowKeys.length)) {
|
||||
this.expandedRowKeys = [this.productListData[0].id]
|
||||
}
|
||||
// 产品机器加载完成后,依据购物车集合执行一次本地禁用与勾选
|
||||
// 新数据结构:机器为扁平 rows 列表;仅当后端返回有效支付方式时才覆盖,避免清空 getPayTypes 的结果
|
||||
try {
|
||||
const payList = res && res.data && res.data.payConfigList
|
||||
if (Array.isArray(payList) && payList.length) {
|
||||
this.paymentMethodList = payList
|
||||
}
|
||||
} catch (e) { /* keep existing paymentMethodList */ }
|
||||
const rows = (res && res.data && (res.data.rows || res.data.list)) || (res && res.rows) || []
|
||||
const normalized = (Array.isArray(rows) ? rows : []).map((m, idx) => ({
|
||||
...m,
|
||||
id: m && (m.id !== undefined && m.id !== null) ? m.id : `m-${idx}`,
|
||||
leaseTime: (m && m.leaseTime && Number(m.leaseTime) > 0) ? Number(m.leaseTime) : 1,
|
||||
_selected: false
|
||||
}))
|
||||
this.machineList = normalized
|
||||
// 清空旧的两层结构数据,避免误用
|
||||
this.productListData = []
|
||||
this.expandedRowKeys = []
|
||||
this.$nextTick(() => {
|
||||
this.machinesLoaded = true
|
||||
// 已取消与购物车对比:不再自动禁用或勾选
|
||||
})
|
||||
}
|
||||
|
||||
@@ -503,9 +549,12 @@ export default {
|
||||
// 取消所有商品勾选(内层表格的自定义 checkbox)
|
||||
clearAllSelections() {
|
||||
try {
|
||||
// 清空选中映射
|
||||
// 清空选中映射(遗留字段)
|
||||
this.selectedMap = {}
|
||||
// 遍历所有系列与机器,复位 _selected
|
||||
if (Array.isArray(this.machineList) && this.machineList.length) {
|
||||
this.machineList.forEach(m => { if (m) this.$set(m, '_selected', false) })
|
||||
return
|
||||
}
|
||||
const groups = Array.isArray(this.productListData) ? this.productListData : []
|
||||
groups.forEach(g => {
|
||||
const list = Array.isArray(g.productMachines) ? g.productMachines : []
|
||||
|
||||
@@ -16,12 +16,13 @@
|
||||
class="pay-item"
|
||||
:aria-label="`支付方式: ${item.payChain}`"
|
||||
>
|
||||
|
||||
<el-tooltip :content="formatPayTooltip(item)" placement="top" :open-delay="80">
|
||||
<img
|
||||
class="pay-icon"
|
||||
:src="item.payCoinImage"
|
||||
:alt="`${item.payChain} 支付`"
|
||||
:title="item.payChain"
|
||||
:src="getPayImageUrl(item)"
|
||||
:alt="`${(item.payChain || '').toUpperCase()} ${(item.payCoin || '').toUpperCase()}`.trim()"
|
||||
:title="formatPayTooltip(item)"
|
||||
tabindex="0"
|
||||
role="img"
|
||||
@keydown.enter.prevent="handlePayIconKeyDown(item)"
|
||||
@@ -31,99 +32,149 @@
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
<!-- 筛选栏 -->
|
||||
<section class="filter-bar" aria-label="筛选条件">
|
||||
<div class="filter-grid">
|
||||
<!-- 支付方式筛选:选择即触发查询 -->
|
||||
<div class="filter-cell">
|
||||
<label class="filter-title" for="payFilter">支付方式筛选</label>
|
||||
<el-select
|
||||
id="payFilter"
|
||||
v-model="selectedPayKey"
|
||||
placeholder="全部"
|
||||
clearable
|
||||
filterable
|
||||
size="small"
|
||||
class="filter-control"
|
||||
@change="handlePayFilterChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="(opt, i) in paymentMethodList"
|
||||
:key="i"
|
||||
:label="formatPayTooltip(opt)"
|
||||
:value="`${opt.payChain || ''}|${opt.payCoin || ''}`"
|
||||
>
|
||||
<div class="pay-opt">
|
||||
<img :src="getPayImageUrl(opt)" class="pay-icon" alt="" />
|
||||
<span>{{ (opt.payChain || '').toUpperCase() }} - {{ (opt.payCoin || '').toUpperCase() }}</span>
|
||||
</div>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
|
||||
<!-- 价格区间 -->
|
||||
<div class="filter-cell center-title">
|
||||
<label class="filter-title">单价区间<span v-if="getPriceCoinSymbol()">({{ getPriceCoinSymbol() }})</span></label>
|
||||
<div class="range-controls">
|
||||
<el-input-number v-model="filters.minPrice" :min="0" :step="1" :precision="0" :controls="false" size="small" class="filter-control" />
|
||||
<span class="filter-sep">-</span>
|
||||
<el-input-number v-model="filters.maxPrice" :min="0" :step="1" :precision="0" :controls="false" size="small" class="filter-control" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 实际算力区间 -->
|
||||
<div class="filter-cell center-title">
|
||||
<label class="filter-title">实际算力</label>
|
||||
<div class="range-controls">
|
||||
<el-input-number v-model="filters.minPower" :min="0" :step="0.1" :precision="2" :controls="false" size="small" class="filter-control" />
|
||||
<span class="filter-sep">-</span>
|
||||
<el-input-number v-model="filters.maxPower" :min="0" :step="0.1" :precision="2" :controls="false" size="small" class="filter-control" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 功耗区间:第二行左侧,占两列 -->
|
||||
<div class="filter-cell filter-cell--span-2 center-title">
|
||||
<label class="filter-title">功耗(kw/h)</label>
|
||||
<div class="range-controls">
|
||||
<el-input-number v-model="filters.minPowerDissipation" :min="0" :step="0.1" :precision="2" :controls="false" size="small" class="filter-control" />
|
||||
<span class="filter-sep">-</span>
|
||||
<el-input-number v-model="filters.maxPowerDissipation" :min="0" :step="0.1" :precision="2" :controls="false" size="small" class="filter-control" />
|
||||
<div class="filter-actions-inline">
|
||||
<el-button type="primary" size="small" @click="handleSearchFilters" aria-label="执行筛选">筛选查询</el-button>
|
||||
<el-button size="small" @click="handleResetFilters" aria-label="重置筛选">重置</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 操作区已合并到功耗区间后面 -->
|
||||
</div>
|
||||
</section>
|
||||
<section class="productList">
|
||||
<!-- 产品列表(可展开) -->
|
||||
<!-- 单层产品列表 -->
|
||||
<el-table
|
||||
ref="seriesTable"
|
||||
ref="machineTable"
|
||||
class="series-table"
|
||||
:data="productListData"
|
||||
:data="machineList"
|
||||
row-key="id"
|
||||
:expand-row-keys="expandedRowKeys"
|
||||
@expand-change="handleExpandChange"
|
||||
@row-click="handleSeriesRowClick"
|
||||
:row-class-name="handleGetSeriesRowClassName"
|
||||
:row-class-name="handleGetRowClass"
|
||||
:header-cell-style="{ textAlign: 'left' }"
|
||||
:cell-style="{ textAlign: 'left' }"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table-column type="expand" width="46">
|
||||
<template #default="outer">
|
||||
<!-- 子表格:展开后显示该行的多个可选条目(来自 productMachines) -->
|
||||
<el-table :data="outer.row.productMachines" size="small" style="width: 100%" :show-header="true" :ref="'innerTable-' + outer.row.id" :row-key="'id'" :reserve-selection="false" :header-cell-style="{ textAlign: 'left' }" :cell-style="{ textAlign: 'left' }" :row-class-name="handleGetInnerRowClass">
|
||||
<el-table-column width="46">
|
||||
<template #default="scope">
|
||||
<el-checkbox
|
||||
v-model="scope.row._selected"
|
||||
:disabled="scope.row.saleState === 1 || scope.row.saleState === 2"
|
||||
:title="(scope.row.saleState === 1 || scope.row.saleState === 2) ? '该机器已售出或售出中,无法选择' : ''"
|
||||
@change="checked => handleManualSelect(outer.row, scope.row, checked)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- 列宽精简,避免横向滚动 -->
|
||||
<el-table-column prop="theoryPower" label="理论算力" min-width="160" header-align="left" align="left" show-overflow-tooltip>
|
||||
<template #default="scope">{{ scope.row.theoryPower }} {{ scope.row.unit }}</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="实际算力" min-width="160" header-align="left" align="left" show-overflow-tooltip>
|
||||
<template #default="scope">{{ scope.row.computingPower }} {{ scope.row.unit }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="powerDissipation" label="功耗(kw/h)" min-width="140" header-align="left" align="left" />
|
||||
<el-table-column prop="algorithm" label="算法" min-width="120" header-align="left" align="left" />
|
||||
|
||||
<el-table-column prop="theoryIncome" min-width="160" header-align="left" align="left" show-overflow-tooltip>
|
||||
<template #header>单机理论收入(每日) <span v-show="outer.row.productMachines[0].coin">({{outer.row.productMachines[0].coin.toUpperCase() }})</span></template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="theoryUsdtIncome" label="单机理论收入(每日/USDT)" min-width="170" header-align="left" align="left" />
|
||||
<!-- 矿机型号置于最后,不影响上层对齐 -->
|
||||
<el-table-column prop="type" label="矿机型号" header-align="left" align="left" min-width="120" />
|
||||
<el-table-column label="最大可租赁(天)" min-width="140" header-align="left" align="left">
|
||||
<template #default="scope">{{ getRowMaxLeaseDays(scope.row) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="租赁天数(天)" min-width="150" header-align="left" align="left">
|
||||
<template #default="scope">
|
||||
<el-input-number
|
||||
v-model="scope.row.leaseTime"
|
||||
:min="1"
|
||||
:max="getRowMaxLeaseDays(scope.row)"
|
||||
:step="1"
|
||||
:precision="0"
|
||||
size="mini"
|
||||
:disabled="scope.row.saleState === 1 || scope.row.saleState === 2"
|
||||
controls-position="right"
|
||||
@change="val => handleLeaseDaysChange(scope.row, val)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="saleState" label="售出状态" header-align="left" align="left" min-width="110">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.saleState === 0 ? 'info' : (scope.row.saleState === 1 ? 'danger' : 'warning')">
|
||||
{{ scope.row.saleState === 0 ? '未售出' : (scope.row.saleState === 1 ? '已售出' : '售出中') }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-table-column width="46">
|
||||
<template #default="scope">
|
||||
<el-checkbox
|
||||
v-model="scope.row._selected"
|
||||
:disabled="scope.row.saleState === 1 || scope.row.saleState === 2"
|
||||
:title="(scope.row.saleState === 1 || scope.row.saleState === 2) ? '该机器已售出或售出中,无法选择' : ''"
|
||||
@change="checked => handleManualSelectFlat(scope.row, checked)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 外层列宽同样收紧,避免横向滚动 -->
|
||||
<el-table-column label="价格 (USDT)" header-align="left" align="left" min-width="120">
|
||||
<template slot-scope="scope"><span class="price-strong">{{ scope.row.productMachineRangeGroupDto && scope.row.productMachineRangeGroupDto.price }}</span></template>
|
||||
|
||||
|
||||
<el-table-column prop="theoryPower" label="理论算力" min-width="160" header-align="left" align="left" show-overflow-tooltip>
|
||||
<template #default="scope">{{ scope.row.theoryPower }} {{ scope.row.unit }}</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="理论算力范围" min-width="220" header-align="left" align="left" show-overflow-tooltip>
|
||||
<template slot-scope="scope">{{ scope.row.productMachineRangeGroupDto && scope.row.productMachineRangeGroupDto.theoryPowerRange }}</template>
|
||||
|
||||
<el-table-column label="实际算力" min-width="160" header-align="left" align="left" show-overflow-tooltip>
|
||||
<template #default="scope">{{ scope.row.computingPower }} {{ scope.row.unit }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="实际算力范围" min-width="200" header-align="left" align="left" show-overflow-tooltip>
|
||||
<template slot-scope="scope">{{ scope.row.productMachineRangeGroupDto && scope.row.productMachineRangeGroupDto.computingPowerRange }}</template>
|
||||
|
||||
<el-table-column prop="powerDissipation" label="功耗(kw/h)" min-width="140" header-align="left" align="left" />
|
||||
<el-table-column prop="algorithm" label="算法" min-width="120" header-align="left" align="left" />
|
||||
|
||||
<el-table-column prop="theoryIncome" width="120" header-align="left" align="left" show-overflow-tooltip>
|
||||
<template #header>
|
||||
单机理论收入(每日)
|
||||
<span v-if="getFirstCoinSymbol()">({{ getFirstCoinSymbol() }})</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="功耗范围" min-width="160" header-align="left" align="left">
|
||||
<template slot-scope="scope">{{ scope.row.productMachineRangeGroupDto && scope.row.productMachineRangeGroupDto.powerRange }}</template>
|
||||
<el-table-column prop="theoryUsdtIncome" label="单机理论收入(每日/USDT)" width="120" header-align="left" align="left" />
|
||||
|
||||
<el-table-column prop="type" label="矿机型号" header-align="left" align="left" min-width="120" />
|
||||
<el-table-column label="最大可租赁(天)" min-width="140" header-align="left" align="left">
|
||||
<template #default="scope">{{ getRowMaxLeaseDays(scope.row) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="数量" min-width="100" header-align="left" align="left">
|
||||
<template slot-scope="scope">{{ scope.row.productMachineRangeGroupDto && scope.row.productMachineRangeGroupDto.number }}</template>
|
||||
<el-table-column label="租赁天数(天)" min-width="150" header-align="left" align="left">
|
||||
<template #default="scope">
|
||||
<el-input-number
|
||||
v-model="scope.row.leaseTime"
|
||||
:min="1"
|
||||
:max="getRowMaxLeaseDays(scope.row)"
|
||||
:step="1"
|
||||
:precision="0"
|
||||
size="mini"
|
||||
:disabled="scope.row.saleState === 1 || scope.row.saleState === 2"
|
||||
controls-position="right"
|
||||
@change="val => handleLeaseDaysChange(scope.row, val)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="price" header-align="left" align="left" min-width="120">
|
||||
<template #header>
|
||||
单价 <span v-if="getPriceCoinSymbol()">({{ getPriceCoinSymbol() }})</span>
|
||||
</template>
|
||||
<template #default="scope"><span class="price-strong">{{ scope.row.price }}</span></template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="saleState" label="售出状态" header-align="left" align="left" min-width="110">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.saleState === 0 ? 'info' : (scope.row.saleState === 1 ? 'danger' : 'warning')">
|
||||
{{ scope.row.saleState === 0 ? '未售出' : (scope.row.saleState === 1 ? '已售出' : '售出中') }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
</el-table>
|
||||
</section>
|
||||
|
||||
@@ -146,7 +197,10 @@
|
||||
<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)" header-align="left" align="left">
|
||||
<el-table-column prop="price" header-align="left" align="left">
|
||||
<template #header>
|
||||
单价 <span v-if="getPriceCoinSymbol()">({{ getPriceCoinSymbol() }})</span>
|
||||
</template>
|
||||
<template #default="scope"><span class="price-strong">{{ scope.row.price }}</span></template>
|
||||
</el-table-column>
|
||||
|
||||
@@ -187,6 +241,87 @@ export default {
|
||||
if (n > 365) return 365
|
||||
return Math.floor(n)
|
||||
},
|
||||
/**
|
||||
* 处理支付方式图片 URL(去除服务端可能带入的换行/空白)
|
||||
* @param {Object} item
|
||||
*/
|
||||
getPayImageUrl(item) {
|
||||
try {
|
||||
const src = (item && item.payCoinImage) ? String(item.payCoinImage) : ''
|
||||
return src.trim()
|
||||
} catch (e) {
|
||||
return ''
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 支付方式下拉变更:选择/清空即触发请求
|
||||
* @param {{payChain?: string, payCoin?: string}|null} val
|
||||
*/
|
||||
handlePayFilterChange(val) {
|
||||
try {
|
||||
const s = typeof val === 'string' ? val : ''
|
||||
if (!s) {
|
||||
this.filters.chain = ''
|
||||
this.filters.coin = ''
|
||||
} else {
|
||||
const [chain, coin] = s.split('|')
|
||||
this.filters.chain = (chain || '').trim()
|
||||
this.filters.coin = (coin || '').trim()
|
||||
}
|
||||
this.handleSearchFilters()
|
||||
} catch (e) { /* noop */ }
|
||||
},
|
||||
/**
|
||||
* 组合筛选参数并请求数据
|
||||
*/
|
||||
handleSearchFilters() {
|
||||
const params = this.buildQueryParams()
|
||||
this.fetchGetMachineInfo(params)
|
||||
},
|
||||
/**
|
||||
* 重置筛选
|
||||
*/
|
||||
handleResetFilters() {
|
||||
this.selectedPayKey = null
|
||||
this.filters = {
|
||||
chain: '',
|
||||
coin: '',
|
||||
minPrice: null,
|
||||
maxPrice: null,
|
||||
minPower: null,
|
||||
maxPower: null,
|
||||
minPowerDissipation: null,
|
||||
maxPowerDissipation: null
|
||||
}
|
||||
this.handleSearchFilters()
|
||||
},
|
||||
/**
|
||||
* 获取列表第一个条目的币种,安全返回大写字符串
|
||||
* 用于表头显示币种,避免空数组时报错
|
||||
*/
|
||||
getFirstCoinSymbol() {
|
||||
try {
|
||||
const list = Array.isArray(this.machineList) ? this.machineList : []
|
||||
const coin = list.length && list[0] && list[0].coin ? String(list[0].coin) : ''
|
||||
return coin ? coin.toUpperCase() : ''
|
||||
} catch (e) {
|
||||
return ''
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 获取价格单位(优先读取每行的 payCoin 字段)
|
||||
*/
|
||||
getPriceCoinSymbol() {
|
||||
try {
|
||||
const list = Array.isArray(this.machineList) ? this.machineList : []
|
||||
// 寻找第一个存在 payCoin 的条目
|
||||
const item = list.find(it => it && it.payCoin)
|
||||
const unit = item && item.payCoin ? String(item.payCoin) : ''
|
||||
return unit ? unit.toUpperCase() : ''
|
||||
} catch (e) {
|
||||
return ''
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 限制并校验租赁天数:区间 [1, max],并取整
|
||||
*/
|
||||
@@ -232,6 +367,52 @@ export default {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('handlePayIconKeyDown error:', err);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 单层:切换勾选
|
||||
* @param {Object} row - 当前机器行
|
||||
* @param {boolean} checked - 勾选状态
|
||||
*/
|
||||
handleManualSelectFlat(row, checked) {
|
||||
try {
|
||||
if (!row) return
|
||||
if (row.saleState === 1 || row.saleState === 2) {
|
||||
this.$message.warning('该机器已售出或售出中,无法选择')
|
||||
this.$set(row, '_selected', false)
|
||||
return
|
||||
}
|
||||
this.$set(row, '_selected', !!checked)
|
||||
} catch (e) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('handleManualSelectFlat error:', e)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 单层:行样式(售出态高亮)
|
||||
*/
|
||||
handleGetRowClass({ row }) {
|
||||
if (!row) return ''
|
||||
return (row.saleState === 1 || row.saleState === 2) ? 'sold-row' : ''
|
||||
},
|
||||
/**
|
||||
* 覆盖 mixin 的多层版本:基于单层勾选打开确认弹窗
|
||||
*/
|
||||
handleOpenAddToCartDialog() {
|
||||
const list = Array.isArray(this.machineList) ? this.machineList : []
|
||||
const pickedAll = list.filter(it => !!it && !!it._selected)
|
||||
const picked = pickedAll.filter(m => m && (m.saleState === 0 || m.saleState === undefined || m.saleState === null))
|
||||
if (!picked.length) {
|
||||
this.$message.warning('请先勾选至少一台矿机')
|
||||
return
|
||||
}
|
||||
if (picked.length < pickedAll.length) {
|
||||
this.$message.warning('部分机器已售出或售出中,已自动为您排除')
|
||||
}
|
||||
this.confirmAddDialog.items = picked.slice()
|
||||
this.confirmAddDialog.visible = true
|
||||
this.$nextTick(() => {
|
||||
try { (this.machineList || []).forEach(m => this.$set(m, '_selected', false)) } catch (e) { /* noop */ }
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -400,6 +581,9 @@ export default {
|
||||
transition: transform 0.15s ease, box-shadow 0.15s ease;
|
||||
}
|
||||
|
||||
.pay-item-inner { display: inline-flex; align-items: center; gap: 8px; }
|
||||
.pay-text { font-size: 12px; color: #2c3e50; }
|
||||
|
||||
.pay-icon:hover {
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
@@ -409,6 +593,76 @@ export default {
|
||||
box-shadow: 0 0 0 3px rgba(25, 118, 210, 0.2);
|
||||
}
|
||||
|
||||
/* 筛选栏样式 */
|
||||
.filter-bar {
|
||||
background: #ffffff;
|
||||
border: 1px solid #eef2f7;
|
||||
border-radius: 8px;
|
||||
padding: 12px 16px;
|
||||
margin: 0 10px 16px 10px;
|
||||
}
|
||||
.filter-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(260px, 1fr));
|
||||
gap: 14px 18px;
|
||||
align-items: end;
|
||||
}
|
||||
.filter-cell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: start;
|
||||
gap: 6px;
|
||||
}
|
||||
.filter-cell.center-title .filter-title { text-align: center; }
|
||||
|
||||
.filter-title {
|
||||
font-size: 14px;
|
||||
color: #34495E;
|
||||
font-weight: 600;
|
||||
margin-bottom: 8px;
|
||||
|
||||
}
|
||||
.filter-control {
|
||||
width: 100%;
|
||||
max-width: 320px;
|
||||
}
|
||||
.range-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.range-controls :deep(.el-input-number) { width: 150px; }
|
||||
.pay-opt { display: inline-flex; align-items: center; gap: 8px; }
|
||||
.filter-sep {
|
||||
color: #9aa4b2;
|
||||
}
|
||||
.filter-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
grid-column: 2 / 3; /* 放到中间这一格 */
|
||||
}
|
||||
|
||||
.filter-actions-inline {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
.filter-grid { grid-template-columns: repeat(2, minmax(220px, 1fr)); }
|
||||
.filter-cell--span-2 { grid-column: 1 / span 1; }
|
||||
.filter-actions { grid-column: 1 / -1; justify-content: flex-end; }
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.filter-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.filter-actions { grid-column: 1 / 2; justify-content: flex-end; }
|
||||
}
|
||||
|
||||
/* 外层系列行:整行可点击 + 视觉增强 */
|
||||
:deep(.series-clickable-row) {
|
||||
cursor: pointer;
|
||||
|
||||
@@ -60,9 +60,17 @@
|
||||
<h4>商品: {{ product.name }}</h4>
|
||||
<p style="font-size: 16px;margin-top: 10px;font-weight: bold;">算法: {{ product.algorithm }}</p>
|
||||
<div class="product-footer">
|
||||
<div class="price-wrap">
|
||||
<span class="product-price">价格: {{ formatPriceRange(product.priceRange) }}</span>
|
||||
<span class="unit">USDT</span>
|
||||
<div class="paytypes">
|
||||
<span class="paytypes-label">支付方式:</span>
|
||||
<el-tooltip
|
||||
v-for="(pt, idx) in (product.payTypes || [])"
|
||||
:key="idx"
|
||||
:content="formatPayType(pt)"
|
||||
placement="top"
|
||||
:open-delay="80"
|
||||
>
|
||||
<img :src="pt.image" :alt="formatPayType(pt)" class="paytype-icon" />
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<span class="product-sold" aria-label="已售数量">已售:{{ product && product.saleNumber != null ? product.saleNumber : 0 }}</span>
|
||||
</div>
|
||||
@@ -89,6 +97,19 @@ export default {
|
||||
|
||||
mounted() {},
|
||||
methods: {
|
||||
/**
|
||||
* 将 payType 显示为 CHAIN-COIN 文本
|
||||
*/
|
||||
formatPayType(item) {
|
||||
try {
|
||||
const chain = (item && item.chain ? String(item.chain) : '').toUpperCase()
|
||||
const coin = (item && item.coin ? String(item.coin) : '').toUpperCase()
|
||||
if (chain && coin) return `${chain}-${coin}`
|
||||
return chain || coin || ''
|
||||
} catch (e) {
|
||||
return ''
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 处理商品点击 - 跳转到详情页
|
||||
*/
|
||||
@@ -195,6 +216,9 @@ export default {
|
||||
.price-wrap { display: inline-flex; align-items: baseline; gap: 6px; }
|
||||
.unit { color: #999; font-size: 12px; }
|
||||
.product-sold { color: #64748b; font-size: 12px; }
|
||||
.paytypes { display: inline-flex; align-items: center; gap: 8px; }
|
||||
.paytype-icon { width: 22px; height: 22px; border-radius: 4px; display: inline-block; }
|
||||
.paytypes-label { color: #64748b; font-size: 12px; }
|
||||
.add-cart-btn {
|
||||
background: #42b983;
|
||||
color: #fff;
|
||||
|
||||
Reference in New Issue
Block a user