feat
parent
305208d91b
commit
2f68c75e8e
|
|
@ -10,9 +10,9 @@ module.exports = {
|
|||
javaGitBranch: "dev",
|
||||
// 本地联调 safetyEval-service(context-path: /safetyEval,默认端口 8095)
|
||||
// 可通过环境变量覆盖: SAFETY_EVAL_API_HOST=http://192.168.x.x:8095
|
||||
API_HOST: "https://gbs-gateway.qhdsafety.com",
|
||||
//API_HOST: "https://gbs-gateway.qhdsafety.com",
|
||||
//API_HOST: "http://192.168.0.150", //太浅
|
||||
//API_HOST: "http://192.168.0.152",
|
||||
API_HOST: "http://192.168.0.152",
|
||||
},
|
||||
production: {
|
||||
// 应用后端分支名称,部署上线需要
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
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 || {}',
|
||||
);
|
||||
|
|
@ -16,4 +16,50 @@ export const createWpsDoc = declareRequest(
|
|||
"createDocLoading",
|
||||
"Post > @/safetyEval-h5/wsp/doc/create",
|
||||
'wpsDoc: {} | res.data || {}',
|
||||
);
|
||||
|
||||
export const customerPage = declareRequest(
|
||||
"customerLoading",
|
||||
"Get > /safetyEval/institution/eval-customer/page",
|
||||
'customerPage: [] | res.data',
|
||||
);
|
||||
|
||||
export const customerSave = declareRequest(
|
||||
"customerSaveLoading",
|
||||
"Post > @/safetyEval/institution/eval-customer/save",
|
||||
);
|
||||
|
||||
export const customerModify = declareRequest(
|
||||
"customerModifyLoading",
|
||||
"Post > @/safetyEval/institution/eval-customer/modify",
|
||||
);
|
||||
|
||||
export const customerDetail = declareRequest(
|
||||
"customerDetailLoading",
|
||||
"Get > /safetyEval/institution/eval-customer/get",
|
||||
);
|
||||
|
||||
export const customerDelete = declareRequest(
|
||||
"customerDeleteLoading",
|
||||
"Post > @/safetyEval/institution/eval-customer/delete",
|
||||
);
|
||||
|
||||
export const customerProjectPage = declareRequest(
|
||||
"customerProjectLoading",
|
||||
"Get > /safetyEval/institution/eval-customer/project/page",
|
||||
);
|
||||
|
||||
export const evalProjectPage = declareRequest(
|
||||
"evalProjectLoading",
|
||||
"Get > /safetyEval/institution/eval-project/page",
|
||||
);
|
||||
|
||||
export const evalProjectSave = declareRequest(
|
||||
"evalProjectSaveLoading",
|
||||
"Post > @/safetyEval/institution/eval-project/save",
|
||||
);
|
||||
|
||||
export const evalProjectDetail = declareRequest(
|
||||
"evalProjectDetailLoading",
|
||||
"Get > /safetyEval/institution/eval-project/get",
|
||||
);
|
||||
|
|
@ -8,6 +8,7 @@ const BaiduMapPicker = ({ visible, onCancel, onConfirm }) => {
|
|||
const markerRef = useRef(null);
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
const [selectedPoint, setSelectedPoint] = useState(null);
|
||||
const [selectedAddress, setSelectedAddress] = useState('');
|
||||
|
||||
// 动态加载百度地图脚本
|
||||
useEffect(() => {
|
||||
|
|
@ -48,12 +49,21 @@ const BaiduMapPicker = ({ visible, onCancel, onConfirm }) => {
|
|||
map.addEventListener('click', (e) => {
|
||||
const point = e.point;
|
||||
setSelectedPoint(point);
|
||||
setSelectedAddress('');
|
||||
if (markerRef.current) {
|
||||
map.removeOverlay(markerRef.current);
|
||||
}
|
||||
const marker = new window.BMap.Marker(point);
|
||||
map.addOverlay(marker);
|
||||
markerRef.current = marker;
|
||||
|
||||
// 逆地理编码获取地址
|
||||
const geocoder = new window.BMap.Geocoder();
|
||||
geocoder.getLocation(point, (result) => {
|
||||
if (result?.address) {
|
||||
setSelectedAddress(result.address);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 搜索自动完成
|
||||
|
|
@ -69,6 +79,7 @@ const BaiduMapPicker = ({ visible, onCancel, onConfirm }) => {
|
|||
const poi = results.getPoi(0);
|
||||
const point = poi.point;
|
||||
setSelectedPoint(point);
|
||||
setSelectedAddress(poi.address || poi.title || '');
|
||||
map.centerAndZoom(point, 15);
|
||||
if (markerRef.current) {
|
||||
map.removeOverlay(markerRef.current);
|
||||
|
|
@ -96,6 +107,7 @@ const BaiduMapPicker = ({ visible, onCancel, onConfirm }) => {
|
|||
if (!visible) {
|
||||
setLoaded(false);
|
||||
setSelectedPoint(null);
|
||||
setSelectedAddress('');
|
||||
markerRef.current = null;
|
||||
mapInstanceRef.current = null;
|
||||
}
|
||||
|
|
@ -106,9 +118,11 @@ const BaiduMapPicker = ({ visible, onCancel, onConfirm }) => {
|
|||
message.warning('请在地图上选择位置');
|
||||
return;
|
||||
}
|
||||
|
||||
onConfirm({
|
||||
lng: selectedPoint.lng,
|
||||
lat: selectedPoint.lat,
|
||||
address: selectedAddress,
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ export const EXPERT_STATUS_MAP = {
|
|||
pending: { label: "待核验", color: "blue" },
|
||||
arranging: { label: "待安排", color: "warning" },
|
||||
passed: { label: "已核验", color: "success" },
|
||||
|
||||
};
|
||||
|
||||
/** 公示状态 */
|
||||
|
|
@ -48,10 +47,9 @@ export const FILING_STATUS_MAP = {
|
|||
|
||||
export const FILING_STATUS_MAP_CHANGE = {
|
||||
1: { label: "已备案", color: "success" },
|
||||
|
||||
|
||||
3: { label: "已打回", color: "error" },
|
||||
4: { label: "变更审核中", color: "processing" },
|
||||
|
||||
};
|
||||
|
||||
/** 变更状态 */
|
||||
|
|
@ -90,7 +88,7 @@ export const GENDER_MAP = {
|
|||
/** 审核结果 */
|
||||
export const REVIEW_RESULT_OPTIONS = [
|
||||
{ label: "通过", value: 1 },
|
||||
|
||||
|
||||
{ label: "不通过", value: 3 },
|
||||
];
|
||||
|
||||
|
|
@ -143,26 +141,26 @@ export const CERT_TYPE_MAP = {
|
|||
|
||||
/** 风险预警中心 — 预警大类映射 */
|
||||
export const RISK_TYPE_MAP = {
|
||||
"资质预警": { label: "资质预警", color: "error" },
|
||||
"项目预警": { label: "项目预警", color: "warning" },
|
||||
资质预警: { label: "资质预警", color: "error" },
|
||||
项目预警: { label: "项目预警", color: "warning" },
|
||||
};
|
||||
|
||||
/** 风险预警中心 — 处理阶段映射 */
|
||||
export const STAGE_MAP = {
|
||||
"未处理": { label: "未处理", color: "error" },
|
||||
"已发机构": { label: "已发机构", color: "warning" },
|
||||
"待复核": { label: "待复核", color: "processing" },
|
||||
"已闭环": { label: "已闭环", color: "success" },
|
||||
未处理: { label: "未处理", color: "error" },
|
||||
已发机构: { label: "已发机构", color: "warning" },
|
||||
待复核: { label: "待复核", color: "processing" },
|
||||
已闭环: { label: "已闭环", color: "success" },
|
||||
};
|
||||
|
||||
/** 风险预警中心 — 机构端状态映射 */
|
||||
export const ORG_STATUS_MAP = {
|
||||
"未发送": { label: "未发送", color: "error" },
|
||||
"机构未读": { label: "机构未读", color: "warning" },
|
||||
"已提交材料": { label: "已提交材料", color: "processing" },
|
||||
"处理中": { label: "处理中", color: "processing" },
|
||||
"已提交说明": { label: "已提交说明", color: "processing" },
|
||||
"整改完成": { label: "整改完成", color: "success" },
|
||||
未发送: { label: "未发送", color: "error" },
|
||||
机构未读: { label: "机构未读", color: "warning" },
|
||||
已提交材料: { label: "已提交材料", color: "processing" },
|
||||
处理中: { label: "处理中", color: "processing" },
|
||||
已提交说明: { label: "已提交说明", color: "processing" },
|
||||
整改完成: { label: "整改完成", color: "success" },
|
||||
};
|
||||
|
||||
/** 风险预警中心 — 预警子类选项 */
|
||||
|
|
@ -176,4 +174,143 @@ export const RISK_SUBTYPE_OPTIONS = [
|
|||
{ label: "从业告知异常", value: "从业告知异常" },
|
||||
{ label: "现场勘查异常", value: "现场勘查异常" },
|
||||
{ label: "过程控制/归档异常", value: "过程控制/归档异常" },
|
||||
];
|
||||
|
||||
export const district = [
|
||||
{
|
||||
code: "500101",
|
||||
name: "万州区",
|
||||
},
|
||||
{
|
||||
code: "500102",
|
||||
name: "涪陵区",
|
||||
},
|
||||
{
|
||||
code: "500103",
|
||||
name: "渝中区",
|
||||
},
|
||||
{
|
||||
code: "500104",
|
||||
name: "大渡口区",
|
||||
},
|
||||
{
|
||||
code: "500105",
|
||||
name: "江北区",
|
||||
},
|
||||
{
|
||||
code: "500106",
|
||||
name: "沙坪坝区",
|
||||
},
|
||||
{
|
||||
code: "500107",
|
||||
name: "九龙坡区",
|
||||
},
|
||||
{
|
||||
code: "500108",
|
||||
name: "南岸区",
|
||||
},
|
||||
{
|
||||
code: "500109",
|
||||
name: "北碚区",
|
||||
},
|
||||
{
|
||||
code: "500110",
|
||||
name: "綦江区",
|
||||
},
|
||||
{
|
||||
code: "500111",
|
||||
name: "大足区",
|
||||
},
|
||||
{
|
||||
code: "500112",
|
||||
name: "渝北区",
|
||||
},
|
||||
{
|
||||
code: "500113",
|
||||
name: "巴南区",
|
||||
},
|
||||
{
|
||||
code: "500114",
|
||||
name: "黔江区",
|
||||
},
|
||||
{
|
||||
code: "500115",
|
||||
name: "长寿区",
|
||||
},
|
||||
{
|
||||
code: "500116",
|
||||
name: "江津区",
|
||||
},
|
||||
{
|
||||
code: "500117",
|
||||
name: "合川区",
|
||||
},
|
||||
{
|
||||
code: "500118",
|
||||
name: "永川区",
|
||||
},
|
||||
{
|
||||
code: "500119",
|
||||
name: "南川区",
|
||||
},
|
||||
{
|
||||
code: "500120",
|
||||
name: "璧山区",
|
||||
},
|
||||
{
|
||||
code: "500151",
|
||||
name: "铜梁区",
|
||||
},
|
||||
{
|
||||
code: "500152",
|
||||
name: "潼南区",
|
||||
},
|
||||
{
|
||||
code: "500153",
|
||||
name: "荣昌区",
|
||||
},
|
||||
{
|
||||
code: "500154",
|
||||
name: "开州区",
|
||||
},
|
||||
{
|
||||
code: "500155",
|
||||
name: "梁平区",
|
||||
},
|
||||
{
|
||||
code: "500156",
|
||||
name: "武隆区",
|
||||
},
|
||||
];
|
||||
|
||||
/** 企业状态:1正常经营 2停产整改 3停业 4注销 */
|
||||
export const ENTERPRISE_STATUS_OPTIONS = [
|
||||
{ label: "正常经营", value: 1 },
|
||||
{ label: "停产整改", value: 2 },
|
||||
{ label: "停业", value: 3 },
|
||||
{ label: "注销", value: 4 },
|
||||
];
|
||||
|
||||
/** 企业规模:LARGE 大型 MEDIUM 中型 SMALL 小型 MICRO 微型 */
|
||||
export const ENTERPRISE_SCALE_OPTIONS = [
|
||||
{ label: "大型", value: "LARGE" },
|
||||
{ label: "中型", value: "MEDIUM" },
|
||||
{ label: "小型", value: "SMALL" },
|
||||
{ label: "微型", value: "MICRO" },
|
||||
];
|
||||
|
||||
|
||||
/** 评价类型选项 */
|
||||
export const EVAL_TYPE_OPTIONS = [
|
||||
{ label: "安全预评价", value: "PRE" },
|
||||
{ label: "安全设施竣工验收评价", value: "ACCEPT" },
|
||||
{ label: "安全现状评价", value: "STATUS" },
|
||||
];
|
||||
|
||||
/** 登记半径选项 */
|
||||
export const CHECKIN_RADIUS_OPTIONS = [
|
||||
{ label: "100 米", value: 100 },
|
||||
{ label: "200 米", value: 200 },
|
||||
{ label: "300 米", value: 300 },
|
||||
{ label: "500 米", value: 500 },
|
||||
];
|
||||
|
|
@ -154,6 +154,12 @@ export const PROFESSIONAL_LEVEL_OPTIONS = [
|
|||
},
|
||||
];
|
||||
|
||||
export const PROFESSIONAL_LEVEL_OPTIONS_MAP =
|
||||
PROFESSIONAL_LEVEL_OPTIONS.reduce(
|
||||
(acc, cur) => ({ ...acc, [cur.value]: cur.label }),
|
||||
{},
|
||||
);
|
||||
|
||||
/** 学历类型 */
|
||||
export const EDUCATION_TYPE_OPTIONS = [
|
||||
{ label: "全日制", value: "全日制" },
|
||||
|
|
|
|||
|
|
@ -23,4 +23,3 @@ 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");
|
||||
|
|
|
|||
|
|
@ -206,6 +206,11 @@ const menuItems = [
|
|||
label: "我的项目",
|
||||
icon: <FileTextOutlined />,
|
||||
},
|
||||
{
|
||||
key: "/safetyEval/container/SafetyEvalBusiness/EvalProject/List",
|
||||
label: "安评项目管理",
|
||||
icon: <FileTextOutlined />,
|
||||
},
|
||||
{
|
||||
key: "/safetyEval/container/SafetyEvalBusiness/CustomerManage",
|
||||
label: "安评客户管理",
|
||||
|
|
|
|||
|
|
@ -1,150 +1,211 @@
|
|||
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 React, { useState, useCallback, useEffect } from "react";
|
||||
import {
|
||||
Form,
|
||||
Table,
|
||||
Button,
|
||||
Modal,
|
||||
Input,
|
||||
Select,
|
||||
Row,
|
||||
Col,
|
||||
message,
|
||||
Descriptions,
|
||||
Flex,
|
||||
Space,
|
||||
} 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 { NS_SAFETY_EVAL_BUSINESS } from "~/enumerate/namespace";
|
||||
import { Connect } from "@cqsjjb/jjb-dva-runtime";
|
||||
import BaiduMapPicker from "~/components/BaiduMapPicker";
|
||||
import { tools } from "@cqsjjb/jjb-common-lib";
|
||||
import {
|
||||
district,
|
||||
ENTERPRISE_STATUS_OPTIONS,
|
||||
ENTERPRISE_SCALE_OPTIONS,
|
||||
} from "~/enumerate/constant";
|
||||
import { QUALIFICATION_INDUSTRY_OPTIONS } from "~/enumerate/enterpriseOptions";
|
||||
import { phoneRule, creditCodeRule } from "~/utils/validators";
|
||||
|
||||
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 { router } = tools;
|
||||
|
||||
const CustomerManage = (props) => {
|
||||
const [searchForm] = Form.useForm();
|
||||
const [modalForm] = Form.useForm();
|
||||
const [dataSource, setDataSource] = useState(MOCK_LIST);
|
||||
const [dataSource, setDataSource] = useState([]);
|
||||
const [total, setTotal] = useState(0);
|
||||
const [mapPickerVisible, setMapPickerVisible] = useState(false);
|
||||
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 [editingId, setEditingId] = useState(null);
|
||||
const {customerLoading, customerModifyLoading, customerSaveLoading}= props.safetyEvalBusiness;
|
||||
|
||||
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 getData = async (pagination) => {
|
||||
const params = {
|
||||
...router.query,
|
||||
current: pagination?.current || router.query.current || 1,
|
||||
size: pagination?.size || router.query.size || 10,
|
||||
};
|
||||
const res = await props.customerPage(params);
|
||||
if (res?.success !== false) {
|
||||
setDataSource(res?.data || []);
|
||||
setTotal(res?.total || 0);
|
||||
}
|
||||
};
|
||||
|
||||
const handleMapConfirm = (location) => {
|
||||
const { lng, lat, address } = location;
|
||||
|
||||
modalForm.setFieldsValue({ longitude: lng, latitude: lat, businessAddress: address });
|
||||
setMapPickerVisible(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
searchForm.setFieldsValue(router.query);
|
||||
getData();
|
||||
}, []);
|
||||
|
||||
const handleReset = useCallback(() => {
|
||||
searchForm.resetFields();
|
||||
setDataSource(MOCK_LIST);
|
||||
}, [searchForm]);
|
||||
const handleSearch = (values) => {
|
||||
router.query = { ...router.query, ...values, current: 1, size: 10 };
|
||||
getData();
|
||||
};
|
||||
|
||||
const handleOpenCreate = useCallback(() => {
|
||||
setCreateMode("platform");
|
||||
setSelectedPlatform(null);
|
||||
const handleReset = (values) => {
|
||||
searchForm.resetFields();
|
||||
router.query = { ...values, current: 1, size: 10 };
|
||||
getData();
|
||||
};
|
||||
|
||||
const handlePageChange = (pagination) => {
|
||||
router.query = {
|
||||
...router.query,
|
||||
current: pagination.current,
|
||||
size: pagination.pageSize,
|
||||
};
|
||||
getData(pagination);
|
||||
};
|
||||
|
||||
const handleOpenCreate = () => {
|
||||
setEditingId(null);
|
||||
setCurrentDetail(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 handleOpenEdit = async (record) => {
|
||||
const res = await props.customerDetail({ id: record.id });
|
||||
if (res?.success !== false) {
|
||||
setEditingId(record.id);
|
||||
setCurrentDetail(res?.data || {});
|
||||
modalForm.setFieldsValue(res?.data || {});
|
||||
setModalOpen(true);
|
||||
}
|
||||
};
|
||||
|
||||
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 = async (record) => {
|
||||
const res = await props.customerDetail({ id: record.id });
|
||||
if (res?.success !== false) {
|
||||
setCurrentDetail(res?.data || {});
|
||||
setDetailOpen(true);
|
||||
}
|
||||
};
|
||||
|
||||
const handleViewDetail = useCallback((record) => {
|
||||
setCurrentDetail(record);
|
||||
setDetailOpen(true);
|
||||
}, []);
|
||||
const handleDelete = async (record) => {
|
||||
Modal.confirm({
|
||||
title: "确认删除",
|
||||
content: "确定要删除这个客户吗?",
|
||||
onOk: async () => {
|
||||
await props.customerDelete({ data: record.id });
|
||||
message.success("删除成功");
|
||||
getData();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleCreateSubmit = () => {
|
||||
modalForm.validateFields().then(async (values) => {
|
||||
if (editingId) {
|
||||
await props.customerModify({ ...values, id: editingId });
|
||||
} else {
|
||||
await props.customerSave(values);
|
||||
}
|
||||
message.success(editingId ? "修改成功" : "创建成功");
|
||||
setModalOpen(false);
|
||||
getData();
|
||||
});
|
||||
};
|
||||
|
||||
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: "customerName", ellipsis: true, width: 220 },
|
||||
{ title: "统一社会信用代码", dataIndex: "creditCode", width: 220 },
|
||||
{ title: "客户联系人", dataIndex: "principalName", width: 150 },
|
||||
{ title: "联系人电话", dataIndex: "principalPhone", width: 160 },
|
||||
{
|
||||
title: "服务项目数", dataIndex: "projectCount", width: 110,
|
||||
title: "属地",
|
||||
dataIndex: "districtName",
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: "企业状态",
|
||||
dataIndex: "enterpriseStatusCode",
|
||||
width: 100,
|
||||
render: (status) => {
|
||||
const statusOption = ENTERPRISE_STATUS_OPTIONS.find(
|
||||
(opt) => opt.value === status,
|
||||
);
|
||||
return statusOption?.label || status;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "服务项目数",
|
||||
dataIndex: "projectCount",
|
||||
width: 110,
|
||||
render: (count) => (
|
||||
<Button type="link" size="small" style={{ padding: 0 }}>{count}</Button>
|
||||
<Button type="link" size="small" style={{ padding: 0 }}>
|
||||
{count || 0}
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "操作", width: 80, fixed: "right",
|
||||
title: "操作",
|
||||
width: 260,
|
||||
fixed: "right",
|
||||
render: (_, record) => (
|
||||
<Button type="link" size="small" onClick={() => handleViewDetail(record)}>查看</Button>
|
||||
<Space>
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
|
||||
onClick={() => handleViewDetail(record)}
|
||||
>
|
||||
查看
|
||||
</Button>
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
|
||||
onClick={() => handleOpenEdit(record)}
|
||||
>
|
||||
编辑
|
||||
</Button>
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
danger
|
||||
|
||||
onClick={() => handleDelete(record)}
|
||||
>
|
||||
删除
|
||||
</Button>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
|
@ -153,7 +214,11 @@ const CustomerManage = (props) => {
|
|||
<PageLayout
|
||||
title="安评客户管理"
|
||||
extra={
|
||||
<Button type="primary" icon={<PlusOutlined />} onClick={handleOpenCreate}>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<PlusOutlined />}
|
||||
onClick={handleOpenCreate}
|
||||
>
|
||||
新建客户
|
||||
</Button>
|
||||
}
|
||||
|
|
@ -164,18 +229,33 @@ const CustomerManage = (props) => {
|
|||
|
||||
<SearchForm
|
||||
form={searchForm}
|
||||
loading={false}
|
||||
loading={customerLoading}
|
||||
formLine={[
|
||||
<Form.Item key="name" name="name">
|
||||
<ControlWrapper.Input label="客户名称" placeholder="关键字查询" allowClear />
|
||||
<Form.Item key="customerName" name="customerName">
|
||||
<ControlWrapper.Input
|
||||
label="客户名称"
|
||||
placeholder="关键字查询"
|
||||
allowClear
|
||||
/>
|
||||
</Form.Item>,
|
||||
<Form.Item key="contact" name="contact">
|
||||
<ControlWrapper.Input label="客户联系人" placeholder="关键字查询" allowClear />
|
||||
<Form.Item key="principalName" name="principalName">
|
||||
<ControlWrapper.Input
|
||||
label="主要负责人"
|
||||
placeholder="关键字查询"
|
||||
allowClear
|
||||
/>
|
||||
</Form.Item>,
|
||||
<Form.Item key="region" name="region">
|
||||
<ControlWrapper.Select label="属地" placeholder="全部" allowClear style={{ width: "100%" }}>
|
||||
{REGION_OPTIONS.map((r) => (
|
||||
<Select.Option key={r} value={r}>{r}</Select.Option>
|
||||
<Form.Item key="districtCode" name="districtCode">
|
||||
<ControlWrapper.Select
|
||||
label="属地"
|
||||
placeholder="全部"
|
||||
allowClear
|
||||
style={{ width: "100%" }}
|
||||
>
|
||||
{district.map((opt) => (
|
||||
<Select.Option key={opt.code} value={opt.code}>
|
||||
{opt.name}
|
||||
</Select.Option>
|
||||
))}
|
||||
</ControlWrapper.Select>
|
||||
</Form.Item>,
|
||||
|
|
@ -185,86 +265,104 @@ const CustomerManage = (props) => {
|
|||
style={{ marginBottom: 16 }}
|
||||
/>
|
||||
|
||||
<div style={{ fontSize: 13, color: "#999", marginBottom: 12 }}>
|
||||
服务项目数可进入该客户的评价项目列表;操作栏仅查看客户详情。
|
||||
</div>
|
||||
|
||||
<Table
|
||||
rowKey="id"
|
||||
columns={columns}
|
||||
dataSource={dataSource}
|
||||
loading={false}
|
||||
scroll={{ y: props.scrollY }}
|
||||
loading={customerLoading}
|
||||
scroll={{ y: props.scrollY, x: 1200 }}
|
||||
pagination={{
|
||||
total: dataSource.length,
|
||||
total,
|
||||
current: router.query.current || 1,
|
||||
pageSize: router.query.size || 10,
|
||||
showSizeChanger: true,
|
||||
showQuickJumper: true,
|
||||
showTotal: (total) => `共 ${total} 条`,
|
||||
pageSize: 10,
|
||||
}}
|
||||
onChange={handlePageChange}
|
||||
/>
|
||||
|
||||
{/* 新建客户弹窗 */}
|
||||
{/* 新建/编辑客户弹窗 */}
|
||||
<Modal
|
||||
title="新建客户"
|
||||
title={editingId ? "编辑客户" : "新建客户"}
|
||||
open={modalOpen}
|
||||
onCancel={() => setModalOpen(false)}
|
||||
onOk={handleCreateSubmit}
|
||||
confirmLoading={editingId ? customerModifyLoading : customerSaveLoading}
|
||||
width={800}
|
||||
destroyOnHidden
|
||||
>
|
||||
<div style={{ fontSize: 13, color: "#666", marginBottom: 16 }}>
|
||||
优先从平台企业库选择;平台没有时可新建客户信息。
|
||||
</div>
|
||||
<Form form={modalForm} layout="vertical">
|
||||
<Form.Item label="建档方式">
|
||||
<Select
|
||||
value={createMode}
|
||||
onChange={(v) => { setCreateMode(v); setSelectedPlatform(null); modalForm.resetFields(); }}
|
||||
>
|
||||
<Select.Option value="platform">选择平台已有企业</Select.Option>
|
||||
<Select.Option value="new">新建客户信息</Select.Option>
|
||||
</Select>
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name="customerName"
|
||||
label="客户名称"
|
||||
rules={[{ required: true, message: "请输入客户名称" }]}
|
||||
>
|
||||
<Input placeholder="请输入" maxLength={100} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item name="creditCode" label="统一社会信用代码" rules={[creditCodeRule(false)]}>
|
||||
<Input placeholder="请输入" maxLength={18} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Form.Item name="districtCode" label="属地">
|
||||
<Select placeholder="请选择" allowClear>
|
||||
{district.map((opt) => (
|
||||
<Select.Option key={opt.code} value={opt.code}>
|
||||
{opt.name}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item name="industryCode" label="所属行业">
|
||||
<Select placeholder="请选择" allowClear>
|
||||
{QUALIFICATION_INDUSTRY_OPTIONS.map((opt) => (
|
||||
<Select.Option key={opt.value} value={opt.value}>
|
||||
{opt.label}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Form.Item label="经营地址">
|
||||
<Flex>
|
||||
<Form.Item name="businessAddress" noStyle>
|
||||
<Input placeholder="请输入经营地址" disabled maxLength={200} />
|
||||
</Form.Item>
|
||||
<Button onClick={() => setMapPickerVisible(true)}>选择</Button>
|
||||
</Flex>
|
||||
</Form.Item>
|
||||
|
||||
{createMode === "platform" ? (
|
||||
<Form.Item
|
||||
name="platformEnterprise"
|
||||
label="平台企业"
|
||||
rules={[{ required: true, message: "请选择平台企业" }]}
|
||||
>
|
||||
<Select
|
||||
placeholder="请选择平台企业"
|
||||
showSearch
|
||||
optionFilterProp="children"
|
||||
onChange={handlePlatformSelect}
|
||||
>
|
||||
{PLATFORM_ENTERPRISES.map((ep) => (
|
||||
<Select.Option key={ep.name} value={ep.name}>{ep.name}</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
) : (
|
||||
<Form.Item
|
||||
name="name"
|
||||
label="客户名称"
|
||||
rules={[{ required: true, message: "请输入客户名称" }]}
|
||||
>
|
||||
<Input placeholder="输入客户名称" />
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Form.Item name="creditCode" label="统一社会信用代码">
|
||||
<Input placeholder="请输入" />
|
||||
<Form.Item name="enterpriseStatusCode" label="企业状态">
|
||||
<Select placeholder="请选择" allowClear>
|
||||
{ENTERPRISE_STATUS_OPTIONS.map((opt) => (
|
||||
<Select.Option key={opt.value} value={opt.value}>
|
||||
{opt.label}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item name="region" label="属地">
|
||||
<Form.Item name="enterpriseScaleCode" label="企业规模">
|
||||
<Select placeholder="请选择" allowClear>
|
||||
{REGION_OPTIONS.map((r) => (
|
||||
<Select.Option key={r} value={r}>{r}</Select.Option>
|
||||
{ENTERPRISE_SCALE_OPTIONS.map((opt) => (
|
||||
<Select.Option key={opt.value} value={opt.value}>
|
||||
{opt.label}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
|
@ -273,78 +371,36 @@ const CustomerManage = (props) => {
|
|||
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Form.Item name="industry" label="所属行业">
|
||||
<Select placeholder="请选择" allowClear>
|
||||
{INDUSTRY_OPTIONS.map((r) => (
|
||||
<Select.Option key={r} value={r}>{r}</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
<Form.Item name="principalName" label="主要负责人">
|
||||
<Input placeholder="请输入" maxLength={50} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item name="status" label="企业状态">
|
||||
<Select placeholder="请选择" allowClear>
|
||||
{STATUS_OPTIONS.map((r) => (
|
||||
<Select.Option key={r} value={r}>{r}</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
<Form.Item name="principalPhone" label="负责人电话" rules={[phoneRule("负责人电话", false)]}>
|
||||
<Input placeholder="请输入" maxLength={20} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Form.Item name="address" label="经营地址">
|
||||
<Input placeholder="请输入经营地址" />
|
||||
<Form.Item name="legalRepresentative" label="法定代表人">
|
||||
<Input placeholder="请输入" maxLength={50} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item name="location" label="地理位置">
|
||||
<Input placeholder="定位后自动生成" />
|
||||
<Form.Item name="legalRepresentativePhone" label="法人电话" rules={[phoneRule("法人电话", false)]}>
|
||||
<Input placeholder="请输入" maxLength={20} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Form.Item name="contact" label="客户联系人">
|
||||
<Input placeholder="请输入" />
|
||||
</Form.Item>
|
||||
<Form.Item name="longitude" label="经度" noStyle></Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item name="phone" label="联系人电话">
|
||||
<Input placeholder="请输入" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Form.Item name="principal" label="主要负责人">
|
||||
<Input placeholder="请输入" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item name="legalPerson" label="法定代表人">
|
||||
<Input placeholder="请输入" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Form.Item name="legalPhone" label="法人电话">
|
||||
<Input placeholder="请输入" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item name="scale" label="企业规模">
|
||||
<Select placeholder="请选择" allowClear>
|
||||
{SCALE_OPTIONS.map((r) => (
|
||||
<Select.Option key={r} value={r}>{r}</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item name="latitude" label="纬度" noStyle></Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form>
|
||||
|
|
@ -352,7 +408,7 @@ const CustomerManage = (props) => {
|
|||
|
||||
{/* 客户详情弹窗 */}
|
||||
<Modal
|
||||
title={`客户详情 - ${currentDetail?.name || ""}`}
|
||||
title={`客户详情 - ${currentDetail?.customerName || ""}`}
|
||||
open={detailOpen}
|
||||
onCancel={() => setDetailOpen(false)}
|
||||
footer={<Button onClick={() => setDetailOpen(false)}>关闭</Button>}
|
||||
|
|
@ -361,48 +417,127 @@ const CustomerManage = (props) => {
|
|||
>
|
||||
{currentDetail && (
|
||||
<>
|
||||
<Descriptions column={2} bordered size="small" style={{ marginBottom: 16 }}>
|
||||
<Descriptions.Item label="客户名称">{currentDetail.name}</Descriptions.Item>
|
||||
<Descriptions.Item label="企业状态">
|
||||
{currentDetail.status}
|
||||
<Descriptions
|
||||
column={2}
|
||||
bordered
|
||||
size="small"
|
||||
style={{ marginBottom: 16 }}
|
||||
>
|
||||
<Descriptions.Item label="客户名称">
|
||||
{currentDetail.customerName}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="统一社会信用代码">
|
||||
{currentDetail.creditCode}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="企业状态">
|
||||
{(() => {
|
||||
const statusOption = ENTERPRISE_STATUS_OPTIONS.find(
|
||||
(opt) => opt.value === currentDetail.enterpriseStatusCode,
|
||||
);
|
||||
return (
|
||||
statusOption?.label || currentDetail.enterpriseStatusName
|
||||
);
|
||||
})()}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="企业规模">
|
||||
{(() => {
|
||||
const scaleOption = ENTERPRISE_SCALE_OPTIONS.find(
|
||||
(opt) => opt.value === currentDetail.enterpriseScaleCode,
|
||||
);
|
||||
return (
|
||||
scaleOption?.label || currentDetail.enterpriseScaleName
|
||||
);
|
||||
})()}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="所属行业">
|
||||
{currentDetail.industryName || currentDetail.industryCode}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="属地">
|
||||
{currentDetail.districtName}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="联系人">
|
||||
{currentDetail.principalName}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="联系电话">
|
||||
{currentDetail.principalPhone}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="主要负责人">
|
||||
{currentDetail.principalName}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="负责人电话">
|
||||
{currentDetail.principalPhone}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="法定代表人">
|
||||
{currentDetail.legalRepresentative}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="法人电话">
|
||||
{currentDetail.legalRepresentativePhone}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="经营地址" span={2}>
|
||||
{currentDetail.businessAddress}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="经度">
|
||||
{currentDetail.longitude}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="纬度">
|
||||
{currentDetail.latitude}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="统一社会信用代码">{currentDetail.creditCode}</Descriptions.Item>
|
||||
<Descriptions.Item label="所属区域">重庆市</Descriptions.Item>
|
||||
<Descriptions.Item label="联系人">{currentDetail.contact}</Descriptions.Item>
|
||||
<Descriptions.Item label="联系电话">{currentDetail.phone}</Descriptions.Item>
|
||||
<Descriptions.Item label="经营地址" span={2}>{currentDetail.address}</Descriptions.Item>
|
||||
</Descriptions>
|
||||
|
||||
<h4 style={{ margin: "12px 0 8px" }}>安全评价项目列表</h4>
|
||||
<Table
|
||||
rowKey="id"
|
||||
dataSource={[
|
||||
{ id: 1, name: `${currentDetail.name}安全现状评价`, type: "安全现状评价", date: "2026-06-15", status: "已完成" },
|
||||
{ id: 2, name: `${currentDetail.name}安全验收评价`, type: "安全验收评价", date: "2023-07-20", status: "已归档" },
|
||||
{ id: 3, name: `${currentDetail.name}安全预评价`, type: "安全预评价", date: "2020-08-12", status: "已归档" },
|
||||
{
|
||||
id: 1,
|
||||
name: `${currentDetail.customerName}安全现状评价`,
|
||||
type: "安全现状评价",
|
||||
date: "2026-06-15",
|
||||
status: "已完成",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: `${currentDetail.customerName}安全验收评价`,
|
||||
type: "安全验收评价",
|
||||
date: "2023-07-20",
|
||||
status: "已归档",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: `${currentDetail.customerName}安全预评价`,
|
||||
type: "安全预评价",
|
||||
date: "2020-08-12",
|
||||
status: "已归档",
|
||||
},
|
||||
]}
|
||||
columns={[
|
||||
{ title: "项目名称", dataIndex: "name" },
|
||||
{ title: "评价类别", dataIndex: "type", width: 120 },
|
||||
{ title: "报告完成日期", dataIndex: "date", width: 130 },
|
||||
{
|
||||
title: "项目状态", dataIndex: "status", width: 100,
|
||||
title: "项目状态",
|
||||
dataIndex: "status",
|
||||
width: 100,
|
||||
fixed: "right",
|
||||
render: (s) => <span style={{ color: "#52c41a" }}>{s}</span>,
|
||||
},
|
||||
]}
|
||||
pagination={false}
|
||||
scroll={{ x: 1200,y: props.scrollY }}
|
||||
|
||||
size="small"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Modal>
|
||||
<BaiduMapPicker
|
||||
visible={mapPickerVisible}
|
||||
onCancel={() => setMapPickerVisible(false)}
|
||||
onConfirm={handleMapConfirm}
|
||||
/>
|
||||
</PageLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default Connect(
|
||||
[NS_CUSTOMER],
|
||||
[NS_SAFETY_EVAL_BUSINESS],
|
||||
true,
|
||||
)(AntdTableFuncControl(CustomerManage));
|
||||
)(AntdTableFuncControl(CustomerManage));
|
||||
|
|
|
|||
|
|
@ -0,0 +1,269 @@
|
|||
import React, { useState, useEffect } from "react";
|
||||
import {
|
||||
Modal,
|
||||
TreeSelect,
|
||||
Input,
|
||||
Table,
|
||||
Button,
|
||||
Tag,
|
||||
Row,
|
||||
Col,
|
||||
Space,
|
||||
} from "antd";
|
||||
import { CloseOutlined } from "@ant-design/icons";
|
||||
import { NS_STAFF_INFO, NS_ORG_DEPARTMENT } from "~/enumerate/namespace";
|
||||
import { Connect } from "@cqsjjb/jjb-dva-runtime";
|
||||
import { QUALIFICATION_INDUSTRY_OPTIONS_MAP, PROFESSIONAL_LEVEL_OPTIONS_MAP, TITLE_LEVEL_MAP } from "~/enumerate/enterpriseOptions";
|
||||
|
||||
|
||||
|
||||
const StaffPickerModal = (props) => {
|
||||
const { open, onCancel, onConfirm, selectedIds = [] } = props;
|
||||
const { staffInfoList: dataSource, staffInfoTotal: total, staffInfoLoading: loading } = props.staffInfo || {};
|
||||
const { orgDepartmentTreeData } = props.orgDepartment || {};
|
||||
|
||||
const [deptId, setDeptId] = useState(undefined);
|
||||
const [keyword, setKeyword] = useState("");
|
||||
const [page, setPage] = useState({ current: 1, size: 10 });
|
||||
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
|
||||
// 用 Map 存储所有已选行,翻页后不丢失
|
||||
const [selectedRowMap, setSelectedRowMap] = useState({});
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
props.orgDepartmentTree();
|
||||
// 从父组件恢复已选数据
|
||||
const initialIds = selectedIds;
|
||||
const initialRows = props.selectedParticipants || [];
|
||||
setSelectedRowKeys(initialIds);
|
||||
const map = {};
|
||||
initialRows.forEach((r) => { map[r.id] = r; });
|
||||
setSelectedRowMap(map);
|
||||
fetchData({ current: 1, size: 10 });
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
const fetchData = (pagination) => {
|
||||
const params = {
|
||||
current: pagination?.current || page.current,
|
||||
size: pagination?.size || page.size,
|
||||
employmentStatusCode: 1,
|
||||
};
|
||||
if (deptId) params.deptId = deptId;
|
||||
if (keyword) params.userName = keyword;
|
||||
props.staffInfoList(params);
|
||||
setPage(params);
|
||||
};
|
||||
|
||||
const handleSearch = () => {
|
||||
fetchData({ current: 1, size: 10 });
|
||||
};
|
||||
|
||||
const handleTableChange = (pagination) => {
|
||||
fetchData(pagination);
|
||||
};
|
||||
|
||||
const handleSelectChange = (rowKeys, rows) => {
|
||||
setSelectedRowKeys(rowKeys);
|
||||
// rows 仅包含当前页的行,用 Map 合并新旧数据
|
||||
setSelectedRowMap((prev) => {
|
||||
const next = { ...prev };
|
||||
// 移除取消勾选的项
|
||||
Object.keys(next).forEach((key) => {
|
||||
if (!rowKeys.includes(key)) {
|
||||
delete next[key];
|
||||
}
|
||||
});
|
||||
// 添加/更新当前页勾选的行
|
||||
rows.forEach((row) => {
|
||||
next[row.id] = row;
|
||||
});
|
||||
return next;
|
||||
});
|
||||
};
|
||||
|
||||
const handleRemoveSelected = (record) => {
|
||||
const newKeys = selectedRowKeys.filter((id) => id !== record.id);
|
||||
setSelectedRowKeys(newKeys);
|
||||
setSelectedRowMap((prev) => {
|
||||
const next = { ...prev };
|
||||
delete next[record.id];
|
||||
return next;
|
||||
});
|
||||
};
|
||||
|
||||
const handleConfirm = () => {
|
||||
onConfirm(Object.values(selectedRowMap));
|
||||
};
|
||||
|
||||
const selectedRows = Object.values(selectedRowMap);
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: "用户名称",
|
||||
dataIndex: "userName",
|
||||
|
||||
width: 200,
|
||||
render: (text, record) => {
|
||||
const name = record.userName || "-";
|
||||
return (
|
||||
<Space size={4} wrap align="start">
|
||||
<span style={{ maxWidth: 100, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", display: "inline-block" }} title={name}>{name}</span>
|
||||
{record.technicalDirectorFlag && (
|
||||
<Tag color="blue" style={{ marginRight: 0, fontSize: 11 }}>技术</Tag>
|
||||
)}
|
||||
{record.processControlLeaderFlag && (
|
||||
<Tag color="purple" style={{ marginRight: 0, fontSize: 11 }}>过程</Tag>
|
||||
)}
|
||||
</Space>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
title: "资质范围",
|
||||
dataIndex: "qualScope",
|
||||
ellipsis: true,
|
||||
render: (code) => {
|
||||
const status = QUALIFICATION_INDUSTRY_OPTIONS_MAP[code];
|
||||
return status || "-";
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "职业等级",
|
||||
dataIndex: "professionalLevelCode",
|
||||
ellipsis: true,
|
||||
render: (code) => {
|
||||
const status = PROFESSIONAL_LEVEL_OPTIONS_MAP[code];
|
||||
return status || "-";
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "职称",
|
||||
dataIndex: "titleCode",
|
||||
width: 80,
|
||||
ellipsis: true,
|
||||
render: (code) => {
|
||||
const status = TITLE_LEVEL_MAP[code];
|
||||
return status || "-";
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title="选择项目参与人员"
|
||||
open={open}
|
||||
onCancel={onCancel}
|
||||
onOk={handleConfirm}
|
||||
width={1000}
|
||||
destroyOnHidden
|
||||
>
|
||||
<Row gutter={16}>
|
||||
<Col span={18}>
|
||||
<Space style={{ marginBottom: 12, width: "100%" }} size={12}>
|
||||
<TreeSelect
|
||||
placeholder="请选择部门"
|
||||
allowClear
|
||||
treeData={orgDepartmentTreeData}
|
||||
fieldNames={{ label: "deptName", value: "id", key: "id" }}
|
||||
treeDefaultExpandAll
|
||||
showSearch
|
||||
style={{ width: 240 }}
|
||||
value={deptId}
|
||||
onChange={(value) => {
|
||||
setDeptId(value);
|
||||
setTimeout(() => handleSearch(), 0);
|
||||
}}
|
||||
/>
|
||||
<Input
|
||||
placeholder="搜索用户名称"
|
||||
allowClear
|
||||
style={{ width: 240 }}
|
||||
value={keyword}
|
||||
onChange={(e) => setKeyword(e.target.value)}
|
||||
onPressEnter={handleSearch}
|
||||
/>
|
||||
<Button type="primary" onClick={handleSearch}>搜索</Button>
|
||||
</Space>
|
||||
<div style={{ marginBottom: 8, fontSize: 12, color: "#666" }}>
|
||||
<Tag color="blue" style={{ fontSize: 11, marginRight: 4 }}>技术</Tag> 技术负责人
|
||||
<Tag color="purple" style={{ fontSize: 11, margin: "0 4px 0 12px" }}>过程</Tag> 过程控制负责人
|
||||
</div>
|
||||
<Table
|
||||
rowKey="id"
|
||||
columns={columns}
|
||||
dataSource={dataSource}
|
||||
loading={loading}
|
||||
size="small"
|
||||
rowSelection={{
|
||||
selectedRowKeys,
|
||||
preserveSelectedRowKeys: true,
|
||||
onChange: handleSelectChange,
|
||||
}}
|
||||
pagination={{
|
||||
total,
|
||||
current: page.current,
|
||||
pageSize: page.size,
|
||||
showSizeChanger: true,
|
||||
showQuickJumper: true,
|
||||
showTotal: (t) => `共 ${t} 条`,
|
||||
onChange: (cur, size) => handleTableChange({ current: cur, size }),
|
||||
}}
|
||||
scroll={{ y: 400, x: 600 }}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<div style={{ fontSize: 13, fontWeight: 500, marginBottom: 8 }}>
|
||||
已选人员({selectedRows.length})
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
border: "1px solid #d9d9d9",
|
||||
borderRadius: 6,
|
||||
padding: 8,
|
||||
minHeight: 300,
|
||||
maxHeight: 460,
|
||||
overflow: "auto",
|
||||
}}
|
||||
>
|
||||
{selectedRows.length === 0 && (
|
||||
<div style={{ color: "#999", fontSize: 13, textAlign: "center", paddingTop: 120 }}>
|
||||
请从左侧勾选人员
|
||||
</div>
|
||||
)}
|
||||
{selectedRows.map((item) => (
|
||||
<div
|
||||
key={item.id}
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
padding: "6px 8px",
|
||||
borderBottom: "1px solid #f0f0f0",
|
||||
}}
|
||||
>
|
||||
|
||||
<div style={{ fontSize: 13, maxWidth: 140, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }} title={item.userName}>{item.userName}</div>
|
||||
|
||||
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
danger
|
||||
icon={<CloseOutlined />}
|
||||
onClick={() => handleRemoveSelected(item)}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default Connect(
|
||||
[NS_STAFF_INFO, NS_ORG_DEPARTMENT],
|
||||
true,
|
||||
)(StaffPickerModal);
|
||||
|
|
@ -0,0 +1,340 @@
|
|||
import React, { useState, useEffect } from "react";
|
||||
import {
|
||||
Form,
|
||||
Button,
|
||||
Input,
|
||||
Select,
|
||||
Row,
|
||||
Col,
|
||||
Card,
|
||||
message,
|
||||
DatePicker,
|
||||
Flex,
|
||||
Tag,
|
||||
} from "antd";
|
||||
import dayjs from "dayjs";
|
||||
import {tools} from "@cqsjjb/jjb-common-lib";
|
||||
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
|
||||
import { NS_SAFETY_EVAL_BUSINESS } from "~/enumerate/namespace";
|
||||
import { Connect } from "@cqsjjb/jjb-dva-runtime";
|
||||
import BaiduMapPicker from "~/components/BaiduMapPicker";
|
||||
import { district } from "~/enumerate/constant";
|
||||
import { phoneRule } from "~/utils/validators";
|
||||
import StaffPickerModal from "./StaffPickerModal";
|
||||
|
||||
import {
|
||||
EVAL_TYPE_OPTIONS,
|
||||
CHECKIN_RADIUS_OPTIONS,
|
||||
} from "~/enumerate/constant";
|
||||
|
||||
const { router } = tools;
|
||||
|
||||
const EvalProjectCreate = (props) => {
|
||||
const [form] = Form.useForm();
|
||||
const [mapPickerVisible, setMapPickerVisible] = useState(false);
|
||||
const [staffPickerVisible, setStaffPickerVisible] = useState(false);
|
||||
const [selectedParticipants, setSelectedParticipants] = useState([]);
|
||||
const [detailLoading, setDetailLoading] = useState(false);
|
||||
|
||||
const projectId = router.query?.id;
|
||||
const isView = projectId;
|
||||
|
||||
const { evalProjectSaveLoading, evalProjectDetailLoading, customerPage: customerList } = props.safetyEvalBusiness;
|
||||
|
||||
useEffect(() => {
|
||||
props.customerPage({ current: 1, size: 999 });
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!projectId) return;
|
||||
(async () => {
|
||||
setDetailLoading(true);
|
||||
try {
|
||||
const res = await props.evalProjectDetail({ id: projectId });
|
||||
if (res?.success !== false && res?.data) {
|
||||
const data = res.data;
|
||||
form.setFieldsValue({
|
||||
...data,
|
||||
planStartDate: data.planStartDate ? dayjs(data.planStartDate) : undefined,
|
||||
planEndDate: data.planEndDate ? dayjs(data.planEndDate) : undefined,
|
||||
});
|
||||
if (data.participants?.length) {
|
||||
setSelectedParticipants(
|
||||
data.participants.map((p) => ({ id: p.id, userName: p.name, ...p })),
|
||||
);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
setDetailLoading(false);
|
||||
}
|
||||
})();
|
||||
}, [projectId]);
|
||||
|
||||
const handleMapConfirm = (location) => {
|
||||
const { lng, lat, address } = location;
|
||||
form.setFieldsValue({
|
||||
longitude: lng,
|
||||
latitude: lat,
|
||||
customerAddress: address,
|
||||
});
|
||||
setMapPickerVisible(false);
|
||||
};
|
||||
|
||||
const handleSubmit = () => {
|
||||
form.validateFields().then(async (values) => {
|
||||
const { planStartDate, planEndDate, ...rest } = values;
|
||||
const params = {
|
||||
...rest,
|
||||
planStartDate: planStartDate?.format("YYYY-MM-DD"),
|
||||
planEndDate: planEndDate?.format("YYYY-MM-DD"),
|
||||
participants: selectedParticipants.map((item) => ({
|
||||
id: item.id,
|
||||
name: item.userName,
|
||||
})),
|
||||
};
|
||||
if (isView) {
|
||||
params.id = projectId;
|
||||
await props.evalProjectSave(params);
|
||||
message.success("修改成功");
|
||||
} else {
|
||||
await props.evalProjectSave(params);
|
||||
message.success("创建成功");
|
||||
}
|
||||
props.history.goBack();
|
||||
});
|
||||
};
|
||||
|
||||
const handleStaffConfirm = (rows) => {
|
||||
setSelectedParticipants(rows);
|
||||
setStaffPickerVisible(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<PageLayout
|
||||
title={isView ? "查看安全评价项目" : "新建安全评价项目"}
|
||||
history={props.history}
|
||||
previous
|
||||
footer={
|
||||
!isView && <Flex gap={24}>
|
||||
<Button onClick={() => props.history.goBack()}>取消</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={handleSubmit}
|
||||
loading={detailLoading || evalProjectSaveLoading}
|
||||
>
|
||||
提交并返回项目列表
|
||||
</Button>
|
||||
</Flex>
|
||||
}
|
||||
loading={detailLoading}
|
||||
>
|
||||
<Form form={form} layout="vertical" disabled={isView}>
|
||||
<Card title="项目基本信息" style={{ marginBottom: 24 }}>
|
||||
<Row gutter={24}>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name="projectName"
|
||||
label="项目名称"
|
||||
rules={[{ required: true, message: "请输入项目名称" }]}
|
||||
>
|
||||
<Input placeholder="请输入项目名称" maxLength={100} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item label="项目编号" name="projectNo">
|
||||
<Input placeholder="提交后自动生成" disabled />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row gutter={24}>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name="evalTypeCode"
|
||||
label="评价类型"
|
||||
rules={[{ required: true, message: "请选择评价类型" }]}
|
||||
>
|
||||
<Select placeholder="请选择" allowClear>
|
||||
{EVAL_TYPE_OPTIONS.map((opt) => (
|
||||
<Select.Option key={opt.value} value={opt.value}>
|
||||
{opt.label}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name="customerId"
|
||||
label="被评价客户"
|
||||
rules={[{ required: true, message: "请选择被评价客户" }]}
|
||||
>
|
||||
<Select
|
||||
placeholder="请选择"
|
||||
allowClear
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
onChange={(value, option) => {
|
||||
const customer = customerList?.find((item) => item.id === value);
|
||||
console.log(customer);
|
||||
form.setFieldsValue({
|
||||
customerContactName: customer?.principalName || "",
|
||||
customerContactPhone: customer?.principalPhone || "",
|
||||
});
|
||||
}}
|
||||
>
|
||||
{(customerList || []).map((item) => (
|
||||
<Select.Option key={item.id} value={item.id}>
|
||||
{item.customerName}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row gutter={24}>
|
||||
<Col span={12}>
|
||||
<Form.Item name="customerContactName" label="客户负责人">
|
||||
<Input placeholder="请输入客户负责人" maxLength={50} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name="customerContactPhone"
|
||||
label="联系电话"
|
||||
rules={[phoneRule("联系电话", false)]}
|
||||
>
|
||||
<Input placeholder="请输入客户负责人联系电话" maxLength={20} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row gutter={24}>
|
||||
<Col span={12}>
|
||||
<Form.Item name="districtCode" label="项目所在地">
|
||||
<Select
|
||||
placeholder="请选择"
|
||||
allowClear
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
>
|
||||
{district.map((opt) => (
|
||||
<Select.Option key={opt.code} value={opt.code}>
|
||||
{opt.name}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item name="projectLeaderName" label="项目负责人">
|
||||
<Input placeholder="请输入" maxLength={50} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row gutter={24}>
|
||||
<Col span={12}>
|
||||
<Form.Item name="planStartDate" label="项目开始时间">
|
||||
<DatePicker style={{ width: "100%" }} placeholder="请选择" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item name="planEndDate" label="项目完成时间">
|
||||
<DatePicker style={{ width: "100%" }} placeholder="请选择" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
|
||||
<Card title="客户所在地与打卡定位" style={{ marginBottom: 24 }}>
|
||||
<Form.Item label="客户所在地" style={{ marginBottom: 16 }}>
|
||||
<Flex>
|
||||
<Form.Item name="customerAddress" noStyle>
|
||||
<Input
|
||||
placeholder="请选择客户或打开地图定位"
|
||||
disabled
|
||||
maxLength={200}
|
||||
style={{ width: 400 }}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Button
|
||||
onClick={() => setMapPickerVisible(true)}
|
||||
style={{ marginLeft: 8 }}
|
||||
>
|
||||
选择地图位置
|
||||
</Button>
|
||||
</Flex>
|
||||
</Form.Item>
|
||||
<Row gutter={24}>
|
||||
<Col span={6}>
|
||||
<Form.Item name="longitude" label="经度">
|
||||
<Input placeholder="自动获取" disabled />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Form.Item name="latitude" label="纬度">
|
||||
<Input placeholder="自动获取" disabled />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Form.Item name="checkinRadiusMeters" label="打卡有效范围">
|
||||
<Select placeholder="请选择" allowClear>
|
||||
{CHECKIN_RADIUS_OPTIONS.map((opt) => (
|
||||
<Select.Option key={opt.value} value={opt.value}>
|
||||
{opt.label}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
<Card title="项目参与人员">
|
||||
<Form.Item label="参与人员" required>
|
||||
<Input
|
||||
placeholder="点击选择参与人员"
|
||||
readOnly
|
||||
value={
|
||||
selectedParticipants.length
|
||||
? `已选 ${selectedParticipants.length} 人`
|
||||
: ""
|
||||
}
|
||||
onClick={() => setStaffPickerVisible(true)}
|
||||
style={{ cursor: "pointer", width: 400 }}
|
||||
/>
|
||||
{selectedParticipants.length > 0 && (
|
||||
<Flex wrap style={{ marginTop: 8, gap: 4 }}>
|
||||
{selectedParticipants.map((item) => (
|
||||
<Tag
|
||||
key={item.id}
|
||||
closable={!isView}
|
||||
onClose={() =>
|
||||
setSelectedParticipants((prev) =>
|
||||
prev.filter((r) => r.id !== item.id),
|
||||
)
|
||||
}
|
||||
>
|
||||
{item.userName}
|
||||
</Tag>
|
||||
))}
|
||||
</Flex>
|
||||
)}
|
||||
</Form.Item>
|
||||
</Card>
|
||||
</Form>
|
||||
|
||||
<StaffPickerModal
|
||||
open={staffPickerVisible}
|
||||
onCancel={() => setStaffPickerVisible(false)}
|
||||
onConfirm={handleStaffConfirm}
|
||||
selectedIds={selectedParticipants.map((item) => item.id)}
|
||||
selectedParticipants={selectedParticipants}
|
||||
/>
|
||||
<BaiduMapPicker
|
||||
visible={mapPickerVisible}
|
||||
onCancel={() => setMapPickerVisible(false)}
|
||||
onConfirm={handleMapConfirm}
|
||||
/>
|
||||
</PageLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default Connect([NS_SAFETY_EVAL_BUSINESS], true)(EvalProjectCreate);
|
||||
|
|
@ -0,0 +1,187 @@
|
|||
import React, { useState, useEffect } from "react";
|
||||
import {
|
||||
Form,
|
||||
Table,
|
||||
Button,
|
||||
Input,
|
||||
Select,
|
||||
Space,
|
||||
Tag,
|
||||
} 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_SAFETY_EVAL_BUSINESS } from "~/enumerate/namespace";
|
||||
import { Connect } from "@cqsjjb/jjb-dva-runtime";
|
||||
import { tools } from "@cqsjjb/jjb-common-lib";
|
||||
|
||||
const { router } = tools;
|
||||
|
||||
const EvalProject = (props) => {
|
||||
const [searchForm] = Form.useForm();
|
||||
const [dataSource, setDataSource] = useState([]);
|
||||
const [total, setTotal] = useState(0);
|
||||
|
||||
const { evalProjectLoading } = props.safetyEvalBusiness;
|
||||
|
||||
const getData = async (pagination) => {
|
||||
const params = {
|
||||
...router.query,
|
||||
current: pagination?.current || router.query.current || 1,
|
||||
size: pagination?.size || router.query.size || 10,
|
||||
};
|
||||
const res = await props.evalProjectPage(params);
|
||||
if (res?.success !== false) {
|
||||
setDataSource(res?.data || []);
|
||||
setTotal(res?.total || 0);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
searchForm.setFieldsValue(router.query);
|
||||
getData();
|
||||
}, []);
|
||||
|
||||
const handleSearch = (values) => {
|
||||
router.query = { ...router.query, ...values, current: 1, size: 10 };
|
||||
getData();
|
||||
};
|
||||
|
||||
const handleReset = (values) => {
|
||||
searchForm.resetFields();
|
||||
router.query = { ...values, current: 1, size: 10 };
|
||||
getData();
|
||||
};
|
||||
|
||||
const handlePageChange = (pagination) => {
|
||||
router.query = {
|
||||
...router.query,
|
||||
current: pagination.current,
|
||||
size: pagination.pageSize,
|
||||
};
|
||||
getData(pagination);
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{ title: "序号", width: 60, render: (_, __, index) => index + 1 },
|
||||
{
|
||||
title: "项目名称",
|
||||
dataIndex: "projectName",
|
||||
ellipsis: true,
|
||||
width: 220,
|
||||
render: (text, record) => (
|
||||
<div>
|
||||
<div>{text}</div>
|
||||
<div style={{ fontSize: 12, color: "#999" }}>
|
||||
{record.projectNo} · {record.evalTypeName}
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{ title: "项目负责人", dataIndex: "projectLeaderName", width: 120 },
|
||||
{ title: "客户负责人", dataIndex: "customerContactName", width: 120 },
|
||||
{ title: "项目开始时间", dataIndex: "planStartDate", width: 120 },
|
||||
{ title: "项目结束时间", dataIndex: "planEndDate", width: 120 },
|
||||
{
|
||||
title: "项目阶段",
|
||||
dataIndex: "projectPhaseName",
|
||||
width: 100,
|
||||
render: (phase) => {
|
||||
const color = phase === "延期" ? "error" : phase === "正常" ? "success" : undefined;
|
||||
return <Tag color={color}>{phase || "-"}</Tag>;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
width: 100,
|
||||
fixed: "right",
|
||||
render: (_, record) => (
|
||||
<Space>
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
onClick={() => props.history.push(`Create?id=${record.id}`)}
|
||||
>
|
||||
查看
|
||||
</Button>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<PageLayout
|
||||
title="安评项目管理"
|
||||
extra={
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<PlusOutlined />}
|
||||
onClick={() => props.history.push("Create")}
|
||||
>
|
||||
新建项目
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<div style={{ fontSize: 13, color: "#666", marginBottom: 16 }}>
|
||||
管理机构全部安全评价项目;各业务节点可独立维护,不以系统顺序替代机构线下实操。
|
||||
</div>
|
||||
|
||||
<SearchForm
|
||||
form={searchForm}
|
||||
loading={false}
|
||||
formLine={[
|
||||
<Form.Item key="projectName" name="projectName">
|
||||
<ControlWrapper.Input
|
||||
label="项目名称"
|
||||
placeholder="关键字查询"
|
||||
allowClear
|
||||
/>
|
||||
</Form.Item>,
|
||||
<Form.Item key="projectLeaderName" name="projectLeaderName">
|
||||
<ControlWrapper.Input
|
||||
label="项目负责人"
|
||||
placeholder="姓名"
|
||||
allowClear
|
||||
/>
|
||||
</Form.Item>,
|
||||
<Form.Item key="customerContactName" name="customerContactName">
|
||||
<ControlWrapper.Input
|
||||
label="客户负责人"
|
||||
placeholder="姓名"
|
||||
allowClear
|
||||
/>
|
||||
</Form.Item>,
|
||||
]}
|
||||
onFinish={handleSearch}
|
||||
onReset={handleReset}
|
||||
style={{ marginBottom: 16 }}
|
||||
/>
|
||||
|
||||
<Table
|
||||
rowKey="id"
|
||||
columns={columns}
|
||||
dataSource={dataSource}
|
||||
loading={evalProjectLoading}
|
||||
scroll={{ y: props.scrollY, x: 1000 }}
|
||||
pagination={{
|
||||
total,
|
||||
current: router.query.current || 1,
|
||||
pageSize: router.query.size || 10,
|
||||
showSizeChanger: true,
|
||||
showQuickJumper: true,
|
||||
showTotal: (total) => `共 ${total} 条`,
|
||||
}}
|
||||
onChange={handlePageChange}
|
||||
/>
|
||||
</PageLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default Connect(
|
||||
[NS_SAFETY_EVAL_BUSINESS],
|
||||
true,
|
||||
)(AntdTableFuncControl(EvalProject));
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
function EvalProject(props) {
|
||||
return (
|
||||
|
||||
props.children
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
export default EvalProject;
|
||||
Loading…
Reference in New Issue