diff --git a/src/api/customer/index.js b/src/api/customer/index.js
new file mode 100644
index 0000000..b6b5520
--- /dev/null
+++ b/src/api/customer/index.js
@@ -0,0 +1,18 @@
+import { declareRequest } from "@cqsjjb/jjb-dva-runtime";
+
+export const customerPage = declareRequest(
+ "customerLoading",
+ "Get > /safetyEval/customer/page",
+ 'customerList: [] | res.data?.records || [] & customerTotal: 0 | res.data?.total || 0',
+);
+
+export const customerSave = declareRequest(
+ "customerSaveLoading",
+ "Post > @/safetyEval/customer/save",
+);
+
+export const customerDetail = declareRequest(
+ "customerDetailLoading",
+ "Get > /safetyEval/customer/detail/{id}",
+ 'customerDetail: {} | res.data || {}',
+);
\ No newline at end of file
diff --git a/src/api/safetyEvalBusiness/index.js b/src/api/safetyEvalBusiness/index.js
index e69de29..d736c14 100644
--- a/src/api/safetyEvalBusiness/index.js
+++ b/src/api/safetyEvalBusiness/index.js
@@ -0,0 +1,7 @@
+import { declareRequest } from "@cqsjjb/jjb-dva-runtime";
+
+export const queryMyOrCreateDoc = declareRequest(
+ "docLoading",
+ "Get > /safetyEval-h5/wsp/doc/my-or-create",
+ 'wpsDoc: {} | res.data || {}',
+);
\ No newline at end of file
diff --git a/src/components/OrgInfoGuard/index.js b/src/components/OrgInfoGuard/index.js
new file mode 100644
index 0000000..203b66f
--- /dev/null
+++ b/src/components/OrgInfoGuard/index.js
@@ -0,0 +1,35 @@
+import React, { useEffect, useState } from "react";
+import { NS_ORG_INFO } from "~/enumerate/namespace";
+import { Connect } from "@cqsjjb/jjb-dva-runtime";
+import { Skeleton } from "antd";
+
+/** 机构信息守卫:确保进入子路由前已有机构信息 */
+function OrgInfoGuard(props) {
+ const [orgInfoId, setOrgInfoId] = useState(
+ sessionStorage.getItem("orgInfoId"),
+ );
+
+ useEffect(() => {
+ if (!orgInfoId) {
+ props.orgInfoGet().then((res) => {
+ if (res?.data?.id) {
+ // 下方两行切勿更换顺序
+ sessionStorage.setItem("orgInfoId", res?.data?.id);
+ setOrgInfoId(res?.data?.id);
+ } else {
+ props.history.push("/container/EnterpriseInfo/OrgInfo");
+ }
+ });
+ }
+ }, []);
+
+ if (props.skipGuard) return props.children;
+
+ return (
+
+ {orgInfoId ? props.children : }
+
+ );
+}
+
+export default Connect([NS_ORG_INFO], true)(OrgInfoGuard);
\ No newline at end of file
diff --git a/src/components/index.js b/src/components/index.js
index cb0ff5c..1cca838 100644
--- a/src/components/index.js
+++ b/src/components/index.js
@@ -1 +1 @@
-export {};
+export { default as OrgInfoGuard } from "./OrgInfoGuard";
\ No newline at end of file
diff --git a/src/enumerate/namespace/index.js b/src/enumerate/namespace/index.js
index a1b9996..35fabf1 100644
--- a/src/enumerate/namespace/index.js
+++ b/src/enumerate/namespace/index.js
@@ -23,3 +23,4 @@ export const NS_QUAL_EXPERT = defineNamespace("qualExpert");
export const NS_DRIVER = defineNamespace("driver");
export const NS_RISK_CENTER = defineNamespace("riskCenter");
export const NS_SAFETY_EVAL_BUSINESS = defineNamespace("safetyEvalBusiness");
+export const NS_CUSTOMER = defineNamespace("customer");
diff --git a/src/pages/Container/EnterpriseInfo/index.js b/src/pages/Container/EnterpriseInfo/index.js
index 9180068..e66f1ff 100644
--- a/src/pages/Container/EnterpriseInfo/index.js
+++ b/src/pages/Container/EnterpriseInfo/index.js
@@ -1,37 +1,11 @@
-import React, { useEffect, useState } from "react";
-import { NS_ORG_INFO } from "~/enumerate/namespace";
-import { Connect } from "@cqsjjb/jjb-dva-runtime";
-import { Skeleton } from "antd";
+import OrgInfoGuard from "~/components/OrgInfoGuard";
-/** 资质申请管理模块入口:需先有机构信息 */
-function EnterpriseInfo(props) {
- const [orgInfoId, setOrgInfoId] = useState(
- sessionStorage.getItem("orgInfoId"),
- );
- useEffect(() => {
- if (!orgInfoId) {
- props.orgInfoGet().then((res) => {
- if (res?.data?.id) {
- // 下方两行切勿更换顺序
- sessionStorage.setItem("orgInfoId", res?.data?.id);
- setOrgInfoId(res?.data?.id);
- }
- else {
- props.history.push("/container/EnterpriseInfo/OrgInfo");
- }
- });
- }
- }, []);
-
- if (props.location.pathname.includes("EnterpriseInfo/OrgInfo")) {
- return props.children;
- }
+export default function EnterpriseInfo(props) {
+ const skipGuard = props.location.pathname.includes("EnterpriseInfo/OrgInfo");
return (
-
- {orgInfoId ? props.children : }
-
+
+ {props.children}
+
);
-}
-
-export default Connect([NS_ORG_INFO], true)(EnterpriseInfo);
+}
\ No newline at end of file
diff --git a/src/pages/Container/Layout/menuConfig.js b/src/pages/Container/Layout/menuConfig.js
index 99c3c1a..d57a1be 100644
--- a/src/pages/Container/Layout/menuConfig.js
+++ b/src/pages/Container/Layout/menuConfig.js
@@ -202,7 +202,11 @@ const menuItems = [
label: "我的项目",
icon: ,
},
-
+ {
+ key: "/safetyEval/container/SafetyEvalBusiness/CustomerManage",
+ label: "安评客户管理",
+ icon: ,
+ },
],
},
{
diff --git a/src/pages/Container/QualApplication/index.js b/src/pages/Container/QualApplication/index.js
index a7fcfe0..090bb06 100644
--- a/src/pages/Container/QualApplication/index.js
+++ b/src/pages/Container/QualApplication/index.js
@@ -1,34 +1,5 @@
-import React, { useEffect, useState } from "react";
-import { NS_ORG_INFO } from "~/enumerate/namespace";
-import { Connect } from "@cqsjjb/jjb-dva-runtime";
-import { Skeleton } from "antd";
+import OrgInfoGuard from "~/components/OrgInfoGuard";
-/** 资质申请管理模块入口:需先有机构信息 */
-function QualApplication(props) {
- const [orgInfoId, setOrgInfoId] = useState(
- sessionStorage.getItem("orgInfoId"),
- );
- useEffect(() => {
- if (!orgInfoId) {
- props.orgInfoGet().then((res) => {
- if (res?.data?.id) {
- // 下方两行切勿更换顺序
- sessionStorage.setItem("orgInfoId", res?.data?.id);
- setOrgInfoId(res?.data?.id);
-
- }
- else{
- props.history.push("/container/EnterpriseInfo/OrgInfo");
- }
- });
- }
- }, []);
-
- return (
-
- {orgInfoId ? props.children : }
-
- );
-}
-
-export default Connect([NS_ORG_INFO], true)(QualApplication);
+export default function QualApplication(props) {
+ return {props.children};
+}
\ No newline at end of file
diff --git a/src/pages/Container/SafetyEvalBusiness/CustomerManage/index.js b/src/pages/Container/SafetyEvalBusiness/CustomerManage/index.js
new file mode 100644
index 0000000..e881010
--- /dev/null
+++ b/src/pages/Container/SafetyEvalBusiness/CustomerManage/index.js
@@ -0,0 +1,408 @@
+import React, { useState, useCallback } from "react";
+import { Form, Table, Button, Modal, Input, Select, Row, Col, message, Descriptions } from "antd";
+import { PlusOutlined } from "@ant-design/icons";
+import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
+import SearchForm from "@cqsjjb/jjb-react-admin-component/SearchForm";
+import ControlWrapper from "@cqsjjb/jjb-react-admin-component/ControlWrapper";
+import { AntdTableFuncControl } from "@cqsjjb/jjb-common-decorator/antd";
+import { NS_CUSTOMER } from "~/enumerate/namespace";
+import { Connect } from "@cqsjjb/jjb-dva-runtime";
+
+const MOCK_LIST = [
+ {
+ id: 1, name: "重庆华安安全科技有限公司", contact: "周经理", phone: "138****5621",
+ region: "渝北区", projectCount: 6,
+ creditCode: "91500112MA5UQ001", industry: "化工生产", address: "重庆市渝北区化工园区东路18号",
+ status: "正常经营", location: "重庆市渝北区 · 东经106.63° / 北纬29.72°",
+ principal: "周海峰", principalPhone: "138****5621", legalPerson: "周海峰", legalPhone: "138****5621", scale: "中型企业",
+ },
+ {
+ id: 2, name: "重庆鼎信安全咨询有限公司", contact: "刘总", phone: "139****1120",
+ region: "南岸区", projectCount: 4,
+ creditCode: "91500108MA61A1120", industry: "建筑施工", address: "重庆市南岸区茶园新区通江大道96号",
+ status: "正常经营", location: "重庆市南岸区 · 东经106.66° / 北纬29.48°",
+ principal: "刘志强", principalPhone: "139****1120", legalPerson: "刘志强", legalPhone: "139****1120", scale: "小型企业",
+ },
+ {
+ id: 3, name: "重庆安环检测技术有限公司", contact: "陈主任", phone: "136****9088",
+ region: "江北区", projectCount: 9,
+ creditCode: "91500105MA60K9088", industry: "其他工贸", address: "重庆市江北区港城工业园A区",
+ status: "正常经营", location: "重庆市江北区 · 东经106.65° / 北纬29.63°",
+ principal: "陈海林", principalPhone: "136****9088", legalPerson: "陈海林", legalPhone: "136****9088", scale: "中型企业",
+ },
+ {
+ id: 4, name: "重庆环宇投资有限公司", contact: "罗经理", phone: "135****8710",
+ region: "沙坪坝区", projectCount: 3,
+ creditCode: "91500106MA60K9099", industry: "危险化学品经营", address: "重庆市沙坪坝区三峡广场88号",
+ status: "正常经营", location: "重庆市沙坪坝区 · 东经106.46° / 北纬29.56°",
+ principal: "罗建国", principalPhone: "135****8710", legalPerson: "罗建国", legalPhone: "135****8710", scale: "大型企业",
+ },
+];
+
+const PLATFORM_ENTERPRISES = [
+ { name: "重庆华安安全科技有限公司", code: "91500112MA5UQ001", region: "渝北区", industry: "化工生产", status: "正常经营", principal: "周海峰", legal: "周海峰" },
+ { name: "重庆鼎信安全咨询有限公司", code: "91500108MA61A1120", region: "南岸区", industry: "建筑施工", status: "正常经营", principal: "刘志强", legal: "刘志强" },
+ { name: "重庆安环检测技术有限公司", code: "91500105MA60K9088", region: "江北区", industry: "其他工贸", status: "正常经营", principal: "陈海林", legal: "陈海林" },
+];
+
+const REGION_OPTIONS = ["渝北区", "南岸区", "江北区", "沙坪坝区", "九龙坡区", "北碚区", "巴南区"];
+const INDUSTRY_OPTIONS = ["化工生产", "危险化学品经营", "金属非金属矿山", "金属冶炼", "建筑施工", "加油站", "其他工贸"];
+const STATUS_OPTIONS = ["正常经营", "停产整改", "停业", "注销"];
+const SCALE_OPTIONS = ["大型企业", "中型企业", "小型企业", "微型企业"];
+
+const CustomerManage = (props) => {
+ const [searchForm] = Form.useForm();
+ const [modalForm] = Form.useForm();
+ const [dataSource, setDataSource] = useState(MOCK_LIST);
+ const [modalOpen, setModalOpen] = useState(false);
+ const [detailOpen, setDetailOpen] = useState(false);
+ const [currentDetail, setCurrentDetail] = useState(null);
+ const [createMode, setCreateMode] = useState("platform"); // "platform" | "new"
+ const [selectedPlatform, setSelectedPlatform] = useState(null);
+
+ const handleSearch = useCallback((values) => {
+ const { name, contact, region } = values || {};
+ const filtered = MOCK_LIST.filter((item) => {
+ if (name && !item.name.includes(name)) return false;
+ if (contact && !item.contact.includes(contact)) return false;
+ if (region && item.region !== region) return false;
+ return true;
+ });
+ setDataSource(filtered);
+ }, []);
+
+ const handleReset = useCallback(() => {
+ searchForm.resetFields();
+ setDataSource(MOCK_LIST);
+ }, [searchForm]);
+
+ const handleOpenCreate = useCallback(() => {
+ setCreateMode("platform");
+ setSelectedPlatform(null);
+ modalForm.resetFields();
+ setModalOpen(true);
+ }, [modalForm]);
+
+ const handlePlatformSelect = useCallback((name) => {
+ const ep = PLATFORM_ENTERPRISES.find((e) => e.name === name);
+ if (ep) {
+ setSelectedPlatform(ep);
+ modalForm.setFieldsValue({
+ creditCode: ep.code,
+ region: ep.region,
+ industry: ep.industry,
+ status: ep.status,
+ principal: ep.principal,
+ legalPerson: ep.legal,
+ });
+ }
+ }, [modalForm]);
+
+ const handleCreateSubmit = useCallback(() => {
+ modalForm.validateFields().then((values) => {
+ const newCustomer = {
+ id: dataSource.length + 1,
+ name: createMode === "platform" ? values.platformEnterprise : values.name,
+ contact: values.contact || "待完善",
+ phone: values.phone || "—",
+ region: values.region || "待完善",
+ projectCount: 0,
+ creditCode: values.creditCode || "",
+ industry: values.industry || "",
+ address: values.address || "",
+ status: values.status || "正常经营",
+ location: values.location || "",
+ principal: values.principal || "",
+ principalPhone: values.phone || "",
+ legalPerson: values.legalPerson || "",
+ legalPhone: values.legalPhone || "",
+ scale: values.scale || "",
+ };
+ setDataSource((prev) => [newCustomer, ...prev]);
+ setModalOpen(false);
+ message.success("安评客户已新建");
+ }).catch(() => {});
+ }, [modalForm, dataSource.length, createMode]);
+
+ const handleViewDetail = useCallback((record) => {
+ setCurrentDetail(record);
+ setDetailOpen(true);
+ }, []);
+
+ const columns = [
+ { title: "序号", width: 60, render: (_, __, index) => index + 1 },
+ { title: "客户名称", dataIndex: "name", ellipsis: true },
+ { title: "客户联系人", dataIndex: "contact", width: 120 },
+ { title: "联系人电话", dataIndex: "phone", width: 130 },
+ { title: "属地", dataIndex: "region", width: 100 },
+ {
+ title: "服务项目数", dataIndex: "projectCount", width: 110,
+ render: (count) => (
+
+ ),
+ },
+ {
+ title: "操作", width: 80, fixed: "right",
+ render: (_, record) => (
+
+ ),
+ },
+ ];
+
+ return (
+ } onClick={handleOpenCreate}>
+ 新建客户
+
+ }
+ >
+
+ 机构管理员维护服务客户,查看客户基本信息及历次安全评价项目。
+
+
+
+
+ ,
+
+
+ ,
+
+
+ {REGION_OPTIONS.map((r) => (
+ {r}
+ ))}
+
+ ,
+ ]}
+ onFinish={handleSearch}
+ onReset={handleReset}
+ style={{ marginBottom: 16 }}
+ />
+
+
+ 服务项目数可进入该客户的评价项目列表;操作栏仅查看客户详情。
+
+
+ `共 ${total} 条`,
+ pageSize: 10,
+ }}
+ />
+
+ {/* 新建客户弹窗 */}
+ setModalOpen(false)}
+ onOk={handleCreateSubmit}
+ width={800}
+ destroyOnClose
+ >
+
+ 优先从平台企业库选择;平台没有时可新建客户信息。
+
+
+
+
+
+ {createMode === "platform" ? (
+
+
+
+ ) : (
+
+
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* 客户详情弹窗 */}
+ setDetailOpen(false)}
+ footer={}
+ width={700}
+ destroyOnHidden
+ >
+ {currentDetail && (
+ <>
+
+ {currentDetail.name}
+
+ {currentDetail.status}
+
+ {currentDetail.creditCode}
+ 重庆市
+ {currentDetail.contact}
+ {currentDetail.phone}
+ {currentDetail.address}
+
+
+ 安全评价项目列表
+ {s},
+ },
+ ]}
+ pagination={false}
+ scroll={{ x: 1200,y: props.scrollY }}
+
+ />
+ >
+ )}
+
+
+ );
+};
+
+export default Connect(
+ [NS_CUSTOMER],
+ true,
+)(AntdTableFuncControl(CustomerManage));
\ No newline at end of file
diff --git a/src/pages/Container/SafetyEvalBusiness/ProjectFlow/index.js b/src/pages/Container/SafetyEvalBusiness/ProjectFlow/index.js
index b5fcda2..c9ca098 100644
--- a/src/pages/Container/SafetyEvalBusiness/ProjectFlow/index.js
+++ b/src/pages/Container/SafetyEvalBusiness/ProjectFlow/index.js
@@ -1,7 +1,9 @@
-import React, { useState } from "react";
-import { Tabs, Tag, Button, Flex } from "antd";
+import React, { useState, useEffect } from "react";
+import { Tabs, Tag, Flex } from "antd";
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
import { AntdTableFuncControl } from "@cqsjjb/jjb-common-decorator/antd";
+import { Connect } from "@cqsjjb/jjb-dva-runtime";
+import { NS_SAFETY_EVAL_BUSINESS } from "~/enumerate/namespace";
import WpsEditor from "~/pages/Container/WpsEditor";
import RiskAnalysisContent from "./RiskAnalysisContent";
import ContractContent from "./ContractContent";
@@ -14,40 +16,123 @@ import ControlContent from "./ControlContent";
import "./index.less";
const PROCESS_TABS = [
- { key: "risk", index: "1", label: "风险分析", state: "已完成", stateType: "success", role: "市场部经办人 / 市场部经理" },
- { key: "contract", index: "2", label: "业务合同", state: "已完成", stateType: "success", role: "机构管理员 / 项目负责人" },
- { key: "team", index: "3", label: "项目组", state: "已完成", stateType: "success", role: "评价部经理 / 机构主账号" },
- { key: "survey", index: "4", label: "现场踏勘", state: "已完成", stateType: "success", role: "项目组成员" },
- { key: "report", index: "5", label: "报告编制", state: "编制中", stateType: "processing", role: "报告编制人" },
- { key: "internal", index: "6", label: "内部审核", state: "待办理", stateType: "default", role: "内部审核人" },
- { key: "technical", index: "7", label: "技术审核", state: "待办理", stateType: "default", role: "技术负责人" },
- { key: "control", index: "8", label: "过程控制", state: "前置未完成", stateType: "warning", role: "过程控制负责人", locked: true },
+ {
+ key: "risk",
+ index: "1",
+ label: "风险分析",
+ state: "已完成",
+ stateType: "success",
+ role: "市场部经办人 / 市场部经理",
+ },
+ {
+ key: "contract",
+ index: "2",
+ label: "业务合同",
+ state: "已完成",
+ stateType: "success",
+ role: "机构管理员 / 项目负责人",
+ },
+ {
+ key: "team",
+ index: "3",
+ label: "项目组",
+ state: "已完成",
+ stateType: "success",
+ role: "评价部经理 / 机构主账号",
+ },
+ {
+ key: "survey",
+ index: "4",
+ label: "现场踏勘",
+ state: "已完成",
+ stateType: "success",
+ role: "项目组成员",
+ },
+ {
+ key: "report",
+ index: "5",
+ label: "报告编制",
+ state: "编制中",
+ stateType: "processing",
+ role: "报告编制人",
+ },
+ {
+ key: "internal",
+ index: "6",
+ label: "内部审核",
+ state: "待办理",
+ stateType: "default",
+ role: "内部审核人",
+ },
+ {
+ key: "technical",
+ index: "7",
+ label: "技术审核",
+ state: "待办理",
+ stateType: "default",
+ role: "技术负责人",
+ },
+ {
+ key: "control",
+ index: "8",
+ label: "过程控制",
+ state: "前置未完成",
+ stateType: "warning",
+ role: "过程控制负责人",
+ locked: true,
+ },
];
const ProjectFlow = (props) => {
const [activeKey, setActiveKey] = useState("risk");
const [visible, setVisible] = useState(false);
+ const { wpsDoc } = props.safetyEvalBusiness || {};
const handleTabChange = (key) => {
if (key === "report") {
- setVisible(true);
+ openReportEditor();
return;
}
setActiveKey(key);
};
+ const openReportEditor = async () => {
+ setVisible(true);
+ };
+
+ useEffect(() => {
+ props.queryMyOrCreateDoc({
+ docxUrl: ''
+ });
+ }, []);
+
const tabItems = PROCESS_TABS.map((item) => {
let children = null;
switch (item.key) {
- case "risk": children = ; break;
- case "contract": children = ; break;
- case "team": children = ; break;
- case "survey": children = ; break;
-
- case "internal": children = ; break;
- case "technical": children = ; break;
- case "control": children = ; break;
- default: children = null;
+ case "risk":
+ children = ;
+ break;
+ case "contract":
+ children = ;
+ break;
+ case "team":
+ children = ;
+ break;
+ case "survey":
+ children = ;
+ break;
+
+ case "internal":
+ children = ;
+ break;
+ case "technical":
+ children = ;
+ break;
+ case "control":
+ children = ;
+ break;
+ default:
+ children = null;
}
return {
@@ -56,7 +141,9 @@ const ProjectFlow = (props) => {
label: (
{item.index} {item.label}
- {item.state}
+
+ {item.state}
+
),
children,
@@ -68,16 +155,23 @@ const ProjectFlow = (props) => {
history={props.history}
previous
title={
-
+
项目法定流程工作台
-
华安化工园区安全预评价 | XP-2026-001
+
+ 华安化工园区安全预评价 | XP-2026-001
+
}
>
- {visible &&
setVisible(false)} />}
+ {visible && (
+ setVisible(false)} fileId={wpsDoc?.fileId} />
+ )}
);
};
-export default AntdTableFuncControl(ProjectFlow);
\ No newline at end of file
+export default Connect(
+ [NS_SAFETY_EVAL_BUSINESS],
+ true,
+)(AntdTableFuncControl(ProjectFlow));
diff --git a/src/pages/Container/SafetyEvalBusiness/index.js b/src/pages/Container/SafetyEvalBusiness/index.js
index baee37d..2eb2988 100644
--- a/src/pages/Container/SafetyEvalBusiness/index.js
+++ b/src/pages/Container/SafetyEvalBusiness/index.js
@@ -1,12 +1,5 @@
+import OrgInfoGuard from "~/components/OrgInfoGuard";
-import React from "react";
-
-function SafetyEvalBusiness(props) {
- return (
-
- props.children
-
- );
-}
-
-export default SafetyEvalBusiness;
+export default function SafetyEvalBusiness(props) {
+ return {props.children};
+}
\ No newline at end of file
diff --git a/src/pages/Container/WpsEditor/index.js b/src/pages/Container/WpsEditor/index.js
index 57b6529..a3613e6 100644
--- a/src/pages/Container/WpsEditor/index.js
+++ b/src/pages/Container/WpsEditor/index.js
@@ -16,7 +16,7 @@ import { createPortal } from "react-dom";
import "./index.less";
const App = (props) => {
- const { onCancel } = props;
+ const { onCancel, fileId } = props;
const [expandedMap, setExpandedMap] = useState({});
const sdkRef = useRef(null);
@@ -40,7 +40,7 @@ const App = (props) => {
officeType: WebOfficeSDK.OfficeType.Writer,
appId: "SX20260717TSGKEA",
mount: document.getElementById("app-sbsb"),
- fileId: "123",
+ fileId: fileId || "123",
token: sessionStorage.getItem("token"),
});
await instance.ready();