From 3c27574c3b78ee18093f833bef6397ca6abf52b0 Mon Sep 17 00:00:00 2001 From: bin <719488417@qq.com> Date: Wed, 12 Nov 2025 16:09:35 +0800 Subject: [PATCH] =?UTF-8?q?refactor=20:=20=E9=87=8D=E6=9E=84=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=E7=AE=A1=E7=90=86=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Application/ModelManagement/index.tsx | 1132 ++++++++++++----- src/services/application/modelManagement.ts | 15 + 2 files changed, 851 insertions(+), 296 deletions(-) create mode 100644 src/services/application/modelManagement.ts diff --git a/src/pages/Application/ModelManagement/index.tsx b/src/pages/Application/ModelManagement/index.tsx index defdd1c..3432866 100644 --- a/src/pages/Application/ModelManagement/index.tsx +++ b/src/pages/Application/ModelManagement/index.tsx @@ -1,303 +1,843 @@ -import React, { Fragment } from 'react'; -import { Collapse, InputNumber, Select } from 'antd'; -import style from './modelStyle.less'; +import React, { Fragment, useState, useEffect } from 'react'; +import { useAccess } from '@umijs/max'; +import { getModelConfig, saveModelConfig } from '@/services/application/modelManagement'; +import { + Card, + Row, + Col, + Form, + InputNumber, + Slider, + Button, + message, + Divider, + Menu, + Space, + Typography, + Select, + Modal, +} from 'antd'; +import { + SaveOutlined, + ReloadOutlined, + SettingOutlined, + UserOutlined, + CalculatorOutlined, + EyeOutlined, + TrophyOutlined, + EnvironmentOutlined, +} from '@ant-design/icons'; -export default function ModelManagement() { - const text = ` - A dog is a type of domesticated animal. - Known for its loyalty and faithfulness, - it can be found as a welcome guest in many households across the world. -`; +const { Title, Text } = Typography; +const { Option } = Select; + +function ModelManagement() { + const access = useAccess(); + const [form] = Form.useForm(); + const [loading, setLoading] = useState(false); + const [selectedModel, setSelectedModel] = useState('preciseMatch'); + const [configData, setConfigData] = useState({}); + const [totalWeight, setTotalWeight] = useState(0); + + // 模型菜单配置 + const modelMenu = [ + { + key: 'preciseMatch', + label: '人岗精准匹配模型', + icon: , + }, + { + key: 'matchDegree', + label: '人岗匹配度计算模型', + icon: , + }, + { + key: 'behaviorRecommend', + label: '基于求职者行为的推荐模型', + icon: , + }, + { + key: 'competitiveness', + label: '竞争力计算模型', + icon: , + }, + { + key: 'locationMatch', + label: '位置匹配模型', + icon: , + }, + ]; + + // 计算总权重 + const calculateTotalWeight = (modelKey: string, formValues: any) => { + const weightConfigs: any = { + preciseMatch: [ + 'firstWorkExpWeight', + 'firstWorkExpTimeWeight', + 'secondWorkExpWeight', + 'secondWorkExpTimeWeight', + 'thirdWorkExpWeight', + 'thirdWorkExpTimeWeight', + ], + matchDegree: [ + 'educationWeight', + 'genderWeight', + 'ageWeight', + 'salaryWeight', + 'experienceWeight', + 'jobTitleWeight', + 'areaWeight', + ], + behaviorRecommend: ['browseWeight', 'applyWeight', 'applyHistoryWeight', 'collectWeight'], + competitiveness: [ + 'educationWeight', + 'genderWeight', + 'ageWeight', + 'salaryWeight', + 'experienceWeight', + 'jobTitleWeight', + 'areaWeight', + ], + locationMatch: [], // 位置匹配模型没有权重 + }; + + const weights = weightConfigs[modelKey] || []; + const total = weights.reduce((sum: number, key: string) => { + const value = formValues[key] || 0; + + return sum + Math.round(value * 100); + }, 0); + + setTotalWeight(total / 100); + }; + + // + const handleFormChange = (changedValues: any, allValues: any) => { + calculateTotalWeight(selectedModel, allValues); + }; + + // 加载配置数据 + const loadConfigData = async (modelKey: string) => { + setLoading(true); + try { + const res = await getModelConfig(modelKey); + if (res.code === 200) { + setConfigData(res.data); + form.setFieldsValue(res.data); + calculateTotalWeight(modelKey, res.data); + } else { + message.error(res.msg); + setDefaultValues(modelKey); + } + } catch (error) { + message.error('加载配置失败'); + setDefaultValues(modelKey); + } finally { + setLoading(false); + } + }; + + // 默认值 + const setDefaultValues = (modelKey: string) => { + const defaultValues: any = { + preciseMatch: { + ageMatchRange: 5, + experienceMatchRange: 2, + firstWorkExpWeight: 0.4, + firstWorkExpTimeWeight: 0.3, + secondWorkExpWeight: 0.3, + secondWorkExpTimeWeight: 0.2, + thirdWorkExpWeight: 0.2, + thirdWorkExpTimeWeight: 0.1, + }, + matchDegree: { + educationWeight: 0.15, + genderWeight: 0.1, + ageWeight: 0.1, + salaryWeight: 0.2, + experienceWeight: 0.15, + jobTitleWeight: 0.2, + areaWeight: 0.1, + }, + behaviorRecommend: { + browseWeight: 0.3, + applyWeight: 0.4, + applyHistoryWeight: 0.2, + collectWeight: 0.1, + }, + competitiveness: { + educationWeight: 0.2, + genderWeight: 0.05, + ageWeight: 0.1, + salaryWeight: 0.15, + experienceWeight: 0.25, + jobTitleWeight: 0.15, + areaWeight: 0.1, + }, + locationMatch: { + distanceAlgorithm: 'euclidean', + subwayDistance: 1000, + businessDistrictDistance: 2000, + personalDistance: 5000, + }, + }; + const values = defaultValues[modelKey] || {}; + setConfigData(values); + form.setFieldsValue(values); + calculateTotalWeight(modelKey, values); + }; + + + const handleSaveConfig = async (values: any) => { + setLoading(true); + try { + const resData = await saveModelConfig({ + modelKey: selectedModel, + ...values, + }); + if (resData.code === 200) { + message.success('配置保存成功'); + setConfigData(values); + } else { + message.error(resData.msg || '保存失败'); + } + } catch (error) { + message.error('保存失败'); + } finally { + setLoading(false); + } + }; + + + const handleResetConfig = async () => { + Modal.confirm({ + title: '重置确认', + content: '确定要重置当前模型的配置参数吗?', + okText: '确认', + cancelText: '取消', + onOk: async () => { + loadConfigData(selectedModel); + }, + }); + }; + + + const handleModelChange = (key: string) => { + setSelectedModel(key); + loadConfigData(key); + }; + + + const getWeightColor = () => { + if (totalWeight == 1) return '#52c41a'; + if (totalWeight > 1) return '#ff4d4f'; + return '#faad14'; + }; + + + const getWeightStatusText = () => { + if (totalWeight == 1) return '权重分配合理'; + if (totalWeight > 1) return '权重超出范围'; + return '权重分配不足'; + }; + + useEffect(() => { + loadConfigData(selectedModel); + }, []); return ( - - -
-
-
年龄匹配范围
-
- -
-
-
-
工作经验范围
-
- -
-
-
-
- -
-
-
学历权重
-
- -
-
-
-
期望薪资权重
-
- -
-
-
-
工作经验权重
-
- -
-
-
-
工作地区权重
-
- -
-
-
-
- -
-
-
浏览记录
-
- -
-
-
-
申请记录
-
- -
-
-
-
收藏记录
-
- -
-
-
-
- -
-
-
学历权重
-
- -
-
-
-
期望薪资权重
-
- -
-
-
-
工作经验权重
-
- -
-
-
-
工作地区权重
-
- -
-
-
-
相似度计算方式
-
- -
-
-
-
索引粒度
-
- -
-
-
-
地铁口附近
-
- -
-
-
-
商圈附近
-
- -
-
-
-
-
+
+ + {/* 左侧模型导航 */} + + + handleModelChange(key)} + /> + + + + {/* 右侧参数配置区 */} + + + + {modelMenu.find((item) => item.key === selectedModel)?.label} + + } + extra={ + + {selectedModel !== 'locationMatch' && ( +
+
+
+ 总权重 +
+
+ {totalWeight.toFixed(2)} +
+
+
+
+
+ 权重状态 +
+
+ {getWeightStatusText()} +
+
+
+ )} + + + + } + style={{ height: '100%' }} + bodyStyle={{ height: 'calc(100% - 60px)', overflow: 'auto' }} + > +
+ {/* 人岗精准匹配模型 */} + {selectedModel === 'preciseMatch' && ( +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ )} + + {/* 人岗匹配度计算模型 */} + {selectedModel === 'matchDegree' && ( +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ )} + + {/* 基于求职者行为的推荐模型 */} + {selectedModel === 'behaviorRecommend' && ( +
+ + + + + + + + + + + + + + + + + + +
+ )} + + {/* 竞争力计算模型 */} + {selectedModel === 'competitiveness' && ( +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ )} + + {/* 位置匹配模型 */} + {selectedModel === 'locationMatch' && ( +
+ + + + + + + + + + + + + + + + + + + + + + +
+ )} +
+ + + +
); } + +export default ModelManagement; diff --git a/src/services/application/modelManagement.ts b/src/services/application/modelManagement.ts new file mode 100644 index 0000000..8168a83 --- /dev/null +++ b/src/services/application/modelManagement.ts @@ -0,0 +1,15 @@ +import { request } from '@umijs/max'; + +export async function getModelConfig(params?: any) { + return request(`/api/cms/appUser/aaa`, { + method: 'GET', + params: params, + }); +} + +export async function saveModelConfig(data?: any) { + return request(`/api/cms/appUser`, { + method: 'POST', + data: data, + }); +}