【新增】私有证书

This commit is contained in:
cai
2025-09-03 15:15:59 +08:00
parent efd052a297
commit 954cd1638d
442 changed files with 76787 additions and 7483 deletions

View File

@@ -0,0 +1,96 @@
import { defineComponent, ref } from 'vue'
import { useWorkflowStore } from '../../store/workflow'
import { ApplyNodeData } from '../../types'
import configStyles from './Config.module.css'
export default defineComponent({
name: 'ApplyNodeConfig',
props: {
nodeId: {
type: String,
required: true,
},
nodeData: {
type: Object as () => ApplyNodeData,
required: true,
},
},
setup(props) {
const workflowStore = useWorkflowStore()
const applicationContent = ref(props.nodeData.applicationContent || '')
const applyStatus = ref<'idle' | 'applying' | 'success' | 'error'>('idle')
const errorMessage = ref('')
// 更新节点标签
const updateNodeLabel = (value: string) => {
workflowStore.updateNodeData(props.nodeId, { label: value })
}
// 更新申请内容
const updateApplicationContent = (value: string) => {
applicationContent.value = value
}
// 提交申请
const submitApplication = () => {
if (!applicationContent.value.trim()) {
errorMessage.value = '请输入申请内容'
return
}
// 模拟申请过程
applyStatus.value = 'applying'
errorMessage.value = ''
setTimeout(() => {
applyStatus.value = 'success'
workflowStore.updateNodeData(props.nodeId, {
applicationContent: applicationContent.value,
})
}, 1000)
}
return () => (
<div class={configStyles.configContainer}>
<div class={configStyles.configField}>
<div class={configStyles.configLabel}></div>
<input
type="text"
value={props.nodeData.label}
onInput={(e) => updateNodeLabel((e.target as HTMLInputElement).value)}
class={configStyles.configInput}
/>
</div>
<div class={configStyles.configField}>
<div class={configStyles.configLabel}></div>
<textarea
value={applicationContent.value}
onInput={(e) => updateApplicationContent((e.target as HTMLTextAreaElement).value)}
class={configStyles.configTextarea}
placeholder="请输入申请内容"
></textarea>
</div>
{errorMessage.value && <div class={configStyles.configError}>{errorMessage.value}</div>}
<div class={configStyles.configActions}>
<button
class={configStyles.configButton}
onClick={submitApplication}
disabled={applyStatus.value === 'applying'}
>
{applyStatus.value === 'applying' ? '申请中...' : '提交申请'}
</button>
{applyStatus.value === 'success' && <div class={configStyles.configSuccess}></div>}
</div>
<div class={configStyles.configInfo}>
<div class={configStyles.configInfoTitle}></div>
<div class={configStyles.configInfoContent}></div>
</div>
</div>
)
},
})

View File

@@ -0,0 +1,126 @@
.configContainer {
padding: 8px 0;
}
.configField {
margin-bottom: 16px;
}
.configLabel {
font-size: 14px;
font-weight: 500;
margin-bottom: 6px;
color: #374151;
}
.configInput {
width: 100%;
padding: 8px 12px;
border: 1px solid #e5e7eb;
border-radius: 4px;
font-size: 14px;
}
.configInput:focus {
outline: none;
border-color: #60a5fa;
box-shadow: 0 0 0 2px rgba(96, 165, 250, 0.2);
}
.configFileInput {
width: 100%;
padding: 8px 0;
font-size: 14px;
}
.configTextarea {
width: 100%;
padding: 8px 12px;
border: 1px solid #e5e7eb;
border-radius: 4px;
font-size: 14px;
resize: vertical;
min-height: 100px;
}
.configTextarea:focus {
outline: none;
border-color: #60a5fa;
box-shadow: 0 0 0 2px rgba(96, 165, 250, 0.2);
}
.configSelect {
width: 100%;
padding: 8px 12px;
border: 1px solid #e5e7eb;
border-radius: 4px;
font-size: 14px;
background-color: white;
}
.configSelect:focus {
outline: none;
border-color: #60a5fa;
box-shadow: 0 0 0 2px rgba(96, 165, 250, 0.2);
}
.configInfo {
margin-top: 20px;
background-color: #f3f4f6;
padding: 12px;
border-radius: 4px;
}
.configInfoTitle {
font-size: 14px;
font-weight: 500;
margin-bottom: 8px;
color: #4b5563;
}
.configInfoContent {
font-size: 13px;
color: #6b7280;
line-height: 1.5;
}
.configError {
margin-bottom: 16px;
padding: 8px;
background-color: #fee2e2;
border-radius: 4px;
color: #b91c1c;
font-size: 14px;
}
.configSuccess {
margin-left: 12px;
color: #047857;
font-size: 14px;
}
.configActions {
display: flex;
align-items: center;
margin-bottom: 16px;
}
.configButton {
padding: 8px 16px;
background-color: #2563eb;
color: white;
border: none;
border-radius: 4px;
font-weight: 500;
cursor: pointer;
transition: background-color 0.2s;
}
.configButton:hover {
background-color: #1d4ed8;
}
.configButton:disabled {
background-color: #93c5fd;
cursor: not-allowed;
}

