This commit is contained in:
parent
27be2e59ca
commit
c8e9b79bce
|
|
@ -9,4 +9,9 @@ export async function getMarketListApi(params: searchParams): Promise<any> {
|
|||
//获取代理点差配置
|
||||
export async function getPointDiffConfigApi(params: pointDiffConfigParams): Promise<any> {
|
||||
return request.get<any>('/agent/getSelfVarietyPointDiffConfig',{...params})
|
||||
}
|
||||
}
|
||||
|
||||
//获取所属上级列表
|
||||
export async function getParAgentListApi(params: pointDiffConfigParams): Promise<any> {
|
||||
return request.get<any>('/agent/getParAgentListByRoleId',{...params})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,5 +4,6 @@ export interface searchParams {
|
|||
}
|
||||
//获取代理点差配置
|
||||
export interface pointDiffConfigParams {
|
||||
user_id: number | string
|
||||
user_id?: number | null
|
||||
role_id?: number | string
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
<!-- 下拉选择框 -->
|
||||
<el-select v-else-if="item.type === 'select'" v-model="formData[item.prop]"
|
||||
:placeholder="item.placeholder" :disabled="item.disabled" clearable style="width: 100%">
|
||||
:placeholder="item.placeholder" :disabled="item.disabled" clearable style="width: 100%" @change="handleItemChange(item.prop, formData[item.prop])">
|
||||
<el-option v-for="option in props.optionsMap?.[item.prop] || []" :key="option.value"
|
||||
:label="option.label" :value="option.value">
|
||||
<template v-if="option.icon" #label>
|
||||
|
|
@ -80,7 +80,11 @@ interface Props {
|
|||
|
||||
const formRef = ref<FormInstance>()
|
||||
const props = defineProps<Props>()
|
||||
const emit = defineEmits(['form-item-change'])
|
||||
|
||||
const handleItemChange = (field: string, value: any) => {
|
||||
emit('form-item-change', field, value)
|
||||
}
|
||||
// 暴露表单验证方法
|
||||
defineExpose({
|
||||
validate: () => formRef.value?.validate(),
|
||||
|
|
|
|||
|
|
@ -56,9 +56,6 @@
|
|||
<!-- 操作按钮 -->
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="$emit('search')">
|
||||
<el-icon>
|
||||
<Search />
|
||||
</el-icon>
|
||||
{{ buttonConfig.searchText || '查询' }}
|
||||
</el-button>
|
||||
<el-button @click="$emit('reset')">
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@ const fetchAgentPointDiffConfig = async (id: number | string) => {
|
|||
try {
|
||||
const data = await getPointDiffConfigApi(params)
|
||||
// 表格数据
|
||||
agentPointTree.value = data.data.map((item: any) => ({
|
||||
agentPointTree.value = data.map((item: any) => ({
|
||||
...item,
|
||||
varietyName: item.name,
|
||||
varietyCode: item.code,
|
||||
|
|
|
|||
|
|
@ -1,17 +1,19 @@
|
|||
<template>
|
||||
<div class="market">
|
||||
<!-- 搜索 -->
|
||||
<MarketSearch :search-form="marketSearchForm" :search-options="marketSearchOptions" @search="handleSearch"
|
||||
@reset="handleReset">
|
||||
<template #button="{ form }">
|
||||
<el-button type="primary" @click="handleAddAgent()">
|
||||
添加代理
|
||||
<el-button type="primary" @click="handleAddMarket()">
|
||||
添加
|
||||
</el-button>
|
||||
<el-button type="primary" @click="handleEditAgent(row)">
|
||||
<el-button type="primary" @click="handleEditMarket()">
|
||||
编辑
|
||||
</el-button>
|
||||
</template>
|
||||
</MarketSearch>
|
||||
|
||||
<!-- 表格 -->
|
||||
<MarketTable :table-data="marketTree" :columns="marketTableColumnsOptions" row-key="id">
|
||||
<template #pointSpread="{ row }">
|
||||
<el-button type="primary" size="small" @click="handleViewPointDiff(row)">
|
||||
|
|
@ -19,19 +21,10 @@
|
|||
</el-button>
|
||||
</template>
|
||||
</MarketTable>
|
||||
<AgentPagination v-model="marketSearchForm.page" v-model:page-size-value="marketSearchForm.pageSize"
|
||||
:total="marketSearchForm.total" @pagination-change="handlePaginationChange" />
|
||||
|
||||
<MarketDialog :visible="dialogVisible" :title="dialogTitle" :type="dialogType" @submit="handleSubmit"
|
||||
@cancel="handleCancel" @close="handleClose" :fullscreen="dialogFullscreen"
|
||||
:class="{ isAddOrEdit: 'isAddOrEdit-dialog' }">
|
||||
<MarketForm :form-data="marketForm" :form-rules="marketFormRules" :form-items="marketFormItemOptions"
|
||||
:options-map="marketFormOptionsMap" v-if="isAddOrEdit" />
|
||||
<MarketTable :table-data="marketPointTree" :columns="marketPointTableColumnsOptions" row-key="id" />
|
||||
</MarketDialog>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
|
||||
<script setup lang="ts">
|
||||
// 组件
|
||||
import MarketSearch from '@/components/common/Search.vue'
|
||||
import MarketTable from '@/components/common/Table.vue'
|
||||
|
|
@ -39,251 +32,142 @@ import MarketPagination from '@/components/common/Pagination.vue'
|
|||
import MarketDialog from '@/components/common/Dialog.vue'
|
||||
import MarketForm from '@/components/common/Form.vue'
|
||||
|
||||
// 接口
|
||||
import { getMarketListApi, getPointDiffConfigApi } from '@/api/modules/account/market'
|
||||
//配置选项和表单验证
|
||||
import { marketSearch, marketTableColumns } from './options'
|
||||
|
||||
// 配置
|
||||
import { marketSearch, marketTableColumns, marketFormItem, marketPointTableColumns } from './options'
|
||||
//接口
|
||||
import { getMarketListApi } from '@/api/modules/account/market'
|
||||
|
||||
/**
|
||||
* @description 搜索配置
|
||||
*/
|
||||
const marketSearchForm = ref<any>({
|
||||
accountName: '',
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
* @description 定义搜索数据
|
||||
*/
|
||||
|
||||
const marketSearchForm = ref({
|
||||
accountName: ''
|
||||
})
|
||||
const marketSearchOptions = ref<any>(marketSearch)
|
||||
const marketSearchOptions = ref(marketSearch)
|
||||
|
||||
/**
|
||||
* @description 表格配置
|
||||
*/
|
||||
const marketTableColumnsOptions = ref<any>(marketTableColumns)
|
||||
const marketTree = ref<any>([
|
||||
{
|
||||
id: 1,
|
||||
agentRelation: "一级代理", // 代理关系
|
||||
accountName: "代理001", // 账户名
|
||||
level: "一级", // 级别
|
||||
basicAccount: "500000.00", // 基本账户
|
||||
margin: "100000.00", // 保证金
|
||||
stopOrderMargin: "50000.00", // 停止接单保证金
|
||||
positionTransferMargin: "30000.00", // 持仓转移保证金
|
||||
pointSpreadCommission: "8520.68", // 点差返佣
|
||||
pointSpread: "0.58", // 点差(插槽字段,单独定义)
|
||||
commissionRatio: "0.05%", // 手续费比例
|
||||
commissionReturn: "4260.34", // 手续费返佣
|
||||
closeProfitLoss: "12850.72", // 平仓盈亏
|
||||
riskMargin: "20000.00", // 风险金
|
||||
serviceFee: "1500.00" // 服务费
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
agentRelation: "二级代理", // 代理关系
|
||||
accountName: "代理002", // 账户名
|
||||
level: "二级", // 级别
|
||||
basicAccount: "200000.00", // 基本账户
|
||||
margin: "50000.00", // 保证金
|
||||
stopOrderMargin: "20000.00", // 停止接单保证金
|
||||
positionTransferMargin: "10000.00", // 持仓转移保证金
|
||||
pointSpreadCommission: "3680.25", // 点差返佣
|
||||
pointSpread: "0.32", // 点差(插槽字段)
|
||||
commissionRatio: "0.03%", // 手续费比例
|
||||
commissionReturn: "1104.08", // 手续费返佣
|
||||
closeProfitLoss: "4820.15", // 平仓盈亏
|
||||
riskMargin: "8000.00", // 风险金
|
||||
serviceFee: "600.00" // 服务费
|
||||
}
|
||||
])
|
||||
* @description 定义表格数据
|
||||
*/
|
||||
const marketTree = ref([])
|
||||
const marketTableColumnsOptions = ref(marketTableColumns)
|
||||
|
||||
/**
|
||||
* @description 分页配置
|
||||
*/
|
||||
const total = ref<number>(0)
|
||||
* @description 定义分页数据
|
||||
*/
|
||||
const total = ref(0)
|
||||
const page = ref(1)
|
||||
const pageSize = ref(10)
|
||||
|
||||
/**
|
||||
* @description dialog配置
|
||||
*/
|
||||
|
||||
const dialogVisible = ref<boolean>(false)
|
||||
const dialogTitle = ref<string>('')
|
||||
const dialogType = ref<string>('add')
|
||||
const dialogFullscreen = ref(false)
|
||||
* @description 定义dialog数据
|
||||
*/
|
||||
|
||||
/**
|
||||
* @description 点差表格配置
|
||||
*/
|
||||
const marketPointTree = ref<any>([])
|
||||
const marketPointTableColumnsOptions = ref<any>(marketPointTableColumns)
|
||||
* @description 数据处理方法
|
||||
*/
|
||||
//映射表格数据
|
||||
const mapMarketTree = (data: any) => {
|
||||
return data.map((item: any) => ({
|
||||
...item,
|
||||
id:item.id, //id
|
||||
agentRelation:item.path_name,//代理关系
|
||||
accountName: item.username,//账户名
|
||||
level: item.role_id,//级别
|
||||
basicAccount: item.wallet_capital.amount,//基本账户
|
||||
margin: item.wallet_capital.bail,//保证金
|
||||
stopOrderMargin: item.wallet_capital.bail_stop_order,//停止接单保证金
|
||||
positionTransferMargin: item.wallet_capital.bail_change_order,//持仓转移保证金
|
||||
positionRatio: item.wallet_capital.toucun_percent,//头寸比率
|
||||
pointSpreadCommission: item.wallet_capital.commission_point_diff,//点差返佣
|
||||
commissionRatio: item.wallet_capital.fee_handling_percent,//手续费比例
|
||||
commissionReturn: item.wallet_capital.commission_fee_handling,//手续费返佣
|
||||
floatProfitLoss:"后续添加",//浮动盈亏
|
||||
closeProfitLoss: item.wallet_capital.closing_profit,//平仓盈亏
|
||||
riskMargin: item.wallet_capital.risk,//风险保证金
|
||||
serviceFee: item.wallet_capital.service//服务费
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @description 表单数据
|
||||
*/
|
||||
const isAddOrEdit = ref<boolean>(false)
|
||||
const marketForm = ref<any>({
|
||||
accountName: '',
|
||||
level: '',
|
||||
parentAccountName: '',
|
||||
position: '',
|
||||
commissionRatio: '',
|
||||
})
|
||||
const marketFormItemOptions = ref<any>(marketFormItem)
|
||||
const marketFormRules = ref<any>()
|
||||
const marketFormOptionsMap = ref<any>({
|
||||
})
|
||||
|
||||
/**
|
||||
* @description 统一获取数据
|
||||
*/
|
||||
|
||||
* @description 统一数据获取
|
||||
*/
|
||||
//获取做市商列表
|
||||
const fetchMarketList = async () => {
|
||||
const getMarketList = async () => {
|
||||
const params = {
|
||||
page: marketSearchForm.value.page,
|
||||
page_size: marketSearchForm.value.pageSize,
|
||||
page: page.value,
|
||||
page_size: pageSize.value,
|
||||
user_name: marketSearchForm.value.accountName
|
||||
}
|
||||
const data = await getMarketListApi(params)
|
||||
|
||||
// 处理表格数据
|
||||
marketTree.value = data.data.map((item: any) => ({
|
||||
...item,
|
||||
agentRelation: item.path_name || '--', // 代理关系
|
||||
accountName: item.username || '', // 账号名称
|
||||
level: item.role_id || '', // 等级(可根据role_id做枚举映射,比如 1->管理员 2->代理)
|
||||
basicAccount: item.wallet_capital?.amount || '0.00000', // 基本账户
|
||||
margin: item.wallet_capital?.bail || '0.00000', // 保证金
|
||||
stopOrderMargin: item.wallet_capital?.bail_stop_order || '0.00000', // 止单保证金
|
||||
positionTransferMargin: item.wallet_capital?.bail_change_order || '0.00000', // 转仓保证金
|
||||
pointSpreadCommission: item.wallet_capital?.commission_point_diff || '0.00000', // 点差佣金
|
||||
commissionRatio: item.wallet_capital?.toucun_percent || '0.00000', // 佣金比例
|
||||
commissionReturn: item.wallet_capital?.commission_fee_handling || '0.00000', // 佣金返还
|
||||
closeProfitLoss: item.wallet_capital?.closing_profit || '0.00000', // 平仓盈亏
|
||||
riskMargin: item.wallet_capital?.risk || '0.00000', // 风险保证金
|
||||
serviceFee: item.wallet_capital?.service || '0.00000' // 服务费
|
||||
}))
|
||||
|
||||
console.log('data', data);
|
||||
// 表格数据
|
||||
marketTree.value = mapMarketTree(data.data)
|
||||
//分页数据
|
||||
total.value = Number(data.total)
|
||||
marketSearchForm.value.page = Number(data.current_page)
|
||||
marketSearchForm.value.pageSize = Number(data.per_page)
|
||||
|
||||
}
|
||||
//获取代理点差配置
|
||||
const fetchPointDiffConfig = async () => {
|
||||
const params = {
|
||||
user_id: marketForm.value.id
|
||||
}
|
||||
const data = await getPointDiffConfigApi(params)
|
||||
total.value = data.total
|
||||
page.value = data.current_page
|
||||
pageSize.value = data.per_page
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 组件挂载时获取数据
|
||||
*/
|
||||
* @description 页面加载完成后需要获取的数据
|
||||
*/
|
||||
onMounted(() => {
|
||||
fetchMarketList()
|
||||
getMarketList()
|
||||
})
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @description 搜索
|
||||
*/
|
||||
* @description search方法
|
||||
*/
|
||||
// 搜索
|
||||
const handleSearch = async () => {
|
||||
await fetchMarketList()
|
||||
}
|
||||
const handleReset = () => {
|
||||
marketSearchForm.value.accountName = ''
|
||||
handleSearch()
|
||||
await getMarketList()
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 处理弹窗类型
|
||||
*/
|
||||
const handleDialogType = (type: string) => {
|
||||
console.log('handleDialogType', type)
|
||||
if (type === 'add') {
|
||||
isAddOrEdit.value = true
|
||||
} else {
|
||||
isAddOrEdit.value = false
|
||||
// 重置
|
||||
const handleReset = async () => {
|
||||
marketSearchForm.value = {
|
||||
accountName: ''
|
||||
}
|
||||
page.value = 1
|
||||
pageSize.value = 10
|
||||
await getMarketList()
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 新增做市商
|
||||
*/
|
||||
const handleAddAgent = () => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = '新增做市商'
|
||||
dialogType.value = 'add'
|
||||
handleDialogType(dialogType.value)
|
||||
// 添加做市商
|
||||
const handleAddMarket = async () => {
|
||||
console.log('添加做市商');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 编辑做市商
|
||||
*/
|
||||
const handleEditAgent = (row: any) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = '编辑做市商'
|
||||
dialogType.value = 'edit'
|
||||
marketForm.value = { ...row }
|
||||
handleDialogType(dialogType.value)
|
||||
}
|
||||
// 编辑做市商
|
||||
const handleEditMarket = async () => {
|
||||
console.log('编辑做市商');
|
||||
|
||||
/**
|
||||
* @description 查看点差
|
||||
*/
|
||||
const handleViewPointDiff = (row: any) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = '查看点差'
|
||||
dialogType.value = 'view'
|
||||
handleDialogType(dialogType.value)
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 分页变更
|
||||
*/
|
||||
const handlePaginationChange = (params: any) => {
|
||||
marketSearchForm.value.page = params.currentPage
|
||||
marketSearchForm.value.pageSize = params.pageSize
|
||||
handleSearch()
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @description 提交表单
|
||||
*/
|
||||
const handleSubmit = async () => {
|
||||
if (dialogType.value === 'add') {
|
||||
} else if (dialogType.value === 'edit') {
|
||||
}
|
||||
dialogVisible.value = false
|
||||
handleSearch()
|
||||
* @description 表格方法
|
||||
*/
|
||||
// 查看点差
|
||||
const handleViewPointDiff = async (row: any) => {
|
||||
console.log('查看点差', row);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 取消操作
|
||||
*/
|
||||
const handleCancel = () => {
|
||||
dialogVisible.value = false
|
||||
dialogType.value = 'add'
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 关闭弹窗
|
||||
*/
|
||||
const handleClose = () => {
|
||||
dialogVisible.value = false
|
||||
dialogType.value = 'add'
|
||||
}
|
||||
* @description dialog方法
|
||||
*/
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.el-form {
|
||||
.el-form-item {
|
||||
flex: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.market {
|
||||
.search-container {
|
||||
:deep(.el-form) {
|
||||
|
|
@ -293,15 +177,4 @@ const handleClose = () => {
|
|||
}
|
||||
}
|
||||
}
|
||||
:deep(.el-dialog) {
|
||||
.el-dialog__footer {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.edit-dialog) {
|
||||
.el-dialog__footer {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
//做市商搜索配置
|
||||
|
||||
export const marketSearch = [
|
||||
{ label: '账户名', prop: 'accountName', type: 'input', placeholder: '请输入账户名', width: '340px', },
|
||||
{ label: '账户名', prop: 'accountName', type: 'input' as const, placeholder: '请输入账户名', width: '340px', },
|
||||
]
|
||||
|
||||
export const marketTableColumns = [
|
||||
// id
|
||||
{ label: 'ID', prop: 'id', align: 'center' as const },
|
||||
//代理关系
|
||||
{ label: '代理关系', prop: 'agentRelation', align: 'center' as const },
|
||||
{ label: '代理关系', prop: 'agentRelation', align: 'center' as const, width: '200px' },
|
||||
//账户名
|
||||
{ label: '账户名', prop: 'accountName', align: 'center' as const },
|
||||
//级别
|
||||
|
|
@ -20,6 +21,8 @@ export const marketTableColumns = [
|
|||
{ label: '停止接单保证金', prop: 'stopOrderMargin', align: 'center' as const, width: '160px' },
|
||||
//持仓转移保证金
|
||||
{ label: '持仓转移保证金', prop: 'positionTransferMargin', align: 'center' as const, width: '160px' },
|
||||
//头寸比率
|
||||
{ label: '头寸比率', prop: 'positionRatio', align: 'center' as const },
|
||||
//点差返佣
|
||||
{ label: '点差返佣', prop: 'pointSpreadCommission', align: 'center' as const },
|
||||
//点差
|
||||
|
|
@ -28,10 +31,10 @@ export const marketTableColumns = [
|
|||
{ label: '手续费比例', prop: 'commissionRatio', align: 'center' as const },
|
||||
//手续费返佣
|
||||
{ label: '手续费返佣', prop: 'commissionReturn', align: 'center' as const },
|
||||
//平仓盈亏
|
||||
{ label: '平仓盈亏', prop: 'closeProfitLoss', align: 'center' as const },
|
||||
//浮动盈亏
|
||||
{ label: '浮动盈亏', prop: 'floatProfitLoss', align: 'center' as const },
|
||||
//平仓盈亏
|
||||
{ label: '平仓盈亏', prop: 'closeProfitLoss', align: 'center' as const },
|
||||
//风险金
|
||||
{ label: '风险金', prop: 'riskMargin', align: 'center' as const },
|
||||
//服务费
|
||||
|
|
@ -40,17 +43,16 @@ export const marketTableColumns = [
|
|||
|
||||
//做市商添加配置
|
||||
export const marketFormItem = [
|
||||
//注册方式
|
||||
{ prop: 'phoneOrEmail', label: '', type: 'slot' as const, slotName: 'phoneOrEmail' },
|
||||
//账户名称
|
||||
{ label: '账户名称', prop: 'accountName', type: 'input', placeholder: '请输入账户名称', },
|
||||
//级别
|
||||
{
|
||||
label: '级别', prop: 'level', type: 'select', placeholder: '请选择级别', options: [
|
||||
{ label: '一级', value: '1' },
|
||||
{ label: '二级', value: '2' },
|
||||
{ label: '三级', value: '3' },
|
||||
]
|
||||
},
|
||||
{ label: '级别', prop: 'level', type: 'select', placeholder: '请选择级别'},
|
||||
//所属上级
|
||||
{ label: '所属上级', prop: 'parentAccountName', type: 'select', placeholder: '请输入所属上级账户名称', },
|
||||
// 风控模板
|
||||
{ label: '风控模板', prop: 'riskControlTemplate', type: 'select', placeholder: '请选择风控模板'},
|
||||
//头寸
|
||||
{ label: '头寸', prop: 'position', type: 'input', placeholder: '请输入头寸', },
|
||||
//手续费
|
||||
|
|
@ -60,5 +62,20 @@ export const marketPointTableColumns = ([
|
|||
{ prop: 'varietyName', label: '名称', align: 'center' as const },
|
||||
{ prop: 'varietyCode', label: '编号', align: 'center' as const },
|
||||
{ prop: 'pointDifference', label: '可分配点差', align: 'center' as const },
|
||||
{ prop: 'assignedPointDifference', label: '已分配点差', align: 'center' as const },
|
||||
{ prop: 'assignedPointDifference', label: '已分配点差', align: 'center' as const, slotName:'assignedPointDiff' ,width:'160px',height:'600px'},
|
||||
])
|
||||
|
||||
// 国家码选项
|
||||
interface CountryItem {
|
||||
name: string
|
||||
code: string
|
||||
}
|
||||
export const countryOptions = ref<CountryItem[]>([
|
||||
{ name: '中国', code: '+86' },
|
||||
{ name: '中国台湾', code: '+886' },
|
||||
{ name: '中国香港', code: '+852' },
|
||||
{ name: '中国澳门', code: '+853' },
|
||||
{ name: '韩国', code: '+82' },
|
||||
{ name: '美国', code: '+1' },
|
||||
{ name: '新加坡', code: '+65' }
|
||||
])
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
// rules.ts
|
||||
import type { FormRules } from 'element-plus'
|
||||
|
||||
// 接收上级上限参数
|
||||
export const rules = (parentLimit: any = { position: 0, commissionRatio: 0 }): FormRules => {
|
||||
return {
|
||||
//手机号
|
||||
phone: [
|
||||
{ required: true, message: '请输入手机号', trigger: ['blur'] },
|
||||
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号格式', trigger: ['blur'] },
|
||||
],
|
||||
//邮箱
|
||||
email: [
|
||||
{ required: true, message: '请输入邮箱地址', trigger: ['blur'] },
|
||||
{ type: 'email', message: '请输入正确的邮箱格式', trigger: ['blur'] }
|
||||
],
|
||||
accountName: [
|
||||
{ required: true, message: '请输入账户名称', trigger: 'blur' },
|
||||
{ min: 2, max: 50, message: '账户名称长度需在2-50个字符之间', trigger: 'blur' }
|
||||
],
|
||||
level: [
|
||||
{ required: true, message: '请选择级别', trigger: 'change' }
|
||||
],
|
||||
parentAccountName: [
|
||||
{ required: true, message: '请选择所属上级', trigger: 'change' }
|
||||
],
|
||||
// 头寸:新增「不超过上级」校验
|
||||
position: [
|
||||
{ required: true, message: '请输入头寸', trigger: 'blur' },
|
||||
{ pattern: /^-?\d+(\.\d+)?$/, message: '头寸需为数字(支持整数/小数/负数)', trigger: 'blur' },
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
if (!value) return callback()
|
||||
const inputVal = Number(value)
|
||||
const maxVal = parentLimit.position
|
||||
if (inputVal > maxVal) {
|
||||
callback(new Error(`头寸不能大于上级的 ${maxVal}`))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
},
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
// 手续费:新增「不超过上级」校验
|
||||
commissionRatio: [
|
||||
{ required: true, message: '请输入手续费', trigger: 'blur' },
|
||||
{ pattern: /^\d+(\.\d+)?$/, message: '手续费需为非负数字(支持整数/小数)', trigger: 'blur' },
|
||||
{ min: 0, max: 100, message: '手续费范围需在0-100之间', trigger: 'blur' },
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
if (!value) return callback()
|
||||
const inputVal = Number(value)
|
||||
const maxVal = parentLimit.commissionRatio
|
||||
if (inputVal > maxVal) {
|
||||
callback(new Error(`手续费不能大于上级的 ${maxVal}`))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
},
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
// 新增:批量校验已分配点差
|
||||
pointDiffList: [
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
// value 是 marketPointTree 数组
|
||||
if (!value || value.length === 0) {
|
||||
return callback() // 没有点差数据时不校验
|
||||
}
|
||||
|
||||
// 遍历所有点差行,校验必填和数值合法性
|
||||
for (const row of value) {
|
||||
// 校验必填
|
||||
if (row.assignedPointDifference === '' || row.assignedPointDifference === undefined) {
|
||||
return callback(new Error(`品种【${row.varietyName || row.name}】的已分配点差不能为空`))
|
||||
}
|
||||
// 校验数值格式(非负数字)
|
||||
const numVal = Number(row.assignedPointDifference)
|
||||
if (isNaN(numVal) || numVal < 0) {
|
||||
return callback(new Error(`品种【${row.varietyName || row.name}】的已分配点差需为非负数字`))
|
||||
}
|
||||
// 可选:校验不超过上级点差
|
||||
if (row.pointDifference && numVal > Number(row.pointDifference)) {
|
||||
return callback(new Error(`品种【${row.varietyName || row.name}】的已分配点差不能超过可分配点差 ${row.pointDifference}`))
|
||||
}
|
||||
}
|
||||
callback()
|
||||
},
|
||||
trigger: 'change' // 数据变化时触发
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,523 @@
|
|||
<template>
|
||||
<div class="market">
|
||||
<MarketSearch :search-form="marketSearchForm" :search-options="marketSearchOptions" @search="handleSearch"
|
||||
@reset="handleReset">
|
||||
<template #button="{ form }">
|
||||
<el-button type="primary" @click="handleAddAgent()">
|
||||
添加代理
|
||||
</el-button>
|
||||
<el-button type="primary" @click="handleEditAgent(tableRow)" :disabled="!tableRow">
|
||||
编辑
|
||||
</el-button>
|
||||
</template>
|
||||
</MarketSearch>
|
||||
|
||||
<MarketTable :table-data="marketTree" :columns="marketTableColumnsOptions" row-key="id"
|
||||
@row-click="handleRowClick">
|
||||
<template #pointSpread="{ row }">
|
||||
<el-button type="primary" size="small" @click="handleViewPointDiff(row)">
|
||||
查看
|
||||
</el-button>
|
||||
</template>
|
||||
</MarketTable>
|
||||
<MarketPagination v-model="marketSearchForm.page" v-model:page-size-value="marketSearchForm.pageSize"
|
||||
:total="total" @pagination-change="handlePaginationChange" />
|
||||
|
||||
<MarketDialog :visible="dialogVisible" :title="dialogTitle" :type="dialogType"
|
||||
:class="{ 'showForm-dialog': dialogType !== 'view' }" @confirm="handleSubmit" @cancel="handleCancel"
|
||||
@close="handleClose" :fullscreen="dialogFullscreen">
|
||||
<MarketForm :form-data="marketForm" :form-rules="marketFormRules" :form-items="marketFormItemOptions"
|
||||
:options-map="marketFormOptionsMap" ref="marketFormRef" @form-item-change="handleFormItemChange"
|
||||
v-if="dialogType !== 'view'">
|
||||
</MarketForm>
|
||||
<MarketTable :table-data="marketPointTree" :columns="marketPointTableColumnsOptions" row-key="id">
|
||||
<template #assignedPointDiff="{ row }">
|
||||
<el-form-item prop="assignedPointDifference">
|
||||
<el-input v-if="dialogType !== 'view'" v-model.number="row.assignedPointDifference" size="small"
|
||||
placeholder="请输入点差" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
</MarketTable>
|
||||
</MarketDialog>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
// 组件
|
||||
import MarketSearch from '@/components/common/Search.vue'
|
||||
import MarketTable from '@/components/common/Table.vue'
|
||||
import MarketPagination from '@/components/common/Pagination.vue'
|
||||
import MarketDialog from '@/components/common/Dialog.vue'
|
||||
import MarketForm from '@/components/common/Form.vue'
|
||||
|
||||
// 接口
|
||||
import { getMarketListApi, getPointDiffConfigApi, getParAgentListApi } from '@/api/modules/account/market'
|
||||
|
||||
// 配置
|
||||
import { marketSearch, marketTableColumns, marketFormItem, marketPointTableColumns, countryOptions } from './options'
|
||||
import { rules } from './rules'
|
||||
|
||||
/**
|
||||
* @description 搜索配置
|
||||
*/
|
||||
const marketSearchForm = ref<any>({
|
||||
accountName: '',
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
})
|
||||
const marketSearchOptions = ref<any>(marketSearch)
|
||||
|
||||
/**
|
||||
* @description 表格配置
|
||||
*/
|
||||
const marketTableColumnsOptions = ref<any>(marketTableColumns)
|
||||
// 可选:存储完整的选中行数据
|
||||
const tableRow = ref<any>(null)
|
||||
const tableRowId = ref<number | null>(null)
|
||||
const tableRowRoleId = ref<number | null>(null)
|
||||
const marketTree = ref<any>([])
|
||||
/**
|
||||
* @description 分页配置
|
||||
*/
|
||||
const total = ref<number>(0)
|
||||
|
||||
/**
|
||||
* @description dialog配置
|
||||
*/
|
||||
|
||||
const dialogVisible = ref<boolean>(false)
|
||||
const dialogTitle = ref<string>('')
|
||||
const dialogType = ref<string>('add')
|
||||
const dialogFullscreen = ref(false)
|
||||
const activeTab = ref('email')
|
||||
/**
|
||||
* @description 点差表格配置
|
||||
*/
|
||||
const marketPointTree = ref<any>([])
|
||||
const marketPointTableColumnsOptions = ref<any>(marketPointTableColumns)
|
||||
|
||||
/**
|
||||
* @description 表单数据
|
||||
*/
|
||||
const marketForm = ref<any>({
|
||||
accountName: '',
|
||||
level: '',
|
||||
parentAccountName: '',
|
||||
position: '',
|
||||
commissionRatio: '',
|
||||
assignedPointDifference: '',
|
||||
pointDiffList: [] // 新增:存储点差表格数据,关联到表单规则
|
||||
})
|
||||
// 新增:存储上级做市商的钱包数据(用于手续费校验)
|
||||
const walletCapital = ref<any>({
|
||||
position: 0,
|
||||
commissionRatio: 0,
|
||||
})
|
||||
const marketFormOptionsMap = ref<any>({
|
||||
level: [
|
||||
{ label: '平台', value: '1' },
|
||||
{ label: '承包商', value: '2' },
|
||||
{ label: '特殊做市', value: '3' },
|
||||
{ label: '普通做市', value: '4' },
|
||||
{ label: '一级代理', value: '5' },
|
||||
],
|
||||
parentAccountName: [] // 新增:存储动态加载的上级代理列表
|
||||
})
|
||||
const showForm = ref<boolean>(true)
|
||||
const marketFormRef = ref<any>()
|
||||
const marketFormItemOptions = ref<any>(marketFormItem)
|
||||
const marketFormRules = ref<any>(rules(walletCapital.value))
|
||||
const pointDiffData = ref<any>({})
|
||||
/**
|
||||
* 级别映射工具函数(全局复用)
|
||||
* @param roleId 级别数字(1/2/3/4/5)
|
||||
* @returns 对应的文字标签
|
||||
*/
|
||||
const getLevelLabel = (roleId: number | string): string => {
|
||||
const levelMap: Record<string | number, string> = {
|
||||
'1': '平台',
|
||||
'2': '承包商',
|
||||
'3': '特殊做市',
|
||||
'4': '普通做市',
|
||||
'5': '一级代理'
|
||||
}
|
||||
return levelMap[roleId] || '未知级别'
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 点击表格行时,存储选中行的ID和数据
|
||||
*/
|
||||
const handleRowClick = (row: any) => {
|
||||
tableRow.value = row
|
||||
tableRowId.value = row.id
|
||||
tableRowRoleId.value = row.role_id
|
||||
console.log('点击行数据', row)
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 监听表单字段变化,获取所属上级列表 / 点差数据
|
||||
*/
|
||||
const handleFormItemChange = (field: string, value: any) => {
|
||||
// 监听 level 字段变化 → 加载所属上级列表
|
||||
if (field === 'level') {
|
||||
const levelRoleMap: Record<string, number> = {
|
||||
'1': 1, // 平台
|
||||
'2': 2, // 承包商
|
||||
'3': 3, // 特殊做市
|
||||
'4': 4, // 普通做市
|
||||
'5': 5 // 一级代理
|
||||
}
|
||||
const roleId = levelRoleMap[value]
|
||||
if (roleId) {
|
||||
fetchParAgentList(roleId)
|
||||
} else {
|
||||
marketFormOptionsMap.value.parentAccountName = []
|
||||
// 清空已选上级和表格数据
|
||||
marketForm.value.parentAccountName = ''
|
||||
marketPointTree.value = []
|
||||
}
|
||||
}
|
||||
|
||||
// 监听 parentAccountName 字段变化 → 用上级ID请求点差表格数据
|
||||
if (field === 'parentAccountName' && value) {
|
||||
// value 就是上级的 id(你在 fetchParAgentList 里配置的 value: item.id)
|
||||
fetchPointDiffConfigByParentId(value)
|
||||
|
||||
// 核心:获取选中上级的上限值
|
||||
const parentAgent = marketFormOptionsMap.value.parentAccountName.find(
|
||||
(item: any) => item.value === value
|
||||
)
|
||||
walletCapital.value.position = parentAgent.position
|
||||
walletCapital.value.commissionRatio = parentAgent.commissionRatio
|
||||
} else if (field === 'parentAccountName' && !value) {
|
||||
// 清空上级时,清空表格数据
|
||||
marketPointTree.value = []
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 统一获取数据
|
||||
*/
|
||||
|
||||
//获取做市商列表
|
||||
const fetchMarketList = async () => {
|
||||
const params = {
|
||||
page: marketSearchForm.value.page,
|
||||
page_size: marketSearchForm.value.pageSize,
|
||||
user_name: marketSearchForm.value.accountName
|
||||
}
|
||||
const data = await getMarketListApi(params)
|
||||
|
||||
// 处理表格数据
|
||||
marketTree.value = data.data.map((item: any) => ({
|
||||
...item,
|
||||
agentRelation: item.path_name || '--', // 代理关系
|
||||
accountName: item.username || '', // 账号名称
|
||||
level: getLevelLabel(item.role_id), // 等级(可根据role_id做枚举映射,比如 1->管理员 2->代理)
|
||||
basicAccount: item.wallet_capital?.amount || '0.00000', // 基本账户
|
||||
margin: item.wallet_capital?.bail || '0.00000', // 保证金
|
||||
stopOrderMargin: item.wallet_capital?.bail_stop_order || '0.00000', // 止单保证金
|
||||
positionTransferMargin: item.wallet_capital?.bail_change_order || '0.00000', // 转仓保证金
|
||||
pointSpreadCommission: item.wallet_capital?.commission_point_diff || '0.00000', // 点差佣金
|
||||
commissionRatio: item.wallet_capital?.toucun_percent || '0.00000', // 佣金比例
|
||||
commissionReturn: item.wallet_capital?.commission_fee_handling || '0.00000', // 佣金返还
|
||||
closeProfitLoss: item.wallet_capital?.closing_profit || '0.00000', // 平仓盈亏
|
||||
riskMargin: item.wallet_capital?.risk || '0.00000', // 风险保证金
|
||||
serviceFee: item.wallet_capital?.service || '0.00000' // 服务费
|
||||
}))
|
||||
|
||||
//分页数据
|
||||
total.value = Number(data.total)
|
||||
marketSearchForm.value.page = Number(data.current_page)
|
||||
marketSearchForm.value.pageSize = Number(data.per_page)
|
||||
|
||||
}
|
||||
|
||||
//获取代理点差配置
|
||||
const fetchPointDiffConfig = async () => {
|
||||
const params = {
|
||||
user_id: tableRowId.value
|
||||
}
|
||||
try {
|
||||
const data = await getPointDiffConfigApi(params)
|
||||
// 表格数据
|
||||
marketPointTree.value = data.map((item: any) => ({
|
||||
...item,
|
||||
varietyName: item.name,
|
||||
varietyCode: item.code,
|
||||
pointDifference: item.point_diff,
|
||||
assignedPointDifference: item.target_point_diff
|
||||
}))
|
||||
} catch (err) {
|
||||
console.error('获取代理点差配置失败', err)
|
||||
}
|
||||
}
|
||||
|
||||
//获取所属上级列表
|
||||
const fetchParAgentList = async (roleId?: number) => {
|
||||
const params = { role_id: roleId }
|
||||
try {
|
||||
const data = await getParAgentListApi(params)
|
||||
marketFormOptionsMap.value.parentAccountName = data.map((item: any) => ({
|
||||
label: item.accountName || item.username, // 显示的文字
|
||||
value: item.id, // 绑定的ID(必须和row.parent_id一致)
|
||||
position: item.wallet_capital.toucun_percent,
|
||||
commissionRatio: item.wallet_capital.toucun_percent
|
||||
}))
|
||||
} catch (err) {
|
||||
console.error('获取所属上级列表失败', err)
|
||||
marketFormOptionsMap.value.parentAccountName = []
|
||||
}
|
||||
}
|
||||
|
||||
// 根据上级代理ID获取点差配置表格数据
|
||||
const fetchPointDiffConfigByParentId = async (parentId: number) => {
|
||||
if (!parentId) {
|
||||
marketPointTree.value = []
|
||||
return
|
||||
}
|
||||
const params = { user_id: parentId }
|
||||
try {
|
||||
const data = await getPointDiffConfigApi(params)
|
||||
marketPointTree.value = data.map((item: any) => ({
|
||||
...item,
|
||||
varietyName: item.name,
|
||||
varietyCode: item.code,
|
||||
pointDifference: item.target_point_diff, // 可分配点差
|
||||
assignedPointDifference: '' // 已分配点差
|
||||
}))
|
||||
} catch (err) {
|
||||
console.error('获取上级点差配置失败', err)
|
||||
marketPointTree.value = []
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 组件挂载时获取数据
|
||||
*/
|
||||
onMounted(() => {
|
||||
fetchMarketList()
|
||||
})
|
||||
|
||||
/**
|
||||
* @description 搜索
|
||||
*/
|
||||
const handleSearch = async () => {
|
||||
await fetchMarketList()
|
||||
}
|
||||
const handleReset = () => {
|
||||
marketSearchForm.value.accountName = ''
|
||||
handleSearch()
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 处理弹窗类型
|
||||
*/
|
||||
const handleDialogType = (type: string) => {
|
||||
if (type === "view") {
|
||||
dialogFullscreen.value = true
|
||||
showForm.value = false
|
||||
} else {
|
||||
dialogFullscreen.value = false
|
||||
showForm.value = true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 新增做市商
|
||||
*/
|
||||
const handleAddAgent = () => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = '新增做市商'
|
||||
dialogType.value = 'add'
|
||||
handleDialogType(dialogType.value)
|
||||
|
||||
// 重置表单
|
||||
marketForm.value = {
|
||||
accountName: '',
|
||||
level: '',
|
||||
parentAccountName: '',
|
||||
position: '',
|
||||
commissionRatio: '',
|
||||
}
|
||||
// 清空上级列表和点差表格
|
||||
marketFormOptionsMap.value.parentAccountName = []
|
||||
marketPointTree.value = []
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 编辑做市商
|
||||
*/
|
||||
const handleEditAgent = (row: any) => {
|
||||
if (!row) return
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = '编辑做市商'
|
||||
dialogType.value = 'edit'
|
||||
handleDialogType(dialogType.value)
|
||||
|
||||
// 1. 级别映射:数字 → 字符串value
|
||||
const levelValueMap: Record<string | number, string> = {
|
||||
'1': '1', '2': '2', '3': '3', '4': '4', '5': '5'
|
||||
}
|
||||
const levelValue = levelValueMap[row.role_id] || ''
|
||||
|
||||
// 2. 从 path 中提取上级 ID(path 格式: "/1/30/33" → 倒数第二段是上级ID)
|
||||
const pathSegments = row.path.split('/').filter(Boolean) // 分割后得到 ['1', '30', '33']
|
||||
const parentId = pathSegments.length >= 2 ? Number(pathSegments[pathSegments.length - 2]) : null
|
||||
|
||||
// 3. 回填表单
|
||||
marketForm.value = {
|
||||
...row,
|
||||
level: levelValue,
|
||||
parentAccountName: parentId // 用解析出的上级ID回填
|
||||
}
|
||||
|
||||
// 4. 加载上级列表
|
||||
const levelRoleMap: Record<string, number> = {
|
||||
'1': 1, '2': 2, '3': 3, '4': 4, '5': 5
|
||||
}
|
||||
const roleId = levelRoleMap[levelValue]
|
||||
if (roleId) {
|
||||
fetchParAgentList(roleId)
|
||||
}
|
||||
|
||||
// 5. 加载点差数据
|
||||
if (parentId) {
|
||||
fetchPointDiffConfigByParentId(parentId)
|
||||
} else {
|
||||
marketPointTree.value = []
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 查看点差
|
||||
*/
|
||||
const handleViewPointDiff = (row: any) => {
|
||||
tableRowId.value = row.id
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = '查看点差'
|
||||
dialogType.value = 'view'
|
||||
handleDialogType(dialogType.value)
|
||||
fetchPointDiffConfig()
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 分页变更
|
||||
*/
|
||||
const handlePaginationChange = (params: any) => {
|
||||
marketSearchForm.value.page = params.currentPage
|
||||
marketSearchForm.value.pageSize = params.pageSize
|
||||
handleSearch()
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 提交表单(新增/编辑做市商)
|
||||
*/
|
||||
const handleSubmit = async () => {
|
||||
// 组装成对象(按id为key),也可以组装成数组:const pointDiffList = []
|
||||
marketPointTree.value.forEach((row: any) => {
|
||||
if (row.assignedPointDifference || row.assignedPointDifference === 0) {
|
||||
pointDiffData.value[row.id] = row.assignedPointDifference
|
||||
}
|
||||
console.log('pointDiffData', pointDiffData.value)
|
||||
})
|
||||
await marketFormRef.value?.validate()
|
||||
if (dialogType.value === 'add') {
|
||||
// 新增做市商
|
||||
const params = {
|
||||
username: '',
|
||||
}
|
||||
} else if (dialogType.value === 'edit') {
|
||||
// 编辑做市商
|
||||
} else {
|
||||
// 查看点差
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 取消操作
|
||||
*/
|
||||
const handleCancel = () => {
|
||||
dialogVisible.value = false
|
||||
dialogType.value = 'add'
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 关闭弹窗
|
||||
*/
|
||||
const handleClose = () => {
|
||||
dialogVisible.value = false
|
||||
dialogType.value = 'add'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.el-form {
|
||||
.el-form-item {
|
||||
flex: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.market {
|
||||
.search-container {
|
||||
:deep(.el-form) {
|
||||
.el-form-item {
|
||||
flex: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-dialog) {
|
||||
.el-dialog__footer {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.showForm-dialog) {
|
||||
.el-dialog__footer {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.phone-email {
|
||||
display: flex;
|
||||
|
||||
:deep(.el-form-item__content) {
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.tab-btn {
|
||||
display: flex;
|
||||
margin-right: 10px;
|
||||
|
||||
.el-button {
|
||||
margin-left: 0;
|
||||
background-color: transparent;
|
||||
border: 1px solid #DCDFE6;
|
||||
color: #303133;
|
||||
height: 32px;
|
||||
|
||||
}
|
||||
|
||||
.active {
|
||||
background-color: #409EFF;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.email-item {
|
||||
.el-input {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.phone-item {
|
||||
.el-select {
|
||||
width: 120px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in New Issue