dev_1.0.1
tangjie 2026-07-01 11:55:19 +08:00
parent 8ec2de142b
commit 6cc9b9d47f
6 changed files with 160 additions and 211 deletions

View File

@ -20,7 +20,7 @@ module.exports = {
}, },
}, },
// 应用唯一标识符 // 应用唯一标识符
appIdentifier: "certificate", appIdentifier: "safety",
// 应用上下文注入全局变量 // 应用上下文注入全局变量
contextInject: { contextInject: {
// 应用Key // 应用Key

View File

@ -3,7 +3,8 @@ import { CHONGQING_DISTRICTS, QUALIFICATION_INDUSTRY_OPTIONS } from "~/enumerate
import { FILING_UNIT_TYPE_OPTIONS } from "~/enumerate/qualFilingOptions"; import { FILING_UNIT_TYPE_OPTIONS } from "~/enumerate/qualFilingOptions";
import FilingUpload, { resolveFilingUploadUrl } from "./FilingUpload"; import FilingUpload, { resolveFilingUploadUrl } from "./FilingUpload";
export default function BasicInfoStep({ form, disabled, onAttachmentChange }) { export default function
BasicInfoStep({ form, disabled, onAttachmentChange }) {
return ( return (
<Form form={form} layout="vertical" disabled={disabled}> <Form form={form} layout="vertical" disabled={disabled}>
<Row gutter={16}> <Row gutter={16}>

View File

@ -1,6 +1,7 @@
import { Connect } from "@cqsjjb/jjb-dva-runtime"; import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { tools } from "@cqsjjb/jjb-common-lib";
import { Button, Form, message, Modal, Space, Spin, Tabs } from "antd"; import { Button, Form, message, Modal, Space, Spin, Tabs } from "antd";
import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useCallback, useEffect, useRef, useState } from "react";
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout"; import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
import { fetchQualFilingDetail } from "~/api/qualFiling"; import { fetchQualFilingDetail } from "~/api/qualFiling";
import { NS_QUAL_FILING } from "~/enumerate/namespace"; import { NS_QUAL_FILING } from "~/enumerate/namespace";
@ -25,7 +26,7 @@ import {
import { verifyFilingPrerequisites } from "../filingVerify"; import { verifyFilingPrerequisites } from "../filingVerify";
import { toDayjs } from "~/utils/dateFormat"; import { toDayjs } from "~/utils/dateFormat";
import { resolveUploadFileId } from "~/utils/mockUpload"; import { resolveUploadFileId } from "~/utils/mockUpload";
import { goFilingList, parseFilingFormQuery } from "../filingPaths"; import { goFilingList } from "../filingPaths";
import BasicInfoStep from "./components/BasicInfoStep"; import BasicInfoStep from "./components/BasicInfoStep";
import CommitmentStep from "./components/CommitmentStep"; import CommitmentStep from "./components/CommitmentStep";
import EquipmentStep from "./components/EquipmentStep"; import EquipmentStep from "./components/EquipmentStep";
@ -48,26 +49,12 @@ const MODE_TITLE = {
}; };
function syncForms(detail, basicForm, commitmentForm) {
if (!detail) {
return;
}
basicForm.setFieldsValue(detail);
commitmentForm.setFieldsValue({
...detail.commitment,
filingUnitName: detail.commitment?.filingUnitName || detail.filingUnitName,
signDate: toDayjs(detail.commitment?.signDate),
});
}
function FilingFormPage(props) { function FilingFormPage(props) {
const query = useMemo( const query = tools.router.query;
() => parseFilingFormQuery(window.location.search),
[],
);
const filingId = query.id; const filingId = query.id;
const mode = query.mode; const mode = query.mode;
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(false);
const [submitting, setSubmitting] = useState(false); const [submitting, setSubmitting] = useState(false);
const [activeStep, setActiveStep] = useState("basic"); const [activeStep, setActiveStep] = useState("basic");
const [detail, setDetail] = useState(null); const [detail, setDetail] = useState(null);
@ -79,32 +66,21 @@ function FilingFormPage(props) {
const [activeFilingId] = useState(filingId); const [activeFilingId] = useState(filingId);
const detailRef = useRef(null); const detailRef = useRef(null);
const readOnly = query.readOnly || (detail ? !isQualFilingEditable(detail.filingStatusCode) : false); const readOnly =
query.readOnly ||
(detail ? !isQualFilingEditable(detail.filingStatusCode) : false);
const listMode = mode === FILING_FORM_MODE.FILED ? "filed" : mode === FILING_FORM_MODE.CHANGE ? "change" : "application"; const listMode =
mode === FILING_FORM_MODE.FILED
? "filed"
: mode === FILING_FORM_MODE.CHANGE
? "change"
: "application";
const goBackList = useCallback(() => goFilingList(listMode), [listMode]); const goBackList = useCallback(() => goFilingList(listMode), [listMode]);
const saveActionHint = mode === FILING_FORM_MODE.FILED ? "请点击提交后保存" : "请点击暂存或提交后保存"; const saveActionHint =
mode === FILING_FORM_MODE.FILED
const loadInitialDetail = useCallback(async () => { ? "请点击提交后保存"
if (activeFilingId) { : "请点击暂存或提交后保存";
const res = await fetchQualFilingDetail(activeFilingId);
if (res?.success === false) {
message.error(res.message || "加载备案详情失败");
return null;
}
const data = res?.data || null;
if (data && !(data.materials || []).length) {
data.materials = createLocalMaterials();
}
return data;
}
const cached = loadLocalDraft(mode);
if (cached) {
return cached;
}
return createEmptyFilingDetail();
}, [activeFilingId, mode]);
const loadPersonnelOptions = useCallback(async () => { const loadPersonnelOptions = useCallback(async () => {
if (personnelOptions.length) { if (personnelOptions.length) {
@ -120,45 +96,36 @@ function FilingFormPage(props) {
}, [detail]); }, [detail]);
useEffect(() => { useEffect(() => {
let cancelled = false; if (
(async () => { !detail?.commitment?.legalRepPersonnelId &&
setLoading(true); !detail?.commitment?.legalRepName
try { ) {
const data = await loadInitialDetail();
if (cancelled || !data) {
return;
}
setDetail(data);
detailRef.current = data;
syncForms(data, basicForm, commitmentForm);
}
finally {
if (!cancelled) {
setLoading(false);
}
}
})();
return () => {
cancelled = true;
};
}, [basicForm, commitmentForm, loadInitialDetail]);
useEffect(() => {
if (!detail?.commitment?.legalRepPersonnelId && !detail?.commitment?.legalRepName) {
return; return;
} }
loadPersonnelOptions(); loadPersonnelOptions();
}, [detail?.commitment?.legalRepPersonnelId, detail?.commitment?.legalRepName, loadPersonnelOptions]); }, [
detail?.commitment?.legalRepPersonnelId,
detail?.commitment?.legalRepName,
loadPersonnelOptions,
]);
useEffect(() => { useEffect(() => {
if (activeStep !== "commitment" && !detail?.commitment?.legalRepPersonnelId) { if (
activeStep !== "commitment" &&
!detail?.commitment?.legalRepPersonnelId
) {
return; return;
} }
if (personnelOptions.length) { if (personnelOptions.length) {
return; return;
} }
loadPersonnelOptions(); loadPersonnelOptions();
}, [activeStep, detail?.commitment?.legalRepPersonnelId, loadPersonnelOptions, personnelOptions.length]); }, [
activeStep,
detail?.commitment?.legalRepPersonnelId,
loadPersonnelOptions,
personnelOptions.length,
]);
useEffect(() => { useEffect(() => {
if (!personnelOptions.length) { if (!personnelOptions.length) {
@ -167,11 +134,16 @@ function FilingFormPage(props) {
const data = detailRef.current; const data = detailRef.current;
const commitment = data?.commitment || {}; const commitment = data?.commitment || {};
if (commitment.legalRepPersonnelId) { if (commitment.legalRepPersonnelId) {
commitmentForm.setFieldValue("legalRepPersonnelId", String(commitment.legalRepPersonnelId)); commitmentForm.setFieldValue(
"legalRepPersonnelId",
String(commitment.legalRepPersonnelId),
);
return; return;
} }
if (commitment.legalRepName) { if (commitment.legalRepName) {
const matched = personnelOptions.find((item) => item.staffName === commitment.legalRepName); const matched = personnelOptions.find(
(item) => item.staffName === commitment.legalRepName,
);
if (matched) { if (matched) {
commitmentForm.setFieldsValue({ commitmentForm.setFieldsValue({
legalRepPersonnelId: matched.value, legalRepPersonnelId: matched.value,
@ -181,16 +153,22 @@ function FilingFormPage(props) {
} }
}, [commitmentForm, personnelOptions]); }, [commitmentForm, personnelOptions]);
const updateDetail = useCallback((updater) => { const updateDetail = useCallback(
setDetail((prev) => { (updater) => {
const next = typeof updater === "function" ? updater(prev) : { ...prev, ...updater }; setDetail((prev) => {
detailRef.current = next; const next =
if (!activeFilingId && !readOnly) { typeof updater === "function"
saveLocalDraft(mode, next); ? updater(prev)
} : { ...prev, ...updater };
return next; detailRef.current = next;
}); if (!activeFilingId && !readOnly) {
}, [activeFilingId, mode, readOnly]); saveLocalDraft(mode, next);
}
return next;
});
},
[activeFilingId, mode, readOnly],
);
const collectCurrentDetail = useCallback(() => { const collectCurrentDetail = useCallback(() => {
const current = detailRef.current || detail; const current = detailRef.current || detail;
@ -211,11 +189,9 @@ function FilingFormPage(props) {
clearLocalDraft(mode); clearLocalDraft(mode);
message.success("已暂存"); message.success("已暂存");
goBackList(); goBackList();
} } catch (err) {
catch (err) {
message.error(err?.message || "暂存失败"); message.error(err?.message || "暂存失败");
} } finally {
finally {
setSubmitting(false); setSubmitting(false);
} }
}; };
@ -241,13 +217,14 @@ function FilingFormPage(props) {
clearLocalDraft(mode); clearLocalDraft(mode);
if (mode === FILING_FORM_MODE.CHANGE) { if (mode === FILING_FORM_MODE.CHANGE) {
const res = await props.qualFilingChangeSubmit({ draftFilingId: saved.id }); const res = await props.qualFilingChangeSubmit({
draftFilingId: saved.id,
});
if (res?.success === false) { if (res?.success === false) {
message.error(res.message || "提交失败"); message.error(res.message || "提交失败");
return; return;
} }
} } else {
else {
const res = await props.qualFilingSubmit({ id: saved.id }); const res = await props.qualFilingSubmit({ id: saved.id });
if (res?.success === false) { if (res?.success === false) {
message.error(res.message || "提交失败"); message.error(res.message || "提交失败");
@ -257,11 +234,9 @@ function FilingFormPage(props) {
setVerifyOpen(false); setVerifyOpen(false);
message.success("提交成功"); message.success("提交成功");
goBackList(); goBackList();
} } catch (err) {
catch (err) {
message.error(err?.message || "提交失败"); message.error(err?.message || "提交失败");
} } finally {
finally {
setSubmitting(false); setSubmitting(false);
} }
}; };
@ -270,16 +245,16 @@ function FilingFormPage(props) {
const attachmentUrl = url || resolveUploadFileId([]); const attachmentUrl = url || resolveUploadFileId([]);
updateDetail((prev) => ({ updateDetail((prev) => ({
...prev, ...prev,
materials: (prev.materials || []).map((item) => ( materials: (prev.materials || []).map((item) =>
item.id === record.id || item.sortOrder === record.sortOrder item.id === record.id || item.sortOrder === record.sortOrder
? { ? {
...item, ...item,
attachmentUrl, attachmentUrl,
uploadStatusCode: 2, uploadStatusCode: 2,
uploadStatusName: "已上传", uploadStatusName: "已上传",
} }
: item : item,
)), ),
})); }));
message.success(`材料已选择,${saveActionHint}`); message.success(`材料已选择,${saveActionHint}`);
}; };
@ -289,7 +264,10 @@ function FilingFormPage(props) {
}; };
const handleSignatureChange = (url, files) => { const handleSignatureChange = (url, files) => {
commitmentForm.setFieldsValue({ signatureFiles: files, legalRepSignatureUrl: url }); commitmentForm.setFieldsValue({
signatureFiles: files,
legalRepSignatureUrl: url,
});
updateDetail((prev) => ({ updateDetail((prev) => ({
...prev, ...prev,
commitment: { commitment: {
@ -302,16 +280,22 @@ function FilingFormPage(props) {
const handlePersonnelAdd = (sourcePersonnelIds, rows = []) => { const handlePersonnelAdd = (sourcePersonnelIds, rows = []) => {
const existing = new Set( const existing = new Set(
(detailRef.current?.personnelList || []).map((item) => String(item.sourcePersonnelId)), (detailRef.current?.personnelList || []).map((item) =>
String(item.sourcePersonnelId),
),
);
const idsToAdd = sourcePersonnelIds.filter(
(id) => !existing.has(String(id)),
); );
const idsToAdd = sourcePersonnelIds.filter((id) => !existing.has(String(id)));
if (!idsToAdd.length) { if (!idsToAdd.length) {
return; return;
} }
const rowMap = new Map((rows || []).map((row) => [String(row.id), row])); const rowMap = new Map((rows || []).map((row) => [String(row.id), row]));
const newRows = idsToAdd.map((id) => { const newRows = idsToAdd.map((id) => {
const row = rowMap.get(String(id)); const row = rowMap.get(String(id));
return row ? mapStaffRowToFilingPersonnel(row) : mapStaffRowToFilingPersonnel({ id }); return row
? mapStaffRowToFilingPersonnel(row)
: mapStaffRowToFilingPersonnel({ id });
}); });
updateDetail((prev) => ({ updateDetail((prev) => ({
...prev, ...prev,
@ -327,7 +311,9 @@ function FilingFormPage(props) {
onOk: () => { onOk: () => {
updateDetail((prev) => ({ updateDetail((prev) => ({
...prev, ...prev,
personnelList: (prev.personnelList || []).filter((item) => item.id !== record.id), personnelList: (prev.personnelList || []).filter(
(item) => item.id !== record.id,
),
})); }));
}, },
}); });
@ -335,16 +321,22 @@ function FilingFormPage(props) {
const handleEquipmentAdd = (sourceEquipmentIds, rows = []) => { const handleEquipmentAdd = (sourceEquipmentIds, rows = []) => {
const existing = new Set( const existing = new Set(
(detailRef.current?.equipmentList || []).map((item) => String(item.sourceEquipmentId)), (detailRef.current?.equipmentList || []).map((item) =>
String(item.sourceEquipmentId),
),
);
const idsToAdd = sourceEquipmentIds.filter(
(id) => !existing.has(String(id)),
); );
const idsToAdd = sourceEquipmentIds.filter((id) => !existing.has(String(id)));
if (!idsToAdd.length) { if (!idsToAdd.length) {
return; return;
} }
const rowMap = new Map((rows || []).map((row) => [String(row.id), row])); const rowMap = new Map((rows || []).map((row) => [String(row.id), row]));
const newRows = idsToAdd.map((id) => { const newRows = idsToAdd.map((id) => {
const row = rowMap.get(String(id)); const row = rowMap.get(String(id));
return row ? mapEquipRowToFilingEquipment(row) : mapEquipRowToFilingEquipment({ id }); return row
? mapEquipRowToFilingEquipment(row)
: mapEquipRowToFilingEquipment({ id });
}); });
updateDetail((prev) => ({ updateDetail((prev) => ({
...prev, ...prev,
@ -360,7 +352,9 @@ function FilingFormPage(props) {
onOk: () => { onOk: () => {
updateDetail((prev) => ({ updateDetail((prev) => ({
...prev, ...prev,
equipmentList: (prev.equipmentList || []).filter((item) => item.id !== record.id), equipmentList: (prev.equipmentList || []).filter(
(item) => item.id !== record.id,
),
})); }));
}, },
}); });
@ -369,9 +363,9 @@ function FilingFormPage(props) {
const handleEquipmentCalibration = (record, url) => { const handleEquipmentCalibration = (record, url) => {
updateDetail((prev) => ({ updateDetail((prev) => ({
...prev, ...prev,
equipmentList: (prev.equipmentList || []).map((item) => ( equipmentList: (prev.equipmentList || []).map((item) =>
item.id === record.id ? { ...item, calibrationReportUrl: url } : item item.id === record.id ? { ...item, calibrationReportUrl: url } : item,
)), ),
})); }));
}; };
@ -381,14 +375,23 @@ function FilingFormPage(props) {
return ( return (
<PageLayout <PageLayout
title={MODE_TITLE[mode] || "资质备案表单"} title={MODE_TITLE[mode] || "资质备案表单"}
extra={( history={props.history}
<Button onClick={() => goFilingList( previous
mode === FILING_FORM_MODE.FILED ? "filed" : mode === FILING_FORM_MODE.CHANGE ? "change" : "application", extra={
)} <Button
onClick={() =>
goFilingList(
mode === FILING_FORM_MODE.FILED
? "filed"
: mode === FILING_FORM_MODE.CHANGE
? "change"
: "application",
)
}
> >
返回列表 返回列表
</Button> </Button>
)} }
> >
<Spin spinning={loading || submitting}> <Spin spinning={loading || submitting}>
<Tabs <Tabs
@ -397,7 +400,9 @@ function FilingFormPage(props) {
onChange={(key) => { onChange={(key) => {
if (key === "commitment") { if (key === "commitment") {
commitmentForm.setFieldsValue({ commitmentForm.setFieldsValue({
filingUnitName: basicForm.getFieldValue("filingUnitName") || detail?.filingUnitName, filingUnitName:
basicForm.getFieldValue("filingUnitName") ||
detail?.filingUnitName,
}); });
loadPersonnelOptions(); loadPersonnelOptions();
} }
@ -445,28 +450,45 @@ function FilingFormPage(props) {
/> />
)} )}
</div> </div>
<div style={{ <div
display: "flex", style={{
justifyContent: "space-between", display: "flex",
marginTop: 24, justifyContent: "space-between",
paddingTop: 16, marginTop: 24,
borderTop: "1px solid #f0f0f0", paddingTop: 16,
}} borderTop: "1px solid #f0f0f0",
}}
> >
<Space> <Space>
<Button onClick={() => goFilingList( <Button
mode === FILING_FORM_MODE.FILED ? "filed" : mode === FILING_FORM_MODE.CHANGE ? "change" : "application", onClick={() =>
)} goFilingList(
mode === FILING_FORM_MODE.FILED
? "filed"
: mode === FILING_FORM_MODE.CHANGE
? "change"
: "application",
)
}
> >
取消 取消
</Button> </Button>
</Space> </Space>
<Space> <Space>
{stepIndex > 0 && ( {stepIndex > 0 && (
<Button onClick={() => setActiveStep(STEP_ITEMS[stepIndex - 1].key)}>上一步</Button> <Button
onClick={() => setActiveStep(STEP_ITEMS[stepIndex - 1].key)}
>
上一步
</Button>
)} )}
{!isLastStep && ( {!isLastStep && (
<Button type="primary" onClick={() => setActiveStep(STEP_ITEMS[stepIndex + 1].key)}>下一步</Button> <Button
type="primary"
onClick={() => setActiveStep(STEP_ITEMS[stepIndex + 1].key)}
>
下一步
</Button>
)} )}
{!readOnly && isLastStep && ( {!readOnly && isLastStep && (
<> <>

View File

@ -139,7 +139,7 @@ export default function FilingListTable({
<Form.Item key="filingTerritoryName" name="filingTerritoryName"> <Form.Item key="filingTerritoryName" name="filingTerritoryName">
<ControlWrapper.Select <ControlWrapper.Select
label="备案属地" label="备案属地"
placeholder="全部" placeholder="请输入"
allowClear allowClear
style={{ width: "100%" }} style={{ width: "100%" }}
> >

View File

@ -24,12 +24,4 @@ export function goFilingList(mode) {
window.location.href = FILING_LIST_PATH[mode] || FILING_LIST_PATH.application; window.location.href = FILING_LIST_PATH[mode] || FILING_LIST_PATH.application;
} }
export function parseFilingFormQuery(search = "") {
const params = new URLSearchParams(search.startsWith("?") ? search.slice(1) : search);
return {
mode: params.get("mode") || "application",
id: params.get("id") || "",
originFilingId: params.get("originFilingId") || "",
readOnly: params.get("readOnly") === "1",
};
}

View File

@ -1,74 +1,8 @@
import { Button, message, Spin } from "antd"; import React from "react";
import { useEffect, useState } from "react";
import {
ensureOrgContext,
ORG_INFO_PAGE_PATH,
} from "~/api/enterpriseInfo/orgBootstrap";
import { getOrgInfoId, setOrgInfoId } from "~/api/enterpriseInfo/orgContext";
/** 本地联调默认机构 id与后端 application-local.yml public-api.org-id 一致) */
const LOCAL_DEV_ORG_INFO_ID = "1";
/** 资质申请管理模块入口:需先有机构信息 */ /** 资质申请管理模块入口:需先有机构信息 */
function QualApplication(props) { function QualApplication(props) {
const [checking, setChecking] = useState(true);
const [blocked, setBlocked] = useState(false);
useEffect(() => {
let cancelled = false;
// 本地独立运行时,优先恢复 session / 默认 orgInfoId避免 getInfo 无用户关联导致卡死
if (!getOrgInfoId() && !window.__POWERED_BY_QIANKUN__) {
setOrgInfoId(LOCAL_DEV_ORG_INFO_ID);
}
ensureOrgContext({ force: true })
.then((ctx) => {
if (cancelled) {
return;
}
if (ctx?.networkError) {
message.warning("机构信息加载失败,请确认后端服务已启动");
setChecking(false);
return;
}
if (!ctx?.hasOrg) {
message.warning("请先完善机构信息");
setBlocked(true);
setChecking(false);
return;
}
setChecking(false);
})
.catch((err) => {
console.warn("[QualApplication] ensureOrgContext failed:", err);
if (!cancelled) {
message.warning("机构信息加载失败,请确认后端服务已启动");
setChecking(false);
}
});
return () => {
cancelled = true;
};
}, []);
if (checking) {
return <Spin fullscreen tip="正在加载机构信息..." />;
}
if (blocked) {
return (
<div style={{ padding: 48, textAlign: "center" }}>
<p style={{ marginBottom: 16, color: "rgba(0,0,0,0.65)" }}>
资质申请管理需先完善机构信息请前往企业信息管理 机构信息管理录入并保存
</p>
<Button type="primary" href={ORG_INFO_PAGE_PATH}>
去完善机构信息
</Button>
</div>
);
}
return ( return (
<div style={{ height: "100%" }}> <div style={{ height: "100%" }}>