style: compact rule detection panel

This commit is contained in:
ngfchl
2026-07-07 19:17:13 +08:00
parent 68b4007e89
commit ef36282fb9
5 changed files with 54 additions and 23 deletions
+24 -18
View File
@@ -854,31 +854,37 @@ onBeforeUnmount(() => {
</div>
<section v-show="activePage === 'rules'" class="tab-page rule-manage-page">
<SCard class="panel page-panel rule-detect-card" title="🧪 规则检测" description="输入聊天内容,检测是否为广告 / 垃圾 / 灰产信息,并可一键写入规则库">
<div class="rule-test-grid">
<div class="rule-editor-block">
<label>聊天内容<textarea v-model="spamTestText" class="form-input rule-textarea" placeholder="粘贴可疑聊天内容,系统会给出规则评分和命中原因"></textarea></label>
<label class="switch-line"><input v-model="spamUseAi" type="checkbox" /> 同时调用 AI 最终检测</label>
<div class="form-actions">
<SButtonLoading color="primary" :loading="spamTesting" :disabled="spamTesting" @click="testSpamText">开始检测</SButtonLoading>
<SButtonLoading variant="outline" color="warning" :loading="ruleSaving" :disabled="ruleSaving || !spamTestText.trim()" @click="ruleForm.pattern = spamTestText; ruleForm.type = 'keyword'; ruleForm.score = 90; saveSpamRule()">将内容写入规则库</SButtonLoading>
</div>
</div>
<div class="rule-result-card">
<div v-if="!spamTestResult" class="empty">暂无检测结果</div>
<SCard class="panel page-panel rule-detect-card compact-detect-card" title="🧪 规则检测" description="粘贴聊天内容,快速判断广告 / 垃圾 / 灰产,并可写入规则库">
<div class="rule-detect-compact">
<label class="rule-text-field">
<span>聊天内容</span>
<textarea v-model="spamTestText" class="form-input rule-textarea compact" placeholder="粘贴可疑聊天内容,例如:点击 https://xxx 领取福利"></textarea>
</label>
<div class="rule-result-card compact" :class="{ danger: spamTestResult?.final_is_spam, ok: spamTestResult && spamTestResult.final_is_spam === false }">
<div v-if="!spamTestResult" class="compact-empty">暂无检测结果</div>
<template v-else>
<div class="rule-result-head"><STag :color="spamTestResult.final_is_spam ? 'destructive' : spamTestResult.final_is_spam === null ? 'warning' : 'success'" variant="soft">{{ spamTestResult.final_is_spam ? '广告/垃圾/灰产' : spamTestResult.final_is_spam === null ? '需 AI 判断' : '正常' }}</STag><strong>评分{{ spamTestResult.score }}</strong></div>
<div class="rule-reason"><b>本地命中</b><span>{{ spamTestResult.reason }}</span></div>
<div v-if="spamTestResult.ai_reason" class="rule-reason"><b>AI 结果</b><span>{{ spamTestResult.ai_reason }}</span></div>
<div class="rule-counts">规则库关键词 {{ spamTestResult.rule_counts?.keywords }} / 正则 {{ spamTestResult.rule_counts?.regex }} / Adblock 域名 {{ spamTestResult.rule_counts?.adblock_domains || spamRules.counts.adblock_domains || 0 }} </div>
<div class="compact-result-top">
<STag :color="spamTestResult.final_is_spam ? 'destructive' : spamTestResult.final_is_spam === null ? 'warning' : 'success'" variant="soft">
{{ spamTestResult.final_is_spam ? '广告/垃圾/灰产' : spamTestResult.final_is_spam === null ? '需 AI 判断' : '正常' }}
</STag>
<strong>评分 {{ spamTestResult.score }}</strong>
</div>
<div class="compact-reason" :title="spamTestResult.reason">{{ spamTestResult.reason }}</div>
<div v-if="spamTestResult.ai_reason" class="compact-reason muted" :title="spamTestResult.ai_reason">AI{{ spamTestResult.ai_reason }}</div>
<div class="compact-counts">关键词 {{ spamTestResult.rule_counts?.keywords }} / 正则 {{ spamTestResult.rule_counts?.regex }} / 域名 {{ spamTestResult.rule_counts?.adblock_domains || spamRules.counts.adblock_domains || 0 }}</div>
</template>
</div>
</div>
<div class="rule-write-grid inline-rule-writer">
<div class="rule-action-bar">
<label class="switch-line compact"><input v-model="spamUseAi" type="checkbox" /> AI 兜底</label>
<SButtonLoading color="primary" :loading="spamTesting" :disabled="spamTesting" @click="testSpamText">开始检测</SButtonLoading>
<SButtonLoading variant="outline" color="warning" :loading="ruleSaving" :disabled="ruleSaving || !spamTestText.trim()" @click="ruleForm.pattern = spamTestText; ruleForm.type = 'keyword'; ruleForm.score = 90; saveSpamRule()">内容入库</SButtonLoading>
</div>
<div class="rule-write-grid inline-rule-writer compact-rule-writer">
<label>类型<select v-model="ruleForm.type" class="form-select"><option value="keyword">关键词</option><option value="regex">正则</option></select></label>
<label>分数<SInput v-model="ruleForm.score" type="number" placeholder="75" /></label>
<label class="rule-pattern-field">规则条目<SInput v-model="ruleForm.pattern" :placeholder="ruleForm.type === 'keyword' ? '如 黑U' : '如 (?:黑\s*[uU]).{0,20}(?:联系|项目)'" /></label>
<SButtonLoading color="primary" :loading="ruleSaving" :disabled="ruleSaving" @click="saveSpamRule">写入规则</SButtonLoading>
<SButtonLoading color="primary" :loading="ruleSaving" :disabled="ruleSaving" @click="saveSpamRule">写入</SButtonLoading>
</div>
</SCard>
+25
View File
@@ -513,3 +513,28 @@ body { background: #f3f6fb; }
.overview-ban-panel .s-card__content { flex: 1 1 auto; min-height: 0; height: auto !important; overflow: hidden; }
.overview-ban-panel .v-overflow,
.overview-ban-list.v-overflow { display: block; height: 100%; max-height: 100%; min-height: 0; overflow-y: auto !important; overflow-x: hidden; padding-right: 6px; overscroll-behavior: contain; scrollbar-gutter: stable; }
/* Compact rule detection redesign */
.compact-detect-card .s-card__content { display: grid; gap: 10px; }
.rule-detect-compact { display: grid; grid-template-columns: minmax(0, 1fr) minmax(280px, 360px); gap: 12px; align-items: stretch; }
.rule-text-field { display: grid; gap: 6px; font-weight: 700; color: #0f172a; }
.rule-textarea.compact { min-height: 104px; max-height: 150px; height: 104px; padding: 12px 14px; border-radius: 14px; font-size: 14px; line-height: 1.45; resize: vertical; }
.rule-result-card.compact { min-height: 104px; padding: 12px; border-radius: 14px; border: 1px solid #dbe3ef; background: linear-gradient(180deg, #f8fafc 0%, #fff 100%); display: grid; align-content: center; gap: 8px; }
.rule-result-card.compact.danger { border-color: #fecaca; background: #fff7f7; }
.rule-result-card.compact.ok { border-color: #bbf7d0; background: #f7fff9; }
.compact-empty { color: #94a3b8; text-align: center; font-weight: 700; }
.compact-result-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.compact-result-top strong { font-size: 18px; color: #0f172a; }
.compact-reason { font-size: 13px; line-height: 1.35; color: #334155; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.compact-reason.muted { color: #64748b; -webkit-line-clamp: 1; }
.compact-counts { font-size: 12px; color: #64748b; padding: 6px 8px; border-radius: 10px; background: #eef2ff; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rule-action-bar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.switch-line.compact { margin-right: auto; font-weight: 700; color: #334155; }
.compact-rule-writer { margin-top: 0; padding-top: 10px; grid-template-columns: 120px 110px minmax(220px, 1fr) 76px !important; gap: 10px; border-top: 1px solid #e2e8f0; align-items: end; }
.compact-rule-writer label { display: grid; gap: 5px; font-size: 13px; font-weight: 700; color: #334155; }
.compact-rule-writer .form-select { height: 36px; }
@media (max-width: 980px) {
.rule-detect-compact, .compact-rule-writer { grid-template-columns: 1fr !important; }
.switch-line.compact { margin-right: 0; }
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -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-CtGHELCH.js"></script>
<link rel="stylesheet" crossorigin href="/static/assets/index-Dookq5Ju.css">
<script type="module" crossorigin src="/static/assets/index-B6Gkvk5q.js"></script>
<link rel="stylesheet" crossorigin href="/static/assets/index-DB2vPoNK.css">
</head>
<body>
<div id="app"></div>