最新需求修改中
This commit is contained in:
@@ -28,6 +28,7 @@
|
||||
v-model="form.coinsInput"
|
||||
placeholder="例如:USDT, BTC, ETH"
|
||||
style="width: 50%;"
|
||||
@input="handleCoinsInput"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="算法(多个用逗号隔开)" prop="algorithmsInput" :required="form.machineCategory === 'ASIC'">
|
||||
@@ -35,13 +36,14 @@
|
||||
v-model="form.algorithmsInput"
|
||||
placeholder="例如:SHA-256, ETHASH"
|
||||
style="width: 50%;"
|
||||
@input="handleAlgorithmsInput"
|
||||
/>
|
||||
</el-form-item>
|
||||
<div style="text-align:left; color:#909399; font-size:12px; margin:-6px 0 10px 160px;">
|
||||
输入多个用逗号隔开
|
||||
</div>
|
||||
|
||||
<el-form-item label="矿机型号">
|
||||
<el-form-item label="矿机型号" prop="type" :required="true">
|
||||
<el-input style="width: 50%;" v-model="form.type" placeholder="示例:龍珠" :maxlength="20" @input="handleTypeInput" />
|
||||
</el-form-item>
|
||||
<el-form-item label="理论算力" prop="theoryPower">
|
||||
@@ -84,7 +86,11 @@
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="统一售价" prop="cost" :required="true">
|
||||
<el-form-item
|
||||
label="统一售价"
|
||||
:prop="(payTypeDefs && payTypeDefs.length) ? 'costMap' : 'cost'"
|
||||
:required="true"
|
||||
>
|
||||
<span slot="label">统一售价</span>
|
||||
<!-- 若商品定义了多个结算币种,则按链-币种动态生成多个售价输入;否则回退为旧的 USDT 单价 -->
|
||||
<div v-if="payTypeDefs && payTypeDefs.length" class="cost-multi">
|
||||
@@ -136,15 +142,33 @@
|
||||
<el-dialog
|
||||
title="请确认上架信息"
|
||||
:visible.sync="confirmVisible"
|
||||
width="400px"
|
||||
width="560px"
|
||||
>
|
||||
<div>
|
||||
<p>请仔细确认已选择机器列表、价格及相关参数定义。</p>
|
||||
<p style="text-align: left;">机器上架后,一经售出,在机器出售期间不能修改价格及机器参数。</p>
|
||||
<div style="text-align:left;line-height:1.9;">
|
||||
<div>币种:<b>{{ confirmData.coin }}</b></div>
|
||||
<div>算法:<b>{{ confirmData.algorithm }}</b></div>
|
||||
<div>最大租赁天数:<b>{{ confirmData.maxLeaseDays || '-' }}</b></div>
|
||||
<div>出售机器数量:<b>{{ confirmData.saleNumbers || '-' }}</b></div>
|
||||
<div style="margin-top:8px;">售价:</div>
|
||||
<el-table
|
||||
:data="confirmData.priceList"
|
||||
border
|
||||
size="mini"
|
||||
style="width:100%;"
|
||||
>
|
||||
<el-table-column prop="chain" label="链" width="120" />
|
||||
<el-table-column prop="coin" label="币种" width="120" />
|
||||
<el-table-column label="价格">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.price }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<p style="color:#666;margin-top:12px;">请仔细确认以上参数无误后提交。</p>
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="confirmVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="saving" @click="doSubmit">确认上架已选择机器</el-button>
|
||||
<el-button type="primary" :loading="saving" @click="doSubmit">确认提交</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
<!-- GPU 引导弹窗 -->
|
||||
@@ -171,8 +195,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { addSingleOrBatchMachine } from '../../api/machine'
|
||||
|
||||
import { addSingleOrBatchMachine ,downloadClient,addAsicMachine} from '../../api/machine'
|
||||
import { getPayTypes } from '../../api/products'
|
||||
export default {
|
||||
name: 'AccountProductMachineAdd',
|
||||
data() {
|
||||
@@ -197,14 +221,47 @@ export default {
|
||||
maxLeaseDays: ''
|
||||
},
|
||||
confirmVisible: false,
|
||||
confirmData: {
|
||||
coin: '',
|
||||
algorithm: '',
|
||||
maxLeaseDays: '',
|
||||
saleNumbers: '',
|
||||
priceList: []
|
||||
},
|
||||
rules: {
|
||||
productName: [ { required: true, message: '商品名称不能为空', trigger: 'change' } ],
|
||||
type: [
|
||||
{ required: true, message: '矿机型号不能为空', trigger: 'blur' },
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
const s = String(value || '')
|
||||
// 不允许全是空格
|
||||
if (s && s.trim().length === 0) {
|
||||
callback(new Error('矿机型号不能全是空格'))
|
||||
return
|
||||
}
|
||||
callback()
|
||||
},
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
coinsInput: [
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
if (String(this.form.machineCategory).toUpperCase() !== 'ASIC') { callback(); return }
|
||||
const s = String(value || '').trim()
|
||||
if (!s) { callback(new Error('请输入币种,多个用逗号隔开')); return }
|
||||
// 禁止汉字,且仅允许英数字与逗号/空格/连字符
|
||||
if (/[\u4e00-\u9fa5]/.test(s)) { callback(new Error('币种不允许输入汉字')); return }
|
||||
// 逐项校验(英文、数字,长度1-10)
|
||||
const tokens = s.split(/[,\s,、]+/).map(i => i.trim()).filter(Boolean)
|
||||
const pattern = /^[A-Za-z0-9]{1,10}$/
|
||||
for (let i = 0; i < tokens.length; i += 1) {
|
||||
if (!pattern.test(tokens[i])) {
|
||||
callback(new Error(`币种“${tokens[i]}”格式非法,仅允许字母或数字`))
|
||||
return
|
||||
}
|
||||
}
|
||||
callback()
|
||||
},
|
||||
trigger: 'blur'
|
||||
@@ -216,6 +273,16 @@ export default {
|
||||
if (String(this.form.machineCategory).toUpperCase() !== 'ASIC') { callback(); return }
|
||||
const s = String(value || '').trim()
|
||||
if (!s) { callback(new Error('请输入算法,多个用逗号隔开')); return }
|
||||
if (/[\u4e00-\u9fa5]/.test(s)) { callback(new Error('算法不允许输入汉字')); return }
|
||||
// 逐项校验(字母/数字/连字符,2-20)
|
||||
const tokens = s.split(/[,\s,、]+/).map(i => i.trim()).filter(Boolean)
|
||||
const pattern = /^[A-Za-z0-9-]{2,20}$/
|
||||
for (let i = 0; i < tokens.length; i += 1) {
|
||||
if (!pattern.test(tokens[i])) {
|
||||
callback(new Error(`算法“${tokens[i]}”格式非法,仅允许字母、数字或“-”`))
|
||||
return
|
||||
}
|
||||
}
|
||||
callback()
|
||||
},
|
||||
trigger: 'blur'
|
||||
@@ -400,6 +467,8 @@ export default {
|
||||
clientDownloadUrl: process.env.VUE_APP_GPU_CLIENT_URL || '',
|
||||
/** 是否点击过下载客户端(用于控制“已启动客户端”按钮禁用态) */
|
||||
hasDownloadedClient: false,
|
||||
/** 支持的支付方式定义(用于动态渲染统一售价输入组) */
|
||||
payTypeDefs: [],
|
||||
params:{
|
||||
cost:353400,
|
||||
powerDissipation:0.01,
|
||||
@@ -435,8 +504,88 @@ export default {
|
||||
if (this.rules && this.rules.cost) {
|
||||
this.$set(this.rules, 'cost', [{ validator: this.validateCost, trigger: 'blur' }])
|
||||
}
|
||||
// 多币种价格专用校验
|
||||
this.$set(this.rules, 'costMap', [{ validator: this.validateCostMap, trigger: 'blur' }])
|
||||
|
||||
this.getPayTypes()
|
||||
},
|
||||
methods: {
|
||||
/** 实时过滤币种输入中的中文字符(仅保留英文/数字/分隔符) */
|
||||
handleCoinsInput() {
|
||||
let v = String(this.form.coinsInput || '')
|
||||
// 去除中文
|
||||
v = v.replace(/[\u4e00-\u9fa5]/g, '')
|
||||
this.form.coinsInput = v
|
||||
},
|
||||
/** 实时过滤算法输入中的中文字符(仅保留英文/数字/连字符/分隔符) */
|
||||
handleAlgorithmsInput() {
|
||||
let v = String(this.form.algorithmsInput || '')
|
||||
v = v.replace(/[\u4e00-\u9fa5]/g, '')
|
||||
this.form.algorithmsInput = v
|
||||
},
|
||||
/** 将输入按中英文逗号、空格分割,去空,统一英文逗号连接;可选:统一大写 */
|
||||
normalizeCsv(input, upper = true) {
|
||||
const arr = String(input || '')
|
||||
.split(/[,\s,、]+/)
|
||||
.map(s => s.trim())
|
||||
.filter(Boolean)
|
||||
const list = upper ? arr.map(s => s.toUpperCase()) : arr
|
||||
return list.join(',')
|
||||
},
|
||||
/** 从 payTypeDefs 与 costMap 生成价格列表 */
|
||||
buildPriceList() {
|
||||
const list = []
|
||||
const defs = Array.isArray(this.payTypeDefs) ? this.payTypeDefs : []
|
||||
defs.forEach(d => {
|
||||
const key = d.key
|
||||
const priceRaw = this.form.costMap ? this.form.costMap[key] : ''
|
||||
const priceNum = Number(priceRaw)
|
||||
// 允许为空/非数字则不加入
|
||||
if (!Number.isFinite(priceNum) || priceNum <= 0) return
|
||||
list.push({
|
||||
chain: d.chain,
|
||||
coin: d.coin,
|
||||
price: priceNum
|
||||
})
|
||||
})
|
||||
return list
|
||||
},
|
||||
async getPayTypes() {
|
||||
try {
|
||||
const res = await getPayTypes()
|
||||
// 期望结构:{ code:200, data:[ { payChain, payCoin, payCoinImage } ] }
|
||||
if (res && (res.code === 0 || res.code === 200)) {
|
||||
const list = Array.isArray(res.data) ? res.data : []
|
||||
const defs = []
|
||||
const seen = new Set()
|
||||
list.forEach(it => {
|
||||
const chain = String(it && it.payChain ? it.payChain : '').toUpperCase()
|
||||
const coin = String(it && it.payCoin ? it.payCoin : '').toUpperCase()
|
||||
if (!chain && !coin) return
|
||||
const key = [chain, coin].filter(Boolean).join('-')
|
||||
if (seen.has(key)) return
|
||||
seen.add(key)
|
||||
defs.push({
|
||||
chain,
|
||||
coin,
|
||||
key,
|
||||
label: key,
|
||||
image: it && it.payCoinImage ? String(it.payCoinImage) : ''
|
||||
})
|
||||
})
|
||||
// 根据接口结果渲染“统一售价”输入组
|
||||
this.payTypeDefs = defs
|
||||
const nextCostMap = {}
|
||||
this.payTypeDefs.forEach(d => {
|
||||
// 保留已输入的数值;否则置空
|
||||
nextCostMap[d.key] = (this.form.costMap && this.form.costMap[d.key]) || ''
|
||||
})
|
||||
this.form.costMap = nextCostMap
|
||||
}
|
||||
} catch (e) {
|
||||
// 忽略错误,维持当前 payTypeDefs
|
||||
}
|
||||
},
|
||||
/**
|
||||
* ASIC 模式:出售机器数量输入,仅允许 0-9999 的整数
|
||||
*/
|
||||
@@ -479,24 +628,35 @@ export default {
|
||||
* 下载 GPU 客户端
|
||||
*/
|
||||
handleDownloadClient() {
|
||||
const url = (this.clientDownloadUrl || '').trim()
|
||||
if (!url) {
|
||||
this.$message.warning('暂无客户端下载地址,请联系管理员')
|
||||
return
|
||||
}
|
||||
try {
|
||||
window.open(url, '_blank')
|
||||
} catch (e) {
|
||||
this.$message.error('打开下载链接失败,请稍后重试')
|
||||
}
|
||||
// 点击下载后即可启用“已启动客户端”按钮
|
||||
this.hasDownloadedClient = true
|
||||
// 走后端接口下载客户端程序
|
||||
downloadClient()
|
||||
.then((res) => {
|
||||
// 处理 blob 下载(兼容封装返回 data 或直接返回 Blob)
|
||||
const data = (res && res.data) ? res.data : res
|
||||
const blob = data instanceof Blob ? data : new Blob([data], { type: 'application/octet-stream' })
|
||||
const url = window.URL.createObjectURL(blob)
|
||||
const a = document.createElement('a')
|
||||
a.style.display = 'none'
|
||||
a.href = url
|
||||
// 默认文件名(可由后端 Content-Disposition 提供,这里简单兜底)
|
||||
a.download = 'gpu-client.zip'
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
document.body.removeChild(a)
|
||||
window.URL.revokeObjectURL(url)
|
||||
this.$message.success('客户端下载已开始')
|
||||
this.hasDownloadedClient = true
|
||||
})
|
||||
.catch(() => {
|
||||
this.$message.error('下载失败,请稍后重试')
|
||||
})
|
||||
},
|
||||
/**
|
||||
* GPU 客户端已启动:跳转至商品列表
|
||||
*/
|
||||
handleGpuClientStarted() {
|
||||
this.$router.push('/productList')
|
||||
// 跳转到“个人中心-商品列表”页面
|
||||
this.$router.push('/account/products')
|
||||
},
|
||||
/**
|
||||
* GPU 弹窗关闭:自动恢复为 ASIC
|
||||
@@ -507,9 +667,9 @@ export default {
|
||||
},
|
||||
/** 统一售价校验:多结算币种时跳过,单价时按 USDT 校验 */
|
||||
validateCost(rule, value, callback) {
|
||||
// 多支付方式:逐个校验 costMap
|
||||
if (Array.isArray(this.payTypeDefs) && this.payTypeDefs.length > 0) {
|
||||
callback()
|
||||
return
|
||||
return this.validateCostMap(rule, value, callback)
|
||||
}
|
||||
const str = String(value || '')
|
||||
if (!str) { callback(new Error('请填写机器成本(USDT)')); return }
|
||||
@@ -518,6 +678,25 @@ export default {
|
||||
if (Number(str) <= 0) { callback(new Error('成本必须大于 0')); return }
|
||||
callback()
|
||||
},
|
||||
// 多支付方式下的价格校验:要求每个支付方式都需填写有效价格
|
||||
validateCostMap(rule, value, callback) {
|
||||
try {
|
||||
const defs = Array.isArray(this.payTypeDefs) ? this.payTypeDefs : []
|
||||
if (!defs.length) { callback(); return }
|
||||
const pattern = /^\d{1,12}(\.\d{1,2})?$/
|
||||
for (let i = 0; i < defs.length; i += 1) {
|
||||
const d = defs[i]
|
||||
const key = d.key
|
||||
const v = this.form && this.form.costMap ? String(this.form.costMap[key] || '') : ''
|
||||
if (!v) { callback(new Error(`请填写 ${d.label} 的价格`)); return }
|
||||
if (!pattern.test(v)) { callback(new Error(`${d.label} 价格整数最多12位,小数最多2位`)); return }
|
||||
if (Number(v) <= 0) { callback(new Error(`${d.label} 价格必须大于0`)); return }
|
||||
}
|
||||
callback()
|
||||
} catch (e) {
|
||||
callback(new Error('价格填写有误,请检查'))
|
||||
}
|
||||
},
|
||||
/** 解析路由参数中的支付方式,生成标准定义 */
|
||||
initPayTypesFromRoute() {
|
||||
this.payTypeDefs = []
|
||||
@@ -895,10 +1074,10 @@ export default {
|
||||
} catch (e) {
|
||||
return
|
||||
}
|
||||
if (!this.form.productId) {
|
||||
this.$message.warning('缺少商品ID')
|
||||
return
|
||||
}
|
||||
// if (!this.form.productId) {
|
||||
// this.$message.warning('缺少商品ID')
|
||||
// return
|
||||
// }
|
||||
// 现在统一按出售数量提交(GPU 模式不在本页提交)
|
||||
{
|
||||
// ASIC:校验出售机器数量(允许 0-9999;为 0 则提示)
|
||||
@@ -929,47 +1108,41 @@ export default {
|
||||
return
|
||||
}
|
||||
// 统一售价与最大租赁天数已在表单级校验中处理,无需逐机校验
|
||||
// 通过所有预校验后,弹出确认框
|
||||
// 组装确认数据并弹框
|
||||
const coinStr = this.normalizeCsv(this.form.coinsInput || this.form.coin, true)
|
||||
const algoStr = this.normalizeCsv(this.form.algorithmsInput, true)
|
||||
this.confirmData = {
|
||||
coin: coinStr || '-',
|
||||
algorithm: algoStr || '-',
|
||||
maxLeaseDays: this.form.maxLeaseDays,
|
||||
saleNumbers: this.form.sellCount,
|
||||
priceList: this.buildPriceList()
|
||||
}
|
||||
this.confirmVisible = true
|
||||
}
|
||||
,
|
||||
async doSubmit() {
|
||||
const [user, coin] = ['','']
|
||||
this.saving = true
|
||||
try {
|
||||
// 若是多结算币种,组装 priceList;否则沿用单价字段
|
||||
const count = Number(this.form.sellCount)
|
||||
const productMachineURDVos = Array.from({ length: count }).map(() => ({
|
||||
price: this.payTypeDefs && this.payTypeDefs.length ? undefined : (Number(this.form.cost) || 0),
|
||||
priceList: this.payTypeDefs && this.payTypeDefs.length ? this.payTypeDefs.map(d => ({
|
||||
chain: d.chain,
|
||||
coin: d.coin,
|
||||
price: Number(this.form.costMap && this.form.costMap[d.key]) || 0
|
||||
})) : undefined,
|
||||
state: 0,
|
||||
type: this.form.type,
|
||||
// 统一售卖新增接口参数
|
||||
const payload = {
|
||||
// 逗号分隔(中英文逗号都兼容),统一为英文逗号并大写
|
||||
coin: this.normalizeCsv(this.form.coinsInput || this.form.coin, true),
|
||||
algorithm: this.normalizeCsv(this.form.algorithmsInput, true),
|
||||
maxLeaseDays: Number(this.form.maxLeaseDays) || 0,
|
||||
name: this.form.type,
|
||||
powerDissipation: Number(this.form.powerDissipation) || 0,
|
||||
theoryPower: Number(this.form.theoryPower) || 0,
|
||||
unit: this.form.unit
|
||||
}))
|
||||
const payload = {
|
||||
productId: this.form.productId,
|
||||
// 逗号分隔:后台若需要数组可在服务端拆分
|
||||
coin: (this.form.coinsInput || this.form.coin || '').toString(),
|
||||
algorithm: (this.form.algorithmsInput || '').toString(),
|
||||
powerDissipation: this.form.powerDissipation,
|
||||
theoryPower: this.form.theoryPower,
|
||||
type: this.form.type,
|
||||
unit: this.form.unit,
|
||||
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
|
||||
saleNumbers: Number(this.form.sellCount) || 0,
|
||||
priceList: this.buildPriceList()
|
||||
}
|
||||
// 过滤空价目
|
||||
payload.priceList = (payload.priceList || []).filter(p => Number(p.price) > 0)
|
||||
|
||||
console.log(payload,"请求参数")
|
||||
|
||||
const res = await addSingleOrBatchMachine(payload)
|
||||
const res = await addAsicMachine(payload)
|
||||
if (res && (res.code === 0 || res.code === 200)) {
|
||||
|
||||
this.$message({
|
||||
@@ -979,7 +1152,7 @@ export default {
|
||||
type: 'success'
|
||||
})
|
||||
this.confirmVisible = false
|
||||
this.$router.back()
|
||||
this.$router.push('/account/products')
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('添加出售机器失败', e)
|
||||
|
||||
Reference in New Issue
Block a user