Browse Source

接口调试

dev-V1.10.0
wangmingyong@163.com 2 weeks ago
parent
commit
cb12de8ca8
  1. 112
      zdxt-web-client/zdxt-admin-plus-ui/src/views/efcode/trend/high-frequency/components/FreqPane.vue
  2. 80
      zdxt-web-client/zdxt-admin-plus-ui/src/views/efcode/trend/inspection-registration/components/StatPane.vue
  3. 107
      zdxt-web-client/zdxt-admin-plus-ui/src/views/efcode/trend/key-enterprise/index.vue
  4. 85
      zdxt-web-client/zdxt-admin-plus-ui/src/views/efcode/trend/scan-district/index.vue
  5. 103
      zdxt-web-client/zdxt-admin-plus-ui/src/views/efcode/trend/scan-enforcer/index.vue
  6. 198
      zdxt-web-client/zdxt-admin-plus-ui/src/views/efcode/trend/scan-field/index.vue
  7. 64
      zdxt-web-client/zdxt-admin-plus-ui/src/views/efcode/trend/situation-trend/components/SituationPane.vue
  8. 32
      zdxt-web-client/zdxt-admin-plus-ui/src/views/efcode/trend/trend-behavior/components/TrendPane.vue

112
zdxt-web-client/zdxt-admin-plus-ui/src/views/efcode/trend/high-frequency/components/FreqPane.vue

