From 6d46c13f40646af866c4308818034d36f9d08f7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8?= Date: Sat, 9 May 2026 17:47:54 +0800 Subject: [PATCH 1/7] =?UTF-8?q?=E8=B0=83=E6=95=B4=E9=87=91=E9=A2=9D&?= =?UTF-8?q?=E7=BC=96=E8=BE=91=E5=AE=A2=E6=88=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/modules/user.ts | 25 +++ src/views/agent/user/index.vue | 350 ++++++++++++++++++++++++------- src/views/agent/user/options.ts | 14 +- src/views/profile/user/index.vue | 116 ++++++++++ src/views/system/role/index.vue | 61 +++++- src/views/user/Login.vue | 6 +- 6 files changed, 483 insertions(+), 89 deletions(-) diff --git a/src/api/modules/user.ts b/src/api/modules/user.ts index 9b79257..48ad565 100644 --- a/src/api/modules/user.ts +++ b/src/api/modules/user.ts @@ -61,3 +61,28 @@ export async function logoutApi(params: { device_no: string }) { export async function sendCodeApi(params: SendCodeParams) { return request.post('/sendCode', params); } + +/** + * 调整用户金额 + * @param params 调整金额参数(目标用户ID/调整金额) + * @returns Promise 调整成功返回true + */ +export async function updateCapitalApi(params: { target_user_id: number; amount: string }) { + return request.post('/user/updateCapital', params); +} + +/** + * 修改用户信息 + * @param params 修改用户参数(用户ID/风控模板ID/手续费模板ID/用户名/是否升级/状态) + * @returns Promise 修改成功返回true + */ +export async function editUserApi(params: { + user_id: number; + risk_control_id?: string | number; + fee_setting_id?: string | number; + username?: string; + is_levelup?: number; + status?: number; +}) { + return request.post('/user/editUser', params); +} diff --git a/src/views/agent/user/index.vue b/src/views/agent/user/index.vue index 1f2803d..24379d3 100644 --- a/src/views/agent/user/index.vue +++ b/src/views/agent/user/index.vue @@ -1,40 +1,20 @@ - + - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 正常 + 封禁 + + + - + @@ -127,6 +142,11 @@ import { getSelfVarietyPointDiffConfigApi, editCustomerApi, } from '@/api/modules/agent/customerList' +import { updateCapitalApi, editUserApi } from '@/api/modules/user' +import { + getFeeTemplateOptionsApi, + getRiskTemplateOptionsApi, +} from '@/api/modules/account/client' // 公用方法 import { getStorage } from '@/utils/storage' @@ -231,6 +251,60 @@ const commissionRatioFormRules = { const commissionRatioFormItemOptions = ref(commissionRatioFormItem) const selectId = ref('') +/** + * @description 定义修改客户弹窗数据 + */ +const editCustomerVisible = ref(false) +const editCustomerFormRef = ref() +const editCustomerUserId = ref(0) +const editCustomerForm = ref({ + feeSettingId: '', + riskControlId: '', + username: '', + isLevelUp: 2, + status: 1, +}) +const editCustomerRules = { + feeSettingId: [{ required: true, message: '请选择手续费模板', trigger: 'change' }], + riskControlId: [{ required: true, message: '请选择风控模板', trigger: 'change' }], + isLevelUp: [{ required: true, message: '请选择是否升级成代理', trigger: 'change' }], + status: [{ required: true, message: '请选择状态', trigger: 'change' }], +} +const feeTemplateOptions = ref([]) +const riskTemplateOptions = ref([]) +const templateOptionsFetched = ref(false) + +/** + * @description 定义调整金额弹窗数据 + */ +const adjustAmountVisible = ref(false) +const adjustAmountFormRef = ref() +const adjustUserId = ref(0) +const adjustAmountForm = ref({ + username: '', + currency: '', + currentBalance: '', + adjustAmount: '', + afterBalance: '', +}) +const adjustAmountRules = { + adjustAmount: [ + { required: true, message: '请输入调整金额', trigger: 'blur' }, + { + validator: (rule: any, value: any, callback: any) => { + if (value === '' || value === null || value === undefined) { + callback(new Error('请输入调整金额')) + } else if (!/^-?\d+(\.\d{1,5})?$/.test(value)) { + callback(new Error('请输入有效的金额格式,最多五位小数')) + } else { + callback() + } + }, + trigger: 'blur' + } + ] +} + /** * @description: 角色映射方法 */ @@ -277,10 +351,10 @@ const getCustomerList = async (id: string) => { children: item.role_id === 5 ? [ - { - hasChildren: true, - }, - ] + { + hasChildren: true, + }, + ] : undefined, })) console.log('tableTree', tableTree.value) @@ -399,10 +473,10 @@ const handleGetSubList = async (row: any) => { children: item.role_id === 5 ? [ - { - hasChildren: true, - }, - ] + { + hasChildren: true, + }, + ] : undefined, })) } @@ -478,6 +552,133 @@ const handleClose = () => { dialogVisible.value = false } +/** + * @description 调整金额相关方法 + */ +// 打开调整金额弹窗 +const handleAdjustAmount = (row: any) => { + adjustAmountVisible.value = true + adjustUserId.value = row.userId + // 注意:这里需要从原始数据中获取余额,如果row中没有balance字段,可能需要重新获取 + // 暂时使用默认值,实际使用时需要根据数据结构调整 + const balance = row.balance || row.wallet_capital?.amount || '0' + adjustAmountForm.value = { + username: row.accountName, + currency: 'USDT', // 默认币种,可根据实际情况调整 + currentBalance: balance, + adjustAmount: '', + afterBalance: balance, + } +} + +// 调整金额输入变化时计算调整后余额 +const handleAdjustAmountInput = () => { + const currentBalance = parseFloat(adjustAmountForm.value.currentBalance) || 0 + const adjustAmount = parseFloat(adjustAmountForm.value.adjustAmount) || 0 + const afterBalance = currentBalance + adjustAmount + adjustAmountForm.value.afterBalance = afterBalance.toFixed(5) +} + +// 提交调整金额 +const handleAdjustAmountSubmit = async () => { + try { + await adjustAmountFormRef.value?.validate() + await updateCapitalApi({ + target_user_id: adjustUserId.value, + amount: adjustAmountForm.value.adjustAmount, + }) + await getCustomerList(userId.value) + adjustAmountVisible.value = false + } catch (err) { + console.error('调整金额表单验证失败', err) + } +} + +// 取消调整金额 +const handleAdjustAmountCancel = () => { + adjustAmountVisible.value = false +} + +// 关闭调整金额弹窗 +const handleAdjustAmountClose = () => { + adjustAmountVisible.value = false +} + +/** + * @description 修改客户相关方法 + */ +// 获取模板选项 +const getTemplateOptions = async () => { + if (templateOptionsFetched.value) return + + try { + const [feeTemplateRes, riskTemplateRes] = await Promise.all([ + getFeeTemplateOptionsApi(), + getRiskTemplateOptionsApi(), + ]) + + feeTemplateOptions.value = Object.entries(feeTemplateRes as Record).map( + ([label, value]) => ({ + label, + value, + }), + ) + riskTemplateOptions.value = Object.entries(riskTemplateRes as Record).map( + ([label, value]) => ({ + label, + value, + }), + ) + + templateOptionsFetched.value = true + } catch (error) { + console.error('获取模板选项失败:', error) + } +} + +// 打开修改客户弹窗 +const handleEditCustomer = async (row: any) => { + await getTemplateOptions() + editCustomerVisible.value = true + editCustomerUserId.value = row.userId + editCustomerForm.value = { + feeSettingId: row.feeSettingId || '', + riskControlId: row.riskControlId || '', + username: row.accountName || '', + isLevelUp: row.isLevelUp || 2, + status: row.status === '正常' ? 1 : 2, + } +} + +// 提交修改客户 +const handleEditCustomerSubmit = async () => { + try { + await editCustomerFormRef.value?.validate() + await editUserApi({ + user_id: editCustomerUserId.value, + fee_setting_id: editCustomerForm.value.feeSettingId, + risk_control_id: editCustomerForm.value.riskControlId, + username: editCustomerForm.value.username, + is_levelup: editCustomerForm.value.isLevelUp, + status: editCustomerForm.value.status, + }) + await getCustomerList(userId.value) + editCustomerVisible.value = false + } catch (err) { + console.error('修改客户表单验证失败', err) + } +} + +// 取消修改客户 +const handleEditCustomerCancel = () => { + editCustomerVisible.value = false +} + +// 关闭修改客户弹窗 +const handleEditCustomerClose = () => { + editCustomerVisible.value = false +} + \ No newline at end of file diff --git a/src/views/agent/user/options.ts b/src/views/agent/user/options.ts index 2fdac52..febbbc2 100644 --- a/src/views/agent/user/options.ts +++ b/src/views/agent/user/options.ts @@ -17,23 +17,23 @@ export const search = [ export const tableColumns = [ // 账号名称 - { label: '账号名称', prop: 'accountName'}, + { label: '账号名称', prop: 'accountName', }, // 账号id { prop: 'userId', label: '账号id' }, // 状态 { prop: 'status', label: '状态' }, // 账号角色 - { prop: 'role', label: '账号角色' }, + { prop: 'role', label: '账号角色', width: 100 }, // 账号数量 - { prop: 'accountQty', label: '账号数量' }, + { prop: 'accountQty', label: '账号数量', width: 100 }, // 点差分配比例 - { prop: 'marginRatio', label: '点差分配比例', slotName: 'marginRatio' }, + { prop: 'marginRatio', label: '点差分配比例', slotName: 'marginRatio' , minWidth: 140}, // 手续费分成比例 - { prop: 'commissionRatio', label: '手续费分成比例', slotName: 'commissionRatio' }, + { prop: 'commissionRatio', label: '手续费分成比例', slotName: 'commissionRatio' , minWidth: 140}, // 注册时间 - { prop: 'createTime', label: '注册时间', sortable: true }, + { prop: 'createTime', label: '注册时间', sortable: true , minWidth: 180}, // 操作 - { label: '操作', slotName: 'action' } + { label: '操作', slotName: 'action', minWidth: 200} ] // 点差表格配置 diff --git a/src/views/profile/user/index.vue b/src/views/profile/user/index.vue index 822a617..4fe9eec 100644 --- a/src/views/profile/user/index.vue +++ b/src/views/profile/user/index.vue @@ -23,6 +23,9 @@ 修改 + + 调整金额 + @@ -92,6 +95,38 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/layout/Header.vue b/src/components/layout/Header.vue index 1d79f70..84d2add 100644 --- a/src/components/layout/Header.vue +++ b/src/components/layout/Header.vue @@ -21,8 +21,8 @@ @@ -249,7 +249,7 @@ const dialogVisible = ref(false) const dialogTitle = ref('') const dialogType = ref('add') -const accountList = ref([]) +const accountList = ref([]) const getAccoundList = async () => { const res = await getAllAccount({ username: '' }) console.log(res) @@ -432,6 +432,18 @@ const handleSetting = (id: string) => { dialogType.value = 'setting' } +/** + * @description: 修改杠杆 + */ +const handleChangeToLeverage = () => { + // 切换到杠杆表单时,设置默认值为当前账户的杠杆值 + const currentAccount = accountList.value.find((item: any) => item.account_id === account_id) + if (currentAccount?.wallets_trade?.lever) { + subAccountForm.value.leverage = currentAccount.wallets_trade.lever + } + dialogType.value = 'leverage' +} + /** * @description: 复制推广二维码和推广链接方法 */ diff --git a/src/views/order/history/options.ts b/src/views/order/history/options.ts index da425c3..a2fc802 100644 --- a/src/views/order/history/options.ts +++ b/src/views/order/history/options.ts @@ -110,7 +110,7 @@ export const positionOrderTableColumns = [ //平仓价格 { label: '平仓价格', prop: 'closePrice' }, //金额 - { label: '金额', prop: 'amount' }, + { label: '平仓盈亏', prop: 'closeProfitLoss', align: 'center' as const, width: '150px' }, //创建时间 { label: '创建时间', prop: 'createdTime' }, ] \ No newline at end of file diff --git a/src/views/profile/business/index.vue b/src/views/profile/business/index.vue index d563a1d..e91ecd2 100644 --- a/src/views/profile/business/index.vue +++ b/src/views/profile/business/index.vue @@ -379,7 +379,7 @@ const handleMarketFormLinkage = async (type: 'level' | 'parentAccountName', valu ...item, varietyName: item.name, varietyCode: item.code, - pointDifference: item.target_point_diff, + pointDifference: dialogType.value === 'add' ? 0 : item.target_point_diff, // 添加时初始化为0,编辑时继承上级值 assignedPointDifference: '', })) } @@ -760,7 +760,7 @@ const handleCancel = async () => { .table-wrapper { flex: 1; - min-height: 0; + min-height: 0; } } diff --git a/src/views/user/components/Captcha.vue b/src/views/user/components/Captcha.vue index 8ae6d45..ae586d9 100644 --- a/src/views/user/components/Captcha.vue +++ b/src/views/user/components/Captcha.vue @@ -3,12 +3,13 @@ - {{ - sendState.isCounting ? `${sendState.countdown}s后重新获取` : '发送验证码' }} + {{ + isCounting ? `${countdown}s后重新获取` : '发送验证码' }} diff --git a/src/views/capital/rechange/options.ts b/src/views/capital/rechange/options.ts index bc51d7b..2cd2b6b 100644 --- a/src/views/capital/rechange/options.ts +++ b/src/views/capital/rechange/options.ts @@ -1,12 +1,11 @@ -// 存款列表表格列配置:当前页面仍然保留列表 + 行内操作按钮的结构。 +// 存款列表表格列配置 export const rechangeTableColumns = [ - { prop: 'depositChannel', label: '存款渠道', align: 'center' as const }, - { prop: 'channelName', label: '渠道名称', align: 'center' as const }, - { prop: 'supportedCurrency', label: '支持货币', align: 'center' as const }, - { prop: 'processingTime', label: '预计处理时间', align: 'center' as const }, - { prop: 'singleAmount', label: '单笔存款金额', align: 'center' as const }, + { prop: 'name', label: '渠道名称', align: 'center' as const }, + { + prop: 'currency.symbol', + label: '支持货币', + align: 'center' as const + }, + { prop: 'channel_fee', label: '渠道费用', align: 'center' as const }, { label: '操作', slotName: 'action', align: 'center' as const, width: 120 }, ] - -// 存款弹窗快捷金额:点击后直接回填到金额输入框。 -export const rechangeQuickAmounts = ['100.00', '500.00', '1000.00', '2000.00', '5000.00'] diff --git a/src/views/capital/rechangeChanl/index.vue b/src/views/capital/rechangeChanl/index.vue index c84b47c..33ffdbc 100644 --- a/src/views/capital/rechangeChanl/index.vue +++ b/src/views/capital/rechangeChanl/index.vue @@ -92,7 +92,9 @@ const formData = ref({ beneficiary_name: '', beneficiary_bank_name: '', swift_bic_code: '', + beneficiary_bank_account: '', beneficiary_bank_address: '', + address: '', channel_code: '', status: 1, }) @@ -128,7 +130,9 @@ const resetFormData = () => { beneficiary_name: '', beneficiary_bank_name: '', swift_bic_code: '', + bank_account: '', beneficiary_bank_address: '', + address: '', channel_code: '', } } @@ -195,3 +199,4 @@ onMounted(() => { } } + diff --git a/src/views/capital/rechangeChanl/options.ts b/src/views/capital/rechangeChanl/options.ts index 505d1e2..017288a 100644 --- a/src/views/capital/rechangeChanl/options.ts +++ b/src/views/capital/rechangeChanl/options.ts @@ -52,12 +52,24 @@ export const formItemsData = [ type: 'input', placeholder: '请输入银行识别代码', }, + { + label: '银行账号', + prop: 'beneficiary_bank_account', + type: 'input', + placeholder: '请输入银行账号', + }, { label: '银行地址', prop: 'beneficiary_bank_address', type: 'input', placeholder: '请输入银行地址', }, + { + label: 'U钱包收款地址', + prop: 'address', + type: 'input', + placeholder: '请输入U钱包收款地址', + }, { label: '渠道编码', prop: 'channel_code', type: 'input', placeholder: '请输入渠道编码' }, { label: '状态', prop: 'status', type: 'radio' }, ] @@ -73,7 +85,9 @@ export const formRulesData = { beneficiary_name: [{ required: true, message: '请输入账户持有人姓名', trigger: ['change'] }], beneficiary_bank_name: [{ required: true, message: '请输入银行名称', trigger: ['change'] }], swift_bic_code: [{ required: true, message: '请输入银行识别代码', trigger: ['change'] }], + beneficiary_bank_account: [{ required: true, message: '请输入银行账号', trigger: ['change'] }], beneficiary_bank_address: [{ required: true, message: '请输入银行地址', trigger: ['change'] }], + address: [{ required: true, message: '请输入U钱包收款地址', trigger: ['change'] }], channel_code: [{ required: true, message: '请输入渠道编码', trigger: ['change'] }], } diff --git a/src/views/profile/business/index.vue b/src/views/profile/business/index.vue index 0d83447..2457b70 100644 --- a/src/views/profile/business/index.vue +++ b/src/views/profile/business/index.vue @@ -190,7 +190,14 @@ const marketForm = ref({ }) const marketFormRules = rules(marketForm.value) const marketFormRef = ref() -const marketFormItemOptions = ref(marketFormItem) +const marketFormItemOptions = computed(() => { + return marketFormItem.map((item) => { + if (item.prop === 'parentAccountName' || item.prop === 'level') { + return { ...item, disabled: dialogType.value === 'edit' } + } + return item + }) +}) interface SelectOption { label: string value: string | number diff --git a/src/views/profile/business/options.ts b/src/views/profile/business/options.ts index a25e2c7..69f7427 100644 --- a/src/views/profile/business/options.ts +++ b/src/views/profile/business/options.ts @@ -58,7 +58,7 @@ export const marketFormItem = [ //级别 { label: '级别', prop: 'level', type: 'select' as const, placeholder: '请选择级别' }, //所属上级 - { label: '所属上级', prop: 'parentAccountName', type: 'select' as const, placeholder: '请选择所属上级账户名称', }, + { label: '所属上级', prop: 'parentAccountName', type: 'select' as const, placeholder: '请选择所属上级账户名称' }, // 风控模板 { label: '风控模板', prop: 'riskControlTemplate', type: 'select' as const, placeholder: '请选择风控模板' }, //头寸 From db0bfc7fd4b9f52ef3107be08592722988fbd046 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8?= Date: Mon, 11 May 2026 16:26:27 +0800 Subject: [PATCH 7/7] =?UTF-8?q?=E4=BB=A3=E7=90=86=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/modules/user.ts | 2 +- src/components/common/Search.vue | 90 ++++---- src/views/agent/user/options.ts | 4 +- src/views/capital/rechange/index.vue | 4 +- src/views/profile/agent/index.vue | 334 +++++++++++++++++++++------ src/views/profile/agent/options.ts | 2 +- 6 files changed, 308 insertions(+), 128 deletions(-) diff --git a/src/api/modules/user.ts b/src/api/modules/user.ts index 48ad565..af5edde 100644 --- a/src/api/modules/user.ts +++ b/src/api/modules/user.ts @@ -81,7 +81,7 @@ export async function editUserApi(params: { risk_control_id?: string | number; fee_setting_id?: string | number; username?: string; - is_levelup?: number; + is_levelup?: number | null; status?: number; }) { return request.post('/user/editUser', params); diff --git a/src/components/common/Search.vue b/src/components/common/Search.vue index f39d670..f6863ac 100644 --- a/src/components/common/Search.vue +++ b/src/components/common/Search.vue @@ -3,60 +3,38 @@ @@ -137,20 +115,34 @@ const handleBlur = () => { .el-form { display: flex; + flex-wrap: wrap; width: 100%; - - .el-form-item { - flex: 1 1 auto; - } + row-gap: 8px; } :deep(.el-form-item) { margin-bottom: 0; margin-right: 16px; + flex: 1 1 auto; + min-width: 200px; } :deep(.el-button) { margin-left: 8px; + white-space: nowrap; + } + + :deep(.el-form-item:last-child) { + display: flex; + width: fit-content; + flex: 0 0 auto; + flex-wrap: wrap; + gap: 8px; + + .el-form-item__content { + justify-content: flex-start; + row-gap: 4px; + } } } \ No newline at end of file diff --git a/src/views/agent/user/options.ts b/src/views/agent/user/options.ts index a364b8c..f871b6a 100644 --- a/src/views/agent/user/options.ts +++ b/src/views/agent/user/options.ts @@ -3,12 +3,12 @@ export const search = [ { prop: 'accountName', label: '账号名称', type: 'input' as const, placeholder: '请输入账号名称', width: '150px' }, // 开始时间 { - prop: "startTime", label: '注册开始时间', type: 'date' as const, dateType: 'date' as const, // 时间范围选择器 + prop: "startTime", label: '注册开始时间', type: 'date' as const, dateType: 'date' as const,placeholder: '请选择注册开始时间', width: '270px', // 时间范围选择器 valueFormat: 'YYYY-MM-DD', }, // 结束时间 { - prop: "endTime", label: '注册结束时间', type: 'date' as const, dateType: 'date' as const, // 时间范围选择器 + prop: "endTime", label: '注册结束时间', type: 'date' as const, dateType: 'date' as const,placeholder: '请选择注册结束时间', width: '270px', // 时间范围选择器 valueFormat: 'YYYY-MM-DD', }, // 状态 diff --git a/src/views/capital/rechange/index.vue b/src/views/capital/rechange/index.vue index aed5119..c7373e7 100644 --- a/src/views/capital/rechange/index.vue +++ b/src/views/capital/rechange/index.vue @@ -323,8 +323,8 @@ onMounted(() => {