View File

@@ -0,0 +1,98 @@
import { defineComponent, ref } from 'vue'
import { useWorkflowStore } from '../../store/workflow'
import { DeployNodeData } from '../../types'
import configStyles from './Config.module.css'
export default defineComponent({
name: 'DeployNodeConfig',
props: {
nodeId: {
type: String,
required: true,
},
nodeData: {
type: Object as () => DeployNodeData,
required: true,
},
},
setup(props) {
const workflowStore = useWorkflowStore()
const certificateContent = ref(props.nodeData.certificateContent || '')
const deployStatus = ref<'idle' | 'deploying' | 'success' | 'error'>('idle')
const errorMessage = ref('')
// 更新节点标签
const updateNodeLabel = (value: string) => {
workflowStore.updateNodeData(props.nodeId, { label: value })
}
// 更新证书内容
const updateCertificateContent = (value: string) => {
certificateContent.value = value
}
// 部署证书
const deployCertificate = () => {
if (!certificateContent.value.trim()) {
errorMessage.value = '请输入证书内容'
return
}
// 模拟部署过程
deployStatus.value = 'deploying'
errorMessage.value = ''
setTimeout(() => {
deployStatus.value = 'success'
workflowStore.updateNodeData(props.nodeId, {
certificateContent: certificateContent.value,
})
}, 1500)
}
return () => (
<div class={configStyles.configContainer}>
<div class={configStyles.configField}>
<div class={configStyles.configLabel}></div>
<input
type="text"
value={props.nodeData.label}
onInput={(e) => updateNodeLabel((e.target as HTMLInputElement).value)}
class={configStyles.configInput}
/>
</div>
<div class={configStyles.configField}>
<div class={configStyles.configLabel}></div>
<textarea
value={certificateContent.value}
onInput={(e) => updateCertificateContent((e.target as HTMLTextAreaElement).value)}
class={configStyles.configTextarea}
placeholder="请输入要部署的证书内容"
></textarea>
</div>
{errorMessage.value && <div class={configStyles.configError}>{errorMessage.value}</div>}
<div class={configStyles.configActions}>
<button
class={configStyles.configButton}
onClick={deployCertificate}
disabled={deployStatus.value === 'deploying'}
>
{deployStatus.value === 'deploying' ? '部署中...' : '部署证书'}
</button>
{deployStatus.value === 'success' && <div class={configStyles.configSuccess}></div>}
</div>
<div class={configStyles.configInfo}>
<div class={configStyles.configInfoTitle}></div>
<div class={configStyles.configInfoContent}>
</div>
</div>
</div>
)
},
})

View File

@@ -0,0 +1,45 @@
import { defineComponent } from 'vue'
import { useWorkflowStore } from '../../store/workflow'
import { EndNodeData } from '../../types'
import configStyles from './Config.module.css'
export default defineComponent({
name: 'EndNodeConfig',
props: {
nodeId: {
type: String,
required: true,
},
nodeData: {
type: Object as () => EndNodeData,
required: true,
},
},
setup(props) {
const workflowStore = useWorkflowStore()
const updateNodeLabel = (value: string) => {
workflowStore.updateNodeData(props.nodeId, { label: value })
}
return () => (
<div class={configStyles.configContainer}>
<div class={configStyles.configField}>
<div class={configStyles.configLabel}></div>
<input
type="text"
value={props.nodeData.label}
onInput={(e) => updateNodeLabel((e.target as HTMLInputElement).value)}
class={configStyles.configInput}
/>
</div>
<div class={configStyles.configInfo}>
<div class={configStyles.configInfoTitle}></div>
<div class={configStyles.configInfoContent}>
</div>
</div>
</div>
)
},
})

