周五固定更新
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
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -61,38 +61,38 @@ export function getShopConfig(id) {
|
|||||||
method: 'post',
|
method: 'post',
|
||||||
data: { id }
|
data: { id }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 新增商铺配置
|
// 新增商铺配置
|
||||||
export function addShopConfig(data) {
|
export function addShopConfig(data) {
|
||||||
return request({
|
return request({
|
||||||
url: `/lease/shop/addShopConfig`,
|
url: `/lease/shop/addShopConfig`,
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data
|
data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据配置id 修改配置
|
// 根据配置id 修改配置
|
||||||
export function updateShopConfig(data) {
|
export function updateShopConfig(data) {
|
||||||
return request({
|
return request({
|
||||||
url: `/lease/shop/updateShopConfig`,
|
url: `/lease/shop/updateShopConfig`,
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data
|
data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据配置id 删除配置
|
// 根据配置id 删除配置
|
||||||
export function deleteShopConfig(data) {
|
export function deleteShopConfig(data) {
|
||||||
return request({
|
return request({
|
||||||
url: `/lease/shop/deleteShopConfig`,
|
url: `/lease/shop/deleteShopConfig`,
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data
|
data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 钱包配置(用于修改卖家钱包地址)----获取链(一级)和币(二级) 下拉列表(获取本系统支持的链和币种)
|
// 钱包配置(用于修改卖家钱包地址)----获取链(一级)和币(二级) 下拉列表(获取本系统支持的链和币种)
|
||||||
export function getChainAndCoin(data) {
|
export function getChainAndCoin(data) {
|
||||||
return request({
|
return request({
|
||||||
url: `/lease/shop/getChainAndCoin`,
|
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
|
return
|
||||||
}
|
}
|
||||||
try {
|
try { this.$router.push(`/account/order-detail/${id}`) } catch (e) {
|
||||||
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) {
|
|
||||||
this.$message({
|
this.$message({
|
||||||
message: '无法跳转到详情页',
|
message: '无法跳转到详情页',
|
||||||
type: 'error',
|
type: 'error',
|
||||||
|
|||||||
@@ -37,21 +37,22 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="价格范围">
|
<!-- <el-form-item label="价格范围">
|
||||||
<el-input :value="product && product.priceRange" disabled />
|
<el-input :value="product && product.priceRange" disabled />
|
||||||
</el-form-item>
|
</el-form-item> -->
|
||||||
</el-col>
|
|
||||||
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="类型">
|
<el-form-item label="类型">
|
||||||
<el-input :value="product && (product.type === 1 ? '算力套餐' : '挖矿机器')" disabled />
|
<el-input :value="product && (product.type === 1 ? '算力套餐' : '挖矿机器')" disabled />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="状态">
|
<el-form-item label="状态">
|
||||||
<el-input :value="product && (product.state === 1 ? '下架' : '上架')" disabled />
|
<el-input :value="product && (product.state === 1 ? '下架' : '上架')" disabled />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
|
||||||
|
</el-col>
|
||||||
|
|
||||||
<!-- <el-col :span="24">
|
<!-- <el-col :span="24">
|
||||||
<el-form-item label="图片">
|
<el-form-item label="图片">
|
||||||
@@ -76,9 +77,9 @@
|
|||||||
<div slot="header" class="section-title">机器组合</div>
|
<div slot="header" class="section-title">机器组合</div>
|
||||||
<div v-if="machineList && machineList.length">
|
<div v-if="machineList && machineList.length">
|
||||||
<el-table :data="machineList" border stripe style="width: 100%">
|
<el-table :data="machineList" border stripe style="width: 100%">
|
||||||
<el-table-column prop="user" label="挖矿账户" min-width="80" />
|
<el-table-column prop="user" label="挖矿账户" />
|
||||||
<el-table-column prop="id" label="矿机ID" min-width="60" />
|
<el-table-column prop="id" label="矿机ID" />
|
||||||
<el-table-column prop="miner" label="机器编号" min-width="100" />
|
<el-table-column prop="miner" label="机器编号" />
|
||||||
<el-table-column label="实际算力" width="100">
|
<el-table-column label="实际算力" width="100">
|
||||||
<template slot="header">
|
<template slot="header">
|
||||||
<el-tooltip content="实际算力为该机器在本矿池过去24H的平均算力" effect="dark" placement="top">
|
<el-tooltip content="实际算力为该机器在本矿池过去24H的平均算力" effect="dark" placement="top">
|
||||||
@@ -100,11 +101,15 @@
|
|||||||
:class="{ 'changed-input': isCellChanged(scope.row, 'theoryPower') }"
|
:class="{ 'changed-input': isCellChanged(scope.row, 'theoryPower') }"
|
||||||
style="max-width: 260px;"
|
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>
|
</el-input>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="功耗(kw/h)" min-width="140">
|
<el-table-column label="功耗(kw/h)" >
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="scope.row.powerDissipation"
|
v-model="scope.row.powerDissipation"
|
||||||
@@ -116,16 +121,16 @@
|
|||||||
:class="{ 'changed-input': isCellChanged(scope.row, 'powerDissipation') }"
|
:class="{ 'changed-input': isCellChanged(scope.row, 'powerDissipation') }"
|
||||||
style="max-width: 260px;"
|
style="max-width: 260px;"
|
||||||
>
|
>
|
||||||
<template slot="append">kw/h</template>
|
<!-- <template slot="append">kw/h</template> -->
|
||||||
</el-input>
|
</el-input>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="型号" min-width="140">
|
<el-table-column label="型号" >
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="scope.row.type"
|
v-model="scope.row.type"
|
||||||
size="small"
|
size="small"
|
||||||
placeholder="矿机型号"
|
|
||||||
:maxlength="20"
|
:maxlength="20"
|
||||||
:disabled="isRowDisabled(scope.row)"
|
:disabled="isRowDisabled(scope.row)"
|
||||||
@input="handleTypeCell(scope.$index)"
|
@input="handleTypeCell(scope.$index)"
|
||||||
@@ -134,7 +139,7 @@
|
|||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="售价(USDT)" min-width="140">
|
<el-table-column label="售价" width="188">
|
||||||
<template slot="header">
|
<template slot="header">
|
||||||
<el-tooltip effect="dark" placement="top">
|
<el-tooltip effect="dark" placement="top">
|
||||||
<div slot="content">
|
<div slot="content">
|
||||||
@@ -145,11 +150,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<i class="el-icon-question label-help" aria-label="帮助" tabindex="0"></i>
|
<i class="el-icon-question label-help" aria-label="帮助" tabindex="0"></i>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<span>售价(USDT)</span>
|
<span>售价(按结算币种)</span>
|
||||||
</template>
|
</template>
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="scope.row.price"
|
v-model="scope.row._priceEditing"
|
||||||
size="small"
|
size="small"
|
||||||
inputmode="decimal"
|
inputmode="decimal"
|
||||||
:disabled="isRowDisabled(scope.row)"
|
:disabled="isRowDisabled(scope.row)"
|
||||||
@@ -158,11 +163,20 @@
|
|||||||
:class="{ 'changed-input': isCellChanged(scope.row, 'price') }"
|
:class="{ 'changed-input': isCellChanged(scope.row, 'price') }"
|
||||||
style="max-width: 260px;"
|
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>
|
</el-input>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="最大租赁天数(天)" min-width="140">
|
<el-table-column label="最大租赁天数(天)" width="100">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="scope.row.maxLeaseDays"
|
v-model="scope.row.maxLeaseDays"
|
||||||
@@ -248,6 +262,8 @@ export default {
|
|||||||
// 可编辑字段快照(用于变更高亮)
|
// 可编辑字段快照(用于变更高亮)
|
||||||
fieldSnapshot: {},
|
fieldSnapshot: {},
|
||||||
updateLoading:false,
|
updateLoading:false,
|
||||||
|
// 算力单位选项(与新增出售机器页面保持一致)
|
||||||
|
unitOptions: ['KH/S','MH/S','GH/S','TH/S','PH/S'],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -269,6 +285,15 @@ export default {
|
|||||||
|
|
||||||
},
|
},
|
||||||
methods: {
|
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 - 当前行数据
|
* @param {Object} row - 当前行数据
|
||||||
@@ -304,7 +329,13 @@ export default {
|
|||||||
|
|
||||||
|
|
||||||
if (res && res.code === 200) {
|
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.refreshStateSnapshot()
|
||||||
this.refreshFieldSnapshot()
|
this.refreshFieldSnapshot()
|
||||||
}
|
}
|
||||||
@@ -338,11 +369,15 @@ export default {
|
|||||||
for (let i = 0; i < list.length; i += 1) {
|
for (let i = 0; i < list.length; i += 1) {
|
||||||
const row = list[i]
|
const row = list[i]
|
||||||
if (!row || typeof row.id === 'undefined') continue
|
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] = {
|
snapshot[row.id] = {
|
||||||
theoryPower: String(row.theoryPower ?? ''),
|
theoryPower: String(row.theoryPower ?? ''),
|
||||||
powerDissipation: String(row.powerDissipation ?? ''),
|
powerDissipation: String(row.powerDissipation ?? ''),
|
||||||
type: String(row.type ?? ''),
|
type: String(row.type ?? ''),
|
||||||
price: String(row.price ?? ''),
|
priceMap,
|
||||||
maxLeaseDays: String(row.maxLeaseDays ?? ''),
|
maxLeaseDays: String(row.maxLeaseDays ?? ''),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -358,6 +393,14 @@ export default {
|
|||||||
isCellChanged(row, key) {
|
isCellChanged(row, key) {
|
||||||
if (!row || typeof row.id === 'undefined') return false
|
if (!row || typeof row.id === 'undefined') return false
|
||||||
const snap = this.fieldSnapshot[row.id] || {}
|
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 current = String(row[key] ?? '')
|
||||||
const original = String(snap[key] ?? '')
|
const original = String(snap[key] ?? '')
|
||||||
return current !== original
|
return current !== original
|
||||||
@@ -438,7 +481,7 @@ export default {
|
|||||||
// - 功耗:6 位整数 + 4 位小数
|
// - 功耗:6 位整数 + 4 位小数
|
||||||
// - 价格:12 位整数 + 2 位小数
|
// - 价格:12 位整数 + 2 位小数
|
||||||
// - 其他:保持原逻辑(6 位小数)
|
// - 其他:保持原逻辑(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, '')
|
v = v.replace(/[^0-9.]/g, '')
|
||||||
const firstDot = v.indexOf('.')
|
const firstDot = v.indexOf('.')
|
||||||
if (firstDot !== -1) {
|
if (firstDot !== -1) {
|
||||||
@@ -460,22 +503,35 @@ export default {
|
|||||||
if (intPart.length > 12) { intPart = intPart.slice(0, 12) }
|
if (intPart.length > 12) { intPart = intPart.slice(0, 12) }
|
||||||
if (decPart) { decPart = decPart.slice(0, 2) }
|
if (decPart) { decPart = decPart.slice(0, 2) }
|
||||||
v = decPart.length ? `${intPart}.${decPart}` : (endsWithDot ? `${intPart}.` : intPart)
|
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 {
|
} else {
|
||||||
if (firstDot !== -1) {
|
if (firstDot !== -1) {
|
||||||
const [i, d] = v.split('.')
|
const [i, d] = v.split('.')
|
||||||
v = i + '.' + (d ? d.slice(0, 6) : '')
|
v = i + '.' + (d ? d.slice(0, 6) : '')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (key !== 'price') {
|
||||||
const row = { ...this.machineList[index], [key]: v }
|
const row = { ...this.machineList[index], [key]: v }
|
||||||
this.$set(this.machineList, index, row)
|
this.$set(this.machineList, index, row)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
handlePriceBlur(index) {
|
handlePriceBlur(index) {
|
||||||
const raw = String(this.machineList[index].price ?? '')
|
const raw = String(this.machineList[index]._priceEditing ?? '')
|
||||||
const pattern = /^\d{1,12}(\.\d{1,2})?$/
|
const pattern = /^\d{1,12}(\.\d{1,2})?$/
|
||||||
if (!raw || Number(raw) <= 0 || !pattern.test(raw)) {
|
if (!raw || Number(raw) <= 0 || !pattern.test(raw)) {
|
||||||
this.$message.warning('单价必须大于0,整数最多12位,小数最多2位')
|
this.$message.warning('单价必须大于0,整数最多12位,小数最多2位')
|
||||||
const row = { ...this.machineList[index], price: '' }
|
this.$set(this.machineList[index], '_priceEditing', '')
|
||||||
this.$set(this.machineList, index, row)
|
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) {
|
handleMaxLeaseDaysInput(index) {
|
||||||
@@ -567,7 +623,7 @@ export default {
|
|||||||
const row = this.machineList[i]
|
const row = this.machineList[i]
|
||||||
const rowLabel = row && (row.miner || row.id || i + 1)
|
const rowLabel = row && (row.miner || row.id || i + 1)
|
||||||
const theoryRaw = String(row.theoryPower ?? '')
|
const theoryRaw = String(row.theoryPower ?? '')
|
||||||
const priceRaw = String(row.price ?? '')
|
const priceRaw = String(row._priceEditing ?? '')
|
||||||
const typeRaw = String(row.type ?? '')
|
const typeRaw = String(row.type ?? '')
|
||||||
const dissRaw = String(row.powerDissipation ?? '')
|
const dissRaw = String(row.powerDissipation ?? '')
|
||||||
const daysRaw = String(row.maxLeaseDays ?? '')
|
const daysRaw = String(row.maxLeaseDays ?? '')
|
||||||
@@ -598,7 +654,7 @@ export default {
|
|||||||
const payload = this.machineList.map(m => ({
|
const payload = this.machineList.map(m => ({
|
||||||
id: m.id,
|
id: m.id,
|
||||||
powerDissipation: Number(m.powerDissipation ?? 0),
|
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),
|
state: Number(m.state ?? 0),
|
||||||
theoryPower: Number(m.theoryPower ?? 0),
|
theoryPower: Number(m.theoryPower ?? 0),
|
||||||
type: m.type || '',
|
type: m.type || '',
|
||||||
@@ -641,17 +697,51 @@ export default {
|
|||||||
.empty-text { color: #909399; text-align: center; padding: 12px 0; }
|
.empty-text { color: #909399; text-align: center; padding: 12px 0; }
|
||||||
|
|
||||||
.label-help { margin-left: 4px; color: #909399; cursor: help; }
|
.label-help { margin-left: 4px; color: #909399; cursor: help; }
|
||||||
|
/* ::v-deep .el-form-item__content{
|
||||||
|
margin-left: 52px !important;
|
||||||
|
} */
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<style>
|
<style>
|
||||||
.el-input-group__append, .el-input-group__prepend{
|
.el-input-group__append, .el-input-group__prepend{
|
||||||
padding: 0 5px !important;
|
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 .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;
|
border-color: #f56c6c !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,6 @@
|
|||||||
<el-form-item label="功耗" prop="powerDissipation">
|
<el-form-item label="功耗" prop="powerDissipation">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="form.powerDissipation"
|
v-model="form.powerDissipation"
|
||||||
placeholder="示例:0.01"
|
|
||||||
inputmode="decimal"
|
inputmode="decimal"
|
||||||
@input="handleNumeric('powerDissipation')"
|
@input="handleNumeric('powerDissipation')"
|
||||||
style="width: 50%;"
|
style="width: 50%;"
|
||||||
@@ -79,7 +78,22 @@
|
|||||||
<i class="el-icon-question label-help" aria-label="帮助" tabindex="0"></i>
|
<i class="el-icon-question label-help" aria-label="帮助" tabindex="0"></i>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</span>
|
</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
|
<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"
|
v-model="form.cost"
|
||||||
placeholder="请输入成本(USDT)"
|
placeholder="请输入成本(USDT)"
|
||||||
inputmode="decimal"
|
inputmode="decimal"
|
||||||
@@ -158,7 +172,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="售价(USDT)" min-width="160">
|
<el-table-column label="售价(按结算币种)" min-width="220">
|
||||||
<template slot="header">
|
<template slot="header">
|
||||||
<el-tooltip effect="dark" placement="top">
|
<el-tooltip effect="dark" placement="top">
|
||||||
<div slot="content">
|
<div slot="content">
|
||||||
@@ -169,10 +183,25 @@
|
|||||||
</div>
|
</div>
|
||||||
<i class="el-icon-question label-help" aria-label="帮助" tabindex="0"></i>
|
<i class="el-icon-question label-help" aria-label="帮助" tabindex="0"></i>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<span>售价(USDT)</span>
|
<span>售价(按结算币种)</span>
|
||||||
</template>
|
</template>
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
|
<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
|
<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"
|
v-model="scope.row.price"
|
||||||
placeholder="价格"
|
placeholder="价格"
|
||||||
inputmode="decimal"
|
inputmode="decimal"
|
||||||
@@ -182,7 +211,7 @@
|
|||||||
>
|
>
|
||||||
<template slot="append">USDT</template>
|
<template slot="append">USDT</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -265,6 +294,7 @@ export default {
|
|||||||
type: '',
|
type: '',
|
||||||
unit: 'TH/S',
|
unit: 'TH/S',
|
||||||
cost: '',
|
cost: '',
|
||||||
|
costMap: {}, // { 'CHAIN-COIN': '123.45' }
|
||||||
maxLeaseDays: ''
|
maxLeaseDays: ''
|
||||||
},
|
},
|
||||||
confirmVisible: false,
|
confirmVisible: false,
|
||||||
@@ -301,15 +331,18 @@ export default {
|
|||||||
],
|
],
|
||||||
unit: [ { required: true, message: '请选择算力单位', trigger: 'change' } ],
|
unit: [ { required: true, message: '请选择算力单位', trigger: 'change' } ],
|
||||||
cost: [
|
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 || '')
|
const str = String(value || '')
|
||||||
if (!str) {
|
if (!str) {
|
||||||
callback(new Error('请填写机器成本(USDT)'))
|
callback(new Error('请填写机器成本(USDT)'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 整数最多12位,小数最多2位
|
|
||||||
const pattern = /^\d{1,12}(\.\d{1,2})?$/
|
const pattern = /^\d{1,12}(\.\d{1,2})?$/
|
||||||
if (!pattern.test(str)) {
|
if (!pattern.test(str)) {
|
||||||
callback(new Error('成本整数最多12位,小数最多2位'))
|
callback(new Error('成本整数最多12位,小数最多2位'))
|
||||||
@@ -421,6 +454,7 @@ export default {
|
|||||||
selectedMachineRows: [],
|
selectedMachineRows: [],
|
||||||
saving: false,
|
saving: false,
|
||||||
lastCostBaseline: 0,
|
lastCostBaseline: 0,
|
||||||
|
lastCostMapBaseline: {}, // { key: number }
|
||||||
lastTypeBaseline: '',
|
lastTypeBaseline: '',
|
||||||
lastMaxLeaseDaysBaseline: 0,
|
lastMaxLeaseDaysBaseline: 0,
|
||||||
lastPowerDissipationBaseline: 0,
|
lastPowerDissipationBaseline: 0,
|
||||||
@@ -455,10 +489,58 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
|
this.initPayTypesFromRoute()
|
||||||
this.fetchMiners()
|
this.fetchMiners()
|
||||||
this.lastTypeBaseline = this.form.type
|
this.lastTypeBaseline = this.form.type
|
||||||
|
// 绑定基于组件实例的校验器,避免 this 丢失
|
||||||
|
if (this.rules && this.rules.cost) {
|
||||||
|
this.$set(this.rules, 'cost', [{ validator: this.validateCost, trigger: 'blur' }])
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
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() {
|
handleBack() {
|
||||||
this.$router.back()
|
this.$router.back()
|
||||||
},
|
},
|
||||||
@@ -516,6 +598,36 @@ export default {
|
|||||||
this.syncCostToRows()
|
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字符
|
* 顶部矿机型号输入:限制20字符
|
||||||
*/
|
*/
|
||||||
@@ -563,18 +675,24 @@ export default {
|
|||||||
if (m) {
|
if (m) {
|
||||||
// 若已存在,沿用已编辑的价格、型号和状态
|
// 若已存在,沿用已编辑的价格、型号和状态
|
||||||
const existed = this.selectedMachineRows.find(r => r.miner === minerId)
|
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({
|
nextRows.push({
|
||||||
user: m.user,
|
user: m.user,
|
||||||
coin: m.coin,
|
coin: m.coin,
|
||||||
miner: m.miner,
|
miner: m.miner,
|
||||||
realPower: m.realPower,
|
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,
|
powerDissipation: existed && existed.powerDissipation !== undefined ? existed.powerDissipation : this.form.powerDissipation,
|
||||||
theoryPower: existed && existed.theoryPower !== undefined ? existed.theoryPower : this.form.theoryPower,
|
theoryPower: existed && existed.theoryPower !== undefined ? existed.theoryPower : this.form.theoryPower,
|
||||||
unit: existed && existed.unit ? existed.unit : this.form.unit,
|
unit: existed && existed.unit ? existed.unit : this.form.unit,
|
||||||
type: existed ? existed.type : this.form.type,
|
type: existed ? existed.type : this.form.type,
|
||||||
state: existed ? existed.state : 0, // 默认上架
|
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)
|
v = decPart.length ? `${intPart}.${decPart}` : (endsWithDot ? `${intPart}.` : intPart)
|
||||||
this.$set(this.selectedMachineRows[index], 'price', v)
|
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) {
|
handleRowPriceBlur(index) {
|
||||||
const raw = String(this.selectedMachineRows[index].price ?? '')
|
const raw = String(this.selectedMachineRows[index].price ?? '')
|
||||||
const pattern = /^\d{1,12}(\.\d{1,2})?$/
|
const pattern = /^\d{1,12}(\.\d{1,2})?$/
|
||||||
@@ -865,15 +1015,28 @@ export default {
|
|||||||
this.$message.warning('存在行的矿机型号全是空格,请修正后再试')
|
this.$message.warning('存在行的矿机型号全是空格,请修正后再试')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 校验:已选择机器的价格必须大于0
|
// 校验:价格与最大租赁天数
|
||||||
for (let i = 0; i < this.selectedMachineRows.length; i += 1) {
|
for (let i = 0; i < this.selectedMachineRows.length; i += 1) {
|
||||||
const row = this.selectedMachineRows[i]
|
const row = this.selectedMachineRows[i]
|
||||||
|
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)
|
const priceNum = Number(row && row.price)
|
||||||
if (!Number.isFinite(priceNum) || priceNum <= 0) {
|
if (!Number.isFinite(priceNum) || priceNum <= 0) {
|
||||||
const label = (row && (row.miner || row.user)) || i + 1
|
const label = (row && (row.miner || row.user)) || i + 1
|
||||||
this.$message.warning(`第${i + 1}行(机器:${label}) 价格必须大于0`)
|
this.$message.warning(`第${i + 1}行(机器:${label}) 价格必须大于0`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// 校验:逐行最大租赁天数 1-365
|
// 校验:逐行最大租赁天数 1-365
|
||||||
const rawDays = String((row && row.maxLeaseDays) ?? '')
|
const rawDays = String((row && row.maxLeaseDays) ?? '')
|
||||||
const n = Number(rawDays)
|
const n = Number(rawDays)
|
||||||
@@ -891,17 +1054,23 @@ export default {
|
|||||||
const [user, coin] = this.selectedMiner.split('|')
|
const [user, coin] = this.selectedMiner.split('|')
|
||||||
this.saving = true
|
this.saving = true
|
||||||
try {
|
try {
|
||||||
|
// 若是多结算币种,组装 priceList;否则沿用单价字段
|
||||||
const payload = {
|
const payload = {
|
||||||
productId: this.form.productId,
|
productId: this.form.productId,
|
||||||
powerDissipation: this.form.powerDissipation,
|
powerDissipation: this.form.powerDissipation,
|
||||||
theoryPower: this.form.theoryPower,
|
theoryPower: this.form.theoryPower,
|
||||||
type: this.form.type,
|
type: this.form.type,
|
||||||
unit: this.form.unit,
|
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,
|
maxLeaseDays: this.form.maxLeaseDays,
|
||||||
productMachineURDVos: this.selectedMachineRows.map(r => ({
|
productMachineURDVos: this.selectedMachineRows.map(r => ({
|
||||||
miner: r.miner,
|
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,
|
state: r.state || 0,
|
||||||
type: r.type || this.form.type,
|
type: r.type || this.form.type,
|
||||||
user: r.user,
|
user: r.user,
|
||||||
@@ -937,6 +1106,20 @@ export default {
|
|||||||
,
|
,
|
||||||
watch: {
|
watch: {
|
||||||
'form.cost': function() { this.syncCostToRows() },
|
'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.type': function() { this.updateMachineType() },
|
||||||
'form.maxLeaseDays': function() { this.syncMaxLeaseDaysToRows() },
|
'form.maxLeaseDays': function() { this.syncMaxLeaseDaysToRows() },
|
||||||
'form.powerDissipation': function() { this.syncPowerDissipationToRows() },
|
'form.powerDissipation': function() { this.syncPowerDissipationToRows() },
|
||||||
@@ -980,5 +1163,37 @@ export default {
|
|||||||
text-align: left;
|
text-align: left;
|
||||||
padding-left: 18px !important;
|
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>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -30,18 +30,32 @@
|
|||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
>
|
>
|
||||||
<!-- <el-table-column prop="id" label="ID" width="80" /> -->
|
<!-- <el-table-column prop="id" label="ID" width="80" /> -->
|
||||||
<el-table-column prop="name" label="名称" min-width="100" />
|
<el-table-column prop="name" label="名称" />
|
||||||
<el-table-column prop="coin" label="币种" width="100" />
|
<el-table-column prop="coin" label="币种" />
|
||||||
<el-table-column prop="priceRange" label="价格范围" width="150" />
|
<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">
|
<!-- <el-table-column label="算力" min-width="140">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<span>{{ scope.row.power }} {{ scope.row.unit }}</span>
|
<span>{{ scope.row.power }} {{ scope.row.unit }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column> -->
|
</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="electricityBill" label="电费" width="100" /> -->
|
||||||
<!-- <el-table-column prop="incomeRate" 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">
|
<template #default="scope">
|
||||||
<el-tag :type="scope.row.type === 1 ? 'success' : 'warning'">
|
<el-tag :type="scope.row.type === 1 ? 'success' : 'warning'">
|
||||||
{{ scope.row.type === 1 ? '算力套餐' : '挖矿机器' }}
|
{{ scope.row.type === 1 ? '算力套餐' : '挖矿机器' }}
|
||||||
@@ -49,9 +63,9 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
<el-table-column prop="saleNumber" label="已售数量" min-width="60" />
|
<el-table-column prop="saleNumber" label="已售数量" />
|
||||||
<el-table-column prop="totalMachineNumber" label="该商品总机器数量" min-width="60" />
|
<el-table-column prop="totalMachineNumber" label="该商品总机器数量" />
|
||||||
<el-table-column prop="state" label="状态" width="100">
|
<el-table-column prop="state" label="状态" >
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-tag :type="scope.row.state === 1 ? 'info' : 'success'">
|
<el-tag :type="scope.row.state === 1 ? 'info' : 'success'">
|
||||||
{{ scope.row.state === 1 ? '下架' : '上架' }}
|
{{ scope.row.state === 1 ? '下架' : '上架' }}
|
||||||
@@ -149,6 +163,17 @@ export default {
|
|||||||
this.fetchTableData()
|
this.fetchTableData()
|
||||||
},
|
},
|
||||||
methods: {
|
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() {
|
initOptions() {
|
||||||
try {
|
try {
|
||||||
@@ -372,7 +397,22 @@ export default {
|
|||||||
this.$message.warning('缺少商品ID')
|
this.$message.warning('缺少商品ID')
|
||||||
return
|
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
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -418,5 +458,10 @@ export default {
|
|||||||
::v-deep .el-form-item__content{
|
::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>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -58,13 +58,61 @@
|
|||||||
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { getMyShop } from "@/api/shops";
|
import { getMyShop } from "@/api/shops";
|
||||||
import { getChainAndList, addWalletShopConfig } from "../../api/wallet";
|
import { getChainAndList, addWalletShopConfig,getProductListForShopWalletConfig,updateProductListForShopWalletConfig } from "../../api/wallet";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "AccountShopConfig",
|
name: "AccountShopConfig",
|
||||||
@@ -116,6 +164,8 @@ export default {
|
|||||||
// },
|
// },
|
||||||
],
|
],
|
||||||
loading: false,
|
loading: false,
|
||||||
|
// 绑定前预检测弹窗数据
|
||||||
|
preCheck: { visible: false, rows: [], prices: {}, rowPrices: {} },
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
@@ -282,14 +332,159 @@ export default {
|
|||||||
return
|
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() {
|
handleReset() {
|
||||||
this.form = { chain: "", payAddress: "", payCoin: "" };
|
this.form = { chain: "", payAddress: "", payCoin: "" };
|
||||||
this.value = []
|
this.value = []
|
||||||
},
|
},
|
||||||
|
// 清空预检测中的价格输入
|
||||||
|
resetPreCheckPrices() {
|
||||||
|
try {
|
||||||
|
this.preCheck.prices = {}
|
||||||
|
this.preCheck.rowPrices = {}
|
||||||
|
} catch (e) { /* noop */ }
|
||||||
|
},
|
||||||
|
// 弹窗关闭时清空价格输入
|
||||||
|
handlePreCheckClose() {
|
||||||
|
this.resetPreCheckPrices()
|
||||||
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
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 { 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 .el-tag { border-radius: 4px; }
|
||||||
.selected-coins .placeholder { color: #c0c4cc; }
|
.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>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
import { getProductById } from '../../utils/productService'
|
import { getProductById } from '../../utils/productService'
|
||||||
import { addToCart } from '../../utils/cartManager'
|
import { addToCart } from '../../utils/cartManager'
|
||||||
import { getMachineInfo } from '../../api/products'
|
import { getMachineInfo, getPayTypes } from '../../api/products'
|
||||||
import { addCart, getGoodsList } from '../../api/shoppingCart'
|
import { addCart, getGoodsList } from '../../api/shoppingCart'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -13,6 +13,21 @@ export default {
|
|||||||
// 默认展开的行keys
|
// 默认展开的行keys
|
||||||
expandedRowKeys: [],
|
expandedRowKeys: [],
|
||||||
selectedMap: {},
|
selectedMap: {},
|
||||||
|
// 新接口:单层矿机列表 & 支付方式
|
||||||
|
machineList: [],
|
||||||
|
paymentMethodList: [],
|
||||||
|
// 筛选状态
|
||||||
|
selectedPayKey: null,
|
||||||
|
filters: {
|
||||||
|
chain: '',
|
||||||
|
coin: '',
|
||||||
|
minPrice: null,
|
||||||
|
maxPrice: null,
|
||||||
|
minPower: null,
|
||||||
|
maxPower: null,
|
||||||
|
minPowerDissipation: null,
|
||||||
|
maxPowerDissipation: null
|
||||||
|
},
|
||||||
params: {
|
params: {
|
||||||
id: "",
|
id: "",
|
||||||
|
|
||||||
@@ -120,6 +135,7 @@ export default {
|
|||||||
this.expandedRowKeys = [this.productListData[0].id]
|
this.expandedRowKeys = [this.productListData[0].id]
|
||||||
}
|
}
|
||||||
this.fetchGetMachineInfo(this.params)
|
this.fetchGetMachineInfo(this.params)
|
||||||
|
this.fetchPayTypes()
|
||||||
} else {
|
} else {
|
||||||
this.$message.error('商品不存在')
|
this.$message.error('商品不存在')
|
||||||
this.product = false
|
this.product = false
|
||||||
@@ -127,6 +143,41 @@ export default {
|
|||||||
this.fetchGetGoodsList()
|
this.fetchGetGoodsList()
|
||||||
},
|
},
|
||||||
methods: {
|
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) {
|
async fetchGetMachineInfo(params) {
|
||||||
this.productDetailLoading = true
|
this.productDetailLoading = true
|
||||||
@@ -134,31 +185,26 @@ export default {
|
|||||||
console.log(res)
|
console.log(res)
|
||||||
if (res && res.code === 200) {
|
if (res && res.code === 200) {
|
||||||
console.log(res.data, 'res.rows');
|
console.log(res.data, 'res.rows');
|
||||||
this.paymentMethodList = res.data.payConfigList || []
|
// 新数据结构:机器为扁平 rows 列表;仅当后端返回有效支付方式时才覆盖,避免清空 getPayTypes 的结果
|
||||||
const list =res.data.machineRangeInfoList || []
|
try {
|
||||||
const withKeys = list.map((group, idx) => {
|
const payList = res && res.data && res.data.payConfigList
|
||||||
const fallbackId = `grp-${idx}`
|
if (Array.isArray(payList) && payList.length) {
|
||||||
const groupId = group.id || group.onlyKey || (group.productMachineRangeGroupDto && group.productMachineRangeGroupDto.id)
|
this.paymentMethodList = payList
|
||||||
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]
|
|
||||||
}
|
}
|
||||||
// 产品机器加载完成后,依据购物车集合执行一次本地禁用与勾选
|
} 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.$nextTick(() => {
|
||||||
this.machinesLoaded = true
|
this.machinesLoaded = true
|
||||||
// 已取消与购物车对比:不再自动禁用或勾选
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -503,9 +549,12 @@ export default {
|
|||||||
// 取消所有商品勾选(内层表格的自定义 checkbox)
|
// 取消所有商品勾选(内层表格的自定义 checkbox)
|
||||||
clearAllSelections() {
|
clearAllSelections() {
|
||||||
try {
|
try {
|
||||||
// 清空选中映射
|
// 清空选中映射(遗留字段)
|
||||||
this.selectedMap = {}
|
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 : []
|
const groups = Array.isArray(this.productListData) ? this.productListData : []
|
||||||
groups.forEach(g => {
|
groups.forEach(g => {
|
||||||
const list = Array.isArray(g.productMachines) ? g.productMachines : []
|
const list = Array.isArray(g.productMachines) ? g.productMachines : []
|
||||||
|
|||||||
@@ -16,12 +16,13 @@
|
|||||||
class="pay-item"
|
class="pay-item"
|
||||||
:aria-label="`支付方式: ${item.payChain}`"
|
:aria-label="`支付方式: ${item.payChain}`"
|
||||||
>
|
>
|
||||||
|
|
||||||
<el-tooltip :content="formatPayTooltip(item)" placement="top" :open-delay="80">
|
<el-tooltip :content="formatPayTooltip(item)" placement="top" :open-delay="80">
|
||||||
<img
|
<img
|
||||||
class="pay-icon"
|
class="pay-icon"
|
||||||
:src="item.payCoinImage"
|
:src="getPayImageUrl(item)"
|
||||||
:alt="`${item.payChain} 支付`"
|
:alt="`${(item.payChain || '').toUpperCase()} ${(item.payCoin || '').toUpperCase()}`.trim()"
|
||||||
:title="item.payChain"
|
:title="formatPayTooltip(item)"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
role="img"
|
role="img"
|
||||||
@keydown.enter.prevent="handlePayIconKeyDown(item)"
|
@keydown.enter.prevent="handlePayIconKeyDown(item)"
|
||||||
@@ -31,36 +32,98 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</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">
|
<section class="productList">
|
||||||
<!-- 产品列表(可展开) -->
|
<!-- 单层产品列表 -->
|
||||||
<el-table
|
<el-table
|
||||||
ref="seriesTable"
|
ref="machineTable"
|
||||||
class="series-table"
|
class="series-table"
|
||||||
:data="productListData"
|
:data="machineList"
|
||||||
row-key="id"
|
row-key="id"
|
||||||
:expand-row-keys="expandedRowKeys"
|
:row-class-name="handleGetRowClass"
|
||||||
@expand-change="handleExpandChange"
|
|
||||||
@row-click="handleSeriesRowClick"
|
|
||||||
:row-class-name="handleGetSeriesRowClassName"
|
|
||||||
:header-cell-style="{ textAlign: 'left' }"
|
:header-cell-style="{ textAlign: 'left' }"
|
||||||
:cell-style="{ textAlign: 'left' }"
|
:cell-style="{ textAlign: 'left' }"
|
||||||
style="width: 100%"
|
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">
|
<el-table-column width="46">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-checkbox
|
<el-checkbox
|
||||||
v-model="scope.row._selected"
|
v-model="scope.row._selected"
|
||||||
:disabled="scope.row.saleState === 1 || scope.row.saleState === 2"
|
:disabled="scope.row.saleState === 1 || scope.row.saleState === 2"
|
||||||
:title="(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)"
|
@change="checked => handleManualSelectFlat(scope.row, checked)"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<!-- 列宽精简,避免横向滚动 -->
|
|
||||||
|
|
||||||
|
|
||||||
<el-table-column prop="theoryPower" label="理论算力" min-width="160" header-align="left" align="left" show-overflow-tooltip>
|
<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>
|
<template #default="scope">{{ scope.row.theoryPower }} {{ scope.row.unit }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -68,14 +131,18 @@
|
|||||||
<el-table-column label="实际算力" min-width="160" header-align="left" align="left" show-overflow-tooltip>
|
<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>
|
<template #default="scope">{{ scope.row.computingPower }} {{ scope.row.unit }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
<el-table-column prop="powerDissipation" label="功耗(kw/h)" min-width="140" header-align="left" align="left" />
|
<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="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>
|
<el-table-column prop="theoryIncome" width="120" 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>
|
<template #header>
|
||||||
|
单机理论收入(每日)
|
||||||
|
<span v-if="getFirstCoinSymbol()">({{ getFirstCoinSymbol() }})</span>
|
||||||
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="theoryUsdtIncome" label="单机理论收入(每日/USDT)" min-width="170" header-align="left" align="left" />
|
<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 prop="type" label="矿机型号" header-align="left" align="left" min-width="120" />
|
||||||
<el-table-column label="最大可租赁(天)" min-width="140" header-align="left" align="left">
|
<el-table-column label="最大可租赁(天)" min-width="140" header-align="left" align="left">
|
||||||
<template #default="scope">{{ getRowMaxLeaseDays(scope.row) }}</template>
|
<template #default="scope">{{ getRowMaxLeaseDays(scope.row) }}</template>
|
||||||
@@ -95,6 +162,12 @@
|
|||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</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">
|
<el-table-column prop="saleState" label="售出状态" header-align="left" align="left" min-width="110">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-tag :type="scope.row.saleState === 0 ? 'info' : (scope.row.saleState === 1 ? 'danger' : 'warning')">
|
<el-tag :type="scope.row.saleState === 0 ? 'info' : (scope.row.saleState === 1 ? 'danger' : 'warning')">
|
||||||
@@ -103,28 +176,6 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
</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>
|
|
||||||
|
|
||||||
<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>
|
|
||||||
<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>
|
|
||||||
<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>
|
|
||||||
<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>
|
|
||||||
|
|
||||||
</el-table>
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<div style="margin: 18px; text-align: right;">
|
<div style="margin: 18px; text-align: right;">
|
||||||
@@ -146,7 +197,10 @@
|
|||||||
<el-table-column label="租赁天数(天)" header-align="left" align="left">
|
<el-table-column label="租赁天数(天)" header-align="left" align="left">
|
||||||
<template #default="scope">{{ Number(scope.row.leaseTime || 1) }}</template>
|
<template #default="scope">{{ Number(scope.row.leaseTime || 1) }}</template>
|
||||||
</el-table-column>
|
</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>
|
<template #default="scope"><span class="price-strong">{{ scope.row.price }}</span></template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
@@ -187,6 +241,87 @@ export default {
|
|||||||
if (n > 365) return 365
|
if (n > 365) return 365
|
||||||
return Math.floor(n)
|
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],并取整
|
* 限制并校验租赁天数:区间 [1, max],并取整
|
||||||
*/
|
*/
|
||||||
@@ -232,6 +367,52 @@ export default {
|
|||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.error('handlePayIconKeyDown error:', err);
|
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;
|
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 {
|
.pay-icon:hover {
|
||||||
transform: translateY(-1px);
|
transform: translateY(-1px);
|
||||||
}
|
}
|
||||||
@@ -409,6 +593,76 @@ export default {
|
|||||||
box-shadow: 0 0 0 3px rgba(25, 118, 210, 0.2);
|
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) {
|
:deep(.series-clickable-row) {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|||||||
@@ -60,9 +60,17 @@
|
|||||||
<h4>商品: {{ product.name }}</h4>
|
<h4>商品: {{ product.name }}</h4>
|
||||||
<p style="font-size: 16px;margin-top: 10px;font-weight: bold;">算法: {{ product.algorithm }}</p>
|
<p style="font-size: 16px;margin-top: 10px;font-weight: bold;">算法: {{ product.algorithm }}</p>
|
||||||
<div class="product-footer">
|
<div class="product-footer">
|
||||||
<div class="price-wrap">
|
<div class="paytypes">
|
||||||
<span class="product-price">价格: {{ formatPriceRange(product.priceRange) }}</span>
|
<span class="paytypes-label">支付方式:</span>
|
||||||
<span class="unit">USDT</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>
|
</div>
|
||||||
<span class="product-sold" aria-label="已售数量">已售:{{ product && product.saleNumber != null ? product.saleNumber : 0 }}</span>
|
<span class="product-sold" aria-label="已售数量">已售:{{ product && product.saleNumber != null ? product.saleNumber : 0 }}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -89,6 +97,19 @@ export default {
|
|||||||
|
|
||||||
mounted() {},
|
mounted() {},
|
||||||
methods: {
|
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; }
|
.price-wrap { display: inline-flex; align-items: baseline; gap: 6px; }
|
||||||
.unit { color: #999; font-size: 12px; }
|
.unit { color: #999; font-size: 12px; }
|
||||||
.product-sold { color: #64748b; 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 {
|
.add-cart-btn {
|
||||||
background: #42b983;
|
background: #42b983;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
|||||||
Reference in New Issue
Block a user