389 lines
12 KiB
Vue
389 lines
12 KiB
Vue
<template>
|
||
<div class="client">
|
||
<!-- 搜索组件 -->
|
||
<ClientSearch :search-form="clientSearchForm" :search-options="clientSearchOptions" @search="handleSearch"
|
||
@reset="handleReset">
|
||
<template #button="{ form }">
|
||
<el-button type="primary" @click="handleAdd()">
|
||
添加客户
|
||
</el-button>
|
||
</template>
|
||
</ClientSearch>
|
||
|
||
<!-- 表格组件 -->
|
||
<ClientTable :table-data="clientTree" :columns="clientTableColumns">
|
||
<template #status="{ row }">
|
||
{{row.status === 1 ? '正常' : '禁用'}}
|
||
</template>
|
||
<template #action="{ row }">
|
||
<el-button type="primary" size="small" @click="handleChange(row)">
|
||
修改
|
||
</el-button>
|
||
</template>
|
||
</ClientTable>
|
||
<!-- 分页组件 -->
|
||
<ClientPagination v-model="currentPage" v-model:pageSizeValue="pageSize" :total="total"
|
||
@pagination-change="handlePaginationChange" />
|
||
</div>
|
||
<!-- 客户弹窗 -->
|
||
<ClientDialog :visible="dialogVisible" :title="dialogTitle" :type="dialogType" @confirm="handleSubmit"
|
||
@cancel="handleCancel" @close="handleClose">
|
||
<ClientForm :form-data="clientForm" :form-rules="clientFormRules" :form-items="clientFormOptions"
|
||
:options-map="clientFormOptionsMap" ref="clientFormRef">
|
||
<template #phoneOrEmail="{ formData }" style="margin-left: 0;">
|
||
<div class="phone-email">
|
||
<div class="tab-btn">
|
||
<el-button :type="activeTab === 'email' ? 'primary' : 'default'" size="small"
|
||
@click="activeTab = 'email'">
|
||
邮箱
|
||
</el-button>
|
||
<el-button :type="activeTab === 'phone' ? 'primary' : 'default'" size="small"
|
||
@click="activeTab = 'phone'">
|
||
手机号
|
||
</el-button>
|
||
</div>
|
||
<div class="group">
|
||
<el-form-item prop="email" v-if="activeTab === 'email'">
|
||
<el-input v-model="clientForm.email" placeholder="请输入邮箱" />
|
||
</el-form-item>
|
||
<el-form-item prop="phone" class="phone-item" v-if="activeTab === 'phone'">
|
||
<el-select v-model="clientForm.countryCode" placeholder="请选择国家区号">
|
||
<el-option v-for="item in countryOptions" :key="item.code" :label="item.code"
|
||
:value="item.code">
|
||
<div class="country-option">
|
||
<span class="country-name">{{ item.name }}</span>
|
||
<span class="country-code">{{ item.code }}</span>
|
||
</div>
|
||
</el-option>
|
||
</el-select>
|
||
<el-input v-model="clientForm.phone" placeholder="请输入手机号" />
|
||
</el-form-item>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
</ClientForm>
|
||
</ClientDialog>
|
||
</template>
|
||
|
||
<script lang="ts" setup>
|
||
// 组件
|
||
import ClientSearch from '@/components/common/Search.vue'
|
||
import ClientTable from '@/components/common/Table.vue'
|
||
import ClientPagination from '@/components/common/Pagination.vue'
|
||
import ClientDialog from '@/components/common/Dialog.vue'
|
||
import ClientForm from '@/components/common/Form.vue'
|
||
// 接口
|
||
import { getClientListApi, getFeeTemplateOptionsApi, getRiskTemplateOptionsApi, getAgentOptionsApi, createClientApi, editClientApi } from '@/api/modules/account/client'
|
||
|
||
// 配置
|
||
import { clientSearch, clientAddFormItem, clientChangeFormItem, countryOptions, clientTableColumns } from './options'
|
||
import { rules } from './rules'
|
||
|
||
/**
|
||
* @description 定义搜索数据
|
||
*/
|
||
const clientSearchOptions = ref(clientSearch)
|
||
const clientSearchForm = ref({
|
||
username: '',
|
||
status: 1,
|
||
startDateTime: '',
|
||
endDateTime: '',
|
||
})
|
||
|
||
/**
|
||
* @description 定义表格数据
|
||
*/
|
||
const clientTree = ref<any[]>([])
|
||
|
||
/**
|
||
* @description 定义分页数据
|
||
*/
|
||
const total = ref(0)
|
||
const currentPage = ref(1)
|
||
const pageSize = ref(10)
|
||
|
||
/**
|
||
* @description 定义dialog数据
|
||
*/
|
||
const dialogTitle = ref('')
|
||
const dialogVisible = ref(false)
|
||
const dialogType = ref('add')
|
||
/**
|
||
* @description 定义dialog中form的数据
|
||
*/
|
||
const clientForm = ref({
|
||
//手续费模板
|
||
feeTemplate: '',
|
||
//风控模板
|
||
riskTemplate: '',
|
||
//所属上级名
|
||
agentUsername: '',
|
||
email: '',
|
||
phone: '',
|
||
countryCode: '+86',
|
||
//是否升级
|
||
isUpgrade: 1,
|
||
//是否变更客户名
|
||
userName: '',
|
||
status: 1,
|
||
})
|
||
|
||
// 定义客户表单配置
|
||
const clientFormRef = ref()
|
||
const clientFormRules = ref(rules())
|
||
// 定义客户表单选项是否已获取
|
||
const optionsFetched = ref(false)
|
||
const useId = ref('')
|
||
const clientFormOptions = computed(() => {
|
||
return dialogType.value === 'add' ? clientAddFormItem : clientChangeFormItem;
|
||
})
|
||
const clientFormOptionsMap = ref<any>({
|
||
feeTemplate: [],
|
||
riskTemplate: [],
|
||
agentUsername: [],
|
||
isUpgrade: [
|
||
{ label: '是', value: 1 },
|
||
{ label: '否', value: 2 }
|
||
],
|
||
status: [
|
||
{ label: '正常', value: 1 },
|
||
{ label: '禁用', value: 2 }
|
||
],
|
||
})
|
||
// 定义客户表单选项切换
|
||
const activeTab = ref('email')
|
||
|
||
/**
|
||
* @description 统一数据处理方法
|
||
*/
|
||
// 映射对象为数组通用方法
|
||
const mapObjectToOptions = (obj: Record<string, string>) => {
|
||
return Object.entries(obj).map(([label, value]) => ({
|
||
label, // 模板/代理名称
|
||
value // 模板/代理ID
|
||
}))
|
||
}
|
||
|
||
/**
|
||
* @description 统一获取数据方法
|
||
*/
|
||
const getClientList = async () => {
|
||
const params = {
|
||
page: currentPage.value,
|
||
page_size: pageSize.value,
|
||
username: clientSearchForm.value.username,
|
||
status: clientSearchForm.value.status || '',
|
||
reg_start_time: clientSearchForm.value.startDateTime.length > 0 ? `${clientSearchForm.value.startDateTime} 0:00:00` : '',
|
||
reg_end_time: clientSearchForm.value.endDateTime.length > 0 ? `${clientSearchForm.value.endDateTime} 23:59:59` : '',
|
||
}
|
||
const data = await getClientListApi(params)
|
||
// 处理表格数据
|
||
clientTree.value = data.data.map(item => ({
|
||
...item,
|
||
//注册时间
|
||
createdTime: item.created_at,
|
||
// 映射所属代理用户名
|
||
agentUsername: item.par_uid === item.parent?.id ? item.parent?.username : '',
|
||
}));
|
||
//分页数据
|
||
total.value = Number(data.total)
|
||
currentPage.value = Number(data.current_page)
|
||
pageSize.value = Number(data.per_page)
|
||
}
|
||
|
||
// 获取客户表单选项
|
||
const getOptions = async () => {
|
||
// 已获取过选项则直接返回,避免重复请求
|
||
if (optionsFetched.value) return
|
||
|
||
try {
|
||
const [agentRes, feeTemplateRes, riskTemplateRes] = await Promise.all([
|
||
getAgentOptionsApi(),
|
||
getFeeTemplateOptionsApi(),
|
||
getRiskTemplateOptionsApi()
|
||
])
|
||
|
||
// label=名称,value=ID
|
||
clientFormOptionsMap.value.agentUsername = mapObjectToOptions(agentRes as Record<string, string>)
|
||
clientFormOptionsMap.value.feeTemplate = mapObjectToOptions(feeTemplateRes as Record<string, string>)
|
||
clientFormOptionsMap.value.riskTemplate = mapObjectToOptions(riskTemplateRes as Record<string, string>)
|
||
|
||
optionsFetched.value = true // 标记已获取
|
||
} catch (error) {
|
||
console.error('获取表单选项失败:', error)
|
||
}
|
||
}
|
||
|
||
//
|
||
|
||
/**
|
||
* @description 页面加载后获取数据
|
||
*/
|
||
onMounted(async () => {
|
||
await getClientList()
|
||
})
|
||
|
||
/**
|
||
* @description 搜索方法
|
||
*/
|
||
const handleSearch = async () => {
|
||
try {
|
||
await getClientList()
|
||
} catch (err) {
|
||
console.error('获取客户列表失败', err)
|
||
}
|
||
}
|
||
const handleReset = async () => {
|
||
// 重置搜索表单
|
||
clientSearchForm.value = {
|
||
username: '',
|
||
status: '正常',
|
||
startDateTime: '',
|
||
endDateTime: '',
|
||
}
|
||
try {
|
||
await getClientList()
|
||
} catch (err) {
|
||
console.error('获取客户列表失败', err)
|
||
}
|
||
}
|
||
// 添加客户
|
||
const handleAdd = () => {
|
||
// 打开添加客户弹窗
|
||
dialogVisible.value = true
|
||
dialogTitle.value = '添加客户'
|
||
dialogType.value = 'add'
|
||
clientForm.value = {
|
||
//手续费模板
|
||
feeTemplate: '',
|
||
//风控模板
|
||
riskTemplate: '',
|
||
//所属上级名
|
||
agentUsername: '',
|
||
email: '',
|
||
phone: '',
|
||
countryCode: '+86',
|
||
//是否升级
|
||
isUpgrade: 1,
|
||
//是否变更客户名
|
||
userName: '',
|
||
status: 1,
|
||
}
|
||
getOptions()
|
||
}
|
||
/**
|
||
* @description 表格方法
|
||
*/
|
||
const handleChange = (row: any) => {
|
||
// 打开修改客户弹窗
|
||
dialogVisible.value = true
|
||
dialogTitle.value = '修改客户'
|
||
dialogType.value = 'change'
|
||
useId.value = row.id
|
||
clientForm.value = {
|
||
//手续费模板
|
||
feeTemplate: row.wallet_capital.fee_setting_id,
|
||
//风控模板
|
||
riskTemplate: row.wallet_capital.risk_control_id,
|
||
userName: row.username,
|
||
status: row.status,
|
||
isUpgrade: 2,
|
||
} as any
|
||
console.log('rowrowrowrowrowrowrowrowrow', clientForm.value)
|
||
getOptions()
|
||
}
|
||
|
||
/**
|
||
* @description 分页方法
|
||
*/
|
||
const handlePaginationChange = (page: { currentPage: number, pageSize: number }) => {
|
||
currentPage.value = page.currentPage
|
||
pageSize.value = page.pageSize
|
||
handleSearch()
|
||
}
|
||
|
||
/**
|
||
* @description 客户弹窗方法
|
||
*/
|
||
const handleSubmit = async () => {
|
||
try {
|
||
await clientFormRef.value?.validate()
|
||
if (dialogType.value === 'add') {
|
||
const params = {
|
||
reg_type: activeTab.value === 'email' ? 1 : 2,
|
||
email: clientForm.value.email,
|
||
mobile: clientForm.value.phone,
|
||
parent_id: clientForm.value.agentUsername,
|
||
risk_control_id: clientForm.value.riskTemplate,
|
||
fee_setting_id: clientForm.value.feeTemplate,
|
||
}
|
||
await createClientApi(params)
|
||
await getClientList()
|
||
dialogVisible.value = false
|
||
} else {
|
||
const params = {
|
||
user_id: useId.value,
|
||
risk_control_id: clientForm.value.riskTemplate,
|
||
fee_setting_id: clientForm.value.feeTemplate,
|
||
username: clientForm.value.userName,
|
||
is_levelup: clientForm.value.isUpgrade,
|
||
status: clientForm.value.status,
|
||
}
|
||
await editClientApi(params)
|
||
await getClientList()
|
||
dialogVisible.value = false
|
||
}
|
||
} catch (err) {
|
||
console.error('客户表单验证失败', err)
|
||
}
|
||
}
|
||
const handleCancel = () => {
|
||
// 关闭弹窗
|
||
dialogVisible.value = false
|
||
}
|
||
const handleClose = () => {
|
||
// 关闭弹窗
|
||
dialogVisible.value = false
|
||
}
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
:deep(.el-form-item__content) {
|
||
margin-left: 0 !important;
|
||
}
|
||
|
||
.phone-email {
|
||
display: flex;
|
||
flex: 1;
|
||
|
||
.tab-btn {
|
||
display: flex;
|
||
margin-right: 10px;
|
||
|
||
.el-button {
|
||
height: 32px;
|
||
margin-left: 0;
|
||
}
|
||
}
|
||
|
||
.group {
|
||
display: flex;
|
||
flex: 1;
|
||
|
||
.el-form-item {
|
||
flex: 1;
|
||
}
|
||
|
||
.phone-item {
|
||
:deep(.el-form-item__content) {
|
||
flex-wrap: nowrap;
|
||
}
|
||
|
||
.el-select {
|
||
width: 120px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</style>
|