diff --git a/src/api/modules/user.ts b/src/api/modules/user.ts index 1a49c4d..9b79257 100644 --- a/src/api/modules/user.ts +++ b/src/api/modules/user.ts @@ -49,8 +49,8 @@ export async function changePasswordApi(params: ChangePasswordParams) { * 退出登录 * @returns Promise 无返回值 */ -export async function logoutApi() { - return request.post('/logout'); +export async function logoutApi(params: { device_no: string }) { + return request.post('/logout', params); } /** diff --git a/src/components/layout/Header.vue b/src/components/layout/Header.vue index 8a3276d..6799f21 100644 --- a/src/components/layout/Header.vue +++ b/src/components/layout/Header.vue @@ -59,7 +59,8 @@ const handleChangePassword = () => { const handleLogout = () => { // 示例:退出登录逻辑 - userStore.logout() + const deviceNo = getStorage('device_no') as string + userStore.logout({ device_no: deviceNo }) } // 处理菜单项点击 diff --git a/src/stores/modules/user.ts b/src/stores/modules/user.ts index 7af6f32..0f31d9e 100644 --- a/src/stores/modules/user.ts +++ b/src/stores/modules/user.ts @@ -55,7 +55,7 @@ const clearUserInfo = () => { removeStorage('userInfo'); removeStorage('menusInfo'); removeStorage('permissionsInfo'); - + removeStorage('device_no'); console.log('用户信息已清空'); }; @@ -100,9 +100,9 @@ const register = async (params: RegisterParams) => { /** * 退出登录 */ -const logout = async () => { +const logout = async (params: { device_no: string }) => { try { - await logoutApi(); + await logoutApi(params); } catch (error) { console.error('退出登录接口调用失败', error); } finally { diff --git a/src/views/account/client/index.vue b/src/views/account/client/index.vue index 788fc7b..4059237 100644 --- a/src/views/account/client/index.vue +++ b/src/views/account/client/index.vue @@ -9,10 +9,11 @@ - + + + - + +
- 邮箱 - + 手机号
- - - - -
- {{ item.name }} - {{ item.code }} -
-
-
- -
+
+ + + + + + +
+ {{ item.name }} + {{ item.code }} +
+
+
+ +
+
@@ -76,31 +79,38 @@ import { getClientListApi, getFeeTemplateOptionsApi, getRiskTemplateOptionsApi, import { clientSearch, clientAddFormItem, clientChangeFormItem, countryOptions, clientTableColumns } from './options' import { rules } from './rules' -//定义客户搜索表单配置 +/** + * @description 定义搜索数据 + */ const clientSearchOptions = ref(clientSearch) const clientSearchForm = ref({ - currentPage: 1, - pageSize: 1, username: '', status: '正常', startDateTime: '', endDateTime: '', }) -//定义表格配置 +/** + * @description 定义表格数据 + */ const clientTree = ref([]) -const treeProps = ref({ - children: 'parent', - label: 'username' -}) -// 定义分页配置 +/** + * @description 定义分页数据 + */ const total = ref(0) +const currentPage = ref(1) +const pageSize = ref(10) -// 定义dialog配置 +/** + * @description 定义dialog数据 + */ const dialogTitle = ref('') const dialogVisible = ref(false) const dialogType = ref('add') +/** + * @description 定义dialog中form的数据 + */ const clientForm = ref({ //手续费模板 feeTemplate: '', @@ -116,28 +126,18 @@ const clientForm = ref({ //是否变更客户名 userName: '', status: 1, - id: '', }) // 定义客户表单配置 const clientFormRef = ref() const clientFormRules = ref(rules()) -const clientFormOptions = ref(clientAddFormItem) -// 定义客户表单选项切换 -const activeTab = ref('email') -// 定义客户表单选项类型 -interface FormOption { - label: string; - value: string | number; -} -interface ClientFormOptionsMap { - feeTemplate: FormOption[]; - riskTemplate: FormOption[]; - agentUsername: FormOption[]; - isUpgrade: FormOption[]; - status: FormOption[]; -} -const clientFormOptionsMap = ref({ +// 定义客户表单选项是否已获取 +const optionsFetched = ref(false) +const useId = ref('') +const clientFormOptions = computed(() => { + return dialogType.value === 'add' ? clientAddFormItem : clientChangeFormItem; +}) +const clientFormOptionsMap = ref({ feeTemplate: [], riskTemplate: [], agentUsername: [], @@ -150,62 +150,12 @@ const clientFormOptionsMap = ref({ { label: '禁用', value: 2 } ], }) +// 定义客户表单选项切换 +const activeTab = ref('email') -// 获取账户列表 -const fetchClientList = async () => { - const params = { - page: clientSearchForm.value.currentPage, - 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 = Number(data.total) - clientSearchForm.value.currentPage = Number(data.current_page) - clientSearchForm.value.pageSize = Number(data.per_page) -} -onMounted(async () => { - await fetchClientList() -}) /** - * @description: 搜索事件 + * @description 统一数据处理方法 */ - -// 搜索 -const handleSearch = async () => { - try { - await fetchClientList() - } catch (err) { - console.error('获取客户列表失败', err) - } -} - -// 搜索重置 -const handleReset = () => { - clientSearchForm.value = { - currentPage: 1, - pageSize: 1, - username: '', - status: '正常', - startDateTime: '', - endDateTime: '', - } - fetchClientList() // 重置后自动刷新列表 -} - -// 客户表单选项切换 -const handleDialogType = (type: string) => { - clientFormOptions.value = type === 'add' ? clientAddFormItem : clientChangeFormItem -} - // 映射对象为数组通用方法 const mapObjectToOptions = (obj: Record) => { return Object.entries(obj).map(([label, value]) => ({ @@ -214,8 +164,34 @@ const mapObjectToOptions = (obj: Record) => { })) } -//统一获取表单选项 -const optionsFetched = ref(false) +/** +* @description 统一获取数据方法 +*/ +const getClientList = async () => { + const params = { + page: currentPage.value, + page_size: pageSize.value, + 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, + //注册时间 + 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 @@ -227,7 +203,7 @@ const getOptions = async () => { getRiskTemplateOptionsApi() ]) - // 修正映射:label=名称,value=ID + // label=名称,value=ID clientFormOptionsMap.value.agentUsername = mapObjectToOptions(agentRes as Record) clientFormOptionsMap.value.feeTemplate = mapObjectToOptions(feeTemplateRes as Record) clientFormOptionsMap.value.riskTemplate = mapObjectToOptions(riskTemplateRes as Record) @@ -235,57 +211,90 @@ const getOptions = async () => { optionsFetched.value = true // 标记已获取 } catch (error) { console.error('获取表单选项失败:', error) - ElMessage.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 + getOptions() +} + /** - * @description: 表格修改事件 + * @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: { currentPage: number, pageSize: number }) => { - clientSearchForm.value.currentPage = page.currentPage - clientSearchForm.value.pageSize = page.pageSize + currentPage.value = page.currentPage + pageSize.value = page.pageSize handleSearch() } /** - * @description: dialog事件 + * @description 客户弹窗方法 */ - -// 客户添加 -const handleAdd = async () => { - dialogVisible.value = true - dialogType.value = 'add' - dialogTitle.value = '添加客户' - handleDialogType(dialogType.value) - //获取表单选项 - await getOptions() -} - const handleSubmit = async () => { try { await clientFormRef.value?.validate() @@ -299,11 +308,11 @@ const handleSubmit = async () => { fee_setting_id: clientForm.value.feeTemplate, } await createClientApi(params) - await fetchClientList() + await getClientList() dialogVisible.value = false } else { const params = { - user_id: clientForm.value.id, + user_id: useId.value, risk_control_id: clientForm.value.riskTemplate, fee_setting_id: clientForm.value.feeTemplate, username: clientForm.value.userName, @@ -311,87 +320,54 @@ const handleSubmit = async () => { status: clientForm.value.status, } await editClientApi(params) - await fetchClientList() + await getClientList() dialogVisible.value = false } } catch (err) { console.error('客户表单验证失败', err) } } - -const resetForm = () => { - clientForm.value = { - //手续费模板 - feeTemplate: '', - //风控模板 - riskTemplate: '', - //所属上级 - agentUsername: '', - email: '', - phone: '', - countryCode: '+86', - //是否升级 - isUpgrade: 1, - //是否变更客户名 - userName: '', - status: 1, - id: '', - - } -} - -// 弹窗取消 const handleCancel = () => { + // 关闭弹窗 dialogVisible.value = false - dialogType.value = 'add' - resetForm() } - -// 弹窗关闭 const handleClose = () => { + // 关闭弹窗 dialogVisible.value = false - dialogType.value = 'add' - resetForm() } - diff --git a/src/views/user/Login.vue b/src/views/user/Login.vue index 7fdf850..951fa0f 100644 --- a/src/views/user/Login.vue +++ b/src/views/user/Login.vue @@ -88,6 +88,22 @@ const loginRules = ref({ //方法 +/** + * 生成时间戳拼接6位随机数字的字符串 + * @returns {string} 格式示例:1710688800000123456 + */ +function generateTimestampWithRandomNum() { + // 1. 获取当前时间戳(毫秒级) + const timestamp = Date.now(); + + // 2. 生成6位随机数字(范围:000000 ~ 999999) + // Math.random()生成0~1的随机数,乘以1000000后取整,不足6位补0 + const random6Num = Math.floor(Math.random() * 1000000).toString().padStart(6, '0'); + + // 3. 拼接并返回 + return timestamp + random6Num; +} + // 登录 const handleLogin = async () => { if (!loginFormRef.value) return; @@ -120,15 +136,19 @@ const handleLogin = async () => { else if (currentTab === 'password') { // 校验密码+验证码 await loginFormRef.value.validateField(['password', 'code']); + // 生成device_no并保存到变量(关键修改1) + const deviceNo = generateTimestampWithRandomNum(); // 登录参数 const loginParams = { email: loginForm.value.email, mobile: loginForm.value.phone, login_type: getStorage('loginType', 'session') === 'email' ? 1 : 2, code: loginForm.value.code, - password: loginForm.value.password + password: loginForm.value.password, + device_no: deviceNo }; await userStore.login(loginParams); + setStorage('device_no', deviceNo); activeTab.value = 'email'; // 重置tab hideTab.value = false; loginForm.value = { // 清空表单