Compare commits

...

10 Commits

Author SHA1 Message Date
2e09a787fd 取款计算金额 2026-06-29 14:50:40 +08:00
e9fbb956e4 右键 2026-06-26 15:28:10 +08:00
3c7c5ae69e 右键 2026-06-26 15:27:58 +08:00
cf6de1d1d7 k线 2026-06-25 17:52:12 +08:00
8d83be7cb3 k线 2026-06-25 16:38:55 +08:00
Songsl 22eb76a440 开发环境配置,转账--三方转账转入账号问题修复 2026-06-25 10:35:23 +08:00
e14dd66e2b 渠道接口换 2026-06-24 14:18:12 +08:00
7010ed1238 打包测试环境 2026-06-24 13:25:28 +08:00
cc502b2ac6 打包测试环境 2026-06-24 09:52:40 +08:00
de39f3a54f bug 2026-06-23 13:10:14 +08:00
18 changed files with 195 additions and 191 deletions

View File

@ -1,14 +1,17 @@
# 开发环境标识Vite 内置变量,无需修改) # 测试环境标识
NODE_ENV = development NODE_ENV = test
# 开发环境接口基础地址(对应你配置的 https://adminapi.apimcapital.top/api # 测试环境接口基础地址
VITE_API_BASE_URL = https://adminapi.apimcapital.top/api VITE_API_BASE_URL = https://adminapi.duxiang.wiki/api
# 请求超时时间(毫秒) # 请求超时时间(毫秒)
VITE_REQUEST_TIMEOUT = 10000 VITE_REQUEST_TIMEOUT = 10000
# 可选:开发环境其他配置(如是否开启调试) # 可选:测试环境其他配置
VITE_DEBUG = true VITE_DEBUG = true
# 交易mqtt # 交易mqtt
VITE_MQTT_TRANSACTION_HOST=wss://adminapi.apimcapital.top/mqtt VITE_MQTT_TRANSACTION_HOST = wss://adminapi.duxiang.wiki/mqtt
# exe 下载基础地址
VITE_EXE_DOWNLOAD_BASE_URL = https://adminh5.duxiang.wiki

View File

@ -11,4 +11,7 @@ VITE_REQUEST_TIMEOUT = 10000
VITE_DEBUG = false VITE_DEBUG = false
# 交易mqtt # 交易mqtt
VITE_MQTT_TRANSACTION_HOST=wss://adminapi.apimcapital.top/mqtt VITE_MQTT_TRANSACTION_HOST=wss://adminapi.apimcapital.top/mqtt
# exe 下载基础地址
VITE_EXE_DOWNLOAD_BASE_URL = https://adminh5.apimcapital.top

17
.env.test Normal file
View File

@ -0,0 +1,17 @@
# 测试环境标识
NODE_ENV = test
# 测试环境接口基础地址
VITE_API_BASE_URL = https://adminapi.duxiang.wiki/api
# 请求超时时间(毫秒)
VITE_REQUEST_TIMEOUT = 10000
# 可选:测试环境其他配置
VITE_DEBUG = true
# 交易mqtt
VITE_MQTT_TRANSACTION_HOST = wss://adminapi.duxiang.wiki/mqtt
# exe 下载基础地址
VITE_EXE_DOWNLOAD_BASE_URL = https://adminh5.duxiang.wiki

View File

@ -5,8 +5,10 @@
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"dev:test": "vite --mode test",
"build": "run-p type-check \"build-only {@}\" --", "build": "run-p type-check \"build-only {@}\" --",
"build:prod": "vite build --mode production", "build:prod": "vite build --mode production",
"build:test": "vite build --mode test",
"preview": "vite preview", "preview": "vite preview",
"build-only": "vite build", "build-only": "vite build",
"type-check": "vue-tsc --build", "type-check": "vue-tsc --build",

View File

