Files
vtsuru.live/src/client/components/autoaction/ActionHistoryViewer.vue
Megghy 4ac793f155 feat: 更新时间显示组件和相关设置
- 在多个组件中引入 NTime 和 NTooltip 以优化时间显示
- 修改 ActionHistoryViewer.vue 和 CheckInSettings.vue 中的时间渲染逻辑
- 在 CheckInRankingView.vue 中实现时间的相对显示和格式化
- 修复商品页加载问题
2025-05-02 06:37:18 +08:00

284 lines
7.4 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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,
sorter: (a: HistoryItem, b: HistoryItem) => a.timestamp - b.timestamp,
render: (row: HistoryItem) => {
return h(NTooltip, {
}, {
trigger: () => h(NTime, {
time: row.timestamp,
type: 'relative'
}),
default: () => new Date(row.timestamp).toLocaleString()
});
},
},
{
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.sort((a, b) => b.timestamp - a.timestamp),
[HistoryType.PRIVATE_MSG]: privateMsgHistory.sort((a, b) => b.timestamp - a.timestamp),
[HistoryType.COMMAND]: commandHistory.sort((a, b) => b.timestamp - a.timestamp)
};
} 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"
default-sort-order="descend"
>
<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>