Browse Source

执法端-计划

dev-V1.0.1
庄锐波 5 months ago
parent
commit
f03757116a
  1. 4
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/enforcement/AppEnfLoginController.java
  2. 8
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/web/supervision/report/RegistrationController.java
  3. 14
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/mapper/EnforcementRegistrationMapper.java
  4. 5
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/report/IEnforcementRegistrationReport.java
  5. 42
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/report/impl/EnforcementRegistrationReportImpl.java
  6. 36
      zdxt-modules/zdxt-enforcement-code/src/main/resources/mapper/enforcementcode/EnforcementRegistrationReportMapper.xml
  7. 147
      zdxt-web-client/zdxt-admin-plus-ui/src/api/efcode/report/registration.ts
  8. 4
      zdxt-web-client/zdxt-admin-plus-ui/src/views/efcode/report/registration/index.vue
  9. 376
      zdxt-web-client/zdxt-admin-plus-ui/src/views/efcode/report/registration/indexChart.vue
  10. 391
      zdxt-web-client/zdxt-admin-plus-ui/src/views/efcode/report/registration/indexList.vue

4
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/enforcement/AppEnfLoginController.java

@ -1,8 +1,6 @@
package com.zdxt.enforcementcode.controller.app.enforcement;
import cn.dev33.satoken.annotation.SaIgnore;
import com.alibaba.fastjson.JSONObject;
import com.zdxt.enforcementcode.controller.app.utils.HttpUtil;
import com.zdxt.enforcementcode.common.util.CommonUtil;
import com.zdxt.enforcementcode.service.enforcement.IAppEnfLoginService;
import com.zdxt.enforcementcode.controller.app.LoginController;
@ -14,11 +12,9 @@ import org.dromara.system.domain.vo.SysDeptVo;
import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.service.ISysDeptService;
import org.dromara.system.service.ISysUserService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**

8
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/web/supervision/report/RegistrationController.java

@ -76,6 +76,14 @@ public class RegistrationController {
return R.ok(registrationReport.deptRankChart(bo));
}
/**
* 接口5.5:被检查企业次数排行(柱状图,TOP10)
*/
@GetMapping("/enterpriseRankChart")
public R<List<ChartDataVo>> enterpriseRankChart(EnforcementRegistrationBo bo) {
return R.ok(registrationReport.enterpriseRankChart(bo));
}
/**
* 接口6:执法分析列表(按企业维度统计执法次数,分页)
*/

14
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/mapper/EnforcementRegistrationMapper.java

@ -10,6 +10,7 @@ import com.zdxt.enforcementcode.domain.vo.EnforcementRegistrationVo;
import com.zdxt.enforcementcode.domain.vo.enterprise.EnforcementRegistrationListVo;
import com.zdxt.enforcementcode.domain.vo.report.ChartDataVo;
import com.zdxt.enforcementcode.domain.vo.report.RegistrationAnalysisVo;
import com.zdxt.enforcementcode.domain.vo.report.RegistrationStatisticsVo;
import org.apache.ibatis.annotations.Param;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
@ -81,6 +82,13 @@ public interface EnforcementRegistrationMapper extends BaseMapperPlus<Enforcemen
// ======================== 报表查询方法 ========================
/**
* 报表-统计指标查询(COUNT聚合)
*/
RegistrationStatisticsVo selectReportStatistics(@Param("bo") EnforcementRegistrationBo bo,
@Param("dataFilter") String dataFilter,
@Param("checkCategoryCode") String checkCategoryCode);
/**
* 报表-分页列表查询
*/
@ -106,6 +114,12 @@ public interface EnforcementRegistrationMapper extends BaseMapperPlus<Enforcemen
List<ChartDataVo> selectReportDeptRankChart(@Param("bo") EnforcementRegistrationBo bo,
@Param("dataFilter") String dataFilter);
/**
* 报表-被检查企业次数排行图表(GROUP BY enterprise_number,前10)
*/
List<ChartDataVo> selectReportEnterpriseRankChart(@Param("bo") EnforcementRegistrationBo bo,
@Param("dataFilter") String dataFilter);
/**
* 报表-执法分析排名(GROUP BY enterprise_number)
*/

5
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/report/IEnforcementRegistrationReport.java

