diff --git a/src/pages/Application/ModelManagement/ModelForms/BehaviorRecommendForm.tsx b/src/pages/Application/ModelManagement/ModelForms/BehaviorRecommendForm.tsx
deleted file mode 100644
index 75dde59..0000000
--- a/src/pages/Application/ModelManagement/ModelForms/BehaviorRecommendForm.tsx
+++ /dev/null
@@ -1,41 +0,0 @@
-import React from 'react';
-import { Form, Row, Col, Card, Slider } from 'antd';
-
-const BehaviorRecommendForm: React.FC = () => {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-};
-
-export default BehaviorRecommendForm;
diff --git a/src/pages/Application/ModelManagement/ModelForms/CompetitivenessForm.tsx b/src/pages/Application/ModelManagement/ModelForms/CompetitivenessForm.tsx
deleted file mode 100644
index 951e6ac..0000000
--- a/src/pages/Application/ModelManagement/ModelForms/CompetitivenessForm.tsx
+++ /dev/null
@@ -1,66 +0,0 @@
-import React from 'react';
-import { Form, Row, Col, Card, Slider } from 'antd';
-
-const CompetitivenessForm: React.FC = () => {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-};
-
-export default CompetitivenessForm;
diff --git a/src/pages/Application/ModelManagement/ModelForms/LocationMatchForm.tsx b/src/pages/Application/ModelManagement/ModelForms/LocationMatchForm.tsx
deleted file mode 100644
index 6a5d687..0000000
--- a/src/pages/Application/ModelManagement/ModelForms/LocationMatchForm.tsx
+++ /dev/null
@@ -1,71 +0,0 @@
-import React from 'react';
-import { Form, Row, Col, Card, InputNumber, Select } from 'antd';
-
-const { Option } = Select;
-
-const LocationMatchForm: React.FC = () => {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-};
-
-export default LocationMatchForm;
diff --git a/src/pages/Application/ModelManagement/ModelForms/MatchDegreeForm.tsx b/src/pages/Application/ModelManagement/ModelForms/MatchDegreeForm.tsx
deleted file mode 100644
index 415df69..0000000
--- a/src/pages/Application/ModelManagement/ModelForms/MatchDegreeForm.tsx
+++ /dev/null
@@ -1,66 +0,0 @@
-import React from 'react';
-import { Form, Row, Col, Card, Slider } from 'antd';
-
-const MatchDegreeForm: React.FC = () => {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-};
-
-export default MatchDegreeForm;
diff --git a/src/pages/Application/ModelManagement/ModelForms/PreciseMatchForm.tsx b/src/pages/Application/ModelManagement/ModelForms/PreciseMatchForm.tsx
deleted file mode 100644
index ce55c9d..0000000
--- a/src/pages/Application/ModelManagement/ModelForms/PreciseMatchForm.tsx
+++ /dev/null
@@ -1,105 +0,0 @@
-import React from 'react';
-import { Form, Row, Col, Card, Divider, InputNumber, Slider } from 'antd';
-
-const PreciseMatchForm: React.FC = () => {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-};
-
-export default PreciseMatchForm;
diff --git a/src/pages/Application/ModelManagement/index.tsx b/src/pages/Application/ModelManagement/index.tsx
index 1109a05..aa33693 100644
--- a/src/pages/Application/ModelManagement/index.tsx
+++ b/src/pages/Application/ModelManagement/index.tsx
@@ -1,7 +1,19 @@
import React, { Fragment, useState, useEffect } from 'react';
import { useAccess } from '@umijs/max';
import { getModelConfig, saveModelConfig } from '@/services/application/modelManagement';
-import { Card, Row, Col, Form, Button, message, Menu, Space, Modal } from 'antd';
+import {
+ Card,
+ Row,
+ Col,
+ Form,
+ Button,
+ message,
+ Menu,
+ Space,
+ Modal,
+ Slider,
+ InputNumber,
+} from 'antd';
import {
SaveOutlined,
ReloadOutlined,
@@ -13,87 +25,57 @@ import {
EnvironmentOutlined,
} from '@ant-design/icons';
-// 导入表单组件
-import PreciseMatchForm from './ModelForms/PreciseMatchForm'; //人岗精准匹配模型
-import MatchDegreeForm from './ModelForms/MatchDegreeForm'; //人岗匹配度计算模型
-import BehaviorRecommendForm from './ModelForms/BehaviorRecommendForm'; //基于求职者行为的推荐模型
-import CompetitivenessForm from './ModelForms/CompetitivenessForm'; //竞争力计算模型
-import LocationMatchForm from './ModelForms/LocationMatchForm'; //位置匹配模型
+interface ConfigItem {
+ createTime: string | null;
+ id: number;
+ configName: string;
+ configValue: string;
+ modelType: string;
+ isWeight?: boolean;
+}
+
+interface ModelConfig {
+ [key: string]: ConfigItem[];
+}
function ModelManagement() {
const access = useAccess();
const [form] = Form.useForm();
const [loading, setLoading] = useState(false);
- const [selectedModel, setSelectedModel] = useState('preciseMatch');
- const [configData, setConfigData] = useState({});
+ const [selectedModel, setSelectedModel] = useState('');
+ const [configData, setConfigData] = useState({});
const [totalWeight, setTotalWeight] = useState(0);
+ const [modelTypes, setModelTypes] = useState([]);
- // 模型菜单配置
- const modelMenu = [
- {
- key: 'preciseMatch',
- label: '人岗精准匹配模型',
- icon: ,
- },
- {
- key: 'matchDegree',
- label: '人岗匹配度计算模型',
- icon: ,
- },
- {
- key: 'behaviorRecommend',
- label: '基于求职者行为的推荐模型',
- icon: ,
- },
- {
- key: 'competitiveness',
- label: '竞争力计算模型',
- icon: ,
- },
- {
- key: 'locationMatch',
- label: '位置匹配模型',
- icon: ,
- },
- ];
+ // 图标映射
+ const iconMap: { [key: string]: React.ReactNode } = {
+ 位置匹配模型: ,
+ 人岗精准匹配模型: ,
+ 基于求职者行为推荐模型: ,
+ 竞争力计算模型: ,
+ 人岗匹配度计算模型: ,
+ };
+
+ const defaultIcon = ;
+
+ // 生成模型菜单
+ const generateModelMenu = () => {
+ return modelTypes.map((modelType) => ({
+ key: modelType,
+ label: modelType,
+ icon: iconMap[modelType] || defaultIcon,
+ }));
+ };
// 计算总权重
- 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);
+ const calculateTotalWeight = (modelType: string, formValues: any) => {
+ const modelConfigs = configData[modelType] || [];
+ const total = modelConfigs.reduce((sum: number, config: ConfigItem) => {
+ if (!!config.isWeight) {
+ const value = parseFloat(formValues[config.configName] || 0);
+ return sum + Math.round(value * 100);
+ }
+ return sum;
}, 0);
setTotalWeight(total / 100);
@@ -103,87 +85,121 @@ function ModelManagement() {
calculateTotalWeight(selectedModel, allValues);
};
+ // 设置表单值
+ const setFormValues = (modelType: string, configs: ConfigItem[]) => {
+ const initialValues: any = {};
+ configs.forEach((config) => {
+ // 确保值为数字类型,如果解析失败则使用0
+ const value = parseFloat(config.configValue);
+ initialValues[config.configName] = isNaN(value) ? 0 : value;
+ });
+
+ // 确保设置表单值
+ setTimeout(() => {
+ form.setFieldsValue(initialValues);
+ calculateTotalWeight(modelType, initialValues);
+ }, 30);
+ };
+
// 加载配置数据
- const loadConfigData = async (modelKey: string) => {
+ const loadConfigData = async () => {
setLoading(true);
try {
- const res = await getModelConfig(modelKey);
+ const res = await getModelConfig('');
if (res.code === 200) {
- setConfigData(res.data);
- form.setFieldsValue(res.data);
- calculateTotalWeight(modelKey, res.data);
+ const data = res.data as ConfigItem[];
+
+ // 按模型类型分组
+ const groupedData: ModelConfig = {};
+ const types: string[] = [];
+
+ data.forEach((item: ConfigItem) => {
+ if (!groupedData[item.modelType]) {
+ groupedData[item.modelType] = [];
+ types.push(item.modelType);
+ }
+ groupedData[item.modelType].push(item);
+ });
+
+ setConfigData(groupedData);
+ setModelTypes(types);
+
+ // 设置默认选中的模型 - 修复:使用最新的 groupedData
+ if (types.length > 0) {
+ const currentModel = selectedModel || types[0];
+ setSelectedModel(currentModel);
+ setFormValues(currentModel, groupedData[currentModel]);
+ }
} 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 getUnit = (configName: string): string => {
+ if (configName.includes('薪资') || configName.includes('工资')) {
+ return '元';
+ }
+ if (configName.includes('距离') || configName.includes('半径') || configName.includes('通勤')) {
+ return 'Km';
+ }
+ return '';
+ };
+
+ // 校验权重
+ const validateWeight = (modelType: string, values: any) => {
+ const modelConfigs = configData[modelType] || [];
+ const hasWeight = modelConfigs.some((config) => !!config.isWeight);
+
+ if (!hasWeight) {
+ return true;
+ }
+
+ const total =
+ modelConfigs.reduce((sum: number, config: ConfigItem) => {
+ if (!!config.isWeight) {
+ const value = values[config.configName] || 0;
+ return sum + Math.round(value * 100);
+ }
+ return sum;
+ }, 0) / 100;
+
+ return Math.abs(total - 1) < 0.01;
};
const handleSaveConfig = async (values: any) => {
+ if (!validateWeight(selectedModel, values)) {
+ message.error(`当前模型权重总和为 ${totalWeight.toFixed(2)},应等于 1.00 。`);
+ return;
+ }
+ await performSave(values);
+ };
+
+ // 保存
+ const performSave = async (values: any) => {
setLoading(true);
try {
- const resData = await saveModelConfig({
- modelKey: selectedModel,
- ...values,
- });
+ const modelConfigs = configData[selectedModel] || [];
+
+ const saveData = modelConfigs.map((config) => ({
+ createTime: config.createTime,
+ id: config.id,
+ configName: config.configName,
+ configValue: values[config.configName].toString(),
+ modelType: config.modelType,
+ isWeight: config.isWeight,
+ }));
+
+ const resData = await saveModelConfig({ modelConfigList: saveData });
if (resData.code === 200) {
message.success('配置保存成功');
- setConfigData(values);
+ // 修复:保存成功后重新加载数据,确保使用最新的接口数据
+ await loadConfigData();
} else {
message.error(resData.msg || '保存失败');
}
@@ -194,7 +210,7 @@ function ModelManagement() {
}
};
- // 重置
+ // 重置 - 修复:确保重置后表单数据正确更新
const handleResetConfig = async () => {
Modal.confirm({
title: '重置确认',
@@ -202,51 +218,114 @@ function ModelManagement() {
okText: '确认',
cancelText: '取消',
onOk: async () => {
- loadConfigData(selectedModel);
+ // 重新加载数据,确保使用服务器的最新数据
+ await loadConfigData();
},
});
};
- // 模型切换
+ // 模型切换 - 修复:使用最新的 configData
const handleModelChange = (key: string) => {
setSelectedModel(key);
- loadConfigData(key);
+ const modelConfigs = configData[key] || [];
+ setFormValues(key, modelConfigs);
};
- //颜色
+ // 颜色
const getWeightColor = () => {
- if (totalWeight == 1) return '#52c41a';
+ if (Math.abs(totalWeight - 1) < 0.01) return '#52c41a';
if (totalWeight > 1) return '#ff4d4f';
return '#faad14';
};
// 状态文本
const getWeightStatusText = () => {
- if (totalWeight == 1) return '权重分配合理';
+ if (Math.abs(totalWeight - 1) < 0.01) return '权重分配合理';
if (totalWeight > 1) return '权重超出范围';
return '权重分配不足';
};
- // 渲染表单组件
+ // 检查当前模型是否有权重配置
+ const hasWeightConfig = () => {
+ const modelConfigs = configData[selectedModel] || [];
+ return modelConfigs.some((config) => !!config.isWeight);
+ };
+
+ // 渲染表单组件 - 修复:添加默认值处理
const renderFormComponent = () => {
- switch (selectedModel) {
- case 'preciseMatch':
- return ;
- case 'matchDegree':
- return ;
- case 'behaviorRecommend':
- return ;
- case 'competitiveness':
- return ;
- case 'locationMatch':
- return ;
- default:
- return null;
- }
+ const modelConfigs = configData[selectedModel] || [];
+
+ return modelConfigs.map((config: ConfigItem) => {
+ const unit = getUnit(config.configName);
+ const isWeight = !!config.isWeight;
+
+ return (
+
+ {config.configName}
+ {isWeight && (
+
+ 权重: {(form.getFieldValue(config.configName) || 0).toFixed(2)}
+
+ )}
+
+ }
+ name={config.configName}
+ initialValue={parseFloat(config.configValue) || 0}
+ rules={[
+ { required: true, message: `请输入${config.configName}` },
+ {
+ validator: (_, value) => {
+ if (value === null || value === undefined) return Promise.resolve();
+ if (value < 0) return Promise.reject(new Error('不能小于0'));
+ if (isWeight && value > 1) return Promise.reject(new Error('权重不能大于1'));
+ return Promise.resolve();
+ },
+ },
+ ]}
+ >
+ {isWeight ? (
+ `${(value || 0).toFixed(2)}${unit ? ` ${unit}` : ''}`,
+ }}
+ />
+ ) : (
+
+ )}
+
+ );
+ });
};
useEffect(() => {
- loadConfigData(selectedModel);
+ loadConfigData();
}, []);
return (
@@ -260,7 +339,7 @@ function ModelManagement() {
mode="inline"
selectedKeys={[selectedModel]}
style={{ border: 'none', height: '100%' }}
- items={modelMenu}
+ items={generateModelMenu()}
onClick={({ key }) => handleModelChange(key)}
/>
@@ -269,15 +348,16 @@ function ModelManagement() {
{/* 右侧参数配置区 */}
- {modelMenu.find((item) => item.key === selectedModel)?.label}
+ {selectedModel}
}
extra={
- {selectedModel !== 'locationMatch' && (
+ {hasWeightConfig() && (
}
onClick={handleResetConfig}
+ loading={loading}
disabled={!access.hasPerms('model:config:update')}
>
重置
@@ -361,7 +442,7 @@ function ModelManagement() {
}
style={{ height: '100%' }}
- styles={{ body:{height:'calc(80vh)'}}}
+ styles={{ body: { height: 'calc(80vh)', overflowY: 'auto', padding: '25px 60px' } }}
>