@ -1,7 +1,16 @@
<script setup lang="ts">
import { nextTick, onMounted, onUnmounted, reactive, ref } from 'vue';
import { ElMessage } from 'element-plus';
import { getDicts } from '@/api/system/dict/data';
import { listHighCheckArea, listHighCheckDomain, type HighFrequencyRow } from '@/api/efcode/report/high-frequency';
import {
listHighCheckArea,
listHighCheckDomain,
exportHighCheckArea,
exportHighCheckDomain,
type HighFrequencyRow
} from '@/api/efcode/report/high-frequency';
import { listKeyEnterpriseDetails } from '@/api/efcode/report/key-enterprises';
import { getDomainInfo, type DashboardDomainVo } from '@/api/efcode/dashboard/area-info';
/** 维度:area=各区域 field=各领域 */
const props = defineProps<{
@ -16,7 +25,7 @@ const queryParams = reactive({
district: undefined as string | undefined, // 区域 tab 用
domainName: undefined as string | undefined, // 领域 tab 用
year: String(now.getFullYear()),
month: `${String(now.getFullYear())}-${String(now.getMonth() + 1).padStart(2, '0')}`
month: String(now.getMonth() + 1).padStart(2, '0')
});
/** 年份选项(近 5 年) */
@ -27,14 +36,7 @@ const yearOptions = (() => {
// ======================== 字典 ========================
const areaOptions = ref<any[]>([]);
const fieldOptions = [
{ label: '食品安全', value: 'food' },
{ label: '药品安全', value: 'drug' },
{ label: '特种设备', value: 'special_equipment' },
{ label: '产品质量', value: 'product' },
{ label: '计量', value: 'metrology' },
{ label: '认证认可', value: 'certification' }
];
const fieldOptions = ref<DashboardDomainVo[]>([]);
const loadAreaDict = async () => {
try {
@ -45,6 +47,15 @@ const loadAreaDict = async () => {
}
};
const loadFieldDict = async () => {
try {
const res = await getDomainInfo();
fieldOptions.value = res.data || [];
} catch (e) {
console.error('加载所属领域字典失败', e);
}
};
// ======================== 表格数据 ========================
const loading = ref(false);
const tableData = ref<HighFrequencyRow[]>([]);
@ -63,23 +74,26 @@ const calcTableHeight = () => {
tableHeight.value = window.innerHeight - rect.top - 84;
};
/** 区域接口/导出使用的执法时间(由年份 + 月份拼接为 YYYY-MM-01) */
const buildEnforcementTime = () => `${queryParams.year}-${queryParams.month}-01`;
const getList = async () => {
loading.value = true;
try {
let rows: HighFrequencyRow[] = [];
let totalNum = 0;
if (props.dimension === 'area') {
// 区域接口:district + enforcementTime(直接用 month 的 YYYY-MM 值)
// 区域接口:district + enforcementTime(由独立年月拼接)
const res = await listHighCheckArea({
pageNum: queryParams.pageNum,
pageSize: queryParams.pageSize,
district: queryParams.district,
enforcementTime: queryParams.month
enforcementTime: buildEnforcementTime()
});
rows = (res as any)?.rows || [];
totalNum = (res as any)?.total || 0;
} else {
// 领域接口:domainName + year + month(month 传值和 area 一样,但后端参数名不同)
// 领域接口:domainName + year + month(独立年月字段)
const res = await listHighCheckDomain({
pageNum: queryParams.pageNum,
pageSize: queryParams.pageSize,
@ -109,15 +123,34 @@ const handleReset = () => {
queryParams.district = undefined;
queryParams.domainName = undefined;
queryParams.year = String(new Date().getFullYear());
queryParams.month = `${String(new Date().getFullYear())}-${String(new Date().getMonth() + 1).padStart(2, '0')}`;
queryParams.month = String(new Date().getMonth() + 1).padStart(2, '0');
queryParams.pageNum = 1;
queryParams.pageSize = 20;
getList();
};
/** 导出 */
const handleExport = () => {
// TODO: 接入导出接口后实现文件下载
/** 导出(各区域走 exporthighCheckArea,各领域走 exportHighCheckDomain) */
const handleExport = async () => {
try {
const res =
props.dimension === 'area'
? await exportHighCheckArea({
district: queryParams.district,
enforcementTime: buildEnforcementTime()
})
: await exportHighCheckDomain({ domainName: queryParams.domainName });
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 = props.dimension === 'area' ? '高频检查区域.xlsx' : '高频检查领域.xlsx';
link.click();
window.URL.revokeObjectURL(url);
} catch (e) {
ElMessage.error('导出失败');
}
};
/** 多选 */
@ -132,6 +165,7 @@ interface EnforcementDetailRow {
bureauDeptName: string;
categoryText: string;
lawEnforcer: string;
lawEnforcerTwo?: string;
itemText: string;
}
@ -147,7 +181,15 @@ const getDetailList = async () => {
detailLoading.value = true;
try {
if (!detailEnterprise.value) return;
// TODO: 接入真实详情接口 listHighFrequencyDetail
// 详情接口:enterpriseNumber(企业统一信用代码)+ enforcementTime(YYYY-MM-01)
const res = await listKeyEnterpriseDetails({
pageNum: detailPage.value,
pageSize: detailPageSize.value,
enterpriseNumber: detailEnterprise.value.enterpriseNumber,
enforcementTime: buildEnforcementTime()
});
detailTableData.value = (res as any)?.rows || (Array.isArray(res) ? res : []);
detailTotal.value = (res as any)?.total ?? detailTableData.value.length;
} finally {
detailLoading.value = false;
}
@ -174,7 +216,11 @@ const handleDetailPageChange = (page: number) => {
};
onMounted(() => {
loadAreaDict();
if (props.dimension === 'area') {
loadAreaDict();
} else {
loadFieldDict();
}
getList();
nextTick(() => calcTableHeight());
window.addEventListener('resize', calcTableHeight);
@ -209,7 +255,7 @@ defineExpose({ calcTableHeight });
class="search-select"
clearable
>
<el-option v-for="item in fieldOptions" :key="item.value" :label="item.label" :value="item.label" />
<el-option v-for="item in fieldOptions" :key="item.domainId" :label="item.domainName" :value="item.domainName" />
</el-select>
</el-form-item>
<el-form-item label="年份">
@ -221,7 +267,7 @@ defineExpose({ calcTableHeight });
<el-date-picker
v-model="queryParams.month"
type="month"
value-format="YYYY-MM"
value-format="MM"
class="period-date"
placeholder="选择月份"
/>
@ -252,11 +298,12 @@ defineExpose({ calcTableHeight });
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="序号" width="90" align="center" type="index" />
<el-table-column prop="enterpriseName" label="企业名称" min-width="300" align="center" />
<el-table-column prop="areaName" label="所属区" width="160" align="center" />
<el-table-column prop="streetName" label="所属街道" width="160" align="center" />
<el-table-column prop="district" label="所属区" width="160" align="center" v-if="dimension === 'area'" />
<el-table-column prop="street" label="所属街道" width="160" align="center" v-if="dimension === 'area'" />
<el-table-column prop="domainName" label="所属领域" width="260" align="center" v-if="dimension !== 'area'" />
<el-table-column label="行政执法次数" width="180" align="center">
<template #default="{ row }">
<span v-if="row.enforcementCount !== null && row.enforcementCount !== undefined">{{ row.enforcementCount }}次</span>
<span>{{ dimension === 'area'? row.xzzfcs:row.jccs }}次</span>
</template>
</el-table-column>
<el-table-column label="操作" width="80" align="center" fixed="right">
@ -301,8 +348,8 @@ defineExpose({ calcTableHeight });
v-if="detailEnterprise"
:closable="false"
show-icon
type="warning"
:title="`预警企业 | 所属区:${detailEnterprise.areaName || '-'} | 所属街道:${detailEnterprise.streetName || '-'} | 行政执法次数:${detailEnterprise.enforcementCount ?? 0}次 | 时段:${queryParams.year}年${queryParams.month}`"
:title="`所属区:${detailEnterprise.district || '-'} | 所属街道:${detailEnterprise.street || '-'} | 行政执法次数:${detailEnterprise.xzzfcs ?? 0}次 | 时段:${queryParams.year}年${queryParams.month}`"
type="info"
class="mb-4"
/>
@ -317,9 +364,13 @@ defineExpose({ calcTableHeight });
<el-table-column label="序号" type="index" width="80" align="center" />
<el-table-column label="执法时间" prop="enforcementTime" min-width="180" align="center" />
<el-table-column label="执法单位" prop="bureauDeptName" min-width="260" align="center" />
<el-table-column label="执法类别" prop="categoryText" width="140" align="center" />
<el-table-column label="执法人员" prop="lawEnforcer" width="140" align="center" />
<el-table-column label="执法事项" prop="itemText" min-width="220" align="center" />
<el-table-column label="执法类别" prop="dictLabel" width="140" align="center" />
<el-table-column label="执法人员" width="140" align="center">
<template #default="{ row }">
{{ [row.lawEnforcer, row.lawEnforcerTwo].filter(Boolean).join('、') }}
</template>
</el-table-column>
<el-table-column label="执法事项" prop="lawEnforcementItemText" min-width="220" align="center" />
</el-table>
<el-pagination
@ -389,4 +440,7 @@ defineExpose({ calcTableHeight });
margin-top: 16px;
justify-content: flex-end;
}
.mb-4 {
margin-bottom: 16px;
}
</style>

80
zdxt-web-client/zdxt-admin-plus-ui/src/views/efcode/trend/inspection-registration/components/StatPane.vue

@ -1,7 +1,15 @@
<script setup lang="ts">
import { nextTick, onMounted, onUnmounted, reactive, ref } from 'vue';
import { ElMessage } from 'element-plus';
import { getDicts } from '@/api/system/dict/data';
import { listAreaRegistration, listFieldRegistration, type CheckRegistrationRow } from '@/api/efcode/report/check-registration';
import { getDomainInfo, type DashboardDomainVo } from '@/api/efcode/dashboard/area-info';
import {
listAreaRegistration,
listFieldRegistration,
exportAreaRegistration,
exportDomainRegistration,
type CheckRegistrationRow
} from '@/api/efcode/report/check-registration';
/** 维度:area=各区域 field=各领域 */
const props = defineProps<{
@ -16,7 +24,7 @@ const queryParams = reactive({
areaName: undefined as string | undefined,
fieldName: undefined as string | undefined,
year: String(now.getFullYear()),
month: `${String(now.getFullYear())}-${String(now.getMonth() + 1).padStart(2, '0')}`
month: String(now.getMonth() + 1).padStart(2, '0')
});
/** 年份选项(近 5 年) */
@ -27,14 +35,7 @@ const yearOptions = (() => {
// ======================== 所属区 / 领域字典 ========================
const areaOptions = ref<any[]>([]);
const fieldOptions = [
{ label: '食品安全', value: 'food' },
{ label: '药品安全', value: 'drug' },
{ label: '特种设备', value: 'special_equipment' },
{ label: '产品质量', value: 'product' },
{ label: '计量', value: 'metrology' },
{ label: '认证认可', value: 'certification' }
];
const fieldOptions = ref<DashboardDomainVo[]>([]);
const loadAreaDict = async () => {
try {
@ -45,6 +46,15 @@ const loadAreaDict = async () => {
}
};
const loadFieldDict = async () => {
try {
const res = await getDomainInfo();
fieldOptions.value = res.data || [];
} catch (e) {
console.error('加载所属领域字典失败', e);
}
};
// ======================== 表格数据 ========================
const loading = ref(false);
const tableData = ref<CheckRegistrationRow[]>([]);
@ -78,7 +88,7 @@ const getList = async () => {
tableData.value = (res as any)?.rows || [];
total.value = (res as any)?.total || 0;
} else {
params.fieldName = queryParams.fieldName;
params.lymc = queryParams.fieldName;
const res = await listFieldRegistration(params);
tableData.value = (res as any)?.rows || [];
total.value = (res as any)?.total || 0;
@ -99,15 +109,35 @@ const handleReset = () => {
queryParams.areaName = undefined;
queryParams.fieldName = undefined;
queryParams.year = String(new Date().getFullYear());
queryParams.month = `${String(new Date().getFullYear())}-${String(new Date().getMonth() + 1).padStart(2, '0')}`;
queryParams.month = String(new Date().getMonth() + 1).padStart(2, '0');
queryParams.pageNum = 1;
queryParams.pageSize = 20;
getList();
};
/** 导出 */
const handleExport = () => {
// TODO: 接入导出接口后实现文件下载
/** 导出(各区域走 exportArea,各领域走 exportDomain) */
const handleExport = async () => {
try {
const params =
props.dimension === 'area'
? { areaName: queryParams.areaName, year: queryParams.year, month: queryParams.month }
: { lymc: queryParams.fieldName, year: queryParams.year, month: queryParams.month };
const res =
props.dimension === 'area'
? await exportAreaRegistration(params)
: await exportDomainRegistration(params);
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 = props.dimension === 'area' ? '检查登记区域.xlsx' : '检查登记领域.xlsx';
link.click();
window.URL.revokeObjectURL(url);
} catch (e) {
ElMessage.error('导出失败');
}
};
/** 多选 */
@ -118,6 +148,8 @@ const handleSelectionChange = (selection: CheckRegistrationRow[]) => {
onMounted(() => {
if (props.dimension === 'area') {
loadAreaDict();
} else {
loadFieldDict();
}
getList();
nextTick(() => calcTableHeight());
@ -153,7 +185,7 @@ defineExpose({ calcTableHeight });
class="search-select"
clearable
>
<el-option v-for="item in fieldOptions" :key="item.value" :label="item.label" :value="item.label" />
<el-option v-for="item in fieldOptions" :key="item.domainId" :label="item.domainName" :value="item.domainName" />
</el-select>
</el-form-item>
<el-form-item label="年份">
@ -165,7 +197,7 @@ defineExpose({ calcTableHeight });
<el-date-picker
v-model="queryParams.month"
type="month"
value-format="YYYY-MM"
value-format="MM"
class="period-date"
placeholder="选择月份"
/>
@ -192,11 +224,15 @@ defineExpose({ calcTableHeight });
>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="序号" width="90" align="center" type="index" />
<el-table-column prop="dimensionName" :label="dimension === 'area' ? '所属区' : '所属领域'" min-width="200" align="center" />
<el-table-column label="行政执法数" min-width="160" align="center" prop="enforcementCount" />
<el-table-column label="行政检查案件数" min-width="180" align="center" prop="inspectionCaseCount" />
<el-table-column label="行政处罚案件数" min-width="180" align="center" prop="penaltyCaseCount" />
<el-table-column label="行政处罚强制数" min-width="180" align="center" prop="coerciveCount" />
<el-table-column prop="dimensionName" :label="dimension === 'area' ? '所属区' : '所属领域'" min-width="200" align="center">
<template #default="scope">
{{ dimension === 'area' ? scope.row.areaName : scope.row.lymc }}
</template>
</el-table-column>
<el-table-column label="行政执法数" min-width="160" align="center" prop="xzzfs" />
<el-table-column label="行政检查案件数" min-width="180" align="center" prop="xzjcajs" />
<el-table-column label="行政处罚案件数" min-width="180" align="center" prop="xzcfajs" />
<el-table-column label="行政强制案件数" min-width="180" align="center" prop="xzqzajs" />
</el-table>
</div>

107
zdxt-web-client/zdxt-admin-plus-ui/src/views/efcode/trend/key-enterprise/index.vue

@ -1,16 +1,18 @@
<script setup lang="ts">
import { computed, nextTick, onMounted, onUnmounted, reactive, ref } from 'vue';
import { computed, onMounted, reactive, ref } from 'vue';
import { ElMessage } from 'element-plus';
import { getDicts } from '@/api/system/dict/data';
import { listKeyEnterprises, listKeyEnterpriseDetails } from '@/api/efcode/report/key-enterprises';
import { listKeyEnterprises, listKeyEnterpriseDetails, exportKeyEnterprises } from '@/api/efcode/report/key-enterprises';
/** 企业行数据 */
interface EnterpriseRow {
id: number | string;
enterpriseNumber: string;
enterpriseName: string;
enterpriseNumber?: string; // 统一社会信用代码/注册号 — 详情接口入参
areaName: string;
districtName: string;
streetName: string;
enforcementCount: number | null;
xzzfzs: number | null;
enterpriseId: number | string;
}
// ======================== 查询条件 ========================
@ -44,13 +46,15 @@ const loadAreaDict = async () => {
}
};
/** 切换所属区时联动加载所属街道 */
/** 切换所属区时联动加载所属街道(选中值是 dictLabel,需取对应 dictValue 拉子级字典) */
const handleAreaChange = async (val: string) => {
queryParams.streetName = undefined;
streetOptions.value = [];
if (!val) return;
const dictValue = areaOptions.value.find((item) => item.dictLabel === val)?.dictValue;
if (!dictValue) return;
try {
const res = await getDicts(val);
const res = await getDicts(dictValue);
streetOptions.value = res.data || [];
} catch (e) {
console.error('加载所属街道字典失败', e);
@ -63,21 +67,23 @@ const tableData = ref<EnterpriseRow[]>([]);
const total = ref(0);
const selectedRows = ref<EnterpriseRow[]>([]);
// 表格高度(与 efcode/report/registration/indexList.vue 实现一致)
const tableHeight = ref<number | undefined>(undefined);
const tableRef = ref<HTMLElement>();
/** 表格固定高度 */
const tableHeight = 600;
/** 计算表格高度:视口高度 - 表格顶部偏移 - 分页区高度(~52px) - 底部边距(16px) */
const calcTableHeight = () => {
if (!tableRef.value) return;
const rect = tableRef.value.getBoundingClientRect();
tableHeight.value = window.innerHeight - rect.top - 84;
/** 月份选择器(YYYY-MM)→ 月号,如 "2026-10" → "10" */
const statMonthValue = () => {
const part = queryParams.statMonth ? queryParams.statMonth.split('-')[1] : undefined;
return part ? String(Number(part)) : undefined;
};
const getList = async () => {
loading.value = true;
try {
const res = await listKeyEnterprises(queryParams);
// 年份取「年份」下拉,月份取「月份」选择器并只传月号(不带年份)
const res = await listKeyEnterprises({
...queryParams,
statMonth: statMonthValue()
});
tableData.value = (res.rows || []) as EnterpriseRow[];
total.value = res.total || 0;
} finally {
@ -104,9 +110,30 @@ const handleReset = () => {
getList();
};
/** 导出 */
const handleExport = () => {
// TODO: 接入导出接口后实现文件下载
/** 导出 — 勾选企业时按其 enterpriseNumber 导出(多选用逗号分隔),未勾选则导出全部;默认带当前年月 */
const handleExport = async () => {
try {
const numbers = selectedRows.value
.map((r) => r.enterpriseId)
.filter(Boolean)
.join(',');
const res = await exportKeyEnterprises({
enterpriseNumber: numbers || undefined,
statYear: queryParams.statYear,
statMonth: statMonthValue()
});
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('导出失败');
}
};
// ======================== 详情弹窗 ========================
@ -116,6 +143,7 @@ interface EnforcementDetailRow {
bureauDeptName: string;
categoryText: string;
lawEnforcer: string;
lawEnforcerTwo?: string;
itemText: string;
}
@ -131,10 +159,12 @@ const getDetailList = async () => {
detailLoading.value = true;
try {
if (!detailEnterprise.value) return;
// 真实接口:enterpriseNumber(企业注册号)+ enforcementTime(按年-月筛选)
// 详情接口:enterpriseNumber(企业id)+ enforcementTime(YYYY-MM),带分页
const res = await listKeyEnterpriseDetails({
pageNum: detailPage.value,
pageSize: detailPageSize.value,
enterpriseNumber: detailEnterprise.value.enterpriseNumber,
enforcementTime: queryParams.statMonth
enforcementTime: queryParams.statMonth + '-01'
});
detailTableData.value = (res as any)?.rows || (Array.isArray(res) ? res : []);
detailTotal.value = (res as any)?.total ?? detailTableData.value.length;
@ -171,12 +201,6 @@ const handleSelectionChange = (selection: EnterpriseRow[]) => {
onMounted(() => {
loadAreaDict();
getList();
nextTick(() => calcTableHeight());
window.addEventListener('resize', calcTableHeight);
});
onUnmounted(() => {
window.removeEventListener('resize', calcTableHeight);
});
</script>
@ -204,7 +228,7 @@ onUnmounted(() => {
clearable
@change="handleAreaChange"
>
<el-option v-for="item in areaOptions" :key="item.dictValue" :label="item.dictLabel" :value="item.dictValue" />
<el-option v-for="item in areaOptions" :key="item.dictValue" :label="item.dictLabel" :value="item.dictLabel" />
</el-select>
</el-form-item>
<el-form-item label="所属街道">
@ -214,7 +238,7 @@ onUnmounted(() => {
class="search-select"
clearable
>
<el-option v-for="item in streetOptions" :key="item.dictValue" :label="item.dictLabel" :value="item.dictValue" />
<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="年份">
@ -241,7 +265,7 @@ onUnmounted(() => {
<!-- ==================== 表格区域 ==================== -->
<div class="table-panel">
<div ref="tableRef">
<div>
<el-table
v-loading="loading"
:data="tableData"
@ -258,17 +282,17 @@ onUnmounted(() => {
</template>
</el-table-column>
<el-table-column prop="enterpriseName" label="企业名称" min-width="300" align="center" />
<el-table-column prop="areaName" label="所属区" width="160" align="center" />
<el-table-column prop="districtName" label="所属区" width="160" align="center" />
<el-table-column prop="streetName" label="所属街道" width="160" align="center" />
<el-table-column label="行政执法次数" width="180" align="center">
<template #default="{ row }">
<span v-if="row.enforcementCount !== null">{{ row.enforcementCount }}次</span>
<span v-if="row.xzzfzs !== null">{{ row.xzzfzs }}次</span>
</template>
</el-table-column>
<el-table-column label="操作" width="80" align="center">
<template #default="{ row }">
<el-button
v-if="row.enterpriseName"
v-if="row.xzzfzs"
type="primary"
link
size="small"
@ -298,7 +322,7 @@ onUnmounted(() => {
<el-dialog
v-model="detailDialogVisible"
width="1100px"
:title="`${detailEnterprise?.enterpriseName || ''} — ${queryParams.statYear}年${queryParams.statMonth} 执法明细`"
:title="`${detailEnterprise?.enterpriseName || ''} — ${queryParams.statMonth} 执法明细`"
append-to-body
top="5vh"
destroy-on-close
@ -307,7 +331,7 @@ onUnmounted(() => {
v-if="detailEnterprise"
:closable="false"
show-icon
:title="`所属区:${detailEnterprise.areaName} | 所属街道:${detailEnterprise.streetName} | 行政执法次数:${detailEnterprise.enforcementCount ?? 0}次 | 时段:${queryParams.statYear}年${queryParams.statMonth}`"
:title="`所属区:${detailEnterprise.districtName || '-'} | 所属街道:${detailEnterprise.streetName || '-'} | 行政执法次数:${detailEnterprise.xzzfzs ?? 0}次 | 时段:${queryParams.statMonth}`"
type="info"
class="mb-4"
/>
@ -323,9 +347,13 @@ onUnmounted(() => {
<el-table-column label="序号" type="index" width="80" align="center" />
<el-table-column label="执法时间" prop="enforcementTime" min-width="180" align="center" />
<el-table-column label="执法单位" prop="bureauDeptName" min-width="260" align="center" />
<el-table-column label="执法类别" prop="categoryText" width="140" align="center" />
<el-table-column label="执法人员" prop="lawEnforcer" width="140" align="center" />
<el-table-column label="执法事项" prop="itemText" min-width="220" align="center" />
<el-table-column label="执法类别" prop="dictLabel" width="140" align="center" />
<el-table-column label="执法人员" width="140" align="center">
<template #default="{ row }">
{{ [row.lawEnforcer, row.lawEnforcerTwo].filter(Boolean).join('、') }}
</template>
</el-table-column>
<el-table-column label="执法事项" prop="lawEnforcementItemText" min-width="220" align="center" />
</el-table>
<el-pagination
@ -346,16 +374,13 @@ onUnmounted(() => {
<style scoped lang="scss">
/* 外层容器:与 border-card tabs 壳提供的白底带边框容器一致 */
.app-container {
padding: 16px;
box-sizing: border-box;
}
.page-card {
background: #fff;
border: 1px solid #dcdfe6;
border-radius: 4px;
box-sizing: border-box;
padding: 16px;
min-height: calc(100vh - 116px);
}

85
zdxt-web-client/zdxt-admin-plus-ui/src/views/efcode/trend/scan-district/index.vue

@ -1,11 +1,15 @@
<script setup lang="ts">
import { computed, nextTick, onMounted, onUnmounted, reactive, ref } from 'vue';
import { ElMessage } from 'element-plus';
import { getDicts } from '@/api/system/dict/data';
import { listScanArea, listScanAreaDetails, exportScanArea, type ScanTimeParams } from '@/api/efcode/report/scan-data-situation';
/** 表格行数据 */
interface DistrictRow {
id: number;
areaName: string;
districtCode?: string;
districtName?: string;
enterpriseCount: number;
adminEnforceCount: number;
adminRank: number;
@ -150,10 +154,41 @@ const calcTableHeight = () => {
tableHeight.value = window.innerHeight - rect.top - 84;
};
/** 按当前时段模式拼装接口时间参数 */
const buildTimeParams = (): ScanTimeParams => {
if (timeMode.value === 'year') {
return { year: selectedYear.value };
}
if (timeMode.value === 'quarter') {
return { year: selectedYear.value, quarter: String(selectedQuarter.value) };
}
if (timeMode.value === 'month') {
if (!selectedMonth.value) return {};
const [y, m] = selectedMonth.value.split('-');
return { year: y, month: m };
}
return { startTime: queryParams.beginTime, endTime: queryParams.endTime };
};
/** 当前选中的所属区(字典 dictValue→districtCode,dictLabel→districtName) */
const currentDistrict = () => {
const item = areaOptions.value.find((i: any) => i.dictValue === queryParams.area);
return { districtCode: queryParams.area, districtName: item?.dictLabel };
};
const getList = async () => {
loading.value = true;
try {
// TODO: 接入真实接口 listDistrictScanData
const res = await listScanArea({
...buildTimeParams(),
pageNum: currentPage.value,
pageSize: pageSize.value,
...currentDistrict()
});
tableData.value = (res as any)?.rows || [];
total.value = (res as any)?.total || 0;
} catch (e) {
console.warn('[scan-district] 列表接口调用失败', e);
} finally {
loading.value = false;
}
@ -180,8 +215,24 @@ const handleReset = () => {
getList();
};
const handleExport = () => {
// TODO: 接入导出接口
const handleExport = async () => {
try {
const res = await exportScanArea({
...buildTimeParams(),
...currentDistrict()
});
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 handleSelectionChange = (selection: DistrictRow[]) => {
@ -222,7 +273,17 @@ const getDetailList = async () => {
detailLoading.value = true;
try {
if (!detailArea.value) return;
// TODO: 接入真实接口 listDistrictScanDetail
const res = await listScanAreaDetails({
...buildTimeParams(),
pageNum: detailPage.value,
pageSize: detailPageSize.value,
districtCode: detailArea.value.districtCode || queryParams.area,
districtName: detailArea.value.districtName || detailArea.value.areaName || currentDistrict().districtName
});
detailTableData.value = (res as any)?.rows || [];
detailTotal.value = (res as any)?.total || 0;
} catch (e) {
console.warn('[scan-district] 详情接口调用失败', e);
} finally {
detailLoading.value = false;
}
@ -326,14 +387,14 @@ onUnmounted(() => {
>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="序号" width="80" align="center" type="index" />
<el-table-column prop="areaName" label="所属区" width="120" align="center" />
<el-table-column prop="districtName" label="所属区" width="120" align="center" />
<el-table-column prop="enterpriseCount" label="商事主体数量(户)" min-width="150" align="center" />
<el-table-column prop="adminEnforceCount" label="行政执法数量(条)" min-width="150" align="center" />
<el-table-column prop="adminRank" label="行政执法数量排名" min-width="150" align="center" />
<el-table-column prop="adminPercent" label="行政执法数量占比(%)" min-width="170" align="center" />
<el-table-column prop="nonAdminEnforceCount" label="非行政执法数量" min-width="140" align="center" />
<el-table-column prop="nonAdminRank" label="非行政执法数量排名" min-width="160" align="center" />
<el-table-column prop="nonAdminPercent" label="非行政执法数量占比(%)" min-width="180" align="center" />
<el-table-column prop="xzzfCount" label="行政执法数量(条)" min-width="150" align="center" />
<el-table-column prop="xzzfslpm" label="行政执法数量排名" min-width="150" align="center" />
<el-table-column prop="xzzfslzb" label="行政执法数量占比(%)" min-width="170" align="center" />
<el-table-column prop="fxzzfsl" label="非行政执法数量" min-width="140" align="center" />
<el-table-column prop="fxzzfslpm" label="非行政执法数量排名" min-width="160" align="center" />
<el-table-column prop="fxzzfslzb" label="非行政执法数量占比(%)" min-width="180" align="center" />
<el-table-column label="操作" width="80" align="center" fixed="right">
<template #default="{ row }">
<el-button type="primary" link size="small" @click="handleDetail(row)">详情</el-button>
@ -412,10 +473,8 @@ onUnmounted(() => {
.page-card {
background: #fff;
border: 1px solid #dcdfe6;
border-radius: 4px;
box-sizing: border-box;
padding: 16px;
min-height: calc(100vh - 116px);
}

103
zdxt-web-client/zdxt-admin-plus-ui/src/views/efcode/trend/scan-enforcer/index.vue

@ -1,7 +1,15 @@
<script setup lang="ts">
import { nextTick, onMounted, onUnmounted, reactive, ref } from 'vue';
import { ElMessage } from 'element-plus';
import { getDicts } from '@/api/system/dict/data';
import { listLawEnforcer, listLawEnforcerDetails, type EnforcerRow, type LawEnforcerDetailRow } from '@/api/efcode/report/law-enforcer';
import {
listLawEnforcer,
listLawEnforcerDetails,
exportLawEnforcer,
type EnforcerRow,
type LawEnforcerDetailRow
} from '@/api/efcode/report/law-enforcer';
import { getDomainInfo, type DashboardDomainVo } from '@/api/efcode/dashboard/area-info';
// ======================== 查询条件 ========================
const queryParams = reactive({
@ -10,16 +18,18 @@ const queryParams = reactive({
lawEnforcer: '' as string, // 执法人员关键字(接口参数名)
area: undefined as string | undefined,
field: undefined as string | undefined,
enforcementTime: String(new Date().getFullYear()),
enforcementTime: `${new Date().getFullYear()}-${String(new Date().getMonth() + 1).padStart(2, '0')}-01`,
orderByColumn: '' as string,
isAsc: '' as string
});
// 时段模式:year | quarter | month
const timeMode = ref('year');
const timeMode = ref('month');
const selectedYear = ref<string>(String(new Date().getFullYear()));
const selectedQuarter = ref<number>(Math.ceil((new Date().getMonth() + 1) / 3));
const selectedMonth = ref<string>('');
const selectedMonth = ref<string>(
`${new Date().getFullYear()}-${String(new Date().getMonth() + 1).padStart(2, '0')}`
);
// 季度选项
const quarterOptions = [
@ -42,7 +52,8 @@ const syncEnforcementTime = () => {
} else if (timeMode.value === 'quarter') {
queryParams.enforcementTime = `${selectedYear.value}-Q${selectedQuarter.value}`;
} else if (timeMode.value === 'month') {
queryParams.enforcementTime = selectedMonth.value;
// 月度模式固定拼 01 日:YYYY-MM-01
queryParams.enforcementTime = selectedMonth.value ? `${selectedMonth.value}-01` : '';
}
};
@ -61,15 +72,7 @@ const handleMonthChange = () => syncEnforcementTime();
// ======================== 字典 ========================
const areaOptions = ref<any[]>([]);
const fieldOptions = [
{ label: '生态环境领域', value: 'ecology' },
{ label: '卫生健康领域', value: 'health' },
{ label: '应急管理领域', value: 'emergency' },
{ label: '交通运输领域', value: 'transport' },
{ label: '市场监管领域', value: 'market' },
{ label: '消防救援领域', value: 'fire' },
{ label: '规划和自然资源领域', value: 'planning' }
];
const fieldOptions = ref<DashboardDomainVo[]>([]);
const loadAreaDict = async () => {
try {
@ -80,6 +83,15 @@ const loadAreaDict = async () => {
}
};
const loadFieldDict = async () => {
try {
const res = await getDomainInfo();
fieldOptions.value = res.data || [];
} catch (e) {
console.error('加载所属领域字典失败', e);
}
};
// ======================== 表格数据 ========================
const loading = ref(false);
const tableData = ref<EnforcerRow[]>([]);
@ -122,16 +134,32 @@ const handleReset = () => {
queryParams.isAsc = '';
queryParams.pageNum = 1;
queryParams.pageSize = 20;
timeMode.value = 'year';
timeMode.value = 'month';
selectedYear.value = String(n.getFullYear());
selectedQuarter.value = Math.ceil((n.getMonth() + 1) / 3);
selectedMonth.value = '';
selectedMonth.value = `${n.getFullYear()}-${String(n.getMonth() + 1).padStart(2, '0')}`;
syncEnforcementTime();
getList();
};
const handleExport = () => {
// TODO: 接入导出接口
const handleExport = async () => {
try {
const res = await exportLawEnforcer({
lawEnforcer: queryParams.lawEnforcer,
enforcementTime: queryParams.enforcementTime
});
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 handleSelectionChange = (selection: EnforcerRow[]) => {
@ -164,15 +192,14 @@ const getDetailList = async () => {
detailLoading.value = true;
try {
if (!detailEnforcer.value?.lawEnforcerId) return;
const res = await listLawEnforcerDetails(detailEnforcer.value.lawEnforcerId);
// 后端可能返回 { rows, total } 或直接数组
if ((res as any)?.rows) {
detailTableData.value = (res as any).rows;
detailTotal.value = (res as any).total ?? detailTableData.value.length;
} else if (Array.isArray(res)) {
detailTableData.value = res as LawEnforcerDetailRow[];
detailTotal.value = detailTableData.value.length;
}
const res = await listLawEnforcerDetails({
lawEnforcerId: detailEnforcer.value.lawEnforcerId,
enforcementTime: queryParams.enforcementTime,
pageNum: detailPage.value,
pageSize: detailPageSize.value
});
detailTableData.value = (res as any)?.rows || [];
detailTotal.value = (res as any)?.total || 0;
} catch (e) {
console.warn('[scan-enforcer] 详情接口调用失败', e);
} finally {
@ -200,6 +227,8 @@ const handleDetailPageChange = (page: number) => {
onMounted(() => {
loadAreaDict();
loadFieldDict();
syncEnforcementTime();
getList();
nextTick(() => calcTableHeight());
window.addEventListener('resize', calcTableHeight);
@ -242,13 +271,13 @@ onUnmounted(() => {
class="search-select"
clearable
>
<el-option v-for="item in fieldOptions" :key="item.value" :label="item.label" :value="item.label" />
<el-option v-for="item in fieldOptions" :key="item.domainId" :label="item.domainName" :value="item.domainId" />
</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="year" />
<el-option label="季度" value="quarter" /> -->
<el-option label="月度" value="month" />
</el-select>
</el-form-item>
@ -298,10 +327,10 @@ onUnmounted(() => {
>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="序号" width="90" align="center" type="index" />
<el-table-column prop="enforcerName" label="执法人员" min-width="160" align="center" />
<el-table-column prop="enforceCount" label="执法次数" width="140" align="center" sortable="custom" />
<el-table-column prop="areaName" label="所属区(市)" min-width="140" align="center" />
<el-table-column prop="bureauName" label="所属单位" min-width="260" align="center" show-overflow-tooltip />
<el-table-column prop="lawEnforcer" label="执法人员" min-width="160" align="center" />
<el-table-column prop="zfcs" label="执法次数" width="140" align="center" sortable="custom" />
<el-table-column prop="districtName" label="所属区(市)" min-width="140" align="center" />
<el-table-column prop="bureauDeptName" label="所属单位" min-width="260" align="center" show-overflow-tooltip />
<el-table-column label="操作" width="90" align="center" fixed="right">
<template #default="{ row }">
<el-button type="primary" link size="small" @click="handleDetail(row)">详情</el-button>
@ -343,9 +372,9 @@ onUnmounted(() => {
<el-table-column label="序号" type="index" width="80" align="center" />
<el-table-column label="执法单位" prop="bureauDeptName" min-width="220" align="center" show-overflow-tooltip />
<el-table-column label="执法人员" prop="lawEnforcer" min-width="160" align="center" />
<el-table-column label="执法时间" prop="scanTime" min-width="180" align="center" />
<el-table-column label="执法时间" prop="enforcementTime" min-width="180" align="center" />
<el-table-column label="被执法企业名称" prop="enterpriseName" min-width="260" align="center" show-overflow-tooltip />
<el-table-column label="执法事项" prop="enforcementItem" width="120" align="center" />
<el-table-column label="执法事项" prop="lawEnforcementItemText" width="120" align="center" />
</el-table>
<el-pagination
@ -370,10 +399,8 @@ onUnmounted(() => {
.page-card {
background: #fff;
border: 1px solid #dcdfe6;
border-radius: 4px;
box-sizing: border-box;
padding: 16px;
min-height: calc(100vh - 116px);
}

198
zdxt-web-client/zdxt-admin-plus-ui/src/views/efcode/trend/scan-field/index.vue

@ -1,11 +1,22 @@
<script setup lang="ts">
import { nextTick, onMounted, onUnmounted, reactive, ref } from 'vue';
import { ElMessage } from 'element-plus';
import { getDicts } from '@/api/system/dict/data';
import {
listScanDomain,
listScanDomainDetails,
exportScanDomain,
type ScanDetailRow,
type ScanTimeParams
} from '@/api/efcode/report/scan-data-situation';
import { getDomainInfo, type DashboardDomainVo } from '@/api/efcode/dashboard/area-info';
/** 表格行数据 */
interface FieldRow {
id: number;
fieldName: string;
domainId?: string | number;
domainName?: string;
adminEnforceCount: number;
adminPercent: string;
nonAdminEnforceCount: number;
@ -35,15 +46,7 @@ const yearOptions = (() => {
// ======================== 字典 ========================
const areaOptions = ref<any[]>([]);
const fieldOptions = ref([
{ label: '生态环境领域', value: 'ecology' },
{ label: '卫生健康领域', value: 'health' },
{ label: '应急管理领域', value: 'emergency' },
{ label: '交通运输领域', value: 'transport' },
{ label: '市场监管领域', value: 'market' },
{ label: '消防救援领域', value: 'fire' },
{ label: '规划和自然资源领域', value: 'planning' }
]);
const fieldOptions = ref<DashboardDomainVo[]>([]);
const loadAreaDict = async () => {
try {
@ -54,6 +57,15 @@ const loadAreaDict = async () => {
}
};
const loadFieldDict = async () => {
try {
const res = await getDomainInfo();
fieldOptions.value = res.data || [];
} catch (e) {
console.error('加载所属领域字典失败', e);
}
};
// ======================== 表格数据 ========================
const loading = ref(false);
const tableData = ref<FieldRow[]>([]);
@ -71,10 +83,49 @@ const calcTableHeight = () => {
tableHeight.value = window.innerHeight - rect.top - 84;
};
/** 按当前时段类型拼装接口时间参数 */
const buildTimeParams = (): ScanTimeParams => {
const year = queryParams.year;
const n = new Date();
if (queryParams.periodType === 'quarter') {
return { year, quarter: String(Math.ceil((n.getMonth() + 1) / 3)) };
}
if (queryParams.periodType === 'month') {
return { year, month: String(n.getMonth() + 1) };
}
if (queryParams.periodType === 'week') {
// 周度:取本周一 ~ 本周日
const day = n.getDay() || 7;
const monday = new Date(n);
monday.setDate(n.getDate() - (day - 1));
const sunday = new Date(monday);
sunday.setDate(monday.getDate() + 6);
const fmt = (d: Date) =>
`${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
return { startTime: fmt(monday), endTime: fmt(sunday) };
}
return { year };
};
/** 当前选中的所属领域(domainId→domainId,domainName→domainName) */
const currentField = () => {
const item = fieldOptions.value.find((i) => i.domainId === queryParams.field);
return { domainId: queryParams.field, domainName: item?.domainName };
};
const getList = async () => {
loading.value = true;
try {
// TODO: 接入真实接口 listFieldScanData
const res = await listScanDomain({
...buildTimeParams(),
pageNum: currentPage.value,
pageSize: pageSize.value,
...currentField()
});
tableData.value = (res as any)?.rows || [];
total.value = (res as any)?.total || 0;
} catch (e) {
console.warn('[scan-field] 列表接口调用失败', e);
} finally {
loading.value = false;
}
@ -95,8 +146,24 @@ const handleReset = () => {
getList();
};
const handleExport = () => {
// TODO: 接入导出接口
const handleExport = async () => {
try {
const res = await exportScanDomain({
...buildTimeParams(),
...currentField()
});
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 handleSelectionChange = (selection: FieldRow[]) => {
@ -114,8 +181,56 @@ const handlePageChange = (page: number) => {
getList();
};
// ======================== 详情弹窗 ========================
const detailDialogVisible = ref(false);
const detailField = ref<FieldRow | null>(null);
const detailLoading = ref(false);
const detailTableData = ref<ScanDetailRow[]>([]);
const detailTotal = ref(0);
const detailPage = ref(1);
const detailPageSize = ref(20);
const getDetailList = async () => {
detailLoading.value = true;
try {
if (!detailField.value) return;
const res = await listScanDomainDetails({
...buildTimeParams(),
pageNum: detailPage.value,
pageSize: detailPageSize.value,
domainId: detailField.value.domainId || queryParams.field,
domainName: detailField.value.domainName || detailField.value.fieldName || currentField().domainName
});
detailTableData.value = (res as any)?.rows || [];
detailTotal.value = (res as any)?.total || 0;
} catch (e) {
console.warn('[scan-field] 详情接口调用失败', e);
} finally {
detailLoading.value = false;
}
};
const handleDetail = (row: FieldRow) => {
detailField.value = row;
detailPage.value = 1;
detailDialogVisible.value = true;
getDetailList();
};
const handleDetailSizeChange = (size: number) => {
detailPageSize.value = size;
detailPage.value = 1;
getDetailList();
};
const handleDetailPageChange = (page: number) => {
detailPage.value = page;
getDetailList();
};
onMounted(() => {
loadAreaDict();
loadFieldDict();
getList();
nextTick(() => calcTableHeight());
window.addEventListener('resize', calcTableHeight);
@ -149,7 +264,7 @@ onUnmounted(() => {
class="search-select"
clearable
>
<el-option v-for="item in fieldOptions" :key="item.value" :label="item.label" :value="item.value" />
<el-option v-for="item in fieldOptions" :key="item.domainId" :label="item.domainName" :value="item.domainId" />
</el-select>
</el-form-item>
<el-form-item label="时段">
@ -184,11 +299,18 @@ onUnmounted(() => {
>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="序号" width="90" align="center" type="index" />
<el-table-column prop="fieldName" label="所属领域" min-width="200" align="center" />
<el-table-column prop="adminEnforceCount" label="行政执法数量(条)" min-width="160" align="center" />
<el-table-column prop="adminPercent" label="行政执法数量占比(%)" min-width="190" align="center" />
<el-table-column prop="nonAdminEnforceCount" label="非行政执法数量(条)" min-width="170" align="center" />
<el-table-column prop="nonAdminPercent" label="非行政执法数量占比(%)" min-width="200" align="center" />
<el-table-column prop="domainName" label="所属领域" min-width="200" align="center" />
<el-table-column prop="xzzfCount" label="行政执法数量(条)" min-width="160" align="center" />
<el-table-column prop="xzzfslzb" label="行政执法数量占比(%)" min-width="190" align="center" />
<el-table-column prop="xzzfslpm" label="行政执法数量排名" min-width="190" align="center" />
<el-table-column prop="fxzzfsl" label="非行政执法数量(条)" min-width="170" align="center" />
<el-table-column prop="fxzzfslpm" label="非行政执法数量排名" min-width="200" align="center" />
<el-table-column prop="fxzzfslzb" label="非行政执法数量占比(%)" min-width="200" align="center" />
<el-table-column label="操作" width="90" align="center" fixed="right">
<template #default="{ row }">
<el-button type="primary" link size="small" @click="handleDetail(row)">详情</el-button>
</template>
</el-table-column>
</el-table>
</div>
@ -204,6 +326,44 @@ onUnmounted(() => {
/>
</div>
</div>
<!-- ==================== 扫码明细弹窗 ==================== -->
<el-dialog
v-model="detailDialogVisible"
width="1100px"
:title="`${detailField?.fieldName || ''} — 扫码明细`"
append-to-body
top="5vh"
destroy-on-close
>
<el-table
v-loading="detailLoading"
:data="detailTableData"
border
stripe
style="width: 100%"
max-height="55vh"
>
<el-table-column label="序号" type="index" width="80" align="center" />
<el-table-column label="企业名称" prop="enterpriseName" min-width="220" align="center" />
<el-table-column label="扫码时间" prop="scanTime" min-width="180" align="center" />
<el-table-column label="扫码类型" prop="scanType" width="120" align="center" />
<el-table-column label="执法单位" prop="bureauDeptName" min-width="220" align="center" />
<el-table-column label="执法人员" prop="lawEnforcer" width="120" align="center" />
<el-table-column label="执法事项" prop="enforcementItem" min-width="200" align="center" />
</el-table>
<el-pagination
v-model:current-page="detailPage"
v-model:page-size="detailPageSize"
:page-sizes="[10, 20, 50, 100]"
:total="detailTotal"
layout="total, sizes, prev, pager, next, jumper"
class="mt-4"
@size-change="handleDetailSizeChange"
@current-change="handleDetailPageChange"
/>
</el-dialog>
</div>
</template>
@ -215,10 +375,8 @@ onUnmounted(() => {
.page-card {
background: #fff;
border: 1px solid #dcdfe6;
border-radius: 4px;
box-sizing: border-box;
padding: 16px;
min-height: calc(100vh - 116px);
}

64
zdxt-web-client/zdxt-admin-plus-ui/src/views/efcode/trend/situation-trend/components/SituationPane.vue

@ -1,7 +1,15 @@
<script setup lang="ts">
import { nextTick, onMounted, onUnmounted, reactive, ref } from 'vue';
import { ElMessage } from 'element-plus';
import { getDicts } from '@/api/system/dict/data';
import { listSituationTrendArea, listSituationTrendDomain, type SituationRow } from '@/api/efcode/report/situation-trend';
import {
listSituationTrendArea,
listSituationTrendDomain,
exportSituationTrendArea,
exportSituationTrendDomain,
type SituationRow
} from '@/api/efcode/report/situation-trend';
import { getDomainInfo, type DashboardDomainVo } from '@/api/efcode/dashboard/area-info';
/** 维度:area=各区域 field=各领域 */
const props = defineProps<{
@ -38,14 +46,7 @@ const quarterOptions = [
// ======================== 字典 ========================
const areaOptions = ref<any[]>([]);
const fieldOptions = [
{ label: '食品安全', value: 'food' },
{ label: '药品安全', value: 'drug' },
{ label: '特种设备', value: 'special_equipment' },
{ label: '产品质量', value: 'product' },
{ label: '计量', value: 'metrology' },
{ label: '认证认可', value: 'certification' }
];
const fieldOptions = ref<DashboardDomainVo[]>([]);
const loadAreaDict = async () => {
try {
@ -56,6 +57,15 @@ const loadAreaDict = async () => {
}
};
const loadFieldDict = async () => {
try {
const res = await getDomainInfo();
fieldOptions.value = res.data || [];
} catch (e) {
console.error('加载所属领域字典失败', e);
}
};
// ======================== 表格数据 ========================
const loading = ref(false);
const tableData = ref<SituationRow[]>([]);
@ -128,9 +138,31 @@ const handleReset = () => {
getList();
};
/** 导出 */
const handleExport = () => {
// TODO: 接入导出接口后实现文件下载
/** 导出(各区域走 exportSituationTrendArea,各领域走 exportSituationTrendDomain) */
const handleExport = async () => {
try {
const baseParams = {
startYear: queryParams.year,
endYear: queryParams.year,
startQuarter: queryParams.quarter,
endQuarter: queryParams.quarter
};
const res =
props.dimension === 'area'
? await exportSituationTrendArea({ ...baseParams, districtName: queryParams.districtName })
: await exportSituationTrendDomain({ ...baseParams, districtName: queryParams.lymc });
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 = props.dimension === 'area' ? '态势趋势区域.xlsx' : '态势趋势领域.xlsx';
link.click();
window.URL.revokeObjectURL(url);
} catch (e) {
ElMessage.error('导出失败');
}
};
/** 多选 */
@ -139,7 +171,11 @@ const handleSelectionChange = (selection: SituationRow[]) => {
};
onMounted(() => {
loadAreaDict();
if (props.dimension === 'area') {
loadAreaDict();
} else {
loadFieldDict();
}
getList();
nextTick(() => calcTableHeight());
window.addEventListener('resize', calcTableHeight);
@ -174,7 +210,7 @@ defineExpose({ calcTableHeight });
class="search-select"
clearable
>
<el-option v-for="item in fieldOptions" :key="item.value" :label="item.label" :value="item.label" />
<el-option v-for="item in fieldOptions" :key="item.domainId" :label="item.domainName" :value="item.domainName" />
</el-select>
</el-form-item>
<el-form-item label="年份">

32
zdxt-web-client/zdxt-admin-plus-ui/src/views/efcode/trend/trend-behavior/components/TrendPane.vue

@ -1,7 +1,14 @@
<script setup lang="ts">
import { nextTick, onMounted, onUnmounted, reactive, ref } from 'vue';
import { ElMessage } from 'element-plus';
import { getDicts } from '@/api/system/dict/data';
import { listTrendBehaviorArea, listTrendBehaviorDomain, type TrendBehaviorRow } from '@/api/efcode/report/trend-behavior';
import {
listTrendBehaviorArea,
listTrendBehaviorDomain,
exportTrendBehaviorArea,
exportTrendBehaviorDomain,
type TrendBehaviorRow
} from '@/api/efcode/report/trend-behavior';
/** 维度:area=各区域 field=各领域 */
const props = defineProps<{
@ -124,9 +131,26 @@ const handleReset = () => {
getList();
};
/** 导出 */
const handleExport = () => {
// TODO: 接入导出接口后实现文件下载
/** 导出(各区域走 exportTrendBehaviorArea,各领域走 exportTrendBehavioDomain) */
const handleExport = async () => {
try {
const qt = buildQuarterTime();
const res =
props.dimension === 'area'
? await exportTrendBehaviorArea({ districtName: queryParams.districtName, quarterTime: qt })
: await exportTrendBehaviorDomain({ domainName: queryParams.domainName, quarterTime: qt });
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 = props.dimension === 'area' ? '趋势行为区域.xlsx' : '趋势行为领域.xlsx';
link.click();
window.URL.revokeObjectURL(url);
} catch (e) {
ElMessage.error('导出失败');
}
};
/** 多选 */

Loading…
Cancel
Save