@ -42,6 +42,11 @@ public interface IEnforcementRegistrationReport {
*/
List<ChartDataVo> deptRankChart(EnforcementRegistrationBo bo);
/**
* 被检查企业次数排行(柱状图TOP10)
*/
List<ChartDataVo> enterpriseRankChart(EnforcementRegistrationBo bo);
/**
* 执法分析列表(按企业维度统计,分页)
*/

42
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/report/impl/EnforcementRegistrationReportImpl.java

@ -3,7 +3,6 @@ package com.zdxt.enforcementcode.report.impl;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zdxt.enforcementcode.common.util.DataFilterUtil;
import com.zdxt.enforcementcode.domain.EnforcementRegistration;
import com.zdxt.enforcementcode.domain.bo.EnforcementRegistrationBo;
import com.zdxt.enforcementcode.domain.vo.EnforcementRegistrationVo;
import com.zdxt.enforcementcode.domain.vo.report.ChartDataVo;
@ -42,33 +41,26 @@ public class EnforcementRegistrationReportImpl implements IEnforcementRegistrati
@Override
public RegistrationStatisticsVo statistics(EnforcementRegistrationBo bo) {
String dataFilter = DataFilterUtil.buildFilterSql(true, true);
List<EnforcementRegistration> list = registrationMapper.selectReportAll(bo, dataFilter);
RegistrationStatisticsVo vo = new RegistrationStatisticsVo();
long total = list.size();
vo.setTotal(total);
// 联合执法: has_union=1 且 union_no 不为空
long unionCount = list.stream()
.filter(r -> r.getHasUnion() != null && r.getHasUnion() == 1
&& StringUtils.isNotBlank(r.getUnionNo()))
.count();
vo.setUnionCount(unionCount);
vo.setUnionPercent(total > 0 ? PERCENT_FORMAT.format((double) unionCount / total) : "0.00%");
// 检查类别统计(默认取"01"对应的类别)
String checkCategoryCode = StringUtils.isNotBlank(bo.getCheckCategory()) ? bo.getCheckCategory() : "01";
// 单条SQL聚合查询,不加载全量数据
RegistrationStatisticsVo vo = registrationMapper.selectReportStatistics(bo, dataFilter, checkCategoryCode);
if (vo == null) {
vo = new RegistrationStatisticsVo();
}
long total = vo.getTotal();
// 计算占比
vo.setUnionPercent(total > 0 ? PERCENT_FORMAT.format((double) vo.getUnionCount() / total) : "0.00%");
vo.setCheckPercent(total > 0 ? PERCENT_FORMAT.format((double) vo.getCheckCount() / total) : "0.00%");
// 字典翻译检查类别名称
String checkCategoryName = dictTypeService.selectDictDataByType("check_category").stream()
.filter(d -> checkCategoryCode.equals(d.getDictValue()))
.map(SysDictDataVo::getDictLabel)
.findFirst().orElse("");
long checkCount = list.stream()
.filter(r -> checkCategoryCode.equals(r.getCheckCategory()))
.count();
vo.setCheckCategoryName(checkCategoryName);
vo.setCheckCount(checkCount);
vo.setCheckPercent(total > 0 ? PERCENT_FORMAT.format((double) checkCount / total) : "0.00%");
return vo;
}
@ -114,6 +106,12 @@ public class EnforcementRegistrationReportImpl implements IEnforcementRegistrati
return registrationMapper.selectReportDeptRankChart(bo, dataFilter);
}
@Override
public List<ChartDataVo> enterpriseRankChart(EnforcementRegistrationBo bo) {
String dataFilter = DataFilterUtil.buildFilterSql(true, true);
return registrationMapper.selectReportEnterpriseRankChart(bo, dataFilter);
}
@Override
public TableDataInfo<RegistrationAnalysisVo> analysisRank(EnforcementRegistrationBo bo, PageQuery pageQuery) {
String dataFilter = DataFilterUtil.buildFilterSql(true, true);

36
zdxt-modules/zdxt-enforcement-code/src/main/resources/mapper/enforcementcode/EnforcementRegistrationReportMapper.xml

@ -8,6 +8,15 @@
<sql id="reportWhereCondition">
AND is_submit = 1
AND is_delete = 0
AND bureau_dept_name is not null
AND law_enforcement_id is not null
AND enterprise_name is not null
AND enterprise_number is not null
AND law_enforcer is not null
AND law_enforcer_two is not null
AND is_complaint is not null
AND check_category is not null
AND bureau_dept_id is not null
AND (status = 0 OR status IS NULL)
<if test="bo.bureauDeptName != null and bo.bureauDeptName != ''">
AND bureau_dept_name LIKE CONCAT('%', #{bo.bureauDeptName}, '%')
@ -85,6 +94,18 @@
</if>
</sql>
<!-- 报表统计指标查询(COUNT聚合,不全量加载) -->
<select id="selectReportStatistics" resultType="com.zdxt.enforcementcode.domain.vo.report.RegistrationStatisticsVo">
SELECT
COUNT(*) AS total,
SUM(CASE WHEN has_union = 1 AND union_no IS NOT NULL AND union_no != '' THEN 1 ELSE 0 END) AS unionCount,
SUM(CASE WHEN check_category = #{checkCategoryCode} THEN 1 ELSE 0 END) AS checkCount
FROM enforcement_registration
<where>
<include refid="reportWhereCondition"/>
</where>
</select>
<!-- 报表分页列表查询 -->
<select id="selectReportPage" resultType="com.zdxt.enforcementcode.domain.vo.EnforcementRegistrationVo">
SELECT
@ -165,6 +186,21 @@
LIMIT 10
</select>
<!-- 被检查企业次数排行(按企业分组计数,取前10) -->
<select id="selectReportEnterpriseRankChart" resultType="com.zdxt.enforcementcode.domain.vo.report.ChartDataVo">
SELECT
enterprise_name AS name,
COUNT(*) AS value
FROM enforcement_registration
<where>
<include refid="reportWhereCondition"/>
AND enterprise_name IS NOT NULL AND enterprise_name != ''
</where>
GROUP BY enterprise_number, enterprise_name
ORDER BY value DESC
LIMIT 10
</select>
<!-- 执法分析排名(按企业税号聚合) -->
<select id="selectReportAnalysisList" resultType="com.zdxt.enforcementcode.domain.vo.report.RegistrationAnalysisVo">
SELECT

147
zdxt-web-client/zdxt-admin-plus-ui/src/api/efcode/report/registration.ts

@ -0,0 +1,147 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
const BASE_URL = '/web/supervision/report/registration';
/** 查询参数 */
export interface RegistrationReportQuery {
pageNum?: number;
pageSize?: number;
bureauDeptName?: string;
lawEnforcer?: string;
enterpriseName?: string;
lawEnforcementItemText?: string;
hasUnion?: number;
hasAuto?: number;
entrustDeptName?: string;
lawEnforceCategory?: string;
isComplaint?: number;
enforce?: string;
checkCss?: string;
district?: string;
street?: string;
checkCategory?: string;
dateType?: number;
dateSearch?: string;
beginTime?: string;
endTime?: string;
}
/** 统计指标VO */
export interface RegistrationStatisticsVo {
total: number;
unionCount: number;
unionPercent: string;
checkCategoryName: string;
checkCount: number;
checkPercent: string;
}
/** 图表数据VO */
export interface ChartDataVo {
name: string;
value: number;
}
/** 执法分析VO */
export interface RegistrationAnalysisVo {
district: string;
street: string;
enterpriseName: string;
bureauDeptName: string;
lawEnforcer: string;
lawEnforcementItemText: string;
enforcementTime: string;
totalCount: number;
colorLevel: string;
}
/**
* 获取统计指标
*/
export const getStatistics = (params: RegistrationReportQuery): AxiosPromise<RegistrationStatisticsVo> => {
return request({
url: `${BASE_URL}/statistics`,
method: 'get',
params
});
};
/**
* 统计列表(分页)
*/
export const getRegistrationList = (params: RegistrationReportQuery): AxiosPromise => {
return request({
url: `${BASE_URL}/list`,
method: 'get',
params
});
};
/**
* 导出统计列表
*/
export const exportRegistrationList = (params: RegistrationReportQuery): AxiosPromise<Blob> => {
return request({
url: `${BASE_URL}/exportList`,
method: 'post',
params,
responseType: 'blob'
});
};
/**
* 执法类别分布(饼图数据)
*/
export const getCategoryChart = (params: RegistrationReportQuery): AxiosPromise<ChartDataVo[]> => {
return request({
url: `${BASE_URL}/categoryChart`,
method: 'get',
params
});
};
/**
* 部门执法排行(柱状图TOP10)
*/
export const getDeptRankChart = (params: RegistrationReportQuery): AxiosPromise<ChartDataVo[]> => {
return request({
url: `${BASE_URL}/deptRankChart`,
method: 'get',
params
});
};
/**
* 被检查企业次数排行(柱状图TOP10)
*/
export const getEnterpriseRankChart = (params: RegistrationReportQuery): AxiosPromise<ChartDataVo[]> => {
return request({
url: `${BASE_URL}/enterpriseRankChart`,
method: 'get',
params
});
};
/**
* 执法分析列表(按企业维度统计)
*/
export const getAnalysisRank = (params: RegistrationReportQuery): AxiosPromise => {
return request({
url: `${BASE_URL}/analysisRank`,
method: 'get',
params
});
};
/**
* 导出执法分析
*/
export const exportAnalysis = (params: RegistrationReportQuery): AxiosPromise<Blob> => {
return request({
url: `${BASE_URL}/exportAnalysis`,
method: 'post',
params,
responseType: 'blob'
});
};

4
zdxt-web-client/zdxt-admin-plus-ui/src/views/efcode/report/registration/index.vue

@ -9,10 +9,10 @@ const activeTab = ref('list');
<template>
<div class="app-container">
<el-tabs v-model="activeTab" type="border-card">
<el-tab-pane label="涉企执法统计" name="list">
<el-tab-pane label="涉企执法统计" name="list" lazy>
<IndexList />
</el-tab-pane>
<el-tab-pane label="涉企执法排行" name="chart">
<el-tab-pane label="涉企执法排行" name="chart" lazy>
<IndexChart />
</el-tab-pane>
</el-tabs>

376
zdxt-web-client/zdxt-admin-plus-ui/src/views/efcode/report/registration/indexChart.vue

@ -1,95 +1,190 @@
<script setup lang="ts">
import { ref, reactive, onMounted, watch, nextTick } from 'vue';
import { ElMessage } from 'element-plus';
import {
getCategoryChart, getDeptRankChart, getAnalysisRank, exportAnalysis
} from '@/api/efcode/report/registration';
import type { RegistrationReportQuery, ChartDataVo, RegistrationAnalysisVo } from '@/api/efcode/report/registration';
import { ref, reactive, onMounted, nextTick } from 'vue';
import { getCategoryChart, getDeptRankChart } from '@/api/efcode/report/registration';
import type { RegistrationReportQuery, ChartDataVo } from '@/api/efcode/report/registration';
import * as echarts from 'echarts';
// 查询参数
const queryParams = reactive<RegistrationReportQuery>({
pageNum: 1,
pageSize: 20,
dateType: 3,
beginTime: undefined,
endTime: undefined
});
const loading = ref(false);
// 饼图时段筛选
const timeMode = ref('year');
const loading = ref(false);
const selectedYear = ref<string>(String(new Date().getFullYear()));
const selectedQuarter = ref<number>(Math.ceil((new Date().getMonth() + 1) / 3));
const selectedMonth = ref<string>('');
const yearOptions = (() => {
const curr = new Date().getFullYear();
return Array.from({ length: 5 }, (_, i) => String(curr - i));
})();
const quarterOptions = [
{ label: '第一季度', value: 1 },
{ label: '第二季度', value: 2 },
{ label: '第三季度', value: 3 },
{ label: '第四季度', value: 4 }
];
// 饼图查询参数
const pieParams = reactive<RegistrationReportQuery>({
beginTime: undefined,
endTime: undefined,
dateSearch: undefined
});
// 图表数据
const categoryData = ref<ChartDataVo[]>([]);
const deptRankData = ref<ChartDataVo[]>([]);
// 分析列表
const analysisTableData = ref<RegistrationAnalysisVo[]>([]);
const analysisTotal = ref(0);
const yearRankData = ref<ChartDataVo[]>([]);
const quarterRankData = ref<ChartDataVo[]>([]);
const monthRankData = ref<ChartDataVo[]>([]);
// 图表DOM引用
const pieChartRef = ref<HTMLElement>();
const barChartRef = ref<HTMLElement>();
const yearBarRef = ref<HTMLElement>();
const quarterBarRef = ref<HTMLElement>();
const monthBarRef = ref<HTMLElement>();
let pieChart: echarts.ECharts | null = null;
let barChart: echarts.ECharts | null = null;
let yearBarChart: echarts.ECharts | null = null;
let quarterBarChart: echarts.ECharts | null = null;
let monthBarChart: echarts.ECharts | null = null;
/** 计算时间范围 */
const getYearRange = () => {
const now = new Date();
const year = now.getFullYear();
return { beginTime: `${year}-01-01`, endTime: `${year}-12-31` };
};
const getQuarterRange = () => {
const now = new Date();
const year = now.getFullYear();
const q = Math.ceil((now.getMonth() + 1) / 3);
const startMonth = (q - 1) * 3 + 1;
const endMonth = q * 3;
const endDay = new Date(year, endMonth, 0).getDate();
return {
beginTime: `${year}-${String(startMonth).padStart(2, '0')}-01`,
endTime: `${year}-${String(endMonth).padStart(2, '0')}-${endDay}`
};
};
const getMonthRange = () => {
const now = new Date();
const year = now.getFullYear();
const month = now.getMonth() + 1;
const endDay = new Date(year, month, 0).getDate();
return {
beginTime: `${year}-${String(month).padStart(2, '0')}-01`,
endTime: `${year}-${String(month).padStart(2, '0')}-${endDay}`
};
};
onMounted(() => {
loadCharts();
loadAnalysisList();
applyPieYearFilter();
loadAllCharts();
});
/** 饼图-年度筛选 */
const applyPieYearFilter = () => {
pieParams.dateSearch = undefined;
const year = selectedYear.value;
pieParams.beginTime = `${year}-01-01`;
pieParams.endTime = `${year}-12-31`;
};
/** 饼图-季度筛选 */
const applyPieQuarterFilter = () => {
pieParams.dateSearch = undefined;
const year = selectedYear.value;
const q = selectedQuarter.value;
const startMonth = (q - 1) * 3 + 1;
const endMonth = q * 3;
const endDay = new Date(Number(year), endMonth, 0).getDate();
pieParams.beginTime = `${year}-${String(startMonth).padStart(2, '0')}-01`;
pieParams.endTime = `${year}-${String(endMonth).padStart(2, '0')}-${endDay}`;
};
/** 饼图-月度筛选 */
const applyPieMonthFilter = () => {
pieParams.beginTime = undefined;
pieParams.endTime = undefined;
if (selectedMonth.value) {
pieParams.dateSearch = selectedMonth.value;
}
};
/** 时段模式变更 */
const handleTimeModeChange = (mode: string) => {
queryParams.dateType = undefined;
queryParams.beginTime = undefined;
queryParams.endTime = undefined;
pieParams.beginTime = undefined;
pieParams.endTime = undefined;
pieParams.dateSearch = undefined;
if (mode === 'year') {
queryParams.dateType = 3;
applyPieYearFilter();
} else if (mode === 'quarter') {
queryParams.dateType = 2;
applyPieQuarterFilter();
} else if (mode === 'month') {
queryParams.dateType = 1;
const now = new Date();
selectedMonth.value = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`;
applyPieMonthFilter();
}
if (mode !== 'custom') {
loadCharts();
loadAnalysisList();
};
/** 饼图搜索 */
const handlePieSearch = async () => {
loading.value = true;
try {
const res = await getCategoryChart(pieParams);
categoryData.value = res.data || [];
await nextTick();
renderPieChart();
} finally {
loading.value = false;
}
};
/** 自定义时段搜索 */
const handleSearch = () => {
queryParams.pageNum = 1;
loadCharts();
loadAnalysisList();
/** 饼图重置 */
const handlePieReset = () => {
timeMode.value = 'year';
selectedYear.value = String(new Date().getFullYear());
selectedQuarter.value = Math.ceil((new Date().getMonth() + 1) / 3);
selectedMonth.value = '';
applyPieYearFilter();
handlePieSearch();
};
/** 加载图表数据 */
const loadCharts = async () => {
/** 加载所有图表数据 */
const loadAllCharts = async () => {
loading.value = true;
try {
const [catRes, deptRes] = await Promise.all([
getCategoryChart(queryParams),
getDeptRankChart(queryParams)
const yearRange = getYearRange();
const quarterRange = getQuarterRange();
const monthRange = getMonthRange();
const yearParams: RegistrationReportQuery = { beginTime: yearRange.beginTime, endTime: yearRange.endTime };
const quarterParams: RegistrationReportQuery = { beginTime: quarterRange.beginTime, endTime: quarterRange.endTime };
const monthParams: RegistrationReportQuery = { beginTime: monthRange.beginTime, endTime: monthRange.endTime };
const [catRes, yearRes, quarterRes, monthRes] = await Promise.all([
getCategoryChart(pieParams),
getDeptRankChart(yearParams),
getDeptRankChart(quarterParams),
getDeptRankChart(monthParams)
]);
categoryData.value = catRes.data || [];
deptRankData.value = deptRes.data || [];
yearRankData.value = yearRes.data || [];
quarterRankData.value = quarterRes.data || [];
monthRankData.value = monthRes.data || [];
await nextTick();
renderPieChart();
renderBarChart();
renderYearBarChart();
renderQuarterBarChart();
renderMonthBarChart();
} finally {
loading.value = false;
}
};
/** 加载分析列表 */
const loadAnalysisList = async () => {
try {
const res = await getAnalysisRank(queryParams);
analysisTableData.value = res.rows || [];
analysisTotal.value = res.total || 0;
} catch (e) {}
};
/** 渲染饼图 */
const renderPieChart = () => {
if (!pieChartRef.value) return;
@ -97,7 +192,7 @@ const renderPieChart = () => {
pieChart = echarts.init(pieChartRef.value);
}
const option: echarts.EChartsOption = {
title: { text: '执法类别分布', left: 'center' },
title: { text: '执法类别数量及占比', left: 'center' },
tooltip: { trigger: 'item', formatter: '{b}: {c} ({d}%)' },
legend: { orient: 'vertical', left: 'left', top: 'middle' },
series: [{
@ -114,75 +209,67 @@ const renderPieChart = () => {
pieChart.setOption(option, true);
};
/** 渲染柱状图 */
const renderBarChart = () => {
if (!barChartRef.value) return;
if (!barChart) {
barChart = echarts.init(barChartRef.value);
/** 渲染竖向柱状图通用方法 */
const renderBarChart = (
chartRef: HTMLElement | undefined,
chartInstance: echarts.ECharts | null,
data: ChartDataVo[],
title: string,
color: string
): echarts.ECharts | null => {
if (!chartRef) return chartInstance;
if (!chartInstance) {
chartInstance = echarts.init(chartRef);
}
const data = [...deptRankData.value].reverse(); // 倒序排列让最多的在上面
const option: echarts.EChartsOption = {
title: { text: '部门执法排行TOP10', left: 'center' },
title: { text: title, left: 'center', textStyle: { fontSize: 14 } },
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
grid: { left: '20%', right: '5%', top: '15%', bottom: '5%' },
xAxis: { type: 'value' },
yAxis: {
grid: { left: '3%', right: '5%', top: '15%', bottom: '25%', containLabel: true },
xAxis: {
type: 'category',
data: data.map(item => item.name),
axisLabel: { width: 120, overflow: 'truncate' }
axisLabel: { rotate: 30, interval: 0, fontSize: 10, width: 70, overflow: 'truncate' }
},
yAxis: { type: 'value' },
series: [{
type: 'bar',
data: data.map(item => item.value),
barWidth: '40%',
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: '#83bff6' },
{ offset: 1, color: '#188df0' }
])
color: color,
borderRadius: [4, 4, 0, 0]
},
label: { show: true, position: 'right', formatter: '{c}' }
label: { show: true, position: 'top', formatter: '{c}', fontSize: 10 }
}]
};
barChart.setOption(option, true);
chartInstance.setOption(option, true);
return chartInstance;
};
/** 颜色分级样式 */
const getColorStyle = (level: string) => {
switch (level) {
case 'green': return { color: '#67c23a' };
case 'yellow': return { color: '#e6a23c' };
case 'orange': return { color: '#f56c6c' };
case 'red': return { color: '#f56c6c', fontWeight: 'bold' };
default: return {};
}
const renderYearBarChart = () => {
yearBarChart = renderBarChart(yearBarRef.value, yearBarChart, yearRankData.value, '年度执法数量排行(TOP10)', '#7ca9fb');
};
/** 导出 */
const handleExport = async () => {
try {
const res = await exportAnalysis(queryParams);
const blob = new Blob([res as any], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = '执法分析.xlsx';
link.click();
window.URL.revokeObjectURL(url);
} catch (e) {
ElMessage.error('导出失败');
}
const renderQuarterBarChart = () => {
quarterBarChart = renderBarChart(quarterBarRef.value, quarterBarChart, quarterRankData.value, '季度执法数量排行(TOP10)', '#188df0');
};
const renderMonthBarChart = () => {
monthBarChart = renderBarChart(monthBarRef.value, monthBarChart, monthRankData.value, '月执法数量排行(TOP10)', '#67c23a');
};
// 响应窗口变化
window.addEventListener('resize', () => {
pieChart?.resize();
barChart?.resize();
yearBarChart?.resize();
quarterBarChart?.resize();
monthBarChart?.resize();
});
</script>
<template>
<div>
<!-- 筛选区 -->
<div v-loading="loading">
<!-- 饼图筛选区 -->
<el-form inline class="search-form">
<el-form-item label="时段">
<el-select v-model="timeMode" style="width: 100px" @change="handleTimeModeChange">
@ -192,58 +279,50 @@ window.addEventListener('resize', () => {
<el-option label="自定义" value="custom" />
</el-select>
</el-form-item>
<el-form-item v-if="timeMode === 'custom'" label="时间范围">
<el-date-picker v-model="queryParams.beginTime" type="date" placeholder="开始" value-format="YYYY-MM-DD" style="width: 130px" />
<el-form-item v-if="timeMode === 'year'">
<el-select v-model="selectedYear" style="width: 100px" @change="applyPieYearFilter">
<el-option v-for="y in yearOptions" :key="y" :label="y + '年'" :value="y" />
</el-select>
</el-form-item>
<el-form-item v-if="timeMode === 'quarter'">
<el-select v-model="selectedYear" style="width: 100px" @change="applyPieQuarterFilter">
<el-option v-for="y in yearOptions" :key="y" :label="y + '年'" :value="y" />
</el-select>
<el-select v-model="selectedQuarter" style="width: 110px; margin-left: 4px" @change="applyPieQuarterFilter">
<el-option v-for="q in quarterOptions" :key="q.value" :label="q.label" :value="q.value" />
</el-select>
</el-form-item>
<el-form-item v-if="timeMode === 'month'">
<el-date-picker v-model="selectedMonth" type="month" placeholder="选择月份" value-format="YYYY-MM" style="width: 150px" @change="applyPieMonthFilter" />
</el-form-item>
<el-form-item v-if="timeMode === 'custom'">
<el-date-picker v-model="pieParams.beginTime" type="date" placeholder="开始" value-format="YYYY-MM-DD" style="width: 130px" />
<span style="margin: 0 4px">-</span>
<el-date-picker v-model="queryParams.endTime" type="date" placeholder="结束" value-format="YYYY-MM-DD" style="width: 130px" />
<el-date-picker v-model="pieParams.endTime" type="date" placeholder="结束" value-format="YYYY-MM-DD" style="width: 130px" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">查询</el-button>
<el-button type="primary" @click="handlePieSearch">搜索</el-button>
<el-button @click="handlePieReset">重置</el-button>
</el-form-item>
</el-form>
<!-- 图表区 -->
<el-row :gutter="16" class="chart-row" v-loading="loading">
<!-- 图表区:2行2列 -->
<el-row :gutter="16" class="chart-row">
<el-col :span="12">
<div ref="pieChartRef" class="chart-container"></div>
</el-col>
<el-col :span="12">
<div ref="barChartRef" class="chart-container"></div>
<div ref="yearBarRef" class="chart-container"></div>
</el-col>
</el-row>
<el-row :gutter="16" class="chart-row">
<el-col :span="12">
<div ref="quarterBarRef" class="chart-container"></div>
</el-col>
<el-col :span="12">
<div ref="monthBarRef" class="chart-container"></div>
</el-col>
</el-row>
<!-- 分析列表 -->
<div class="analysis-section">
<div class="section-header">
<span class="section-title">执法分析(按企业维度统计)</span>
<el-button type="warning" size="small" @click="handleExport">导出</el-button>
</div>
<el-table :data="analysisTableData" border stripe style="width: 100%">
<el-table-column label="区域" prop="district" width="100" />
<el-table-column label="街道" prop="street" width="100" />
<el-table-column label="企业名称" prop="enterpriseName" min-width="180" show-overflow-tooltip />
<el-table-column label="执法单位" prop="bureauDeptName" min-width="150" show-overflow-tooltip />
<el-table-column label="执法人员" prop="lawEnforcer" width="120" show-overflow-tooltip />
<el-table-column label="执法事项" prop="lawEnforcementItemText" min-width="150" show-overflow-tooltip />
<el-table-column label="执法时间" prop="enforcementTime" width="110" align="center" />
<el-table-column label="执法次数" prop="totalCount" width="90" align="center">
<template #default="{ row }">
<span :style="getColorStyle(row.colorLevel)">{{ row.totalCount }}</span>
</template>
</el-table-column>
</el-table>
<el-pagination
v-model:current-page="queryParams.pageNum"
v-model:page-size="queryParams.pageSize"
:page-sizes="[10, 20, 50, 100]"
:total="analysisTotal"
layout="total, sizes, prev, pager, next, jumper"
@size-change="loadAnalysisList"
@current-change="loadAnalysisList"
class="mt-4"
/>
</div>
</div>
</template>
@ -252,27 +331,10 @@ window.addEventListener('resize', () => {
margin-bottom: 16px;
}
.chart-row {
margin-bottom: 24px;
margin-bottom: 16px;
}
.chart-container {
width: 100%;
height: 350px;
}
.analysis-section {
margin-top: 16px;
.section-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 12px;
.section-title {
font-size: 16px;
font-weight: bold;
color: #303133;
}
}
}
.mt-4 {
margin-top: 16px;
height: 380px;
}
</style>

391
zdxt-web-client/zdxt-admin-plus-ui/src/views/efcode/report/registration/indexList.vue

@ -1,5 +1,5 @@
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue';
import { ref, reactive, onMounted, onUnmounted, computed, nextTick } from 'vue';
import { ElMessage } from 'element-plus';
import {
getStatistics, getRegistrationList, exportRegistrationList
@ -25,7 +25,7 @@ const queryParams = reactive<RegistrationReportQuery>({
district: undefined,
street: undefined,
checkCategory: undefined,
dateType: 3,
dateType: undefined,
dateSearch: undefined,
beginTime: undefined,
endTime: undefined
@ -34,6 +34,8 @@ const queryParams = reactive<RegistrationReportQuery>({
const loading = ref(false);
const tableData = ref<any[]>([]);
const total = ref(0);
const tableHeight = ref<number | undefined>(undefined);
const tableRef = ref<HTMLElement>();
// 统计指标
const stats = ref<RegistrationStatisticsVo>({
@ -54,12 +56,44 @@ const streetOptions = ref<any[]>([]);
// 时段模式
const timeMode = ref('year'); // year | quarter | month | custom
const selectedYear = ref<string>(String(new Date().getFullYear()));
const selectedQuarter = ref<number>(Math.ceil((new Date().getMonth() + 1) / 3));
const selectedMonth = ref<string>('');
// 季度选项
const quarterOptions = [
{ label: '第一季度', value: 1 },
{ label: '第二季度', value: 2 },
{ label: '第三季度', value: 3 },
{ label: '第四季度', value: 4 }
];
// 年份选项(近几年)
const yearOptions = (() => {
const curr = new Date().getFullYear();
return Array.from({ length: 5 }, (_, i) => String(curr - i));
})();
onMounted(() => {
loadDicts();
applyYearFilter();
handleQuery();
nextTick(() => calcTableHeight());
window.addEventListener('resize', calcTableHeight);
});
onUnmounted(() => {
window.removeEventListener('resize', calcTableHeight);
});
/** 计算表格高度 */
const calcTableHeight = () => {
if (!tableRef.value) return;
const rect = tableRef.value.getBoundingClientRect();
// 视口高度 - 表格顶部偏移 - 分页区高度(~52px) - 底部边距(16px)
tableHeight.value = window.innerHeight - rect.top - 84;
};
/** 加载字典 */
const loadDicts = async () => {
try {
@ -78,14 +112,17 @@ const loadDicts = async () => {
}
};
/** 区域变更时加载街道 */
/** 区域变更时加载街道(用区的dictValue作为街道字典类型) */
const handleDistrictChange = async (val: string) => {
queryParams.street = undefined;
streetOptions.value = [];
if (val) {
try {
const res = await getDicts(val);
streetOptions.value = res.data || [];
const matched = addressTypeOptions.value.find((item: any) => item.dictLabel === val);
if (matched) {
const res = await getDicts(matched.dictValue);
streetOptions.value = res.data || [];
}
} catch (e) {}
}
};
@ -97,14 +134,63 @@ const handleTimeModeChange = (mode: string) => {
queryParams.beginTime = undefined;
queryParams.endTime = undefined;
if (mode === 'year') {
queryParams.dateType = 3;
applyYearFilter();
} else if (mode === 'quarter') {
queryParams.dateType = 2;
applyQuarterFilter();
} else if (mode === 'month') {
queryParams.dateType = 1;
// 默认选当前月
const now = new Date();
selectedMonth.value = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`;
applyMonthFilter();
}
// custom 模式等用户手动选择时间范围
};
/** 年度筛选 */
const applyYearFilter = () => {
const year = selectedYear.value;
queryParams.beginTime = `${year}-01-01`;
queryParams.endTime = `${year}-12-31`;
};
/** 季度筛选 */
const applyQuarterFilter = () => {
const year = selectedYear.value;
const q = selectedQuarter.value;
const startMonth = (q - 1) * 3 + 1;
const endMonth = q * 3;
const endDay = new Date(Number(year), endMonth, 0).getDate();
queryParams.beginTime = `${year}-${String(startMonth).padStart(2, '0')}-01`;
queryParams.endTime = `${year}-${String(endMonth).padStart(2, '0')}-${endDay}`;
};
/** 月度筛选 */
const applyMonthFilter = () => {
if (selectedMonth.value) {
queryParams.dateSearch = selectedMonth.value;
}
};
/** 年份变更 */
const handleYearChange = () => {
if (timeMode.value === 'year') {
applyYearFilter();
} else if (timeMode.value === 'quarter') {
applyQuarterFilter();
}
};
/** 季度变更 */
const handleQuarterChange = () => {
applyQuarterFilter();
};
/** 月份变更 */
const handleMonthChange = (val: string) => {
selectedMonth.value = val;
queryParams.dateSearch = val;
};
/** 搜索 */
const handleQuery = () => {
queryParams.pageNum = 1;
@ -131,13 +217,17 @@ const resetQuery = () => {
district: undefined,
street: undefined,
checkCategory: undefined,
dateType: 3,
dateType: undefined,
dateSearch: undefined,
beginTime: undefined,
endTime: undefined
});
timeMode.value = 'year';
selectedYear.value = String(new Date().getFullYear());
selectedQuarter.value = Math.ceil((new Date().getMonth() + 1) / 3);
selectedMonth.value = '';
streetOptions.value = [];
applyYearFilter();
handleQuery();
};
@ -179,6 +269,27 @@ const handleExport = async () => {
}
};
/** 时段文本 */
const timePeriodText = computed(() => {
if (timeMode.value === 'year') {
return `${selectedYear.value}年`;
} else if (timeMode.value === 'quarter') {
return `${selectedYear.value}年第${selectedQuarter.value}季度`;
} else if (timeMode.value === 'month') {
if (selectedMonth.value) {
const [y, m] = selectedMonth.value.split('-');
return `${y}年${parseInt(m)}月`;
}
return '';
} else if (timeMode.value === 'custom') {
const b = queryParams.beginTime || '';
const e = queryParams.endTime || '';
if (b && e) return `${b} ~ ${e}`;
return '';
}
return '';
});
/** 格式化日期 */
const formatDate = (row: any) => {
if (!row.enforcementTime) return '';
@ -189,114 +300,125 @@ const formatDate = (row: any) => {
<template>
<div>
<!-- 统计指标卡片 -->
<el-row :gutter="16" class="stats-row">
<el-col :span="8">
<el-card shadow="hover" class="stat-card">
<div class="stat-label">涉企执法总数</div>
<div class="stat-value">{{ stats.total }}</div>
</el-card>
</el-col>
<el-col :span="8">
<el-card shadow="hover" class="stat-card">
<div class="stat-label">联合执法</div>
<div class="stat-value">{{ stats.unionCount }} <span class="stat-percent">占比 {{ stats.unionPercent }}</span></div>
</el-card>
</el-col>
<el-col :span="8">
<el-card shadow="hover" class="stat-card">
<div class="stat-label">{{ stats.checkCategoryName || '检查类别' }}</div>
<div class="stat-value">{{ stats.checkCount }} <span class="stat-percent">占比 {{ stats.checkPercent }}</span></div>
</el-card>
</el-col>
</el-row>
<!-- 搜索表单 -->
<el-form :model="queryParams" inline class="search-form">
<el-form-item label="执法单位">
<el-input v-model="queryParams.bureauDeptName" placeholder="请输入" clearable style="width: 150px" />
</el-form-item>
<el-form-item label="执法人员">
<el-input v-model="queryParams.lawEnforcer" placeholder="请输入" clearable style="width: 120px" />
</el-form-item>
<el-form-item label="企业名称">
<el-input v-model="queryParams.enterpriseName" placeholder="请输入" clearable style="width: 150px" />
</el-form-item>
<el-form-item label="执法事项">
<el-input v-model="queryParams.lawEnforcementItemText" placeholder="请输入" clearable style="width: 150px" />
</el-form-item>
<el-form-item label="联合执法">
<el-select v-model="queryParams.hasUnion" placeholder="全部" clearable style="width: 90px">
<el-option label="是" :value="1" />
<el-option label="否" :value="0" />
</el-select>
</el-form-item>
<el-form-item label="受委托">
<el-select v-model="queryParams.hasAuto" placeholder="全部" clearable style="width: 90px">
<el-option label="是" :value="1" />
<el-option label="否" :value="0" />
</el-select>
</el-form-item>
<el-form-item label="委托单位">
<el-input v-model="queryParams.entrustDeptName" placeholder="请输入" clearable style="width: 130px" />
</el-form-item>
<el-form-item label="执法类别">
<el-select v-model="queryParams.lawEnforceCategory" placeholder="全部" clearable style="width: 120px">
<el-option v-for="item in lawEnforceCategoryOptions" :key="item.dictValue" :label="item.dictLabel" :value="item.dictValue" />
</el-select>
</el-form-item>
<el-form-item label="是否投诉">
<el-select v-model="queryParams.isComplaint" placeholder="全部" clearable style="width: 90px">
<el-option label="是" :value="1" />
<el-option label="否" :value="0" />
</el-select>
</el-form-item>
<el-form-item label="是否扫码">
<el-select v-model="queryParams.enforce" placeholder="全部" clearable style="width: 90px">
<el-option label="是" value="0" />
<el-option label="否" value="1" />
</el-select>
</el-form-item>
<el-form-item label="未扫码原因">
<el-select v-model="queryParams.checkCss" placeholder="全部" clearable style="width: 120px">
<el-option v-for="item in noScanReasonOptions" :key="item.dictValue" :label="item.dictLabel" :value="item.dictValue" />
</el-select>
</el-form-item>
<el-form-item label="企业注册地">
<el-select v-model="queryParams.district" placeholder="全市" clearable style="width: 110px" @change="handleDistrictChange">
<el-option v-for="item in addressTypeOptions" :key="item.dictValue" :label="item.dictLabel" :value="item.dictLabel" />
</el-select>
<el-select v-model="queryParams.street" placeholder="全部街道" clearable style="width: 120px; margin-left: 4px">
<el-option v-for="item in streetOptions" :key="item.dictValue" :label="item.dictLabel" :value="item.dictLabel" />
</el-select>
</el-form-item>
<el-form-item label="任务来源">
<el-select v-model="queryParams.checkCategory" placeholder="全部" clearable style="width: 120px">
<el-option v-for="item in checkCategoryOptions" :key="item.dictValue" :label="item.dictLabel" :value="item.dictValue" />
</el-select>
</el-form-item>
<el-form-item label="时段">
<el-select v-model="timeMode" style="width: 100px" @change="handleTimeModeChange">
<el-option label="年度" value="year" />
<el-option label="季度" value="quarter" />
<el-option label="月度" value="month" />
<el-option label="自定义" value="custom" />
</el-select>
</el-form-item>
<el-form-item v-if="timeMode === 'custom'" label="执法时间">
<el-date-picker v-model="queryParams.beginTime" type="date" placeholder="开始" value-format="YYYY-MM-DD" style="width: 130px" />
<span style="margin: 0 4px">-</span>
<el-date-picker v-model="queryParams.endTime" type="date" placeholder="结束" value-format="YYYY-MM-DD" style="width: 130px" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleQuery">搜索</el-button>
<el-button @click="resetQuery">重置</el-button>
<el-button type="warning" @click="handleExport">导出</el-button>
</el-form-item>
</el-form>
<el-form :model="queryParams" inline class="search-form" label-width="100px">
<div class="search-row">
<el-form-item label="执法单位">
<el-input v-model="queryParams.bureauDeptName" placeholder="请输入" clearable style="width: 150px" />
</el-form-item>
<el-form-item label="执法人员">
<el-input v-model="queryParams.lawEnforcer" placeholder="请输入" clearable style="width: 120px" />
</el-form-item>
<el-form-item label="企业名称">
<el-input v-model="queryParams.enterpriseName" placeholder="请输入" clearable style="width: 150px" />
</el-form-item>
<el-form-item label="执法事项">
<el-input v-model="queryParams.lawEnforcementItemText" placeholder="请输入" clearable style="width: 150px" />
</el-form-item>
<el-form-item label="联合执法">
<el-select v-model="queryParams.hasUnion" placeholder="全部" clearable style="width: 120px">
<el-option label="是" :value="1" />
<el-option label="否" :value="0" />
</el-select>
</el-form-item>
<el-form-item label="受委托">
<el-select v-model="queryParams.hasAuto" placeholder="全部" clearable style="width: 90px">
<el-option label="是" :value="1" />
<el-option label="否" :value="0" />
</el-select>
</el-form-item>
</div>
<div class="search-row">
<el-form-item label="委托单位">
<el-input v-model="queryParams.entrustDeptName" placeholder="请输入" clearable style="width: 150px" />
</el-form-item>
<el-form-item label="执法类别">
<el-select v-model="queryParams.lawEnforceCategory" placeholder="全部" clearable style="width: 120px">
<el-option v-for="item in lawEnforceCategoryOptions" :key="item.dictValue" :label="item.dictLabel" :value="item.dictValue" />
</el-select>
</el-form-item>
<el-form-item label="是否投诉">
<el-select v-model="queryParams.isComplaint" placeholder="全部" clearable style="width: 150px">
<el-option label="是" :value="1" />
<el-option label="否" :value="0" />
</el-select>
</el-form-item>
<el-form-item label="是否扫码">
<el-select v-model="queryParams.enforce" placeholder="全部" clearable style="width: 150px">
<el-option label="是" value="0" />
<el-option label="否" value="1" />
</el-select>
</el-form-item>
<el-form-item label="未扫码原因">
<el-select v-model="queryParams.checkCss" placeholder="全部" clearable style="width: 120px">
<el-option v-for="item in noScanReasonOptions" :key="item.dictValue" :label="item.dictLabel" :value="item.dictValue" />
</el-select>
</el-form-item>
<el-form-item label="任务来源">
<el-select v-model="queryParams.checkCategory" placeholder="全部" clearable style="width: 150px">
<el-option v-for="item in checkCategoryOptions" :key="item.dictValue" :label="item.dictLabel" :value="item.dictValue" />
</el-select>
</el-form-item>
</div>
<div class="search-row">
<el-form-item label="企业注册地">
<el-select v-model="queryParams.district" placeholder="全市" clearable style="width: 150px" @change="handleDistrictChange">
<el-option v-for="item in addressTypeOptions" :key="item.dictValue" :label="item.dictLabel" :value="item.dictLabel" />
</el-select>
<el-select v-model="queryParams.street" placeholder="全部街道" clearable style="width: 248px; margin-left: 4px">
<el-option v-for="item in streetOptions" :key="item.dictValue" :label="item.dictLabel" :value="item.dictLabel" />
</el-select>
</el-form-item>
<el-form-item label="时段">
<el-select v-model="timeMode" style="width: 100px" @change="handleTimeModeChange">
<el-option label="年度" value="year" />
<el-option label="季度" value="quarter" />
<el-option label="月度" value="month" />
<el-option label="自定义" value="custom" />
</el-select>
</el-form-item>
<el-form-item v-if="timeMode === 'year'">
<el-select v-model="selectedYear" style="width: 100px" @change="handleYearChange">
<el-option v-for="y in yearOptions" :key="y" :label="y + '年'" :value="y" />
</el-select>
</el-form-item>
<el-form-item v-if="timeMode === 'quarter'">
<el-select v-model="selectedYear" style="width: 100px" @change="handleYearChange">
<el-option v-for="y in yearOptions" :key="y" :label="y + '年'" :value="y" />
</el-select>
<el-select v-model="selectedQuarter" style="width: 110px; margin-left: 4px" @change="handleQuarterChange">
<el-option v-for="q in quarterOptions" :key="q.value" :label="q.label" :value="q.value" />
</el-select>
</el-form-item>
<el-form-item v-if="timeMode === 'month'">
<el-date-picker v-model="selectedMonth" type="month" placeholder="选择月份" value-format="YYYY-MM" style="width: 150px" @change="handleMonthChange" />
</el-form-item>
<el-form-item v-if="timeMode === 'custom'" label="执法时间">
<el-date-picker v-model="queryParams.beginTime" type="date" placeholder="开始" value-format="YYYY-MM-DD" style="width: 130px" />
<span style="margin: 0 4px">-</span>
<el-date-picker v-model="queryParams.endTime" type="date" placeholder="结束" value-format="YYYY-MM-DD" style="width: 130px" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleQuery">搜索</el-button>
<el-button @click="resetQuery">重置</el-button>
<el-button type="warning" @click="handleExport">导出</el-button>
</el-form-item>
</div>
</el-form>
<!-- 统计指标(旧系统样式) -->
<div class="stats-line">
<span class="stats-period">{{ timePeriodText }}</span>
<span class="stats-item">涉企执法总数<em>{{ stats.total }}</em></span>
<span class="stats-item">联合执法<em>{{ stats.unionCount }}</em></span>
<span class="stats-item">占比<em>{{ stats.unionPercent }}</em></span>
<span class="stats-item">{{ stats.checkCategoryName || '日常检查' }}<em>{{ stats.checkCount }}</em></span>
<span class="stats-item">占比<em>{{ stats.checkPercent }}</em></span>
</div>
<!-- 列表表格 -->
<el-table v-loading="loading" :data="tableData" border stripe style="width: 100%">
<div ref="tableRef">
<el-table v-loading="loading" :data="tableData" border stripe style="width: 100%" :height="tableHeight">
<el-table-column label="企业名称" prop="enterpriseName" min-width="180" show-overflow-tooltip />
<el-table-column label="执法单位" prop="bureauDeptName" min-width="150" show-overflow-tooltip />
<el-table-column label="执法人员" min-width="120">
@ -333,6 +455,7 @@ const formatDate = (row: any) => {
</el-table-column>
<el-table-column label="委托单位" prop="entrustDeptName" width="130" show-overflow-tooltip />
</el-table>
</div>
<!-- 分页 -->
<el-pagination
@ -349,31 +472,37 @@ const formatDate = (row: any) => {
</template>
<style scoped lang="scss">
.stats-row {
.stats-line {
margin-bottom: 16px;
}
.stat-card {
text-align: center;
.stat-label {
font-size: 14px;
color: #909399;
margin-bottom: 8px;
}
.stat-value {
font-size: 24px;
padding: 12px 16px;
background: #e8e8e8;
border-radius: 4px;
font-size: 14px;
color: #303133;
display: flex;
align-items: center;
gap: 24px;
.stats-period {
font-weight: bold;
color: #303133;
.stat-percent {
font-size: 13px;
color: #909399;
font-weight: normal;
margin-left: 8px;
color: #409eff;
}
.stats-item {
em {
font-style: normal;
font-weight: bold;
color: #e6a23c;
margin-left: 4px;
}
}
}
.search-form {
margin-bottom: 16px;
}
.search-row {
display: flex;
flex-wrap: nowrap;
align-items: center;
}
.mt-4 {
margin-top: 16px;
}

Loading…
Cancel
Save