政策筛选功能开发
Some checks failed
Node CI / build (14.x, macOS-latest) (push) Has been cancelled
Node CI / build (14.x, ubuntu-latest) (push) Has been cancelled
Node CI / build (14.x, windows-latest) (push) Has been cancelled
Node CI / build (16.x, macOS-latest) (push) Has been cancelled
Node CI / build (16.x, ubuntu-latest) (push) Has been cancelled
Node CI / build (16.x, windows-latest) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
coverage CI / build (push) Has been cancelled
Node pnpm CI / build (16.x, macOS-latest) (push) Has been cancelled
Node pnpm CI / build (16.x, ubuntu-latest) (push) Has been cancelled
Node pnpm CI / build (16.x, windows-latest) (push) Has been cancelled
Some checks failed
Node CI / build (14.x, macOS-latest) (push) Has been cancelled
Node CI / build (14.x, ubuntu-latest) (push) Has been cancelled
Node CI / build (14.x, windows-latest) (push) Has been cancelled
Node CI / build (16.x, macOS-latest) (push) Has been cancelled
Node CI / build (16.x, ubuntu-latest) (push) Has been cancelled
Node CI / build (16.x, windows-latest) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
coverage CI / build (push) Has been cancelled
Node pnpm CI / build (16.x, macOS-latest) (push) Has been cancelled
Node pnpm CI / build (16.x, ubuntu-latest) (push) Has been cancelled
Node pnpm CI / build (16.x, windows-latest) (push) Has been cancelled
This commit is contained in:
@@ -51,8 +51,70 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.policy-tag-filter {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding-bottom: 16px;
|
||||||
|
border-bottom: 1px solid @jp-border;
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
flex-shrink: 0;
|
||||||
|
line-height: 22px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: @jp-text-secondary;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__tags {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__item {
|
||||||
|
cursor: pointer;
|
||||||
|
margin: 0;
|
||||||
|
border-radius: @jp-radius;
|
||||||
|
transition: all 0.2s;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: @jp-primary;
|
||||||
|
border-color: @jp-primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--active {
|
||||||
|
color: #fff !important;
|
||||||
|
background: @jp-primary !important;
|
||||||
|
border-color: @jp-primary !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--recommend {
|
||||||
|
color: #ec4827 !important;
|
||||||
|
background: #fff5f3 !important;
|
||||||
|
border-color: rgba(236, 72, 39, 0.25) !important;
|
||||||
|
font-weight: 500;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #ec4827 !important;
|
||||||
|
border-color: #ec4827 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.policy-tag-filter__item--active {
|
||||||
|
color: #fff !important;
|
||||||
|
background: #ec4827 !important;
|
||||||
|
border-color: #ec4827 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.policy-list {
|
.policy-list {
|
||||||
.policy-item {
|
.policy-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
padding: 20px 0;
|
padding: 20px 0;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-bottom: 1px solid @jp-border;
|
border-bottom: 1px solid @jp-border;
|
||||||
@@ -71,6 +133,38 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.policy-item-body {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.policy-download-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-left: 16px;
|
||||||
|
padding: 6px 14px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: @jp-primary;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid @jp-primary;
|
||||||
|
border-radius: @jp-radius;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
user-select: none;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #fff;
|
||||||
|
background: @jp-primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__text {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.policy-title {
|
.policy-title {
|
||||||
margin: 0 0 10px;
|
margin: 0 0 10px;
|
||||||
color: @jp-text-primary;
|
color: @jp-text-primary;
|
||||||
|
|||||||
@@ -14,10 +14,13 @@ import {
|
|||||||
FileTextOutlined,
|
FileTextOutlined,
|
||||||
SearchOutlined,
|
SearchOutlined,
|
||||||
BankOutlined,
|
BankOutlined,
|
||||||
|
DownloadOutlined,
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import { history } from '@umijs/max';
|
import { history } from '@umijs/max';
|
||||||
import JobPortalHeader from '@/components/JobPortalHeader';
|
import JobPortalHeader from '@/components/JobPortalHeader';
|
||||||
import { getPolicyInfoList } from '@/services/cms/policyInfo';
|
import { getPolicyInfoList } from '@/services/cms/policyInfo';
|
||||||
|
import { getDictValueEnum } from '@/services/system/dict';
|
||||||
|
import type { DictValueEnumObj } from '@/components/DictTag';
|
||||||
import './index.less';
|
import './index.less';
|
||||||
|
|
||||||
const { Title, Text } = Typography;
|
const { Title, Text } = Typography;
|
||||||
@@ -30,6 +33,43 @@ const PolicyListPage: React.FC = () => {
|
|||||||
const [pageSize] = useState(10);
|
const [pageSize] = useState(10);
|
||||||
const [searchValue, setSearchValue] = useState('');
|
const [searchValue, setSearchValue] = useState('');
|
||||||
const [keyword, setKeyword] = useState('');
|
const [keyword, setKeyword] = useState('');
|
||||||
|
const [selectedPolicyTag, setSelectedPolicyTag] = useState<string>('__recommended__');
|
||||||
|
const [userTypeEnum, setUserTypeEnum] = useState<DictValueEnumObj>({});
|
||||||
|
|
||||||
|
/** 从 localStorage 读取当前用户的 personnel type */
|
||||||
|
const currentUserType = (() => {
|
||||||
|
try {
|
||||||
|
const raw = localStorage.getItem('userInfo');
|
||||||
|
if (raw) {
|
||||||
|
const userInfo = JSON.parse(raw);
|
||||||
|
return userInfo?.userType ? String(userInfo.userType) : '';
|
||||||
|
}
|
||||||
|
} catch { /* ignore */ }
|
||||||
|
return '';
|
||||||
|
})();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getDictValueEnum('user_type', false, true).then((data) => {
|
||||||
|
setUserTypeEnum(data);
|
||||||
|
// 无用户类型时默认选中「全部」
|
||||||
|
if (!currentUserType) {
|
||||||
|
setSelectedPolicyTag('');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
/** 将字典枚举转为标签数组,按 value 排序 */
|
||||||
|
const userTypeTags = Object.values(userTypeEnum)
|
||||||
|
.map((item) => ({ label: item.label, value: item.value }))
|
||||||
|
.sort((a, b) => Number(a.value) - Number(b.value));
|
||||||
|
|
||||||
|
/** 将内部标记转为实际 API 参数 */
|
||||||
|
const resolvePolicyTag = (tag: string): string | undefined => {
|
||||||
|
if (tag === '__recommended__') {
|
||||||
|
return currentUserType || undefined;
|
||||||
|
}
|
||||||
|
return tag || undefined;
|
||||||
|
};
|
||||||
|
|
||||||
const fetchList = useCallback(async () => {
|
const fetchList = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
@@ -38,6 +78,7 @@ const PolicyListPage: React.FC = () => {
|
|||||||
pageNum,
|
pageNum,
|
||||||
pageSize,
|
pageSize,
|
||||||
searchValue: keyword || undefined,
|
searchValue: keyword || undefined,
|
||||||
|
policyTag: resolvePolicyTag(selectedPolicyTag),
|
||||||
});
|
});
|
||||||
if (response?.code === 200) {
|
if (response?.code === 200) {
|
||||||
setList(response.rows || []);
|
setList(response.rows || []);
|
||||||
@@ -51,7 +92,7 @@ const PolicyListPage: React.FC = () => {
|
|||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}, [pageNum, pageSize, keyword]);
|
}, [pageNum, pageSize, keyword, selectedPolicyTag]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchList();
|
fetchList();
|
||||||
@@ -62,10 +103,34 @@ const PolicyListPage: React.FC = () => {
|
|||||||
setKeyword(searchValue.trim());
|
setKeyword(searchValue.trim());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** 点击人员类型标签:选中/取消选中 */
|
||||||
|
const handleTagClick = (tagValue: string) => {
|
||||||
|
setPageNum(1);
|
||||||
|
if (selectedPolicyTag === tagValue) {
|
||||||
|
setSelectedPolicyTag('');
|
||||||
|
} else {
|
||||||
|
setSelectedPolicyTag(tagValue);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleItemClick = (item: API.PolicyInfo.PolicyInfoItem) => {
|
const handleItemClick = (item: API.PolicyInfo.PolicyInfoItem) => {
|
||||||
history.push(`/job-portal/policy/detail?id=${item.id}`);
|
history.push(`/job-portal/policy/detail?id=${item.id}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** 下载政策附件 */
|
||||||
|
const handleDownload = (e: React.MouseEvent, item: API.PolicyInfo.PolicyInfoItem) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (!item.fileUrl) return;
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = item.fileUrl;
|
||||||
|
link.download = item.fileName || '';
|
||||||
|
link.target = '_blank';
|
||||||
|
link.rel = 'noopener noreferrer';
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="policy-page">
|
<div className="policy-page">
|
||||||
<JobPortalHeader showSearch={false} showHotJobs={false} />
|
<JobPortalHeader showSearch={false} showHotJobs={false} />
|
||||||
@@ -95,6 +160,34 @@ const PolicyListPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Spin spinning={loading}>
|
<Spin spinning={loading}>
|
||||||
|
{userTypeTags.length > 0 && (
|
||||||
|
<div className="policy-tag-filter">
|
||||||
|
<span className="policy-tag-filter__label">人员类型:</span>
|
||||||
|
<div className="policy-tag-filter__tags">
|
||||||
|
{currentUserType && (
|
||||||
|
<Tag
|
||||||
|
className={`policy-tag-filter__item policy-tag-filter__item--recommend ${
|
||||||
|
selectedPolicyTag === '__recommended__' ? 'policy-tag-filter__item--active' : ''
|
||||||
|
}`}
|
||||||
|
onClick={() => handleTagClick('__recommended__')}
|
||||||
|
>
|
||||||
|
推荐
|
||||||
|
</Tag>
|
||||||
|
)}
|
||||||
|
{userTypeTags.map((tag) => (
|
||||||
|
<Tag
|
||||||
|
key={tag.value}
|
||||||
|
className={`policy-tag-filter__item ${
|
||||||
|
selectedPolicyTag === tag.value ? 'policy-tag-filter__item--active' : ''
|
||||||
|
}`}
|
||||||
|
onClick={() => handleTagClick(tag.value)}
|
||||||
|
>
|
||||||
|
{tag.label}
|
||||||
|
</Tag>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{list.length > 0 ? (
|
{list.length > 0 ? (
|
||||||
<>
|
<>
|
||||||
<List
|
<List
|
||||||
@@ -126,6 +219,16 @@ const PolicyListPage: React.FC = () => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{item.fileUrl && (
|
||||||
|
<span
|
||||||
|
className="policy-download-btn"
|
||||||
|
title={`下载 ${item.fileName || '附件'}`}
|
||||||
|
onClick={(e) => handleDownload(e, item)}
|
||||||
|
>
|
||||||
|
<DownloadOutlined />
|
||||||
|
<span className="policy-download-btn__text">下载</span>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</List.Item>
|
</List.Item>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
1
src/types/cms/policyInfo.d.ts
vendored
1
src/types/cms/policyInfo.d.ts
vendored
@@ -49,5 +49,6 @@ declare namespace API.PolicyInfo {
|
|||||||
pageNum?: number;
|
pageNum?: number;
|
||||||
pageSize?: number;
|
pageSize?: number;
|
||||||
searchValue?: string;
|
searchValue?: string;
|
||||||
|
policyTag?: string;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user