diff --git a/front/src/App.vue b/front/src/App.vue
index 0abd7b2..77a4eb4 100644
--- a/front/src/App.vue
+++ b/front/src/App.vue
@@ -47,6 +47,15 @@ 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) });
const chatContextMenu = reactive({ open: false, x: 0, y: 0, item: null as ChatItem | null });
+const banDialog = reactive({ open: false, mode: 'ban' as 'ban' | 'delete_ban', item: null as ChatItem | null, duration: 0, customMinutes: '' });
+const banDurationOptions = [
+ { label: '永久', value: 0 },
+ { label: '1 小时', value: 60 },
+ { label: '6 小时', value: 360 },
+ { label: '1 天', value: 1440 },
+ { label: '7 天', value: 10080 },
+ { label: '30 天', value: 43200 },
+];
const authReady = ref(false);
const authenticated = ref(false);
const loginForm = reactive({ username: '', password: '' });
@@ -645,38 +654,55 @@ async function contextDeleteMessage(ban = false) {
closeChatContextMenu();
if (item) await deleteChatMessage(item, ban);
}
-async function pickBanDuration() {
- const text = window.prompt('选择封禁时间:0=永久,60=1小时,360=6小时,1440=1天,10080=7天', '0');
- if (text === null) return null;
- const value = Number(String(text).trim());
- if (!Number.isFinite(value) || value < 0) {
- showToast('封禁时间无效,请输入 0 或正整数分钟');
- return null;
- }
- return Math.floor(value);
-}
function banDurationText(minutes: number) {
if (!minutes) return '永久';
if (minutes % 1440 === 0) return `${minutes / 1440} 天`;
if (minutes % 60 === 0) return `${minutes / 60} 小时`;
return `${minutes} 分钟`;
}
+function openBanDialog(item: ChatItem, mode: 'ban' | 'delete_ban') {
+ if (!item.user_id) return showToast('当前记录没有用户 ID,无法封禁');
+ banDialog.item = item;
+ banDialog.mode = mode;
+ banDialog.duration = 0;
+ banDialog.customMinutes = '';
+ banDialog.open = true;
+}
+function closeBanDialog() {
+ banDialog.open = false;
+ banDialog.item = null;
+ banDialog.customMinutes = '';
+}
+function selectBanDuration(minutes: number) {
+ banDialog.duration = minutes;
+ banDialog.customMinutes = '';
+}
+function applyCustomBanDuration() {
+ const raw = String(banDialog.customMinutes || '').trim();
+ if (raw === '') return;
+ const value = Number(raw);
+ if (!Number.isFinite(value) || value < 0) return showToast('封禁时间无效,请输入 0 或正整数分钟');
+ banDialog.duration = Math.floor(value);
+}
+async function confirmBanDialog() {
+ const item = banDialog.item;
+ if (!item) return closeBanDialog();
+ applyCustomBanDuration();
+ const duration = banDialog.duration;
+ const mode = banDialog.mode;
+ closeBanDialog();
+ if (mode === 'delete_ban' && !item.deleted) await deleteChatMessage(item, true, duration, true);
+ else await banChatMessage(item, duration, true);
+}
async function contextDeleteAndBanMessage() {
const item = chatContextMenu.item;
closeChatContextMenu();
- if (!item) return;
- const duration = await pickBanDuration();
- if (duration === null) return;
- if (item.deleted) await banChatMessage(item, duration);
- else await deleteChatMessage(item, true, duration);
+ if (item) openBanDialog(item, 'delete_ban');
}
async function contextBanMessage() {
const item = chatContextMenu.item;
closeChatContextMenu();
- if (!item) return;
- const duration = await pickBanDuration();
- if (duration === null) return;
- await banChatMessage(item, duration);
+ if (item) openBanDialog(item, 'ban');
}
async function contextCopyText() {
const item = chatContextMenu.item;
@@ -699,9 +725,9 @@ function contextReplyMessage() {
}
function cancelReply() { replyTarget.value = null; }
-async function banChatMessage(item: ChatItem, durationMinutes = 0) {
+async function banChatMessage(item: ChatItem, durationMinutes = 0, confirmed = false) {
if (!item.user_id) return showToast('当前记录没有用户 ID,无法封禁');
- if (!await askConfirm('封禁用户', `确认封禁 @${item.username || item.first_name || item.user_id}?时长:${banDurationText(durationMinutes)}`, { confirmText: '封禁', danger: true })) return;
+ if (!confirmed && !await askConfirm('封禁用户', `确认封禁 @${item.username || item.first_name || item.user_id}?时长:${banDurationText(durationMinutes)}`, { confirmText: '封禁', danger: true })) return;
item._deleting = true;
try {
await fetchJson(`/api/messages/${item.id}/ban`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ duration_minutes: durationMinutes }) });
@@ -731,11 +757,11 @@ async function addChatMessageToRules(item: ChatItem) {
}
}
-async function deleteChatMessage(item: ChatItem, ban = false, durationMinutes = 0) {
+async function deleteChatMessage(item: ChatItem, ban = false, durationMinutes = 0, confirmed = false) {
if (item.manually_deleted || item.deleted) return showToast('该消息已删除');
const action = ban ? '删除并封禁' : '删除';
const message = ban ? `确认删除这条消息并封禁用户?时长:${banDurationText(durationMinutes)}` : '确认删除这条消息?';
- if (!await askConfirm(action, message, { confirmText: action, danger: true })) return;
+ if (!confirmed && !await askConfirm(action, message, { confirmText: action, danger: true })) return;
item._deleting = true;
try {
await fetchJson(`/api/messages/${item.id}/delete`, {
@@ -1080,6 +1106,31 @@ onBeforeUnmount(() => {
@{{ banDialog.item?.username || banDialog.item?.first_name || banDialog.item?.user_id }} · 选择封禁时长
+库存填 -1 表示不限库存,0 表示售罄。