refactor: remove standalone chat and log pages
This commit is contained in:
+4
-26
@@ -21,7 +21,7 @@ const lotteries = ref<LotteryItem[]>([]);
|
||||
const lotteryParticipants = ref<Record<string, any>[]>([]);
|
||||
const shopForm = reactive({ item_key: '', name: '', cost: 0, desc: '', enabled: true });
|
||||
const lotteryForm = reactive({ id: 0, title: '', prize: '', description: '', condition_type: 'all', condition_value: 0, draw_type: 'people', end_type: 'people', end_value: 3, max_participants: 3, draw_at: '', status: 'draft' });
|
||||
const activePage = ref<'dashboard' | 'chat' | 'logs' | 'shop' | 'lottery' | 'bans'>('dashboard');
|
||||
const activePage = ref<'dashboard' | 'shop' | 'lottery' | 'bans'>('dashboard');
|
||||
const activeTab = reactive({ shop: 'items', lottery: 'events' });
|
||||
const toast = ref('');
|
||||
const confirmState = reactive({ open: false, title: '', message: '', confirmText: '确认', danger: false, resolver: null as null | ((value: boolean) => void) });
|
||||
@@ -42,10 +42,10 @@ const chatCount = computed(() => chatLogs.value.length);
|
||||
const selectedQuery = computed(() => selectedChatId.value ? `?chat_id=${selectedChatId.value}` : '');
|
||||
|
||||
const pageTitle = computed(() => ({
|
||||
dashboard: '群组安全总览', chat: '聊天记录', logs: '运行日志', shop: '商城管理', lottery: '抽奖管理', bans: '封禁记录'
|
||||
dashboard: '群组安全总览', shop: '商城管理', lottery: '抽奖管理', bans: '封禁记录'
|
||||
}[activePage.value]));
|
||||
const pageDesc = computed(() => ({
|
||||
dashboard: '实时审计聊天、封禁、日志和运营活动', chat: '群消息实时审计与手动处置', logs: '服务运行状态与错误排查', shop: '商品 CRUD 与兑换交易记录', lottery: '抽奖项目、开奖条件与参与记录', bans: '封禁审计与手动解封'
|
||||
dashboard: '实时审计聊天、封禁、日志和运营活动', shop: '商品 CRUD 与兑换交易记录', lottery: '抽奖项目、开奖条件与参与记录', bans: '封禁审计与手动解封'
|
||||
}[activePage.value]));
|
||||
function goPage(page: typeof activePage.value) {
|
||||
activePage.value = page;
|
||||
@@ -383,8 +383,6 @@ onBeforeUnmount(() => {
|
||||
</div>
|
||||
<nav class="side-nav">
|
||||
<button :class="{active: activePage === 'dashboard'}" @click="goPage('dashboard')"><span>总览</span><em>Dashboard</em></button>
|
||||
<button :class="{active: activePage === 'chat'}" @click="goPage('chat')"><span>聊天审计</span><em>Messages</em></button>
|
||||
<button :class="{active: activePage === 'logs'}" @click="goPage('logs')"><span>运行日志</span><em>Logs</em></button>
|
||||
<button :class="{active: activePage === 'shop'}" @click="goPage('shop')"><span>商城管理</span><em>Shop</em></button>
|
||||
<button :class="{active: activePage === 'lottery'}" @click="goPage('lottery')"><span>抽奖管理</span><em>Lottery</em></button>
|
||||
<button :class="{active: activePage === 'bans'}" @click="goPage('bans')"><span>封禁记录</span><em>Bans</em></button>
|
||||
@@ -469,26 +467,6 @@ onBeforeUnmount(() => {
|
||||
</SCard>
|
||||
</section>
|
||||
|
||||
<section v-show="activePage === 'chat'" class="tab-page chat-page">
|
||||
<SCard class="panel chat-panel page-panel" title="聊天记录" :description="`${chatCount} 条`">
|
||||
<div class="scroll-area chat-scroll">
|
||||
<div v-if="!chatLogs.length" class="empty">等待消息...</div>
|
||||
<article v-for="(item, i) in chatLogs" :key="item.id || i" :class="chatItemClass(item)">
|
||||
<div class="chat-head"><div class="chat-user-line"><STag size="xs" :color="tagColor(item)" variant="soft">{{ chatStatusText(item) }}</STag><span class="chat-user">@{{ item.username || item.first_name || 'unknown' }}</span><span class="chat-uid">{{ item.user_id }}</span></div><div class="chat-meta-line"><span class="chat-id">#{{ item.msg_id || '-' }}</span><span class="chat-time">{{ formatTime(item.time) }}</span></div></div>
|
||||
<div class="chat-text">{{ item.text || '(空消息)' }}</div>
|
||||
<div class="chat-foot"><STag v-if="item.spam" class="reason-tag" size="xs" color="destructive" variant="soft" :title="item.reason || item.spam_reason">{{ item.reason || item.spam_reason }}</STag><span v-else /><div class="chat-actions"><STag v-if="item.deleted" size="xs" color="secondary" variant="soft">已删 · {{ formatTime(item.deleted_at) }}</STag><template v-else><SButton size="xs" variant="link" color="destructive" :disabled="item._deleting" @click="deleteChatMessage(item, false)">删除</SButton><SButton v-if="item.user_id" size="xs" variant="link" color="warning" :disabled="item._deleting" @click="deleteChatMessage(item, true)">删封</SButton></template></div></div>
|
||||
</article>
|
||||
</div>
|
||||
</SCard>
|
||||
</section>
|
||||
|
||||
<section v-show="activePage === 'logs'" class="tab-page log-page">
|
||||
<SCard class="panel log-panel page-panel" title="运行日志" :description="`${logs.length} 条`">
|
||||
<template #extra><div class="panel-extra"><SButton size="xs" variant="outline" @click="copyLogs">复制</SButton><SButton size="xs" variant="outline" @click="loadLogs">刷新</SButton></div></template>
|
||||
<div ref="logScroll" class="log-window"><div v-if="!logs.length" class="empty dark">等待日志...</div><div v-for="(item, i) in logs" :key="i" class="log-line" :class="item.level" :title="compactLog(item.message)"><span class="log-time">{{ formatTime(item.time) }}</span><span class="log-level">{{ item.level }}</span><span class="log-msg">{{ compactLog(item.message) }}</span></div></div>
|
||||
</SCard>
|
||||
</section>
|
||||
|
||||
<section v-show="activePage === 'shop'" class="tab-page manager-layout">
|
||||
<aside class="editor-panel"><div class="section-title"><span>商品编辑</span><small>{{ shopForm.item_key ? '编辑中' : '新建' }}</small></div><label>商品编号<SInput v-model="shopForm.item_key" placeholder="如 1 / vip_card" /></label><label>商品名称<SInput v-model="shopForm.name" placeholder="补签卡" /></label><label>积分价格<SInput v-model="shopForm.cost" placeholder="50" /></label><label>说明<SInput v-model="shopForm.desc" placeholder="展示在 /shop 内" /></label><label class="switch-line"><input v-model="shopForm.enabled" type="checkbox" /> 上架销售</label><div class="editor-actions"><SButton size="xs" color="primary" @click="saveShopItem">保存商品</SButton><SButton size="xs" variant="outline" @click="Object.assign(shopForm, { item_key: '', name: '', cost: 0, desc: '', enabled: true })">新建</SButton></div></aside>
|
||||
<section class="data-panel"><div class="tabbar"><button :class="{active: activeTab.shop === 'items'}" @click="activeTab.shop='items'">商品列表</button><button :class="{active: activeTab.shop === 'tx'}" @click="activeTab.shop='tx'; loadShopTransactions()">交易记录</button></div><div v-if="activeTab.shop === 'items'" class="data-table"><div class="table-head"><span>编号/商品</span><span>价格</span><span>状态</span><span>操作</span></div><div v-for="item in shopItems" :key="item.item_key" class="table-row"><span><b>{{ item.item_key }}</b><em>{{ item.name }}</em><small>{{ item.desc || '无说明' }}</small></span><span>{{ item.cost }} 分</span><span><STag size="xs" :color="item.enabled ? 'success' : 'secondary'" variant="soft">{{ item.enabled ? '上架' : '下架' }}</STag></span><span class="row-actions"><SButton size="xs" variant="outline" @click="editShopItem(item)">编辑</SButton><SButton size="xs" variant="soft" color="destructive" @click="deleteShopItem(item)">删除</SButton></span></div></div><div v-else class="data-table tx-table"><div class="table-head"><span>用户</span><span>商品</span><span>积分</span><span>时间</span></div><div v-for="tx in shopTransactions" :key="tx.id" class="table-row"><span>@{{ tx.username || tx.user_id }}</span><span>{{ tx.item_name }}</span><span>{{ tx.cost }} 分</span><span>{{ formatTime(tx.created_at) }}</span></div></div></section>
|
||||
@@ -503,7 +481,7 @@ onBeforeUnmount(() => {
|
||||
<SCard class="ban-panel page-panel" title="封禁记录" :description="`${bans.length} 条`"><template #extra><SButton size="xs" variant="outline" @click="loadBans">刷新</SButton></template><div class="ban-list"><div v-if="!bans.length" class="empty">暂无封禁记录</div><div v-for="(item, i) in bans" :key="item.id || i" class="ban-item"><div class="ban-main"><b>@{{ item.username || item.first_name || item.user_id }}</b><span>{{ item.reason || '-' }}</span></div><div class="ban-meta"><span>{{ formatTime(item.time) }}</span><SButton v-if="item.auto_unban" size="xs" variant="outline" color="success" :disabled="item._loading" @click="unban(item)">解封</SButton><STag v-else size="xs" color="secondary" variant="soft">已处理</STag></div></div></div></SCard>
|
||||
</section>
|
||||
|
||||
<form v-if="activePage === 'chat'" class="send-box floating-chat-send" @submit.prevent="sendMessage">
|
||||
<form v-if="activePage === 'dashboard'" class="send-box floating-chat-send" @submit.prevent="sendMessage">
|
||||
<SInput v-model="newMessage" placeholder="发送消息到 TG 群" />
|
||||
<SButton type="submit" color="primary" :disabled="sending || !newMessage.trim()">发送</SButton>
|
||||
</form>
|
||||
|
||||
@@ -214,3 +214,8 @@ body { background: #f3f6fb; }
|
||||
.admin-shell .send-box:not(.floating-chat-send) { position: static; }
|
||||
@media (max-width: 1100px) { .admin-shell .floating-chat-send { left: 16px; right: 16px; } .tab-page { min-height: auto; } }
|
||||
@media (max-width: 520px) { .admin-shell .floating-chat-send { left: 8px; right: 8px; bottom: 8px; } .chat-page .chat-panel { height: calc(100vh - 270px); min-height: 420px; } }
|
||||
|
||||
/* Remove standalone chat/log pages: composer belongs to dashboard */
|
||||
.admin-shell .floating-chat-send { left: 284px; right: 20px; bottom: 18px; }
|
||||
.main-grid { padding-bottom: 72px; }
|
||||
@media (max-width: 1100px) { .admin-shell .floating-chat-send { left: 16px; right: 16px; } }
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -5,8 +5,8 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="icon" type="image/svg+xml" href="/static/icon.svg" />
|
||||
<title>TG Spam Guard</title>
|
||||
<script type="module" crossorigin src="/static/assets/index-BLyluaUp.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/static/assets/index-DEuQ3oiJ.css">
|
||||
<script type="module" crossorigin src="/static/assets/index-D_B5Zn2J.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/static/assets/index-Djy9tMxw.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
Reference in New Issue
Block a user