【新增】在翻译文件中添加多吉云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

@@ -11217,5 +11217,73 @@
"arDZ": "الرجاء إدخال المعلمات بتنسيق JSON، على سبيل المثال:"
},
"timestamp": "2025-06-18T01:38:26.776Z"
},
"请输入多吉云AccessKey": {
"text": "请输入多吉云AccessKey",
"key": "t_0_1750320239265",
"translations": {
"zhCN": "请输入多吉云AccessKey",
"zhTW": "請輸入多吉雲AccessKey",
"enUS": "Please enter DogeCloud AccessKey",
"jaJP": "DogeCloudのAccessKeyを入力してください",
"koKR": "DogeCloud AccessKey를 입력하세요",
"ruRU": "Пожалуйста, введите DogeCloud AccessKey",
"ptBR": "Por favor, insira o AccessKey do DogeCloud",
"frFR": "Veuillez entrer la clé d'accès DogeCloud",
"esAR": "Por favor ingrese la AccessKey de DogeCloud",
"arDZ": "الرجاء إدخال مفتاح الوصول DogeCloud"
},
"timestamp": "2025-06-19T08:04:01.428Z"
},
"请输入多吉云SecretKey": {
"text": "请输入多吉云SecretKey",
"key": "t_1_1750320241427",
"translations": {
"zhCN": "请输入多吉云SecretKey",
"zhTW": "請輸入多吉雲SecretKey",
"enUS": "Please enter DogeCloud SecretKey",
"jaJP": "ドージークラウドのSecretKeyを入力してください",
"koKR": "도지클라우드 SecretKey를 입력하세요",
"ruRU": "Пожалуйста, введите SecretKey DogeCloud",
"ptBR": "Por favor, insira a SecretKey do DogeCloud",
"frFR": "Veuillez entrer la SecretKey de DogeCloud",
"esAR": "Por favor ingrese la SecretKey de DogeCloud",
"arDZ": "الرجاء إدخال مفتاح SecretKey الخاص بـ DogeCloud"
},
"timestamp": "2025-06-19T08:04:01.428Z"
},
"跳过发送通知": {
"text": "跳过发送通知",
"key": "t_2_1750320237611",
"translations": {
"zhCN": "跳过发送通知",
"zhTW": "跳過發送通知",
"enUS": "Skip sending notification",
"jaJP": "通知の送信をスキップ",
"koKR": "알림 보내기 건너뛰기",
"ruRU": "Пропустить отправку уведомления",
"ptBR": "Pular envio de notificação",
"frFR": "Ignorer l'envoi de notification",
"esAR": "Omitir el envío de notificación",
"arDZ": "تخطي إرسال الإشعار"
},
"timestamp": "2025-06-19T08:04:01.428Z"
},
"发送": {
"text": "发送",
"key": "t_3_1750320237991",
"translations": {
"zhCN": "发送",
"zhTW": "發送",
"enUS": "Send",
"jaJP": "送信",
"koKR": "보내기",
"ruRU": "Отправить",
"ptBR": "Enviar",
"frFR": "Envoyer",
"esAR": "Enviar",
"arDZ": "إرسال"
},
"timestamp": "2025-06-19T08:04:01.428Z"
}
}

View File

