【调整】新增部署插件扩展功能

【新增】多吉云cdn配置
【优化】分页新增本地存储功能
This commit is contained in:
chudong
2025-06-18 11:17:44 +08:00
parent 143db0baae
commit f1a17de516
456 changed files with 3894 additions and 3082 deletions

View File

@@ -15,7 +15,7 @@ import BaseComponent from '@components/BaseLayout'
export default defineComponent({
name: 'AuthApiManage',
setup() {
const { ApiTable, ApiTablePage, param, fetch, total, openAddForm } = useController()
const { TableComponent, PageComponent, param, fetch, total, openAddForm } = useController()
const cssVar = useThemeCssVar(['contentPadding', 'borderColor', 'headerHeight', 'iconColorHover'])
return () => (
@@ -50,7 +50,7 @@ export default defineComponent({
),
content: () => (
<div class="rounded-lg">
<ApiTable
<TableComponent
size="medium"
v-slots={{
empty: () => <EmptyState addButtonText={$t('t_0_1745289355714')} onAddClick={openAddForm} />,
@@ -60,7 +60,7 @@ export default defineComponent({
),
footerRight: () => (
<div class="mt-4 flex justify-end">
<ApiTablePage
<PageComponent
v-slots={{
prefix: () => (
<span>

View File

@@ -1,5 +1,4 @@
import {
FormInst,
FormItemRule,
FormProps,
FormRules,
@@ -14,13 +13,13 @@ import {
NSpace,
NTag,
NText,
NTooltip,
type DataTableColumns,
} from 'naive-ui'
import {
useModal,
useDialog,
useTable,
useTablePage,
useModalHooks,
useFormHooks,
useForm,
@@ -47,9 +46,12 @@ import type {
BunnyAccessConfig,
GcoreAccessConfig,
JdcloudAccessConfig,
DogeAccessConfig,
PluginAccessConfig,
} from '@/types/access'
import type { VNode, Ref } from 'vue'
import { testAccess } from '@/api/access'
import { testAccess, getPlugins } from '@/api/access'
// import { useLocalStorage } from '@vueuse/core'
import ApiManageForm from './components/ApiManageModel'
import SvgIcon from '@components/SvgIcon'
@@ -63,8 +65,8 @@ import { JSX } from 'vue/jsx-runtime'
interface AuthApiManageControllerExposes {
loading: Ref<boolean>
fetch: () => Promise<void>
ApiTable: (props: Record<string, unknown>, context: Record<string, unknown>) => JSX.Element
ApiTablePage: (props: Record<string, unknown>, context: Record<string, unknown>) => JSX.Element
TableComponent: (props: Record<string, unknown>, context: Record<string, unknown>) => VNode
PageComponent: (props: Record<string, unknown>, context: Record<string, unknown>) => VNode
param: Ref<AccessListParams>
total: Ref<number>
openAddForm: () => void
@@ -181,31 +183,17 @@ export const useController = (): AuthApiManageControllerExposes => {
]
// 表格实例
const {
component: ApiTable,
loading,
param,
total,
fetch,
} = useTable<AccessItem, AccessListParams>({
const { TableComponent, PageComponent, loading, param, total, fetch } = useTable<AccessItem, AccessListParams>({
config: createColumns(),
request: fetchAccessList,
watchValue: ['p', 'limit'],
storage: 'authApiManage',
alias: { page: 'p', pageSize: 'limit' },
defaultValue: {
p: 1,
limit: 10,
search: '',
},
watchValue: ['p', 'limit'],
})
// 分页实例
const { component: ApiTablePage } = useTablePage({
param,
total,
alias: {
page: 'p',
pageSize: 'limit',
},
})
/**
@@ -265,8 +253,8 @@ export const useController = (): AuthApiManageControllerExposes => {
return {
loading,
fetch,
ApiTable,
ApiTablePage,
TableComponent,
PageComponent,
param,
total,
openAddForm,
@@ -280,6 +268,15 @@ interface ApiFormControllerProps {
data?: AccessItem
}
interface PluginOption {
label: string
value: string
description?: string
pluginName?: string
config?: Record<string, any>
params?: string
}
/**
* 授权API表单控制器
* @description 处理授权API表单相关的业务逻辑
@@ -293,6 +290,10 @@ export const useApiFormController = (props: ApiFormControllerProps): ApiFormCont
const param = (props.data?.id ? ref({ ...props.data, config: JSON.parse(props.data.config) }) : apiFormProps) as Ref<
AddAccessParams | UpdateAccessParams
>
const pluginActionTips = ref('')
// 插件列表
const pluginList = ref<Array<PluginOption>>([])
// 表单规则
const rules = {
@@ -403,14 +404,30 @@ export const useApiFormController = (props: ApiFormControllerProps): ApiFormCont
trigger: 'input',
},
access_key_id: {
required: true,
message: $t('t_4_1745317314054'),
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: {
required: true,
message: $t('t_5_1745317315285'),
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,
@@ -491,6 +508,11 @@ export const useApiFormController = (props: ApiFormControllerProps): ApiFormCont
callback()
},
},
'config.name': {
required: true,
message: $t('t_0_1750144125193'),
trigger: 'change',
},
},
}
@@ -689,6 +711,73 @@ 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(() => {
return (
<NFormItem label={$t('t_1_1750144122230')} path="config.name" showRequireMark={true}>
<NSelect
class="w-full"
options={pluginList.value}
placeholder={$t('t_2_1750144123753')}
filterable
renderLabel={renderPluginLabel}
renderTag={renderPluginTag}
v-model:value={(param.value.config as PluginAccessConfig).name}
onUpdateValue={(value: string, option: PluginOption) => {
;(param.value.config as PluginAccessConfig).name = value
pluginActionTips.value = renderPluginTips(option.config || {})
}}
v-slots={{
empty: () => {
return <span class="text-[1.4rem]">{$t('t_0_1750210698345')}</span>
},
}}
/>
</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:
break
}
@@ -813,10 +902,47 @@ export const useApiFormController = (props: ApiFormControllerProps): ApiFormCont
secret_access_key: '',
} as JdcloudAccessConfig
break
case 'doge':
param.value.config = {
access_key_id: '',
access_key_secret: '',
} as DogeAccessConfig
break
case 'plugin':
param.value.config = {
name: pluginList.value[0]?.value || '',
config: '',
} as PluginAccessConfig
break
}
},
)
// 获取插件列表
const loadPlugins = async (): Promise<void> => {
try {
const { data } = await getPlugins().fetch()
if (data && Array.isArray(data)) {
// 处理插件数据,将每个插件的 actions 展开为选项
const pluginOptions: Array<PluginOption> = []
data.forEach((plugin: { name: string; actions: { name: string; description: string }[] }) => {
// 如果没有 actions直接使用插件名称
pluginOptions.push({
label: plugin.name,
value: plugin.name,
description: plugin.actions.map((action: { description: string }) => action.description).join('、'),
pluginName: plugin.name,
config: plugin.config,
})
})
pluginList.value = pluginOptions
pluginActionTips.value = renderPluginTips(pluginOptions[0]?.config || {})
}
} catch (error) {
console.error($t('t_1_1750210699272'), error)
}
}
/**
* 渲染单选标签
* @param {Record<string, any>} option - 选项
@@ -839,36 +965,64 @@ export const useApiFormController = (props: ApiFormControllerProps): ApiFormCont
* @param {Record<string, any>} option - 选项
* @returns {VNode} 渲染后的VNode
*/
const renderLabel = (option: { value: string; label: string; access: string[] }): VNode => {
const renderLabel = (option: PluginOption): VNode => {
return (
<NFlex justify="space-between" class="w-[38rem]">
<NFlex align="center" size="small">
<SvgIcon icon={`resources-${option.value}`} size="1.6rem" />
<NText>{option.label}</NText>
{option.description && <div class="text-[1.2rem] text-gray-500 mt-[0.2rem]">{option.description}</div>}
</NFlex>
<NFlex class="pr-[1rem]">
{option.access &&
option.access.map((item: string) => {
return (
<NTag type={item === 'dns' ? 'success' : 'info'} size="small" key={item}>
{accessTypeMap[item as keyof typeof accessTypeMap]}
</NTag>
)
})}
</NFlex>
)
}
/**
* 渲染插件标签
* @param {Record<string, any>} option - 选项
* @returns {VNode} 渲染后的VNode
*/
const renderPluginLabel = (option: PluginOption): VNode => {
return (
<NFlex justify="space-between" class="w-full">
<NFlex align="center" size="small">
<SvgIcon icon={`resources-${option.value}`} size="1.6rem" />
<div>
<NText>{option.label}</NText>
{option.description && <div class="text-[1.2rem] text-gray-500 mt-[0.2rem]">{option.description}</div>}
</div>
</NFlex>
</NFlex>
)
}
/**
* 渲染插件选中标签
* @param props - 属性对象
* @returns {VNode} 渲染后的VNode
*/
const renderPluginTag = (props: { option: PluginOption }): VNode => {
const { option } = props
return (
<NFlex class="w-full">
{option?.label ? (
<NFlex align="center" size="small">
<SvgIcon icon={`resources-${option.value}`} size="1.4rem" />
<NText>{option.label}</NText>
</NFlex>
) : (
<span class="text-[1.4rem] text-gray-400">{$t('t_2_1750210698518')}</span>
)}
</NFlex>
)
}
/**
* 提交授权API表单
* @param {UpdateAccessParams | AddAccessParams} param 请求参数
* @param {Ref<FormInst>} formRef 表单实例
*/
const submitApiManageForm = async (
param: UpdateAccessParams | AddAccessParams,
_formRef: Ref<FormInst>,
): Promise<void> => {
const submitApiManageForm = async (param: UpdateAccessParams | AddAccessParams): Promise<void> => {
try {
const data = { ...param, config: JSON.stringify(param.config) } as UpdateAccessParams<string>
if ('id' in param) {
@@ -882,6 +1036,15 @@ export const useApiFormController = (props: ApiFormControllerProps): ApiFormCont
}
}
/**
* @description 渲染插件tips
* @param {PluginOption} option - 插件选项
* @returns {VNode} 渲染后的VNode
*/
const renderPluginTips = (tips: Record<string, any>): string => {
return $t('t_3_1750210706775') + JSON.stringify(tips || {})
}
// 使用表单hooks
const { component: ApiManageForm, fetch } = useForm({
config,
@@ -904,8 +1067,10 @@ export const useApiFormController = (props: ApiFormControllerProps): ApiFormCont
}
})
// 组件初始化时获取插件列表
onMounted(loadPlugins)
return {
ApiManageForm,
}
}

View File

@@ -53,6 +53,7 @@ interface AuthApiManageStoreExposes {
const accessTypeMap = {
dns: $t('t_3_1745735765112'),
host: $t('t_0_1746754500246'),
plugin: '插件',
}
// -------------------- 请求方法 --------------------

View File

@@ -1,4 +1,4 @@
import { NFormItem, NSwitch, NText, SelectOption } from 'naive-ui'
import { NFormItem, NInput, NSwitch, NText, NTooltip, SelectOption } from 'naive-ui'
import { $t } from '@locales/index'
import { Ref } from 'vue'
import { useFormHooks } from '@baota/naive-ui/hooks'

View File

@@ -1,9 +1,9 @@
import { NButton, NCard, NStep, NSteps, NText, NTooltip, NTabs, NTabPane, NInput, NDivider } from 'naive-ui'
import { NButton, NCard, NStep, NSteps, NText, NTooltip, NTabs, NTabPane, NInput, NDivider, NFormItem } from 'naive-ui'
import { useForm, useModalClose, useModalOptions, useMessage } from '@baota/naive-ui/hooks'
import { useThemeCssVar } from '@baota/naive-ui/theme'
import { useError } from '@baota/hooks/error'
import { useStore } from '@components/FlowChart/useStore'
import { getSites } from '@api/access'
import { getSites, getPlugins } from '@api/access'
import { $t } from '@locales/index'
import { deepClone } from '@baota/utils/data'
@@ -84,6 +84,12 @@ export default defineComponent({
const siteOptions = ref<FormOption[]>([])
// 网站选项加载状态
const siteOptionsLoading = ref(false)
// 插件选项
const pluginOptions = ref<FormOption[]>([])
// 插件方法选项
const pluginActionOptions = ref<FormOption[]>([])
// 插件方法选项加载状态
const pluginActionOptionsLoading = ref(false)
// 当前步骤
const current = ref(1)
// 是否是下一步
@@ -95,6 +101,9 @@ export default defineComponent({
// 搜索关键字
const searchKeyword = ref('')
// 插件方法提示
const pluginActionTips = ref('')
// 表单参数
const param = ref(deepClone(props.node.config))
// 本地提供商
@@ -125,7 +134,7 @@ export default defineComponent({
value: param.value.provider_id,
valueType: 'value' as const,
isAddMode: true,
'onUpdate:value': (val: { value: number | string; type: string }) => {
'onUpdate:value': (val: { value: number | string; type: string; data: string }) => {
if (
val.value !== '' &&
param.value.provider_id !== '' &&
@@ -136,6 +145,7 @@ export default defineComponent({
}
param.value.provider_id = val.value
param.value.type = val.type
param.value.provider_data = val?.data || ''
},
}
return (<DnsProviderSelect {...dnsProviderProps} />) as VNode
@@ -155,7 +165,6 @@ export default defineComponent({
}),
)
console.log(param.value.provider)
// 根据不同的部署类型添加不同的表单配置
switch (param.value.provider) {
case 'localhost':
@@ -198,6 +207,7 @@ export default defineComponent({
case 'qiniu-cdn':
case 'qiniu-oss':
case 'huaweicloud-cdn':
case 'doge-cdn':
config.push(...formConfig.cdnDeploy())
break
case 'volcengine-cdn':
@@ -215,6 +225,60 @@ export default defineComponent({
case 'aliyun-oss':
config.push(...formConfig.storageDeploy())
break
case 'plugin':
// 插件部署配置
config.push(
// ...formConfig.pluginDeploy(param, pluginActionOptions, pluginActionOptionsLoading, pluginActionTips.value),
...[
formConfig.select('插件方法', 'action', pluginActionOptions.value, {
placeholder: '请选择插件方法',
filterable: true,
clearable: true,
loading: pluginActionOptionsLoading.value,
onUpdateValue: (value: string, option: FormOption) => {
param.value.action = value
pluginActionTips.value = renderPluginActionTips(option?.params || {})
},
}),
{
type: 'custom' as const,
render: () => {
return (
<NFormItem
label="自定义参数"
path="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"
v-model:value={param.value['params']}
placeholder={pluginActionTips.value}
rows={4}
/>
</NFormItem>
)
},
},
],
)
break
}
// 添加跳过选项
@@ -224,7 +288,13 @@ export default defineComponent({
watch(
() => param.value.provider_id,
() => handleSiteSearch(''),
() => {
handleSiteSearch('')
// 如果是插件类型,加载插件方法
if (param.value.provider === 'plugin') {
loadPluginActions()
}
},
)
/**
@@ -256,6 +326,47 @@ export default defineComponent({
}
}, 1000)
/**
* @description 渲染插件方法提示
*/
const renderPluginActionTips = (tips: Record<string, any>): string => {
return '请输入JSON格式的参数例如: ' + JSON.stringify(tips || {})
}
/**
* 加载插件方法
*/
const loadPluginActions = async (): Promise<void> => {
if (!param.value.provider_id) return
try {
pluginActionOptionsLoading.value = true
// 先获取插件列表,找到对应的插件
const config = JSON.parse(param.value.provider_data?.data?.config || '{}')
if (config.name) {
const { data } = await getPlugins().fetch()
const selectedPlugin = data?.find((plugin: { name: string }) => plugin.name === config.name)
const actions = selectedPlugin?.actions || []
pluginActionOptions.value = actions.map((item: any) => ({
label: `${item.description}`,
value: item.name,
params: item.params,
}))
if (!param.value.action) {
const action = actions[0]
param.value.action = action?.name
pluginActionTips.value = renderPluginActionTips(action?.params || {})
}
delete param.value.provider_data
}
} catch (error) {
handleError(error)
pluginActionOptions.value = []
} finally {
pluginActionOptionsLoading.value = false
}
}
/**
* 下一步
*/
@@ -345,6 +456,10 @@ export default defineComponent({
}
handleSiteSearch('')
}
// 如果是插件类型,加载插件方法
if (param.value.provider === 'plugin') {
loadPluginActions()
}
nextStep()
}
})

View File

@@ -60,4 +60,7 @@ export default {
// 存储桶相关字段验证
region: validator.required('region', $t('t_25_1745735766651'), 'input'),
bucket: validator.required('bucket', $t('t_26_1745735767144'), 'input'),
// 插件相关字段验证
action: validator.required('action', '请选择插件方法', 'select'),
} as FormRules

View File

@@ -1,13 +1,28 @@
import { defineComponent } from 'vue'
import { useCAFormController } from '../useController'
interface CAManageFormProps {
isEdit?: boolean
editId?: string
}
/**
* CA授权表单组件
* ACME账户表单组件
*/
export default defineComponent({
name: 'CAManageForm',
setup() {
const { CAForm } = useCAFormController()
props: {
isEdit: {
type: Boolean,
default: false,
},
editId: {
type: String,
default: '',
},
},
setup(props: CAManageFormProps) {
const { CAForm } = useCAFormController(props)
return () => <CAForm labelPlacement="top" />
},
})

View File

@@ -7,7 +7,7 @@ import EmptyState from '@components/TableEmptyState'
import BaseComponent from '@components/BaseLayout'
/**
* CA授权管理模态框组件
* ACME账户管理模态框组件
*/
export default defineComponent({
name: 'CAManageModal',
@@ -18,7 +18,7 @@ export default defineComponent({
},
},
setup(props) {
const { CATable, CATablePage, handleOpenAddForm, total } = useCAManageController(props)
const { TableComponent, PageComponent, handleOpenAddForm, total } = useCAManageController(props)
return () => (
<BaseComponent
v-slots={{
@@ -30,7 +30,7 @@ export default defineComponent({
),
content: () => (
<div class="rounded-lg">
<CATable
<TableComponent
size="medium"
v-slots={{
empty: () => <EmptyState addButtonText={$t('t_4_1747903685371')} onAddClick={handleOpenAddForm} />,
@@ -40,7 +40,7 @@ export default defineComponent({
),
footerRight: () => (
<div class="flex justify-end mt-4">
<CATablePage
<PageComponent
v-slots={{
prefix: () => (
<span>

View File

@@ -15,7 +15,7 @@ export default defineComponent({
},
},
setup(props) {
const { WorkflowHistoryTable, WorkflowHistoryTablePage, fetch } = useHistoryController(props.id)
const { TableComponent, PageComponent, fetch } = useHistoryController(props.id)
onMounted(() => {
fetch()
})
@@ -30,8 +30,8 @@ export default defineComponent({
</NButton>
</div>
),
content: () => <WorkflowHistoryTable />,
footerRight: () => <WorkflowHistoryTablePage />,
content: () => <TableComponent />,
footerRight: () => <PageComponent />,
}}
></BaseComponent>
</div>

View File

@@ -16,8 +16,8 @@ export default defineComponent({
name: 'WorkflowManager',
setup() {
const {
WorkflowTable,
WorkflowTablePage,
TableComponent,
PageComponent,
isDetectionAddWorkflow,
isDetectionOpenCAManage,
isDetectionOpenAddCAForm,
@@ -87,18 +87,19 @@ export default defineComponent({
),
content: () => (
<div class="rounded-lg ">
<WorkflowTable size="medium">
{{
<TableComponent
size="medium"
v-slots={{
empty: () => (
<EmptyState addButtonText={$t('t_0_1747047213730')} onAddClick={handleAddWorkflow} />
),
}}
</WorkflowTable>
/>
</div>
),
footerRight: () => (
<div class="mt-4 flex justify-end">
<WorkflowTablePage
<PageComponent
v-slots={{
prefix: () => <span>{$t('t_0_1746773350551', [data.value.total])}</span>,
}}

View File

@@ -1,21 +1,12 @@
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,
useTablePage,
useModal,
useFormHooks,
useForm,
useModalHooks,
useMessage,
} from '@baota/naive-ui/hooks'
import { useDialog, useTable, useModal, useFormHooks, useForm, useModalHooks, useMessage } 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'
import HistoryModal from './components/HistoryModal'
import HistoryLogsModal from './components/HistoryLogsModal'
import HistoryLogsModal from './components/historyLogsModal'
import CAManageModal from './components/CAManageModal'
import { $t } from '@/locales'
import { router } from '@router/index'
@@ -41,6 +32,7 @@ const {
caFormData,
fetchEabList,
addNewEab,
updateExistingEab,
deleteExistingEab,
resetCaForm,
} = useStore()
@@ -127,7 +119,13 @@ export const useController = () => {
width: 180,
render: (row: WorkflowItem) => row.create_time || '-',
},
statusCol<WorkflowItem>('last_run_status', $t('t_0_1746677882486')),
{
title: $t('t_1_1750129254278'),
key: 'last_run_time',
width: 180,
render: (row: WorkflowItem) => row.last_run_time || '-',
},
statusCol<WorkflowItem>('last_run_status', $t('t_2_1750129253921')),
{
title: $t('t_8_1745215914610'),
key: 'actions',
@@ -154,34 +152,15 @@ export const useController = () => {
]
// 表格实例
const {
component: WorkflowTable,
loading,
param,
data,
total,
fetch,
} = useTable<WorkflowItem, WorkflowListParams>({
const { TableComponent, PageComponent, loading, param, data, fetch } = useTable<WorkflowItem, WorkflowListParams>({
config: createColumns(),
request: fetchWorkflowList,
defaultValue: {
p: 1,
limit: 10,
search: '',
},
storage: 'autoDeployPageSize',
defaultValue: { p: 1, limit: 10, search: '' },
alias: { page: 'p', pageSize: 'limit' },
watchValue: ['p', 'limit'],
})
// 分页实例
const { component: WorkflowTablePage } = useTablePage({
param,
total,
alias: {
page: 'p',
pageSize: 'limit',
},
})
// 节流渲染
const throttleFn = useThrottleFn(() => {
setTimeout(() => {
@@ -354,8 +333,8 @@ export const useController = () => {
}
return {
WorkflowTable,
WorkflowTablePage,
TableComponent,
PageComponent,
isDetectionAddWorkflow, // 检测是否需要添加工作流
isDetectionOpenCAManage, // 检测是否需要打开CA授权管理弹窗
isDetectionOpenAddCAForm, // 检测是否需要打开添加CA授权弹窗
@@ -502,40 +481,33 @@ export const useHistoryController = (id: string) => {
]
// 表格实例
const {
component: WorkflowHistoryTable,
loading,
param,
total,
fetch,
} = useTable<WorkflowHistoryItem, WorkflowHistoryParams>({
const { TableComponent, PageComponent, loading, fetch } = useTable<WorkflowHistoryItem, WorkflowHistoryParams>({
config: createColumns(),
request: fetchWorkflowHistory,
defaultValue: {
id,
p: 1,
limit: 10,
},
defaultValue: { id, p: 1, limit: 10 },
alias: { page: 'p', pageSize: 'limit' },
watchValue: ['p', 'limit'],
})
const { component: WorkflowHistoryTablePage } = useTablePage({
param,
total,
alias: {
page: 'p',
pageSize: 'limit',
},
storage: 'autoDeployHistoryPageSize',
})
return {
WorkflowHistoryTable,
WorkflowHistoryTablePage,
TableComponent,
PageComponent,
loading,
fetch,
}
}
/**
* @description 处理CA授权类型
* @param type - CA授权类型
* @returns 返回CA授权类型名称
*/
const handleCertAuth = (type: string) => {
const name = type.replaceAll('.', '').replaceAll("'", '').replaceAll(' ', '').toLowerCase() // 处理类型中的特殊字符
return CACertificateAuthorization[name as keyof typeof CACertificateAuthorization].type
}
/**
* @description CA授权管理业务逻辑控制器
* @returns {Object} 返回CA授权管理控制器对象
@@ -543,17 +515,10 @@ export const useHistoryController = (id: string) => {
export const useCAManageController = (props: { type: string }) => {
const { handleError } = useError()
// 表格配置
const columns = [
{
title: $t('t_2_1745289353944'),
key: 'name',
ellipsis: {
tooltip: true,
},
},
const columns: DataTableColumn<EabItem>[] = [
{
title: $t('t_1_1745735764953'),
key: 'mail',
key: 'email',
ellipsis: {
tooltip: true,
},
@@ -561,13 +526,16 @@ export const useCAManageController = (props: { type: string }) => {
{
title: $t('t_9_1747903669360'),
key: 'ca',
width: 120,
render: (row: EabItem) => (
<NFlex align="center">
<SvgIcon icon={`cert-${row.ca}`} size="2rem" />
<NText>{CACertificateAuthorization[row.ca as keyof typeof CACertificateAuthorization].name}</NText>
</NFlex>
),
width: 200,
render: (row: EabItem) => {
const name = handleCertAuth(row.ca)
return (
<NFlex align="center">
<SvgIcon icon={`cert-${name}`} size="2rem" />
<NText>{name}</NText>
</NFlex>
)
},
},
{
title: $t('t_7_1745215914189'),
@@ -578,11 +546,14 @@ export const useCAManageController = (props: { type: string }) => {
{
title: $t('t_8_1745215914610'),
key: 'actions',
width: 80,
width: 120,
align: 'right' as const,
fixed: 'right' as const,
render: (row: EabItem) => (
<NSpace justify="end">
<NButton size="tiny" strong secondary type="primary" onClick={() => handleEdit(row)}>
{$t('t_11_1745215915429')}
</NButton>
<NButton size="tiny" strong secondary type="error" onClick={() => confirmDelete(row.id.toString())}>
{$t('t_12_1745215914312')}
</NButton>
@@ -592,31 +563,24 @@ export const useCAManageController = (props: { type: string }) => {
]
// 表格实例
const {
component: CATable,
loading,
param,
total,
fetch,
} = useTable<EabItem, EabListParams>({
const { TableComponent, PageComponent, loading, param, total, fetch } = useTable<EabItem, EabListParams>({
config: columns,
request: fetchEabList,
defaultValue: {
p: 1,
limit: 10,
},
defaultValue: { p: 1, limit: 10 },
alias: { page: 'p', pageSize: 'limit' },
watchValue: ['p', 'limit'],
storage: 'caManagePageSize',
})
// 分页实例
const { component: CATablePage } = useTablePage({
param,
total,
alias: {
page: 'p',
pageSize: 'limit',
},
})
// // 分页实例
// const { component: CATablePage } = useTablePage({
// param,
// total,
// alias: {
// page: 'p',
// pageSize: 'limit',
// },
// })
/**
* 确认删除CA授权
@@ -637,6 +601,31 @@ export const useCAManageController = (props: { type: string }) => {
})
}
/**
* 编辑CA授权
* @param {EabItem} row - CA授权数据
*/
const handleEdit = (row: EabItem) => {
// 填充表单数据 - 添加编辑模式标识
Object.assign(caFormData.value, {
email: row.email,
ca: row.ca,
Kid: row.Kid || '',
HmacEncoded: row.HmacEncoded || '',
CADirURL: row.CADirURL || '',
})
useModal({
title: $t('t_3_1750129254533'),
area: 500,
component: () => import('./components/CAManageForm').then((m) => m.default),
footer: true,
componentProps: { isEdit: true, editId: row.id.toString() },
onUpdateShow: (show) => {
if (!show) fetch()
},
})
}
/**
* 打开添加CA授权表单
*/
@@ -660,13 +649,14 @@ export const useCAManageController = (props: { type: string }) => {
})
return {
CATable,
CATablePage,
TableComponent,
PageComponent,
loading,
param,
total,
fetch,
handleOpenAddForm,
handleEdit,
}
}
@@ -674,46 +664,60 @@ export const useCAManageController = (props: { type: string }) => {
* @description CA授权表单控制器
* @returns {Object} 返回CA授权表单控制器对象
*/
export const useCAFormController = () => {
export const useCAFormController = (props?: { isEdit?: boolean; editId?: string }) => {
const { handleError } = useError()
const message = useMessage()
const { confirm } = useModalHooks()
const { useFormInput, useFormCustom } = useFormHooks()
// 表单验证规则
const formRules = {
name: {
required: true,
message: $t('t_25_1746773349596'),
trigger: ['blur', 'input'],
},
mail: {
required: true,
message: $t('t_6_1747817644358'),
trigger: ['blur', 'input'],
validator: (rule: any, value: string) => {
if (!value) return true
if (!isEmail(value)) {
return new Error($t('t_7_1747817613773'))
}
return true
// 动态验证规则
const getFormRules = () => {
const rules: any = {
email: {
required: true,
message: $t('t_6_1747817644358'),
trigger: ['blur', 'input'],
validator: (rule: any, value: string) => {
if (!value) return new Error($t('t_6_1747817644358'))
if (!isEmail(value)) {
return new Error($t('t_7_1747817613773'))
}
return true
},
},
},
Kid: {
required: true,
message: $t('t_5_1747903671439'),
trigger: ['blur', 'input'],
},
HmacEncoded: {
required: true,
message: $t('t_6_1747903672931'),
trigger: ['blur', 'input'],
},
ca: {
required: true,
message: $t('t_7_1747903678624'),
trigger: 'change',
},
ca: {
required: true,
message: $t('t_7_1747903678624'),
trigger: 'change',
},
}
const currentCa = caFormData.value.ca
// SSL.com 和 Google 必填 EAB 参数
if (currentCa === 'sslcom' || currentCa === 'google') {
rules.Kid = {
required: true,
message: $t('t_5_1747903671439'),
trigger: ['blur', 'input'],
}
rules.HmacEncoded = {
required: true,
message: $t('t_6_1747903672931'),
trigger: ['blur', 'input'],
}
}
// 自定义类型必填 CADirURL
if (currentCa === 'custom') {
rules.CADirURL = {
required: true,
message: $t('t_4_1750129259795'),
trigger: ['blur', 'input'],
}
}
return rules
}
// 渲染标签函数
@@ -745,12 +749,27 @@ export const useCAFormController = () => {
value: item.type,
}))
// 判断是否显示EAB字段
const shouldShowEab = computed(() => {
const ca = caFormData.value.ca
// Buypass和Letsencrypt不显示EAB字段
return ca !== 'buypass' && ca !== 'letsencrypt'
})
// 判断是否显示CADirURL字段
const shouldShowCADirURL = computed(() => {
return caFormData.value.ca === 'custom'
})
// 判断是否显示必填标记
const shouldShowRequireMark = computed(() => {
const ca = caFormData.value.ca
return ca !== 'zerossl' && ca !== 'custom'
})
// 表单配置
const formConfig = [
useFormInput($t('t_2_1745289353944'), 'name', {
placeholder: $t('t_8_1747903675532'),
}),
useFormInput($t('t_1_1745735764953'), 'mail', {
const formConfig = computed(() => [
useFormInput($t('t_1_1745735764953'), 'email', {
placeholder: $t('t_0_1747965909665'),
}),
useFormCustom(() => {
@@ -773,21 +792,49 @@ export const useCAFormController = () => {
</NFormItem>
)
}),
useFormInput($t('t_10_1747903662994'), 'Kid', {
placeholder: $t('t_11_1747903674802'),
}),
useFormInput($t('t_12_1747903662994'), 'HmacEncoded', {
type: 'textarea',
placeholder: $t('t_13_1747903673007'),
rows: 3,
}),
]
// 条件显示CADirURL字段
...(shouldShowCADirURL.value
? [
useFormInput($t('t_5_1750129253961'), 'CADirURL', {
placeholder: $t('t_6_1750129255766'),
}),
]
: []),
// 条件显示EAB字段
...(shouldShowEab.value
? [
useFormInput(
$t('t_10_1747903662994'),
'Kid',
{
placeholder: $t('t_11_1747903674802'),
},
{ showRequireMark: shouldShowRequireMark.value },
),
useFormInput(
$t('t_12_1747903662994'),
'HmacEncoded',
{
type: 'textarea',
placeholder: $t('t_13_1747903673007'),
rows: 3,
},
{ showRequireMark: shouldShowRequireMark.value },
),
]
: []),
])
// 提交表单
const submitForm = async (formData: any) => {
try {
await addNewEab(formData)
message.success($t('t_40_1745289355715'))
if (props?.isEdit && props?.editId) {
// 编辑模式
await updateExistingEab({ ...formData, id: props.editId })
} else {
// 新增模式
await addNewEab(formData)
}
return true
} catch (error) {
handleError(error)
@@ -798,7 +845,7 @@ export const useCAFormController = () => {
// 表单实例
const { component: CAForm } = useForm({
config: formConfig,
rules: formRules,
rules: getFormRules(),
defaultValue: caFormData,
request: submitForm,
})

View File

@@ -7,7 +7,7 @@ import {
enableWorkflow,
stopWorkflow,
} from '@/api/workflow'
import { getEabList, addEab, deleteEab } from '@/api/access'
import { getEabList, addEab, deleteEab, updateEab } from '@/api/access'
import { useError } from '@baota/hooks/error'
// import { useMessage } from '@baota/naive-ui/hooks'
import { $t } from '@locales/index'
@@ -18,9 +18,8 @@ import type {
WorkflowItem,
UpdateWorkflowExecTypeParams,
EnableWorkflowParams,
StopWorkflowParams,
} from '@/types/workflow'
import type { EabItem, EabListParams, EabAddParams } from '@/types/access'
import type { EabItem, EabListParams, EabAddParams, EabUpdateParams } from '@/types/access'
import type { TableResponse } from '@baota/naive-ui/types/table'
const { handleError } = useError()
@@ -46,12 +45,13 @@ export const useWorkflowStore = defineStore('workflow-store', () => {
])
// CA授权管理相关数据
// CA授权表单数据
const caFormData = ref<EabAddParams>({
name: '',
// ACME账户表单数据
const caFormData = ref<EabAddParams & { id?: string }>({
email: '',
Kid: '',
HmacEncoded: '',
ca: 'zerossl',
CADirURL: '',
})
// -------------------- 工具方法 --------------------
@@ -196,6 +196,21 @@ export const useWorkflowStore = defineStore('workflow-store', () => {
}
}
/**
* 更新CA授权
* @param {EabUpdateParams} params - CA授权更新数据
*/
const updateExistingEab = async (formData: EabUpdateParams): Promise<void> => {
try {
const { message, fetch } = updateEab(formData)
message.value = true
await fetch()
resetCaForm() // 重置表单
} catch (error) {
handleError(error)
}
}
/**
* 删除CA授权
* @param {string} id - CA授权ID
@@ -211,14 +226,15 @@ export const useWorkflowStore = defineStore('workflow-store', () => {
}
/**
* 重置CA表单
* 重置ACME账户表单
*/
const resetCaForm = () => {
caFormData.value = {
name: '',
email: '',
Kid: '',
HmacEncoded: '',
ca: 'zerossl',
CADirURL: '',
}
}
@@ -240,6 +256,7 @@ export const useWorkflowStore = defineStore('workflow-store', () => {
setWorkflowExecType,
fetchEabList,
addNewEab,
updateExistingEab,
deleteExistingEab,
resetCaForm,
}

View File

@@ -13,7 +13,7 @@ import EmptyState from '@components/TableEmptyState'
export default defineComponent({
name: 'CertManage',
setup() {
const { CertTable, CertTablePage, fetch, data, param, openUploadModal, getRowClassName } = useController()
const { TableComponent, PageComponent, fetch, data, param, openUploadModal, getRowClassName } = useController()
const cssVar = useThemeCssVar(['contentPadding', 'borderColor', 'headerHeight', 'iconColorHover'])
// 挂载时请求数据
onMounted(() => fetch())
@@ -51,16 +51,18 @@ export default defineComponent({
),
content: () => (
<div class="rounded-lg">
<CertTable size="medium" rowClassName={getRowClassName}>
{{
<TableComponent
size="medium"
rowClassName={getRowClassName}
v-slots={{
empty: () => <EmptyState addButtonText={$t('t_1_1747047213009')} onAddClick={openUploadModal} />,
}}
</CertTable>
/>
</div>
),
footerRight: () => (
<div class="mt-4 flex justify-end">
<CertTablePage
<PageComponent
v-slots={{
prefix: () => (
<span>

View File

@@ -1,13 +1,13 @@
import { NButton, NSpace, NTag, useMessage, type DataTableColumns } from 'naive-ui'
import { NButton, NSpace, NTag, type DataTableColumns } from 'naive-ui'
import {
useModal,
useTable,
useTablePage,
useDialog,
useFormHooks,
useModalHooks,
useForm,
useLoadingMask,
useMessage,
} from '@baota/naive-ui/hooks'
import { useError } from '@baota/hooks/error'
import { $t } from '@locales/index'
@@ -126,32 +126,13 @@ export const useController = () => {
}
// 表格实例
const {
component: CertTable,
loading,
param,
data,
total,
fetch,
} = useTable<CertItem, CertListParams>({
const { TableComponent, PageComponent, loading, param, data, fetch } = useTable<CertItem, CertListParams>({
config: createColumns(),
request: fetchCertList,
defaultValue: {
p: 1,
limit: 10,
search: '',
},
defaultValue: { p: 1, limit: 10, search: '' },
alias: { page: 'p', pageSize: 'limit' },
watchValue: ['p', 'limit'],
})
// 分页实例
const { component: CertTablePage } = useTablePage({
param,
total,
alias: {
page: 'p',
pageSize: 'limit',
},
storage: 'certManagePageSize',
})
/**
@@ -211,8 +192,8 @@ export const useController = () => {
return {
loading,
fetch,
CertTable,
CertTablePage,
TableComponent,
PageComponent,
getRowClassName,
param,
data,

View File

@@ -129,9 +129,9 @@ export const useLayoutStore = defineStore('layout-store', (): LayoutStoreInterfa
}
/**
* @description 获取DNS提供商
* @description 获取提供商
* @param type - 类型 (简化了联合类型,实际使用时可根据需要定义更精确的类型别名)
* @returns DNS提供商
* @returns 提供商
*/
const fetchDnsProvider = async (type: string = ''): Promise<void> => {
try {
@@ -142,6 +142,7 @@ export const useLayoutStore = defineStore('layout-store', (): LayoutStoreInterfa
label: item.name,
value: item.id.toString(),
type: item.type,
data: item,
})) || []
} catch (error) {
dnsProvider.value = []

View File

@@ -18,7 +18,7 @@ export default defineComponent({
name: 'MonitorManage',
setup() {
// 使用控制器获取数据和方法
const { MonitorTable, MonitorTablePage, param, fetch, data, openAddForm, isDetectionAddMonitor } = useController()
const { TableComponent, PageComponent, param, fetch, data, openAddForm, isDetectionAddMonitor } = useController()
// 获取主题CSS变量
const cssVar = useThemeCssVar(['contentPadding', 'borderColor', 'headerHeight', 'iconColorHover'])
@@ -67,17 +67,18 @@ export default defineComponent({
// 内容区域 - 监控表格
content: () => (
<div class="rounded-lg">
<MonitorTable size="medium">
{{
<TableComponent
size="medium"
v-slots={{
empty: () => <EmptyState addButtonText={$t('t_11_1745289354516')} onAddClick={openAddForm} />,
}}
</MonitorTable>
/>
</div>
),
// 底部右侧区域 - 分页组件
footerRight: () => (
<div class="mt-4 flex justify-end">
<MonitorTablePage
<PageComponent
v-slots={{
prefix: () => (
<span>

View File

@@ -6,7 +6,6 @@ import { FormRules, NButton, NSpace, NSwitch, type DataTableColumns } from 'naiv
import {
useModal,
useTable,
useTablePage,
useDialog,
useModalHooks,
useForm,
@@ -37,8 +36,8 @@ import type {
*/
interface MonitorControllerExposes {
// 表格相关
MonitorTable: ReturnType<typeof useTable>['component']
MonitorTablePage: ReturnType<typeof useTablePage>['component']
TableComponent: ReturnType<typeof useTable>['TableComponent']
PageComponent: ReturnType<typeof useTable>['PageComponent']
loading: Ref<boolean>
param: Ref<SiteMonitorListParams>
data: Ref<{ list: SiteMonitorItem[]; total: number }>
@@ -197,36 +196,27 @@ export const useController = (): MonitorControllerExposes => {
* 表格实例
* @description 创建表格实例并管理相关状态
*/
const {
component: MonitorTable,
loading,
param,
data,
total,
fetch,
} = useTable<SiteMonitorItem, SiteMonitorListParams>({
const { TableComponent, PageComponent, loading, param, data, total, fetch } = useTable<
SiteMonitorItem,
SiteMonitorListParams
>({
config: createColumns(),
request: fetchMonitorList,
defaultValue: {
p: 1,
limit: 10,
search: '',
},
defaultValue: { p: 1, limit: 10, search: '' },
alias: { page: 'p', pageSize: 'limit' },
watchValue: ['p', 'limit'],
storage: 'monitorPageSize',
})
/**
* 分页实例
* @description 创建表格分页组件
*/
const { component: MonitorTablePage } = useTablePage({
param,
total,
alias: {
page: 'p',
pageSize: 'limit',
},
})
// /**
// * 分页实例
// * @description 创建表格分页组件
// */
// const { component: MonitorTablePage } = useTablePage({
// param,
// total,
// ,
// })
/**
* 打开添加监控弹窗
@@ -305,8 +295,8 @@ export const useController = (): MonitorControllerExposes => {
return {
loading,
fetch,
MonitorTable,
MonitorTablePage,
TableComponent,
PageComponent,
isDetectionAddMonitor,
param,
data,