From c6d665c370d2bd9cc43ef1805cccd203575c5298 Mon Sep 17 00:00:00 2001 From: lapuda <577732344@qq.com> Date: Fri, 24 Jul 2026 09:42:37 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E6=8B=9B=E8=81=98?= =?UTF-8?q?=E4=BC=9A=E6=9C=BA=E6=9E=84=E7=AE=A1=E7=90=86=E5=92=8C=E5=9C=BA?= =?UTF-8?q?=E5=9C=B0=E6=9C=BA=E6=9E=84=E9=80=89=E6=8B=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/routes.ts | 5 + src/pages/Jobfair/Institution/index.tsx | 172 ++++++++++++++++++ src/pages/Jobfair/Venueinfo/Detail/index.tsx | 63 ++++--- .../Venueinfo/components/EditModal.tsx | 91 +++++++++ src/pages/Jobfair/Venueinfo/index.tsx | 39 +++- src/services/jobportal/jobFairInstitution.ts | 72 ++++++++ src/services/jobportal/venueInfo.ts | 6 + 7 files changed, 426 insertions(+), 22 deletions(-) create mode 100644 src/pages/Jobfair/Institution/index.tsx create mode 100644 src/services/jobportal/jobFairInstitution.ts diff --git a/config/routes.ts b/config/routes.ts index 3645f66..e2b1ecd 100644 --- a/config/routes.ts +++ b/config/routes.ts @@ -301,6 +301,11 @@ export default [ path: '/jobfair/venue-info', component: './Jobfair/Venueinfo', }, + { + name: '机构管理', + path: '/jobfair/institution', + component: './Jobfair/Institution', + }, { name: '场地信息详情', path: '/jobfair/venue-info/detail', diff --git a/src/pages/Jobfair/Institution/index.tsx b/src/pages/Jobfair/Institution/index.tsx new file mode 100644 index 0000000..6406264 --- /dev/null +++ b/src/pages/Jobfair/Institution/index.tsx @@ -0,0 +1,172 @@ +import React, { useRef, useState } from 'react'; +import { useAccess } from '@umijs/max'; +import { Button, message, Modal } from 'antd'; +import { + ActionType, + ModalForm, + PageContainer, + ProColumns, + ProFormText, + ProFormTextArea, + ProTable, +} from '@ant-design/pro-components'; +import { DeleteOutlined, EditOutlined, PlusOutlined } from '@ant-design/icons'; +import { + addJobFairInstitution, + deleteJobFairInstitution, + getJobFairInstitutionList, + updateJobFairInstitution, +} from '@/services/jobportal/jobFairInstitution'; +import type { + JobFairInstitutionForm, + JobFairInstitutionItem, +} from '@/services/jobportal/jobFairInstitution'; + +const InstitutionManagement: React.FC = () => { + const access = useAccess(); + const actionRef = useRef(); + const [formOpen, setFormOpen] = useState(false); + const [currentRow, setCurrentRow] = useState(); + + const handleDelete = (record: JobFairInstitutionItem) => { + Modal.confirm({ + title: '确认删除', + content: `确定要删除机构「${record.institutionName}」吗?如果该机构已被场地使用,需要先解除场地关联。`, + onOk: async () => { + const res = await deleteJobFairInstitution(record.id); + if (res.code === 200) { + message.success('删除成功'); + actionRef.current?.reload(); + } else { + message.error(res.msg || '删除失败'); + } + }, + }); + }; + + const columns: ProColumns[] = [ + { + title: '机构名称', + dataIndex: 'institutionName', + ellipsis: true, + width: 260, + }, + { + title: '机构简介', + dataIndex: 'institutionIntro', + ellipsis: true, + hideInSearch: true, + }, + { + title: '创建时间', + dataIndex: 'createTime', + valueType: 'dateTime', + hideInSearch: true, + width: 180, + }, + { + title: '操作', + valueType: 'option', + width: 160, + fixed: 'right', + render: (_, record) => [ + , + , + ], + }, + ]; + + return ( + + + headerTitle="机构列表" + actionRef={actionRef} + rowKey="id" + columns={columns} + search={{ labelWidth: 'auto' }} + scroll={{ x: 'max-content' }} + request={async (params) => { + const res = await getJobFairInstitutionList({ + institutionName: params.institutionName, + current: params.current, + pageSize: params.pageSize, + }); + return { data: res.rows || [], total: res.total || 0, success: res.code === 200 }; + }} + toolBarRender={() => [ + , + ]} + /> + + + title={currentRow ? '编辑机构' : '新增机构'} + open={formOpen} + initialValues={currentRow} + modalProps={{ destroyOnClose: true, onCancel: () => setFormOpen(false) }} + onFinish={async (values) => { + const res = currentRow + ? await updateJobFairInstitution({ ...values, id: currentRow.id }) + : await addJobFairInstitution(values); + if (res.code === 200) { + message.success(currentRow ? '修改成功' : '新增成功'); + setFormOpen(false); + setCurrentRow(undefined); + actionRef.current?.reload(); + return true; + } + message.error(res.msg || '操作失败'); + return false; + }} + > + + + + + ); +}; + +export default InstitutionManagement; diff --git a/src/pages/Jobfair/Venueinfo/Detail/index.tsx b/src/pages/Jobfair/Venueinfo/Detail/index.tsx index 467bbc3..cc7831d 100644 --- a/src/pages/Jobfair/Venueinfo/Detail/index.tsx +++ b/src/pages/Jobfair/Venueinfo/Detail/index.tsx @@ -8,9 +8,21 @@ import { getVenueInfo, updateVenueInfo, } from '@/services/jobportal/venueInfo'; -import type { VenueInfoDictForm, VenueInfoForm, VenueInfoItem } from '@/services/jobportal/venueInfo'; +import type { + VenueInfoDictForm, + VenueInfoForm, + VenueInfoItem, +} from '@/services/jobportal/venueInfo'; import { getCmsLineList } from '@/services/area/subway'; import { getDictSelectOption, getDictValueEnum } from '@/services/system/dict'; +import { + addJobFairInstitution, + getJobFairInstitutionList, +} from '@/services/jobportal/jobFairInstitution'; +import type { + JobFairInstitutionForm, + JobFairInstitutionItem, +} from '@/services/jobportal/jobFairInstitution'; import EditModal from '../components/EditModal'; const VENUE_TYPE_DICT = 'venue_info_type'; @@ -25,6 +37,7 @@ const VenueInfoDetail: React.FC = () => { const [venueTypeOptions, setVenueTypeOptions] = useState([]); const [venueTypeValueEnum, setVenueTypeValueEnum] = useState>({}); const [lineOptions, setLineOptions] = useState([]); + const [institutionOptions, setInstitutionOptions] = useState([]); const fetchVenueInfo = useCallback(async () => { if (!venueId) { @@ -45,10 +58,11 @@ const VenueInfoDetail: React.FC = () => { }, [venueId]); const refreshOptions = useCallback(async () => { - const [valueEnum, options, lineRes] = await Promise.all([ + const [valueEnum, options, lineRes, institutionRes] = await Promise.all([ getDictValueEnum(VENUE_TYPE_DICT), getDictSelectOption(VENUE_TYPE_DICT), getCmsLineList({ current: '1', pageSize: '999' } as any), + getJobFairInstitutionList({ current: 1, pageSize: 9999 }), ]); setVenueTypeValueEnum(valueEnum); setVenueTypeOptions(options); @@ -60,8 +74,23 @@ const VenueInfoDetail: React.FC = () => { })), ); } + if (institutionRes.code === 200) { + setInstitutionOptions(institutionRes.rows || []); + } }, []); + const handleCreateInstitution = async (values: JobFairInstitutionForm) => { + const res = await addJobFairInstitution(values); + if (res.code === 200 && res.data) { + const listRes = await getJobFairInstitutionList({ current: 1, pageSize: 9999 }); + if (listRes.code === 200) setInstitutionOptions(listRes.rows || []); + message.success('机构新增成功'); + return res.data; + } + message.error(res.msg || '机构新增失败'); + return undefined; + }; + useEffect(() => { fetchVenueInfo(); refreshOptions(); @@ -80,11 +109,7 @@ const VenueInfoDetail: React.FC = () => { const venueTypeLabel = (val?: string) => { if (!val) return '--'; - return ( - venueTypeValueEnum[val]?.text || - venueTypeValueEnum[val]?.label || - val - ); + return venueTypeValueEnum[val]?.text || venueTypeValueEnum[val]?.label || val; }; return ( @@ -120,27 +145,20 @@ const VenueInfoDetail: React.FC = () => { {venueInfo.venueArea != null ? `${venueInfo.venueArea} ㎡` : '--'} - - {venueInfo.floorCount ?? '--'} - + {venueInfo.floorCount ?? '--'} {venueInfo.venueAddress || '--'} - - {venueInfo.contactPhone || '--'} - + {venueInfo.contactPhone || '--'} {venueInfo.routeLineNames || '--'} - - {venueInfo.handleDate || '--'} - - - {venueInfo.handleOrg || '--'} - - - {venueInfo.handler || '--'} + {venueInfo.handleDate || '--'} + {venueInfo.handleOrg || '--'} + + {venueInfo.institutionNames || '--'} + {venueInfo.handler || '--'} {venueInfo.remark || '--'} @@ -155,6 +173,9 @@ const VenueInfoDetail: React.FC = () => { values={venueInfo} venueTypeOptions={venueTypeOptions} lineOptions={lineOptions} + institutionOptions={institutionOptions} + canCreateInstitution={access.hasPerms('cms:jobFairInstitution:add')} + onCreateInstitution={handleCreateInstitution} onCreateDictOption={handleCreateDictOption} onCancel={() => setEditModalOpen(false)} onSubmit={async (values: VenueInfoForm) => { diff --git a/src/pages/Jobfair/Venueinfo/components/EditModal.tsx b/src/pages/Jobfair/Venueinfo/components/EditModal.tsx index 2845309..1b52e72 100644 --- a/src/pages/Jobfair/Venueinfo/components/EditModal.tsx +++ b/src/pages/Jobfair/Venueinfo/components/EditModal.tsx @@ -10,6 +10,10 @@ import { } from '@ant-design/pro-components'; import { Button, Divider, Form, Input, Modal, Space } from 'antd'; import { PlusOutlined } from '@ant-design/icons'; +import type { + JobFairInstitutionForm, + JobFairInstitutionItem, +} from '@/services/jobportal/jobFairInstitution'; import type { VenueInfoDictForm, VenueInfoForm, @@ -21,8 +25,13 @@ interface EditModalProps { values?: VenueInfoItem; venueTypeOptions: any[]; lineOptions: any[]; + institutionOptions: JobFairInstitutionItem[]; + canCreateInstitution: boolean; defaultHandler?: string; onCreateDictOption: (values: VenueInfoDictForm) => Promise; + onCreateInstitution: ( + values: JobFairInstitutionForm, + ) => Promise; onCancel: () => void; onSubmit: (values: VenueInfoForm) => Promise; } @@ -44,8 +53,11 @@ const EditModal: React.FC = ({ values, venueTypeOptions, lineOptions, + institutionOptions, + canCreateInstitution, defaultHandler, onCreateDictOption, + onCreateInstitution, onCancel, onSubmit, }) => { @@ -53,6 +65,9 @@ const EditModal: React.FC = ({ const [dictModalOpen, setDictModalOpen] = useState(false); const [dictInputValue, setDictInputValue] = useState(''); const [dictSubmitLoading, setDictSubmitLoading] = useState(false); + const [institutionModalOpen, setInstitutionModalOpen] = useState(false); + const [institutionSubmitLoading, setInstitutionSubmitLoading] = useState(false); + const [institutionForm] = Form.useForm(); useEffect(() => { if (open) { @@ -61,6 +76,7 @@ const EditModal: React.FC = ({ form.setFieldsValue({ ...values, routeLineIds: splitRouteLineIds(values.routeLineIds), + institutionIds: values.institutionIds || [], }); } else { form.setFieldsValue({ @@ -111,6 +127,45 @@ const EditModal: React.FC = ({ } }; + const renderInstitutionDropdown = (menu: React.ReactElement): React.ReactElement => ( + <> + {menu} + {canCreateInstitution && ( + <> + + + + + + )} + + ); + + const handleInstitutionModalOk = async () => { + try { + const values = await institutionForm.validateFields(); + setInstitutionSubmitLoading(true); + const created = await onCreateInstitution(values); + if (created) { + const selectedIds = (form.getFieldValue('institutionIds') || []) as number[]; + form.setFieldValue('institutionIds', [...selectedIds, created.id]); + setInstitutionModalOpen(false); + } + } finally { + setInstitutionSubmitLoading(false); + } + }; + return ( = ({ }} placeholder="请选择乘坐线路" /> + ({ + label: item.institutionName, + value: item.id, + }))} + fieldProps={{ + showSearch: true, + optionFilterProp: 'label', + dropdownRender: renderInstitutionDropdown, + }} + placeholder="请选择所属机构,可多选" + /> = ({ onPressEnter={handleDictModalOk} /> + setInstitutionModalOpen(false)} + destroyOnClose + > +
+ + + + + + +
+
); }; diff --git a/src/pages/Jobfair/Venueinfo/index.tsx b/src/pages/Jobfair/Venueinfo/index.tsx index 7486af6..84d0f7e 100644 --- a/src/pages/Jobfair/Venueinfo/index.tsx +++ b/src/pages/Jobfair/Venueinfo/index.tsx @@ -18,6 +18,14 @@ import type { } from '@/services/jobportal/venueInfo'; import { getCmsLineList } from '@/services/area/subway'; import { getDictSelectOption, getDictValueEnum } from '@/services/system/dict'; +import { + addJobFairInstitution, + getJobFairInstitutionList, +} from '@/services/jobportal/jobFairInstitution'; +import type { + JobFairInstitutionForm, + JobFairInstitutionItem, +} from '@/services/jobportal/jobFairInstitution'; import EditModal from './components/EditModal'; import VenueBoothMapModal from './components/VenueBoothMapModal'; @@ -33,6 +41,7 @@ const VenueInfoList: React.FC = () => { const [venueTypeValueEnum, setVenueTypeValueEnum] = useState>({}); const [venueTypeOptions, setVenueTypeOptions] = useState([]); const [lineOptions, setLineOptions] = useState([]); + const [institutionOptions, setInstitutionOptions] = useState([]); const defaultHandler = initialState?.currentUser?.nickName || initialState?.currentUser?.userName || undefined; @@ -58,10 +67,29 @@ const VenueInfoList: React.FC = () => { } }, []); + const refreshInstitutionOptions = useCallback(async () => { + const res = await getJobFairInstitutionList({ current: 1, pageSize: 9999 }); + if (res.code === 200) { + setInstitutionOptions(res.rows || []); + } + }, []); + useEffect(() => { refreshVenueTypeOptions(); refreshLineOptions(); - }, [refreshVenueTypeOptions, refreshLineOptions]); + refreshInstitutionOptions(); + }, [refreshVenueTypeOptions, refreshLineOptions, refreshInstitutionOptions]); + + const handleCreateInstitution = async (values: JobFairInstitutionForm) => { + const res = await addJobFairInstitution(values); + if (res.code === 200 && res.data) { + await refreshInstitutionOptions(); + message.success('机构新增成功'); + return res.data; + } + message.error(res.msg || '机构新增失败'); + return undefined; + }; const handleCreateDictOption = async (values: VenueInfoDictForm) => { const res = await addVenueInfoDictOption(values); @@ -118,6 +146,12 @@ const VenueInfoList: React.FC = () => { dataIndex: 'venueAddress', ellipsis: true, }, + { + title: '所属机构', + dataIndex: 'institutionNames', + ellipsis: true, + hideInSearch: true, + }, { title: '联系电话', dataIndex: 'contactPhone', @@ -234,6 +268,9 @@ const VenueInfoList: React.FC = () => { values={currentRow} venueTypeOptions={venueTypeOptions} lineOptions={lineOptions} + institutionOptions={institutionOptions} + canCreateInstitution={access.hasPerms('cms:jobFairInstitution:add')} + onCreateInstitution={handleCreateInstitution} defaultHandler={defaultHandler} onCreateDictOption={handleCreateDictOption} onCancel={() => { diff --git a/src/services/jobportal/jobFairInstitution.ts b/src/services/jobportal/jobFairInstitution.ts new file mode 100644 index 0000000..cf804c7 --- /dev/null +++ b/src/services/jobportal/jobFairInstitution.ts @@ -0,0 +1,72 @@ +import { request } from '@umijs/max'; + +export interface JobFairInstitutionItem { + id: number; + /** 机构名称 */ + institutionName: string; + /** 机构简介 */ + institutionIntro?: string; + createTime?: string; + updateTime?: string; +} + +export interface JobFairInstitutionForm { + id?: number; + institutionName: string; + institutionIntro?: string; +} + +export interface JobFairInstitutionListParams { + institutionName?: string; + current?: number; + pageSize?: number; +} + +export interface JobFairInstitutionListResult { + code: number; + msg?: string; + total: number; + rows: JobFairInstitutionItem[]; +} + +export interface JobFairInstitutionResult { + code: number; + msg?: string; + data?: JobFairInstitutionItem; +} + +const BASE_URL = '/api/cms/job-fair-institution'; + +export async function getJobFairInstitutionList(params?: JobFairInstitutionListParams) { + return request(`${BASE_URL}/list`, { + method: 'GET', + params, + }); +} + +export async function getJobFairInstitution(id: number) { + return request(`${BASE_URL}/${id}`, { + method: 'GET', + }); +} + +export async function addJobFairInstitution(data: JobFairInstitutionForm) { + return request(BASE_URL, { + method: 'POST', + data, + }); +} + +export async function updateJobFairInstitution(data: JobFairInstitutionForm) { + return request(BASE_URL, { + method: 'PUT', + data, + }); +} + +export async function deleteJobFairInstitution(ids: number | number[]) { + const idPath = Array.isArray(ids) ? ids.join(',') : ids; + return request(`${BASE_URL}/${idPath}`, { + method: 'DELETE', + }); +} diff --git a/src/services/jobportal/venueInfo.ts b/src/services/jobportal/venueInfo.ts index 8084d1f..70630d8 100644 --- a/src/services/jobportal/venueInfo.ts +++ b/src/services/jobportal/venueInfo.ts @@ -27,6 +27,10 @@ export interface VenueInfoItem { handler?: string; /** 备注 */ remark?: string; + /** 所属机构ID,支持多个 */ + institutionIds?: number[]; + /** 所属机构名称,多个用顿号分隔 */ + institutionNames?: string; createTime?: string; updateTime?: string; } @@ -63,6 +67,7 @@ export interface VenueInfoForm { handleOrg?: string; handler?: string; remark?: string; + institutionIds?: number[]; } /** 通用响应 */ @@ -103,6 +108,7 @@ const formatDate = (value: any) => { const toApiPayload = (data: VenueInfoForm) => ({ ...data, routeLineIds: Array.isArray(data.routeLineIds) ? data.routeLineIds.join(',') : data.routeLineIds, + institutionIds: Array.isArray(data.institutionIds) ? data.institutionIds : [], handleDate: formatDate(data.handleDate), });