Browse Source

监督端-查询统计

dev-V1.0.1
庄锐波 5 months ago
parent
commit
9aaba6fe93
  1. 71
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/web/supervision/report/RegistrationStatisticsController.java
  2. 5
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/report/RegistrationAnalysisVo.java
  3. 38
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/report/RegistrationStatisticsAnalysisDetailVo.java
  4. 57
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/report/RegistrationStatisticsAnalysisExportVo.java
  5. 18
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/mapper/EnforcementRegistrationMapper.java
  6. 33
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/report/IEnforcementRegistrationStatisticsService.java
  7. 73
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/report/impl/EnforcementRegistrationStatisticsServiceImpl.java
  8. 155
      zdxt-modules/zdxt-enforcement-code/src/main/resources/mapper/enforcementcode/EnforcementRegistrationReportMapper.xml
  9. 80
      zdxt-web-client/zdxt-admin-plus-ui/src/api/efcode/report/registration-statistics.ts
  10. 367
      zdxt-web-client/zdxt-admin-plus-ui/src/views/efcode/report/registration_statistics/index.vue

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

@ -0,0 +1,71 @@
package com.zdxt.enforcementcode.controller.web.supervision.report;
import com.zdxt.enforcementcode.domain.bo.EnforcementRegistrationBo;
import com.zdxt.enforcementcode.domain.vo.report.RegistrationAnalysisVo;
import com.zdxt.enforcementcode.domain.vo.report.RegistrationStatisticsAnalysisDetailVo;
import com.zdxt.enforcementcode.domain.vo.report.RegistrationStatisticsAnalysisExportVo;
import com.zdxt.enforcementcode.report.IEnforcementRegistrationStatisticsService;
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-statistics")
public class RegistrationStatisticsController {
private final IEnforcementRegistrationStatisticsService statisticsService;
/**
* 执法分析列表(分页,V2排序)
*/
@GetMapping("/list")
public TableDataInfo<RegistrationAnalysisVo> list(EnforcementRegistrationBo bo, PageQuery pageQuery) {
return statisticsService.list(bo, pageQuery);
}
/**
* 执法分析详情(指定企业的执法明细聚合)
*/
@GetMapping("/detail")
public R<List<RegistrationStatisticsAnalysisDetailVo>> detail(EnforcementRegistrationBo bo) {
return R.ok(statisticsService.detail(bo));
}
/**
* 执法分析导出
*/
@PostMapping("/exportList")
public void exportList(EnforcementRegistrationBo bo, HttpServletResponse response) {
try {
List<RegistrationStatisticsAnalysisExportVo> list = statisticsService.exportList(bo);
ExcelUtil.exportExcel(list, "执法分析", RegistrationStatisticsAnalysisExportVo.class, response);
} catch (Exception e) {
log.error("导出执法分析Excel异常", e);
response.reset();
response.setContentType("application/json; charset=utf-8");
try {
response.getWriter().write("{\"code\":500,\"msg\":\"导出失败: " + e.getMessage() + "\"}");
} catch (Exception ignored) {
}
}
}
}

5
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/report/RegistrationAnalysisVo.java

@ -35,6 +35,11 @@ public class RegistrationAnalysisVo implements Serializable {
@ExcelProperty(value = "企业名称")
private String enterpriseName;
/**
* 企业统一信用代码(用于详情查询,不导出)
*/
private String enterpriseNumber;
/**
* 执法单位
*/

38
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/report/RegistrationStatisticsAnalysisDetailVo.java

@ -0,0 +1,38 @@
package com.zdxt.enforcementcode.domain.vo.report;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 执法分析-详情弹窗VO(指定企业的执法明细聚合)
*
* @author zdxt
*/
@Data
public class RegistrationStatisticsAnalysisDetailVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 执法单位
*/
private String bureauDeptName;
/**
* 执法人员
*/
private String lawEnforcer;
/**
* 执法事项
*/
private String lawEnforcementItemText;
/**
* 数量(同一执法单位+人员+事项的次数)
*/
private Integer total;
}

57
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/report/RegistrationStatisticsAnalysisExportVo.java

@ -0,0 +1,57 @@
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;
/**
* 执法分析-导出Excel VO(12列,对齐旧系统导出格式)
*
* @author zdxt
*/
@Data
@ExcelIgnoreUnannotated
public class RegistrationStatisticsAnalysisExportVo 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;
@ExcelProperty(value = "2次及以下")
private String level2;
@ExcelProperty(value = "3至4次(黄色)")
private String level34;
@ExcelProperty(value = "5至6次(橙色)")
private String level56;
@ExcelProperty(value = "7次及以上(红色)")
private String level7plus;
}

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

