Merge branch 'dev' of http://47.92.113.182:3000/cq_anquan/safety-eval-service-frontend into dev
commit
e96c72adb5
|
|
@ -27,7 +27,14 @@
|
|||
basename: '<%= $basename %>',
|
||||
API_HOST: '<%= $API_HOST %>'
|
||||
};
|
||||
APP_ENV.API_HOST = sessionStorage.API_HOST || APP_ENV.API_HOST || window.location.origin;
|
||||
const injectedApiHost = APP_ENV.API_HOST;
|
||||
const isDev = '<%= $mode %>' === 'development';
|
||||
// 开发环境优先 jjb.config 注入的 API_HOST,避免 sessionStorage 残留网关地址导致 Network Error
|
||||
if (isDev && injectedApiHost && injectedApiHost.indexOf('http') === 0) {
|
||||
APP_ENV.API_HOST = injectedApiHost;
|
||||
} else {
|
||||
APP_ENV.API_HOST = sessionStorage.API_HOST || injectedApiHost || window.location.origin;
|
||||
}
|
||||
window.process = {
|
||||
env: { app: APP_ENV },
|
||||
NODE_ENV: '<%= $mode %>'
|
||||
|
|
@ -38,6 +45,46 @@
|
|||
FRAMEWORK: APP_ENV.antd
|
||||
};
|
||||
})();
|
||||
|
||||
// 抑制 ResizeObserver 在页面/标签切换时的无害告警,避免 dev overlay 误报
|
||||
(function () {
|
||||
if (typeof window.ResizeObserver !== 'undefined') {
|
||||
var NativeResizeObserver = window.ResizeObserver;
|
||||
window.ResizeObserver = class extends NativeResizeObserver {
|
||||
constructor(callback) {
|
||||
super(function (entries, observer) {
|
||||
window.requestAnimationFrame(function () {
|
||||
callback(entries, observer);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function isResizeObserverLoopError(message) {
|
||||
return typeof message === 'string' && message.indexOf('ResizeObserver loop') !== -1;
|
||||
}
|
||||
|
||||
function isAxiosNetworkError(reason) {
|
||||
if (!reason) return false;
|
||||
var msg = typeof reason === 'string' ? reason : (reason.message || '');
|
||||
return msg === 'Network Error' || (reason.isAxiosError && msg === 'Network Error');
|
||||
}
|
||||
|
||||
window.addEventListener('error', function (event) {
|
||||
if (isResizeObserverLoopError(event.message)) {
|
||||
event.stopImmediatePropagation();
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('unhandledrejection', function (event) {
|
||||
if (isAxiosNetworkError(event.reason)) {
|
||||
console.warn('[dev] API unreachable:', event.reason?.config?.url || event.reason?.message);
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
<!-- SCRIPTS -->
|
||||
<% for (const item of $scripts) { %>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/** 企业信息管理:前后端字段与分页格式适配 */
|
||||
|
||||
import { formatDate, formatDateTime, toApiDate, toApiDateTime } from "../../utils/dateFormat";
|
||||
import { formatDate, formatDateTime, normalizeDateValue, toApiDate, toApiDateTime } from "../../utils/dateFormat";
|
||||
import { resolveUploadFileIds } from "../../utils/mockUpload";
|
||||
import { asId, isIdFieldName, normalizeQueryIds } from "./idUtil";
|
||||
import { withOrgId } from "./orgContext";
|
||||
|
|
@ -162,6 +162,102 @@ export function fromOrgInfoForm(values = {}, options = {}) {
|
|||
};
|
||||
}
|
||||
|
||||
/** 监管端机构账号:后端 state 0=启用,1=禁用 */
|
||||
export function isOrgAccountEnabled(record = {}) {
|
||||
return Number(record.state) === 0;
|
||||
}
|
||||
|
||||
export function toOrgAccountRow(data = {}) {
|
||||
return {
|
||||
id: asId(data.id),
|
||||
orgName: data.unitName,
|
||||
district: data.districtName,
|
||||
state: data.state,
|
||||
createTime: formatDate(normalizeDateValue(data.createTime)),
|
||||
};
|
||||
}
|
||||
|
||||
/** 监管端分页查询(不带 orgInfoId;部分筛选项需后端 OrgInfoPageQuery 扩展后生效) */
|
||||
export function toRegulatorOrgAccountPageQuery(params = {}) {
|
||||
const query = {
|
||||
current: params.pageIndex ?? params.current ?? 1,
|
||||
size: params.pageSize ?? params.size ?? 10,
|
||||
};
|
||||
if (params.orgName) {
|
||||
query.unitName = params.orgName;
|
||||
}
|
||||
if (params.district) {
|
||||
query.districtName = params.district;
|
||||
}
|
||||
const state = params.state;
|
||||
if (state !== undefined && state !== null && state !== "" && state !== "全部") {
|
||||
query.state = Number(state);
|
||||
}
|
||||
const range = params.openTimeRange;
|
||||
if (Array.isArray(range) && range.length === 2 && range[0] && range[1]) {
|
||||
query.createTimeStart = range[0].format ? range[0].format("YYYY-MM-DD") : range[0];
|
||||
query.createTimeEnd = range[1].format ? range[1].format("YYYY-MM-DD") : range[1];
|
||||
}
|
||||
return normalizeQueryIds(query);
|
||||
}
|
||||
|
||||
/** 监管端已备案机构列表行 */
|
||||
export function toRegisteredOrgRow(data = {}) {
|
||||
return {
|
||||
id: asId(data.id),
|
||||
orgName: data.unitName,
|
||||
registerAddress: data.registerAddress,
|
||||
serviceEnterpriseCount: 0,
|
||||
evalProjectCount: 0,
|
||||
filingType: data.filingTypeName,
|
||||
filingRecordStatusCode: data.filingRecordStatusCode,
|
||||
filingRecordStatusName: data.filingRecordStatusName,
|
||||
};
|
||||
}
|
||||
|
||||
/** 监管端已备案机构分页查询 */
|
||||
export function toRegisteredOrgPageQuery(params = {}) {
|
||||
const query = {
|
||||
current: params.pageIndex ?? params.current ?? 1,
|
||||
size: params.pageSize ?? params.size ?? 10,
|
||||
};
|
||||
if (params.orgName) {
|
||||
query.unitName = params.orgName;
|
||||
}
|
||||
if (params.registerAddress) {
|
||||
query.registerAddress = params.registerAddress;
|
||||
}
|
||||
const filingTypeCode = params.filingType;
|
||||
if (filingTypeCode !== undefined && filingTypeCode !== null && filingTypeCode !== "") {
|
||||
query.filingTypeCode = String(filingTypeCode);
|
||||
}
|
||||
const filingRecordStatusCode = params.filingRecordStatus;
|
||||
if (filingRecordStatusCode !== undefined && filingRecordStatusCode !== null && filingRecordStatusCode !== "") {
|
||||
query.filingRecordStatusCode = Number(filingRecordStatusCode);
|
||||
}
|
||||
return normalizeQueryIds(query);
|
||||
}
|
||||
|
||||
export function fromRegulatorOrgInfoModify(values = {}, existing = {}) {
|
||||
const merged = {
|
||||
...toOrgInfoForm(existing),
|
||||
...values,
|
||||
id: asId(values.id ?? existing.id),
|
||||
};
|
||||
const payload = fromOrgInfoForm(merged, { isDraft: false });
|
||||
return {
|
||||
...payload,
|
||||
id: asId(values.id ?? existing.id),
|
||||
authStatusCode: existing.authStatusCode ?? payload.authStatusCode,
|
||||
authStatusName: existing.authStatusName ?? payload.authStatusName,
|
||||
tenantId: existing.tenantId ?? payload.tenantId,
|
||||
filingTypeCode: existing.filingTypeCode ?? payload.filingTypeCode,
|
||||
filingTypeName: existing.filingTypeName ?? payload.filingTypeName,
|
||||
filingRecordStatusCode: existing.filingRecordStatusCode ?? payload.filingRecordStatusCode,
|
||||
filingRecordStatusName: existing.filingRecordStatusName ?? payload.filingRecordStatusName,
|
||||
};
|
||||
}
|
||||
|
||||
// ─── 部门 / 岗位 ───
|
||||
|
||||
export function toDepartmentForm(data = {}) {
|
||||
|
|
@ -411,6 +507,68 @@ export function fromQualificationForm(values = {}) {
|
|||
};
|
||||
}
|
||||
|
||||
function isQualificationExpireWarning(dateStr) {
|
||||
if (!dateStr) {
|
||||
return false;
|
||||
}
|
||||
const end = new Date(String(dateStr).replace(/-/g, "/"));
|
||||
if (Number.isNaN(end.getTime())) {
|
||||
return false;
|
||||
}
|
||||
const diff = end.getTime() - Date.now();
|
||||
return diff > 0 && diff < 90 * 24 * 3600 * 1000;
|
||||
}
|
||||
|
||||
/** classGet 分组 Map → 已备案机构详情「申请清单材料」结构 */
|
||||
export function fromQualificationClassGetResponse(res) {
|
||||
const map = res?.data;
|
||||
if (!map || typeof map !== "object") {
|
||||
return [];
|
||||
}
|
||||
return Object.entries(map).map(([code, list], index) => {
|
||||
const items = (Array.isArray(list) ? list : []).map(toQualificationForm);
|
||||
const scopeName = items[0]?.certType || code || `资质${index + 1}`;
|
||||
const expireDate = items.reduce((latest, item) => {
|
||||
const d = item.validEndDate;
|
||||
if (!d) {
|
||||
return latest;
|
||||
}
|
||||
return !latest || d > latest ? d : latest;
|
||||
}, "");
|
||||
return {
|
||||
id: code || String(index),
|
||||
scopeName,
|
||||
expireDate: expireDate || "-",
|
||||
expireWarning: isQualificationExpireWarning(expireDate),
|
||||
materials: items.map((item, idx) => {
|
||||
const previewUrl = item.certImgFiles?.[0]?.url || item.certImgs?.[0]?.url;
|
||||
return {
|
||||
id: item.id || `${code}-${idx}`,
|
||||
name: item.certName || item.certType || "-",
|
||||
required: true,
|
||||
status: previewUrl ? "uploaded" : "pending",
|
||||
previewUrl,
|
||||
raw: item,
|
||||
};
|
||||
}),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/** 已备案机构详情人员列表行 */
|
||||
export function toRegisteredOrgPersonnelRow(data = {}) {
|
||||
const form = toStaffForm(data);
|
||||
return {
|
||||
id: form.id,
|
||||
name: form.staffName,
|
||||
gender: GENDER_NAME[form.gender] || "-",
|
||||
position: form.positionName,
|
||||
qualScope: form.qualScope,
|
||||
education: form.education || form.educationLevel,
|
||||
registerEngineer: form.registerEngineerFlag === 1 ? 1 : 0,
|
||||
};
|
||||
}
|
||||
|
||||
// ─── 装备 ───
|
||||
|
||||
/** 装备 enableFlag(1启用/2禁用) ↔ 前端 enableStatus(1启用/0禁用) */
|
||||
|
|
|
|||
|
|
@ -45,20 +45,20 @@ export async function apiGet(path, params = {}, headers = {}, options = {}) {
|
|||
}
|
||||
|
||||
/** @ 前缀表示 Post 请求体不包裹 request 字段 */
|
||||
export async function apiPost(path, data = {}, headers = {}) {
|
||||
export async function apiPost(path, data = {}, headers = {}, options = {}) {
|
||||
const url = path.startsWith("@") ? path : `@${path}`;
|
||||
try {
|
||||
return await Post(url, withIds(data), buildRequestHeaders(headers));
|
||||
return await Post(url, withIds(data), buildRequestHeaders(headers, options));
|
||||
}
|
||||
catch (err) {
|
||||
return parseHttpError(err);
|
||||
}
|
||||
}
|
||||
|
||||
export async function apiPostDelete(path, id) {
|
||||
export async function apiPostDelete(path, id, options = {}) {
|
||||
const url = `${path}?id=${encodeURIComponent(asId(id) ?? "")}`;
|
||||
try {
|
||||
return await Post(url, {}, buildRequestHeaders());
|
||||
return await Post(url, {}, buildRequestHeaders({}, options));
|
||||
}
|
||||
catch (err) {
|
||||
return parseHttpError(err);
|
||||
|
|
|
|||
|
|
@ -52,3 +52,15 @@ export const orgInfoDraft = declareRequest("orgInfoLoading", safeAction(async (v
|
|||
persistOrgInfoId(result, res?.data);
|
||||
return result;
|
||||
}));
|
||||
|
||||
export {
|
||||
orgAccountList,
|
||||
orgAccountGet,
|
||||
orgAccountModify,
|
||||
orgAccountDelete,
|
||||
orgAccountUpdateState,
|
||||
orgAccountResetPassword,
|
||||
registeredOrgList,
|
||||
registeredOrgGet,
|
||||
fetchRegisteredOrgDetail,
|
||||
} from "../regulatorOrgInfo";
|
||||
|
|
|
|||
|
|
@ -0,0 +1,108 @@
|
|||
import { declareRequest } from "@cqsjjb/jjb-dva-runtime";
|
||||
import {
|
||||
fromPageResponse,
|
||||
fromQualificationClassGetResponse,
|
||||
fromRegulatorOrgInfoModify,
|
||||
fromSingleResponse,
|
||||
toOrgAccountRow,
|
||||
toOrgInfoForm,
|
||||
toRegisteredOrgPageQuery,
|
||||
toRegisteredOrgPersonnelRow,
|
||||
toRegisteredOrgRow,
|
||||
toRegulatorOrgAccountPageQuery,
|
||||
toStaffCertForm,
|
||||
toStaffForm,
|
||||
} from "../enterpriseInfo/adapter";
|
||||
import { apiGet, apiPost, apiPostDelete, safeAction, safePageResult } from "../enterpriseInfo/http";
|
||||
|
||||
const NO_ORG_CONTEXT = { includeOrgContext: false };
|
||||
|
||||
export const orgAccountList = declareRequest("orgInfoLoading", safePageResult(async (params) => {
|
||||
const query = toRegulatorOrgAccountPageQuery(params);
|
||||
const res = await apiGet("/safety-eval/org-info/page", query, {}, NO_ORG_CONTEXT);
|
||||
return fromPageResponse(res, toOrgAccountRow);
|
||||
}));
|
||||
|
||||
export const orgAccountGet = declareRequest("orgInfoLoading", safeAction(async ({ id }) => {
|
||||
const res = await apiGet("/safety-eval/org-info/get", { id }, {}, NO_ORG_CONTEXT);
|
||||
const result = fromSingleResponse(res, toOrgInfoForm);
|
||||
return {
|
||||
...result,
|
||||
raw: res?.data ?? null,
|
||||
};
|
||||
}));
|
||||
|
||||
export const orgAccountModify = declareRequest("orgInfoLoading", safeAction(async (values) => {
|
||||
const { raw, ...formValues } = values;
|
||||
const payload = fromRegulatorOrgInfoModify(formValues, raw || {});
|
||||
const res = await apiPost("/safety-eval/org-info/modify", payload, {}, NO_ORG_CONTEXT);
|
||||
return fromSingleResponse(res, toOrgInfoForm);
|
||||
}));
|
||||
|
||||
export const orgAccountDelete = declareRequest("orgInfoLoading", safeAction(async ({ id }) => {
|
||||
return apiPostDelete("/safety-eval/org-info/delete", id, NO_ORG_CONTEXT);
|
||||
}));
|
||||
|
||||
export const orgAccountUpdateState = declareRequest("orgInfoLoading", safeAction(async ({ id, state }) => {
|
||||
return apiPost("/safety-eval/org-info/update-state", { id, state }, {}, NO_ORG_CONTEXT);
|
||||
}));
|
||||
|
||||
export const orgAccountResetPassword = declareRequest("orgInfoLoading", safeAction(async ({ id }) => {
|
||||
const url = `/safety-eval/org-info/reset-password?id=${encodeURIComponent(id)}`;
|
||||
return apiPost(url, {}, {}, NO_ORG_CONTEXT);
|
||||
}));
|
||||
|
||||
export const registeredOrgList = declareRequest("orgInfoLoading", safePageResult(async (params) => {
|
||||
const query = toRegisteredOrgPageQuery(params);
|
||||
const res = await apiGet("/safety-eval/org-info/page", query, {}, NO_ORG_CONTEXT);
|
||||
return fromPageResponse(res, toRegisteredOrgRow);
|
||||
}));
|
||||
|
||||
export const registeredOrgGet = declareRequest("orgInfoLoading", safeAction(async ({ id }) => {
|
||||
const res = await apiGet("/safety-eval/org-info/get", { id }, {}, NO_ORG_CONTEXT);
|
||||
return fromSingleResponse(res, toOrgInfoForm);
|
||||
}));
|
||||
|
||||
/** 详情页直调 HTTP,避免 DVA loading 与 useEffect 依赖 dispatch 函数导致反复取消 */
|
||||
export async function fetchRegisteredOrgDetail(id) {
|
||||
const res = await apiGet("/safety-eval/org-info/get", { id }, {}, NO_ORG_CONTEXT);
|
||||
return fromSingleResponse(res, toOrgInfoForm);
|
||||
}
|
||||
|
||||
export async function fetchRegisteredOrgQualificationGroups(orgInfoId) {
|
||||
const res = await apiGet("/safety-eval/org-qualification/classGet", { id: orgInfoId }, {}, NO_ORG_CONTEXT);
|
||||
if (res?.success === false) {
|
||||
throw new Error(res?.message || "加载申请材料失败");
|
||||
}
|
||||
return fromQualificationClassGetResponse(res);
|
||||
}
|
||||
|
||||
export async function fetchRegisteredOrgPersonnelList(orgInfoId) {
|
||||
const res = await apiGet("/safety-eval/org-personnel/page", {
|
||||
orgId: orgInfoId,
|
||||
current: 1,
|
||||
size: 999,
|
||||
}, {}, NO_ORG_CONTEXT);
|
||||
const page = fromPageResponse(res, toRegisteredOrgPersonnelRow);
|
||||
return page?.data || [];
|
||||
}
|
||||
|
||||
export async function fetchRegisteredOrgPersonnelDetail({ id }) {
|
||||
const res = await apiGet("/safety-eval/org-personnel/get", { id }, {}, NO_ORG_CONTEXT);
|
||||
return fromSingleResponse(res, toStaffForm);
|
||||
}
|
||||
|
||||
export async function fetchRegisteredOrgPersonnelCertList(personnelId) {
|
||||
const res = await apiGet("/safety-eval/org-personnel-cert/page", {
|
||||
personnelId,
|
||||
current: 1,
|
||||
size: 999,
|
||||
}, {}, NO_ORG_CONTEXT);
|
||||
const page = fromPageResponse(res, toStaffCertForm);
|
||||
return page?.data || [];
|
||||
}
|
||||
|
||||
export async function fetchRegisteredOrgPersonnelCertDetail({ id }) {
|
||||
const res = await apiGet("/safety-eval/org-personnel-cert/get", { id }, {}, NO_ORG_CONTEXT);
|
||||
return fromSingleResponse(res, toStaffCertForm);
|
||||
}
|
||||
|
|
@ -35,3 +35,21 @@ export const staffCertificateEdit = declareRequest("staffCertificateLoading", sa
|
|||
export const staffCertificateRemove = declareRequest("staffCertificateLoading", safeAction(async ({ id }) => {
|
||||
return apiPostDelete("/safety-eval/org-personnel-cert/delete", id);
|
||||
}));
|
||||
|
||||
export async function fetchStaffCertListByPersonnelId(personnelId) {
|
||||
const query = toPageQuery({
|
||||
pageIndex: 1,
|
||||
pageSize: 999,
|
||||
eqStaffId: personnelId,
|
||||
}, {
|
||||
eqStaffId: "personnelId",
|
||||
});
|
||||
const res = await apiGet("/safety-eval/org-personnel-cert/page", query);
|
||||
const page = fromPageResponse(res, toStaffCertForm);
|
||||
return page?.data || [];
|
||||
}
|
||||
|
||||
export async function fetchStaffCertDetail({ id }) {
|
||||
const res = await apiGet("/safety-eval/org-personnel-cert/get", { id });
|
||||
return fromSingleResponse(res, toStaffCertForm);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,106 @@
|
|||
import { Image, Modal } from "antd";
|
||||
|
||||
export function getFilePreviewType(url = "", fileName = "") {
|
||||
const hint = `${fileName || ""} ${url || ""}`.toLowerCase();
|
||||
if (/\.(jpe?g|png|gif|webp|bmp)(\?|#|$)/i.test(hint)) {
|
||||
return "image";
|
||||
}
|
||||
if (/\.pdf(\?|#|$)/i.test(hint)) {
|
||||
return "pdf";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
export function FilePreviewContent({ url, fileName, style }) {
|
||||
if (!url) {
|
||||
return (
|
||||
<div style={{
|
||||
minHeight: 120,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
background: "#fafafa",
|
||||
color: "rgba(0,0,0,0.45)",
|
||||
...style,
|
||||
}}
|
||||
>
|
||||
暂无上传文件
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const type = getFilePreviewType(url, fileName);
|
||||
if (type === "image") {
|
||||
return <Image src={url} alt={fileName || "预览"} style={{ maxHeight: 480, ...style }} />;
|
||||
}
|
||||
if (type === "pdf") {
|
||||
return (
|
||||
<iframe
|
||||
title={fileName || "PDF 预览"}
|
||||
src={url}
|
||||
style={{ width: "100%", height: 480, border: "none", ...style }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
minHeight: 120,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
background: "#fafafa",
|
||||
color: "rgba(0,0,0,0.45)",
|
||||
...style,
|
||||
}}
|
||||
>
|
||||
暂不支持在线预览,请使用「新窗口打开」
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function FilePreviewModal({
|
||||
open,
|
||||
title = "文件预览",
|
||||
fileName,
|
||||
url,
|
||||
width = 720,
|
||||
onCancel,
|
||||
}) {
|
||||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
destroyOnClose
|
||||
title={title}
|
||||
width={width}
|
||||
cancelText="关闭"
|
||||
okText={url ? "新窗口打开" : "关闭"}
|
||||
onCancel={onCancel}
|
||||
onOk={() => {
|
||||
if (url) {
|
||||
window.open(url, "_blank");
|
||||
}
|
||||
onCancel?.();
|
||||
}}
|
||||
>
|
||||
{open && (
|
||||
<div>
|
||||
{fileName && (
|
||||
<div style={{
|
||||
padding: 12,
|
||||
background: "#f8fafc",
|
||||
border: "1px solid #f0f0f0",
|
||||
borderRadius: 4,
|
||||
marginBottom: 12,
|
||||
fontWeight: 500,
|
||||
}}
|
||||
>
|
||||
{fileName}
|
||||
</div>
|
||||
)}
|
||||
<FilePreviewContent url={url} fileName={fileName} />
|
||||
</div>
|
||||
)}
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
|
@ -31,12 +31,33 @@ export const FILING_TYPE_OPTIONS = [
|
|||
{ label: "确认备案", value: "确认备案" },
|
||||
];
|
||||
|
||||
/** 监管端已备案机构 - 备案类型搜索 */
|
||||
export const REGISTERED_ORG_FILING_TYPE_SEARCH_OPTIONS = [
|
||||
{ label: "全部", value: "" },
|
||||
{ label: "审核备案", value: "1" },
|
||||
{ label: "确认备案", value: "2" },
|
||||
];
|
||||
|
||||
/** 监管端已备案机构 - 备案状态搜索(1=已备案,2=未备案) */
|
||||
export const REGISTERED_ORG_FILING_RECORD_STATUS_OPTIONS = [
|
||||
{ label: "全部", value: "" },
|
||||
{ label: "已备案", value: "1" },
|
||||
{ label: "未备案", value: "2" },
|
||||
];
|
||||
|
||||
/** 备案状态(机构信息) */
|
||||
export const FILING_RECORD_STATUS_OPTIONS = [
|
||||
{ label: "已备案", value: "已备案" },
|
||||
{ label: "未备案", value: "未备案" },
|
||||
];
|
||||
|
||||
/** 监管端机构账号状态(后端 state:0=启用,1=禁用) */
|
||||
export const ORG_ACCOUNT_STATE_OPTIONS = [
|
||||
{ label: "全部", value: "" },
|
||||
{ label: "启用", value: "0" },
|
||||
{ label: "禁用", value: "1" },
|
||||
];
|
||||
|
||||
/** 资质范围 / 证照类型(行业) */
|
||||
export const QUALIFICATION_INDUSTRY_OPTIONS = [
|
||||
{ label: "煤炭开采业", value: "煤炭开采业" },
|
||||
|
|
|
|||
|
|
@ -13,7 +13,14 @@ dayjs.locale("zh-cn");
|
|||
setJJBCommonAntdMessage(message);
|
||||
|
||||
const app = setup();
|
||||
getFileUrlFromServer();
|
||||
|
||||
// jjb.config contextInject.fileUrl 优先,避免启动时请求网关附件接口失败触发 dev overlay
|
||||
if (process.env.app?.fileUrl) {
|
||||
window.fileUrl = process.env.app.fileUrl;
|
||||
}
|
||||
getFileUrlFromServer().catch((err) => {
|
||||
console.warn("[getFileUrlFromServer] failed:", err?.message || err);
|
||||
});
|
||||
// 非底座环境运行
|
||||
if (!window.__POWERED_BY_QIANKUN__) {
|
||||
// 云组件默认依赖
|
||||
|
|
|
|||
|
|
@ -0,0 +1,391 @@
|
|||
/** 监管端 - 基础信息管理 mock 数据(暂不对接后端) */
|
||||
|
||||
const EVALUATOR_DETAILS = {
|
||||
1: {
|
||||
id: 1,
|
||||
name: "张建国",
|
||||
gender: "男",
|
||||
birthDate: "1985-03-15",
|
||||
idCard: "500***********1234",
|
||||
education: "全日制 · 本科",
|
||||
graduateSchool: "重庆大学",
|
||||
major: "安全工程",
|
||||
orgName: "重庆安评技术研究院有限公司",
|
||||
deptName: "评价一部",
|
||||
positionName: "高级评价师",
|
||||
account: "138****5678",
|
||||
employmentStatus: 1,
|
||||
registerEngineerFlag: 1,
|
||||
status: "normal",
|
||||
statusTip: "",
|
||||
certificatesSummary: "安全评价师一级, 注册安全工程师",
|
||||
certificates: [
|
||||
{
|
||||
id: 101,
|
||||
certName: "安全评价师一级",
|
||||
certNo: "SJP-2020-****",
|
||||
issueDate: "2020-06-01",
|
||||
expireDate: "2026-06-01",
|
||||
status: "expired",
|
||||
statusLabel: "已过期",
|
||||
statusTip: "证书已过期",
|
||||
},
|
||||
{
|
||||
id: 102,
|
||||
certName: "注册安全工程师",
|
||||
certNo: "ZGAQ-2018-****",
|
||||
issueDate: "2018-09-15",
|
||||
expireDate: "2027-09-15",
|
||||
status: "normal",
|
||||
statusLabel: "正常",
|
||||
statusTip: "",
|
||||
},
|
||||
{
|
||||
id: 103,
|
||||
certName: "安全评价师二级",
|
||||
certNo: "SJP-2023-****",
|
||||
issueDate: "2023-05-01",
|
||||
expireDate: "2026-08-01",
|
||||
status: "expiring",
|
||||
statusLabel: "临期",
|
||||
statusTip: "证书即将到期(不足3月)",
|
||||
},
|
||||
],
|
||||
},
|
||||
2: {
|
||||
id: 2,
|
||||
name: "李明华",
|
||||
gender: "男",
|
||||
birthDate: "1988-07-22",
|
||||
idCard: "500***********5678",
|
||||
education: "全日制 · 硕士",
|
||||
graduateSchool: "中国矿业大学",
|
||||
major: "安全技术及工程",
|
||||
orgName: "重庆安评技术研究院有限公司",
|
||||
deptName: "评价二部",
|
||||
positionName: "评价师",
|
||||
account: "139****1234",
|
||||
employmentStatus: 1,
|
||||
registerEngineerFlag: 1,
|
||||
status: "normal",
|
||||
statusTip: "",
|
||||
certificatesSummary: "安全评价师二级, 注册安全工程师",
|
||||
certificates: [
|
||||
{
|
||||
id: 201,
|
||||
certName: "安全评价师二级",
|
||||
certNo: "SJP-2021-****",
|
||||
issueDate: "2021-03-10",
|
||||
expireDate: "2027-03-10",
|
||||
status: "normal",
|
||||
statusLabel: "正常",
|
||||
statusTip: "",
|
||||
},
|
||||
{
|
||||
id: 202,
|
||||
certName: "注册安全工程师",
|
||||
certNo: "ZGAQ-2019-****",
|
||||
issueDate: "2019-11-20",
|
||||
expireDate: "2028-11-20",
|
||||
status: "normal",
|
||||
statusLabel: "正常",
|
||||
statusTip: "",
|
||||
},
|
||||
],
|
||||
},
|
||||
3: {
|
||||
id: 3,
|
||||
name: "王丽萍",
|
||||
gender: "女",
|
||||
birthDate: "1990-11-08",
|
||||
idCard: "500***********9012",
|
||||
education: "全日制 · 本科",
|
||||
graduateSchool: "西南大学",
|
||||
major: "化学工程",
|
||||
orgName: "重庆恒安安全评价有限公司",
|
||||
deptName: "评价部",
|
||||
positionName: "评价师",
|
||||
account: "137****8899",
|
||||
employmentStatus: 1,
|
||||
registerEngineerFlag: 0,
|
||||
status: "abnormal",
|
||||
statusTip: "此评价师的社保存在多处缴纳",
|
||||
certificatesSummary: "安全评价师三级",
|
||||
certificates: [
|
||||
{
|
||||
id: 301,
|
||||
certName: "安全评价师三级",
|
||||
certNo: "SJP-2022-****",
|
||||
issueDate: "2022-08-01",
|
||||
expireDate: "2028-08-01",
|
||||
status: "normal",
|
||||
statusLabel: "正常",
|
||||
statusTip: "",
|
||||
},
|
||||
],
|
||||
},
|
||||
4: {
|
||||
id: 4,
|
||||
name: "陈华",
|
||||
gender: "男",
|
||||
birthDate: "1987-04-18",
|
||||
idCard: "500***********3456",
|
||||
education: "在职教育 · 本科",
|
||||
graduateSchool: "重庆理工大学",
|
||||
major: "安全工程",
|
||||
orgName: "重庆渝安风险评估中心",
|
||||
deptName: "技术部",
|
||||
positionName: "评价师",
|
||||
account: "136****7788",
|
||||
employmentStatus: 1,
|
||||
registerEngineerFlag: 1,
|
||||
status: "abnormal",
|
||||
statusTip: "评价师证书临期(不足3月)",
|
||||
certificatesSummary: "安全评价师二级",
|
||||
certificates: [
|
||||
{
|
||||
id: 401,
|
||||
certName: "安全评价师二级",
|
||||
certNo: "SJP-2020-****",
|
||||
issueDate: "2020-04-15",
|
||||
expireDate: "2026-08-15",
|
||||
status: "expiring",
|
||||
statusLabel: "临期",
|
||||
statusTip: "证书即将到期(不足3月)",
|
||||
},
|
||||
],
|
||||
},
|
||||
5: {
|
||||
id: 5,
|
||||
name: "赵敏",
|
||||
gender: "女",
|
||||
birthDate: "1992-01-30",
|
||||
idCard: "500***********7890",
|
||||
education: "全日制 · 本科",
|
||||
graduateSchool: "重庆科技学院",
|
||||
major: "安全工程",
|
||||
orgName: "重庆安环检测技术有限公司",
|
||||
deptName: "评价中心",
|
||||
positionName: "评价师",
|
||||
account: "135****6677",
|
||||
employmentStatus: 0,
|
||||
registerEngineerFlag: 0,
|
||||
status: "abnormal",
|
||||
statusTip: "此评价师的社保存在多处缴纳;证书临期(不足3月)",
|
||||
certificatesSummary: "安全评价师三级",
|
||||
certificates: [
|
||||
{
|
||||
id: 501,
|
||||
certName: "安全评价师三级",
|
||||
certNo: "SJP-2023-****",
|
||||
issueDate: "2023-06-01",
|
||||
expireDate: "2026-07-01",
|
||||
status: "expiring",
|
||||
statusLabel: "临期",
|
||||
statusTip: "证书即将到期(不足3月)",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const EVALUATOR_LIST = Object.values(EVALUATOR_DETAILS).map((item) => ({
|
||||
id: item.id,
|
||||
orgName: item.orgName,
|
||||
evaluatorName: item.name,
|
||||
registerEngineerFlag: item.registerEngineerFlag,
|
||||
registerEngineerLabel: item.registerEngineerFlag === 1 ? "是" : "否",
|
||||
employmentStatus: item.employmentStatus,
|
||||
employmentLabel: item.employmentStatus === 1 ? "是" : "否",
|
||||
certificatesSummary: item.certificatesSummary,
|
||||
status: item.status,
|
||||
statusLabel: item.status === "normal" ? "正常" : "异常",
|
||||
statusTip: item.statusTip,
|
||||
}));
|
||||
|
||||
const ENTERPRISE_PORTRAIT_DETAILS = {
|
||||
1: {
|
||||
id: 1,
|
||||
enterpriseName: "重庆华安安全科技有限公司",
|
||||
creditCode: "91500112MA******",
|
||||
industry: "化工行业",
|
||||
district: "渝北区",
|
||||
accountSource: "企业注册",
|
||||
projectOnTime: "12/15",
|
||||
projectOnTimeRate: "80%",
|
||||
reportQuality: "合格",
|
||||
complaintCount: 0,
|
||||
penaltyCount: 0,
|
||||
infoLevel: "一级",
|
||||
infoLevelCode: "level1",
|
||||
scoreGrade: "A级",
|
||||
evalCount: 15,
|
||||
reportQualityDesc: "近6个月无不合格报告",
|
||||
penaltyDesc: "无不良记录",
|
||||
evalCountDesc: "累计安全评价",
|
||||
},
|
||||
2: {
|
||||
id: 2,
|
||||
enterpriseName: "重庆鼎信安全咨询有限公司",
|
||||
creditCode: "91500105MA******",
|
||||
industry: "建筑施工",
|
||||
district: "江北区",
|
||||
accountSource: "机构注册",
|
||||
projectOnTime: "8/10",
|
||||
projectOnTimeRate: "80%",
|
||||
reportQuality: "合格",
|
||||
complaintCount: 1,
|
||||
penaltyCount: 0,
|
||||
infoLevel: "二级",
|
||||
infoLevelCode: "level2",
|
||||
scoreGrade: "B级",
|
||||
evalCount: 10,
|
||||
reportQualityDesc: "近6个月1份需整改",
|
||||
penaltyDesc: "无行政处罚",
|
||||
evalCountDesc: "累计安全评价",
|
||||
},
|
||||
3: {
|
||||
id: 3,
|
||||
enterpriseName: "重庆安环检测技术有限公司",
|
||||
creditCode: "91500108MA******",
|
||||
industry: "仓储物流",
|
||||
district: "南岸区",
|
||||
accountSource: "监管注册",
|
||||
projectOnTime: "5/8",
|
||||
projectOnTimeRate: "62.5%",
|
||||
reportQuality: "不合格",
|
||||
complaintCount: 2,
|
||||
penaltyCount: 1,
|
||||
infoLevel: "三级",
|
||||
infoLevelCode: "level3",
|
||||
scoreGrade: "C级",
|
||||
evalCount: 8,
|
||||
reportQualityDesc: "近6个月存在不合格报告",
|
||||
penaltyDesc: "1次行政处罚",
|
||||
evalCountDesc: "累计安全评价",
|
||||
},
|
||||
4: {
|
||||
id: 4,
|
||||
enterpriseName: "重庆恒安安全评价有限公司",
|
||||
creditCode: "91500106MA******",
|
||||
industry: "矿山",
|
||||
district: "九龙坡区",
|
||||
accountSource: "企业注册",
|
||||
projectOnTime: "20/22",
|
||||
projectOnTimeRate: "91%",
|
||||
reportQuality: "合格",
|
||||
complaintCount: 0,
|
||||
penaltyCount: 0,
|
||||
infoLevel: "一级",
|
||||
infoLevelCode: "level1",
|
||||
scoreGrade: "A级",
|
||||
evalCount: 22,
|
||||
reportQualityDesc: "近6个月无不合格报告",
|
||||
penaltyDesc: "无不良记录",
|
||||
evalCountDesc: "累计安全评价",
|
||||
},
|
||||
5: {
|
||||
id: 5,
|
||||
enterpriseName: "重庆渝安风险评估中心",
|
||||
creditCode: "91500107MA******",
|
||||
industry: "石油加工",
|
||||
district: "渝中区",
|
||||
accountSource: "机构注册",
|
||||
projectOnTime: "6/9",
|
||||
projectOnTimeRate: "67%",
|
||||
reportQuality: "合格",
|
||||
complaintCount: 0,
|
||||
penaltyCount: 0,
|
||||
infoLevel: "二级",
|
||||
infoLevelCode: "level2",
|
||||
scoreGrade: "B级",
|
||||
evalCount: 9,
|
||||
reportQualityDesc: "近6个月无不合格报告",
|
||||
penaltyDesc: "无不良记录",
|
||||
evalCountDesc: "累计安全评价",
|
||||
},
|
||||
};
|
||||
|
||||
const ENTERPRISE_PORTRAIT_LIST = Object.values(ENTERPRISE_PORTRAIT_DETAILS).map((item) => ({
|
||||
id: item.id,
|
||||
enterpriseName: item.enterpriseName,
|
||||
district: item.district,
|
||||
accountSource: item.accountSource,
|
||||
projectOnTime: item.projectOnTime,
|
||||
reportQuality: item.reportQuality,
|
||||
complaintCount: item.complaintCount,
|
||||
penaltyCount: item.penaltyCount,
|
||||
infoLevel: item.infoLevel,
|
||||
infoLevelCode: item.infoLevelCode,
|
||||
}));
|
||||
|
||||
function paginate(list, params = {}) {
|
||||
const pageIndex = Number(params.pageIndex ?? params.current ?? 1);
|
||||
const pageSize = Number(params.pageSize ?? params.size ?? 10);
|
||||
const start = (pageIndex - 1) * pageSize;
|
||||
return {
|
||||
success: true,
|
||||
data: list.slice(start, start + pageSize),
|
||||
totalCount: list.length,
|
||||
};
|
||||
}
|
||||
|
||||
function includesKeyword(value, keyword) {
|
||||
if (!keyword) return true;
|
||||
return String(value || "").includes(String(keyword).trim());
|
||||
}
|
||||
|
||||
export function queryEvaluatorPage(params = {}) {
|
||||
let list = [...EVALUATOR_LIST];
|
||||
if (params.evaluatorName) {
|
||||
list = list.filter((item) => includesKeyword(item.evaluatorName, params.evaluatorName));
|
||||
}
|
||||
if (params.orgName) {
|
||||
list = list.filter((item) => includesKeyword(item.orgName, params.orgName));
|
||||
}
|
||||
if (params.employmentStatus !== undefined && params.employmentStatus !== null && params.employmentStatus !== "") {
|
||||
list = list.filter((item) => item.employmentStatus === Number(params.employmentStatus));
|
||||
}
|
||||
if (params.registerEngineerFlag !== undefined && params.registerEngineerFlag !== null && params.registerEngineerFlag !== "") {
|
||||
list = list.filter((item) => item.registerEngineerFlag === Number(params.registerEngineerFlag));
|
||||
}
|
||||
if (params.status) {
|
||||
list = list.filter((item) => item.status === params.status);
|
||||
}
|
||||
return Promise.resolve(paginate(list, params));
|
||||
}
|
||||
|
||||
export function getEvaluatorDetail(id) {
|
||||
const detail = EVALUATOR_DETAILS[id];
|
||||
return Promise.resolve({
|
||||
success: !!detail,
|
||||
data: detail || null,
|
||||
});
|
||||
}
|
||||
|
||||
export function queryEnterprisePortraitPage(params = {}) {
|
||||
let list = [...ENTERPRISE_PORTRAIT_LIST];
|
||||
if (params.enterpriseName) {
|
||||
list = list.filter((item) => includesKeyword(item.enterpriseName, params.enterpriseName));
|
||||
}
|
||||
if (params.district) {
|
||||
list = list.filter((item) => item.district === params.district);
|
||||
}
|
||||
if (params.accountSource) {
|
||||
list = list.filter((item) => item.accountSource === params.accountSource);
|
||||
}
|
||||
return Promise.resolve(paginate(list, params));
|
||||
}
|
||||
|
||||
export function getEnterprisePortraitDetail(id) {
|
||||
const detail = ENTERPRISE_PORTRAIT_DETAILS[id];
|
||||
return Promise.resolve({
|
||||
success: !!detail,
|
||||
data: detail || null,
|
||||
});
|
||||
}
|
||||
|
||||
export const ACCOUNT_SOURCE_OPTIONS = [
|
||||
{ label: "企业注册", value: "企业注册" },
|
||||
{ label: "机构注册", value: "机构注册" },
|
||||
{ label: "监管注册", value: "监管注册" },
|
||||
];
|
||||
|
|
@ -0,0 +1,363 @@
|
|||
/** 监管端 - 机构账号 / 已备案机构 mock 数据 */
|
||||
|
||||
import { FILING_TYPE_OPTIONS } from "~/enumerate/enterpriseOptions";
|
||||
|
||||
function paginate(list, params = {}) {
|
||||
const pageIndex = Number(params.pageIndex ?? params.current ?? 1);
|
||||
const pageSize = Number(params.pageSize ?? params.size ?? 10);
|
||||
const start = (pageIndex - 1) * pageSize;
|
||||
return {
|
||||
success: true,
|
||||
data: list.slice(start, start + pageSize),
|
||||
totalCount: list.length,
|
||||
};
|
||||
}
|
||||
|
||||
function includesKeyword(value, keyword) {
|
||||
if (!keyword) return true;
|
||||
return String(value || "").includes(String(keyword).trim());
|
||||
}
|
||||
|
||||
function inDateRange(dateStr, start, end) {
|
||||
if (!dateStr) return false;
|
||||
if (start && dateStr < start) return false;
|
||||
if (end && dateStr > end) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
export const ORG_ACCOUNT_SOURCE_OPTIONS = [
|
||||
{ label: "机构注册", value: "机构注册" },
|
||||
{ label: "监管注册", value: "监管注册" },
|
||||
];
|
||||
|
||||
export const ORG_ACCOUNT_STATUS_OPTIONS = [
|
||||
{ label: "启用", value: 1 },
|
||||
{ label: "禁用", value: 0 },
|
||||
];
|
||||
|
||||
const ORG_ACCOUNT_DETAILS = {
|
||||
1: {
|
||||
id: 1,
|
||||
orgName: "重庆安评技术研究院有限公司",
|
||||
creditCode: "91500112MA7******",
|
||||
district: "渝北区",
|
||||
industries: ["化学原料/危化品生产、储存、经营、医药化工"],
|
||||
businessAddress: "重庆市渝北区龙山街道100号",
|
||||
enterpriseStatus: "正常",
|
||||
longitude: "106.532",
|
||||
latitude: "29.612",
|
||||
principalName: "刘强",
|
||||
principalPhone: "138****5678",
|
||||
legalPerson: "刘强",
|
||||
legalPhone: "023-6789****",
|
||||
enterpriseScale: "中型",
|
||||
aboveScaleFlag: 0,
|
||||
accountSource: "机构注册",
|
||||
enableStatus: 1,
|
||||
openTime: "2025-09-01",
|
||||
},
|
||||
2: {
|
||||
id: 2,
|
||||
orgName: "重庆恒安安全评价有限公司",
|
||||
creditCode: "91500105MA6******",
|
||||
district: "南岸区",
|
||||
industries: ["石油天然气开采、油气管道、石油加工"],
|
||||
businessAddress: "重庆市南岸区南坪西路200号",
|
||||
enterpriseStatus: "正常",
|
||||
longitude: "106.571",
|
||||
latitude: "29.523",
|
||||
principalName: "王芳",
|
||||
principalPhone: "139****2345",
|
||||
legalPerson: "王芳",
|
||||
legalPhone: "023-6288****",
|
||||
enterpriseScale: "小型",
|
||||
aboveScaleFlag: 0,
|
||||
accountSource: "监管注册",
|
||||
enableStatus: 1,
|
||||
openTime: "2025-10-15",
|
||||
},
|
||||
3: {
|
||||
id: 3,
|
||||
orgName: "重庆渝安风险评估中心",
|
||||
creditCode: "91500107MA5******",
|
||||
district: "九龙坡区",
|
||||
industries: ["建筑施工、建材"],
|
||||
businessAddress: "重庆市九龙坡区石桥铺300号",
|
||||
enterpriseStatus: "正常",
|
||||
longitude: "106.480",
|
||||
latitude: "29.502",
|
||||
principalName: "陈明",
|
||||
principalPhone: "137****8899",
|
||||
legalPerson: "陈明",
|
||||
legalPhone: "023-6866****",
|
||||
enterpriseScale: "微型",
|
||||
aboveScaleFlag: 0,
|
||||
accountSource: "机构注册",
|
||||
enableStatus: 0,
|
||||
openTime: "2025-06-20",
|
||||
},
|
||||
};
|
||||
|
||||
let orgAccountStore = Object.values(ORG_ACCOUNT_DETAILS).map((item) => ({
|
||||
id: item.id,
|
||||
orgName: item.orgName,
|
||||
district: item.district,
|
||||
accountSource: item.accountSource,
|
||||
enableStatus: item.enableStatus,
|
||||
openTime: item.openTime,
|
||||
}));
|
||||
|
||||
const REGISTERED_ORG_DETAILS = {
|
||||
1: {
|
||||
id: 1,
|
||||
orgName: "重庆安评技术研究院有限公司",
|
||||
registerAddress: "渝北区龙山街道100号",
|
||||
serviceEnterpriseCount: 12,
|
||||
evalProjectCount: 45,
|
||||
filingType: "审核备案",
|
||||
filingStatus: "filed",
|
||||
filingStatusLabel: "已备案",
|
||||
filingInfo: {
|
||||
filingDistrict: "重庆市沙坪坝区",
|
||||
filingUnit: "重庆市安全生产科学研究有限公司",
|
||||
filingUnitType: "本地单位",
|
||||
registerAddress: "重庆市沙坪坝区大学城东路20号重庆科技学院第39栋西南角实习用房三楼",
|
||||
officeAddress: "重庆市沙坪坝区大学城东路20号重庆科技学院第39栋西南角实习用房三楼",
|
||||
qualCertNo: "API-2026-001",
|
||||
creditCode: "91500112MA7******",
|
||||
legalPersonPhone: "刘强 / 138****5678",
|
||||
infoUrl: "www.cqap.com",
|
||||
fixedAssetTotal: "1200",
|
||||
contactPhone: "陈芳 / 139****9012",
|
||||
archiveArea: "80 ㎡",
|
||||
workplaceArea: "1200 ㎡",
|
||||
fullTimeEvaluatorCount: "25 人",
|
||||
registerEngineerCount: "12 人",
|
||||
intro: "重庆安全生产科学研究有限公司成立于2015年,是一家专业从事安全评价、安全咨询、安全技术开发与转让的综合性安全技术服务机构。",
|
||||
attachments: ["资质申请书.pdf", "营业执照副本.pdf", "社保缴纳证明.pdf"],
|
||||
},
|
||||
materialGroups: [
|
||||
{
|
||||
id: 1,
|
||||
scopeName: "化工",
|
||||
expireDate: "2029-01-14",
|
||||
materials: [
|
||||
{ id: 11, name: "化工领域安全评价机构资质申请书", required: true, status: "uploaded" },
|
||||
{ id: 12, name: "化工领域技术人员资质证书", required: true, status: "uploaded" },
|
||||
{ id: 13, name: "化工领域业绩证明材料", required: false, status: "uploaded" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
scopeName: "矿山",
|
||||
expireDate: "2028-06-30",
|
||||
materials: [
|
||||
{ id: 21, name: "矿山领域安全评价机构资质申请书", required: true, status: "uploaded" },
|
||||
{ id: 22, name: "矿山领域技术人员资质证书", required: true, status: "uploaded" },
|
||||
{ id: 23, name: "矿山领域安全管理制度文件", required: true, status: "pending" },
|
||||
{ id: 24, name: "矿山领域业绩证明材料", required: false, status: "uploaded" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
scopeName: "建筑施工",
|
||||
expireDate: "2026-05-31",
|
||||
expireWarning: true,
|
||||
materials: [
|
||||
{ id: 31, name: "建筑施工领域安全评价机构资质申请书", required: true, status: "uploaded" },
|
||||
{ id: 32, name: "建筑施工领域技术人员资质证书", required: true, status: "uploaded" },
|
||||
{ id: 33, name: "建筑施工领域设备清单及检定报告", required: true, status: "pending" },
|
||||
],
|
||||
},
|
||||
],
|
||||
personnel: [
|
||||
{ id: 1, name: "张建国", gender: "男", personType: "专职评价师", qualScope: "化工", education: "本科", registerEngineer: 1 },
|
||||
{ id: 2, name: "李明华", gender: "男", personType: "专职评价师", qualScope: "矿山", education: "硕士", registerEngineer: 1 },
|
||||
{ id: 3, name: "王丽萍", gender: "女", personType: "基础人员", qualScope: "建筑施工", education: "本科", registerEngineer: 0 },
|
||||
{ id: 4, name: "陈志强", gender: "男", personType: "专职评价师", qualScope: "化工", education: "本科", registerEngineer: 1 },
|
||||
{ id: 5, name: "赵磊", gender: "男", personType: "管理人员", qualScope: "—", education: "专科", registerEngineer: 0 },
|
||||
],
|
||||
evalProjects: [
|
||||
{ id: 1, projectType: "安全预评价", projectNo: "YP-2026-0012", projectName: "华安化工园区安全预评价项目", projectStatus: "待现场勘查" },
|
||||
{ id: 2, projectType: "安全验收评价", projectNo: "YS-2026-0008", projectName: "鼎信建设安全验收评价", projectStatus: "过程管控" },
|
||||
{ id: 3, projectType: "安全现状评价", projectNo: "XZ-2026-0003", projectName: "安环检测年度安全现状评价", projectStatus: "归档" },
|
||||
],
|
||||
},
|
||||
2: {
|
||||
id: 2,
|
||||
orgName: "重庆恒安安全评价有限公司",
|
||||
registerAddress: "南岸区南坪西路200号",
|
||||
serviceEnterpriseCount: 8,
|
||||
evalProjectCount: 32,
|
||||
filingType: "确认备案",
|
||||
filingStatus: "filing",
|
||||
filingStatusLabel: "备案中",
|
||||
filingInfo: {
|
||||
filingDistrict: "重庆市南岸区",
|
||||
filingUnit: "重庆恒安安全评价有限公司",
|
||||
filingUnitType: "本地单位",
|
||||
registerAddress: "重庆市南岸区南坪西路200号",
|
||||
officeAddress: "重庆市南岸区南坪西路200号",
|
||||
qualCertNo: "API-2026-002",
|
||||
creditCode: "91500105MA6******",
|
||||
legalPersonPhone: "王芳 / 139****2345",
|
||||
infoUrl: "www.ha-anping.com",
|
||||
fixedAssetTotal: "800",
|
||||
contactPhone: "李强 / 136****5566",
|
||||
archiveArea: "50 ㎡",
|
||||
workplaceArea: "600 ㎡",
|
||||
fullTimeEvaluatorCount: "15 人",
|
||||
registerEngineerCount: "6 人",
|
||||
intro: "重庆恒安安全评价有限公司专注于矿山、化工行业安全评价服务。",
|
||||
attachments: ["资质申请书.pdf", "营业执照副本.pdf"],
|
||||
},
|
||||
materialGroups: [
|
||||
{
|
||||
id: 1,
|
||||
scopeName: "矿山",
|
||||
expireDate: "2028-12-31",
|
||||
materials: [
|
||||
{ id: 11, name: "矿山领域安全评价机构资质申请书", required: true, status: "uploaded" },
|
||||
{ id: 12, name: "矿山领域技术人员资质证书", required: true, status: "uploaded" },
|
||||
],
|
||||
},
|
||||
],
|
||||
personnel: [
|
||||
{ id: 1, name: "李明华", gender: "男", personType: "专职评价师", qualScope: "矿山", education: "硕士", registerEngineer: 1 },
|
||||
],
|
||||
evalProjects: [
|
||||
{ id: 1, projectType: "安全现状评价", projectNo: "XZ-2026-0010", projectName: "恒安矿山安全现状评价", projectStatus: "过程管控" },
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const REGISTERED_ORG_LIST = Object.values(REGISTERED_ORG_DETAILS).map((item) => ({
|
||||
id: item.id,
|
||||
orgName: item.orgName,
|
||||
registerAddress: item.registerAddress,
|
||||
serviceEnterpriseCount: item.serviceEnterpriseCount,
|
||||
evalProjectCount: item.evalProjectCount,
|
||||
filingType: item.filingType,
|
||||
filingStatus: item.filingStatus,
|
||||
filingStatusLabel: item.filingStatusLabel,
|
||||
}));
|
||||
|
||||
export const FILING_STATUS_OPTIONS = [
|
||||
{ label: "待备案", value: "pending" },
|
||||
{ label: "备案中", value: "filing" },
|
||||
{ label: "已备案", value: "filed" },
|
||||
];
|
||||
|
||||
export function queryOrgAccountPage(params = {}) {
|
||||
let list = [...orgAccountStore];
|
||||
if (params.orgName) {
|
||||
list = list.filter((item) => includesKeyword(item.orgName, params.orgName));
|
||||
}
|
||||
if (params.district) {
|
||||
list = list.filter((item) => item.district === params.district);
|
||||
}
|
||||
if (params.enableStatus !== undefined && params.enableStatus !== null && params.enableStatus !== "") {
|
||||
list = list.filter((item) => item.enableStatus === Number(params.enableStatus));
|
||||
}
|
||||
if (params.accountSource) {
|
||||
list = list.filter((item) => item.accountSource === params.accountSource);
|
||||
}
|
||||
const range = params.openTimeRange;
|
||||
if (Array.isArray(range) && range.length === 2 && range[0] && range[1]) {
|
||||
const start = range[0].format ? range[0].format("YYYY-MM-DD") : range[0];
|
||||
const end = range[1].format ? range[1].format("YYYY-MM-DD") : range[1];
|
||||
list = list.filter((item) => inDateRange(item.openTime, start, end));
|
||||
}
|
||||
return Promise.resolve(paginate(list, params));
|
||||
}
|
||||
|
||||
export function getOrgAccountDetail(id) {
|
||||
const detail = ORG_ACCOUNT_DETAILS[id];
|
||||
if (!detail) {
|
||||
return Promise.resolve({ success: false, data: null });
|
||||
}
|
||||
const current = orgAccountStore.find((item) => item.id === Number(id));
|
||||
return Promise.resolve({
|
||||
success: true,
|
||||
data: { ...detail, enableStatus: current?.enableStatus ?? detail.enableStatus },
|
||||
});
|
||||
}
|
||||
|
||||
export function updateOrgAccount(id, values) {
|
||||
const numId = Number(id);
|
||||
const detail = ORG_ACCOUNT_DETAILS[numId];
|
||||
if (!detail) {
|
||||
return Promise.resolve({ success: false, message: "记录不存在" });
|
||||
}
|
||||
Object.assign(detail, values);
|
||||
const row = orgAccountStore.find((item) => item.id === numId);
|
||||
if (row) {
|
||||
row.orgName = values.orgName ?? row.orgName;
|
||||
row.district = values.district ?? row.district;
|
||||
}
|
||||
return Promise.resolve({ success: true, data: detail });
|
||||
}
|
||||
|
||||
export function toggleOrgAccountStatus(id) {
|
||||
const numId = Number(id);
|
||||
const row = orgAccountStore.find((item) => item.id === numId);
|
||||
if (!row) {
|
||||
return Promise.resolve({ success: false, message: "记录不存在" });
|
||||
}
|
||||
row.enableStatus = row.enableStatus === 1 ? 0 : 1;
|
||||
if (ORG_ACCOUNT_DETAILS[numId]) {
|
||||
ORG_ACCOUNT_DETAILS[numId].enableStatus = row.enableStatus;
|
||||
}
|
||||
return Promise.resolve({ success: true, data: row });
|
||||
}
|
||||
|
||||
export function deleteOrgAccount(id) {
|
||||
const numId = Number(id);
|
||||
orgAccountStore = orgAccountStore.filter((item) => item.id !== numId);
|
||||
return Promise.resolve({ success: true });
|
||||
}
|
||||
|
||||
export function resetOrgAccountPassword(id) {
|
||||
return Promise.resolve({
|
||||
success: true,
|
||||
message: "密码已重置为初始密码 Ap@123456",
|
||||
data: { id: Number(id), initialPassword: "Ap@123456" },
|
||||
});
|
||||
}
|
||||
|
||||
export function queryRegisteredOrgPage(params = {}) {
|
||||
let list = [...REGISTERED_ORG_LIST];
|
||||
if (params.orgName) {
|
||||
list = list.filter((item) => includesKeyword(item.orgName, params.orgName));
|
||||
}
|
||||
if (params.registerAddress) {
|
||||
list = list.filter((item) => includesKeyword(item.registerAddress, params.registerAddress));
|
||||
}
|
||||
if (params.filingType) {
|
||||
list = list.filter((item) => item.filingType === params.filingType);
|
||||
}
|
||||
if (params.filingStatus) {
|
||||
list = list.filter((item) => item.filingStatus === params.filingStatus);
|
||||
}
|
||||
return Promise.resolve(paginate(list, params));
|
||||
}
|
||||
|
||||
export function getRegisteredOrgDetail(id) {
|
||||
const detail = REGISTERED_ORG_DETAILS[id];
|
||||
return Promise.resolve({ success: !!detail, data: detail || null });
|
||||
}
|
||||
|
||||
/** 详情页 Mock 扩展数据(申请清单材料、人员信息,后端暂无接口) */
|
||||
export function getRegisteredOrgDetailMockExtras(id) {
|
||||
const detail = REGISTERED_ORG_DETAILS[id] || REGISTERED_ORG_DETAILS[1];
|
||||
const personnel = (detail?.personnel || []).map((item) => ({
|
||||
...item,
|
||||
position: item.position || item.personType,
|
||||
}));
|
||||
return Promise.resolve({
|
||||
materialGroups: detail?.materialGroups || [],
|
||||
personnel,
|
||||
});
|
||||
}
|
||||
|
||||
export { FILING_TYPE_OPTIONS };
|
||||
|
|
@ -0,0 +1,198 @@
|
|||
import React from "react";
|
||||
import Upload from "zy-react-library/components/Upload";
|
||||
import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender";
|
||||
import {
|
||||
CHONGQING_DISTRICTS,
|
||||
ENTERPRISE_SCALE_OPTIONS,
|
||||
ENTERPRISE_STATUS_OPTIONS,
|
||||
FILING_RECORD_STATUS_OPTIONS,
|
||||
FILING_TYPE_OPTIONS,
|
||||
} from "~/enumerate/enterpriseOptions";
|
||||
import { formSelectField } from "~/utils/enterpriseForm";
|
||||
import {
|
||||
creditCodeRule,
|
||||
latitudeRule,
|
||||
longitudeRule,
|
||||
nonNegativeIntegerRule,
|
||||
phoneRule,
|
||||
positiveNumberRule,
|
||||
urlRule,
|
||||
} from "~/utils/validators";
|
||||
|
||||
const numberFieldProps = { style: { width: "100%" } };
|
||||
|
||||
/** 机构信息管理 / 备案信息详情 共用表单字段配置 */
|
||||
export function buildOrgInfoFormOptions() {
|
||||
return [
|
||||
{
|
||||
name: "orgName",
|
||||
label: "生产经营单位名称",
|
||||
rules: [{ required: true, message: "请输入生产经营单位名称" }],
|
||||
},
|
||||
{
|
||||
name: "creditCode",
|
||||
label: "统一社会信用代码",
|
||||
rules: [creditCodeRule(true)],
|
||||
},
|
||||
{
|
||||
name: "safetyIndustryCategory",
|
||||
label: "安全生产监管行业类别",
|
||||
rules: [{ required: true, message: "请输入安全生产监管行业类别" }],
|
||||
},
|
||||
{
|
||||
...formSelectField("regionCountyName", "属地", CHONGQING_DISTRICTS, {
|
||||
rules: [{ required: true, message: "请选择属地" }],
|
||||
colProps: { span: 12 },
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "regionStreetName",
|
||||
label: "所属镇、街道",
|
||||
rules: [{ required: true, message: "请输入所属镇街道" }],
|
||||
},
|
||||
{
|
||||
name: "regionCommunityName",
|
||||
label: "属村(社区)",
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: "longitude",
|
||||
label: "所在地坐标经度",
|
||||
render: FORM_ITEM_RENDER_ENUM.INPUT_NUMBER,
|
||||
useConstraints: false,
|
||||
componentProps: { ...numberFieldProps, min: -180, max: 180, precision: 6 },
|
||||
rules: [longitudeRule(false)],
|
||||
},
|
||||
{
|
||||
name: "latitude",
|
||||
label: "所在地坐标纬度",
|
||||
render: FORM_ITEM_RENDER_ENUM.INPUT_NUMBER,
|
||||
useConstraints: false,
|
||||
componentProps: { ...numberFieldProps, min: -90, max: 90, precision: 6 },
|
||||
rules: [latitudeRule(false)],
|
||||
},
|
||||
{
|
||||
name: "registerAddress",
|
||||
label: "注册地址",
|
||||
rules: [{ required: true, message: "请输入注册地址" }],
|
||||
render: FORM_ITEM_RENDER_ENUM.TEXTAREA,
|
||||
},
|
||||
{
|
||||
name: "businessAddress",
|
||||
label: "经营地址",
|
||||
rules: [{ required: true, message: "请输入经营地址" }],
|
||||
render: FORM_ITEM_RENDER_ENUM.TEXTAREA,
|
||||
},
|
||||
{
|
||||
name: "ownershipType",
|
||||
label: "归属类型",
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: "gbIndustryCode",
|
||||
label: "国民经济行业分类(GB/T4754-2017)",
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: "legalRepresentative",
|
||||
label: "法定代表人",
|
||||
rules: [{ required: true, message: "请输入法定代表人" }],
|
||||
},
|
||||
{
|
||||
name: "legalRepPhone",
|
||||
label: "法定代表人联系电话",
|
||||
rules: [phoneRule("法定代表人联系电话", false)],
|
||||
},
|
||||
{
|
||||
name: "principal",
|
||||
label: "主要负责人",
|
||||
rules: [{ required: true, message: "请输入主要负责人" }],
|
||||
},
|
||||
{
|
||||
name: "principalPhone",
|
||||
label: "主要负责人联系电话",
|
||||
rules: [phoneRule("主要负责人联系电话", true)],
|
||||
},
|
||||
{
|
||||
name: "safetyDeptHead",
|
||||
label: "安全管理部门负责人",
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: "safetyDeptPhone",
|
||||
label: "安全管理部门负责人联系电话",
|
||||
rules: [phoneRule("安全管理部门负责人联系电话", false)],
|
||||
},
|
||||
{
|
||||
name: "safetyVp",
|
||||
label: "主管安全副总",
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: "safetyVpPhone",
|
||||
label: "主管安全副总联系电话",
|
||||
rules: [phoneRule("主管安全副总联系电话", false)],
|
||||
},
|
||||
{
|
||||
name: "productionDate",
|
||||
label: "投产日期",
|
||||
required: false,
|
||||
render: FORM_ITEM_RENDER_ENUM.DATE,
|
||||
},
|
||||
{
|
||||
name: "businessStatus",
|
||||
label: "企业经营状态",
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: "disclosureUrl",
|
||||
label: "信息公开网址",
|
||||
useConstraints: false,
|
||||
rules: [urlRule("信息公开网址", false)],
|
||||
},
|
||||
{
|
||||
name: "workplaceArea",
|
||||
label: "工作场所建筑面积",
|
||||
render: FORM_ITEM_RENDER_ENUM.INPUT_NUMBER,
|
||||
useConstraints: false,
|
||||
componentProps: { ...numberFieldProps, min: 0, precision: 2 },
|
||||
rules: [positiveNumberRule("工作场所建筑面积", false)],
|
||||
},
|
||||
{
|
||||
name: "archiveRoomArea",
|
||||
label: "档案室面积",
|
||||
render: FORM_ITEM_RENDER_ENUM.INPUT_NUMBER,
|
||||
useConstraints: false,
|
||||
componentProps: { ...numberFieldProps, min: 0, precision: 2 },
|
||||
rules: [positiveNumberRule("档案室面积", false)],
|
||||
},
|
||||
{
|
||||
name: "fullTimeEvaluatorCount",
|
||||
label: "专职安全评价师数量",
|
||||
render: FORM_ITEM_RENDER_ENUM.INPUT_NUMBER,
|
||||
useConstraints: false,
|
||||
componentProps: { ...numberFieldProps, min: 0, precision: 0 },
|
||||
rules: [nonNegativeIntegerRule("专职安全评价师数量", false)],
|
||||
},
|
||||
{
|
||||
name: "registeredSafetyEngineerCount",
|
||||
label: "注册安全工程师数量",
|
||||
render: FORM_ITEM_RENDER_ENUM.INPUT_NUMBER,
|
||||
useConstraints: false,
|
||||
componentProps: { ...numberFieldProps, min: 0, precision: 0 },
|
||||
rules: [nonNegativeIntegerRule("注册安全工程师数量", false)],
|
||||
},
|
||||
formSelectField("enterpriseStatus", "企业状态", ENTERPRISE_STATUS_OPTIONS, { required: false }),
|
||||
formSelectField("enterpriseScale", "企业规模", ENTERPRISE_SCALE_OPTIONS, { required: false }),
|
||||
formSelectField("filingType", "备案类型", FILING_TYPE_OPTIONS, { required: false }),
|
||||
formSelectField("filingRecordStatus", "备案状态", FILING_RECORD_STATUS_OPTIONS, { required: false }),
|
||||
{
|
||||
name: "attachments",
|
||||
label: "上传附件",
|
||||
required: false,
|
||||
render: (
|
||||
<Upload maxCount={5} />
|
||||
),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
@ -3,31 +3,11 @@ import { Button, Form, message, Space } from "antd";
|
|||
import { useEffect, useState } from "react";
|
||||
import FormBuilder from "zy-react-library/components/FormBuilder";
|
||||
import Page from "zy-react-library/components/Page";
|
||||
import Upload from "zy-react-library/components/Upload";
|
||||
import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender";
|
||||
import { getOrgInfoDetail } from "~/api/enterpriseInfo/orgBootstrap";
|
||||
import PageHeader from "../components/PageHeader";
|
||||
import { NS_ORG_INFO } from "~/enumerate/namespace";
|
||||
import {
|
||||
CHONGQING_DISTRICTS,
|
||||
ENTERPRISE_SCALE_OPTIONS,
|
||||
ENTERPRISE_STATUS_OPTIONS,
|
||||
FILING_RECORD_STATUS_OPTIONS,
|
||||
FILING_TYPE_OPTIONS,
|
||||
} from "~/enumerate/enterpriseOptions";
|
||||
import { formSelectField } from "~/utils/enterpriseForm";
|
||||
import { mockUploadFileList } from "~/utils/mockUpload";
|
||||
import {
|
||||
creditCodeRule,
|
||||
latitudeRule,
|
||||
longitudeRule,
|
||||
nonNegativeIntegerRule,
|
||||
phoneRule,
|
||||
positiveNumberRule,
|
||||
urlRule,
|
||||
} from "~/utils/validators";
|
||||
|
||||
const numberFieldProps = { style: { width: "100%" } };
|
||||
import { buildOrgInfoFormOptions } from "./formOptions";
|
||||
|
||||
function OrgInfoPage(props) {
|
||||
const [form] = Form.useForm();
|
||||
|
|
@ -37,6 +17,8 @@ function OrgInfoPage(props) {
|
|||
/** 是否已存在机构数据(有 id 视为已入库,只能修改) */
|
||||
const [hasExistingData, setHasExistingData] = useState(false);
|
||||
|
||||
const formOptions = buildOrgInfoFormOptions();
|
||||
|
||||
const loadDetail = async () => {
|
||||
try {
|
||||
const res = getOrgInfoDetail();
|
||||
|
|
@ -117,179 +99,6 @@ function OrgInfoPage(props) {
|
|||
}
|
||||
};
|
||||
|
||||
const formOptions = [
|
||||
{
|
||||
name: "orgName",
|
||||
label: "生产经营单位名称",
|
||||
rules: [{ required: true, message: "请输入生产经营单位名称" }],
|
||||
},
|
||||
{
|
||||
name: "creditCode",
|
||||
label: "统一社会信用代码",
|
||||
rules: [creditCodeRule(true)],
|
||||
},
|
||||
{
|
||||
name: "safetyIndustryCategory",
|
||||
label: "安全生产监管行业类别",
|
||||
rules: [{ required: true, message: "请输入安全生产监管行业类别" }],
|
||||
},
|
||||
{
|
||||
...formSelectField("regionCountyName", "属地", CHONGQING_DISTRICTS, {
|
||||
rules: [{ required: true, message: "请选择属地" }],
|
||||
colProps: { span: 12 },
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "regionStreetName",
|
||||
label: "所属镇、街道",
|
||||
rules: [{ required: true, message: "请输入所属镇街道" }],
|
||||
},
|
||||
{
|
||||
name: "regionCommunityName",
|
||||
label: "属村(社区)",
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: "longitude",
|
||||
label: "所在地坐标经度",
|
||||
render: FORM_ITEM_RENDER_ENUM.INPUT_NUMBER,
|
||||
useConstraints: false,
|
||||
componentProps: { ...numberFieldProps, min: -180, max: 180, precision: 6 },
|
||||
rules: [longitudeRule(false)],
|
||||
},
|
||||
{
|
||||
name: "latitude",
|
||||
label: "所在地坐标纬度",
|
||||
render: FORM_ITEM_RENDER_ENUM.INPUT_NUMBER,
|
||||
useConstraints: false,
|
||||
componentProps: { ...numberFieldProps, min: -90, max: 90, precision: 6 },
|
||||
rules: [latitudeRule(false)],
|
||||
},
|
||||
{
|
||||
name: "registerAddress",
|
||||
label: "注册地址",
|
||||
rules: [{ required: true, message: "请输入注册地址" }],
|
||||
render: FORM_ITEM_RENDER_ENUM.TEXTAREA,
|
||||
},
|
||||
{
|
||||
name: "businessAddress",
|
||||
label: "经营地址",
|
||||
rules: [{ required: true, message: "请输入经营地址" }],
|
||||
render: FORM_ITEM_RENDER_ENUM.TEXTAREA,
|
||||
},
|
||||
{
|
||||
name: "ownershipType",
|
||||
label: "归属类型",
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: "gbIndustryCode",
|
||||
label: "国民经济行业分类(GB/T4754-2017)",
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: "legalRepresentative",
|
||||
label: "法定代表人",
|
||||
rules: [{ required: true, message: "请输入法定代表人" }],
|
||||
},
|
||||
{
|
||||
name: "legalRepPhone",
|
||||
label: "法定代表人联系电话",
|
||||
rules: [phoneRule("法定代表人联系电话", false)],
|
||||
},
|
||||
{
|
||||
name: "principal",
|
||||
label: "主要负责人",
|
||||
rules: [{ required: true, message: "请输入主要负责人" }],
|
||||
},
|
||||
{
|
||||
name: "principalPhone",
|
||||
label: "主要负责人联系电话",
|
||||
rules: [phoneRule("主要负责人联系电话", true)],
|
||||
},
|
||||
{
|
||||
name: "safetyDeptHead",
|
||||
label: "安全管理部门负责人",
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: "safetyDeptPhone",
|
||||
label: "安全管理部门负责人联系电话",
|
||||
rules: [phoneRule("安全管理部门负责人联系电话", false)],
|
||||
},
|
||||
{
|
||||
name: "safetyVp",
|
||||
label: "主管安全副总",
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: "safetyVpPhone",
|
||||
label: "主管安全副总联系电话",
|
||||
rules: [phoneRule("主管安全副总联系电话", false)],
|
||||
},
|
||||
{
|
||||
name: "productionDate",
|
||||
label: "投产日期",
|
||||
required: false,
|
||||
render: FORM_ITEM_RENDER_ENUM.DATE,
|
||||
},
|
||||
{
|
||||
name: "businessStatus",
|
||||
label: "企业经营状态",
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: "disclosureUrl",
|
||||
label: "信息公开网址",
|
||||
useConstraints: false,
|
||||
rules: [urlRule("信息公开网址", false)],
|
||||
},
|
||||
{
|
||||
name: "workplaceArea",
|
||||
label: "工作场所建筑面积",
|
||||
render: FORM_ITEM_RENDER_ENUM.INPUT_NUMBER,
|
||||
useConstraints: false,
|
||||
componentProps: { ...numberFieldProps, min: 0, precision: 2 },
|
||||
rules: [positiveNumberRule("工作场所建筑面积", false)],
|
||||
},
|
||||
{
|
||||
name: "archiveRoomArea",
|
||||
label: "档案室面积",
|
||||
render: FORM_ITEM_RENDER_ENUM.INPUT_NUMBER,
|
||||
useConstraints: false,
|
||||
componentProps: { ...numberFieldProps, min: 0, precision: 2 },
|
||||
rules: [positiveNumberRule("档案室面积", false)],
|
||||
},
|
||||
{
|
||||
name: "fullTimeEvaluatorCount",
|
||||
label: "专职安全评价师数量",
|
||||
render: FORM_ITEM_RENDER_ENUM.INPUT_NUMBER,
|
||||
useConstraints: false,
|
||||
componentProps: { ...numberFieldProps, min: 0, precision: 0 },
|
||||
rules: [nonNegativeIntegerRule("专职安全评价师数量", false)],
|
||||
},
|
||||
{
|
||||
name: "registeredSafetyEngineerCount",
|
||||
label: "注册安全工程师数量",
|
||||
render: FORM_ITEM_RENDER_ENUM.INPUT_NUMBER,
|
||||
useConstraints: false,
|
||||
componentProps: { ...numberFieldProps, min: 0, precision: 0 },
|
||||
rules: [nonNegativeIntegerRule("注册安全工程师数量", false)],
|
||||
},
|
||||
formSelectField("enterpriseStatus", "企业状态", ENTERPRISE_STATUS_OPTIONS, { required: false }),
|
||||
formSelectField("enterpriseScale", "企业规模", ENTERPRISE_SCALE_OPTIONS, { required: false }),
|
||||
formSelectField("filingType", "备案类型", FILING_TYPE_OPTIONS, { required: false }),
|
||||
formSelectField("filingRecordStatus", "备案状态", FILING_RECORD_STATUS_OPTIONS, { required: false }),
|
||||
{
|
||||
name: "attachments",
|
||||
label: "上传附件",
|
||||
required: false,
|
||||
render: (
|
||||
<Upload maxCount={5} />
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Page isShowAllAction={false}>
|
||||
<PageHeader
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { Connect } from "@cqsjjb/jjb-dva-runtime";
|
||||
import { Button, DatePicker, Descriptions, Form, Input, message, Modal, Row, Col, Select, Space } from "antd";
|
||||
import { Button, DatePicker, Form, Input, message, Modal, Row, Col, Select, Space } from "antd";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import AddIcon from "zy-react-library/components/Icon/AddIcon";
|
||||
import Page from "zy-react-library/components/Page";
|
||||
|
|
@ -23,9 +23,7 @@ import { toDayjs } from "~/utils/dateFormat";
|
|||
import { formSelectField } from "~/utils/enterpriseForm";
|
||||
import { idCardRule, mobileRule } from "~/utils/validators";
|
||||
import PageHeader from "../../components/PageHeader";
|
||||
|
||||
const GENDER_MAP = { 1: "男", 2: "女" };
|
||||
const REGISTER_ENGINEER_MAP = { 1: "是", 2: "否" };
|
||||
import StaffViewModal from "../StaffViewModal";
|
||||
|
||||
function mapPositionOptions(list = []) {
|
||||
return list.map((p) => ({
|
||||
|
|
@ -501,80 +499,4 @@ function StaffFormModal({
|
|||
);
|
||||
}
|
||||
|
||||
function StaffViewModal({ open, currentId, requestDetails, onCancel }) {
|
||||
const [info, setInfo] = useState({});
|
||||
const [detailLoading, setDetailLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open || !currentId) {
|
||||
setInfo({});
|
||||
return;
|
||||
}
|
||||
let cancelled = false;
|
||||
setDetailLoading(true);
|
||||
safeRequest(requestDetails, { id: currentId }).then((res) => {
|
||||
if (!cancelled) {
|
||||
setInfo(res?.data || {});
|
||||
}
|
||||
}).finally(() => {
|
||||
if (!cancelled) {
|
||||
setDetailLoading(false);
|
||||
}
|
||||
});
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [open, currentId]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
destroyOnClose
|
||||
title="查看"
|
||||
width={900}
|
||||
loading={detailLoading}
|
||||
cancelText="返回"
|
||||
okButtonProps={{ style: { display: "none" } }}
|
||||
onCancel={onCancel}
|
||||
>
|
||||
<Descriptions bordered column={2} labelStyle={{ width: 120 }}>
|
||||
<Descriptions.Item label="姓名">{info.staffName}</Descriptions.Item>
|
||||
<Descriptions.Item label="性别">{GENDER_MAP[info.gender]}</Descriptions.Item>
|
||||
<Descriptions.Item label="出生日期">{info.birthDate}</Descriptions.Item>
|
||||
<Descriptions.Item label="账号">{info.account}</Descriptions.Item>
|
||||
<Descriptions.Item label="部门">{info.deptName}</Descriptions.Item>
|
||||
<Descriptions.Item label="岗位">{info.positionName}</Descriptions.Item>
|
||||
<Descriptions.Item label="人员类型">{info.personType || "基础人员"}</Descriptions.Item>
|
||||
<Descriptions.Item label="资质范围">{info.qualScope || "-"}</Descriptions.Item>
|
||||
<Descriptions.Item label="职业等级">{info.professionalLevel || "-"}</Descriptions.Item>
|
||||
<Descriptions.Item label="证书编号">{info.evaluatorCertNo || "-"}</Descriptions.Item>
|
||||
<Descriptions.Item label="学历类型">{info.educationType || "-"}</Descriptions.Item>
|
||||
<Descriptions.Item label="学历层次">{info.educationLevel || "-"}</Descriptions.Item>
|
||||
<Descriptions.Item label="职称">{info.titleName || "-"}</Descriptions.Item>
|
||||
<Descriptions.Item label="是否注册安全工程师">
|
||||
{REGISTER_ENGINEER_MAP[info.registerEngineerFlag] ?? "-"}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="身份证号">{info.idCardNo}</Descriptions.Item>
|
||||
<Descriptions.Item label="学历">{info.education || "-"}</Descriptions.Item>
|
||||
<Descriptions.Item label="现住地址" span={2}>{info.homeAddress}</Descriptions.Item>
|
||||
<Descriptions.Item label="办公地址" span={2}>{info.officeAddress}</Descriptions.Item>
|
||||
<Descriptions.Item label="毕业院校">{info.graduateSchool}</Descriptions.Item>
|
||||
<Descriptions.Item label="专业">{info.major}</Descriptions.Item>
|
||||
<Descriptions.Item label="出版学术专著、专利、获奖、发表学术论文等" span={2}>
|
||||
{info.publications || "-"}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="自我申报的专业能力及认定方式" span={2}>
|
||||
{info.abilityDeclaration || "-"}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="主要学习工作经历" span={2}>
|
||||
{info.workExperience || "-"}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="申报专业能力证明材料" span={2}>
|
||||
{(info.proofMaterials || []).map((file) => file.name || file.fileName).join("、") || "-"}
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
export default Connect([NS_STAFF_INFO, NS_ORG_DEPARTMENT, NS_ORG_POSITION], true)(PersonnelInfoPage);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,220 @@
|
|||
import { Button, Descriptions, Modal, Space, Table } from "antd";
|
||||
import { useEffect, useState } from "react";
|
||||
import FilePreviewModal, { FilePreviewContent } from "~/components/FilePreviewModal";
|
||||
import { fetchStaffCertDetail, fetchStaffCertListByPersonnelId } from "~/api/staffCertificate";
|
||||
import { safeRequest } from "~/utils";
|
||||
|
||||
const GENDER_MAP = { 1: "男", 2: "女" };
|
||||
const REGISTER_ENGINEER_MAP = { 1: "是", 2: "否" };
|
||||
|
||||
export default function StaffViewModal({
|
||||
open,
|
||||
currentId,
|
||||
requestDetails,
|
||||
requestCertList = fetchStaffCertListByPersonnelId,
|
||||
requestCertDetails = fetchStaffCertDetail,
|
||||
onCancel,
|
||||
}) {
|
||||
const [info, setInfo] = useState({});
|
||||
const [certificates, setCertificates] = useState([]);
|
||||
const [detailLoading, setDetailLoading] = useState(false);
|
||||
const [attachmentPreview, setAttachmentPreview] = useState(null);
|
||||
const [certPreview, setCertPreview] = useState(null);
|
||||
const [certPreviewLoading, setCertPreviewLoading] = useState(false);
|
||||
const [certPreviewInfo, setCertPreviewInfo] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open || !currentId) {
|
||||
setInfo({});
|
||||
setCertificates([]);
|
||||
return;
|
||||
}
|
||||
let cancelled = false;
|
||||
setDetailLoading(true);
|
||||
Promise.all([
|
||||
safeRequest(requestDetails, { id: currentId }),
|
||||
typeof requestCertList === "function"
|
||||
? Promise.resolve(requestCertList(currentId)).catch((err) => {
|
||||
console.warn("[StaffViewModal] load certificates failed:", err);
|
||||
return [];
|
||||
})
|
||||
: Promise.resolve([]),
|
||||
]).then(([detailRes, certList]) => {
|
||||
if (!cancelled) {
|
||||
setInfo(detailRes?.data || {});
|
||||
setCertificates(Array.isArray(certList) ? certList : []);
|
||||
}
|
||||
}).finally(() => {
|
||||
if (!cancelled) {
|
||||
setDetailLoading(false);
|
||||
}
|
||||
});
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [open, currentId, requestDetails, requestCertList]);
|
||||
|
||||
const openCertPreview = async (record) => {
|
||||
setCertPreview(record);
|
||||
setCertPreviewInfo(null);
|
||||
if (typeof requestCertDetails !== "function") {
|
||||
setCertPreviewInfo(record);
|
||||
return;
|
||||
}
|
||||
setCertPreviewLoading(true);
|
||||
try {
|
||||
const res = await safeRequest(requestCertDetails, { id: record.id });
|
||||
setCertPreviewInfo(res?.data || record);
|
||||
}
|
||||
finally {
|
||||
setCertPreviewLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const proofFiles = info.proofMaterials || [];
|
||||
const certPreviewFiles = certPreviewInfo?.certImgs?.length
|
||||
? certPreviewInfo.certImgs
|
||||
: certPreviewInfo?.certImgFiles || [];
|
||||
const certPreviewUrl = certPreviewFiles[0]?.url;
|
||||
const certPreviewName = certPreviewFiles[0]?.fileName || certPreviewFiles[0]?.name || certPreviewInfo?.certName;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
open={open}
|
||||
destroyOnClose
|
||||
title="查看"
|
||||
width={900}
|
||||
loading={detailLoading}
|
||||
cancelText="关闭"
|
||||
okButtonProps={{ style: { display: "none" } }}
|
||||
onCancel={onCancel}
|
||||
>
|
||||
<Descriptions bordered column={2} labelStyle={{ width: 120 }}>
|
||||
<Descriptions.Item label="姓名">{info.staffName}</Descriptions.Item>
|
||||
<Descriptions.Item label="性别">{GENDER_MAP[info.gender]}</Descriptions.Item>
|
||||
<Descriptions.Item label="出生日期">{info.birthDate}</Descriptions.Item>
|
||||
<Descriptions.Item label="账号">{info.account}</Descriptions.Item>
|
||||
<Descriptions.Item label="部门">{info.deptName}</Descriptions.Item>
|
||||
<Descriptions.Item label="岗位">{info.positionName}</Descriptions.Item>
|
||||
<Descriptions.Item label="人员类型">{info.personType || "基础人员"}</Descriptions.Item>
|
||||
<Descriptions.Item label="资质范围">{info.qualScope || "-"}</Descriptions.Item>
|
||||
<Descriptions.Item label="职业等级">{info.professionalLevel || "-"}</Descriptions.Item>
|
||||
<Descriptions.Item label="证书编号">{info.evaluatorCertNo || "-"}</Descriptions.Item>
|
||||
<Descriptions.Item label="学历类型">{info.educationType || "-"}</Descriptions.Item>
|
||||
<Descriptions.Item label="学历层次">{info.educationLevel || "-"}</Descriptions.Item>
|
||||
<Descriptions.Item label="职称">{info.titleName || "-"}</Descriptions.Item>
|
||||
<Descriptions.Item label="是否注册安全工程师">
|
||||
{REGISTER_ENGINEER_MAP[info.registerEngineerFlag] ?? "-"}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="身份证号">{info.idCardNo}</Descriptions.Item>
|
||||
<Descriptions.Item label="学历">{info.education || "-"}</Descriptions.Item>
|
||||
<Descriptions.Item label="现住地址" span={2}>{info.homeAddress}</Descriptions.Item>
|
||||
<Descriptions.Item label="办公地址" span={2}>{info.officeAddress}</Descriptions.Item>
|
||||
<Descriptions.Item label="毕业院校">{info.graduateSchool}</Descriptions.Item>
|
||||
<Descriptions.Item label="专业">{info.major}</Descriptions.Item>
|
||||
<Descriptions.Item label="出版学术专著、专利、获奖、发表学术论文等" span={2}>
|
||||
{info.publications || "-"}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="自我申报的专业能力及认定方式" span={2}>
|
||||
{info.abilityDeclaration || "-"}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="主要学习工作经历" span={2}>
|
||||
{info.workExperience || "-"}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="申报专业能力证明材料" span={2}>
|
||||
{proofFiles.length ? (
|
||||
<Space direction="vertical" size={4}>
|
||||
{proofFiles.map((file, index) => {
|
||||
const fileName = file.name || file.fileName || `专业能力证明${index + 1}.pdf`;
|
||||
return (
|
||||
<Space key={`${fileName}-${index}`}>
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
style={{ padding: 0 }}
|
||||
onClick={() => setAttachmentPreview({ url: file.url, fileName })}
|
||||
>
|
||||
查看附件
|
||||
</Button>
|
||||
<span>{fileName}</span>
|
||||
</Space>
|
||||
);
|
||||
})}
|
||||
</Space>
|
||||
) : "-"}
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
|
||||
<div style={{ marginTop: 16, marginBottom: 8, fontWeight: 600 }}>证照信息</div>
|
||||
<Table
|
||||
size="small"
|
||||
bordered
|
||||
rowKey="id"
|
||||
pagination={false}
|
||||
dataSource={certificates}
|
||||
locale={{ emptyText: "暂无证照信息" }}
|
||||
columns={[
|
||||
{ title: "证照名称", dataIndex: "certName" },
|
||||
{ title: "证书编号", dataIndex: "certNo" },
|
||||
{ title: "证书类别", dataIndex: "certCategory" },
|
||||
{ title: "发证机关", dataIndex: "issueOrg" },
|
||||
{ title: "有效开始日期", dataIndex: "validStartDate", width: 120 },
|
||||
{ title: "有效结束日期", dataIndex: "validEndDate", width: 120 },
|
||||
{
|
||||
title: "操作",
|
||||
width: 90,
|
||||
render: (_, record) => (
|
||||
<Button type="link" size="small" onClick={() => openCertPreview(record)}>查看证书</Button>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Modal>
|
||||
|
||||
<FilePreviewModal
|
||||
open={!!attachmentPreview}
|
||||
title="附件预览"
|
||||
fileName={attachmentPreview?.fileName}
|
||||
url={attachmentPreview?.url}
|
||||
onCancel={() => setAttachmentPreview(null)}
|
||||
/>
|
||||
|
||||
<Modal
|
||||
open={!!certPreview}
|
||||
destroyOnClose
|
||||
title="证书预览"
|
||||
width={720}
|
||||
loading={certPreviewLoading}
|
||||
cancelText="关闭"
|
||||
okText={certPreviewUrl ? "新窗口打开" : "关闭"}
|
||||
okButtonProps={{ style: certPreviewUrl ? undefined : { display: "none" } }}
|
||||
onCancel={() => {
|
||||
setCertPreview(null);
|
||||
setCertPreviewInfo(null);
|
||||
}}
|
||||
onOk={() => {
|
||||
if (certPreviewUrl) {
|
||||
window.open(certPreviewUrl, "_blank");
|
||||
}
|
||||
setCertPreview(null);
|
||||
setCertPreviewInfo(null);
|
||||
}}
|
||||
>
|
||||
{certPreviewInfo && (
|
||||
<div>
|
||||
<Descriptions bordered column={1} size="small" style={{ marginBottom: 12 }}>
|
||||
<Descriptions.Item label="证照名称">{certPreviewInfo.certName}</Descriptions.Item>
|
||||
<Descriptions.Item label="证书编号">{certPreviewInfo.certNo}</Descriptions.Item>
|
||||
<Descriptions.Item label="证书类别">{certPreviewInfo.certCategory || "-"}</Descriptions.Item>
|
||||
<Descriptions.Item label="发证机关">{certPreviewInfo.issueOrg || "-"}</Descriptions.Item>
|
||||
<Descriptions.Item label="有效开始日期">{certPreviewInfo.validStartDate || "-"}</Descriptions.Item>
|
||||
<Descriptions.Item label="有效结束日期">{certPreviewInfo.validEndDate || "-"}</Descriptions.Item>
|
||||
</Descriptions>
|
||||
<FilePreviewContent url={certPreviewUrl} fileName={certPreviewName} />
|
||||
</div>
|
||||
)}
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -16,6 +16,8 @@ import {
|
|||
ApartmentOutlined,
|
||||
SwapOutlined,
|
||||
LogoutOutlined,
|
||||
FileTextOutlined,
|
||||
KeyOutlined,
|
||||
} from "@ant-design/icons";
|
||||
|
||||
const menuItems = [
|
||||
|
|
@ -29,6 +31,33 @@ const menuItems = [
|
|||
label: "Test2 测试",
|
||||
icon: <ExperimentOutlined />,
|
||||
},
|
||||
{
|
||||
key: "/certificate/container/Supervision/BasicInfo",
|
||||
label: "基础信息管理",
|
||||
icon: <FileTextOutlined />,
|
||||
children: [
|
||||
{
|
||||
key: "/certificate/container/Supervision/BasicInfo/OrgAccount",
|
||||
label: "机构账号管理",
|
||||
icon: <KeyOutlined />,
|
||||
},
|
||||
{
|
||||
key: "/certificate/container/Supervision/BasicInfo/RegisteredOrg/List",
|
||||
label: "已备案机构管理",
|
||||
icon: <FileProtectOutlined />,
|
||||
},
|
||||
{
|
||||
key: "/certificate/container/Supervision/BasicInfo/EvaluatorInfo",
|
||||
label: "评价师信息管理",
|
||||
icon: <UserOutlined />,
|
||||
},
|
||||
{
|
||||
key: "/certificate/container/Supervision/BasicInfo/EnterprisePortrait",
|
||||
label: "企业画像管理",
|
||||
icon: <BarChartOutlined />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: "/certificate/container/Supervision/EnterpriseLicense",
|
||||
label: "企业证照",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,205 @@
|
|||
import { Button, Card, Col, Descriptions, Form, Modal, Row, Statistic, Tag } from "antd";
|
||||
import { useEffect, useState } from "react";
|
||||
import Page from "zy-react-library/components/Page";
|
||||
import Search from "zy-react-library/components/Search";
|
||||
import Table from "zy-react-library/components/Table";
|
||||
import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender";
|
||||
import useTable from "zy-react-library/hooks/useTable";
|
||||
import { CHONGQING_DISTRICTS } from "~/enumerate/enterpriseOptions";
|
||||
import {
|
||||
ACCOUNT_SOURCE_OPTIONS,
|
||||
getEnterprisePortraitDetail,
|
||||
queryEnterprisePortraitPage,
|
||||
} from "~/mock/regulator/basicInfo";
|
||||
import PageHeader from "../../../EnterpriseInfo/components/PageHeader";
|
||||
|
||||
const INFO_LEVEL_COLOR = {
|
||||
level1: "success",
|
||||
level2: "processing",
|
||||
level3: "warning",
|
||||
};
|
||||
|
||||
const SCORE_RING_COLOR = {
|
||||
"A级": "#52c41a",
|
||||
"B级": "#1677ff",
|
||||
"C级": "#faad14",
|
||||
};
|
||||
|
||||
function EnterprisePortraitPage() {
|
||||
const [searchForm] = Form.useForm();
|
||||
const [viewOpen, setViewOpen] = useState(false);
|
||||
const [detail, setDetail] = useState(null);
|
||||
const [detailLoading, setDetailLoading] = useState(false);
|
||||
|
||||
const { tableProps, getData } = useTable(queryEnterprisePortraitPage, { form: searchForm });
|
||||
|
||||
useEffect(() => {
|
||||
getData();
|
||||
}, []);
|
||||
|
||||
const openProfile = async (id) => {
|
||||
setViewOpen(true);
|
||||
setDetailLoading(true);
|
||||
const res = await getEnterprisePortraitDetail(id);
|
||||
setDetail(res?.data || null);
|
||||
setDetailLoading(false);
|
||||
};
|
||||
|
||||
const closeProfile = () => {
|
||||
setViewOpen(false);
|
||||
setDetail(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<Page isShowAllAction={false}>
|
||||
<PageHeader
|
||||
title="企业画像管理"
|
||||
subTitle="监管端查看企业安全能力画像与综合评级(当前为 Mock 数据)。"
|
||||
/>
|
||||
<Search
|
||||
form={searchForm}
|
||||
options={[
|
||||
{ name: "enterpriseName", label: "企业名称", placeholder: "关键字搜索" },
|
||||
{
|
||||
name: "district",
|
||||
label: "属地",
|
||||
render: FORM_ITEM_RENDER_ENUM.SELECT,
|
||||
options: CHONGQING_DISTRICTS,
|
||||
},
|
||||
{
|
||||
name: "accountSource",
|
||||
label: "开户来源",
|
||||
render: FORM_ITEM_RENDER_ENUM.SELECT,
|
||||
options: ACCOUNT_SOURCE_OPTIONS,
|
||||
},
|
||||
]}
|
||||
onFinish={getData}
|
||||
/>
|
||||
<Table
|
||||
{...tableProps}
|
||||
columns={[
|
||||
{ title: "企业名称", dataIndex: "enterpriseName", ellipsis: true },
|
||||
{ title: "项目按时完成", dataIndex: "projectOnTime", width: 120 },
|
||||
{ title: "报告质量", dataIndex: "reportQuality", width: 100 },
|
||||
{ title: "投诉记录", dataIndex: "complaintCount", width: 90 },
|
||||
{ title: "行政处罚", dataIndex: "penaltyCount", width: 90 },
|
||||
{
|
||||
title: "信息等级",
|
||||
width: 100,
|
||||
render: (_, record) => (
|
||||
<Tag color={INFO_LEVEL_COLOR[record.infoLevelCode] || "default"}>
|
||||
{record.infoLevel}
|
||||
</Tag>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
width: 100,
|
||||
render: (_, record) => (
|
||||
<Button type="link" onClick={() => openProfile(record.id)}>查看画像</Button>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<Modal
|
||||
open={viewOpen}
|
||||
destroyOnClose
|
||||
title="企业安全能力画像"
|
||||
width={800}
|
||||
loading={detailLoading}
|
||||
cancelText="关闭"
|
||||
okButtonProps={{ style: { display: "none" } }}
|
||||
onCancel={closeProfile}
|
||||
>
|
||||
{detail && (
|
||||
<>
|
||||
<div style={{ display: "flex", gap: 24, marginBottom: 24, alignItems: "center" }}>
|
||||
<div
|
||||
style={{
|
||||
width: 80,
|
||||
height: 80,
|
||||
borderRadius: "50%",
|
||||
border: `4px solid ${SCORE_RING_COLOR[detail.scoreGrade] || "#1677ff"}`,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
fontSize: 20,
|
||||
fontWeight: 700,
|
||||
color: SCORE_RING_COLOR[detail.scoreGrade] || "#1677ff",
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
{detail.scoreGrade}
|
||||
</div>
|
||||
<div>
|
||||
<h3 style={{ margin: "0 0 4px" }}>{detail.enterpriseName}</h3>
|
||||
<p style={{ margin: 0, color: "rgba(0,0,0,0.45)", fontSize: 13 }}>
|
||||
统一社会信用代码:
|
||||
{detail.creditCode}
|
||||
{" "}
|
||||
|
|
||||
{detail.industry}
|
||||
{" "}
|
||||
|
|
||||
{detail.district}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<Row gutter={16}>
|
||||
<Col span={6}>
|
||||
<Card size="small">
|
||||
<Statistic title="项目按时完成率" value={detail.projectOnTimeRate} />
|
||||
<div style={{ marginTop: 8, color: "#52c41a", fontSize: 12 }}>
|
||||
↑
|
||||
{" "}
|
||||
{detail.projectOnTime}
|
||||
</div>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Card size="small">
|
||||
<Statistic title="报告质量" value={detail.reportQuality} />
|
||||
<div style={{ marginTop: 8, color: "rgba(0,0,0,0.45)", fontSize: 12 }}>
|
||||
{detail.reportQualityDesc}
|
||||
</div>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Card size="small">
|
||||
<Statistic
|
||||
title="投诉/处罚"
|
||||
value={detail.complaintCount + detail.penaltyCount}
|
||||
/>
|
||||
<div style={{ marginTop: 8, color: "rgba(0,0,0,0.45)", fontSize: 12 }}>
|
||||
{detail.penaltyDesc}
|
||||
</div>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Card size="small">
|
||||
<Statistic title="评价次数" value={detail.evalCount} />
|
||||
<div style={{ marginTop: 8, color: "rgba(0,0,0,0.45)", fontSize: 12 }}>
|
||||
{detail.evalCountDesc}
|
||||
</div>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
<Descriptions bordered column={2} size="small" style={{ marginTop: 24 }}>
|
||||
<Descriptions.Item label="信息等级">
|
||||
<Tag color={INFO_LEVEL_COLOR[detail.infoLevelCode] || "default"}>
|
||||
{detail.infoLevel}
|
||||
</Tag>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="开户来源">{detail.accountSource}</Descriptions.Item>
|
||||
<Descriptions.Item label="投诉记录">{detail.complaintCount}</Descriptions.Item>
|
||||
<Descriptions.Item label="行政处罚">{detail.penaltyCount}</Descriptions.Item>
|
||||
</Descriptions>
|
||||
</>
|
||||
)}
|
||||
</Modal>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
|
||||
export default EnterprisePortraitPage;
|
||||
|
|
@ -0,0 +1,208 @@
|
|||
import { Button, Descriptions, Form, Modal, Table, Tag, Tooltip } from "antd";
|
||||
import { useEffect, useState } from "react";
|
||||
import Page from "zy-react-library/components/Page";
|
||||
import Search from "zy-react-library/components/Search";
|
||||
import TableList from "zy-react-library/components/Table";
|
||||
import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender";
|
||||
import useTable from "zy-react-library/hooks/useTable";
|
||||
import { getEvaluatorDetail, queryEvaluatorPage } from "~/mock/regulator/basicInfo";
|
||||
import PageHeader from "../../../EnterpriseInfo/components/PageHeader";
|
||||
|
||||
const YES_NO_OPTIONS = [
|
||||
{ label: "是", value: 1 },
|
||||
{ label: "否", value: 0 },
|
||||
];
|
||||
|
||||
const STATUS_OPTIONS = [
|
||||
{ label: "正常", value: "normal" },
|
||||
{ label: "异常", value: "abnormal" },
|
||||
];
|
||||
|
||||
const CERT_STATUS_COLOR = {
|
||||
normal: "success",
|
||||
expiring: "warning",
|
||||
expired: "error",
|
||||
};
|
||||
|
||||
function EvaluatorInfoPage() {
|
||||
const [searchForm] = Form.useForm();
|
||||
const [viewOpen, setViewOpen] = useState(false);
|
||||
const [detail, setDetail] = useState(null);
|
||||
const [detailLoading, setDetailLoading] = useState(false);
|
||||
const [certPreview, setCertPreview] = useState(null);
|
||||
|
||||
const { tableProps, getData } = useTable(queryEvaluatorPage, { form: searchForm });
|
||||
|
||||
useEffect(() => {
|
||||
getData();
|
||||
}, []);
|
||||
|
||||
const openDetail = async (id) => {
|
||||
setViewOpen(true);
|
||||
setDetailLoading(true);
|
||||
const res = await getEvaluatorDetail(id);
|
||||
setDetail(res?.data || null);
|
||||
setDetailLoading(false);
|
||||
};
|
||||
|
||||
const closeDetail = () => {
|
||||
setViewOpen(false);
|
||||
setDetail(null);
|
||||
setCertPreview(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<Page isShowAllAction={false}>
|
||||
<PageHeader
|
||||
title="评价师信息管理"
|
||||
subTitle="监管端查看辖区内评价师备案及资质状态(当前为 Mock 数据)。"
|
||||
/>
|
||||
<Search
|
||||
form={searchForm}
|
||||
options={[
|
||||
{ name: "evaluatorName", label: "评价师名称", placeholder: "关键字搜索" },
|
||||
{ name: "orgName", label: "机构名称", placeholder: "关键字搜索" },
|
||||
{
|
||||
name: "employmentStatus",
|
||||
label: "是否在职",
|
||||
render: FORM_ITEM_RENDER_ENUM.SELECT,
|
||||
options: YES_NO_OPTIONS,
|
||||
},
|
||||
{
|
||||
name: "registerEngineerFlag",
|
||||
label: "是否注册安全工程师",
|
||||
render: FORM_ITEM_RENDER_ENUM.SELECT,
|
||||
options: YES_NO_OPTIONS,
|
||||
},
|
||||
{
|
||||
name: "status",
|
||||
label: "状态",
|
||||
render: FORM_ITEM_RENDER_ENUM.SELECT,
|
||||
options: STATUS_OPTIONS,
|
||||
},
|
||||
]}
|
||||
onFinish={getData}
|
||||
/>
|
||||
<TableList
|
||||
{...tableProps}
|
||||
columns={[
|
||||
{ title: "机构名称", dataIndex: "orgName", ellipsis: true },
|
||||
{ title: "评价师名称", dataIndex: "evaluatorName", width: 120 },
|
||||
{
|
||||
title: "是否注册安全工程师",
|
||||
dataIndex: "registerEngineerLabel",
|
||||
width: 160,
|
||||
},
|
||||
{ title: "资格证书", dataIndex: "certificatesSummary", ellipsis: true },
|
||||
{
|
||||
title: "状态",
|
||||
width: 90,
|
||||
render: (_, record) => {
|
||||
const tag = (
|
||||
<Tag color={record.status === "normal" ? "success" : "error"}>
|
||||
{record.statusLabel}
|
||||
</Tag>
|
||||
);
|
||||
return record.statusTip ? <Tooltip title={record.statusTip}>{tag}</Tooltip> : tag;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
width: 80,
|
||||
render: (_, record) => (
|
||||
<Button type="link" onClick={() => openDetail(record.id)}>查看</Button>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<Modal
|
||||
open={viewOpen}
|
||||
destroyOnClose
|
||||
title="评价师详细信息"
|
||||
width={900}
|
||||
loading={detailLoading}
|
||||
cancelText="关闭"
|
||||
okButtonProps={{ style: { display: "none" } }}
|
||||
onCancel={closeDetail}
|
||||
>
|
||||
{detail && (
|
||||
<>
|
||||
<Descriptions title="个人信息" bordered column={2} size="small" style={{ marginBottom: 16 }}>
|
||||
<Descriptions.Item label="姓名">{detail.name}</Descriptions.Item>
|
||||
<Descriptions.Item label="性别">{detail.gender}</Descriptions.Item>
|
||||
<Descriptions.Item label="出生日期">{detail.birthDate}</Descriptions.Item>
|
||||
<Descriptions.Item label="身份证号">{detail.idCard}</Descriptions.Item>
|
||||
<Descriptions.Item label="学历">{detail.education}</Descriptions.Item>
|
||||
<Descriptions.Item label="毕业院校">{detail.graduateSchool}</Descriptions.Item>
|
||||
<Descriptions.Item label="专业">{detail.major}</Descriptions.Item>
|
||||
</Descriptions>
|
||||
<Descriptions title="机构信息" bordered column={2} size="small" style={{ marginBottom: 16 }}>
|
||||
<Descriptions.Item label="所在机构">{detail.orgName}</Descriptions.Item>
|
||||
<Descriptions.Item label="部门">{detail.deptName}</Descriptions.Item>
|
||||
<Descriptions.Item label="岗位">{detail.positionName}</Descriptions.Item>
|
||||
<Descriptions.Item label="账号">{detail.account}</Descriptions.Item>
|
||||
</Descriptions>
|
||||
<div style={{ marginBottom: 8, fontWeight: 600 }}>资质证照</div>
|
||||
<Table
|
||||
size="small"
|
||||
bordered
|
||||
rowKey="id"
|
||||
pagination={false}
|
||||
dataSource={detail.certificates || []}
|
||||
columns={[
|
||||
{ title: "证书名称", dataIndex: "certName" },
|
||||
{ title: "证书编号", dataIndex: "certNo" },
|
||||
{ title: "发证日期", dataIndex: "issueDate", width: 110 },
|
||||
{ title: "有效期至", dataIndex: "expireDate", width: 110 },
|
||||
{
|
||||
title: "状态",
|
||||
width: 90,
|
||||
render: (_, record) => {
|
||||
const tag = (
|
||||
<Tag color={CERT_STATUS_COLOR[record.status] || "default"}>
|
||||
{record.statusLabel}
|
||||
</Tag>
|
||||
);
|
||||
return record.statusTip ? <Tooltip title={record.statusTip}>{tag}</Tooltip> : tag;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
width: 90,
|
||||
render: (_, record) => (
|
||||
<Button type="link" size="small" onClick={() => setCertPreview(record)}>
|
||||
查看证书
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
open={!!certPreview}
|
||||
destroyOnClose
|
||||
title="证书预览"
|
||||
width={520}
|
||||
cancelText="关闭"
|
||||
okButtonProps={{ style: { display: "none" } }}
|
||||
onCancel={() => setCertPreview(null)}
|
||||
>
|
||||
{certPreview && (
|
||||
<Descriptions bordered column={1} size="small">
|
||||
<Descriptions.Item label="证书名称">{certPreview.certName}</Descriptions.Item>
|
||||
<Descriptions.Item label="证书编号">{certPreview.certNo}</Descriptions.Item>
|
||||
<Descriptions.Item label="发证日期">{certPreview.issueDate}</Descriptions.Item>
|
||||
<Descriptions.Item label="有效期至">{certPreview.expireDate}</Descriptions.Item>
|
||||
<Descriptions.Item label="状态">{certPreview.statusLabel}</Descriptions.Item>
|
||||
</Descriptions>
|
||||
)}
|
||||
</Modal>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
|
||||
export default EvaluatorInfoPage;
|
||||
|
|
@ -0,0 +1,346 @@
|
|||
import {
|
||||
Button, Descriptions, Form, Input, InputNumber, message, Modal, Row, Col, Select, Space, Tag,
|
||||
} from "antd";
|
||||
import { Connect } from "@cqsjjb/jjb-dva-runtime";
|
||||
import { useEffect, useState } from "react";
|
||||
import Page from "zy-react-library/components/Page";
|
||||
import Search from "zy-react-library/components/Search";
|
||||
import Table from "zy-react-library/components/Table";
|
||||
import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender";
|
||||
import useTable from "zy-react-library/hooks/useTable";
|
||||
import { isOrgAccountEnabled } from "~/api/enterpriseInfo/adapter";
|
||||
import {
|
||||
CHONGQING_DISTRICTS,
|
||||
ENTERPRISE_SCALE_OPTIONS,
|
||||
ENTERPRISE_STATUS_OPTIONS,
|
||||
ORG_ACCOUNT_STATE_OPTIONS,
|
||||
} from "~/enumerate/enterpriseOptions";
|
||||
import { NS_ORG_INFO } from "~/enumerate/namespace";
|
||||
import { safeListRequest, safeRequest } from "~/utils";
|
||||
import { formSelectField } from "~/utils/enterpriseForm";
|
||||
import PageHeader from "../../../EnterpriseInfo/components/PageHeader";
|
||||
|
||||
const SEARCH_COL = { xs: 24, sm: 12, md: 8, lg: 6 };
|
||||
const SEARCH_DEFAULT_VALUES = { state: "" };
|
||||
|
||||
function OrgAccountPage(props) {
|
||||
const [searchForm] = Form.useForm();
|
||||
const [editForm] = Form.useForm();
|
||||
const [viewOpen, setViewOpen] = useState(false);
|
||||
const [editOpen, setEditOpen] = useState(false);
|
||||
const [currentId, setCurrentId] = useState(null);
|
||||
const [detail, setDetail] = useState(null);
|
||||
const [rawDetail, setRawDetail] = useState(null);
|
||||
const [detailLoading, setDetailLoading] = useState(false);
|
||||
const [editLoading, setEditLoading] = useState(false);
|
||||
|
||||
const { tableProps, getData } = useTable(safeListRequest(props.orgAccountList), { form: searchForm });
|
||||
|
||||
useEffect(() => {
|
||||
getData();
|
||||
}, []);
|
||||
|
||||
const loadDetail = async (id) => {
|
||||
setDetailLoading(true);
|
||||
const res = await safeRequest(props.orgAccountGet, { id });
|
||||
setDetail(res?.data || null);
|
||||
setRawDetail(res?.raw || null);
|
||||
setDetailLoading(false);
|
||||
return res;
|
||||
};
|
||||
|
||||
const openView = async (id) => {
|
||||
setCurrentId(id);
|
||||
setViewOpen(true);
|
||||
await loadDetail(id);
|
||||
};
|
||||
|
||||
const openEdit = async (id) => {
|
||||
setCurrentId(id);
|
||||
setEditOpen(true);
|
||||
const res = await loadDetail(id);
|
||||
if (res?.data) {
|
||||
editForm.setFieldsValue(res.data);
|
||||
}
|
||||
};
|
||||
|
||||
const onToggleStatus = (record) => {
|
||||
const enabled = isOrgAccountEnabled(record);
|
||||
Modal.confirm({
|
||||
title: "确认操作",
|
||||
content: enabled ? `确认禁用「${record.orgName}」账号?` : `确认启用「${record.orgName}」账号?`,
|
||||
okText: "确认",
|
||||
cancelText: "取消",
|
||||
onOk: async () => {
|
||||
const nextState = enabled ? 1 : 0;
|
||||
const res = await props.orgAccountUpdateState({ id: record.id, state: nextState });
|
||||
if (res?.success !== false) {
|
||||
message.success("操作成功");
|
||||
await getData();
|
||||
}
|
||||
else {
|
||||
message.error(res?.message || "操作失败");
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const onResetPassword = (record) => {
|
||||
Modal.confirm({
|
||||
title: "重置密码",
|
||||
content: (
|
||||
<div>
|
||||
<p>
|
||||
确认将
|
||||
<strong>{record.orgName}</strong>
|
||||
的密码重置为初始密码?
|
||||
</p>
|
||||
<p style={{ color: "rgba(0,0,0,0.45)", fontSize: 12 }}>初始密码:Ap@123456</p>
|
||||
</div>
|
||||
),
|
||||
okText: "确认重置",
|
||||
cancelText: "取消",
|
||||
onOk: async () => {
|
||||
const res = await props.orgAccountResetPassword({ id: record.id });
|
||||
if (res?.success !== false) {
|
||||
message.success("密码已重置");
|
||||
}
|
||||
else {
|
||||
message.error(res?.message || "重置失败");
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const onDelete = (record) => {
|
||||
Modal.confirm({
|
||||
title: "确认删除",
|
||||
content: (
|
||||
<div>
|
||||
<p>
|
||||
确认删除
|
||||
<strong>{record.orgName}</strong>
|
||||
的账号?
|
||||
</p>
|
||||
<p style={{ color: "rgba(0,0,0,0.45)", fontSize: 12 }}>此操作不可恢复,请谨慎操作。</p>
|
||||
</div>
|
||||
),
|
||||
okText: "确认删除",
|
||||
okButtonProps: { danger: true },
|
||||
cancelText: "取消",
|
||||
onOk: async () => {
|
||||
const res = await props.orgAccountDelete({ id: record.id });
|
||||
if (res?.success !== false) {
|
||||
message.success("删除成功");
|
||||
await getData();
|
||||
}
|
||||
else {
|
||||
message.error(res?.message || "删除失败");
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const onEditSubmit = async () => {
|
||||
const values = await editForm.validateFields();
|
||||
setEditLoading(true);
|
||||
const res = await props.orgAccountModify({
|
||||
...values,
|
||||
id: currentId,
|
||||
raw: rawDetail,
|
||||
});
|
||||
setEditLoading(false);
|
||||
if (res?.success !== false) {
|
||||
message.success("保存成功");
|
||||
setEditOpen(false);
|
||||
setCurrentId(null);
|
||||
setRawDetail(null);
|
||||
editForm.resetFields();
|
||||
await getData();
|
||||
}
|
||||
else {
|
||||
message.error(res?.message || "保存失败");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Page isShowAllAction={false}>
|
||||
<PageHeader
|
||||
title="评价机构账号管理"
|
||||
subTitle="监管端维护评价机构开户账号。"
|
||||
/>
|
||||
<Search
|
||||
form={searchForm}
|
||||
values={SEARCH_DEFAULT_VALUES}
|
||||
options={[
|
||||
{ name: "orgName", label: "机构名称", placeholder: "关键字搜索", colProps: SEARCH_COL },
|
||||
formSelectField("district", "属地", CHONGQING_DISTRICTS, {
|
||||
colProps: SEARCH_COL,
|
||||
placeholder: "请选择属地",
|
||||
}),
|
||||
formSelectField("state", "状态", ORG_ACCOUNT_STATE_OPTIONS, {
|
||||
colProps: SEARCH_COL,
|
||||
componentProps: { allowClear: false, showSearch: false },
|
||||
}),
|
||||
{
|
||||
name: "openTimeRange",
|
||||
label: "开户时间",
|
||||
render: FORM_ITEM_RENDER_ENUM.DATE_RANGE,
|
||||
colProps: { ...SEARCH_COL, lg: 8 },
|
||||
},
|
||||
]}
|
||||
onFinish={getData}
|
||||
/>
|
||||
<Table
|
||||
{...tableProps}
|
||||
columns={[
|
||||
{ title: "机构名称", dataIndex: "orgName", ellipsis: true },
|
||||
{ title: "属地", dataIndex: "district", width: 100 },
|
||||
{
|
||||
title: "状态",
|
||||
width: 80,
|
||||
render: (_, record) => (
|
||||
<Tag color={isOrgAccountEnabled(record) ? "success" : "error"}>
|
||||
{isOrgAccountEnabled(record) ? "启用" : "禁用"}
|
||||
</Tag>
|
||||
),
|
||||
},
|
||||
{ title: "开户时间", dataIndex: "createTime", width: 110 },
|
||||
{
|
||||
title: "操作",
|
||||
width: 320,
|
||||
fixed: "right",
|
||||
render: (_, record) => (
|
||||
<Space size={0} wrap>
|
||||
<Button type="link" size="small" onClick={() => openView(record.id)}>查看</Button>
|
||||
<Button type="link" size="small" onClick={() => openEdit(record.id)}>编辑</Button>
|
||||
<Button type="link" size="small" onClick={() => onResetPassword(record)}>重置密码</Button>
|
||||
<Button type="link" size="small" danger onClick={() => onDelete(record)}>删除</Button>
|
||||
<Button type="link" size="small" onClick={() => onToggleStatus(record)}>
|
||||
{isOrgAccountEnabled(record) ? "禁用" : "启用"}
|
||||
</Button>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<Modal
|
||||
open={viewOpen}
|
||||
destroyOnClose
|
||||
title="机构信息查看"
|
||||
width={760}
|
||||
loading={detailLoading}
|
||||
cancelText="关闭"
|
||||
okButtonProps={{ style: { display: "none" } }}
|
||||
onCancel={() => { setViewOpen(false); setDetail(null); setRawDetail(null); }}
|
||||
>
|
||||
{detail && (
|
||||
<Descriptions bordered column={2} size="small">
|
||||
<Descriptions.Item label="机构名称">{detail.orgName}</Descriptions.Item>
|
||||
<Descriptions.Item label="统一社会信用代码">{detail.creditCode}</Descriptions.Item>
|
||||
<Descriptions.Item label="属地">{detail.regionCountyName}</Descriptions.Item>
|
||||
<Descriptions.Item label="安全生产监管行业类别">{detail.safetyIndustryCategory}</Descriptions.Item>
|
||||
<Descriptions.Item label="经营地址" span={2}>{detail.businessAddress}</Descriptions.Item>
|
||||
<Descriptions.Item label="企业状态">{detail.enterpriseStatus}</Descriptions.Item>
|
||||
<Descriptions.Item label="经度/纬度">{detail.longitude}, {detail.latitude}</Descriptions.Item>
|
||||
<Descriptions.Item label="主要负责人">{detail.principal}</Descriptions.Item>
|
||||
<Descriptions.Item label="主要负责人电话">{detail.principalPhone}</Descriptions.Item>
|
||||
<Descriptions.Item label="法定代表人">{detail.legalRepresentative}</Descriptions.Item>
|
||||
<Descriptions.Item label="法人电话">{detail.legalRepPhone}</Descriptions.Item>
|
||||
<Descriptions.Item label="企业规模" span={2}>{detail.enterpriseScale}</Descriptions.Item>
|
||||
</Descriptions>
|
||||
)}
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
open={editOpen}
|
||||
destroyOnClose
|
||||
title="编辑机构信息"
|
||||
width={760}
|
||||
confirmLoading={editLoading}
|
||||
okText="保存"
|
||||
cancelText="取消"
|
||||
onCancel={() => {
|
||||
setEditOpen(false);
|
||||
setCurrentId(null);
|
||||
setRawDetail(null);
|
||||
editForm.resetFields();
|
||||
}}
|
||||
onOk={onEditSubmit}
|
||||
>
|
||||
<Form form={editForm} layout="vertical">
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Form.Item name="orgName" label="机构名称" rules={[{ required: true, message: "请输入机构名称" }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item name="creditCode" label="统一社会信用代码" rules={[{ required: true, message: "请输入信用代码" }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item name="regionCountyName" label="属地">
|
||||
<Select options={CHONGQING_DISTRICTS} placeholder="请选择属地" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item name="safetyIndustryCategory" label="安全生产监管行业类别">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Form.Item name="businessAddress" label="企事业单位经营地址">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item name="enterpriseStatus" label="企业状态">
|
||||
<Select options={ENTERPRISE_STATUS_OPTIONS} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Form.Item name="longitude" label="经度">
|
||||
<InputNumber style={{ width: "100%" }} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Form.Item name="latitude" label="纬度">
|
||||
<InputNumber style={{ width: "100%" }} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item name="principal" label="主要负责人">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item name="principalPhone" label="主要负责人电话">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item name="legalRepresentative" label="法定代表人">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item name="legalRepPhone" label="法人电话">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item name="enterpriseScale" label="企业规模">
|
||||
<Select options={ENTERPRISE_SCALE_OPTIONS.map((o) => ({ label: o.label, value: o.label }))} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form>
|
||||
</Modal>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
|
||||
export default Connect([NS_ORG_INFO], true)(OrgAccountPage);
|
||||
|
|
@ -0,0 +1,274 @@
|
|||
import { Button, Card, Form, Spin, Table, Tabs, Tag, message } from "antd";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import FormBuilder from "zy-react-library/components/FormBuilder";
|
||||
import Page from "zy-react-library/components/Page";
|
||||
import FilePreviewModal from "~/components/FilePreviewModal";
|
||||
import {
|
||||
fetchRegisteredOrgDetail,
|
||||
fetchRegisteredOrgPersonnelCertDetail,
|
||||
fetchRegisteredOrgPersonnelCertList,
|
||||
fetchRegisteredOrgPersonnelDetail,
|
||||
fetchRegisteredOrgPersonnelList,
|
||||
fetchRegisteredOrgQualificationGroups,
|
||||
} from "~/api/regulatorOrgInfo";
|
||||
import { buildOrgInfoFormOptions } from "../../../../EnterpriseInfo/OrgInfo/formOptions";
|
||||
import PageHeader from "../../../../EnterpriseInfo/components/PageHeader";
|
||||
import StaffViewModal from "../../../../EnterpriseInfo/PersonnelInfo/StaffViewModal";
|
||||
|
||||
const LIST_PATH = "/container/supervision/basicInfo/registeredOrg/list";
|
||||
const ORG_INFO_FORM_OPTIONS = buildOrgInfoFormOptions();
|
||||
|
||||
function parseQueryId(location) {
|
||||
const search = location?.search || window.location.search || "";
|
||||
return new URLSearchParams(search).get("id");
|
||||
}
|
||||
|
||||
function MaterialsTab({ materialGroups, loading, onPreview }) {
|
||||
if (loading) {
|
||||
return <Spin />;
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<div style={{ marginBottom: 12, color: "rgba(0,0,0,0.45)" }}>
|
||||
该机构共申请
|
||||
<strong>{materialGroups.length}</strong>
|
||||
个业务范围资质,以下为各范围对应的申请材料清单
|
||||
</div>
|
||||
{materialGroups.map((group) => (
|
||||
<Card
|
||||
key={group.id}
|
||||
size="small"
|
||||
style={{ marginBottom: 16 }}
|
||||
title={`业务范围:${group.scopeName}`}
|
||||
extra={(
|
||||
<span style={{ fontSize: 12 }}>
|
||||
证书有效期:
|
||||
<span style={{ color: group.expireWarning ? "#faad14" : "#52c41a", fontWeight: 500 }}>
|
||||
{group.expireDate}
|
||||
{group.expireWarning ? "(即将到期)" : ""}
|
||||
</span>
|
||||
</span>
|
||||
)}
|
||||
>
|
||||
<Table
|
||||
size="small"
|
||||
bordered
|
||||
rowKey="id"
|
||||
pagination={false}
|
||||
dataSource={group.materials}
|
||||
columns={[
|
||||
{ title: "材料名称", dataIndex: "name" },
|
||||
{
|
||||
title: "操作",
|
||||
width: 80,
|
||||
render: (_, record) => (
|
||||
<Button type="link" size="small" onClick={() => onPreview(record)}>预览</Button>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function PersonnelTab({ personnel, loading, onView }) {
|
||||
if (loading) {
|
||||
return <Spin />;
|
||||
}
|
||||
return (
|
||||
<Table
|
||||
size="small"
|
||||
bordered
|
||||
rowKey="id"
|
||||
pagination={false}
|
||||
dataSource={personnel}
|
||||
columns={[
|
||||
{ title: "序号", width: 60, render: (_, __, index) => index + 1 },
|
||||
{ title: "姓名", dataIndex: "name", width: 90 },
|
||||
{ title: "性别", dataIndex: "gender", width: 60 },
|
||||
{ title: "岗位", dataIndex: "position", width: 110 },
|
||||
{ title: "资质范围", dataIndex: "qualScope", width: 90 },
|
||||
{ title: "学历", dataIndex: "education", width: 80 },
|
||||
{
|
||||
title: "注册安全工程师",
|
||||
width: 120,
|
||||
render: (_, record) => (
|
||||
<Tag color={record.registerEngineer === 1 ? "success" : "error"}>
|
||||
{record.registerEngineer === 1 ? "是" : "否"}
|
||||
</Tag>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
width: 80,
|
||||
render: (_, record) => (
|
||||
<Button type="link" size="small" onClick={() => onView(record)}>查看</Button>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function RegisteredOrgDetailPage(props) {
|
||||
const id = useMemo(() => parseQueryId(props.location), [props.location?.search]);
|
||||
const [form] = Form.useForm();
|
||||
const [orgLoading, setOrgLoading] = useState(Boolean(parseQueryId(props.location)));
|
||||
const [extrasLoading, setExtrasLoading] = useState(Boolean(parseQueryId(props.location)));
|
||||
const [orgName, setOrgName] = useState("");
|
||||
const [materialGroups, setMaterialGroups] = useState([]);
|
||||
const [personnel, setPersonnel] = useState([]);
|
||||
const [filePreview, setFilePreview] = useState(null);
|
||||
const [viewPersonnelId, setViewPersonnelId] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
if (!id) {
|
||||
setOrgLoading(false);
|
||||
setExtrasLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
let cancelled = false;
|
||||
setOrgLoading(true);
|
||||
setExtrasLoading(true);
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
const [orgRes, groups, staffList] = await Promise.all([
|
||||
fetchRegisteredOrgDetail(id),
|
||||
fetchRegisteredOrgQualificationGroups(id).catch((err) => {
|
||||
console.warn("[RegisteredOrgDetail] qualification load failed:", err);
|
||||
message.error(err?.message || "加载申请材料失败");
|
||||
return [];
|
||||
}),
|
||||
fetchRegisteredOrgPersonnelList(id).catch((err) => {
|
||||
console.warn("[RegisteredOrgDetail] personnel load failed:", err);
|
||||
message.error(err?.message || "加载人员信息失败");
|
||||
return [];
|
||||
}),
|
||||
]);
|
||||
if (cancelled) {
|
||||
return;
|
||||
}
|
||||
if (orgRes?.data) {
|
||||
form.setFieldsValue(orgRes.data);
|
||||
setOrgName(orgRes.data.orgName || "");
|
||||
}
|
||||
setMaterialGroups(groups || []);
|
||||
setPersonnel(staffList || []);
|
||||
}
|
||||
catch (err) {
|
||||
if (!cancelled) {
|
||||
console.warn("[RegisteredOrgDetail] load failed:", err);
|
||||
message.error("加载机构详情失败");
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (!cancelled) {
|
||||
setOrgLoading(false);
|
||||
setExtrasLoading(false);
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [id]);
|
||||
|
||||
const goBack = () => {
|
||||
if (props.history?.goBack && props.history.length > 1) {
|
||||
props.history.goBack();
|
||||
return;
|
||||
}
|
||||
if (props.history?.push) {
|
||||
props.history.push(LIST_PATH);
|
||||
return;
|
||||
}
|
||||
window.location.href = `/certificate${LIST_PATH}`;
|
||||
};
|
||||
|
||||
if (!id) {
|
||||
return (
|
||||
<Page isShowAllAction={false}>
|
||||
<PageHeader title="备案信息详情" subTitle="缺少机构 ID 参数" extra={<Button onClick={goBack}>返回</Button>} />
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
|
||||
const tabItems = [
|
||||
{
|
||||
key: "info",
|
||||
label: "备案信息",
|
||||
children: (
|
||||
<Spin spinning={orgLoading}>
|
||||
<FormBuilder
|
||||
form={form}
|
||||
span={12}
|
||||
disabled
|
||||
useAutoGenerateRequired={false}
|
||||
options={ORG_INFO_FORM_OPTIONS}
|
||||
labelCol={{ span: 10 }}
|
||||
showActionButtons={false}
|
||||
/>
|
||||
</Spin>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "materials",
|
||||
label: "申请清单材料",
|
||||
children: (
|
||||
<MaterialsTab
|
||||
materialGroups={materialGroups}
|
||||
loading={extrasLoading}
|
||||
onPreview={setFilePreview}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "personnel",
|
||||
label: "人员信息",
|
||||
children: (
|
||||
<PersonnelTab
|
||||
personnel={personnel}
|
||||
loading={extrasLoading}
|
||||
onView={(record) => setViewPersonnelId(record.id)}
|
||||
/>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Page isShowAllAction={false}>
|
||||
<PageHeader
|
||||
title="备案信息详情"
|
||||
subTitle={orgName || undefined}
|
||||
extra={<Button onClick={goBack}>← 返回</Button>}
|
||||
/>
|
||||
<Tabs items={tabItems} />
|
||||
|
||||
<FilePreviewModal
|
||||
open={!!filePreview}
|
||||
title="文件预览"
|
||||
fileName={filePreview?.name}
|
||||
url={filePreview?.previewUrl}
|
||||
onCancel={() => setFilePreview(null)}
|
||||
/>
|
||||
|
||||
{viewPersonnelId && (
|
||||
<StaffViewModal
|
||||
open={!!viewPersonnelId}
|
||||
currentId={viewPersonnelId}
|
||||
requestDetails={fetchRegisteredOrgPersonnelDetail}
|
||||
requestCertList={fetchRegisteredOrgPersonnelCertList}
|
||||
requestCertDetails={fetchRegisteredOrgPersonnelCertDetail}
|
||||
onCancel={() => setViewPersonnelId("")}
|
||||
/>
|
||||
)}
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
|
||||
export default RegisteredOrgDetailPage;
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
import { Button, Form, Tag } from "antd";
|
||||
import { Connect } from "@cqsjjb/jjb-dva-runtime";
|
||||
import { useEffect } from "react";
|
||||
import Page from "zy-react-library/components/Page";
|
||||
import Search from "zy-react-library/components/Search";
|
||||
import TableList from "zy-react-library/components/Table";
|
||||
import useTable from "zy-react-library/hooks/useTable";
|
||||
import {
|
||||
REGISTERED_ORG_FILING_RECORD_STATUS_OPTIONS,
|
||||
REGISTERED_ORG_FILING_TYPE_SEARCH_OPTIONS,
|
||||
} from "~/enumerate/enterpriseOptions";
|
||||
import { NS_ORG_INFO } from "~/enumerate/namespace";
|
||||
import { safeListRequest } from "~/utils";
|
||||
import { formSelectField } from "~/utils/enterpriseForm";
|
||||
import PageHeader from "../../../../EnterpriseInfo/components/PageHeader";
|
||||
|
||||
const FILING_RECORD_STATUS_COLOR = {
|
||||
1: "success",
|
||||
2: "default",
|
||||
};
|
||||
|
||||
const SEARCH_COL = { xs: 24, sm: 12, md: 8, lg: 6 };
|
||||
const SEARCH_DEFAULT_VALUES = { filingType: "", filingRecordStatus: "" };
|
||||
/** 约定式路由首字母小写:registeredOrg/detail */
|
||||
const DETAIL_PATH = "/container/supervision/basicInfo/registeredOrg/detail";
|
||||
|
||||
function RegisteredOrgListPage(props) {
|
||||
const [searchForm] = Form.useForm();
|
||||
|
||||
const { tableProps, getData } = useTable(safeListRequest(props.registeredOrgList), { form: searchForm });
|
||||
|
||||
useEffect(() => {
|
||||
getData();
|
||||
}, []);
|
||||
|
||||
const goDetail = (id) => {
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
const query = `${DETAIL_PATH}?id=${encodeURIComponent(id)}`;
|
||||
if (props.history?.push) {
|
||||
props.history.push(query);
|
||||
return;
|
||||
}
|
||||
window.location.href = `/certificate${query}`;
|
||||
};
|
||||
|
||||
return (
|
||||
<Page isShowAllAction={false}>
|
||||
<PageHeader
|
||||
title="已备案机构管理"
|
||||
subTitle="监管端查看已备案评价机构及备案详情。"
|
||||
/>
|
||||
<Search
|
||||
form={searchForm}
|
||||
values={SEARCH_DEFAULT_VALUES}
|
||||
options={[
|
||||
{ name: "orgName", label: "评价机构名称", placeholder: "关键字搜索", colProps: SEARCH_COL },
|
||||
{ name: "registerAddress", label: "注册地址", placeholder: "关键字搜索", colProps: SEARCH_COL },
|
||||
formSelectField("filingType", "备案类型", REGISTERED_ORG_FILING_TYPE_SEARCH_OPTIONS, {
|
||||
colProps: SEARCH_COL,
|
||||
componentProps: { allowClear: false, showSearch: false },
|
||||
}),
|
||||
formSelectField("filingRecordStatus", "备案状态", REGISTERED_ORG_FILING_RECORD_STATUS_OPTIONS, {
|
||||
colProps: SEARCH_COL,
|
||||
componentProps: { allowClear: false, showSearch: false },
|
||||
}),
|
||||
]}
|
||||
onFinish={getData}
|
||||
/>
|
||||
<TableList
|
||||
{...tableProps}
|
||||
columns={[
|
||||
{ title: "评价机构名称", dataIndex: "orgName", ellipsis: true },
|
||||
{ title: "注册地址", dataIndex: "registerAddress", ellipsis: true },
|
||||
{ title: "服务企业数", dataIndex: "serviceEnterpriseCount", width: 100 },
|
||||
{ title: "评价项目数", dataIndex: "evalProjectCount", width: 100 },
|
||||
{ title: "备案类型", dataIndex: "filingType", width: 100 },
|
||||
{
|
||||
title: "备案状态",
|
||||
width: 90,
|
||||
render: (_, record) => (
|
||||
<Tag color={FILING_RECORD_STATUS_COLOR[record.filingRecordStatusCode] || "default"}>
|
||||
{record.filingRecordStatusName || "-"}
|
||||
</Tag>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
width: 120,
|
||||
render: (_, record) => (
|
||||
<Button type="link" onClick={() => goDetail(record.id)}>查看备案信息</Button>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
|
||||
export default Connect([NS_ORG_INFO], true)(RegisteredOrgListPage);
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
function RegisteredOrg(props) {
|
||||
return props.children || null;
|
||||
}
|
||||
|
||||
export default RegisteredOrg;
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
function BasicInfo(props) {
|
||||
return <div>{props.children}</div>;
|
||||
}
|
||||
|
||||
export default BasicInfo;
|
||||
|
|
@ -11,11 +11,38 @@ export function formatDateTime(value) {
|
|||
|
||||
/** 展示:2026-01-23 */
|
||||
export function formatDate(value) {
|
||||
if (value == null || value === "") {
|
||||
const normalized = normalizeDateValue(value);
|
||||
if (normalized == null || normalized === "") {
|
||||
return "-";
|
||||
}
|
||||
const d = dayjs(value);
|
||||
return d.isValid() ? d.format("YYYY-MM-DD") : String(value);
|
||||
const d = dayjs(normalized);
|
||||
return d.isValid() ? d.format("YYYY-MM-DD") : String(normalized);
|
||||
}
|
||||
|
||||
/** 兼容 Jackson LocalDateTime 数组 / 对象 / 字符串 */
|
||||
export function normalizeDateValue(value) {
|
||||
if (value == null || value === "") {
|
||||
return null;
|
||||
}
|
||||
if (typeof value === "string" || typeof value === "number") {
|
||||
return value;
|
||||
}
|
||||
if (dayjs.isDayjs(value)) {
|
||||
return value;
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
const [y, mo, d, h = 0, mi = 0, s = 0] = value;
|
||||
return new Date(y, mo - 1, d, h, mi, s);
|
||||
}
|
||||
if (typeof value === "object") {
|
||||
const y = value.year ?? value.y;
|
||||
const mo = value.monthValue ?? value.month ?? value.m;
|
||||
const d = value.dayOfMonth ?? value.day ?? value.d;
|
||||
if (y != null && mo != null && d != null) {
|
||||
return new Date(y, mo - 1, d);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** 提交 API:日期 */
|
||||
|
|
|
|||
Loading…
Reference in New Issue