周五固定更新
This commit is contained in:
@@ -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>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user