@ -11,6 +11,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.RegistrationStatisticsAnalysisDetailVo;
import com.zdxt.enforcementcode.domain.vo.report.RegistrationStatisticsVo;
import org.apache.ibatis.annotations.Param;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
@ -142,4 +143,21 @@ public interface EnforcementRegistrationMapper extends BaseMapperPlus<Enforcemen
List<RegistrationAnalysisVo> selectReportAnalysisList(@Param("bo") EnforcementRegistrationBo bo,
@Param("dataFilter") String dataFilter);
// ======================== 执法分析V2(独立菜单页,SQL JOIN排序) ========================
/**
* 执法分析-分页列表V2(按企业聚合统计,SQL JOIN字典表排序)
*/
@EfcodeDataFilter
IPage<RegistrationAnalysisVo> selectAnalysisPageV2(@Param("page") Page<RegistrationAnalysisVo> page,
@Param("bo") EnforcementRegistrationBo bo,
@Param("dataFilter") String dataFilter);
/**
* 执法分析-详情(指定企业的执法明细聚合)
*/
@EfcodeDataFilter
List<RegistrationStatisticsAnalysisDetailVo> selectAnalysisDetail(@Param("bo") EnforcementRegistrationBo bo,
@Param("dataFilter") String dataFilter);
}

33
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/report/IEnforcementRegistrationStatisticsService.java

@ -0,0 +1,33 @@
package com.zdxt.enforcementcode.report;
import com.zdxt.enforcementcode.domain.bo.EnforcementRegistrationBo;
import com.zdxt.enforcementcode.domain.vo.report.RegistrationAnalysisVo;
import com.zdxt.enforcementcode.domain.vo.report.RegistrationStatisticsAnalysisDetailVo;
import com.zdxt.enforcementcode.domain.vo.report.RegistrationStatisticsAnalysisExportVo;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import java.util.List;
/**
* 执法分析服务接口(独立菜单页,按企业维度聚合统计执法次数)
*
* @author zdxt
*/
public interface IEnforcementRegistrationStatisticsService {
/**
* 执法分析列表(分页,V2排序)
*/
TableDataInfo<RegistrationAnalysisVo> list(EnforcementRegistrationBo bo, PageQuery pageQuery);
/**
* 执法分析详情(指定企业的逐条执法记录聚合)
*/
List<RegistrationStatisticsAnalysisDetailVo> detail(EnforcementRegistrationBo bo);
/**
* 执法分析导出(全量,V2排序)
*/
List<RegistrationStatisticsAnalysisExportVo> exportList(EnforcementRegistrationBo bo);
}

73
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/report/impl/EnforcementRegistrationStatisticsServiceImpl.java

