import { FormItemRule, FormProps, FormRules, NAlert, NButton, NFlex, NFormItem, NFormItemGi, NGrid, NInput, NInputNumber, NSelect, NSpace, NTag, NText, NTooltip, NEmpty, type DataTableColumns, } from 'naive-ui' import { useModal, useDialog, useTable, useSearch, useModalHooks, useFormHooks, useForm, useLoadingMask, } from '@baota/naive-ui/hooks' import { useError } from '@baota/hooks/error' import { isEmail, isIp, isPort, isUrl, isDomain } from '@baota/utils/business' import { $t } from '@locales/index' import { useStore } from './useStore' import { ApiProjectConfig } from '@config/data' import type { AccessItem, AccessListParams, AddAccessParams, SshAccessConfig, UpdateAccessParams, PanelAccessConfig, NamecheapAccessConfig, NS1AccessConfig, CloudnsAccessConfig, AwsAccessConfig, AzureAccessConfig, NamesiloAccessConfig, NamedotcomAccessConfig, BunnyAccessConfig, GcoreAccessConfig, JdcloudAccessConfig, DogeAccessConfig, PluginAccessConfig, LecdnAccessConfig, ConstellixAccessConfig, WebhookAccessConfig, SpaceshipAccessConfig, BTDomainAccessConfig, } from "@/types/access"; import type { VNode, Ref } from "vue"; import { testAccess, getPlugins } from "@/api/access"; // import { useLocalStorage } from '@vueuse/core' import ApiManageForm from "./components/ApiManageModel"; import SvgIcon from "@components/svgIcon"; import TypeIcon from "@components/typeIcon"; import { noSideSpace } from "@lib/utils"; import { JSX } from "vue/jsx-runtime"; /** * 授权API管理控制器接口 */ interface AuthApiManageControllerExposes { loading: Ref; fetch: (resetPage?: boolean) => Promise; TableComponent: ( props: Record, context: Record ) => VNode; PageComponent: ( props: Record, context: Record ) => VNode; SearchComponent: ( props: Record, context: Record ) => VNode; param: Ref; openAddForm: () => void; } /** * API表单控制器接口 */ interface ApiFormControllerExposes { ApiManageForm: ( props: FormProps, context: Record ) => any; } // 获取Store中的状态和方法 const { accessTypeMap, apiFormProps, fetchAccessList, deleteExistingAccess, addNewAccess, updateExistingAccess, resetApiForm, } = useStore(); // 错误处理钩子 const { handleError } = useError(); /** * 授权API管理业务逻辑控制器 * @description 处理授权API相关的业务逻辑,包括列表展示、表单操作等 * @returns {AuthApiManageControllerExposes} 返回授权API相关的状态数据和处理方法 */ export const useController = (): AuthApiManageControllerExposes => { /** * 测试授权API * @param {AccessItem} row - 授权API信息 */ const handleTestAccess = async (row: AccessItem): Promise => { try { const { fetch, message } = testAccess({ id: row.id, type: row.type }); message.value = true; await fetch(); } catch (error) { handleError(error); } }; /** * 创建表格列配置 * @returns {DataTableColumns} 返回表格列配置数组 */ const createColumns = (): DataTableColumns => [ { title: $t("t_2_1745289353944"), key: "name", width: 200, ellipsis: { tooltip: true, }, }, { title: $t("t_1_1746754499371"), key: "type", width: 140, render: (row) => , }, { title: $t("t_2_1746754500270"), key: "type", width: 200, render: (row) => ( {row.access_type?.map((type) => { return ( {accessTypeMap[type as keyof typeof accessTypeMap]} ); })} ), }, { title: $t("t_7_1745215914189"), key: "create_time", width: 180, }, { title: $t("t_0_1745295228865"), key: "update_time", width: 180, }, { title: $t("t_8_1745215914610"), key: "actions", width: 200, align: "right", fixed: "right", render: (row) => { return ( handleTestAccess(row)} > {$t("t_16_1746676855270")} openEditForm(row)} > {$t("t_11_1745215915429")} confirmDelete(row.id)} > {$t("t_12_1745215914312")} ); }, }, ]; // 表格实例 const { TableComponent, PageComponent, loading, param, fetch } = useTable< AccessItem, AccessListParams >({ config: createColumns(), request: fetchAccessList, watchValue: ["p", "limit"], storage: "authApiManage", alias: { page: "p", pageSize: "limit" }, defaultValue: { p: 1, limit: 10, search: "", }, }); // 搜索实例 const { SearchComponent, search } = useSearch({ onSearch: (value) => { param.value.search = value; fetch(); }, }); /** * 打开添加授权API弹窗 */ const openAddForm = (): void => { useModal({ title: $t("t_0_1745289355714"), area: 500, component: ApiManageForm, footer: true, onUpdateShow: (show) => { if (!show) fetch(); resetApiForm(); }, }); }; /** * 打开编辑授权API弹窗 * @param {AccessItem} row - 授权API信息 */ const openEditForm = (row: AccessItem): void => { useModal({ title: $t("t_4_1745289354902"), area: 500, component: ApiManageForm, componentProps: { data: row }, footer: true, onUpdateShow: (show) => { if (!show) fetch(); resetApiForm(); }, }); }; /** * 确认删除授权API * @param {string} id - 授权API ID */ const confirmDelete = (id: string): void => { useDialog({ title: $t("t_5_1745289355718"), content: $t("t_6_1745289358340"), confirmText: $t("t_5_1744870862719"), cancelText: $t("t_4_1744870861589"), onPositiveClick: async () => { await deleteExistingAccess(id); await fetch(); }, }); }; // 挂载时,获取数据 onMounted(fetch); return { loading, fetch, TableComponent, PageComponent, SearchComponent, param, openAddForm, }; }; /** * 表单控制器Props接口 */ interface ApiFormControllerProps { data?: AccessItem; } interface PluginOption { label: string; value: string; description?: string; pluginName?: string; config?: ConfigItem[]; params?: string; } interface ConfigItem { name: string; type: 'string' | 'boolean' | 'enum'; description: string; required: boolean; options?: Array; config?: any } /** * 授权API表单控制器 * @description 处理授权API表单相关的业务逻辑 * @param {ApiFormControllerProps} props - 表单控制器属性 * @returns {ApiFormControllerExposes} 返回表单控制器对象 */ export const useApiFormController = ( props: ApiFormControllerProps ): ApiFormControllerExposes => { const { confirm } = useModalHooks(); // 弹窗挂载方法 const { open: openLoad, close: closeLoad } = useLoadingMask({ text: $t("t_0_1746667592819"), }); const { useFormInput, useFormRadioButton, useFormSwitch, useFormTextarea, useFormCustom, useFormSelect, useFormHelp, } = useFormHooks(); const param = ( props.data?.id ? ref({ ...props.data, config: JSON.parse(props.data.config) }) : apiFormProps ) as Ref; const pluginActionTips = ref(""); // 插件列表 const pluginList = ref>([]); // 生成插件配置表单控件的辅助函数 const generatePluginFormControl = (configItem: ConfigItem, fieldPath: string, noSideSpace: any) => { const itemAttrs = { required: configItem.required || false, showRequireMark: configItem.required || false, trigger: configItem.required ? 'input' : undefined }; // 根据配置项类型渲染不同的表单控件 switch (configItem.type) { case 'string': return useFormInput( configItem.description, fieldPath, { allowInput: noSideSpace }, itemAttrs ); case 'boolean': return useFormSwitch( configItem.description, fieldPath, { checkedValue: true, uncheckedValue: false }, itemAttrs ); case 'enum': // 确保options存在且是数组 const options = configItem.options || []; return useFormSelect( configItem.description, fieldPath, options.map((opt: any) => ({ label: typeof opt === 'string' ? opt : opt.label || opt, value: typeof opt === 'string' ? opt : opt.value || opt })), {}, itemAttrs ); default: // 默认使用字符串输入框 return useFormInput( configItem.description, fieldPath, { allowInput: noSideSpace }, itemAttrs ); } }; // 表单规则 const rules: Record = { name: { required: true, message: $t("t_27_1745289355721"), trigger: "input", }, type: { required: true, message: $t("t_28_1745289356040"), trigger: "change", }, config: { host: { required: true, trigger: "input", validator: ( rule: FormItemRule, value: string, callback: (error?: Error) => void ) => { if (!isIp(value) && !isDomain(value)) { return callback(new Error($t("t_0_1749119980577"))); } callback(); }, }, port: { required: true, trigger: "input", validator: ( rule: FormItemRule, value: number, callback: (error?: Error) => void ) => { if (!isPort(value.toString())) { return callback(new Error($t("t_1_1745317313096"))); } callback(); }, }, user: { required: true, trigger: "input", message: $t("t_3_1744164839524"), }, username: { trigger: "input", validator: ( rule: FormItemRule, value: string, callback: (error?: Error) => void ) => { if (!value) { const mapTips = { westcn: $t("t_0_1747365600180"), namedotcom: "请输入用户名", lecdn: "请输入用户名", }; return callback( new Error( mapTips[param.value.type as keyof typeof mapTips] || $t("t_0_1747365600180") ) ); } callback(); }, }, password: { trigger: "input", validator: ( rule: FormItemRule, value: string, callback: (error?: Error) => void ) => { // SSH 类型的密码字段在密码模式下是必填的,在密钥模式下是可选的 if (param.value.type === "ssh") { const sshConfig = param.value.config as SshAccessConfig; if (sshConfig?.mode === "password" && !value) { return callback(new Error($t("t_0_1747711335067"))); } // 密钥模式下密码是可选的,不需要验证 callback(); return; } if (!value) { const mapTips = { westcn: $t("t_1_1747365603108"), lecdn: "请输入密码", }; return callback( new Error(mapTips[param.value.type as keyof typeof mapTips]) ); } callback(); }, }, key: { required: true, message: $t("t_31_1745289355715"), trigger: "input", }, url: { required: true, trigger: "input", validator: ( rule: FormItemRule, value: string, callback: (error?: Error) => void ) => { if (!isUrl(value)) { const mapTips = { btpanel: $t("t_2_1745317314362"), btwaf: $t("t_0_1747271295174"), safeline: $t("t_0_1747300383756"), lecdn: "请输入正确的URL地址", webhook: "请输入回调地址", }; return callback( new Error(mapTips[param.value.type as keyof typeof mapTips]) ); } callback(); }, }, api_key: { trigger: "input", validator: ( rule: FormItemRule, value: string, callback: (error?: Error) => void ) => { if (!value || !value.length) { const mapTips = { cloudflare: $t("t_0_1747042966820"), btpanel: $t("t_1_1747042969705"), btwaf: $t("t_1_1747300384579"), godaddy: $t("t_0_1747984137443"), ns1: "请输入API Key", namecheap: "请输入API Key", constellix: "请输入API Key", spaceship: "请输入 Spaceship API Key", }; return callback( new Error(mapTips[param.value.type as keyof typeof mapTips]) ); } callback(); }, }, api_secret: { trigger: "input", validator: ( rule: FormItemRule, value: string, callback: (error?: Error) => void ) => { if (!value) { const mapTips = { godaddy: $t("t_1_1747984133312"), spaceship: "请输入 Spaceship API Secret", }; return callback( new Error(mapTips[param.value.type as keyof typeof mapTips]) ); } callback(); }, }, account_id: { trigger: "input", validator: ( rule: FormItemRule, value: string, callback: (error?: Error) => void ) => { if (!value) { const mapTips = { btdomain: "请输入 宝塔域名 Account ID", }; return callback( new Error(mapTips[param.value.type as keyof typeof mapTips]) ); } callback(); }, }, access_secret: { required: true, message: $t("t_2_1747984134626"), trigger: "input", }, api_token: { required: true, message: $t("t_0_1747617113090"), trigger: "input", }, // access_key_id: { // trigger: 'input', // validator: (rule: FormItemRule, value: string, callback: (error?: Error) => void) => { // if (!value) { // const mapTips = { // aliyun: $t('t_4_1745317314054'), // doge: '请输入多吉云AccessKeyId', // } // return callback(new Error(mapTips[param.value.type as keyof typeof mapTips] || $t('t_4_1745317314054'))) // } // callback() // }, // }, // access_key_secret: { // trigger: 'input', // validator: (rule: FormItemRule, value: string, callback: (error?: Error) => void) => { // if (!value) { // const mapTips = { // aliyun: $t('t_5_1745317315285'), // doge: '请输入多吉云AccessKeySecret', // } // return callback(new Error(mapTips[param.value.type as keyof typeof mapTips] || $t('t_5_1745317315285'))) // } // callback() // }, // }, secret_access_key: { required: true, message: "请输入Secret Access Key", trigger: "input", }, api_user: { required: true, message: "请输入API User", trigger: "input", }, auth_id: { required: true, message: "请输入Auth ID", trigger: "input", }, auth_password: { required: true, message: "请输入Auth Password", trigger: "input", }, tenant_id: { required: true, message: "请输入Tenant ID", trigger: "input", }, client_id: { required: true, message: "请输入Client ID", trigger: "input", }, client_secret: { required: true, message: "请输入Client Secret", trigger: "input", }, secret_id: { trigger: "input", validator: ( rule: FormItemRule, value: string, callback: (error?: Error) => void ) => { if (!value || !value.length) { const mapTips = { tencentcloud: $t("t_2_1747042967277"), edgeone: "请输入正确的 EdgeOne SecretId", }; return callback( new Error(mapTips[param.value.type as keyof typeof mapTips]) ); } callback(); }, }, access_key: { trigger: "input", validator: ( rule: FormItemRule, value: string, callback: (error?: Error) => void ) => { if (!value) { const mapTips = { huawei: $t("t_2_1747271295877"), baidu: $t("t_3_1747271294475"), volcengine: $t("t_3_1747365600828"), qiniu: $t("t_3_1747984134586"), doge: $t("t_0_1750320239265"), btdomain: "请输入 宝塔域名 Access Key", }; return callback( new Error(mapTips[param.value.type as keyof typeof mapTips]) ); } callback(); }, }, secret_key: { trigger: "input", validator: ( rule: FormItemRule, value: string, callback: (error?: Error) => void ) => { if (!value || !value.length) { const mapTips = { tencentcloud: $t("t_2_1747042967277"), edgeone: "请输入 EdgeOne Secret Key", huawei: $t("t_3_1747042967608"), baidu: $t("t_4_1747271294621"), volcengine: $t("t_4_1747365600137"), doge: $t("t_1_1750320241427"), constellix: "请输入Secret Key", btdomain: "请输入 宝塔域名 Secret Key", }; return callback( new Error(mapTips[param.value.type as keyof typeof mapTips]) ); } callback(); }, }, email: { trigger: "input", validator: ( rule: FormItemRule, value: string, callback: (error?: Error) => void ) => { if ( param.value.type === "cloudflare" && (!value || value.trim() === "") ) { return callback(); } if (!isEmail(value)) { return callback(new Error($t("t_5_1747042965911"))); } callback(); }, }, region: { required: true, message: "请输入Region", trigger: "input", }, "config.name": { required: true, message: $t("t_0_1750144125193"), trigger: "change", }, }, }; // 类型列表 const typeList = Object.entries(ApiProjectConfig) .filter( ([_, value]) => !(typeof value.notApi === "boolean" && !value.notApi) ) .map(([key, value]) => ({ label: value.name, value: key, access: value.type || [], })); const typeUrlMap = new Map([ ["btwaf", "宝塔WAF-URL"], ["btpanel", "宝塔面板-URL"], ["1panel", "1Panel-URL"], ["safeline", "雷池WAF-URL"], ]); // 表单配置 const config = computed(() => { const items = [ useFormInput($t("t_2_1745289353944"), "name"), useFormCustom(() => { return ( { return ( {$t("t_0_1745833934390")} ); }, }} /> ); }), ] as any; // 根据不同类型渲染不同的表单项 switch (param.value.type) { case "ssh": { // SSH 基础配置项 const sshBaseItems = [ useFormCustom(() => { return ( ); }), useFormInput($t("t_44_1745289354583"), "config.user"), useFormRadioButton($t("t_45_1745289355714"), "config.mode", [ { label: $t("t_48_1745289355714"), value: "password" }, { label: $t("t_1_1746667588689"), value: "key" }, ]), ]; // 根据认证模式添加对应的字段 const sshAuthItems = []; if ((param.value.config as SshAccessConfig)?.mode === "password") { sshAuthItems.push( useFormInput($t("t_48_1745289355714"), "config.password", { type: "password", showPasswordOn: "click", allowInput: noSideSpace, }) ); } else if ((param.value.config as SshAccessConfig)?.mode === "key") { sshAuthItems.push( useFormTextarea($t("t_1_1746667588689"), "config.key", { rows: 3, placeholder: $t("t_0_1747709067998"), }), // 私钥密码输入框(使用 password 字段) useFormInput( "私钥密码", "config.password", { type: "password", showPasswordOn: "click", allowInput: noSideSpace, placeholder: "请输入私钥密码(可选)", }, { showRequireMark: false } ) ); } // 合并所有 SSH 配置项 items.push(...sshBaseItems, ...sshAuthItems); break; } case "1panel": items.push( // 1Panel版本选择下拉框 useFormCustom(() => { const config = param.value.config as PanelAccessConfig; // 如果version为空或者没有version字段,默认为v1 const currentVersion = config.version || "v1"; return ( { (param.value.config as PanelAccessConfig).version = value; }} /> ); }), useFormInput(typeUrlMap.get(param.value.type) || "", "config.url", { allowInput: noSideSpace, }), useFormInput($t("t_55_1745289355715"), "config.api_key", { type: "password", showPasswordOn: "click", allowInput: noSideSpace, }), useFormSwitch( $t("t_3_1746667592270"), "config.ignore_ssl", { checkedValue: "1", uncheckedValue: "0" }, { showRequireMark: false } ) ); break; case "btpanel": case "btwaf": case "safeline": items.push( useFormInput(typeUrlMap.get(param.value.type) || "", "config.url", { allowInput: noSideSpace, }), useFormInput( param.value.type === "safeline" ? $t("t_1_1747617105179") : $t("t_55_1745289355715"), param.value.type === "safeline" ? "config.api_token" : "config.api_key", { type: "password", showPasswordOn: "click", allowInput: noSideSpace, } ), useFormSwitch( $t("t_3_1746667592270"), "config.ignore_ssl", { checkedValue: "1", uncheckedValue: "0" }, { showRequireMark: false } ) ); break; case "aliyun": items.push( useFormInput("AccessKeyId", "config.access_key_id", { allowInput: noSideSpace, }), useFormInput("AccessKeySecret", "config.access_key_secret", { type: "password", showPasswordOn: "click", allowInput: noSideSpace, }) ); break; case "tencentcloud": items.push( useFormInput("SecretId", "config.secret_id", { allowInput: noSideSpace, }), useFormInput("SecretKey", "config.secret_key", { type: "password", showPasswordOn: "click", allowInput: noSideSpace, }) ); break; case "huaweicloud": case "baidu": case "volcengine": case "doge": items.push( useFormInput("AccessKey", "config.access_key", { allowInput: noSideSpace, }), useFormInput("SecretKey", "config.secret_key", { type: "password", showPasswordOn: "click", allowInput: noSideSpace, }) ); break; case "cloudflare": items.push( useFormInput( "邮箱", "config.email", { allowInput: noSideSpace }, { showRequireMark: false } ), useFormInput("APIKey", "config.api_key", { type: "password", showPasswordOn: "click", allowInput: noSideSpace, }), useFormCustom(() => { return ( 使用API令牌时不要填写邮箱,否则将作为Global Key请求Cloudflare ); }) ); break; case "westcn": items.push( useFormInput("Username", "config.username", { allowInput: noSideSpace, }), useFormInput("Password", "config.password", { type: "password", showPasswordOn: "click", allowInput: noSideSpace, }) ); break; case "godaddy": items.push( useFormInput("API Key", "config.api_key", { type: "password", showPasswordOn: "click", allowInput: noSideSpace, }), useFormInput("API Secret", "config.api_secret", { type: "password", showPasswordOn: "click", allowInput: noSideSpace, }) ); break; case "qiniu": items.push( useFormInput("AccessKey", "config.access_key", { allowInput: noSideSpace, }), useFormInput("AccessSecret", "config.access_secret", { type: "password", showPasswordOn: "click", allowInput: noSideSpace, }) ); break; case "namecheap": items.push( useFormInput("API User", "config.api_user", { allowInput: noSideSpace, }), useFormInput("API Key", "config.api_key", { type: "password", showPasswordOn: "click", allowInput: noSideSpace, }) ); break; case "ns1": items.push( useFormInput("API Key", "config.api_key", { type: "password", showPasswordOn: "click", allowInput: noSideSpace, }) ); break; case "cloudns": items.push( useFormInput("Auth ID", "config.auth_id", { allowInput: noSideSpace, }), useFormInput("Auth Password", "config.auth_password", { type: "password", showPasswordOn: "click", allowInput: noSideSpace, }) ); break; case "aws": items.push( useFormInput("Access Key ID", "config.access_key_id", { allowInput: noSideSpace, }), useFormInput("Secret Access Key", "config.secret_access_key", { type: "password", showPasswordOn: "click", allowInput: noSideSpace, }), useFormInput("Region", "config.region", { allowInput: noSideSpace, }) ); break; case "azure": items.push( useFormInput("Tenant ID", "config.tenant_id", { allowInput: noSideSpace, }), useFormInput("Client ID", "config.client_id", { allowInput: noSideSpace, }), useFormInput("Client Secret", "config.client_secret", { type: "password", showPasswordOn: "click", allowInput: noSideSpace, }), useFormInput("Environment", "config.environment", { allowInput: noSideSpace, placeholder: "public", }) ); break; case "namesilo": items.push( useFormInput("API Key", "config.api_key", { type: "password", showPasswordOn: "click", allowInput: noSideSpace, }) ); break; case "namedotcom": items.push( useFormInput("Username", "config.username", { allowInput: noSideSpace, }), useFormInput("API Token", "config.api_token", { type: "password", showPasswordOn: "click", allowInput: noSideSpace, }) ); break; case "bunny": items.push( useFormInput("API Key", "config.api_key", { type: "password", showPasswordOn: "click", allowInput: noSideSpace, }) ); break; case "gcore": items.push( useFormInput("API Token", "config.api_token", { type: "password", showPasswordOn: "click", allowInput: noSideSpace, }) ); break; case "jdcloud": items.push( useFormInput("Access Key ID", "config.access_key_id", { allowInput: noSideSpace, }), useFormInput("Secret Access Key", "config.secret_access_key", { type: "password", showPasswordOn: "click", allowInput: noSideSpace, }) ); break; case "lecdn": items.push( useFormInput("URL", "config.url", { allowInput: noSideSpace }), useFormInput("Username", "config.username", { allowInput: noSideSpace, }), useFormInput("Password", "config.password", { type: "password", showPasswordOn: "click", allowInput: noSideSpace, }), useFormSwitch( $t("t_3_1746667592270"), "config.ignore_ssl", { checkedValue: "1", uncheckedValue: "0" }, { showRequireMark: false } ) ); break; case "constellix": items.push( useFormInput("API Key", "config.api_key", { type: "password", showPasswordOn: "click", allowInput: noSideSpace, }), useFormInput("Secret Key", "config.secret_key", { type: "password", showPasswordOn: "click", allowInput: noSideSpace, }) ); break; case "webhook": items.push( useFormInput("WebHook回调地址", "config.url"), useFormSelect("请求方式", "config.method", [ { label: "POST", value: "POST" }, { label: "GET", value: "GET" }, ]), useFormTextarea( "WebHook请求头(可选)", "config.headers", { rows: 3, placeholder: "Content-Type: application/json" }, { showRequireMark: false } ), useFormTextarea( "WebHook推送通知回调数据(可选)", "config.data", { rows: 3, placeholder: '请使用JSON格式,例如:{"title":"test","content":"test"}', }, { showRequireMark: false } ), useFormSwitch( $t("t_3_1746667592270"), "config.ignore_ssl", { checkedValue: "1", uncheckedValue: "0" }, { showRequireMark: false } ), useFormCustom(() => { return (
用于部署时可用的模板变量:
__cert__ :证书内容
__key__ :私钥内容
用于申请时可用的模板变量:
__domain__ :完整域名,如:_acme-challenge.allinssl.com
__keyAuth__ :域名解析值
__action__ :执行的操作,需要自行根据参数值判断,为present时执行写入,cleanup清理记录
); }) ); break; case "spaceship": items.push( useFormInput("API Key", "config.api_key", { allowInput: noSideSpace, }), useFormInput("API Secret", "config.api_secret", { type: "password", showPasswordOn: "click", allowInput: noSideSpace, }) ); break; case "btdomain": items.push( useFormInput("Access Key", "config.access_key", { allowInput: noSideSpace, }), useFormInput("Secret Key", "config.secret_key", { type: "password", showPasswordOn: "click", allowInput: noSideSpace, }), useFormInput("Account ID", "config.account_id", { allowInput: noSideSpace, }) ); break; case "rainyun": items.push( useFormInput("API Key", "config.api_key", { type: "password", showPasswordOn: "click", allowInput: noSideSpace, }), ); break; case "edgeone": items.push( useFormInput("SecretId", "config.secret_id", { allowInput: noSideSpace, }), useFormInput("SecretKey", "config.secret_key", { type: "password", showPasswordOn: "click", allowInput: noSideSpace, }) ); break; case "plugin": // 插件名称选择器 items.push( useFormCustom(function() { return ( { param.value.config.name = value; pluginActionTips.value = renderPluginTips(option.config || {}); if (!param.value.config.config || typeof param.value.config.config === 'string') { param.value.config.config = {}; } }} v-slots={{ empty: function() { return {$t("t_0_1750210698345")}; } }} /> ); }) ); // 配置模式选择器 items.push( useFormRadioButton( "配置模式", "config.mode", [ { label: "默认", value: "default" }, { label: "自定义", value: "custom" } ], {}, { showRequireMark: false } ) ); // 获取插件配置 var pluginConfig = param.value.config as PluginAccessConfig; pluginConfig.mode = pluginConfig.mode || "default"; var selectedPlugin = pluginList.value.find(function(item) { return item.value === pluginConfig.name; }); // 根据配置模式初始化不同类型的config if (pluginConfig.mode === "default") { // 默认模式下确保config是对象类型 if (!pluginConfig.config || typeof pluginConfig.config !== 'object') { // 如果是字符串,尝试解析为JSON对象 if (typeof pluginConfig.config === 'string' && pluginConfig.config.trim()) { try { pluginConfig.config = JSON.parse(pluginConfig.config.trim()); } catch { pluginConfig.config = {}; } } else { pluginConfig.config = {}; } } } else { // 自定义模式下确保config是字符串类型 if (pluginConfig.config === undefined || pluginConfig.config === null) { pluginConfig.config = ''; } else if (typeof pluginConfig.config === 'object') { // 空对象时,根据插件配置生成默认的空值对象 if (Object.keys(pluginConfig.config).length === 0 && selectedPlugin?.config) { const defaultConfig: Record = {}; selectedPlugin.config.forEach(configItem => { defaultConfig[configItem.name] = ''; }); pluginConfig.config = JSON.stringify(defaultConfig); } else { try { // 不使用缩进,生成紧凑的JSON字符串 pluginConfig.config = JSON.stringify(pluginConfig.config); } catch { pluginConfig.config = ''; } } } } // 未选择插件时显示提示 if (!selectedPlugin) { items.push( useFormCustom(function() { return ( ); }) ); } else if (pluginConfig.mode === "default") { // 默认模式 - 动态表单 if (!selectedPlugin.config || selectedPlugin.config.length === 0) { items.push( useFormCustom(function() { return ( ); }) ); } else { selectedPlugin.config.forEach(function(configItem) { console.log('configItem', configItem) var fieldPath = 'config.config.' + configItem.name; // 为动态字段添加验证规则 if (!rules.config.config) rules.config.config = {}; rules.config.config[configItem.name] = { required: configItem.required, trigger: 'input', message: '请输入' + (configItem.description || configItem.name) }; items.push(generatePluginFormControl(configItem, fieldPath, noSideSpace)); }); } } else { // 自定义模式 - JSON文本框 items.push( useFormCustom(function() { const getConfigValue = () => { // 确保当config为undefined、null或空对象时返回空字符串 if (pluginConfig.config === undefined || pluginConfig.config === null) { return ''; } // 如果是对象且为空对象,也返回空字符串 if (typeof pluginConfig.config === 'object' && Object.keys(pluginConfig.config).length === 0) { return ''; } return typeof pluginConfig.config === "string" ? pluginConfig.config : JSON.stringify(pluginConfig.config, null, 2); }; return ( 自定义参数 ? ); } }} > {pluginActionTips.value} ); } }} > { pluginConfig.config = value; }} placeholder={pluginActionTips.value} rows={4} /> ); }), useFormHelp([{ content: '未来版本将取消自定义参数填写。', isHtml: true, }]) ); } break; default: break; } return items; }); // 切换类型时,重置表单 watch( () => param.value.type, (newVal) => { switch (newVal) { case "ssh": param.value.config = { host: "", port: 22, user: "root", mode: "password", password: "", } as SshAccessConfig; break; case "1panel": param.value.config = { url: "", api_key: "", ignore_ssl: "0", version: "v1", // 默认选择 v1 版本 } as PanelAccessConfig; break; case "btpanel": case "btwaf": param.value.config = { url: "", api_key: "", ignore_ssl: "0", }; break; case "aliyun": param.value.config = { access_key_id: "", access_key_secret: "", }; break; case "baidu": case "huaweicloud": param.value.config = { access_key: "", secret_key: "", }; break; case "cloudflare": param.value.config = { email: "", api_key: "", }; break; case "tencentcloud": param.value.config = { secret_id: "", secret_key: "", }; break; case "edgeone": param.value.config = { secret_id: "", secret_key: "", }; break; case "godaddy": param.value.config = { api_key: "", api_secret: "", }; break; case "qiniu": param.value.config = { access_key: "", access_secret: "", }; break; case "namecheap": param.value.config = { api_user: "", api_key: "", } as NamecheapAccessConfig; break; case "ns1": param.value.config = { api_key: "", } as NS1AccessConfig; break; case "cloudns": param.value.config = { auth_id: "", auth_password: "", } as CloudnsAccessConfig; break; case "aws": param.value.config = { access_key_id: "", secret_access_key: "", region: "", } as AwsAccessConfig; break; case "azure": param.value.config = { tenant_id: "", client_id: "", client_secret: "", environment: "", } as AzureAccessConfig; break; case "namesilo": param.value.config = { api_key: "", } as NamesiloAccessConfig; break; case "namedotcom": param.value.config = { username: "", api_token: "", } as NamedotcomAccessConfig; break; case "bunny": param.value.config = { api_key: "", } as BunnyAccessConfig; break; case "gcore": param.value.config = { api_token: "", } as GcoreAccessConfig; break; case "jdcloud": param.value.config = { access_key_id: "", secret_access_key: "", } as JdcloudAccessConfig; break; case "lecdn": param.value.config = { url: "", username: "", password: "", ignore_ssl: "0", } as LecdnAccessConfig; break; case "constellix": param.value.config = { api_key: "", secret_key: "", } as ConstellixAccessConfig; break; case "doge": param.value.config = { access_key: "", secret_key: "", } as DogeAccessConfig; break; case "webhook": param.value.config = { url: "", method: "POST", headers: "Content-Type: application/json", data: "", ignore_ssl: "0", } as WebhookAccessConfig; break; case "spaceship": param.value.config = { api_key: "", api_secret: "", } as SpaceshipAccessConfig; break; case "btdomain": param.value.config = { access_key: "", secret_key: "", account_id: "", } as BTDomainAccessConfig; break; case "plugin": param.value.config = { name: pluginList.value[0]?.value || "", mode: "default", config: {}, } as PluginAccessConfig; break; } } ); // 获取插件列表 const loadPlugins = async (): Promise => { try { const { data } = await getPlugins().fetch(); if (data && Array.isArray(data)) { // 处理插件数据,将每个插件的 actions 展开为选项 const pluginOptions: Array = []; data.forEach( (plugin: { name: string; actions: { name: string; description: string }[]; params?: string; config?: any; }) => { // 如果没有 actions,直接使用插件名称 pluginOptions.push({ label: plugin.name, value: plugin.name, description: plugin.actions .map((action: { description: string }) => action.description) .join("、"), pluginName: plugin.name, config: plugin.config, }); } ); pluginList.value = pluginOptions; pluginActionTips.value = renderPluginTips( pluginOptions[0]?.config || {} ); } } catch (error) { console.error($t("t_1_1750210699272"), error); } }; /** * 渲染单选标签 * @param {Record} option - 选项 * @returns {VNode} 渲染后的VNode */ const renderSingleSelectTag = ({ option }: Record): VNode => { return ( {option.label ? ( renderTypeLabel(option) ) : ( {$t("t_0_1745833934390")} )} ); }; /** * 类型选项接口 */ interface TypeOption { label: string; value: string; access: string[]; } /** * 渲染类型选择器标签 * @param {TypeOption} option - 类型选项 * @returns {VNode} 渲染后的VNode */ const renderTypeLabel = (option: TypeOption): VNode => { const accessTypeMap = { dns: $t("t_3_1745735765112"), host: $t("t_0_1746754500246"), plugin: "插件", }; return ( {option.label} {option.access && option.access.length > 0 && ( {option.access.map((type: string) => ( {accessTypeMap[type as keyof typeof accessTypeMap] || type} ))} )} ); }; /** * 渲染插件标签 * @param {Record} option - 选项 * @returns {VNode} 渲染后的VNode */ const renderPluginLabel = (option: PluginOption): VNode => { return (
{option.label}
{option.description && (
{option.description}
)}
); }; /** * 渲染插件选中标签 * @param props - 属性对象 * @returns {VNode} 渲染后的VNode */ const renderPluginTag = (props: { option: PluginOption }): VNode => { const { option } = props; return ( {option?.label ? ( {option.label} ) : ( {$t("t_2_1750210698518")} )} ); }; /** * 提交授权API表单 * @param {UpdateAccessParams | AddAccessParams} param 请求参数 * @param {Ref} formRef 表单实例 */ const submitApiManageForm = async ( param: UpdateAccessParams | AddAccessParams ): Promise => { try { let data: UpdateAccessParams | AddAccessParams; if (param.type === "plugin") { let finalConfig; if (typeof param.config.config === 'string') { finalConfig = { name: param.config.name, config: JSON.parse(param.config.config) }; } else { finalConfig = { name: param.config.name, config: param.config.config }; } data = { ...param, config: JSON.stringify(finalConfig), } as UpdateAccessParams; } else { data = { ...param, config: JSON.stringify(param.config), } as UpdateAccessParams; } if ("id" in data) { const { id, name, config } = data; await updateExistingAccess({ id: id.toString(), name, config, } as UpdateAccessParams); } else { await addNewAccess(data as AddAccessParams); } } catch (error) { throw handleError(new Error($t("t_4_1746667590873"))); } }; /** * @description 渲染插件tips * @param {PluginOption} option - 插件选项 * @returns {VNode} 渲染后的VNode */ const renderPluginTips = (tips: Record): string => { return $t("t_3_1750210706775") + JSON.stringify(tips || {}); }; // 使用表单hooks const { component: ApiManageForm, fetch, validate } = useForm({ config, defaultValue: param, request: submitApiManageForm, rules: rules as any, }); // 关联确认按钮 confirm(async (close) => { try { // 先进行表单校验 await validate(); openLoad(); await fetch(); resetApiForm(); close(); } catch (error) { return handleError(error); } finally { closeLoad(); } }); // 组件初始化时获取插件列表 onMounted(loadPlugins); return { ApiManageForm, }; };