View File

@@ -0,0 +1,82 @@
import { defineComponent, ref } from 'vue'
import { useWorkflowStore } from '../../store/workflow'
import { NormalNodeData } from '../../types'
import configStyles from './Config.module.css'
export default defineComponent({
name: 'NormalNodeConfig',
props: {
nodeId: {
type: String,
required: true,
},
nodeData: {
type: Object as () => NormalNodeData,
required: true,
},
},
setup(props) {
const workflowStore = useWorkflowStore()
const message = ref(props.nodeData.message || '')
const status = ref(props.nodeData.status || 'info')
// 更新节点标签
const updateNodeLabel = (value: string) => {
workflowStore.updateNodeData(props.nodeId, { label: value })
}
// 更新消息内容
const updateMessage = (value: string) => {
message.value = value
workflowStore.updateNodeData(props.nodeId, { message: value })
}
// 更新状态
const updateStatus = (value: string) => {
status.value = value as 'success' | 'error' | 'info'
workflowStore.updateNodeData(props.nodeId, { status: value as 'success' | 'error' | 'info' })
}
return () => (
<div class={configStyles.configContainer}>
<div class={configStyles.configField}>
<div class={configStyles.configLabel}></div>
<input
type="text"
value={props.nodeData.label}
onInput={(e) => updateNodeLabel((e.target as HTMLInputElement).value)}
class={configStyles.configInput}
/>
</div>
<div class={configStyles.configField}>
<div class={configStyles.configLabel}></div>
<select
value={status.value}
onChange={(e) => updateStatus((e.target as HTMLSelectElement).value)}
class={configStyles.configSelect}
>
<option value="info"></option>
<option value="success"></option>
<option value="error"></option>
</select>
</div>
<div class={configStyles.configField}>
<div class={configStyles.configLabel}></div>
<textarea
value={message.value}
onInput={(e) => updateMessage((e.target as HTMLTextAreaElement).value)}
class={configStyles.configTextarea}
placeholder="请输入消息内容"
></textarea>
</div>
<div class={configStyles.configInfo}>
<div class={configStyles.configInfoTitle}></div>
<div class={configStyles.configInfoContent}>/</div>
</div>
</div>
)
},
})

View File

@@ -0,0 +1,85 @@
import { defineComponent, ref } from 'vue'
import { useWorkflowStore } from '../../store/workflow'
import { NotifyNodeData } from '../../types'
import configStyles from './Config.module.css'
export default defineComponent({
name: 'NotifyNodeConfig',
props: {
nodeId: {
type: String,
required: true,
},
nodeData: {
type: Object as () => NotifyNodeData,
required: true,
},
},
setup(props) {
const workflowStore = useWorkflowStore()
const message = ref(props.nodeData.message || '')
const notifyType = ref(props.nodeData.notifyType || 'email')
// 更新节点标签
const updateNodeLabel = (value: string) => {
workflowStore.updateNodeData(props.nodeId, { label: value })
}
// 更新通知消息
const updateMessage = (value: string) => {
message.value = value
workflowStore.updateNodeData(props.nodeId, { message: value })
}
// 更新通知类型
const updateNotifyType = (value: string) => {
notifyType.value = value
workflowStore.updateNodeData(props.nodeId, { notifyType: value })
}
return () => (
<div class={configStyles.configContainer}>
<div class={configStyles.configField}>
<div class={configStyles.configLabel}></div>
<input
type="text"
value={props.nodeData.label}
onInput={(e) => updateNodeLabel((e.target as HTMLInputElement).value)}
class={configStyles.configInput}
/>
</div>
<div class={configStyles.configField}>
<div class={configStyles.configLabel}></div>
<select
value={notifyType.value}
onChange={(e) => updateNotifyType((e.target as HTMLSelectElement).value)}
class={configStyles.configSelect}
>
<option value="email"></option>
<option value="sms"></option>
<option value="wechat"></option>
<option value="dingding"></option>
</select>
</div>
<div class={configStyles.configField}>
<div class={configStyles.configLabel}></div>
<textarea
value={message.value}
onInput={(e) => updateMessage((e.target as HTMLTextAreaElement).value)}
class={configStyles.configTextarea}
placeholder="请输入通知内容"
></textarea>
</div>
<div class={configStyles.configInfo}>
<div class={configStyles.configInfoTitle}></div>
<div class={configStyles.configInfoContent}>
</div>
</div>
</div>
)
},
})