@ -0,0 +1,73 @@
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.domain.bo.EnforcementRegistrationBo;
import com.zdxt.enforcementcode.domain.vo.report.RegistrationAnalysisVo;
import com.zdxt.enforcementcode.domain.vo.report.RegistrationStatisticsAnalysisDetailVo;
import com.zdxt.enforcementcode.domain.vo.report.RegistrationStatisticsAnalysisExportVo;
import com.zdxt.enforcementcode.mapper.EnforcementRegistrationMapper;
import com.zdxt.enforcementcode.report.IEnforcementRegistrationStatisticsService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
/**
* 执法分析服务实现(独立菜单页,按企业维度聚合统计执法次数,V2排序)
*
* @author zdxt
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class EnforcementRegistrationStatisticsServiceImpl implements IEnforcementRegistrationStatisticsService {
private final EnforcementRegistrationMapper registrationMapper;
@Override
public TableDataInfo<RegistrationAnalysisVo> list(EnforcementRegistrationBo bo, PageQuery pageQuery) {
Page<RegistrationAnalysisVo> page = pageQuery.build();
IPage<RegistrationAnalysisVo> result = registrationMapper.selectAnalysisPageV2(page, bo, null);
return TableDataInfo.build(result);
}
@Override
public List<RegistrationStatisticsAnalysisDetailVo> detail(EnforcementRegistrationBo bo) {
return registrationMapper.selectAnalysisDetail(bo, null);
}
@Override
public List<RegistrationStatisticsAnalysisExportVo> exportList(EnforcementRegistrationBo bo) {
// 全量加载(不分页)
Page<RegistrationAnalysisVo> page = new Page<>(1, Integer.MAX_VALUE, false);
IPage<RegistrationAnalysisVo> result = registrationMapper.selectAnalysisPageV2(page, bo, null);
List<RegistrationAnalysisVo> records = result.getRecords();
// 转换为导出VO(含颜色分级列)
return records.stream().map(vo -> {
RegistrationStatisticsAnalysisExportVo exportVo = new RegistrationStatisticsAnalysisExportVo();
exportVo.setDistrict(vo.getDistrict());
exportVo.setStreet(vo.getStreet());
exportVo.setEnterpriseName(vo.getEnterpriseName());
exportVo.setBureauDeptName(vo.getBureauDeptName());
exportVo.setLawEnforcer(vo.getLawEnforcer());
exportVo.setLawEnforcementItemText(vo.getLawEnforcementItemText());
exportVo.setEnforcementTime(vo.getEnforcementTime());
exportVo.setTotalCount(vo.getTotalCount());
// 颜色分级列
int count = vo.getTotalCount() != null ? vo.getTotalCount() : 0;
exportVo.setLevel2(count <= 2 ? String.valueOf(count) : "-");
exportVo.setLevel34(count >= 3 && count <= 4 ? String.valueOf(count) : "-");
exportVo.setLevel56(count >= 5 && count <= 6 ? String.valueOf(count) : "-");
exportVo.setLevel7plus(count >= 7 ? String.valueOf(count) : "-");
return exportVo;
}).collect(Collectors.toList());
}
}

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

@ -364,4 +364,159 @@
ORDER BY t.num DESC
</select>
<!-- 执法分析V2(按企业聚合,SQL JOIN字典表按dict_sort排序) -->
<select id="selectAnalysisPageV2" resultType="com.zdxt.enforcementcode.domain.vo.report.RegistrationAnalysisVo">
SELECT
t.district,
t.street,
t.enterprise_name AS enterpriseName,
t.enterprise_number AS enterpriseNumber,
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
FROM (
SELECT
er.district, er.street, er.enterprise_name, er.enterprise_number,
er.bureau_dept_name, er.law_enforcer, er.law_enforcer_two,
er.law_enforcement_item_text,
MAX(er.enforcement_time) AS enforcement_time,
COUNT(er.enterprise_number) AS num
FROM enforcement_registration er
<where>
AND er.is_submit = 1
AND er.is_delete = 0
AND er.bureau_dept_name is not null
AND er.law_enforcement_id is not null
AND er.enterprise_name is not null
AND er.enterprise_number is not null AND er.enterprise_number != ''
AND er.law_enforcer is not null
AND er.law_enforcer_two is not null
AND er.is_complaint is not null
AND er.check_category is not null
AND er.bureau_dept_id is not null
AND (er.status = 0 OR er.status IS NULL)
<if test="bo.bureauDeptName != null and bo.bureauDeptName != ''">
AND er.bureau_dept_name LIKE CONCAT('%', #{bo.bureauDeptName}, '%')
</if>
<if test="bo.lawEnforcer != null and bo.lawEnforcer != ''">
AND (er.law_enforcer LIKE CONCAT('%', #{bo.lawEnforcer}, '%')
OR er.law_enforcer_two LIKE CONCAT('%', #{bo.lawEnforcer}, '%'))
</if>
<if test="bo.enterpriseName != null and bo.enterpriseName != ''">
AND er.enterprise_name LIKE CONCAT('%', #{bo.enterpriseName}, '%')
</if>
<if test="bo.lawEnforcementItemText != null and bo.lawEnforcementItemText != ''">
AND er.law_enforcement_item_text LIKE CONCAT('%', #{bo.lawEnforcementItemText}, '%')
</if>
<if test="bo.district != null and bo.district != ''">
AND er.district = #{bo.district}
</if>
<if test="bo.street != null and bo.street != ''">
AND er.street = #{bo.street}
</if>
<if test="bo.dateType != null and bo.dateType == 1">
AND DATE_FORMAT(er.enforcement_time, '%Y-%m') = DATE_FORMAT(CURDATE(), '%Y-%m')
</if>
<if test="bo.dateType != null and bo.dateType == 2">
AND DATE_FORMAT(er.enforcement_time, '%Y-%m') &gt;= 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(er.enforcement_time, '%Y-%m') &lt;= 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(er.enforcement_time, '%Y') = DATE_FORMAT(CURDATE(), '%Y')
</if>
<if test="bo.dateSearch != null and bo.dateSearch != ''">
AND DATE_FORMAT(er.enforcement_time, '%Y-%m') = #{bo.dateSearch}
</if>
<if test="bo.beginTime != null and bo.beginTime != ''">
AND DATE_FORMAT(er.enforcement_time, '%Y-%m-%d') &gt;= #{bo.beginTime}
</if>
<if test="bo.endTime != null and bo.endTime != ''">
AND DATE_FORMAT(er.enforcement_time, '%Y-%m-%d') &lt;= #{bo.endTime}
</if>
<if test="dataFilter != null and dataFilter != ''">
${dataFilter}
</if>
</where>
GROUP BY er.enterprise_number
) t
LEFT JOIN sys_dict_data area
ON area.dict_type = 'address_type'
AND area.status = '0'
AND area.dict_label = t.district
LEFT JOIN sys_dict_data str
ON str.dict_type = area.dict_value
AND str.status = '0'
AND str.dict_label = t.street
<where>
<if test="bo.numLeft != null and bo.numLeft != ''">
AND t.num &gt;= #{bo.numLeft}
</if>
<if test="bo.numRight != null and bo.numRight != ''">
AND t.num &lt;= #{bo.numRight}
</if>
<if test="bo.num != null and bo.num != ''">
AND t.num &gt;= #{bo.num}
</if>
</where>
ORDER BY IFNULL(area.dict_sort, 9999), IFNULL(str.dict_sort, 9999), t.enforcement_time ASC
</select>
<!-- 执法分析-详情(指定企业的执法明细聚合) -->
<select id="selectAnalysisDetail" resultType="com.zdxt.enforcementcode.domain.vo.report.RegistrationStatisticsAnalysisDetailVo">
SELECT
bureau_dept_name AS bureauDeptName,
CONCAT_WS(',', law_enforcer, NULLIF(law_enforcer_two, '')) AS lawEnforcer,
law_enforcement_item_text AS lawEnforcementItemText,
COUNT(*) AS total
FROM enforcement_registration
<where>
AND is_submit = 1
AND is_delete = 0
AND (status = 0 OR status IS NULL)
AND enterprise_number = #{bo.enterpriseNumber}
<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') &gt;= 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') &lt;= 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') &gt;= #{bo.beginTime}
</if>
<if test="bo.endTime != null and bo.endTime != ''">
AND DATE_FORMAT(enforcement_time, '%Y-%m-%d') &lt;= #{bo.endTime}
</if>
<if test="dataFilter != null and dataFilter != ''">
${dataFilter}
</if>
</where>
GROUP BY bureau_dept_name, law_enforcer, law_enforcer_two, law_enforcement_item_text
ORDER BY total DESC
</select>
</mapper>

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

@ -0,0 +1,80 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
const BASE_URL = '/web/supervision/report/registration-statistics';
/** 查询参数 */
export interface AnalysisQuery {
pageNum?: number;
pageSize?: number;
bureauDeptName?: string;
lawEnforcer?: string;
enterpriseName?: string;
lawEnforcementItemText?: string;
district?: string;
street?: string;
dateType?: number;
dateSearch?: string;
beginTime?: string;
endTime?: string;
numLeft?: string;
numRight?: string;
num?: string;
enterpriseNumber?: string;
}
/** 执法分析行VO */
export interface RegistrationAnalysisVo {
district: string;
street: string;
enterpriseName: string;
enterpriseNumber: string;
bureauDeptName: string;
lawEnforcer: string;
lawEnforcementItemText: string;
enforcementTime: string;
totalCount: number;
colorLevel: string;
}
/** 详情VO */
export interface AnalysisDetailVo {
bureauDeptName: string;
lawEnforcer: string;
lawEnforcementItemText: string;
total: number;
}
/**
* 执法分析列表(分页)
*/
export const getAnalysisList = (params: AnalysisQuery): AxiosPromise => {
return request({
url: `${BASE_URL}/list`,
method: 'get',
params
});
};
/**
* 执法分析详情(指定企业执法明细)
*/
export const getAnalysisDetail = (params: AnalysisQuery): AxiosPromise => {
return request({
url: `${BASE_URL}/detail`,
method: 'get',
params
});
};
/**
* 执法分析导出
*/
export const exportAnalysisList = (params: AnalysisQuery): AxiosPromise<Blob> => {
return request({
url: `${BASE_URL}/exportList`,
method: 'post',
params,
responseType: 'blob'
});
};

