From b7d7437c217a81b5fbb23a22737368c6fc7e2c6e Mon Sep 17 00:00:00 2001 From: Apcallover <1503963513@qq.com> Date: Mon, 27 Apr 2026 09:13:30 +0800 Subject: [PATCH] =?UTF-8?q?flat:=20=E6=B7=BB=E5=8A=A0=E6=9D=A5=E6=BA=90?= =?UTF-8?q?=E7=9B=91=E6=B5=8B=E7=9B=B8=E5=85=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .DS_Store | Bin 0 -> 10244 bytes config/proxy.ts | 3 +- src/app.tsx | 3 +- .../JobInfo/detail.tsx | 119 ++++++++ .../JobInfo/index.tsx | 286 ++++++++++++++++++ .../JobMonitor/detail.tsx | 102 +++++++ .../JobMonitor/index.tsx | 146 +++++++++ .../MetricAdmin/edit.tsx | 118 ++++++++ .../MetricAdmin/index.tsx | 272 +++++++++++++++++ .../SourceManager/edit.tsx | 125 ++++++++ .../SourceManager/index.tsx | 278 +++++++++++++++++ src/services/Management/list.ts | 7 + .../recruitmentDataCollection/jobMonitor.ts | 24 ++ .../recruitmentDataCollection/metricAdmin.ts | 35 +++ .../sourceManager.ts | 35 +++ .../RecruitmentDataCollection/jobMonitor.d.ts | 49 +++ .../metricAdmin.d.ts | 46 +++ .../sourceManager.d.ts | 50 +++ 18 files changed, 1696 insertions(+), 2 deletions(-) create mode 100644 .DS_Store create mode 100644 src/pages/RecruitmentDataCollection/JobInfo/detail.tsx create mode 100644 src/pages/RecruitmentDataCollection/JobInfo/index.tsx create mode 100644 src/pages/RecruitmentDataCollection/JobMonitor/detail.tsx create mode 100644 src/pages/RecruitmentDataCollection/JobMonitor/index.tsx create mode 100644 src/pages/RecruitmentDataCollection/MetricAdmin/edit.tsx create mode 100644 src/pages/RecruitmentDataCollection/MetricAdmin/index.tsx create mode 100644 src/pages/RecruitmentDataCollection/SourceManager/edit.tsx create mode 100644 src/pages/RecruitmentDataCollection/SourceManager/index.tsx create mode 100644 src/services/recruitmentDataCollection/jobMonitor.ts create mode 100644 src/services/recruitmentDataCollection/metricAdmin.ts create mode 100644 src/services/recruitmentDataCollection/sourceManager.ts create mode 100644 src/types/RecruitmentDataCollection/jobMonitor.d.ts create mode 100644 src/types/RecruitmentDataCollection/metricAdmin.d.ts create mode 100644 src/types/RecruitmentDataCollection/sourceManager.d.ts diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..352069cf2de6362286ce8a5363a09a52a42f78f2 GIT binary patch literal 10244 zcmeHM&u<$=6n+z@iQU+VlP0BzUsioVY6vM%3gUoq5>TKtQJjWEN$c9)jk9FEV|LdG zX%yr$+_-S#!XJRd0da&Q7mi#xa7SD?m+#H4o87gIIJHR3NIP$K=6&<#eLM4i`V|B;Ns;3FLPPWLDWcNAj(~0)0$g z94D?OaHG&_@=6nWUnca%dwO(09jeE*M>u#y{kZr@d$1JFa*RXjwoDPWJ1-+;fmn2dNn zLrb(q%d|pO$ahim`)%~xL2DCpw8)72$b7`+7z=NS2JABCBc?lvePMz*A2SDcOA3WI zKe<+BBZkfKo=Tv&2RBsl6*3nw z9|ruXDujXEX|s26oZ=vgo5qf)@%*Tp(0>UiWe+S2^hYrfogtf;VF*s~5XFrFhZCgE zcygEX&iEt3LWVZL*E`q=A3_e>2T{a0z6GD61NiiStN|?igLeJcYlqS4hkDz#Jb!WV zS22;|bU*I*tfz>z)y%In7y!tJl@A;LvIFZdwPUWX(E=|u)&&^$a z>dMnI&pi8FHGipIYaBLRUhk5$rCYW;W;H+M!RytAbk<$-2;Oe{=BIwedVrys1YpB7 z?wxmSp0uGed)}cYea|&bXSeBE#@hbAXZqWb_6}QXxYD-QrDyq;bawWXb>D2QI{Ok9 zc?D~29`d$K*MscCY~QtOb<d7Ve?jKk3OL;eMR5U zxAYVJLch~rA|-O-vbZX)iFt8D+!Q6TD&7~j#2v9Ec7+klFw*)6t1VuVo>@q>WFgX2K$#DU=O7CjItp!$A%)DezqT|l4=0%#)4a2Ol87dgN=4ypep zTyj8 https://preview.pro.ant.design/api/** '/api/': { // 要代理的地址 - target: 'http://localhost:9091', // 本地代理 + target: 'http://39.98.44.136:6024/api/shihezi/', + // target: 'http://localhost:9091', // 本地代理 // target: 'http://wykj.cdwsx.com/api',// 后端 // target: 'http://ks.zhaopinzao8dian.com/api/ks', // 配置了这个可以从 http 代理到 https diff --git a/src/app.tsx b/src/app.tsx index 6b36537..5416802 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -219,7 +219,8 @@ export const request = { ...errorConfig, // baseURL: process.env.NODE_ENV === 'development' ? '' : 'http://ks.zhaopinzao8dian.com/api/ks', // baseURL: process.env.NODE_ENV === 'development' ? '' : 'http://36.105.163.21:30081/api/ks', - baseURL: process.env.NODE_ENV === 'development' ? '' : 'https://xjshzly.longbiosphere.com:30081/api/ks', + // baseURL: process.env.NODE_ENV === 'development' ? '' : 'https://xjshzly.longbiosphere.com:30081/api/ks', + baseURL: process.env.NODE_ENV === 'development' ? '' : 'http://39.98.44.136:6024/api/shihezi/', // baseURL: 'http://39.98.44.136:8080', requestInterceptors: [ (url: any, options: { headers: any }) => { diff --git a/src/pages/RecruitmentDataCollection/JobInfo/detail.tsx b/src/pages/RecruitmentDataCollection/JobInfo/detail.tsx new file mode 100644 index 0000000..b677914 --- /dev/null +++ b/src/pages/RecruitmentDataCollection/JobInfo/detail.tsx @@ -0,0 +1,119 @@ +import React, { useEffect } from 'react'; +import { Modal, Descriptions,Button } from 'antd'; +import { DictValueEnumObj } from '@/components/DictTag'; + +export type DetailModalProps = { + onCancel: () => void; + open: boolean; + values?: Partial; + educationEnum?: DictValueEnumObj; + experienceEnum?: DictValueEnumObj; + areaEnum?: DictValueEnumObj; +}; + +const DetailModal: React.FC = (props) => { + const { values } = props; + + // 可以在这里获取字典数据,如果传入的话 + // 或者通过 useEffect 请求 + + // 如果没有传入字典数据,可以在这里获取 + useEffect(() => { + if (props.open && !props.educationEnum) { + // 这里可以获取字典数据 + // getDictValueEnum('education', true, true).then(setEducationEnum); + } + }, [props.open]); + + const handleCancel = () => { + props.onCancel(); + }; + + // 如果没有数据,显示加载状态 + if (!values) { + return null; + } + + return ( + + 关闭 + + ]} + destroyOnHidden + > + + + {values.jobTitle || '-'} + + + + {values.companyName || '-'} + + + + {`${values.minSalary || 0} - ${values.maxSalary || 0} 元/月`} + + + + {props.educationEnum ? ( + {props.educationEnum[values.education as string] || values.education || '-'} + ) : ( + values.education || '-' + )} + + + + {props.experienceEnum ? ( + {props.experienceEnum[values.experience as string] || values.experience || '-'} + ) : ( + values.experience || '-' + )} + + + + {props.areaEnum ? ( + {props.areaEnum[values.jobLocationAreaCode as string] || values.jobLocationAreaCode || '-'} + ) : ( + values.jobLocationAreaCode || '-' + )} + + + + {values.vacancies || 0} 人 + + + + {values.jobLocation || '-'} + + + +
+ {values.description || '-'} +
+
+ + + {values.view || 0} + + + + {values.createTime || '-'} + + + {values.isPublish !== undefined && ( + + {values.isPublish === 1 ? '已发布' : '未发布'} + + )} +
+
+ ); +}; + +export default DetailModal; \ No newline at end of file diff --git a/src/pages/RecruitmentDataCollection/JobInfo/index.tsx b/src/pages/RecruitmentDataCollection/JobInfo/index.tsx new file mode 100644 index 0000000..a008fe1 --- /dev/null +++ b/src/pages/RecruitmentDataCollection/JobInfo/index.tsx @@ -0,0 +1,286 @@ +import React, { Fragment, useRef, useState, useEffect } from 'react'; +import { history, useAccess } from '@umijs/max'; +import { getCmsJobList, getJobTrend } from '@/services/Management/list'; +import { Button, DatePicker, FormInstance, message, Space, Card } from 'antd'; +import { ActionType, ProColumns, ProTable } from '@ant-design/pro-components'; +import { AlignLeftOutlined } from '@ant-design/icons'; +import { Line } from '@ant-design/charts'; +import dayjs from 'dayjs'; +import { getDictValueEnum } from '@/services/system/dict'; + +import DictTag from '@/components/DictTag'; +import DetailModal from './detail'; + +const { RangePicker } = DatePicker; + +function JobStatistics() { + const access = useAccess(); + const formTableRef = useRef(); + const actionRef = useRef(); + + const [chartData, setChartData] = useState([]); + const [currentRow, setCurrentRow] = useState(); + const [detailVisible, setDetailVisible] = useState(false); + + const [educationEnum, setEducationEnum] = useState([]); + const [experienceEnum, setExperienceEnum] = useState([]); + const [areaEnum, setAreaEnum] = useState([]); + const [isPublishEnum, setIsPublishEnum] = useState([]); + + const [params, setParams] = useState({ + beginTime: dayjs().subtract(13, 'day').format('YYYY-MM-DD'), + endTime: dayjs().format('YYYY-MM-DD'), + }); + + const getPickerValue = () => { + return [dayjs(params.beginTime), dayjs(params.endTime)]; + }; + + const disabledDate = (current: dayjs.Dayjs) => { + const now = dayjs(); + return current.isAfter(now.endOf('day')); + }; + + // 初始化图表数据 + useEffect(() => { + getChartData(); + }, [params.beginTime, params.endTime]); + + useEffect(() => { + getDictValueEnum('education', true, true).then((data) => { + setEducationEnum(data); + }); + getDictValueEnum('experience', true, true).then((data) => { + setExperienceEnum(data); + }); + getDictValueEnum('area', true, true).then((data) => { + setAreaEnum(data); + }); + getDictValueEnum('is_publish', true).then((data) => { + setIsPublishEnum(data); + }); + }, []); + + const handleDateRangeChange = (dates: any, dateStrings: [string, string]) => { + if (dates && dates[0] && dates[1]) { + setParams(() => ({ + beginTime: dateStrings[0], + endTime: dateStrings[1], + })); + } + }; + async function getChartData() { + let { data } = await getJobTrend(params); + setChartData(data); // 暂时使用模拟数据 + } + + // 图表配置 + const chartConfig = { + data: chartData, + xField: 'storageTime', + yField: 'insertCount', + style: { + columnWidthRatio: 0.6, + }, + xAxis: { + label: { + autoHide: true, + autoRotate: false, + }, + }, + point: { + size: 4, + shape: 'circle', + }, + line: { + size: 3, + style: { + lineCap: 'round', + }, + }, + tooltip: { + items: [ + { + field: 'insertCount', + name: '录入数量', + valueFormatter: (v) => `${v}个`, + title: '日期', + }, + ], + }, + }; + + const columns: ProColumns[] = [ + { + title: '岗位采集日期', + dataIndex: 'collectionDate1', + hideInTable: true, + valueType: 'dateRange', + search: { + transform: (value) => { + return { + beginTime: value[0], + endTime: value[1], + }; + }, + }, + }, + { + title: '岗位名称', + dataIndex: 'jobTitle', + valueType: 'text', + align: 'center', + }, + { + title: '最小薪资(元/月)', + dataIndex: 'minSalary', + valueType: 'text', + align: 'center', + hideInSearch: true, + }, + { + title: '最大薪资(元/月)', + dataIndex: 'maxSalary', + valueType: 'text', + align: 'center', + hideInSearch: true, + }, + { + title: '单位名称', + dataIndex: 'companyName', + valueType: 'text', + align: 'center', + }, + { + title: '学历要求', + dataIndex: 'education', + valueType: 'select', + align: 'center', + valueEnum: educationEnum, + render: (_, record) => { + return ; + }, + hideInSearch: true, + }, + { + title: '经验要求', + dataIndex: 'experience', + valueType: 'select', + align: 'center', + valueEnum: experienceEnum, + render: (_, record) => { + return ; + }, + hideInSearch: true, + }, + { + title: '是否发布', + dataIndex: 'isPublish', + valueType: 'select', + align: 'center', + valueEnum: isPublishEnum, + render: (_, record) => { + return ; + }, + hideInSearch: true, + }, + { + title: '招聘人数', + dataIndex: 'vacancies', + valueType: 'text', + align: 'center', + hideInSearch: true, + }, + { + title: '浏览量', + dataIndex: 'view', + valueType: 'text', + align: 'center', + hideInSearch: true, + }, + { + title: '操作', + hideInSearch: true, + align: 'center', + dataIndex: 'jobId', + width: 100, + render: (jobId, record) => ( + + ), + }, + ]; + + return ( + + 📊 采集趋势分析} + style={{ + marginBottom: 16, + }} + extra={ +
+ 统计日期范围: + +
+ } + > +
+ +
+
+ +
+ + actionRef={actionRef} + formRef={formTableRef} + rowKey="jobId" + key="jobStatisticsList" + columns={columns} + request={(params) => { + return getCmsJobList(params as API.ManagementList.ListParams).then((res) => { + const result = { + data: res.rows, + total: res.total, + success: true, + }; + return result; + }); + }} + search={{ + labelWidth: 120, + }} + toolBarRender={false} + pagination={{ + pageSize: 10, + }} + /> +
+ + { + setDetailVisible(false); + setCurrentRow(undefined); + }} + values={currentRow} + /> +
+ ); +} + +export default JobStatistics; diff --git a/src/pages/RecruitmentDataCollection/JobMonitor/detail.tsx b/src/pages/RecruitmentDataCollection/JobMonitor/detail.tsx new file mode 100644 index 0000000..c71bccb --- /dev/null +++ b/src/pages/RecruitmentDataCollection/JobMonitor/detail.tsx @@ -0,0 +1,102 @@ +import { Modal, Table } from 'antd'; +import { ProDescriptions } from '@ant-design/pro-components'; +import type { ColumnsType } from 'antd/es/table'; +import React from 'react'; + +export type StorageFormProps = { + onCancel: (flag?: boolean, formVals?: unknown) => void; + open: boolean; + values?: Partial; +}; + +const ViewStorageDetail: React.FC = (props) => { + // 使用 Antd Table 的标准 ColumnsType + const detailColumns: ColumnsType = [ + // { + // title: '详情ID', + // dataIndex: 'detailId', + // key: 'detailId', + // width: 80, + // ellipsis: true, + // }, + { + title: '入库来源网站名称', + dataIndex: 'websiteName', + key: 'websiteName', + width: 150, + }, + { + title: '入库成功数量', + dataIndex: 'successNumber', + key: 'successNumber', + width: 120, + align: 'center', + }, + { + title: '入库失败数量', + dataIndex: 'failedNumber', + key: 'failedNumber', + width: 120, + align: 'center', + }, + { + title: '入库数据详情', + dataIndex: 'storageDetail', + key: 'storageDetail', + width: 200, + ellipsis: true, + }, + { + title: '入库具体时间', + dataIndex: 'storageTime', + key: 'storageTime', + width: 180, + }, + ]; + + const handleCancel = () => { + props.onCancel(); + }; + + return ( + + {/* 基本信息 */} +
+

基本信息

+ + column={2} + dataSource={props.values || {}} + bordered + > + {/* */} + + + + + +
+ + {/* 入库数据来源详情列表 */} +
+

入库数据来源详情

+ + columns={detailColumns} + dataSource={props.values?.details || []} + rowKey="detailId" + pagination={false} + bordered + size="middle" + scroll={{ x: 800 }} + /> +
+
+ ); +}; + +export default ViewStorageDetail; diff --git a/src/pages/RecruitmentDataCollection/JobMonitor/index.tsx b/src/pages/RecruitmentDataCollection/JobMonitor/index.tsx new file mode 100644 index 0000000..b5bba9b --- /dev/null +++ b/src/pages/RecruitmentDataCollection/JobMonitor/index.tsx @@ -0,0 +1,146 @@ +import React, { Fragment, useRef, useState } from 'react'; +import { useAccess } from '@umijs/max'; +import { + getStorageDetectionList, + getStorageDetectionSingle, +} from '@/services/recruitmentDataCollection/jobMonitor'; +import { Button, FormInstance, message } from 'antd'; +import { ActionType, ProColumns, ProTable } from '@ant-design/pro-components'; +import { EyeOutlined } from '@ant-design/icons'; +import ViewStorageDetail from './detail'; + +function StorageDetectionList() { + const access = useAccess(); + + const formTableRef = useRef(); + const actionRef = useRef(); + + const [currentRow, setCurrentRow] = useState(); + const [modalVisible, setModalVisible] = useState(false); + const [loading, setLoading] = useState(false); + + // 查看详情 + const handleViewDetail = async (detectionId: any) => { + setLoading(true); + try { + const res = await getStorageDetectionSingle(detectionId); + if (res.code === 200) { + setCurrentRow(res.data); + setModalVisible(true); + } else { + message.error(res.msg); + } + } catch (error) { + message.error('获取详情失败'); + } finally { + setLoading(false); + } + }; + + const columns: ProColumns[] = [ + { + title: '采集入库日期', + dataIndex: 'storageDate', + valueType: 'date', + align: 'center', + }, + { + title: '采集入库数量', + dataIndex: 'storageNumber', + valueType: 'text', + align: 'center', + hideInSearch: true, + }, + { + title: '采集入库结果', + dataIndex: 'storageResult', + valueType: 'text', + align: 'center', + hideInSearch: true, + }, + // { + // title: '入库数据来源名称', + // dataIndex: 'websiteName', + // valueType: 'text', + // align: 'center', + // hideInSearch: true, + // ellipsis: true, + // }, + { + title: '入库数据详情', + dataIndex: 'storageDetail', + valueType: 'text', + align: 'center', + hideInSearch: true, + ellipsis: true, + }, + { + title: '是否异常', + dataIndex: 'failedReason', + valueType: 'text', + align: 'center', + }, + { + title: '操作', + hideInSearch: true, + align: 'center', + dataIndex: 'detectionId', + width: 120, + render: (detectionId, record) => ( +
+ +
+ ), + }, + ]; + + return ( + +
+ + actionRef={actionRef} + formRef={formTableRef} + rowKey="detectionId" + key="storageDetectionIndex" + columns={columns} + search={{ labelWidth: 'auto' }} + request={async ( + params: API.StorageDetection.ListParams & { + pageSize?: number; + current?: number; + }, + ) => { + const res = await getStorageDetectionList({ + ...params, + } as API.StorageDetection.ListParams); + return { + data: res.rows, + total: res.total, + success: true, + }; + }} + /> +
+ { + setModalVisible(false); + setCurrentRow(undefined); + }} + /> +
+ ); +} + +export default StorageDetectionList; diff --git a/src/pages/RecruitmentDataCollection/MetricAdmin/edit.tsx b/src/pages/RecruitmentDataCollection/MetricAdmin/edit.tsx new file mode 100644 index 0000000..ec3149c --- /dev/null +++ b/src/pages/RecruitmentDataCollection/MetricAdmin/edit.tsx @@ -0,0 +1,118 @@ +import { + ModalForm, + ProForm, + ProFormSelect, + ProFormText, + ProFormTextArea, + ProDescriptions, +} from '@ant-design/pro-components'; +import { Form } from 'antd'; +import React, { useEffect } from 'react'; +import DictTag from '@/components/DictTag'; + +export type JobIndexFormProps = { + onCancel: (flag?: boolean, formVals?: unknown) => void; + onSubmit: (values: API.JobIndex.JobIndexItem) => Promise; + open: boolean; + values?: Partial; + mode?: 'view' | 'edit' | 'create'; + isActiveEnum: any; +}; + +const JobIndexEdit: React.FC = (props) => { + const [form] = Form.useForm(); + const { mode = props.values ? 'edit' : 'create', isActiveEnum } = props; + + useEffect(() => { + if (props.open) { + form.resetFields(); + if (props.values) { + form.setFieldsValue(props.values); + } + } + }, [form, props.values?.indexId, props.open]); + + const handleCancel = () => { + props.onCancel(); + form.resetFields(); + }; + + const handleFinish = async (values: Record) => { + await props.onSubmit(values as API.JobIndex.JobIndexItem); + }; + + if (mode === 'view') { + return ( + handleCancel(), + footer: null, + }} + submitter={false} + > + column={2} bordered dataSource={props.values || {}}> + {/* */} + + + } + /> + + + ); + } + + return ( + + title={mode === 'edit' ? '编辑指标信息' : '新建指标信息'} + form={form} + autoFocusFirstInput + open={props.open} + modalProps={{ + destroyOnClose: true, + onCancel: () => handleCancel(), + }} + submitTimeout={2000} + onFinish={handleFinish} + > + + + + + + + ); +}; + +export default JobIndexEdit; diff --git a/src/pages/RecruitmentDataCollection/MetricAdmin/index.tsx b/src/pages/RecruitmentDataCollection/MetricAdmin/index.tsx new file mode 100644 index 0000000..d7a8ce3 --- /dev/null +++ b/src/pages/RecruitmentDataCollection/MetricAdmin/index.tsx @@ -0,0 +1,272 @@ +import React, { Fragment, useRef, useState, useEffect } from 'react'; +import { useAccess } from '@umijs/max'; +import { + getJobIndexList, + getJobIndexSingle, + saveJobIndex, + updateJobIndex, + deleteJobIndex, +} from '@/services/recruitmentDataCollection/metricAdmin'; +import { Button, FormInstance, message, Modal } from 'antd'; +import { ActionType, ProColumns, ProTable } from '@ant-design/pro-components'; +import { DeleteOutlined, FormOutlined, PlusOutlined, EyeOutlined } from '@ant-design/icons'; +import { getDictValueEnum } from '@/services/system/dict'; +import DictTag from '@/components/DictTag'; +import EditJobIndexRow from './edit'; + +function JobIndexList() { + const access = useAccess(); + + const formTableRef = useRef(); + const actionRef = useRef(); + + const [currentRow, setCurrentRow] = useState(); + const [modalVisible, setModalVisible] = useState(false); + const [mode, setMode] = useState<'view' | 'edit' | 'create'>('create'); + const [loading, setLoading] = useState(false); + const [isActiveEnum, setIsActiveEnum] = useState([]); + + useEffect(() => { + getDictValueEnum('enable_status', true).then((data) => { + setIsActiveEnum(data); + }); + }, []); + + const handleRemoveOne = async (indexId: any) => { + const hide = message.loading('正在删除'); + if (!indexId) return true; + try { + const resp = await deleteJobIndex({ indexId }); + hide(); + if (resp.code === 200) { + message.success('删除成功,即将刷新'); + } else { + message.error(resp.msg); + } + return true; + } catch (error) { + hide(); + message.error('删除失败,请重试'); + return false; + } + }; + + // 查看详情 + const handleViewDetail = async (indexId: any) => { + setLoading(true); + try { + const res = await getJobIndexSingle(indexId); + if (res.code === 200) { + setCurrentRow(res.data); + setModalVisible(true); + setMode('view'); + } else { + message.error(res.msg); + } + } catch (error) { + message.error('获取详情失败'); + } finally { + setLoading(false); + } + }; + + // 编辑 + const handleEdit = async (indexId: any) => { + setLoading(true); + try { + const res = await getJobIndexSingle(indexId); + if (res.code === 200) { + setCurrentRow(res.data); + setModalVisible(true); + setMode('edit'); + } else { + message.error(res.msg); + } + } catch (error) { + message.error('获取编辑数据失败'); + } finally { + setLoading(false); + } + }; + + const columns: ProColumns[] = [ + // { + // title: '指标ID', + // dataIndex: 'indexId', + // valueType: 'text', + // align: 'center', + // hideInSearch: true, + // }, + { + title: '指标名称', + dataIndex: 'indexName', + valueType: 'text', + align: 'center', + }, + { + title: '指标描述', + dataIndex: 'indexDesc', + valueType: 'text', + align: 'center', + }, + { + title: '是否启用', + dataIndex: 'isActive', + valueType: 'select', + align: 'center', + valueEnum: isActiveEnum, + fieldProps: { + allowClear: false, + defaultValue: '1', + }, + render: (_, record) => { + return ; + }, + }, + { + title: '操作', + hideInSearch: true, + align: 'center', + dataIndex: 'indexId', + width: 300, + render: (indexId, record) => ( +
+ + + +
+ ), + }, + ]; + + return ( + +
+ + actionRef={actionRef} + formRef={formTableRef} + rowKey="indexId" + key="jobIndexIndex" + columns={columns} + search={{ + labelWidth: 'auto', + }} + request={async ( + params: API.JobIndex.ListParams & { + pageSize?: number; + current?: number; + }, + ) => { + const queryParams = { + ...params, + isActive: params.isActive || '1', //默认查询启用 + }; + const res = await getJobIndexList({ ...queryParams } as API.JobIndex.ListParams); + return { + data: res.rows, + total: res.total, + success: true, + }; + }} + toolBarRender={() => [ + , + ]} + /> +
+ { + try { + let resData; + if (values.indexId) { + resData = await updateJobIndex(values as API.JobIndex.UpdateParams); + } else { + resData = await saveJobIndex(values as API.JobIndex.AddParams); + } + if (resData.code === 200) { + setModalVisible(false); + setCurrentRow(undefined); + if (values.indexId) { + message.success('修改成功'); + } else { + message.success('新增成功'); + } + if (actionRef.current) { + actionRef.current.reload(); + } + } else { + message.error(resData.msg || '操作失败'); + } + } catch (error) { + message.error('操作失败'); + } + }} + values={currentRow} + onCancel={() => { + setModalVisible(false); + setCurrentRow(undefined); + }} + /> +
+ ); +} + +export default JobIndexList; diff --git a/src/pages/RecruitmentDataCollection/SourceManager/edit.tsx b/src/pages/RecruitmentDataCollection/SourceManager/edit.tsx new file mode 100644 index 0000000..d5c3ef5 --- /dev/null +++ b/src/pages/RecruitmentDataCollection/SourceManager/edit.tsx @@ -0,0 +1,125 @@ +import { + ModalForm, + ProForm, + ProFormSelect, + ProFormText, + ProDescriptions, +} from '@ant-design/pro-components'; +import { Form } from 'antd'; +import React, { useEffect } from 'react'; +import DictTag from '@/components/DictTag'; + +export type WebsiteFormProps = { + onCancel: (flag?: boolean, formVals?: unknown) => void; + onSubmit: (values: API.Website.WebsiteItem) => Promise; + open: boolean; + values?: Partial; + mode?: 'view' | 'edit' | 'create'; + isActiveEnum: any; +}; + +const WebsiteEdit: React.FC = (props) => { + const [form] = Form.useForm(); + const { mode = props.values ? 'edit' : 'create', isActiveEnum } = props; + + useEffect(() => { + if (props.open) { + form.resetFields(); + if (props.values) { + form.setFieldsValue(props.values); + } + } + }, [form, props.values?.websiteId, props.open]); + + const handleCancel = () => { + props.onCancel(); + form.resetFields(); + }; + + const handleFinish = async (values: Record) => { + await props.onSubmit(values as API.Website.WebsiteItem); + }; + + if (mode === 'view') { + return ( + handleCancel(), + footer: null, + }} + submitter={false} + > + column={2} bordered dataSource={props.values || {}}> + {/* */} + + + + } + /> + + + ); + } + + return ( + + title={mode === 'edit' ? '编辑网站信息' : '新建网站信息'} + form={form} + autoFocusFirstInput + open={props.open} + modalProps={{ + destroyOnClose: true, + onCancel: () => handleCancel(), + }} + submitTimeout={2000} + onFinish={handleFinish} + > + + + + + + + + ); +}; + +export default WebsiteEdit; diff --git a/src/pages/RecruitmentDataCollection/SourceManager/index.tsx b/src/pages/RecruitmentDataCollection/SourceManager/index.tsx new file mode 100644 index 0000000..68538ba --- /dev/null +++ b/src/pages/RecruitmentDataCollection/SourceManager/index.tsx @@ -0,0 +1,278 @@ +import React, { Fragment, useRef, useState, useEffect } from 'react'; +import { useAccess } from '@umijs/max'; +import { + getWebsiteList, + getWebsiteSingle, + saveWebsite, + updateWebsite, + deleteWebsite, +} from '@/services/recruitmentDataCollection/sourceManager'; +import { getDictValueEnum } from '@/services/system/dict'; +import { Button, FormInstance, message, Modal } from 'antd'; +import { ActionType, ProColumns, ProTable } from '@ant-design/pro-components'; +import DictTag from '@/components/DictTag'; +import { DeleteOutlined, FormOutlined, PlusOutlined, EyeOutlined } from '@ant-design/icons'; +import EditWebsiteRow from './edit'; + +function WebsiteList() { + const access = useAccess(); + + const formTableRef = useRef(); + const actionRef = useRef(); + + const [currentRow, setCurrentRow] = useState(); + const [modalVisible, setModalVisible] = useState(false); + const [mode, setMode] = useState<'view' | 'edit' | 'create'>('create'); + const [loading, setLoading] = useState(false); + const [isActiveEnum, setIsActiveEnum] = useState([]); + + useEffect(() => { + getDictValueEnum('enable_status', true).then((data) => { + setIsActiveEnum(data); + }); + }, []); + + const handleRemoveOne = async (websiteId: any) => { + const hide = message.loading('正在删除'); + if (!websiteId) return true; + try { + const resp = await deleteWebsite({ websiteId }); + hide(); + if (resp.code === 200) { + message.success('删除成功,即将刷新'); + } else { + message.error(resp.msg); + } + return true; + } catch (error) { + hide(); + message.error('删除失败,请重试'); + return false; + } + }; + + // 查看详情 + const handleViewDetail = async (websiteId: any) => { + setLoading(true); + try { + const res = await getWebsiteSingle(websiteId); + if (res.code === 200) { + setCurrentRow(res.data); + setModalVisible(true); + setMode('view'); + } else { + message.error(res.msg); + } + } catch (error) { + message.error('获取详情失败'); + } finally { + setLoading(false); + } + }; + + // 编辑 + const handleEdit = async (websiteId: any) => { + setLoading(true); + try { + const res = await getWebsiteSingle(websiteId); + if (res.code === 200) { + setCurrentRow(res.data); + setModalVisible(true); + setMode('edit'); + } else { + message.error(res.msg); + } + } catch (error) { + message.error('获取编辑数据失败'); + } finally { + setLoading(false); + } + }; + + const columns: ProColumns[] = [ + // { + // title: '网站ID', + // dataIndex: 'websiteId', + // valueType: 'text', + // align: 'center', + // hideInSearch: true, + // }, + { + title: '网站名称', + dataIndex: 'websiteName', + valueType: 'text', + align: 'center', + }, + { + title: '网站地址', + dataIndex: 'websiteUrl', + valueType: 'text', + align: 'center', + }, + { + title: '归属单位公司', + dataIndex: 'websiteOwnerCompany', + valueType: 'text', // 改为文本输入 + align: 'center', + }, + { + title: '是否启用', + dataIndex: 'isActive', + valueType: 'select', + align: 'center', + valueEnum: isActiveEnum, + fieldProps: { + allowClear: false, + defaultValue: '1', + }, + render: (_, record) => { + return ; + }, + }, + { + title: '操作', + hideInSearch: true, + align: 'center', + dataIndex: 'websiteId', + width: 300, + render: (websiteId, record) => ( +
+ + + +
+ ), + }, + ]; + + return ( + +
+ + actionRef={actionRef} + formRef={formTableRef} + rowKey="websiteId" + key="websiteIndex" + columns={columns} + search={{ + labelWidth: 'auto', + }} + request={async ( + params: API.Website.ListParams & { + pageSize?: number; + current?: number; + }, + ) => { + const queryParams = { + ...params, + isActive: params.isActive || '1', //默认查询启用 + }; + const res = await getWebsiteList({ ...queryParams } as API.Website.ListParams); + return { + data: res.rows, + total: res.total, + success: true, + }; + }} + toolBarRender={() => [ + , + ]} + /> +
+ { + try { + let resData; + if (values.websiteId) { + resData = await updateWebsite(values as API.Website.UpdateParams); + } else { + resData = await saveWebsite(values as API.Website.AddParams); + } + if (resData.code === 200) { + setModalVisible(false); + setCurrentRow(undefined); + if (values.websiteId) { + message.success('修改成功'); + } else { + message.success('新增成功'); + } + if (actionRef.current) { + actionRef.current.reload(); + } + } else { + message.error(resData.msg || '操作失败'); + } + } catch (error) { + message.error('操作失败'); + } + }} + values={currentRow} + onCancel={() => { + setModalVisible(false); + setCurrentRow(undefined); + }} + /> +
+ ); +} + +export default WebsiteList; diff --git a/src/services/Management/list.ts b/src/services/Management/list.ts index 45a52f3..b165847 100644 --- a/src/services/Management/list.ts +++ b/src/services/Management/list.ts @@ -57,6 +57,13 @@ export async function exportCmsJobCandidates(ids: string) { }); } +export async function getJobTrend(params) { + return request(`/api/cms/jobTrend/list`, { + method: 'GET', + params: params + }); +} + export async function addCmsEmployeeConfirmList(params?: API.Management.Hire) { return request(`/api/cms/employeeConfirm`, { method: 'POST', diff --git a/src/services/recruitmentDataCollection/jobMonitor.ts b/src/services/recruitmentDataCollection/jobMonitor.ts new file mode 100644 index 0000000..038cb5d --- /dev/null +++ b/src/services/recruitmentDataCollection/jobMonitor.ts @@ -0,0 +1,24 @@ +import { request } from '@umijs/max'; + +export async function getStorageDetectionList(params?: API.StorageDetection.ListParams) { + return request(`/api/cms/storageDetection/getList`, { + method: 'GET', + params: params, + }); +} + +export async function getStorageDetectionSingle(detectionId: string) { + return request( + `/api/cms/storageDetection/getSingle/${detectionId}`, + { + method: 'GET', + }, + ); +} + +export async function saveStorageDetection(params?: API.StorageDetection.AddParams) { + return request(`/api/cms/storageDetection/save`, { + method: 'POST', + data: params, + }); +} diff --git a/src/services/recruitmentDataCollection/metricAdmin.ts b/src/services/recruitmentDataCollection/metricAdmin.ts new file mode 100644 index 0000000..f0840c0 --- /dev/null +++ b/src/services/recruitmentDataCollection/metricAdmin.ts @@ -0,0 +1,35 @@ +import { request } from '@umijs/max'; + +export async function getJobIndexList(params?: API.JobIndex.ListParams) { + return request(`/api/cms/jobIndex/getList`, { + method: 'GET', + params: params, + }); +} + +export async function getJobIndexSingle(indexId: string) { + return request(`/api/cms/jobIndex/getSingle/${indexId}`, { + method: 'GET', + }); +} + +export async function saveJobIndex(params?: API.JobIndex.AddParams) { + return request(`/api/cms/jobIndex/save`, { + method: 'POST', + data: params, + }); +} + +export async function updateJobIndex(params?: API.JobIndex.UpdateParams) { + return request(`/api/cms/jobIndex/update`, { + method: 'POST', + data: params, + }); +} + +export async function deleteJobIndex(params?: API.JobIndex.DeleteParams) { + return request(`/api/cms/jobIndex/delete`, { + method: 'POST', + data: params, + }); +} diff --git a/src/services/recruitmentDataCollection/sourceManager.ts b/src/services/recruitmentDataCollection/sourceManager.ts new file mode 100644 index 0000000..709321c --- /dev/null +++ b/src/services/recruitmentDataCollection/sourceManager.ts @@ -0,0 +1,35 @@ +import { request } from '@umijs/max'; + +export async function getWebsiteList(params?: API.Website.ListParams) { + return request(`/api/cms/website/getList`, { + method: 'GET', + params: params, + }); +} + +export async function getWebsiteSingle(websiteId: string) { + return request(`/api/cms/website/getSingle/${websiteId}`, { + method: 'GET', + }); +} + +export async function saveWebsite(params?: API.Website.AddParams) { + return request(`/api/cms/website/save`, { + method: 'POST', + data: params, + }); +} + +export async function updateWebsite(params?: API.Website.UpdateParams) { + return request(`/api/cms/website/update`, { + method: 'POST', + data: params, + }); +} + +export async function deleteWebsite(params?: API.Website.DeleteParams) { + return request(`/api/cms/website/delete`, { + method: 'POST', + data: params, + }); +} diff --git a/src/types/RecruitmentDataCollection/jobMonitor.d.ts b/src/types/RecruitmentDataCollection/jobMonitor.d.ts new file mode 100644 index 0000000..12c5264 --- /dev/null +++ b/src/types/RecruitmentDataCollection/jobMonitor.d.ts @@ -0,0 +1,49 @@ +declare namespace API.StorageDetection { + export interface StorageDetailItem { + detailId?: string; + websiteId?: string; + websiteName?: string; + successNumber?: string; + failedNumber?: string; + storageDetail?: string; + storageTime?: string; + } + + export interface StorageItem { + detectionId?: string; + storageDate?: string; + storageNumber?: string; + storageResult?: string; + storageDetail?: string; + websiteId?: string; + websiteName?: string; + details?: StorageDetailItem[]; + } + + export interface AddParams { + storageDate?: string; + storageNumber?: string; + storageResult?: string; + storageDetail?: string; + websiteName?: string; + } + + export interface ListParams { + storageDate?: string; + pageSize?: number; + current?: number; + } + + export interface StorageIdResult { + code: number; + msg: string; + data: StorageItem; + } + + export interface StoragePageResult { + code: number; + msg: string; + total: number; + rows: Array; + } +} \ No newline at end of file diff --git a/src/types/RecruitmentDataCollection/metricAdmin.d.ts b/src/types/RecruitmentDataCollection/metricAdmin.d.ts new file mode 100644 index 0000000..ff50903 --- /dev/null +++ b/src/types/RecruitmentDataCollection/metricAdmin.d.ts @@ -0,0 +1,46 @@ +declare namespace API.JobIndex { + export interface JobIndexItem { + indexId?: string; + indexName?: string; + indexDesc?: string; + isActive?: string; + } + + export interface AddParams { + indexName?: string; + indexDesc?: string; + isActive?: string; + } + + export interface UpdateParams { + indexId?: string; + indexName?: string; + indexDesc?: string; + isActive?: string; + } + + export interface ListParams { + indexName?: string; + indexDesc?: string; + isActive?: string; + pageSize?: number; + current?: number; + } + + export interface DeleteParams { + indexId?: string; + } + + export interface JobIndexIdResult { + code: number; + msg: string; + data: JobIndexItem; + } + + export interface JobIndexPageResult { + code: number; + msg: string; + total: number; + rows: Array; + } +} diff --git a/src/types/RecruitmentDataCollection/sourceManager.d.ts b/src/types/RecruitmentDataCollection/sourceManager.d.ts new file mode 100644 index 0000000..a5c7c69 --- /dev/null +++ b/src/types/RecruitmentDataCollection/sourceManager.d.ts @@ -0,0 +1,50 @@ +declare namespace API.Website { + export interface WebsiteItem { + websiteId?: string; + websiteName?: string; + websiteUrl?: string; + websiteOwnerCompany?: string; + isActive?: string; + } + + export interface AddParams { + websiteName?: string; + websiteUrl?: string; + websiteOwnerCompany?: string; + isActive?: string; + } + + export interface UpdateParams { + websiteId?: string; + websiteName?: string; + websiteUrl?: string; + websiteOwnerCompany?: string; + isActive?: string; + } + + export interface ListParams { + websiteName?: string; + websiteUrl?: string; + websiteOwnerCompany?: string; + isActive?: string; + pageSize?: number; + current?: number; + } + + export interface DeleteParams { + websiteId?: string; + } + + export interface WebsiteIdResult { + code: number; + msg: string; + data: WebsiteItem; + } + + export interface WebsitePageResult { + code: number; + msg: string; + total: number; + rows: Array; + } +}