Compare commits
10 Commits
50732616d0
...
2e09a787fd
| Author | SHA1 | Date |
|---|---|---|
|
|
2e09a787fd | |
|
|
e9fbb956e4 | |
|
|
3c7c5ae69e | |
|
|
cf6de1d1d7 | |
|
|
8d83be7cb3 | |
|
|
22eb76a440 | |
|
|
e14dd66e2b | |
|
|
7010ed1238 | |
|
|
cc502b2ac6 | |
|
|
de39f3a54f |
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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 }
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -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: '',
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算扣除明细
|
// 计算扣除明细
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 },
|
||||||
//服务费
|
//服务费
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -75,19 +75,27 @@ defineOptions({ name: 'Exe' })
|
||||||
|
|
||||||
type DownloadType = 'pc' | 'android' | 'ios'
|
type DownloadType = 'pc' | 'android' | 'ios'
|
||||||
|
|
||||||
|
// exe 下载基础地址,根据不同环境从 .env.* 注入
|
||||||
|
// 生产:adminh5.apimcapital.top;测试:adminh5.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>
|
||||||
|
|
|
||||||
|
|
@ -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('')
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue