diff --git a/config/routes.ts b/config/routes.ts index 40a9833..e0d66b9 100644 --- a/config/routes.ts +++ b/config/routes.ts @@ -88,6 +88,10 @@ export default [ path: '/job-portal/personal-center/complaints', // 我的投诉 component: './JobPortal/PersonalCenter/Complaints', }, + { + path: '/job-portal/personal-center/blocked-companies', // 屏蔽企业列表 + component: './JobPortal/PersonalCenter/BlockedCompanies', + }, { path: '/job-portal/message', // 消息通知页面 component: './JobPortal/Message', diff --git a/src/pages/JobPortal/Detail/index.less b/src/pages/JobPortal/Detail/index.less index 84338b5..e309ea7 100644 --- a/src/pages/JobPortal/Detail/index.less +++ b/src/pages/JobPortal/Detail/index.less @@ -128,6 +128,12 @@ } } + .company-block-action { + flex-shrink: 0; + margin-left: auto; + align-self: center; + } + .company-section-divider { margin: 18px 0 16px; } diff --git a/src/pages/JobPortal/Detail/index.tsx b/src/pages/JobPortal/Detail/index.tsx index 7855ebe..339f835 100644 --- a/src/pages/JobPortal/Detail/index.tsx +++ b/src/pages/JobPortal/Detail/index.tsx @@ -11,7 +11,8 @@ import { Progress, Divider, Avatar, - message + message, + Modal } from 'antd'; import { ArrowLeftOutlined, @@ -23,7 +24,8 @@ import { TrophyOutlined, FireOutlined, StarOutlined, - FlagOutlined + FlagOutlined, + StopOutlined } from '@ant-design/icons'; import { Radar } from '@ant-design/charts'; import JobPortalHeader from '@/components/JobPortalHeader'; @@ -33,7 +35,7 @@ import { getEmptyCompetitivenessRadar, } from '@/services/jobportal/competitiveness'; import { isJobPortalLoggedIn } from '@/utils/jobPortalAuth'; -import { favoriteJob, unfavoriteJob, applyJob, browseJob, getJobDetail } from '@/services/jobportal/user'; +import { favoriteJob, unfavoriteJob, applyJob, browseJob, getJobDetail, blockCompany } from '@/services/jobportal/user'; import { ensureJobPortalLogin, requireJobPortalUserId } from '@/utils/jobPortalAuth'; import { getDictLabel, resolveIndustryLabel } from '@/utils/jobPortalDict'; import JobComplaintModal from '@/components/JobComplaintModal'; @@ -444,6 +446,37 @@ const JobDetailPage: React.FC = () => { } }; + const handleBlockCompany = async () => { + const companyId = originalJobData?.companyId || originalJobData?.companyVo?.companyId; + if (!companyId) { + message.error('未获取到企业信息'); + return; + } + const userId = await requireJobPortalUserId('屏蔽企业'); + if (!userId) { + return; + } + Modal.confirm({ + title: '屏蔽该企业', + content: '确定要屏蔽该企业吗?屏蔽后将不再收到该企业的招聘信息。', + okText: '确定屏蔽', + cancelText: '取消', + okButtonProps: { danger: true }, + onOk: async () => { + try { + const res = await blockCompany({ userId, companyId }); + if (res?.code === 200) { + message.success('已屏蔽该企业'); + } else { + message.error(res?.msg || '屏蔽失败,请稍后重试'); + } + } catch { + message.error('操作失败,请稍后重试'); + } + }, + }); + }; + return (
@@ -551,6 +584,15 @@ const JobDetailPage: React.FC = () => {
)} +
+ +
diff --git a/src/pages/JobPortal/List/index.less b/src/pages/JobPortal/List/index.less index 6e5637f..4e84c2e 100644 --- a/src/pages/JobPortal/List/index.less +++ b/src/pages/JobPortal/List/index.less @@ -317,6 +317,12 @@ } } + .company-block-action { + flex-shrink: 0; + margin-left: auto; + align-self: center; + } + .company-section-divider { margin: 18px 0 16px; } diff --git a/src/pages/JobPortal/List/index.tsx b/src/pages/JobPortal/List/index.tsx index 750e755..6f77075 100644 --- a/src/pages/JobPortal/List/index.tsx +++ b/src/pages/JobPortal/List/index.tsx @@ -11,7 +11,8 @@ import { Button, Select, Input, - message + message, + Modal } from 'antd'; import { EnvironmentOutlined, @@ -21,12 +22,13 @@ import { FlagOutlined, ArrowLeftOutlined, SearchOutlined, - TrophyOutlined + TrophyOutlined, + StopOutlined } from '@ant-design/icons'; import { history, useLocation } from '@umijs/max'; import { getJobList, getJobRecommend } from '@/services/common/jobTitle'; import JobPortalHeader from '@/components/JobPortalHeader'; -import { favoriteJob, unfavoriteJob, applyJob, browseJob } from '@/services/jobportal/user'; +import { favoriteJob, unfavoriteJob, applyJob, browseJob, blockCompany } from '@/services/jobportal/user'; import { ensureJobPortalLogin, requireJobPortalUserId } from '@/utils/jobPortalAuth'; import { getDictValueEnum } from '@/services/system/dict'; import { getDictLabel, findTreeLabelById, resolveIndustryLabel } from '@/utils/jobPortalDict'; @@ -525,6 +527,37 @@ const JobListPage: React.FC = () => { } }; + const handleBlockCompany = async () => { + const companyId = selectedJob?.companyId || selectedJob?.companyVo?.companyId; + if (!companyId) { + message.error('未获取到企业信息'); + return; + } + const userId = await requireJobPortalUserId('屏蔽企业'); + if (!userId) { + return; + } + Modal.confirm({ + title: '屏蔽该企业', + content: '确定要屏蔽该企业吗?屏蔽后将不再收到该企业的招聘信息。', + okText: '确定屏蔽', + cancelText: '取消', + okButtonProps: { danger: true }, + onOk: async () => { + try { + const res = await blockCompany({ userId, companyId }); + if (res?.code === 200) { + message.success('已屏蔽该企业'); + } else { + message.error(res?.msg || '屏蔽失败,请稍后重试'); + } + } catch { + message.error('操作失败,请稍后重试'); + } + }, + }); + }; + // 处理搜索功能(服务端搜索,不再做客户端过滤) const handleSearch = async () => { const term = searchValue.trim(); @@ -975,6 +1008,15 @@ const JobListPage: React.FC = () => {
)} +
+ +
diff --git a/src/pages/JobPortal/PersonalCenter/BlockedCompanies/index.less b/src/pages/JobPortal/PersonalCenter/BlockedCompanies/index.less new file mode 100644 index 0000000..8f3eed6 --- /dev/null +++ b/src/pages/JobPortal/PersonalCenter/BlockedCompanies/index.less @@ -0,0 +1,79 @@ +@import '../../theme.less'; + +.blocked-companies-page { + min-height: 100vh; + background: @jp-bg-page; + padding-bottom: 40px; + + .blocked-companies-container { + .jp-page-container(); + margin-top: 20px; + padding: 0 20px; + box-sizing: border-box; + } + + .page-header { + display: flex; + align-items: center; + gap: 12px; + margin-bottom: 16px; + + .back-btn { + flex-shrink: 0; + color: @jp-text-secondary; + padding: 0 8px; + + &:hover { + color: @jp-primary; + } + } + + .page-title { + margin: 0; + font-size: 20px; + font-weight: 600; + color: @jp-text-primary; + } + } + + .list-card { + .jp-card-base(); + + .blocked-item { + padding: 16px 20px; + + &:not(:last-child) { + border-bottom: 1px solid @jp-border; + } + + .company-avatar { + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + background: #fff2f0; + border-radius: @jp-radius; + color: #ff4d4f; + font-size: 18px; + } + + .company-name { + font-size: 15px; + color: @jp-text-primary; + } + + .block-time { + font-size: 13px; + color: @jp-text-muted; + } + } + } +} + +@media (max-width: 768px) { + .blocked-companies-page .blocked-companies-container { + padding: 0 12px; + margin-top: 12px; + } +} diff --git a/src/pages/JobPortal/PersonalCenter/BlockedCompanies/index.tsx b/src/pages/JobPortal/PersonalCenter/BlockedCompanies/index.tsx new file mode 100644 index 0000000..ca19300 --- /dev/null +++ b/src/pages/JobPortal/PersonalCenter/BlockedCompanies/index.tsx @@ -0,0 +1,153 @@ +import React, { useState, useEffect } from 'react'; +import { + Card, + List, + Typography, + Button, + message, + Modal, + Spin, + Empty +} from 'antd'; +import { + StopOutlined, + DeleteOutlined, + ArrowLeftOutlined +} from '@ant-design/icons'; +import { history } from '@umijs/max'; +import JobPortalHeader from '@/components/JobPortalHeader'; +import { getBlockedCompanyList } from '@/services/jobportal/user'; +import { request } from '@umijs/max'; +import './index.less'; + +const { Title, Text } = Typography; + +interface BlockedCompany { + id: number; + userId: number; + companyId: number; + companyName: string; + name: string; + createTime: string; + updateTime: string; +} + +const BlockedCompaniesPage: React.FC = () => { + const [loading, setLoading] = useState(false); + const [dataSource, setDataSource] = useState([]); + const [total, setTotal] = useState(0); + + const fetchList = async () => { + setLoading(true); + try { + const res = await getBlockedCompanyList({ pageNum: 1, pageSize: 100 }); + if (res?.code === 200) { + setDataSource((res as any)?.rows || []); + setTotal((res as any)?.total || 0); + } else { + message.error(res?.msg || '获取屏蔽企业列表失败'); + } + } catch { + message.error('获取屏蔽企业列表失败'); + } finally { + setLoading(false); + } + }; + + useEffect(() => { + fetchList(); + }, []); + + const handleCancelBlock = (record: BlockedCompany) => { + Modal.confirm({ + title: '取消屏蔽', + content: `确定要取消屏蔽"${record.companyName}"吗?`, + okText: '确定取消', + cancelText: '取消', + onOk: async () => { + try { + const res = await request('/api/cms/blockCompany/' + record.id, { + method: 'DELETE', + }); + if (res?.code === 200) { + message.success('已取消屏蔽'); + setDataSource(prev => prev.filter(item => item.id !== record.id)); + setTotal(prev => prev - 1); + } else { + message.error(res?.msg || '取消屏蔽失败'); + } + } catch { + message.error('操作失败,请稍后重试'); + } + }, + }); + }; + + return ( +
+ + +
+
+ + 屏蔽企业 +
+ + + + {dataSource.length === 0 && !loading ? ( + + ) : ( + ( + } + onClick={() => handleCancelBlock(item)} + > + 取消屏蔽 + + ]} + > + + +
+ } + title={ + + {item.companyName} + + } + description={ + + 屏蔽时间:{item.createTime} + + } + /> + + )} + /> + )} + + +
+
+ ); +}; + +export default BlockedCompaniesPage; diff --git a/src/pages/JobPortal/PersonalCenter/index.less b/src/pages/JobPortal/PersonalCenter/index.less index 63c906f..09a803e 100644 --- a/src/pages/JobPortal/PersonalCenter/index.less +++ b/src/pages/JobPortal/PersonalCenter/index.less @@ -97,6 +97,16 @@ } .stats-section { + .stats-row { + flex-wrap: nowrap; + justify-content: space-around; + } + + .stat-col { + flex: 1 1 0; + min-width: 0; + } + .stat-item { text-align: center; cursor: pointer; diff --git a/src/pages/JobPortal/PersonalCenter/index.tsx b/src/pages/JobPortal/PersonalCenter/index.tsx index d7006b1..35ea3ce 100644 --- a/src/pages/JobPortal/PersonalCenter/index.tsx +++ b/src/pages/JobPortal/PersonalCenter/index.tsx @@ -138,6 +138,7 @@ const PersonalCenter: React.FC = () => { footprints: 0, appointments: 0, complaints: 0, + blockedCompanies: 0, }); useEffect(() => { @@ -151,6 +152,7 @@ const PersonalCenter: React.FC = () => { footprints: Number(response.data.yzj) || 0, appointments: Number(response.data.yyy) || 0, complaints: Number(response.data.yts) || 0, + blockedCompanies: Number(response.data.ypbqys) || 0, }); } } catch (error) { @@ -269,8 +271,8 @@ const PersonalCenter: React.FC = () => {
- - + +
history.push('/job-portal/personal-center/applications')} @@ -279,7 +281,7 @@ const PersonalCenter: React.FC = () => {
投递
- +
history.push('/job-portal/personal-center/favorites')} @@ -288,7 +290,7 @@ const PersonalCenter: React.FC = () => {
收藏
- +
history.push('/job-portal/personal-center/footprints')} @@ -297,7 +299,7 @@ const PersonalCenter: React.FC = () => {
足迹
- +
history.push('/job-portal/personal-center/complaints')} @@ -306,6 +308,15 @@ const PersonalCenter: React.FC = () => {
投诉
+ +
history.push('/job-portal/personal-center/blocked-companies')} + > +
{statistics.blockedCompanies || 0}
+
屏蔽企业
+
+
diff --git a/src/services/jobportal/user.ts b/src/services/jobportal/user.ts index 5f4c442..5430411 100644 --- a/src/services/jobportal/user.ts +++ b/src/services/jobportal/user.ts @@ -182,3 +182,22 @@ export async function submitJobComplaint(params: { }); } +/** 屏蔽企业 POST /cms/blockCompany */ +export async function blockCompany(params: { userId: number; companyId: number }) { + return request('/api/cms/blockCompany', { + method: 'POST', + data: params, + }); +} + +/** 获取屏蔽企业列表 GET /cms/blockCompany/list */ +export async function getBlockedCompanyList(params?: { pageNum?: number; pageSize?: number }) { + return request('/api/cms/blockCompany/list', { + method: 'GET', + params: { + pageNum: params?.pageNum || 1, + pageSize: params?.pageSize || 10, + }, + }); +} + diff --git a/src/types/jobportal/user.d.ts b/src/types/jobportal/user.d.ts index 58d9e65..437c9a8 100644 --- a/src/types/jobportal/user.d.ts +++ b/src/types/jobportal/user.d.ts @@ -73,6 +73,7 @@ declare namespace API { yzj?: string; // 足迹 yyy?: string; // 预约 yts?: string; // 投诉 + ypbqys?: string; // 屏蔽企业数 } export interface AppUserYhscResult {