From 53d7617b94329606ea04e3288c0d970ea311d7e0 Mon Sep 17 00:00:00 2001 From: huwei <3313749341@qq.com> Date: Fri, 31 Jul 2026 15:17:21 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9C=BA=E6=9E=84=E7=AB=AF=E3=80=81=E7=9B=91?= =?UTF-8?q?=E7=AE=A1=E7=AB=AF=E9=A6=96=E9=A1=B5=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/机构端-首页驾驶舱.openapi.yaml | 464 ++++++++++ docs/监管端-驾驶舱大屏.openapi.yaml | 818 ++++++++++++++++++ src/api/institution/index.js | 50 ++ src/api/supervision/index.js | 60 ++ src/pages/Container/Driver/index.js | 32 +- .../components/EnterpriseTypeChart.jsx | 76 +- .../Institution/components/InfoAlerts.jsx | 39 +- .../components/ProjectCompletionTable.jsx | 77 +- .../components/ProjectTypeChart.jsx | 79 +- .../Institution/components/StatisticCards.jsx | 36 +- src/pages/Container/Institution/index.css | 533 +++++++----- src/pages/Container/Institution/index.js | 262 +++++- src/pages/Container/Institution/mockData.js | 103 +-- .../Container/Supervision/Cockpit/index.js | 400 +++++++-- src/utils/resolveDriverAuthParams.js | 29 + 15 files changed, 2510 insertions(+), 548 deletions(-) create mode 100644 docs/机构端-首页驾驶舱.openapi.yaml create mode 100644 docs/监管端-驾驶舱大屏.openapi.yaml create mode 100644 src/api/institution/index.js create mode 100644 src/api/supervision/index.js diff --git a/docs/机构端-首页驾驶舱.openapi.yaml b/docs/机构端-首页驾驶舱.openapi.yaml new file mode 100644 index 0000000..22f1ba8 --- /dev/null +++ b/docs/机构端-首页驾驶舱.openapi.yaml @@ -0,0 +1,464 @@ +openapi: 3.0.3 +info: + title: 机构端-首页驾驶舱 API + description: | + 重庆市安全评价服务平台 - 机构端首页(驾驶舱)接口(`/container/driver?pageMode=3`)。 + + - 统一前缀:`/safetyEval/institution/dashboard` + - 全部为**新增**聚合接口(落在 `InstitutionDashboardController`),不依赖/不改动既有机构端接口 + - 鉴权:`orgId` 取自机构端请求上下文;未取到时返回空数据(不报错) + - 成功响应 `code = "0"` + - 大屏建议对 5 个接口加 30~60s 短 TTL 缓存,面板级相互独立 + + **Apifox 导入方式**:项目设置 → 导入数据 → 选择本文件(OpenAPI/Swagger 格式) + version: 1.0.0 + contact: + name: safety-eval-service + +servers: + - url: http://localhost:8095 + description: 本地开发 + - url: http://192.168.20.100:30140 + description: 测试网关(100) + +tags: + - name: 机构端-首页驾驶舱 + description: 机构端首页(驾驶舱)相关聚合接口 + +paths: + /safetyEval/institution/dashboard/project-node-stats: + get: + tags: + - 机构端-首页驾驶舱 + summary: 当前项目节点统计 + description: | + 当前机构项目节点统计:项目总数/法定项目 + 8 个流程节点待办数 + 已归档/项目延期。 + + - 数据源:`eval_project`(orgId)、`EvalProjectNodeOverviewAssembler.buildForInstitution`、延期计数 + - 节点待办口径:节点状态为「未开始」 + - 单项目装配异常跳过,不影响其余节点 + operationId: projectNodeStats + responses: + '200': + description: 成功 + content: + application/json: + schema: + $ref: '#/components/schemas/SingleResponseInstitutionDashboardProjectNodeStatsCO' + example: + success: true + code: "0" + data: + totalProjects: 12 + statutoryProjects: 8 + nodeStats: + - nodeCode: NODE_01 + nodeName: 待风险分析 + pendingCount: 3 + - nodeCode: NODE_02 + nodeName: 待合同录入 + pendingCount: 3 + - nodeCode: NODE_03 + nodeName: 待项目组成立 + pendingCount: 2 + - nodeCode: NODE_04 + nodeName: 待现场踏勘 + pendingCount: 4 + - nodeCode: NODE_05 + nodeName: 待报告编制 + pendingCount: 2 + - nodeCode: NODE_06 + nodeName: 待内部审核 + pendingCount: 1 + - nodeCode: NODE_07 + nodeName: 待技术审核 + pendingCount: 1 + - nodeCode: NODE_08 + nodeName: 待过程控制审核 + pendingCount: 2 + archivedProjectCount: 5 + delayedProjectCount: 1 + + /safetyEval/institution/dashboard/notices: + get: + tags: + - 机构端-首页驾驶舱 + summary: 通知提醒 + description: | + 通知提醒:资质现场审查通知 / 监督检查通知 / 未读监管通知。 + + - 数据源:`safety_message`(按 orgId + sendType;未读=sendState!=1) + - `qualOnSiteReviewCount`:sendType=ON_SITE_REVIEW_NOTICE + - `inspectionCount`:sendType=INSP_NOTICE_ORG + - `unreadRegulatoryCount`:本机构 sendState!=1 的消息数 + operationId: notices + responses: + '200': + description: 成功 + content: + application/json: + schema: + $ref: '#/components/schemas/SingleResponseInstitutionDashboardNoticeCO' + example: + success: true + code: "0" + data: + qualOnSiteReviewCount: 2 + inspectionCount: 5 + unreadRegulatoryCount: 7 + + /safetyEval/institution/dashboard/industry-stat: + get: + tags: + - 机构端-首页驾驶舱 + summary: 服务行业项目统计 + description: | + 按行业统计项目数与法定项目数(柱状图)。 + + - 数据源:`eval_project`(orgId)按 industry_code 分组 + - 行业名称取自 IndustryEnum;仅返回有项目的行业(前端按固定类目轴合并补 0) + operationId: industryStat + responses: + '200': + description: 成功 + content: + application/json: + schema: + $ref: '#/components/schemas/SingleResponseInstitutionDashboardIndustryStatCO' + example: + success: true + code: "0" + data: + list: + - industryCode: METAL_SMELTING + industryName: 金属冶炼 + projectCount: 4 + statutoryProjectCount: 3 + - industryCode: PETROCHEMICAL_CHEMICAL_PHARMACEUTICAL + industryName: 石油加工业,化学原料、化学品及医药制造业 + projectCount: 6 + statutoryProjectCount: 5 + - industryCode: FIREWORKS_FIRECRACKERS + industryName: 烟花爆竹制造业和仓储业 + projectCount: 2 + statutoryProjectCount: 0 + + /safetyEval/institution/dashboard/eval-type-ratio: + get: + tags: + - 机构端-首页驾驶舱 + summary: 评价类别占比 + description: | + 按评价类型统计项目数(环形图)。 + + - 数据源:`eval_project`(orgId)按 eval_type_code 分组 + - 名称取自 EvalTypeEnum(PRE/ACCEPT/STATUS) + operationId: evalTypeRatio + responses: + '200': + description: 成功 + content: + application/json: + schema: + $ref: '#/components/schemas/SingleResponseInstitutionDashboardEvalTypeRatioCO' + example: + success: true + code: "0" + data: + totalProjectCount: 12 + items: + - evalTypeCode: PRE + evalTypeName: 安全预评价 + count: 5 + - evalTypeCode: ACCEPT + evalTypeName: 安全设施竣工验收评价 + count: 4 + - evalTypeCode: STATUS + evalTypeName: 安全现状评价 + count: 3 + + /safetyEval/institution/dashboard/project-execution: + get: + tags: + - 机构端-首页驾驶舱 + summary: 项目执行情况 + description: | + 项目执行情况列表(表格)。 + + - 数据源:`eval_project`(orgId)、`eval_customer`(被评价企业名称) + - 按 planEndDate 升序取前 limit 条(最近到期在前,空日期置尾) + - 被评价企业名称解析失败置空,不影响其余行 + operationId: projectExecution + parameters: + - name: limit + in: query + required: false + description: 返回条数(默认 10,范围 1~50) + schema: + type: integer + format: int32 + default: 10 + minimum: 1 + maximum: 50 + responses: + '200': + description: 成功 + content: + application/json: + schema: + $ref: '#/components/schemas/SingleResponseInstitutionDashboardProjectExecutionCO' + example: + success: true + code: "0" + data: + list: + - projectId: 101 + projectNo: P-2026-001 + projectName: 某化工企业安全预评价 + customerName: 某化工有限公司 + evalTypeCode: PRE + evalTypeName: 安全预评价 + projectLeaderName: 张工 + planEndDate: "2026-08-15" + - projectId: 102 + projectNo: P-2026-002 + projectName: 某金属冶炼验收评价 + customerName: 某冶炼厂 + evalTypeCode: ACCEPT + evalTypeName: 安全设施竣工验收评价 + projectLeaderName: 李工 + planEndDate: "2026-09-01" + +components: + schemas: + SingleResponseInstitutionDashboardProjectNodeStatsCO: + type: object + properties: + success: + type: boolean + example: true + code: + type: string + example: "0" + message: + type: string + errMessage: + type: string + data: + $ref: '#/components/schemas/InstitutionDashboardProjectNodeStatsCO' + + SingleResponseInstitutionDashboardNoticeCO: + type: object + properties: + success: + type: boolean + example: true + code: + type: string + example: "0" + message: + type: string + errMessage: + type: string + data: + $ref: '#/components/schemas/InstitutionDashboardNoticeCO' + + SingleResponseInstitutionDashboardIndustryStatCO: + type: object + properties: + success: + type: boolean + example: true + code: + type: string + example: "0" + message: + type: string + errMessage: + type: string + data: + $ref: '#/components/schemas/InstitutionDashboardIndustryStatCO' + + SingleResponseInstitutionDashboardEvalTypeRatioCO: + type: object + properties: + success: + type: boolean + example: true + code: + type: string + example: "0" + message: + type: string + errMessage: + type: string + data: + $ref: '#/components/schemas/InstitutionDashboardEvalTypeRatioCO' + + SingleResponseInstitutionDashboardProjectExecutionCO: + type: object + properties: + success: + type: boolean + example: true + code: + type: string + example: "0" + message: + type: string + errMessage: + type: string + data: + $ref: '#/components/schemas/InstitutionDashboardProjectExecutionCO' + + InstitutionDashboardProjectNodeStatsCO: + type: object + description: 当前项目节点统计 + properties: + totalProjects: + type: integer + format: int32 + description: 项目总数 + statutoryProjects: + type: integer + format: int32 + description: 法定项目数 + nodeStats: + type: array + description: 8 个流程节点待办数(待办=节点状态未开始) + items: + $ref: '#/components/schemas/NodeStat' + archivedProjectCount: + type: integer + format: int32 + description: 已归档项目数 + delayedProjectCount: + type: integer + format: int32 + description: 项目延期数 + + NodeStat: + type: object + properties: + nodeCode: + type: string + description: 节点编码(NODE_01..NODE_08) + nodeName: + type: string + description: 节点展示名称(待X) + pendingCount: + type: integer + format: int32 + description: 待办数 + + InstitutionDashboardNoticeCO: + type: object + description: 通知提醒 + properties: + qualOnSiteReviewCount: + type: integer + format: int32 + description: 资质现场审查通知数(sendType=ON_SITE_REVIEW_NOTICE) + inspectionCount: + type: integer + format: int32 + description: 监督检查通知数(sendType=INSP_NOTICE_ORG) + unreadRegulatoryCount: + type: integer + format: int32 + description: 未读监管通知数(sendState!=1) + + InstitutionDashboardIndustryStatCO: + type: object + description: 服务行业项目统计 + properties: + list: + type: array + description: 行业分布统计(仅返回有项目的行业) + items: + $ref: '#/components/schemas/IndustryStat' + + IndustryStat: + type: object + properties: + industryCode: + type: string + description: 行业编码(IndustryEnum) + industryName: + type: string + description: 行业名称 + projectCount: + type: integer + format: int32 + description: 项目数 + statutoryProjectCount: + type: integer + format: int32 + description: 法定项目数 + + InstitutionDashboardEvalTypeRatioCO: + type: object + description: 评价类别占比 + properties: + totalProjectCount: + type: integer + format: int32 + description: 项目总数 + items: + type: array + description: 各评价类别项目数 + items: + $ref: '#/components/schemas/EvalTypeRatio' + + EvalTypeRatio: + type: object + properties: + evalTypeCode: + type: string + description: 评价类型编码(EvalTypeEnum) + evalTypeName: + type: string + description: 评价类型名称 + count: + type: integer + format: int32 + description: 项目数 + + InstitutionDashboardProjectExecutionCO: + type: object + description: 项目执行情况 + properties: + list: + type: array + description: 项目执行情况列表(按 planEndDate 升序取前 N 条) + items: + $ref: '#/components/schemas/ProjectExecutionItem' + + ProjectExecutionItem: + type: object + properties: + projectId: + type: integer + format: int64 + description: 项目ID + projectNo: + type: string + description: 项目编号 + projectName: + type: string + description: 项目名称 + customerName: + type: string + description: 被评价企业 + evalTypeCode: + type: string + description: 评价类型编码 + evalTypeName: + type: string + description: 评价类型名称 + projectLeaderName: + type: string + description: 项目负责人 + planEndDate: + type: string + description: 项目结束日期(yyyy-MM-dd) diff --git a/docs/监管端-驾驶舱大屏.openapi.yaml b/docs/监管端-驾驶舱大屏.openapi.yaml new file mode 100644 index 0000000..9b0c09e --- /dev/null +++ b/docs/监管端-驾驶舱大屏.openapi.yaml @@ -0,0 +1,818 @@ +openapi: 3.0.3 +info: + title: 监管端-驾驶舱大屏 API + description: | + 重庆市安全评价服务平台 - 监管端「驾驶舱大屏」页面接口(`/container/driver` → 监管端 Cockpit)。 + + - 统一前缀:`/safetyEval/regulator/cockpit` + - 全部为**新增**聚合接口(落在 `RegulatorCockpitController`),不依赖/不改动既有监管端接口 + - 成功响应 `code = "0"` + - 大屏建议对 7 个接口加 30~60s 短 TTL 缓存,降低 DB 压力;面板级接口相互独立,单接口异常不影响其余面板 + + **Apifox 导入方式**:项目设置 → 导入数据 → 选择本文件(OpenAPI/Swagger 格式) + version: 1.0.0 + contact: + name: safety-eval-service + +servers: + - url: http://localhost:8095 + description: 本地开发 + - url: http://192.168.20.100:30140 + description: 测试网关(100) + +tags: + - name: 监管端-驾驶舱大屏 + description: 监管端驾驶舱大屏相关聚合接口 + +paths: + /safetyEval/regulator/cockpit/qualification-overview: + get: + tags: + - 监管端-驾驶舱大屏 + summary: 资质全生命周期管理 + description: | + 资质全生命周期统计:机构备案、评价师、行业分布。 + + - 数据源:`org_info`、`org_personnel`、`IndustryEnum` + - 边界:仅机构/人员/资质维度,不含项目与风险 + - 已知缺口:`logoutOrgCount` 现有字典无「注销」状态,暂置 0(待补来源) + operationId: qualificationOverview + parameters: + - $ref: '#/components/parameters/yearParam' + responses: + '200': + description: 成功 + content: + application/json: + schema: + $ref: '#/components/schemas/SingleResponseCockpitQualificationOverviewCO' + example: + success: true + code: "0" + data: + lifecycleStats: + newFilingOrgCount: 12 + currentFilingOrgCount: 86 + logoutOrgCount: 0 + totalFilingOrgCount: 120 + exitedEvaluatorCount: 5 + currentEvaluatorCount: 340 + progress: + filingCompleteRate: 71.67 + bizActiveRate: 63.95 + newGrowthRate: 13.95 + industryList: + - industryCode: "1" + industryName: 危险化学品 + orgCount: 32 + typeCount: 32 + - industryCode: "2" + industryName: 非煤矿山 + orgCount: 24 + typeCount: 24 + - industryCode: "3" + industryName: 金属冶炼 + orgCount: 18 + typeCount: 18 + + /safetyEval/regulator/cockpit/kpi: + get: + tags: + - 监管端-驾驶舱大屏 + summary: 核心KPI指标 + description: | + 核心 KPI 统计,仅提供「备案项目开展评价率」(现有数据源可支撑)。 + + - 数据源:`eval_project`(`is_statutory=1`)、`eval_risk_analysis`(风险分析已建立=已开展评价) + - 边界:**不提供**「企业评价项目合格率」(无合格状态字段)、「隐患整改率」(无隐患表) + - `value`=率值(百分比);`yoy`=当前周期率 − 上一周期率(百分点) + operationId: kpi + parameters: + - name: cycle + in: query + description: 统计周期 + schema: + type: string + enum: + - year + - quarter + - month + default: year + - $ref: '#/components/parameters/yearParam' + responses: + '200': + description: 成功 + content: + application/json: + schema: + $ref: '#/components/schemas/SingleResponseCockpitKpiCO' + example: + success: true + code: "0" + data: + items: + - code: EVAL_RATE + name: 备案项目开展评价率 + value: 78.5 + yoy: 4.2 + + /safetyEval/regulator/cockpit/region-distribution: + get: + tags: + - 监管端-驾驶舱大屏 + summary: 区域地图分布 + description: | + 按区县统计评价项目数与备案机构数。 + + - 数据源:`eval_project.district_code`(法定项目按区县计数)、`org_info.district_code`(已备案机构按区县计数) + - 边界:仅「评价项目数」「备案机构数」两维度;隐患/整改率无数据源,前端可用项目数替代 + - 两路按 `district_code` 分组后对齐,缺侧补 0 + operationId: regionDistribution + responses: + '200': + description: 成功 + content: + application/json: + schema: + $ref: '#/components/schemas/SingleResponseCockpitRegionDistributionCO' + example: + success: true + code: "0" + data: + list: + - districtCode: "500101" + districtName: 万州区 + evalProjectCount: 45 + filingOrgCount: 8 + - districtCode: "500103" + districtName: 渝中区 + evalProjectCount: 32 + filingOrgCount: 6 + summary: + totalEvalProject: 520 + totalFilingOrg: 86 + + /safetyEval/regulator/cockpit/process-overview: + get: + tags: + - 监管端-驾驶舱大屏 + summary: 执业全过程管控 + 项目流程(节点聚合) + description: | + 执业全过程管控与项目流程节点聚合统计。 + + - 数据源:复用 `EvalProjectNodeOverviewAssembler.buildForRegulator(projectId)` 逐项目取节点状态,全局累加 + - `totalCount`=到达该节点项目数(状态≠未开始);`pendingCount`=未开始(待办)项目数 + - 覆盖 8 个真实节点(风险分析/合同签订/成立项目组/现场勘查/过程管控/报告草稿/内审/技审);大屏额外的「初勘/从业告知/编制检查表/制定工作计划/归档」无独立实体,由前端并入相邻节点或留空 + operationId: processOverview + responses: + '200': + description: 成功 + content: + application/json: + schema: + $ref: '#/components/schemas/SingleResponseCockpitProcessOverviewCO' + example: + success: true + code: "0" + data: + nodes: + - nodeCode: RISK_ANALYSIS + nodeName: 风险分析 + totalCount: 480 + pendingCount: 40 + - nodeCode: CONTRACT_SIGN + nodeName: 合同签订 + totalCount: 455 + pendingCount: 25 + - nodeCode: GROUP_SETUP + nodeName: 成立项目组 + totalCount: 450 + pendingCount: 15 + - nodeCode: SITE_SURVEY + nodeName: 现场勘查 + totalCount: 430 + pendingCount: 30 + - nodeCode: PROCESS_CONTROL + nodeName: 过程管控 + totalCount: 400 + pendingCount: 50 + - nodeCode: REPORT_DRAFT + nodeName: 报告草稿 + totalCount: 360 + pendingCount: 40 + - nodeCode: INTERNAL_REVIEW + nodeName: 内审 + totalCount: 330 + pendingCount: 20 + - nodeCode: TECH_REVIEW + nodeName: 技审 + totalCount: 310 + pendingCount: 10 + + /safetyEval/regulator/cockpit/eval-type-trend: + get: + tags: + - 监管端-驾驶舱大屏 + summary: 评价类型趋势 + description: | + 按评价类型统计各时间桶的项目数与监督检查数。 + + - 数据源:`eval_project.eval_type_code`/`create_time`、`insp_notice.create_time`/`project_id` + - `projectCounts` 来自 `eval_project` 按 `eval_type_code`+时间桶;`inspCounts` 来自 `insp_notice` 经 `project_id` 关联 `eval_project` 的 `eval_type_code`+时间桶 + - 时间桶:月=01..12;季=Q1..Q4;月粒度=`period=month` 时按 `year` 的 1..12 + - 仅统计能关联到项目的检查 + operationId: evalTypeTrend + parameters: + - name: period + in: query + required: true + description: 时间粒度 + schema: + type: string + enum: + - year + - quarter + - month + - $ref: '#/components/parameters/yearParam' + responses: + '200': + description: 成功 + content: + application/json: + schema: + $ref: '#/components/schemas/SingleResponseCockpitEvalTypeTrendCO' + example: + success: true + code: "0" + data: + buckets: + - "01" + - "02" + - "03" + - "04" + series: + - evalTypeCode: "1" + evalTypeName: 安全预评价 + projectCounts: [12, 15, 10, 18] + inspCounts: [20, 25, 18, 30] + - evalTypeCode: "2" + evalTypeName: 安全验收评价 + projectCounts: [8, 9, 11, 7] + inspCounts: [15, 18, 20, 12] + + /safetyEval/regulator/cockpit/review-summary: + get: + tags: + - 监管端-驾驶舱大屏 + summary: 复盘评估改进提效 + description: | + 复盘评估统计:监督检查分类、发现问题、超期/处罚分布。 + + - 数据源:`insp_notice`、`eval_project`(`plan_end_date` 逾期、未归档) + - 检查类按 `insp_type_code` 与 `result_code` 分组:`insp_type_code` ∈ PROJECT_PROCESS/QUAL_KEEP/SPECIAL;`result_code` 1未发现问题/2责令整改/3转入风险预警 + - `pie` 仅输出可用的 OVERDUE(超期未完成数)、PUNISH(监管处罚数=resultCode=3);原大屏「现场确认风险隐患数/复查评估报告数/项目主要负责人变更数」无现成数据源,本次不输出 + operationId: reviewSummary + parameters: + - $ref: '#/components/parameters/yearParam' + responses: + '200': + description: 成功 + content: + application/json: + schema: + $ref: '#/components/schemas/SingleResponseCockpitReviewSummaryCO' + example: + success: true + code: "0" + data: + stats: + inspCount: 320 + onsiteCheckCount: 180 + qualKeepCount: 90 + specialCount: 50 + checkedOrgCount: 60 + foundProblemCount: 75 + pie: + - code: OVERDUE + name: 超期未完成 + value: 28 + - code: PUNISH + name: 监管处罚 + value: 12 + + /safetyEval/regulator/cockpit/project-monitor: + get: + tags: + - 监管端-驾驶舱大屏 + summary: 项目实时监控 + description: | + 项目实时监控与踏勘/打卡统计。 + + - 数据源:`eval_project`、`eval_project_process_waring`、复用既有 `selectRegulatorSurveyStat()` + - `warningCount`=存在过程预警的项目数(**真实计算,替代原硬编码 0**),调用 `EvalProjectProcessWaringGateway.checkProjectHasWaring(projectId)` + - `survey` 四项(告知书/打卡)复用既有聚合 SQL;监控/预警/计划完成数为本次新实现 + operationId: projectMonitor + parameters: + - name: month + in: query + required: false + description: 统计月份(yyyy-MM,默认当前月) + schema: + type: string + example: "2026-07" + responses: + '200': + description: 成功 + content: + application/json: + schema: + $ref: '#/components/schemas/SingleResponseCockpitProjectMonitorCO' + example: + success: true + code: "0" + data: + monitor: + activeProjectCount: 520 + normalExecCount: 480 + warningCount: 36 + monthPlanFinishCount: 45 + survey: + noticeArchived: 500 + noticeAbnormal: 12 + clockInNormal: 980 + clockInAbnormal: 25 + +components: + parameters: + yearParam: + name: year + in: query + required: false + description: 统计年份(默认当前年) + schema: + type: integer + format: int32 + example: 2026 + + schemas: + # ---------- 通用响应 ---------- + SingleResponseError: + type: object + properties: + success: + type: boolean + example: false + code: + type: string + example: "404" + message: + type: string + errMessage: + type: string + + SingleResponseCockpitQualificationOverviewCO: + type: object + properties: + success: + type: boolean + example: true + code: + type: string + example: "0" + message: + type: string + errMessage: + type: string + data: + $ref: '#/components/schemas/CockpitQualificationOverviewCO' + + SingleResponseCockpitKpiCO: + type: object + properties: + success: + type: boolean + example: true + code: + type: string + example: "0" + message: + type: string + errMessage: + type: string + data: + $ref: '#/components/schemas/CockpitKpiCO' + + SingleResponseCockpitRegionDistributionCO: + type: object + properties: + success: + type: boolean + example: true + code: + type: string + example: "0" + message: + type: string + errMessage: + type: string + data: + $ref: '#/components/schemas/CockpitRegionDistributionCO' + + SingleResponseCockpitProcessOverviewCO: + type: object + properties: + success: + type: boolean + example: true + code: + type: string + example: "0" + message: + type: string + errMessage: + type: string + data: + $ref: '#/components/schemas/CockpitProcessOverviewCO' + + SingleResponseCockpitEvalTypeTrendCO: + type: object + properties: + success: + type: boolean + example: true + code: + type: string + example: "0" + message: + type: string + errMessage: + type: string + data: + $ref: '#/components/schemas/CockpitEvalTypeTrendCO' + + SingleResponseCockpitReviewSummaryCO: + type: object + properties: + success: + type: boolean + example: true + code: + type: string + example: "0" + message: + type: string + errMessage: + type: string + data: + $ref: '#/components/schemas/CockpitReviewSummaryCO' + + SingleResponseCockpitProjectMonitorCO: + type: object + properties: + success: + type: boolean + example: true + code: + type: string + example: "0" + message: + type: string + errMessage: + type: string + data: + $ref: '#/components/schemas/CockpitProjectMonitorCO' + + # ---------- 业务模型 ---------- + CockpitQualificationOverviewCO: + type: object + description: 资质全生命周期管理 + properties: + lifecycleStats: + $ref: '#/components/schemas/LifecycleStat' + progress: + $ref: '#/components/schemas/Progress' + industryList: + type: array + description: 行业分布(机构数) + items: + $ref: '#/components/schemas/IndustryItem' + + LifecycleStat: + type: object + description: 生命周期核心指标 + properties: + newFilingOrgCount: + type: integer + format: int32 + description: 本年新增备案机构数 + currentFilingOrgCount: + type: integer + format: int32 + description: 当前备案机构数 + logoutOrgCount: + type: integer + format: int32 + description: 本年注销机构数(现有字典无注销状态,暂置 0,待补源) + totalFilingOrgCount: + type: integer + format: int32 + description: 全部备案机构数 + exitedEvaluatorCount: + type: integer + format: int32 + description: 退出备案评价师数 + currentEvaluatorCount: + type: integer + format: int32 + description: 当前备案评价师数 + + Progress: + type: object + description: 完成率/开展率/新增率(百分比) + properties: + filingCompleteRate: + type: number + format: double + description: 备案完成率 + bizActiveRate: + type: number + format: double + description: 开展业务率 + newGrowthRate: + type: number + format: double + description: 新增率 + + IndustryItem: + type: object + description: 行业分布项 + properties: + industryCode: + type: string + description: 行业编码 + industryName: + type: string + description: 行业名称 + orgCount: + type: integer + format: int32 + description: 机构数 + typeCount: + type: integer + format: int32 + description: 类型数(口径待定,暂与机构数一致) + + CockpitKpiCO: + type: object + description: 核心KPI指标 + properties: + items: + type: array + description: KPI指标列表 + items: + $ref: '#/components/schemas/KpiItem' + + KpiItem: + type: object + description: KPI指标项 + properties: + code: + type: string + description: 指标编码 + example: EVAL_RATE + name: + type: string + description: 指标名称 + example: 备案项目开展评价率 + value: + type: number + format: double + description: 当前值(百分比) + yoy: + type: number + format: double + description: 同比(百分点) + + CockpitRegionDistributionCO: + type: object + description: 区域地图分布 + properties: + list: + type: array + description: 各区县分布 + items: + $ref: '#/components/schemas/DistrictItem' + summary: + $ref: '#/components/schemas/RegionSummary' + + DistrictItem: + type: object + description: 区县分布项 + properties: + districtCode: + type: string + description: 区县编码 + districtName: + type: string + description: 区县名称 + evalProjectCount: + type: integer + format: int32 + description: 评价项目数 + filingOrgCount: + type: integer + format: int32 + description: 备案机构数 + + RegionSummary: + type: object + description: 汇总 + properties: + totalEvalProject: + type: integer + format: int32 + description: 评价项目总数 + totalFilingOrg: + type: integer + format: int32 + description: 备案机构总数 + + CockpitProcessOverviewCO: + type: object + description: 执业全过程管控 + 项目流程(节点聚合) + properties: + nodes: + type: array + description: 节点聚合(totalCount=到达该节点项目数;pendingCount=未开始/待办项目数) + items: + $ref: '#/components/schemas/NodeItem' + + NodeItem: + type: object + description: 节点项 + properties: + nodeCode: + type: string + description: 节点编码 + nodeName: + type: string + description: 节点名称 + totalCount: + type: integer + format: int32 + description: 到达该节点项目数 + pendingCount: + type: integer + format: int32 + description: 待办(未开始)项目数 + + CockpitEvalTypeTrendCO: + type: object + description: 评价类型趋势 + properties: + buckets: + type: array + description: 时间桶(月=01..12;季=Q1..Q4;月粒度=01..月末) + items: + type: string + series: + type: array + description: 各评价类型序列 + items: + $ref: '#/components/schemas/EvalTypeSeries' + + EvalTypeSeries: + type: object + description: 评价类型趋势序列 + properties: + evalTypeCode: + type: string + description: 评价类型编码 + evalTypeName: + type: string + description: 评价类型名称 + projectCounts: + type: array + description: 项目数(与 buckets 对齐) + items: + type: integer + format: int32 + inspCounts: + type: array + description: 监督检查数(与 buckets 对齐) + items: + type: integer + format: int32 + + CockpitReviewSummaryCO: + type: object + description: 复盘评估改进提效 + properties: + stats: + $ref: '#/components/schemas/ReviewStat' + pie: + type: array + description: 饼图分布 + items: + $ref: '#/components/schemas/ReviewPieItem' + + ReviewStat: + type: object + description: 检查类统计 + properties: + inspCount: + type: integer + format: int32 + description: 监督检查总数 + onsiteCheckCount: + type: integer + format: int32 + description: 项目过程检查数 + qualKeepCount: + type: integer + format: int32 + description: 资质保持检查数 + specialCount: + type: integer + format: int32 + description: 专项检查数 + checkedOrgCount: + type: integer + format: int32 + description: 检查机构数(去重) + foundProblemCount: + type: integer + format: int32 + description: 发现问题数(resultCode=2/3) + + ReviewPieItem: + type: object + description: 饼图分布项 + properties: + code: + type: string + description: 编码 + enum: + - OVERDUE + - PUNISH + name: + type: string + description: 名称 + enum: + - 超期未完成 + - 监管处罚 + value: + type: integer + format: int32 + description: 数值 + + CockpitProjectMonitorCO: + type: object + description: 项目实时监控 + properties: + monitor: + $ref: '#/components/schemas/ProjectMonitor' + survey: + $ref: '#/components/schemas/SurveyStat' + + ProjectMonitor: + type: object + description: 项目监控核心指标 + properties: + activeProjectCount: + type: integer + format: int32 + description: 在监项目数 + normalExecCount: + type: integer + format: int32 + description: 正常执行数 + warningCount: + type: integer + format: int32 + description: 预警数(基于过程预警真实计算) + monthPlanFinishCount: + type: integer + format: int32 + description: 本月计划完成数 + + SurveyStat: + type: object + description: 踏勘/打卡统计 + properties: + noticeArchived: + type: integer + format: int32 + description: 告知书已归档数 + noticeAbnormal: + type: integer + format: int32 + description: 告知异常数 + clockInNormal: + type: integer + format: int32 + description: 人员打卡正常数 + clockInAbnormal: + type: integer + format: int32 + description: 打卡异常数 diff --git a/src/api/institution/index.js b/src/api/institution/index.js new file mode 100644 index 0000000..2d96bd4 --- /dev/null +++ b/src/api/institution/index.js @@ -0,0 +1,50 @@ +const getApiHost = () => { + if (typeof window !== 'undefined' && window.process?.env?.app?.API_HOST) { + return window.process.env.app.API_HOST; + } + return ''; +}; + +async function request(path, params = {}) { + const apiHost = getApiHost(); + const url = new URL(path, apiHost); + Object.entries(params).forEach(([k, v]) => { + if (v !== undefined && v !== null && v !== '') { + url.searchParams.append(k, v); + } + }); + const response = await fetch(url.toString()); + if (!response.ok) { + throw new Error(`HTTP ${response.status}`); + } + const json = await response.json(); + if (json.code !== '0') { + throw new Error(json.errMessage || json.message || 'API error'); + } + return json.data; +} + +// 当前项目节点统计 +export function fetchProjectNodeStats() { + return request('/safetyEval/institution/dashboard/project-node-stats'); +} + +// 通知提醒 +export function fetchNotices() { + return request('/safetyEval/institution/dashboard/notices'); +} + +// 服务行业项目统计 +export function fetchIndustryStat() { + return request('/safetyEval/institution/dashboard/industry-stat'); +} + +// 评价类别占比 +export function fetchEvalTypeRatio() { + return request('/safetyEval/institution/dashboard/eval-type-ratio'); +} + +// 项目执行情况(limit 默认 10) +export function fetchProjectExecution(limit = 10) { + return request('/safetyEval/institution/dashboard/project-execution', { limit }); +} diff --git a/src/api/supervision/index.js b/src/api/supervision/index.js new file mode 100644 index 0000000..06666ec --- /dev/null +++ b/src/api/supervision/index.js @@ -0,0 +1,60 @@ +const getApiHost = () => { + if (typeof window !== 'undefined' && window.process?.env?.app?.API_HOST) { + return window.process.env.app.API_HOST; + } + return ''; +}; + +async function request(path, params = {}) { + const apiHost = getApiHost(); + const url = new URL(path, apiHost); + Object.entries(params).forEach(([k, v]) => { + if (v !== undefined && v !== null && v !== '') { + url.searchParams.append(k, v); + } + }); + const response = await fetch(url.toString()); + if (!response.ok) { + throw new Error(`HTTP ${response.status}`); + } + const json = await response.json(); + if (json.code !== '0') { + throw new Error(json.errMessage || json.message || 'API error'); + } + return json.data; +} + +// 资质全生命周期管理 +export function fetchQualificationOverview(year) { + return request('/safetyEval/regulator/cockpit/qualification-overview', { year }); +} + +// 核心KPI指标 +export function fetchKpi(cycle, year) { + return request('/safetyEval/regulator/cockpit/kpi', { cycle, year }); +} + +// 区域地图分布 +export function fetchRegionDistribution() { + return request('/safetyEval/regulator/cockpit/region-distribution'); +} + +// 执业全过程管控 + 项目流程 +export function fetchProcessOverview() { + return request('/safetyEval/regulator/cockpit/process-overview'); +} + +// 评价类型趋势 +export function fetchEvalTypeTrend(period, year) { + return request('/safetyEval/regulator/cockpit/eval-type-trend', { period, year }); +} + +// 复盘评估改进提效 +export function fetchReviewSummary(year) { + return request('/safetyEval/regulator/cockpit/review-summary', { year }); +} + +// 项目实时监控 +export function fetchProjectMonitor(month) { + return request('/safetyEval/regulator/cockpit/project-monitor', { month }); +} diff --git a/src/pages/Container/Driver/index.js b/src/pages/Container/Driver/index.js index 13cde9d..6802368 100644 --- a/src/pages/Container/Driver/index.js +++ b/src/pages/Container/Driver/index.js @@ -5,7 +5,7 @@ import { NS_DRIVER } from "~/enumerate/namespace"; import DriverBack from "~/pages/Container/Driver/components/DriverBack"; import Institution from "~/pages/Container/Institution/Dashboard"; import Cockpit from "~/pages/Container/Supervision/Cockpit"; -import { getDriverToken, resolveDriverAuthParams } from "~/utils/resolveDriverAuthParams"; +import { getDriverToken, getPageMode, resolveDriverAuthParams } from "~/utils/resolveDriverAuthParams"; import { getApiErrorMessage } from "~/utils/handleApiResponse"; import "./index.less"; @@ -27,7 +27,11 @@ function DriverErrorPanel({ message }) { ); } -function resolveTerminalType(driverConfig, actionResult) { +function resolveTerminalType(driverConfig, actionResult, pageMode = 1) { + // pageMode 直接指定时,跳过 store / actionResult 取值 + if (pageMode === 2) return 2; + if (pageMode === 3) return 1; + const fromStore = Number(driverConfig); if (fromStore === 1 || fromStore === 2) { return fromStore; @@ -43,6 +47,8 @@ function resolveTerminalType(driverConfig, actionResult) { const Driver = (props) => { const authParams = useMemo(() => resolveDriverAuthParams(), []); + const pageMode = useMemo(() => getPageMode(), []); + const bypassApi = pageMode === 2 || pageMode === 3; const requestStartedRef = useRef(false); const actionResultRef = useRef(null); const [pageError, setPageError] = useState(""); @@ -56,6 +62,12 @@ const Driver = (props) => { } requestStartedRef.current = true; + // pageMode 2/3:跳过 checkTerminalType 请求,直接展示指定页面 + if (bypassApi) { + setRequestDone(true); + return; + } + (async () => { try { const token = getDriverToken(); @@ -73,19 +85,25 @@ const Driver = (props) => { setRequestDone(true); } })(); - }, [queryDriverConfig]); + }, [queryDriverConfig, bypassApi]); useEffect(() => { if (!requestDone || queryDriverConfigLoading) { return; } + // pageMode 2/3:跳过 token / 终端类型校验 + if (bypassApi) { + setPageError(""); + return; + } + if (!authParams.token) { setPageError("登录凭证获取失败,请重新从基座登录进入"); return; } - const terminalType = resolveTerminalType(driverConfig, actionResultRef.current); + const terminalType = resolveTerminalType(driverConfig, actionResultRef.current, pageMode); if (terminalType === 1 || terminalType === 2) { setPageError(""); return; @@ -94,16 +112,16 @@ const Driver = (props) => { setPageError( getApiErrorMessage(actionResultRef.current, "终端类型获取失败,请重新从基座登录进入"), ); - }, [requestDone, queryDriverConfigLoading, driverConfig, authParams.token]); + }, [requestDone, queryDriverConfigLoading, driverConfig, authParams.token, bypassApi, pageMode]); - const terminalType = resolveTerminalType(driverConfig, actionResultRef.current); + const terminalType = resolveTerminalType(driverConfig, actionResultRef.current, pageMode); const isInstitution = terminalType === 1; const isSupervision = terminalType === 2; const isValidTerminal = isInstitution || isSupervision; const hasToken = Boolean(authParams.token); const isLoading = !requestDone || queryDriverConfigLoading; const showError = requestDone && !queryDriverConfigLoading && Boolean(pageError); - const showContent = requestDone && !queryDriverConfigLoading && hasToken && isValidTerminal && !pageError; + const showContent = requestDone && !queryDriverConfigLoading && (hasToken || bypassApi) && isValidTerminal && !pageError; if (showError) { return ( diff --git a/src/pages/Container/Institution/components/EnterpriseTypeChart.jsx b/src/pages/Container/Institution/components/EnterpriseTypeChart.jsx index 41c931e..1d43a17 100644 --- a/src/pages/Container/Institution/components/EnterpriseTypeChart.jsx +++ b/src/pages/Container/Institution/components/EnterpriseTypeChart.jsx @@ -1,11 +1,10 @@ import React, { useEffect, useMemo, useRef } from 'react'; import * as echarts from 'echarts'; -import { Card } from 'antd'; -const BLUE = '#5b8ff9'; -const ORANGE = '#ffb33e'; +const BLUE = '#4285f4'; +const ORANGE = '#ffab31'; -function EChart({ option, className }) { +export default function EnterpriseTypeChart({ data }) { const chartRef = useRef(null); const instanceRef = useRef(null); @@ -16,7 +15,6 @@ function EChart({ option, className }) { const observer = typeof ResizeObserver !== 'undefined' ? new ResizeObserver(resize) : null; if (observer) observer.observe(chartRef.current); window.addEventListener('resize', resize); - return () => { window.removeEventListener('resize', resize); if (observer) observer.disconnect(); @@ -25,67 +23,47 @@ function EChart({ option, className }) { }; }, []); - useEffect(() => { - if (instanceRef.current) instanceRef.current.setOption(option, true); - }, [option]); + const list = data && data.list ? data.list : []; + const categories = list.map(item => item.industryName); + const projectData = list.map(item => Number(item.projectCount) || 0); + const statutoryData = list.map(item => Number(item.statutoryProjectCount) || 0); + const maxVal = Math.max(...projectData, ...statutoryData, 1); + const yMax = Math.ceil(maxVal / 10) * 10 || 10; - return
; -} - -export default function EnterpriseTypeChart({ data }) { const option = useMemo(() => ({ color: [BLUE, ORANGE], - grid: { left: 38, right: 10, top: 26, bottom: 46 }, + grid: { left: 36, right: 10, top: 12, bottom: 28 }, tooltip: { trigger: 'axis', - axisPointer: { type: 'shadow', shadowStyle: { color: 'rgba(91, 143, 249, .08)' } }, backgroundColor: '#fff', - borderColor: '#e9edf4', + borderColor: '#e2e8f0', borderWidth: 1, - textStyle: { color: '#333', fontSize: 12 }, + textStyle: { color: '#1e293b', fontSize: 12 }, }, xAxis: { type: 'category', - data: data.categories, + data: categories, axisTick: { show: false }, - axisLine: { lineStyle: { color: '#edf0f5' } }, - axisLabel: { - color: '#666', - fontSize: 12, - interval: 0, - width: 58, - overflow: 'break', - lineHeight: 14, - }, + axisLine: { lineStyle: { color: '#e2e8f0' } }, + axisLabel: { color: '#64748b', fontSize: 11, interval: 0 }, }, yAxis: { type: 'value', min: 0, - max: 60, - splitNumber: 6, - axisLabel: { color: '#8792a2', fontSize: 12 }, - splitLine: { lineStyle: { color: '#e9edf4', type: 'dashed' } }, + max: yMax, + interval: yMax > 10 ? yMax / 3 : 5, + axisLabel: { color: '#8492a6', fontSize: 10, margin: 4 }, + splitLine: { lineStyle: { color: '#dce5ef', type: 'dashed' } }, }, series: [ - { name: '项目数', type: 'bar', barWidth: 12, data: data.projectCount, itemStyle: { borderRadius: [2, 2, 0, 0] } }, - { name: '金额', type: 'bar', barWidth: 12, data: data.amount, itemStyle: { borderRadius: [2, 2, 0, 0] } }, + { name: '项目数', type: 'bar', barWidth: 14, barGap: '30%', data: projectData, itemStyle: { borderRadius: [2, 2, 0, 0] } }, + { name: '法定项目', type: 'bar', barWidth: 14, data: statutoryData, itemStyle: { borderRadius: [2, 2, 0, 0] } }, ], - }), [data]); + }), [categories, projectData, statutoryData, yMax]); - return ( -| 序号 | +项目编号 | +项目名称 | +被评价企业 | +评价类别 | +项目负责人 | +项目结束日期 | +操作 | +
|---|---|---|---|---|---|---|---|
| {index + 1} | +{record.projectNo} | +{record.projectName} | +{record.customerName} | +{record.evalTypeName} | +{record.projectLeaderName} | +{record.planEndDate} | ++ + | +
{item.label}
{item.value}{item.title}
{item.value}同比 ↗ {item.change}{item.name}
+ {item.value}% + 同比 ↗ {item.yoy}% +{item.label}
{item.value}{item.nodeName}
{item.pendingCount}{item.label}:{item.value}
{item.label}:{item.value}