Compare commits
2 Commits
FengHui
...
b937b189c2
| Author | SHA1 | Date | |
|---|---|---|---|
| b937b189c2 | |||
| 95c31a359a |
@@ -278,18 +278,18 @@ export default [
|
|||||||
{
|
{
|
||||||
name: '公共招聘会列表',
|
name: '公共招聘会列表',
|
||||||
path: '/jobfair/public-job-fair',
|
path: '/jobfair/public-job-fair',
|
||||||
component: './Jobfair/Publicjobfair',
|
component: './Jobfair/PublicJobFair',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '招聘会详情',
|
name: '招聘会详情',
|
||||||
path: '/jobfair/public-job-fair/detail',
|
path: '/jobfair/public-job-fair/detail',
|
||||||
component: './Jobfair/Publicjobfair/Detail',
|
component: './Jobfair/PublicJobFair/Detail',
|
||||||
access: 'canViewPublicJobFairDetail',
|
access: 'canViewPublicJobFairDetail',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '报名人员',
|
name: '报名人员',
|
||||||
path: '/jobfair/public-job-fair/signups',
|
path: '/jobfair/public-job-fair/signups',
|
||||||
component: './Jobfair/Publicjobfair/Signups',
|
component: './Jobfair/PublicJobFair/Signups',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '线下招聘会管理',
|
name: '线下招聘会管理',
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ export type MapProps = {
|
|||||||
open: boolean;
|
open: boolean;
|
||||||
};
|
};
|
||||||
export const aMapConfig = {
|
export const aMapConfig = {
|
||||||
key: 'b79ea14cb17704ab1a1a32c12d72f634',
|
key: '9cfc9370bd8a941951da1cea0308e9e3',
|
||||||
securityJsCode: '04795ef5b20919d34cadc2e33e01d609',
|
securityJsCode: '7b16386c7f744c3ca05595965f2b037f',
|
||||||
};
|
};
|
||||||
|
|
||||||
/** 石河子市地图默认中心点与行政区划编码 */
|
/** 石河子市地图默认中心点与行政区划编码 */
|
||||||
|
|||||||
@@ -40,15 +40,13 @@ const ListEdit: React.FC<ListFormProps> = (props) => {
|
|||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (props.open) {
|
form.resetFields();
|
||||||
form.resetFields();
|
if (props.values) {
|
||||||
if (props.values) {
|
form.setFieldsValue({
|
||||||
form.setFieldsValue({
|
...props.values,
|
||||||
...props.values,
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, [form, props.values?.companyId, props.open]);
|
}, [form, props]);
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
props.onCancel();
|
props.onCancel();
|
||||||
@@ -57,7 +55,6 @@ const ListEdit: React.FC<ListFormProps> = (props) => {
|
|||||||
|
|
||||||
const handleFinish = async (values: Record<string, any>) => {
|
const handleFinish = async (values: Record<string, any>) => {
|
||||||
await props.onSubmit(values as API.CompanyList.Company);
|
await props.onSubmit(values as API.CompanyList.Company);
|
||||||
return true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -146,19 +146,8 @@ function ManagementList() {
|
|||||||
{
|
{
|
||||||
title: '公司行业',
|
title: '公司行业',
|
||||||
dataIndex: 'industry',
|
dataIndex: 'industry',
|
||||||
valueType: 'treeSelect',
|
valueType: 'text',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
fieldProps: {
|
|
||||||
fieldNames: { label: 'label', value: 'id', children: 'children' },
|
|
||||||
treeDefaultExpandAll: false,
|
|
||||||
showSearch: true,
|
|
||||||
allowClear: true,
|
|
||||||
placeholder: '请选择行业',
|
|
||||||
},
|
|
||||||
request: async () => {
|
|
||||||
const res = await getCmsIndustryTreeList();
|
|
||||||
return (res?.data || []) as any;
|
|
||||||
},
|
|
||||||
render: (_, record) => {
|
render: (_, record) => {
|
||||||
return <DictTag enums={industryEnum} value={record.industry} />;
|
return <DictTag enums={industryEnum} value={record.industry} />;
|
||||||
},
|
},
|
||||||
@@ -181,24 +170,6 @@ function ManagementList() {
|
|||||||
valueEnum: companyNatureEnum,
|
valueEnum: companyNatureEnum,
|
||||||
render: (_, record) => <DictTag enums={companyNatureEnum} value={record.companyNature} />,
|
render: (_, record) => <DictTag enums={companyNatureEnum} value={record.companyNature} />,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: '法定代表人',
|
|
||||||
dataIndex: 'legalPerson',
|
|
||||||
valueType: 'text',
|
|
||||||
align: 'center',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '联系人',
|
|
||||||
dataIndex: 'contactPerson',
|
|
||||||
valueType: 'text',
|
|
||||||
align: 'center',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '联系电话',
|
|
||||||
dataIndex: 'contactPersonPhone',
|
|
||||||
valueType: 'text',
|
|
||||||
align: 'center',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: '公司位置',
|
title: '公司位置',
|
||||||
dataIndex: 'location',
|
dataIndex: 'location',
|
||||||
@@ -358,9 +329,6 @@ function ManagementList() {
|
|||||||
};
|
};
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
search={{
|
|
||||||
defaultCollapsed: false,
|
|
||||||
}}
|
|
||||||
toolBarRender={() => [
|
toolBarRender={() => [
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import React, { useState, useEffect, useMemo } from 'react';
|
||||||
import { useParams, history, useLocation, useSearchParams } from '@umijs/max';
|
import { useParams, history, useLocation, useSearchParams } from '@umijs/max';
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
@@ -92,7 +92,6 @@ const transformJobData = (jobData: any) => {
|
|||||||
experience: jobData.experience ? experienceMap[jobData.experience] || jobData.experience : '不限',
|
experience: jobData.experience ? experienceMap[jobData.experience] || jobData.experience : '不限',
|
||||||
education: jobData.education ? educationMap[jobData.education] || jobData.education : '不限',
|
education: jobData.education ? educationMap[jobData.education] || jobData.education : '不限',
|
||||||
tags: Array.isArray(jobData.tags) ? jobData.tags : [],
|
tags: Array.isArray(jobData.tags) ? jobData.tags : [],
|
||||||
jobFeature: jobData.jobFeature,
|
|
||||||
salaryComposition: parseCommaSeparatedTags(jobData.salaryComposition),
|
salaryComposition: parseCommaSeparatedTags(jobData.salaryComposition),
|
||||||
welfareBenefits: parseCommaSeparatedTags(jobData.welfareBenefits),
|
welfareBenefits: parseCommaSeparatedTags(jobData.welfareBenefits),
|
||||||
workSchedule: parseCommaSeparatedTags(jobData.workSchedule),
|
workSchedule: parseCommaSeparatedTags(jobData.workSchedule),
|
||||||
@@ -144,7 +143,6 @@ const mockJobDetail = {
|
|||||||
experience: '3-5年',
|
experience: '3-5年',
|
||||||
education: '本科',
|
education: '本科',
|
||||||
companyNature: '',
|
companyNature: '',
|
||||||
jobFeature: '',
|
|
||||||
tags: [],
|
tags: [],
|
||||||
salaryComposition: [] as string[],
|
salaryComposition: [] as string[],
|
||||||
welfareBenefits: [] as string[],
|
welfareBenefits: [] as string[],
|
||||||
@@ -205,59 +203,9 @@ const JobDetailPage: React.FC = () => {
|
|||||||
]);
|
]);
|
||||||
const [scaleEnum, setScaleEnum] = useState<DictValueEnumObj>({});
|
const [scaleEnum, setScaleEnum] = useState<DictValueEnumObj>({});
|
||||||
const [companyNatureEnum, setCompanyNatureEnum] = useState<DictValueEnumObj>({});
|
const [companyNatureEnum, setCompanyNatureEnum] = useState<DictValueEnumObj>({});
|
||||||
const [jobFeatureEnum, setJobFeatureEnum] = useState<DictValueEnumObj>({});
|
|
||||||
const [industryTree, setIndustryTree] = useState<any[]>([]);
|
const [industryTree, setIndustryTree] = useState<any[]>([]);
|
||||||
const [complaintVisible, setComplaintVisible] = useState(false);
|
const [complaintVisible, setComplaintVisible] = useState(false);
|
||||||
|
|
||||||
// 行为上报:浏览岗位时长统计
|
|
||||||
const enterTimeRef = useRef<number>(0);
|
|
||||||
const reportedRef = useRef<boolean>(false);
|
|
||||||
const jobIdRef = useRef<string>('');
|
|
||||||
|
|
||||||
// 页面进入/离开时上报浏览行为
|
|
||||||
useEffect(() => {
|
|
||||||
enterTimeRef.current = Date.now();
|
|
||||||
reportedRef.current = false;
|
|
||||||
jobIdRef.current = String(originalJobData?.jobId || originalJobData?.id || id || '');
|
|
||||||
|
|
||||||
const calcDuration = () =>
|
|
||||||
Math.max(1, Math.floor((Date.now() - enterTimeRef.current) / 1000));
|
|
||||||
|
|
||||||
const doReport = (durationSeconds: number) => {
|
|
||||||
if (reportedRef.current) return;
|
|
||||||
reportedRef.current = true;
|
|
||||||
|
|
||||||
const actorId = localStorage.getItem('appUserId');
|
|
||||||
const targetId = jobIdRef.current;
|
|
||||||
|
|
||||||
if (!actorId || !targetId || durationSeconds <= 0) return;
|
|
||||||
|
|
||||||
fetch('/api/cms/behavior/report', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({
|
|
||||||
actorType: '1',
|
|
||||||
actorId: String(actorId),
|
|
||||||
targetType: '2',
|
|
||||||
targetId: String(targetId),
|
|
||||||
viewDuration: String(durationSeconds),
|
|
||||||
}),
|
|
||||||
keepalive: true,
|
|
||||||
}).catch(() => {});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleBeforeUnload = () => {
|
|
||||||
doReport(calcDuration());
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener('beforeunload', handleBeforeUnload);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener('beforeunload', handleBeforeUnload);
|
|
||||||
doReport(calcDuration());
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const detailTagGroups = useMemo(
|
const detailTagGroups = useMemo(
|
||||||
() => [
|
() => [
|
||||||
{ label: '薪资范围与构成', values: jobDetail.salaryComposition },
|
{ label: '薪资范围与构成', values: jobDetail.salaryComposition },
|
||||||
@@ -271,12 +219,10 @@ const JobDetailPage: React.FC = () => {
|
|||||||
Promise.all([
|
Promise.all([
|
||||||
getDictValueEnum('scale', true, true),
|
getDictValueEnum('scale', true, true),
|
||||||
getDictValueEnum('company_nature', false, true),
|
getDictValueEnum('company_nature', false, true),
|
||||||
getDictValueEnum('job_feature', false, true),
|
|
||||||
getCmsIndustryTreeList(),
|
getCmsIndustryTreeList(),
|
||||||
]).then(([scaleData, companyNatureData, jobFeatureData, industryRes]) => {
|
]).then(([scaleData, companyNatureData, industryRes]) => {
|
||||||
setScaleEnum(scaleData);
|
setScaleEnum(scaleData);
|
||||||
setCompanyNatureEnum(companyNatureData);
|
setCompanyNatureEnum(companyNatureData);
|
||||||
setJobFeatureEnum(jobFeatureData);
|
|
||||||
if (industryRes?.code === 200 && industryRes?.data) {
|
if (industryRes?.code === 200 && industryRes?.data) {
|
||||||
setIndustryTree(industryRes.data);
|
setIndustryTree(industryRes.data);
|
||||||
}
|
}
|
||||||
@@ -569,19 +515,11 @@ const JobDetailPage: React.FC = () => {
|
|||||||
<Col span={16}>
|
<Col span={16}>
|
||||||
<div className="job-title-section">
|
<div className="job-title-section">
|
||||||
<Space direction="vertical" size={8}>
|
<Space direction="vertical" size={8}>
|
||||||
<div style={{ display: 'flex', alignItems: 'baseline', gap: '16px', flexWrap: 'wrap' }}>
|
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '16px', flexWrap: 'wrap' }}>
|
||||||
<Title level={2} className="job-title" style={{ marginBottom: 0 }}>
|
<Space align="baseline" size={16}>
|
||||||
{jobDetail.title}
|
<Title level={2} className="job-title" style={{ marginBottom: 0 }}>{jobDetail.title}</Title>
|
||||||
{jobDetail.jobFeature && (
|
<Text className="job-salary-display">{jobDetail.salary}</Text>
|
||||||
<Tag
|
</Space>
|
||||||
color="orange"
|
|
||||||
style={{ marginLeft: 10, fontSize: 13, lineHeight: '24px', verticalAlign: 'middle' }}
|
|
||||||
>
|
|
||||||
{getDictLabel(jobFeatureEnum, jobDetail.jobFeature)}
|
|
||||||
</Tag>
|
|
||||||
)}
|
|
||||||
</Title>
|
|
||||||
<Text className="job-salary-display">{jobDetail.salary}</Text>
|
|
||||||
</div>
|
</div>
|
||||||
<Space size={16}>
|
<Space size={16}>
|
||||||
<Text className="job-meta">
|
<Text className="job-meta">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import React, { useState, useEffect, useMemo } from 'react';
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
Row,
|
Row,
|
||||||
@@ -200,7 +200,6 @@ const JobListPage: React.FC = () => {
|
|||||||
const [educationEnum, setEducationEnum] = useState<DictValueEnumObj>({});
|
const [educationEnum, setEducationEnum] = useState<DictValueEnumObj>({});
|
||||||
const [scaleEnum, setScaleEnum] = useState<DictValueEnumObj>({});
|
const [scaleEnum, setScaleEnum] = useState<DictValueEnumObj>({});
|
||||||
const [companyNatureEnum, setCompanyNatureEnum] = useState<DictValueEnumObj>({});
|
const [companyNatureEnum, setCompanyNatureEnum] = useState<DictValueEnumObj>({});
|
||||||
const [jobFeatureEnum, setJobFeatureEnum] = useState<DictValueEnumObj>({});
|
|
||||||
const [jobTitleTree, setJobTitleTree] = useState<any[]>([]);
|
const [jobTitleTree, setJobTitleTree] = useState<any[]>([]);
|
||||||
const [industryTree, setIndustryTree] = useState<any[]>([]);
|
const [industryTree, setIndustryTree] = useState<any[]>([]);
|
||||||
const [searchValue, setSearchValue] = useState<string>(''); // 搜索框的值
|
const [searchValue, setSearchValue] = useState<string>(''); // 搜索框的值
|
||||||
@@ -217,70 +216,6 @@ const JobListPage: React.FC = () => {
|
|||||||
const [complaintVisible, setComplaintVisible] = useState(false);
|
const [complaintVisible, setComplaintVisible] = useState(false);
|
||||||
const selectedJobId = selectedJob?.jobId || selectedJob?.id;
|
const selectedJobId = selectedJob?.jobId || selectedJob?.id;
|
||||||
|
|
||||||
// 行为上报:浏览岗位时长统计
|
|
||||||
const jobStartTimeRef = useRef<number>(0);
|
|
||||||
const behaviorReportedRef = useRef<boolean>(false);
|
|
||||||
const currentJobIdRef = useRef<string>('');
|
|
||||||
|
|
||||||
const reportJobBehavior = useCallback((jobId: string) => {
|
|
||||||
if (behaviorReportedRef.current) return;
|
|
||||||
behaviorReportedRef.current = true;
|
|
||||||
|
|
||||||
const actorId = localStorage.getItem('appUserId');
|
|
||||||
if (!actorId || !jobId) return;
|
|
||||||
|
|
||||||
const duration = Math.max(1, Math.floor((Date.now() - jobStartTimeRef.current) / 1000));
|
|
||||||
if (duration <= 0) return;
|
|
||||||
|
|
||||||
fetch('/api/cms/behavior/report', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({
|
|
||||||
actorType: '1',
|
|
||||||
actorId: String(actorId),
|
|
||||||
targetType: '2',
|
|
||||||
targetId: String(jobId),
|
|
||||||
viewDuration: String(duration),
|
|
||||||
}),
|
|
||||||
keepalive: true,
|
|
||||||
}).catch(() => {});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// 切换岗位时上报上一个岗位的浏览时长
|
|
||||||
useEffect(() => {
|
|
||||||
if (!isJobPortalLoggedIn()) return;
|
|
||||||
|
|
||||||
const newJobId = String(selectedJobId || '');
|
|
||||||
|
|
||||||
// 如果之前有浏览中的岗位,先上报
|
|
||||||
if (currentJobIdRef.current && currentJobIdRef.current !== newJobId) {
|
|
||||||
reportJobBehavior(currentJobIdRef.current);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 重置计时器,开始计时新岗位
|
|
||||||
behaviorReportedRef.current = false;
|
|
||||||
jobStartTimeRef.current = Date.now();
|
|
||||||
currentJobIdRef.current = newJobId;
|
|
||||||
}, [selectedJobId, reportJobBehavior]);
|
|
||||||
|
|
||||||
// 页面离开时上报当前岗位的浏览时长
|
|
||||||
useEffect(() => {
|
|
||||||
const handleBeforeUnload = () => {
|
|
||||||
if (currentJobIdRef.current) {
|
|
||||||
reportJobBehavior(currentJobIdRef.current);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener('beforeunload', handleBeforeUnload);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener('beforeunload', handleBeforeUnload);
|
|
||||||
if (currentJobIdRef.current) {
|
|
||||||
reportJobBehavior(currentJobIdRef.current);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}, [reportJobBehavior]);
|
|
||||||
|
|
||||||
const selectedJobDetailTagGroups = useMemo(
|
const selectedJobDetailTagGroups = useMemo(
|
||||||
() =>
|
() =>
|
||||||
[
|
[
|
||||||
@@ -317,14 +252,12 @@ const JobListPage: React.FC = () => {
|
|||||||
getDictValueEnum('education', true, true),
|
getDictValueEnum('education', true, true),
|
||||||
getDictValueEnum('scale', true, true),
|
getDictValueEnum('scale', true, true),
|
||||||
getDictValueEnum('company_nature', false, true),
|
getDictValueEnum('company_nature', false, true),
|
||||||
getDictValueEnum('job_feature', false, true),
|
|
||||||
getCmsIndustryTreeList(),
|
getCmsIndustryTreeList(),
|
||||||
getJobTitleTreeSelect(),
|
getJobTitleTreeSelect(),
|
||||||
]).then(([educationData, scaleData, companyNatureData, jobFeatureData, industryRes, jobTitleRes]) => {
|
]).then(([educationData, scaleData, companyNatureData, industryRes, jobTitleRes]) => {
|
||||||
setEducationEnum(educationData);
|
setEducationEnum(educationData);
|
||||||
setScaleEnum(scaleData);
|
setScaleEnum(scaleData);
|
||||||
setCompanyNatureEnum(companyNatureData);
|
setCompanyNatureEnum(companyNatureData);
|
||||||
setJobFeatureEnum(jobFeatureData);
|
|
||||||
if (industryRes?.code === 200 && industryRes?.data) {
|
if (industryRes?.code === 200 && industryRes?.data) {
|
||||||
setIndustryTree(industryRes.data);
|
setIndustryTree(industryRes.data);
|
||||||
}
|
}
|
||||||
@@ -891,20 +824,12 @@ const JobListPage: React.FC = () => {
|
|||||||
{job.isUrgent === 1 && (
|
{job.isUrgent === 1 && (
|
||||||
<Tag
|
<Tag
|
||||||
color="red"
|
color="red"
|
||||||
style={{ marginRight: 6, fontSize: 12, lineHeight: '20px' }}
|
style={{ marginRight: 8, fontSize: 12, lineHeight: '20px' }}
|
||||||
>
|
>
|
||||||
急聘
|
急聘
|
||||||
</Tag>
|
</Tag>
|
||||||
)}
|
)}
|
||||||
{job.jobTitle || job.title}
|
{job.jobTitle || job.title}
|
||||||
{job.jobFeature && (
|
|
||||||
<Tag
|
|
||||||
color="orange"
|
|
||||||
style={{ marginLeft: 6, fontSize: 12, lineHeight: '20px' }}
|
|
||||||
>
|
|
||||||
{getDictLabel(jobFeatureEnum, job.jobFeature)}
|
|
||||||
</Tag>
|
|
||||||
)}
|
|
||||||
</Title>
|
</Title>
|
||||||
<Text className="job-salary">
|
<Text className="job-salary">
|
||||||
{job.minSalary && job.maxSalary
|
{job.minSalary && job.maxSalary
|
||||||
@@ -983,20 +908,12 @@ const JobListPage: React.FC = () => {
|
|||||||
{selectedJob?.isUrgent === 1 && (
|
{selectedJob?.isUrgent === 1 && (
|
||||||
<Tag
|
<Tag
|
||||||
color="red"
|
color="red"
|
||||||
style={{ marginRight: 6, fontSize: 12, lineHeight: '20px' }}
|
style={{ marginRight: 8, fontSize: 12, lineHeight: '20px' }}
|
||||||
>
|
>
|
||||||
急聘
|
急聘
|
||||||
</Tag>
|
</Tag>
|
||||||
)}
|
)}
|
||||||
{selectedJob?.jobTitle || selectedJob?.title || '请选择职位'}
|
{selectedJob?.jobTitle || selectedJob?.title || '请选择职位'}
|
||||||
{selectedJob?.jobFeature && (
|
|
||||||
<Tag
|
|
||||||
color="orange"
|
|
||||||
style={{ marginLeft: 8, fontSize: 12, lineHeight: '22px' }}
|
|
||||||
>
|
|
||||||
{getDictLabel(jobFeatureEnum, selectedJob.jobFeature)}
|
|
||||||
</Tag>
|
|
||||||
)}
|
|
||||||
</Title>
|
</Title>
|
||||||
<Text className="salary-text">
|
<Text className="salary-text">
|
||||||
{selectedJob?.minSalary && selectedJob?.maxSalary
|
{selectedJob?.minSalary && selectedJob?.maxSalary
|
||||||
|
|||||||
@@ -8,11 +8,31 @@
|
|||||||
.jp-page-container();
|
.jp-page-container();
|
||||||
padding: 20px 20px 40px;
|
padding: 20px 20px 40px;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// iframe 区域确保撑满
|
.section {
|
||||||
#resume-generator {
|
.jp-card-base();
|
||||||
display: block;
|
margin-bottom: 16px;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
.ant-card-head {
|
||||||
|
border-bottom: 1px solid @jp-border;
|
||||||
|
|
||||||
|
.ant-card-head-title {
|
||||||
|
color: @jp-primary;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-profile .name {
|
||||||
|
color: @jp-text-primary;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-btn-primary {
|
||||||
|
background: @jp-primary;
|
||||||
|
border-color: @jp-primary;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1366px) {
|
@media (max-width: 1366px) {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -18,6 +18,7 @@ import {
|
|||||||
addOutdoorFairDictOption,
|
addOutdoorFairDictOption,
|
||||||
signupOutdoorFair,
|
signupOutdoorFair,
|
||||||
getMyOutdoorFairQrCode,
|
getMyOutdoorFairQrCode,
|
||||||
|
getOutdoorFairMiniProgramQrCode,
|
||||||
} from '@/services/jobportal/outdoorFair';
|
} from '@/services/jobportal/outdoorFair';
|
||||||
import type {
|
import type {
|
||||||
OutdoorFairItem,
|
OutdoorFairItem,
|
||||||
@@ -25,6 +26,7 @@ import type {
|
|||||||
OutdoorFairForm,
|
OutdoorFairForm,
|
||||||
OutdoorFairDictForm,
|
OutdoorFairDictForm,
|
||||||
OutdoorFairCompanyQrCodeInfo,
|
OutdoorFairCompanyQrCodeInfo,
|
||||||
|
OutdoorFairMiniProgramQrCodeInfo,
|
||||||
} from '@/services/jobportal/outdoorFair';
|
} from '@/services/jobportal/outdoorFair';
|
||||||
import { getParticipatingJobs } from '@/services/jobportal/outdoorFairDetail';
|
import { getParticipatingJobs } from '@/services/jobportal/outdoorFairDetail';
|
||||||
import { getDictSelectOption, getDictValueEnum } from '@/services/system/dict';
|
import { getDictSelectOption, getDictValueEnum } from '@/services/system/dict';
|
||||||
@@ -51,6 +53,10 @@ const OutdoorFairList: React.FC = () => {
|
|||||||
const [jobEditModalOpen, setJobEditModalOpen] = useState(false);
|
const [jobEditModalOpen, setJobEditModalOpen] = useState(false);
|
||||||
const [qrModalOpen, setQrModalOpen] = useState(false);
|
const [qrModalOpen, setQrModalOpen] = useState(false);
|
||||||
const [qrInfo, setQrInfo] = useState<OutdoorFairCompanyQrCodeInfo | null>(null);
|
const [qrInfo, setQrInfo] = useState<OutdoorFairCompanyQrCodeInfo | null>(null);
|
||||||
|
const [miniProgramQrModalOpen, setMiniProgramQrModalOpen] = useState(false);
|
||||||
|
const [miniProgramQrInfo, setMiniProgramQrInfo] =
|
||||||
|
useState<OutdoorFairMiniProgramQrCodeInfo | null>(null);
|
||||||
|
const [miniProgramQrLoadingFairId, setMiniProgramQrLoadingFairId] = useState<number>();
|
||||||
const [rejectReasonModalOpen, setRejectReasonModalOpen] = useState(false);
|
const [rejectReasonModalOpen, setRejectReasonModalOpen] = useState(false);
|
||||||
const [rejectReason, setRejectReason] = useState('');
|
const [rejectReason, setRejectReason] = useState('');
|
||||||
const [companyDetailFair, setCompanyDetailFair] = useState<OutdoorFairItem | null>(null);
|
const [companyDetailFair, setCompanyDetailFair] = useState<OutdoorFairItem | null>(null);
|
||||||
@@ -191,6 +197,23 @@ const OutdoorFairList: React.FC = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const openMiniProgramQrCode = async (record: OutdoorFairItem) => {
|
||||||
|
setMiniProgramQrLoadingFairId(record.id);
|
||||||
|
try {
|
||||||
|
const res = await getOutdoorFairMiniProgramQrCode(record.id);
|
||||||
|
if (res.code === 200 && res.data?.imageData) {
|
||||||
|
setMiniProgramQrInfo(res.data);
|
||||||
|
setMiniProgramQrModalOpen(true);
|
||||||
|
} else {
|
||||||
|
message.error(res.msg || '小程序二维码加载失败');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
message.error('小程序二维码加载失败,请稍后重试');
|
||||||
|
} finally {
|
||||||
|
setMiniProgramQrLoadingFairId(undefined);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const openJobList = (record: OutdoorFairItem) => {
|
const openJobList = (record: OutdoorFairItem) => {
|
||||||
setSelectedFair(record);
|
setSelectedFair(record);
|
||||||
setJobListModalOpen(true);
|
setJobListModalOpen(true);
|
||||||
@@ -374,7 +397,7 @@ const OutdoorFairList: React.FC = () => {
|
|||||||
{
|
{
|
||||||
title: '操作',
|
title: '操作',
|
||||||
valueType: 'option',
|
valueType: 'option',
|
||||||
width: isEnterprise ? 520 : 160,
|
width: isEnterprise ? 520 : 260,
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
render: (_, record) => [
|
render: (_, record) => [
|
||||||
isEnterprise && !record.signedUp ? (
|
isEnterprise && !record.signedUp ? (
|
||||||
@@ -410,12 +433,22 @@ const OutdoorFairList: React.FC = () => {
|
|||||||
</Button>
|
</Button>
|
||||||
) : null,
|
) : null,
|
||||||
isEnterprise ? (
|
isEnterprise ? (
|
||||||
<Button key="companyDetail" type="link" size="small" onClick={() => openCompanyDetail(record)}>
|
<Button
|
||||||
|
key="companyDetail"
|
||||||
|
type="link"
|
||||||
|
size="small"
|
||||||
|
onClick={() => openCompanyDetail(record)}
|
||||||
|
>
|
||||||
招聘会详情
|
招聘会详情
|
||||||
</Button>
|
</Button>
|
||||||
) : null,
|
) : null,
|
||||||
isEnterprise && record.signedUp && record.reviewStatus === '1' ? (
|
isEnterprise && record.signedUp && record.reviewStatus === '1' ? (
|
||||||
<Button key="boothMap" type="link" size="small" onClick={() => openCompanyBoothMap(record)}>
|
<Button
|
||||||
|
key="boothMap"
|
||||||
|
type="link"
|
||||||
|
size="small"
|
||||||
|
onClick={() => openCompanyBoothMap(record)}
|
||||||
|
>
|
||||||
查看展位图
|
查看展位图
|
||||||
</Button>
|
</Button>
|
||||||
) : null,
|
) : null,
|
||||||
@@ -429,6 +462,17 @@ const OutdoorFairList: React.FC = () => {
|
|||||||
>
|
>
|
||||||
详情
|
详情
|
||||||
</Button>,
|
</Button>,
|
||||||
|
<Button
|
||||||
|
key="miniProgramQrCode"
|
||||||
|
type="link"
|
||||||
|
size="small"
|
||||||
|
icon={<QrcodeOutlined />}
|
||||||
|
loading={miniProgramQrLoadingFairId === record.id}
|
||||||
|
hidden={isEnterprise || !access.hasPerms('cms:outdoorFair:qrcode')}
|
||||||
|
onClick={() => openMiniProgramQrCode(record)}
|
||||||
|
>
|
||||||
|
小程序二维码
|
||||||
|
</Button>,
|
||||||
<Button
|
<Button
|
||||||
key="edit"
|
key="edit"
|
||||||
type="link"
|
type="link"
|
||||||
@@ -552,6 +596,46 @@ const OutdoorFairList: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Modal>
|
</Modal>
|
||||||
|
<Modal
|
||||||
|
title="招聘会小程序二维码"
|
||||||
|
open={miniProgramQrModalOpen}
|
||||||
|
width={420}
|
||||||
|
footer={[
|
||||||
|
<Button
|
||||||
|
key="close"
|
||||||
|
type="primary"
|
||||||
|
onClick={() => {
|
||||||
|
setMiniProgramQrModalOpen(false);
|
||||||
|
setMiniProgramQrInfo(null);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
关闭
|
||||||
|
</Button>,
|
||||||
|
]}
|
||||||
|
destroyOnHidden
|
||||||
|
onCancel={() => {
|
||||||
|
setMiniProgramQrModalOpen(false);
|
||||||
|
setMiniProgramQrInfo(null);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{miniProgramQrInfo?.imageData && (
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
|
||||||
|
<img
|
||||||
|
src={miniProgramQrInfo.imageData}
|
||||||
|
alt="招聘会小程序二维码"
|
||||||
|
style={{ width: 260, height: 260, objectFit: 'contain' }}
|
||||||
|
/>
|
||||||
|
<Typography.Text strong style={{ marginTop: 16, fontSize: 16 }}>
|
||||||
|
请使用微信扫码
|
||||||
|
</Typography.Text>
|
||||||
|
{miniProgramQrInfo.permanent && (
|
||||||
|
<Typography.Text type="secondary" style={{ marginTop: 8 }}>
|
||||||
|
此小程序码长期有效,无需定期更换
|
||||||
|
</Typography.Text>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Modal>
|
||||||
<Modal
|
<Modal
|
||||||
title="驳回原因"
|
title="驳回原因"
|
||||||
open={rejectReasonModalOpen}
|
open={rejectReasonModalOpen}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import React, { Fragment, useEffect, useRef, useState } from 'react';
|
import React, { Fragment, useEffect, useRef, useState } from 'react';
|
||||||
import { FormattedMessage, useAccess, useNavigate, useParams } from '@umijs/max';
|
import { FormattedMessage, useAccess, useParams } from '@umijs/max';
|
||||||
import { Button, FormInstance, message, Tag } from 'antd';
|
import { Button, FormInstance, message, Tag } from 'antd';
|
||||||
import { ActionType, ProColumns, ProTable } from '@ant-design/pro-components';
|
import { ActionType, ProColumns, ProTable } from '@ant-design/pro-components';
|
||||||
import { FormOutlined, PlusOutlined, AlignLeftOutlined, SendOutlined, FilterOutlined, ArrowLeftOutlined } from '@ant-design/icons';
|
import { FormOutlined, PlusOutlined, AlignLeftOutlined, SendOutlined, FilterOutlined } from '@ant-design/icons';
|
||||||
import { getDictValueEnum } from '@/services/system/dict';
|
import { getDictValueEnum } from '@/services/system/dict';
|
||||||
import DictTag from '@/components/DictTag';
|
import DictTag from '@/components/DictTag';
|
||||||
import { exportCmsAppUserExport } from '@/services/mobileusers/list';
|
import { exportCmsAppUserExport } from '@/services/mobileusers/list';
|
||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
import similarityJobs from '@/utils/similarity_Job';
|
import similarityJobs from '@/utils/similarity_Job';
|
||||||
import Detail from './detail';
|
import Detail from './detail';
|
||||||
import Hire from './hire';
|
import Hire from './hire';
|
||||||
|
import ResumeView from './resumeView';
|
||||||
import InterviewInvite from '../ResumeRecommend/InterviewInvite';
|
import InterviewInvite from '../ResumeRecommend/InterviewInvite';
|
||||||
import ResumeFilter from './resumeFilter';
|
import ResumeFilter from './resumeFilter';
|
||||||
|
|
||||||
@@ -34,7 +35,6 @@ const handleExport = async (values: API.MobileUser.ListParams) => {
|
|||||||
|
|
||||||
function ManagementList() {
|
function ManagementList() {
|
||||||
const access = useAccess();
|
const access = useAccess();
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
const formTableRef = useRef<FormInstance>();
|
const formTableRef = useRef<FormInstance>();
|
||||||
const actionRef = useRef<ActionType>();
|
const actionRef = useRef<ActionType>();
|
||||||
@@ -52,6 +52,7 @@ function ManagementList() {
|
|||||||
const [hireVisible, setHireVisible] = useState<boolean>(false);
|
const [hireVisible, setHireVisible] = useState<boolean>(false);
|
||||||
const [jobInfo, setJobInfo] = useState({});
|
const [jobInfo, setJobInfo] = useState({});
|
||||||
const [matchingDegree, setMatchingDegree] = useState<any>(null);
|
const [matchingDegree, setMatchingDegree] = useState<any>(null);
|
||||||
|
const [resumeVisible, setResumeVisible] = useState<boolean>(false);
|
||||||
const [inviteVisible, setInviteVisible] = useState<boolean>(false);
|
const [inviteVisible, setInviteVisible] = useState<boolean>(false);
|
||||||
const [filterVisible, setFilterVisible] = useState<boolean>(false);
|
const [filterVisible, setFilterVisible] = useState<boolean>(false);
|
||||||
const [hireAdd, setHireAdd] = useState<any>(null);
|
const [hireAdd, setHireAdd] = useState<any>(null);
|
||||||
@@ -204,10 +205,11 @@ function ManagementList() {
|
|||||||
icon={<AlignLeftOutlined/>}
|
icon={<AlignLeftOutlined/>}
|
||||||
hidden={!access.hasPerms('cms:userworkexperiences:list')}
|
hidden={!access.hasPerms('cms:userworkexperiences:list')}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
navigate(`/usermgmt/appuser/resume/${record.userId}?readOnly=1`);
|
setCurrentRow(record);
|
||||||
|
setResumeVisible(true);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
简历
|
查看简历
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type="link"
|
type="link"
|
||||||
@@ -281,7 +283,6 @@ function ManagementList() {
|
|||||||
rowKey="jobId"
|
rowKey="jobId"
|
||||||
key="index"
|
key="index"
|
||||||
columns={columns}
|
columns={columns}
|
||||||
search={{ defaultCollapsed: false }}
|
|
||||||
request={(params) =>
|
request={(params) =>
|
||||||
exportCmsJobCandidates(jobId).then((res) => {
|
exportCmsJobCandidates(jobId).then((res) => {
|
||||||
// const v = similarityJobs.calculationMatchingDegreeJob(res.rows[0]);
|
// const v = similarityJobs.calculationMatchingDegreeJob(res.rows[0]);
|
||||||
@@ -295,13 +296,6 @@ function ManagementList() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
toolBarRender={() => [
|
toolBarRender={() => [
|
||||||
<Button
|
|
||||||
key="back-to-list"
|
|
||||||
icon={<ArrowLeftOutlined />}
|
|
||||||
onClick={() => navigate(-1)}
|
|
||||||
>
|
|
||||||
返回岗位列表
|
|
||||||
</Button>,
|
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
key="export"
|
key="export"
|
||||||
@@ -351,6 +345,15 @@ function ManagementList() {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
></Hire>
|
></Hire>
|
||||||
|
<ResumeView
|
||||||
|
values={currentRow}
|
||||||
|
open={resumeVisible}
|
||||||
|
matching={{ jobId, companyId: (jobInfo as any)?.companyId, applyId: currentRow?.applyId, jobName: (jobInfo as any)?.jobTitle }}
|
||||||
|
onClose={() => {
|
||||||
|
setResumeVisible(false);
|
||||||
|
setCurrentRow(undefined);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<InterviewInvite
|
<InterviewInvite
|
||||||
open={inviteVisible}
|
open={inviteVisible}
|
||||||
userId={currentRow?.userId}
|
userId={currentRow?.userId}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import { useModel } from '@umijs/max';
|
|||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import { DictValueEnumObj } from '@/components/DictTag';
|
import { DictValueEnumObj } from '@/components/DictTag';
|
||||||
import { getCmsCompanyList } from '@/services/company/list';
|
import { getCmsCompanyList } from '@/services/company/list';
|
||||||
import { getCmsJobDetail, delCmsJobContact } from '@/services/Management/list';
|
import { getCmsJobDetail } from '@/services/Management/list';
|
||||||
import { getJobTitleTreeSelect } from '@/services/common/jobTitle';
|
import { getJobTitleTreeSelect } from '@/services/common/jobTitle';
|
||||||
import ProFromMap from '@/components/ProFromMap';
|
import ProFromMap from '@/components/ProFromMap';
|
||||||
import { parseCommaSeparatedTags, serializeCommaSeparatedTags } from '@/utils/jobDetailTags';
|
import { parseCommaSeparatedTags, serializeCommaSeparatedTags } from '@/utils/jobDetailTags';
|
||||||
@@ -32,7 +32,7 @@ const toManageFormValues = (
|
|||||||
values: Partial<API.ManagementList.Manage>,
|
values: Partial<API.ManagementList.Manage>,
|
||||||
): Partial<ManageFormValues> => ({
|
): Partial<ManageFormValues> => ({
|
||||||
...values,
|
...values,
|
||||||
jobLocationAreaCode: values.jobLocationAreaCode != null ? String(values.jobLocationAreaCode) : undefined,
|
jobLocationAreaCode: String(values.jobLocationAreaCode || ''),
|
||||||
salaryComposition: parseCommaSeparatedTags(values.salaryComposition),
|
salaryComposition: parseCommaSeparatedTags(values.salaryComposition),
|
||||||
welfareBenefits: parseCommaSeparatedTags(values.welfareBenefits),
|
welfareBenefits: parseCommaSeparatedTags(values.welfareBenefits),
|
||||||
workSchedule: parseCommaSeparatedTags(values.workSchedule),
|
workSchedule: parseCommaSeparatedTags(values.workSchedule),
|
||||||
@@ -65,8 +65,6 @@ export type ListFormProps = {
|
|||||||
salaryCompositionEnum: DictValueEnumObj;
|
salaryCompositionEnum: DictValueEnumObj;
|
||||||
welfareBenefitsEnum: DictValueEnumObj;
|
welfareBenefitsEnum: DictValueEnumObj;
|
||||||
workScheduleEnum: DictValueEnumObj;
|
workScheduleEnum: DictValueEnumObj;
|
||||||
keyPopulationsEnum: DictValueEnumObj;
|
|
||||||
jobFeatureEnum: DictValueEnumObj;
|
|
||||||
mode?: 'view' | 'edit' | 'create';
|
mode?: 'view' | 'edit' | 'create';
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -83,8 +81,6 @@ const listEdit: React.FC<ListFormProps> = (props) => {
|
|||||||
const isEnterprise = initialState?.currentUser?.userType === 'view';
|
const isEnterprise = initialState?.currentUser?.userType === 'view';
|
||||||
const [form] = Form.useForm<ManageFormValues>();
|
const [form] = Form.useForm<ManageFormValues>();
|
||||||
const companyNameMap = useRef<Record<number, string>>({});
|
const companyNameMap = useRef<Record<number, string>>({});
|
||||||
const [companyOptions, setCompanyOptions] = useState<{ label: string; value: number }[]>([]);
|
|
||||||
const [areaOptions, setAreaOptions] = useState<{ label: string; value: string }[]>([]);
|
|
||||||
const [jobDetail, setJobDetail] = useState<API.ManagementList.Manage | null>(null);
|
const [jobDetail, setJobDetail] = useState<API.ManagementList.Manage | null>(null);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [jobCategoryTreeData, setJobCategoryTreeData] = useState<any[]>([]);
|
const [jobCategoryTreeData, setJobCategoryTreeData] = useState<any[]>([]);
|
||||||
@@ -98,37 +94,13 @@ const listEdit: React.FC<ListFormProps> = (props) => {
|
|||||||
salaryCompositionEnum,
|
salaryCompositionEnum,
|
||||||
welfareBenefitsEnum,
|
welfareBenefitsEnum,
|
||||||
workScheduleEnum,
|
workScheduleEnum,
|
||||||
keyPopulationsEnum,
|
|
||||||
jobFeatureEnum,
|
|
||||||
} = props;
|
} = props;
|
||||||
const { mode = props.values ? 'edit' : 'create' } = props;
|
const { mode = props.values ? 'edit' : 'create' } = props;
|
||||||
const [showKeyPopulations, setShowKeyPopulations] = useState(false);
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (props.open) {
|
if (props.open) {
|
||||||
form.resetFields();
|
form.resetFields();
|
||||||
if (props.values) {
|
if (props.values) {
|
||||||
form.setFieldsValue(toManageFormValues(props.values));
|
form.setFieldsValue(toManageFormValues(props.values));
|
||||||
// 预填充公司选项(编辑时回显公司名称)
|
|
||||||
if (props.values.companyId && props.values.companyName) {
|
|
||||||
companyNameMap.current[props.values.companyId] = props.values.companyName;
|
|
||||||
setCompanyOptions([
|
|
||||||
{ label: props.values.companyName, value: props.values.companyId },
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
setCompanyOptions([]);
|
|
||||||
}
|
|
||||||
// 预填充区县选项(编辑时回显工作区县名称)
|
|
||||||
if (props.values.jobLocationAreaCode && areaEnum) {
|
|
||||||
const code = String(props.values.jobLocationAreaCode);
|
|
||||||
const areaItem = areaEnum[code];
|
|
||||||
if (areaItem) {
|
|
||||||
setAreaOptions([{ label: areaItem.label ?? areaItem.text, value: code }]);
|
|
||||||
} else {
|
|
||||||
setAreaOptions([]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setAreaOptions([]);
|
|
||||||
}
|
|
||||||
// 初始化地图位置信息(编辑时回显)
|
// 初始化地图位置信息(编辑时回显)
|
||||||
if (props.values.latitude || props.values.longitude) {
|
if (props.values.latitude || props.values.longitude) {
|
||||||
setMapViewInfo({
|
setMapViewInfo({
|
||||||
@@ -139,17 +111,8 @@ const listEdit: React.FC<ListFormProps> = (props) => {
|
|||||||
} else {
|
} else {
|
||||||
setMapViewInfo({});
|
setMapViewInfo({});
|
||||||
}
|
}
|
||||||
// 初始化重点人群显示状态(编辑时回显)
|
|
||||||
if (props.values.isKeyPopulations === '0') {
|
|
||||||
setShowKeyPopulations(true);
|
|
||||||
} else {
|
|
||||||
setShowKeyPopulations(false);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
setMapViewInfo({});
|
setMapViewInfo({});
|
||||||
setShowKeyPopulations(false);
|
|
||||||
setCompanyOptions([]);
|
|
||||||
setAreaOptions([]);
|
|
||||||
}
|
}
|
||||||
// 加载岗位标签树数据
|
// 加载岗位标签树数据
|
||||||
getJobTitleTreeSelect()
|
getJobTitleTreeSelect()
|
||||||
@@ -160,22 +123,6 @@ const listEdit: React.FC<ListFormProps> = (props) => {
|
|||||||
}
|
}
|
||||||
}, [form, props.values?.jobId, props.open]);
|
}, [form, props.values?.jobId, props.open]);
|
||||||
|
|
||||||
// 区县字典加载完成后,回显工作区县名称
|
|
||||||
useEffect(() => {
|
|
||||||
if (
|
|
||||||
props.open &&
|
|
||||||
props.values?.jobLocationAreaCode &&
|
|
||||||
areaEnum &&
|
|
||||||
Object.keys(areaEnum).length > 0
|
|
||||||
) {
|
|
||||||
const code = String(props.values.jobLocationAreaCode);
|
|
||||||
const areaItem = areaEnum[code];
|
|
||||||
if (areaItem) {
|
|
||||||
setAreaOptions([{ label: areaItem.label ?? areaItem.text, value: code }]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [areaEnum, props.open, props.values?.jobLocationAreaCode]);
|
|
||||||
|
|
||||||
// 在查看模式和编辑模式下调用API获取详情
|
// 在查看模式和编辑模式下调用API获取详情
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (props.open && (mode === 'view' || mode === 'edit') && props.values?.jobId) {
|
if (props.open && (mode === 'view' || mode === 'edit') && props.values?.jobId) {
|
||||||
@@ -194,21 +141,6 @@ const listEdit: React.FC<ListFormProps> = (props) => {
|
|||||||
// 如果是编辑模式,将获取到的详情数据设置到表单中
|
// 如果是编辑模式,将获取到的详情数据设置到表单中
|
||||||
if (mode === 'edit') {
|
if (mode === 'edit') {
|
||||||
form.setFieldsValue(toManageFormValues(response.data));
|
form.setFieldsValue(toManageFormValues(response.data));
|
||||||
// 预填充公司选项(从详情API获取的数据回显公司名称)
|
|
||||||
if (response.data.companyId && response.data.companyName) {
|
|
||||||
companyNameMap.current[response.data.companyId] = response.data.companyName;
|
|
||||||
setCompanyOptions([
|
|
||||||
{ label: response.data.companyName, value: response.data.companyId },
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
// 预填充区县选项(从详情API获取的数据回显工作区县名称)
|
|
||||||
if (response.data.jobLocationAreaCode && areaEnum) {
|
|
||||||
const code = String(response.data.jobLocationAreaCode);
|
|
||||||
const areaItem = areaEnum[code];
|
|
||||||
if (areaItem) {
|
|
||||||
setAreaOptions([{ label: areaItem.label ?? areaItem.text, value: code }]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -264,17 +196,13 @@ const listEdit: React.FC<ListFormProps> = (props) => {
|
|||||||
description: values.description,
|
description: values.description,
|
||||||
jobContactList: (values.jobContactList ?? []).map(
|
jobContactList: (values.jobContactList ?? []).map(
|
||||||
(item: API.ManagementList.ContactPerson) => ({
|
(item: API.ManagementList.ContactPerson) => ({
|
||||||
id: item.id,
|
|
||||||
contactPerson: item.contactPerson,
|
contactPerson: item.contactPerson,
|
||||||
contactPersonPhone: item.contactPersonPhone,
|
contactPersonPhone: item.contactPersonPhone,
|
||||||
position: item.position,
|
position: item.position,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
jobCategory: values.jobCategory,
|
jobCategory: values.jobCategory,
|
||||||
jobFeature: values.jobFeature,
|
|
||||||
isUrgent: values.isUrgent ? 1 : 0,
|
isUrgent: values.isUrgent ? 1 : 0,
|
||||||
isKeyPopulations: showKeyPopulations ? '0' : '1',
|
|
||||||
keyPopulations: showKeyPopulations ? values.keyPopulations : undefined,
|
|
||||||
latitude: values.latitude,
|
latitude: values.latitude,
|
||||||
longitude: values.longitude,
|
longitude: values.longitude,
|
||||||
};
|
};
|
||||||
@@ -309,16 +237,6 @@ const listEdit: React.FC<ListFormProps> = (props) => {
|
|||||||
label="是否急聘"
|
label="是否急聘"
|
||||||
valueEnum={{ 0: '否', 1: '是' }}
|
valueEnum={{ 0: '否', 1: '是' }}
|
||||||
/>
|
/>
|
||||||
<ProDescriptions.Item
|
|
||||||
dataIndex="isKeyPopulations"
|
|
||||||
label="是否重点服务人群"
|
|
||||||
valueEnum={{ 0: '是', 1: '否' }}
|
|
||||||
/>
|
|
||||||
<ProDescriptions.Item
|
|
||||||
dataIndex="keyPopulations"
|
|
||||||
label="重点人群"
|
|
||||||
valueEnum={keyPopulationsEnum}
|
|
||||||
/>
|
|
||||||
<ProDescriptions.Item dataIndex="minSalary" label="最低薪资(元/月)" />
|
<ProDescriptions.Item dataIndex="minSalary" label="最低薪资(元/月)" />
|
||||||
<ProDescriptions.Item dataIndex="maxSalary" label="最高薪资(元/月)" />
|
<ProDescriptions.Item dataIndex="maxSalary" label="最高薪资(元/月)" />
|
||||||
<ProDescriptions.Item
|
<ProDescriptions.Item
|
||||||
@@ -358,11 +276,6 @@ const listEdit: React.FC<ListFormProps> = (props) => {
|
|||||||
<ProDescriptions.Item dataIndex="jobLocation" label="工作地点" />
|
<ProDescriptions.Item dataIndex="jobLocation" label="工作地点" />
|
||||||
<ProDescriptions.Item dataIndex="jobType" label="区域" valueEnum={jobTypeEnum} />
|
<ProDescriptions.Item dataIndex="jobType" label="区域" valueEnum={jobTypeEnum} />
|
||||||
<ProDescriptions.Item dataIndex="jobCategory" label="岗位标签" />
|
<ProDescriptions.Item dataIndex="jobCategory" label="岗位标签" />
|
||||||
<ProDescriptions.Item
|
|
||||||
dataIndex="jobFeature"
|
|
||||||
label="岗位特征"
|
|
||||||
valueEnum={jobFeatureEnum}
|
|
||||||
/>
|
|
||||||
<ProDescriptions.Item
|
<ProDescriptions.Item
|
||||||
dataIndex="description"
|
dataIndex="description"
|
||||||
label="岗位描述"
|
label="岗位描述"
|
||||||
@@ -545,33 +458,6 @@ const listEdit: React.FC<ListFormProps> = (props) => {
|
|||||||
initialValue={0}
|
initialValue={0}
|
||||||
fieldProps={{ defaultChecked: false }}
|
fieldProps={{ defaultChecked: false }}
|
||||||
/>
|
/>
|
||||||
<ProFormSwitch
|
|
||||||
width="md"
|
|
||||||
name="isKeyPopulations"
|
|
||||||
label="是否重点服务人群"
|
|
||||||
checkedChildren="是"
|
|
||||||
unCheckedChildren="否"
|
|
||||||
initialValue="1"
|
|
||||||
fieldProps={{
|
|
||||||
defaultChecked: false,
|
|
||||||
onChange: (checked) => {
|
|
||||||
setShowKeyPopulations(checked);
|
|
||||||
if (!checked) {
|
|
||||||
form.setFieldValue('keyPopulations', undefined);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{showKeyPopulations && (
|
|
||||||
<ProFormSelect
|
|
||||||
width="md"
|
|
||||||
name="keyPopulations"
|
|
||||||
label="重点人群"
|
|
||||||
valueEnum={keyPopulationsEnum}
|
|
||||||
placeholder="请选择重点人群"
|
|
||||||
rules={[{ required: true, message: '请选择重点人群!' }]}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{!isEnterprise && (
|
{!isEnterprise && (
|
||||||
<ProFormSelect
|
<ProFormSelect
|
||||||
showSearch
|
showSearch
|
||||||
@@ -588,7 +474,6 @@ const listEdit: React.FC<ListFormProps> = (props) => {
|
|||||||
onChange: (companyId: number) => {
|
onChange: (companyId: number) => {
|
||||||
form.setFieldValue('companyName', companyNameMap.current[companyId] ?? '');
|
form.setFieldValue('companyName', companyNameMap.current[companyId] ?? '');
|
||||||
},
|
},
|
||||||
options: companyOptions.length > 0 ? companyOptions : undefined,
|
|
||||||
}}
|
}}
|
||||||
placeholder="请输入公司名称选择公司"
|
placeholder="请输入公司名称选择公司"
|
||||||
rules={[{ required: true, message: '请输入公司名称选择公司!' }]}
|
rules={[{ required: true, message: '请输入公司名称选择公司!' }]}
|
||||||
@@ -658,9 +543,6 @@ const listEdit: React.FC<ListFormProps> = (props) => {
|
|||||||
label={'工作区县'}
|
label={'工作区县'}
|
||||||
valueEnum={areaEnum}
|
valueEnum={areaEnum}
|
||||||
placeholder="请选择区县"
|
placeholder="请选择区县"
|
||||||
fieldProps={{
|
|
||||||
options: areaOptions.length > 0 ? areaOptions : undefined,
|
|
||||||
}}
|
|
||||||
rules={[{ required: true, message: '请选择区县!' }]}
|
rules={[{ required: true, message: '请选择区县!' }]}
|
||||||
/>
|
/>
|
||||||
<ProFormDigit
|
<ProFormDigit
|
||||||
@@ -694,16 +576,9 @@ const listEdit: React.FC<ListFormProps> = (props) => {
|
|||||||
return title.toLowerCase().includes(inputValue.toLowerCase());
|
return title.toLowerCase().includes(inputValue.toLowerCase());
|
||||||
}}
|
}}
|
||||||
placeholder="请搜索或选择岗位标签"
|
placeholder="请搜索或选择岗位标签"
|
||||||
style={{ width: 328 }}
|
style={{ width: '100%' }}
|
||||||
/>
|
/>
|
||||||
</ProForm.Item>
|
</ProForm.Item>
|
||||||
<ProFormSelect
|
|
||||||
width="md"
|
|
||||||
name="jobFeature"
|
|
||||||
label="岗位特征"
|
|
||||||
valueEnum={jobFeatureEnum}
|
|
||||||
placeholder="请选择岗位特征"
|
|
||||||
/>
|
|
||||||
<ProFormText width="md" name="jobLocation" label="工作地点" placeholder="请输入工作地点" />
|
<ProFormText width="md" name="jobLocation" label="工作地点" placeholder="请输入工作地点" />
|
||||||
<ProForm.Item label="位置选择" colProps={{ span: 12 }}>
|
<ProForm.Item label="位置选择" colProps={{ span: 12 }}>
|
||||||
<div>
|
<div>
|
||||||
@@ -788,13 +663,7 @@ const listEdit: React.FC<ListFormProps> = (props) => {
|
|||||||
type="text"
|
type="text"
|
||||||
danger
|
danger
|
||||||
icon={<MinusCircleOutlined />}
|
icon={<MinusCircleOutlined />}
|
||||||
onClick={async () => {
|
onClick={() => remove(name)}
|
||||||
const contactId = form.getFieldValue(['jobContactList', name, 'id']);
|
|
||||||
if (contactId) {
|
|
||||||
await delCmsJobContact(String(contactId));
|
|
||||||
}
|
|
||||||
remove(name);
|
|
||||||
}}
|
|
||||||
style={{ marginBottom: 0 }}
|
style={{ marginBottom: 0 }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -97,8 +97,6 @@ function ManagementList() {
|
|||||||
const [salaryCompositionEnum, setSalaryCompositionEnum] = useState<any>([]);
|
const [salaryCompositionEnum, setSalaryCompositionEnum] = useState<any>([]);
|
||||||
const [welfareBenefitsEnum, setWelfareBenefitsEnum] = useState<any>([]);
|
const [welfareBenefitsEnum, setWelfareBenefitsEnum] = useState<any>([]);
|
||||||
const [workScheduleEnum, setWorkScheduleEnum] = useState<any>([]);
|
const [workScheduleEnum, setWorkScheduleEnum] = useState<any>([]);
|
||||||
const [keyPopulationsEnum, setKeyPopulationsEnum] = useState<any>([]);
|
|
||||||
const [jobFeatureEnum, setJobFeatureEnum] = useState<any>([]);
|
|
||||||
const [currentRow, setCurrentRow] = useState<API.ManagementList.Manage>();
|
const [currentRow, setCurrentRow] = useState<API.ManagementList.Manage>();
|
||||||
const [modalVisible, setModalVisible] = useState<boolean>(false);
|
const [modalVisible, setModalVisible] = useState<boolean>(false);
|
||||||
const [mode, setMode] = useState<'view' | 'edit' | 'create'>('create');
|
const [mode, setMode] = useState<'view' | 'edit' | 'create'>('create');
|
||||||
@@ -130,12 +128,6 @@ function ManagementList() {
|
|||||||
getDictValueEnum('work_schedule', false, true).then((data) => {
|
getDictValueEnum('work_schedule', false, true).then((data) => {
|
||||||
setWorkScheduleEnum(data);
|
setWorkScheduleEnum(data);
|
||||||
});
|
});
|
||||||
getDictValueEnum('kp_type', false, true).then((data) => {
|
|
||||||
setKeyPopulationsEnum(data);
|
|
||||||
});
|
|
||||||
getDictValueEnum('job_feature', false, true).then((data) => {
|
|
||||||
setJobFeatureEnum(data);
|
|
||||||
});
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
async function changeRelease(record: API.ManagementList.Manage) {
|
async function changeRelease(record: API.ManagementList.Manage) {
|
||||||
@@ -448,9 +440,6 @@ function ManagementList() {
|
|||||||
rowKey="jobId"
|
rowKey="jobId"
|
||||||
key="index"
|
key="index"
|
||||||
columns={columns}
|
columns={columns}
|
||||||
search={{
|
|
||||||
defaultCollapsed: false,
|
|
||||||
}}
|
|
||||||
request={(params) =>
|
request={(params) =>
|
||||||
getCmsJobList({ ...params } as API.ManagementList.ListParams).then((res) => {
|
getCmsJobList({ ...params } as API.ManagementList.ListParams).then((res) => {
|
||||||
console.log(params);
|
console.log(params);
|
||||||
@@ -544,8 +533,6 @@ function ManagementList() {
|
|||||||
salaryCompositionEnum={salaryCompositionEnum}
|
salaryCompositionEnum={salaryCompositionEnum}
|
||||||
welfareBenefitsEnum={welfareBenefitsEnum}
|
welfareBenefitsEnum={welfareBenefitsEnum}
|
||||||
workScheduleEnum={workScheduleEnum}
|
workScheduleEnum={workScheduleEnum}
|
||||||
keyPopulationsEnum={keyPopulationsEnum}
|
|
||||||
jobFeatureEnum={jobFeatureEnum}
|
|
||||||
></EditManageRow>
|
></EditManageRow>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import { useModel, useParams, useNavigate, useSearchParams } from '@umijs/max';
|
import { useParams, useNavigate, useSearchParams } from '@umijs/max';
|
||||||
import { Button, Card, message, Space, Switch, Typography } from 'antd';
|
import { Button, Card, message, Space, Switch, Typography } from 'antd';
|
||||||
import { ArrowLeftOutlined, EyeOutlined } from '@ant-design/icons';
|
import { ArrowLeftOutlined, EyeOutlined } from '@ant-design/icons';
|
||||||
import Iframe from 'iframe-js';
|
import Iframe from 'iframe-js';
|
||||||
@@ -253,60 +253,6 @@ function AppUserResume() {
|
|||||||
const iframeRef = useRef<HTMLIFrameElement>(null);
|
const iframeRef = useRef<HTMLIFrameElement>(null);
|
||||||
const resumeDataRef = useRef<API.CmsAppUser.ResumeData | null>(null);
|
const resumeDataRef = useRef<API.CmsAppUser.ResumeData | null>(null);
|
||||||
|
|
||||||
// 行为上报:记录浏览时长和浏览次数
|
|
||||||
const { initialState } = useModel('@@initialState');
|
|
||||||
const enterTimeRef = useRef<number>(0);
|
|
||||||
const reportedRef = useRef<boolean>(false);
|
|
||||||
const actorIdRef = useRef<string | undefined>('');
|
|
||||||
const targetIdRef = useRef<number>(0);
|
|
||||||
actorIdRef.current = initialState?.currentUser?.userId;
|
|
||||||
targetIdRef.current = userId;
|
|
||||||
|
|
||||||
// 页面进入/离开时上报浏览行为
|
|
||||||
useEffect(() => {
|
|
||||||
enterTimeRef.current = Date.now();
|
|
||||||
reportedRef.current = false;
|
|
||||||
|
|
||||||
const doReport = (durationSeconds: number) => {
|
|
||||||
if (reportedRef.current) return;
|
|
||||||
reportedRef.current = true;
|
|
||||||
|
|
||||||
const actorId = actorIdRef.current;
|
|
||||||
const targetId = targetIdRef.current;
|
|
||||||
|
|
||||||
if (!actorId || !targetId || durationSeconds <= 0) return;
|
|
||||||
|
|
||||||
const payload = {
|
|
||||||
actorType: '2',
|
|
||||||
actorId: String(actorId),
|
|
||||||
targetType: '1',
|
|
||||||
targetId: String(targetId),
|
|
||||||
viewDuration: String(durationSeconds),
|
|
||||||
};
|
|
||||||
|
|
||||||
fetch('/api/cms/behavior/report', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify(payload),
|
|
||||||
keepalive: true,
|
|
||||||
}).catch(() => {});
|
|
||||||
};
|
|
||||||
|
|
||||||
const calcDuration = () =>
|
|
||||||
Math.max(1, Math.floor((Date.now() - enterTimeRef.current) / 1000));
|
|
||||||
|
|
||||||
const handleBeforeUnload = () => {
|
|
||||||
doReport(calcDuration());
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener('beforeunload', handleBeforeUnload);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener('beforeunload', handleBeforeUnload);
|
|
||||||
doReport(calcDuration());
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// 字典数据(code → 汉字)
|
// 字典数据(code → 汉字)
|
||||||
const [sexDict, setSexDict] = useState<DictMap>({});
|
const [sexDict, setSexDict] = useState<DictMap>({});
|
||||||
const [educationDict, setEducationDict] = useState<DictMap>({});
|
const [educationDict, setEducationDict] = useState<DictMap>({});
|
||||||
|
|||||||
@@ -1,81 +1,14 @@
|
|||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import { Form, Input, Modal, Switch, Tag, Button, message } from 'antd';
|
import { Form, Input, Modal, Switch, Tag, message } from 'antd';
|
||||||
import { ActionType, ProColumns, ProTable } from '@ant-design/pro-components';
|
import { ActionType, ProColumns, ProTable } from '@ant-design/pro-components';
|
||||||
import { useAccess } from '@umijs/max';
|
|
||||||
import {
|
|
||||||
DeleteOutlined,
|
|
||||||
FormOutlined,
|
|
||||||
AlignLeftOutlined,
|
|
||||||
AuditOutlined,
|
|
||||||
PlusOutlined,
|
|
||||||
} from '@ant-design/icons';
|
|
||||||
import EditCompanyListRow, { CompanyDetailView } from '@/pages/Company/List/edit';
|
|
||||||
import {
|
|
||||||
addCmsCompanyList,
|
|
||||||
delCmsCompanyList,
|
|
||||||
getCmsCompanyDetail,
|
|
||||||
parseCmsCompanyDetailResponse,
|
|
||||||
putCmsCompanyList,
|
|
||||||
} from '@/services/company/list';
|
|
||||||
import { postCompanyApproval } from '@/services/company/review';
|
|
||||||
import { changeLcCompanyStatus, getAdminCompanyList, resetLcPsw } from '@/services/cms/company';
|
import { changeLcCompanyStatus, getAdminCompanyList, resetLcPsw } from '@/services/cms/company';
|
||||||
import { getDictValueEnum } from '@/services/system/dict';
|
import { getDictValueEnum } from '@/services/system/dict';
|
||||||
import { getCmsIndustryTreeList } from '@/services/classify/industry';
|
import DictTag from '@/components/DictTag';
|
||||||
import DictTag, { DictValueEnumObj } from '@/components/DictTag';
|
|
||||||
|
|
||||||
/** 将行业树数据展平为 DictValueEnumObj */
|
|
||||||
const flattenIndustryTree = (tree: { id: number; label: string; children?: any[] }[]): DictValueEnumObj => {
|
|
||||||
const map: DictValueEnumObj = {};
|
|
||||||
const walk = (nodes: { id: number; label: string; children?: any[] }[]) => {
|
|
||||||
nodes?.forEach((node) => {
|
|
||||||
map[node.id] = {
|
|
||||||
text: node.label,
|
|
||||||
label: node.label,
|
|
||||||
value: node.id,
|
|
||||||
};
|
|
||||||
if (node.children?.length) {
|
|
||||||
walk(node.children);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
walk(tree);
|
|
||||||
return map;
|
|
||||||
};
|
|
||||||
|
|
||||||
const loadCompanyDetail = async (record: API.CmsCompany.CompanyUser) => {
|
|
||||||
const resp = await getCmsCompanyDetail(record.companyId);
|
|
||||||
const detail = parseCmsCompanyDetailResponse(resp);
|
|
||||||
if (!detail) {
|
|
||||||
return { ...record } as any;
|
|
||||||
}
|
|
||||||
return { ...record, ...detail } as any;
|
|
||||||
};
|
|
||||||
|
|
||||||
function CompanyUserList() {
|
function CompanyUserList() {
|
||||||
const access = useAccess();
|
|
||||||
const actionRef = useRef<ActionType>();
|
const actionRef = useRef<ActionType>();
|
||||||
|
|
||||||
// ---- 字典数据 ----
|
|
||||||
const [scaleEnum, setScaleEnum] = useState<Record<string, any>>({});
|
const [scaleEnum, setScaleEnum] = useState<Record<string, any>>({});
|
||||||
const [natureEnum, setNatureEnum] = useState<Record<string, any>>({});
|
const [natureEnum, setNatureEnum] = useState<Record<string, any>>({});
|
||||||
const [companyNatureEnum, setCompanyNatureEnum] = useState<DictValueEnumObj>({});
|
|
||||||
const [industryEnum, setIndustryEnum] = useState<DictValueEnumObj>({});
|
|
||||||
|
|
||||||
// ---- 编辑弹窗 ----
|
|
||||||
const [currentRow, setCurrentRow] = useState<API.CompanyList.Company>();
|
|
||||||
const [modalVisible, setModalVisible] = useState<boolean>(false);
|
|
||||||
|
|
||||||
// ---- 详情弹窗 ----
|
|
||||||
const [detailVisible, setDetailVisible] = useState<boolean>(false);
|
|
||||||
const [detailLoading, setDetailLoading] = useState<boolean>(false);
|
|
||||||
const [detailData, setDetailData] = useState<API.CompanyList.Company | undefined>();
|
|
||||||
|
|
||||||
// ---- 审核弹窗 ----
|
|
||||||
const [approvalVisible, setApprovalVisible] = useState<boolean>(false);
|
|
||||||
const [approvalStatus, setApprovalStatus] = useState<1 | 2>(1);
|
|
||||||
const [approvalReason, setApprovalReason] = useState<string>('');
|
|
||||||
|
|
||||||
// ---- 密码修改弹窗 ----
|
|
||||||
const [passwordModalOpen, setPasswordModalOpen] = useState(false);
|
const [passwordModalOpen, setPasswordModalOpen] = useState(false);
|
||||||
const [currentRecord, setCurrentRecord] = useState<API.CmsCompany.CompanyUser | null>(null);
|
const [currentRecord, setCurrentRecord] = useState<API.CmsCompany.CompanyUser | null>(null);
|
||||||
const [passwordForm] = Form.useForm();
|
const [passwordForm] = Form.useForm();
|
||||||
@@ -83,34 +16,8 @@ function CompanyUserList() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getDictValueEnum('scale', true, true).then(setScaleEnum);
|
getDictValueEnum('scale', true, true).then(setScaleEnum);
|
||||||
getDictValueEnum('company_nature', true, true).then(setNatureEnum);
|
getDictValueEnum('company_nature', true, true).then(setNatureEnum);
|
||||||
getDictValueEnum('company_nature', false, true).then(setCompanyNatureEnum);
|
|
||||||
getCmsIndustryTreeList().then((res) => {
|
|
||||||
if (res?.data) {
|
|
||||||
setIndustryEnum(flattenIndustryTree(res.data));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// ---- 删除处理 ----
|
|
||||||
const handleRemoveOne = async (companyId: string) => {
|
|
||||||
const hide = message.loading('正在删除');
|
|
||||||
if (!companyId) return true;
|
|
||||||
try {
|
|
||||||
const resp = await delCmsCompanyList(companyId);
|
|
||||||
hide();
|
|
||||||
if (resp.code === 200) {
|
|
||||||
message.success('删除成功,即将刷新');
|
|
||||||
} else {
|
|
||||||
message.error(resp.msg);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} catch (error) {
|
|
||||||
hide();
|
|
||||||
message.error('删除失败,请重试');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const columns: ProColumns<API.CmsCompany.CompanyUser>[] = [
|
const columns: ProColumns<API.CmsCompany.CompanyUser>[] = [
|
||||||
{
|
{
|
||||||
title: '企业名称',
|
title: '企业名称',
|
||||||
@@ -142,7 +49,7 @@ function CompanyUserList() {
|
|||||||
align: 'center',
|
align: 'center',
|
||||||
width: 100,
|
width: 100,
|
||||||
valueEnum: scaleEnum,
|
valueEnum: scaleEnum,
|
||||||
render: (_, record) => <DictTag enums={scaleEnum} value={record.scale ?? undefined} />,
|
render: (_, record) => <DictTag enums={scaleEnum} value={record.scale} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '注册地址',
|
title: '注册地址',
|
||||||
@@ -161,41 +68,20 @@ function CompanyUserList() {
|
|||||||
width: 100,
|
width: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '企业联系人',
|
title: '联系人',
|
||||||
dataIndex: 'contactPerson',
|
dataIndex: 'contactPerson',
|
||||||
valueType: 'text',
|
valueType: 'text',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
width: 180,
|
width: 100,
|
||||||
hideInTable: true,
|
render: (_, record) => record.contactPerson || '-',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '联系人电话',
|
title: '联系电话',
|
||||||
dataIndex: 'contactPersonPhone',
|
dataIndex: 'contactPersonPhone',
|
||||||
valueType: 'text',
|
valueType: 'text',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
width: 160,
|
width: 130,
|
||||||
hideInTable: true,
|
render: (_, record) => record.contactPersonPhone || '-',
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '企业联系人',
|
|
||||||
dataIndex: 'companyContactList',
|
|
||||||
valueType: 'text',
|
|
||||||
align: 'center',
|
|
||||||
width: 180,
|
|
||||||
hideInSearch: true,
|
|
||||||
render: (_, record) => {
|
|
||||||
const list = record.companyContactList;
|
|
||||||
if (!list || list.length === 0) return '-';
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{list.map((item, index) => (
|
|
||||||
<div key={index}>
|
|
||||||
{item.contactPerson || '-'}:{item.contactPersonPhone || '-'}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '状态',
|
title: '状态',
|
||||||
@@ -258,96 +144,11 @@ function CompanyUserList() {
|
|||||||
{
|
{
|
||||||
title: '操作',
|
title: '操作',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
width: 320,
|
width: 120,
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
hideInSearch: true,
|
hideInSearch: true,
|
||||||
render: (_, record) => [
|
render: (_, record) => (
|
||||||
<Button
|
<a
|
||||||
type="link"
|
|
||||||
size="small"
|
|
||||||
key="detail"
|
|
||||||
icon={<AlignLeftOutlined />}
|
|
||||||
hidden={!access.hasPerms('cms:company:query')}
|
|
||||||
onClick={async () => {
|
|
||||||
setDetailData({ ...record } as any);
|
|
||||||
setDetailVisible(true);
|
|
||||||
setDetailLoading(true);
|
|
||||||
try {
|
|
||||||
const merged = await loadCompanyDetail(record);
|
|
||||||
setDetailData(merged);
|
|
||||||
} catch (e) {
|
|
||||||
message.error('获取详情失败');
|
|
||||||
} finally {
|
|
||||||
setDetailLoading(false);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
详情
|
|
||||||
</Button>,
|
|
||||||
<Button
|
|
||||||
type="link"
|
|
||||||
size="small"
|
|
||||||
key="edit"
|
|
||||||
icon={<FormOutlined />}
|
|
||||||
hidden={!access.hasPerms('cms:company:edit')}
|
|
||||||
onClick={async () => {
|
|
||||||
const hide = message.loading('正在加载详情');
|
|
||||||
try {
|
|
||||||
const merged = await loadCompanyDetail(record);
|
|
||||||
setCurrentRow(merged);
|
|
||||||
setModalVisible(true);
|
|
||||||
} catch (e) {
|
|
||||||
message.error('获取详情失败');
|
|
||||||
} finally {
|
|
||||||
hide();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
编辑
|
|
||||||
</Button>,
|
|
||||||
<Button
|
|
||||||
type="link"
|
|
||||||
size="small"
|
|
||||||
key="approval"
|
|
||||||
hidden={!access.hasPerms('sys:company:approval')}
|
|
||||||
icon={<AuditOutlined />}
|
|
||||||
onClick={() => {
|
|
||||||
setCurrentRow(record as any);
|
|
||||||
setApprovalStatus(1);
|
|
||||||
setApprovalReason('');
|
|
||||||
setApprovalVisible(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
审核
|
|
||||||
</Button>,
|
|
||||||
<Button
|
|
||||||
type="link"
|
|
||||||
size="small"
|
|
||||||
danger
|
|
||||||
key="batchRemove"
|
|
||||||
icon={<DeleteOutlined />}
|
|
||||||
hidden={!access.hasPerms('cms:company:remove')}
|
|
||||||
onClick={async () => {
|
|
||||||
Modal.confirm({
|
|
||||||
title: '删除',
|
|
||||||
content: '确定删除该项吗?',
|
|
||||||
okText: '确认',
|
|
||||||
cancelText: '取消',
|
|
||||||
onOk: async () => {
|
|
||||||
const success = await handleRemoveOne(String(record.companyId));
|
|
||||||
if (success) {
|
|
||||||
actionRef.current?.reload();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
删除
|
|
||||||
</Button>,
|
|
||||||
<Button
|
|
||||||
type="link"
|
|
||||||
size="small"
|
|
||||||
key="resetPwd"
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setCurrentRecord(record);
|
setCurrentRecord(record);
|
||||||
passwordForm.resetFields();
|
passwordForm.resetFields();
|
||||||
@@ -355,8 +156,8 @@ function CompanyUserList() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
修改密码
|
修改密码
|
||||||
</Button>,
|
</a>
|
||||||
],
|
),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -373,159 +174,11 @@ function CompanyUserList() {
|
|||||||
success: true,
|
success: true,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
search={{ labelWidth: 'auto', defaultCollapsed: false }}
|
search={{ labelWidth: 'auto' }}
|
||||||
scroll={{ x: 'max-content' }}
|
scroll={{ x: 'max-content' }}
|
||||||
pagination={{ defaultPageSize: 10, showSizeChanger: true }}
|
pagination={{ defaultPageSize: 10, showSizeChanger: true }}
|
||||||
headerTitle="企业用户管理"
|
headerTitle="企业用户管理"
|
||||||
toolBarRender={() => [
|
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
key="add"
|
|
||||||
hidden={!access.hasPerms('cms:company:add')}
|
|
||||||
onClick={async () => {
|
|
||||||
setCurrentRow(undefined);
|
|
||||||
setModalVisible(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<PlusOutlined /> 新建
|
|
||||||
</Button>,
|
|
||||||
]}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* 编辑/新增弹窗 */}
|
|
||||||
<EditCompanyListRow
|
|
||||||
open={modalVisible}
|
|
||||||
onSubmit={async (values) => {
|
|
||||||
let resData;
|
|
||||||
if (values.companyId) {
|
|
||||||
resData = await putCmsCompanyList({
|
|
||||||
...values,
|
|
||||||
companyNature: values.companyNature || '',
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
resData = await addCmsCompanyList({
|
|
||||||
...values,
|
|
||||||
companyNature: values.companyNature || '',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (resData.code === 200) {
|
|
||||||
setModalVisible(false);
|
|
||||||
setCurrentRow(undefined);
|
|
||||||
message.success(values.companyId ? '修改成功' : '新增成功');
|
|
||||||
actionRef.current?.reload();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onCancel={() => {
|
|
||||||
setModalVisible(false);
|
|
||||||
setCurrentRow(undefined);
|
|
||||||
}}
|
|
||||||
values={currentRow}
|
|
||||||
scaleEnum={scaleEnum}
|
|
||||||
companyNatureEnum={companyNatureEnum}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* 详情弹窗 */}
|
|
||||||
<CompanyDetailView
|
|
||||||
open={detailVisible}
|
|
||||||
loading={detailLoading}
|
|
||||||
onCancel={() => {
|
|
||||||
setDetailVisible(false);
|
|
||||||
setDetailData(undefined);
|
|
||||||
}}
|
|
||||||
record={detailData}
|
|
||||||
scaleEnum={scaleEnum}
|
|
||||||
companyNatureEnum={companyNatureEnum}
|
|
||||||
industryEnum={industryEnum}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* 审核弹窗 */}
|
|
||||||
<Modal
|
|
||||||
title="企业资质审核"
|
|
||||||
open={approvalVisible}
|
|
||||||
width={520}
|
|
||||||
onCancel={() => {
|
|
||||||
setApprovalVisible(false);
|
|
||||||
setApprovalReason('');
|
|
||||||
}}
|
|
||||||
footer={null}
|
|
||||||
>
|
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
|
|
||||||
<div>
|
|
||||||
<span style={{ marginRight: 12 }}>审核结果:</span>
|
|
||||||
<label style={{ marginRight: 16 }}>
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="approvalStatus"
|
|
||||||
checked={approvalStatus === 1}
|
|
||||||
onChange={() => setApprovalStatus(1)}
|
|
||||||
/>
|
|
||||||
<span style={{ marginLeft: 6 }}>通过</span>
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="approvalStatus"
|
|
||||||
checked={approvalStatus === 2}
|
|
||||||
onChange={() => setApprovalStatus(2)}
|
|
||||||
/>
|
|
||||||
<span style={{ marginLeft: 6 }}>驳回</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
{approvalStatus === 2 && (
|
|
||||||
<div>
|
|
||||||
<div style={{ marginBottom: 6 }}>驳回原因(必填):</div>
|
|
||||||
<textarea
|
|
||||||
style={{ width: '100%', minHeight: 100 }}
|
|
||||||
placeholder="请输入驳回原因"
|
|
||||||
value={approvalReason}
|
|
||||||
onChange={(e) => setApprovalReason(e.target.value)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div style={{ textAlign: 'right', marginTop: 8 }}>
|
|
||||||
<Button onClick={() => setApprovalVisible(false)} style={{ marginRight: 8 }}>
|
|
||||||
取消
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
onClick={async () => {
|
|
||||||
if (!currentRow?.companyId) {
|
|
||||||
message.error('缺少企业ID');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (approvalStatus === 2 && !approvalReason.trim()) {
|
|
||||||
message.warning('请填写驳回原因');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const hide = message.loading('正在提交审核');
|
|
||||||
try {
|
|
||||||
const resp = await postCompanyApproval({
|
|
||||||
companyId: currentRow.companyId,
|
|
||||||
status: approvalStatus,
|
|
||||||
notPassReason: approvalStatus === 2 ? approvalReason.trim() : undefined,
|
|
||||||
});
|
|
||||||
hide();
|
|
||||||
if (resp?.code === 200) {
|
|
||||||
message.success('提交成功');
|
|
||||||
setApprovalVisible(false);
|
|
||||||
setApprovalReason('');
|
|
||||||
actionRef.current?.reload();
|
|
||||||
} else {
|
|
||||||
message.error(resp?.msg || '提交失败');
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
hide();
|
|
||||||
message.error('提交失败');
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
确认
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
|
|
||||||
{/* 修改密码弹窗 */}
|
|
||||||
<Modal
|
<Modal
|
||||||
title="修改密码"
|
title="修改密码"
|
||||||
open={passwordModalOpen}
|
open={passwordModalOpen}
|
||||||
|
|||||||
@@ -40,12 +40,6 @@ export async function delCmsJobIds(ids: string) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function delCmsJobContact(ids: string) {
|
|
||||||
return request<API.ManagementList.ManagePageResult>(`/api/cms/jobcontact/${ids}`, {
|
|
||||||
method: 'DELETE',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function exportCmsJob(params?: API.ManagementList.ListParams) {
|
export async function exportCmsJob(params?: API.ManagementList.ListParams) {
|
||||||
return downLoadXlsx(`/api/cms/job/export`, { params }, `job_data_${new Date().getTime()}.xlsx`);
|
return downLoadXlsx(`/api/cms/job/export`, { params }, `job_data_${new Date().getTime()}.xlsx`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,6 +134,18 @@ export interface OutdoorFairCompanyQrCodeInfo {
|
|||||||
qrCodeContent: string;
|
qrCodeContent: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 管理端查看的招聘会微信小程序码。 */
|
||||||
|
export interface OutdoorFairMiniProgramQrCodeInfo {
|
||||||
|
fairId: number;
|
||||||
|
page: string;
|
||||||
|
scene: string;
|
||||||
|
imageData: string;
|
||||||
|
/** getUnlimitedQRCode 生成的小程序码长期有效。 */
|
||||||
|
permanent: boolean;
|
||||||
|
/** 小程序码本身是否需要周期轮换。 */
|
||||||
|
rotationRequired: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
/** 企业审核通过后可读取的只读展位图。 */
|
/** 企业审核通过后可读取的只读展位图。 */
|
||||||
export interface OutdoorFairCompanyBoothMap {
|
export interface OutdoorFairCompanyBoothMap {
|
||||||
currentCompanyId: number;
|
currentCompanyId: number;
|
||||||
@@ -307,6 +319,17 @@ export async function getMyOutdoorFairQrCode(fairId: number) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 管理员获取招聘会微信小程序码
|
||||||
|
* GET /api/cms/outdoor-fair/{fairId}/mini-program-qrcode
|
||||||
|
*/
|
||||||
|
export async function getOutdoorFairMiniProgramQrCode(fairId: number) {
|
||||||
|
return request<{ code: number; msg?: string; data: OutdoorFairMiniProgramQrCodeInfo }>(
|
||||||
|
`${CMS_OUTDOOR_FAIR_BASE}/${fairId}/mini-program-qrcode`,
|
||||||
|
{ method: 'GET' },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 上传户外招聘会照片
|
* 上传户外招聘会照片
|
||||||
* POST /api/common/upload
|
* POST /api/common/upload
|
||||||
|
|||||||
9
src/types/Management/list.d.ts
vendored
9
src/types/Management/list.d.ts
vendored
@@ -1,6 +1,5 @@
|
|||||||
declare namespace API.ManagementList {
|
declare namespace API.ManagementList {
|
||||||
export interface ContactPerson {
|
export interface ContactPerson {
|
||||||
id?: number;
|
|
||||||
contactPerson?: string;
|
contactPerson?: string;
|
||||||
contactPersonPhone?: string;
|
contactPersonPhone?: string;
|
||||||
position?: string;
|
position?: string;
|
||||||
@@ -40,10 +39,6 @@ declare namespace API.ManagementList {
|
|||||||
createTime?: string;
|
createTime?: string;
|
||||||
jobCategory?: string;
|
jobCategory?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
/** 是否重点人群 0是,1否 */
|
|
||||||
isKeyPopulations?: string;
|
|
||||||
/** 重点人群-1农民工、2团场职工、3失业人员、4零就业家庭、5零工人员、6退役军人 */
|
|
||||||
keyPopulations?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AddParams {
|
export interface AddParams {
|
||||||
@@ -76,10 +71,6 @@ declare namespace API.ManagementList {
|
|||||||
jobContactList?: ContactPerson[];
|
jobContactList?: ContactPerson[];
|
||||||
jobCategory?: string;
|
jobCategory?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
/** 是否重点人群 0是,1否 */
|
|
||||||
isKeyPopulations?: string;
|
|
||||||
/** 重点人群-1农民工、2团场职工、3失业人员、4零就业家庭、5零工人员、6退役军人 */
|
|
||||||
keyPopulations?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ListParams {
|
export interface ListParams {
|
||||||
|
|||||||
11
src/types/cms/company.d.ts
vendored
11
src/types/cms/company.d.ts
vendored
@@ -7,15 +7,6 @@ declare namespace API.CmsCompany {
|
|||||||
data?: any;
|
data?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CompanyContact {
|
|
||||||
createTime: string | null;
|
|
||||||
updateTime: string | null;
|
|
||||||
id: number | null;
|
|
||||||
companyId: number;
|
|
||||||
contactPerson: string;
|
|
||||||
contactPersonPhone: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CompanyUser {
|
export interface CompanyUser {
|
||||||
createTime: string;
|
createTime: string;
|
||||||
updateTime: string;
|
updateTime: string;
|
||||||
@@ -38,7 +29,7 @@ declare namespace API.CmsCompany {
|
|||||||
contactPersonPhone?: string | null;
|
contactPersonPhone?: string | null;
|
||||||
status: number; // 0 待审核, 1 通过, 2 驳回
|
status: number; // 0 待审核, 1 通过, 2 驳回
|
||||||
notPassReason?: string | null;
|
notPassReason?: string | null;
|
||||||
companyContactList?: CompanyContact[];
|
companyContactList?: any;
|
||||||
registeredAddress: string;
|
registeredAddress: string;
|
||||||
isAbnormal?: any;
|
isAbnormal?: any;
|
||||||
rejectTime?: string | null;
|
rejectTime?: string | null;
|
||||||
|
|||||||
24
src/types/jobportal/user.d.ts
vendored
24
src/types/jobportal/user.d.ts
vendored
@@ -16,7 +16,6 @@ declare namespace API {
|
|||||||
education?: string;
|
education?: string;
|
||||||
politicalAffiliation?: string | null;
|
politicalAffiliation?: string | null;
|
||||||
phone?: string;
|
phone?: string;
|
||||||
email?: string;
|
|
||||||
avatar?: string;
|
avatar?: string;
|
||||||
salaryMin?: string;
|
salaryMin?: string;
|
||||||
salaryMax?: string;
|
salaryMax?: string;
|
||||||
@@ -38,32 +37,9 @@ declare namespace API {
|
|||||||
company?: string | null;
|
company?: string | null;
|
||||||
experiencesList?: WorkExperience[];
|
experiencesList?: WorkExperience[];
|
||||||
appSkillsList?: AppSkill[];
|
appSkillsList?: AppSkill[];
|
||||||
educationsList?: AppUserEducation[];
|
|
||||||
trainsList?: AppUserTrain[];
|
|
||||||
fileList?: any[];
|
fileList?: any[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AppUserEducation {
|
|
||||||
id?: number;
|
|
||||||
userId?: number;
|
|
||||||
schoolName?: string;
|
|
||||||
major?: string;
|
|
||||||
degree?: string;
|
|
||||||
startTime?: string;
|
|
||||||
endTime?: string;
|
|
||||||
content?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AppUserTrain {
|
|
||||||
id?: number;
|
|
||||||
userId?: number;
|
|
||||||
trainOrg?: string;
|
|
||||||
trainCourse?: string;
|
|
||||||
startTime?: string;
|
|
||||||
endTime?: string;
|
|
||||||
trainContent?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface WorkExperience {
|
export interface WorkExperience {
|
||||||
createTime?: string;
|
createTime?: string;
|
||||||
updateTime?: string;
|
updateTime?: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user