@ -279,15 +279,26 @@ const emit = defineEmits<{
(e: 'filter-change', filters: { [key: string]: any }): void (e: 'filter-change', filters: { [key: string]: any }): void
}>() }>()
// Store
const route = useRoute() const route = useRoute()
const userStore = useUserStore() const userStore = useUserStore()
// key 使
const CACHE_VERSION = 'v1'
// key // key
const getCacheKey = () => { const getCacheKey = () => {
const baseKey = props.columnSettingStorageKey || String(route.name || route.path) const baseKey = props.columnSettingStorageKey
const userId = String(userStore.userInfo?.id || 'guest') || String((route.name as string) || route.path || 'default')
return `table-column-setting-${baseKey}-${userId}` const userId = String((userStore.userInfo as any)?.id || 'guest')
return `table-column-setting-${CACHE_VERSION}-${baseKey}-${userId}`
}
//
const getColumnKey = (col: TableColumn): string => {
if (col.prop) return `prop:${col.prop}`
if (col.slotName) return `slot:${col.slotName}`
if (col.type) return `type:${col.type}`
return `label:${col.label}`
} }
// //
@ -306,63 +317,59 @@ const finalColumns = computed(() => {
}) })
// //
// props.columns
// - visible / sortOrder
// - visible true
// -
const initColumns = () => { const initColumns = () => {
if (!props.columns || props.columns.length === 0) { if (!props.columns || props.columns.length === 0) {
localColumns.value = [] localColumns.value = []
return return
} }
//
const cacheKey = getCacheKey() const cacheKey = getCacheKey()
let cachedConfig: any[] = []
const cached = localStorage.getItem(cacheKey) const cached = localStorage.getItem(cacheKey)
if (cached) { if (cached) {
try { try {
const cachedConfig = JSON.parse(cached) const parsed = JSON.parse(cached)
// sortOrder if (Array.isArray(parsed)) cachedConfig = parsed
cachedConfig.sort((a: any, b: any) => (a.sortOrder || 0) - (b.sortOrder || 0))
//
localColumns.value = cachedConfig.map((cached: any) => {
// prop
const originalCol = props.columns?.find((c) => c.prop === cached.prop)
if (originalCol) {
return { ...originalCol, ...cached }
}
// prop slotName
if (cached.slotName) {
const slotCol = props.columns?.find((c) => c.slotName === cached.slotName)
if (slotCol) {
return { ...slotCol, ...cached }
}
}
return null
}).filter(Boolean)
} catch { } catch {
// 使 cachedConfig = []
localColumns.value = props.columns.map((col) => ({
...col,
visible: col.visible !== false,
fixed: col.fixed || false,
}))
} }
} else {
// 使
localColumns.value = props.columns.map((col) => ({
...col,
visible: col.visible !== false,
fixed: col.fixed || false,
}))
} }
// key -> { visible, sortOrder }
const cachedMap = new Map<string, { visible: boolean; sortOrder: number }>()
cachedConfig.forEach((c, idx) => {
cachedMap.set(c.key || getColumnKey(c), {
visible: c.visible !== false,
sortOrder: typeof c.sortOrder === 'number' ? c.sortOrder : idx,
})
})
// visible / sortOrder
const maxOrder = Math.max(-1, ...Array.from(cachedMap.values()).map((v) => v.sortOrder))
let nextOrder = maxOrder + 1
localColumns.value = props.columns.map((col) => {
const hit = cachedMap.get(getColumnKey(col))
if (hit) {
return { ...col, visible: hit.visible, sortOrder: hit.sortOrder }
}
return { ...col, visible: col.visible !== false, sortOrder: nextOrder++ }
})
// sortOrder
localColumns.value.sort((a, b) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0))
} }
// localStorage // localStorage key / visible / sortOrder
const saveToStorage = () => { const saveToStorage = () => {
const config = localColumns.value.map((col, index) => ({ const config = localColumns.value.map((col, index) => ({
prop: col.prop, key: getColumnKey(col),
visible: col.visible, visible: col.visible !== false,
label: col.label,
width: col.width,
slotName: col.slotName,
sortOrder: index, sortOrder: index,
})) }))
localStorage.setItem(getCacheKey(), JSON.stringify(config)) localStorage.setItem(getCacheKey(), JSON.stringify(config))
@ -390,48 +397,33 @@ const handleReset = () => {
} }
// columns // columns
// columns /
//
let lastColumnsKey = ''
watch( watch(
() => props.columns, () => props.columns,
(newColumns) => { (newColumns) => {
if (newColumns && newColumns.length > 0) { if (!newColumns || newColumns.length === 0) return
// // + key
if (localColumns.value.length === 0) { const newKey = newColumns.map((c) => getColumnKey(c)).join('|')
initColumns() if (newKey === lastColumnsKey && localColumns.value.length > 0) return
} lastColumnsKey = newKey
} initColumns()
}, },
{ immediate: true } { immediate: true, deep: true }
) )
// //
onMounted(() => { onMounted(() => {
// props.columns if (props.columns && props.columns.length > 0 && localColumns.value.length === 0) {
setTimeout(() => { initColumns()
if (props.columns && props.columns.length > 0 && localColumns.value.length === 0) { }
initColumns()
}
}, 100)
}) })
// tableData const tableRef = ref<TableInstance>()
watch(
() => props.tableData,
() => {
if (props.singleSelect) {
selectedRadioRow.value = null
}
}
)
// tableData //
watch( const selectedRow = ref<any>(null)
() => props.tableData,
() => {
if (props.singleSelect) {
selectedRadioRow.value = null
}
}
)
// //
const handleSelectionChange = (rows: any[]) => { const handleSelectionChange = (rows: any[]) => {
@ -444,11 +436,6 @@ const handleCurrentChange = (row: any) => {
emit('current-change', row) emit('current-change', row)
} }
const tableRef = ref<TableInstance>()
//
const selectedRow = ref<any>(null)
// //
const handleRowClick = (row: any) => { const handleRowClick = (row: any) => {
if (!row) return if (!row) return
@ -467,23 +454,17 @@ const handleRowContextmenu = (row: any, column: any, event: MouseEvent) => {
emit('row-contextmenu', row, column, event) emit('row-contextmenu', row, column, event)
} }
// tableData // tableData /
watch( watch(
() => props.tableData, () => props.tableData,
() => { () => {
if (props.singleSelect) { if (props.singleSelect) {
selectedRadioRow.value = null selectedRadioRow.value = null
} }
}
)
// tableData
watch(
() => props.tableData,
() => {
selectedRow.value = null selectedRow.value = null
tableRef.value?.setCurrentRow(null) tableRef.value?.setCurrentRow(null)
}, },
{ deep: true }, { deep: true }
) )
// //
@ -561,6 +542,11 @@ defineExpose({
} }
} }
::v-deep(.cell) {
display: flex;
justify-content: center;
align-items: center;
}
.column-setting-content { .column-setting-content {
.setting-header { .setting-header {
display: flex; display: flex;

View File

@ -48,8 +48,6 @@ export const useTransactionStore = defineStore('transaction', () => {
const positionList = ref<PositionItem[]>([]) const positionList = ref<PositionItem[]>([])
/** 做市商浮动盈亏数据 Map<account_id, float_profit_loss> */ /** 做市商浮动盈亏数据 Map<account_id, float_profit_loss> */
const marketFloatProfitMap = ref<Map<string, string>>(new Map()) const marketFloatProfitMap = ref<Map<string, string>>(new Map())
/** 做市商平仓盈亏数据 Map<account_id, close_profit_loss> */
const marketCloseProfitMap = ref<Map<string, string>>(new Map())
// ------------------ 初始化 ------------------ // ------------------ 初始化 ------------------
@ -120,23 +118,20 @@ export const useTransactionStore = defineStore('transaction', () => {
* account_id:float_profit_loss:close_profit_loss (84:0.000000:0.000000) * account_id:float_profit_loss:close_profit_loss (84:0.000000:0.000000)
*/ */
const handleMarketFloatProfit = (data: string): void => { const handleMarketFloatProfit = (data: string): void => {
console.log('data:', data);
if (!data) return if (!data) return
const fields = data.split(':') const fields = data.split(':')
if (fields.length < 3) return if (fields.length < 2) return
const account_id = fields[0] const account_id = fields[0]
const float_profit_loss = fields[1] as string const float_profit_loss = fields[1] as string
const close_profit_loss = fields[2] as string
// 更新浮动盈亏 Map // 更新浮动盈亏 Map
marketFloatProfitMap.value.set(account_id, float_profit_loss) marketFloatProfitMap.value.set(account_id, float_profit_loss)
console.log('marketFloatProfitMap.value:', marketFloatProfitMap.value);
marketFloatProfitMap.value = new Map(marketFloatProfitMap.value) marketFloatProfitMap.value = new Map(marketFloatProfitMap.value)
// 更新平仓盈亏 Map
marketCloseProfitMap.value.set(account_id, close_profit_loss)
marketCloseProfitMap.value = new Map(marketCloseProfitMap.value)
} }
@ -178,7 +173,6 @@ export const useTransactionStore = defineStore('transaction', () => {
mqttClient?.disconnect() mqttClient?.disconnect()
positionList.value = [] positionList.value = []
marketFloatProfitMap.value.clear() marketFloatProfitMap.value.clear()
marketCloseProfitMap.value.clear()
} }
/** /**
@ -193,7 +187,6 @@ export const useTransactionStore = defineStore('transaction', () => {
mqttClient, mqttClient,
positionList, positionList,
marketFloatProfitMap, marketFloatProfitMap,
marketCloseProfitMap,
initMarketConditionsMQTT, initMarketConditionsMQTT,
getPositionList, getPositionList,
requestMarketFloatProfit, requestMarketFloatProfit,

View File

@ -47,7 +47,7 @@ export const tableColumns = [
// 手续费分成比例 // 手续费分成比例
{ prop: 'commissionRatio', label: '手续费分成比例', slotName: 'commissionRatio', width: 140 }, { prop: 'commissionRatio', label: '手续费分成比例', slotName: 'commissionRatio', width: 140 },
// 注册时间 // 注册时间
{ prop: 'createTime', label: '注册时间', sortable: true, minWidth: 180 }, { prop: 'createTime', label: '注册时间', sortable: true, minWidth: '200px' },
// 操作 // 操作
{ label: '操作', slotName: 'action', width: 200 } { label: '操作', slotName: 'action', width: 200 }
] ]

View File

@ -295,9 +295,9 @@ const sendCodeApi = async (receiver: string) => {
params.email = receiver params.email = receiver
} else { } else {
params.mobile = receiver params.mobile = receiver
// // foreign_userInfo mobile_area
const match = receiver.match(/^\+(\d{1,4})/) const foreignUserInfo = getStorage<any>('foreign_userInfo')
params.mobile_area = match ? '+' + match[1] : '+86' params.mobile_area = foreignUserInfo?.mobile_area || '+86'
} }
return userStore.sendCode(params) return userStore.sendCode(params)
} }
@ -399,21 +399,6 @@ const handleTransferTabClick = async (type: string) => {
transferType.value = type transferType.value = type
await resetTransferForm() await resetTransferForm()
} }
/**
* @description 切换验证方式
* @param type 验证方式
*/
const handleVerifyParams = (type: string) => {
if (type === 'external') {
if (verifyForm.value.verifyType === 'email') {
return userInfo.email
} else {
return userInfo.mobile
}
} else {
return transferForm.value.transferInAccount
}
}
/** /**
* @description 提交转账表单 * @description 提交转账表单
@ -428,7 +413,7 @@ const handleSubmit = async () => {
const params = { const params = {
type: transferType.value === 'internal' ? '1' : '2', type: transferType.value === 'internal' ? '1' : '2',
from: String(transferForm.value.transferOutAccount) + '', from: String(transferForm.value.transferOutAccount) + '',
to: handleVerifyParams(transferType.value) + '', to: transferForm.value.transferInAccount + '',
amount: transferForm.value.transferAmount, amount: transferForm.value.transferAmount,
verify_type: '', verify_type: '',
verify_code: '', verify_code: '',

View File

@ -19,7 +19,7 @@
<div class="amount-section"> <div class="amount-section">
<div class="section-label"> <div class="section-label">
{{ t('withdraw.dialog.amount') }} {{ t('withdraw.dialog.amount') }}
<span class="max-balance">最大可取{{ totalWalletBalance }} {{ currentCurrency }}</span> <span class="max-balance">最大可取{{ totalWalletBalance }}</span>
</div> </div>
<el-input v-model="withdrawAmount" type="number" :placeholder="t('withdraw.dialog.amountPlaceholder')" <el-input v-model="withdrawAmount" type="number" :placeholder="t('withdraw.dialog.amountPlaceholder')"
@input="handleAmountInput"> @input="handleAmountInput">
@ -139,7 +139,7 @@ import { ref, computed, watch } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { ElMessage, type FormInstance, type FormRules } from 'element-plus' import { ElMessage, type FormInstance, type FormRules } from 'element-plus'
import Dialog from '@/components/common/Dialog.vue' import Dialog from '@/components/common/Dialog.vue'
import { getWalletCapital, getWithdrawChannel, getAgentWithdrawChannel, agentWithdraw, withdraw } from '@/api/modules/capital/withdraw' import { getWalletCapital, agentWithdraw, withdraw } from '@/api/modules/capital/withdraw'
import { getStorage } from '@/utils/storage' import { getStorage } from '@/utils/storage'
const { t } = useI18n() const { t } = useI18n()
@ -182,11 +182,6 @@ interface WalletInfo {
closing_profit: string closing_profit: string
} }
interface ChannelItem {
id: number
name: string
}
interface CalculatedItem { interface CalculatedItem {
type: string type: string
typeLabel: string typeLabel: string
@ -238,16 +233,19 @@ const availableBalance = computed(() => {
const amount = parseFloat(walletInfo.value.amount || '0') const amount = parseFloat(walletInfo.value.amount || '0')
const freeze = parseFloat(walletInfo.value.freeze || '0') const freeze = parseFloat(walletInfo.value.freeze || '0')
// //
const walletSum = let walletSum =
Math.max(0, parseFloat(walletInfo.value.freeze)) + Math.max(0, parseFloat(walletInfo.value.freeze)) +
Math.max(0, parseFloat(walletInfo.value.risk || '0')) + Math.max(0, parseFloat(walletInfo.value.risk || '0')) +
Math.max(0, parseFloat(walletInfo.value.service || '0')) + Math.max(0, parseFloat(walletInfo.value.service || '0')) +
Math.max(0, parseFloat(walletInfo.value.commission_fee_handling || '0')) + Math.max(0, parseFloat(walletInfo.value.commission_fee_handling || '0')) +
Math.max(0, parseFloat(walletInfo.value.commission_point_diff || '0')) Math.max(0, parseFloat(walletInfo.value.commission_point_diff || '0'));
// Math.max(0, parseFloat(walletInfo.value.closing_profit || '0'))
if (+roleId === 3 || +roleId === 4) {
walletSum += Math.max(0, parseFloat(walletInfo.value.closing_profit || '0'))
}
return floorMoney(amount - walletSum) return floorMoney(amount - walletSum)
}) })
// //
// roleId >= 5 totalWalletBalance availableBalance // roleId >= 5 totalWalletBalance availableBalance
const totalWalletBalance = computed(() => { const totalWalletBalance = computed(() => {
@ -261,8 +259,7 @@ const totalWalletBalance = computed(() => {
parseFloat(walletInfo.value.commission_point_diff || '0') + parseFloat(walletInfo.value.commission_point_diff || '0') +
parseFloat(walletInfo.value.service || '0') + parseFloat(walletInfo.value.service || '0') +
parseFloat(walletInfo.value.risk || '0') + parseFloat(walletInfo.value.risk || '0') +
parseFloat(Math.min(0, +walletInfo.value.closing_profit) + '') parseFloat(Math.max(0, +walletInfo.value.closing_profit) + '')
if (roleId >= 5) { if (roleId >= 5) {
// roleId >= 5 // roleId >= 5
// availableBalance = amount - freeze - walletSum // availableBalance = amount - freeze - walletSum
@ -354,9 +351,6 @@ const walletInfo = ref<WalletInfo>({
closing_profit: '0', closing_profit: '0',
}) })
//
const channelList = ref<ChannelItem[]>([])
// //
const calculatedData = ref<CalculatedItem[]>([]) const calculatedData = ref<CalculatedItem[]>([])
@ -452,31 +446,6 @@ const initData = async () => {
} catch (error) { } catch (error) {
console.error('获取钱包信息失败', error) console.error('获取钱包信息失败', error)
} }
//
try {
const api = isAgent.value ? getWithdrawChannel : getAgentWithdrawChannel
const res: any = await api()
if (res && Array.isArray(res)) {
channelList.value = res.map((item: any) => ({
id: item.id,
name: item.name,
channel_code: item.channel_code || '',
}))
} else if (res?.data) {
channelList.value = res.data.map((item: any) => ({
id: item.id,
name: item.name,
channel_code: item.channel_code || '',
}))
}
//
if (channelList.value.length > 0) {
currentChannelCode.value = channelList.value[0].channel_code || ''
}
} catch (error) {
console.error('获取渠道列表失败', error)
}
} }
// //

View File

@ -19,6 +19,7 @@ export const formItemsData = [
{ {
label: '渠道费用', label: '渠道费用',
prop: 'channel_fee', prop: 'channel_fee',
type: 'number',
min: 0, min: 0,
max: 999999, max: 999999,
controls: false, controls: false,

View File

@ -26,8 +26,8 @@
<el-button link type="primary" size="small" @click="handleEditCustomer(row)"> <el-button link type="primary" size="small" @click="handleEditCustomer(row)">
编辑 编辑
</el-button> </el-button>
<el-divider direction="vertical" /> <el-divider direction="vertical" v-auth="'user_updateCapital'" />
<el-button link type="warning" size="small" @click="handleAdjustAmount(row)"> <el-button link type="warning" size="small" @click="handleAdjustAmount(row)" v-auth="'user_updateCapital'">
调整金额 调整金额
</el-button> </el-button>
</template> </template>

View File

@ -36,9 +36,9 @@
<template #floatProfitLoss="{ row }"> <template #floatProfitLoss="{ row }">
<span>{{ getMarketFloatProfit(row.id) }}</span> <span>{{ getMarketFloatProfit(row.id) }}</span>
</template> </template>
<template #closeProfitLoss="{ row }"> <!-- <template #closeProfitLoss="{ row }">
<span>{{ getMarketCloseProfit(row.id) }}</span> <span>{{ getMarketCloseProfit(row.id) }}</span>
</template> </template> -->
<template #pointSpread="{ row }"> <template #pointSpread="{ row }">
<el-button type="primary" size="small" @click="handleViewPointDiff(row)"> 查看 </el-button> <el-button type="primary" size="small" @click="handleViewPointDiff(row)"> 查看 </el-button>
</template> </template>
@ -71,7 +71,7 @@
label="设置做市状态" label="设置做市状态"
@click="handleAction('status')" @click="handleAction('status')"
/> />
<context-menu-item <!-- <context-menu-item
v-if=" v-if="
([1, 2, 3, 4, 5].includes(userStore!.userInfo!.role_id) && ([1, 2, 3, 4, 5].includes(userStore!.userInfo!.role_id) &&
userStore!.userInfo!.role_id === currentRow.role_id) userStore!.userInfo!.role_id === currentRow.role_id)
@ -79,7 +79,7 @@
label="取款" label="取款"
divided divided
@click="handleAction('deposit')" @click="handleAction('deposit')"
/> /> -->
</context-menu> </context-menu>
<!-- 分页 --> <!-- 分页 -->
@ -370,7 +370,7 @@ import {
updateCapital, updateCapital,
editAgentBail, editAgentBail,
} from '@/api/modules/profile/business.ts' } from '@/api/modules/profile/business.ts'
import { getAgentWithdrawChannel, agentWithdraw } from '@/api/modules/capital/withdraw' import { getWithdrawChannel, agentWithdraw } from '@/api/modules/capital/withdraw'
const userStore = useUserStore() const userStore = useUserStore()
@ -531,6 +531,14 @@ const menuVisible = ref(false)
const menuOptions = reactive({ x: 0, y: 0, zIndex: 3000 }) const menuOptions = reactive({ x: 0, y: 0, zIndex: 3000 })
const currentRow = ref<any>(null) const currentRow = ref<any>(null)
const rightClickDialogVisible = ref(false) const rightClickDialogVisible = ref(false)
//
const hasMenuItems = computed(() => {
if (!currentRow.value) return false
return (
[3, 4].includes(currentRow.value.role_id) &&
userStore?.userInfo?.role_id === 1
)
})
const rightClickDialogType = ref<'account' | 'acceptOrders' | 'transfer' | 'status' | 'deposit' | 'withdraw'>( const rightClickDialogType = ref<'account' | 'acceptOrders' | 'transfer' | 'status' | 'deposit' | 'withdraw'>(
'account', 'account',
) )
@ -573,6 +581,8 @@ const handleRowContextMenu = (row: any, column: any, event: any) => {
return return
event?.preventDefault() event?.preventDefault()
currentRow.value = row currentRow.value = row
//
if (!hasMenuItems.value) return
menuOptions.x = event?.clientX menuOptions.x = event?.clientX
menuOptions.y = event?.clientY menuOptions.y = event?.clientY
menuOptions.zIndex = 3000 menuOptions.zIndex = 3000
@ -734,7 +744,7 @@ const withdrawChannelOptions = ref<{ label: string; value: number }[]>([])
// //
const fetchWithdrawChannel = async () => { const fetchWithdrawChannel = async () => {
try { try {
const res = await getAgentWithdrawChannel() const res = await getWithdrawChannel()
withdrawChannelOptions.value = res.map((item: any) => ({ withdrawChannelOptions.value = res.map((item: any) => ({
label: item.name, label: item.name,
value: item.id, value: item.id,
@ -1419,3 +1429,4 @@ const handleCancel = async () => {
} }
} }
</style> </style>

View File

@ -51,7 +51,7 @@ export const marketTableColumns = [
//浮动盈亏 //浮动盈亏
{ label: '浮动盈亏', slotName: 'floatProfitLoss', align: 'center' as const, width: '100px', isNumber: true }, { label: '浮动盈亏', slotName: 'floatProfitLoss', align: 'center' as const, width: '100px', isNumber: true },
//平仓盈亏 //平仓盈亏
{ label: '平仓盈亏', slotName: 'closeProfitLoss', align: 'center' as const, width: '150px', isNumber: true }, { label: '平仓盈亏', prop: 'closeProfitLoss', align: 'center' as const, width: '150px', isNumber: true },
//风险金 //风险金
{ label: '风险金', prop: 'riskMargin', align: 'center' as const, width: '150px', isNumber: true }, { label: '风险金', prop: 'riskMargin', align: 'center' as const, width: '150px', isNumber: true },
//服务费 //服务费

View File

@ -536,10 +536,10 @@ const handlePasswordConfirm = async () => {
}; };
// mobile_area // mobile_area
if (passwordForm.verifyType === 'mobile' && userInfo.mobile) { // if (passwordForm.verifyType === 'mobile' && userInfo.mobile) {
const match = userInfo.mobile.match(/^\+(\d{1,4})/); // // userInfo mobile_area
params.mobile_area = match ? '+' + match[1] : '+86'; // params.mobile_area = userInfo.mobile_area || '+86';
} // }
await updatePwdByCaptchaApi(params); await updatePwdByCaptchaApi(params);
passwordDialogVisible.value = false; passwordDialogVisible.value = false;

View File

@ -75,19 +75,27 @@ defineOptions({ name: 'Exe' })
type DownloadType = 'pc' | 'android' | 'ios' type DownloadType = 'pc' | 'android' | 'ios'
// exe .env.*
// adminh5.apimcapital.topadminh5.duxiang.wiki
const exeBaseUrl = import.meta.env.VITE_EXE_DOWNLOAD_BASE_URL || 'https://adminh5.apimcapital.top'
// 使 debug 使 release
const isTestEnv = import.meta.env.MODE === 'test' || import.meta.env.NODE_ENV === 'test'
const androidApkName = isTestEnv ? 'app-production-debug.apk' : 'app-production-release.apk'
const downloadUrlMap: Record<DownloadType, string> = { const downloadUrlMap: Record<DownloadType, string> = {
pc: 'https://adminh5.apimcapital.top/pc/Apim Capital-1.0.24.exe', pc: `${exeBaseUrl}/pc/Apim Capital-1.0.27.exe`,
android: 'https://adminh5.apimcapital.top/app/app-production-release.apk', android: `${exeBaseUrl}/app/${androidApkName}`,
ios: 'https://adminh5.apimcapital.top/app/xxxx.apk' ios: `${exeBaseUrl}/app/xxxx.apk`
} }
const handleDownload = (type: DownloadType) => { const handleDownload = (type: DownloadType) => {
const url: string = downloadUrlMap[type] const url: string = downloadUrlMap[type]
// 使 window.open , // 使 window.open ,
// a.download SPA // a.download SPA
const newWindow = window.open(url, '_blank', 'noopener,noreferrer') // const newWindow = window.open(url, '_blank', 'noopener,noreferrer')
// , // ,
if (!newWindow) { // if (!newWindow) {
const a = document.createElement('a') const a = document.createElement('a')
a.href = url a.href = url
a.target = '_blank' a.target = '_blank'
@ -97,7 +105,7 @@ const handleDownload = (type: DownloadType) => {
document.body.appendChild(a) document.body.appendChild(a)
a.click() a.click()
document.body.removeChild(a) document.body.removeChild(a)
} // }
} }
</script> </script>
@ -142,6 +150,9 @@ const handleDownload = (type: DownloadType) => {
&.pc { &.pc {
background: linear-gradient(135deg, #409eff, #2b7cd3); background: linear-gradient(135deg, #409eff, #2b7cd3);
color: #fff;
font-size: 23px;
font-weight: 900;
} }
&.android { &.android {
background: linear-gradient(135deg, #36c66c, #26a357); background: linear-gradient(135deg, #36c66c, #26a357);
@ -192,4 +203,4 @@ const handleDownload = (type: DownloadType) => {
} }
} }
} }
</style> </style>

View File

@ -14,7 +14,7 @@
v-for="tab in tabList" v-for="tab in tabList"
:key="tab.name" :key="tab.name"
:class="['tab-item', { active: activeTab === tab.name }]" :class="['tab-item', { active: activeTab === tab.name }]"
@click="activeTab = tab.name" @click="switchTab(tab.name)"
> >
{{ tab.label }} {{ tab.label }}
</div> </div>
@ -121,6 +121,18 @@ const rules: FormRules = {
], ],
} }
//
function switchTab(name: string) {
if (activeTab.value === name) return
activeTab.value = name
//
loginForm.value.email = ''
loginForm.value.phone = ''
loginForm.value.password = ''
//
loginFormRef.value?.clearValidate()
}
// //
const dialogVisible = ref(false) const dialogVisible = ref(false)
const dialogTitle = ref('') const dialogTitle = ref('')

View File

@ -71,7 +71,7 @@
<script setup lang="ts"> <script setup lang="ts">
import mammoth from 'mammoth' import mammoth from 'mammoth'
import { ref, onUnmounted, onMounted } from 'vue' import { ref, onUnmounted, onMounted, watch } from 'vue'
import termFile from '@/assets/file/服务条款.docx?url' import termFile from '@/assets/file/服务条款.docx?url'
import privacyFile from '@/assets/file/隐私协议.docx?url' import privacyFile from '@/assets/file/隐私协议.docx?url'
import riskFile from '@/assets/file/风险提示.docx?url' import riskFile from '@/assets/file/风险提示.docx?url'
@ -136,6 +136,17 @@ const registerForm = ref<any>({
inviteCode: '', inviteCode: '',
agreeAgreement: false, agreeAgreement: false,
}) })
//
watch(activeTab, () => {
registerForm.value.email = ''
registerForm.value.countryCode = '+86'
registerForm.value.phone = ''
registerForm.value.code = ''
//
registerFormRef.value?.clearValidate()
})
const registerRules = ref<FormRules>({ const registerRules = ref<FormRules>({
email: emailRules().email, email: emailRules().email,
phone: phoneRules(registerForm.value).phone, phone: phoneRules(registerForm.value).phone,

View File

@ -209,7 +209,7 @@ const handleRefresh = async (row?: AddressRow) => {
ElMessage.success('刷新成功') ElMessage.success('刷新成功')
const index = tableData.value.findIndex(item => item.address === targetRow.address) const index = tableData.value.findIndex(item => item.address === targetRow.address)
if (index !== -1) { if (index !== -1) {
tableData.value[index]!.trxBalance = res.data; tableData.value[index]!.usdtBalance = res.data;
} }
} }
} catch (error) { } catch (error) {