【新增】在翻译文件中添加多吉云AccessKey和SecretKey的相关翻译,更新阿里云ESA的配置接口,优化CA管理功能,增强表单验证逻辑,提升用户体验。

This commit is contained in:
chudong
2025-06-20 10:07:56 +08:00
parent cc5b8aada4
commit eb6172436c
107 changed files with 1008 additions and 406 deletions

View File

@@ -15,7 +15,7 @@ import BaseComponent from '@components/BaseLayout'
export default defineComponent({
name: 'AuthApiManage',
setup() {
const { TableComponent, PageComponent, param, fetch, total, openAddForm } = useController()
const { TableComponent, PageComponent, SearchComponent, openAddForm } = useController()
const cssVar = useThemeCssVar(['contentPadding', 'borderColor', 'headerHeight', 'iconColorHover'])
return () => (
@@ -28,26 +28,7 @@ export default defineComponent({
{$t('t_0_1745289355714')}
</NButton>
),
headerRight: () => (
<NInput
v-model:value={param.value.search}
onKeydown={(e: KeyboardEvent) => {
if (e.key === 'Enter') fetch()
}}
onClear={() => useTimeoutFn(() => fetch(), 100)}
placeholder={$t('t_0_1745289808449')}
clearable
size="large"
class="min-w-[300px]"
v-slots={{
suffix: () => (
<div class="flex items-center cursor-pointer" onClick={fetch}>
<Search class="text-[var(--text-color-3)] w-[1.6rem] font-bold" />
</div>
),
}}
/>
),
headerRight: () => <SearchComponent placeholder={$t('t_0_1745289808449')} />,
content: () => (
<div class="rounded-lg">
<TableComponent
@@ -60,15 +41,7 @@ export default defineComponent({
),
footerRight: () => (
<div class="mt-4 flex justify-end">
<PageComponent
v-slots={{
prefix: () => (
<span>
{$t('t_15_1745227839354')} {total.value} {$t('t_16_1745227838930')}
</span>
),
}}
/>
<PageComponent />
</div>
),
}}

View File

@@ -20,6 +20,7 @@ import {
useModal,
useDialog,
useTable,
useSearch,
useModalHooks,
useFormHooks,
useForm,
@@ -64,11 +65,11 @@ import { JSX } from 'vue/jsx-runtime'
*/
interface AuthApiManageControllerExposes {
loading: Ref<boolean>
fetch: () => Promise<void>
fetch: (resetPage?: boolean) => Promise<void>
TableComponent: (props: Record<string, unknown>, context: Record<string, unknown>) => VNode
PageComponent: (props: Record<string, unknown>, context: Record<string, unknown>) => VNode
SearchComponent: (props: Record<string, unknown>, context: Record<string, unknown>) => VNode
param: Ref<AccessListParams>
total: Ref<number>
openAddForm: () => void
}
@@ -183,7 +184,7 @@ export const useController = (): AuthApiManageControllerExposes => {
]
// 表格实例
const { TableComponent, PageComponent, loading, param, total, fetch } = useTable<AccessItem, AccessListParams>({
const { TableComponent, PageComponent, loading, param, fetch } = useTable<AccessItem, AccessListParams>({
config: createColumns(),
request: fetchAccessList,
watchValue: ['p', 'limit'],
@@ -196,6 +197,14 @@ export const useController = (): AuthApiManageControllerExposes => {
},
})
// 搜索实例
const { SearchComponent, search } = useSearch({
onSearch: (value) => {
param.value.search = value
fetch()
},
})
/**
* 打开添加授权API弹窗
*/
@@ -255,8 +264,8 @@ export const useController = (): AuthApiManageControllerExposes => {
fetch,
TableComponent,
PageComponent,
SearchComponent,
param,
total,
openAddForm,
}
}
@@ -403,32 +412,32 @@ export const useApiFormController = (props: ApiFormControllerProps): ApiFormCont
message: $t('t_0_1747617113090'),
trigger: 'input',
},
access_key_id: {
trigger: 'input',
validator: (rule: FormItemRule, value: string, callback: (error?: Error) => void) => {
if (!value) {
const mapTips = {
aliyun: $t('t_4_1745317314054'),
doge: '请输入多吉云AccessKeyId',
}
return callback(new Error(mapTips[param.value.type as keyof typeof mapTips] || $t('t_4_1745317314054')))
}
callback()
},
},
access_key_secret: {
trigger: 'input',
validator: (rule: FormItemRule, value: string, callback: (error?: Error) => void) => {
if (!value) {
const mapTips = {
aliyun: $t('t_5_1745317315285'),
doge: '请输入多吉云AccessKeySecret',
}
return callback(new Error(mapTips[param.value.type as keyof typeof mapTips] || $t('t_5_1745317315285')))
}
callback()
},
},
// access_key_id: {
// trigger: 'input',
// validator: (rule: FormItemRule, value: string, callback: (error?: Error) => void) => {
// if (!value) {
// const mapTips = {
// aliyun: $t('t_4_1745317314054'),
// doge: '请输入多吉云AccessKeyId',
// }
// return callback(new Error(mapTips[param.value.type as keyof typeof mapTips] || $t('t_4_1745317314054')))
// }
// callback()
// },
// },
// access_key_secret: {
// trigger: 'input',
// validator: (rule: FormItemRule, value: string, callback: (error?: Error) => void) => {
// if (!value) {
// const mapTips = {
// aliyun: $t('t_5_1745317315285'),
// doge: '请输入多吉云AccessKeySecret',
// }
// return callback(new Error(mapTips[param.value.type as keyof typeof mapTips] || $t('t_5_1745317315285')))
// }
// callback()
// },
// },
secret_access_key: {
required: true,
message: '请输入Secret Access Key',
@@ -478,6 +487,7 @@ export const useApiFormController = (props: ApiFormControllerProps): ApiFormCont
baidu: $t('t_3_1747271294475'),
volcengine: $t('t_3_1747365600828'),
qiniu: $t('t_3_1747984134586'),
doge: $t('t_0_1750320239265'),
}
return callback(new Error(mapTips[param.value.type as keyof typeof mapTips]))
}
@@ -493,6 +503,7 @@ export const useApiFormController = (props: ApiFormControllerProps): ApiFormCont
huawei: $t('t_3_1747042967608'),
baidu: $t('t_4_1747271294621'),
volcengine: $t('t_4_1747365600137'),
doge: $t('t_1_1750320241427'),
}
return callback(new Error(mapTips[param.value.type as keyof typeof mapTips]))
}
@@ -632,6 +643,7 @@ export const useApiFormController = (props: ApiFormControllerProps): ApiFormCont
case 'huaweicloud':
case 'baidu':
case 'volcengine':
case 'doge':
items.push(
useFormInput('AccessKey', 'config.access_key', { allowInput: noSideSpace }),
useFormInput('SecretKey', 'config.secret_key', { allowInput: noSideSpace }),
@@ -711,12 +723,6 @@ export const useApiFormController = (props: ApiFormControllerProps): ApiFormCont
useFormInput('Secret Access Key', 'config.secret_access_key', { allowInput: noSideSpace }),
)
break
case 'doge':
items.push(
useFormInput('AccessKeyId', 'config.access_key_id', { allowInput: noSideSpace }),
useFormInput('AccessKeySecret', 'config.access_key_secret', { allowInput: noSideSpace }),
)
break
case 'plugin':
items.push(
useFormCustom(() => {
@@ -743,39 +749,39 @@ export const useApiFormController = (props: ApiFormControllerProps): ApiFormCont
</NFormItem>
)
}),
useFormCustom(() => {
const pluginConfig = param.value.config as PluginAccessConfig
const getConfigValue = () => {
return typeof pluginConfig.config === 'string'
? pluginConfig.config
: JSON.stringify(pluginConfig.config, null, 2)
}
return (
<NFormItem
path="config.params"
v-slots={{
label: () => (
<div>
<NText></NText>
<NTooltip
v-slots={{
trigger: () => (
<span class="inline-flex ml-2 -mt-1 cursor-pointer text-base rounded-full w-[14px] h-[14px] justify-center items-center text-orange-600 border border-orange-600">
?
</span>
),
}}
>
{pluginActionTips.value}
</NTooltip>
</div>
),
}}
>
<NInput type="textarea" value={getConfigValue()} placeholder={pluginActionTips.value} rows={4} />
</NFormItem>
)
}),
useFormCustom(() => {
const pluginConfig = param.value.config as PluginAccessConfig
const getConfigValue = () => {
return typeof pluginConfig.config === 'string'
? pluginConfig.config
: JSON.stringify(pluginConfig.config, null, 2)
}
return (
<NFormItem
path="config.params"
v-slots={{
label: () => (
<div>
<NText></NText>
<NTooltip
v-slots={{
trigger: () => (
<span class="inline-flex ml-2 -mt-1 cursor-pointer text-base rounded-full w-[14px] h-[14px] justify-center items-center text-orange-600 border border-orange-600">
?
</span>
),
}}
>
{pluginActionTips.value}
</NTooltip>
</div>
),
}}
>
<NInput type="textarea" value={getConfigValue()} placeholder={pluginActionTips.value} rows={4} />
</NFormItem>
)
}),
)
break
default:
@@ -904,8 +910,8 @@ export const useApiFormController = (props: ApiFormControllerProps): ApiFormCont
break
case 'doge':
param.value.config = {
access_key_id: '',
access_key_secret: '',
access_key: '',
secret_key: '',
} as DogeAccessConfig
break
case 'plugin':

View File

@@ -189,6 +189,13 @@ export function createNodeFormConfig() {
]
},
/**
* 创建阿里云ESA相关字段
*/
aliyunEsaDeploy() {
return [this.input('站点ID', 'site_id', { placeholder: '请输入ESA站点ID' })]
},
/**
* 创建跳过选项字段
* @param valueRef 值引用

View File

@@ -1,13 +1,29 @@
import { NFormItem, NInputNumber, NSwitch } from 'naive-ui'
import {
NFormItem,
NInputNumber,
NSwitch,
NSelect,
NAutoComplete,
NInput,
NFlex,
NText,
NButton,
NGrid,
NFormItemGi,
NSpin,
NDropdown,
} from 'naive-ui'
import { useForm, useFormHooks, useModalHooks } from '@baota/naive-ui/hooks'
import { useStore } from '@components/FlowChart/useStore'
import { $t } from '@locales/index'
import rules from './verify'
import DnsProviderSelect from '@components/DnsProviderSelect'
import CAProviderSelect from '@components/CAProviderSelect'
import type { ApplyNodeConfig } from '@components/FlowChart/types'
import { deepClone } from '@baota/utils/data'
import { noSideSpace } from '@lib/utils'
import { getEabList } from '@api/access'
import SvgIcon from '@components/SvgIcon'
import { CACertificateAuthorization } from '@config/data'
export default defineComponent({
name: 'ApplyNodeDrawer',
@@ -45,6 +61,150 @@ export default defineComponent({
// 表单参数
const param = ref(deepClone(props.node.config))
// 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<any>(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 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)
// 查找预定义配置中对应的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
}
}
// 加载邮件选项
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 (!emailOptions.value.length) {
param.value.email = ''
}
// 如果邮箱数组有内容且当前邮箱为空,自动填充第一个邮箱地址
if (emailOptions.value.length > 0 && emailOptions.value[0]) {
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(value)
}
// 跳转到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 renderSingleSelectTag = ({ option }: { 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 handleEmailFocus = () => {
if (emailOptions.value.length > 0) {
showEmailDropdown.value = true
}
}
// 处理邮箱输入框失焦
const handleEmailBlur = () => {
// 延迟关闭下拉,确保点击选项有时间触发
setTimeout(() => {
showEmailDropdown.value = false
}, 200)
}
// 选择邮箱地址
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,
}))
})
// 判断是否需要输入框letsencrypt、buypass、zerossl
const shouldUseInputForEmail = computed(() => {
return ['letsencrypt', 'buypass', 'zerossl'].includes(param.value.ca)
})
// 表单渲染配置
const config = computed(() => {
// 基本选项
@@ -80,27 +240,80 @@ export default defineComponent({
type: 'custom' as const,
render: () => {
return (
<CAProviderSelect
path="eabId"
value={param.value.eabId}
email={param.value.email}
ca={param.value.ca}
{...{
'onUpdate:value': (val: { value: string; ca: string; email: string }) => {
param.value.eabId = val.value
param.value.ca = val.ca
// 始终更新邮件,确保 Let's Encrypt 和 Buypass 的邮件能正确显示
param.value.email = val.email
},
}}
/>
<NSpin show={isLoadingCA.value}>
<NGrid cols={24}>
<NFormItemGi span={13} label={$t('证书CA')} 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('添加CA授权')}
</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>
)
},
},
useFormInput($t('t_68_1745289354676'), 'email', {
placeholder: $t('t_2_1748052862259'),
allowInput: noSideSpace,
}),
{
type: 'custom' as const,
@@ -232,8 +445,37 @@ export default defineComponent({
// 创建表单实例
const { component: Form, data, example } = useForm<ApplyNodeConfig>({ defaultValue: param, config, rules })
onMounted(() => {
// 监听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
}
},
)
onMounted(async () => {
advancedOptions.value = false
await loadCAOptions()
// 如果初始化时已有CA值加载对应的邮箱选项
if (param.value.ca) {
await loadEmailOptions(param.value.ca)
}
})
// 确认事件触发

View File

@@ -225,6 +225,9 @@ export default defineComponent({
case 'aliyun-oss':
config.push(...formConfig.storageDeploy())
break
case 'aliyun-esa':
config.push(...formConfig.aliyunEsaDeploy())
break
case 'plugin':
// 插件部署配置
config.push(

View File

@@ -1,7 +1,7 @@
import { FormRules } from 'naive-ui'
import { $t } from '@locales/index'
import { createNodeValidator } from '@workflowView/lib/NodeValidator'
import { isDomain } from '@baota/utils/business'
import { isDomain, isWildcardDomain } from '@baota/utils/business'
// 创建部署节点验证器
const validator = createNodeValidator($t('t_11_1747817612051'))
@@ -51,7 +51,7 @@ export default {
if (!value) {
return new Error($t('t_0_1744958839535'))
}
if (!isDomain(value)) {
if (!isWildcardDomain(value)) {
return new Error($t('t_0_1744958839535'))
}
return true

View File

@@ -11,6 +11,7 @@ import verify from './verify'
import { NotifyNodeConfig } from '@components/FlowChart/types'
import { deepClone } from '@baota/utils/data'
import { noSideSpace } from '@lib/utils'
import { NSwitch, NFormItem, NText } from 'naive-ui'
export default defineComponent({
name: 'NotifyNodeDrawer',
@@ -25,6 +26,7 @@ export default defineComponent({
provider_id: '',
subject: '',
body: '',
skip: false,
},
}),
},
@@ -36,6 +38,14 @@ export default defineComponent({
const { handleError } = useError()
const param = ref(deepClone(props.node.config))
// 创建一个用于开关的响应式值(发送通知的状态)
const shouldSendNotify = computed({
get: () => !param.value.skip,
set: (value: boolean) => {
param.value.skip = !value
},
})
// 表单渲染配置
const formConfig: FormConfig = [
useFormInput($t('t_0_1745920566646'), 'subject', {
@@ -58,6 +68,21 @@ export default defineComponent({
}}
/>
)),
useFormCustom(() => (
<NFormItem label={$t('t_2_1750320237611')} path="skip">
<NText></NText>
<NSwitch
v-model:value={shouldSendNotify.value}
checkedValue={true}
uncheckedValue={false}
class="mx-[.5rem]"
v-slots={{
checked: () => $t('t_3_1750320237991'),
unchecked: () => $t('t_11_1747280809178'),
}}
/>
</NFormItem>
)),
]
// 创建表单实例

View File

@@ -11,6 +11,7 @@ import type { FormConfig } from '@baota/naive-ui/types/form'
import type { VNode } from 'vue'
import { useError } from '@baota/hooks/error'
import { deepClone } from '@baota/utils/data'
import { isUndefined } from '@baota/utils/type'
export default defineComponent({
name: 'StartNodeDrawer',
@@ -27,7 +28,7 @@ export default defineComponent({
},
},
setup(props) {
const { updateNodeConfig, isRefreshNode } = useStore()
const { updateNodeConfig, isRefreshNode, flowData } = useStore()
// 弹窗辅助
const { confirm } = useModalHooks()
// 错误处理
@@ -90,7 +91,17 @@ export default defineComponent({
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} />
<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' && (
@@ -99,9 +110,7 @@ export default defineComponent({
<NSelect
value={param.value.week}
onUpdateValue={(val: number) => {
if (typeof val === 'number') {
param.value.week = val
}
param.value.week = val
}}
options={weekOptions}
/>
@@ -161,19 +170,24 @@ export default defineComponent({
// 更新参数的函数
const updateParamValue = (updates: StartNodeConfig) => {
console.log(updates)
let newParams = { ...updates }
if (newParams.exec_type === 'manual') {
// 小时随机 1-6
const randomHour = Math.floor(Math.random() * 6) + 1
// 分钟每5分钟随机0-55
const randomMinute = Math.floor(Math.random() * 12) * 5
newParams = {
...newParams,
hour: randomHour,
minute: randomMinute,
}
param.value = newParams
// 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)
}
// 监听执行类型变化
@@ -210,6 +224,13 @@ export default defineComponent({
}
})
onMounted(() => {
if (isUndefined(flowData.value.id)) {
updateParamValueByType('day')
updateNodeConfig(props.node.id, param.value) // 更新节点配置
}
})
return () => (
<div class="apply-node-drawer">
<Form labelPlacement="top" />

View File

@@ -18,7 +18,7 @@ export default defineComponent({
},
},
setup(props) {
const { TableComponent, PageComponent, handleOpenAddForm, total } = useCAManageController(props)
const { TableComponent, PageComponent, handleOpenAddForm } = useCAManageController(props)
return () => (
<BaseComponent
v-slots={{
@@ -40,15 +40,7 @@ export default defineComponent({
),
footerRight: () => (
<div class="flex justify-end mt-4">
<PageComponent
v-slots={{
prefix: () => (
<span>
{$t('t_15_1745227839354')} {total.value} {$t('t_16_1745227838930')}
</span>
),
}}
/>
<PageComponent />
</div>
),
}}

View File

@@ -18,15 +18,14 @@ export default defineComponent({
const {
TableComponent,
PageComponent,
SearchComponent,
isDetectionAddWorkflow,
isDetectionOpenCAManage,
isDetectionOpenAddCAForm,
handleAddWorkflow,
handleOpenCAManage,
hasChildRoutes,
param,
fetch,
data,
} = useController()
const router = useRouter()
// 获取主题变量
@@ -65,26 +64,7 @@ export default defineComponent({
</NButton>
</NSpace>
),
headerRight: () => (
<NInput
v-model:value={param.value.search}
onKeydown={(e: KeyboardEvent) => {
if (e.key === 'Enter') fetch()
}}
onClear={() => useTimeoutFn(fetch, 100)}
placeholder={$t('t_1_1745227838776')}
clearable
size="large"
class="min-w-[300px]"
v-slots={{
suffix: () => (
<div class="flex items-center" onClick={fetch}>
<Search class="text-[var(--text-color-3)] w-[1.6rem] cursor-pointer font-bold" />
</div>
),
}}
></NInput>
),
headerRight: () => <SearchComponent placeholder={$t('t_1_1745227838776')} />,
content: () => (
<div class="rounded-lg ">
<TableComponent
@@ -99,11 +79,7 @@ export default defineComponent({
),
footerRight: () => (
<div class="mt-4 flex justify-end">
<PageComponent
v-slots={{
prefix: () => <span>{$t('t_0_1746773350551', [data.value.total])}</span>,
}}
/>
<PageComponent />
</div>
),
}}

View File

@@ -1,7 +1,16 @@
import { NSwitch, NTag, NButton, NSpace, NFlex, NText, NFormItem, NSelect } from 'naive-ui'
import { useRoute, useRouter } from 'vue-router'
import { useStore } from '@autoDeploy/useStore'
import { useDialog, useTable, useModal, useFormHooks, useForm, useModalHooks, useMessage } from '@baota/naive-ui/hooks'
import {
useDialog,
useTable,
useModal,
useFormHooks,
useForm,
useModalHooks,
useMessage,
useSearch,
} from '@baota/naive-ui/hooks'
import { useStore as useWorkflowViewStore } from '@autoDeploy/children/workflowView/useStore'
import { useError } from '@baota/hooks/error'
import AddWorkflowModal from './components/WorkflowModal'
@@ -152,7 +161,7 @@ export const useController = () => {
]
// 表格实例
const { TableComponent, PageComponent, loading, param, data, fetch } = useTable<WorkflowItem, WorkflowListParams>({
const { TableComponent, PageComponent, loading, param, fetch } = useTable<WorkflowItem, WorkflowListParams>({
config: createColumns(),
request: fetchWorkflowList,
storage: 'autoDeployPageSize',
@@ -161,6 +170,14 @@ export const useController = () => {
watchValue: ['p', 'limit'],
})
// 搜索实例
const { SearchComponent } = useSearch({
onSearch: (value) => {
param.value.search = value
fetch()
},
})
// 节流渲染
const throttleFn = useThrottleFn(() => {
setTimeout(() => {
@@ -229,7 +246,7 @@ export const useController = () => {
title: exec_type === 'manual' ? $t('t_2_1745457488661') : $t('t_3_1745457486983'),
content: exec_type === 'manual' ? $t('t_4_1745457497303') : $t('t_5_1745457494695'),
onPositiveClick: () => setWorkflowExecType({ id, exec_type }),
onNegativeClick: fetch,
onNegativeClick: () => fetch(),
onClose: fetch,
})
}
@@ -243,8 +260,8 @@ export const useController = () => {
title: !active ? $t('t_6_1745457487560') : $t('t_7_1745457487185'),
content: !active ? $t('t_8_1745457496621') : $t('t_9_1745457500045'),
onPositiveClick: () => setWorkflowActive({ id, active }),
onNegativeClick: fetch,
onClose: fetch,
onNegativeClick: () => fetch(),
onClose: () => fetch(),
})
}
@@ -335,6 +352,7 @@ export const useController = () => {
return {
TableComponent,
PageComponent,
SearchComponent,
isDetectionAddWorkflow, // 检测是否需要添加工作流
isDetectionOpenCAManage, // 检测是否需要打开CA授权管理弹窗
isDetectionOpenAddCAForm, // 检测是否需要打开添加CA授权弹窗
@@ -348,7 +366,6 @@ export const useController = () => {
handleOpenCAManage, // 打开CA授权管理弹窗
hasChildRoutes,
fetch,
data,
loading,
param,
}
@@ -504,8 +521,14 @@ export const useHistoryController = (id: string) => {
* @returns 返回CA授权类型名称
*/
const handleCertAuth = (type: string) => {
const name = type.replaceAll('.', '').replaceAll("'", '').replaceAll(' ', '').toLowerCase() // 处理类型中的特殊字符
return CACertificateAuthorization[name as keyof typeof CACertificateAuthorization].type
// console.log(type)
try {
const name = type.replaceAll('.', '').replaceAll("'", '').replaceAll(' ', '').toLowerCase() // 处理类型中的特殊字符
const caType = CACertificateAuthorization[name as keyof typeof CACertificateAuthorization].type
return { type: caType, icon: type }
} catch (error) {
return { type, icon: 'custom' }
}
}
/**
@@ -528,11 +551,11 @@ export const useCAManageController = (props: { type: string }) => {
key: 'ca',
width: 200,
render: (row: EabItem) => {
const name = handleCertAuth(row.ca)
const { type, icon } = handleCertAuth(row.ca)
return (
<NFlex align="center">
<SvgIcon icon={`cert-${name}`} size="2rem" />
<NText>{name}</NText>
<SvgIcon icon={`cert-${icon}`} size="2rem" />
<NText>{type}</NText>
</NFlex>
)
},
@@ -606,10 +629,14 @@ export const useCAManageController = (props: { type: string }) => {
* @param {EabItem} row - CA授权数据
*/
const handleEdit = (row: EabItem) => {
const caTypes = Object.values(CACertificateAuthorization).map((item) => item.type)
const isCustomCa = !caTypes.includes(row.ca)
// 填充表单数据 - 添加编辑模式标识
Object.assign(caFormData.value, {
email: row.email,
ca: row.ca,
ca: isCustomCa ? 'custom' : row.ca,
caName: isCustomCa ? row.ca : '',
Kid: row.Kid || '',
HmacEncoded: row.HmacEncoded || '',
CADirURL: row.CADirURL || '',
@@ -666,7 +693,6 @@ export const useCAManageController = (props: { type: string }) => {
*/
export const useCAFormController = (props?: { isEdit?: boolean; editId?: string }) => {
const { handleError } = useError()
const message = useMessage()
const { confirm } = useModalHooks()
const { useFormInput, useFormCustom } = useFormHooks()
@@ -710,10 +736,26 @@ export const useCAFormController = (props?: { isEdit?: boolean; editId?: string
// 自定义类型必填 CADirURL
if (currentCa === 'custom') {
rules.caName = {
required: true,
message: '请输入CA名称',
trigger: ['blur', 'input'],
}
rules.CADirURL = {
required: true,
message: $t('t_4_1750129259795'),
trigger: ['blur', 'input'],
validator: (rule: any, value: string) => {
if (!value) {
return new Error('请输入CA目录URL')
}
// Basic URL validation
try {
new URL(value)
return true
} catch (e) {
return new Error('请输入有效的URL地址')
}
},
}
}
@@ -769,9 +811,6 @@ export const useCAFormController = (props?: { isEdit?: boolean; editId?: string
// 表单配置
const formConfig = computed(() => [
useFormInput($t('t_1_1745735764953'), 'email', {
placeholder: $t('t_0_1747965909665'),
}),
useFormCustom(() => {
return (
<NFormItem label={$t('t_9_1747903669360')} path="ca">
@@ -792,11 +831,17 @@ export const useCAFormController = (props?: { isEdit?: boolean; editId?: string
</NFormItem>
)
}),
useFormInput($t('t_1_1745735764953'), 'email', {
placeholder: $t('t_0_1747965909665'),
}),
// 条件显示CADirURL字段
...(shouldShowCADirURL.value
? [
useFormInput($t('t_5_1750129253961'), 'CADirURL', {
placeholder: $t('t_6_1750129255766'),
useFormInput('CA名称', 'caName', {
placeholder: '请输入CA提供商名称',
}),
useFormInput($t('ACME服务URL地址'), 'CADirURL', {
placeholder: $t('请输入ACME服务URL地址'),
}),
]
: []),
@@ -828,12 +873,17 @@ export const useCAFormController = (props?: { isEdit?: boolean; editId?: string
// 提交表单
const submitForm = async (formData: any) => {
try {
const dataToSubmit = { ...formData }
if (dataToSubmit.ca === 'custom') {
dataToSubmit.ca = dataToSubmit.caName
}
delete dataToSubmit.caName // 删除临时字段
if (props?.isEdit && props?.editId) {
// 编辑模式
await updateExistingEab({ ...formData, id: props.editId })
await updateExistingEab({ ...dataToSubmit, id: props.editId })
} else {
// 新增模式
await addNewEab(formData)
await addNewEab(dataToSubmit)
}
return true
} catch (error) {
@@ -843,7 +893,7 @@ export const useCAFormController = (props?: { isEdit?: boolean; editId?: string
}
// 表单实例
const { component: CAForm } = useForm({
const { component: CAForm, fetch } = useForm({
config: formConfig,
rules: getFormRules(),
defaultValue: caFormData,
@@ -853,7 +903,7 @@ export const useCAFormController = (props?: { isEdit?: boolean; editId?: string
// 确认提交表单
confirm(async (close) => {
try {
await submitForm(caFormData.value)
await fetch()
close()
} catch (error) {
handleError(error)

View File

@@ -13,12 +13,10 @@ import EmptyState from '@components/TableEmptyState'
export default defineComponent({
name: 'CertManage',
setup() {
const { TableComponent, PageComponent, fetch, data, param, openUploadModal, getRowClassName } = useController()
const { TableComponent, PageComponent, SearchComponent, openUploadModal, getRowClassName } = useController()
const cssVar = useThemeCssVar(['contentPadding', 'borderColor', 'headerHeight', 'iconColorHover'])
// 挂载时请求数据
onMounted(() => fetch())
const { theme, themeOverrides } = useTheme()
console.log(theme.value, themeOverrides.value)
return () => (
<div class="h-full flex flex-col" style={cssVar.value}>
<div class="mx-auto max-w-[1600px] w-full p-6">
@@ -29,26 +27,7 @@ export default defineComponent({
{$t('t_13_1745227838275')}
</NButton>
),
headerRight: () => (
<NInput
v-model:value={param.value.search}
onKeydown={(e: KeyboardEvent) => {
if (e.key === 'Enter') fetch()
}}
onClear={() => useThrottleFn(fetch, 100)}
placeholder={$t('t_14_1745227840904')}
clearable
size="large"
class="min-w-[300px]"
v-slots={{
suffix: () => (
<div class="flex items-center" onClick={fetch}>
<Search class="text-[var(--text-color-3)] w-[1.6rem] cursor-pointer font-bold" />
</div>
),
}}
></NInput>
),
headerRight: () => <SearchComponent placeholder={$t('t_14_1745227840904')} />,
content: () => (
<div class="rounded-lg">
<TableComponent
@@ -62,15 +41,7 @@ export default defineComponent({
),
footerRight: () => (
<div class="mt-4 flex justify-end">
<PageComponent
v-slots={{
prefix: () => (
<span>
{$t('t_15_1745227839354')} {data.value.total} {$t('t_16_1745227838930')}
</span>
),
}}
/>
<PageComponent />
</div>
),
}}

View File

@@ -8,6 +8,7 @@ import {
useForm,
useLoadingMask,
useMessage,
useSearch,
} from '@baota/naive-ui/hooks'
import { useError } from '@baota/hooks/error'
import { $t } from '@locales/index'
@@ -135,6 +136,14 @@ export const useController = () => {
storage: 'certManagePageSize',
})
// 搜索实例
const { SearchComponent } = useSearch({
onSearch: (value) => {
param.value.search = value
fetch()
},
})
/**
* @description 打开上传证书弹窗
*/
@@ -189,14 +198,14 @@ export const useController = () => {
})
}
onMounted(() => fetch())
return {
loading,
fetch,
TableComponent,
PageComponent,
SearchComponent,
getRowClassName,
param,
data,
openUploadModal,
openViewModal,
}

View File

@@ -18,7 +18,8 @@ export default defineComponent({
name: 'MonitorManage',
setup() {
// 使用控制器获取数据和方法
const { TableComponent, PageComponent, param, fetch, data, openAddForm, isDetectionAddMonitor } = useController()
const { TableComponent, PageComponent, SearchComponent, fetch, openAddForm, isDetectionAddMonitor } =
useController()
// 获取主题CSS变量
const cssVar = useThemeCssVar(['contentPadding', 'borderColor', 'headerHeight', 'iconColorHover'])
@@ -44,26 +45,7 @@ export default defineComponent({
</NButton>
),
// 头部右侧区域 - 搜索框
headerRight: () => (
<NInput
v-model:value={param.value.search}
onKeydown={(e: KeyboardEvent) => {
if (e.key === 'Enter') fetch()
}}
onClear={() => fetch()}
placeholder={$t('t_12_1745289356974')}
clearable
size="large"
class="min-w-[300px]"
v-slots={{
suffix: () => (
<div class="flex items-center" onClick={fetch}>
<Search class="text-[var(--text-color-3)] w-[1.6rem] cursor-pointer font-bold" />
</div>
),
}}
></NInput>
),
headerRight: () => <SearchComponent placeholder={$t('t_12_1745289356974')} />,
// 内容区域 - 监控表格
content: () => (
<div class="rounded-lg">
@@ -78,15 +60,7 @@ export default defineComponent({
// 底部右侧区域 - 分页组件
footerRight: () => (
<div class="mt-4 flex justify-end">
<PageComponent
v-slots={{
prefix: () => (
<span>
{$t('t_15_1745227839354')} {data.value.total} {$t('t_16_1745227838930')}
</span>
),
}}
/>
<PageComponent />
</div>
),
}}

View File

@@ -11,6 +11,7 @@ import {
useForm,
useFormHooks,
useLoadingMask,
useSearch,
} from '@baota/naive-ui/hooks'
import { useError } from '@baota/hooks/error'
import { isDomain, isPort, isIp } from '@baota/utils/business'
@@ -38,9 +39,10 @@ interface MonitorControllerExposes {
// 表格相关
TableComponent: ReturnType<typeof useTable>['TableComponent']
PageComponent: ReturnType<typeof useTable>['PageComponent']
SearchComponent: ReturnType<typeof useSearch>['SearchComponent']
loading: Ref<boolean>
param: Ref<SiteMonitorListParams>
data: Ref<{ list: SiteMonitorItem[]; total: number }>
// param: Ref<SiteMonitorListParams>
// data: Ref<{ list: SiteMonitorItem[]; total: number }>
fetch: () => Promise<void>
// 表单和操作相关
@@ -144,6 +146,24 @@ export const useController = (): MonitorControllerExposes => {
width: 150,
render: (row: SiteMonitorItem) => row.end_time + '(' + row.end_day + ')',
},
{
title: $t('上次异常时间'),
key: 'except_end_time',
width: 150,
render: (row: SiteMonitorItem) => row.except_end_time || '-',
},
{
title: $t('上次检查时间'),
key: 'last_time',
width: 150,
render: (row: SiteMonitorItem) => row.last_time || '-',
},
{
title: $t('更新时间'),
key: 'update_time',
width: 150,
render: (row: SiteMonitorItem) => row.update_time || '-',
},
{
title: $t('t_18_1745289354598'),
key: 'report_type',
@@ -160,12 +180,6 @@ export const useController = (): MonitorControllerExposes => {
return <NSwitch value={row.active === 1} onUpdateValue={() => toggleStatus(row)} />
},
},
{
title: $t('t_19_1745289354676'),
key: 'update_time',
width: 150,
render: (row: SiteMonitorItem) => row.update_time || '-',
},
{
title: $t('t_7_1745215914189'),
key: 'create_time',
@@ -196,10 +210,7 @@ export const useController = (): MonitorControllerExposes => {
* 表格实例
* @description 创建表格实例并管理相关状态
*/
const { TableComponent, PageComponent, loading, param, data, total, fetch } = useTable<
SiteMonitorItem,
SiteMonitorListParams
>({
const { TableComponent, PageComponent, loading, param, fetch } = useTable<SiteMonitorItem, SiteMonitorListParams>({
config: createColumns(),
request: fetchMonitorList,
defaultValue: { p: 1, limit: 10, search: '' },
@@ -208,15 +219,13 @@ export const useController = (): MonitorControllerExposes => {
storage: 'monitorPageSize',
})
// /**
// * 分页实例
// * @description 创建表格分页组件
// */
// const { component: MonitorTablePage } = useTablePage({
// param,
// total,
// ,
// })
// 搜索实例
const { SearchComponent } = useSearch({
onSearch: (value) => {
param.value.search = value
fetch()
},
})
/**
* 打开添加监控弹窗
@@ -297,9 +306,8 @@ export const useController = (): MonitorControllerExposes => {
fetch,
TableComponent,
PageComponent,
SearchComponent,
isDetectionAddMonitor,
param,
data,
openAddForm,
}
}