feat: 详细清单统计

master
李小林 7 months ago
parent 31b5532801
commit 189d56b9d1
  1. 28
      src/api/report/index.ts
  2. 61
      src/api/report/types.ts
  3. 8
      src/plugins/permission.ts
  4. 1
      src/views/family/operate/remote-operation/components/SoapPackageLog.vue
  5. 467
      src/views/family/statement/detail-inventory-statistics/index.vue
  6. 25
      src/views/family/statement/index.vue

@ -0,0 +1,28 @@
import { AxiosPromise } from "axios";
import request from "@/utils/request";
import { ReportBasicQuery, ReportDetailPageResult } from "@/api/report/types";
export function reportArea(groupId?: number): AxiosPromise<OptionType[]> {
return request({
url: `/api/domain/v1/report-area/options/${groupId}`,
method: "GET",
});
}
export function reportDetailPage(
data: ReportBasicQuery
): AxiosPromise<ReportDetailPageResult> {
return request({
url: `/api/report/v1/detail-inventory-statistics`,
method: "POST",
data,
});
}
export function reportExportDetail(queryParams: ReportBasicQuery) {
return request({
url: `/api/report/v1/_export`,
method: "get",
params: queryParams,
responseType: "arraybuffer",
});
}

@ -0,0 +1,61 @@
export interface ReportBasicQuery extends PageQuery {
area?: number;
subarea?: number;
category?: string;
devName?: string;
accessType?: string;
devType?: string;
gateWayType?: string;
firstStartTime?: string;
firstEndTime?: string;
lastStartTime?: string;
lastEndTime?: string;
onLineStartTime?: string;
onLineEndTime?: string;
}
export interface DetailInventoryStatisticsVO {
area?: String;
subArea?: string;
devId?: string;
category?: string;
devName?: string;
oui?: string;
devSno?: string;
devHard?: string;
devSoft?: string;
gateWayType?: string;
sno?: string;
pppoe?: string;
createTime?: string;
modifyTime?: string;
onLineTime?: string;
rgMode?: string;
}
export type ReportDetailPageResult = PageResult<DetailInventoryStatisticsVO[]>;

