12 changed files with 1371 additions and 1 deletions
@ -0,0 +1,95 @@ |
|||
package com.zdxt.enforcementcode.controller.web.supervision.report; |
|||
|
|||
import com.zdxt.enforcementcode.domain.bo.EnforcementRegistrationBo; |
|||
import com.zdxt.enforcementcode.domain.vo.EnforcementRegistrationVo; |
|||
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 com.zdxt.enforcementcode.report.IEnforcementRegistrationReport; |
|||
import jakarta.servlet.http.HttpServletResponse; |
|||
import lombok.RequiredArgsConstructor; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.dromara.common.core.domain.R; |
|||
import org.dromara.common.excel.utils.ExcelUtil; |
|||
import org.dromara.common.mybatis.core.page.PageQuery; |
|||
import org.dromara.common.mybatis.core.page.TableDataInfo; |
|||
import org.springframework.validation.annotation.Validated; |
|||
import org.springframework.web.bind.annotation.GetMapping; |
|||
import org.springframework.web.bind.annotation.PostMapping; |
|||
import org.springframework.web.bind.annotation.RequestMapping; |
|||
import org.springframework.web.bind.annotation.RestController; |
|||
|
|||
import java.util.List; |
|||
|
|||
/** |
|||
* 涉企执法情况报表 |
|||
* |
|||
* @author zdxt |
|||
*/ |
|||
@Slf4j |
|||
@Validated |
|||
@RequiredArgsConstructor |
|||
@RestController |
|||
@RequestMapping("/web/supervision/report/registration") |
|||
public class RegistrationController { |
|||
|
|||
private final IEnforcementRegistrationReport registrationReport; |
|||
|
|||
/** |
|||
* 接口1:涉企执法统计指标 |
|||
*/ |
|||
@GetMapping("/statistics") |
|||
public R<RegistrationStatisticsVo> statistics(EnforcementRegistrationBo bo) { |
|||
return R.ok(registrationReport.statistics(bo)); |
|||
} |
|||
|
|||
/** |
|||
* 接口2:涉企执法统计列表(分页) |
|||
*/ |
|||
@GetMapping("/list") |
|||
public TableDataInfo<EnforcementRegistrationVo> list(EnforcementRegistrationBo bo, PageQuery pageQuery) { |
|||
return registrationReport.list(bo, pageQuery); |
|||
} |
|||
|
|||
/** |
|||
* 接口3:涉企执法统计列表导出 |
|||
*/ |
|||
@PostMapping("/exportList") |
|||
public void exportList(EnforcementRegistrationBo bo, HttpServletResponse response) { |
|||
List<EnforcementRegistrationVo> list = registrationReport.listAll(bo); |
|||
ExcelUtil.exportExcel(list, "涉企执法统计", EnforcementRegistrationVo.class, response); |
|||
} |
|||
|
|||
/** |
|||
* 接口4:执法类别分布(饼图) |
|||
*/ |
|||
@GetMapping("/categoryChart") |
|||
public R<List<ChartDataVo>> categoryChart(EnforcementRegistrationBo bo) { |
|||
return R.ok(registrationReport.categoryChart(bo)); |
|||
} |
|||
|
|||
/** |
|||
* 接口5:部门执法排行(柱状图,TOP10) |
|||
*/ |
|||
@GetMapping("/deptRankChart") |
|||
public R<List<ChartDataVo>> deptRankChart(EnforcementRegistrationBo bo) { |
|||
return R.ok(registrationReport.deptRankChart(bo)); |
|||
} |
|||
|
|||
/** |
|||
* 接口6:执法分析列表(按企业维度统计执法次数,分页) |
|||
*/ |
|||
@GetMapping("/analysisRank") |
|||
public TableDataInfo<RegistrationAnalysisVo> analysisRank(EnforcementRegistrationBo bo, PageQuery pageQuery) { |
|||
return registrationReport.analysisRank(bo, pageQuery); |
|||
} |
|||
|
|||
/** |
|||
* 接口7:执法分析导出 |
|||
*/ |
|||
@PostMapping("/exportAnalysis") |
|||
public void exportAnalysis(EnforcementRegistrationBo bo, HttpServletResponse response) { |
|||
List<RegistrationAnalysisVo> list = registrationReport.analysisAll(bo); |
|||
ExcelUtil.exportExcel(list, "执法分析", RegistrationAnalysisVo.class, response); |
|||
} |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
package com.zdxt.enforcementcode.domain.vo.report; |
|||
|
|||
import lombok.Data; |
|||
|
|||
import java.io.Serial; |
|||
import java.io.Serializable; |
|||
|
|||
/** |
|||
* 图表数据通用VO(name + value) |
|||
*/ |
|||
@Data |
|||
public class ChartDataVo implements Serializable { |
|||
|
|||
@Serial |
|||
private static final long serialVersionUID = 1L; |
|||
|
|||
/** |
|||
* 名称(如部门名、类别名) |
|||
*/ |
|||
private String name; |
|||
|
|||
/** |
|||
* 数值 |
|||
*/ |
|||
private long value; |
|||
|
|||
public ChartDataVo() {} |
|||
|
|||
public ChartDataVo(String name, long value) { |
|||
this.name = name; |
|||
this.value = value; |
|||
} |
|||
} |
|||
@ -0,0 +1,72 @@ |
|||
package com.zdxt.enforcementcode.domain.vo.report; |
|||
|
|||
import cn.idev.excel.annotation.ExcelIgnoreUnannotated; |
|||
import cn.idev.excel.annotation.ExcelProperty; |
|||
import lombok.Data; |
|||
|
|||
import java.io.Serial; |
|||
import java.io.Serializable; |
|||
|
|||
/** |
|||
* 执法分析行VO(按企业维度聚合统计) |
|||
*/ |
|||
@Data |
|||
@ExcelIgnoreUnannotated |
|||
public class RegistrationAnalysisVo implements Serializable { |
|||
|
|||
@Serial |
|||
private static final long serialVersionUID = 1L; |
|||
|
|||
/** |
|||
* 企业注册区 |
|||
*/ |
|||
@ExcelProperty(value = "区域") |
|||
private String district; |
|||
|
|||
/** |
|||
* 企业注册街道 |
|||
*/ |
|||
@ExcelProperty(value = "街道") |
|||
private String street; |
|||
|
|||
/** |
|||
* 企业名称 |
|||
*/ |
|||
@ExcelProperty(value = "企业名称") |
|||
private String enterpriseName; |
|||
|
|||
/** |
|||
* 执法单位 |
|||
*/ |
|||
@ExcelProperty(value = "执法单位") |
|||
private String bureauDeptName; |
|||
|
|||
/** |
|||
* 执法人员 |
|||
*/ |
|||
@ExcelProperty(value = "执法人员") |
|||
private String lawEnforcer; |
|||
|
|||
/** |
|||
* 执法事项 |
|||
*/ |
|||
@ExcelProperty(value = "执法事项") |
|||
private String lawEnforcementItemText; |
|||
|
|||
/** |
|||
* 最近执法时间 |
|||
*/ |
|||
@ExcelProperty(value = "执法时间") |
|||
private String enforcementTime; |
|||
|
|||
/** |
|||
* 执法次数 |
|||
*/ |
|||
@ExcelProperty(value = "执法次数") |
|||
private Integer totalCount; |
|||
|
|||
/** |
|||
* 颜色分级:green=2次及以下, yellow=3-4次, orange=5-6次, red=7次及以上 |
|||
*/ |
|||
private String colorLevel; |
|||
} |
|||
@ -0,0 +1,46 @@ |
|||
package com.zdxt.enforcementcode.domain.vo.report; |
|||
|
|||
import lombok.Data; |
|||
|
|||
import java.io.Serial; |
|||
import java.io.Serializable; |
|||
|
|||
/** |
|||
* 涉企执法统计指标VO |
|||
*/ |
|||
@Data |
|||
public class RegistrationStatisticsVo implements Serializable { |
|||
|
|||
@Serial |
|||
private static final long serialVersionUID = 1L; |
|||
|
|||
/** |
|||
* 涉企执法总数 |
|||
*/ |
|||
private long total; |
|||
|
|||
/** |
|||
* 联合执法数量 |
|||
*/ |
|||
private long unionCount; |
|||
|
|||
/** |
|||
* 联合执法占比(百分比字符串,如"12.50%") |
|||
*/ |
|||
private String unionPercent; |
|||
|
|||
/** |
|||
* 检查类别名称 |
|||
*/ |
|||
private String checkCategoryName; |
|||
|
|||
/** |
|||
* 指定检查类别数量 |
|||
*/ |
|||
private long checkCount; |
|||
|
|||
/** |
|||
* 指定检查类别占比 |
|||
*/ |
|||
private String checkPercent; |
|||
} |
|||
@ -0,0 +1,54 @@ |
|||
package com.zdxt.enforcementcode.report; |
|||
|
|||
import com.zdxt.enforcementcode.domain.bo.EnforcementRegistrationBo; |
|||
import com.zdxt.enforcementcode.domain.vo.EnforcementRegistrationVo; |
|||
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.dromara.common.mybatis.core.page.PageQuery; |
|||
import org.dromara.common.mybatis.core.page.TableDataInfo; |
|||
|
|||
import java.util.List; |
|||
|
|||
/** |
|||
* 涉企执法情况报表服务接口 |
|||
* |
|||
* @author zdxt |
|||
*/ |
|||
public interface IEnforcementRegistrationReport { |
|||
|
|||
/** |
|||
* 统计指标 |
|||
*/ |
|||
RegistrationStatisticsVo statistics(EnforcementRegistrationBo bo); |
|||
|
|||
/** |
|||
* 统计列表(分页) |
|||
*/ |
|||
TableDataInfo<EnforcementRegistrationVo> list(EnforcementRegistrationBo bo, PageQuery pageQuery); |
|||
|
|||
/** |
|||
* 统计列表(全量,用于导出) |
|||
*/ |
|||
List<EnforcementRegistrationVo> listAll(EnforcementRegistrationBo bo); |
|||
|
|||
/** |
|||
* 执法类别分布(饼图数据) |
|||
*/ |
|||
List<ChartDataVo> categoryChart(EnforcementRegistrationBo bo); |
|||
|
|||
/** |
|||
* 部门执法排行(柱状图TOP10) |
|||
*/ |
|||
List<ChartDataVo> deptRankChart(EnforcementRegistrationBo bo); |
|||
|
|||
/** |
|||
* 执法分析列表(按企业维度统计,分页) |
|||
*/ |
|||
TableDataInfo<RegistrationAnalysisVo> analysisRank(EnforcementRegistrationBo bo, PageQuery pageQuery); |
|||
|
|||
/** |
|||
* 执法分析全量(用于导出) |
|||
*/ |
|||
List<RegistrationAnalysisVo> analysisAll(EnforcementRegistrationBo bo); |
|||
} |
|||
@ -0,0 +1,138 @@ |
|||
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; |
|||
import com.zdxt.enforcementcode.domain.vo.report.RegistrationAnalysisVo; |
|||
import com.zdxt.enforcementcode.domain.vo.report.RegistrationStatisticsVo; |
|||
import com.zdxt.enforcementcode.mapper.EnforcementRegistrationMapper; |
|||
import com.zdxt.enforcementcode.report.IEnforcementRegistrationReport; |
|||
import lombok.RequiredArgsConstructor; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.dromara.common.core.utils.StringUtils; |
|||
import org.dromara.common.mybatis.core.page.PageQuery; |
|||
import org.dromara.common.mybatis.core.page.TableDataInfo; |
|||
import org.dromara.system.domain.vo.SysDictDataVo; |
|||
import org.dromara.system.service.ISysDictTypeService; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
import java.text.DecimalFormat; |
|||
import java.util.*; |
|||
import java.util.stream.Collectors; |
|||
|
|||
/** |
|||
* 涉企执法情况报表服务实现 |
|||
* |
|||
* @author zdxt |
|||
*/ |
|||
@Slf4j |
|||
@RequiredArgsConstructor |
|||
@Service |
|||
public class EnforcementRegistrationReportImpl implements IEnforcementRegistrationReport { |
|||
|
|||
private final EnforcementRegistrationMapper registrationMapper; |
|||
private final ISysDictTypeService dictTypeService; |
|||
|
|||
private static final DecimalFormat PERCENT_FORMAT = new DecimalFormat("0.00%"); |
|||
|
|||
@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"; |
|||
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; |
|||
} |
|||
|
|||
@Override |
|||
public TableDataInfo<EnforcementRegistrationVo> list(EnforcementRegistrationBo bo, PageQuery pageQuery) { |
|||
String dataFilter = DataFilterUtil.buildFilterSql(true, true); |
|||
Page<EnforcementRegistrationVo> page = pageQuery.build(); |
|||
IPage<EnforcementRegistrationVo> result = registrationMapper.selectReportPage(page, bo, dataFilter); |
|||
return TableDataInfo.build(result); |
|||
} |
|||
|
|||
@Override |
|||
public List<EnforcementRegistrationVo> listAll(EnforcementRegistrationBo bo) { |
|||
String dataFilter = DataFilterUtil.buildFilterSql(true, true); |
|||
// 复用 selectReportPage 不分页方式(传一个大页)
|
|||
Page<EnforcementRegistrationVo> page = new Page<>(1, Integer.MAX_VALUE, false); |
|||
IPage<EnforcementRegistrationVo> result = registrationMapper.selectReportPage(page, bo, dataFilter); |
|||
return result.getRecords(); |
|||
} |
|||
|
|||
@Override |
|||
public List<ChartDataVo> categoryChart(EnforcementRegistrationBo bo) { |
|||
String dataFilter = DataFilterUtil.buildFilterSql(true, true); |
|||
List<ChartDataVo> list = registrationMapper.selectReportCategoryChart(bo, dataFilter); |
|||
|
|||
// 字典翻译
|
|||
Map<String, String> dictMap = dictTypeService.selectDictDataByType("law_enforce_category").stream() |
|||
.collect(Collectors.toMap(SysDictDataVo::getDictValue, SysDictDataVo::getDictLabel, (a, b) -> a)); |
|||
|
|||
list.forEach(item -> { |
|||
String translated = dictMap.get(item.getName()); |
|||
if (translated != null) { |
|||
item.setName(translated); |
|||
} |
|||
}); |
|||
|
|||
return list; |
|||
} |
|||
|
|||
@Override |
|||
public List<ChartDataVo> deptRankChart(EnforcementRegistrationBo bo) { |
|||
String dataFilter = DataFilterUtil.buildFilterSql(true, true); |
|||
return registrationMapper.selectReportDeptRankChart(bo, dataFilter); |
|||
} |
|||
|
|||
@Override |
|||
public TableDataInfo<RegistrationAnalysisVo> analysisRank(EnforcementRegistrationBo bo, PageQuery pageQuery) { |
|||
String dataFilter = DataFilterUtil.buildFilterSql(true, true); |
|||
List<RegistrationAnalysisVo> analysisList = registrationMapper.selectReportAnalysisList(bo, dataFilter); |
|||
|
|||
// 手动分页
|
|||
int pageNum = pageQuery.getPageNum(); |
|||
int pageSize = pageQuery.getPageSize(); |
|||
int total = analysisList.size(); |
|||
int fromIndex = (pageNum - 1) * pageSize; |
|||
int toIndex = Math.min(fromIndex + pageSize, total); |
|||
List<RegistrationAnalysisVo> pageData = fromIndex < total ? analysisList.subList(fromIndex, toIndex) : Collections.emptyList(); |
|||
|
|||
return new TableDataInfo<>(pageData, (long) total); |
|||
} |
|||
|
|||
@Override |
|||
public List<RegistrationAnalysisVo> analysisAll(EnforcementRegistrationBo bo) { |
|||
String dataFilter = DataFilterUtil.buildFilterSql(true, true); |
|||
return registrationMapper.selectReportAnalysisList(bo, dataFilter); |
|||
} |
|||
} |
|||
@ -0,0 +1,213 @@ |
|||
<?xml version="1.0" encoding="UTF-8" ?> |
|||
<!DOCTYPE mapper |
|||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" |
|||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
|||
<mapper namespace="com.zdxt.enforcementcode.mapper.EnforcementRegistrationMapper"> |
|||
|
|||
<!-- 报表通用 WHERE 条件片段 --> |
|||
<sql id="reportWhereCondition"> |
|||
AND is_submit = 1 |
|||
AND is_delete = 0 |
|||
AND (status = 0 OR status IS NULL) |
|||
<if test="bo.bureauDeptName != null and bo.bureauDeptName != ''"> |
|||
AND bureau_dept_name LIKE CONCAT('%', #{bo.bureauDeptName}, '%') |
|||
</if> |
|||
<if test="bo.lawEnforcer != null and bo.lawEnforcer != ''"> |
|||
AND (law_enforcer LIKE CONCAT('%', #{bo.lawEnforcer}, '%') |
|||
OR law_enforcer_two LIKE CONCAT('%', #{bo.lawEnforcer}, '%')) |
|||
</if> |
|||
<if test="bo.enterpriseName != null and bo.enterpriseName != ''"> |
|||
AND enterprise_name LIKE CONCAT('%', #{bo.enterpriseName}, '%') |
|||
</if> |
|||
<if test="bo.lawEnforcementItemText != null and bo.lawEnforcementItemText != ''"> |
|||
AND law_enforcement_item_text LIKE CONCAT('%', #{bo.lawEnforcementItemText}, '%') |
|||
</if> |
|||
<if test="bo.hasUnion != null"> |
|||
AND has_union = #{bo.hasUnion} |
|||
</if> |
|||
<if test="bo.hasAuto != null"> |
|||
AND has_auto = #{bo.hasAuto} |
|||
</if> |
|||
<if test="bo.entrustDeptName != null and bo.entrustDeptName != ''"> |
|||
AND entrust_dept_name LIKE CONCAT('%', #{bo.entrustDeptName}, '%') |
|||
</if> |
|||
<if test="bo.lawEnforceCategory != null and bo.lawEnforceCategory != ''"> |
|||
AND law_enforce_category = #{bo.lawEnforceCategory} |
|||
</if> |
|||
<if test="bo.isComplaint != null"> |
|||
AND is_complaint = #{bo.isComplaint} |
|||
</if> |
|||
<if test="bo.enforce != null and bo.enforce != ''"> |
|||
AND enforce = #{bo.enforce} |
|||
</if> |
|||
<if test="bo.checkCss != null and bo.checkCss != ''"> |
|||
AND check_css = #{bo.checkCss} |
|||
</if> |
|||
<if test="bo.district != null and bo.district != ''"> |
|||
AND district = #{bo.district} |
|||
</if> |
|||
<if test="bo.street != null and bo.street != ''"> |
|||
AND street = #{bo.street} |
|||
</if> |
|||
<if test="bo.checkCategory != null and bo.checkCategory != ''"> |
|||
AND check_category = #{bo.checkCategory} |
|||
</if> |
|||
<!-- dateType: 1=本月, 2=本季度, 3=本年 --> |
|||
<if test="bo.dateType != null and bo.dateType == 1"> |
|||
AND DATE_FORMAT(enforcement_time, '%Y-%m') = DATE_FORMAT(CURDATE(), '%Y-%m') |
|||
</if> |
|||
<if test="bo.dateType != null and bo.dateType == 2"> |
|||
AND DATE_FORMAT(enforcement_time, '%Y-%m') >= CONCAT(YEAR(CURDATE()), '-', |
|||
CASE WHEN QUARTER(CURDATE()) IN (1) THEN '01' |
|||
WHEN QUARTER(CURDATE()) IN (2) THEN '04' |
|||
WHEN QUARTER(CURDATE()) IN (3) THEN '07' |
|||
ELSE '10' END) |
|||
AND DATE_FORMAT(enforcement_time, '%Y-%m') <= CONCAT(YEAR(CURDATE()), '-', |
|||
CASE WHEN QUARTER(CURDATE()) IN (1) THEN '03' |
|||
WHEN QUARTER(CURDATE()) IN (2) THEN '06' |
|||
WHEN QUARTER(CURDATE()) IN (3) THEN '09' |
|||
ELSE '12' END) |
|||
</if> |
|||
<if test="bo.dateType != null and bo.dateType == 3"> |
|||
AND DATE_FORMAT(enforcement_time, '%Y') = DATE_FORMAT(CURDATE(), '%Y') |
|||
</if> |
|||
<if test="bo.dateSearch != null and bo.dateSearch != ''"> |
|||
AND DATE_FORMAT(enforcement_time, '%Y-%m') = #{bo.dateSearch} |
|||
</if> |
|||
<if test="bo.beginTime != null and bo.beginTime != ''"> |
|||
AND DATE_FORMAT(enforcement_time, '%Y-%m-%d') >= #{bo.beginTime} |
|||
</if> |
|||
<if test="bo.endTime != null and bo.endTime != ''"> |
|||
AND DATE_FORMAT(enforcement_time, '%Y-%m-%d') <= #{bo.endTime} |
|||
</if> |
|||
<if test="dataFilter != null and dataFilter != ''"> |
|||
${dataFilter} |
|||
</if> |
|||
</sql> |
|||
|
|||
<!-- 报表分页列表查询 --> |
|||
<select id="selectReportPage" resultType="com.zdxt.enforcementcode.domain.vo.EnforcementRegistrationVo"> |
|||
SELECT |
|||
id, law_enforcement, law_enforcement_id, enforce, |
|||
enterprise_id, enterprise_name, enterprise_number, |
|||
enforcement_time, create_time, create_by, |
|||
law_enforcer, law_enforcer_id, law_enforcer_tel, |
|||
law_enforcer_two, law_enforcer_id_two, law_enforcer_tel_two, |
|||
subsidiaryer, subsidiaryer_id, subsidiaryer_tel, |
|||
law_enforcement_item, law_enforcement_item_text, |
|||
law_enforcement_basis, law_enforcement_basis_text, |
|||
remark, reslut, user_id, dept_id, is_delete, status, |
|||
law_enforcement_code, is_complaint, is_submit, |
|||
unite_check, check_css, enforce_reason, law_field, |
|||
check_category, bureau_dept_id, bureau_dept_name, |
|||
is_evaluate, law_id, law_text, |
|||
has_union, has_auto, union_no, law_enforce_category, |
|||
entrust_dept_id, entrust_dept_name, |
|||
enterprise_address, district, street, zone, is_real_union |
|||
FROM enforcement_registration |
|||
<where> |
|||
<include refid="reportWhereCondition"/> |
|||
</where> |
|||
ORDER BY enforcement_time DESC |
|||
</select> |
|||
|
|||
<!-- 报表全量列表查询(统计/导出) --> |
|||
<select id="selectReportAll" resultType="com.zdxt.enforcementcode.domain.EnforcementRegistration"> |
|||
SELECT |
|||
id, law_enforcement, law_enforcement_id, enforce, |
|||
enterprise_id, enterprise_name, enterprise_number, |
|||
enforcement_time, create_time, create_by, |
|||
law_enforcer, law_enforcer_id, law_enforcer_tel, |
|||
law_enforcer_two, law_enforcer_id_two, law_enforcer_tel_two, |
|||
subsidiaryer, subsidiaryer_id, subsidiaryer_tel, |
|||
law_enforcement_item, law_enforcement_item_text, |
|||
law_enforcement_basis, law_enforcement_basis_text, |
|||
remark, reslut, user_id, dept_id, is_delete, status, |
|||
law_enforcement_code, is_complaint, is_submit, |
|||
unite_check, check_css, enforce_reason, law_field, |
|||
check_category, bureau_dept_id, bureau_dept_name, |
|||
is_evaluate, law_id, law_text, |
|||
has_union, has_auto, union_no, law_enforce_category, |
|||
entrust_dept_id, entrust_dept_name, |
|||
enterprise_address, district, street, zone, is_real_union |
|||
FROM enforcement_registration |
|||
<where> |
|||
<include refid="reportWhereCondition"/> |
|||
</where> |
|||
ORDER BY enforcement_time DESC |
|||
</select> |
|||
|
|||
<!-- 执法类别图表(按 law_enforce_category 分组计数) --> |
|||
<select id="selectReportCategoryChart" resultType="com.zdxt.enforcementcode.domain.vo.report.ChartDataVo"> |
|||
SELECT |
|||
IFNULL(law_enforce_category, '未分类') AS name, |
|||
COUNT(*) AS value |
|||
FROM enforcement_registration |
|||
<where> |
|||
<include refid="reportWhereCondition"/> |
|||
</where> |
|||
GROUP BY law_enforce_category |
|||
ORDER BY value DESC |
|||
</select> |
|||
|
|||
<!-- 部门排行图表(按 bureau_dept_id 分组计数,取前10) --> |
|||
<select id="selectReportDeptRankChart" resultType="com.zdxt.enforcementcode.domain.vo.report.ChartDataVo"> |
|||
SELECT |
|||
bureau_dept_name AS name, |
|||
COUNT(*) AS value |
|||
FROM enforcement_registration |
|||
<where> |
|||
<include refid="reportWhereCondition"/> |
|||
AND bureau_dept_id IS NOT NULL AND bureau_dept_id != '' |
|||
</where> |
|||
GROUP BY bureau_dept_id, bureau_dept_name |
|||
ORDER BY value DESC |
|||
LIMIT 10 |
|||
</select> |
|||
|
|||
<!-- 执法分析排名(按企业税号聚合) --> |
|||
<select id="selectReportAnalysisList" resultType="com.zdxt.enforcementcode.domain.vo.report.RegistrationAnalysisVo"> |
|||
SELECT |
|||
t.district, |
|||
t.street, |
|||
t.enterprise_name AS enterpriseName, |
|||
t.bureau_dept_name AS bureauDeptName, |
|||
CONCAT_WS(',', t.law_enforcer, NULLIF(t.law_enforcer_two, '')) AS lawEnforcer, |
|||
t.law_enforcement_item_text AS lawEnforcementItemText, |
|||
DATE_FORMAT(t.enforcement_time, '%Y-%m-%d') AS enforcementTime, |
|||
t.num AS totalCount, |
|||
CASE |
|||
WHEN t.num <= 2 THEN 'green' |
|||
WHEN t.num <= 4 THEN 'yellow' |
|||
WHEN t.num <= 6 THEN 'orange' |
|||
ELSE 'red' |
|||
END AS colorLevel |
|||
FROM ( |
|||
SELECT |
|||
district, street, enterprise_name, enterprise_number, |
|||
bureau_dept_name, law_enforcer, law_enforcer_two, |
|||
law_enforcement_item_text, enforcement_time, |
|||
COUNT(enterprise_number) AS num |
|||
FROM enforcement_registration |
|||
<where> |
|||
<include refid="reportWhereCondition"/> |
|||
AND enterprise_number IS NOT NULL AND enterprise_number != '' |
|||
</where> |
|||
GROUP BY enterprise_number |
|||
ORDER BY enforcement_time DESC |
|||
) t |
|||
<where> |
|||
<if test="bo.numLeft != null and bo.numLeft != ''"> |
|||
AND t.num >= #{bo.numLeft} |
|||
</if> |
|||
<if test="bo.numRight != null and bo.numRight != ''"> |
|||
AND t.num <= #{bo.numRight} |
|||
</if> |
|||
<if test="bo.num != null and bo.num != ''"> |
|||
AND t.num >= #{bo.num} |
|||
</if> |
|||
</where> |
|||
ORDER BY t.num DESC |
|||
</select> |
|||
|
|||
</mapper> |
|||
@ -0,0 +1,26 @@ |
|||
<script setup lang="ts"> |
|||
import { ref } from 'vue'; |
|||
import IndexList from './indexList.vue'; |
|||
import IndexChart from './indexChart.vue'; |
|||
|
|||
const activeTab = ref('list'); |
|||
</script> |
|||
|
|||
<template> |
|||
<div class="app-container"> |
|||
<el-tabs v-model="activeTab" type="border-card"> |
|||
<el-tab-pane label="涉企执法统计" name="list"> |
|||
<IndexList /> |
|||
</el-tab-pane> |
|||
<el-tab-pane label="涉企执法排行" name="chart"> |
|||
<IndexChart /> |
|||
</el-tab-pane> |
|||
</el-tabs> |
|||
</div> |
|||
</template> |
|||
|
|||
<style scoped lang="scss"> |
|||
.app-container { |
|||
padding: 16px; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,278 @@ |
|||
<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 * as echarts from 'echarts'; |
|||
|
|||
// 查询参数 |
|||
const queryParams = reactive<RegistrationReportQuery>({ |
|||
pageNum: 1, |
|||
pageSize: 20, |
|||
dateType: 3, |
|||
beginTime: undefined, |
|||
endTime: undefined |
|||
}); |
|||
|
|||
const timeMode = ref('year'); |
|||
const loading = ref(false); |
|||
|
|||
// 图表数据 |
|||
const categoryData = ref<ChartDataVo[]>([]); |
|||
const deptRankData = ref<ChartDataVo[]>([]); |
|||
|
|||
// 分析列表 |
|||
const analysisTableData = ref<RegistrationAnalysisVo[]>([]); |
|||
const analysisTotal = ref(0); |
|||
|
|||
// 图表DOM引用 |
|||
const pieChartRef = ref<HTMLElement>(); |
|||
const barChartRef = ref<HTMLElement>(); |
|||
let pieChart: echarts.ECharts | null = null; |
|||
let barChart: echarts.ECharts | null = null; |
|||
|
|||
onMounted(() => { |
|||
loadCharts(); |
|||
loadAnalysisList(); |
|||
}); |
|||
|
|||
/** 时段模式变更 */ |
|||
const handleTimeModeChange = (mode: string) => { |
|||
queryParams.dateType = undefined; |
|||
queryParams.beginTime = undefined; |
|||
queryParams.endTime = undefined; |
|||
if (mode === 'year') { |
|||
queryParams.dateType = 3; |
|||
} else if (mode === 'quarter') { |
|||
queryParams.dateType = 2; |
|||
} else if (mode === 'month') { |
|||
queryParams.dateType = 1; |
|||
} |
|||
if (mode !== 'custom') { |
|||
loadCharts(); |
|||
loadAnalysisList(); |
|||
} |
|||
}; |
|||
|
|||
/** 自定义时段搜索 */ |
|||
const handleSearch = () => { |
|||
queryParams.pageNum = 1; |
|||
loadCharts(); |
|||
loadAnalysisList(); |
|||
}; |
|||
|
|||
/** 加载图表数据 */ |
|||
const loadCharts = async () => { |
|||
loading.value = true; |
|||
try { |
|||
const [catRes, deptRes] = await Promise.all([ |
|||
getCategoryChart(queryParams), |
|||
getDeptRankChart(queryParams) |
|||
]); |
|||
categoryData.value = catRes.data || []; |
|||
deptRankData.value = deptRes.data || []; |
|||
await nextTick(); |
|||
renderPieChart(); |
|||
renderBarChart(); |
|||
} 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; |
|||
if (!pieChart) { |
|||
pieChart = echarts.init(pieChartRef.value); |
|||
} |
|||
const option: echarts.EChartsOption = { |
|||
title: { text: '执法类别分布', left: 'center' }, |
|||
tooltip: { trigger: 'item', formatter: '{b}: {c} ({d}%)' }, |
|||
legend: { orient: 'vertical', left: 'left', top: 'middle' }, |
|||
series: [{ |
|||
type: 'pie', |
|||
radius: ['40%', '70%'], |
|||
center: ['60%', '50%'], |
|||
data: categoryData.value.map(item => ({ name: item.name, value: item.value })), |
|||
emphasis: { |
|||
itemStyle: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0,0,0,0.5)' } |
|||
}, |
|||
label: { formatter: '{b}\n{c}次' } |
|||
}] |
|||
}; |
|||
pieChart.setOption(option, true); |
|||
}; |
|||
|
|||
/** 渲染柱状图 */ |
|||
const renderBarChart = () => { |
|||
if (!barChartRef.value) return; |
|||
if (!barChart) { |
|||
barChart = echarts.init(barChartRef.value); |
|||
} |
|||
const data = [...deptRankData.value].reverse(); // 倒序排列让最多的在上面 |
|||
const option: echarts.EChartsOption = { |
|||
title: { text: '部门执法排行TOP10', left: 'center' }, |
|||
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } }, |
|||
grid: { left: '20%', right: '5%', top: '15%', bottom: '5%' }, |
|||
xAxis: { type: 'value' }, |
|||
yAxis: { |
|||
type: 'category', |
|||
data: data.map(item => item.name), |
|||
axisLabel: { width: 120, overflow: 'truncate' } |
|||
}, |
|||
series: [{ |
|||
type: 'bar', |
|||
data: data.map(item => item.value), |
|||
itemStyle: { |
|||
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [ |
|||
{ offset: 0, color: '#83bff6' }, |
|||
{ offset: 1, color: '#188df0' } |
|||
]) |
|||
}, |
|||
label: { show: true, position: 'right', formatter: '{c}' } |
|||
}] |
|||
}; |
|||
barChart.setOption(option, true); |
|||
}; |
|||
|
|||
/** 颜色分级样式 */ |
|||
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 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('导出失败'); |
|||
} |
|||
}; |
|||
|
|||
// 响应窗口变化 |
|||
window.addEventListener('resize', () => { |
|||
pieChart?.resize(); |
|||
barChart?.resize(); |
|||
}); |
|||
</script> |
|||
|
|||
<template> |
|||
<div> |
|||
<!-- 筛选区 --> |
|||
<el-form inline class="search-form"> |
|||
<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="handleSearch">查询</el-button> |
|||
</el-form-item> |
|||
</el-form> |
|||
|
|||
<!-- 图表区 --> |
|||
<el-row :gutter="16" class="chart-row" v-loading="loading"> |
|||
<el-col :span="12"> |
|||
<div ref="pieChartRef" class="chart-container"></div> |
|||
</el-col> |
|||
<el-col :span="12"> |
|||
<div ref="barChartRef" 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> |
|||
|
|||
<style scoped lang="scss"> |
|||
.search-form { |
|||
margin-bottom: 16px; |
|||
} |
|||
.chart-row { |
|||
margin-bottom: 24px; |
|||
} |
|||
.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; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,380 @@ |
|||
<script setup lang="ts"> |
|||
import { ref, reactive, onMounted } from 'vue'; |
|||
import { ElMessage } from 'element-plus'; |
|||
import { |
|||
getStatistics, getRegistrationList, exportRegistrationList |
|||
} from '@/api/efcode/report/registration'; |
|||
import type { RegistrationReportQuery, RegistrationStatisticsVo } from '@/api/efcode/report/registration'; |
|||
import { getDicts } from '@/api/system/dict/data'; |
|||
|
|||
// 查询参数 |
|||
const queryParams = reactive<RegistrationReportQuery>({ |
|||
pageNum: 1, |
|||
pageSize: 20, |
|||
bureauDeptName: undefined, |
|||
lawEnforcer: undefined, |
|||
enterpriseName: undefined, |
|||
lawEnforcementItemText: undefined, |
|||
hasUnion: undefined, |
|||
hasAuto: undefined, |
|||
entrustDeptName: undefined, |
|||
lawEnforceCategory: undefined, |
|||
isComplaint: undefined, |
|||
enforce: undefined, |
|||
checkCss: undefined, |
|||
district: undefined, |
|||
street: undefined, |
|||
checkCategory: undefined, |
|||
dateType: 3, |
|||
dateSearch: undefined, |
|||
beginTime: undefined, |
|||
endTime: undefined |
|||
}); |
|||
|
|||
const loading = ref(false); |
|||
const tableData = ref<any[]>([]); |
|||
const total = ref(0); |
|||
|
|||
// 统计指标 |
|||
const stats = ref<RegistrationStatisticsVo>({ |
|||
total: 0, |
|||
unionCount: 0, |
|||
unionPercent: '0.00%', |
|||
checkCategoryName: '', |
|||
checkCount: 0, |
|||
checkPercent: '0.00%' |
|||
}); |
|||
|
|||
// 字典 |
|||
const lawEnforceCategoryOptions = ref<any[]>([]); |
|||
const checkCategoryOptions = ref<any[]>([]); |
|||
const noScanReasonOptions = ref<any[]>([]); |
|||
const addressTypeOptions = ref<any[]>([]); |
|||
const streetOptions = ref<any[]>([]); |
|||
|
|||
// 时段模式 |
|||
const timeMode = ref('year'); // year | quarter | month | custom |
|||
|
|||
onMounted(() => { |
|||
loadDicts(); |
|||
handleQuery(); |
|||
}); |
|||
|
|||
/** 加载字典 */ |
|||
const loadDicts = async () => { |
|||
try { |
|||
const [res1, res2, res3, res4] = await Promise.all([ |
|||
getDicts('law_enforce_category'), |
|||
getDicts('check_category'), |
|||
getDicts('no-scan-reason'), |
|||
getDicts('address_type') |
|||
]); |
|||
lawEnforceCategoryOptions.value = res1.data || []; |
|||
checkCategoryOptions.value = res2.data || []; |
|||
noScanReasonOptions.value = res3.data || []; |
|||
addressTypeOptions.value = res4.data || []; |
|||
} catch (e) { |
|||
console.error('加载字典失败', e); |
|||
} |
|||
}; |
|||
|
|||
/** 区域变更时加载街道 */ |
|||
const handleDistrictChange = async (val: string) => { |
|||
queryParams.street = undefined; |
|||
streetOptions.value = []; |
|||
if (val) { |
|||
try { |
|||
const res = await getDicts(val); |
|||
streetOptions.value = res.data || []; |
|||
} catch (e) {} |
|||
} |
|||
}; |
|||
|
|||
/** 时段模式变更 */ |
|||
const handleTimeModeChange = (mode: string) => { |
|||
queryParams.dateType = undefined; |
|||
queryParams.dateSearch = undefined; |
|||
queryParams.beginTime = undefined; |
|||
queryParams.endTime = undefined; |
|||
if (mode === 'year') { |
|||
queryParams.dateType = 3; |
|||
} else if (mode === 'quarter') { |
|||
queryParams.dateType = 2; |
|||
} else if (mode === 'month') { |
|||
queryParams.dateType = 1; |
|||
} |
|||
}; |
|||
|
|||
/** 搜索 */ |
|||
const handleQuery = () => { |
|||
queryParams.pageNum = 1; |
|||
getList(); |
|||
getStats(); |
|||
}; |
|||
|
|||
/** 重置 */ |
|||
const resetQuery = () => { |
|||
Object.assign(queryParams, { |
|||
pageNum: 1, |
|||
pageSize: 20, |
|||
bureauDeptName: undefined, |
|||
lawEnforcer: undefined, |
|||
enterpriseName: undefined, |
|||
lawEnforcementItemText: undefined, |
|||
hasUnion: undefined, |
|||
hasAuto: undefined, |
|||
entrustDeptName: undefined, |
|||
lawEnforceCategory: undefined, |
|||
isComplaint: undefined, |
|||
enforce: undefined, |
|||
checkCss: undefined, |
|||
district: undefined, |
|||
street: undefined, |
|||
checkCategory: undefined, |
|||
dateType: 3, |
|||
dateSearch: undefined, |
|||
beginTime: undefined, |
|||
endTime: undefined |
|||
}); |
|||
timeMode.value = 'year'; |
|||
streetOptions.value = []; |
|||
handleQuery(); |
|||
}; |
|||
|
|||
/** 查询列表 */ |
|||
const getList = async () => { |
|||
loading.value = true; |
|||
try { |
|||
const res = await getRegistrationList(queryParams); |
|||
tableData.value = res.rows || []; |
|||
total.value = res.total || 0; |
|||
} finally { |
|||
loading.value = false; |
|||
} |
|||
}; |
|||
|
|||
/** 查询统计指标 */ |
|||
const getStats = async () => { |
|||
try { |
|||
const res = await getStatistics(queryParams); |
|||
if (res.data) { |
|||
stats.value = res.data; |
|||
} |
|||
} catch (e) {} |
|||
}; |
|||
|
|||
/** 导出 */ |
|||
const handleExport = async () => { |
|||
try { |
|||
const res = await exportRegistrationList(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 formatDate = (row: any) => { |
|||
if (!row.enforcementTime) return ''; |
|||
const d = new Date(row.enforcementTime); |
|||
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`; |
|||
}; |
|||
</script> |
|||
|
|||
<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-table v-loading="loading" :data="tableData" border stripe style="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="执法人员" min-width="120"> |
|||
<template #default="{ row }"> |
|||
{{ row.lawEnforcer }}{{ row.lawEnforcerTwo ? ',' + row.lawEnforcerTwo : '' }} |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="执法事项" prop="lawEnforcementItemText" min-width="150" show-overflow-tooltip /> |
|||
<el-table-column label="执法时间" width="110" align="center"> |
|||
<template #default="{ row }">{{ formatDate(row) }}</template> |
|||
</el-table-column> |
|||
<el-table-column label="联合执法" width="90" align="center"> |
|||
<template #default="{ row }"> |
|||
<el-tag v-if="row.hasUnion === 1 && row.unionNo" type="success" size="small">是</el-tag> |
|||
<span v-else>否</span> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="委托执法" width="90" align="center"> |
|||
<template #default="{ row }"> |
|||
<el-tag v-if="row.hasAuto === 1" type="warning" size="small">是</el-tag> |
|||
<span v-else>否</span> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="执法类别" prop="lawEnforceCategory" width="100" align="center"> |
|||
<template #default="{ row }"> |
|||
{{ lawEnforceCategoryOptions.find(d => d.dictValue === row.lawEnforceCategory)?.dictLabel || row.lawEnforceCategory || '-' }} |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="投诉" width="70" align="center"> |
|||
<template #default="{ row }"> |
|||
<el-tag v-if="row.isComplaint === 1" type="danger" size="small">是</el-tag> |
|||
<span v-else>否</span> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="委托单位" prop="entrustDeptName" width="130" show-overflow-tooltip /> |
|||
</el-table> |
|||
|
|||
<!-- 分页 --> |
|||
<el-pagination |
|||
v-model:current-page="queryParams.pageNum" |
|||
v-model:page-size="queryParams.pageSize" |
|||
:page-sizes="[10, 20, 50, 100]" |
|||
:total="total" |
|||
layout="total, sizes, prev, pager, next, jumper" |
|||
@size-change="getList" |
|||
@current-change="getList" |
|||
class="mt-4" |
|||
/> |
|||
</div> |
|||
</template> |
|||
|
|||
<style scoped lang="scss"> |
|||
.stats-row { |
|||
margin-bottom: 16px; |
|||
} |
|||
.stat-card { |
|||
text-align: center; |
|||
.stat-label { |
|||
font-size: 14px; |
|||
color: #909399; |
|||
margin-bottom: 8px; |
|||
} |
|||
.stat-value { |
|||
font-size: 24px; |
|||
font-weight: bold; |
|||
color: #303133; |
|||
.stat-percent { |
|||
font-size: 13px; |
|||
color: #909399; |
|||
font-weight: normal; |
|||
margin-left: 8px; |
|||
} |
|||
} |
|||
} |
|||
.search-form { |
|||
margin-bottom: 16px; |
|||
} |
|||
.mt-4 { |
|||
margin-top: 16px; |
|||
} |
|||
</style> |
|||
Loading…
Reference in new issue