feat(dingtalk): add download link for human input card template and enhance dynamic form configuration

This commit is contained in:
fdc310
2026-06-24 11:23:02 +08:00
parent ea4cd80939
commit 5e6b3cc550
7 changed files with 140 additions and 6 deletions
@@ -268,6 +268,10 @@ export default function BotForm({
options: item.options,
show_if: item.show_if,
login_platform: item.login_platform,
url: item.url,
download_filename: item.download_filename,
help_links: item.help_links,
help_label: item.help_label,
}),
),
);
@@ -20,9 +20,17 @@ import { useTranslation } from 'react-i18next';
import { cn } from '@/lib/utils';
import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button';
import { Copy, Check, Globe, QrCode } from 'lucide-react';
import {
Copy,
Check,
Globe,
QrCode,
Download,
ExternalLink,
} from 'lucide-react';
import { copyToClipboard } from '@/app/utils/clipboard';
import { systemInfo } from '@/app/infra/http';
import { getAdapterDocUrl } from '@/app/infra/entities/adapter-docs';
/**
* Resolve the value referenced by a `show_if.field` string.
@@ -190,6 +198,50 @@ function WebhookUrlField({
);
}
function DownloadLinkField({
label,
description,
url,
filename,
helpUrl,
helpLabel,
}: {
label: string;
description?: string;
url: string;
filename?: string;
helpUrl?: string | null;
helpLabel: string;
}) {
const baseUrl = import.meta.env.VITE_API_BASE_URL || window.location.origin;
const downloadUrl = url.startsWith('http') ? url : `${baseUrl}${url}`;
return (
<FormItem>
<FormLabel>{label}</FormLabel>
<div className="flex items-center gap-2">
<Button asChild variant="outline" size="sm">
<a href={downloadUrl} download={filename}>
<Download className="h-4 w-4" />
{label}
</a>
</Button>
{helpUrl && (
<Button asChild variant="ghost" size="sm">
<a href={helpUrl} target="_blank" rel="noopener noreferrer">
<ExternalLink className="h-4 w-4" />
{helpLabel}
</a>
</Button>
)}
</div>
{description && (
<p className="text-sm text-muted-foreground max-w-2xl">{description}</p>
)}
</FormItem>
);
}
export default function DynamicFormComponent({
itemConfigList,
onSubmit,
@@ -215,7 +267,7 @@ export default function DynamicFormComponent({
}) {
const isInitialMount = useRef(true);
const previousInitialValues = useRef(initialValues);
const { t } = useTranslation();
const { t, i18n } = useTranslation();
// Normalize a form value according to its field type.
// This ensures legacy/malformed data (e.g. a plain string for
@@ -261,7 +313,8 @@ export default function DynamicFormComponent({
(item) =>
item.type !== 'webhook-url' &&
item.type !== 'embed-code' &&
item.type !== 'qr-code-login',
item.type !== 'qr-code-login' &&
item.type !== 'download-link',
),
[itemConfigList],
);
@@ -563,6 +616,30 @@ export default function DynamicFormComponent({
);
}
if (config.type === 'download-link') {
if (!config.url) return null;
return (
<DownloadLinkField
key={config.id}
label={extractI18nObject(config.label)}
description={
config.description
? extractI18nObject(config.description)
: undefined
}
url={config.url}
filename={config.download_filename}
helpUrl={getAdapterDocUrl(config.help_links, i18n.language)}
helpLabel={
config.help_label
? extractI18nObject(config.help_label)
: t('bots.viewAdapterDocs')
}
/>
);
}
// QR code login button (e.g. Feishu one-click create, WeChat scan login)
if (config.type === 'qr-code-login') {
return (
@@ -17,6 +17,10 @@ export class DynamicFormItemConfig implements IDynamicFormItemSchema {
options?: IDynamicFormItemOption[];
show_if?: IShowIfCondition;
login_platform?: string;
url?: string;
download_filename?: string;
help_links?: Record<string, string>;
help_label?: I18nObject;
constructor(params: IDynamicFormItemSchema) {
this.id = params.id;
@@ -29,6 +33,10 @@ export class DynamicFormItemConfig implements IDynamicFormItemSchema {
this.options = params.options;
this.show_if = params.show_if;
this.login_platform = params.login_platform;
this.url = params.url;
this.download_filename = params.download_filename;
this.help_links = params.help_links;
this.help_label = params.help_label;
}
}
@@ -22,6 +22,10 @@ export interface IDynamicFormItemSchema {
scopes?: string[];
accept?: string; // For file type: accepted MIME types
login_platform?: string; // For qr-code-login type: platform identifier (e.g. 'feishu', 'weixin')
url?: string; // For download-link type: relative or absolute download URL
download_filename?: string; // Optional filename for download-link type
help_links?: Record<string, string>; // Optional docs links for display-only fields
help_label?: I18nObject; // Optional label for help_links
}
export enum DynamicFormItemType {
@@ -48,6 +52,7 @@ export enum DynamicFormItemType {
WEBHOOK_URL = 'webhook-url',
EMBED_CODE = 'embed-code',
QR_CODE_LOGIN = 'qr-code-login',
DOWNLOAD_LINK = 'download-link',
}
export interface IFileConfig {
+8
View File
@@ -229,6 +229,10 @@ export default function WizardPage() {
options: item.options,
show_if: item.show_if,
login_platform: item.login_platform,
url: item.url,
download_filename: item.download_filename,
help_links: item.help_links,
help_label: item.help_label,
}),
);
}, [adapters, selectedAdapter]);
@@ -249,6 +253,10 @@ export default function WizardPage() {
options: item.options,
show_if: item.show_if,
login_platform: item.login_platform,
url: item.url,
download_filename: item.download_filename,
help_links: item.help_links,
help_label: item.help_label,
}),
);
}, [selectedRunnerConfigStage]);