每周更新

This commit is contained in:
2026-01-30 16:20:22 +08:00
parent f34f36b507
commit 172c94baad
14 changed files with 541 additions and 154 deletions

View File

@@ -45,6 +45,7 @@
<div class="desc">{{ shop.description || '这家店还没有描述~' }}</div>
<div class="meta">
<span>手续费率{{ formatFeeRate(shop.feeRate) }}</span>
<span>网络手续费{{ shopNetworkFeeText }}</span>
</div>
<div class="actions">
<el-button size="small" type="primary" @click="handleOpenEdit">修改店铺</el-button>
@@ -218,9 +219,27 @@
<label class="label">手续费比例</label>
<el-input
v-model="editForm.feeRate"
placeholder="比例区间 0.01 - 0.1 之间最多6位小数"
class="fee-rate-input"
placeholder="1 - 10"
@input="handleEditFeeRateInput"
/>
>
<template slot="append">%</template>
</el-input>
</div>
<div class="row">
<label class="label">专用网络</label>
<div class="edit-network">
<div class="edit-network-line">
<span class="edit-network-label">是否开启</span>
<el-switch v-model="editForm.isOpen" />
<span class="edit-network-fee">
网络手续费
<span class="edit-fee-value" :class="{ on: editForm.isOpen }">{{ editNetworkFeeText }}</span>
</span>
</div>
<div class="edit-network-hint">专用网络会增加2%的手续费建议无法直连矿池的用户开启</div>
</div>
</div>
<div class="row">
<label class="label">谷歌验证码</label>
@@ -286,11 +305,13 @@ export default {
image: '',
description: '',
feeRate: '',
networkFee: 0,
isOpen: false,
del: true,
state: 0
},
visibleEdit: false,
editForm: { id: '', name: '', image: '', description: '', feeRate: '', gCode: '' },
editForm: { id: '', name: '', image: '', description: '', feeRate: '', gCode: '', isOpen: false, networkFee: 0 },
// 店铺配置列表
shopConfigs: [],
visibleConfigEdit: false,
@@ -335,6 +356,17 @@ export default {
if (this.shop.state === 2) return 'info'
return 'info'
},
shopNetworkFeeText() {
const fee = Number(this.shop.networkFee)
const feeValid = Number.isFinite(fee) && fee > 0
const isOpen = this.normalizeIsOpen(this.shop.isOpen)
if (!isOpen) return '0'
const rate = feeValid ? fee : 0
if (!rate) return '0'
const percent = rate * 100
const fixed = percent.toFixed(6)
return `${fixed.replace(/\.?0+$/, '')}%`
},
hasShop() {
return !!(this.shop && Number(this.shop.id) > 0)
},
@@ -352,6 +384,9 @@ export default {
const map = new Map((this.editCoinOptions || []).map(o => [String(o.value), String(o.label).toUpperCase()]))
return (this.configForm.payCoins || []).map(v => map.get(String(v)) || String(v).toUpperCase())
},
editNetworkFeeText() {
return this.editForm.isOpen ? '2%' : '0'
},
/* 提现弹窗标题:如 USDT提现 */
withdrawDialogTitle() {
const sym = String((this.currentWithdrawRow && this.currentWithdrawRow.payCoin) || '').toUpperCase() || ''
@@ -379,6 +414,34 @@ export default {
this.fetchMyShop()
},
methods: {
/**
* 专用网络状态解析(兼容 boolean/number/string
*/
normalizeIsOpen(value) {
if (value === true || value === 1) return true
if (typeof value === 'string') {
const v = value.trim().toLowerCase()
return v === 'true' || v === '1'
}
return false
},
/**
* 统一解析店铺数据结构(兼容 data/data.list/array/string
*/
normalizeShopData(raw) {
let data = raw && raw.data != null ? raw.data : raw
if (Array.isArray(data)) return data[0] || {}
if (typeof data === 'string') {
try {
const parsed = JSON.parse(data)
if (Array.isArray(parsed)) return parsed[0] || {}
return parsed || {}
} catch (e) {
return {}
}
}
return data || {}
},
/**
* 修改店铺谷歌验证码输入仅数字最多6位
*/
@@ -669,8 +732,9 @@ export default {
if (value === null || value === undefined || value === '') return '-'
const num = Number(value)
if (!Number.isFinite(num)) return '-'
const fixed = num.toFixed(6)
return fixed.replace(/\.?0+$/, '')
const percent = num * 100
const fixed = percent.toFixed(6)
return `${fixed.replace(/\.?0+$/, '')}%`
},
/**
* 修改弹窗 - 手续费输入允许一个小数点最多6位小数允许尾随点
@@ -709,6 +773,9 @@ export default {
name: '',
image: '',
description: '',
feeRate: '',
networkFee: 0,
isOpen: false,
del: true,
state: 0
}
@@ -721,17 +788,23 @@ export default {
const res = await getMyShop()
// 预期格式:{"code":0,"data":{"del":true,"description":"","id":0,"image":"","name":"","state":0},"msg":""}
if (res && (res.code === 0 || res.code === 200) && res.data) {
const shopData = this.normalizeShopData(res.data)
const feeRaw = Number(shopData.networkFee)
const feeValid = Number.isFinite(feeRaw) && feeRaw > 0
const isOpen = this.normalizeIsOpen(shopData.isOpen) || feeValid
this.shop = {
id: res.data.id,
name: res.data.name,
image: res.data.image,
description: res.data.description,
feeRate: res.data.feeRate,
del: !!res.data.del,
state: Number(res.data.state || 0)
id: shopData.id,
name: shopData.name,
image: shopData.image,
description: shopData.description,
feeRate: shopData.feeRate,
networkFee: feeValid ? feeRaw : 0,
isOpen,
del: !!shopData.del,
state: Number(shopData.state || 0)
}
// 同步加载钱包绑定
this.fetchShopConfigs(res.data.id)
this.fetchShopConfigs(shopData.id)
} else {
// 当接口返回错误或没有数据时,重置店铺状态
this.resetShopState()
@@ -940,38 +1013,54 @@ export default {
this.visibleEdit = true
// 查询最新店铺详情
const res = await queryShop({ id: this.shop.id })
if (res && (res.code === 0 || res.code === 200) && res.data) {
if (res && (res.code === 0 || res.code === 200)) {
const shopData = this.normalizeShopData(res.data)
const feeRaw = Number(shopData.networkFee)
const feeValid = Number.isFinite(feeRaw) && feeRaw > 0
const isOpen = this.normalizeIsOpen(shopData.isOpen) || feeValid
const feeRateNum = Number(shopData.feeRate)
const feeRatePercent = Number.isFinite(feeRateNum) ? (feeRateNum * 100) : ''
this.editForm = {
id: res.data.id,
name: res.data.name,
image: res.data.image,
description: res.data.description,
feeRate: res.data.feeRate,
gCode: ''
id: shopData.id,
name: shopData.name,
image: shopData.image,
description: shopData.description,
feeRate: feeRatePercent === '' ? '' : String(feeRatePercent),
gCode: '',
isOpen,
networkFee: feeValid ? feeRaw : 0
}
} else {
// 回退到当前展示的数据
const fallbackRateNum = Number(this.shop.feeRate)
const fallbackRatePercent = Number.isFinite(fallbackRateNum) ? (fallbackRateNum * 100) : ''
this.editForm = {
id: this.shop.id,
name: this.shop.name,
image: this.shop.image,
description: this.shop.description,
feeRate: this.shop.feeRate,
gCode: ''
feeRate: fallbackRatePercent === '' ? '' : String(fallbackRatePercent),
gCode: '',
isOpen: false,
networkFee: 0
}
this.$message.warning(res && res.msg ? res.msg : '未获取到店铺详情')
}
} catch (error) {
// 出错时回退到当前展示的数据
const fallbackRateNum = Number(this.shop.feeRate)
const fallbackRatePercent = Number.isFinite(fallbackRateNum) ? (fallbackRateNum * 100) : ''
this.editForm = {
id: this.shop.id,
name: this.shop.name,
image: this.shop.image,
description: this.shop.description,
feeRate: this.shop.feeRate,
gCode: ''
feeRate: fallbackRatePercent === '' ? '' : String(fallbackRatePercent),
gCode: '',
isOpen: false,
networkFee: 0
}
console.error('查询店铺详情失败:', error)
@@ -1014,19 +1103,19 @@ export default {
this.$message.warning('店铺描述不能超过300个字符')
return
}
// 手续费比例:必填、0.01-0.1、最多6位小数
// 手续费比例:必填、1-10百分比最多6位小数
const rateRaw = String(this.editForm.feeRate || '').trim()
if (!rateRaw) {
this.$message.warning('请填写店铺手续费比例(0.01 - 0.1最多6位小数')
this.$message.warning('请填写店铺手续费比例1 - 10最多6位小数')
return
}
const rateNum = Number(rateRaw)
const decOk = rateRaw.includes('.') ? ((rateRaw.split('.')[1] || '').length <= 6) : true
if (!Number.isFinite(rateNum) || rateNum < 0.01 || rateNum > 0.1 || !decOk) {
this.$message.warning('手续费比例需在 0.01 - 0.1 之间且小数位不超过6位')
if (!Number.isFinite(rateNum) || rateNum < 1 || rateNum > 10 || !decOk) {
this.$message.warning('手续费比例需在 1 - 10 之间且小数位不超过6位')
return
}
this.editForm.feeRate = rateNum.toString()
this.editForm.feeRate = (rateNum / 100).toString()
// 谷歌验证码:必填 6 位数字
const gCode = String(this.editForm.gCode || '').trim()
@@ -1035,6 +1124,8 @@ export default {
return
}
const feeRaw = Number(this.editForm.networkFee)
this.editForm.networkFee = this.editForm.isOpen ? (Number.isFinite(feeRaw) && feeRaw > 0 ? feeRaw : 0.02) : 0
const payload = { ...this.editForm, gCode }
const res = await updateShop(payload)
if (res && (res.code === 0 || res.code === 200)) {
@@ -1203,6 +1294,57 @@ export default {
/* 余额数字红色显示 */
.balance-num { color: #ff4d4f; font-weight: 600; }
.balance-unit { color: #606266; }
.fee-rate-input {
max-width: 220px;
}
.edit-network {
display: flex;
flex-direction: column;
gap: 6px;
padding: 8px 10px;
border-radius: 8px;
background: #f7f9fc;
border: 1px solid #eef2f7;
align-items: flex-start;
}
.edit-network-line {
display: inline-flex;
align-items: center;
gap: 12px;
flex-wrap: wrap;
}
.edit-network-label {
color: #606266;
font-size: 13px;
}
.edit-network-fee {
display: inline-flex;
align-items: center;
gap: 6px;
color: #606266;
font-size: 13px;
}
.edit-fee-value {
display: inline-block;
min-width: 40px;
text-align: center;
padding: 2px 8px;
border-radius: 10px;
background: #eef2f7;
color: #606266;
font-weight: 600;
}
.edit-fee-value.on {
background: #fff7ed;
color: #c2410c;
border: 1px solid #fed7aa;
}
.edit-network-hint {
color: #909399;
font-size: 12px;
line-height: 1.4;
text-align: left;
}
</style>
<style>

View File

@@ -10,9 +10,18 @@
<div class="row"><span class="label">店铺</span><span class="value">{{ order.shopName || '—' }}</span></div>
<div class="row"><span class="label">订单总价<span v-if="order.payCoin">({{ order.payCoin }})</span></span><span class="value strong">{{ order.totalPrice }}</span></div>
<div class="row"><span class="label">实际支付总金额<span v-if="order.payCoin">({{ order.payCoin }})</span></span><span class="value strong">{{ order.totalPayAmount !== null && order.totalPayAmount !== undefined ? order.totalPayAmount : '' }}</span></div>
<div class="row"><span class="label">实际算力</span><span class="value">{{ order.totalPracticalPower !== null && order.totalPracticalPower !== undefined ? order.totalPracticalPower : '' }}</span></div>
<div class="row"><span class="label">理论总算力</span><span class="value">{{ order.totalTheoryPower !== null && order.totalTheoryPower !== undefined ? order.totalTheoryPower : '' }}</span></div>
<div class="row"><span class="label">实际/理论算力比值</span><span class="value">{{ order.powerRatio !== null && order.powerRatio !== undefined ? order.powerRatio : '' }}</span></div>
<div class="row">
<span class="label">
<el-tooltip content="当前为平均算力值,实际算力值以最终结算为准。" placement="top">
<i class="el-icon-question tip-icon" />
</el-tooltip>
平均算力
</span>
<span class="value">{{ formatPowerWithUnit(order.totalPracticalPower, order.unit) }}</span>
</div>
<div class="row"><span class="label">理论总算力</span><span class="value">{{ formatPowerWithUnit(order.totalTheoryPower, order.unit) }}</span></div>
<div class="row"><span class="label">实际/理论算力比值</span><span class="value">{{ formatPowerRatio(order.powerRatio) }}</span></div>
<div class="row"><span class="label">创建时间</span><span class="value">{{ formatDateTime(order.createTime) }}</span></div>
<div v-if="Number(order.status) === 8" class="row"><span class="label">订单完成时间:</span><span class="value">{{ formatDateTime(order.endTime) }}</span></div>
</el-card>
@@ -25,9 +34,23 @@
<el-table-column label="矿机类型" min-width="100">
<template #default="scope">{{ formatMachineType(scope.row && scope.row.type) }}</template>
</el-table-column>
<el-table-column prop="practicalPower" label="实际算力" min-width="120" />
<el-table-column prop="theoryPower" label="理论算力" min-width="120" />
<el-table-column prop="powerRatio" label="实际/理论算力比值" min-width="160" />
<el-table-column min-width="120">
<template #header>
<span>
<el-tooltip content="当前为平均算力值,实际算力值以最终结算为准。" placement="top">
<i class="el-icon-question tip-icon" />
</el-tooltip>
平均算力
</span>
</template>
<template #default="scope">{{ formatPowerWithUnit(scope.row && scope.row.practicalPower, scope.row && scope.row.unit) }}</template>
</el-table-column>
<el-table-column label="理论算力" min-width="120">
<template #default="scope">{{ formatPowerWithUnit(scope.row && scope.row.theoryPower, scope.row && scope.row.unit) }}</template>
</el-table-column>
<el-table-column label="实际/理论算力比值" min-width="160">
<template #default="scope">{{ formatPowerRatio(scope.row && scope.row.powerRatio) }}</template>
</el-table-column>
<el-table-column prop="leaseTime" label="租赁天数" min-width="100" />
<el-table-column label="购买数量" min-width="100">
<template #default="scope">{{ scope.row && scope.row.numbers != null ? scope.row.numbers : '—' }}</template>
@@ -125,6 +148,25 @@ export default {
if (typeNum === 0) return 'ASIC'
if (typeNum === 1) return 'GPU'
return '—'
},
/**
* 实际/理论算力比值显示为百分比
*/
formatPowerRatio(value) {
if (value === null || value === undefined || value === '') return '—'
const num = Number(value)
if (!Number.isFinite(num)) return '—'
const percent = num * 100
const fixed = percent.toFixed(2)
return `${fixed.replace(/\.?0+$/, '')}%`
},
/**
* 实际/理论算力展示拼接单位unit 有则显示)
*/
formatPowerWithUnit(power, unit) {
if (power === null || power === undefined || power === '') return '—'
const u = unit != null ? String(unit).trim() : ''
return u ? `${power} ${u}` : String(power)
}
}
}
@@ -141,6 +183,7 @@ export default {
.value.mono { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; word-break: break-all; }
.value.strong { font-weight: 700; color: #e74c3c; }
.actions { margin-top: 12px; }
.tip-icon { margin: 0 4px; color: #6b7280; cursor: pointer; }
</style>

View File

@@ -6,17 +6,6 @@
<h2 class="page-title">商品列表</h2>
</div>
<div class="right-area">
<el-select
v-model="listParams.type"
placeholder="矿机种类"
size="small"
class="mr-12"
style="width: 140px"
@change="handleTypeChange"
>
<el-option :label="'ASIC'" :value="0" />
<el-option :label="'GPU'" :value="1" />
</el-select>
<el-input
v-model="searchKeyword"
:placeholder="searchPlaceholder"
@@ -49,13 +38,31 @@
</div>
</div>
<!-- 售价展示币种选择影响 ASIC 表格售价列展示 -->
<!-- 矿机类型切换按钮表格左上方 -->
<div class="list-actions-row">
<div class="machine-type-switch">
<el-button
size="small"
:type="listParams.type === 0 ? 'primary' : 'default'"
:class="{ active: listParams.type === 0 }"
@click="handleTypeChange(0)"
>
ASIC
</el-button>
<el-button
size="small"
:type="listParams.type === 1 ? 'primary' : 'default'"
:class="{ active: listParams.type === 1 }"
@click="handleTypeChange(1)"
>
GPU
</el-button>
</div>
<div
v-if="listParams.type === 0 && payTypes && payTypes.length"
class="price-select-bar"
style="margin:8px 0 4px; display:flex; justify-content:flex-end; align-items:center;"
class="price-select-bar inline"
>
<span style="margin-right:8px;color:#606266;font-size: 14px;">筛选售价</span>
<span class="price-label">筛选售价</span>
<el-select
v-model="selectedPayKey"
size="small"
@@ -84,6 +91,7 @@
</el-option>
</el-select>
</div>
</div>
<!-- 表格列表ASIC -->
<el-table
@@ -158,7 +166,12 @@
<!-- 操作去掉详情与添加出售机器 -->
<el-table-column label="操作" fixed="right" width="140">
<template #default="scope">
<el-button type="text" size="small" @click="handleEdit(scope.row)">修改</el-button>
<el-button
type="text"
size="small"
:disabled="Number(scope.row.saleState) === 1"
@click="handleEdit(scope.row)"
>修改</el-button>
<el-button type="text" size="small" style="color:#f56c6c" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
@@ -197,6 +210,7 @@
:active-value="0"
:inactive-value="1"
:value="(updateMap[getRowId(scope.row)] && updateMap[getRowId(scope.row)].state) != null ? updateMap[getRowId(scope.row)].state : 1"
:disabled="Number(scope.row.saleState) === 1"
@change="handleToggleState(scope.row, $event)"
/>
<span
@@ -222,6 +236,7 @@
class="gpu-price-input"
:value="getPriceValue(getRowId(scope.row), pt)"
placeholder="价格"
:disabled="Number(scope.row.saleState) === 1"
@input="handlePriceInput(scope.row, pt, $event)"
@blur="handlePriceBlur(scope.row, pt)"
style="width: 100px"
@@ -236,7 +251,7 @@
size="small"
:value="String((updateMap[getRowId(scope.row)] && updateMap[getRowId(scope.row)].maxLeaseDays) || '')"
placeholder="1-365"
:disabled="Number(scope.row.saleState) === 1"
@input="handleMaxLeaseDaysInput(scope.row, $event)"
/>
</template>
@@ -244,7 +259,13 @@
<!-- 操作 -->
<el-table-column label="操作" fixed="right" width="120">
<template #default="scope">
<el-button type="text" size="small" style="color:#f56c6c" @click="handleDelete(scope.row)">删除</el-button>
<el-button
type="text"
size="small"
style="color:#f56c6c"
:disabled="Number(scope.row.saleState) === 1"
@click="handleDelete(scope.row)"
>删除</el-button>
</template>
</el-table-column>
</el-table>
@@ -1589,6 +1610,16 @@ export default {
display: flex;
align-items: center;
}
.price-select-bar.inline {
display: inline-flex;
align-items: center;
margin-right: 12px;
}
.price-select-bar.inline .price-label {
margin-right: 8px;
color: #606266;
font-size: 14px;
}
.paytypes-bar {
display: flex;
align-items: center;
@@ -1623,6 +1654,39 @@ export default {
margin-top: 12px;
}
.list-actions-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
margin: 6px 0 10px;
}
.machine-type-switch {
display: flex;
align-items: center;
gap: 8px;
}
.machine-type-switch .el-button {
border-radius: 18px;
padding: 6px 18px;
font-weight: 600;
color: #6b7280;
background: #f3f4f6;
border-color: #e5e7eb;
}
.machine-type-switch .el-button.active {
color: #fff;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-color: transparent;
box-shadow: 0 6px 12px rgba(118, 75, 162, 0.2);
}
.machine-type-switch .el-button:not(.active):hover {
color: #374151;
background: #e5e7eb;
border-color: #d1d5db;
}
/* 编辑弹窗内:让下拉/单选与输入框左边缘对齐 */
.edit-form .align-like-input .el-form-item__content {
padding-left: 12px; /* 对齐到上方 el-input 的内边距视觉效果 */

View File

@@ -101,7 +101,7 @@
</el-table-column>
<el-table-column prop="power" label="实时算力" min-width="140">
<template #default="scope">
<span>{{ scope.row.power || '—' }}</span>
<span>{{ formatPowerWithUnit(scope.row && scope.row.power, scope.row && scope.row.unit) }}</span>
</template>
</el-table-column>
<el-table-column prop="recordTime" label="最近实时算力记录时间" min-width="180">
@@ -218,6 +218,14 @@ export default {
return String(value)
}
},
/**
* 实时算力展示拼接单位unit 有则显示)
*/
formatPowerWithUnit(power, unit) {
if (power === null || power === undefined || power === '') return '—'
const u = unit != null ? String(unit).trim() : ''
return u ? `${power} ${u}` : String(power)
},
/**
* 复制文本到剪贴板
* @param {string} text - 需要复制的内容

View File

@@ -38,7 +38,7 @@
border
stripe
size="small"
style="width: 100%; table-layout: auto;"
style="width: 100%; table-layout: fixed;"
:row-key="getRowKey"
:expand-row-keys="expandedRowKeys"
:row-class-name="getRowClassName"
@@ -51,24 +51,33 @@
<template #default="scope">
<div class="detail-panel">
<div class="detail-grid">
<div class="detail-item">
<span class="detail-label">付款网络/币种</span>
<span class="detail-value">
<span class="badge">{{ formatChain(scope.row.fromChain) || '-' }}</span>
<span class="badge badge-blue">{{ String((scope.row.fromSymbol || scope.row.coin) || '') .toUpperCase() }}</span>
</span>
</div>
<div class="detail-item">
<span class="detail-label">订单号</span>
<span class="detail-value mono">{{ scope.row.orderId || '-' }}</span>
</div>
<div class="detail-item">
<span class="detail-label">付款链</span>
<span class="detail-value"><span class="badge">{{ formatChain(scope.row.fromChain) || '-' }}</span></span>
<span class="detail-label">实收金额</span>
<span class="detail-value">{{ formatAmount(scope.row.receivedAmount, scope.row.toSymbol || scope.row.coin || '').text }}</span>
</div>
<div class="detail-item">
<span class="detail-label">付款币种</span>
<span class="detail-value"><span class="badge badge-blue">{{ String((scope.row.fromSymbol || scope.row.coin) || '') .toUpperCase() }}</span></span>
<span class="detail-label">应收金额</span>
<span class="detail-value">{{ formatAmount(scope.row.realAmount, scope.row.toSymbol || scope.row.coin || '').text }}</span>
</div>
<div class="detail-item detail-item-full">
<span class="detail-label">付款地址</span>
<span class="detail-value address">
<span class="mono-ellipsis" :title="scope.row.fromAddress">{{ scope.row.fromAddress || '-' }}</span>
<el-button type="text" size="mini" @click.stop="copy(scope.row.fromAddress)" v-if="scope.row.fromAddress">复制</el-button>
</span>
<div class="detail-item">
<span class="detail-label">手续费</span>
<span class="detail-value">{{ formatAmount(scope.row.fee, scope.row.toSymbol || scope.row.coin || '').text }}</span>
</div>
<div class="detail-item">
<span class="detail-label">手续费比例</span>
<span class="detail-value">{{ formatFeeRate(scope.row.feeRate) }}</span>
</div>
</div>
</div>
@@ -77,45 +86,33 @@
<el-table-column label="支付时间" width="160">
<template #default="scope">{{ formatFullTime(scope.row.createTime) }}</template>
</el-table-column>
<el-table-column label="收金额" width="140" align="right">
<el-table-column label="收金额" width="140" align="right">
<template #default="scope">
<span class="amount-green">
<el-tooltip
v-if="formatAmount(scope.row.realAmount, scope.row.toSymbol || scope.row.coin || '').truncated"
:content="`+${formatAmount(scope.row.realAmount, scope.row.toSymbol || scope.row.coin || '').full} ${(scope.row.toSymbol || scope.row.coin || '').toUpperCase()}`"
v-if="formatAmount(scope.row.receivedAmount, scope.row.toSymbol || scope.row.coin || '').truncated"
:content="`+${formatAmount(scope.row.receivedAmount, scope.row.toSymbol || scope.row.coin || '').full} ${(scope.row.toSymbol || scope.row.coin || '').toUpperCase()}`"
placement="top"
>
<span>
+{{ formatAmount(scope.row.realAmount, scope.row.toSymbol || scope.row.coin || '').text }}
+{{ formatAmount(scope.row.receivedAmount, scope.row.toSymbol || scope.row.coin || '').text }}
{{ (scope.row.toSymbol || scope.row.coin || '').toUpperCase() }}
<i class="el-icon-more amount-more"></i>
</span>
</el-tooltip>
<span v-else>
+{{ formatAmount(scope.row.realAmount, scope.row.toSymbol || scope.row.coin || '').text }}
+{{ formatAmount(scope.row.receivedAmount, scope.row.toSymbol || scope.row.coin || '').text }}
{{ (scope.row.toSymbol || scope.row.coin || '').toUpperCase() }}
</span>
</span>
</template>
</el-table-column>
<el-table-column label="收款" width="140">
<el-table-column label="收款网络" width="140">
<template #default="scope">{{ formatChain(scope.row.toChain) }}</template>
</el-table-column>
<el-table-column label="收款币种" width="100">
<template #default="scope">{{ String(scope.row.coin || '').toUpperCase() }}</template>
</el-table-column>
<el-table-column label="收款地址" min-width="200">
<template #default="scope">
<span class="mono-ellipsis" :title="scope.row.toAddress">{{ scope.row.toAddress }}</span>
<el-button type="text" size="mini" @click.stop="copy(scope.row.toAddress)">复制</el-button>
</template>
</el-table-column>
<el-table-column label="交易HASH" min-width="200">
<template #default="scope">
<span class="mono-ellipsis" :title="scope.row.txHash">{{ scope.row.txHash }}</span>
<el-button type="text" size="mini" @click.stop="copy(scope.row.txHash)" v-if="scope.row.txHash">复制</el-button>
</template>
</el-table-column>
<el-table-column label="支付状态" width="120">
<template #default="scope">
<el-tag :type="getStatusType(scope.row.status)" size="small">{{ getStatusText(scope.row.status) }}</el-tag>
@@ -155,33 +152,53 @@ export default {
data() {
return {
loading: false,
useMock: true,
rows: [
// {
// orderId: '1234567890',
// fromChain: 'tron',
// fromSymbol: 'USDT',
// fromAddress: 'TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE',
// toChain: 'tron',
// coin: 'USDT',
// toAddress: 'TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE',
// txHash: 'TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE',
// status: 2,
// updateTime: '2024-01-15 14:30:25',
// realAmount: 100,
// },
// {
// orderId: '1234567890',
// fromChain: 'tron',
// fromSymbol: 'USDT',
// fromAddress: 'TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE',
// toChain: 'tron',
// coin: 'USDT',
// toAddress: 'TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE',
// txHash: 'TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE',
// status: 1,
// updateTime: '2024-01-15 14:30:25',
// realAmount: 106,
// }
{
orderId: '202401150001',
fromChain: 'tron',
fromSymbol: 'USDT',
toChain: 'tron',
toSymbol: 'USDT',
coin: 'USDT',
status: 1,
createTime: '2024-01-15 12:10:25',
updateTime: '2024-01-15 12:12:08',
realAmount: 100,
receivedAmount: 98,
feeRate: 0.02,
fee: 2
},
{
orderId: '202401150002',
fromChain: 'ethereum',
fromSymbol: 'ETH',
toChain: 'ethereum',
toSymbol: 'ETH',
coin: 'ETH',
status: 2,
createTime: '2024-01-15 13:22:40',
updateTime: '2024-01-15 13:25:55',
realAmount: 0.5,
receivedAmount: 0.49,
feeRate: 0.02,
fee: 0.01
},
{
orderId: '202401150003',
fromChain: 'bsc',
fromSymbol: 'USDT',
toChain: 'bsc',
toSymbol: 'USDT',
coin: 'USDT',
status: 0,
createTime: '2024-01-15 14:05:12',
updateTime: '2024-01-15 14:06:09',
realAmount: 200,
receivedAmount: 0,
feeRate: 0.01,
fee: 0
}
],
page: 1,
pageSize: 10,
@@ -253,6 +270,14 @@ export default {
const map = { tron: 'Tron (TRC20)', ethereum: 'Ethereum (ERC20)', bsc: 'BSC (BEP20)', polygon: 'Polygon' }
return map[chain] || chain || '-'
},
formatFeeRate(value) {
if (value === null || value === undefined || value === '') return '-'
const num = Number(value)
if (!Number.isFinite(num)) return '-'
const percent = num * 100
const fixed = percent.toFixed(6)
return `${fixed.replace(/\.?0+$/, '')}%`
},
getStatusType(status) {
const map = { 0: 'danger', 1: 'success', 2: 'warning', 3: 'danger' }
return map[status] || 'info'
@@ -281,6 +306,11 @@ export default {
this.page = 1
},
async fetchList() {
if (this.useMock) {
this.rows = this.withKeys(this.rows)
this.total = this.rows.length
return
}
this.loading = true
try {
const params = {
@@ -329,16 +359,21 @@ export default {
width: 100% !important;
}
.receipt-page :deep(.el-table__header),
.receipt-page :deep(.el-table__body) {
width: 100% !important;
}
.receipt-page :deep(.el-table__body-wrapper) {
overflow-x: hidden;
}
/* 展开详情样式 */
.detail-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px 24px; padding: 8px 4px; }
.detail-item { display: grid; grid-template-columns: 90px 1fr; align-items: center; gap: 8px; }
.detail-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px 24px; padding: 8px 4px; }
.detail-item { display: grid; grid-template-columns: 96px 1fr; align-items: center; gap: 8px; }
.detail-item-full { grid-column: 1 / -1; }
.detail-label { color: #666; font-size: 13px; text-align: left; }
.detail-value { color: #333; font-size: 13px; text-align: left; }
.detail-value { color: #333; font-size: 13px; text-align: left; display: inline-flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.detail-value.address { font-family: "Monaco", "Menlo", monospace; word-break: break-all; }
/* 单行等宽省略 */

View File

@@ -31,14 +31,31 @@
<label class="label required">手续费比例</label>
<el-input
v-model="form.feeRate"
placeholder="比例区间 0.01 - 0.1 之间最多6位小数"
class="fee-rate-input"
placeholder="1 - 10"
@input="handleFeeRateInput"
/>
>
<template slot="append">%</template>
</el-input>
</div>
<div class="row" style="margin-top:-6px;">
<div></div>
<div style="color:#909399; font-size:12px; text-align:left;">
为提升您的店铺曝光您可为平台交易设置手续费比例该手续费为商家向平台支付的交易佣金,手续费比例将作为影响店铺排名的关键因素,该比例越高您的店铺排名越靠前
该手续费为商家向平台支付的交易佣金比例越高店铺排名越靠前
</div>
</div>
<div class="row" style="margin-top:16px;">
<label class="label">专用网络</label>
<div class="network-card">
<div class="network-line">
<span class="network-label">是否开启</span>
<el-switch v-model="form.isOpen" />
<span class="network-fee">
网络手续费
<span class="fee-value" :class="{ on: form.isOpen }">{{ networkFeeText }}</span>
</span>
</div>
<div class="network-hint">专用网络会增加2%的手续费建议无法直连矿池的用户开启</div>
</div>
</div>
<div class="row" style="margin-top:50px;">
@@ -55,9 +72,14 @@ import { getAddShop } from "@/api/shops";
export default {
data() {
return {
form: { name: "", description: "", image: "", feeRate: "" },
form: { name: "", description: "", image: "", feeRate: "", isOpen: false, networkFee: 0 },
};
},
computed: {
networkFeeText() {
return this.form.isOpen ? '2%' : '0'
}
},
mounted() {},
methods: {
// 简单的emoji检测覆盖常见表情平面与符号范围
@@ -175,11 +197,11 @@ export default {
return
}
// 手续费比例校验:必填、0.01-0.1 且最多6位小数
// 手续费比例校验:必填、1-10百分比且最多6位小数
const rateRaw = String(this.form.feeRate || '').trim()
if (!rateRaw) {
this.$message({
message: '请填写店铺手续费比例(0.01 - 0.1最多6位小数',
message: '请填写店铺手续费比例1 - 10最多6位小数',
type: 'warning',
showClose: true
})
@@ -187,15 +209,16 @@ export default {
}
const rateNum = Number(rateRaw)
const decOk = rateRaw.includes('.') ? ((rateRaw.split('.')[1] || '').length <= 6) : true
if (!Number.isFinite(rateNum) || rateNum < 0.01 || rateNum > 0.1 || !decOk) {
if (!Number.isFinite(rateNum) || rateNum < 1 || rateNum > 10 || !decOk) {
this.$message({
message: '手续费比例需在 0.01 - 0.1 之间且小数位不超过6位',
message: '手续费比例需在 1 - 10 之间且小数位不超过6位',
type: 'warning',
showClose: true
})
return
}
this.form.feeRate = rateNum.toString()
this.form.feeRate = (rateNum / 100).toString()
this.form.networkFee = this.form.isOpen ? 0.02 : 0
this.fetchAddShop(this.form)
},
@@ -211,10 +234,10 @@ export default {
}
.row {
display: grid;
grid-template-columns: 140px 1fr;
grid-template-columns: 120px 1fr;
gap: 12px;
align-items: center;
margin-bottom: 12px;
margin-bottom: 14px;
}
.label {
color: #666;
@@ -222,6 +245,59 @@ export default {
white-space: nowrap; /* 左侧文字不换行 */
word-break: keep-all;
}
.fee-rate-input {
max-width: 220px;
}
.network-card {
display: flex;
flex-direction: column;
gap: 6px;
padding: 10px 12px;
border-radius: 10px;
background: #f7f9fc;
border: 1px solid #eef2f7;
width: 100%;
align-items: flex-start;
}
.network-line {
display: inline-flex;
align-items: center;
gap: 12px;
flex-wrap: wrap;
}
.network-label {
color: #606266;
font-size: 13px;
}
.network-fee {
display: inline-flex;
align-items: center;
gap: 6px;
color: #606266;
font-size: 13px;
}
.fee-value {
display: inline-block;
min-width: 40px;
text-align: center;
padding: 2px 8px;
border-radius: 10px;
background: #eef2f7;
color: #606266;
font-weight: 600;
}
.fee-value.on {
background: #fff7ed;
color: #c2410c;
border: 1px solid #fed7aa;
}
.network-hint {
color: #909399;
font-size: 12px;
line-height: 1.4;
width: 100%;
text-align: left;
}
.actions-center {
grid-column: 1 / -1; /* 跨两列,居中显示 */
text-align: center;

View File

@@ -1955,6 +1955,7 @@ export default {
orderMiningInfoDtoList.push({
coinConfigId: coinConfigId,
coin: config.coin || '', // 用户选择的币种/算法框里的币种
algorithm: config.algorithm || config.originalAlgorithm || '', // 算法传参
poolName: config.poolName || '',
poolUser: poolUser,
walletAddress: walletAddress,

View File

@@ -864,6 +864,8 @@ export default {
duration: 3000,
showClose: true
})
// 重新拉取店铺机器列表,刷新 inCart 状态
try { await this.fetchGetMachineInfo(this.buildQueryParams()) } catch (e) { /* noop */ }
}
this.confirmAddDialog.visible = false

View File

@@ -126,7 +126,7 @@
:cell-style="{ textAlign: 'left' }"
>
<!-- 勾选框列首列 -->
<el-table-column width="46" fixed="left">
<el-table-column width="46" fixed="left" show-overflow-tooltip>
<template #default="{ row }">
<el-checkbox
v-model="row._selected"
@@ -136,6 +136,12 @@
/>
</template>
</el-table-column>
<el-table-column label="购物车" width="68" show-overflow-tooltip>
<template #default="{ row }">
<el-tag v-if="row && row.inCart" class="cart-flag" size="mini">已添加</el-tag>
<el-tag v-else size="mini" type="info">未添加</el-tag>
</template>
</el-table-column>
<el-table-column
v-for="(col, colIdx) in getRenderedColumns()"
:key="col.key || colIdx"
@@ -1208,6 +1214,12 @@ export default {
margin-left: 4px;
}
.cart-flag {
background: #fef3c7;
border-color: #f59e0b;
color: #b45309;
}
::v-deep .el-input__prefix, .el-input__suffix{
top:24%;

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
<!doctype html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><title>power_leasing</title><script defer="defer" src="/js/chunk-vendors.92ffcf12.js"></script><script defer="defer" src="/js/app.d58df3d3.js"></script><link href="/css/chunk-vendors.10dd4e95.css" rel="stylesheet"><link href="/css/app.954338a1.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but power_leasing doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>
<!doctype html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><title>power_leasing</title><script defer="defer" src="/js/chunk-vendors.92ffcf12.js"></script><script defer="defer" src="/js/app.bf293dd7.js"></script><link href="/css/chunk-vendors.10dd4e95.css" rel="stylesheet"><link href="/css/app.4bdb375c.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but power_leasing doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long