@@ -2,6 +2,7 @@ import { Transition, type Component as ComponentType, h } from 'vue'
import { RouterView } from 'vue-router'
import CustomProvider from '@baota/naive-ui/components/customProvider'
export default defineComponent({
name: 'App',
setup() {

View File

@@ -93,7 +93,7 @@ export default defineComponent<DnsProviderSelectProps>({
*/
const renderSingleSelectTag = ({ option }: { option: DnsProviderOption }): VNode => {
return (
<div class="flex items-center">
<NFlex align="center">
{option.label ? (
renderLabel(option)
) : (
@@ -101,7 +101,7 @@ export default defineComponent<DnsProviderSelectProps>({
{props.type === 'dns' ? $t('t_0_1747019621052') : $t('t_0_1746858920894')}
</NText>
)}
</div>
</NFlex>
)
}

View File

@@ -180,6 +180,7 @@ nodeOptions[NOTIFY] = () =>
provider_id: '',
subject: '',
body: '',
skip: false,
},
childNode: null,
},

View File

@@ -246,6 +246,7 @@ export interface DeployConfig<
| 'aliyun-cdn'
| 'aliyun-oss'
| 'aliyun-waf'
| 'aliyun-esa'
| 'doge-cdn'
| 'baidu-cdn'
| 'qiniu-cdn'
@@ -308,6 +309,11 @@ export interface DeployStorageConfig {
bucket: string
}
// 部署阿里云ESA
export interface DeployAliyunESAConfig {
site_id: string
}
// 部署节点配置雷池WAF
export interface DeploySafelineConfig {
[key: string]: unknown
@@ -340,6 +346,7 @@ export type DeployNodeConfig = DeployConfig<
| DeployCDNConfig // 部署节点配置腾讯云CDN/阿里云CDN
| DeployWAFConfig // 部署节点配置阿里云WAF
| DeployStorageConfig // 部署节点配置腾讯云COS/阿里云OSS
| DeployAliyunESAConfig // 部署节点配置阿里云ESA
| DeploySafelineConfig // 部署节点配置雷池WAF
| DeploySafelineSiteConfig // 部署节点配置雷池WAF站点
| DeployBTPanelDockerSiteConfig // 部署节点配置宝塔docker站点
@@ -358,6 +365,7 @@ interface NotifyNodeConfig {
provider_id: string
subject: string
body: string
skip: boolean // 当结果来源为跳过状态时,跳过/继续发送通知true:跳过false:继续
}
// 定义上传节点配置类型

View File

@@ -87,6 +87,7 @@ export const ApiProjectConfig: Record<string, ApiProjectType> = {
cdn: { name: $t('t_16_1745735766712') },
oss: { name: $t('t_2_1746697487164') },
waf: { name: $t('t_10_1744958860078') },
esa: { name: '阿里云ESA' },
},
sort: 6,
},

View File

@@ -1,4 +1,9 @@
{
"t_11_1747280809178": "تخطي",
"t_0_1750320239265": "الرجاء إدخال مفتاح الوصول DogeCloud",
"t_1_1750320241427": "الرجاء إدخال مفتاح SecretKey الخاص بـ DogeCloud",
"t_2_1750320237611": "تخطي إرسال الإشعار",
"t_3_1750320237991": "إرسال",
"t_0_1744098811152": "تحذير: لقد دخلتم منطقة غير معروفة، الصفحة التي تحاول زيارتها غير موجودة، يرجى الضغط على الزر للعودة إلى الصفحة الرئيسية.",
"t_1_1744098801860": "رجوع إلى الصفحة الرئيسية",
"t_2_1744098804908": "نصيحة أمنية: إذا كنت تعتقد أن هذا خطأ، يرجى الاتصال بالمدير على الفور",
@@ -529,7 +534,6 @@
"t_8_1747280809382": "دلو",
"t_9_1747280810169": "نشر متكرر",
"t_10_1747280816952": "عندما يكون الشهادة هي نفسها كما في النشر الأخير وكان النشر الأخير ناجحًا",
"t_11_1747280809178": "تخطي",
"t_12_1747280809893": "لا تتخطى",
"t_13_1747280810369": "إعادة النشر",
"t_14_1747280811231": "بحث نوع النشر",

View File

@@ -1,4 +1,9 @@
{
"t_11_1747280809178": "Skip",
"t_0_1750320239265": "Please enter DogeCloud AccessKey",
"t_1_1750320241427": "Please enter DogeCloud SecretKey",
"t_2_1750320237611": "Skip sending notification",
"t_3_1750320237991": "Send",
"t_0_1744098811152": "Warning: You have entered an unknown area, the page you are visiting does not exist, please click the button to return to the homepage.",
"t_1_1744098801860": "Return Home",
"t_2_1744098804908": "Safety Tip: If you think this is an error, please contact the administrator immediately",
@@ -529,7 +534,6 @@
"t_8_1747280809382": "Bucket",
"t_9_1747280810169": "Repeated deployment",
"t_10_1747280816952": "When the certificate is the same as the last deployment and the last deployment was successful",
"t_11_1747280809178": "Skip",
"t_12_1747280809893": "Do not skip",
"t_13_1747280810369": "Re-deployment",
"t_14_1747280811231": "Search deployment type",

View File

@@ -1,4 +1,9 @@
{
"t_11_1747280809178": "Saltar",
"t_0_1750320239265": "Por favor ingrese la AccessKey de DogeCloud",
"t_1_1750320241427": "Por favor ingrese la SecretKey de DogeCloud",
"t_2_1750320237611": "Omitir el envío de notificación",
"t_3_1750320237991": "Enviar",
"t_0_1744098811152": "Advertencia: Ha ingresado a una zona desconocida, la página que intenta visitar no existe, por favor, haga clic en el botón para regresar a la página de inicio.",
"t_1_1744098801860": "Volver al inicio",
"t_2_1744098804908": "Consejo de seguridad: Si piensa que es un error, póngase en contacto con el administrador inmediatamente",
@@ -529,7 +534,6 @@
"t_8_1747280809382": "Cubo",
"t_9_1747280810169": "Implementación repetida",
"t_10_1747280816952": "Cuando el certificado es el mismo que el último despliegue y el último despliegue fue exitoso",
"t_11_1747280809178": "Saltar",
"t_12_1747280809893": "No omitir",
"t_13_1747280810369": "Redespliegue",
"t_14_1747280811231": "Buscar tipo de implementación",

View File

@@ -1,4 +1,9 @@
{
"t_11_1747280809178": "Passer",
"t_0_1750320239265": "Veuillez entrer la clé d'accès DogeCloud",
"t_1_1750320241427": "Veuillez entrer la SecretKey de DogeCloud",
"t_2_1750320237611": "Ignorer l'envoi de notification",
"t_3_1750320237991": "Envoyer",
"t_0_1744098811152": "Avertissement : Vous avez entré dans une zone inconnue, la page que vous visitez n'existe pas, veuillez cliquer sur le bouton pour revenir à la page d'accueil.",
"t_1_1744098801860": "Retour à l'accueil",
"t_2_1744098804908": "Avis de sécurité : Si vous pensez que c'est une erreur, veuillez contacter l'administrateur immédiatement",
@@ -529,7 +534,6 @@
"t_8_1747280809382": "Seau",
"t_9_1747280810169": "Déploiement répété",
"t_10_1747280816952": "Lorsque le certificat est identique au dernier déploiement et que le dernier déploiement a réussi",
"t_11_1747280809178": "Passer",
"t_12_1747280809893": "Ne pas sauter",
"t_13_1747280810369": "Redéploiement",
"t_14_1747280811231": "Rechercher le type de déploiement",

View File

@@ -1,4 +1,9 @@
{
"t_11_1747280809178": "スキップ",
"t_0_1750320239265": "DogeCloudのAccessKeyを入力してください",
"t_1_1750320241427": "ドージークラウドのSecretKeyを入力してください",
"t_2_1750320237611": "通知の送信をスキップ",
"t_3_1750320237991": "送信",
"t_0_1744098811152": "警告:未知のエリアに進入しました。アクセスしようとしたページは存在しません。ボタンをクリックしてホームページに戻ってください。",
"t_1_1744098801860": "ホームに戻る",
"t_2_1744098804908": "安全注意:これが誤りだと思われる場合は、すぐに管理者に連絡してください",
@@ -529,7 +534,6 @@
"t_8_1747280809382": "バケット",
"t_9_1747280810169": "重複デプロイ",
"t_10_1747280816952": "前回の展開と同じ証明書で、前回の展開が成功した場合",
"t_11_1747280809178": "スキップ",
"t_12_1747280809893": "スキップしない",
"t_13_1747280810369": "再展開",
"t_14_1747280811231": "展開タイプを検索",

View File

@@ -1,4 +1,9 @@
{
"t_11_1747280809178": "건너뛰기",
"t_0_1750320239265": "DogeCloud AccessKey를 입력하세요",
"t_1_1750320241427": "도지클라우드 SecretKey를 입력하세요",
"t_2_1750320237611": "알림 보내기 건너뛰기",
"t_3_1750320237991": "보내기",
"t_0_1744098811152": "경고: 알 수 없는 영역에 진입했습니다. 방문하려는 페이지가 존재하지 않습니다. 버튼을 클릭하여 홈페이지로 돌아가세요。",
"t_1_1744098801860": "홈으로 돌아가기",
"t_2_1744098804908": "안전 유의사항: 이가 오류라면 즉시 관리자에게 연락하십시오",
@@ -529,7 +534,6 @@
"t_8_1747280809382": "버킷",
"t_9_1747280810169": "반복 배포",
"t_10_1747280816952": "이전 배포와 동일한 인증서이며 이전 배포가 성공한 경우",
"t_11_1747280809178": "건너뛰기",
"t_12_1747280809893": "건너뛰지 않음",
"t_13_1747280810369": "재배치",
"t_14_1747280811231": "배포 유형 검색",

View File

@@ -1,4 +1,9 @@
{
"t_11_1747280809178": "Pular",
"t_0_1750320239265": "Por favor, insira o AccessKey do DogeCloud",
"t_1_1750320241427": "Por favor, insira a SecretKey do DogeCloud",
"t_2_1750320237611": "Pular envio de notificação",
"t_3_1750320237991": "Enviar",
"t_0_1744098811152": "Aviso: Você entrou em uma área desconhecida, a página que você está visitando não existe, por favor, clique no botão para voltar para a página inicial.",
"t_1_1744098801860": "Voltar para a homepage",
"t_2_1744098804908": "Dica de Segurança: Se você acha que isso é um erro, entre em contato com o administrador imediatamente",
@@ -529,7 +534,6 @@
"t_8_1747280809382": "Balde",
"t_9_1747280810169": "Implantações repetidas",
"t_10_1747280816952": "Quando o certificado é o mesmo da última implantação e a última implantação foi bem-sucedida",
"t_11_1747280809178": "Pular",
"t_12_1747280809893": "Não pular",
"t_13_1747280810369": "Reimplantação",
"t_14_1747280811231": "Pesquisar tipo de implantação",

View File

@@ -1,4 +1,9 @@
{
"t_11_1747280809178": "Пропустить",
"t_0_1750320239265": "Пожалуйста, введите DogeCloud AccessKey",
"t_1_1750320241427": "Пожалуйста, введите SecretKey DogeCloud",
"t_2_1750320237611": "Пропустить отправку уведомления",
"t_3_1750320237991": "Отправить",
"t_0_1744098811152": "Предупреждение: Вы вошли в неизвестную зону, посещаемая страница не существует, пожалуйста, нажмите кнопку, чтобы вернуться на главную страницу.",
"t_1_1744098801860": "Вернуться на главную",
"t_2_1744098804908": "Совет по безопасности: Если вы считаете, что это ошибка, немедленно свяжитесь с администратором",
@@ -529,7 +534,6 @@
"t_8_1747280809382": "Ведро",
"t_9_1747280810169": "Повторное развертывание",
"t_10_1747280816952": "Когда сертификат совпадает с последним развертыванием и последнее развертывание было успешным",
"t_11_1747280809178": "Пропустить",
"t_12_1747280809893": "Не пропускать",
"t_13_1747280810369": "Повторное развертывание",
"t_14_1747280811231": "Поиск типа развертывания",

View File

@@ -1,4 +1,9 @@
{
"t_11_1747280809178": "跳过",
"t_0_1750320239265": "请输入多吉云AccessKey",
"t_1_1750320241427": "请输入多吉云SecretKey",
"t_2_1750320237611": "跳过发送通知",
"t_3_1750320237991": "发送",
"t_0_1744098811152": "警告:您已进入未知区域,所访问的页面不存在,请点击按钮返回首页。",
"t_1_1744098801860": "返回首页",
"t_2_1744098804908": "安全提示:如果您认为这是个错误,请立即联系管理员",
@@ -529,7 +534,6 @@
"t_8_1747280809382": "存储桶",
"t_9_1747280810169": "重复部署",
"t_10_1747280816952": "当与上次部署的证书相同且上次部署成功时",
"t_11_1747280809178": "跳过",
"t_12_1747280809893": "不跳过",
"t_13_1747280810369": "重新部署",
"t_14_1747280811231": "搜索部署类型",

View File

@@ -1,4 +1,9 @@
{
"t_11_1747280809178": "跳過",
"t_0_1750320239265": "請輸入多吉雲AccessKey",
"t_1_1750320241427": "請輸入多吉雲SecretKey",
"t_2_1750320237611": "跳過發送通知",
"t_3_1750320237991": "發送",
"t_0_1744098811152": "警告:您已進入未知區域,所訪問的頁面不存在,請點擊按鈕返回首頁。",
"t_1_1744098801860": "返回首頁",
"t_2_1744098804908": "安全提示:如果您認為這是個錯誤,請立即聯繫管理員",
@@ -528,7 +533,6 @@
"t_8_1747280809382": "儲存桶",
"t_9_1747280810169": "重複部署",
"t_10_1747280816952": "當與上次部署的證書相同且上次部署成功時",
"t_11_1747280809178": "跳過",
"t_12_1747280809893": "不跳過",
"t_13_1747280810369": "重新部署",
"t_14_1747280811231": "搜尋部署類型",

View File

@@ -268,8 +268,8 @@ export interface JdcloudAccessConfig {
* 多吉云授权配置
*/
export interface DogeAccessConfig {
access_key_id: string
access_key_secret: string
access_key: string
secret_key: string
}
/**
@@ -361,6 +361,8 @@ export interface AcmeAccountDeleteParams {
// 保持向后兼容的类型别名
export interface EabListParams {
search?: string
ca?: string
p: number
limit: number
}

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,
}
}