Refactor AI chat: streaming, tool calls, UI polish
All checks were successful
CI / check (pull_request) Successful in 3m25s

This commit is contained in:
2026-07-24 16:27:50 +08:00
parent e605586fc9
commit 8656394b9b
55 changed files with 2774 additions and 460 deletions

View File

@@ -41,6 +41,12 @@ import {
isAppSecretRequired,
type DingTalkConfigFormValues,
} from './integration-config-form';
import {
cacheDingTalkDraft,
cacheDingTalkServerSnapshot,
commitDingTalkConfig,
readDingTalkConfigCache,
} from './integration-config-cache';
interface DingTalkConfig {
agentId: string;
@@ -111,13 +117,14 @@ interface DeleteAttendanceGroupsResponse {
}
const IntegrationConfigPage: React.FC = () => {
const initialCache = useMemo(() => readDingTalkConfigCache(), []);
const { hasPermission, hasAllPermissions } = usePermission();
const canCreateClass = hasPermission('class:create');
const [loading, setLoading] = useState(false);
const [loading, setLoading] = useState(!initialCache.loaded);
const [saving, setSaving] = useState(false);
const [testing, setTesting] = useState(false);
const [config, setConfig] = useState<DingTalkConfig | null>(null);
const [verified, setVerified] = useState<boolean | null>(null);
const [config, setConfig] = useState<DingTalkConfig | null>(initialCache.config);
const [verified, setVerified] = useState<boolean | null>(initialCache.verified);
const [form] = Form.useForm<DingTalkConfigFormValues>();
// ── Manual organization sync ──
@@ -137,8 +144,8 @@ const IntegrationConfigPage: React.FC = () => {
const [loadingGroups, setLoadingGroups] = useState(false);
const [deletingGroups, setDeletingGroups] = useState(false);
const fetchConfig = async () => {
setLoading(true);
const fetchConfig = useCallback(async (showLoading = false) => {
if (showLoading) setLoading(true);
try {
const res = await api.get<{
success: boolean;
@@ -148,18 +155,24 @@ const IntegrationConfigPage: React.FC = () => {
if (dt) {
setConfig(dt.config);
setVerified(dt.verify);
form.setFieldsValue(dt.config);
cacheDingTalkServerSnapshot(dt.config, dt.verify);
form.setFieldsValue(readDingTalkConfigCache().formValues);
} else {
setConfig(null);
setVerified(null);
cacheDingTalkServerSnapshot(null, null);
}
} catch {
// not configured
} finally {
setLoading(false);
if (showLoading) setLoading(false);
}
};
}, [form]);
useEffect(() => {
void fetchConfig();
}, []);
form.setFieldsValue(initialCache.formValues);
void fetchConfig(!initialCache.loaded);
}, [fetchConfig, form, initialCache]);
const handleSave = async () => {
const values = await form.validateFields();
@@ -168,6 +181,8 @@ const IntegrationConfigPage: React.FC = () => {
try {
await api.post('/integration/config', { type: 'DINGTALK', config: payload });
message.success('配置已保存');
commitDingTalkConfig({ corpId: payload.corpId, agentId: payload.agentId });
form.setFieldValue('appSecret', undefined);
await fetchConfig();
} catch (e: unknown) {
const err = e as { message?: string };
@@ -631,7 +646,13 @@ const IntegrationConfigPage: React.FC = () => {
showIcon
/>
<Form form={form} layout="vertical" style={{ maxWidth: 520 }}>
<Form
form={form}
layout="vertical"
initialValues={initialCache.formValues}
onValuesChange={(_changed, values) => cacheDingTalkDraft(values)}
style={{ maxWidth: 520 }}
>
<Form.Item
name="corpId"
label="CorpId企业ID"