View File

@@ -0,0 +1,45 @@
import { defineComponent } from 'vue'
import { useWorkflowStore } from '../../store/workflow'
import { StartNodeData } from '../../types'
import configStyles from './Config.module.css'
export default defineComponent({
name: 'StartNodeConfig',
props: {
nodeId: {
type: String,
required: true,
},
nodeData: {
type: Object as () => StartNodeData,
required: true,
},
},
setup(props) {
const workflowStore = useWorkflowStore()
const updateNodeLabel = (value: string) => {
workflowStore.updateNodeData(props.nodeId, { label: value })
}
return () => (
<div class={configStyles.configContainer}>
<div class={configStyles.configField}>
<div class={configStyles.configLabel}></div>
<input
type="text"
value={props.nodeData.label}
onInput={(e) => updateNodeLabel((e.target as HTMLInputElement).value)}
class={configStyles.configInput}
/>
</div>
<div class={configStyles.configInfo}>
<div class={configStyles.configInfoTitle}></div>
<div class={configStyles.configInfoContent}>
</div>
</div>
</div>
)
},
})

View File

@@ -0,0 +1,123 @@
import { defineComponent, ref } from 'vue'
import { useWorkflowStore } from '../../store/workflow'
import { UploadNodeData } from '../../types'
import configStyles from './Config.module.css'
export default defineComponent({
name: 'UploadNodeConfig',
props: {
nodeId: {
type: String,
required: true,
},
nodeData: {
type: Object as () => UploadNodeData,
required: true,
},
},
setup(props) {
const workflowStore = useWorkflowStore()
const certificateContent = ref(props.nodeData.certificateContent || '')
const uploadStatus = ref<'idle' | 'uploading' | 'success' | 'error'>('idle')
const errorMessage = ref('')
// 更新节点标签
const updateNodeLabel = (value: string) => {
workflowStore.updateNodeData(props.nodeId, { label: value })
}
// 更新证书内容
const updateCertificateContent = (value: string) => {
certificateContent.value = value
}
// 上传证书
const uploadCertificate = () => {
if (!certificateContent.value.trim()) {
errorMessage.value = '请输入证书内容'
return
}
// 模拟上传过程
uploadStatus.value = 'uploading'
errorMessage.value = ''
setTimeout(() => {
uploadStatus.value = 'success'
workflowStore.updateNodeData(props.nodeId, {
certificateContent: certificateContent.value,
})
}, 1000)
}
// 处理文件上传
const handleFileUpload = (event: Event) => {
const fileInput = event.target as HTMLInputElement
const file = fileInput.files?.[0]
if (file) {
const reader = new FileReader()
reader.onload = (e) => {
const content = e.target?.result as string
certificateContent.value = content
}
reader.readAsText(file)
}
}
return () => (
<div class={configStyles.configContainer}>
<div class={configStyles.configField}>
<div class={configStyles.configLabel}></div>
<input
type="text"
value={props.nodeData.label}
onInput={(e) => updateNodeLabel((e.target as HTMLInputElement).value)}
class={configStyles.configInput}
/>
</div>
<div class={configStyles.configField}>
<div class={configStyles.configLabel}></div>
<input
type="file"
class={configStyles.configFileInput}
onChange={handleFileUpload}
accept=".pem,.crt,.key,.cert"
/>
</div>
<div class={configStyles.configField}>
<div class={configStyles.configLabel}></div>
<textarea
value={certificateContent.value}
onInput={(e) => updateCertificateContent((e.target as HTMLTextAreaElement).value)}
class={configStyles.configTextarea}
placeholder="请粘贴证书内容"
></textarea>
</div>
{errorMessage.value && <div class={configStyles.configError}>{errorMessage.value}</div>}
<div class={configStyles.configActions}>
<button
class={configStyles.configButton}
onClick={uploadCertificate}
disabled={uploadStatus.value === 'uploading'}
>
{uploadStatus.value === 'uploading' ? '上传中...' : '上传证书'}
</button>
{uploadStatus.value === 'success' && <div class={configStyles.configSuccess}></div>}
</div>
<div class={configStyles.configInfo}>
<div class={configStyles.configInfoTitle}></div>
<div class={configStyles.configInfoContent}>
</div>
</div>
</div>
)
},
})