mirror of
https://gitee.com/mirrors/AllinSSL.git
synced 2026-03-08 15:51:11 +08:00
【修复】申请配置证书CA列表,授权api新增新增btdomain
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -103,10 +103,9 @@ export default defineComponent({
|
||||
language="custom-logs"
|
||||
trim={false}
|
||||
fontSize={14}
|
||||
lineHeight={1.5}
|
||||
class="h-full" // NLog 充满 NSpin
|
||||
style={{
|
||||
// height: '500px', // 改为 flex 布局后,由父容器控制高度
|
||||
lineHeight: '1.5rem',
|
||||
border: '1px solid var(--n-border-color)',
|
||||
borderRadius: 'var(--n-border-radius)', // 使用 Naive UI 变量
|
||||
padding: '10px',
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
.flowContainer {
|
||||
@apply flex relative box-border w-full h-[calc(100vh-19rem)] overflow-x-auto overflow-y-auto p-[1rem] justify-center;
|
||||
@apply flex relative box-border w-full h-[calc(100vh-19rem)] overflow-hidden p-[1rem] justify-center;
|
||||
user-select: none; /* 防止拖拽时选中文本 */
|
||||
cursor: grab; /* 默认鼠标样式 */
|
||||
}
|
||||
|
||||
.flowContainer:active {
|
||||
cursor: grabbing; /* 拖拽时的鼠标样式 */
|
||||
}
|
||||
|
||||
.flowProcess {
|
||||
@@ -7,7 +13,7 @@
|
||||
}
|
||||
|
||||
.flowZoom {
|
||||
@apply flex fixed items-center justify-between h-[4rem] w-[12.5rem] bottom-[4rem] z-[99];
|
||||
@apply flex fixed items-center justify-between h-[4rem] w-[16rem] bottom-[6rem] z-[99];
|
||||
}
|
||||
|
||||
.flowZoomIcon {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { NButton, NIcon, NInput } from 'naive-ui'
|
||||
import { SaveOutlined, ArrowLeftOutlined } from '@vicons/antd'
|
||||
import { SaveOutlined, ArrowLeftOutlined, ReloadOutlined } from "@vicons/antd";
|
||||
import { $t } from '@locales/index'
|
||||
import SvgIcon from '@components/SvgIcon'
|
||||
|
||||
@@ -14,47 +14,188 @@ import type { FlowNode, FlowNodeProps } from './types'
|
||||
import { useThemeCssVar } from '@baota/naive-ui/theme'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'FlowChart',
|
||||
props: {
|
||||
isEdit: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
type: {
|
||||
type: String as PropType<'quick' | 'advanced'>,
|
||||
default: 'quick',
|
||||
},
|
||||
node: {
|
||||
type: Object as PropType<FlowNode>,
|
||||
default: () => ({}),
|
||||
},
|
||||
// 任务节点列表
|
||||
taskComponents: {
|
||||
type: Object as PropType<Record<string, Component>>,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
setup(props: FlowNodeProps, { slots }) {
|
||||
const cssVars = useThemeCssVar([
|
||||
'borderColor',
|
||||
'dividerColor',
|
||||
'textColor1',
|
||||
'textColor2',
|
||||
'primaryColor',
|
||||
'primaryColorHover',
|
||||
'bodyColor',
|
||||
])
|
||||
const { flowData, selectedNodeId, flowZoom, resetFlowData } = useStore()
|
||||
const { initData, handleSaveConfig, handleZoom, goBack } = useController({
|
||||
type: props?.type,
|
||||
node: props?.node,
|
||||
isEdit: props?.isEdit,
|
||||
})
|
||||
// 提供任务节点组件映射给后代组件使用
|
||||
provide('taskComponents', props.taskComponents)
|
||||
onMounted(initData)
|
||||
onUnmounted(resetFlowData)
|
||||
return () => (
|
||||
name: "FlowChart",
|
||||
props: {
|
||||
isEdit: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
type: {
|
||||
type: String as PropType<"quick" | "advanced">,
|
||||
default: "quick",
|
||||
},
|
||||
node: {
|
||||
type: Object as PropType<FlowNode>,
|
||||
default: () => ({}),
|
||||
},
|
||||
// 任务节点列表
|
||||
taskComponents: {
|
||||
type: Object as PropType<Record<string, Component>>,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
setup(props: FlowNodeProps, { slots }) {
|
||||
const cssVars = useThemeCssVar([
|
||||
"borderColor",
|
||||
"dividerColor",
|
||||
"textColor1",
|
||||
"textColor2",
|
||||
"primaryColor",
|
||||
"primaryColorHover",
|
||||
"bodyColor",
|
||||
]);
|
||||
const { flowData, selectedNodeId, flowZoom, resetFlowData, setZoomValue } =
|
||||
useStore();
|
||||
const { initData, handleSaveConfig, handleZoom, goBack } = useController({
|
||||
type: props?.type,
|
||||
node: props?.node,
|
||||
isEdit: props?.isEdit,
|
||||
});
|
||||
|
||||
// 拖拽状态管理
|
||||
const dragState = reactive({
|
||||
isDragging: false,
|
||||
startX: 0,
|
||||
startY: 0,
|
||||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
});
|
||||
|
||||
// 画布容器引用
|
||||
const canvasRef = ref<HTMLElement | null>(null);
|
||||
|
||||
// 画布样式(通过transform实现拖拽)
|
||||
const canvasStyle = computed(() => ({
|
||||
transform: `translate(${dragState.offsetX}px, ${dragState.offsetY}px)`,
|
||||
}));
|
||||
|
||||
// 鼠标按下事件
|
||||
const handleMouseDown = (e: MouseEvent) => {
|
||||
// 只有左键点击才触发拖拽
|
||||
if (e.button !== 0) return;
|
||||
|
||||
dragState.isDragging = true;
|
||||
dragState.startX = e.clientX;
|
||||
dragState.startY = e.clientY;
|
||||
|
||||
// 添加鼠标样式反馈
|
||||
document.body.style.cursor = "grabbing";
|
||||
};
|
||||
|
||||
// 鼠标移动事件
|
||||
const handleMouseMove = (e: MouseEvent) => {
|
||||
if (!dragState.isDragging || !isComponentMounted.value) return;
|
||||
|
||||
try {
|
||||
// 计算位移差
|
||||
const deltaX = e.clientX - dragState.startX;
|
||||
const deltaY = e.clientY - dragState.startY;
|
||||
|
||||
// 更新偏移量
|
||||
dragState.offsetX += deltaX;
|
||||
dragState.offsetY += deltaY;
|
||||
|
||||
// 更新起始位置(用于连续计算)
|
||||
dragState.startX = e.clientX;
|
||||
dragState.startY = e.clientY;
|
||||
} catch (error) {
|
||||
console.warn("拖拽移动处理出错:", error);
|
||||
}
|
||||
};
|
||||
|
||||
// 鼠标抬起事件
|
||||
const handleMouseUp = () => {
|
||||
if (dragState.isDragging) {
|
||||
dragState.isDragging = false;
|
||||
document.body.style.cursor = ""; // 恢复鼠标样式
|
||||
}
|
||||
};
|
||||
|
||||
// 重置缩放和位置
|
||||
const handleReset = () => {
|
||||
try {
|
||||
// 重置缩放为100%
|
||||
setZoomValue(100);
|
||||
// 重置拖拽位置
|
||||
dragState.offsetX = 0;
|
||||
dragState.offsetY = 0;
|
||||
} catch (error) {
|
||||
console.warn("重置处理出错:", error);
|
||||
}
|
||||
};
|
||||
|
||||
// 滚轮缩放事件(带节流优化)
|
||||
let wheelTimeout: number | null = null;
|
||||
const isComponentMounted = ref(true);
|
||||
|
||||
const handleWheel = (e: WheelEvent) => {
|
||||
// 阻止默认滚动行为
|
||||
e.preventDefault();
|
||||
|
||||
// 如果组件已卸载,不处理事件
|
||||
if (!isComponentMounted.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 节流处理,避免频繁触发
|
||||
if (wheelTimeout) {
|
||||
clearTimeout(wheelTimeout);
|
||||
}
|
||||
|
||||
wheelTimeout = window.setTimeout(() => {
|
||||
// 再次检查组件是否仍然挂载
|
||||
if (!isComponentMounted.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const delta = e.deltaY > 0 ? -10 : 10; // 向下滚动缩小,向上滚动放大
|
||||
const newZoom = Math.max(50, Math.min(300, flowZoom.value + delta));
|
||||
|
||||
if (newZoom !== flowZoom.value) {
|
||||
setZoomValue(newZoom); // 直接设置缩放值
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn("滚轮缩放处理出错:", error);
|
||||
}
|
||||
}, 16);
|
||||
};
|
||||
|
||||
// 提供任务节点组件映射给后代组件使用
|
||||
provide("taskComponents", props.taskComponents);
|
||||
|
||||
onMounted(() => {
|
||||
initData();
|
||||
// 绑定全局事件
|
||||
document.addEventListener("mousemove", handleMouseMove);
|
||||
document.addEventListener("mouseup", handleMouseUp);
|
||||
// 绑定滚轮事件到画布容器
|
||||
if (canvasRef.value) {
|
||||
canvasRef.value.addEventListener("wheel", handleWheel, {
|
||||
passive: false,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
// 标记组件已卸载
|
||||
isComponentMounted.value = false;
|
||||
|
||||
resetFlowData();
|
||||
// 解绑事件,避免内存泄漏
|
||||
document.removeEventListener("mousemove", handleMouseMove);
|
||||
document.removeEventListener("mouseup", handleMouseUp);
|
||||
// 解绑滚轮事件
|
||||
if (canvasRef.value) {
|
||||
canvasRef.value.removeEventListener("wheel", handleWheel);
|
||||
}
|
||||
// 清理定时器
|
||||
if (wheelTimeout) {
|
||||
clearTimeout(wheelTimeout);
|
||||
wheelTimeout = null;
|
||||
}
|
||||
});
|
||||
return () => (
|
||||
<div class="flex flex-col w-full h-full" style={cssVars.value}>
|
||||
<div class="w-full h-[6rem] px-[2rem] mb-[2rem] rounded-lg flex items-center gap-2 justify-between">
|
||||
<div class="flex items-center">
|
||||
@@ -85,13 +226,25 @@ export default defineComponent({
|
||||
</NButton>
|
||||
</div>
|
||||
</div>
|
||||
<div class={styles.flowContainer}>
|
||||
<div
|
||||
class={styles.flowContainer}
|
||||
ref={canvasRef}
|
||||
onMousedown={handleMouseDown}
|
||||
onMouseleave={handleMouseUp}
|
||||
>
|
||||
{/* 左侧流程容器 */}
|
||||
<div class="flex min-w-0">
|
||||
{/* 流程容器*/}
|
||||
<div
|
||||
class={styles.flowProcess}
|
||||
style={{ transform: `scale(${flowZoom.value / 100})` }}
|
||||
style={{
|
||||
transform: `scale(${flowZoom.value / 100}) ${
|
||||
canvasStyle.value.transform
|
||||
}`,
|
||||
transition: dragState.isDragging
|
||||
? "none"
|
||||
: "transform 0.05s ease-out",
|
||||
}}
|
||||
>
|
||||
{/* 渲染流程节点 */}
|
||||
<NodeWrap node={flowData.value.childNode} />
|
||||
@@ -117,10 +270,19 @@ export default defineComponent({
|
||||
color="#5a5e66"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class={styles.flowZoomIcon}
|
||||
onClick={handleReset}
|
||||
title="重置视图"
|
||||
>
|
||||
<NIcon size="16" color="#5a5e66">
|
||||
<ReloadOutlined />
|
||||
</NIcon>
|
||||
</div>
|
||||
</div>
|
||||
{/* 保留原有插槽 */}
|
||||
{slots.default?.()}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
})
|
||||
},
|
||||
});
|
||||
|
||||
@@ -20,29 +20,29 @@ import { $t } from '@locales/index'
|
||||
* 用于管理流程图的状态、缩放等数据
|
||||
*/
|
||||
export const useFlowStore = defineStore('flow-store', () => {
|
||||
const flowData = ref<FlowNode>({
|
||||
id: '',
|
||||
name: '',
|
||||
childNode: {
|
||||
id: 'start-1',
|
||||
name: '开始',
|
||||
type: 'start',
|
||||
config: {
|
||||
exec_type: 'manual',
|
||||
},
|
||||
childNode: null,
|
||||
},
|
||||
}) // 流程图数据
|
||||
const flowZoom = ref(100) // 流程图缩放比例
|
||||
const advancedOptions = ref(false) // 高级选项
|
||||
const addNodeSelectList = ref<NodeSelect[]>([]) // 添加节点选项列表
|
||||
const excludeNodeSelectList = ref<NodeNum[]>([]) // 排除的节点选项列表
|
||||
const addNodeBtnRef = ref<HTMLElement | null>(null) // 添加节点按钮
|
||||
const addNodeSelectRef = ref<HTMLElement | null>(null) // 添加节点选择框
|
||||
const addNodeSelectPostion = ref<number | null>(null) // 添加节点选择框位置
|
||||
const selectedNodeId = ref<string | null>(null) // 当前选中的节点ID
|
||||
const isRefreshNode = ref<string | null>(null) // 是否刷新节点
|
||||
const startNodeSavedByUser = ref<boolean>(false); // 开始节点是否已被用户手动保存过
|
||||
const flowData = ref<FlowNode>({
|
||||
id: "",
|
||||
name: "",
|
||||
childNode: {
|
||||
id: "start-1",
|
||||
name: "开始",
|
||||
type: "start",
|
||||
config: {
|
||||
exec_type: "manual",
|
||||
},
|
||||
childNode: null,
|
||||
},
|
||||
}); // 流程图数据
|
||||
const flowZoom = ref(100); // 流程图缩放比例
|
||||
const advancedOptions = ref(false); // 高级选项
|
||||
const addNodeSelectList = ref<NodeSelect[]>([]); // 添加节点选项列表
|
||||
const excludeNodeSelectList = ref<NodeNum[]>([]); // 排除的节点选项列表
|
||||
const addNodeBtnRef = ref<HTMLElement | null>(null); // 添加节点按钮
|
||||
const addNodeSelectRef = ref<HTMLElement | null>(null); // 添加节点选择框
|
||||
const addNodeSelectPostion = ref<number | null>(null); // 添加节点选择框位置
|
||||
const selectedNodeId = ref<string | null>(null); // 当前选中的节点ID
|
||||
const isRefreshNode = ref<string | null>(null); // 是否刷新节点
|
||||
const startNodeSavedByUser = ref<boolean>(false); // 开始节点是否已被用户手动保存过
|
||||
|
||||
// 计算添加节点选项列表,排除的节点选项列表
|
||||
const nodeSelectList = computed(() => {
|
||||
@@ -606,16 +606,30 @@ export const useFlowStore = defineStore('flow-store', () => {
|
||||
/**
|
||||
* 设置流程图缩放比例
|
||||
* 控制流程图的显示大小
|
||||
* @param {number} type - 缩放类型:1 表示缩小,2 表示放大
|
||||
* @param {number} type - 缩放类型:1 表示缩小,2 表示放大,或者直接传入缩放值(0.5-3.0)
|
||||
*/
|
||||
const setflowZoom = (type: number) => {
|
||||
if (type === 1 && flowZoom.value > 50) {
|
||||
// 如果传入的是小数(0.5-3.0),直接设置缩放值
|
||||
if (type < 1) {
|
||||
const zoomValue = Math.round(type * 100);
|
||||
flowZoom.value = Math.max(50, Math.min(300, zoomValue));
|
||||
} else if (type === 1 && flowZoom.value > 50) {
|
||||
// 缩小
|
||||
flowZoom.value -= 10;
|
||||
} else if (type === 2 && flowZoom.value < 300) {
|
||||
// 放大
|
||||
flowZoom.value += 10;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 直接设置缩放值(用于滚轮缩放)
|
||||
* @param {number} zoomValue - 缩放值(50-300)
|
||||
*/
|
||||
const setZoomValue = (zoomValue: number) => {
|
||||
flowZoom.value = Math.max(50, Math.min(300, zoomValue));
|
||||
};
|
||||
|
||||
/**
|
||||
* 设置开始节点已被用户保存的状态
|
||||
* @param {boolean} saved - 是否已被保存
|
||||
@@ -632,7 +646,7 @@ export const useFlowStore = defineStore('flow-store', () => {
|
||||
startNodeSavedByUser.value = false;
|
||||
};
|
||||
|
||||
return {
|
||||
return {
|
||||
// 数据
|
||||
flowData, // 流程图数据
|
||||
flowZoom, // 流程图缩放比例
|
||||
@@ -649,6 +663,7 @@ export const useFlowStore = defineStore('flow-store', () => {
|
||||
getResultData, // 获取流程图数据
|
||||
updateFlowData, // 更新流程图数据
|
||||
setflowZoom, // 设置流程图缩放比例
|
||||
setZoomValue, // 直接设置缩放值(用于滚轮缩放)
|
||||
setStartNodeSavedByUser, // 设置开始节点已被用户保存的状态
|
||||
resetStartNodeSavedState, // 重置开始节点保存状态
|
||||
|
||||
|
||||
@@ -271,6 +271,13 @@ export const ApiProjectConfig: Record<string, ApiProjectType> = {
|
||||
hostRelated: { default: { name: "Spaceship" } },
|
||||
sort: 32,
|
||||
},
|
||||
btdomain: {
|
||||
name: "BTDomain",
|
||||
icon: "btdomain",
|
||||
type: ["dns"],
|
||||
hostRelated: { default: { name: "BTDomain" } },
|
||||
sort: 33,
|
||||
},
|
||||
plugin: {
|
||||
name: "插件",
|
||||
icon: "plugin",
|
||||
|
||||
@@ -65,6 +65,7 @@ export interface AddAccessParams<
|
||||
| ConstellixAccessConfig
|
||||
| WebhookAccessConfig
|
||||
| SpaceshipAccessConfig
|
||||
| BTDomainAccessConfig
|
||||
> {
|
||||
name: string;
|
||||
type: string;
|
||||
@@ -102,6 +103,7 @@ export interface UpdateAccessParams<
|
||||
| ConstellixAccessConfig
|
||||
| WebhookAccessConfig
|
||||
| SpaceshipAccessConfig
|
||||
| BTDomainAccessConfig
|
||||
> extends AddAccessParams<T> {
|
||||
id: string;
|
||||
}
|
||||
@@ -322,6 +324,12 @@ export interface SpaceshipAccessConfig {
|
||||
api_secret: string;
|
||||
}
|
||||
|
||||
export interface BTDomainAccessConfig {
|
||||
access_key: string;
|
||||
secret_key: string;
|
||||
account_id: string;
|
||||
}
|
||||
|
||||
/** 删除授权请求参数 */
|
||||
export interface DeleteAccessParams {
|
||||
id: string
|
||||
|
||||
@@ -55,6 +55,7 @@ import type {
|
||||
ConstellixAccessConfig,
|
||||
WebhookAccessConfig,
|
||||
SpaceshipAccessConfig,
|
||||
BTDomainAccessConfig,
|
||||
} from "@/types/access";
|
||||
import type { VNode, Ref } from "vue";
|
||||
import { testAccess, getPlugins } from "@/api/access";
|
||||
@@ -494,7 +495,7 @@ export const useApiFormController = (
|
||||
value: string,
|
||||
callback: (error?: Error) => void
|
||||
) => {
|
||||
if (!value.length) {
|
||||
if (!value || !value.length) {
|
||||
const mapTips = {
|
||||
cloudflare: $t("t_0_1747042966820"),
|
||||
btpanel: $t("t_1_1747042969705"),
|
||||
@@ -523,6 +524,25 @@ export const useApiFormController = (
|
||||
const mapTips = {
|
||||
godaddy: $t("t_1_1747984133312"),
|
||||
spaceship: "请输入 Spaceship API Secret",
|
||||
btdomain: "请输入 BTDomain Secret Key",
|
||||
};
|
||||
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: "请输入 BTDomain Account ID",
|
||||
};
|
||||
return callback(
|
||||
new Error(mapTips[param.value.type as keyof typeof mapTips])
|
||||
@@ -621,6 +641,7 @@ export const useApiFormController = (
|
||||
volcengine: $t("t_3_1747365600828"),
|
||||
qiniu: $t("t_3_1747984134586"),
|
||||
doge: $t("t_0_1750320239265"),
|
||||
btdomain: "请输入 BTDomain Access Key",
|
||||
};
|
||||
return callback(
|
||||
new Error(mapTips[param.value.type as keyof typeof mapTips])
|
||||
@@ -636,7 +657,7 @@ export const useApiFormController = (
|
||||
value: string,
|
||||
callback: (error?: Error) => void
|
||||
) => {
|
||||
if (!value.length) {
|
||||
if (!value || !value.length) {
|
||||
const mapTips = {
|
||||
tencentcloud: $t("t_2_1747042967277"),
|
||||
huawei: $t("t_3_1747042967608"),
|
||||
@@ -644,6 +665,7 @@ export const useApiFormController = (
|
||||
volcengine: $t("t_4_1747365600137"),
|
||||
doge: $t("t_1_1750320241427"),
|
||||
constellix: "请输入Secret Key",
|
||||
btdomain: "请输入 BTDomain Secret Key",
|
||||
};
|
||||
return callback(
|
||||
new Error(mapTips[param.value.type as keyof typeof mapTips])
|
||||
@@ -1230,6 +1252,21 @@ export const useApiFormController = (
|
||||
})
|
||||
);
|
||||
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 "plugin":
|
||||
items.push(
|
||||
useFormCustom(() => {
|
||||
@@ -1478,7 +1515,14 @@ export const useApiFormController = (
|
||||
api_key: "",
|
||||
api_secret: "",
|
||||
} as SpaceshipAccessConfig;
|
||||
break;
|
||||
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 || "",
|
||||
|
||||
@@ -77,7 +77,9 @@ export default defineComponent({
|
||||
const caOptions = ref<
|
||||
Array<{ label: string; value: string; icon: string }>
|
||||
>([]);
|
||||
const emailOptions = ref<string[]>([]);
|
||||
const emailOptions = ref<
|
||||
Array<{ label: string; value: string; id: number; email: string }>
|
||||
>([]);
|
||||
const isLoadingCA = ref(false);
|
||||
const isLoadingEmails = ref(false);
|
||||
const showEmailDropdown = ref(false);
|
||||
@@ -88,7 +90,7 @@ export default defineComponent({
|
||||
isLoadingCA.value = true;
|
||||
try {
|
||||
const { data } = await getEabList({
|
||||
ca: param.value.ca,
|
||||
ca: "",
|
||||
p: 1,
|
||||
limit: 1000,
|
||||
}).fetch();
|
||||
@@ -144,7 +146,14 @@ export default defineComponent({
|
||||
try {
|
||||
const { data } = await getEabList({ ca, p: 1, limit: 1000 }).fetch();
|
||||
emailOptions.value =
|
||||
data?.map((item) => item.email).filter(Boolean) || [];
|
||||
data
|
||||
?.map((item) => ({
|
||||
label: item.email,
|
||||
value: `${item.id}`, // 使用 id 作为 value 确保唯一性
|
||||
id: item.id,
|
||||
email: item.email,
|
||||
}))
|
||||
.filter((item) => item.email) || [];
|
||||
|
||||
// 检查是否为编辑模式且有外部传入的邮箱
|
||||
if (isEdit.value && routeEmail.value) {
|
||||
@@ -154,15 +163,19 @@ export default defineComponent({
|
||||
// 非编辑模式:保持原有逻辑
|
||||
if (!emailOptions.value.length) {
|
||||
param.value.email = "";
|
||||
param.value.eabId = "";
|
||||
} else {
|
||||
// 如果邮箱数组有内容,自动填充第一个邮箱地址
|
||||
// 移除 !param.value.email 条件,让切换CA时总是更新为第一个选项
|
||||
if (emailOptions.value[0]) {
|
||||
param.value.email = emailOptions.value[0].email;
|
||||
param.value.eabId = emailOptions.value[0].id.toString();
|
||||
}
|
||||
}
|
||||
// 如果邮箱数组有内容且当前邮箱为空,自动填充第一个邮箱地址
|
||||
if (
|
||||
emailOptions.value.length > 0 &&
|
||||
emailOptions.value[0] &&
|
||||
!param.value.email
|
||||
) {
|
||||
param.value.email = emailOptions.value[0];
|
||||
}
|
||||
}
|
||||
|
||||
if (example.value) {
|
||||
example.value.restoreValidation();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("加载邮件选项失败:", error);
|
||||
@@ -239,17 +252,35 @@ export default defineComponent({
|
||||
|
||||
// 创建邮箱下拉选项
|
||||
const emailDropdownOptions = computed(() => {
|
||||
return emailOptions.value.map((email) => ({
|
||||
label: email,
|
||||
key: email,
|
||||
return emailOptions.value.map((item) => ({
|
||||
label: item.email,
|
||||
key: item.email,
|
||||
}));
|
||||
});
|
||||
|
||||
// 计算输入框宽度用于下拉菜单
|
||||
const inputWidth = computed(() => {
|
||||
if (emailInputRef.value?.$el) {
|
||||
return emailInputRef.value.$el.offsetWidth;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
// 判断是否需要输入框(letsencrypt、buypass、zerossl)
|
||||
const shouldUseInputForEmail = computed(() => {
|
||||
return ["letsencrypt", "buypass", "zerossl"].includes(param.value.ca);
|
||||
});
|
||||
|
||||
// 计算当前选中的邮箱选项的 value(用于 NSelect)
|
||||
const currentEmailValue = computed(() => {
|
||||
if (!param.value.eabId) return null;
|
||||
// 优先使用 eabId 来查找匹配的选项
|
||||
const matchedOption = emailOptions.value.find(
|
||||
(item) => item.id.toString() === param.value.eabId
|
||||
);
|
||||
return matchedOption ? matchedOption.value : null;
|
||||
});
|
||||
|
||||
// 表单渲染配置
|
||||
const config = computed(() => {
|
||||
// 基本选项
|
||||
@@ -343,7 +374,20 @@ export default defineComponent({
|
||||
options={emailDropdownOptions.value}
|
||||
onSelect={handleSelectEmail}
|
||||
placement="bottom-start"
|
||||
style="width: 100%"
|
||||
menu-props={() => ({
|
||||
style: {
|
||||
width: `${inputWidth.value}px`,
|
||||
maxHeight: "40rem",
|
||||
overflowY: "auto",
|
||||
},
|
||||
})}
|
||||
node-props={(option: any) => ({
|
||||
style: {
|
||||
padding: "8px 12px",
|
||||
cursor: "pointer",
|
||||
},
|
||||
class: "hover:bg-gray-50",
|
||||
})}
|
||||
>
|
||||
<NInput
|
||||
ref={emailInputRef}
|
||||
@@ -358,16 +402,26 @@ export default defineComponent({
|
||||
</NDropdown>
|
||||
) : (
|
||||
<NSelect
|
||||
v-model:value={param.value.email}
|
||||
options={emailOptions.value.map((email) => ({
|
||||
label: email,
|
||||
value: email,
|
||||
}))}
|
||||
value={currentEmailValue.value}
|
||||
options={emailOptions.value}
|
||||
placeholder={$t("t_2_1748052862259")}
|
||||
clearable
|
||||
filterable
|
||||
loading={isLoadingEmails.value}
|
||||
class="w-full"
|
||||
onUpdateValue={(value: string) => {
|
||||
// 根据选择的 id 找到对应的邮箱地址和 eabId
|
||||
const selectedOption = emailOptions.value.find(
|
||||
(item) => item.value === value
|
||||
);
|
||||
if (selectedOption) {
|
||||
param.value.email = selectedOption.email;
|
||||
param.value.eabId = selectedOption.id.toString();
|
||||
} else {
|
||||
param.value.email = value;
|
||||
param.value.eabId = "";
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</NFormItem>
|
||||
@@ -545,6 +599,7 @@ export default defineComponent({
|
||||
} else {
|
||||
emailOptions.value = [];
|
||||
param.value.email = "";
|
||||
param.value.eabId = "";
|
||||
showEmailDropdown.value = false;
|
||||
}
|
||||
}
|
||||
@@ -564,14 +619,14 @@ export default defineComponent({
|
||||
advancedOptions.value = false;
|
||||
await loadCAOptions();
|
||||
|
||||
// 如果当前已经有CA值,主动加载对应的邮件选项
|
||||
if (param.value.ca) {
|
||||
await loadEmailOptions(param.value.ca);
|
||||
}
|
||||
|
||||
// 如果是编辑模式且有外部传入的邮箱,直接设置邮箱值
|
||||
if (isEdit.value && routeEmail.value) {
|
||||
param.value.email = routeEmail.value;
|
||||
} else {
|
||||
// 非编辑模式:如果当前已经有CA值,主动加载对应的邮件选项
|
||||
if (param.value.ca) {
|
||||
await loadEmailOptions(param.value.ca);
|
||||
}
|
||||
}
|
||||
|
||||
// 移除重复调用,让 watch 监听器处理 CA 值变化
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { defineComponent, ref, computed, watch } from 'vue';
|
||||
import { defineComponent, ref, computed, watch } from "vue";
|
||||
import {
|
||||
NForm,
|
||||
NFormItem,
|
||||
@@ -127,7 +127,7 @@ export default defineComponent({
|
||||
const selectedRootCa = rootCaList.value.find(
|
||||
(ca) => ca.id.toString() === newRootId
|
||||
);
|
||||
if (selectedRootCa) {
|
||||
if (selectedRootCa && selectedRootCa.algorithm) {
|
||||
addForm.value.algorithm = selectedRootCa.algorithm;
|
||||
if (selectedRootCa.algorithm === "ecdsa") {
|
||||
addForm.value.key_length = "256";
|
||||
@@ -269,45 +269,43 @@ export default defineComponent({
|
||||
</div>
|
||||
</NDivider>
|
||||
</div>
|
||||
{showAdvancedConfig.value && (
|
||||
<div class="space-y-4 mt-4">
|
||||
<NFormItem label="组织(O)">
|
||||
<NInput
|
||||
v-model:value={addForm.value.o}
|
||||
placeholder="请输入组织名称"
|
||||
/>
|
||||
</NFormItem>
|
||||
<div class="mt-4" v-show={showAdvancedConfig.value}>
|
||||
<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="国家(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="组织单位(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.province}
|
||||
placeholder="请输入省份"
|
||||
/>
|
||||
</NFormItem>
|
||||
|
||||
<NFormItem label="城市">
|
||||
<NInput
|
||||
v-model:value={addForm.value.locality}
|
||||
placeholder="请输入城市"
|
||||
/>
|
||||
</NFormItem>
|
||||
</div>
|
||||
)}
|
||||
<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>
|
||||
|
||||
@@ -251,8 +251,10 @@ export const useController = () => {
|
||||
if (data.value?.status === true) {
|
||||
rootCaList.value = data.value.data;
|
||||
if (createType.value === 'intermediate' && rootCaList.value.length > 0 && rootCaList.value[0]) {
|
||||
addForm.value.root_id = rootCaList.value[0].id.toString();
|
||||
}
|
||||
addForm.value.root_id = rootCaList.value[0].id.toString();
|
||||
addForm.value.algorithm = rootCaList.value[0].algorithm;
|
||||
addForm.value.key_length = rootCaList.value[0].key_length.toString();
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取根证书列表失败:', error);
|
||||
|
||||
Reference in New Issue
Block a user