367
zdxt-web-client/zdxt-admin-plus-ui/src/views/efcode/report/registration_statistics/index.vue

@ -0,0 +1,367 @@
<script setup lang="ts">
import { ref, reactive, onMounted, onUnmounted, nextTick } from 'vue';
import { ElMessage } from 'element-plus';
import {
getAnalysisList, getAnalysisDetail, exportAnalysisList
} from '@/api/efcode/report/registration-statistics';
import type { AnalysisQuery, RegistrationAnalysisVo, AnalysisDetailVo } from '@/api/efcode/report/registration-statistics';
import { getDicts } from '@/api/system/dict/data';
// 查询参数
const queryParams = reactive<AnalysisQuery>({
pageNum: 1,
pageSize: 20,
bureauDeptName: undefined,
lawEnforcer: undefined,
enterpriseName: undefined,
lawEnforcementItemText: undefined,
district: undefined,
street: undefined,
dateType: undefined,
dateSearch: undefined,
beginTime: undefined,
endTime: undefined
});
const loading = ref(false);
const tableData = ref<RegistrationAnalysisVo[]>([]);
const total = ref(0);
const tableHeight = ref<number | undefined>(undefined);
const tableRef = ref();
// 字典
const addressTypeOptions = ref<any[]>([]);
const streetOptions = ref<any[]>([]);
// 时段模式
const timeMode = ref('year');
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));
})();
// 详情弹窗
const detailDialogVisible = ref(false);
const detailLoading = ref(false);
const detailData = ref<AnalysisDetailVo[]>([]);
const detailEnterpriseName = ref('');
onMounted(() => {
loadDicts();
applyYearFilter();
handleQuery();
nextTick(() => calcTableHeight());
window.addEventListener('resize', calcTableHeight);
});
onUnmounted(() => {
window.removeEventListener('resize', calcTableHeight);
});
/** 计算表格高度 */
const calcTableHeight = () => {
const el = tableRef.value?.$el as HTMLElement;
if (!el) return;
const rect = el.getBoundingClientRect();
tableHeight.value = window.innerHeight - rect.top - 102;
};
/** 加载字典 */
const loadDicts = async () => {
try {
const res = await getDicts('address_type');
addressTypeOptions.value = res.data || [];
} catch (e) {
console.error('加载字典失败', e);
}
};
/** 区域变更时加载街道 */
const handleDistrictChange = async (val: string) => {
queryParams.street = undefined;
streetOptions.value = [];
if (val) {
try {
const matched = addressTypeOptions.value.find((item: any) => item.dictLabel === val);
if (matched) {
const res = await getDicts(matched.dictValue);
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') {
applyYearFilter();
} else if (mode === 'quarter') {
applyQuarterFilter();
} else if (mode === 'month') {
const now = new Date();
selectedMonth.value = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`;
applyMonthFilter();
}
};
/** 年度筛选 */
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;
getList();
};
/** 重置 */
const resetQuery = () => {
Object.assign(queryParams, {
pageNum: 1,
pageSize: 20,
bureauDeptName: undefined,
lawEnforcer: undefined,
enterpriseName: undefined,
lawEnforcementItemText: undefined,
district: undefined,
street: undefined,
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();
};
/** 查询列表 */
const getList = async () => {
loading.value = true;
try {
const res = await getAnalysisList(queryParams);
tableData.value = res.rows || [];
total.value = res.total || 0;
} finally {
loading.value = false;
}
};
/** 导出 */
const handleExport = async () => {
try {
const res = await exportAnalysisList(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 handleShowDetail = async (row: RegistrationAnalysisVo) => {
detailEnterpriseName.value = row.enterpriseName;
detailDialogVisible.value = true;
detailLoading.value = true;
try {
const params: AnalysisQuery = {
enterpriseNumber: row.enterpriseNumber,
dateType: queryParams.dateType,
dateSearch: queryParams.dateSearch,
beginTime: queryParams.beginTime,
endTime: queryParams.endTime
};
const res = await getAnalysisDetail(params);
detailData.value = res.data || [];
} catch (e) {
ElMessage.error('获取详情失败');
detailData.value = [];
} finally {
detailLoading.value = false;
}
};
/** 颜色分级列样式 */
const getLevelCellStyle = (level: string) => {
const colorMap: Record<string, string> = {
yellow: '#e6a23c',
orange: '#f56c6c',
red: '#c00'
};
return colorMap[level] || '';
};
</script>
<template>
<div class="p-2">
<!-- 搜索区域 -->
<div class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="执法单位" prop="bureauDeptName">
<el-input v-model="queryParams.bureauDeptName" placeholder="请输入执法单位" clearable @keyup.enter="handleQuery" style="width: 120px" />
</el-form-item>
<el-form-item label="执法人员" prop="lawEnforcer">
<el-input v-model="queryParams.lawEnforcer" placeholder="请输入执法人员" clearable @keyup.enter="handleQuery" style="width: 90px" />
</el-form-item>
<el-form-item label="企业名称" prop="enterpriseName">
<el-input v-model="queryParams.enterpriseName" placeholder="请输入企业名称" clearable @keyup.enter="handleQuery" style="width: 120px" />
</el-form-item>
<el-form-item label="执法事项" prop="lawEnforcementItemText">
<el-input v-model="queryParams.lawEnforcementItemText" placeholder="请输入执法事项" clearable @keyup.enter="handleQuery" style="width: 120px" />
</el-form-item>
<el-form-item label="时段">
<el-select v-model="timeMode" style="width: 90px" @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-select v-if="timeMode === 'year' || timeMode === 'quarter'" v-model="selectedYear" style="width: 100px; margin-left: 4px" @change="handleYearChange">
<el-option v-for="y in yearOptions" :key="y" :label="y + '年'" :value="y" />
</el-select>
<el-select v-if="timeMode === 'quarter'" 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-date-picker v-if="timeMode === 'month'" v-model="selectedMonth" type="month" placeholder="选择月份" value-format="YYYY-MM" style="width: 150px; margin-left: 4px" @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" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
<el-button type="warning" icon="Download" @click="handleExport">导出</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
<!-- 表格区域 -->
<el-card shadow="hover">
<el-table ref="tableRef" v-loading="loading" :data="tableData" :height="tableHeight" border stripe>
<el-table-column label="区域" prop="district" width="100" align="center" />
<el-table-column label="街道" prop="street" width="120" align="center" />
<el-table-column label="企业名称" prop="enterpriseName" min-width="200" :show-overflow-tooltip="true" />
<el-table-column label="执法单位" prop="bureauDeptName" min-width="150" :show-overflow-tooltip="true" />
<el-table-column label="执法人员" prop="lawEnforcer" min-width="120" :show-overflow-tooltip="true" />
<el-table-column label="执法事项" prop="lawEnforcementItemText" min-width="150" :show-overflow-tooltip="true" />
<el-table-column label="执法时间" prop="enforcementTime" width="110" align="center" />
<el-table-column label="执法次数" width="100" align="center">
<template #default="{ row }">
<el-link type="success" :underline="false" @click="handleShowDetail(row)">
{{ row.totalCount }}
</el-link>
</template>
</el-table-column>
<el-table-column label="2次及以下" width="100" align="center">
<template #default="{ row }">
{{ row.totalCount <= 2 ? row.totalCount : '-' }}
</template>
</el-table-column>
<el-table-column label="3至4次" width="90" align="center">
<template #default="{ row }">
<span v-if="row.totalCount >= 3 && row.totalCount <= 4" style="color: #e6a23c; font-weight: bold;">{{ row.totalCount }}</span>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="5至6次" width="90" align="center">
<template #default="{ row }">
<span v-if="row.totalCount >= 5 && row.totalCount <= 6" style="color: #f56c6c; font-weight: bold;">{{ row.totalCount }}</span>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="7次及以上" width="100" align="center">
<template #default="{ row }">
<span v-if="row.totalCount >= 7" style="color: #c00; font-weight: bold;">{{ row.totalCount }}</span>
<span v-else>-</span>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
</el-card>
<!-- 详情弹窗 -->
<el-dialog v-model="detailDialogVisible" :title="`执法详情 - ${detailEnterpriseName}`" width="700px" append-to-body destroy-on-close>
<el-table v-loading="detailLoading" :data="detailData" border stripe max-height="400">
<el-table-column label="执法单位" prop="bureauDeptName" min-width="150" :show-overflow-tooltip="true" />
<el-table-column label="执法人员" prop="lawEnforcer" min-width="120" :show-overflow-tooltip="true" />
<el-table-column label="执法事项" prop="lawEnforcementItemText" min-width="150" :show-overflow-tooltip="true" />
<el-table-column label="数量" prop="total" width="80" align="center" />
</el-table>
</el-dialog>
</div>
</template>
<style scoped lang="scss">
</style>
Loading…
Cancel
Save