Browse Source

移动监督端

dev-V1.10.0
wangmingyong@163.com 2 hours ago
parent
commit
3b0b9840da
  1. 196
      zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/enforce/enforcementRecord.vue

196
zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/enforce/enforcementRecord.vue

@ -22,15 +22,15 @@
<!-- 第一行:三列统计 -->
<div class="stats-row three-col">
<div class="stat-item">
<div class="stat-value">443.9216万</div>
<div class="stat-value">{{ formatEnterpriseTotal(overviewData.qyzs) }}</div>
<div class="stat-label">企业总数</div>
</div>
<div class="stat-item">
<div class="stat-value">49225次</div>
<div class="stat-value">{{ lawStats.ddzf ?? '-' }}次</div>
<div class="stat-label">单独执法</div>
</div>
<div class="stat-item">
<div class="stat-value">1277次</div>
<div class="stat-value">{{ lawStats.lhzf ?? '-' }}次</div>
<div class="stat-label">联合执法</div>
</div>
</div>
@ -38,11 +38,11 @@
<!-- 第二行:两列统计 -->
<div class="stats-row two-col">
<div class="stat-item">
<div class="stat-value">33548家</div>
<div class="stat-value">{{ lawStats.bzfqy ?? '-' }}家</div>
<div class="stat-label">被执法企业</div>
</div>
<div class="stat-item">
<div class="stat-value">6%</div>
<div class="stat-value">{{ formatReduceRate(lawStats.rqyjl) }}</div>
<div class="stat-label">入企压减率</div>
</div>
</div>
@ -73,11 +73,11 @@
<!-- 执法力量 -->
<div v-if="overviewTab === 1" class="tab-content-cards">
<div class="large-card">
<div class="large-card-value">503家</div>
<div class="large-card-value">{{ overviewData.zfdws ?? '-' }}<span class="large-card-unit">家</span></div>
<div class="large-card-label">执法单位数</div>
</div>
<div class="large-card">
<div class="large-card-value">16359人</div>
<div class="large-card-value">{{ overviewData.zfrys ?? '-' }}<span class="large-card-unit">人</span></div>
<div class="large-card-label">执法人员数</div>
</div>
</div>
@ -85,7 +85,7 @@
<!-- 执法频次 -->
<div v-if="overviewTab === 2" class="tab-content-cards">
<div class="large-card">
<div class="large-card-value">3<span class="large-card-unit">次/周</span></div>
<div class="large-card-value">{{ overviewData.pjjcpc ?? '-' }}<span class="large-card-unit">次/周</span></div>
<div class="large-card-label">涉企检查频次</div>
</div>
<div class="large-card">
@ -124,23 +124,23 @@
<div class="section-title">执法监管</div>
<div class="supervision-grid">
<div class="supervision-item">
<div class="sv-value">336<span class="sv-unit">条</span></div>
<div class="sv-value">{{ overviewData.jhzs ?? '-' }}<span class="sv-unit">条</span></div>
<div class="sv-label">计划总数</div>
</div>
<div class="supervision-item">
<div class="sv-value">1473<span class="sv-unit">条</span></div>
<div class="sv-value">{{ overviewData.ygzs ?? '-' }}<span class="sv-unit">条</span></div>
<div class="sv-label">预告总数</div>
</div>
<div class="supervision-item">
<div class="sv-value">50503<span class="sv-unit">条</span></div>
<div class="sv-value">{{ overviewData.sqzfzs ?? '-' }}<span class="sv-unit">条</span></div>
<div class="sv-label">涉企执法总数</div>
</div>
<div class="supervision-item">
<div class="sv-value">53<span class="sv-unit">条</span></div>
<div class="sv-value">{{ overviewData.jcpjzs ?? '-' }}<span class="sv-unit">条</span></div>
<div class="sv-label">检查反馈总数</div>
</div>
<div class="supervision-item">
<div class="sv-value">59<span class="sv-unit">条</span></div>
<div class="sv-value">{{ overviewData.jctszs ?? '-' }}<span class="sv-unit">条</span></div>
<div class="sv-label">检查投诉总数</div>
</div>
<!-- <div class="supervision-item">
@ -157,11 +157,11 @@
<!-- 第一行:两列统计 -->
<div class="warning-grid">
<div class="warning-item">
<div class="sv-value">45<span class="sv-unit">条</span></div>
<div class="sv-value">{{ overviewData.fxxyyj ?? '-' }}<span class="sv-unit">条</span></div>
<div class="sv-label">风险信用预警</div>
</div>
<div class="warning-item">
<div class="sv-value">65<span class="sv-unit">条</span></div>
<div class="sv-value">{{ overviewData.gpjcyj ?? '-' }}<span class="sv-unit">条</span></div>
<div class="sv-label">高额检查预警</div>
</div>
</div>
@ -198,7 +198,7 @@
<!-- 时间轴作为最后一行 -->
<tr class="wt-timeline-row">
<td class="wt-name"></td>
<td v-for="(item, idx) in warningTimeline" :key="idx" class="wt-time-cell">{{ item }}
<td v-for="(item, idx) in currentTimeline" :key="idx" class="wt-time-cell">{{ item }}
</td>
</tr>
</tbody>
@ -212,6 +212,11 @@
<script setup name="enforcementRecord">
import { ref, reactive, computed, watch, nextTick, onMounted, onUnmounted } from 'vue';
import * as echarts from 'echarts';
import {
getOverviewLawEnforcement,
querySituationTrendAreaList,
querySituationTrendDomainList,
} from '@/api/enforcement/supervision';
const activeTab = ref(0);
const overviewTab = ref(0);
@ -222,13 +227,44 @@ const contentRef = ref(null);
const forceChartRef = ref(null);
let forceChartInstance = null;
// 接口数据
const overviewData = ref({});
const lawStats = ref({});
// 执法力度图表数据
const forceChartData = [
{ name: '行政检查', value: 5068, percent: 96.09, color: '#5b8ff9' },
{ name: '行政处罚', value: 152, percent: 2.88, color: '#5ad8a6' },
{ name: '行政强制', value: 5, percent: 0.09, color: '#5d7092' },
{ name: '其他', value: 49, percent: 0.92, color: '#f6bd16' },
];
const forceChartData = ref([]);
const forceTotal = ref(0);
const forceColors = ['#5b8ff9', '#5ad8a6', '#5d7092', '#f6bd16', '#e8684a'];
// 企业总数格式化
const formatEnterpriseTotal = (value) => {
if (value === null || value === undefined || value === '') return '-';
const num = Number(value);
if (Number.isNaN(num)) return '-';
if (num >= 10000) return `${(num / 10000).toFixed(4)}万`;
return String(num);
};
// 入企压减率格式化(后端返回 0.06 形式的比例)
const formatReduceRate = (value) => {
if (value === null || value === undefined || value === '') return '-';
const num = Number(value);
if (Number.isNaN(num)) return '-';
return `${(num * 100).toFixed(0)}%`;
};
// 更新执法力度图表数据
const updateForceChartData = () => {
const list = lawStats.value.zfld || [];
const total = list.reduce((sum, item) => sum + (Number(item.number) || 0), 0);
forceTotal.value = total;
forceChartData.value = list.map((item, index) => ({
name: item.name || '-',
value: Number(item.number) || 0,
percent: total ? ((Number(item.number) || 0) / total * 100).toFixed(2) : 0,
color: forceColors[index % forceColors.length],
}));
};
// 执法频次数据
const frequencyData = ref([
@ -271,13 +307,13 @@ const renderForceChart = () => {
label: {
show: true,
position: 'center',
formatter: '{value|5274次}\n{label|合计}',
formatter: `{value|${forceTotal.value}次}\n{label|合计}`,
rich: {
value: { fontSize: 14, fontWeight: '500', color: '#333', lineHeight: 28 },
label: { fontSize: 12, color: '#999', lineHeight: 18 },
},
},
data: forceChartData.map(item => ({ value: item.value, itemStyle: { color: item.color } })),
data: forceChartData.value.map(item => ({ value: item.value, itemStyle: { color: item.color } })),
}],
};
forceChartInstance.setOption(option);
@ -346,6 +382,8 @@ onMounted(() => {
if (container) {
container.addEventListener('scroll', handleScroll, { passive: true });
}
fetchOverview();
fetchWarningTables();
nextTick(() => {
if (overviewTab.value === 0) {
renderForceChart();
@ -361,42 +399,86 @@ onUnmounted(() => {
if (observer) observer.disconnect();
});
// 预警态势表格数据
// 预警态势时间轴
const warningTimeline = ref(['2025Q2', '2025Q3', '2025Q4', '2026Q1']);
// 各区数据
const warningTableData = ref([
{ name: '福田区', statuses: ['一般', '良好', '良好', '优良'] },
{ name: '罗湖区', statuses: ['优良', '良好', '优良', '良好'] },
{ name: '盐田区', statuses: ['一般', '优良', '优良', '良好'] },
{ name: '南山区', statuses: ['一般', '良好', '良好', '良好'] },
{ name: '宝安区', statuses: ['良好', '优良', '优良', '优良'] },
{ name: '龙岗区', statuses: ['一般', '优良', '良好', '优良'] },
{ name: '龙华区', statuses: ['优良', '良好', '优良', '优良'] },
{ name: '坪山区', statuses: ['优良', '优良', '优良', '良好'] },
{ name: '光明区', statuses: ['良好', '良好', '优良', '优良'] },
{ name: '大鹏新区', statuses: ['一般', '良好', '良好', '优良'] },
{ name: '深汕特别合作区', statuses: ['一般', '良好', '优良', '良好'] },
]);
// 预警态势时间轴与表格数据
const areaTimeline = ref([]);
const domainTimeline = ref([]);
const areaTableData = ref([]);
const domainTableData = ref([]);
// 各领域数据
const fieldTableData = ref([
{ name: '应急管理局', statuses: ['一般', '优良', '优良', '优良'] },
{ name: '消防救援', statuses: ['一般', '良好', '优良', '良好'] },
{ name: '住建和建设局', statuses: ['一般', '良好', '良好', '优良'] },
{ name: '生态环境局', statuses: ['一般', '优良', '优良', '优良'] },
{ name: '卫生健康委员会', statuses: ['优良', '良好', '优良', '良好'] },
{ name: '交通运输委员会', statuses: ['一般', '优良', '良好', '优良'] },
{ name: '市场监督管理局', statuses: ['良好', '良好', '优良', '良好'] },
{ name: '城市管理和综合执法局', statuses: ['一般', '优良', '良好', '优良'] },
{ name: '消防救援支队', statuses: ['一般', '良好', '优良', '良好'] },
]);
const mapGrade = (code) => ({ A: '优良', B: '良好', C: '一般' }[code] || '-');
// 根据 Tab 切换数据源
const currentTableData = computed(() => {
return warningTab.value === 0 ? warningTableData.value : fieldTableData.value;
// 按名称聚合,季度作为列
const buildTrendTable = (list, nameKey) => {
const periodMap = new Map();
list.forEach(item => {
periodMap.set(`${item.year}Q${item.quarter}`, { year: Number(item.year), quarter: Number(item.quarter) });
});
const periods = [...periodMap.values()]
.sort((a, b) => a.year - b.year || a.quarter - b.quarter)
.map(item => `${item.year}Q${item.quarter}`);
const rows = [];
const rowMap = new Map();
list.forEach(item => {
const name = item[nameKey] || '-';
if (!rowMap.has(name)) {
const row = { name, statusMap: {} };
rowMap.set(name, row);
rows.push(row);
}
rowMap.get(name).statusMap[`${item.year}Q${item.quarter}`] = mapGrade(item.zhpj);
});
return {
periods,
rows: rows.map(row => ({
name: row.name,
statuses: periods.map(period => row.statusMap[period] || '-'),
})),
};
};
// 移动监督端指标
const fetchOverview = () => {
getOverviewLawEnforcement({}, (res) => {
if (res && res.code === 200 && res.data) {
overviewData.value = res.data.data || {};
lawStats.value = {
ddzf: res.data.ddzf,
lhzf: res.data.lhzf,
bzfqy: res.data.bzfqy,
rqyjl: res.data.rqyjl,
zfld: res.data.zfld || [],
};
updateForceChartData();
nextTick(() => {
if (overviewTab.value === 0) renderForceChart();
});
}
});
};
// 各区/各领域风险态势
const fetchWarningTables = () => {
querySituationTrendAreaList({}, (res) => {
if (res && res.code === 200 && Array.isArray(res.data)) {
const { periods, rows } = buildTrendTable(res.data, 'districtName');
areaTimeline.value = periods;
areaTableData.value = rows;
}
});
querySituationTrendDomainList({}, (res) => {
if (res && res.code === 200 && Array.isArray(res.data)) {
const { periods, rows } = buildTrendTable(res.data, 'lymc');
domainTimeline.value = periods;
domainTableData.value = rows;
}
});
};
// 根据 Tab 切换数据源
const currentTimeline = computed(() => warningTab.value === 0 ? areaTimeline.value : domainTimeline.value);
const currentTableData = computed(() => warningTab.value === 0 ? areaTableData.value : domainTableData.value);
</script>
<style lang="less" scoped>

Loading…
Cancel
Save