kt-template-admin/apps/web-antdv-next/src/views/qqbot/account/components/AccountConfigPanel.tsx

456 lines
14 KiB
TypeScript
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.

import type { TableColumnType } from 'antdv-next';
import type { PropType } from 'vue';
import type { QqbotApi } from '#/api/qqbot';
import type { KtTableRowAction } from '#/components/ktTable';
import { computed, defineComponent, ref, watch } from 'vue';
import { message, Spin, Tabs, Tag } from 'antdv-next';
import {
bindQqbotAccountCommand,
bindQqbotAccountRule,
bindQqbotEventPlugin,
getQqbotCommandList,
getQqbotEventPluginList,
getQqbotRuleList,
unbindQqbotAccountCommand,
unbindQqbotAccountRule,
unbindQqbotEventPlugin,
} from '#/api/qqbot';
import { KtTable } from '#/components/ktTable';
import {
getOptionLabel,
qqbotRuleMatchOptions,
qqbotRuleTargetOptions,
} from '../../modules/options';
const AKtTable = KtTable as any;
const ASpin = Spin as any;
const ATabs = Tabs as any;
const configTabItems = [
{ key: 'command', label: '在线命令' },
{ key: 'event', label: '事件触发' },
{ key: 'rule', label: '自动回复规则' },
] as const;
type ConfigTabKey = (typeof configTabItems)[number]['key'];
export default defineComponent({
name: 'QqBotAccountConfigPanel',
props: {
account: {
default: undefined,
type: Object as PropType<QqbotApi.Account | undefined>,
},
},
setup(props) {
const activeTab = ref<ConfigTabKey>('command');
const boundCommands = ref<QqbotApi.Command[]>([]);
const boundRules = ref<QqbotApi.Rule[]>([]);
const commandTemplates = ref<QqbotApi.Command[]>([]);
const eventPlugins = ref<QqbotApi.EventPlugin[]>([]);
const loading = ref(false);
const ruleTemplates = ref<QqbotApi.Rule[]>([]);
const currentSelfId = computed(() => props.account?.selfId || '');
const boundCommandIds = computed(
() => new Set(boundCommands.value.map((item) => item.id)),
);
const boundRuleIds = computed(
() => new Set(boundRules.value.map((item) => item.id)),
);
const mergedCommandTemplates = computed(() =>
mergeById(commandTemplates.value, boundCommands.value),
);
const mergedRuleTemplates = computed(() =>
mergeById(ruleTemplates.value, boundRules.value),
);
const commandColumns: Array<TableColumnType<QqbotApi.Command>> = [
{ dataIndex: 'name', key: 'name', title: '命令模板', width: 160 },
{ dataIndex: 'code', key: 'code', title: '命令编码', width: 140 },
{ dataIndex: 'aliases', key: 'aliases', title: '别名', width: 200 },
{ dataIndex: 'pluginKey', key: 'pluginKey', title: '插件', width: 140 },
{
dataIndex: 'targetType',
key: 'targetType',
title: '目标范围',
width: 100,
},
{ dataIndex: 'enabled', key: 'enabled', title: '模板状态', width: 100 },
{ dataIndex: 'bound', key: 'bound', title: '绑定状态', width: 100 },
];
const commandRowActions: Array<KtTableRowAction<QqbotApi.Command>> = [
{
key: 'bind',
label: '绑定',
onClick: async (row) => handleCommandBind(row),
rowVisible: (row) => !boundCommandIds.value.has(row.id),
},
{
confirm: (row) => `确认从当前账号解绑「${row.name || row.code}」吗?`,
danger: true,
key: 'unbind',
label: '解绑',
onClick: async (row) => handleCommandUnbind(row),
rowVisible: (row) => boundCommandIds.value.has(row.id),
},
];
const eventColumns: Array<TableColumnType<QqbotApi.EventPlugin>> = [
{ dataIndex: 'name', key: 'name', title: '插件模板', width: 160 },
{ dataIndex: 'key', key: 'key', title: '插件 Key', width: 160 },
{
dataIndex: 'triggerType',
key: 'triggerType',
title: '触发类型',
width: 100,
},
{
dataIndex: 'description',
key: 'description',
title: '说明',
width: 320,
},
{ dataIndex: 'bound', key: 'bound', title: '绑定状态', width: 100 },
];
const eventRowActions: Array<KtTableRowAction<QqbotApi.EventPlugin>> = [
{
key: 'bind',
label: '绑定',
onClick: async (row) => handleEventBind(row),
rowVisible: (row) => !row.bound,
},
{
confirm: (row) => `确认从当前账号解绑「${row.name}」吗?`,
danger: true,
key: 'unbind',
label: '解绑',
onClick: async (row) => handleEventUnbind(row),
rowVisible: (row) => row.bound,
},
];
const ruleColumns: Array<TableColumnType<QqbotApi.Rule>> = [
{ dataIndex: 'name', key: 'name', title: '规则模板', width: 160 },
{ dataIndex: 'keyword', key: 'keyword', title: '关键词', width: 180 },
{
dataIndex: 'matchType',
key: 'matchType',
title: '匹配方式',
width: 110,
},
{
dataIndex: 'targetType',
key: 'targetType',
title: '目标范围',
width: 100,
},
{
dataIndex: 'replyContent',
key: 'replyContent',
title: '回复模板',
width: 320,
},
{ dataIndex: 'enabled', key: 'enabled', title: '模板状态', width: 100 },
{ dataIndex: 'bound', key: 'bound', title: '绑定状态', width: 100 },
];
const ruleRowActions: Array<KtTableRowAction<QqbotApi.Rule>> = [
{
key: 'bind',
label: '绑定',
onClick: async (row) => handleRuleBind(row),
rowVisible: (row) => !boundRuleIds.value.has(row.id),
},
{
confirm: (row) =>
`确认从当前账号解绑「${row.name || row.keyword}」吗?`,
danger: true,
key: 'unbind',
label: '解绑',
onClick: async (row) => handleRuleUnbind(row),
rowVisible: (row) => boundRuleIds.value.has(row.id),
},
];
const activeColumns = computed(() => {
if (activeTab.value === 'event') return eventColumns;
if (activeTab.value === 'rule') return ruleColumns;
return commandColumns;
});
const activeRows = computed(() => {
if (activeTab.value === 'event') return eventPlugins.value;
if (activeTab.value === 'rule') return mergedRuleTemplates.value;
return mergedCommandTemplates.value;
});
const activeRowActions = computed(() => {
if (activeTab.value === 'event') return eventRowActions;
if (activeTab.value === 'rule') return ruleRowActions;
return commandRowActions;
});
const activeRowKey = computed(() => {
if (activeTab.value === 'event') {
return (row: QqbotApi.EventPlugin) =>
`${currentSelfId.value}:${row.key}`;
}
return 'id';
});
watch(
currentSelfId,
(selfId) => {
if (!selfId) {
boundCommands.value = [];
boundRules.value = [];
commandTemplates.value = [];
eventPlugins.value = [];
ruleTemplates.value = [];
return;
}
void refreshAll();
},
{ immediate: true },
);
async function refreshAll() {
loading.value = true;
try {
await Promise.all([
refreshCommandTemplates(),
refreshEventPlugins(),
refreshRuleTemplates(),
]);
} finally {
loading.value = false;
}
}
async function refreshCommandTemplates() {
const [templateResult, boundResult] = await Promise.all([
getQqbotCommandList({ pageNo: 1, pageSize: 500 }),
getQqbotCommandList({
pageNo: 1,
pageSize: 500,
selfId: currentSelfId.value,
}),
]);
commandTemplates.value = templateResult.list || [];
boundCommands.value = boundResult.list || [];
}
async function refreshCommandBindings() {
const result = await getQqbotCommandList({
pageNo: 1,
pageSize: 500,
selfId: currentSelfId.value,
});
boundCommands.value = result.list || [];
}
async function refreshEventPlugins() {
eventPlugins.value = await getQqbotEventPluginList({
selfId: currentSelfId.value,
});
}
async function refreshRuleTemplates() {
const [templateResult, boundResult] = await Promise.all([
getQqbotRuleList({ pageNo: 1, pageSize: 500 }),
getQqbotRuleList({
pageNo: 1,
pageSize: 500,
selfId: currentSelfId.value,
}),
]);
ruleTemplates.value = templateResult.list || [];
boundRules.value = boundResult.list || [];
}
async function refreshRuleBindings() {
const result = await getQqbotRuleList({
pageNo: 1,
pageSize: 500,
selfId: currentSelfId.value,
});
boundRules.value = result.list || [];
}
async function handleCommandBind(row: QqbotApi.Command) {
if (!ensureSelfId()) return;
await bindQqbotAccountCommand(currentSelfId.value, row.id);
message.success('命令已绑定到当前账号');
await refreshCommandBindings();
}
async function handleCommandUnbind(row: QqbotApi.Command) {
if (!ensureSelfId()) return;
await unbindQqbotAccountCommand(currentSelfId.value, row.id);
message.success('命令已从当前账号解绑');
await refreshCommandBindings();
}
async function handleEventBind(row: QqbotApi.EventPlugin) {
if (!ensureSelfId()) return;
await bindQqbotEventPlugin(currentSelfId.value, row.key);
message.success('事件插件已绑定到当前账号');
await refreshEventPlugins();
}
async function handleEventUnbind(row: QqbotApi.EventPlugin) {
if (!ensureSelfId()) return;
await unbindQqbotEventPlugin(currentSelfId.value, row.key);
message.success('事件插件已从当前账号解绑');
await refreshEventPlugins();
}
async function handleRuleBind(row: QqbotApi.Rule) {
if (!ensureSelfId()) return;
await bindQqbotAccountRule(currentSelfId.value, row.id);
message.success('规则已绑定到当前账号');
await refreshRuleBindings();
}
async function handleRuleUnbind(row: QqbotApi.Rule) {
if (!ensureSelfId()) return;
await unbindQqbotAccountRule(currentSelfId.value, row.id);
message.success('规则已从当前账号解绑');
await refreshRuleBindings();
}
function ensureSelfId() {
if (currentSelfId.value) return true;
message.warning('缺少账号 Self ID请从账号连接列表进入配置页');
return false;
}
function mergeById<T extends { id: string }>(templates: T[], bound: T[]) {
const map = new Map<string, T>();
templates.forEach((item) => map.set(item.id, item));
bound.forEach((item) => {
if (!map.has(item.id)) map.set(item.id, item);
});
return [...map.values()];
}
const renderBoundTag = (bound: boolean) => {
return (
<Tag color={bound ? 'success' : 'default'}>
{bound ? '已绑定' : '未绑定'}
</Tag>
);
};
const renderEnabledTag = (enabled: boolean) => {
return (
<Tag color={enabled ? 'success' : 'default'}>
{enabled ? '启用' : '停用'}
</Tag>
);
};
const renderTableTitle = () => {
return (
<div class="qqbot-account-config-panel__table-title">
<span></span>
<Tag color="processing">{`Self ID${currentSelfId.value || '-'}`}</Tag>
{props.account?.name ? <Tag>{props.account.name}</Tag> : null}
</div>
);
};
const renderHeaderControls = () => {
return (
<div class="kt-table__header-control-group">
<ATabs
class="kt-table__header-tabs"
items={[...configTabItems]}
v-model:activeKey={activeTab.value}
/>
</div>
);
};
const renderBodyCell = ({ column, record }: any) => {
if (activeTab.value === 'event') {
const row = record as QqbotApi.EventPlugin;
if (column.key === 'triggerType') {
return row.triggerType === 'message' ? '消息事件' : row.triggerType;
}
if (column.key === 'bound') {
return renderBoundTag(row.bound);
}
return undefined;
}
if (activeTab.value === 'rule') {
const row = record as QqbotApi.Rule;
const bound = boundRuleIds.value.has(row.id);
if (column.key === 'matchType') {
return getOptionLabel(qqbotRuleMatchOptions, row.matchType);
}
if (column.key === 'targetType') {
return getOptionLabel(qqbotRuleTargetOptions, row.targetType);
}
if (column.key === 'replyContent') {
return (
<span class="qqbot-account-config-panel__ellipsis">
{row.replyContent || '-'}
</span>
);
}
if (column.key === 'enabled') {
return renderEnabledTag(row.enabled);
}
if (column.key === 'bound') {
return renderBoundTag(bound);
}
return undefined;
}
const row = record as QqbotApi.Command;
const bound = boundCommandIds.value.has(row.id);
if (column.key === 'aliases') {
return row.aliases?.join(' / ') || '-';
}
if (column.key === 'targetType') {
return getOptionLabel(qqbotRuleTargetOptions, row.targetType);
}
if (column.key === 'enabled') {
return renderEnabledTag(row.enabled);
}
if (column.key === 'bound') {
return renderBoundTag(bound);
}
return undefined;
};
return () => (
<div class="qqbot-account-config-panel">
<div class="qqbot-account-config-panel__spin">
<ASpin spinning={loading.value}>
<AKtTable
class="qqbot-account-config-panel__table"
columns={activeColumns.value}
dataSource={activeRows.value}
rowActions={activeRowActions.value}
rowKey={activeRowKey.value}
showDefaultButtons={false}
showFooter={false}
showIndex={false}
showPagination={false}
showTableSetting={false}
size="small"
v-slots={{
bodyCell: renderBodyCell,
headerControls: renderHeaderControls,
title: renderTableTitle,
}}
/>
</ASpin>
</div>
</div>
);
},
});