Files
account-manager/src/components/mainPage/monitoring.vue
2026-06-04 16:20:45 +08:00

668 lines
21 KiB
Vue

<template>
<div>
<!-- 表格 -->
<el-card>
<div class="flex justify-center flex-wrap">
<TabelTools @showTools="showTools" :dialogObj="monitoringMsgActive" />
<el-table
ref="tableRef"
:data="tableData"
style="width: 100%"
max-height="50rem"
table-layout="auto"
scrollbar-always-on
@selection-change="handleSelectionChange"
>
<el-table-column
v-if="dialogOperationVisible"
fixed="left"
type="selection"
width="40"
column-key="selection"
/>
<TabelSelect
v-if="dialogOperationVisible"
ref="DelSelectRef"
:holderText="delSelectHold"
singleColor="danger"
multipleColor="warning"
allColor="danger"
singleSize="small"
multipleSize="small"
allSize="small"
:singlePlain="true"
:multiplePlain="false"
:allPlain="false"
operateType="del"
@showComfirmDialog="showComfirmDialog"
/>
<el-table-column min-width="100" column-key="operate">
<template #header>
<SelectSearch
v-if="dialogSearchVisible"
ref="SelectOperateSearchRef"
holderText="选择操作名称以搜索相关数据"
:options="operateOptions"
@search="monitoringMsg"
/>
<div>
<el-text class="mx-1"> 操作名称 </el-text>
</div>
</template>
<template #default="scope">
<div
:style="styleShunt(operateOptions, scope.row.operate)"
class="flex justify-start items-center text-base"
>
{{ labelShunt(operateOptions, scope.row.operate) }}
</div>
</template>
</el-table-column>
<el-table-column min-width="100" column-key="operate_msg">
<template #header>
<div>
<SearchKey
v-if="dialogSearchVisible"
ref="SearchOperateMsgKeyRef"
holderText="输入操作描述以搜索相关数据"
@search="monitoringMsg"
/>
<div>
<el-text class="mx-1"> 操作描述 </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.operate_msg }}
</div>
</template>
</el-table-column>
<el-table-column min-width="100" column-key="type">
<template #header>
<SelectSearch
v-if="dialogSearchVisible"
ref="SelectTypeSearchRef"
holderText="选择危险等级以搜索相关数据"
:options="typeOptions"
@search="monitoringMsg"
/>
<div>
<el-text class="mx-1"> 危险等级 </el-text>
</div>
</template>
<template #default="scope">
<div
:style="styleShunt(typeOptions, scope.row.type)"
class="flex justify-start items-center text-base"
>
{{ labelShunt(typeOptions, scope.row.type) }}
</div>
</template>
</el-table-column>
<el-table-column min-width="150" column-key="status">
<template #header>
<SelectSearch
v-if="dialogSearchVisible"
ref="SelectStatusSearchRef"
holderText="选择执行状态以搜索相关数据"
:options="statusOptions"
@search="monitoringMsg"
/>
<div>
<el-text class="mx-1"> 执行状态 </el-text>
</div>
</template>
<template #default="scope">
<div
:style="styleShunt(statusOptions, scope.row.status)"
class="flex justify-start items-center text-base"
>
{{ labelShunt(statusOptions, scope.row.status) }}
</div>
</template>
</el-table-column>
<el-table-column min-width="80" column-key="affect_entries">
<template #header>
<div>
<div>
<el-text class="mx-1"> 影响到的数据数目 </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.affect_entries }}
</div>
</template>
</el-table-column>
<el-table-column min-width="140" column-key="user">
<template #header>
<div>
<SearchKey
v-if="dialogSearchVisible"
ref="SearchUserKeyRef"
holderText="输入操作者uid以搜索相关数据"
@search="monitoringMsg"
/>
<div>
<el-text class="mx-1"> 操作者uid </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.user }}
</div>
</template>
</el-table-column>
<el-table-column min-width="140" column-key="name">
<template #header>
<div>
<SearchKey
v-if="dialogSearchVisible"
ref="SearchNameKeyRef"
holderText="输入操作者姓名以搜索相关数据"
@search="monitoringMsg"
/>
<div>
<el-text class="mx-1"> 操作者姓名 </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.name }}
</div>
</template>
</el-table-column>
<el-table-column min-width="200" column-key="created_at">
<template #header>
<DateTimePicker
v-if="dialogSearchVisible"
ref="createdDateTimePickerRef"
DateTimePicker="createdDateTimePicker"
@search="monitoringMsg"
/>
<div>
<el-text class="mx-1"> 行为记录时间 </el-text>
<SortSearch
v-if="dialogSortVisible"
ref="createdSortDateTimeRef"
activeText="顺序"
inactiveText="倒序"
@search="monitoringMsg"
/>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.created_at }}
</div>
</template>
</el-table-column>
</el-table>
<!-- 表格 -->
<Pagination
ref="PaginationRef"
:active="monitoringMsgActive"
@sizeChangeBack="changePage"
@search="monitoringMsg"
/>
</div>
</el-card>
<PasswordOauth
ref="delPasswordOauthRef"
operateLabel="删除系统记录"
@requestOperate="requestOperate"
/>
<DownloadData
ref="downloadDataRef"
operateLabel="导出系统记录数据"
@downloadData="executeDownloadData"
/>
</div>
</template>
<script setup>
import { ref, reactive, watch, onMounted, nextTick } from "vue";
import { ElMessage } from "element-plus";
import TabelTools from "../operate/tabelTools.vue";
import TabelSelect from "../operate/tabelSelect.vue";
import SearchKey from "../search/searchKey.vue";
import SortSearch from "../search/sortSearch.vue";
import SelectSearch from "../search/selectSearch.vue";
import DateTimePicker from "../search/dateTimePicker.vue";
import Pagination from "../search/pagination.vue";
import PasswordOauth from "../operate/passwordOauth.vue";
import DownloadData from "../operate/downloadData.vue";
import monitoringActiveStore from "../../stores/monitoring";
import moment from "moment";
import $http from "../../api/index";
import $url from "../../api/url";
import { encrypt } from "../../utils/aes";
import { judgment } from "../../utils/judgment.js";
import { hasNullOrEmptyPropertyWithTypeof } from "../../utils/hasNullOrEmptyPropertyWithTypeof.js";
import { downloadData } from "../../utils/downloadData.js";
const monitoringMsgActive = monitoringActiveStore();
const delSelectHold = {
single: "删除",
multiple: "删除选中",
all: "删除所有",
label: "删除系统记录",
};
const DelSelectRef = ref(null);
const SelectOperateSearchRef = ref(null);
const SearchOperateMsgKeyRef = ref(null);
const SelectTypeSearchRef = ref(null);
const SelectStatusSearchRef = ref(null);
const SearchUserKeyRef = ref(null);
const SearchNameKeyRef = ref(null);
const createdDateTimePickerRef = ref(null);
const createdSortDateTimeRef = ref(null);
const PaginationRef = ref(null);
const downloadDataRef = ref(null);
const delPasswordOauthRef = ref(null);
const dialogSearchVisible = ref(false);
const dialogSortVisible = ref(false);
const dialogOperationVisible = ref(false);
const tableRef = ref();
const offsetOff = ref(true);
const pageCount = ref(0);
const operate = ref("del");
const searchOptions = reactive({
operate: "",
operate_msg: "",
type: "",
status: "",
affect_entries: "",
user: "",
created_at: "",
});
const sortOptions = reactive({
created_at: false,
});
const tableData = ref([]);
const operateOptions = [
{
value: "0",
label: "新增数据",
style: "color: rgb(16, 184, 130);background-color: rgba(16, 184, 130, 0.1);padding: 0;",
},
{
value: "1",
label: "编辑数据",
style: "color: rgb(255, 200, 0);background-color: rgba(255, 200, 0, 0.1);padding: 0;",
},
{
value: "2",
label: "删除数据",
style: "color: rgb(240, 70, 70);background-color: rgba(240, 70, 70, 0.1);padding: 0;",
},
];
const typeOptions = [
{
value: "0",
label: "普通",
style: "color: rgb(16, 184, 130);background-color: rgba(16, 184, 130, 0.1);padding: 0;",
},
{
value: "1",
label: "重要",
style: "color: rgb(255, 200, 0);background-color: rgba(255, 200, 0, 0.1);padding: 0;",
},
{
value: "2",
label: "危险",
style: "color: rgb(240, 70, 70);background-color: rgba(240, 70, 70, 0.1);padding: 0;",
},
];
const statusOptions = [
{
value: "0",
label: "操作执行失败,原因:无用户身份",
style: "color: rgb(120, 140, 180);background-color: rgba(120, 140, 180, 0.1);padding: 0;",
},
{
value: "1",
label: "操作执行失败,原因:密码错误",
style: "color: rgb(255, 100, 180);background-color: rgba(255, 100, 180, 0.1);padding: 0;",
},
{
value: "2",
label: "操作执行失败,原因:权限不足",
style: "color: rgb(56, 188, 248);background-color: rgba(56, 188, 248, 0.1);padding: 0;",
},
{
value: "3",
label: "操作执行失败,原因:查询条件错误",
style: "color: rgb(255, 200, 0);background-color: rgba(255, 200, 0, 0.1);padding: 0;",
},
{
value: "4",
label: "操作执行成功",
style: "color: rgb(16, 184, 130);background-color: rgba(16, 184, 130, 0.1);padding: 0;",
},
{
value: "5",
label: "操作执行失败,原因:程序错误",
style: "color: rgb(240, 70, 70);background-color: rgba(240, 70, 70, 0.1);padding: 0;",
},
];
let singleData = reactive({});
let operateData = reactive({});
let operateDataClone = reactive({});
const styleShunt = (options, data) => {
let nowStyle;
options.forEach((item) => {
if (data == item.value) {
nowStyle = item.style;
}
});
return nowStyle;
};
const labelShunt = (options, data) => {
let nowLabel;
options.forEach((item) => {
if (data == item.value) {
nowLabel = item.label;
}
});
return nowLabel;
};
const showTools = (operateType) => {
switch (operateType) {
case "search":
dialogSearchVisible.value = !dialogSearchVisible.value;
monitoringMsgActive.dialogSearchVisible = dialogSearchVisible.value;
break;
case "sort":
dialogSortVisible.value = !dialogSortVisible.value;
monitoringMsgActive.dialogSortVisible = dialogSortVisible.value;
break;
case "operation":
dialogOperationVisible.value = !dialogOperationVisible.value;
monitoringMsgActive.dialogOperationVisible = dialogOperationVisible.value;
break;
case "downloadData":
downloadDataRef.value.downloadDataDialogVisible = true;
break;
default:
break;
}
};
const resetOffset = () => {
if (hasNullOrEmptyPropertyWithTypeof(searchOptions)) {
if (PaginationRef.value.offset !== 1 && offsetOff.value) {
PaginationRef.value.handleCurrentChange(1);
offsetOff.value = false;
}
} else {
offsetOff.value = true;
}
};
const monitoringMsg = () => {
judgment(searchOptions, "operate", SelectOperateSearchRef.value, "searchMsg");
judgment(searchOptions, "operate_msg", SearchOperateMsgKeyRef.value, "searchMsg");
judgment(searchOptions, "type", SelectTypeSearchRef.value, "searchMsg");
judgment(searchOptions, "status", SelectStatusSearchRef.value, "searchMsg");
judgment(searchOptions, "created_at", createdDateTimePickerRef.value, "searchMsg");
judgment(searchOptions, "user", SearchUserKeyRef.value, "searchMsg");
judgment(searchOptions, "name", SearchNameKeyRef.value, "searchMsg");
judgment(sortOptions, "created_at", createdSortDateTimeRef.value, "searchMsg");
resetOffset();
const data = {
uid: localStorage.data,
offset: PaginationRef.value == null ? 1 : PaginationRef.value.offset,
limit: PaginationRef.value == null ? 10 : PaginationRef.value.limit,
searchOptions: searchOptions,
sortOptions: sortOptions,
};
$http.post($url.showMonitoring, data).then((res) => {
if (res.data.code === 200) {
tableData.value = res.data.MonitoringMsg;
tableData.value.forEach((item) => {
item.origin_createdAt = item.created_at;
item.created_at = moment
.utc(item.created_at)
.add(0, "hours")
.format("YYYY-MM-DD-HH:mm:ss");
});
PaginationRef.value.dataCount = res.data.count;
monitoringMsgActive.dataCount = PaginationRef.value.dataCount;
pageCount.value = Math.ceil(
PaginationRef.value.dataCount / PaginationRef.value.limit
);
monitoringMsgActive.pageCount = pageCount.value;
if (!operateData[PaginationRef.value.offset]) return;
operateData[PaginationRef.value.offset].forEach((element) => {
tableData.value.forEach((item) => {
if (item.created_at == element.created_at) {
nextTick(() => {
tableRef.value.toggleRowSelection(item, true);
});
}
});
});
} else {
tableData.value = [];
ElMessage.error("未查找到相关数据!");
}
});
};
const changePage = (val) => {
pageCount.value = Math.ceil(PaginationRef.value.dataCount / val);
operateData = {};
for (let i = 0; i < pageCount.value; i++) {
operateData[i + 1] = [];
for (const key in operateDataClone) {
operateDataClone[key].forEach((element) => {
// 判断数据在第几页
if (Math.ceil(element.location / PaginationRef.value.limit) == i + 1) {
operateData[i + 1].push(element);
}
});
}
}
};
const handleSelectionChange = (val) => {
operateData[PaginationRef.value.offset] = val;
tableData.value.forEach((item, index) => {
operateData[PaginationRef.value.offset].forEach((element) => {
if (item.created_at == element.created_at) {
// 数据在表格中的位置
element.location =
(PaginationRef.value.offset - 1) * PaginationRef.value.limit + (index + 1);
}
});
});
operateDataClone = JSON.parse(JSON.stringify(operateData));
for (const key in operateDataClone) {
operateDataClone[key].forEach((item) => {
if (item.origin_createdAt) {
item.created_at = item.origin_createdAt;
}
delete item.origin_createdAt;
delete item.location;
});
}
};
const showComfirmDialog = (singleMsg) => {
singleData = { ...singleMsg.data };
singleData.created_at = singleData.origin_createdAt;
delete singleData.origin_createdAt;
operate.value = singleMsg.operateType;
if (singleMsg.operateType == "del") {
delPasswordOauthRef.value.operateDialogVisible = true;
}
};
const requestOperate = () => {
let operateType, url, model, password;
if (operate.value == "del") {
operateType = "删除";
url = $url.deleteMonitoring;
model = DelSelectRef.value.model;
password = delPasswordOauthRef.value.ruleForm.password;
}
const data = {
uid: localStorage.data,
model: encrypt(model),
password: encrypt(password),
requestMsg:
model == "single" ? singleData : model == "multiple" ? operateDataClone : "",
};
$http.post(url, data).then((res) => {
if (res.data.code === 200) {
tableRef.value.clearSelection();
operateData = {};
operateDataClone = {};
ElMessage({
message: `${operateType}成功!`,
type: "success",
});
monitoringMsg();
} else {
ElMessage.error(`${operateType}失败!`);
}
});
};
const executeDownloadData = (form) => {
// 根据数据量大小来决定在前端还是后端导出数据
if (
tableData.value == [] ||
tableData.value == undefined ||
PaginationRef.value.dataCount == 0 ||
PaginationRef.value.dataCount == null
) {
ElMessage.error("暂无数据!");
return;
}
let dataClone = JSON.parse(JSON.stringify(tableData.value));
dataClone.forEach((item) => {
delete item.origin_time;
});
const dataHeaders = [
"操作名称",
"操作描述",
"危险等级",
"执行状态",
"影响到的数据数目",
"操作者uid",
"操作者姓名",
"行为记录时间"
];
downloadData(form, dataClone, dataHeaders);
if (PaginationRef.value.dataCount <= PaginationRef.value.limit) {
downloadData(form, dataClone, dataHeaders);
} else {
const data = {
form,
searchMsg: {
searchOptions: searchOptions,
sortOptions: sortOptions,
},
dataHeaders,
};
$http.post($url.downloadMonitoring, data).then((res) => {
if (res.data.code === 200) {
ElMessage({
message: "数据导出成功!",
type: "success",
});
// 创建一个隐藏的<a>标签
const link = document.createElement("a");
link.style.display = "none";
link.href = res.data.fileUrl;
// 设置下载的文件名
link.download = res.data.fileName;
// 触发点击事件来下载文件
document.body.appendChild(link);
link.click();
// 清理
document.body.removeChild(link);
URL.revokeObjectURL(link.href);
} else {
ElMessage.error("数据导出失败!");
}
});
}
};
defineExpose({ monitoringMsg });
watch(searchOptions, (value) => {
monitoringMsgActive.searchOptions = value;
});
watch(sortOptions, (value) => {
monitoringMsgActive.sortOptions = value;
});
onMounted(() => {
pageCount.value = monitoringMsgActive.pageCount;
dialogSearchVisible.value = monitoringMsgActive.dialogSearchVisible;
dialogSortVisible.value = monitoringMsgActive.dialogSortVisible;
dialogOperationVisible.value = monitoringMsgActive.dialogOperationVisible;
if (dialogSearchVisible.value) {
searchOptions.operate = monitoringMsgActive.searchOptions.operate;
searchOptions.operate_msg = monitoringMsgActive.searchOptions.operate_msg;
searchOptions.type = monitoringMsgActive.searchOptions.type;
searchOptions.status = monitoringMsgActive.searchOptions.status;
searchOptions.created_at = monitoringMsgActive.searchOptions.created_at;
searchOptions.user = monitoringMsgActive.searchOptions.user;
}
if (dialogSortVisible.value) {
sortOptions.created_at = monitoringMsgActive.sortOptions.created_at;
}
if (dialogOperationVisible.value) {
}
setTimeout(() => {
monitoringMsg();
}, 200);
setTimeout(() => {
if (dialogSearchVisible.value) {
SelectOperateSearchRef.value.searchMsg = monitoringMsgActive.searchOptions.operate;
SearchOperateMsgKeyRef.value.searchMsg =
monitoringMsgActive.searchOptions.operate_msg;
SelectTypeSearchRef.value.searchMsg = monitoringMsgActive.searchOptions.type;
SelectStatusSearchRef.value.searchMsg = monitoringMsgActive.searchOptions.status;
createdDateTimePickerRef.value.created_at =
monitoringMsgActive.searchOptions.created_at;
SearchUserKeyRef.value.searchMsg = monitoringMsgActive.searchOptions.user;
SearchNameKeyRef.value.searchMsg = monitoringMsgActive.searchOptions.name;
}
if (dialogSortVisible.value) {
createdSortDateTimeRef.value.searchMsg = monitoringMsgActive.sortOptions.created_at;
}
if (dialogOperationVisible.value) {
}
}, 0);
});
</script>
<style scoped></style>