@ -6,7 +6,13 @@ import NProgress from "@/utils/nprogress";
export function setupPermission() {
// 白名单路由
const whiteList = ["/login"];
const threeLevelCatalog = ["Operate", "Broadband", "Iptv", "Voip"];
const threeLevelCatalog = [
"Operate",
"Broadband",
"Iptv",
"Voip",
"Statement",
];
router.beforeEach(async (to, from, next) => {
NProgress.start();

@ -103,7 +103,6 @@ import {
Refresh,
} from "@element-plus/icons-vue";
import { confirm } from "@/utils/confirm";
import { exportUser } from "@/api/user";
const route = useRoute();
let devId: number = parseInt(<string>route.params.devId);

@ -1,27 +1,472 @@
<template>
<div class="app-container"></div>
<div class="app-container">
<el-card v-show="active === 0" shadow="never">
<template #header>
<div class="flex justify-between">
<div style="display: flex; align-items: center">
<el-icon size="15"><Grid /></el-icon>&nbsp;<span
style="font-weight: 700; font-size: 14px; line-height: 16px"
>详细清单统计</span
>
</div>
<el-button type="primary" @click="getData">运行报表</el-button>
</div>
</template>
<div>
<el-form :model="queryForm">
<el-descriptions :column="2" border>
<el-descriptions-item
label="分公司"
label-align="left"
align="center"
label-class-name="my-label"
class-name="my-content"
width="150px"
>
<el-form-item prop="area">
<el-select v-model="queryForm.area">
<el-option
v-for="item in areaOption"
:label="item.label"
:value="item.value"
:key="item.value"
/>
</el-select>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item
label="区局"
label-align="left"
align="center"
label-class-name="my-label"
class-name="my-content"
width="150px"
>
<el-form-item prop="subarea">
<el-select v-model="queryForm.subarea">
<el-option
v-for="item in subareaOption"
:label="item.label"
:value="item.value"
:key="item.value"
/>
</el-select>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item
label="设备厂商"
label-align="left"
align="center"
label-class-name="my-label"
class-name="my-content"
width="150px"
>
<el-form-item prop="category">
<el-select v-model="queryForm.category" filterable>
<el-option
v-for="item in vendorNameOption"
:label="item.label"
:value="item.value"
:key="item.value"
/>
</el-select>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item
label="终端型号"
label-align="left"
align="center"
label-class-name="my-label"
class-name="my-content"
width="150px"
>
<el-form-item prop="devName">
<el-select v-model="queryForm.devName" filterable>
<el-option
v-for="item in typeNameOption"
:label="item.label"
:value="item.value"
:key="item.value"
/>
</el-select>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item
label="设备网络侧接口"
label-align="left"
align="center"
label-class-name="my-label"
class-name="my-content"
width="150px"
>
<el-form-item prop="accessType">
<dictionary
v-model="queryForm.accessType"
type-code="dev_access_type"
/>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item
label="设备类型"
label-align="left"
align="center"
label-class-name="my-label"
class-name="my-content"
width="150px"
>
<el-form-item prop="devType">
<dictionary
v-model="queryForm.devType"
type-code="dev_type_name"
/>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item
label="网关类型"
label-align="left"
align="center"
label-class-name="my-label"
class-name="my-content"
width="150px"
>
<el-form-item prop="gateWayType">
<dictionary
v-model="queryForm.gateWayType"
type-code="dev_type_name_new"
/>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item
label="首次装机时间"
label-align="left"
align="center"
label-class-name="my-label"
class-name="my-content"
width="150px"
>
<div class="flex justify-between" style="width: 160px">
<el-form-item prop="firstStartTime">
<el-date-picker
style="width: 230px"
v-model="queryForm.firstStartTime"
type="datetime"
value-format="YYYY-MM-DD hh:mm:ss"
/>
</el-form-item>
<span
style="line-height: 35px; margin-right: 5px; margin-left: 5px"
></span
>
<el-form-item prop="firstEndTime">
<el-date-picker
style="width: 235px"
v-model="queryForm.firstEndTime"
type="datetime"
value-format="YYYY-MM-DD hh:mm:ss"
/>
</el-form-item>
</div>
</el-descriptions-item>
<el-descriptions-item
label="最近一次装机时间"
label-align="left"
align="center"
label-class-name="my-label"
class-name="my-content"
width="150px"
>
<div class="flex justify-between" style="width: 160px">
<el-form-item prop="lastStartTime">
<el-date-picker
style="width: 230px"
v-model="queryForm.lastStartTime"
type="datetime"
/>
</el-form-item>
<span
style="line-height: 35px; margin-right: 5px; margin-left: 5px"
></span
>
<el-form-item prop="lastEndTime">
<el-date-picker
style="width: 235px"
v-model="queryForm.lastEndTime"
type="datetime"
/>
</el-form-item>
</div>
</el-descriptions-item>
<el-descriptions-item
label="最近一次上线时间"
label-align="left"
align="center"
label-class-name="my-label"
class-name="my-content"
width="150px"
>
<div class="flex justify-between" style="width: 160px">
<el-form-item prop="onLineStartTime">
<el-date-picker
style="width: 230px"
v-model="queryForm.onLineStartTime"
type="datetime"
/>
</el-form-item>
<span
style="line-height: 35px; margin-right: 5px; margin-left: 5px"
></span
>
<el-form-item prop="onLineEndTime">
<el-date-picker
style="width: 235px"
v-model="queryForm.onLineEndTime"
type="datetime"
/>
</el-form-item>
</div>
</el-descriptions-item>
</el-descriptions>
</el-form>
</div>
</el-card>
<el-card v-show="active === 1" shadow="never">
<template #header>
<div class="flex justify-between">
<div style="display: flex; align-items: center">
<el-icon size="15"><Grid /></el-icon>&nbsp;<span
style="font-weight: 700; font-size: 14px; line-height: 16px"
>设备信息列表</span
>
</div>
<div>
<el-button type="primary" @click="exportDetail">导出报表</el-button>
<el-button type="primary" @click="back">返回</el-button>
</div>
</div>
</template>
<div class="any-table">
<el-table
:data="tableData"
v-loading="loading"
style="width: 100%"
max-height="450"
border
>
<el-table-column
label="分公司"
prop="area"
align="center"
fixed
width="100"
/>
<el-table-column
label="区局"
prop="subArea"
align="center"
width="150"
/>
<el-table-column
label="厂商"
prop="category"
align="center"
width="150"
show-overflow-tooltip
/>
<el-table-column
label="设备型号"
prop="oui"
align="center"
width="100"
/>
<el-table-column
label="设备序列号"
prop="devSno"
align="center"
width="200"
/>
<el-table-column
label="硬件版本"
prop="devHard"
align="center"
width="150"
/>
<el-table-column
label="软件版本"
prop="devSoft"
align="center"
width="200"
/>
<el-table-column
label="网关类型"
prop="gateWayType"
align="center"
width="100"
/>
<el-table-column
label="接入类型"
prop="rgMode"
align="center"
width="100"
/>
<el-table-column
label="逻辑ID"
prop="sno"
align="center"
width="150"
/>
<el-table-column
label="宽带账号"
prop="pppoe"
align="center"
width="150"
/>
<el-table-column
label="首次装机时间"
prop="createTime"
width="200"
align="center"
/>
<el-table-column
label="最近一次装机时间"
prop="modifyTime"
width="200"
align="center"
/>
<el-table-column
label="最近一次上线时间"
prop="onLineTime"
width="200"
align="center"
/>
</el-table>
<pagination
v-if="total > 0"
v-model:total="total"
v-model:page="queryForm.pageNum"
v-model:limit="queryForm.pageSize"
@pagination="getData"
/>
</div>
</el-card>
</div>
</template>
<script setup lang="ts">
import { Grid } from "@element-plus/icons-vue";
import {
DetailInventoryStatisticsVO,
ReportBasicQuery,
} from "@/api/report/types";
import { reportArea, reportDetailPage, reportExportDetail } from "@/api/report";
import { getTypeNameOption, getVendorNameOption } from "@/api/device-type";
import { confirm } from "@/utils/confirm";
defineOptions({
name: "DetailInventoryStatistics",
inheritAttrs: false,
});
const queryForm = ref<SelectForm>({
const queryForm = ref<ReportBasicQuery>({
pageNum: 1,
pageSize: 10,
});
const options = ref<OptionType[]>([
{ label: "客户名称", value: "customName" },
{ label: "系统管理域", value: "regionAreaId" },
]);
const buttonColSpan = computed(() => {
return queryForm.value.selectName === undefined ? 18 : 12;
});
const resetSelect = () => {
queryForm.value.selectValue = undefined;
const active = ref<number>(0);
const areaOption = ref<OptionType[]>([{ label: "全省", value: 0 }]);
const subareaOption = ref<OptionType[]>([]);
const vendorNameOption = ref<OptionType[]>([{ label: "全选", value: "-1" }]);
const typeNameOption = ref<OptionType[]>([{ label: "全选", value: "-1" }]);
const tableData = ref<DetailInventoryStatisticsVO[]>([]);
const loading = ref<boolean>(false);
const total = ref<number>(0);
watch(
() => queryForm.value.area,
() => {
if (queryForm.value.area === undefined) {
return;
}
loadSubarea(queryForm.value.area);
}
);
const getData = () => {
active.value = 1;
loading.value = true;
reportDetailPage(queryForm.value)
.then(({ data }) => {
tableData.value = data.list;
total.value = data.total;
})
.finally(() => {
loading.value = false;
});
};
const back = () => {
active.value = 0;
};
const loadVendorNameOption = () => {
getVendorNameOption().then(({ data }) => {
vendorNameOption.value.push(...data);
});
};
const loadSubarea = (groupId: number) => {
reportArea(groupId).then(({ data }) => {
subareaOption.value = data;
});
};
const loadTypeNameOption = () => {
getTypeNameOption("").then(({ data }) => {
typeNameOption.value.push(...data);
});
};
const loadArea = (groupId: number) => {
reportArea(groupId).then(({ data }) => {
areaOption.value.push(...data);
});
};
const exportDetail = () => {
confirm("确认导出该数据吗", () => {
loading.value = true;
reportExportDetail(queryForm.value)
.then((response: any) => {
const fileData = response.data;
const fileName = decodeURI(
response.headers["content-disposition"].split(";")[1].split("=")[1]
);
const fileType =
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8";
const blob = new Blob([fileData], { type: fileType });
const downloadUrl = window.URL.createObjectURL(blob);
const downloadLink = document.createElement("a");
downloadLink.href = downloadUrl;
downloadLink.download = fileName;
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
window.URL.revokeObjectURL(downloadUrl);
})
.finally(() => {
loading.value = false;
});
});
};
onMounted(() => {
loadArea(0);
loadVendorNameOption();
loadTypeNameOption();
});
</script>
<style scoped lang="scss">
:deep(.my-label) {
width: 50px !important;
background: var(--el-color-white) !important;
}
:deep(.el-card__body) {
padding: 10px 5px;
}
:deep(.el-form-item--default) {
margin-bottom: 0;
}
</style>

@ -1,6 +1,23 @@
<template>
<div>
<router-view />
</div>
<router-view>
<template #default="{ Component, route }">
<transition
enter-active-class="animate__animated animate__fadeIn"
mode="out-in"
>
<keep-alive :include="cachedViews">
<component :is="Component" :key="route.path" />
</keep-alive>
</transition>
</template>
</router-view>
</template>
<script setup lang="ts"></script>
<script setup lang="ts">
import { useTagsViewStore } from "@/store";
defineOptions({
name: "Statement",
inheritAttrs: false,
});
const cachedViews = computed(() => useTagsViewStore().cachedViews); // ;
</script>

Loading…
Cancel
Save