feat: 完善QQBot权限配置和账号删除操作
This commit is contained in:
parent
9fc876b006
commit
99aa4a1fe7
@ -80,7 +80,7 @@ export namespace QqbotApi {
|
||||
priority: number;
|
||||
remark?: string;
|
||||
replyContent: string;
|
||||
targetType: 'all' | 'group' | 'private';
|
||||
targetType: 'all' | 'channel' | 'group' | 'private';
|
||||
}
|
||||
|
||||
export interface RuleBody {
|
||||
@ -93,7 +93,7 @@ export namespace QqbotApi {
|
||||
priority?: number;
|
||||
remark?: string;
|
||||
replyContent: string;
|
||||
targetType?: 'all' | 'group' | 'private';
|
||||
targetType?: 'all' | 'channel' | 'group' | 'private';
|
||||
}
|
||||
|
||||
export interface Conversation {
|
||||
@ -105,7 +105,7 @@ export namespace QqbotApi {
|
||||
selfId: string;
|
||||
targetId: string;
|
||||
targetName?: string;
|
||||
targetType: 'group' | 'private';
|
||||
targetType: 'channel' | 'group' | 'private';
|
||||
}
|
||||
|
||||
export interface Message {
|
||||
@ -113,7 +113,7 @@ export namespace QqbotApi {
|
||||
eventTime: string;
|
||||
id: string;
|
||||
messageText: string;
|
||||
messageType: 'group' | 'private';
|
||||
messageType: 'channel' | 'group' | 'private';
|
||||
senderNickname?: string;
|
||||
selfId: string;
|
||||
targetId: string;
|
||||
@ -129,25 +129,34 @@ export namespace QqbotApi {
|
||||
selfId: string;
|
||||
status: 'failed' | 'pending' | 'success';
|
||||
targetId: string;
|
||||
targetType: 'group' | 'private';
|
||||
targetType: 'channel' | 'group' | 'private';
|
||||
}
|
||||
|
||||
export interface PermissionConfig {
|
||||
allowlistEnabled: boolean;
|
||||
blocklistEnabled: boolean;
|
||||
}
|
||||
|
||||
export interface Permission {
|
||||
enabled: boolean;
|
||||
id: string;
|
||||
preciseUser: boolean;
|
||||
remark?: string;
|
||||
selfId?: string;
|
||||
targetId: string;
|
||||
targetType: 'all' | 'group' | 'private';
|
||||
targetType: 'channel' | 'group' | 'private' | 'qq';
|
||||
userId?: string;
|
||||
}
|
||||
|
||||
export interface PermissionBody {
|
||||
enabled?: boolean;
|
||||
id?: string;
|
||||
preciseUser?: boolean;
|
||||
remark?: string;
|
||||
selfId?: string;
|
||||
targetId: string;
|
||||
targetType: 'all' | 'group' | 'private';
|
||||
targetType: 'channel' | 'group' | 'private' | 'qq';
|
||||
userId?: string;
|
||||
}
|
||||
|
||||
export type Query = Recordable<any>;
|
||||
@ -179,7 +188,9 @@ export function updateQqbotAccount(data: QqbotApi.AccountBody) {
|
||||
}
|
||||
|
||||
export function deleteQqbotAccount(id: string) {
|
||||
return requestClient.post<boolean>(`/qqbot/account/delete?id=${id}`);
|
||||
return requestClient.post<{ deletedContainers: number }>(
|
||||
`/qqbot/account/delete?id=${id}`,
|
||||
);
|
||||
}
|
||||
|
||||
export function kickQqbotAccount(selfId: string) {
|
||||
@ -291,6 +302,21 @@ export function getQqbotPermissionList(
|
||||
);
|
||||
}
|
||||
|
||||
export function getQqbotPermissionConfig() {
|
||||
return requestClient.get<QqbotApi.PermissionConfig>(
|
||||
'/qqbot/permission/config',
|
||||
);
|
||||
}
|
||||
|
||||
export function updateQqbotPermissionConfig(
|
||||
data: Partial<QqbotApi.PermissionConfig>,
|
||||
) {
|
||||
return requestClient.post<QqbotApi.PermissionConfig>(
|
||||
'/qqbot/permission/config',
|
||||
data,
|
||||
);
|
||||
}
|
||||
|
||||
export function createQqbotPermission(
|
||||
kind: 'allowlist' | 'blocklist',
|
||||
data: QqbotApi.PermissionBody,
|
||||
|
||||
@ -141,6 +141,23 @@ export default defineComponent({
|
||||
onClick: openScanRefresh,
|
||||
permissionCodes: ['QqBot:Account:RefreshLogin'],
|
||||
},
|
||||
{
|
||||
confirm: (row) =>
|
||||
`确认删除账号「${row.selfId}」吗?该操作会同时删除该账号专属的 NapCat 容器。`,
|
||||
danger: true,
|
||||
key: 'delete',
|
||||
label: '删除',
|
||||
onClick: async (row, context) => {
|
||||
const result = await deleteQqbotAccount(row.id);
|
||||
message.success(
|
||||
result.deletedContainers > 0
|
||||
? `账号删除成功,已删除 ${result.deletedContainers} 个 NapCat 容器`
|
||||
: '账号删除成功',
|
||||
);
|
||||
await context.reload();
|
||||
},
|
||||
permissionCodes: ['QqBot:Account:Delete'],
|
||||
},
|
||||
{
|
||||
disabled: (row) => row.connectStatus !== 'online',
|
||||
key: 'kick',
|
||||
@ -158,18 +175,6 @@ export default defineComponent({
|
||||
onClick: openEdit,
|
||||
permissionCodes: ['QqBot:Account:Edit'],
|
||||
},
|
||||
{
|
||||
confirm: (row) => `确认删除账号「${row.selfId}」吗?`,
|
||||
danger: true,
|
||||
key: 'delete',
|
||||
label: '删除',
|
||||
onClick: async (row, context) => {
|
||||
await deleteQqbotAccount(row.id);
|
||||
message.success('账号删除成功');
|
||||
await context.reload();
|
||||
},
|
||||
permissionCodes: ['QqBot:Account:Delete'],
|
||||
},
|
||||
];
|
||||
const [registerTable, tableApi] = useKtTable<QqbotApi.Account>({
|
||||
api,
|
||||
|
||||
@ -2,11 +2,13 @@ export const qqbotTargetTypeOptions = [
|
||||
{ label: '全部', value: 'all' },
|
||||
{ label: '私聊', value: 'private' },
|
||||
{ label: '群聊', value: 'group' },
|
||||
{ label: '频道', value: 'channel' },
|
||||
];
|
||||
|
||||
export const qqbotMessageTypeOptions = [
|
||||
{ label: '私聊', value: 'private' },
|
||||
{ label: '群聊', value: 'group' },
|
||||
{ label: '频道', value: 'channel' },
|
||||
];
|
||||
|
||||
export const qqbotRuleMatchOptions = [
|
||||
@ -17,6 +19,12 @@ export const qqbotRuleMatchOptions = [
|
||||
|
||||
export const qqbotRuleTargetOptions = qqbotTargetTypeOptions;
|
||||
|
||||
export const qqbotPermissionTargetOptions = [
|
||||
{ label: 'QQ号', value: 'qq' },
|
||||
{ label: '群聊', value: 'group' },
|
||||
{ label: '频道', value: 'channel' },
|
||||
];
|
||||
|
||||
const qqbotDefaultSendStatusOption = {
|
||||
color: 'default',
|
||||
label: '等待中',
|
||||
|
||||
@ -7,12 +7,20 @@ import type {
|
||||
KtTableRowAction,
|
||||
} from '#/components/ktTable';
|
||||
|
||||
import { computed, defineComponent, reactive, ref, watch } from 'vue';
|
||||
import {
|
||||
computed,
|
||||
defineComponent,
|
||||
onMounted,
|
||||
reactive,
|
||||
ref,
|
||||
watch,
|
||||
} from 'vue';
|
||||
|
||||
import { Page } from '@vben/common-ui';
|
||||
import { Plus } from '@vben/icons';
|
||||
|
||||
import {
|
||||
Button,
|
||||
Form,
|
||||
FormItem,
|
||||
Input,
|
||||
@ -27,14 +35,20 @@ import {
|
||||
import {
|
||||
createQqbotPermission,
|
||||
deleteQqbotPermission,
|
||||
getQqbotPermissionConfig,
|
||||
getQqbotPermissionList,
|
||||
updateQqbotPermission,
|
||||
updateQqbotPermissionConfig,
|
||||
} from '#/api/qqbot';
|
||||
import { KtTable, useKtTable } from '#/components/ktTable';
|
||||
|
||||
import { getOptionLabel, qqbotTargetTypeOptions } from '../modules/options';
|
||||
import {
|
||||
getOptionLabel,
|
||||
qqbotPermissionTargetOptions,
|
||||
} from '../modules/options';
|
||||
|
||||
const AKtTable = KtTable as any;
|
||||
const AButton = Button as any;
|
||||
const AInput = Input as any;
|
||||
const AModal = Modal as any;
|
||||
const ASelect = Select as any;
|
||||
@ -42,20 +56,33 @@ const ASwitch = Switch as any;
|
||||
const ATabs = Tabs as any;
|
||||
|
||||
type PermissionKind = 'allowlist' | 'blocklist';
|
||||
type PermissionTargetType = QqbotApi.PermissionBody['targetType'];
|
||||
const permissionTargetTabItems = qqbotPermissionTargetOptions.map((item) => ({
|
||||
key: item.value,
|
||||
label: item.label,
|
||||
}));
|
||||
|
||||
export default defineComponent({
|
||||
name: 'QqBotPermissionList',
|
||||
setup() {
|
||||
const activeKind = ref<PermissionKind>('allowlist');
|
||||
const activeTargetType = ref<PermissionTargetType>('qq');
|
||||
const configSaving = ref(false);
|
||||
const saving = ref(false);
|
||||
const modalOpen = ref(false);
|
||||
const editingId = ref<string>();
|
||||
const permissionConfig = reactive<QqbotApi.PermissionConfig>({
|
||||
allowlistEnabled: false,
|
||||
blocklistEnabled: true,
|
||||
});
|
||||
const form = reactive<QqbotApi.PermissionBody>({
|
||||
enabled: true,
|
||||
preciseUser: false,
|
||||
remark: '',
|
||||
selfId: '',
|
||||
targetId: '',
|
||||
targetType: 'private',
|
||||
targetType: 'qq',
|
||||
userId: '',
|
||||
});
|
||||
const columns: Array<TableColumnType<QqbotApi.Permission>> = [
|
||||
{ dataIndex: 'selfId', key: 'selfId', title: 'Self ID', width: 150 },
|
||||
@ -66,12 +93,22 @@ export default defineComponent({
|
||||
width: 110,
|
||||
},
|
||||
{ dataIndex: 'targetId', key: 'targetId', title: '目标 ID', width: 160 },
|
||||
{
|
||||
dataIndex: 'preciseUser',
|
||||
key: 'preciseUser',
|
||||
title: '精确 QQ',
|
||||
width: 100,
|
||||
},
|
||||
{ dataIndex: 'userId', key: 'userId', title: 'QQ 号', width: 150 },
|
||||
{ dataIndex: 'enabled', key: 'enabled', title: '状态', width: 100 },
|
||||
{ dataIndex: 'remark', key: 'remark', title: '备注', width: 260 },
|
||||
];
|
||||
const api: KtTableApi<QqbotApi.Permission> = {
|
||||
list: async (params) =>
|
||||
await getQqbotPermissionList(activeKind.value, params),
|
||||
await getQqbotPermissionList(activeKind.value, {
|
||||
...params,
|
||||
targetType: activeTargetType.value,
|
||||
}),
|
||||
};
|
||||
const buttons: Array<KtTableButton<QqbotApi.Permission>> = [
|
||||
{
|
||||
@ -116,58 +153,99 @@ export default defineComponent({
|
||||
fieldName: 'selfId',
|
||||
label: 'Self ID',
|
||||
},
|
||||
{
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
options: qqbotTargetTypeOptions,
|
||||
},
|
||||
fieldName: 'targetType',
|
||||
label: '目标类型',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
componentProps: { allowClear: true, placeholder: '目标 ID' },
|
||||
fieldName: 'targetId',
|
||||
label: '目标 ID',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
componentProps: { allowClear: true, placeholder: 'QQ 号' },
|
||||
fieldName: 'userId',
|
||||
label: 'QQ 号',
|
||||
},
|
||||
],
|
||||
},
|
||||
rowActions,
|
||||
tableTitle: '权限名单',
|
||||
});
|
||||
const activeTargetLabel = computed(() => getPermissionTargetLabel());
|
||||
const modalTitle = computed(
|
||||
() =>
|
||||
`${editingId.value ? '编辑' : '新增'}${activeKind.value === 'allowlist' ? '白名单' : '黑名单'}`,
|
||||
`${editingId.value ? '编辑' : '新增'}${activeTargetLabel.value}${activeKind.value === 'allowlist' ? '白名单' : '黑名单'}`,
|
||||
);
|
||||
const targetIdLabel = computed(() => {
|
||||
if (activeTargetType.value === 'group') return '群号';
|
||||
if (activeTargetType.value === 'channel') return '频道 ID';
|
||||
return 'QQ 号';
|
||||
});
|
||||
|
||||
watch(activeKind, async () => {
|
||||
onMounted(() => {
|
||||
void loadConfig();
|
||||
});
|
||||
|
||||
watch([activeKind, activeTargetType], async () => {
|
||||
await tableApi.reset();
|
||||
});
|
||||
|
||||
async function loadConfig() {
|
||||
Object.assign(permissionConfig, await getQqbotPermissionConfig());
|
||||
}
|
||||
|
||||
async function saveConfig() {
|
||||
configSaving.value = true;
|
||||
try {
|
||||
Object.assign(
|
||||
permissionConfig,
|
||||
await updateQqbotPermissionConfig(permissionConfig),
|
||||
);
|
||||
message.success('权限配置保存成功');
|
||||
} finally {
|
||||
configSaving.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function openCreate() {
|
||||
editingId.value = undefined;
|
||||
Object.assign(form, {
|
||||
enabled: true,
|
||||
preciseUser: false,
|
||||
remark: '',
|
||||
selfId: '',
|
||||
targetId: '',
|
||||
targetType: 'private',
|
||||
targetType: activeTargetType.value,
|
||||
userId: '',
|
||||
});
|
||||
modalOpen.value = true;
|
||||
}
|
||||
|
||||
function openEdit(row: QqbotApi.Permission) {
|
||||
editingId.value = row.id;
|
||||
Object.assign(form, { ...row });
|
||||
activeTargetType.value = normalizePermissionTargetType(row.targetType);
|
||||
Object.assign(form, {
|
||||
...row,
|
||||
preciseUser: !!row.preciseUser,
|
||||
targetType: activeTargetType.value,
|
||||
userId: row.userId || '',
|
||||
});
|
||||
modalOpen.value = true;
|
||||
}
|
||||
|
||||
async function submitPermission() {
|
||||
if (form.targetType !== 'all' && !form.targetId.trim()) {
|
||||
message.warning('请填写目标 ID');
|
||||
form.targetType = activeTargetType.value;
|
||||
if (!form.targetId.trim()) {
|
||||
message.warning(`请填写${targetIdLabel.value}`);
|
||||
return;
|
||||
}
|
||||
if (isPreciseAvailable() && form.preciseUser && !form.userId?.trim()) {
|
||||
message.warning('开启精确到 QQ 号后必须填写 QQ 号');
|
||||
return;
|
||||
}
|
||||
if (!isPreciseAvailable()) {
|
||||
form.preciseUser = false;
|
||||
form.userId = '';
|
||||
}
|
||||
|
||||
saving.value = true;
|
||||
try {
|
||||
@ -185,9 +263,65 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
|
||||
function getPermissionTargetLabel(value = activeTargetType.value) {
|
||||
return getOptionLabel(qqbotPermissionTargetOptions, value);
|
||||
}
|
||||
|
||||
function isPreciseAvailable() {
|
||||
return (
|
||||
activeTargetType.value === 'group' ||
|
||||
activeTargetType.value === 'channel'
|
||||
);
|
||||
}
|
||||
|
||||
function normalizePermissionTargetType(
|
||||
value?: string,
|
||||
): PermissionTargetType {
|
||||
if (value === 'group' || value === 'channel' || value === 'qq') {
|
||||
return value;
|
||||
}
|
||||
return 'qq';
|
||||
}
|
||||
|
||||
return () => (
|
||||
<Page autoContentHeight>
|
||||
<div style={{ display: 'grid', gap: '12px' }}>
|
||||
<div
|
||||
style={{
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
gap: '20px',
|
||||
justifyContent: 'space-between',
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', gap: '20px' }}>
|
||||
<span>
|
||||
白名单过滤:
|
||||
<ASwitch
|
||||
checked={permissionConfig.allowlistEnabled}
|
||||
{...{
|
||||
'onUpdate:checked': (value: boolean) => {
|
||||
permissionConfig.allowlistEnabled = value;
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
黑名单过滤:
|
||||
<ASwitch
|
||||
checked={permissionConfig.blocklistEnabled}
|
||||
{...{
|
||||
'onUpdate:checked': (value: boolean) => {
|
||||
permissionConfig.blocklistEnabled = value;
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<AButton loading={configSaving.value} onClick={saveConfig}>
|
||||
保存配置
|
||||
</AButton>
|
||||
</div>
|
||||
<ATabs
|
||||
activeKey={activeKind.value}
|
||||
items={[
|
||||
@ -200,6 +334,15 @@ export default defineComponent({
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<ATabs
|
||||
activeKey={activeTargetType.value}
|
||||
items={permissionTargetTabItems}
|
||||
{...{
|
||||
'onUpdate:activeKey': (value: PermissionTargetType) => {
|
||||
activeTargetType.value = value;
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<AKtTable
|
||||
onRegister={registerTable}
|
||||
v-slots={{
|
||||
@ -213,7 +356,16 @@ export default defineComponent({
|
||||
);
|
||||
}
|
||||
if (column.key === 'targetType') {
|
||||
return getOptionLabel(qqbotTargetTypeOptions, row.targetType);
|
||||
return getPermissionTargetLabel(row.targetType);
|
||||
}
|
||||
if (column.key === 'preciseUser') {
|
||||
if (row.targetType === 'qq' || row.targetType === 'private') {
|
||||
return '-';
|
||||
}
|
||||
return row.preciseUser ? '是' : '否';
|
||||
}
|
||||
if (column.key === 'userId') {
|
||||
return row.preciseUser ? row.userId || '-' : '-';
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
@ -246,29 +398,50 @@ export default defineComponent({
|
||||
</FormItem>
|
||||
<FormItem label="目标类型">
|
||||
<ASelect
|
||||
{...{
|
||||
'onUpdate:value': (
|
||||
value: QqbotApi.PermissionBody['targetType'],
|
||||
) => {
|
||||
form.targetType = value;
|
||||
},
|
||||
}}
|
||||
options={qqbotTargetTypeOptions}
|
||||
value={form.targetType}
|
||||
disabled
|
||||
options={qqbotPermissionTargetOptions}
|
||||
value={activeTargetType.value}
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label="目标 ID">
|
||||
<FormItem label={targetIdLabel.value}>
|
||||
<AInput
|
||||
disabled={form.targetType === 'all'}
|
||||
{...{
|
||||
'onUpdate:value': (value: string) => {
|
||||
form.targetId = value;
|
||||
},
|
||||
}}
|
||||
placeholder="私聊填 QQ 号,群聊填群号"
|
||||
placeholder={`请填写${targetIdLabel.value}`}
|
||||
value={form.targetId}
|
||||
/>
|
||||
</FormItem>
|
||||
{isPreciseAvailable() && (
|
||||
<>
|
||||
<FormItem label="精确 QQ">
|
||||
<ASwitch
|
||||
checked={form.preciseUser}
|
||||
{...{
|
||||
'onUpdate:checked': (value: boolean) => {
|
||||
form.preciseUser = value;
|
||||
if (!value) form.userId = '';
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</FormItem>
|
||||
{form.preciseUser && (
|
||||
<FormItem label="QQ 号">
|
||||
<AInput
|
||||
{...{
|
||||
'onUpdate:value': (value: string) => {
|
||||
form.userId = value;
|
||||
},
|
||||
}}
|
||||
placeholder="请填写需要精确匹配的 QQ 号"
|
||||
value={form.userId}
|
||||
/>
|
||||
</FormItem>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<FormItem label="启用">
|
||||
<ASwitch
|
||||
checked={form.enabled}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user