mirror of
https://gitee.com/mirrors/AllinSSL.git
synced 2026-03-08 07:41:10 +08:00
【修复】表单初始化,申请节点随机时间
This commit is contained in:
@@ -33,6 +33,40 @@ export const useController = (props: FlowNodeProps = { type: 'quick', node: flow
|
||||
// 使用store获取所有需要的方法和状态
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const { startNodeSavedByUser } = useStore();
|
||||
|
||||
/**
|
||||
* 生成开始节点的随机时间
|
||||
* @param {any} childNode - 子节点配置
|
||||
* @returns {any} 处理后的子节点配置
|
||||
*/
|
||||
const generateStartNodeRandomTime = (childNode: any) => {
|
||||
// 检查是否是开始节点
|
||||
if (childNode.type === "start") {
|
||||
if (startNodeSavedByUser.value) {
|
||||
return childNode;
|
||||
}
|
||||
const config = childNode.config;
|
||||
const hasUserSetTime =
|
||||
config.hour !== undefined &&
|
||||
config.minute !== undefined &&
|
||||
!(config.hour === 1 && config.minute === 0);
|
||||
if (!hasUserSetTime) {
|
||||
const randomHour = Math.floor(Math.random() * 4) + 1;
|
||||
const randomMinute = Math.floor(Math.random() * 12) * 5;
|
||||
|
||||
return {
|
||||
...childNode,
|
||||
config: {
|
||||
...config,
|
||||
hour: randomHour,
|
||||
minute: randomMinute,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
return childNode;
|
||||
};
|
||||
|
||||
/**
|
||||
* 保存节点配置
|
||||
@@ -45,14 +79,16 @@ export const useController = (props: FlowNodeProps = { type: 'quick', node: flow
|
||||
if (res.valid && flowData.value.name) {
|
||||
const { active } = workflowData.value
|
||||
const { id, name, childNode } = flowData.value
|
||||
const { exec_type, ...exec_time } = childNode.config as unknown as StartNodeConfig
|
||||
const param = {
|
||||
name,
|
||||
active,
|
||||
content: JSON.stringify(childNode),
|
||||
exec_type,
|
||||
exec_time: JSON.stringify(exec_time || {}),
|
||||
}
|
||||
const processedChildNode = generateStartNodeRandomTime(childNode);
|
||||
const { exec_type, ...exec_time } =
|
||||
processedChildNode.config as unknown as StartNodeConfig;
|
||||
const param = {
|
||||
name,
|
||||
active,
|
||||
content: JSON.stringify(processedChildNode),
|
||||
exec_type,
|
||||
exec_time: JSON.stringify(exec_time || {}),
|
||||
};
|
||||
if (route.query.isEdit) {
|
||||
updateWorkflowData({ id, ...param })
|
||||
} else {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -478,6 +478,7 @@ export const useApiFormController = (
|
||||
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])
|
||||
|
||||
@@ -1,55 +1,62 @@
|
||||
import { defineComponent, onBeforeMount, onMounted, ref } from 'vue'
|
||||
import type { Component } from 'vue'
|
||||
|
||||
import FlowChart from '@components/FlowChart'
|
||||
import { useStore } from '@autoDeploy/children/workflowView/useStore'
|
||||
import { useController } from './useController'
|
||||
import FlowChart from "@components/FlowChart";
|
||||
import { useStore } from "@autoDeploy/children/workflowView/useStore";
|
||||
import { useController } from "./useController";
|
||||
import { useStore as useFlowStore } from "@components/FlowChart/useStore";
|
||||
/**
|
||||
* @description 工作流视图主组件,负责加载和渲染流程图。
|
||||
*/
|
||||
export default defineComponent({
|
||||
name: 'WorkflowView',
|
||||
setup() {
|
||||
const { init } = useController()
|
||||
const { workflowType, workDefalutNodeData, isEdit } = useStore()
|
||||
name: "WorkflowView",
|
||||
setup() {
|
||||
const { init } = useController();
|
||||
const { workflowType, workDefalutNodeData, isEdit } = useStore();
|
||||
const { resetStartNodeSavedState } = useFlowStore();
|
||||
|
||||
// 使用import.meta.glob一次性加载所有节点组件
|
||||
const modules = import.meta.glob('./node/*/index.tsx', { eager: true })
|
||||
// 使用import.meta.glob一次性加载所有节点组件
|
||||
const modules = import.meta.glob("./node/*/index.tsx", { eager: true });
|
||||
|
||||
// 创建节点组件映射
|
||||
const taskComponents = ref<Record<string, Component>>({})
|
||||
// 创建节点组件映射
|
||||
const taskComponents = ref<Record<string, Component>>({});
|
||||
|
||||
// 初始化任务组件映射
|
||||
const initTaskComponents = () => {
|
||||
const componentsMap: Record<string, Component> = {}
|
||||
// 获取文件夹名称(对应节点类型)并映射到组件
|
||||
Object.entries(modules).forEach(([path, module]) => {
|
||||
// 获取路径中的节点类型
|
||||
const match = path.match(/\/node\/([^/]+)\/index\.tsx$/)
|
||||
if (match && match[1]) {
|
||||
const nodeType = match[1]
|
||||
const componentKey = `${nodeType}Node`
|
||||
// @ts-ignore
|
||||
componentsMap[componentKey] = module.default || module
|
||||
}
|
||||
})
|
||||
taskComponents.value = componentsMap
|
||||
console.log('已加载节点组件:', Object.keys(componentsMap))
|
||||
}
|
||||
// 初始化任务组件映射
|
||||
const initTaskComponents = () => {
|
||||
const componentsMap: Record<string, Component> = {};
|
||||
// 获取文件夹名称(对应节点类型)并映射到组件
|
||||
Object.entries(modules).forEach(([path, module]) => {
|
||||
// 获取路径中的节点类型
|
||||
const match = path.match(/\/node\/([^/]+)\/index\.tsx$/);
|
||||
if (match && match[1]) {
|
||||
const nodeType = match[1];
|
||||
const componentKey = `${nodeType}Node`;
|
||||
// @ts-ignore
|
||||
componentsMap[componentKey] = module.default || module;
|
||||
}
|
||||
});
|
||||
taskComponents.value = componentsMap;
|
||||
console.log("已加载节点组件:", Object.keys(componentsMap));
|
||||
};
|
||||
|
||||
// 初始化组件
|
||||
onBeforeMount(initTaskComponents)
|
||||
// 初始化组件
|
||||
onBeforeMount(initTaskComponents);
|
||||
|
||||
// 初始化数据
|
||||
onMounted(init)
|
||||
// 初始化数据
|
||||
onMounted(init);
|
||||
|
||||
return () => (
|
||||
<FlowChart
|
||||
type={workflowType.value}
|
||||
node={workDefalutNodeData.value}
|
||||
isEdit={isEdit.value}
|
||||
taskComponents={taskComponents.value}
|
||||
/>
|
||||
)
|
||||
},
|
||||
})
|
||||
// 组件销毁时重置开始节点保存状态
|
||||
onUnmounted(() => {
|
||||
resetStartNodeSavedState();
|
||||
});
|
||||
|
||||
return () => (
|
||||
<FlowChart
|
||||
type={workflowType.value}
|
||||
node={workDefalutNodeData.value}
|
||||
isEdit={isEdit.value}
|
||||
taskComponents={taskComponents.value}
|
||||
/>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -53,467 +53,548 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const { updateNodeConfig, advancedOptions, isRefreshNode } = useStore()
|
||||
// 获取路由信息
|
||||
const route = useRoute()
|
||||
// 弹窗辅助
|
||||
const { confirm } = useModalHooks()
|
||||
// 获取表单助手函数
|
||||
const { useFormInput, useFormSelect, useFormMore, useFormHelp, useFormSwitch } = useFormHooks()
|
||||
// 表单参数
|
||||
const param = ref(deepClone(props.node.config))
|
||||
const { updateNodeConfig, advancedOptions, isRefreshNode } = useStore();
|
||||
// 获取路由信息
|
||||
const route = useRoute();
|
||||
// 弹窗辅助
|
||||
const { confirm } = useModalHooks();
|
||||
// 获取表单助手函数
|
||||
const {
|
||||
useFormInput,
|
||||
useFormSelect,
|
||||
useFormMore,
|
||||
useFormHelp,
|
||||
useFormSwitch,
|
||||
} = useFormHooks();
|
||||
// 表单参数
|
||||
const param = ref(deepClone(props.node.config));
|
||||
|
||||
// 获取路由参数
|
||||
const isEdit = computed(() => route.query.isEdit === 'true')
|
||||
const routeEmail = computed(() => (route.query.email as string) || '')
|
||||
// 获取路由参数
|
||||
const isEdit = computed(() => route.query.isEdit === "true");
|
||||
const routeEmail = computed(() => (route.query.email as string) || "");
|
||||
|
||||
// CA选项状态
|
||||
const caOptions = ref<Array<{ label: string; value: string; icon: string }>>([])
|
||||
const emailOptions = ref<string[]>([])
|
||||
const isLoadingCA = ref(false)
|
||||
const isLoadingEmails = ref(false)
|
||||
const showEmailDropdown = ref(false)
|
||||
const emailInputRef = ref<InstanceType<typeof NInput> | null>(null)
|
||||
// CA选项状态
|
||||
const caOptions = ref<
|
||||
Array<{ label: string; value: string; icon: string }>
|
||||
>([]);
|
||||
const emailOptions = ref<string[]>([]);
|
||||
const isLoadingCA = ref(false);
|
||||
const isLoadingEmails = ref(false);
|
||||
const showEmailDropdown = ref(false);
|
||||
const emailInputRef = ref<InstanceType<typeof NInput> | null>(null);
|
||||
|
||||
// 加载CA选项
|
||||
const loadCAOptions = async () => {
|
||||
isLoadingCA.value = true
|
||||
try {
|
||||
const { data } = await getEabList({ ca: '', p: 1, limit: 1000 }).fetch()
|
||||
const uniqueCATypes = new Set<string>()
|
||||
const caList: Array<{ label: string; value: string; icon: string }> = []
|
||||
// 加载CA选项
|
||||
const loadCAOptions = async () => {
|
||||
isLoadingCA.value = true;
|
||||
try {
|
||||
const { data } = await getEabList({
|
||||
ca: param.value.ca,
|
||||
p: 1,
|
||||
limit: 1000,
|
||||
}).fetch();
|
||||
const uniqueCATypes = new Set<string>();
|
||||
const caList: Array<{ label: string; value: string; icon: string }> =
|
||||
[];
|
||||
|
||||
// 优先添加重要的CA类型(确保始终显示)
|
||||
const priorityCATypes = ['letsencrypt', 'buypass', 'zerossl']
|
||||
priorityCATypes.forEach((caType) => {
|
||||
if (!uniqueCATypes.has(caType)) {
|
||||
uniqueCATypes.add(caType)
|
||||
const predefinedCA = Object.values(CACertificateAuthorization).find((ca) => ca.type === caType)
|
||||
caList.push({
|
||||
label: predefinedCA ? predefinedCA.name : caType.toUpperCase(),
|
||||
value: caType,
|
||||
icon: `cert-${caType}`,
|
||||
})
|
||||
}
|
||||
})
|
||||
// 优先添加重要的CA类型(确保始终显示)
|
||||
const priorityCATypes = ["letsencrypt", "buypass", "zerossl"];
|
||||
priorityCATypes.forEach((caType) => {
|
||||
if (!uniqueCATypes.has(caType)) {
|
||||
uniqueCATypes.add(caType);
|
||||
const predefinedCA = Object.values(CACertificateAuthorization).find(
|
||||
(ca) => ca.type === caType
|
||||
);
|
||||
caList.push({
|
||||
label: predefinedCA ? predefinedCA.name : caType.toUpperCase(),
|
||||
value: caType,
|
||||
icon: `cert-${caType}`,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 添加API返回的其他CA类型(去重)
|
||||
data?.forEach((item) => {
|
||||
if (item.ca && !uniqueCATypes.has(item.ca)) {
|
||||
uniqueCATypes.add(item.ca)
|
||||
// 添加API返回的其他CA类型(去重)
|
||||
data?.forEach((item) => {
|
||||
if (item.ca && !uniqueCATypes.has(item.ca)) {
|
||||
uniqueCATypes.add(item.ca);
|
||||
|
||||
// 查找预定义配置中对应的CA信息
|
||||
const predefinedCA = Object.values(CACertificateAuthorization).find((ca) => ca.type === item.ca)
|
||||
caList.push({
|
||||
label: predefinedCA ? predefinedCA.name : item.ca.toUpperCase(),
|
||||
value: item.ca,
|
||||
icon: predefinedCA ? `cert-${item.ca}` : 'cert-custom', // 如果不在预定义配置中,使用custom图标;否则使用对应的cert图标
|
||||
})
|
||||
}
|
||||
})
|
||||
// 查找预定义配置中对应的CA信息
|
||||
const predefinedCA = Object.values(CACertificateAuthorization).find(
|
||||
(ca) => ca.type === item.ca
|
||||
);
|
||||
caList.push({
|
||||
label: predefinedCA ? predefinedCA.name : item.ca.toUpperCase(),
|
||||
value: item.ca,
|
||||
icon: predefinedCA ? `cert-${item.ca}` : "cert-custom", // 如果不在预定义配置中,使用custom图标;否则使用对应的cert图标
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
caOptions.value = caList
|
||||
} catch (error) {
|
||||
console.error('加载CA选项失败:', error)
|
||||
} finally {
|
||||
isLoadingCA.value = false
|
||||
}
|
||||
}
|
||||
caOptions.value = caList;
|
||||
} catch (error) {
|
||||
console.error("加载CA选项失败:", error);
|
||||
} finally {
|
||||
isLoadingCA.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 加载邮件选项
|
||||
const loadEmailOptions = async (ca: string) => {
|
||||
if (!ca) return
|
||||
isLoadingEmails.value = true
|
||||
try {
|
||||
const { data } = await getEabList({ ca, p: 1, limit: 1000 }).fetch()
|
||||
emailOptions.value = data?.map((item) => item.email).filter(Boolean) || []
|
||||
// 加载邮件选项
|
||||
const loadEmailOptions = async (ca: string) => {
|
||||
if (!ca) return;
|
||||
isLoadingEmails.value = true;
|
||||
try {
|
||||
const { data } = await getEabList({ ca, p: 1, limit: 1000 }).fetch();
|
||||
emailOptions.value =
|
||||
data?.map((item) => item.email).filter(Boolean) || [];
|
||||
|
||||
// 检查是否为编辑模式且有外部传入的邮箱
|
||||
if (isEdit.value && routeEmail.value) {
|
||||
// 编辑模式:使用外部传入的邮箱地址
|
||||
param.value.email = routeEmail.value
|
||||
} else {
|
||||
// 非编辑模式:保持原有逻辑
|
||||
if (!emailOptions.value.length) {
|
||||
param.value.email = ''
|
||||
}
|
||||
// 如果邮箱数组有内容且当前邮箱为空,自动填充第一个邮箱地址
|
||||
if (emailOptions.value.length > 0 && emailOptions.value[0] && !param.value.email) {
|
||||
param.value.email = emailOptions.value[0]
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载邮件选项失败:', error)
|
||||
} finally {
|
||||
isLoadingEmails.value = false
|
||||
}
|
||||
}
|
||||
// 检查是否为编辑模式且有外部传入的邮箱
|
||||
if (isEdit.value && routeEmail.value) {
|
||||
// 编辑模式:使用外部传入的邮箱地址
|
||||
param.value.email = routeEmail.value;
|
||||
} else {
|
||||
// 非编辑模式:保持原有逻辑
|
||||
if (!emailOptions.value.length) {
|
||||
param.value.email = "";
|
||||
}
|
||||
// 如果邮箱数组有内容且当前邮箱为空,自动填充第一个邮箱地址
|
||||
if (
|
||||
emailOptions.value.length > 0 &&
|
||||
emailOptions.value[0] &&
|
||||
!param.value.email
|
||||
) {
|
||||
param.value.email = emailOptions.value[0];
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("加载邮件选项失败:", error);
|
||||
} finally {
|
||||
isLoadingEmails.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 处理CA选择变化
|
||||
const handleCAChange = (value: string) => {
|
||||
param.value.ca = value
|
||||
// 移除直接调用 loadEmailOptions,让 watch 监听器统一处理
|
||||
// 这样避免了用户切换CA时的重复 API 请求
|
||||
}
|
||||
// 处理CA选择变化
|
||||
const handleCAChange = (value: string) => {
|
||||
param.value.ca = value;
|
||||
// 移除直接调用 loadEmailOptions,让 watch 监听器统一处理
|
||||
// 这样避免了用户切换CA时的重复 API 请求
|
||||
};
|
||||
|
||||
// 跳转到CA管理页面
|
||||
const goToAddCAProvider = () => {
|
||||
window.open('/auto-deploy?type=caManage', '_blank')
|
||||
}
|
||||
// 跳转到CA管理页面
|
||||
const goToAddCAProvider = () => {
|
||||
window.open("/auto-deploy?type=caManage", "_blank");
|
||||
};
|
||||
|
||||
// 渲染CA选择器标签
|
||||
const renderLabel = (option: { label: string; value: string; icon: string }) => {
|
||||
return (
|
||||
<NFlex align="center">
|
||||
<SvgIcon icon={option.icon} size="2rem" />
|
||||
<NText>{option.label}</NText>
|
||||
</NFlex>
|
||||
)
|
||||
}
|
||||
// 渲染CA选择器标签
|
||||
const renderLabel = (option: {
|
||||
label: string;
|
||||
value: string;
|
||||
icon: string;
|
||||
}) => {
|
||||
return (
|
||||
<NFlex align="center">
|
||||
<SvgIcon icon={option.icon} size="2rem" />
|
||||
<NText>{option.label}</NText>
|
||||
</NFlex>
|
||||
);
|
||||
};
|
||||
|
||||
// 渲染CA选择器单选标签
|
||||
const renderSingleSelectTag = ({ option }: any) => {
|
||||
return (
|
||||
<NFlex align="center">
|
||||
{option.label ? renderLabel(option) : <NText class="text-[#aaa]">{$t('t_0_1747990228780')}</NText>}
|
||||
</NFlex>
|
||||
)
|
||||
}
|
||||
// 渲染CA选择器单选标签
|
||||
const renderSingleSelectTag = ({ option }: any) => {
|
||||
return (
|
||||
<NFlex align="center">
|
||||
{option.label ? (
|
||||
renderLabel(option)
|
||||
) : (
|
||||
<NText class="text-[#aaa]">{$t("t_0_1747990228780")}</NText>
|
||||
)}
|
||||
</NFlex>
|
||||
);
|
||||
};
|
||||
|
||||
// 过滤函数
|
||||
const handleFilter = (pattern: string, option: any): boolean => {
|
||||
return option.label.toLowerCase().includes(pattern.toLowerCase())
|
||||
}
|
||||
// 过滤函数
|
||||
const handleFilter = (pattern: string, option: any): boolean => {
|
||||
return option.label.toLowerCase().includes(pattern.toLowerCase());
|
||||
};
|
||||
|
||||
// 处理邮箱输入框焦点
|
||||
const handleEmailFocus = () => {
|
||||
if (emailOptions.value.length > 0) {
|
||||
showEmailDropdown.value = true
|
||||
}
|
||||
}
|
||||
// 处理邮箱输入框焦点
|
||||
const handleEmailFocus = () => {
|
||||
if (emailOptions.value.length > 0) {
|
||||
showEmailDropdown.value = true;
|
||||
}
|
||||
};
|
||||
|
||||
// 处理邮箱输入框失焦
|
||||
const handleEmailBlur = () => {
|
||||
// 延迟关闭下拉,确保点击选项有时间触发
|
||||
setTimeout(() => {
|
||||
showEmailDropdown.value = false
|
||||
}, 200)
|
||||
}
|
||||
// 处理邮箱输入框失焦
|
||||
const handleEmailBlur = () => {
|
||||
// 延迟关闭下拉,确保点击选项有时间触发
|
||||
setTimeout(() => {
|
||||
showEmailDropdown.value = false;
|
||||
}, 200);
|
||||
};
|
||||
|
||||
// 选择邮箱地址
|
||||
const handleSelectEmail = (email: string) => {
|
||||
param.value.email = email
|
||||
showEmailDropdown.value = false
|
||||
emailInputRef.value?.blur()
|
||||
}
|
||||
// 选择邮箱地址
|
||||
const handleSelectEmail = (email: string) => {
|
||||
param.value.email = email;
|
||||
showEmailDropdown.value = false;
|
||||
emailInputRef.value?.blur();
|
||||
};
|
||||
|
||||
// 创建邮箱下拉选项
|
||||
const emailDropdownOptions = computed(() => {
|
||||
return emailOptions.value.map((email) => ({
|
||||
label: email,
|
||||
key: email,
|
||||
}))
|
||||
})
|
||||
// 创建邮箱下拉选项
|
||||
const emailDropdownOptions = computed(() => {
|
||||
return emailOptions.value.map((email) => ({
|
||||
label: email,
|
||||
key: email,
|
||||
}));
|
||||
});
|
||||
|
||||
// 判断是否需要输入框(letsencrypt、buypass、zerossl)
|
||||
const shouldUseInputForEmail = computed(() => {
|
||||
return ['letsencrypt', 'buypass', 'zerossl'].includes(param.value.ca)
|
||||
})
|
||||
// 判断是否需要输入框(letsencrypt、buypass、zerossl)
|
||||
const shouldUseInputForEmail = computed(() => {
|
||||
return ["letsencrypt", "buypass", "zerossl"].includes(param.value.ca);
|
||||
});
|
||||
|
||||
// 表单渲染配置
|
||||
const config = computed(() => {
|
||||
// 基本选项
|
||||
return [
|
||||
useFormInput($t('t_17_1745227838561'), 'domains', {
|
||||
placeholder: $t('t_0_1745735774005'),
|
||||
allowInput: noSideSpace,
|
||||
onInput: (val: string) => {
|
||||
param.value.domains = val.replace(/,/g, ',').replace(/;/g, ',') // 中文逗号分隔
|
||||
},
|
||||
}),
|
||||
{
|
||||
type: 'custom' as const,
|
||||
render: () => {
|
||||
return (
|
||||
<DnsProviderSelect
|
||||
type="dns"
|
||||
path="provider_id"
|
||||
value={param.value.provider_id}
|
||||
valueType="value"
|
||||
isAddMode={true}
|
||||
{...{
|
||||
'onUpdate:value': (val: { value: string; type: string }) => {
|
||||
param.value.provider_id = val.value
|
||||
param.value.provider = val.type
|
||||
},
|
||||
}}
|
||||
/>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'custom' as const,
|
||||
render: () => {
|
||||
return (
|
||||
<NSpin show={isLoadingCA.value}>
|
||||
<NGrid cols={24}>
|
||||
<NFormItemGi span={13} label={$t('t_3_1750399513606')} path="ca" showRequireMark={true}>
|
||||
<NSelect
|
||||
value={param.value.ca}
|
||||
options={caOptions.value}
|
||||
renderLabel={renderLabel}
|
||||
renderTag={renderSingleSelectTag}
|
||||
filterable
|
||||
filter={handleFilter}
|
||||
loading={isLoadingCA.value}
|
||||
placeholder={$t('t_0_1747990228780')}
|
||||
onUpdateValue={handleCAChange}
|
||||
class="flex-1 w-full"
|
||||
v-slots={{
|
||||
empty: () => {
|
||||
return <span class="text-[1.4rem]">{$t('t_2_1747990228008')}</span>
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span={11}>
|
||||
<NButton class="mx-[8px]" onClick={goToAddCAProvider}>
|
||||
{$t('t_4_1747903685371')}
|
||||
</NButton>
|
||||
<NButton onClick={loadCAOptions} loading={isLoadingCA.value}>
|
||||
{$t('t_0_1746497662220')}
|
||||
</NButton>
|
||||
</NFormItemGi>
|
||||
</NGrid>
|
||||
</NSpin>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'custom' as const,
|
||||
render: () => {
|
||||
return (
|
||||
<NFormItem label={$t('t_68_1745289354676')} path="email">
|
||||
{shouldUseInputForEmail.value ? (
|
||||
<NDropdown
|
||||
trigger="manual"
|
||||
show={showEmailDropdown.value}
|
||||
options={emailDropdownOptions.value}
|
||||
onSelect={handleSelectEmail}
|
||||
placement="bottom-start"
|
||||
style="width: 100%"
|
||||
>
|
||||
<NInput
|
||||
ref={emailInputRef}
|
||||
v-model:value={param.value.email}
|
||||
placeholder={$t('t_2_1748052862259')}
|
||||
clearable
|
||||
loading={isLoadingEmails.value}
|
||||
onFocus={handleEmailFocus}
|
||||
onBlur={handleEmailBlur}
|
||||
class="w-full"
|
||||
/>
|
||||
</NDropdown>
|
||||
) : (
|
||||
<NSelect
|
||||
v-model:value={param.value.email}
|
||||
options={emailOptions.value.map((email) => ({ label: email, value: email }))}
|
||||
placeholder={$t('t_2_1748052862259')}
|
||||
clearable
|
||||
filterable
|
||||
loading={isLoadingEmails.value}
|
||||
class="w-full"
|
||||
/>
|
||||
)}
|
||||
</NFormItem>
|
||||
)
|
||||
},
|
||||
},
|
||||
// 表单渲染配置
|
||||
const config = computed(() => {
|
||||
// 基本选项
|
||||
return [
|
||||
useFormInput($t("t_17_1745227838561"), "domains", {
|
||||
placeholder: $t("t_0_1745735774005"),
|
||||
allowInput: noSideSpace,
|
||||
onInput: (val: string) => {
|
||||
param.value.domains = val.replace(/,/g, ",").replace(/;/g, ","); // 中文逗号分隔
|
||||
},
|
||||
}),
|
||||
{
|
||||
type: "custom" as const,
|
||||
render: () => {
|
||||
return (
|
||||
<DnsProviderSelect
|
||||
type="dns"
|
||||
path="provider_id"
|
||||
value={param.value.provider_id}
|
||||
valueType="value"
|
||||
isAddMode={true}
|
||||
{...{
|
||||
"onUpdate:value": (val: { value: string; type: string }) => {
|
||||
param.value.provider_id = val.value;
|
||||
param.value.provider = val.type;
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "custom" as const,
|
||||
render: () => {
|
||||
return (
|
||||
<NSpin show={isLoadingCA.value}>
|
||||
<NGrid cols={24}>
|
||||
<NFormItemGi
|
||||
span={13}
|
||||
label={$t("t_3_1750399513606")}
|
||||
path="ca"
|
||||
showRequireMark={true}
|
||||
>
|
||||
<NSelect
|
||||
value={param.value.ca}
|
||||
options={caOptions.value}
|
||||
renderLabel={renderLabel}
|
||||
renderTag={renderSingleSelectTag}
|
||||
filterable
|
||||
filter={handleFilter}
|
||||
loading={isLoadingCA.value}
|
||||
placeholder={$t("t_0_1747990228780")}
|
||||
onUpdateValue={handleCAChange}
|
||||
class="flex-1 w-full"
|
||||
v-slots={{
|
||||
empty: () => {
|
||||
return (
|
||||
<span class="text-[1.4rem]">
|
||||
{$t("t_2_1747990228008")}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span={11}>
|
||||
<NButton class="mx-[8px]" onClick={goToAddCAProvider}>
|
||||
{$t("t_4_1747903685371")}
|
||||
</NButton>
|
||||
<NButton
|
||||
onClick={loadCAOptions}
|
||||
loading={isLoadingCA.value}
|
||||
>
|
||||
{$t("t_0_1746497662220")}
|
||||
</NButton>
|
||||
</NFormItemGi>
|
||||
</NGrid>
|
||||
</NSpin>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "custom" as const,
|
||||
render: () => {
|
||||
return (
|
||||
<NFormItem label={$t("t_68_1745289354676")} path="email">
|
||||
{shouldUseInputForEmail.value ? (
|
||||
<NDropdown
|
||||
trigger="manual"
|
||||
show={showEmailDropdown.value}
|
||||
options={emailDropdownOptions.value}
|
||||
onSelect={handleSelectEmail}
|
||||
placement="bottom-start"
|
||||
style="width: 100%"
|
||||
>
|
||||
<NInput
|
||||
ref={emailInputRef}
|
||||
v-model:value={param.value.email}
|
||||
placeholder={$t("t_2_1748052862259")}
|
||||
clearable
|
||||
loading={isLoadingEmails.value}
|
||||
onFocus={handleEmailFocus}
|
||||
onBlur={handleEmailBlur}
|
||||
class="w-full"
|
||||
/>
|
||||
</NDropdown>
|
||||
) : (
|
||||
<NSelect
|
||||
v-model:value={param.value.email}
|
||||
options={emailOptions.value.map((email) => ({
|
||||
label: email,
|
||||
value: email,
|
||||
}))}
|
||||
placeholder={$t("t_2_1748052862259")}
|
||||
clearable
|
||||
filterable
|
||||
loading={isLoadingEmails.value}
|
||||
class="w-full"
|
||||
/>
|
||||
)}
|
||||
</NFormItem>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
type: 'custom' as const,
|
||||
render: () => {
|
||||
return (
|
||||
<NFormItem label={$t('t_4_1747990227956')} path="end_day">
|
||||
<div class="flex items-center">
|
||||
<span class="text-[1.4rem] mr-[1.2rem]">{$t('t_5_1747990228592')}</span>
|
||||
<NInputNumber v-model:value={param.value.end_day} showButton={false} min={1} class="w-[120px]" />
|
||||
<span class="text-[1.4rem] ml-[1.2rem]">{$t('t_6_1747990228465')}</span>
|
||||
</div>
|
||||
</NFormItem>
|
||||
)
|
||||
},
|
||||
},
|
||||
useFormMore(advancedOptions),
|
||||
...(advancedOptions.value
|
||||
? [
|
||||
useFormSelect(
|
||||
$t('t_0_1747647014927'),
|
||||
'algorithm',
|
||||
[
|
||||
{ label: 'RSA2048', value: 'RSA2048' },
|
||||
{ label: 'RSA3072', value: 'RSA3072' },
|
||||
{ label: 'RSA4096', value: 'RSA4096' },
|
||||
{ label: 'RSA8192', value: 'RSA8192' },
|
||||
{ label: 'EC256', value: 'EC256' },
|
||||
{ label: 'EC384', value: 'EC384' },
|
||||
],
|
||||
{},
|
||||
{ showRequireMark: false },
|
||||
),
|
||||
useFormInput(
|
||||
$t('t_7_1747990227761'),
|
||||
'proxy',
|
||||
{
|
||||
placeholder: $t('t_8_1747990235316'),
|
||||
allowInput: noSideSpace,
|
||||
},
|
||||
{ showRequireMark: false },
|
||||
),
|
||||
useFormSwitch(
|
||||
$t('t_2_1749204567193'),
|
||||
'close_cname',
|
||||
{
|
||||
checkedValue: 1,
|
||||
uncheckedValue: 0,
|
||||
},
|
||||
{ showRequireMark: false },
|
||||
),
|
||||
useFormSwitch(
|
||||
$t('t_2_1747106957037'),
|
||||
'skip_check',
|
||||
{
|
||||
checkedValue: 1,
|
||||
uncheckedValue: 0,
|
||||
},
|
||||
{ showRequireMark: false },
|
||||
),
|
||||
// 只有在跳过预检查关闭时才显示DNS递归服务器、预检查超时时间和忽略预检查结果
|
||||
...(param.value.skip_check === 0
|
||||
? [
|
||||
useFormInput(
|
||||
$t('t_0_1747106957037'),
|
||||
'name_server',
|
||||
{
|
||||
placeholder: $t('t_1_1747106961747'),
|
||||
allowInput: noSideSpace,
|
||||
onInput: (val: string) => {
|
||||
param.value.name_server = val.replace(/,/g, ',').replace(/;/g, ',') // 中文逗号分隔
|
||||
},
|
||||
},
|
||||
{ showRequireMark: false },
|
||||
),
|
||||
{
|
||||
type: 'custom' as const,
|
||||
render: () => {
|
||||
return (
|
||||
<NFormItem label={$t('t_0_1749263105073')} path="max_wait">
|
||||
<NInputNumber
|
||||
v-model:value={(param.value as ApplyNodeConfig & { max_wait?: number }).max_wait}
|
||||
showButton={false}
|
||||
min={1}
|
||||
class="w-full"
|
||||
placeholder={$t('t_1_1749263104936')}
|
||||
/>
|
||||
</NFormItem>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'custom' as const,
|
||||
render: () => {
|
||||
return (
|
||||
<NFormItem label={$t('t_2_1749263103765')} path="ignore_check">
|
||||
<div class="flex items-center">
|
||||
<span class="text-[1.4rem] mr-[1.2rem]">{$t('t_3_1749263104237')}</span>
|
||||
<NSwitch
|
||||
v-model:value={param.value.ignore_check}
|
||||
checkedValue={1}
|
||||
uncheckedValue={0}
|
||||
class="mx-[.5rem]"
|
||||
v-slots={{
|
||||
checked: () => $t('t_4_1749263101853'),
|
||||
unchecked: () => $t('t_5_1749263101934'),
|
||||
}}
|
||||
/>
|
||||
<span class="text-[1.4rem] ml-[1.2rem]">{$t('t_6_1749263103891')}</span>
|
||||
</div>
|
||||
</NFormItem>
|
||||
)
|
||||
},
|
||||
},
|
||||
]
|
||||
: []),
|
||||
]
|
||||
: []),
|
||||
useFormHelp([
|
||||
{
|
||||
content: $t('t_0_1747040228657'),
|
||||
},
|
||||
{
|
||||
content: $t('t_1_1747040226143'),
|
||||
},
|
||||
]),
|
||||
]
|
||||
})
|
||||
{
|
||||
type: "custom" as const,
|
||||
render: () => {
|
||||
return (
|
||||
<NFormItem label={$t("t_4_1747990227956")} path="end_day">
|
||||
<div class="flex items-center">
|
||||
<span class="text-[1.4rem] mr-[1.2rem]">
|
||||
{$t("t_5_1747990228592")}
|
||||
</span>
|
||||
<NInputNumber
|
||||
v-model:value={param.value.end_day}
|
||||
showButton={false}
|
||||
min={1}
|
||||
class="w-[120px]"
|
||||
/>
|
||||
<span class="text-[1.4rem] ml-[1.2rem]">
|
||||
{$t("t_6_1747990228465")}
|
||||
</span>
|
||||
</div>
|
||||
</NFormItem>
|
||||
);
|
||||
},
|
||||
},
|
||||
useFormMore(advancedOptions),
|
||||
...(advancedOptions.value
|
||||
? [
|
||||
useFormSelect(
|
||||
$t("t_0_1747647014927"),
|
||||
"algorithm",
|
||||
[
|
||||
{ label: "RSA2048", value: "RSA2048" },
|
||||
{ label: "RSA3072", value: "RSA3072" },
|
||||
{ label: "RSA4096", value: "RSA4096" },
|
||||
{ label: "RSA8192", value: "RSA8192" },
|
||||
{ label: "EC256", value: "EC256" },
|
||||
{ label: "EC384", value: "EC384" },
|
||||
],
|
||||
{},
|
||||
{ showRequireMark: false }
|
||||
),
|
||||
useFormInput(
|
||||
$t("t_7_1747990227761"),
|
||||
"proxy",
|
||||
{
|
||||
placeholder: $t("t_8_1747990235316"),
|
||||
allowInput: noSideSpace,
|
||||
},
|
||||
{ showRequireMark: false }
|
||||
),
|
||||
useFormSwitch(
|
||||
$t("t_2_1749204567193"),
|
||||
"close_cname",
|
||||
{
|
||||
checkedValue: 1,
|
||||
uncheckedValue: 0,
|
||||
},
|
||||
{ showRequireMark: false }
|
||||
),
|
||||
useFormSwitch(
|
||||
$t("t_2_1747106957037"),
|
||||
"skip_check",
|
||||
{
|
||||
checkedValue: 1,
|
||||
uncheckedValue: 0,
|
||||
},
|
||||
{ showRequireMark: false }
|
||||
),
|
||||
// 只有在跳过预检查关闭时才显示DNS递归服务器、预检查超时时间和忽略预检查结果
|
||||
...(param.value.skip_check === 0
|
||||
? [
|
||||
useFormInput(
|
||||
$t("t_0_1747106957037"),
|
||||
"name_server",
|
||||
{
|
||||
placeholder: $t("t_1_1747106961747"),
|
||||
allowInput: noSideSpace,
|
||||
onInput: (val: string) => {
|
||||
param.value.name_server = val
|
||||
.replace(/,/g, ",")
|
||||
.replace(/;/g, ","); // 中文逗号分隔
|
||||
},
|
||||
},
|
||||
{ showRequireMark: false }
|
||||
),
|
||||
{
|
||||
type: "custom" as const,
|
||||
render: () => {
|
||||
return (
|
||||
<NFormItem
|
||||
label={$t("t_0_1749263105073")}
|
||||
path="max_wait"
|
||||
>
|
||||
<NInputNumber
|
||||
v-model:value={
|
||||
(
|
||||
param.value as ApplyNodeConfig & {
|
||||
max_wait?: number;
|
||||
}
|
||||
).max_wait
|
||||
}
|
||||
showButton={false}
|
||||
min={1}
|
||||
class="w-full"
|
||||
placeholder={$t("t_1_1749263104936")}
|
||||
/>
|
||||
</NFormItem>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "custom" as const,
|
||||
render: () => {
|
||||
return (
|
||||
<NFormItem
|
||||
label={$t("t_2_1749263103765")}
|
||||
path="ignore_check"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<span class="text-[1.4rem] mr-[1.2rem]">
|
||||
{$t("t_3_1749263104237")}
|
||||
</span>
|
||||
<NSwitch
|
||||
v-model:value={param.value.ignore_check}
|
||||
checkedValue={1}
|
||||
uncheckedValue={0}
|
||||
class="mx-[.5rem]"
|
||||
v-slots={{
|
||||
checked: () => $t("t_4_1749263101853"),
|
||||
unchecked: () => $t("t_5_1749263101934"),
|
||||
}}
|
||||
/>
|
||||
<span class="text-[1.4rem] ml-[1.2rem]">
|
||||
{$t("t_6_1749263103891")}
|
||||
</span>
|
||||
</div>
|
||||
</NFormItem>
|
||||
);
|
||||
},
|
||||
},
|
||||
]
|
||||
: []),
|
||||
]
|
||||
: []),
|
||||
useFormHelp([
|
||||
{
|
||||
content: $t("t_0_1747040228657"),
|
||||
},
|
||||
{
|
||||
content: $t("t_1_1747040226143"),
|
||||
},
|
||||
]),
|
||||
];
|
||||
});
|
||||
|
||||
// 创建表单实例
|
||||
const { component: Form, data, example } = useForm<ApplyNodeConfig>({ defaultValue: param, config, rules })
|
||||
// 创建表单实例
|
||||
const {
|
||||
component: Form,
|
||||
data,
|
||||
example,
|
||||
} = useForm<ApplyNodeConfig>({ defaultValue: param, config, rules });
|
||||
|
||||
// 监听CA值变化,自动加载邮箱选项
|
||||
watch(
|
||||
() => param.value.ca,
|
||||
async (newCA) => {
|
||||
if (newCA) {
|
||||
await loadEmailOptions(newCA)
|
||||
} else {
|
||||
emailOptions.value = []
|
||||
param.value.email = ''
|
||||
showEmailDropdown.value = false
|
||||
}
|
||||
},
|
||||
)
|
||||
// 监听CA值变化,自动加载邮箱选项
|
||||
watch(
|
||||
() => param.value.ca,
|
||||
async (newCA) => {
|
||||
if (newCA) {
|
||||
await loadEmailOptions(newCA);
|
||||
} else {
|
||||
emailOptions.value = [];
|
||||
param.value.email = "";
|
||||
showEmailDropdown.value = false;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 监听邮箱选项变化,如果当前下拉显示且没有选项了就关闭下拉
|
||||
watch(
|
||||
() => emailOptions.value,
|
||||
(newOptions) => {
|
||||
if (showEmailDropdown.value && newOptions.length === 0) {
|
||||
showEmailDropdown.value = false
|
||||
}
|
||||
},
|
||||
)
|
||||
// 监听邮箱选项变化,如果当前下拉显示且没有选项了就关闭下拉
|
||||
watch(
|
||||
() => emailOptions.value,
|
||||
(newOptions) => {
|
||||
if (showEmailDropdown.value && newOptions.length === 0) {
|
||||
showEmailDropdown.value = false;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(async () => {
|
||||
advancedOptions.value = false
|
||||
await loadCAOptions()
|
||||
onMounted(async () => {
|
||||
advancedOptions.value = false;
|
||||
await loadCAOptions();
|
||||
|
||||
// 如果是编辑模式且有外部传入的邮箱,直接设置邮箱值
|
||||
if (isEdit.value && routeEmail.value) {
|
||||
param.value.email = routeEmail.value
|
||||
}
|
||||
// 如果当前已经有CA值,主动加载对应的邮件选项
|
||||
if (param.value.ca) {
|
||||
await loadEmailOptions(param.value.ca);
|
||||
}
|
||||
|
||||
// 移除重复调用,让 watch 监听器处理 CA 值变化
|
||||
// 这样避免了 onMounted 和 watch 同时调用 loadEmailOptions 导致的重复请求
|
||||
})
|
||||
// 如果是编辑模式且有外部传入的邮箱,直接设置邮箱值
|
||||
if (isEdit.value && routeEmail.value) {
|
||||
param.value.email = routeEmail.value;
|
||||
}
|
||||
|
||||
// 确认事件触发
|
||||
confirm(async (close) => {
|
||||
try {
|
||||
await example.value?.validate()
|
||||
// 移除重复调用,让 watch 监听器处理 CA 值变化
|
||||
// 这样避免了 onMounted 和 watch 同时调用 loadEmailOptions 导致的重复请求
|
||||
});
|
||||
|
||||
updateNodeConfig(props.node.id, data.value) // 更新节点配置
|
||||
isRefreshNode.value = props.node.id // 刷新节点
|
||||
close()
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
})
|
||||
// 确认事件触发
|
||||
confirm(async (close) => {
|
||||
try {
|
||||
await example.value?.validate();
|
||||
|
||||
return () => (
|
||||
<div class="apply-node-drawer">
|
||||
<Form labelPlacement="top" />
|
||||
</div>
|
||||
)
|
||||
},
|
||||
updateNodeConfig(props.node.id, data.value); // 更新节点配置
|
||||
isRefreshNode.value = props.node.id; // 刷新节点
|
||||
close();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
});
|
||||
|
||||
return () => (
|
||||
<div class="apply-node-drawer">
|
||||
<Form labelPlacement="top" />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
})
|
||||
|
||||
@@ -28,208 +28,235 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const { updateNodeConfig, isRefreshNode, flowData } = useStore()
|
||||
// 弹窗辅助
|
||||
const { confirm } = useModalHooks()
|
||||
// 错误处理
|
||||
const { handleError } = useError()
|
||||
// 获取表单助手函数
|
||||
const { useFormRadio, useFormCustom } = useFormHooks()
|
||||
// 表单参数
|
||||
const param = ref(deepClone(props.node.config))
|
||||
|
||||
// 周期类型选项
|
||||
const cycleTypeOptions = [
|
||||
{ label: $t('t_2_1744875938555'), value: 'day' },
|
||||
{ label: $t('t_0_1744942117992'), value: 'week' },
|
||||
{ label: $t('t_3_1744875938310'), value: 'month' },
|
||||
]
|
||||
|
||||
// 星期选项
|
||||
const weekOptions = [
|
||||
{ label: $t('t_1_1744942116527'), value: 1 },
|
||||
{ label: $t('t_2_1744942117890'), value: 2 },
|
||||
{ label: $t('t_3_1744942117885'), value: 3 },
|
||||
{ label: $t('t_4_1744942117738'), value: 4 },
|
||||
{ label: $t('t_5_1744942117167'), value: 5 },
|
||||
{ label: $t('t_6_1744942117815'), value: 6 },
|
||||
{ label: $t('t_7_1744942117862'), value: 0 },
|
||||
]
|
||||
|
||||
// 定义默认值常量,避免重复
|
||||
const DEFAULT_AUTO_SETTINGS: Record<string, StartNodeConfig> = {
|
||||
day: { exec_type: 'auto', type: 'day', hour: 1, minute: 0 },
|
||||
week: { exec_type: 'auto', type: 'week', hour: 1, minute: 0, week: 1 },
|
||||
month: { exec_type: 'auto', type: 'month', hour: 1, minute: 0, month: 1 },
|
||||
}
|
||||
|
||||
// 创建时间输入input
|
||||
const createTimeInput = (value: number, updateFn: (val: number) => void, max: number, label: string): VNode => (
|
||||
<NInputGroup>
|
||||
<NInputNumber
|
||||
value={value}
|
||||
onUpdateValue={(val: number | null) => {
|
||||
if (val !== null) {
|
||||
updateFn(val)
|
||||
}
|
||||
}}
|
||||
max={max}
|
||||
min={0}
|
||||
showButton={false}
|
||||
class="w-full"
|
||||
/>
|
||||
<NInputGroupLabel>{label}</NInputGroupLabel>
|
||||
</NInputGroup>
|
||||
)
|
||||
|
||||
// 表单渲染
|
||||
const formRender = computed(() => {
|
||||
const formItems: FormConfig = []
|
||||
if (param.value.exec_type === 'auto') {
|
||||
formItems.push(
|
||||
useFormCustom<StartNodeConfig>(() => {
|
||||
return (
|
||||
<NGrid cols={24} xGap={24}>
|
||||
<NFormItemGi label={$t('t_2_1744879616413')} span={8} showRequireMark path="type">
|
||||
<NSelect
|
||||
class="w-full"
|
||||
options={cycleTypeOptions}
|
||||
v-model:value={param.value.type}
|
||||
onUpdateValue={(val: 'day' | 'week' | 'month') => {
|
||||
if (val) {
|
||||
param.value.type = val
|
||||
updateParamValueByType(val)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</NFormItemGi>
|
||||
|
||||
{param.value.type !== 'day' && (
|
||||
<NFormItemGi span={5} path={param.value.type === 'week' ? 'week' : 'month'}>
|
||||
{param.value.type === 'week' ? (
|
||||
<NSelect
|
||||
value={param.value.week}
|
||||
onUpdateValue={(val: number) => {
|
||||
param.value.week = val
|
||||
}}
|
||||
options={weekOptions}
|
||||
/>
|
||||
) : (
|
||||
createTimeInput(
|
||||
param.value.month || 0,
|
||||
(val: number) => (param.value.month = val),
|
||||
31,
|
||||
$t('t_29_1744958838904'),
|
||||
)
|
||||
)}
|
||||
</NFormItemGi>
|
||||
)}
|
||||
|
||||
<NFormItemGi span={param.value.type === 'day' ? 7 : 5} path="hour">
|
||||
{createTimeInput(
|
||||
param.value.hour || 0,
|
||||
(val: number) => (param.value.hour = val),
|
||||
23,
|
||||
$t('t_5_1744879615277'),
|
||||
)}
|
||||
</NFormItemGi>
|
||||
|
||||
<NFormItemGi span={param.value.type === 'day' ? 7 : 5} path="minute">
|
||||
{createTimeInput(
|
||||
param.value.minute || 0,
|
||||
(val: number) => (param.value.minute = val),
|
||||
59,
|
||||
$t('t_3_1744879615723'),
|
||||
)}
|
||||
</NFormItemGi>
|
||||
</NGrid>
|
||||
)
|
||||
}),
|
||||
)
|
||||
}
|
||||
return [
|
||||
// 运行模式选择
|
||||
useFormRadio($t('t_30_1745735764748'), 'exec_type', [
|
||||
{ label: $t('t_4_1744875940750'), value: 'auto' },
|
||||
{ label: $t('t_5_1744875940010'), value: 'manual' },
|
||||
]),
|
||||
...formItems,
|
||||
]
|
||||
})
|
||||
|
||||
// 创建表单实例
|
||||
const {
|
||||
component: Form,
|
||||
data,
|
||||
example,
|
||||
} = useForm<StartNodeConfig>({
|
||||
defaultValue: param,
|
||||
config: formRender,
|
||||
rules,
|
||||
})
|
||||
updateNodeConfig,
|
||||
isRefreshNode,
|
||||
flowData,
|
||||
setStartNodeSavedByUser,
|
||||
startNodeSavedByUser,
|
||||
} = useStore();
|
||||
// 弹窗辅助
|
||||
const { confirm } = useModalHooks();
|
||||
// 错误处理
|
||||
const { handleError } = useError();
|
||||
// 获取表单助手函数
|
||||
const { useFormRadio, useFormCustom } = useFormHooks();
|
||||
// 表单参数
|
||||
const param = ref(deepClone(props.node.config));
|
||||
|
||||
// 更新参数的函数
|
||||
const updateParamValue = (updates: StartNodeConfig) => {
|
||||
console.log(updates)
|
||||
let newParams = { ...updates }
|
||||
// if (newParams.exec_type === 'manual') {
|
||||
// 小时随机 1-6
|
||||
const randomHour = Math.floor(Math.random() * 4) + 1
|
||||
// 分钟每5分钟随机,0-55
|
||||
const randomMinute = Math.floor(Math.random() * 12) * 5
|
||||
newParams = {
|
||||
...newParams,
|
||||
hour: randomHour,
|
||||
minute: randomMinute,
|
||||
}
|
||||
param.value = newParams
|
||||
// }
|
||||
}
|
||||
// 周期类型选项
|
||||
const cycleTypeOptions = [
|
||||
{ label: $t("t_2_1744875938555"), value: "day" },
|
||||
{ label: $t("t_0_1744942117992"), value: "week" },
|
||||
{ label: $t("t_3_1744875938310"), value: "month" },
|
||||
];
|
||||
|
||||
const updateParamValueByType = (type: 'day' | 'week' | 'month') => {
|
||||
updateParamValue(DEFAULT_AUTO_SETTINGS[type] as StartNodeConfig)
|
||||
}
|
||||
// 星期选项
|
||||
const weekOptions = [
|
||||
{ label: $t("t_1_1744942116527"), value: 1 },
|
||||
{ label: $t("t_2_1744942117890"), value: 2 },
|
||||
{ label: $t("t_3_1744942117885"), value: 3 },
|
||||
{ label: $t("t_4_1744942117738"), value: 4 },
|
||||
{ label: $t("t_5_1744942117167"), value: 5 },
|
||||
{ label: $t("t_6_1744942117815"), value: 6 },
|
||||
{ label: $t("t_7_1744942117862"), value: 0 },
|
||||
];
|
||||
|
||||
// 监听执行类型变化
|
||||
watch(
|
||||
() => param.value.exec_type,
|
||||
(newVal) => {
|
||||
if (newVal === 'auto') {
|
||||
updateParamValue(DEFAULT_AUTO_SETTINGS.day as StartNodeConfig)
|
||||
} else if (newVal === 'manual') {
|
||||
updateParamValue({ exec_type: 'manual' })
|
||||
}
|
||||
},
|
||||
)
|
||||
// 定义默认值常量,避免重复
|
||||
const DEFAULT_AUTO_SETTINGS: Record<string, StartNodeConfig> = {
|
||||
day: { exec_type: "auto", type: "day", hour: 1, minute: 0 },
|
||||
week: { exec_type: "auto", type: "week", hour: 1, minute: 0, week: 1 },
|
||||
month: { exec_type: "auto", type: "month", hour: 1, minute: 0, month: 1 },
|
||||
};
|
||||
|
||||
// 监听类型变化
|
||||
watch(
|
||||
() => param.value.type,
|
||||
(newVal) => {
|
||||
if (newVal && param.value.exec_type === 'auto') {
|
||||
updateParamValue(DEFAULT_AUTO_SETTINGS[newVal] as StartNodeConfig)
|
||||
}
|
||||
},
|
||||
)
|
||||
// 创建时间输入input
|
||||
const createTimeInput = (
|
||||
value: number,
|
||||
updateFn: (val: number) => void,
|
||||
max: number,
|
||||
label: string
|
||||
): VNode => (
|
||||
<NInputGroup>
|
||||
<NInputNumber
|
||||
value={value}
|
||||
onUpdateValue={(val: number | null) => {
|
||||
if (val !== null) {
|
||||
updateFn(val);
|
||||
}
|
||||
}}
|
||||
max={max}
|
||||
min={0}
|
||||
showButton={false}
|
||||
class="w-full"
|
||||
/>
|
||||
<NInputGroupLabel>{label}</NInputGroupLabel>
|
||||
</NInputGroup>
|
||||
);
|
||||
|
||||
// 确认事件触发
|
||||
confirm(async (close) => {
|
||||
try {
|
||||
await example.value?.validate()
|
||||
updateNodeConfig(props.node.id, data.value) // 更新节点配置
|
||||
isRefreshNode.value = props.node.id // 刷新节点
|
||||
close()
|
||||
} catch (error) {
|
||||
handleError(error)
|
||||
}
|
||||
})
|
||||
// 表单渲染
|
||||
const formRender = computed(() => {
|
||||
const formItems: FormConfig = [];
|
||||
if (param.value.exec_type === "auto") {
|
||||
formItems.push(
|
||||
useFormCustom<StartNodeConfig>(() => {
|
||||
return (
|
||||
<NGrid cols={24} xGap={24}>
|
||||
<NFormItemGi
|
||||
label={$t("t_2_1744879616413")}
|
||||
span={8}
|
||||
showRequireMark
|
||||
path="type"
|
||||
>
|
||||
<NSelect
|
||||
class="w-full"
|
||||
options={cycleTypeOptions}
|
||||
v-model:value={param.value.type}
|
||||
onUpdateValue={(val: "day" | "week" | "month") => {
|
||||
if (val) {
|
||||
param.value.type = val;
|
||||
updateParamValueByType(val);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</NFormItemGi>
|
||||
|
||||
onMounted(() => {
|
||||
if (isUndefined(flowData.value.id)) {
|
||||
updateParamValueByType('day')
|
||||
updateNodeConfig(props.node.id, param.value) // 更新节点配置
|
||||
}
|
||||
})
|
||||
{param.value.type !== "day" && (
|
||||
<NFormItemGi
|
||||
span={5}
|
||||
path={param.value.type === "week" ? "week" : "month"}
|
||||
>
|
||||
{param.value.type === "week" ? (
|
||||
<NSelect
|
||||
value={param.value.week}
|
||||
onUpdateValue={(val: number) => {
|
||||
param.value.week = val;
|
||||
}}
|
||||
options={weekOptions}
|
||||
/>
|
||||
) : (
|
||||
createTimeInput(
|
||||
param.value.month || 0,
|
||||
(val: number) => (param.value.month = val),
|
||||
31,
|
||||
$t("t_29_1744958838904")
|
||||
)
|
||||
)}
|
||||
</NFormItemGi>
|
||||
)}
|
||||
|
||||
<NFormItemGi
|
||||
span={param.value.type === "day" ? 7 : 5}
|
||||
path="hour"
|
||||
>
|
||||
{createTimeInput(
|
||||
param.value.hour || 0,
|
||||
(val: number) => (param.value.hour = val),
|
||||
23,
|
||||
$t("t_5_1744879615277")
|
||||
)}
|
||||
</NFormItemGi>
|
||||
|
||||
<NFormItemGi
|
||||
span={param.value.type === "day" ? 7 : 5}
|
||||
path="minute"
|
||||
>
|
||||
{createTimeInput(
|
||||
param.value.minute || 0,
|
||||
(val: number) => (param.value.minute = val),
|
||||
59,
|
||||
$t("t_3_1744879615723")
|
||||
)}
|
||||
</NFormItemGi>
|
||||
</NGrid>
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
return [
|
||||
// 运行模式选择
|
||||
useFormRadio($t("t_30_1745735764748"), "exec_type", [
|
||||
{ label: $t("t_4_1744875940750"), value: "auto" },
|
||||
{ label: $t("t_5_1744875940010"), value: "manual" },
|
||||
]),
|
||||
...formItems,
|
||||
];
|
||||
});
|
||||
|
||||
// 创建表单实例
|
||||
const {
|
||||
component: Form,
|
||||
data,
|
||||
example,
|
||||
} = useForm<StartNodeConfig>({
|
||||
defaultValue: param,
|
||||
config: formRender,
|
||||
rules,
|
||||
});
|
||||
|
||||
// 更新参数的函数
|
||||
const updateParamValue = (updates: StartNodeConfig) => {
|
||||
console.log(updates);
|
||||
let newParams = { ...updates };
|
||||
// if (newParams.exec_type === 'manual') {
|
||||
// 小时随机 1-6
|
||||
const randomHour = Math.floor(Math.random() * 4) + 1;
|
||||
// 分钟每5分钟随机,0-55
|
||||
const randomMinute = Math.floor(Math.random() * 12) * 5;
|
||||
newParams = {
|
||||
...newParams,
|
||||
hour: randomHour,
|
||||
minute: randomMinute,
|
||||
};
|
||||
param.value = newParams;
|
||||
// }
|
||||
};
|
||||
|
||||
const updateParamValueByType = (type: "day" | "week" | "month") => {
|
||||
updateParamValue(DEFAULT_AUTO_SETTINGS[type] as StartNodeConfig);
|
||||
};
|
||||
|
||||
// 监听执行类型变化
|
||||
watch(
|
||||
() => param.value.exec_type,
|
||||
(newVal) => {
|
||||
if (newVal === "auto") {
|
||||
updateParamValue(DEFAULT_AUTO_SETTINGS.day as StartNodeConfig);
|
||||
} else if (newVal === "manual") {
|
||||
updateParamValue({ exec_type: "manual" });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 监听类型变化
|
||||
watch(
|
||||
() => param.value.type,
|
||||
(newVal) => {
|
||||
if (newVal && param.value.exec_type === "auto") {
|
||||
updateParamValue(DEFAULT_AUTO_SETTINGS[newVal] as StartNodeConfig);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 确认事件触发
|
||||
confirm(async (close) => {
|
||||
try {
|
||||
await example.value?.validate();
|
||||
updateNodeConfig(props.node.id, data.value); // 更新节点配置
|
||||
isRefreshNode.value = props.node.id; // 刷新节点
|
||||
setStartNodeSavedByUser(true);
|
||||
close();
|
||||
} catch (error) {
|
||||
handleError(error);
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
console.log("开始节点初始化");
|
||||
if (isUndefined(flowData.value.id) && !startNodeSavedByUser.value) {
|
||||
updateParamValueByType("day");
|
||||
updateNodeConfig(props.node.id, param.value); // 更新节点配置
|
||||
}
|
||||
});
|
||||
|
||||
return () => (
|
||||
<div class="apply-node-drawer">
|
||||
|
||||
@@ -91,6 +91,57 @@ export const useController = () => {
|
||||
// 判断是否为子路由
|
||||
const hasChildRoutes = computed(() => route.path !== "/auto-deploy");
|
||||
|
||||
/**
|
||||
* @description 格式化执行周期显示
|
||||
* @param {string} execTime - 执行时间配置JSON字符串
|
||||
* @param {string} execType - 执行类型
|
||||
* @returns {string} 格式化后的执行周期文本
|
||||
*/
|
||||
const formatExecTime = (execTime: string, execType: string): string => {
|
||||
// 如果是手动执行,显示 --
|
||||
if (execType !== "auto") {
|
||||
return "--";
|
||||
}
|
||||
|
||||
// 如果没有执行时间配置,默认为每日
|
||||
if (!execTime) {
|
||||
return "每日";
|
||||
}
|
||||
|
||||
try {
|
||||
const timeConfig = JSON.parse(execTime);
|
||||
const { type = "day", hour, minute, week, month } = timeConfig;
|
||||
|
||||
// 格式化时间
|
||||
const timeStr = `${hour.toString().padStart(2, "0")}:${minute
|
||||
.toString()
|
||||
.padStart(2, "0")}`;
|
||||
|
||||
switch (type) {
|
||||
case "day":
|
||||
return `每日 ${timeStr}`;
|
||||
case "week":
|
||||
const weekDays = [
|
||||
"周日",
|
||||
"周一",
|
||||
"周二",
|
||||
"周三",
|
||||
"周四",
|
||||
"周五",
|
||||
"周六",
|
||||
];
|
||||
return `每周${weekDays[week] || "周" + week} ${timeStr}`;
|
||||
case "month":
|
||||
return `每月${month}日 ${timeStr}`;
|
||||
default:
|
||||
return `每日 ${timeStr}`;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("解析执行时间配置失败:", error);
|
||||
return "每日";
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 创建表格列配置
|
||||
* @returns {DataTableColumn<WorkflowItem>[]} 返回表格列配置数组
|
||||
@@ -140,6 +191,14 @@ export const useController = () => {
|
||||
render: (row: WorkflowItem) => row.last_run_time || "-",
|
||||
},
|
||||
statusCol<WorkflowItem>("last_run_status", $t("t_2_1750129253921")),
|
||||
{
|
||||
title: "执行周期",
|
||||
key: "exec_time",
|
||||
width: 150,
|
||||
render: (row: WorkflowItem) => (
|
||||
<span>{formatExecTime(row.exec_time, row.exec_type)}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: $t("t_8_1745215914610"),
|
||||
key: "actions",
|
||||
@@ -342,12 +401,17 @@ export const useController = () => {
|
||||
* @description 复制工作流
|
||||
* @param {WorkflowItem} workflow - 工作流对象
|
||||
*/
|
||||
const handleCopyWorkflow = async (workflow: WorkflowItem) => {
|
||||
console.log(workflow, 'workflow123123123123');
|
||||
const { name, content, exec_type, active, exec_time } = workflow;
|
||||
const params = { name: `${name} - 副本`, content, exec_type, active, exec_time };
|
||||
await copyExistingWorkflow(params as WorkflowItem);
|
||||
await fetch();
|
||||
const handleCopyWorkflow = async (workflow: WorkflowItem) => {
|
||||
const { name, content, exec_type, active, exec_time } = workflow;
|
||||
const params = {
|
||||
name: `${name} - 副本`,
|
||||
content,
|
||||
exec_type,
|
||||
active,
|
||||
exec_time,
|
||||
};
|
||||
await copyExistingWorkflow(params as WorkflowItem);
|
||||
await fetch();
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -45,18 +45,21 @@ export default defineComponent({
|
||||
<NSelect
|
||||
options={[
|
||||
{ label: "全部", value: "" },
|
||||
...intermediateCaList.value.map((item) => ({
|
||||
...(intermediateCaList.value || []).map((item) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}))
|
||||
})),
|
||||
]}
|
||||
placeholder="请选择中间证书"
|
||||
size="large"
|
||||
style={{ width: "180px" }}
|
||||
defaultValue={""}
|
||||
style={{ width: "180px" }}
|
||||
defaultValue={""}
|
||||
onUpdateValue={handleCaIdChange}
|
||||
/>
|
||||
<SearchComponent placeholder="请输入名称搜索" style={{ width: "240px" }} />
|
||||
<SearchComponent
|
||||
placeholder="请输入名称搜索"
|
||||
style={{ width: "240px" }}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
content: () => (
|
||||
|
||||
@@ -38,19 +38,24 @@ export const useStore = () => {
|
||||
|
||||
const getIntermediateCaList = async () => {
|
||||
try {
|
||||
const { fetch, data } = getCaList({
|
||||
const { fetch, data } = getCaList({
|
||||
p: "-1",
|
||||
limit: "-1",
|
||||
level: "intermediate",
|
||||
});
|
||||
await fetch();
|
||||
if (data.value?.status === true) {
|
||||
intermediateCaList.value = data.value.data;
|
||||
return data.value.data;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取中间证书列表失败:', error);
|
||||
}
|
||||
});
|
||||
await fetch();
|
||||
if (data.value?.status === true) {
|
||||
intermediateCaList.value = data.value.data || [];
|
||||
return data.value.data || [];
|
||||
} else {
|
||||
intermediateCaList.value = [];
|
||||
return [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("获取中间证书列表失败:", error);
|
||||
intermediateCaList.value = [];
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,174 +1,177 @@
|
||||
import { defineComponent, ref, computed, watch } from 'vue';
|
||||
import { NForm, NFormItem, NInput, NSelect, NSpace, NButton, FormRules, useMessage } from 'naive-ui';
|
||||
import { useStore } from '../useStore';
|
||||
import { useAddCaController } from '../useController';
|
||||
import { useModalClose } from '@baota/naive-ui/hooks';
|
||||
import {
|
||||
NForm,
|
||||
NFormItem,
|
||||
NInput,
|
||||
NSelect,
|
||||
NSpace,
|
||||
NButton,
|
||||
FormRules,
|
||||
useMessage,
|
||||
NDivider,
|
||||
NIcon,
|
||||
} from "naive-ui";
|
||||
import { useStore } from "../useStore";
|
||||
import { useAddCaController } from "../useController";
|
||||
import { useModalClose } from "@baota/naive-ui/hooks";
|
||||
import { ChevronDown } from "@vicons/ionicons5";
|
||||
|
||||
/**
|
||||
* 添加CA模态框组件
|
||||
*/
|
||||
export default defineComponent({
|
||||
emits: ['success'],
|
||||
setup(props, { emit }) {
|
||||
const { addForm, resetAddForm, createType, rootCaList } = useStore();
|
||||
const message = useMessage();
|
||||
const closeModal = useModalClose();
|
||||
|
||||
// 表单引用
|
||||
const formRef = ref();
|
||||
|
||||
// 有效期单位选择
|
||||
const validityUnit = ref<'day' | 'year'>('day');
|
||||
|
||||
// 使用表单控制器
|
||||
const { handleSubmit } = useAddCaController();
|
||||
|
||||
// 表单验证规则
|
||||
const rules = computed((): FormRules => {
|
||||
const baseRules: any = {
|
||||
name: [
|
||||
{ required: true, message: '请输入CA名称', trigger: 'blur' }
|
||||
],
|
||||
cn: [
|
||||
{ required: true, message: '请输入通用名称', trigger: 'blur' }
|
||||
],
|
||||
o: [
|
||||
{ required: true, message: '请输入组织名称', trigger: 'blur' }
|
||||
],
|
||||
c: [
|
||||
{ required: true, message: '请选择国家', trigger: 'change' }
|
||||
],
|
||||
ou: [
|
||||
{ required: true, message: '请输入组织单位', trigger: 'blur' }
|
||||
],
|
||||
province: [
|
||||
{ required: true, message: '请输入省份', trigger: 'blur' }
|
||||
],
|
||||
locality: [
|
||||
{ required: true, message: '请输入城市', trigger: 'blur' }
|
||||
],
|
||||
key_length: [
|
||||
{ required: true, message: '请选择密钥长度', trigger: 'change' }
|
||||
],
|
||||
valid_days: [
|
||||
{ required: true, message: '请选择有效期', trigger: 'change' }
|
||||
]
|
||||
};
|
||||
emits: ["success"],
|
||||
setup(props, { emit }) {
|
||||
const { addForm, resetAddForm, createType, rootCaList } = useStore();
|
||||
const message = useMessage();
|
||||
const closeModal = useModalClose();
|
||||
|
||||
if (createType.value === 'root') {
|
||||
baseRules.algorithm = [
|
||||
{ required: true, message: '请选择加密算法', trigger: 'change' }
|
||||
];
|
||||
}
|
||||
// 表单引用
|
||||
const formRef = ref();
|
||||
|
||||
if (createType.value === 'intermediate') {
|
||||
baseRules.root_id = [
|
||||
{ required: true, message: '请选择父级CA', trigger: 'change' }
|
||||
];
|
||||
}
|
||||
// 有效期单位选择
|
||||
const validityUnit = ref<"day" | "year">("day");
|
||||
|
||||
return baseRules;
|
||||
});
|
||||
// 展开收起状态
|
||||
const showAdvancedConfig = ref(false);
|
||||
|
||||
// 算法选项
|
||||
const algorithmOptions = [
|
||||
{ label: "ECDSA", value: "ecdsa" },
|
||||
{ label: "RSA", value: "rsa" },
|
||||
{ label: "SM2", value: "sm2" },
|
||||
];
|
||||
// 使用表单控制器
|
||||
const { handleSubmit } = useAddCaController();
|
||||
|
||||
const keyLengthOptions = computed(() => {
|
||||
switch (addForm.value.algorithm) {
|
||||
case 'ecdsa':
|
||||
return [
|
||||
{ label: "P-256 (256 bit)", value: "256" },
|
||||
{ label: "P-384 (384 bit)", value: "384" },
|
||||
{ label: "P-521 (521 bit)", value: "521" },
|
||||
];
|
||||
case 'rsa':
|
||||
return [
|
||||
{ label: "2048 bit", value: "2048" },
|
||||
{ label: "3072 bit", value: "3072" },
|
||||
{ label: "4096 bit", value: "4096" },
|
||||
];
|
||||
case 'sm2':
|
||||
return [
|
||||
{ label: "SM2 (256 bit)", value: "256" },
|
||||
];
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
});
|
||||
// 表单验证规则
|
||||
const rules = computed((): FormRules => {
|
||||
const baseRules: any = {
|
||||
name: [{ required: true, message: "请输入CA名称", trigger: "blur" }],
|
||||
cn: [{ required: true, message: "请输入通用名称", trigger: "blur" }],
|
||||
c: [{ required: true, message: "请选择国家", trigger: "change" }],
|
||||
key_length: [
|
||||
{ required: true, message: "请选择密钥长度", trigger: "change" },
|
||||
],
|
||||
valid_days: [
|
||||
{ required: true, message: "请选择有效期", trigger: "change" },
|
||||
],
|
||||
};
|
||||
|
||||
// 国家选项
|
||||
const countryOptions = [
|
||||
{ label: "中国", value: "CN" },
|
||||
{ label: "美国", value: "US" },
|
||||
{ label: "日本", value: "JP" },
|
||||
{ label: "德国", value: "DE" },
|
||||
{ label: "英国", value: "GB" },
|
||||
];
|
||||
if (createType.value === "root") {
|
||||
baseRules.algorithm = [
|
||||
{ required: true, message: "请选择加密算法", trigger: "change" },
|
||||
];
|
||||
}
|
||||
|
||||
// 监听算法变化,重置密钥长度选择
|
||||
watch(() => addForm.value.algorithm, (newAlgorithm) => {
|
||||
addForm.value.key_length = '';
|
||||
if (newAlgorithm === 'ecdsa') {
|
||||
addForm.value.key_length = '256';
|
||||
} else if (newAlgorithm === 'sm2') {
|
||||
addForm.value.key_length = '256';
|
||||
} else if (newAlgorithm === 'rsa') {
|
||||
addForm.value.key_length = '2048';
|
||||
}
|
||||
});
|
||||
if (createType.value === "intermediate") {
|
||||
baseRules.root_id = [
|
||||
{ required: true, message: "请选择父级CA", trigger: "change" },
|
||||
];
|
||||
}
|
||||
|
||||
// 监听父级CA选择,自动填充算法值
|
||||
watch(() => addForm.value.root_id, (newRootId) => {
|
||||
if (createType.value === 'intermediate' && newRootId) {
|
||||
const selectedRootCa = rootCaList.value.find(ca => ca.id.toString() === newRootId);
|
||||
if (selectedRootCa) {
|
||||
addForm.value.algorithm = selectedRootCa.algorithm;
|
||||
if (selectedRootCa.algorithm === 'ecdsa') {
|
||||
addForm.value.key_length = '256';
|
||||
} else if (selectedRootCa.algorithm === 'sm2') {
|
||||
addForm.value.key_length = '256';
|
||||
} else if (selectedRootCa.algorithm === 'rsa') {
|
||||
addForm.value.key_length = '2048';
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return baseRules;
|
||||
});
|
||||
|
||||
// 处理表单提交
|
||||
const handleFormSubmit = async () => {
|
||||
try {
|
||||
// 先验证表单
|
||||
await formRef.value?.validate();
|
||||
const formData = { ...addForm.value };
|
||||
if (validityUnit.value === 'year' && formData.valid_days) {
|
||||
const years = parseInt(formData.valid_days);
|
||||
if (!isNaN(years)) {
|
||||
formData.valid_days = (years * 365).toString();
|
||||
}
|
||||
}
|
||||
const success = await handleSubmit(formData);
|
||||
if (success) {
|
||||
resetAddForm();
|
||||
closeModal();
|
||||
return true;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('表单验证失败:', error);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// 处理取消操作
|
||||
const handleCancel = () => {
|
||||
resetAddForm();
|
||||
closeModal();
|
||||
};
|
||||
// 算法选项
|
||||
const algorithmOptions = [
|
||||
{ label: "ECDSA", value: "ecdsa" },
|
||||
{ label: "RSA", value: "rsa" },
|
||||
{ label: "SM2", value: "sm2" },
|
||||
];
|
||||
|
||||
return () => (
|
||||
const keyLengthOptions = computed(() => {
|
||||
switch (addForm.value.algorithm) {
|
||||
case "ecdsa":
|
||||
return [
|
||||
{ label: "P-256 (256 bit)", value: "256" },
|
||||
{ label: "P-384 (384 bit)", value: "384" },
|
||||
{ label: "P-521 (521 bit)", value: "521" },
|
||||
];
|
||||
case "rsa":
|
||||
return [
|
||||
{ label: "2048 bit", value: "2048" },
|
||||
{ label: "3072 bit", value: "3072" },
|
||||
{ label: "4096 bit", value: "4096" },
|
||||
];
|
||||
case "sm2":
|
||||
return [{ label: "SM2 (256 bit)", value: "256" }];
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
});
|
||||
|
||||
// 国家选项
|
||||
const countryOptions = [
|
||||
{ label: "中国", value: "CN" },
|
||||
{ label: "美国", value: "US" },
|
||||
{ label: "日本", value: "JP" },
|
||||
{ label: "德国", value: "DE" },
|
||||
{ label: "英国", value: "GB" },
|
||||
];
|
||||
|
||||
// 监听算法变化,重置密钥长度选择
|
||||
watch(
|
||||
() => addForm.value.algorithm,
|
||||
(newAlgorithm) => {
|
||||
addForm.value.key_length = "";
|
||||
if (newAlgorithm === "ecdsa") {
|
||||
addForm.value.key_length = "256";
|
||||
} else if (newAlgorithm === "sm2") {
|
||||
addForm.value.key_length = "256";
|
||||
} else if (newAlgorithm === "rsa") {
|
||||
addForm.value.key_length = "2048";
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 监听父级CA选择,自动填充算法值
|
||||
watch(
|
||||
() => addForm.value.root_id,
|
||||
(newRootId) => {
|
||||
if (createType.value === "intermediate" && newRootId) {
|
||||
const selectedRootCa = rootCaList.value.find(
|
||||
(ca) => ca.id.toString() === newRootId
|
||||
);
|
||||
if (selectedRootCa) {
|
||||
addForm.value.algorithm = selectedRootCa.algorithm;
|
||||
if (selectedRootCa.algorithm === "ecdsa") {
|
||||
addForm.value.key_length = "256";
|
||||
} else if (selectedRootCa.algorithm === "sm2") {
|
||||
addForm.value.key_length = "256";
|
||||
} else if (selectedRootCa.algorithm === "rsa") {
|
||||
addForm.value.key_length = "2048";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 处理表单提交
|
||||
const handleFormSubmit = async () => {
|
||||
try {
|
||||
// 先验证表单
|
||||
await formRef.value?.validate();
|
||||
const formData = { ...addForm.value };
|
||||
if (validityUnit.value === "year" && formData.valid_days) {
|
||||
const years = parseInt(formData.valid_days);
|
||||
if (!isNaN(years)) {
|
||||
formData.valid_days = (years * 365).toString();
|
||||
}
|
||||
}
|
||||
const success = await handleSubmit(formData);
|
||||
if (success) {
|
||||
resetAddForm();
|
||||
closeModal();
|
||||
return true;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("表单验证失败:", error);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// 处理取消操作
|
||||
const handleCancel = () => {
|
||||
resetAddForm();
|
||||
closeModal();
|
||||
};
|
||||
|
||||
return () => (
|
||||
<NForm
|
||||
ref={formRef}
|
||||
model={addForm.value}
|
||||
@@ -191,42 +194,6 @@ export default defineComponent({
|
||||
/>
|
||||
</NFormItem>
|
||||
|
||||
<NFormItem label="组织(O)" path="o" required>
|
||||
<NInput
|
||||
v-model:value={addForm.value.o}
|
||||
placeholder="请输入组织名称"
|
||||
/>
|
||||
</NFormItem>
|
||||
|
||||
<NFormItem label="国家(C)" path="c" required>
|
||||
<NSelect
|
||||
v-model:value={addForm.value.c}
|
||||
options={countryOptions}
|
||||
placeholder="请选择国家"
|
||||
/>
|
||||
</NFormItem>
|
||||
|
||||
<NFormItem label="组织单位(OU)" path="ou" required>
|
||||
<NInput
|
||||
v-model:value={addForm.value.ou}
|
||||
placeholder="请输入组织单位"
|
||||
/>
|
||||
</NFormItem>
|
||||
|
||||
<NFormItem label="省份" path="province" required>
|
||||
<NInput
|
||||
v-model:value={addForm.value.province}
|
||||
placeholder="请输入省份"
|
||||
/>
|
||||
</NFormItem>
|
||||
|
||||
<NFormItem label="城市" path="locality" required>
|
||||
<NInput
|
||||
v-model:value={addForm.value.locality}
|
||||
placeholder="请输入城市"
|
||||
/>
|
||||
</NFormItem>
|
||||
|
||||
{createType.value === "intermediate" && (
|
||||
<NFormItem label="父级CA" path="root_id" required>
|
||||
<NSelect
|
||||
@@ -270,14 +237,78 @@ export default defineComponent({
|
||||
<NSelect
|
||||
v-model:value={validityUnit.value}
|
||||
options={[
|
||||
{ label: '天', value: 'day' },
|
||||
{ label: '年', value: 'year' }
|
||||
{ label: "天", value: "day" },
|
||||
{ label: "年", value: "year" },
|
||||
]}
|
||||
style={{ width: '80px' }}
|
||||
style={{ width: "80px" }}
|
||||
/>
|
||||
</NSpace>
|
||||
</NFormItem>
|
||||
<div class="mt-4 mb-4">
|
||||
<div
|
||||
class="flex items-center justify-center cursor-pointer py-2"
|
||||
onClick={() =>
|
||||
(showAdvancedConfig.value = !showAdvancedConfig.value)
|
||||
}
|
||||
>
|
||||
<NDivider>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-[#18a058] font-medium">更多配置</span>
|
||||
<NIcon
|
||||
size="16"
|
||||
color="#18a058"
|
||||
class="transition-transform duration-200"
|
||||
style={{
|
||||
transform: showAdvancedConfig.value
|
||||
? "rotate(180deg)"
|
||||
: "rotate(0deg)",
|
||||
}}
|
||||
>
|
||||
<ChevronDown />
|
||||
</NIcon>
|
||||
</div>
|
||||
</NDivider>
|
||||
</div>
|
||||
{showAdvancedConfig.value && (
|
||||
<div class="space-y-4 mt-4">
|
||||
<NFormItem label="组织(O)">
|
||||
<NInput
|
||||
v-model:value={addForm.value.o}
|
||||
placeholder="请输入组织名称"
|
||||
/>
|
||||
</NFormItem>
|
||||
|
||||
<NFormItem label="国家(C)" path="c" required>
|
||||
<NSelect
|
||||
v-model:value={addForm.value.c}
|
||||
options={countryOptions}
|
||||
placeholder="请选择国家"
|
||||
/>
|
||||
</NFormItem>
|
||||
|
||||
<NFormItem label="组织单位(OU)">
|
||||
<NInput
|
||||
v-model:value={addForm.value.ou}
|
||||
placeholder="请输入组织单位"
|
||||
/>
|
||||
</NFormItem>
|
||||
|
||||
<NFormItem label="省份">
|
||||
<NInput
|
||||
v-model:value={addForm.value.province}
|
||||
placeholder="请输入省份"
|
||||
/>
|
||||
</NFormItem>
|
||||
|
||||
<NFormItem label="城市">
|
||||
<NInput
|
||||
v-model:value={addForm.value.locality}
|
||||
placeholder="请输入城市"
|
||||
/>
|
||||
</NFormItem>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div class="flex justify-end gap-3 mt-6">
|
||||
<NButton onClick={handleCancel}>取消</NButton>
|
||||
<NButton type="primary" onClick={handleFormSubmit}>
|
||||
@@ -286,5 +317,5 @@ export default defineComponent({
|
||||
</div>
|
||||
</NForm>
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -428,11 +428,7 @@ export const useAddCaController = () => {
|
||||
const baseRules: any = {
|
||||
name: [{ required: true, message: '请输入CA名称', trigger: 'blur' }],
|
||||
cn: [{ required: true, message: '请输入通用名称', trigger: 'blur' }],
|
||||
o: [{ required: true, message: '请输入组织名称', trigger: 'blur' }],
|
||||
c: [{ required: true, message: '请选择国家', trigger: 'change' }],
|
||||
ou: [{ required: true, message: '请输入组织单位', trigger: 'blur' }],
|
||||
province: [{ required: true, message: '请输入省份', trigger: 'blur' }],
|
||||
locality: [{ required: true, message: '请输入城市', trigger: 'blur' }],
|
||||
key_length: [{ required: true, message: '请选择密钥长度', trigger: 'change' }],
|
||||
valid_days: [{ required: true, message: '请选择有效期', trigger: 'change' }],
|
||||
};
|
||||
@@ -468,7 +464,7 @@ export const useAddCaController = () => {
|
||||
try {
|
||||
openLoad();
|
||||
// 验证必填字段
|
||||
let requiredFields: string[] = ['name', 'cn', 'o', 'c', 'ou', 'province', 'locality', 'key_length', 'valid_days'];
|
||||
let requiredFields: string[] = ['name', 'cn', 'c', 'key_length', 'valid_days'];
|
||||
if (createType.value === 'root') {
|
||||
requiredFields.push('algorithm');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user