bug修护
parent
4950605bff
commit
862b434777
|
|
@ -10,7 +10,7 @@ module.exports = {
|
||||||
javaGitBranch: "<branch-name>",
|
javaGitBranch: "<branch-name>",
|
||||||
// 本地联调 safetyEval-service(context-path: /safetyEval,默认端口 8095)
|
// 本地联调 safetyEval-service(context-path: /safetyEval,默认端口 8095)
|
||||||
// 可通过环境变量覆盖: SAFETY_EVAL_API_HOST=http://192.168.x.x:8095
|
// 可通过环境变量覆盖: SAFETY_EVAL_API_HOST=http://192.168.x.x:8095
|
||||||
API_HOST: "http://192.168.0.152",
|
API_HOST: "http://127.0.0.1",
|
||||||
},
|
},
|
||||||
production: {
|
production: {
|
||||||
// 应用后端分支名称,部署上线需要
|
// 应用后端分支名称,部署上线需要
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -16,7 +16,30 @@ function parseAttachmentUrls(urls, defaultName = "附件.pdf") {
|
||||||
if (!urls) {
|
if (!urls) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
return String(urls)
|
const raw = String(urls).trim();
|
||||||
|
|
||||||
|
// JSON 数组字符串:如 '[{"url":"...","uid":"...","name":"...","status":"done"}]'
|
||||||
|
if (raw.startsWith("[") && raw.endsWith("]")) {
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(raw);
|
||||||
|
if (Array.isArray(parsed)) {
|
||||||
|
return parsed
|
||||||
|
.filter((item) => item?.url)
|
||||||
|
.map((item) => ({
|
||||||
|
name: item.name || `${defaultName.replace(".pdf", "")}1`,
|
||||||
|
fileName: item.fileName || item.name || `${defaultName.replace(".pdf", "")}1`,
|
||||||
|
url: item.url,
|
||||||
|
uid: item.uid || undefined,
|
||||||
|
status: item.status || "done",
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
} catch (_) {
|
||||||
|
// JSON 解析失败,回退到逗号分隔处理
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 旧格式:逗号分隔的 URL 字符串
|
||||||
|
return raw
|
||||||
.split(",")
|
.split(",")
|
||||||
.map((url) => url.trim())
|
.map((url) => url.trim())
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { declareRequest } from "@cqsjjb/jjb-dva-runtime";
|
import { declareRequest } from "@cqsjjb/jjb-dva-runtime";
|
||||||
|
import { apiPostDelete, safeAction } from "../enterpriseInfo/http";
|
||||||
|
|
||||||
export const staffChangeLogStaffList = declareRequest(
|
export const staffChangeLogStaffList = declareRequest(
|
||||||
"staffChangeLogLoading",
|
"staffChangeLogLoading",
|
||||||
|
|
@ -14,7 +15,7 @@ export const staffChangeLogList = declareRequest(
|
||||||
|
|
||||||
export const staffChangeLogRemove = declareRequest(
|
export const staffChangeLogRemove = declareRequest(
|
||||||
"staffChangeLogLoading",
|
"staffChangeLogLoading",
|
||||||
"Post > @/safetyEval/org-personnel-change/delete",
|
safeAction(async ({ id }) => apiPostDelete("/safetyEval/org-personnel-change/delete", id)),
|
||||||
);
|
);
|
||||||
|
|
||||||
export const staffResignationAudit = declareRequest(
|
export const staffResignationAudit = declareRequest(
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { declareRequest } from "@cqsjjb/jjb-dva-runtime";
|
import { declareRequest } from "@cqsjjb/jjb-dva-runtime";
|
||||||
|
import { apiPost, apiPostDelete, safeAction } from "../enterpriseInfo/http";
|
||||||
|
|
||||||
export const staffInfoList = declareRequest(
|
export const staffInfoList = declareRequest(
|
||||||
"staffInfoLoading",
|
"staffInfoLoading",
|
||||||
|
|
@ -24,10 +25,13 @@ export const staffInfoEdit = declareRequest(
|
||||||
|
|
||||||
export const staffInfoRemove = declareRequest(
|
export const staffInfoRemove = declareRequest(
|
||||||
"staffInfoLoading",
|
"staffInfoLoading",
|
||||||
"Post > @/safetyEval/org-personnel/delete",
|
safeAction(async ({ id }) => apiPostDelete("/safetyEval/org-personnel/delete", id)),
|
||||||
);
|
);
|
||||||
|
|
||||||
export const staffInfoResetPassword = declareRequest(
|
export const staffInfoResetPassword = declareRequest(
|
||||||
"staffInfoLoading",
|
"staffInfoLoading",
|
||||||
"Get > /safetyEval/org-personnel/reset-password",
|
safeAction(async ({ id }) => {
|
||||||
|
const url = `/safetyEval/org-personnel/reset-password?id=${encodeURIComponent(id)}`;
|
||||||
|
return apiPost(url, {});
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
|
@ -1,18 +1,24 @@
|
||||||
import { Image, Modal } from "antd";
|
import { Image, Modal } from "antd";
|
||||||
|
|
||||||
|
function resolveUrl(raw) {
|
||||||
|
if (!raw) return "";
|
||||||
|
const u = String(raw);
|
||||||
|
if (/^https?:\/\//i.test(u)) return u;
|
||||||
|
const base = window.fileUrl || "";
|
||||||
|
return base ? `${base}${u}` : u;
|
||||||
|
}
|
||||||
|
|
||||||
export function getFilePreviewType(url = "", fileName = "") {
|
export function getFilePreviewType(url = "", fileName = "") {
|
||||||
const hint = `${fileName || ""} ${url || ""}`.toLowerCase();
|
const hint = `${fileName || ""} ${url || ""}`.toLowerCase();
|
||||||
if (/\.(jpe?g|png|gif|webp|bmp)(\?|#|$)/i.test(hint)) {
|
if (/\.(jpe?g|png|gif|webp|bmp)(\?|#|$)/i.test(hint)) {
|
||||||
return "image";
|
return "image";
|
||||||
}
|
}
|
||||||
if (/\.pdf(\?|#|$)/i.test(hint)) {
|
return "other";
|
||||||
return "pdf";
|
|
||||||
}
|
|
||||||
return "unknown";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FilePreviewContent({ url, fileName, style }) {
|
export function FilePreviewContent({ url, fileName, style }) {
|
||||||
if (!url) {
|
const resolved = resolveUrl(url);
|
||||||
|
if (!resolved) {
|
||||||
return (
|
return (
|
||||||
<div style={{
|
<div style={{
|
||||||
minHeight: 120,
|
minHeight: 120,
|
||||||
|
|
@ -29,24 +35,16 @@ export function FilePreviewContent({ url, fileName, style }) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const type = getFilePreviewType(url, fileName);
|
const type = getFilePreviewType(resolved, fileName);
|
||||||
if (type === "image") {
|
if (type === "image") {
|
||||||
return <Image src={url} alt={fileName || "预览"} style={{ maxHeight: 480, ...style }} />;
|
return <Image src={resolved} alt={fileName || "预览"} style={{ maxHeight: 480, ...style }} />;
|
||||||
}
|
|
||||||
if (type === "pdf") {
|
|
||||||
return (
|
|
||||||
<iframe
|
|
||||||
title={fileName || "PDF 预览"}
|
|
||||||
src={url}
|
|
||||||
style={{ width: "100%", height: 480, border: "none", ...style }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{
|
<div style={{
|
||||||
minHeight: 120,
|
minHeight: 120,
|
||||||
display: "flex",
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
background: "#fafafa",
|
background: "#fafafa",
|
||||||
|
|
@ -54,7 +52,8 @@ export function FilePreviewContent({ url, fileName, style }) {
|
||||||
...style,
|
...style,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
暂不支持在线预览,请使用「新窗口打开」
|
<span style={{ marginBottom: 8 }}>此文件类型不支持内嵌预览</span>
|
||||||
|
<span>请点击下方「新窗口打开」按钮在浏览器中查看</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -67,6 +66,9 @@ export default function FilePreviewModal({
|
||||||
width = 720,
|
width = 720,
|
||||||
onCancel,
|
onCancel,
|
||||||
}) {
|
}) {
|
||||||
|
const resolved = resolveUrl(url);
|
||||||
|
const isImageFile = resolved && /\.(jpe?g|png|gif|webp|bmp)(\?|#|$)/i.test(resolved.toLowerCase());
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
open={open}
|
open={open}
|
||||||
|
|
@ -74,11 +76,11 @@ export default function FilePreviewModal({
|
||||||
title={title}
|
title={title}
|
||||||
width={width}
|
width={width}
|
||||||
cancelText="关闭"
|
cancelText="关闭"
|
||||||
okText={url ? "新窗口打开" : "关闭"}
|
okText={resolved ? (isImageFile ? "新窗口打开" : "重新打开") : "关闭"}
|
||||||
onCancel={onCancel}
|
onCancel={onCancel}
|
||||||
onOk={() => {
|
onOk={() => {
|
||||||
if (url) {
|
if (resolved) {
|
||||||
window.open(url, "_blank");
|
window.open(resolved, "_blank");
|
||||||
}
|
}
|
||||||
onCancel?.();
|
onCancel?.();
|
||||||
}}
|
}}
|
||||||
|
|
@ -98,7 +100,7 @@ export default function FilePreviewModal({
|
||||||
{fileName}
|
{fileName}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<FilePreviewContent url={url} fileName={fileName} />
|
<FilePreviewContent url={resolved} fileName={fileName} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
|
||||||
|
|
@ -4,20 +4,29 @@ import { Button, Image } from "antd";
|
||||||
const isImage = (url) =>
|
const isImage = (url) =>
|
||||||
/\.(png|jpe?g|gif|bmp|webp|svg)(\?.*)?$/i.test(url || "");
|
/\.(png|jpe?g|gif|bmp|webp|svg)(\?.*)?$/i.test(url || "");
|
||||||
|
|
||||||
|
function resolveUrl(raw) {
|
||||||
|
if (!raw) return "";
|
||||||
|
const u = String(raw);
|
||||||
|
if (/^https?:\/\//i.test(u)) return u;
|
||||||
|
const base = window.fileUrl || "";
|
||||||
|
return base ? `${base}${u}` : u;
|
||||||
|
}
|
||||||
|
|
||||||
export default function PreviewUrlButton({ url, children = "预览", ...btnProps }) {
|
export default function PreviewUrlButton({ url, children = "预览", ...btnProps }) {
|
||||||
const [previewImage, setPreviewImage] = useState("");
|
const [previewImage, setPreviewImage] = useState("");
|
||||||
|
const resolved = resolveUrl(url);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
type="link"
|
type="link"
|
||||||
size="small"
|
size="small"
|
||||||
disabled={!url}
|
disabled={!resolved}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (isImage(url)) {
|
if (isImage(resolved)) {
|
||||||
setPreviewImage(url);
|
setPreviewImage(resolved);
|
||||||
} else {
|
} else {
|
||||||
window.open(url);
|
window.open(resolved, "_blank");
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
{...btnProps}
|
{...btnProps}
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,12 @@ function PersonnelChangePage(props) {
|
||||||
okText: "是",
|
okText: "是",
|
||||||
cancelText: "否",
|
cancelText: "否",
|
||||||
onOk: async () => {
|
onOk: async () => {
|
||||||
const res = await props.staffInfoRemove?.({ id: record.staffId || record.id });
|
const id = record.id ?? record.staffId;
|
||||||
|
if (!id) {
|
||||||
|
message.error("缺少人员ID,无法删除");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const res = await props.staffInfoRemove?.({ id });
|
||||||
if (res?.success !== false) {
|
if (res?.success !== false) {
|
||||||
message.success("删除成功");
|
message.success("删除成功");
|
||||||
handleSearch();
|
handleSearch();
|
||||||
|
|
@ -97,6 +102,10 @@ function PersonnelChangePage(props) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const openResignView = async (record) => {
|
const openResignView = async (record) => {
|
||||||
|
if (!record?.resignApplyId) {
|
||||||
|
message.warning("该人员暂无离职申请记录");
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const res = await props.staffResignationInfo({ id: record.resignApplyId });
|
const res = await props.staffResignationInfo({ id: record.resignApplyId });
|
||||||
setResignInfo(res?.data || {});
|
setResignInfo(res?.data || {});
|
||||||
|
|
@ -183,7 +192,7 @@ function PersonnelChangePage(props) {
|
||||||
render: (_, record) => (
|
render: (_, record) => (
|
||||||
<TableAction>
|
<TableAction>
|
||||||
<Button type="link" size="small" onClick={() => openResignView(record)}>
|
<Button type="link" size="small" onClick={() => openResignView(record)}>
|
||||||
查看
|
查看离职状态
|
||||||
</Button>
|
</Button>
|
||||||
{record.resignApplyId && Number(record.resignAuditStatus) === 0 && (
|
{record.resignApplyId && Number(record.resignAuditStatus) === 0 && (
|
||||||
<Button type="link" size="small" onClick={() => openResignAudit(record)}>
|
<Button type="link" size="small" onClick={() => openResignAudit(record)}>
|
||||||
|
|
|
||||||
|
|
@ -86,12 +86,12 @@ function StaffCertificatePage(props) {
|
||||||
<ControlWrapper.Input label="证书作业类别名称" placeholder="请输入证书作业类别名称" allowClear />
|
<ControlWrapper.Input label="证书作业类别名称" placeholder="请输入证书作业类别名称" allowClear />
|
||||||
</Form.Item>,
|
</Form.Item>,
|
||||||
]}
|
]}
|
||||||
onReset={() => {
|
onReset={(values) => {
|
||||||
router.query = { ...router.query, current: 1, size: 10 };
|
router.query = { ...values, current: 1, size: 10 };
|
||||||
handleSearch();
|
handleSearch();
|
||||||
}}
|
}}
|
||||||
onFinish={() => {
|
onFinish={(values) => {
|
||||||
router.query = { ...router.query, current: 1, size: 10 };
|
router.query = { ...values, current: 1, size: 10 };
|
||||||
handleSearch();
|
handleSearch();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { Connect } from "@cqsjjb/jjb-dva-runtime";
|
import { Connect } from "@cqsjjb/jjb-dva-runtime";
|
||||||
import { Button, DatePicker, Form, Input, message, Modal, Row, Col, Select, Table, Upload } from "antd";
|
import { Button, DatePicker, Form, Image, Input, message, Modal, Row, Col, Select, Table, Upload } from "antd";
|
||||||
import { UploadOutlined } from "@ant-design/icons";
|
import { UploadOutlined } from "@ant-design/icons";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
|
|
@ -250,10 +250,13 @@ function StaffFormModal({
|
||||||
const [deptPositionOptions, setDeptPositionOptions] = useState([]);
|
const [deptPositionOptions, setDeptPositionOptions] = useState([]);
|
||||||
const [positionLoading, setPositionLoading] = useState(false);
|
const [positionLoading, setPositionLoading] = useState(false);
|
||||||
const [uploadFileList, setUploadFileList] = useState([]);
|
const [uploadFileList, setUploadFileList] = useState([]);
|
||||||
|
const [previewImage, setPreviewImage] = useState("");
|
||||||
const watchedDeptId = Form.useWatch("deptId", form);
|
const watchedDeptId = Form.useWatch("deptId", form);
|
||||||
const wasOpenRef = useRef(false);
|
const wasOpenRef = useRef(false);
|
||||||
const inflightDeptRef = useRef(null);
|
const inflightDeptRef = useRef(null);
|
||||||
|
|
||||||
|
const isImage = (url) => /\.(png|jpe?g|gif|bmp|webp|svg)(\?.*)?$/i.test(url || "");
|
||||||
|
|
||||||
const loadPositionsByDept = async (deptId) => {
|
const loadPositionsByDept = async (deptId) => {
|
||||||
const id = deptId;
|
const id = deptId;
|
||||||
if (!id) {
|
if (!id) {
|
||||||
|
|
@ -287,6 +290,7 @@ function StaffFormModal({
|
||||||
form.resetFields();
|
form.resetFields();
|
||||||
setDeptPositionOptions([]);
|
setDeptPositionOptions([]);
|
||||||
setUploadFileList([]);
|
setUploadFileList([]);
|
||||||
|
setPreviewImage("");
|
||||||
form.setFieldsValue({
|
form.setFieldsValue({
|
||||||
registerEngineerFlag: 2,
|
registerEngineerFlag: 2,
|
||||||
});
|
});
|
||||||
|
|
@ -295,6 +299,7 @@ function StaffFormModal({
|
||||||
if (!open) {
|
if (!open) {
|
||||||
setDeptPositionOptions([]);
|
setDeptPositionOptions([]);
|
||||||
setUploadFileList([]);
|
setUploadFileList([]);
|
||||||
|
setPreviewImage("");
|
||||||
}
|
}
|
||||||
wasOpenRef.current = open;
|
wasOpenRef.current = open;
|
||||||
}, [open, currentId, form]);
|
}, [open, currentId, form]);
|
||||||
|
|
@ -333,6 +338,7 @@ function StaffFormModal({
|
||||||
form.resetFields();
|
form.resetFields();
|
||||||
setDeptPositionOptions([]);
|
setDeptPositionOptions([]);
|
||||||
setUploadFileList([]);
|
setUploadFileList([]);
|
||||||
|
setPreviewImage("");
|
||||||
onCancel();
|
onCancel();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -364,7 +370,7 @@ function StaffFormModal({
|
||||||
educationLevelCode: values.educationLevelName,
|
educationLevelCode: values.educationLevelName,
|
||||||
educationLevelName: values.educationLevelName,
|
educationLevelName: values.educationLevelName,
|
||||||
proofMaterialUrl: Array.isArray(values.proofMaterialUrl)
|
proofMaterialUrl: Array.isArray(values.proofMaterialUrl)
|
||||||
? values.proofMaterialUrl.map((f) => f.url || f.response?.url).filter(Boolean).join(",")
|
? values.proofMaterialUrl.map((f) => f.url || f.response?.data?.url).filter(Boolean).join(",")
|
||||||
: "",
|
: "",
|
||||||
};
|
};
|
||||||
if (payload.birthDate && typeof payload.birthDate !== "string") {
|
if (payload.birthDate && typeof payload.birthDate !== "string") {
|
||||||
|
|
@ -507,21 +513,46 @@ function StaffFormModal({
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<Form.Item name="proofMaterialUrl" label="申报专业能力证明材料" valuePropName="fileList" getValueFromEvent={(e) => {
|
<Form.Item
|
||||||
|
name="proofMaterialUrl"
|
||||||
|
label="申报专业能力证明材料"
|
||||||
|
valuePropName="fileList"
|
||||||
|
getValueFromEvent={(e) => {
|
||||||
if (Array.isArray(e)) return e;
|
if (Array.isArray(e)) return e;
|
||||||
return e?.fileList;
|
return e?.fileList;
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
<Upload
|
<Upload
|
||||||
action="/api/upload"
|
action={`${window.process.env.app.API_HOST}/safetyEval/file/upload`}
|
||||||
|
headers={{ token: sessionStorage.getItem("token") }}
|
||||||
maxCount={5}
|
maxCount={5}
|
||||||
fileList={uploadFileList}
|
fileList={uploadFileList}
|
||||||
onChange={(info) => {
|
onChange={(info) => {
|
||||||
setUploadFileList(info.fileList);
|
setUploadFileList(info.fileList);
|
||||||
}}
|
}}
|
||||||
|
onPreview={(file) => {
|
||||||
|
const src = file.url || file.response?.data?.url || file.thumbUrl;
|
||||||
|
if (!src) return;
|
||||||
|
if (isImage(src)) {
|
||||||
|
setPreviewImage(src);
|
||||||
|
} else {
|
||||||
|
window.open(src, "_blank");
|
||||||
|
}
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Button icon={<UploadOutlined />}>上传文件</Button>
|
<Button icon={<UploadOutlined />}>上传文件</Button>
|
||||||
</Upload>
|
</Upload>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
<Image
|
||||||
|
style={{ display: "none" }}
|
||||||
|
preview={{
|
||||||
|
visible: !!previewImage,
|
||||||
|
src: previewImage,
|
||||||
|
onVisibleChange: (visible) => {
|
||||||
|
if (!visible) setPreviewImage("");
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={12}>
|
<Col span={12}>
|
||||||
<Form.Item name="currentAddress" label="现住地址">
|
<Form.Item name="currentAddress" label="现住地址">
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import {
|
||||||
} from "antd";
|
} from "antd";
|
||||||
import TableAction from "@cqsjjb/jjb-react-admin-component/TableAction";
|
import TableAction from "@cqsjjb/jjb-react-admin-component/TableAction";
|
||||||
import { UploadOutlined } from "@ant-design/icons";
|
import { UploadOutlined } from "@ant-design/icons";
|
||||||
|
import PreviewUrlButton from "~/components/PreviewUrlButton";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
|
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
|
||||||
import SearchForm from "@cqsjjb/jjb-react-admin-component/SearchForm";
|
import SearchForm from "@cqsjjb/jjb-react-admin-component/SearchForm";
|
||||||
|
|
@ -297,9 +298,17 @@ function AddModal({ open, staffOptions, requestAdd, onCancel, onSuccess }) {
|
||||||
>
|
>
|
||||||
<Upload
|
<Upload
|
||||||
action={`${window.process.env.app.API_HOST}/safetyEval/file/upload`}
|
action={`${window.process.env.app.API_HOST}/safetyEval/file/upload`}
|
||||||
showUploadList={false}
|
|
||||||
maxCount={1}
|
maxCount={1}
|
||||||
accept=".pdf"
|
accept=".pdf"
|
||||||
|
showUploadList={{
|
||||||
|
showPreviewIcon: true,
|
||||||
|
showRemoveIcon: true,
|
||||||
|
showDownloadIcon: false,
|
||||||
|
}}
|
||||||
|
onPreview={() => {
|
||||||
|
const url = form.getFieldValue("reportFileUrl");
|
||||||
|
if (url) window.open(url);
|
||||||
|
}}
|
||||||
onChange={(info) => {
|
onChange={(info) => {
|
||||||
if (info.file.status === "uploading") {
|
if (info.file.status === "uploading") {
|
||||||
setUploading(true);
|
setUploading(true);
|
||||||
|
|
@ -308,11 +317,19 @@ function AddModal({ open, staffOptions, requestAdd, onCancel, onSuccess }) {
|
||||||
if (info.file.status === "done") {
|
if (info.file.status === "done") {
|
||||||
const data = info.file.response?.data;
|
const data = info.file.response?.data;
|
||||||
if (data) {
|
if (data) {
|
||||||
form.setFieldsValue({ reportFileUrl: data.url || data });
|
const url = data.url || data;
|
||||||
}
|
form.setFieldsValue({ reportFileUrl: url });
|
||||||
|
} else {
|
||||||
|
message.error("上传失败,未获取到文件地址");
|
||||||
|
}
|
||||||
|
} else if (info.file.status === "error") {
|
||||||
|
message.error(`${info.file.name} 上传失败`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
onRemove={() => {
|
||||||
|
form.setFieldsValue({ reportFileUrl: "" });
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Button loading={uploading} icon={<UploadOutlined />}>
|
<Button loading={uploading} icon={<UploadOutlined />}>
|
||||||
上传文件
|
上传文件
|
||||||
|
|
@ -371,7 +388,13 @@ function ViewModal({ open, currentId, requestDetail, onCancel }) {
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
<Descriptions.Item label="备注">{info.remark}</Descriptions.Item>
|
<Descriptions.Item label="备注">{info.remark}</Descriptions.Item>
|
||||||
<Descriptions.Item label="离职通知报告">
|
<Descriptions.Item label="离职通知报告">
|
||||||
{info.reportFileUrl || "-"}
|
{info.reportFileUrl ? (
|
||||||
|
<PreviewUrlButton url={info.reportFileUrl}>
|
||||||
|
查看文件
|
||||||
|
</PreviewUrlButton>
|
||||||
|
) : (
|
||||||
|
"-"
|
||||||
|
)}
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
</Descriptions>
|
</Descriptions>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
|
||||||
|
|
@ -30,15 +30,20 @@ const menuItems = [
|
||||||
icon: <DashboardOutlined />,
|
icon: <DashboardOutlined />,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
key: "/safetyEval/container/Supervision/Dashboard",
|
key: "/safetyEval/container/Institution/Dashboard",
|
||||||
label: "机构端首页",
|
label: "机构端首页",
|
||||||
icon: <DashboardOutlined />,
|
icon: <DashboardOutlined />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "/safetyEval/container/Supervision/Cockpit",
|
key: "/safetyEval/container/Supervision/Dashboard",
|
||||||
label: "监管端首页",
|
label: "监管端首页",
|
||||||
icon: <PieChartOutlined />,
|
icon: <PieChartOutlined />,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: "/safetyEval/container/Supervision/Cockpit",
|
||||||
|
label: "监管端驾驶舱",
|
||||||
|
icon: <PieChartOutlined />,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: "/safetyEval/container/Supervision/BasicInfo",
|
key: "/safetyEval/container/Supervision/BasicInfo",
|
||||||
label: "基础信息管理",
|
label: "基础信息管理",
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,18 @@ function parseQueryId(location) {
|
||||||
return new URLSearchParams(search).get("id");
|
return new URLSearchParams(search).get("id");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resolvePreviewUrl(raw) {
|
||||||
|
if (!raw) return "";
|
||||||
|
const u = String(raw);
|
||||||
|
if (/^https?:\/\//i.test(u)) return u;
|
||||||
|
const base = window.fileUrl || "";
|
||||||
|
return base ? `${base}${u}` : u;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isImageUrl(url) {
|
||||||
|
return /\.(jpe?g|png|gif|webp|bmp)(\?|#|$)/i.test(String(url || "").toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
function MaterialsTab({ materialGroups, loading, onPreview }) {
|
function MaterialsTab({ materialGroups, loading, onPreview }) {
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return <Spin />;
|
return <Spin />;
|
||||||
|
|
@ -224,7 +236,18 @@ function RegisteredOrgDetailPage(props) {
|
||||||
<MaterialsTab
|
<MaterialsTab
|
||||||
materialGroups={materialGroups}
|
materialGroups={materialGroups}
|
||||||
loading={extrasLoading}
|
loading={extrasLoading}
|
||||||
onPreview={setFilePreview}
|
onPreview={(record) => {
|
||||||
|
const url = resolvePreviewUrl(record?.previewUrl);
|
||||||
|
if (!url) {
|
||||||
|
message.warning("该材料暂无可预览的附件");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isImageUrl(url)) {
|
||||||
|
setFilePreview(record);
|
||||||
|
} else {
|
||||||
|
window.open(url, "_blank");
|
||||||
|
}
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
@ -252,7 +275,7 @@ function RegisteredOrgDetailPage(props) {
|
||||||
open={!!filePreview}
|
open={!!filePreview}
|
||||||
title="文件预览"
|
title="文件预览"
|
||||||
fileName={filePreview?.name}
|
fileName={filePreview?.name}
|
||||||
url={filePreview?.previewUrl}
|
url={resolvePreviewUrl(filePreview?.previewUrl)}
|
||||||
onCancel={() => setFilePreview(null)}
|
onCancel={() => setFilePreview(null)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,23 +11,53 @@ export function resolveUploadFileId(_files) {
|
||||||
return DEFAULT_UPLOAD_FILE_URL;
|
return DEFAULT_UPLOAD_FILE_URL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 多附件:逗号拼接 URL */
|
/** 多附件:序列化为 JSON 数组字符串,与后端格式保持一致 */
|
||||||
export function resolveUploadFileIds(files) {
|
export function resolveUploadFileIds(files) {
|
||||||
if (!files?.length) {
|
if (!files?.length) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
const urls = files.map((file) => file?.url || file?.response?.url).filter(Boolean);
|
const valid = files
|
||||||
if (!urls.length) {
|
.filter((file) => file?.url)
|
||||||
|
.map((file) => ({
|
||||||
|
url: file.url,
|
||||||
|
uid: file.uid || undefined,
|
||||||
|
name: file.name || file.fileName || "附件",
|
||||||
|
status: file.status || "done",
|
||||||
|
}));
|
||||||
|
if (!valid.length) {
|
||||||
return DEFAULT_UPLOAD_FILE_URL;
|
return DEFAULT_UPLOAD_FILE_URL;
|
||||||
}
|
}
|
||||||
return urls.join(",");
|
return JSON.stringify(valid);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parseUploadFileList(urls, defaultName = "附件.pdf") {
|
export function parseUploadFileList(urls, defaultName = "附件.pdf") {
|
||||||
if (!urls) {
|
if (!urls) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
return String(urls)
|
const raw = String(urls).trim();
|
||||||
|
|
||||||
|
// JSON 数组字符串:如 '[{"url":"...","uid":"...","name":"...","status":"done"}]'
|
||||||
|
if (raw.startsWith("[") && raw.endsWith("]")) {
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(raw);
|
||||||
|
if (Array.isArray(parsed)) {
|
||||||
|
return parsed
|
||||||
|
.filter((item) => item?.url)
|
||||||
|
.map((item) => ({
|
||||||
|
name: item.name || `${defaultName.replace(".pdf", "")}1`,
|
||||||
|
fileName: item.fileName || item.name || `${defaultName.replace(".pdf", "")}1`,
|
||||||
|
url: item.url,
|
||||||
|
uid: item.uid || undefined,
|
||||||
|
status: item.status || "done",
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
} catch (_) {
|
||||||
|
// JSON 解析失败,回退到逗号分隔处理
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 旧格式:逗号分隔的 URL 字符串
|
||||||
|
return raw
|
||||||
.split(",")
|
.split(",")
|
||||||
.map((url) => url.trim())
|
.map((url) => url.trim())
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue