This commit is contained in:
parent
e26deae5b5
commit
ba89a965c7
|
|
@ -0,0 +1,32 @@
|
|||
import { request } from '@/api';
|
||||
import type { accountSearchParams, accountListResponse, accountCreateParams } from '@/api/types/account'
|
||||
|
||||
// 获取客户列表
|
||||
export async function getClientListApi(params: accountSearchParams): Promise<accountListResponse> {
|
||||
return request.get<accountListResponse>('/user/getUserList', { ...params })
|
||||
}
|
||||
|
||||
//获取代理列表
|
||||
export async function getAgentOptionsApi(){
|
||||
return request.get('/user/getParOptions')
|
||||
}
|
||||
|
||||
//获取手续费模板列表
|
||||
export async function getFeeTemplateOptionsApi(){
|
||||
return request.get('/user/getFeeSettingOptions')
|
||||
}
|
||||
|
||||
//获取风控模板列表
|
||||
export async function getRiskTemplateOptionsApi() {
|
||||
return request.get('/user/getRiskControlOptions')
|
||||
}
|
||||
|
||||
// 添加客户
|
||||
export async function createClientApi(params: accountCreateParams): Promise<void> {
|
||||
return request.post<void>('/user/createUser', params)
|
||||
}
|
||||
|
||||
// 更新客户
|
||||
export async function editClientApi(params: accountCreateParams): Promise<void> {
|
||||
return request.post<void>('/user/editUser', params)
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
/**
|
||||
* @description 客户列表查询参数
|
||||
* @param {accountSearchParams} params - 查询参数类型
|
||||
* @response {accountListResponse} 客户列表响应类型
|
||||
*
|
||||
*/
|
||||
// 客户列表查询参数
|
||||
export interface accountSearchParams {
|
||||
page?: number
|
||||
page_size?: number
|
||||
username?: string
|
||||
status?: string | number
|
||||
reg_start_time?: string
|
||||
reg_end_time?: string
|
||||
}
|
||||
|
||||
// 定义列表项的类型
|
||||
interface UserItem {
|
||||
id: number;
|
||||
username: string;
|
||||
par_uid: number;
|
||||
created_at: string; // 时间字符串类型
|
||||
status: number;
|
||||
parent: ParentUser; // 嵌套的父级用户对象
|
||||
}
|
||||
|
||||
// 定义完整响应数据的类型
|
||||
interface UserResponse {
|
||||
data: UserItem[]; // 数据是 UserItem 类型的数组
|
||||
}
|
||||
|
||||
export interface accountListResponse {
|
||||
data: UserItem[];
|
||||
total: number;
|
||||
per_page: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 客户创建类型
|
||||
* @param {accountCreateParams} params - 创建参数类型
|
||||
* @response {void} 无响应体
|
||||
*/
|
||||
// 客户创建类型
|
||||
export interface accountCreateParams {
|
||||
reg_type?: number
|
||||
email?: string
|
||||
mobile?: string
|
||||
parent_id?: number | string
|
||||
fee_setting_id?: number | string
|
||||
risk_control_id?: number | string
|
||||
user_id?: string;
|
||||
username?: string;
|
||||
is_levelup?: number;
|
||||
status?: number;
|
||||
}
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
// src/utils/request.types.ts
|
||||
import type {
|
||||
AxiosRequestConfig,
|
||||
AxiosResponse,
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ export interface MenuItem {
|
|||
parent_id: string; // 上级菜单ID(string类型,匹配后端)
|
||||
sort: string; // 排序(string类型)
|
||||
type: string; // 类型:1菜单2按钮(string类型)
|
||||
status?: string; // 状态:1启用2禁用(可选,编辑时用)
|
||||
status?: string | null; // 状态:1启用2禁用(可选,编辑时用)
|
||||
id?: string; // 菜单ID(仅编辑/删除时传)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
<!-- src/components/CommonDialog.vue -->
|
||||
<template>
|
||||
<el-dialog :model-value="visible" :title="title" :width="width" :destroy-on-close="destroyOnClose" @close="handleClose"
|
||||
v-bind="$attrs">
|
||||
<el-dialog :model-value="visible" :title="title" :width="width" :destroy-on-close="destroyOnClose"
|
||||
@close="handleClose" v-bind="$attrs">
|
||||
<!-- 对话框内容插槽 -->
|
||||
<slot />
|
||||
|
||||
|
|
@ -50,4 +49,7 @@ const handleCancel = () => {
|
|||
const handleClose = () => {
|
||||
emit('close')
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="100px" v-bind="$attrs">
|
||||
<!-- 动态表单项 -->
|
||||
<template v-for="(item, index) in formItems" :key="index">
|
||||
<el-form-item :label="item.label" :prop="item.prop" :required="item.required">
|
||||
<el-form-item :label="item.label" :prop="item.prop" :required="item.required" label-position="left" :label-width="item.labelWidth || '100px'">
|
||||
<!-- 输入框 -->
|
||||
<el-input v-if="item.type === 'input'" v-model="formData[item.prop]" :placeholder="item.placeholder"
|
||||
:disabled="item.disabled" clearable />
|
||||
|
|
@ -63,6 +63,8 @@ interface FormItem {
|
|||
min?: number
|
||||
max?: number
|
||||
slotName?: string // 自定义插槽名称
|
||||
labelPosition?: 'left' | 'right'
|
||||
labelWidth?: string
|
||||
}
|
||||
|
||||
interface Props {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
:start-placeholder="item.startPlaceholder || '开始日期'"
|
||||
:end-placeholder="item.endPlaceholder || '结束日期'"
|
||||
:value-format="item.valueFormat || 'YYYY-MM-DD'" :clearable="item.clearable ?? true"
|
||||
:disabled="item.disabled" :locale="'zhCn'" :picker-options="item.pickerOptions"
|
||||
:disabled-date="item.disabledDate" :locale="'zhCn'"
|
||||
/>
|
||||
|
||||
<!-- 单选框组 -->
|
||||
|
|
@ -89,10 +89,7 @@ interface SearchOption {
|
|||
startPlaceholder?: string; // 日期范围开始占位符
|
||||
endPlaceholder?: string; // 日期范围结束占位符
|
||||
valueFormat?: string; // 日期格式
|
||||
// 添加年份/日期限制配置
|
||||
pickerOptions?: {
|
||||
disabledDate?: (date: Date) => boolean;
|
||||
};
|
||||
disabledDate?: (date: Date) => boolean; // 禁用日期函数
|
||||
width?: string; // 宽度
|
||||
minWidth?: string; // 最小宽度
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,88 +1,384 @@
|
|||
<template>
|
||||
<div class="client">
|
||||
<!-- 搜索组件 -->
|
||||
<ClientSearch :search-form="ClientSearchForm" :search-options="clientSearchOptions" @search="handleSearch"
|
||||
<ClientSearch :search-form="clientSearchForm" :search-options="clientSearchOptions" @search="handleSearch"
|
||||
@reset="handleReset">
|
||||
<!-- 这里可以插入自定义插槽(如果需要) -->
|
||||
<!-- button 插槽:自定义额外按钮 -->
|
||||
<template #button="{ form }">
|
||||
<!-- form 就是子组件的 searchForm,可以直接使用 -->
|
||||
<el-button type="primary" @click="handleAdd()">
|
||||
添加客户
|
||||
</el-button>
|
||||
</template>
|
||||
</ClientSearch>
|
||||
<!-- table组件 -->
|
||||
<!-- <ClientTable :table-data="ClientTableTree" :columns="clientTableColumns">
|
||||
<ClientTable :table-data="clientTree" :columns="clientTableColumns" row-key="id" :tree-props="treeProps">
|
||||
<template #status="{ row }">
|
||||
<el-switch v-model="row.status" :active-value="1" :inactive-value="2" active-text="启用"
|
||||
inactive-text="禁用" @change="handleStatusChange(row)" />
|
||||
<el-switch :model-value="row.status === 1" :active-value="true" :inactive-value="false" active-text="启用"
|
||||
inactive-text="禁用" />
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<el-button type="primary" size="small" @click="handleChange(row)">
|
||||
修改
|
||||
</el-button>
|
||||
</template>
|
||||
</ClientTable>
|
||||
<!-- 分页组件 -->
|
||||
<!-- <ClientPagination :pagination-data="ClientPaginationData" @page-change="handlePageChange" /> -->
|
||||
<ClientPagination v-model="currentPage" v-model:page-size-value="pageSize" :total="total"
|
||||
@pagination-change="handlePaginationChange" />
|
||||
</div>
|
||||
<!-- dialog -->
|
||||
<!-- <ClientDialog :dialog-visible="dialogVisible" :dialog-title="dialogTitle" :dialog-type="dialogType" :form-options="clientAddFormOptions" @close="dialogVisible = false" @submit="handleSubmit"/> --> -->
|
||||
<!-- 客户弹窗 -->
|
||||
<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 }">
|
||||
<div class="phone-email">
|
||||
<div class="tab-btn">
|
||||
<el-button type="primary" size="small" :class="{ 'active': activeTab === 'email' }"
|
||||
@click="activeTab = 'email'">
|
||||
邮箱
|
||||
</el-button>
|
||||
<el-button type="primary" class="phone" size="small"
|
||||
:class="{ 'active': activeTab === 'phone' }" @click="activeTab = 'phone'">
|
||||
手机号
|
||||
</el-button>
|
||||
</div>
|
||||
<el-form-item prop="email" v-if="activeTab === 'email'" class="email-item">
|
||||
<el-input v-model="formData.email" placeholder="请输入邮箱"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="phone" v-else class="phone-item">
|
||||
<el-select v-model="formData.countryCode" :teleported="false" value-key="code" placeholder="国家"
|
||||
class="country-select">
|
||||
<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="formData.phone" placeholder="请输入手机号" clearable />
|
||||
</el-form-item>
|
||||
</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'
|
||||
|
||||
|
||||
// 引入配置
|
||||
import { clientSearchOptions, clientTableColumns,clientAddFormOptions,clientChangeFormOptions } from './options'
|
||||
|
||||
//定义搜索表单数据
|
||||
const ClientSearchForm = ref({
|
||||
//定义客户搜索表单配置
|
||||
const clientSearchOptions = ref(clientSearch)
|
||||
const clientSearchForm = ref({
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
username: '',
|
||||
status: '',
|
||||
startDate: '',
|
||||
endDate: ''
|
||||
status: '正常',
|
||||
startDateTime: '',
|
||||
endDateTime: '',
|
||||
})
|
||||
|
||||
//定义table数据
|
||||
const ClientTableTree = ref([])
|
||||
|
||||
//定义分页数据
|
||||
const ClientPaginationData = ref({
|
||||
total: 0,
|
||||
currentPage: 1,
|
||||
pageSize: 10
|
||||
//定义表格配置
|
||||
const clientTree = ref<any[]>([])
|
||||
const treeProps = ref({
|
||||
children: 'parent',
|
||||
})
|
||||
|
||||
//定义dialog数据
|
||||
const dialogVisible = ref(false)
|
||||
// 定义分页配置
|
||||
const total = ref(0)
|
||||
const pageSize = ref(1)
|
||||
const currentPage = ref(1)
|
||||
|
||||
// 定义dialog配置
|
||||
const dialogTitle = ref('')
|
||||
const dialogType = ref('')
|
||||
const dialogVisible = ref(false)
|
||||
const dialogType = ref('add')
|
||||
const clientForm = ref({
|
||||
//手续费模板
|
||||
feeTemplate: '',
|
||||
//风控模板
|
||||
riskTemplate: '',
|
||||
//所属上级名
|
||||
agentUsername: '',
|
||||
email: '',
|
||||
phone: '',
|
||||
countryCode: '+86',
|
||||
//是否升级
|
||||
isUpgrade: 1,
|
||||
//是否变更客户名
|
||||
userName: '',
|
||||
status: 1,
|
||||
id: '',
|
||||
})
|
||||
|
||||
/**
|
||||
* @description: 搜索方法
|
||||
* @param {any} formData 搜索表单数据
|
||||
* @return {void}
|
||||
*/
|
||||
//定义搜索方法
|
||||
const handleSearch = () => {
|
||||
console.log('搜索表单数据:', ClientSearchForm.value)
|
||||
// 定义客户表单配置
|
||||
const clientFormRef = ref()
|
||||
const clientFormRules = ref(rules())
|
||||
const clientFormOptions = ref<any[]>(clientAddFormItem)
|
||||
// 定义客户表单选项切换
|
||||
const activeTab = ref('email')
|
||||
// 定义客户表单选项类型
|
||||
interface FormOption {
|
||||
label: string;
|
||||
value: string | number;
|
||||
}
|
||||
interface ClientFormOptionsMap {
|
||||
feeTemplate: FormOption[];
|
||||
riskTemplate: FormOption[];
|
||||
agentUsername: FormOption[];
|
||||
isUpgrade: FormOption[];
|
||||
}
|
||||
const clientFormOptionsMap = ref<ClientFormOptionsMap>({
|
||||
feeTemplate: [],
|
||||
riskTemplate: [],
|
||||
agentUsername: [],
|
||||
isUpgrade: [
|
||||
{ label: '是', value: 1 },
|
||||
{ label: '否', value: 2 }
|
||||
],
|
||||
})
|
||||
|
||||
//定义重置方法
|
||||
const handleReset = () => {
|
||||
ClientSearchForm.value = {
|
||||
username: '',
|
||||
status: '',
|
||||
startDate: '',
|
||||
endDate: ''
|
||||
// 获取账户列表
|
||||
const fetchClientList = async () => {
|
||||
const params = {
|
||||
page: clientSearchForm.value.page,
|
||||
page_size: clientSearchForm.value.pageSize,
|
||||
username: clientSearchForm.value.username,
|
||||
status: clientSearchForm.value.status === '正常' ? 1 : 2,
|
||||
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,
|
||||
// 映射所属代理用户名
|
||||
agentUsername: item.par_uid === item.parent?.id ? item.parent?.username : '',
|
||||
}));
|
||||
//把接口返回的分页字段赋值
|
||||
total.value = data.total; // 总条数
|
||||
pageSize.value = Number(data.per_page); // 转成数字,和组件保持一致
|
||||
}
|
||||
onMounted(async () => {
|
||||
console.log('客户列表', clientTree.value)
|
||||
await fetchClientList()
|
||||
})
|
||||
/**
|
||||
* @description: 搜索事件
|
||||
*/
|
||||
|
||||
// 搜索
|
||||
const handleSearch = async () => {
|
||||
try {
|
||||
await fetchClientList()
|
||||
console.log('客户列表', clientSearchForm.value)
|
||||
} catch (err) {
|
||||
console.error('获取客户列表失败', err)
|
||||
}
|
||||
}
|
||||
|
||||
//定义添加客户方法
|
||||
const handleAdd = () => {
|
||||
console.log('添加客户')
|
||||
// 搜索重置
|
||||
const handleReset = () => {
|
||||
clientSearchForm.value = {
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
username: '',
|
||||
status: '正常',
|
||||
startDateTime: '',
|
||||
endDateTime: '',
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
// 客户表单选项切换
|
||||
const handleDialogType = (type: string) => {
|
||||
clientFormOptions.value = type === 'add' ? clientAddFormItem : clientChangeFormItem
|
||||
}
|
||||
|
||||
//统一获取表单选项
|
||||
const getOptions = async () => {
|
||||
//获取代理列表
|
||||
const agentOptions = await getAgentOptionsApi()
|
||||
//获取手续费模板列表
|
||||
const feeTemplateOptions = await getFeeTemplateOptionsApi()
|
||||
//获取风控模板列表
|
||||
const riskTemplateOptions = await getRiskTemplateOptionsApi()
|
||||
|
||||
clientFormOptionsMap.value.feeTemplate = mapObjectToOptions(feeTemplateOptions as Record<string, string>)
|
||||
clientFormOptionsMap.value.riskTemplate = mapObjectToOptions(riskTemplateOptions as Record<string, string>)
|
||||
clientFormOptionsMap.value.agentUsername = mapObjectToOptions(agentOptions as Record<string, string>)
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 表格修改事件
|
||||
*/
|
||||
|
||||
const handleChange = async (row: any) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = '修改客户'
|
||||
dialogType.value = 'edit'
|
||||
handleDialogType(dialogType.value)
|
||||
//获取表单选项
|
||||
await getOptions()
|
||||
// 核心:将选中行的数值赋值给表单,尤其是id
|
||||
clientForm.value = {
|
||||
id: row.id, // 关键:传递用户ID
|
||||
feeTemplate: row.feeTemplate || '', // 回显手续费模板
|
||||
riskTemplate: row.riskTemplate || '', // 回显风控模板
|
||||
agentUsername: row.agentUsername || '', // 回显所属上级
|
||||
email: row.email || '', // 回显邮箱
|
||||
phone: row.phone || '', // 回显手机号
|
||||
countryCode: row.countryCode || '+86', // 回显国家码
|
||||
isUpgrade: row.isUpgrade || 1, // 回显是否升级
|
||||
userName: row.username || '', // 回显用户名
|
||||
status: row.status || 1, // 回显状态
|
||||
}
|
||||
}
|
||||
|
||||
// 分页事件
|
||||
const handlePaginationChange = (page: number) => {
|
||||
clientSearchForm.value.page = page
|
||||
handleSearch()
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: dialog事件
|
||||
*/
|
||||
|
||||
|
||||
// 映射对象为数组通用方法
|
||||
const mapObjectToOptions = (obj: Record<string, string>) => {
|
||||
return Object.entries(obj).map(([label, value]) => ({
|
||||
label: label, // 模板名称作为显示标签
|
||||
value: value // 模板ID作为对应值
|
||||
}));
|
||||
}
|
||||
|
||||
// 客户添加
|
||||
const handleAdd = async () => {
|
||||
dialogVisible.value = true
|
||||
dialogType.value = 'add'
|
||||
dialogTitle.value = '添加客户'
|
||||
handleDialogType(dialogType.value)
|
||||
//获取表单选项
|
||||
await getOptions()
|
||||
}
|
||||
|
||||
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 fetchClientList()
|
||||
dialogVisible.value = false
|
||||
} else {
|
||||
console.log('编辑客户', clientForm.value)
|
||||
const params = {
|
||||
user_id: clientForm.value.id,
|
||||
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 fetchClientList()
|
||||
dialogVisible.value = false
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('客户表单验证失败', err)
|
||||
}
|
||||
}
|
||||
|
||||
// 弹窗取消
|
||||
const handleCancel = () => {
|
||||
dialogVisible.value = false
|
||||
dialogType.value = 'add'
|
||||
clientForm.value = {
|
||||
//手续费模板
|
||||
feeTemplate: '',
|
||||
//风控模板
|
||||
riskTemplate: '',
|
||||
//所属上级
|
||||
agentUsername: '',
|
||||
email: '',
|
||||
phone: '',
|
||||
countryCode: '+86',
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// 弹窗关闭
|
||||
const handleClose = () => {
|
||||
dialogVisible.value = false
|
||||
dialogType.value = 'add'
|
||||
clientForm.value = {
|
||||
//手续费模板
|
||||
feeTemplate: '',
|
||||
//风控模板
|
||||
riskTemplate: '',
|
||||
//所属上级
|
||||
agentUsername: '',
|
||||
email: '',
|
||||
phone: '',
|
||||
countryCode: '+86',
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.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>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,13 @@
|
|||
// 获取当前年份
|
||||
const currentYear = new Date().getFullYear()
|
||||
|
||||
// 定义禁用日期函数:只能选择今年及以后的日期
|
||||
const disabledDate = (time: Date) => {
|
||||
// 时间早于今年1月1日则禁用
|
||||
return time.getTime() < new Date(currentYear, 0, 1).getTime()
|
||||
}
|
||||
//定义客户搜索表单配置
|
||||
export const clientSearchOptions = [
|
||||
export const clientSearch = [
|
||||
{
|
||||
prop: 'username',
|
||||
label: '用户名',
|
||||
|
|
@ -8,22 +16,24 @@ export const clientSearchOptions = [
|
|||
width: '120px'
|
||||
},
|
||||
{
|
||||
prop: "startDate",
|
||||
prop: "startDateTime",
|
||||
label: '注册开始时间',
|
||||
type: 'date' as const,
|
||||
dateType: 'date' as const, // 时间范围选择器
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
// minWidth: '200px'
|
||||
width: '180px'
|
||||
width: '180px',
|
||||
disabledDate
|
||||
},
|
||||
{
|
||||
prop: "endDate",
|
||||
prop: "endDateTime",
|
||||
label: '注册结束时间',
|
||||
type: 'date' as const,
|
||||
dateType: 'date' as const, // 时间范围选择器
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
// minWidth: '200px'
|
||||
width: '180px'
|
||||
width: '180px',
|
||||
disabledDate
|
||||
},
|
||||
{
|
||||
prop: 'status',
|
||||
|
|
@ -32,37 +42,52 @@ export const clientSearchOptions = [
|
|||
placeholder: '选择状态',
|
||||
width: '80px',
|
||||
options: [
|
||||
{ label: '全部', value: '' },
|
||||
{ label: '开启', value: 1 },
|
||||
{ label: '关闭', value: 0 }
|
||||
{ label: '正常', value: 1 },
|
||||
{ label: '封禁', value: 2 }
|
||||
],
|
||||
}
|
||||
]
|
||||
|
||||
//定义客户table列配置
|
||||
export const clientTableColumns = [
|
||||
{ prop: 'id', label: 'ID' },
|
||||
{ prop: 'username', label: '用户名' },
|
||||
{ prop: 'registerTime', label: '注册时间' },
|
||||
{ prop: 'agentUsername', label: '所属代理' },
|
||||
{ prop: 'status', label: '状态' },
|
||||
{ prop: 'action', label: '操作' },
|
||||
{ prop: 'id', label: 'ID', align: 'center' as const },
|
||||
{ prop: 'username', label: '用户名', align: 'center' as const },
|
||||
{ prop: 'created_at', label: '注册时间', align: 'center' as const },
|
||||
{ prop: 'agentUsername', label: '所属代理', align: 'center' as const },
|
||||
{ label: '状态', slotName: 'status', align: 'center' as const },
|
||||
{ label: '操作', slotName: 'action', align: 'center' as const }
|
||||
]
|
||||
|
||||
//定义添加客户form配置
|
||||
export const clientAddFormOptions = [
|
||||
{ prop: 'clientName', label: '客户名称', type: 'input' as const, placeholder: '客户名称' },
|
||||
export const clientAddFormItem = [
|
||||
// { prop: 'clientName', label: '客户名称', type: 'input' as const, placeholder: '客户名称' },
|
||||
// { prop: 'isUpgrade', label: '是否升级为代理', type: 'radio' as const, placeholder: '是否升级为代理' },
|
||||
{ prop: 'feeTemplate', label: '手续费模板', type: 'select' as const, placeholder: '选择手续费模板' },
|
||||
{ prop: 'isUpgrade', label: '是否升级为代理', type: 'radio' as const, placeholder: '是否升级为代理' },
|
||||
{ prop: 'riskTemplate', label: '风控模板', type: 'select' as const, placeholder: '选择风控模板' },
|
||||
{ prop: 'parentUsername', label: '所属上级', type: 'input' as const, placeholder: '所属上级' },
|
||||
{ prop: 'contactPhoneOrEmail', label: '手机号或邮箱', type: 'input' as const, placeholder: '手机号或邮箱' },
|
||||
{ prop: 'agentUsername', label: '所属上级', type: 'select' as const, placeholder: '所属上级' },
|
||||
{ prop: 'phoneOrEmail', label: '', type: 'slot' as const, slotName: 'phoneOrEmail'},
|
||||
]
|
||||
|
||||
//定义修改客户form配置
|
||||
export const clientChangeFormOptions = [
|
||||
export const clientChangeFormItem = [
|
||||
{ prop: 'feeTemplate', label: '手续费模板', type: 'select' as const, placeholder: '选择手续费模板' },
|
||||
{ prop: 'riskTemplate', label: '风控模板', type: 'select' as const, placeholder: '选择风控模板' },
|
||||
{ prop: 'isUpgrade', label: '是否升级为代理', type: 'radio' as const, placeholder: '是否升级为代理' },
|
||||
{ prop: 'changeName', label: '客户名称', type: 'input' as const, placeholder: '客户名称' },
|
||||
{ prop: 'isUpgrade', label: '是否升级为代理', type: 'radio' as const, placeholder: '是否升级为代理', labelWidth: '140px' },
|
||||
]
|
||||
|
||||
]
|
||||
// 国家码选项
|
||||
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,35 @@
|
|||
import type { FormRules } from 'element-plus'
|
||||
export const rules = (): FormRules => {
|
||||
return {
|
||||
//手续费模板
|
||||
feeTemplate: [
|
||||
{ required: true, message: '请选择手续费模板', trigger: ['blur'] },
|
||||
],
|
||||
//风控模板
|
||||
riskTemplate: [
|
||||
{ required: true, message: '请选择风控模板', trigger: ['blur'] },
|
||||
],
|
||||
//所属上级
|
||||
agentUsername: [
|
||||
{ required: true, message: '请选择所属上级', trigger: ['blur'] },
|
||||
],
|
||||
//手机号
|
||||
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'] }
|
||||
],
|
||||
//是否升级为代理
|
||||
isUpgrade: [
|
||||
{ required: true, message: '请选择是否升级为代理', trigger: ['blur'] },
|
||||
],
|
||||
//客户名称
|
||||
changeName: [
|
||||
{ required: true, message: '请输入客户名称', trigger: ['blur'] },
|
||||
],
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
// 建议单独创建 types.ts 文件管理所有类型
|
||||
// types.ts
|
||||
export interface ClientSearchForm {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
username: string;
|
||||
status: '正常' | '禁用';
|
||||
startDateTime: string;
|
||||
endDateTime: string;
|
||||
}
|
||||
|
||||
export interface ClientItem {
|
||||
id: string | number;
|
||||
username: string;
|
||||
status: 1 | 2;
|
||||
email?: string;
|
||||
phone?: string;
|
||||
countryCode?: string;
|
||||
feeTemplate?: string | number;
|
||||
riskTemplate?: string | number;
|
||||
agentUsername?: string;
|
||||
isUpgrade?: 1 | 2;
|
||||
par_uid?: string | number;
|
||||
parent?: {
|
||||
id: string | number;
|
||||
username: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ClientForm {
|
||||
id: string;
|
||||
feeTemplate: string | number;
|
||||
riskTemplate: string | number;
|
||||
agentUsername: string | number;
|
||||
email: string;
|
||||
phone: string;
|
||||
countryCode: string;
|
||||
isUpgrade: 1 | 2;
|
||||
userName: string;
|
||||
status: 1 | 2;
|
||||
}
|
||||
|
||||
export interface ClientListResponse {
|
||||
data: ClientItem[];
|
||||
total: number;
|
||||
per_page: number | string;
|
||||
}
|
||||
|
|
@ -14,8 +14,8 @@
|
|||
{{ row.type === 1 ? '目录' : '按钮' }}
|
||||
</template>
|
||||
<template #status="{ row }">
|
||||
<el-switch v-model="row.status" :active-value="1" :inactive-value="2" active-text="启用"
|
||||
inactive-text="禁用" @change="handleStatusChange(row)" />
|
||||
<el-switch :model-value="row.status === 1" :active-value="true" :inactive-value="false" active-text="启用"
|
||||
inactive-text="禁用"/>
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<el-button link type="primary" size="small" @click="handleEditMenu(row)">
|
||||
|
|
@ -24,10 +24,6 @@
|
|||
<el-button link type="danger" size="small" @click="handleDeleteMenu(row)">
|
||||
删除
|
||||
</el-button>
|
||||
<!-- <el-button link size="small" :type="row.status === 1 ? 'warning' : 'success'"
|
||||
@click="handleToggleStatus(row)">
|
||||
{{ row.status === 1 ? '禁用' : '启用' }}
|
||||
</el-button> -->
|
||||
</template>
|
||||
</MenuTable>
|
||||
</div>
|
||||
|
|
@ -49,7 +45,7 @@ import MenuForm from '@/components/common/Form.vue'
|
|||
import { menuTableColumns, menuiconOptions, menuiconMap, menuFormItemOptions } from './options'
|
||||
import { rules } from './rules'
|
||||
|
||||
import { getMenuApi, createMenuApi, editMenuApi, deleteMenuApi } from '@/api/modules/menu'
|
||||
import { getMenuApi, createMenuApi, editMenuApi, deleteMenuApi } from '@/api/modules/system/menu'
|
||||
// 定义header配置
|
||||
const menuTitle = ref('菜单管理')
|
||||
const menuHeaderButtons = [
|
||||
|
|
@ -86,6 +82,10 @@ const menuFormOptionsMap = ref({
|
|||
'type': [
|
||||
{ label: '菜单', value: 1 },
|
||||
{ label: '按钮', value: 2 }
|
||||
],
|
||||
'status': [
|
||||
{ label: '启用', value: 1 },
|
||||
{ label: '禁用', value: 2 }
|
||||
]
|
||||
})
|
||||
|
||||
|
|
@ -95,26 +95,18 @@ const menuFormOptionsMap = ref({
|
|||
const handleAddMenu = () => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = '新增菜单'
|
||||
menuForm.value.status = 1 as any // 默认是启用状态
|
||||
}
|
||||
|
||||
/**
|
||||
@description: 菜单数据处理
|
||||
**/
|
||||
// 处理菜单状态,只保留 status=1 的菜单
|
||||
const filterMenus = (menuList: any[]) => {
|
||||
const enabledMenus = menuList.filter(menu => menu.status === 1)
|
||||
enabledMenus.forEach(menu => {
|
||||
if (menu.son && menu.son.length) {
|
||||
menu.son = filterMenus(menu.son)
|
||||
}
|
||||
})
|
||||
return enabledMenus
|
||||
}
|
||||
|
||||
// 获取菜单列表
|
||||
const fetchMenuList = async () => {
|
||||
try {
|
||||
const data = await getMenuApi()
|
||||
menuTree.value = filterMenus(data)
|
||||
menuTree.value = data
|
||||
// 处理parentName选项,添加一级菜单选项
|
||||
const parentNameOptions = data.map(item => ({
|
||||
label: item.name,
|
||||
|
|
@ -156,25 +148,6 @@ const handleDeleteMenu = async (row: any) => {
|
|||
console.error('删除菜单失败')
|
||||
}
|
||||
}
|
||||
const handleStatusChange = async (row: any) => {
|
||||
const oldStatus = row.status === 1 ? 2 : 1
|
||||
|
||||
try {
|
||||
// 调用后端修改状态接口
|
||||
// await updateMenuStatusApi({
|
||||
// id: row.id,
|
||||
// status: row.status
|
||||
// })
|
||||
// ElMessage.success(`已${row.status === 1 ? '启用' : '禁用'}`)
|
||||
// 刷新列表
|
||||
// await fetchMenuList()
|
||||
} catch (err) {
|
||||
// 失败回滚
|
||||
row.status = oldStatus
|
||||
ElMessage.error('状态修改失败')
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@description: 处理弹窗提交/取消/关闭事件
|
||||
|
|
@ -197,6 +170,7 @@ const handleSubmit = async () => {
|
|||
await fetchMenuList()
|
||||
} else {
|
||||
await editMenuApi(menuForm.value)
|
||||
console.log('编辑菜单成功', menuForm.value)
|
||||
await fetchMenuList()
|
||||
}
|
||||
dialogVisible.value = false
|
||||
|
|
|
|||
|
|
@ -43,5 +43,6 @@ export const menuFormItemOptions = [
|
|||
{ prop: 'api', label: '后端路由', type: 'input' as const, placeholder: '请输入后端路由' },
|
||||
{ prop: 'icon', label: '图标名称', type: 'select' as const, placeholder: '请选择图标名称' },
|
||||
{ prop: 'sort', label: '排序', type: 'number' as const, placeholder: '请输入排序,数字越小越靠前' },
|
||||
{ prop: 'type', label: '菜单类型', type: 'radio' as const, placeholder: '请选择菜单类型' }
|
||||
{ prop: 'type', label: '菜单类型', type: 'radio' as const, placeholder: '请选择菜单类型' },
|
||||
{ prop: 'status', label: '状态', type: 'radio' as const, placeholder: '请选择状态' },
|
||||
]
|
||||
|
|
|
|||
|
|
@ -32,6 +32,12 @@ export const rules = (menuForm: any, iconOptions: any[]): FormRules => {
|
|||
type: [
|
||||
{ required: true, message: '请选择菜单类型', trigger: 'change' },
|
||||
{ type: 'number', enum: [1, 2], message: '菜单类型只能是1(菜单)或2(按钮)', trigger: 'change' }
|
||||
],
|
||||
// 8. 状态:必填(单选框默认1,无需手动选)
|
||||
status: [
|
||||
{ required: true, message: '请选择状态', trigger: 'change' },
|
||||
{ type: 'number', enum: [1, 2], message: '状态只能是1(启用)或2(禁用)', trigger: 'change' }
|
||||
]
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -5,8 +5,8 @@
|
|||
<!-- 角色列表表格 -->
|
||||
<RoleTable :table-data="roleTree" :columns="roleTableColumns" row-key="id">
|
||||
<template #status="{ row }">
|
||||
<el-switch v-model="row.status" :active-value="1" :inactive-value="2" active-text="启用"
|
||||
inactive-text="禁用" @change="handleStatusChange(row)" />
|
||||
<el-switch :model-value="row.status === 1" :active-value="true" :inactive-value="false" active-text="启用"
|
||||
inactive-text="禁用"/>
|
||||
</template>
|
||||
<template #createTime="{ row }">
|
||||
{{ row.created_at }}
|
||||
|
|
@ -25,7 +25,7 @@
|
|||
</RoleTable>
|
||||
</div>
|
||||
<!-- 角色新增/编辑弹窗 -->
|
||||
<RoleDialog :visible="dialogVisible" :title="dialogTitle" :type="dialogType" @confirm="handleSubmit"
|
||||
<RoleDialog :visible="dialogVisible" :title="dialogTitle" :type="dialogType" :width="dialogWidth" @confirm="handleSubmit"
|
||||
@cancel="handleCancel" @close="handleClose">
|
||||
<RoleForm :form-data="roleForm" :form-rules="roleFormRules" :form-items="roleFormItemOptions" ref="roleFormRef"
|
||||
:options-map="roleFormOptionsMap" :dialog-type="dialogType">
|
||||
|
|
@ -64,7 +64,7 @@ import RoleForm from '@/components/common/Form.vue'
|
|||
import { roleTableColumns, roleFormItem, permissionsFormItem } from './options'
|
||||
import { rules } from './rules'
|
||||
|
||||
import { getRoleApi, createRoleApi, editRoleApi, deleteRoleApi, getRolePermissionApi, setRolePermissionApi } from '@/api/modules/role'
|
||||
import { getRoleApi, createRoleApi, editRoleApi, deleteRoleApi, getRolePermissionApi, setRolePermissionApi } from '@/api/modules/system/role'
|
||||
|
||||
// 定义header配置
|
||||
const roleTitle = ref('角色管理')
|
||||
|
|
@ -79,6 +79,7 @@ const roleTree = ref<any[]>([])
|
|||
const dialogVisible = ref(false)
|
||||
const dialogTitle = ref('')
|
||||
const dialogType = ref('add')
|
||||
const dialogWidth = ref('600px')
|
||||
|
||||
// 定义角色表单数据
|
||||
const roleForm = ref({
|
||||
|
|
@ -121,11 +122,12 @@ const filterRoles = (roleList: any[]) => {
|
|||
const enabledRoles = roleList.filter(role => role.status === 1)
|
||||
return enabledRoles
|
||||
}
|
||||
|
||||
// 获取角色列表
|
||||
const fetchRoleList = async () => {
|
||||
try {
|
||||
const data = await getRoleApi()
|
||||
roleTree.value = filterRoles(data)
|
||||
roleTree.value = data
|
||||
} catch (err) {
|
||||
console.error('获取角色列表失败', err)
|
||||
}
|
||||
|
|
@ -160,33 +162,15 @@ const handleDeleteRole = async (row: any) => {
|
|||
console.error('删除角色失败')
|
||||
}
|
||||
}
|
||||
// 角色状态切换
|
||||
const handleStatusChange = async (row: any) => {
|
||||
const oldStatus = row.status === 1 ? 2 : 1
|
||||
|
||||
try {
|
||||
// // 调用后端修改状态接口
|
||||
// await updateMenuStatusApi({
|
||||
// id: row.id,
|
||||
// status: row.status
|
||||
// })
|
||||
// ElMessage.success(`已${row.status === 1 ? '启用' : '禁用'}`)
|
||||
// // 刷新列表
|
||||
// await fetchMenuList()
|
||||
} catch (err) {
|
||||
// 失败回滚
|
||||
row.status = oldStatus
|
||||
ElMessage.error('状态修改失败')
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
|
||||
//区分新增和编辑角色表单选项
|
||||
const handleDialogType = (type: string) => {
|
||||
if (type === 'permissions') {
|
||||
roleFormItemOptions.value = permissionsFormItem as any;
|
||||
dialogWidth.value = '900px'
|
||||
} else {
|
||||
roleFormItemOptions.value = roleFormItem as any;
|
||||
dialogWidth.value = '600px'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -309,6 +293,9 @@ const handleClose = () => {
|
|||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.permission-group{
|
||||
width: 100%;
|
||||
}
|
||||
:deep(.el-form-item__content) {
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue