状态监控界面修复+导出操作日志+导出异常记录
This commit is contained in:
@@ -36,4 +36,13 @@ export function managelogbyid(id: string | number) {
|
|||||||
url: `/manage-log/getById/${id}`,
|
url: `/manage-log/getById/${id}`,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function managelogexport(params: any) {
|
||||||
|
return request({
|
||||||
|
url: '/manage-log/export',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
responseType: 'blob',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -37,3 +37,12 @@ export function recordInfoupd(data: any) {
|
|||||||
data,
|
data,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function recordInfoexport(params: any) {
|
||||||
|
return request({
|
||||||
|
url: '/recordInfo/export',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
responseType: 'blob',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -105,7 +105,7 @@
|
|||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
<el-menu-item index="/status-monitor">
|
<el-menu-item index="/status-monitor">
|
||||||
<el-icon><component :is="getMenuIcon('监控')" /></el-icon>
|
<el-icon><component :is="getMenuIcon('监控')" /></el-icon>
|
||||||
<span>状态监控界面</span>
|
<span>状态监控</span>
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
<el-menu-item v-if="hasPermission('tb:goodrecord')" index="/goods-info">
|
<el-menu-item v-if="hasPermission('tb:goodrecord')" index="/goods-info">
|
||||||
<el-icon><component :is="getMenuIcon('样品管理')" /></el-icon>
|
<el-icon><component :is="getMenuIcon('样品管理')" /></el-icon>
|
||||||
|
|||||||
@@ -59,6 +59,10 @@
|
|||||||
<el-button type="primary" @click="handleSearch">查询</el-button>
|
<el-button type="primary" @click="handleSearch">查询</el-button>
|
||||||
<el-button @click="resetSearch">重置</el-button>
|
<el-button @click="resetSearch">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item style="margin-left: 84px">
|
||||||
|
<el-button v-permission="'sys:managelog:excel'" type="success" :loading="exportLoading" @click="handleExport">导出日志</el-button>
|
||||||
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
@@ -131,7 +135,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, reactive, ref } from 'vue'
|
import { onMounted, reactive, ref } from 'vue'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { managelogbyid, manageloglist } from '@/api/system/manage-log'
|
import { managelogbyid, managelogexport, manageloglist } from '@/api/system/manage-log'
|
||||||
import { userselect } from '@/api/system/user'
|
import { userselect } from '@/api/system/user'
|
||||||
|
|
||||||
interface LogItem {
|
interface LogItem {
|
||||||
@@ -153,6 +157,7 @@ interface UserOption {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
const exportLoading = ref(false)
|
||||||
const tableData = ref<LogItem[]>([])
|
const tableData = ref<LogItem[]>([])
|
||||||
const total = ref(0)
|
const total = ref(0)
|
||||||
const userOptions = ref<UserOption[]>([])
|
const userOptions = ref<UserOption[]>([])
|
||||||
@@ -236,6 +241,22 @@ const normalizeDateTimeParam = (value?: string | Date) => {
|
|||||||
return raw
|
return raw
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const buildQueryParams = () => {
|
||||||
|
const params: Record<string, any> = {
|
||||||
|
pageNum: pagination.pageNum,
|
||||||
|
pageSize: pagination.pageSize,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (queryForm.logType) params.logType = queryForm.logType
|
||||||
|
if (queryForm.createId !== '' && queryForm.createId !== null) params.createId = queryForm.createId
|
||||||
|
if (queryForm.logWritetimeRange?.length === 2) {
|
||||||
|
params.logWritetimeStart = normalizeDateTimeParam(queryForm.logWritetimeRange[0])
|
||||||
|
params.logWritetimeEnd = normalizeDateTimeParam(queryForm.logWritetimeRange[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
return params
|
||||||
|
}
|
||||||
|
|
||||||
const loadUserOptions = async () => {
|
const loadUserOptions = async () => {
|
||||||
try {
|
try {
|
||||||
const res: any = await userselect({})
|
const res: any = await userselect({})
|
||||||
@@ -272,19 +293,7 @@ const loadUserOptions = async () => {
|
|||||||
const loadList = async () => {
|
const loadList = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
const params: any = {
|
const res: any = await manageloglist(buildQueryParams())
|
||||||
pageNum: pagination.pageNum,
|
|
||||||
pageSize: pagination.pageSize,
|
|
||||||
}
|
|
||||||
|
|
||||||
if (queryForm.logType) params.logType = queryForm.logType
|
|
||||||
if (queryForm.createId !== '' && queryForm.createId !== null) params.createId = queryForm.createId
|
|
||||||
if (queryForm.logWritetimeRange?.length === 2) {
|
|
||||||
params.logWritetimeStart = normalizeDateTimeParam(queryForm.logWritetimeRange[0])
|
|
||||||
params.logWritetimeEnd = normalizeDateTimeParam(queryForm.logWritetimeRange[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
const res: any = await manageloglist(params)
|
|
||||||
const data = res?.data ?? res ?? {}
|
const data = res?.data ?? res ?? {}
|
||||||
|
|
||||||
const recordsFromData =
|
const recordsFromData =
|
||||||
@@ -353,6 +362,45 @@ const openDetailDialog = async (row: LogItem) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getFileNameFromDisposition = (contentDisposition?: string) => {
|
||||||
|
if (!contentDisposition) return ''
|
||||||
|
const utf8Match = contentDisposition.match(/filename\*=UTF-8''([^;]+)/i)
|
||||||
|
if (utf8Match?.[1]) return decodeURIComponent(utf8Match[1])
|
||||||
|
|
||||||
|
const standardMatch = contentDisposition.match(/filename=([^;]+)/i)
|
||||||
|
if (standardMatch?.[1]) {
|
||||||
|
return standardMatch[1].replace(/['"]/g, '').trim()
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const downloadBlobFile = (blob: Blob, fileName: string) => {
|
||||||
|
const downloadUrl = window.URL.createObjectURL(blob)
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.href = downloadUrl
|
||||||
|
link.download = fileName
|
||||||
|
document.body.appendChild(link)
|
||||||
|
link.click()
|
||||||
|
document.body.removeChild(link)
|
||||||
|
window.URL.revokeObjectURL(downloadUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleExport = async () => {
|
||||||
|
exportLoading.value = true
|
||||||
|
try {
|
||||||
|
const res: any = await managelogexport(buildQueryParams())
|
||||||
|
const blob = res instanceof Blob ? res : new Blob([res as BlobPart], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' })
|
||||||
|
const fileName = getFileNameFromDisposition((res as any)?.headers?.['content-disposition']) || '操作日志.xlsx'
|
||||||
|
downloadBlobFile(blob, fileName)
|
||||||
|
ElMessage.success('导出成功')
|
||||||
|
} catch (error) {
|
||||||
|
console.error('export log error', error)
|
||||||
|
ElMessage.error('导出失败')
|
||||||
|
} finally {
|
||||||
|
exportLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await loadUserOptions()
|
await loadUserOptions()
|
||||||
loadList()
|
loadList()
|
||||||
|
|||||||
@@ -29,6 +29,9 @@
|
|||||||
<el-button type="primary" @click="handleSearch">查询</el-button>
|
<el-button type="primary" @click="handleSearch">查询</el-button>
|
||||||
<el-button @click="resetSearch">重置</el-button>
|
<el-button @click="resetSearch">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item style="margin-left: 276px" >
|
||||||
|
<el-button v-permission="'tb:exceptionrecord:excel'" type="success" :loading="exportLoading" @click="handleExport">导出记录</el-button>
|
||||||
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
@@ -173,7 +176,7 @@ import type { FormInstance, FormRules } from 'element-plus'
|
|||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { islandInfolist } from '@/api/tb/islandinfo'
|
import { islandInfolist } from '@/api/tb/islandinfo'
|
||||||
import { devselect } from '@/api/tb/devinfo'
|
import { devselect } from '@/api/tb/devinfo'
|
||||||
import { recordInfoupd, recordInfolist } from '@/api/tb/recordinfo'
|
import { recordInfoexport, recordInfoupd, recordInfolist } from '@/api/tb/recordinfo'
|
||||||
import { recordDealadd, recordDealupd, recordDeallistByRecordId, recordDeallist } from '@/api/tb/recorddeal'
|
import { recordDealadd, recordDealupd, recordDeallistByRecordId, recordDeallist } from '@/api/tb/recorddeal'
|
||||||
|
|
||||||
interface RecordInfoItem {
|
interface RecordInfoItem {
|
||||||
@@ -210,6 +213,7 @@ interface ActionItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
const exportLoading = ref(false)
|
||||||
const tableData = ref<RecordInfoItem[]>([])
|
const tableData = ref<RecordInfoItem[]>([])
|
||||||
const total = ref(0)
|
const total = ref(0)
|
||||||
const detailVisible = ref(false)
|
const detailVisible = ref(false)
|
||||||
@@ -304,14 +308,7 @@ const loadLookups = async () => {
|
|||||||
const loadTableData = async () => {
|
const loadTableData = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
const params = {
|
const res = await recordInfolist(buildListParams())
|
||||||
pageNum: pagination.pageNum,
|
|
||||||
pageSize: pagination.pageSize,
|
|
||||||
recordName: queryForm.recordName || undefined,
|
|
||||||
recordType: queryForm.recordType === '' ? undefined : queryForm.recordType,
|
|
||||||
recordStatus: queryForm.recordStatus === '' ? undefined : queryForm.recordStatus,
|
|
||||||
}
|
|
||||||
const res = await recordInfolist(params)
|
|
||||||
tableData.value = getListFromResponse(res)
|
tableData.value = getListFromResponse(res)
|
||||||
total.value = getTotalFromResponse(res)
|
total.value = getTotalFromResponse(res)
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
@@ -393,6 +390,43 @@ const initDealDialog = async (row: RecordInfoItem) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const buildListParams = () => ({
|
||||||
|
pageNum: pagination.pageNum,
|
||||||
|
pageSize: pagination.pageSize,
|
||||||
|
recordName: queryForm.recordName || undefined,
|
||||||
|
recordType: queryForm.recordType === '' ? undefined : queryForm.recordType,
|
||||||
|
recordStatus: queryForm.recordStatus === '' ? undefined : queryForm.recordStatus,
|
||||||
|
})
|
||||||
|
|
||||||
|
const buildExportParams = () => ({
|
||||||
|
recordName: queryForm.recordName || undefined,
|
||||||
|
recordType: queryForm.recordType === '' ? undefined : queryForm.recordType,
|
||||||
|
recordStatus: queryForm.recordStatus === '' ? undefined : queryForm.recordStatus,
|
||||||
|
})
|
||||||
|
|
||||||
|
const getFileNameFromDisposition = (disposition?: string) => {
|
||||||
|
if (!disposition) return '异常记录.xlsx'
|
||||||
|
const match = disposition.match(/filename\*=UTF-8''([^;]+)|filename=\"?([^\";]+)\"?/i)
|
||||||
|
const encodedName = match?.[1] || match?.[2]
|
||||||
|
if (!encodedName) return '异常记录.xlsx'
|
||||||
|
try {
|
||||||
|
return decodeURIComponent(encodedName)
|
||||||
|
} catch {
|
||||||
|
return encodedName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const downloadBlob = (blob: Blob, fileName: string) => {
|
||||||
|
const url = window.URL.createObjectURL(blob)
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.href = url
|
||||||
|
link.download = fileName
|
||||||
|
document.body.appendChild(link)
|
||||||
|
link.click()
|
||||||
|
document.body.removeChild(link)
|
||||||
|
window.URL.revokeObjectURL(url)
|
||||||
|
}
|
||||||
|
|
||||||
const handleSearch = () => {
|
const handleSearch = () => {
|
||||||
pagination.pageNum = 1
|
pagination.pageNum = 1
|
||||||
loadTableData()
|
loadTableData()
|
||||||
@@ -417,6 +451,21 @@ const handleSizeChange = (size: number) => {
|
|||||||
loadTableData()
|
loadTableData()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleExport = async () => {
|
||||||
|
exportLoading.value = true
|
||||||
|
try {
|
||||||
|
const res: any = await recordInfoexport(buildExportParams())
|
||||||
|
const blob = new Blob([res?.data ?? res], { type: 'application/vnd.ms-excel' })
|
||||||
|
const disposition = res?.headers?.['content-disposition'] || res?.headers?.['Content-Disposition']
|
||||||
|
downloadBlob(blob, getFileNameFromDisposition(disposition))
|
||||||
|
ElMessage.success('导出成功')
|
||||||
|
} catch (error: any) {
|
||||||
|
ElMessage.error(error?.message || '导出异常记录失败')
|
||||||
|
} finally {
|
||||||
|
exportLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const handleView = (row: RecordInfoItem) => {
|
const handleView = (row: RecordInfoItem) => {
|
||||||
detailData.value = { ...row }
|
detailData.value = { ...row }
|
||||||
detailVisible.value = true
|
detailVisible.value = true
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<el-breadcrumb separator="/" class="page-breadcrumb">
|
<el-breadcrumb separator="/" class="page-breadcrumb">
|
||||||
<el-breadcrumb-item><router-link to="/home">首页</router-link></el-breadcrumb-item>
|
<el-breadcrumb-item><router-link to="/home">首页</router-link></el-breadcrumb-item>
|
||||||
<el-breadcrumb-item>业务流程控制</el-breadcrumb-item>
|
<el-breadcrumb-item>业务流程控制</el-breadcrumb-item>
|
||||||
<el-breadcrumb-item>状态监控界面</el-breadcrumb-item>
|
<el-breadcrumb-item>状态监控</el-breadcrumb-item>
|
||||||
</el-breadcrumb>
|
</el-breadcrumb>
|
||||||
|
|
||||||
<div class="monitor-layout">
|
<div class="monitor-layout">
|
||||||
@@ -36,6 +36,7 @@
|
|||||||
<el-tag class="island-status-tag" :type="getStatusTag(getIslandStatus(item))" size="small" effect="light">
|
<el-tag class="island-status-tag" :type="getStatusTag(getIslandStatus(item))" size="small" effect="light">
|
||||||
{{ getStatusText(getIslandStatus(item)) }}
|
{{ getStatusText(getIslandStatus(item)) }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
|
<div v-if="getIslandRecordCount(item) > 0" class="record-dot island-list-record-dot">{{ getIslandRecordCount(item) }}</div>
|
||||||
|
|
||||||
<div class="action-badges" v-if="getIslandActionList(item).length > 0">
|
<div class="action-badges" v-if="getIslandActionList(item).length > 0">
|
||||||
<div
|
<div
|
||||||
@@ -83,6 +84,7 @@
|
|||||||
<el-tag :type="getStatusTag(getIslandStatus(selectedIsland))" effect="light" size="small">
|
<el-tag :type="getStatusTag(getIslandStatus(selectedIsland))" effect="light" size="small">
|
||||||
{{ getStatusText(getIslandStatus(selectedIsland)) }}
|
{{ getStatusText(getIslandStatus(selectedIsland)) }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
|
<div v-if="getIslandRecordCount(selectedIsland) > 0" class="record-dot island-detail-record-dot">{{ getIslandRecordCount(selectedIsland) }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -104,6 +106,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<el-tag :type="getStatusTag(getActionStatus(group.action))" effect="light" size="small">
|
<el-tag :type="getStatusTag(getActionStatus(group.action))" effect="light" size="small">
|
||||||
{{ getStatusText(getActionStatus(group.action)) }}</el-tag>
|
{{ getStatusText(getActionStatus(group.action)) }}</el-tag>
|
||||||
|
<div v-if="getActionRecordCount(group.action) > 0" class="record-dot action-record-dot">{{ getActionRecordCount(group.action) }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="arrow-down" aria-hidden="true">
|
<div class="arrow-down" aria-hidden="true">
|
||||||
@@ -145,12 +148,37 @@
|
|||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="功能岛描述">{{ getIslandDesc(dialogIsland) }}</el-descriptions-item>
|
<el-descriptions-item label="功能岛描述">{{ getIslandDesc(dialogIsland) }}</el-descriptions-item>
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
|
|
||||||
|
<div class="record-section">
|
||||||
|
<div class="record-section-title">待处理异常记录</div>
|
||||||
|
<div v-if="dialogIslandRecords.length === 0" class="record-empty-text">当前功能岛暂无待处理异常记录</div>
|
||||||
|
<el-scrollbar v-else max-height="220px">
|
||||||
|
<div class="record-list">
|
||||||
|
<div v-for="record in dialogIslandRecords" :key="record.id" class="record-item">
|
||||||
|
<div class="record-item-header">
|
||||||
|
<span class="record-item-name">{{ record.recordName || '未命名异常' }}</span>
|
||||||
|
<el-tag type="warning" size="small">待处理</el-tag>
|
||||||
|
</div>
|
||||||
|
<div class="record-item-content">{{ record.recordContent || '暂无异常内容' }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-scrollbar>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer-actions">
|
||||||
|
<el-button @click="islandDialogVisible = false">关闭</el-button>
|
||||||
|
<el-button v-if="dialogIslandRecords.length > 0" type="warning" @click="goToExceptionRecords">
|
||||||
|
异常处理
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
<el-dialog v-model="actionDialogVisible" title="动作状态详情" width="640px" destroy-on-close>
|
<el-dialog v-model="actionDialogVisible" title="动作状态详情" width="640px" destroy-on-close>
|
||||||
<el-descriptions :column="1" border>
|
<el-descriptions :column="1" border>
|
||||||
<el-descriptions-item label="动作名称">{{ getActionName(dialogAction) }}</el-descriptions-item>
|
<el-descriptions-item label="动作名称">{{ getActionName(dialogAction) }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="所属功能岛">{{ getIslandName(selectedIsland) }}</el-descriptions-item>
|
<el-descriptions-item label="所属功能岛">{{ getIslandName(selectedIsland) }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="动作状态">
|
<el-descriptions-item label="动作状态">
|
||||||
<el-tag :type="getStatusTag(getActionStatus(dialogAction))" effect="light">
|
<el-tag :type="getStatusTag(getActionStatus(dialogAction))" effect="light">
|
||||||
{{ getStatusText(getActionStatus(dialogAction)) }}
|
{{ getStatusText(getActionStatus(dialogAction)) }}
|
||||||
@@ -158,17 +186,44 @@
|
|||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="动作描述">{{ getActionDesc(dialogAction) }}</el-descriptions-item>
|
<el-descriptions-item label="动作描述">{{ getActionDesc(dialogAction) }}</el-descriptions-item>
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
|
|
||||||
|
<div class="record-section">
|
||||||
|
<div class="record-section-title">待处理异常记录</div>
|
||||||
|
<div v-if="dialogActionRecords.length === 0" class="record-empty-text">当前动作暂无待处理异常记录</div>
|
||||||
|
<el-scrollbar v-else max-height="220px">
|
||||||
|
<div class="record-list">
|
||||||
|
<div v-for="record in dialogActionRecords" :key="record.id" class="record-item">
|
||||||
|
<div class="record-item-header">
|
||||||
|
<span class="record-item-name">{{ record.recordName || '未命名异常' }}</span>
|
||||||
|
<el-tag type="warning" size="small">待处理</el-tag>
|
||||||
|
</div>
|
||||||
|
<div class="record-item-content">{{ record.recordContent || '暂无异常内容' }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-scrollbar>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer-actions">
|
||||||
|
<el-button @click="actionDialogVisible = false">关闭</el-button>
|
||||||
|
<el-button v-if="dialogActionRecords.length > 0" type="warning" @click="goToExceptionRecords">
|
||||||
|
异常处理
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onMounted, ref } from 'vue'
|
import { computed, onMounted, ref } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { Bottom } from '@element-plus/icons-vue'
|
import { Bottom } from '@element-plus/icons-vue'
|
||||||
import { islandInfolist } from '@/api/tb/islandinfo'
|
import { islandInfolist } from '@/api/tb/islandinfo'
|
||||||
import { devInfolist } from '@/api/tb/devinfo'
|
import { devInfolist } from '@/api/tb/devinfo'
|
||||||
import { devparamselect } from '@/api/tb/devparam'
|
import { devparamselect } from '@/api/tb/devparam'
|
||||||
|
import { recordInfolist } from '@/api/tb/recordinfo'
|
||||||
|
|
||||||
interface AnyItem {
|
interface AnyItem {
|
||||||
id?: string | number
|
id?: string | number
|
||||||
@@ -193,14 +248,19 @@ interface AnyItem {
|
|||||||
status?: number | string
|
status?: number | string
|
||||||
state?: number | string
|
state?: number | string
|
||||||
onlineStatus?: number | string
|
onlineStatus?: number | string
|
||||||
|
recordName?: string
|
||||||
|
recordContent?: string
|
||||||
|
recordStatus?: boolean | number | string
|
||||||
[key: string]: any
|
[key: string]: any
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
const islandLoading = ref(false)
|
const islandLoading = ref(false)
|
||||||
const detailLoading = ref(false)
|
const detailLoading = ref(false)
|
||||||
const islands = ref<AnyItem[]>([])
|
const islands = ref<AnyItem[]>([])
|
||||||
const actions = ref<AnyItem[]>([])
|
const actions = ref<AnyItem[]>([])
|
||||||
const params = ref<AnyItem[]>([])
|
const params = ref<AnyItem[]>([])
|
||||||
|
const abnormalRecords = ref<AnyItem[]>([])
|
||||||
|
|
||||||
const selectedIslandId = ref<string | number | ''>('')
|
const selectedIslandId = ref<string | number | ''>('')
|
||||||
const selectedActionId = ref<string | number | ''>('')
|
const selectedActionId = ref<string | number | ''>('')
|
||||||
@@ -218,6 +278,11 @@ const getResponseList = (res: any) => {
|
|||||||
return data.records || data.list || data.rows || data.items || data.data || []
|
return data.records || data.list || data.rows || data.items || data.data || []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isUnprocessedRecord = (item: AnyItem) => {
|
||||||
|
const value = item?.recordStatus ?? item?.status ?? item?.dealStatus
|
||||||
|
return value === false || value === 0 || value === '0' || value === '待处理' || value === 'false'
|
||||||
|
}
|
||||||
|
|
||||||
const getItemId = (item: AnyItem) => item?.id ?? item?.islandId ?? item?.devId ?? item?.paramId ?? ''
|
const getItemId = (item: AnyItem) => item?.id ?? item?.islandId ?? item?.devId ?? item?.paramId ?? ''
|
||||||
const getIslandName = (item?: AnyItem | null) => item?.islandName || item?.name || '未命名功能岛'
|
const getIslandName = (item?: AnyItem | null) => item?.islandName || item?.name || '未命名功能岛'
|
||||||
const getIslandDesc = (item?: AnyItem | null) => item?.desc || item?.islandDesc || '暂无描述'
|
const getIslandDesc = (item?: AnyItem | null) => item?.desc || item?.islandDesc || '暂无描述'
|
||||||
@@ -269,15 +334,17 @@ const loadData = async () => {
|
|||||||
islandLoading.value = true
|
islandLoading.value = true
|
||||||
detailLoading.value = true
|
detailLoading.value = true
|
||||||
try {
|
try {
|
||||||
const [islandRes, actionRes, paramRes] = await Promise.all([
|
const [islandRes, actionRes, paramRes, recordRes] = await Promise.all([
|
||||||
islandInfolist({ pageNum: 1, pageSize: 1000 }),
|
islandInfolist({ pageNum: 1, pageSize: 1000 }),
|
||||||
devInfolist({ pageNum: 1, pageSize: 1000, devModelNot: 'PLC' }),
|
devInfolist({ pageNum: 1, pageSize: 1000, devModelNot: 'PLC' }),
|
||||||
devparamselect({ pageNum: 1, pageSize: 5000 }),
|
devparamselect({ pageNum: 1, pageSize: 5000 }),
|
||||||
|
recordInfolist({ pageNum: 1, pageSize: 5000 }),
|
||||||
])
|
])
|
||||||
|
|
||||||
islands.value = getResponseList(islandRes)
|
islands.value = getResponseList(islandRes)
|
||||||
actions.value = getResponseList(actionRes)
|
actions.value = getResponseList(actionRes)
|
||||||
params.value = getResponseList(paramRes)
|
params.value = getResponseList(paramRes)
|
||||||
|
abnormalRecords.value = getResponseList(recordRes).filter((item: AnyItem) => isUnprocessedRecord(item))
|
||||||
|
|
||||||
const firstIsland = islands.value[0]
|
const firstIsland = islands.value[0]
|
||||||
if (firstIsland) {
|
if (firstIsland) {
|
||||||
@@ -322,6 +389,21 @@ const getParamsForAction = (action: AnyItem) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getAbnormalRecordsByIsland = (item?: AnyItem | null) => {
|
||||||
|
const islandId = String(getItemId(item ?? {}))
|
||||||
|
if (!islandId) return []
|
||||||
|
return abnormalRecords.value.filter(record => String(record.islandId ?? record.functionIslandId ?? record.island_id ?? '') === islandId)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getAbnormalRecordsByAction = (item?: AnyItem | null) => {
|
||||||
|
const actionId = String(getItemId(item ?? {}))
|
||||||
|
if (!actionId) return []
|
||||||
|
return abnormalRecords.value.filter(record => String(record.devId ?? record.actionId ?? record.dev_id ?? '') === actionId)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getIslandRecordCount = (item?: AnyItem | null) => getAbnormalRecordsByIsland(item).length
|
||||||
|
const getActionRecordCount = (item?: AnyItem | null) => getAbnormalRecordsByAction(item).length
|
||||||
|
|
||||||
const openIslandDialog = (item: AnyItem) => {
|
const openIslandDialog = (item: AnyItem) => {
|
||||||
dialogIsland.value = item
|
dialogIsland.value = item
|
||||||
activeDialogType.value = 'island'
|
activeDialogType.value = 'island'
|
||||||
@@ -335,11 +417,25 @@ const openActionDialog = (item: AnyItem) => {
|
|||||||
actionDialogVisible.value = true
|
actionDialogVisible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const goToExceptionRecords = () => {
|
||||||
|
const query: Record<string, string> = {}
|
||||||
|
if (activeDialogType.value === 'island' && dialogIsland.value) {
|
||||||
|
query.islandId = String(getItemId(dialogIsland.value))
|
||||||
|
}
|
||||||
|
if (activeDialogType.value === 'action' && dialogAction.value) {
|
||||||
|
query.devId = String(getItemId(dialogAction.value))
|
||||||
|
}
|
||||||
|
router.push({ path: '/record-info', query })
|
||||||
|
}
|
||||||
|
|
||||||
const actionsToRender = computed(() => getIslandRelatedActions(selectedIslandId.value).map(action => ({
|
const actionsToRender = computed(() => getIslandRelatedActions(selectedIslandId.value).map(action => ({
|
||||||
action,
|
action,
|
||||||
params: getParamsForAction(action),
|
params: getParamsForAction(action),
|
||||||
})))
|
})))
|
||||||
|
|
||||||
|
const dialogIslandRecords = computed(() => getAbnormalRecordsByIsland(dialogIsland.value))
|
||||||
|
const dialogActionRecords = computed(() => getAbnormalRecordsByAction(dialogAction.value))
|
||||||
|
|
||||||
onMounted(loadData)
|
onMounted(loadData)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -427,6 +523,37 @@ onMounted(loadData)
|
|||||||
right: 12px;
|
right: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.record-dot {
|
||||||
|
position: absolute;
|
||||||
|
min-width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
padding: 0 5px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: #f56c6c;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 18px;
|
||||||
|
text-align: center;
|
||||||
|
box-shadow: 0 0 0 2px #fff;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-record-dot {
|
||||||
|
top: 10px;
|
||||||
|
right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.island-list-record-dot {
|
||||||
|
bottom: 10px;
|
||||||
|
right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.island-detail-record-dot {
|
||||||
|
top: 10px;
|
||||||
|
right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.island-card-main {
|
.island-card-main {
|
||||||
padding-right: 80px;
|
padding-right: 80px;
|
||||||
}
|
}
|
||||||
@@ -622,6 +749,63 @@ onMounted(loadData)
|
|||||||
color: var(--el-text-color-secondary);
|
color: var(--el-text-color-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.record-section {
|
||||||
|
margin-top: 16px;
|
||||||
|
padding-top: 12px;
|
||||||
|
border-top: 1px solid #ebeef5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-section-title {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--el-text-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-empty-text {
|
||||||
|
color: var(--el-text-color-secondary);
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-item {
|
||||||
|
padding: 12px 14px;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: #fafcff;
|
||||||
|
border: 1px solid #ebeef5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-item-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-item-name {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--el-text-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-item-content {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--el-text-color-secondary);
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-footer-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
@media (min-width: 1201px) {
|
@media (min-width: 1201px) {
|
||||||
.actions-row {
|
.actions-row {
|
||||||
grid-template-columns: repeat(var(--actions-count), minmax(340px, 1fr));
|
grid-template-columns: repeat(var(--actions-count), minmax(340px, 1fr));
|
||||||
|
|||||||
Reference in New Issue
Block a user