You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
437 lines
13 KiB
437 lines
13 KiB
<script setup lang="ts">
|
|
import { computed, onMounted, reactive, ref } from 'vue';
|
|
import { ElMessage } from 'element-plus';
|
|
import { getDicts } from '@/api/system/dict/data';
|
|
import { listKeyEnterprises, listKeyEnterpriseDetails, exportKeyEnterprises } from '@/api/efcode/report/key-enterprises';
|
|
|
|
/** 企业行数据 */
|
|
interface EnterpriseRow {
|
|
id: number | string;
|
|
enterpriseNumber: string;
|
|
enterpriseName: string;
|
|
districtName: string;
|
|
streetName: string;
|
|
xzzfzs: number | null;
|
|
enterpriseId: number | string;
|
|
}
|
|
|
|
// ======================== 查询条件 ========================
|
|
const now = new Date();
|
|
const queryParams = reactive({
|
|
pageNum: 1,
|
|
pageSize: 20,
|
|
enterpriseName: '' as string,
|
|
districtName: undefined as string | undefined,
|
|
streetName: undefined as string | undefined,
|
|
statYear: String(now.getFullYear()),
|
|
statMonth: `${String(now.getFullYear())}-${String(now.getMonth() + 1).padStart(2, '0')}`
|
|
});
|
|
|
|
/** 年份选项(近 5 年) */
|
|
const yearOptions = (() => {
|
|
const curr = now.getFullYear();
|
|
return Array.from({ length: 5 }, (_, i) => String(curr - i));
|
|
})();
|
|
|
|
// ======================== 区域/街道字典 ========================
|
|
const areaOptions = ref<any[]>([]);
|
|
const streetOptions = ref<any[]>([]);
|
|
|
|
const loadAreaDict = async () => {
|
|
try {
|
|
const res = await getDicts('address_type');
|
|
areaOptions.value = res.data || [];
|
|
} catch (e) {
|
|
console.error('加载所属区字典失败', e);
|
|
}
|
|
};
|
|
|
|
/** 切换所属区时联动加载所属街道(选中值是 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(dictValue);
|
|
streetOptions.value = res.data || [];
|
|
} catch (e) {
|
|
console.error('加载所属街道字典失败', e);
|
|
}
|
|
};
|
|
|
|
// ======================== 表格数据 ========================
|
|
const loading = ref(false);
|
|
const tableData = ref<EnterpriseRow[]>([]);
|
|
const total = ref(0);
|
|
const selectedRows = ref<EnterpriseRow[]>([]);
|
|
|
|
/** 表格固定高度 */
|
|
const tableHeight = 600;
|
|
|
|
/** 月份选择器(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,
|
|
statMonth: statMonthValue()
|
|
});
|
|
tableData.value = (res.rows || []) as EnterpriseRow[];
|
|
total.value = res.total || 0;
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
};
|
|
|
|
/** 搜索 */
|
|
const handleSearch = () => {
|
|
queryParams.pageNum = 1;
|
|
getList();
|
|
};
|
|
|
|
/** 重置 */
|
|
const handleReset = () => {
|
|
queryParams.enterpriseName = '';
|
|
queryParams.districtName = undefined;
|
|
queryParams.streetName = undefined;
|
|
streetOptions.value = [];
|
|
queryParams.statYear = String(new Date().getFullYear());
|
|
queryParams.statMonth = `${String(new Date().getFullYear())}-${String(new Date().getMonth() + 1).padStart(2, '0')}`;
|
|
queryParams.pageNum = 1;
|
|
queryParams.pageSize = 20;
|
|
getList();
|
|
};
|
|
|
|
/** 导出 — 勾选企业时按其 enterpriseNumber 导出(多选用逗号分隔),未勾选则导出全部;默认带当前年月 */
|
|
const handleExport = async () => {
|
|
try {
|
|
const numbers = selectedRows.value
|
|
.map((r) => r.enterpriseNumber)
|
|
.filter(Boolean)
|
|
.join(',');
|
|
const res = await exportKeyEnterprises({
|
|
enterpriseNumber: numbers || undefined,
|
|
enterpriseName: queryParams.enterpriseName || undefined,
|
|
districtName: queryParams.districtName,
|
|
streetName: queryParams.streetName,
|
|
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('导出失败');
|
|
}
|
|
};
|
|
|
|
// ======================== 详情弹窗 ========================
|
|
interface EnforcementDetailRow {
|
|
id: number;
|
|
enforcementTime: string;
|
|
bureauDeptName: string;
|
|
categoryText: string;
|
|
lawEnforcer: string;
|
|
lawEnforcerTwo?: string;
|
|
itemText: string;
|
|
}
|
|
|
|
const detailDialogVisible = ref(false);
|
|
const detailEnterprise = ref<EnterpriseRow | null>(null);
|
|
const detailLoading = ref(false);
|
|
const detailTableData = ref<EnforcementDetailRow[]>([]);
|
|
const detailTotal = ref(0);
|
|
const detailPage = ref(1);
|
|
const detailPageSize = ref(20);
|
|
|
|
const getDetailList = async () => {
|
|
detailLoading.value = true;
|
|
try {
|
|
if (!detailEnterprise.value) return;
|
|
// 详情接口:enterpriseNumber(企业id)+ enforcementTime(YYYY-MM),带分页
|
|
const res = await listKeyEnterpriseDetails({
|
|
pageNum: detailPage.value,
|
|
pageSize: detailPageSize.value,
|
|
enterpriseNumber: detailEnterprise.value.enterpriseNumber,
|
|
enforcementTime: queryParams.statMonth + '-01'
|
|
});
|
|
detailTableData.value = (res as any)?.rows || (Array.isArray(res) ? res : []);
|
|
detailTotal.value = (res as any)?.total ?? detailTableData.value.length;
|
|
} finally {
|
|
detailLoading.value = false;
|
|
}
|
|
};
|
|
|
|
/** 查看详情 — 打开弹窗并加载当前时段的执法明细 */
|
|
const handleDetail = (row: EnterpriseRow) => {
|
|
if (!row.enterpriseName) return;
|
|
detailEnterprise.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();
|
|
};
|
|
|
|
/** 多选 */
|
|
const handleSelectionChange = (selection: EnterpriseRow[]) => {
|
|
selectedRows.value = selection;
|
|
};
|
|
|
|
onMounted(() => {
|
|
loadAreaDict();
|
|
getList();
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<div class="app-container">
|
|
<!-- 单页白底带边框容器,与 border-card tabs 壳提供的容器效果一致 -->
|
|
<div class="page-card">
|
|
<!-- ==================== 搜索区域 ==================== -->
|
|
<div class="search-panel">
|
|
<el-form :inline="true" label-colon>
|
|
<el-form-item label="企业名称">
|
|
<el-input
|
|
v-model="queryParams.enterpriseName"
|
|
placeholder="请输入关键字"
|
|
class="search-input"
|
|
clearable
|
|
@keyup.enter="handleSearch"
|
|
/>
|
|
</el-form-item>
|
|
<el-form-item label="所属区">
|
|
<el-select
|
|
v-model="queryParams.districtName"
|
|
placeholder="请选择"
|
|
class="search-select"
|
|
clearable
|
|
@change="handleAreaChange"
|
|
>
|
|
<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="所属街道">
|
|
<el-select
|
|
v-model="queryParams.streetName"
|
|
placeholder="请选择"
|
|
class="search-select"
|
|
clearable
|
|
>
|
|
<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.statYear" class="period-select">
|
|
<el-option v-for="y in yearOptions" :key="y" :label="y + '年'" :value="y" />
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item label="月份">
|
|
<el-date-picker
|
|
v-model="queryParams.statMonth"
|
|
type="month"
|
|
value-format="YYYY-MM"
|
|
class="period-date"
|
|
placeholder="选择月份"
|
|
/>
|
|
</el-form-item>
|
|
<el-form-item>
|
|
<el-button type="primary" @click="handleSearch">搜索</el-button>
|
|
<el-button type="primary" @click="handleReset">重置</el-button>
|
|
<el-button @click="handleExport">导出</el-button>
|
|
</el-form-item>
|
|
</el-form>
|
|
</div>
|
|
|
|
<!-- ==================== 表格区域 ==================== -->
|
|
<div class="table-panel">
|
|
<div>
|
|
<el-table
|
|
v-loading="loading"
|
|
:data="tableData"
|
|
border
|
|
stripe
|
|
style="width: 100%"
|
|
:height="tableHeight"
|
|
@selection-change="handleSelectionChange"
|
|
>
|
|
<el-table-column type="selection" width="55" align="center" />
|
|
<el-table-column label="序号" width="90" align="center">
|
|
<template #default="{ $index }">
|
|
{{ tableData[$index].enterpriseName ? $index + 1 : '' }}
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="enterpriseName" label="企业名称" min-width="300" 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.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.xzzfzs"
|
|
type="primary"
|
|
link
|
|
size="small"
|
|
@click="handleDetail(row)"
|
|
>
|
|
详情
|
|
</el-button>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
</div>
|
|
|
|
<!-- ==================== 分页 ==================== -->
|
|
<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"
|
|
class="mt-4"
|
|
@size-change="getList"
|
|
@current-change="getList"
|
|
/>
|
|
</div>
|
|
|
|
<!-- ==================== 企业执法详情弹窗 ==================== -->
|
|
<el-dialog
|
|
v-model="detailDialogVisible"
|
|
width="1100px"
|
|
:title="`${detailEnterprise?.enterpriseName || ''} — ${queryParams.statMonth} 执法明细`"
|
|
append-to-body
|
|
top="5vh"
|
|
destroy-on-close
|
|
>
|
|
<el-alert
|
|
v-if="detailEnterprise"
|
|
:closable="false"
|
|
show-icon
|
|
:title="`所属区:${detailEnterprise.districtName || '-'} | 所属街道:${detailEnterprise.streetName || '-'} | 行政执法次数:${detailEnterprise.xzzfzs ?? 0}次 | 时段:${queryParams.statMonth}`"
|
|
type="info"
|
|
class="mb-4"
|
|
/>
|
|
|
|
<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="enforcementTime" min-width="180" align="center" />
|
|
<el-table-column label="执法单位" prop="bureauDeptName" min-width="260" 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
|
|
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>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped lang="scss">
|
|
/* 外层容器:与 border-card tabs 壳提供的白底带边框容器一致 */
|
|
.app-container {
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.page-card {
|
|
background: #fff;
|
|
border-radius: 4px;
|
|
box-sizing: border-box;
|
|
min-height: calc(100vh - 116px);
|
|
}
|
|
|
|
/* ==================== 搜索面板 ==================== */
|
|
.search-panel {
|
|
background: #fff;
|
|
border: 1px solid #e4e7ed;
|
|
border-radius: 4px;
|
|
padding: 18px 20px 0;
|
|
margin-bottom: 16px;
|
|
|
|
:deep(.el-form-item__label) {
|
|
font-weight: 600;
|
|
color: #333;
|
|
}
|
|
|
|
.search-input {
|
|
width: 210px;
|
|
}
|
|
|
|
.search-select {
|
|
width: 220px;
|
|
}
|
|
|
|
.period-select {
|
|
width: 110px;
|
|
}
|
|
|
|
.period-date {
|
|
width: 150px;
|
|
}
|
|
}
|
|
|
|
/* ==================== 表格面板 ==================== */
|
|
.table-panel {
|
|
background: #fff;
|
|
border: 1px solid #e4e7ed;
|
|
border-radius: 4px;
|
|
padding: 16px 20px;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
/* ==================== 分页 ==================== */
|
|
.mt-4 {
|
|
margin-top: 16px;
|
|
justify-content: flex-end;
|
|
}
|
|
.mb-4 {
|
|
margin-bottom: 16px;
|
|
}
|
|
</style>
|
|
|