651 lines
20 KiB
Vue
651 lines
20 KiB
Vue
<template>
|
||
<div class="alert-container">
|
||
<!-- <el-row class="top-pan">
|
||
<el-col :sm="24" :md="12" class="panel-section">
|
||
<statistics />
|
||
</el-col>
|
||
<el-col :sm="24" :md="12" class="panel-section">
|
||
<alertChart />
|
||
</el-col>
|
||
</el-row> -->
|
||
<!-- <el-row class="middle-row">
|
||
<span>
|
||
告警总数:{{ displayTotalItems }}
|
||
</span>
|
||
</el-row> -->
|
||
<el-row class="filter-row">
|
||
<el-col :span="4">
|
||
<el-form-item label="摄像头名称">
|
||
<el-select v-model="filterParams.cameraId" placeholder="请选择摄像头" filterable clearable>
|
||
<el-option v-for="camera in cameras" :key="camera.id" :label="camera.name" :value="camera.id">
|
||
</el-option>
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="3">
|
||
<el-form-item label="告警类型">
|
||
<el-select v-model="filterParams.types" placeholder="请选择类型" clearable>
|
||
<el-option v-for="(label, key) in typeMapping" :key="key" :label="label" :value="key"></el-option>
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="3">
|
||
<el-form-item label="处理情况">
|
||
<el-select v-model="filterParams.status" placeholder="全部" clearable>
|
||
<el-option label="全部" value=""></el-option>
|
||
<el-option label="待处理" value="pending"></el-option>
|
||
<el-option label="已处理" value="closed"></el-option>
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="4">
|
||
<el-form-item label="开始时间">
|
||
<el-date-picker v-model="filterParams.timeAfter" type="datetime" placeholder="请选择开始时间"></el-date-picker>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="4">
|
||
<el-form-item label="结束时间">
|
||
<el-date-picker v-model="filterParams.timeBefore" type="datetime" placeholder="请选择结束时间"></el-date-picker>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="4" class="filter-buttons">
|
||
<el-button type="primary" @click="handleFilter">查询</el-button>
|
||
<el-button @click="handleReset">重置</el-button>
|
||
<el-button :disabled="isExporting" @click="exportData">
|
||
{{ isExporting ? '正在导出,请勿重复点击' : '导出' }}
|
||
</el-button>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<el-row class="table-row">
|
||
<el-col :span="24" class="table-col">
|
||
<div class="table-container">
|
||
<el-table :data="tableData" header-row-class-name="table-header" :fit="true" height="580">
|
||
<el-table-column prop="id" label="告警编号" min-width="100"></el-table-column>
|
||
<el-table-column label="告警类型" min-width="150">
|
||
<template v-slot="scope">
|
||
{{ typeMapping[scope.row.types] }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="camera.name" label="告警位置" min-width="150"></el-table-column>
|
||
<el-table-column label="告警时间" min-width="200">
|
||
<template v-slot="scope">
|
||
{{ formatDateTime(scope.row.ended_at) }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="status" label="告警状态" min-width="100">
|
||
<template v-slot="scope">
|
||
<el-tag :type="scope.row.status === 'pending' ? 'warning' : 'success'">{{ statusMapping[scope.row.status]
|
||
}}</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="操作" min-width="150">
|
||
<template v-slot="scope">
|
||
<el-button type="text" @click="handleView(scope.row)">查看</el-button>
|
||
<el-button type="text" v-if="scope.row.status === 'pending'"
|
||
@click="submitStatusUpdate('closed', scope.row)">
|
||
标记为已处理
|
||
</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</div>
|
||
<div class="pagination-container">
|
||
<el-pagination @size-change="handleSizeChange" @current-change="handlePageChange" :current-page="currentPage"
|
||
:page-size="pageSize" :total="totalItems" layout="total, sizes, prev, pager, next, jumper">
|
||
</el-pagination>
|
||
</div>
|
||
</el-col>
|
||
</el-row>
|
||
<el-dialog v-model="dialogVisible" title="告警详情" width="80%">
|
||
<div>
|
||
<el-row>
|
||
<el-col :span="6">
|
||
<p>告警编号: {{ selectedRow.id }}</p>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<p>告警类型: {{ typeMapping[selectedRow.types] }}</p>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<p>告警位置: {{ selectedRow.camera.name }}</p>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<p>告警时间: {{ selectedRow.formatted_started_at }}</p>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row>
|
||
<el-col :span="6">
|
||
<p>告警状态: <el-tag :type="selectedRow.status === 'pending' ? 'warning' : 'success'">{{
|
||
statusMapping[selectedRow.status]
|
||
}}</el-tag></p>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<p>摄像头编号: {{ selectedRow.camera_id }}</p>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<p>持续时间: {{ duration }}</p>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<p>备注: {{ selectedRow.remark }}</p>
|
||
</el-col>
|
||
</el-row>
|
||
<!-- <div class="event-media">
|
||
<div v-for="medium in selectedRow.mediums" :key="medium.id" class="media-container">
|
||
<div v-if="medium.name === 'video'" class="media-item video-item">
|
||
<p>告警关联视频</p>
|
||
<video :src="medium.file" controls></video>
|
||
</div>
|
||
<div v-if="medium.name === 'snapshot'" class="media-item image-item">
|
||
<p>告警关联图片</p>
|
||
<el-image :src="medium.file" fit="contain"></el-image>
|
||
</div>
|
||
</div>
|
||
</div> -->
|
||
<div class="event-media" :class="{ 'center-media': mediums.length === 1 }">
|
||
<!-- 告警关联视频 -->
|
||
<div v-if="hasVideo" class="media-container video-item">
|
||
<p>告警关联视频</p>
|
||
<video :src="videoFile" controls @click="openMediaDialog('video', videoFile)"></video>
|
||
</div>
|
||
|
||
<!-- 告警关联图片 -->
|
||
<div v-if="hasSnapshot" class="media-container image-item">
|
||
<p>告警关联图片</p>
|
||
<el-image :src="snapshotFile" fit="contain" @click="openMediaDialog('image', snapshotFile)"></el-image>
|
||
</div>
|
||
</div>
|
||
<!-- 备注输入框 -->
|
||
<el-row>
|
||
<el-col :span="24">
|
||
<el-form-item>
|
||
<el-input v-model="remark" placeholder="请描述事件原因,处理过程方法,以及处理结果" type="textarea" rows="5"></el-input>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<!-- 条件显示按钮 -->
|
||
<el-row class="dialog-button">
|
||
<el-col :span="24" style="text-align: center;">
|
||
<el-button v-if="selectedRow.status === 'pending'" type="primary" @click="submitStatusUpdate('closed')">
|
||
标记为已处理
|
||
</el-button>
|
||
<el-button v-else type="primary" @click="submitStatusUpdate(selectedRow.status)">
|
||
提交
|
||
</el-button>
|
||
</el-col>
|
||
</el-row>
|
||
</div>
|
||
</el-dialog>
|
||
<el-dialog v-model="mediaDialogVisible" width="70%">
|
||
<div v-if="mediaType === 'image'">
|
||
<el-image :src="mediaSrc" fit="contain" style="width: 100%;"></el-image>
|
||
</div>
|
||
<div v-if="mediaType === 'video'">
|
||
<video :src="mediaSrc" controls style="width: 100%;"></video>
|
||
</div>
|
||
</el-dialog>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, reactive, onMounted, computed } from 'vue';
|
||
// import Statistics from '@/components/Statistics.vue';
|
||
// import AlertChart from '@/components/AlertChart.vue';
|
||
import { BoxApi } from '@/utils/boxApi.ts'; // 导入 BoxApi
|
||
|
||
import { saveAs } from 'file-saver';
|
||
import Papa from 'papaparse';
|
||
|
||
// 创建 BoxApi 实例
|
||
const boxApi = new BoxApi();
|
||
|
||
// 响应式数据
|
||
const tableData = ref([]);
|
||
const dialogVisible = ref(false);
|
||
const remark = ref("");
|
||
const mediaDialogVisible = ref(false);
|
||
const mediaType = ref('');
|
||
const mediaSrc = ref('');
|
||
|
||
const selectedRow = ref({});
|
||
const mediums = ref([]);
|
||
const duration = ref('');
|
||
const typeMapping = reactive({});
|
||
const statusMapping = {
|
||
'pending': '待处理',
|
||
'assigned': '处理中',
|
||
'closed': '已处理'
|
||
};
|
||
const currentPage = ref(1);
|
||
const pageSize = ref(20);
|
||
const token = ref(null);
|
||
const totalItems = ref(0);
|
||
const displayTotalItems = ref(0); // 用于展示的数字
|
||
const cameras = ref([]);
|
||
|
||
const formatDateTimeToISO = (datetime) => {
|
||
return new Date(datetime).toISOString().replace('.000', '');
|
||
};
|
||
|
||
|
||
const isExporting = ref(false);
|
||
// 导出数据方法
|
||
const exportData = async () => {
|
||
if (isExporting.value) return; // 如果已经在导出,直接返回,避免重复点击
|
||
|
||
isExporting.value = true; // 设置为正在导出状态
|
||
try {
|
||
const allData = [];
|
||
let page = 1;
|
||
let totalPages = 1;
|
||
const limit = 500;
|
||
|
||
// 循环分页请求所有数据
|
||
do {
|
||
const { results, count } = await boxApi.getEventsByParams(
|
||
token.value,
|
||
limit,
|
||
page,
|
||
filterParams.timeBefore ? formatDateTimeToISO(filterParams.timeBefore) : null,
|
||
filterParams.timeAfter ? formatDateTimeToISO(filterParams.timeAfter) : null,
|
||
filterParams.types,
|
||
filterParams.cameraId,
|
||
filterParams.status
|
||
);
|
||
|
||
// 格式化数据
|
||
const formattedData = results.map(item => ({
|
||
'告警编号': item.id,
|
||
'告警类型': typeMapping[item.types] || item.types,
|
||
'告警位置': item.camera ? item.camera.name : '',
|
||
'告警时间': formatDateTime(item.ended_at),
|
||
'告警状态': statusMapping[item.status] || item.status
|
||
}));
|
||
|
||
allData.push(...formattedData);
|
||
totalPages = Math.ceil(count / limit);
|
||
page += 1;
|
||
} while (page <= totalPages);
|
||
|
||
// 使用 PapaParse 将 JSON 转换为 CSV,指定列标题
|
||
const csv = Papa.unparse(allData, {
|
||
columns: ['告警编号', '告警类型', '告警位置', '告警时间', '告警状态']
|
||
});
|
||
|
||
// 添加 BOM,确保中文编码正确
|
||
const blob = new Blob(["\uFEFF" + csv], { type: 'text/csv;charset=utf-8;' });
|
||
saveAs(blob, 'Alert_data.csv');
|
||
} catch (error) {
|
||
console.error("导出数据错误:", error);
|
||
}finally {
|
||
isExporting.value = false;
|
||
}
|
||
};
|
||
|
||
|
||
const fetchCameras = async () => {
|
||
try {
|
||
const token = localStorage.getItem('alertToken');
|
||
const limit = 20; // 每次请求 20 条数据
|
||
let offset = 0; // 从 0 开始
|
||
let allCameras = [];
|
||
|
||
// 第一次请求,用于获取总数
|
||
const firstResponse = await boxApi.getCameras(limit, offset, token);
|
||
const cameraCount = firstResponse.count;
|
||
allCameras = firstResponse.results;
|
||
|
||
// 根据总数继续请求剩余的数据
|
||
while (offset + limit < cameraCount) {
|
||
offset += limit;
|
||
const response = await boxApi.getCameras(limit, offset, token);
|
||
allCameras = allCameras.concat(response.results);
|
||
}
|
||
|
||
cameras.value = allCameras; // 存储所有摄像头信息
|
||
} catch (error) {
|
||
console.error("Error fetching cameras:", error);
|
||
}
|
||
};
|
||
|
||
|
||
|
||
|
||
const filterParams = reactive({
|
||
types: null,
|
||
timeAfter: null,
|
||
timeBefore: null,
|
||
cameraId: null,
|
||
status: null,
|
||
});
|
||
|
||
|
||
// 更新状态和备注的方法
|
||
const submitStatusUpdate = async (newStatus, row = null) => {
|
||
try {
|
||
// console.console.log(row,row.id);
|
||
const eventId = row ? row.id : selectedRow.value.id;
|
||
const remarkContent = remark.value && remark.value.trim() !== "" ? remark.value : null;
|
||
|
||
// 调用 setEventStatus 更新事件状态和备注
|
||
await boxApi.setEventStatus(eventId, newStatus, remarkContent);
|
||
|
||
|
||
if (row) {
|
||
row.status = newStatus;
|
||
row.remark = remarkContent; // 更新该行的备注
|
||
} else {
|
||
selectedRow.value.status = newStatus;
|
||
selectedRow.value.remark = remarkContent;
|
||
dialogVisible.value = false; // 关闭弹窗
|
||
remark.value = ""; // 清空备注
|
||
}
|
||
} catch (error) {
|
||
console.error("Error updating event status:", error);
|
||
}
|
||
};
|
||
|
||
const handleFilter = async () => {
|
||
try {
|
||
const types = filterParams.types || null;
|
||
// const timeAfter = filterParams.timeAfter ? new Date(filterParams.timeAfter).toISOString() : null;
|
||
// const timeBefore = filterParams.timeBefore ? new Date(filterParams.timeBefore).toISOString() : null;
|
||
const timeAfter = filterParams.timeAfter ? formatDateTimeToISO(new Date(filterParams.timeAfter)) : null;
|
||
const timeBefore = filterParams.timeBefore ? formatDateTimeToISO(new Date(filterParams.timeBefore)) : null;
|
||
const cameraId = filterParams.cameraId || null;
|
||
const status = filterParams.status || null;
|
||
|
||
const { results, count } = await boxApi.getEventsByParams(
|
||
token.value,
|
||
pageSize.value,
|
||
currentPage.value,
|
||
timeBefore,
|
||
timeAfter,
|
||
types,
|
||
cameraId,
|
||
status
|
||
);
|
||
tableData.value = results;
|
||
totalItems.value = count;
|
||
} catch (error) {
|
||
console.error("Error fetching filtered events:", error);
|
||
}
|
||
};
|
||
|
||
const handleReset = () => {
|
||
filterParams.types = null;
|
||
filterParams.timeAfter = null;
|
||
filterParams.timeBefore = null;
|
||
filterParams.cameraId = null;
|
||
filterParams.status = null;
|
||
fetchEvents(); // 重置筛选条件后,重新获取所有告警数据
|
||
};
|
||
|
||
|
||
|
||
const openMediaDialog = (type, src) => {
|
||
mediaType.value = type; // 设置媒体类型
|
||
mediaSrc.value = src; // 设置媒体源文件
|
||
mediaDialogVisible.value = true; // 打开弹窗
|
||
};
|
||
|
||
// 计算属性:根据 mediums 动态判断是否存在视频和图片
|
||
const hasVideo = computed(() => mediums.value.some(medium => medium.name === 'video'));
|
||
const hasSnapshot = computed(() => mediums.value.some(medium => medium.name === 'snapshot'));
|
||
|
||
// 获取视频和图片的文件
|
||
const videoFile = computed(() => {
|
||
const video = mediums.value.find(medium => medium.name === 'video');
|
||
return video ? video.file : '';
|
||
});
|
||
|
||
const snapshotFile = computed(() => {
|
||
const snapshot = mediums.value.find(medium => medium.name === 'snapshot');
|
||
return snapshot ? snapshot.file : '';
|
||
});
|
||
|
||
// 获取类型映射
|
||
const fetchTypeMapping = async (token) => {
|
||
try {
|
||
const algorithms = await boxApi.getAlgorithms(token); // 使用 BoxApi 的 getAlgorithms 方法
|
||
const additionalMappings = [{ code_name: 'minizawu:532', name: '杂物堆积' }];
|
||
algorithms.forEach((algorithm) => {
|
||
typeMapping[algorithm.code_name] = algorithm.name;
|
||
});
|
||
} catch (error) {
|
||
console.error("Error fetching algorithms:", error);
|
||
}
|
||
};
|
||
|
||
// 获取告警数据
|
||
const fetchEvents = async () => {
|
||
try {
|
||
const { tableData: data, totalItems: total } = await boxApi.getEvents(token.value, pageSize.value, currentPage.value); // 使用 BoxApi 的 getEvents 方法
|
||
tableData.value = data;
|
||
totalItems.value = total;
|
||
animateNumberChange(total);
|
||
} catch (error) {
|
||
console.error("Error fetching events data:", error);
|
||
}
|
||
};
|
||
|
||
const animateNumberChange = (newTotal) => {
|
||
const stepTime = 50; // 每次递增的时间间隔
|
||
// const stepCount = Math.abs(newTotal - displayTotalItems.value);
|
||
const stepCount = Math.abs(newTotal - displayTotalItems.value) / 20;
|
||
|
||
const incrementStep = Math.ceil((newTotal - displayTotalItems.value) / stepCount); // 每次递增的值
|
||
|
||
const step = () => {
|
||
if (displayTotalItems.value < newTotal) {
|
||
displayTotalItems.value = Math.min(displayTotalItems.value + incrementStep, newTotal);
|
||
requestAnimationFrame(step);
|
||
} else if (displayTotalItems.value > newTotal) {
|
||
displayTotalItems.value = Math.max(displayTotalItems.value - incrementStep, newTotal);
|
||
requestAnimationFrame(step);
|
||
}
|
||
};
|
||
|
||
step();
|
||
};
|
||
|
||
// 查看告警详情
|
||
const handleView = (row) => {
|
||
selectedRow.value = row;
|
||
duration.value = calculateDuration(row.started_at, row.ended_at);
|
||
row.formatted_started_at = formatDateTime(row.started_at);
|
||
dialogVisible.value = true;
|
||
mediums.value = row.mediums || [];
|
||
remark.value = row.remark || "";
|
||
};
|
||
|
||
const closeDialog = () => {
|
||
remark.value = "";
|
||
};
|
||
|
||
// 分页处理
|
||
const handlePageChange = (page) => {
|
||
currentPage.value = page;
|
||
if (filterParams.types || filterParams.timeAfter || filterParams.timeBefore || filterParams.cameraId || filterParams.status) {
|
||
handleFilter();
|
||
} else {
|
||
fetchEvents();
|
||
}
|
||
};
|
||
|
||
// 页大小处理
|
||
const handleSizeChange = (size) => {
|
||
pageSize.value = size;
|
||
fetchEvents();
|
||
};
|
||
|
||
// 计算持续时间
|
||
const calculateDuration = (started_at, ended_at) => {
|
||
const start = new Date(started_at);
|
||
const end = new Date(ended_at);
|
||
const durationMs = end - start;
|
||
const minutes = Math.floor(durationMs / 60000);
|
||
const seconds = ((durationMs % 60000) / 1000).toFixed(0);
|
||
return `${minutes}分${seconds < 10 ? '0' : ''}${seconds}秒`;
|
||
};
|
||
|
||
// 格式化日期时间
|
||
const formatDateTime = (datetime) => {
|
||
const date = new Date(datetime);
|
||
const year = date.getFullYear();
|
||
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
||
const day = date.getDate().toString().padStart(2, '0');
|
||
const hours = date.getHours().toString().padStart(2, '0');
|
||
const minutes = date.getMinutes().toString().padStart(2, '0');
|
||
const seconds = date.getSeconds().toString().padStart(2, '0');
|
||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||
};
|
||
|
||
// 页面加载后执行
|
||
onMounted(async () => {
|
||
token.value = localStorage.getItem('alertToken');
|
||
await fetchTypeMapping(token.value);
|
||
await fetchEvents();
|
||
await fetchCameras();
|
||
});
|
||
</script>
|
||
|
||
<style scoped>
|
||
.alert-container {
|
||
padding: 0;
|
||
margin: 0;
|
||
background-color: #001529;
|
||
overflow-y: auto;
|
||
height: 100%;
|
||
}
|
||
|
||
/* .top-pan {
|
||
padding: 0px;
|
||
margin: 0px;
|
||
display: flex;
|
||
gap: 10px;
|
||
background-color: #fff;
|
||
overflow: auto;
|
||
}
|
||
|
||
.panel-section {
|
||
flex: 1;
|
||
background-color: #fff;
|
||
box-shadow: 0 20px 8px rgba(0, 0, 0, 0.1);
|
||
border-radius: 15px;
|
||
} */
|
||
|
||
/* .middle-row {
|
||
min-height: 5vw;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-content: center;
|
||
font-size: 30px;
|
||
font-weight: bold;
|
||
background: linear-gradient(to bottom left, rgb(150, 151, 243), rgb(215, 214, 250));
|
||
border-radius: 8px;
|
||
} */
|
||
|
||
.filter-row {
|
||
display: flex;
|
||
justify-content: center;
|
||
align-content: center;
|
||
height: 15vh;
|
||
padding: 0px 0;
|
||
margin: 5vh 6vw 5vh 1vw;
|
||
gap: 8px;
|
||
/* font-weight: bold; */
|
||
font-size: 15px;
|
||
background-color: rgb(251, 254, 255);
|
||
border-radius: 8px;
|
||
}
|
||
|
||
.table-container {
|
||
max-width: 100%;
|
||
height: 80%;
|
||
/* min-height: 50vh; */
|
||
/* max-height: 70vh; */
|
||
overflow-x: auto;
|
||
margin: 0vh 6vw 0vh 1vw;
|
||
padding: 0;
|
||
overflow-y: hidden;
|
||
border-radius: 8px 8px 0 0;
|
||
background-color: white;
|
||
|
||
}
|
||
|
||
.pagination-container {
|
||
display: flex;
|
||
justify-content: flex-start;
|
||
background-color: #e9eefc;
|
||
padding-left: 20px;
|
||
margin: 0vh 6vw 5vh 1vw;
|
||
}
|
||
|
||
.table-header {
|
||
background-color: #f7f8fa;
|
||
font-weight: bold;
|
||
}
|
||
|
||
::v-deep .el-table th.el-table__cell {
|
||
background-color: #e9eefca4;
|
||
color: #000;
|
||
}
|
||
|
||
.event-media {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
flex-wrap: wrap;
|
||
width: 100%;
|
||
}
|
||
|
||
.center-media {
|
||
justify-content: center;
|
||
}
|
||
|
||
.media-container {
|
||
flex: 0 0 48%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-bottom: 20px;
|
||
box-sizing: border-box;
|
||
padding: 10px;
|
||
}
|
||
|
||
.table-col {
|
||
max-height: 100%;
|
||
}
|
||
|
||
.video-item video,
|
||
.image-item img {
|
||
width: 100%;
|
||
height: auto;
|
||
border-radius: 8px;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.video-item p,
|
||
.image-item p {
|
||
margin-bottom: 8px;
|
||
text-align: center;
|
||
font-weight: bold;
|
||
}
|
||
|
||
|
||
|
||
::v-deep .pagination-container .el-pagination__total,
|
||
::v-deep .pagination-container .el-pagination__goto,
|
||
::v-deep .pagination-container .el-pagination__classifier {
|
||
color: #000;
|
||
}
|
||
|
||
.dialog-button {
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
margin-top: 20px;
|
||
}
|
||
</style>
|