每周更新
This commit is contained in:
5
power_leasing/package-lock.json
generated
5
power_leasing/package-lock.json
generated
@@ -5171,6 +5171,11 @@
|
|||||||
"esprima": "^4.0.0"
|
"esprima": "^4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"jsencrypt": {
|
||||||
|
"version": "3.5.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsencrypt/-/jsencrypt-3.5.4.tgz",
|
||||||
|
"integrity": "sha512-kNjfYEMNASxrDGsmcSQh/rUTmcoRfSUkxnAz+MMywM8jtGu+fFEZ3nJjHM58zscVnwR0fYmG9sGkTDjqUdpiwA=="
|
||||||
|
},
|
||||||
"jsesc": {
|
"jsesc": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-3.1.0.tgz",
|
"resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-3.1.0.tgz",
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
"axios": "^1.11.0",
|
"axios": "^1.11.0",
|
||||||
"core-js": "^3.8.3",
|
"core-js": "^3.8.3",
|
||||||
"element-ui": "^2.15.14",
|
"element-ui": "^2.15.14",
|
||||||
|
"jsencrypt": "^3.5.4",
|
||||||
"vue": "^2.6.14",
|
"vue": "^2.6.14",
|
||||||
"vue-router": "^3.5.1",
|
"vue-router": "^3.5.1",
|
||||||
"vuex": "^3.6.2"
|
"vuex": "^3.6.2"
|
||||||
|
|||||||
@@ -108,5 +108,17 @@ export function getPurchasedItems(data) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//已购商品详情
|
||||||
|
export function getPurchasedInfoV2(data) {
|
||||||
|
return request({
|
||||||
|
url: `/lease/v2/order/info/getPurchasedInfo`,
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -154,6 +154,15 @@ export function balanceWithdrawListV2(data) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 修改店铺钱包配置 V2
|
||||||
|
export function updateShopConfigV2(data) {
|
||||||
|
return request({
|
||||||
|
url: `/lease/v2/shop/updateShopConfigV2`,
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import './utils/loginInfo.js';
|
|||||||
// 全局输入防表情守卫(极简、无侵入)
|
// 全局输入防表情守卫(极简、无侵入)
|
||||||
import { initNoEmojiGuard } from './utils/noEmojiGuard.js';
|
import { initNoEmojiGuard } from './utils/noEmojiGuard.js';
|
||||||
|
|
||||||
// console.log = ()=>{} //全局关闭打印
|
console.log = ()=>{} //全局关闭打印
|
||||||
|
|
||||||
|
|
||||||
Vue.config.productionTip = false
|
Vue.config.productionTip = false
|
||||||
|
|||||||
@@ -100,9 +100,13 @@ export const accountRoutes = [
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// 兼容旧入口:收款记录 -> 卖家资金流水(收款tab)
|
||||||
path: 'receipt-record',
|
path: 'receipt-record',
|
||||||
name: 'accountReceiptRecord',
|
name: 'accountReceiptRecord',
|
||||||
component: () => import('../views/account/receiptRecord.vue'),
|
redirect: (to) => ({
|
||||||
|
path: '/account/seller-funds-flow',
|
||||||
|
query: { ...(to && to.query ? to.query : {}), tab: 'receipt' }
|
||||||
|
}),
|
||||||
meta: {
|
meta: {
|
||||||
title: '收款记录',
|
title: '收款记录',
|
||||||
description: '卖家收款流水记录',
|
description: '卖家收款流水记录',
|
||||||
@@ -110,15 +114,29 @@ export const accountRoutes = [
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// 兼容旧入口:提现记录 -> 卖家资金流水(提现tab)
|
||||||
path: 'withdraw-record',
|
path: 'withdraw-record',
|
||||||
name: 'accountWithdrawRecord',
|
name: 'accountWithdrawRecord',
|
||||||
component: () => import('../views/account/withdrawRecord.vue'),
|
redirect: (to) => ({
|
||||||
|
path: '/account/seller-funds-flow',
|
||||||
|
query: { ...(to && to.query ? to.query : {}), tab: 'withdraw' }
|
||||||
|
}),
|
||||||
meta: {
|
meta: {
|
||||||
title: '提现记录',
|
title: '提现记录',
|
||||||
description: '卖家提现流水记录',
|
description: '卖家提现流水记录',
|
||||||
allAuthority: ['all']
|
allAuthority: ['all']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'seller-funds-flow',
|
||||||
|
name: 'accountSellerFundsFlow',
|
||||||
|
component: () => import('../views/account/sellerFundsFlow.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '资金流水',
|
||||||
|
description: '卖家收款/提现记录切换查看',
|
||||||
|
allAuthority: ['all']
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'shop-new',
|
path: 'shop-new',
|
||||||
name: 'accountShopNew',
|
name: 'accountShopNew',
|
||||||
@@ -169,23 +187,23 @@ export const accountRoutes = [
|
|||||||
allAuthority: ['all']
|
allAuthority: ['all']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: 'purchased',
|
|
||||||
name: 'accountPurchased',
|
|
||||||
component: () => import('../views/account/purchased.vue'),
|
|
||||||
meta: {
|
|
||||||
title: '已购商品',
|
|
||||||
description: '查看已购买的商品列表',
|
|
||||||
allAuthority: ['all']
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: 'purchased-machine-config',
|
path: 'purchased-machine-config',
|
||||||
name: 'accountPurchasedMachineConfig',
|
name: 'accountPurchasedMachineConfig',
|
||||||
component: () => import('../views/account/purchasedMachineConfig.vue'),
|
component: () => import('../views/account/purchasedMachineConfig.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: '已购矿机配置',
|
title: '已购商品',
|
||||||
description: '查看已购买矿机的配置信息',
|
description: '查看已购买商品的配置信息',
|
||||||
|
allAuthority: ['all']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'purchased-machine-detail/:id',
|
||||||
|
name: 'purchasedMachineDetail',
|
||||||
|
component: () => import('../views/account/purchasedMachineDetail.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '已购商品详情',
|
||||||
|
description: '查看已购买商品的详细信息',
|
||||||
allAuthority: ['all']
|
allAuthority: ['all']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -199,16 +217,6 @@ export const accountRoutes = [
|
|||||||
allAuthority: ['all']
|
allAuthority: ['all']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: 'purchased-detail/:id',
|
|
||||||
name: 'PurchasedDetail',
|
|
||||||
component: () => import('../views/account/purchasedDetail.vue'),
|
|
||||||
meta: {
|
|
||||||
title: '已购商品详情',
|
|
||||||
description: '查看已购商品详细信息',
|
|
||||||
allAuthority: ['all']
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: 'orders',
|
path: 'orders',
|
||||||
name: 'accountOrders',
|
name: 'accountOrders',
|
||||||
|
|||||||
101
power_leasing/src/utils/rsaEncrypt.js
Normal file
101
power_leasing/src/utils/rsaEncrypt.js
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
/**
|
||||||
|
* RSA 加密工具
|
||||||
|
* 使用 jsencrypt 库进行 RSA 公钥加密
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 导入 jsencrypt
|
||||||
|
import JSEncrypt from 'jsencrypt'
|
||||||
|
|
||||||
|
// RSA 公钥(Base64 格式,不带 BEGIN/END 标记)
|
||||||
|
const RSA_PUBLIC_KEY_BASE64 = `MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsQVIKYozXCfnXUw8+omYLdcdL1pTzmQh35YPsvn22wM4SQJKvMmXmcS6bI5Bu+5zCjL0F56DzfKz0BNZEwb46UshUOO+KFBUr8CxjYE8NOgIsoe5FUn57O6er9/KySaWlkpGZX49K+l3e90R+dFUEfRE/ijYpeZWkLRwcgWZ+2u6HGpl9h/eF6XD0aW9asDjdAbxUQ48TlaWgfP+OHC+Zy2GKGQG16EcDMczrN6a2HbFnwRIUKrFP67UqyRq11BTUziOhXLY8J0MFuwXUk2OY4VpqjrJjHHjlHYADjIL/5K4Io2AhRU9+QSsKFR2wGxi4e8vw2IXDzscrDuah/7YSwIDAQAB`
|
||||||
|
|
||||||
|
// RSA 公钥(PEM 格式)
|
||||||
|
const RSA_PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
|
||||||
|
${RSA_PUBLIC_KEY_BASE64}
|
||||||
|
-----END PUBLIC KEY-----`
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 JSEncrypt 构造函数
|
||||||
|
* @returns {Function} JSEncrypt 构造函数
|
||||||
|
*/
|
||||||
|
function getJSEncrypt() {
|
||||||
|
// 优先使用已导入的 JSEncrypt
|
||||||
|
if (JSEncrypt) {
|
||||||
|
return JSEncrypt
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试从全局获取(可能通过 CDN 引入)
|
||||||
|
if (typeof window !== 'undefined' && window.JSEncrypt) {
|
||||||
|
return window.JSEncrypt
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RSA 加密函数(同步版本)
|
||||||
|
* @param {string} plainText - 要加密的明文
|
||||||
|
* @returns {string|null} 加密后的密文(Base64 编码),失败返回 null
|
||||||
|
*/
|
||||||
|
export function rsaEncryptSync(plainText) {
|
||||||
|
if (!plainText || typeof plainText !== 'string') {
|
||||||
|
console.error('RSA 加密:输入必须是非空字符串')
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const Encrypt = getJSEncrypt()
|
||||||
|
if (!Encrypt) {
|
||||||
|
console.error('JSEncrypt 未加载')
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const encrypt = new Encrypt()
|
||||||
|
encrypt.setPublicKey(RSA_PUBLIC_KEY)
|
||||||
|
const encrypted = encrypt.encrypt(plainText)
|
||||||
|
|
||||||
|
if (!encrypted) {
|
||||||
|
console.error('RSA 加密失败:返回值为空')
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return encrypted
|
||||||
|
} catch (error) {
|
||||||
|
console.error('RSA 加密异常:', error)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RSA 加密函数(异步版本,兼容同步调用)
|
||||||
|
* @param {string} plainText - 要加密的明文
|
||||||
|
* @returns {Promise<string|null>} 加密后的密文(Base64 编码),失败返回 null
|
||||||
|
*/
|
||||||
|
export async function rsaEncrypt(plainText) {
|
||||||
|
if (!plainText || typeof plainText !== 'string') {
|
||||||
|
console.error('RSA 加密:输入必须是非空字符串')
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const Encrypt = getJSEncrypt()
|
||||||
|
if (!Encrypt) {
|
||||||
|
console.error('JSEncrypt 未加载')
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const encrypt = new Encrypt()
|
||||||
|
encrypt.setPublicKey(RSA_PUBLIC_KEY)
|
||||||
|
const encrypted = encrypt.encrypt(plainText)
|
||||||
|
|
||||||
|
if (!encrypted) {
|
||||||
|
console.error('RSA 加密失败:返回值为空')
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return encrypted
|
||||||
|
} catch (error) {
|
||||||
|
console.error('RSA 加密异常:', error)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -63,8 +63,7 @@ export default {
|
|||||||
// 买家侧导航
|
// 买家侧导航
|
||||||
buyerLinks: [
|
buyerLinks: [
|
||||||
{ label: '我的钱包', to: '/account/wallet' },
|
{ label: '我的钱包', to: '/account/wallet' },
|
||||||
{ label: '已购商品', to: '/account/purchased' },
|
{ label: '已购商品', to: '/account/purchased-machine-config' },
|
||||||
{ label: '已购矿机配置', to: '/account/purchased-machine-config' },
|
|
||||||
{ label: '订单列表', to: '/account/orders' },
|
{ label: '订单列表', to: '/account/orders' },
|
||||||
// { label: '充值记录', to: '/account/rechargeRecord' },
|
// { label: '充值记录', to: '/account/rechargeRecord' },
|
||||||
// { label: '提现记录', to: '/account/withdrawalHistory' },
|
// { label: '提现记录', to: '/account/withdrawalHistory' },
|
||||||
@@ -76,8 +75,7 @@ export default {
|
|||||||
{ label: '我的店铺', to: '/account/shops' },
|
{ label: '我的店铺', to: '/account/shops' },
|
||||||
{ label: '商品列表', to: '/account/products' },
|
{ label: '商品列表', to: '/account/products' },
|
||||||
{ label: '已售出订单', to: '/account/seller-orders' },
|
{ label: '已售出订单', to: '/account/seller-orders' },
|
||||||
{ label: '收款记录', to: '/account/receipt-record' },
|
{ label: '资金流水', to: '/account/seller-funds-flow' },
|
||||||
{ label: '提现记录', to: '/account/withdraw-record' },
|
|
||||||
|
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@@ -158,9 +156,8 @@ export default {
|
|||||||
// 买家前缀优先匹配,确保"已购详情"等页面归属买家侧
|
// 买家前缀优先匹配,确保"已购详情"等页面归属买家侧
|
||||||
const buyerPrefixes = [
|
const buyerPrefixes = [
|
||||||
'/account/wallet',
|
'/account/wallet',
|
||||||
'/account/purchased',
|
|
||||||
'/account/purchased-detail',
|
|
||||||
'/account/purchased-machine-config',
|
'/account/purchased-machine-config',
|
||||||
|
'/account/purchased-machine-detail',
|
||||||
'/account/orders',
|
'/account/orders',
|
||||||
'/account/funds-flow'
|
'/account/funds-flow'
|
||||||
]
|
]
|
||||||
@@ -172,6 +169,8 @@ export default {
|
|||||||
'/account/product-detail',
|
'/account/product-detail',
|
||||||
'/account/product-machine-add',
|
'/account/product-machine-add',
|
||||||
'/account/seller-orders',
|
'/account/seller-orders',
|
||||||
|
'/account/seller-funds-flow',
|
||||||
|
// 兼容旧路径(会被路由重定向到 seller-funds-flow)
|
||||||
'/account/receipt-record',
|
'/account/receipt-record',
|
||||||
'/account/withdraw-record',
|
'/account/withdraw-record',
|
||||||
'/account/shop-config'
|
'/account/shop-config'
|
||||||
@@ -208,16 +207,18 @@ export default {
|
|||||||
const map = {
|
const map = {
|
||||||
'/account/seller-orders': ['/account/seller-orders'],
|
'/account/seller-orders': ['/account/seller-orders'],
|
||||||
'/account/products': ['/account/products', '/account/product-detail'],
|
'/account/products': ['/account/products', '/account/product-detail'],
|
||||||
'/account/purchased': ['/account/purchased', '/account/purchased-detail'],
|
'/account/purchased-machine-config': ['/account/purchased-machine-config', '/account/purchased-machine-detail']
|
||||||
'/account/purchased-machine-config': ['/account/purchased-machine-config']
|
|
||||||
}
|
}
|
||||||
const prefixes = map[pathLike]
|
const prefixes = map[pathLike]
|
||||||
if (Array.isArray(prefixes)) {
|
if (Array.isArray(prefixes)) {
|
||||||
return prefixes.some(p => {
|
return prefixes.some(p => {
|
||||||
// 精确匹配
|
// 精确匹配
|
||||||
if (current === p) return true
|
if (current === p) return true
|
||||||
// 对于详情页路径,使用前缀匹配(支持 /account/purchased-detail/:id)
|
// 对于详情页路径,使用前缀匹配(支持 /account/product-detail/:id)
|
||||||
if (p === '/account/purchased-detail' || p === '/account/product-detail') {
|
if (p === '/account/product-detail') {
|
||||||
|
return current.indexOf(p) === 0
|
||||||
|
}
|
||||||
|
if (p === '/account/purchased-machine-detail') {
|
||||||
return current.indexOf(p) === 0
|
return current.indexOf(p) === 0
|
||||||
}
|
}
|
||||||
// 列表页已经在上面精确匹配了,这里不需要额外处理
|
// 列表页已经在上面精确匹配了,这里不需要额外处理
|
||||||
@@ -225,8 +226,6 @@ export default {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
// 不在 map 中的路径,精确匹配
|
// 不在 map 中的路径,精确匹配
|
||||||
// 特别注意:避免 /account/purchased 匹配到 /account/purchased-machine-config
|
|
||||||
// 使用精确匹配而不是前缀匹配
|
|
||||||
return current === pathLike
|
return current === pathLike
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -13,13 +13,17 @@
|
|||||||
请在本页点击 <b>钱包绑定</b>,配置自己的收款地址(支持不同链与币种)。
|
请在本页点击 <b>钱包绑定</b>,配置自己的收款地址(支持不同链与币种)。
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<b>商品</b>:完成钱包绑定后,即可在“我的店铺”页面 <b>创建商品</b>。
|
<b>创建商品</b>:完成钱包绑定后,即可在“我的店铺”页面 点击<b>新增商品</b>按钮。
|
||||||
商品可按 <b>币种</b> 进行分类管理,创建的商品会在商城对买家展示。
|
<ul class="guide-substeps">
|
||||||
商品可理解为“不同算法、币种的机器集合分类”。
|
<li>
|
||||||
</li>
|
<b>ASIC 商品创建</b>:选择矿机种类为 ASIC,填写页面商品信息后创建,商品可按 <b>币种</b> 进行分类管理,创建的商品会在商城对买家展示;
|
||||||
<li>
|
商品可理解为“不同算法、币种的机器集合分类”。
|
||||||
<b>出售机器</b>:创建商品后,请进入 <b>商品列表</b> 为该商品 <b>添加出售机器明细</b>。
|
</li>
|
||||||
必须添加出售机器,否则买家无法下单。买家点击某个商品后,会看到该商品下的机器明细并进行选购。
|
<li>
|
||||||
|
<b>GPU 商品创建</b>:选择矿机种类为 GPU,查看页面注意事项并下载对应客户端,启动后读取自动创建。创建完成请进入 <b>商品列表</b> 为该商品手动配置售价等相关信息并上架。
|
||||||
|
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
<div class="guide-note">提示:建议先创建店铺 → 完成钱包绑定 → 创建商品的顺序,避免漏配导致无法收款或无法下单。</div>
|
<div class="guide-note">提示:建议先创建店铺 → 完成钱包绑定 → 创建商品的顺序,避免漏配导致无法收款或无法下单。</div>
|
||||||
@@ -245,6 +249,7 @@ import { getMyShop, updateShop, deleteShop, queryShop, closeShop ,updateShopConf
|
|||||||
|
|
||||||
import { coinList } from '@/utils/coinList'
|
import { coinList } from '@/utils/coinList'
|
||||||
import { getShopConfig,getShopConfigV2 ,withdrawBalanceForSeller,updateShopConfigV2} from '@/api/wallet'
|
import { getShopConfig,getShopConfigV2 ,withdrawBalanceForSeller,updateShopConfigV2} from '@/api/wallet'
|
||||||
|
import { rsaEncrypt, rsaEncryptSync } from '@/utils/rsaEncrypt'
|
||||||
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -434,12 +439,55 @@ export default {
|
|||||||
this.withdrawLoading = true
|
this.withdrawLoading = true
|
||||||
try {
|
try {
|
||||||
const row = this.currentWithdrawRow || {}
|
const row = this.currentWithdrawRow || {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提现地址 RSA 加密(与“钱包绑定”页面保持一致:同步优先,异步兜底)
|
||||||
|
* - toAddress: 用户输入/默认地址
|
||||||
|
* - fromAddress: 当前绑定的钱包地址(后端可能用于校验来源)
|
||||||
|
*/
|
||||||
|
const toAddressPlain = String(this.withdrawForm.toAddress || '').trim()
|
||||||
|
const fromAddressPlain = String(row.payAddress || this.withdrawForm.toAddress || '').trim()
|
||||||
|
|
||||||
|
/** @type {string} */
|
||||||
|
let encryptedToAddress = toAddressPlain
|
||||||
|
if (encryptedToAddress) {
|
||||||
|
const syncEncrypted = rsaEncryptSync(encryptedToAddress)
|
||||||
|
if (syncEncrypted) {
|
||||||
|
encryptedToAddress = syncEncrypted
|
||||||
|
} else {
|
||||||
|
const asyncEncrypted = await rsaEncrypt(encryptedToAddress)
|
||||||
|
if (asyncEncrypted) {
|
||||||
|
encryptedToAddress = asyncEncrypted
|
||||||
|
} else {
|
||||||
|
this.$message.error('钱包地址加密失败,请重试')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {string} */
|
||||||
|
let encryptedFromAddress = fromAddressPlain
|
||||||
|
if (encryptedFromAddress) {
|
||||||
|
const syncEncrypted = rsaEncryptSync(encryptedFromAddress)
|
||||||
|
if (syncEncrypted) {
|
||||||
|
encryptedFromAddress = syncEncrypted
|
||||||
|
} else {
|
||||||
|
const asyncEncrypted = await rsaEncrypt(encryptedFromAddress)
|
||||||
|
if (asyncEncrypted) {
|
||||||
|
encryptedFromAddress = asyncEncrypted
|
||||||
|
} else {
|
||||||
|
this.$message.error('钱包地址加密失败,请重试')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
toChain: row.chain,
|
toChain: row.chain,
|
||||||
toSymbol: row.payCoin,
|
toSymbol: row.payCoin,
|
||||||
amount: Number(this.withdrawForm.amount),
|
amount: Number(this.withdrawForm.amount),
|
||||||
toAddress: this.withdrawForm.toAddress,
|
toAddress: encryptedToAddress,
|
||||||
fromAddress: row.payAddress || this.withdrawForm.toAddress || '',
|
fromAddress: encryptedFromAddress,
|
||||||
code: this.withdrawForm.googleCode,
|
code: this.withdrawForm.googleCode,
|
||||||
serviceCharge: Number(this.withdrawForm.fee) || 0
|
serviceCharge: Number(this.withdrawForm.fee) || 0
|
||||||
}
|
}
|
||||||
@@ -679,26 +727,53 @@ export default {
|
|||||||
this.deleteShopConfig({id:row.id})
|
this.deleteShopConfig({id:row.id})
|
||||||
},
|
},
|
||||||
|
|
||||||
submitConfigEdit() {
|
/**
|
||||||
|
* 提交配置修改
|
||||||
|
*/
|
||||||
|
async submitConfigEdit() {
|
||||||
// 仅校验钱包地址
|
// 仅校验钱包地址
|
||||||
const addr = (this.configForm.payAddress || '').trim()
|
const addr = (this.configForm.payAddress || '').trim()
|
||||||
if (!addr) {
|
if (!addr) {
|
||||||
this.$message.warning('请输入钱包地址')
|
this.$message.warning('请输入钱包地址')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用 RSA 加密钱包地址(与“钱包绑定”页面保持一致:同步优先,异步兜底)
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
let encryptedPayAddress = addr
|
||||||
|
if (encryptedPayAddress) {
|
||||||
|
const syncEncrypted = rsaEncryptSync(encryptedPayAddress)
|
||||||
|
if (syncEncrypted) {
|
||||||
|
encryptedPayAddress = syncEncrypted
|
||||||
|
} else {
|
||||||
|
const asyncEncrypted = await rsaEncrypt(encryptedPayAddress)
|
||||||
|
if (asyncEncrypted) {
|
||||||
|
encryptedPayAddress = asyncEncrypted
|
||||||
|
} else {
|
||||||
|
this.$message.error('钱包地址加密失败,请重试')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
id: this.configForm.id,
|
id: this.configForm.id,
|
||||||
chain: this.configForm.chainValue || this.configForm.chainLabel || '',
|
chain: this.configForm.chainValue || this.configForm.chainLabel || '',
|
||||||
payAddress: this.configForm.payAddress
|
payAddress: encryptedPayAddress
|
||||||
}
|
}
|
||||||
;(async () => {
|
try {
|
||||||
const res = await updateShopConfigV2(payload)
|
const res = await updateShopConfigV2(payload)
|
||||||
if (res && (res.code === 0 || res.code === 200)) {
|
if (res && (res.code === 0 || res.code === 200)) {
|
||||||
this.$message.success('保存成功')
|
this.$message.success('保存成功')
|
||||||
this.visibleConfigEdit = false
|
this.visibleConfigEdit = false
|
||||||
this.fetchShopConfigs(this.shop.id)
|
this.fetchShopConfigs(this.shop.id)
|
||||||
}
|
}
|
||||||
})()
|
} catch (e) {
|
||||||
|
console.error('修改配置失败', e)
|
||||||
|
this.$message.error('修改配置失败,请重试')
|
||||||
|
}
|
||||||
},
|
},
|
||||||
removeSelectedCoin(labelUpper) {
|
removeSelectedCoin(labelUpper) {
|
||||||
const label = String(labelUpper || '').toLowerCase()
|
const label = String(labelUpper || '').toLowerCase()
|
||||||
@@ -935,6 +1010,8 @@ export default {
|
|||||||
.guide-steps { margin: 0; padding-left: 18px; color: #374151; }
|
.guide-steps { margin: 0; padding-left: 18px; color: #374151; }
|
||||||
.guide-steps li { line-height: 1.9; margin: 6px 0; }
|
.guide-steps li { line-height: 1.9; margin: 6px 0; }
|
||||||
.guide-steps b { color: #111827; }
|
.guide-steps b { color: #111827; }
|
||||||
|
.guide-substeps { margin: 6px 0 0 0; padding-left: 18px; list-style: disc; }
|
||||||
|
.guide-substeps li { line-height: 1.8; margin: 4px 0; }
|
||||||
.guide-note { margin-top: 10px; color: #6b7280; font-size: 13px; background: #f9fafb; border: 1px dashed #e5e7eb; padding: 8px 10px; border-radius: 8px; }
|
.guide-note { margin-top: 10px; color: #6b7280; font-size: 13px; background: #f9fafb; border: 1px dashed #e5e7eb; padding: 8px 10px; border-radius: 8px; }
|
||||||
.coin-list { display: flex; align-items: center; gap: 8px; }
|
.coin-list { display: flex; align-items: center; gap: 8px; }
|
||||||
.coin-img { width: 20px; height: 20px; border-radius: 4px; display: inline-block; }
|
.coin-img { width: 20px; height: 20px; border-radius: 4px; display: inline-block; }
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div class="product-machine-add">
|
<div class="product-machine-add">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<el-button type="text" @click="handleBack">返回</el-button>
|
<el-button type="text" @click="handleBack">返回</el-button>
|
||||||
<h2 class="title">添加出售机器</h2>
|
<h2 class="title">创建商品</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- <el-alert
|
<!-- <el-alert
|
||||||
@@ -10,8 +10,8 @@
|
|||||||
type="warning"
|
type="warning"
|
||||||
show-icon
|
show-icon
|
||||||
:closable="false"
|
:closable="false"
|
||||||
title="新增出售机器必须在 M2pool 有挖矿算力记录才能添加出租"
|
title="新增商品必须在 M2pool 有挖矿算力记录才能添加出租"
|
||||||
description="建议稳定在 M2pool 矿池挖矿 24 小时之后,再添加出售该机器"
|
description="建议稳定在 M2pool 矿池挖矿 24 小时之后,再创建该商品"
|
||||||
/> -->
|
/> -->
|
||||||
|
|
||||||
<el-card shadow="never" class="form-card">
|
<el-card shadow="never" class="form-card">
|
||||||
@@ -236,7 +236,7 @@
|
|||||||
<div v-if="form.machineCategory === 'ASIC'" class="actions">
|
<div v-if="form.machineCategory === 'ASIC'" class="actions">
|
||||||
<el-button @click="handleBack">取消</el-button>
|
<el-button @click="handleBack">取消</el-button>
|
||||||
<el-button type="primary" :loading="saving" @click="handleSave"
|
<el-button type="primary" :loading="saving" @click="handleSave"
|
||||||
>确认添加</el-button
|
>确认创建</el-button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1531,7 +1531,7 @@ export default {
|
|||||||
const res = await addAsicMachine(payload);
|
const res = await addAsicMachine(payload);
|
||||||
if (res && (res.code === 0 || res.code === 200)) {
|
if (res && (res.code === 0 || res.code === 200)) {
|
||||||
this.$message({
|
this.$message({
|
||||||
message: "添加成功",
|
message: "创建成功",
|
||||||
duration: 3000,
|
duration: 3000,
|
||||||
showClose: true,
|
showClose: true,
|
||||||
type: "success",
|
type: "success",
|
||||||
@@ -1540,8 +1540,8 @@ export default {
|
|||||||
this.$router.push("/account/products");
|
this.$router.push("/account/products");
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("添加出售机器失败", e);
|
console.error("创建商品失败", e);
|
||||||
console.log("添加失败");
|
console.log("创建失败");
|
||||||
} finally {
|
} finally {
|
||||||
this.saving = false;
|
this.saving = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -196,7 +196,7 @@
|
|||||||
<el-switch
|
<el-switch
|
||||||
:active-value="0"
|
:active-value="0"
|
||||||
:inactive-value="1"
|
:inactive-value="1"
|
||||||
:value="(updateMap[getRowId(scope.row)] && updateMap[getRowId(scope.row)].state) || 0"
|
:value="(updateMap[getRowId(scope.row)] && updateMap[getRowId(scope.row)].state) != null ? updateMap[getRowId(scope.row)].state : 1"
|
||||||
@change="handleToggleState(scope.row, $event)"
|
@change="handleToggleState(scope.row, $event)"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
@@ -896,15 +896,31 @@ export default {
|
|||||||
const merged = { ...exist }
|
const merged = { ...exist }
|
||||||
// 覆盖为后端最新状态/天数(若存在)
|
// 覆盖为后端最新状态/天数(若存在)
|
||||||
if (m && typeof m.maxLeaseDays !== 'undefined') merged.maxLeaseDays = m.maxLeaseDays
|
if (m && typeof m.maxLeaseDays !== 'undefined') merged.maxLeaseDays = m.maxLeaseDays
|
||||||
if (m && (m.state === 0 || m.state === 1)) merged.state = m.state
|
// 如果接口有返回 state,使用接口返回的值;否则保持现有值
|
||||||
|
if (m && (m.state === 0 || m.state === 1)) {
|
||||||
|
merged.state = m.state
|
||||||
|
} else {
|
||||||
|
// 如果接口没有返回 state,默认下架(state = 1)
|
||||||
|
merged.state = 1
|
||||||
|
}
|
||||||
|
// 保存 effect 字段(从原始数据获取)
|
||||||
|
if (m && (m.effect === 0 || m.effect === 1)) {
|
||||||
|
merged.effect = m.effect
|
||||||
|
} else if (merged.effect === undefined) {
|
||||||
|
// 如果原始数据没有 effect,默认设为 1(能上架)
|
||||||
|
merged.effect = 1
|
||||||
|
}
|
||||||
merged.priceList = normalizePriceList(m && m.priceList, exist.priceList)
|
merged.priceList = normalizePriceList(m && m.priceList, exist.priceList)
|
||||||
nextMap[key] = merged
|
nextMap[key] = merged
|
||||||
} else {
|
} else {
|
||||||
|
// 如果接口没有返回 state,默认下架(state = 1)
|
||||||
|
const defaultState = (m && (m.state === 0 || m.state === 1)) ? m.state : 1
|
||||||
nextMap[key] = {
|
nextMap[key] = {
|
||||||
id: key,
|
id: key,
|
||||||
maxLeaseDays: (m && typeof m.maxLeaseDays !== 'undefined') ? m.maxLeaseDays : '',
|
maxLeaseDays: (m && typeof m.maxLeaseDays !== 'undefined') ? m.maxLeaseDays : '',
|
||||||
priceList: normalizePriceList(m && m.priceList, []),
|
priceList: normalizePriceList(m && m.priceList, []),
|
||||||
state: (m && (m.state === 0 || m.state === 1)) ? m.state : 0
|
state: defaultState,
|
||||||
|
effect: (m && (m.effect === 0 || m.effect === 1)) ? m.effect : 1 // 保存 effect 字段,默认1(能上架)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -1015,7 +1031,35 @@ export default {
|
|||||||
/** 切换状态(0 上架 1 下架) */
|
/** 切换状态(0 上架 1 下架) */
|
||||||
handleToggleState(row, val) {
|
handleToggleState(row, val) {
|
||||||
const rowId = this.getRowId(row) || row.__key
|
const rowId = this.getRowId(row) || row.__key
|
||||||
if (this.updateMap[rowId]) this.updateMap[rowId].state = val
|
// 如果要上架(val === 0),需要检查 effect 字段
|
||||||
|
if (val === 0) {
|
||||||
|
// 从原始数据或 updateMap 中获取 effect 字段
|
||||||
|
let effect = 1 // 默认能上架
|
||||||
|
if (row && (row.effect === 0 || row.effect === 1)) {
|
||||||
|
effect = row.effect
|
||||||
|
} else if (this.updateMap[rowId] && (this.updateMap[rowId].effect === 0 || this.updateMap[rowId].effect === 1)) {
|
||||||
|
effect = this.updateMap[rowId].effect
|
||||||
|
}
|
||||||
|
|
||||||
|
if (effect === 0) {
|
||||||
|
// effect = 0 表示不能上架,提示用户
|
||||||
|
this.$message.warning('本网站暂时不支持该矿机币种及算法,暂时不能上架!')
|
||||||
|
// 恢复为下架状态
|
||||||
|
if (this.updateMap[rowId]) {
|
||||||
|
this.updateMap[rowId].state = 1
|
||||||
|
}
|
||||||
|
this.updateArr = Object.values(this.updateMap)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 允许切换状态
|
||||||
|
if (this.updateMap[rowId]) {
|
||||||
|
this.updateMap[rowId].state = val
|
||||||
|
// 确保 effect 字段被保存(从原始数据获取)
|
||||||
|
if (row && (row.effect === 0 || row.effect === 1)) {
|
||||||
|
this.updateMap[rowId].effect = row.effect
|
||||||
|
}
|
||||||
|
}
|
||||||
this.updateArr = Object.values(this.updateMap)
|
this.updateArr = Object.values(this.updateMap)
|
||||||
},
|
},
|
||||||
/** 提交 GPU 更新(触发按钮) */
|
/** 提交 GPU 更新(触发按钮) */
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div class="account-purchased-machine-config">
|
<div class="account-purchased-machine-config">
|
||||||
<div class="toolbar">
|
<div class="toolbar">
|
||||||
<div class="left-area">
|
<div class="left-area">
|
||||||
<h2 class="page-title">已购矿机配置</h2>
|
<h2 class="page-title">已购商品</h2>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -15,63 +15,6 @@
|
|||||||
:header-cell-style="{ textAlign: 'left' }"
|
:header-cell-style="{ textAlign: 'left' }"
|
||||||
:cell-style="{ textAlign: 'left' }"
|
:cell-style="{ textAlign: 'left' }"
|
||||||
>
|
>
|
||||||
<el-table-column type="expand" width="46">
|
|
||||||
<template #default="scope">
|
|
||||||
<div class="expand-content">
|
|
||||||
<div class="expand-row" v-if="scope.row.walletAddress">
|
|
||||||
<div class="expand-label">钱包地址:</div>
|
|
||||||
<div class="expand-value">
|
|
||||||
<span class="mono-ellipsis" style="font-family: monospace;">{{ scope.row.walletAddress }}</span>
|
|
||||||
<el-button
|
|
||||||
type="text"
|
|
||||||
size="mini"
|
|
||||||
icon="el-icon-document-copy"
|
|
||||||
@click="handleCopy(scope.row.walletAddress, '钱包地址')"
|
|
||||||
class="copy-btn"
|
|
||||||
>
|
|
||||||
复制
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="expand-row" v-if="scope.row.poolUrl">
|
|
||||||
<div class="expand-label">矿池地址:</div>
|
|
||||||
<div class="expand-value">
|
|
||||||
<span class="mono-ellipsis">{{ scope.row.poolUrl }}</span>
|
|
||||||
<el-button
|
|
||||||
type="text"
|
|
||||||
size="mini"
|
|
||||||
icon="el-icon-document-copy"
|
|
||||||
@click="handleCopy(scope.row.poolUrl, '矿池地址')"
|
|
||||||
class="copy-btn"
|
|
||||||
>
|
|
||||||
复制
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="expand-row" v-if="scope.row.watchUrl">
|
|
||||||
<div class="expand-label">挖矿信息页面地址:</div>
|
|
||||||
<div class="expand-value">
|
|
||||||
<span class="mono-ellipsis">{{ scope.row.watchUrl }}</span>
|
|
||||||
<el-button
|
|
||||||
type="text"
|
|
||||||
size="mini"
|
|
||||||
icon="el-icon-document-copy"
|
|
||||||
@click="handleCopy(scope.row.watchUrl, '挖矿信息页面地址')"
|
|
||||||
class="copy-btn"
|
|
||||||
>
|
|
||||||
复制
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="!scope.row.walletAddress && !scope.row.poolUrl && !scope.row.watchUrl" class="expand-empty">
|
|
||||||
暂无地址信息
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
|
|
||||||
<!-- <el-table-column prop="id" label="ID" width="80" /> -->
|
|
||||||
|
|
||||||
<el-table-column prop="coin" label="币种" width="100">
|
<el-table-column prop="coin" label="币种" width="100">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<span>{{ scope.row.coin || '—' }}</span>
|
<span>{{ scope.row.coin || '—' }}</span>
|
||||||
@@ -90,31 +33,53 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
<el-table-column prop="poolUser" label="矿池用户" min-width="140">
|
<el-table-column prop="walletAddress" label="钱包地址" min-width="200">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<span>{{ scope.row.poolUser || '—' }}</span>
|
<div class="address-cell">
|
||||||
|
<span v-if="scope.row.walletAddress" class="mono-ellipsis" style="font-family: monospace;">{{ scope.row.walletAddress }}</span>
|
||||||
|
<span v-else>—</span>
|
||||||
|
<el-button
|
||||||
|
v-if="scope.row.walletAddress"
|
||||||
|
type="text"
|
||||||
|
size="mini"
|
||||||
|
icon="el-icon-document-copy"
|
||||||
|
@click="handleCopy(scope.row.walletAddress, '钱包地址')"
|
||||||
|
class="copy-btn"
|
||||||
|
>
|
||||||
|
复制
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column prop="poolUrl" label="矿池地址" min-width="200">
|
||||||
|
|
||||||
<el-table-column prop="startTime" label="开始时间" min-width="160">
|
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<span>{{ formatDateTime(scope.row.startTime) }}</span>
|
<div class="address-cell">
|
||||||
|
<span v-if="scope.row.poolUrl" class="mono-ellipsis">{{ scope.row.poolUrl }}</span>
|
||||||
|
<span v-else>—</span>
|
||||||
|
<el-button
|
||||||
|
v-if="scope.row.poolUrl"
|
||||||
|
type="text"
|
||||||
|
size="mini"
|
||||||
|
icon="el-icon-document-copy"
|
||||||
|
@click="handleCopy(scope.row.poolUrl, '矿池地址')"
|
||||||
|
class="copy-btn"
|
||||||
|
>
|
||||||
|
复制
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
<el-table-column prop="endTime" label="结束时间" min-width="160">
|
<el-table-column label="操作" width="120" fixed="right">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<span>{{ formatDateTime(scope.row.endTime) }}</span>
|
<el-button
|
||||||
</template>
|
type="text"
|
||||||
</el-table-column>
|
size="mini"
|
||||||
|
@click="handleViewDetail(scope.row)"
|
||||||
<el-table-column prop="status" label="状态" width="140">
|
>
|
||||||
<template #default="scope">
|
详情
|
||||||
<el-tag :type="getStatusType(scope.row.status)">
|
</el-button>
|
||||||
{{ getStatusText(scope.row.status) }}
|
|
||||||
</el-tag>
|
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
@@ -182,11 +147,7 @@ export default {
|
|||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('获取已购矿机配置失败', e)
|
console.error('获取已购矿机配置失败', e)
|
||||||
this.$message({
|
|
||||||
message: '获取已购矿机配置失败,请重试',
|
|
||||||
type: 'error',
|
|
||||||
showClose: true
|
|
||||||
})
|
|
||||||
this.tableData = []
|
this.tableData = []
|
||||||
this.total = 0
|
this.total = 0
|
||||||
this.totalPage = 0
|
this.totalPage = 0
|
||||||
@@ -283,6 +244,22 @@ export default {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* 查看详情
|
||||||
|
* @param {Object} row - 行数据
|
||||||
|
*/
|
||||||
|
handleViewDetail(row) {
|
||||||
|
// 跳转到详情页面,传递行数据的ID
|
||||||
|
const id = row.id || row.productMachineId || row.machineId
|
||||||
|
if (id) {
|
||||||
|
this.$router.push({
|
||||||
|
name: 'purchasedMachineDetail',
|
||||||
|
params: { id: id }
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.$message.warning('无法获取详情,缺少ID信息')
|
||||||
|
}
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
* 获取状态文本
|
* 获取状态文本
|
||||||
* @param {number|boolean} status - 状态值
|
* @param {number|boolean} status - 状态值
|
||||||
@@ -345,49 +322,21 @@ export default {
|
|||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 展开内容样式 */
|
/* 地址单元格样式 */
|
||||||
.expand-content {
|
.address-cell {
|
||||||
padding: 16px;
|
|
||||||
background-color: #fafafa;
|
|
||||||
}
|
|
||||||
|
|
||||||
.expand-row {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
align-items: center;
|
||||||
margin-bottom: 16px;
|
|
||||||
padding: 12px;
|
|
||||||
background-color: #fff;
|
|
||||||
border-radius: 4px;
|
|
||||||
border: 1px solid #e4e7ed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.expand-row:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.expand-label {
|
|
||||||
min-width: 100px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #606266;
|
|
||||||
margin-right: 12px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.expand-value {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-start;
|
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.expand-value .mono-ellipsis {
|
.address-cell .mono-ellipsis {
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
color: #303133;
|
color: #303133;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
flex: 0 1 auto;
|
flex: 1;
|
||||||
margin-right: 8px;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.copy-btn {
|
.copy-btn {
|
||||||
@@ -400,13 +349,6 @@ export default {
|
|||||||
color: #66b1ff;
|
color: #66b1ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.expand-empty {
|
|
||||||
padding: 20px;
|
|
||||||
text-align: center;
|
|
||||||
color: #909399;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 钱包地址和URL格式化显示样式 */
|
/* 钱包地址和URL格式化显示样式 */
|
||||||
.mono-ellipsis {
|
.mono-ellipsis {
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
|
|||||||
410
power_leasing/src/views/account/purchasedMachineDetail.vue
Normal file
410
power_leasing/src/views/account/purchasedMachineDetail.vue
Normal file
@@ -0,0 +1,410 @@
|
|||||||
|
<template>
|
||||||
|
<div class="purchased-machine-detail">
|
||||||
|
<div class="toolbar">
|
||||||
|
<div class="left-area">
|
||||||
|
<el-button
|
||||||
|
type="text"
|
||||||
|
icon="el-icon-arrow-left"
|
||||||
|
@click="handleBack"
|
||||||
|
class="back-btn"
|
||||||
|
>
|
||||||
|
返回
|
||||||
|
</el-button>
|
||||||
|
<h2 class="page-title">已购商品详情</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="detail-content" v-loading="loading">
|
||||||
|
<!-- 基本信息卡片 -->
|
||||||
|
<el-card v-if="detailData" class="detail-card">
|
||||||
|
<div slot="header" class="card-header">
|
||||||
|
<span>基本信息</span>
|
||||||
|
</div>
|
||||||
|
<el-descriptions :column="2" border>
|
||||||
|
<el-descriptions-item label="挖矿账户">
|
||||||
|
{{ detailData.poolUser || '—' }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="矿池名称">
|
||||||
|
{{ detailData.pool || '—' }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="矿池挖矿地址">
|
||||||
|
<div class="address-item" v-if="detailData.poolUrl">
|
||||||
|
<span class="mono-ellipsis">{{ detailData.poolUrl }}</span>
|
||||||
|
<el-button
|
||||||
|
type="text"
|
||||||
|
size="mini"
|
||||||
|
icon="el-icon-document-copy"
|
||||||
|
@click="handleCopy(detailData.poolUrl, '矿池挖矿地址')"
|
||||||
|
class="copy-btn"
|
||||||
|
>
|
||||||
|
复制
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<span v-else>—</span>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="矿池所挖币种">
|
||||||
|
{{ detailData.coin || '—' }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="币种对应算法">
|
||||||
|
{{ detailData.algorithm || '—' }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="收款钱包">
|
||||||
|
<div class="address-item" v-if="detailData.walletAddress">
|
||||||
|
<span class="mono-ellipsis" style="font-family: monospace;">{{ detailData.walletAddress }}</span>
|
||||||
|
<el-button
|
||||||
|
type="text"
|
||||||
|
size="mini"
|
||||||
|
icon="el-icon-document-copy"
|
||||||
|
@click="handleCopy(detailData.walletAddress, '收款钱包')"
|
||||||
|
class="copy-btn"
|
||||||
|
>
|
||||||
|
复制
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<span v-else>—</span>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="挖矿信息页面地址" v-if="detailData.watchUrl">
|
||||||
|
<div class="address-item">
|
||||||
|
<span class="mono-ellipsis">{{ detailData.watchUrl }}</span>
|
||||||
|
<el-button
|
||||||
|
type="text"
|
||||||
|
size="mini"
|
||||||
|
icon="el-icon-document-copy"
|
||||||
|
@click="handleCopy(detailData.watchUrl, '挖矿信息页面地址')"
|
||||||
|
class="copy-btn"
|
||||||
|
>
|
||||||
|
复制
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<!-- 已购矿机信息列表 -->
|
||||||
|
<el-card v-if="detailData" class="detail-card">
|
||||||
|
<div slot="header" class="card-header">
|
||||||
|
<span>已购矿机信息</span>
|
||||||
|
</div>
|
||||||
|
<el-table
|
||||||
|
:data="purchasedMachinesList"
|
||||||
|
border
|
||||||
|
stripe
|
||||||
|
style="width: 100%"
|
||||||
|
:header-cell-style="{ textAlign: 'left' }"
|
||||||
|
:cell-style="{ textAlign: 'left' }"
|
||||||
|
v-if="purchasedMachinesList && purchasedMachinesList.length > 0"
|
||||||
|
>
|
||||||
|
<el-table-column prop="workerId" label="矿工号" min-width="120">
|
||||||
|
<template #default="scope">
|
||||||
|
<span>{{ scope.row.workerId || '—' }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="power" label="实时算力" min-width="140">
|
||||||
|
<template #default="scope">
|
||||||
|
<span>{{ scope.row.power || '—' }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="recordTime" label="最近实时算力记录时间" min-width="180">
|
||||||
|
<template #default="scope">
|
||||||
|
<span>{{ formatDateTime(scope.row.recordTime) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="startTime" label="挖矿开始时间" min-width="160">
|
||||||
|
<template #default="scope">
|
||||||
|
<span>{{ formatDateTime(scope.row.startTime) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="endTime" label="挖矿结束时间" min-width="160">
|
||||||
|
<template #default="scope">
|
||||||
|
<span>{{ formatDateTime(scope.row.endTime) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="status" label="状态" width="140">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag :type="getStatusType(scope.row.status)">
|
||||||
|
{{ getStatusText(scope.row.status) }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div v-else class="empty-table">
|
||||||
|
<p>暂无矿机信息</p>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<div v-if="!loading && !detailData" class="empty-state">
|
||||||
|
<p>未找到详情信息</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getPurchasedInfoV2 } from '../../api/order'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'PurchasedMachineDetail',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
detailData: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.fetchDetail()
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
/**
|
||||||
|
* 已购矿机列表(从 detailData.purchasedMachines 中提取)
|
||||||
|
*/
|
||||||
|
purchasedMachinesList() {
|
||||||
|
if (!this.detailData) return []
|
||||||
|
return Array.isArray(this.detailData.purchasedMachines) ? this.detailData.purchasedMachines : []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* 获取详情数据
|
||||||
|
*/
|
||||||
|
async fetchDetail() {
|
||||||
|
const id = this.$route.params.id
|
||||||
|
if (!id) {
|
||||||
|
this.$message.error('缺少ID参数')
|
||||||
|
this.handleBack()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loading = true
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
id: id
|
||||||
|
}
|
||||||
|
const res = await getPurchasedInfoV2(params)
|
||||||
|
if (res && (res.code === 0 || res.code === 200)) {
|
||||||
|
// 接口返回的数据可能在 res.data 中
|
||||||
|
this.detailData = res.data || res
|
||||||
|
|
||||||
|
if (!this.detailData) {
|
||||||
|
this.$message.warning('未找到对应的详情信息')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const errorMsg = (res && res.msg) || '获取详情失败'
|
||||||
|
this.$message.error(errorMsg)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('获取详情失败', e)
|
||||||
|
this.$message.error('获取详情失败,请稍后重试')
|
||||||
|
} finally {
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 返回上一页
|
||||||
|
*/
|
||||||
|
handleBack() {
|
||||||
|
this.$router.go(-1)
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 格式化日期时间
|
||||||
|
* @param {string|number} value - 日期时间值
|
||||||
|
* @returns {string} 格式化后的日期时间字符串
|
||||||
|
*/
|
||||||
|
formatDateTime(value) {
|
||||||
|
if (!value) return '—'
|
||||||
|
try {
|
||||||
|
const str = String(value)
|
||||||
|
return str.includes('T') ? str.replace('T', ' ') : str
|
||||||
|
} catch (e) {
|
||||||
|
return String(value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 复制文本到剪贴板
|
||||||
|
* @param {string} text - 需要复制的内容
|
||||||
|
* @param {string} label - 语义标签,用于提示文案
|
||||||
|
*/
|
||||||
|
async handleCopy(text, label = '内容') {
|
||||||
|
if (!text) {
|
||||||
|
this.$message({
|
||||||
|
message: `${label}为空,无法复制`,
|
||||||
|
type: 'warning',
|
||||||
|
showClose: true
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const value = String(text).trim()
|
||||||
|
if (navigator && navigator.clipboard && navigator.clipboard.writeText) {
|
||||||
|
await navigator.clipboard.writeText(value)
|
||||||
|
this.$message({
|
||||||
|
message: `${label}已复制到剪贴板`,
|
||||||
|
type: 'success',
|
||||||
|
showClose: true
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// 备用复制方法
|
||||||
|
const textArea = document.createElement('textarea')
|
||||||
|
textArea.value = value
|
||||||
|
textArea.style.position = 'fixed'
|
||||||
|
textArea.style.left = '-9999px'
|
||||||
|
document.body.appendChild(textArea)
|
||||||
|
textArea.focus()
|
||||||
|
textArea.select()
|
||||||
|
try {
|
||||||
|
document.execCommand('copy')
|
||||||
|
this.$message({
|
||||||
|
message: `${label}已复制到剪贴板`,
|
||||||
|
type: 'success',
|
||||||
|
showClose: true
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
this.$message({
|
||||||
|
message: '复制失败,请手动复制',
|
||||||
|
type: 'error',
|
||||||
|
showClose: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
document.body.removeChild(textArea)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('复制失败', e)
|
||||||
|
this.$message({
|
||||||
|
message: '复制失败,请手动复制',
|
||||||
|
type: 'error',
|
||||||
|
showClose: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 获取状态文本
|
||||||
|
* @param {number|boolean} status - 状态值
|
||||||
|
* @returns {string} 状态文本
|
||||||
|
*/
|
||||||
|
getStatusText(status) {
|
||||||
|
const statusNum = Number(status)
|
||||||
|
if (statusNum === 0) return '租约已到期'
|
||||||
|
if (statusNum === 1) return '挖矿中'
|
||||||
|
if (statusNum === 2) return '卖家矿机启动中'
|
||||||
|
// 兼容旧数据(boolean类型)
|
||||||
|
if (status === true) return '挖矿中'
|
||||||
|
return '未知状态'
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 获取状态标签类型
|
||||||
|
* @param {number|boolean} status - 状态值
|
||||||
|
* @returns {string} el-tag 的类型
|
||||||
|
*/
|
||||||
|
getStatusType(status) {
|
||||||
|
const statusNum = Number(status)
|
||||||
|
if (statusNum === 0) return 'info' // 租约已到期 - 灰色
|
||||||
|
if (statusNum === 1) return 'success' // 挖矿中 - 绿色
|
||||||
|
if (statusNum === 2) return 'warning' // 卖家矿机启动中 - 黄色
|
||||||
|
// 兼容旧数据(boolean类型)
|
||||||
|
if (status === true) return 'success'
|
||||||
|
return 'info'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.purchased-machine-detail {
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-area {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-btn {
|
||||||
|
padding: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-btn:hover {
|
||||||
|
color: #66b1ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #2c3e50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-content {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-card {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
|
.address-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.address-item .mono-ellipsis {
|
||||||
|
font-family: monospace;
|
||||||
|
color: #303133;
|
||||||
|
line-height: 1.5;
|
||||||
|
word-break: break-all;
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-btn {
|
||||||
|
flex-shrink: 0;
|
||||||
|
padding: 0 8px;
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-btn:hover {
|
||||||
|
color: #66b1ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mono-ellipsis {
|
||||||
|
font-family: monospace;
|
||||||
|
display: inline-block;
|
||||||
|
max-width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state {
|
||||||
|
padding: 40px;
|
||||||
|
text-align: center;
|
||||||
|
color: #909399;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-table {
|
||||||
|
padding: 40px;
|
||||||
|
text-align: center;
|
||||||
|
color: #909399;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
border
|
border
|
||||||
stripe
|
stripe
|
||||||
size="small"
|
size="small"
|
||||||
style="width: 100%"
|
style="width: 100%; table-layout: auto;"
|
||||||
:row-key="getRowKey"
|
:row-key="getRowKey"
|
||||||
:expand-row-keys="expandedRowKeys"
|
:expand-row-keys="expandedRowKeys"
|
||||||
:row-class-name="getRowClassName"
|
:row-class-name="getRowClassName"
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
:header-cell-style="{ textAlign: 'left' }"
|
:header-cell-style="{ textAlign: 'left' }"
|
||||||
:cell-style="{ textAlign: 'left' }"
|
:cell-style="{ textAlign: 'left' }"
|
||||||
>
|
>
|
||||||
<el-table-column type="expand" width="46">
|
<el-table-column v-if="rows.length > 0" type="expand" width="46">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<div class="detail-panel">
|
<div class="detail-panel">
|
||||||
<div class="detail-grid">
|
<div class="detail-grid">
|
||||||
@@ -74,10 +74,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="支付时间" min-width="160">
|
<el-table-column label="支付时间" width="160">
|
||||||
<template #default="scope">{{ formatFullTime(scope.row.createTime) }}</template>
|
<template #default="scope">{{ formatFullTime(scope.row.createTime) }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="收款金额(USDT)" min-width="160" align="right">
|
<el-table-column label="收款金额(USDT)" width="140" align="right">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<span class="amount-green">
|
<span class="amount-green">
|
||||||
<el-tooltip
|
<el-tooltip
|
||||||
@@ -96,30 +96,30 @@
|
|||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="收款链" min-width="120">
|
<el-table-column label="收款链" width="140">
|
||||||
<template #default="scope">{{ formatChain(scope.row.toChain) }}</template>
|
<template #default="scope">{{ formatChain(scope.row.toChain) }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="收款币种" min-width="100">
|
<el-table-column label="收款币种" width="100">
|
||||||
<template #default="scope">{{ String(scope.row.coin || '').toUpperCase() }}</template>
|
<template #default="scope">{{ String(scope.row.coin || '').toUpperCase() }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="收款地址" min-width="260">
|
<el-table-column label="收款地址" min-width="200">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<span class="mono-ellipsis" :title="scope.row.toAddress">{{ scope.row.toAddress }}</span>
|
<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>
|
<el-button type="text" size="mini" @click.stop="copy(scope.row.toAddress)">复制</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="交易HASH" min-width="260">
|
<el-table-column label="交易HASH" min-width="200">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<span class="mono-ellipsis" :title="scope.row.txHash">{{ scope.row.txHash }}</span>
|
<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>
|
<el-button type="text" size="mini" @click.stop="copy(scope.row.txHash)" v-if="scope.row.txHash">复制</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="支付状态" min-width="120">
|
<el-table-column label="支付状态" width="120">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-tag :type="getStatusType(scope.row.status)" size="small">{{ getStatusText(scope.row.status) }}</el-tag>
|
<el-tag :type="getStatusType(scope.row.status)" size="small">{{ getStatusText(scope.row.status) }}</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="状态更新时间" min-width="160">
|
<el-table-column label="状态更新时间" width="160">
|
||||||
<template #default="scope">{{ formatFullTime(scope.row.updateTime) }}</template>
|
<template #default="scope">{{ formatFullTime(scope.row.updateTime) }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
@@ -306,8 +306,8 @@ export default {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
.receipt-page { margin: 0; box-sizing: border-box; overflow-x: hidden; }
|
.receipt-page { margin: 0; box-sizing: border-box; overflow-x: hidden; width: 100%; }
|
||||||
.card { background: #fff; border: 1px solid #eee; border-radius: 10px; padding: 12px; box-shadow: 0 4px 18px rgba(0,0,0,0.04); overflow-x: auto; }
|
.card { background: #fff; border: 1px solid #eee; border-radius: 10px; padding: 12px; box-shadow: 0 4px 18px rgba(0,0,0,0.04); width: 100%; box-sizing: border-box; }
|
||||||
.card-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
|
.card-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
|
||||||
.card-title { margin: 0; font-size: 18px; font-weight: 700; color: #2c3e50; }
|
.card-title { margin: 0; font-size: 18px; font-weight: 700; color: #2c3e50; }
|
||||||
.card-actions { display: flex; align-items: center; gap: 8px; }
|
.card-actions { display: flex; align-items: center; gap: 8px; }
|
||||||
@@ -322,6 +322,15 @@ export default {
|
|||||||
.type-red { color: #ef4444; }
|
.type-red { color: #ef4444; }
|
||||||
.pagination { display: flex; justify-content: flex-end; margin-top: 8px; }
|
.pagination { display: flex; justify-content: flex-end; margin-top: 8px; }
|
||||||
|
|
||||||
|
/* 表格容器自适应 */
|
||||||
|
.receipt-page :deep(.el-table) {
|
||||||
|
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-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-item { display: grid; grid-template-columns: 90px 1fr; align-items: center; gap: 8px; }
|
||||||
@@ -331,7 +340,7 @@ export default {
|
|||||||
.detail-value.address { font-family: "Monaco", "Menlo", monospace; word-break: break-all; }
|
.detail-value.address { font-family: "Monaco", "Menlo", monospace; word-break: break-all; }
|
||||||
|
|
||||||
/* 单行等宽省略 */
|
/* 单行等宽省略 */
|
||||||
.mono-ellipsis { font-family: "Monaco", "Menlo", monospace; max-width: 360px; display: inline-block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: middle; }
|
.mono-ellipsis { font-family: "Monaco", "Menlo", monospace; display: inline-block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: middle; max-width: 100%; }
|
||||||
|
|
||||||
/* 可点击行的轻交互提示 */
|
/* 可点击行的轻交互提示 */
|
||||||
.clickable-row:hover > td { background: #f8fafc !important; cursor: pointer; }
|
.clickable-row:hover > td { background: #f8fafc !important; cursor: pointer; }
|
||||||
|
|||||||
118
power_leasing/src/views/account/sellerFundsFlow.vue
Normal file
118
power_leasing/src/views/account/sellerFundsFlow.vue
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
<template>
|
||||||
|
<div class="funds-page">
|
||||||
|
<!-- 页面标题:卖家资金流水(与买家 fundsFlow.vue 视觉对齐) -->
|
||||||
|
<h3 class="title">资金流水</h3>
|
||||||
|
|
||||||
|
<!-- Tab 容器:仅卖家维度(收款记录/提现记录) -->
|
||||||
|
<div class="tabs-card" aria-label="资金流水tab" tabindex="0">
|
||||||
|
<el-tabs v-model="activeTab" @tab-click="handleTabClick">
|
||||||
|
<el-tab-pane label="收款记录" name="receipt" />
|
||||||
|
<el-tab-pane label="提现记录" name="withdraw" />
|
||||||
|
</el-tabs>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
使用 keep-alive + 动态组件:
|
||||||
|
- 避免一次性挂载两个页面导致双请求
|
||||||
|
- 切换 Tab 时缓存组件状态(如分页页码/展开行)
|
||||||
|
-->
|
||||||
|
<keep-alive>
|
||||||
|
<component :is="activeComponentName" />
|
||||||
|
</keep-alive>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import AccountReceiptRecord from './receiptRecord.vue'
|
||||||
|
import AccountWithdrawRecord from './withdrawRecord.vue'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 卖家资金流水聚合页
|
||||||
|
* - 仅整合“收款记录/提现记录”两个页面
|
||||||
|
* - 复用原有页面组件,保证请求接口与内容展示完全不变
|
||||||
|
*/
|
||||||
|
export default {
|
||||||
|
name: 'AccountSellerFundsFlow',
|
||||||
|
components: {
|
||||||
|
AccountReceiptRecord,
|
||||||
|
AccountWithdrawRecord
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
/** @type {'receipt'|'withdraw'} */
|
||||||
|
activeTab: 'receipt'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
/**
|
||||||
|
* 根据当前 tab 返回动态组件名
|
||||||
|
* @returns {'AccountReceiptRecord'|'AccountWithdrawRecord'}
|
||||||
|
*/
|
||||||
|
activeComponentName() {
|
||||||
|
return this.activeTab === 'withdraw' ? 'AccountWithdrawRecord' : 'AccountReceiptRecord'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.syncTabFromRoute()
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
/**
|
||||||
|
* 监听 query.tab 变化(兼容外部链接/旧路由重定向)
|
||||||
|
*/
|
||||||
|
'$route.query.tab': {
|
||||||
|
immediate: false,
|
||||||
|
handler() {
|
||||||
|
this.syncTabFromRoute()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* 从路由 query.tab 同步当前 tab
|
||||||
|
* 允许值:receipt / withdraw;其他值回退为 receipt
|
||||||
|
*/
|
||||||
|
syncTabFromRoute() {
|
||||||
|
const tab = String((this.$route && this.$route.query && this.$route.query.tab) || '')
|
||||||
|
this.activeTab = tab === 'withdraw' ? 'withdraw' : 'receipt'
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Tab 点击:同步写入 URL(replace,避免历史栈过长)
|
||||||
|
*/
|
||||||
|
handleTabClick() {
|
||||||
|
const next = this.activeTab === 'withdraw' ? 'withdraw' : 'receipt'
|
||||||
|
const current = String((this.$route && this.$route.query && this.$route.query.tab) || '')
|
||||||
|
if (current === next) return
|
||||||
|
this.$router.replace({ query: { ...(this.$route.query || {}), tab: next } })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* 外层容器:与买家 fundsFlow.vue 的布局对齐 */
|
||||||
|
.funds-page {
|
||||||
|
margin: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin: 0 0 18px 0;
|
||||||
|
text-align: left;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #2c3e50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs-card {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #eee;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 12px;
|
||||||
|
box-shadow: 0 4px 18px rgba(0, 0, 0, 0.04);
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
@@ -113,6 +113,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { getMyShop } from "@/api/shops";
|
import { getMyShop } from "@/api/shops";
|
||||||
import { getChainAndList, addWalletShopConfig,getProductListForShopWalletConfig,updateProductListForShopWalletConfig } from "../../api/wallet";
|
import { getChainAndList, addWalletShopConfig,getProductListForShopWalletConfig,updateProductListForShopWalletConfig } from "../../api/wallet";
|
||||||
|
import { rsaEncrypt, rsaEncryptSync } from "../../utils/rsaEncrypt";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "AccountShopConfig",
|
name: "AccountShopConfig",
|
||||||
@@ -280,7 +281,33 @@ export default {
|
|||||||
},
|
},
|
||||||
async FetchAddWalletShopConfig(params) {
|
async FetchAddWalletShopConfig(params) {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
const res = await addWalletShopConfig(params);
|
|
||||||
|
// 使用 RSA 加密钱包地址
|
||||||
|
let encryptedPayAddress = params.payAddress
|
||||||
|
if (params.payAddress) {
|
||||||
|
// 优先使用同步版本(如果 JSEncrypt 已加载)
|
||||||
|
const syncEncrypted = rsaEncryptSync(params.payAddress)
|
||||||
|
if (syncEncrypted) {
|
||||||
|
encryptedPayAddress = syncEncrypted
|
||||||
|
} else {
|
||||||
|
// 如果同步版本失败,使用异步版本
|
||||||
|
const asyncEncrypted = await rsaEncrypt(params.payAddress)
|
||||||
|
if (asyncEncrypted) {
|
||||||
|
encryptedPayAddress = asyncEncrypted
|
||||||
|
} else {
|
||||||
|
this.$message.error('钱包地址加密失败,请重试')
|
||||||
|
this.loading = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const encryptedParams = {
|
||||||
|
...params,
|
||||||
|
payAddress: encryptedPayAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await addWalletShopConfig(encryptedParams);
|
||||||
if (res && (res.code === 0 || res.code === 200)) {
|
if (res && (res.code === 0 || res.code === 200)) {
|
||||||
this.$message.success("绑定成功");
|
this.$message.success("绑定成功");
|
||||||
this.$router.push("/account/shops");
|
this.$router.push("/account/shops");
|
||||||
@@ -341,10 +368,25 @@ export default {
|
|||||||
async preCheckBeforeBind() {
|
async preCheckBeforeBind() {
|
||||||
try {
|
try {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
|
// 先关闭弹窗,确保不会显示旧数据
|
||||||
|
this.preCheck.visible = false
|
||||||
|
this.preCheck.rows = []
|
||||||
|
|
||||||
const params = { chain: this.form.chain, payCoin: this.form.payCoin }
|
const params = { chain: this.form.chain, payCoin: this.form.payCoin }
|
||||||
const res = await getProductListForShopWalletConfig(params)
|
const res = await getProductListForShopWalletConfig(params)
|
||||||
|
|
||||||
|
// 检查接口是否成功(code === 0 或 code === 200)
|
||||||
|
const isSuccess = res && (res.code === 0 || res.code === 200)
|
||||||
|
if (!isSuccess) {
|
||||||
|
// 接口失败,直接走绑定流程,不显示弹窗
|
||||||
|
await this.submitBindWithPrice([])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 接口成功,提取数据
|
||||||
const rows = Array.isArray(res && res.data) ? res.data : (Array.isArray(res && res.rows) ? res.rows : [])
|
const rows = Array.isArray(res && res.data) ? res.data : (Array.isArray(res && res.rows) ? res.rows : [])
|
||||||
if (rows && rows.length) {
|
// 确保 rows 是有效数组且有数据
|
||||||
|
if (Array.isArray(rows) && rows.length > 0) {
|
||||||
this.preCheck.rows = rows
|
this.preCheck.rows = rows
|
||||||
// 初始化各币种价格输入
|
// 初始化各币种价格输入
|
||||||
const coins = (this.form.payCoin || '').split(',').map(s => s.trim().toUpperCase()).filter(Boolean)
|
const coins = (this.form.payCoin || '').split(',').map(s => s.trim().toUpperCase()).filter(Boolean)
|
||||||
@@ -360,11 +402,15 @@ export default {
|
|||||||
})
|
})
|
||||||
this.preCheck.visible = true
|
this.preCheck.visible = true
|
||||||
} else {
|
} else {
|
||||||
// 无关联商品,直接绑定并设置(机器列表为空)
|
// 无关联商品或数据为空,直接绑定并设置(机器列表为空),不显示弹窗
|
||||||
|
this.preCheck.visible = false
|
||||||
|
this.preCheck.rows = []
|
||||||
await this.submitBindWithPrice([])
|
await this.submitBindWithPrice([])
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// 接口异常不阻塞绑定流程
|
// 接口异常不阻塞绑定流程,直接走绑定,不显示弹窗
|
||||||
|
this.preCheck.visible = false
|
||||||
|
this.preCheck.rows = []
|
||||||
await this.submitBindWithPrice([])
|
await this.submitBindWithPrice([])
|
||||||
} finally {
|
} finally {
|
||||||
this.loading = false
|
this.loading = false
|
||||||
@@ -427,21 +473,45 @@ export default {
|
|||||||
(g.machineIds || []).forEach(id => { list.push({ productMachineId: id, price: priceStr }) })
|
(g.machineIds || []).forEach(id => { list.push({ productMachineId: id, price: priceStr }) })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 使用 RSA 加密钱包地址
|
||||||
|
let encryptedPayAddress = this.form.payAddress
|
||||||
|
if (this.form.payAddress) {
|
||||||
|
// 优先使用同步版本(如果 JSEncrypt 已加载)
|
||||||
|
const syncEncrypted = rsaEncryptSync(this.form.payAddress)
|
||||||
|
if (syncEncrypted) {
|
||||||
|
encryptedPayAddress = syncEncrypted
|
||||||
|
} else {
|
||||||
|
// 如果同步版本失败,使用异步版本
|
||||||
|
const asyncEncrypted = await rsaEncrypt(this.form.payAddress)
|
||||||
|
if (asyncEncrypted) {
|
||||||
|
encryptedPayAddress = asyncEncrypted
|
||||||
|
} else {
|
||||||
|
this.$message.error('钱包地址加密失败,请重试')
|
||||||
|
this.loading = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
chain: this.form.chain,
|
chain: this.form.chain,
|
||||||
symbol: this.form.payCoin,
|
symbol: this.form.payCoin,
|
||||||
payAddress: this.form.payAddress,
|
// payAddress: this.form.payAddress,//使用未加密地址
|
||||||
|
payAddress: encryptedPayAddress, // 使用加密后的地址
|
||||||
productMachineForWalletConfigVoList: list
|
productMachineForWalletConfigVoList: list
|
||||||
}
|
}
|
||||||
const res = await updateProductListForShopWalletConfig(payload)
|
const res = await updateProductListForShopWalletConfig(payload)
|
||||||
if (res && (res.code === 0 || res.code === 200)) {
|
if (res && (res.code === 0 || res.code === 200)) {
|
||||||
|
|
||||||
this.preCheck.visible = false
|
this.preCheck.visible = false
|
||||||
this.resetPreCheckPrices()
|
this.resetPreCheckPrices()
|
||||||
this.$message.success('绑定成功')
|
this.$message.success('绑定成功')
|
||||||
this.$router.push('/account/shops')
|
this.$router.push('/account/shops')
|
||||||
}else{
|
} else {
|
||||||
this.preCheck.visible = true
|
// 绑定失败,关闭弹窗并显示错误消息
|
||||||
|
this.preCheck.visible = false
|
||||||
|
this.resetPreCheckPrices()
|
||||||
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// 错误交由全局拦截或简单提示
|
// 错误交由全局拦截或简单提示
|
||||||
|
|||||||
@@ -306,6 +306,7 @@
|
|||||||
import { getWalletInfo, withdrawBalance ,balanceRechargeList,balanceWithdrawList,getRecentlyTransaction} from '@/api/wallet'
|
import { getWalletInfo, withdrawBalance ,balanceRechargeList,balanceWithdrawList,getRecentlyTransaction} from '@/api/wallet'
|
||||||
|
|
||||||
import { getChainAndList,bindWallet } from "../../api/wallet";
|
import { getChainAndList,bindWallet } from "../../api/wallet";
|
||||||
|
import { rsaEncrypt, rsaEncryptSync } from '@/utils/rsaEncrypt'
|
||||||
export default {
|
export default {
|
||||||
name: 'WalletPage',
|
name: 'WalletPage',
|
||||||
data() {
|
data() {
|
||||||
@@ -893,13 +894,55 @@ export default {
|
|||||||
this.withdrawLoading = true
|
this.withdrawLoading = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
/**
|
||||||
|
* 提现地址 RSA 加密(与“钱包绑定”页面保持一致:同步优先,异步兜底)
|
||||||
|
* - toAddress: 用户输入的收款地址
|
||||||
|
* - fromAddress: 当前钱包来源地址(后端可能用于校验来源)
|
||||||
|
*/
|
||||||
|
const toAddressPlain = String(this.withdrawForm.toAddress || '').trim()
|
||||||
|
const fromAddressPlain = String((this.WalletData && this.WalletData.fromAddress) || '').trim()
|
||||||
|
|
||||||
|
/** @type {string} */
|
||||||
|
let encryptedToAddress = toAddressPlain
|
||||||
|
if (encryptedToAddress) {
|
||||||
|
const syncEncrypted = rsaEncryptSync(encryptedToAddress)
|
||||||
|
if (syncEncrypted) {
|
||||||
|
encryptedToAddress = syncEncrypted
|
||||||
|
} else {
|
||||||
|
const asyncEncrypted = await rsaEncrypt(encryptedToAddress)
|
||||||
|
if (asyncEncrypted) {
|
||||||
|
encryptedToAddress = asyncEncrypted
|
||||||
|
} else {
|
||||||
|
this.$message.error('钱包地址加密失败,请重试')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {string} */
|
||||||
|
let encryptedFromAddress = fromAddressPlain
|
||||||
|
if (encryptedFromAddress) {
|
||||||
|
const syncEncrypted = rsaEncryptSync(encryptedFromAddress)
|
||||||
|
if (syncEncrypted) {
|
||||||
|
encryptedFromAddress = syncEncrypted
|
||||||
|
} else {
|
||||||
|
const asyncEncrypted = await rsaEncrypt(encryptedFromAddress)
|
||||||
|
if (asyncEncrypted) {
|
||||||
|
encryptedFromAddress = asyncEncrypted
|
||||||
|
} else {
|
||||||
|
this.$message.error('钱包地址加密失败,请重试')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 调用后台提现API,添加谷歌验证码参数
|
// 调用后台提现API,添加谷歌验证码参数
|
||||||
const res = await withdrawBalance({
|
const res = await withdrawBalance({
|
||||||
toChain: (this.WalletData && (this.WalletData.fromChain || this.WalletData.chain)) || this.withdrawForm.toChain,
|
toChain: (this.WalletData && (this.WalletData.fromChain || this.WalletData.chain)) || this.withdrawForm.toChain,
|
||||||
toSymbol: (this.WalletData && (this.WalletData.fromSymbol || this.WalletData.coin)) || this.withdrawForm.toSymbol,
|
toSymbol: (this.WalletData && (this.WalletData.fromSymbol || this.WalletData.coin)) || this.withdrawForm.toSymbol,
|
||||||
amount: parseFloat(this.withdrawForm.amount),
|
amount: parseFloat(this.withdrawForm.amount),
|
||||||
toAddress: this.withdrawForm.toAddress,
|
toAddress: encryptedToAddress,
|
||||||
fromAddress: (this.WalletData && this.WalletData.fromAddress) || '',
|
fromAddress: encryptedFromAddress,
|
||||||
code: this.withdrawForm.googleCode // 添加谷歌验证码
|
code: this.withdrawForm.googleCode // 添加谷歌验证码
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
:row-key="'id'" reserve-selection
|
:row-key="'id'" reserve-selection
|
||||||
:ref="'innerTable-' + shopScope.row.id"
|
:ref="'innerTable-' + shopScope.row.id"
|
||||||
@selection-change="sels => handleShopInnerSelectionChange(shopScope.row, sels)"
|
@selection-change="sels => handleShopInnerSelectionChange(shopScope.row, sels)"
|
||||||
|
:row-class-name="getMachineRowClassName"
|
||||||
:header-cell-style="{ textAlign: 'left' }" :cell-style="{ textAlign: 'left' }">
|
:header-cell-style="{ textAlign: 'left' }" :cell-style="{ textAlign: 'left' }">
|
||||||
<el-table-column type="selection" width="46" :selectable="row => isRowSelectableByShop(shopScope.row, row)" />
|
<el-table-column type="selection" width="46" :selectable="row => isRowSelectableByShop(shopScope.row, row)" />
|
||||||
<el-table-column prop="name" label="商品名称" />
|
<el-table-column prop="name" label="商品名称" />
|
||||||
@@ -711,6 +712,7 @@
|
|||||||
import { getGoodsList, deleteBatchGoods as apiDeleteBatchGoods ,deleteBatchGoodsForIsDeleteV2,deleteBatchGoodsV2,deleteBatchGoodsForIsDelete,getGoodsListV2} from '../../api/shoppingCart'
|
import { getGoodsList, deleteBatchGoods as apiDeleteBatchGoods ,deleteBatchGoodsForIsDeleteV2,deleteBatchGoodsV2,deleteBatchGoodsForIsDelete,getGoodsListV2} from '../../api/shoppingCart'
|
||||||
import { addOrders,cancelOrder,getOrdersByIds,getOrdersByStatus , getMachineSupportPool,getChainAndListForSeller,getCoinPrice,getMachineSupportCoinAndAlgorithm, addOrdersV2} from '../../api/order'
|
import { addOrders,cancelOrder,getOrdersByIds,getOrdersByStatus , getMachineSupportPool,getChainAndListForSeller,getCoinPrice,getMachineSupportCoinAndAlgorithm, addOrdersV2} from '../../api/order'
|
||||||
import { truncateAmountByCoin, truncateTo6 } from '../../utils/amount'
|
import { truncateAmountByCoin, truncateTo6 } from '../../utils/amount'
|
||||||
|
import { rsaEncrypt, rsaEncryptSync } from '../../utils/rsaEncrypt'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Cart',
|
name: 'Cart',
|
||||||
@@ -1081,10 +1083,12 @@ export default {
|
|||||||
: (Array.isArray(sp && sp.productMachineDtoList) ? sp.productMachineDtoList : [])
|
: (Array.isArray(sp && sp.productMachineDtoList) ? sp.productMachineDtoList : [])
|
||||||
// 记录每台机器原始租期,便于后续判断是否修改
|
// 记录每台机器原始租期,便于后续判断是否修改
|
||||||
try { machines.forEach(m => { if (m && m._origLeaseTime == null) m._origLeaseTime = Number(m.leaseTime || 1) }) } catch (e) { /* noop */ }
|
try { machines.forEach(m => { if (m && m._origLeaseTime == null) m._origLeaseTime = Number(m.leaseTime || 1) }) } catch (e) { /* noop */ }
|
||||||
|
// 对机器列表进行排序:下架的排到最下面
|
||||||
|
const sortedMachines = this.sortMachinesByShelfStatus(machines)
|
||||||
return {
|
return {
|
||||||
...sp,
|
...sp,
|
||||||
id: sp && sp.id != null ? String(sp.id) : `shop-${idx}`,
|
id: sp && sp.id != null ? String(sp.id) : `shop-${idx}`,
|
||||||
productMachineDtoList: machines
|
productMachineDtoList: sortedMachines
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.shops = normalized
|
this.shops = normalized
|
||||||
@@ -1153,10 +1157,39 @@ export default {
|
|||||||
isRowSelectable(row, index) {
|
isRowSelectable(row, index) {
|
||||||
return !(Number(row && row.del) === 1 || Number(row && row.state) === 1)
|
return !(Number(row && row.del) === 1 || Number(row && row.state) === 1)
|
||||||
},
|
},
|
||||||
// 判断机器是否“上架”(用于未展开时的自动筛选)
|
// 判断机器是否"上架"(用于未展开时的自动筛选)
|
||||||
isOnShelf(row) {
|
isOnShelf(row) {
|
||||||
return !(Number(row && row.del) === 1 || Number(row && row.state) === 1)
|
return !(Number(row && row.del) === 1 || Number(row && row.state) === 1)
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* 对机器列表进行排序:下架的排到最下面
|
||||||
|
* @param {Array} machines - 机器列表
|
||||||
|
* @returns {Array} 排序后的机器列表
|
||||||
|
*/
|
||||||
|
sortMachinesByShelfStatus(machines) {
|
||||||
|
if (!Array.isArray(machines)) return []
|
||||||
|
// 上架的机器排前面,下架的排后面
|
||||||
|
return [...machines].sort((a, b) => {
|
||||||
|
const aOnShelf = this.isOnShelf(a)
|
||||||
|
const bOnShelf = this.isOnShelf(b)
|
||||||
|
// 如果都是上架或都是下架,保持原有顺序
|
||||||
|
if (aOnShelf === bOnShelf) return 0
|
||||||
|
// 上架的排前面(返回-1),下架的排后面(返回1)
|
||||||
|
return aOnShelf ? -1 : 1
|
||||||
|
})
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 获取机器表格行的类名(用于样式)
|
||||||
|
* @param {Object} param - 包含 row, rowIndex 的对象
|
||||||
|
* @returns {string} 行类名
|
||||||
|
*/
|
||||||
|
getMachineRowClassName({ row }) {
|
||||||
|
// 如果机器已下架,返回下架样式类
|
||||||
|
if (!this.isOnShelf(row)) {
|
||||||
|
return 'off-shelf-row'
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
},
|
||||||
// 获取本地最大可租赁天数,默认 365
|
// 获取本地最大可租赁天数,默认 365
|
||||||
getRowMaxLeaseDaysLocal(row) {
|
getRowMaxLeaseDaysLocal(row) {
|
||||||
const raw = row && row.maxLeaseDays
|
const raw = row && row.maxLeaseDays
|
||||||
@@ -1510,11 +1543,13 @@ export default {
|
|||||||
try {
|
try {
|
||||||
withShopKeys.forEach(sp => this.ensureDefaultPaySelection(sp))
|
withShopKeys.forEach(sp => this.ensureDefaultPaySelection(sp))
|
||||||
} catch (e) { /* noop */ }
|
} catch (e) { /* noop */ }
|
||||||
// 记录每台机器的原始租期,便于判断是否被修改
|
// 记录每台机器的原始租期,便于判断是否被修改,并对机器列表进行排序
|
||||||
try {
|
try {
|
||||||
withShopKeys.forEach(sp => {
|
withShopKeys.forEach(sp => {
|
||||||
const list = Array.isArray(sp.productMachineDtoList) ? sp.productMachineDtoList : []
|
const list = Array.isArray(sp.productMachineDtoList) ? sp.productMachineDtoList : []
|
||||||
list.forEach(m => { if (m && m._origLeaseTime == null) m._origLeaseTime = Number(m.leaseTime || 1) })
|
list.forEach(m => { if (m && m._origLeaseTime == null) m._origLeaseTime = Number(m.leaseTime || 1) })
|
||||||
|
// 对机器列表进行排序:下架的排到最下面
|
||||||
|
sp.productMachineDtoList = this.sortMachinesByShelfStatus(list)
|
||||||
})
|
})
|
||||||
} catch (e) { /* noop */ }
|
} catch (e) { /* noop */ }
|
||||||
this.shops = withShopKeys
|
this.shops = withShopKeys
|
||||||
@@ -1765,6 +1800,7 @@ export default {
|
|||||||
// 为每台机器创建独立的配置项
|
// 为每台机器创建独立的配置项
|
||||||
orderMiningInfoDtoList.push({
|
orderMiningInfoDtoList.push({
|
||||||
coinConfigId: coinConfigId,
|
coinConfigId: coinConfigId,
|
||||||
|
coin: config.coin || '', // 用户选择的币种/算法框里的币种
|
||||||
poolName: config.poolName || '',
|
poolName: config.poolName || '',
|
||||||
poolUser: poolUser,
|
poolUser: poolUser,
|
||||||
walletAddress: walletAddress,
|
walletAddress: walletAddress,
|
||||||
@@ -1773,7 +1809,30 @@ export default {
|
|||||||
machineId: String(machine.id) // 使用 machineId(string)而不是 machineIds(array)
|
machineId: String(machine.id) // 使用 machineId(string)而不是 machineIds(array)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 结算提交前:对配置中的钱包地址进行 RSA 加密(字段名不变,仍为 walletAddress)
|
||||||
|
* 规则:同步优先(rsaEncryptSync),失败再异步兜底(rsaEncrypt);仍失败则提示并中断结算
|
||||||
|
*/
|
||||||
|
for (const item of (orderMiningInfoDtoList || [])) {
|
||||||
|
const plainWalletAddress = String(item && item.walletAddress ? item.walletAddress : '').trim()
|
||||||
|
// 空地址无需加密(例如 walletMining=false 时可能仅提交 poolUser)
|
||||||
|
if (!plainWalletAddress) continue
|
||||||
|
|
||||||
|
/** @type {string|null} */
|
||||||
|
let encryptedWalletAddress = rsaEncryptSync(plainWalletAddress)
|
||||||
|
if (!encryptedWalletAddress) {
|
||||||
|
encryptedWalletAddress = await rsaEncrypt(plainWalletAddress)
|
||||||
|
}
|
||||||
|
if (!encryptedWalletAddress) {
|
||||||
|
this.$message.error('钱包地址加密失败,请重试')
|
||||||
|
// 中断前恢复勾选 UI(与其它失败分支保持一致)
|
||||||
|
this.reapplySelectionsForPendingShop()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
item.walletAddress = encryptedWalletAddress
|
||||||
|
}
|
||||||
|
|
||||||
this.creatingOrder = true
|
this.creatingOrder = true
|
||||||
try {
|
try {
|
||||||
const res = await this.fetchAddOrdersV2(orderInfoVoList, orderMiningInfoDtoList, googleCode)
|
const res = await this.fetchAddOrdersV2(orderInfoVoList, orderMiningInfoDtoList, googleCode)
|
||||||
@@ -3317,4 +3376,13 @@ export default {
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 下架商品行样式 */
|
||||||
|
:deep(.off-shelf-row) {
|
||||||
|
background-color: #f5f5f5 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.off-shelf-row:hover > td) {
|
||||||
|
background-color: #e8e8e8 !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -215,9 +215,9 @@ export default {
|
|||||||
return { text: t.text, full: t.full, truncated: t.truncated }
|
return { text: t.text, full: t.full, truncated: t.truncated }
|
||||||
}
|
}
|
||||||
if (col.type === 'hashrate') {
|
if (col.type === 'hashrate') {
|
||||||
const unit = col.unit ? ` ${col.unit}` : ''
|
// 接口已返回带单位的内容,直接返回字符串值,不拼接单位
|
||||||
const t = truncateTo6(val)
|
const s = String(val || '')
|
||||||
return { text: `${t.text}${unit}`, full: `${t.full}${unit}`, truncated: t.truncated }
|
return { text: s, full: s, truncated: false }
|
||||||
}
|
}
|
||||||
if (col.type === 'days') {
|
if (col.type === 'days') {
|
||||||
const n = Number(val)
|
const n = Number(val)
|
||||||
|
|||||||
Binary file not shown.
1
power_leasing/test/css/app.262a57c7.css
Normal file
1
power_leasing/test/css/app.262a57c7.css
Normal file
File diff suppressed because one or more lines are too long
@@ -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"><link rel="icon" href="/favicon.ico"><title>power_leasing</title><script defer="defer" src="/js/chunk-vendors.f4da7ffe.js"></script><script defer="defer" src="/js/app.551d07c7.js"></script><link href="/css/chunk-vendors.10dd4e95.css" rel="stylesheet"><link href="/css/app.5ed3e526.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"><link rel="icon" href="/favicon.ico"><title>power_leasing</title><script defer="defer" src="/js/chunk-vendors.d698a8ca.js"></script><script defer="defer" src="/js/app.58e678d6.js"></script><link href="/css/chunk-vendors.10dd4e95.css" rel="stylesheet"><link href="/css/app.262a57c7.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>
|
||||||
2
power_leasing/test/js/app.58e678d6.js
Normal file
2
power_leasing/test/js/app.58e678d6.js
Normal file
File diff suppressed because one or more lines are too long
1
power_leasing/test/js/app.58e678d6.js.map
Normal file
1
power_leasing/test/js/app.58e678d6.js.map
Normal file
File diff suppressed because one or more lines are too long
43
power_leasing/test/js/chunk-vendors.d698a8ca.js
Normal file
43
power_leasing/test/js/chunk-vendors.d698a8ca.js
Normal file
File diff suppressed because one or more lines are too long
1
power_leasing/test/js/chunk-vendors.d698a8ca.js.map
Normal file
1
power_leasing/test/js/chunk-vendors.d698a8ca.js.map
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user