feat: 在 ClientAutoAction 组件中新增操作历史标签页和相关功能

- 引入 ActionHistoryViewer 组件,展示执行历史。
- 更新主标签页逻辑,调整为操作管理和执行历史两个标签。
- 在自动操作逻辑中增加弹幕和私信发送历史记录功能,提升操作追踪能力。
This commit is contained in:
2025-04-22 19:08:31 +08:00
parent d6577ec129
commit b97081a870
4 changed files with 695 additions and 170 deletions

View File

@@ -0,0 +1,278 @@
<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted, h } from 'vue';
import {
NSpace, NSelect, NButton, NDataTable, NPopconfirm,
NCard, NEmpty, NTag, NTabPane, NTabs, NTime,
useMessage, NIcon, NTooltip, NSpin
} from 'naive-ui';
import { ArrowClockwise16Filled, Delete16Filled, CheckmarkCircle16Filled, DismissCircle16Filled } from '@vicons/fluent';
import { HistoryType, HistoryItem, getHistoryByType, clearHistory, clearAllHistory } from '../../store/autoAction/utils/historyLogger';
const message = useMessage();
const loading = ref(true);
const activeTab = ref(HistoryType.DANMAKU);
const historyData = ref<Record<HistoryType, HistoryItem[]>>({
[HistoryType.DANMAKU]: [],
[HistoryType.PRIVATE_MSG]: [],
[HistoryType.COMMAND]: []
});
// 类型名称映射
const typeNameMap = {
[HistoryType.DANMAKU]: '弹幕发送',
[HistoryType.PRIVATE_MSG]: '私信发送',
[HistoryType.COMMAND]: '命令执行'
};
// 刷新间隔(毫秒)
const refreshInterval = 10000;
let refreshTimer: number | null = null;
// 列定义
const columns = [
{
title: '时间',
key: 'timestamp',
width: 180,
render: (row: HistoryItem) => {
return h(NTime, {
time: new Date(row.timestamp),
format: 'yyyy-MM-dd HH:mm:ss'
});
}
},
{
title: '操作名称',
key: 'actionName',
width: 160
},
{
title: '内容',
key: 'content',
ellipsis: {
tooltip: true
}
},
{
title: '目标',
key: 'target',
width: 120
},
{
title: '状态',
key: 'success',
width: 100,
render: (row: HistoryItem) => {
if (row.success) {
return h(
NTooltip,
{ trigger: 'hover' },
{
trigger: () => h(
NTag,
{ type: 'success', size: 'small', round: true },
{ default: () => '成功', icon: () => h(NIcon, { component: CheckmarkCircle16Filled }) }
),
default: () => '执行成功'
}
);
} else {
return h(
NTooltip,
{ trigger: 'hover' },
{
trigger: () => h(
NTag,
{ type: 'error', size: 'small', round: true },
{ default: () => '失败', icon: () => h(NIcon, { component: DismissCircle16Filled }) }
),
default: () => row.error || '执行失败'
}
);
}
}
},
];
// 加载历史数据
async function loadHistory() {
loading.value = true;
try {
// 并行加载所有类型的历史
const [danmakuHistory, privateMsgHistory, commandHistory] = await Promise.all([
getHistoryByType(HistoryType.DANMAKU),
getHistoryByType(HistoryType.PRIVATE_MSG),
getHistoryByType(HistoryType.COMMAND)
]);
historyData.value = {
[HistoryType.DANMAKU]: danmakuHistory,
[HistoryType.PRIVATE_MSG]: privateMsgHistory,
[HistoryType.COMMAND]: commandHistory
};
} catch (error) {
console.error('加载历史数据失败:', error);
message.error('加载历史数据失败');
} finally {
loading.value = false;
}
}
// 清除历史
async function handleClearHistory(type: HistoryType) {
try {
await clearHistory(type);
historyData.value[type] = [];
message.success(`已清空${typeNameMap[type]}历史`);
} catch (error) {
console.error('清除历史失败:', error);
message.error('清除历史失败');
}
}
// 清除所有历史
async function handleClearAllHistory() {
try {
await clearAllHistory();
Object.keys(historyData.value).forEach((type) => {
historyData.value[type as HistoryType] = [];
});
message.success('已清空所有历史记录');
} catch (error) {
console.error('清除所有历史失败:', error);
message.error('清除所有历史失败');
}
}
// 开始定时刷新
function startRefreshTimer() {
stopRefreshTimer();
refreshTimer = window.setInterval(() => {
loadHistory();
}, refreshInterval);
}
// 停止定时刷新
function stopRefreshTimer() {
if (refreshTimer !== null) {
clearInterval(refreshTimer);
refreshTimer = null;
}
}
onMounted(() => {
loadHistory();
startRefreshTimer();
});
onUnmounted(() => {
stopRefreshTimer();
});
</script>
<template>
<div class="history-viewer">
<NSpace vertical>
<NSpace justify="space-between">
<div class="history-title">
操作执行历史
</div>
<NSpace>
<NButton
size="small"
:loading="loading"
@click="loadHistory"
>
<template #icon>
<NIcon :component="ArrowClockwise16Filled" />
</template>
刷新
</NButton>
<NPopconfirm
placement="bottom"
@positive-click="handleClearAllHistory"
>
<template #trigger>
<NButton
size="small"
type="error"
ghost
>
<template #icon>
<NIcon :component="Delete16Filled" />
</template>
清空所有历史
</NButton>
</template>
确定要清空所有类型的历史记录吗此操作不可恢复
</NPopconfirm>
</NSpace>
</NSpace>
<NTabs
v-model:value="activeTab"
type="line"
animated
>
<NTabPane
v-for="(label, type) in typeNameMap"
:key="type"
:name="type"
:tab="label"
>
<NSpin :show="loading">
<NSpace vertical>
<NSpace justify="end">
<NPopconfirm
placement="bottom"
@positive-click="() => handleClearHistory(type as HistoryType)"
>
<template #trigger>
<NButton
size="small"
type="warning"
ghost
>
<template #icon>
<NIcon :component="Delete16Filled" />
</template>
清空{{ label }}历史
</NButton>
</template>
确定要清空所有{{ label }}历史记录吗?此操作不可恢复。
</NPopconfirm>
</NSpace>
<NDataTable
:columns="columns"
:data="historyData[type as HistoryType]"
:bordered="false"
:pagination="{
pageSize: 10,
showSizePicker: true,
pageSizes: [10, 20, 50],
}"
:row-key="row => row.id"
>
<template #empty>
<NEmpty description="暂无历史记录" />
</template>
</NDataTable>
</NSpace>
</NSpin>
</NTabPane>
</NTabs>
</NSpace>
</div>
</template>
<style scoped>
.history-viewer {
width: 100%;
}
.history-title {
font-size: 16px;
font-weight: 500;
}
</style>