政策下载功能开发
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:
@@ -17,8 +17,8 @@ export default {
|
|||||||
// localhost:8000/api/** -> https://preview.pro.ant.design/api/**
|
// localhost:8000/api/** -> https://preview.pro.ant.design/api/**
|
||||||
'/api/': {
|
'/api/': {
|
||||||
// 要代理的地址
|
// 要代理的地址
|
||||||
target: localBackendUrl,
|
// target: localBackendUrl, // 本地环境
|
||||||
// target: 'http://39.98.44.136:6024/api/shihezi/', // 线上环境
|
target: 'http://39.98.44.136:6024/api/shihezi/', // 线上环境
|
||||||
// target: 'http://wykj.cdwsx.com/api',// 后端
|
// target: 'http://wykj.cdwsx.com/api',// 后端
|
||||||
// target: 'http://ks.zhaopinzao8dian.com/api/ks',
|
// target: 'http://ks.zhaopinzao8dian.com/api/ks',
|
||||||
// 配置了这个可以从 http 代理到 https
|
// 配置了这个可以从 http 代理到 https
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import {
|
|||||||
} 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 { getPortalPolicyList } from '@/services/cms/policyInfo';
|
import { getPortalPolicyList, exportPolicy } from '@/services/cms/policyInfo';
|
||||||
import { getDictValueEnum } from '@/services/system/dict';
|
import { getDictValueEnum } from '@/services/system/dict';
|
||||||
import type { DictValueEnumObj } from '@/components/DictTag';
|
import type { DictValueEnumObj } from '@/components/DictTag';
|
||||||
import './index.less';
|
import './index.less';
|
||||||
@@ -34,7 +34,9 @@ const PolicyListPage: React.FC = () => {
|
|||||||
const [searchValue, setSearchValue] = useState('');
|
const [searchValue, setSearchValue] = useState('');
|
||||||
const [keyword, setKeyword] = useState('');
|
const [keyword, setKeyword] = useState('');
|
||||||
const [selectedPolicyTag, setSelectedPolicyTag] = useState<string>('__recommended__');
|
const [selectedPolicyTag, setSelectedPolicyTag] = useState<string>('__recommended__');
|
||||||
|
const [selectedPolicyCategory, setSelectedPolicyCategory] = useState<string>('');
|
||||||
const [userTypeEnum, setUserTypeEnum] = useState<DictValueEnumObj>({});
|
const [userTypeEnum, setUserTypeEnum] = useState<DictValueEnumObj>({});
|
||||||
|
const [policyCategoryEnum, setPolicyCategoryEnum] = useState<DictValueEnumObj>({});
|
||||||
|
|
||||||
/** 从 localStorage 读取当前用户的 personnel type */
|
/** 从 localStorage 读取当前用户的 personnel type */
|
||||||
const currentUserType = (() => {
|
const currentUserType = (() => {
|
||||||
@@ -56,6 +58,9 @@ const PolicyListPage: React.FC = () => {
|
|||||||
setSelectedPolicyTag('');
|
setSelectedPolicyTag('');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
getDictValueEnum('policy_category', false, true).then((data) => {
|
||||||
|
setPolicyCategoryEnum(data);
|
||||||
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
/** 将字典枚举转为标签数组,按 value 排序 */
|
/** 将字典枚举转为标签数组,按 value 排序 */
|
||||||
@@ -63,6 +68,11 @@ const PolicyListPage: React.FC = () => {
|
|||||||
.map((item) => ({ label: item.label, value: item.value }))
|
.map((item) => ({ label: item.label, value: item.value }))
|
||||||
.sort((a, b) => Number(a.value) - Number(b.value));
|
.sort((a, b) => Number(a.value) - Number(b.value));
|
||||||
|
|
||||||
|
/** 将政策分类字典转为标签数组 */
|
||||||
|
const policyCategoryTags = Object.values(policyCategoryEnum)
|
||||||
|
.map((item) => ({ label: item.label, value: String(item.value) }))
|
||||||
|
.sort((a, b) => Number(a.value) - Number(b.value));
|
||||||
|
|
||||||
/** 将内部标记转为实际 API 参数 */
|
/** 将内部标记转为实际 API 参数 */
|
||||||
const resolvePolicyTag = (tag: string): string | undefined => {
|
const resolvePolicyTag = (tag: string): string | undefined => {
|
||||||
if (tag === '__recommended__') {
|
if (tag === '__recommended__') {
|
||||||
@@ -79,6 +89,7 @@ const PolicyListPage: React.FC = () => {
|
|||||||
pageSize,
|
pageSize,
|
||||||
searchValue: keyword || undefined,
|
searchValue: keyword || undefined,
|
||||||
policyTag: resolvePolicyTag(selectedPolicyTag),
|
policyTag: resolvePolicyTag(selectedPolicyTag),
|
||||||
|
policyCategory: selectedPolicyCategory || undefined,
|
||||||
});
|
});
|
||||||
if (response?.code === 200) {
|
if (response?.code === 200) {
|
||||||
setList(response.rows || []);
|
setList(response.rows || []);
|
||||||
@@ -92,7 +103,7 @@ const PolicyListPage: React.FC = () => {
|
|||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}, [pageNum, pageSize, keyword, selectedPolicyTag]);
|
}, [pageNum, pageSize, keyword, selectedPolicyTag, selectedPolicyCategory]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchList();
|
fetchList();
|
||||||
@@ -113,6 +124,16 @@ const PolicyListPage: React.FC = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** 点击政策分类标签:选中/取消选中 */
|
||||||
|
const handlePolicyCategoryClick = (tagValue: string) => {
|
||||||
|
setPageNum(1);
|
||||||
|
if (selectedPolicyCategory === tagValue) {
|
||||||
|
setSelectedPolicyCategory('');
|
||||||
|
} else {
|
||||||
|
setSelectedPolicyCategory(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}`);
|
||||||
};
|
};
|
||||||
@@ -131,6 +152,31 @@ const PolicyListPage: React.FC = () => {
|
|||||||
document.body.removeChild(link);
|
document.body.removeChild(link);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** 导出政策 */
|
||||||
|
const handleExport = async (e: React.MouseEvent, item: API.PolicyInfo.PolicyInfoItem) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
try {
|
||||||
|
const blob = await exportPolicy(item.id);
|
||||||
|
// 如果后端返回 JSON 错误(如 401),blob 可能仍是 JSON 格式
|
||||||
|
if (blob.type === 'application/json') {
|
||||||
|
const text = await blob.text();
|
||||||
|
const err = JSON.parse(text);
|
||||||
|
message.error(err.msg || '导出失败');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const url = window.URL.createObjectURL(blob);
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = url;
|
||||||
|
link.download = `${item.zcmc || '政策'}.docx`;
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
} catch (err: any) {
|
||||||
|
message.error(err?.message || '导出失败');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="policy-page">
|
<div className="policy-page">
|
||||||
<JobPortalHeader showSearch={false} showHotJobs={false} />
|
<JobPortalHeader showSearch={false} showHotJobs={false} />
|
||||||
@@ -188,6 +234,24 @@ const PolicyListPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{policyCategoryTags.length > 0 && (
|
||||||
|
<div className="policy-tag-filter">
|
||||||
|
<span className="policy-tag-filter__label">政策分类:</span>
|
||||||
|
<div className="policy-tag-filter__tags">
|
||||||
|
{policyCategoryTags.map((tag) => (
|
||||||
|
<Tag
|
||||||
|
key={tag.value}
|
||||||
|
className={`policy-tag-filter__item ${
|
||||||
|
selectedPolicyCategory === tag.value ? 'policy-tag-filter__item--active' : ''
|
||||||
|
}`}
|
||||||
|
onClick={() => handlePolicyCategoryClick(tag.value)}
|
||||||
|
>
|
||||||
|
{tag.label}
|
||||||
|
</Tag>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{list.length > 0 ? (
|
{list.length > 0 ? (
|
||||||
<>
|
<>
|
||||||
<List
|
<List
|
||||||
@@ -219,6 +283,14 @@ const PolicyListPage: React.FC = () => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<span
|
||||||
|
className="policy-download-btn policy-export-btn"
|
||||||
|
title="导出政策"
|
||||||
|
onClick={(e) => handleExport(e, item)}
|
||||||
|
>
|
||||||
|
<DownloadOutlined />
|
||||||
|
<span className="policy-download-btn__text">导出</span>
|
||||||
|
</span>
|
||||||
{item.fileUrl && (
|
{item.fileUrl && (
|
||||||
<span
|
<span
|
||||||
className="policy-download-btn"
|
className="policy-download-btn"
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ const DetailModal: React.FC<DetailModalProps> = ({ open, data, onCancel }) => {
|
|||||||
<Descriptions column={2} bordered size="small">
|
<Descriptions column={2} bordered size="small">
|
||||||
<Descriptions.Item label="政策名称" span={2}>{data?.zcmc}</Descriptions.Item>
|
<Descriptions.Item label="政策名称" span={2}>{data?.zcmc}</Descriptions.Item>
|
||||||
<Descriptions.Item label="政策类型">{data?.zclx}</Descriptions.Item>
|
<Descriptions.Item label="政策类型">{data?.zclx}</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="政策分类">{data?.policyCategory || '-'}</Descriptions.Item>
|
||||||
<Descriptions.Item label="政策级别">{data?.zcLevel}</Descriptions.Item>
|
<Descriptions.Item label="政策级别">{data?.zcLevel}</Descriptions.Item>
|
||||||
<Descriptions.Item label="发文单位">{data?.sourceUnit}</Descriptions.Item>
|
<Descriptions.Item label="发文单位">{data?.sourceUnit}</Descriptions.Item>
|
||||||
<Descriptions.Item label="受理单位">{data?.acceptUnit}</Descriptions.Item>
|
<Descriptions.Item label="受理单位">{data?.acceptUnit}</Descriptions.Item>
|
||||||
|
|||||||
@@ -23,11 +23,15 @@ const EditModal: React.FC<EditModalProps> = ({ open, values, onCancel, onSubmit
|
|||||||
const [fileList, setFileList] = useState<UploadFile[]>([]);
|
const [fileList, setFileList] = useState<UploadFile[]>([]);
|
||||||
const [uploading, setUploading] = useState(false);
|
const [uploading, setUploading] = useState(false);
|
||||||
const [userTypeEnum, setUserTypeEnum] = useState<DictValueEnumObj>({});
|
const [userTypeEnum, setUserTypeEnum] = useState<DictValueEnumObj>({});
|
||||||
|
const [policyCategoryEnum, setPolicyCategoryEnum] = useState<DictValueEnumObj>({});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getDictValueEnum('user_type', false, true).then((data) => {
|
getDictValueEnum('user_type', false, true).then((data) => {
|
||||||
setUserTypeEnum(data);
|
setUserTypeEnum(data);
|
||||||
});
|
});
|
||||||
|
getDictValueEnum('policy_category', false, true).then((data) => {
|
||||||
|
setPolicyCategoryEnum(data);
|
||||||
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -59,6 +63,13 @@ const EditModal: React.FC<EditModalProps> = ({ open, values, onCancel, onSubmit
|
|||||||
}
|
}
|
||||||
}, [open, values, form]);
|
}, [open, values, form]);
|
||||||
|
|
||||||
|
// 字典选项加载完成后,重新回填 policyCategory 确保 Select 正确显示
|
||||||
|
useEffect(() => {
|
||||||
|
if (open && values?.policyCategory && Object.keys(policyCategoryEnum).length > 0) {
|
||||||
|
form.setFieldsValue({ policyCategory: values.policyCategory });
|
||||||
|
}
|
||||||
|
}, [open, policyCategoryEnum, values, form]);
|
||||||
|
|
||||||
const handleUpload = async (file: File) => {
|
const handleUpload = async (file: File) => {
|
||||||
setUploading(true);
|
setUploading(true);
|
||||||
try {
|
try {
|
||||||
@@ -113,6 +124,11 @@ const EditModal: React.FC<EditModalProps> = ({ open, values, onCancel, onSubmit
|
|||||||
value: item.value,
|
value: item.value,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const policyCategoryOptions = Object.values(policyCategoryEnum).map((item) => ({
|
||||||
|
label: item.label,
|
||||||
|
value: item.value,
|
||||||
|
}));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
title={values ? '编辑政策' : '新增政策'}
|
title={values ? '编辑政策' : '新增政策'}
|
||||||
@@ -139,6 +155,15 @@ const EditModal: React.FC<EditModalProps> = ({ open, values, onCancel, onSubmit
|
|||||||
<Input placeholder="请输入政策级别" />
|
<Input placeholder="请输入政策级别" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
|
<Col span={12}>
|
||||||
|
<Form.Item name="policyCategory" label="政策分类">
|
||||||
|
<Select
|
||||||
|
allowClear
|
||||||
|
placeholder="请选择政策分类"
|
||||||
|
options={policyCategoryOptions}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
<Col span={12}>
|
<Col span={12}>
|
||||||
<Form.Item name="policyTag" label="人员类型">
|
<Form.Item name="policyTag" label="人员类型">
|
||||||
<Select
|
<Select
|
||||||
|
|||||||
@@ -24,11 +24,15 @@ const PolicyMgmt: React.FC = () => {
|
|||||||
const [currentRow, setCurrentRow] = useState<API.PolicyInfo.PolicyInfoItem>();
|
const [currentRow, setCurrentRow] = useState<API.PolicyInfo.PolicyInfoItem>();
|
||||||
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
|
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
|
||||||
const [userTypeEnum, setUserTypeEnum] = useState<DictValueEnumObj>({});
|
const [userTypeEnum, setUserTypeEnum] = useState<DictValueEnumObj>({});
|
||||||
|
const [policyCategoryEnum, setPolicyCategoryEnum] = useState<DictValueEnumObj>({});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getDictValueEnum('user_type', false, true).then((data) => {
|
getDictValueEnum('user_type', false, true).then((data) => {
|
||||||
setUserTypeEnum(data);
|
setUserTypeEnum(data);
|
||||||
});
|
});
|
||||||
|
getDictValueEnum('policy_category', false, true).then((data) => {
|
||||||
|
setPolicyCategoryEnum(data);
|
||||||
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleDelete = async (ids: string) => {
|
const handleDelete = async (ids: string) => {
|
||||||
@@ -103,6 +107,16 @@ const PolicyMgmt: React.FC = () => {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '政策分类',
|
||||||
|
dataIndex: 'policyCategory',
|
||||||
|
hideInSearch: true,
|
||||||
|
width: 120,
|
||||||
|
render: (_, record) => {
|
||||||
|
if (!record.policyCategory) return '-';
|
||||||
|
return <DictTag enums={policyCategoryEnum} value={record.policyCategory} />;
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '发文单位',
|
title: '发文单位',
|
||||||
dataIndex: 'sourceUnit',
|
dataIndex: 'sourceUnit',
|
||||||
|
|||||||
@@ -71,3 +71,12 @@ export async function uploadPolicyFile(file: File) {
|
|||||||
data: formData,
|
data: formData,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 导出/下载政策
|
||||||
|
export async function exportPolicy(id: number | string) {
|
||||||
|
return request('/api/cms/policyInfo/exportUtil', {
|
||||||
|
method: 'GET',
|
||||||
|
params: { id },
|
||||||
|
responseType: 'blob',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
2
src/types/cms/policyInfo.d.ts
vendored
2
src/types/cms/policyInfo.d.ts
vendored
@@ -25,6 +25,7 @@ declare namespace API.PolicyInfo {
|
|||||||
export interface PolicyInfoItem {
|
export interface PolicyInfoItem {
|
||||||
id: number;
|
id: number;
|
||||||
policyTag?: string;
|
policyTag?: string;
|
||||||
|
policyCategory?: string;
|
||||||
zcmc: string;
|
zcmc: string;
|
||||||
zclx: string;
|
zclx: string;
|
||||||
zcLevel: string;
|
zcLevel: string;
|
||||||
@@ -51,5 +52,6 @@ declare namespace API.PolicyInfo {
|
|||||||
pageSize?: number;
|
pageSize?: number;
|
||||||
searchValue?: string;
|
searchValue?: string;
|
||||||
policyTag?: string;
|
policyTag?: string;
|
||||||
|
policyCategory?: string;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user