You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

373 lines
10 KiB
Vue

<script lang="ts" setup>
import { onBeforeRouteLeave } from "vue-router";
import { BaseTable } from "@/components/CustomTable";
import SwiperMonitor from "./components/SwiperMonitor.vue";
import AppearanceDailyDetectionChart from "@/components/Charts/appearanceDailyDetectionChart.vue";
import AppearanceDailyAlertChart from "@/components/Charts/appearanceDailyAlertChart.vue";
import AppearanceAlarmModal from "./components/AppearanceAlarmModal.vue";
import DeleteModal from "./components/DeleteModal.vue";
import VideoExport from "./components/VideoExport.vue";
import {
getAppearanceMonitorApi,
getAppearanceMonitorDetailApi,
getBeforeMonitorDetailApi,
} from "@/api/dashboard";
import { isSuccessApi } from "@/utils/forApi";
import { useWebSocketStore } from "@/stores/websocketStore";
// const isPointOpen = ref<Boolean>(false); //点云弹窗
const isAlarmOpen = ref<Boolean>(false); //详情弹窗
const isDeleteOpen = ref<Boolean>(false); //删除弹窗
const websocketStore = useWebSocketStore();
// 监听 messages 的变化
watch(
() => websocketStore.messages,
(newMessages: string[], oldMessages: string[]) => {
if (newMessages?.length > 0 && !isAlarmOpen.value) {
console.log(
newMessages[newMessages?.length - 1],
"newMessages[newMessages?.length - 1]"
);
currentRow.value = newMessages[newMessages?.length - 1];
currFileList.value = newMessages[newMessages?.length - 1]?.images;
isAlarmOpen.value = true;
}
},
{ deep: true, immediate: true }
);
const columns = [
{
label: "站点",
property: "station",
width: 75,
},
{
label: "车号",
property: "train_number",
width: 155,
},
{
label: "车型",
property: "train_model",
width: 70,
},
{
label: "车厢号",
property: "train_carriage_number",
width: 95,
},
{
label: "告警类型",
property: "alarm_type",
width: 90,
},
{
label: "故障类型",
property: "fault_type",
width: 90,
},
{
label: "等级",
property: "level",
width: 60,
},
{
label: "时间",
property: "created_at",
},
{
type: "action",
label: "操作",
width: 180,
},
];
const pagination = ref({ currentPage: 1, pageSize: 10, total: 0 });
const listData = ref([]); // 列表数据
const currentRow = ref<Record<string, any>>({}); // 当前选中行
const currFileList = ref<Record<string, any>[]>([]); // 选中行的文件列表
const currBeforeFileList = ref<Record<string, any>[]>([]); // 选中行历史文件列表
// 搜索表单
const searchForm = reactive({
train_number: "",
train_carriage_number: "",
fault_type: "",
station: "",
type: "appearance",
});
const dataLoading = ref(true);
// 文件详情
const getFileList = async () => {
try {
const res = await getAppearanceMonitorDetailApi({
id: currentRow.value?.id,
current: 1,
pageSize: 1000,
});
console.log(res.data, "getDetailList_data");
if (isSuccessApi(res)) {
currFileList.value = res.data.data;
}
} catch (error) {
console.log(error, "getDetailList_error");
}
};
// 故障前详情
const getBeforeFileList = async () => {
try {
const res = await getBeforeMonitorDetailApi({
id: currentRow.value?.id,
current: 1,
pageSize: 1000,
});
console.log(res.data, "getDetailList_data");
if (isSuccessApi(res)) {
currBeforeFileList.value = res.data.data;
}
} catch (error) {
console.log(error, "getDetailList_error");
}
};
// TODO mock 获取视频列表数据
// const getFileList = async () => {
// try {
// const resAll = await fetch('/api/v1/record/record_detail_list/', {
// method: 'POST'
// })
// const res = await resAll.json()
// if (isSuccessApi(res)) {
// currFileList.value = res.data.data;
// }
// } catch (error) {
// console.error('获取数据失败:', error)
// }
// }
function loadDetail() {
currentRow.value = listData.value[0];
getFileList();
getBeforeFileList();
}
// 获取列表
const getList = async () => {
try {
const { currentPage, pageSize } = pagination.value;
const res = await getAppearanceMonitorApi({
...searchForm,
current: currentPage,
pageSize,
});
console.log(res.data, "getList_data");
if (isSuccessApi(res)) {
listData.value = res.data.data;
loadDetail();
pagination.value = {
...pagination.value,
total: res.data.total,
};
}
} catch (error) {
console.error("获取数据失败:", error);
}
};
// 查询方法
const handleQuery = () => {
getList();
};
// 重置方法
const handleReset = () => {
searchForm.train_number = "";
searchForm.station = "";
searchForm.train_carriage_number = "";
searchForm.fault_type = "";
getList();
};
function handleTableChange(record) {
console.log("handleTableChange_record", record);
pagination.value = {
...pagination.value,
currentPage: record.page,
pageSize: record.pageSize,
};
getList();
}
// 定义行类名方法
const handleRowClassName = ({ row }) => {
return row.id === currentRow.value.id ? "selected-row" : "";
};
/**查看详情 */
function openCurrent(row) {
console.log(row, "openCurrent");
currentRow.value = row;
isAlarmOpen.value = true;
}
// 删除
function deleteCurrent(row) {
isDeleteOpen.value = true;
currentRow.value = row;
}
// 行点击事件处理
const handleRowClick = (row, event, rowIndex) => {
currentRow.value = row;
getFileList();
getBeforeFileList();
};
onBeforeRouteLeave(() => {
isAlarmOpen.value = false;
currentRow.value = {};
currFileList.value = [];
});
onMounted(() => {
getList();
});
</script>
<template>
<div class="appearance-monitor-warp">
<div class="appearance-monitor-main-content">
<div class="module-header">
<div class="fg-title pl-[16px] py-[16px]">
<span class="text-[18px]">外观监测</span>
</div>
</div>
<div class="appearance-monitor-body">
<!-- 搜索区域 -->
<div class="flex items-center justify-between">
<div class="appearance-monitor-search-box">
<el-select
v-model="searchForm.station"
placeholder="站点"
class="custom-select"
>
<el-option label="小觉站" value="小觉站"></el-option>
<el-option label="东西站" value="东西站"></el-option>
<el-option label="立杆区" value="立杆区"></el-option>
</el-select>
<el-input
v-model="searchForm.train_number"
placeholder="请输入列车号"
class="custom-input"
clearable
/>
<el-input
v-model="searchForm.train_carriage_number"
placeholder="请输入车厢号"
class="custom-input"
clearable
/>
<el-select
v-model="searchForm.fault_type"
placeholder="故障类型"
class="custom-select"
>
<el-option label="下侧门板缺失" value="下侧门板缺失"></el-option>
<el-option label="门折页座脱落" value="门折页座脱落"></el-option>
<el-option label="小门塔扣丢失" value="小门塔扣丢失"></el-option>
<el-option label="小窗裂纹" value="小窗裂纹"></el-option>
<el-option label="搭扣未搭" value="搭扣未搭"></el-option>
<el-option label="小门外胀" value="小门外胀"></el-option>
</el-select>
<el-button
type="primary"
@click="handleQuery"
class="fg-basic-btn fg-info-button"
>
<span class="icon"></span> 查询
</el-button>
<el-button @click="handleReset" class="fg-basic-btn fg-reset-btn">
<span class="icon"></span> 重置
</el-button>
</div>
<div>
<VideoExport />
</div>
</div>
<div class="flex justify-between appearance-monitor-banner">
<!-- 左侧视频与缩略图区域 -->
<div class="left-panel">
<SwiperMonitor :fileList="currFileList" />
</div>
<!-- 右侧表格区域 -->
<div class="right-panel">
<div class="bg-transparent baseTable_wrap">
<template v-if="pagination.total > 0">
<BaseTable
class="bg-transparent baseTable_box"
:total="pagination.total"
:pageSize="pagination.pageSize"
:dataSource="listData"
:isFixedPagination="true"
:columns="columns"
:page="pagination.currentPage"
@change="handleTableChange"
:row-class-name="handleRowClassName"
@row-click="handleRowClick"
>
<template v-slot:actionBar="{ row }">
<ul class="flex table_action_box">
<li
class="flex items-center mr-[8px]"
@click="openCurrent(row)"
>
<div class="fg-button-primary">详情</div>
</li>
<li
class="flex items-center mr-[8px]"
@click="deleteCurrent(row)"
>
<div class="fg-button-primary-danger">删除</div>
</li>
</ul>
</template>
</BaseTable>
</template>
</div>
</div>
</div>
</div>
</div>
<ul class="fg-footer-charts">
<li>
<div class="fg-footer-charts-title">日检出量</div>
<div class="fg-footer-charts-content">
<AppearanceDailyDetectionChart :datas="[]" />
</div>
</li>
<li>
<div class="fg-footer-charts-title">日告警分类</div>
<div class="fg-footer-charts-content">
<AppearanceDailyAlertChart :data="[]" />
</div>
</li>
</ul>
<AppearanceAlarmModal
v-model:value="isAlarmOpen"
:info="currentRow"
:image="currFileList"
:beforeImage="currBeforeFileList"
@close="isAlarmOpen = false"
/>
<DeleteModal
v-model:value="isDeleteOpen"
@delete-success="getList()"
:info="currentRow"
@close="isDeleteOpen = false"
/>
</div>
</template>
<style lang="scss">
@import url("./AppearanceMonitor.scss");
</style>