diff --git a/packages/ui/certd-client/src/App.vue b/packages/ui/certd-client/src/App.vue index 7faa70cc..57557ef9 100644 --- a/packages/ui/certd-client/src/App.vue +++ b/packages/ui/certd-client/src/App.vue @@ -1,5 +1,5 @@ - + @@ -8,46 +8,22 @@ diff --git a/packages/ui/certd-client/src/components/cron-editor/index.vue b/packages/ui/certd-client/src/components/cron-editor/index.vue index 3b2956a2..c411b4de 100644 --- a/packages/ui/certd-client/src/components/cron-editor/index.vue +++ b/packages/ui/certd-client/src/components/cron-editor/index.vue @@ -1,120 +1,132 @@ - - - - - - - - - 下次触发时间:{{ nextTime }} - {{ errorMessage }} - + + + + + + + + + {{ t('certd.cron.nextTrigger') }}:{{ nextTime }} + {{ errorMessage }} + + diff --git a/packages/ui/certd-client/src/components/plugins/common/remote-select.vue b/packages/ui/certd-client/src/components/plugins/common/remote-select.vue index a8e55d97..b4953379 100644 --- a/packages/ui/certd-client/src/components/plugins/common/remote-select.vue +++ b/packages/ui/certd-client/src/components/plugins/common/remote-select.vue @@ -195,6 +195,7 @@ watch( const input = (pluginType === "plugin" ? form?.input : form) || {}; const watches = {}; for (const key of props.watches) { + //@ts-ignore watches[key] = input[key]; } return { diff --git a/packages/ui/certd-client/src/components/tutorial/index.vue b/packages/ui/certd-client/src/components/tutorial/index.vue index b37e87e1..d1eec41e 100644 --- a/packages/ui/certd-client/src/components/tutorial/index.vue +++ b/packages/ui/certd-client/src/components/tutorial/index.vue @@ -16,18 +16,23 @@ const slots = defineSlots(); - - 使用教程 + + {{$t('tutorial.title')}} - 使用教程 + {{$t('tutorial.title')}} + diff --git a/packages/ui/certd-client/src/i18n/index.ts b/packages/ui/certd-client/src/i18n/index.ts deleted file mode 100644 index 260002d0..00000000 --- a/packages/ui/certd-client/src/i18n/index.ts +++ /dev/null @@ -1,24 +0,0 @@ -import en from "./locale/en"; -import zh from "./locale/zh_CN"; -import { SupportedLanguagesType } from "/@/vben/locales"; -export const messages = { - "en-US": { - label: "English", - ...en - }, - "zh-CN": { - label: "简体中文", - ...zh - } -}; - -// export default createI18n({ -// legacy: false, -// locale: "zh-cn", -// fallbackLocale: "zh-cn", -// messages -// }); - -export async function loadMessages(lang: SupportedLanguagesType) { - return messages[lang]; -} diff --git a/packages/ui/certd-client/src/i18n/locale/en.ts b/packages/ui/certd-client/src/i18n/locale/en.ts deleted file mode 100644 index 363053dc..00000000 --- a/packages/ui/certd-client/src/i18n/locale/en.ts +++ /dev/null @@ -1,8 +0,0 @@ -export default { - app: { crud: { i18n: { name: "name", city: "city", status: "status" } } }, - fs: { - rowHandle: { - title: "Operation" - } - } -}; diff --git a/packages/ui/certd-client/src/i18n/locale/zh_CN.ts b/packages/ui/certd-client/src/i18n/locale/zh_CN.ts deleted file mode 100644 index bf298990..00000000 --- a/packages/ui/certd-client/src/i18n/locale/zh_CN.ts +++ /dev/null @@ -1,14 +0,0 @@ -export default { - app: { - crud: { i18n: { name: "姓名", city: "城市", status: "状态" } }, - login: { - logoutTip: "确认", - logoutMessage: "确定要注销登录吗?", - }, - }, - fs: { - rowHandle: { - title: "操作列", - }, - }, -}; diff --git a/packages/ui/certd-client/src/layout/components/user-info/index.vue b/packages/ui/certd-client/src/layout/components/user-info/index.vue index 9f2c2e7c..a93ac584 100644 --- a/packages/ui/certd-client/src/layout/components/user-info/index.vue +++ b/packages/ui/certd-client/src/layout/components/user-info/index.vue @@ -1,13 +1,13 @@ - 您好,{{ userStore.getUserInfo?.nickName || userStore.getUserInfo?.username }} + {{ t('user.greeting') }},{{ userStore.getUserInfo?.nickName || userStore.getUserInfo?.username }} - 账号信息 + {{ t('user.profile') }} - 注销登录 + {{ t('user.logout') }} diff --git a/packages/ui/certd-client/src/layout/layout-basic.vue b/packages/ui/certd-client/src/layout/layout-basic.vue index c38fd64d..e6dd1a01 100644 --- a/packages/ui/certd-client/src/layout/layout-basic.vue +++ b/packages/ui/certd-client/src/layout/layout-basic.vue @@ -9,6 +9,9 @@ import { useSettingStore } from "/@/store/settings"; import PageFooter from "./components/footer/index.vue"; import { useRouter } from "vue-router"; import MaxKBChat from "/@/components/ai/index.vue"; +import { useI18n } from "vue-i18n"; + +const { t } = useI18n(); const userStore = useUserStore(); @@ -19,14 +22,14 @@ const menus = computed(() => [ router.push("/certd/mine/user-profile"); }, icon: "fa-solid:book", - text: "账号信息", + text: t("certd.accountInfo"), }, { handler: () => { router.push("/certd/mine/security"); }, icon: "fluent:shield-keyhole-16-regular", - text: "认证安全设置", + text: t("certd.securitySettings"), }, ]); diff --git a/packages/ui/certd-client/src/locales/antdv.ts b/packages/ui/certd-client/src/locales/antdv.ts new file mode 100644 index 00000000..ecc891df --- /dev/null +++ b/packages/ui/certd-client/src/locales/antdv.ts @@ -0,0 +1,18 @@ +import { ref } from "vue"; +import "dayjs/locale/zh-cn"; +import "dayjs/locale/en"; +import zhCN from "ant-design-vue/es/locale/zh_CN"; +import enUS from "ant-design-vue/es/locale/en_US"; +import dayjs from "dayjs"; +export const antdvLocale = ref(zhCN); + +export async function setAntdvLocale(value: string) { + console.log("locale changed:", value); + if (value.startsWith("zh")) { + dayjs.locale("zh-cn"); + antdvLocale.value = zhCN; + } else { + dayjs.locale("en"); + antdvLocale.value = enUS; + } +} diff --git a/packages/ui/certd-client/src/vben/locales/i18n.ts b/packages/ui/certd-client/src/locales/i18n.ts similarity index 90% rename from packages/ui/certd-client/src/vben/locales/i18n.ts rename to packages/ui/certd-client/src/locales/i18n.ts index 73188ada..63210113 100644 --- a/packages/ui/certd-client/src/vben/locales/i18n.ts +++ b/packages/ui/certd-client/src/locales/i18n.ts @@ -1,18 +1,24 @@ import type { App } from "vue"; import type { Locale } from "vue-i18n"; - +import { setAntdvLocale } from "./antdv"; import type { ImportLocaleFn, LoadMessageFn, LocaleSetupOptions, SupportedLanguagesType } from "./typing"; import { unref } from "vue"; import { createI18n } from "vue-i18n"; +import en_US from "./langs/en-US/index"; +import zh_CN from "./langs/zh-CN/index"; import { useSimpleLocale } from "/@/vben/composables"; const i18n = createI18n({ globalInjection: true, legacy: false, - locale: "", - messages: {} + fallbackLocale: "en-US", + locale: "en-US", + messages: { + "zh-CN": zh_CN, + "en-US": en_US, + }, }); const modules = import.meta.glob("./langs/**/*.json"); @@ -83,13 +89,15 @@ function loadLocalesMapFromDir(regexp: RegExp, modules: Record Pro * @param locale */ function setI18nLanguage(locale: Locale) { + setAntdvLocale(locale); + //@ts-ignore i18n.global.locale.value = locale; document?.querySelector("html")?.setAttribute("lang", locale); } async function setupI18n(app: App, options: LocaleSetupOptions = {}) { - const { defaultLocale = "zh-CN" } = options; + const { defaultLocale = "en-US" } = options; // app可以自行扩展一些第三方库和组件库的国际化 loadMessages = options.loadMessages || (async () => ({})); app.use(i18n); @@ -116,6 +124,7 @@ async function loadLocaleMessages(lang: SupportedLanguagesType) { const message = await localesMap[lang]?.(); if (message?.default) { + //@ts-ignore i18n.global.setLocaleMessage(lang, message.default); } @@ -124,5 +133,5 @@ async function loadLocaleMessages(lang: SupportedLanguagesType) { return setI18nLanguage(lang); } - -export { i18n, loadLocaleMessages, loadLocalesMap, loadLocalesMapFromDir, setupI18n }; +export { i18n, loadLocaleMessages, loadLocalesMap, loadLocalesMapFromDir, setupI18n, setI18nLanguage }; +export default i18n; diff --git a/packages/ui/certd-client/src/vben/locales/index.ts b/packages/ui/certd-client/src/locales/index.ts similarity index 80% rename from packages/ui/certd-client/src/vben/locales/index.ts rename to packages/ui/certd-client/src/locales/index.ts index 34539733..3ae503d0 100644 --- a/packages/ui/certd-client/src/vben/locales/index.ts +++ b/packages/ui/certd-client/src/locales/index.ts @@ -1,9 +1,9 @@ -import { i18n, loadLocaleMessages, loadLocalesMap, loadLocalesMapFromDir, setupI18n } from "./i18n"; +import { i18n, loadLocaleMessages, loadLocalesMap, loadLocalesMapFromDir, setupI18n, setI18nLanguage } from "./i18n"; const $t = i18n.global.t; const $te = i18n.global.te; -export { $t, $te, i18n, loadLocaleMessages, loadLocalesMap, loadLocalesMapFromDir, setupI18n }; +export { $t, $te, i18n, loadLocaleMessages, loadLocalesMap, loadLocalesMapFromDir, setupI18n, setI18nLanguage }; export { type ImportLocaleFn, type LocaleSetupOptions, type SupportedLanguagesType } from "./typing"; // export type { CompileError } from "@intlify/core-base"; diff --git a/packages/ui/certd-client/src/locales/langs/en-US/authentication.ts b/packages/ui/certd-client/src/locales/langs/en-US/authentication.ts new file mode 100644 index 00000000..d133bf28 --- /dev/null +++ b/packages/ui/certd-client/src/locales/langs/en-US/authentication.ts @@ -0,0 +1,84 @@ +export default { + welcomeBack: "Welcome Back", + pageTitle: "Plug-and-play Admin system", + pageDesc: "Efficient, versatile frontend template", + loginSuccess: "Login Successful", + loginSuccessDesc: "Welcome Back", + loginSubtitle: "Enter your account details to manage your projects", + selectAccount: "Quick Select Account", + username: "Username", + password: "Password", + usernameTip: "Please enter username", + passwordErrorTip: "Password is incorrect", + passwordTip: "Please enter password", + verifyRequiredTip: "Please complete the verification first", + rememberMe: "Remember Me", + createAnAccount: "Create an Account", + createAccount: "Create Account", + alreadyHaveAccount: "Already have an account?", + accountTip: "Don't have an account?", + signUp: "Sign Up", + signUpSubtitle: "Make managing your applications simple and fun", + confirmPassword: "Confirm Password", + confirmPasswordTip: "The passwords do not match", + agree: "I agree to", + privacyPolicy: "Privacy-policy", + terms: "Terms", + agreeTip: "Please agree to the Privacy Policy and Terms", + goToLogin: "Login instead", + passwordStrength: "Use 8 or more characters with a mix of letters, numbers & symbols", + forgetPassword: "Forget Password?", + forgetPasswordSubtitle: "Enter your email and we'll send you instructions to reset your password", + emailTip: "Please enter email", + emailValidErrorTip: "The email format you entered is incorrect", + sendResetLink: "Send Reset Link", + email: "Email", + qrcodeSubtitle: "Scan the QR code with your phone to login", + qrcodePrompt: "Click 'Confirm' after scanning to complete login", + qrcodeLogin: "QR Code Login", + codeSubtitle: "Enter your phone number to start managing your project", + code: "Security code", + codeTip: "Security code required {0} characters", + mobile: "Mobile", + mobileLogin: "Mobile Login", + mobileTip: "Please enter mobile number", + mobileErrortip: "The phone number format is incorrect", + sendCode: "Get Security code", + sendText: "Resend in {0}s", + thirdPartyLogin: "Or continue with", + loginAgainTitle: "Please Log In Again", + loginAgainSubTitle: "Your login session has expired. Please log in again to continue.", + layout: { + center: "Align Center", + alignLeft: "Align Left", + alignRight: "Align Right", + }, + usernamePlaceholder: "Please enter username/email/phone number", + passwordPlaceholder: "Please enter your password", + mobilePlaceholder: "Please enter your mobile number", + loginButton: "Log In", + forgotAdminPassword: "Forgot admin password?", + registerLink: "Register", + + smsTab: "Login via SMS code", + passwordTab: "Password login", + title: "Change Password", + weakPasswordWarning: "For your account security, please change your password immediately", + changeNow: "Change Now", + successMessage: "Changed successfully", + oldPassword: "Old Password", + oldPasswordRequired: "Please enter the old password", + newPassword: "New Password", + newPasswordRequired: "Please enter the new password", + confirmNewPassword: "Confirm New Password", + confirmNewPasswordRequired: "Please confirm the new password", + changePasswordButton: "Change Password", + enterPassword: "Please enter the password", + newPasswordNotSameOld: "The new password cannot be the same as the old password", + enterPasswordAgain: "Please enter the password again", + passwordsNotMatch: "The two passwords do not match!", + avatar: "Avatar", + nickName: "Nickname", + phoneNumber: "Phone Number", + changePassword: "Change Password", +}; diff --git a/packages/ui/certd-client/src/locales/langs/en-US/certd.ts b/packages/ui/certd-client/src/locales/langs/en-US/certd.ts new file mode 100644 index 00000000..70ec1e23 --- /dev/null +++ b/packages/ui/certd-client/src/locales/langs/en-US/certd.ts @@ -0,0 +1,685 @@ +export default { + app: { + crud: { + i18n: { + name: "name", + city: "city", + status: "status", + }, + }, + }, + fs: { + rowHandle: { + title: "Operation", + }, + }, + order: { + confirmTitle: "Order Confirmation", + package: "Package", + description: "Description", + specifications: "Specifications", + pipeline: "Pipeline", + domain: "Domain", + deployTimes: "Deployments", + duration: "Duration", + price: "Price", + paymentMethod: "Payment Method", + free: "Free", + unit: { + pieces: "pieces", + count: "count", + times: "times", + }, + }, + framework: { + title: "Framework", + home: "Home", + }, + title: "Certificate Automation", + pipeline: "Pipeline", + pipelineEdit: "Edit Pipeline", + history: "Execution History", + certStore: "Certificate Repository", + siteMonitor: "Site Certificate Monitor", + settings: "Settings", + accessManager: "Access Management", + cnameRecord: "CNAME Record Management", + subDomain: "Subdomain Delegation Settings", + pipelineGroup: "Pipeline Group Management", + openKey: "Open API Key", + notification: "Notification Settings", + siteMonitorSetting: "Site Monitor Settings", + userSecurity: "Security Settings", + userProfile: "Account Info", + suite: "Suite", + mySuite: "My Suite", + suiteBuy: "Suite Purchase", + myTrade: "My Orders", + paymentReturn: "Payment Return", + user: { + greeting: "Hello", + profile: "Account Info", + logout: "Logout", + }, + dashboard: { + greeting: "Hello, {name}, welcome to 【{site}】", + latestVersion: "Latest version: {version}", + validUntil: "Valid until:", + tutorialTooltip: "Click to view detailed tutorial", + tutorialText: "Only 3 steps to automatically apply and deploy certificates", + alertMessage: "Certificates and credentials are sensitive. Do not use untrusted online Certd services or images. Always self-host and use official release channels:", + helpDoc: "Help Docs", + pipelineCount: "Number of Certificate Pipelines", + noPipeline: "You have no certificate pipelines yet", + createNow: "Create Now", + managePipeline: "Manage Pipelines", + pipelineStatus: "Pipeline Status", + recentRun: "Recent Run Statistics", + runCount: "Run Count", + expiringCerts: "Soon-to-Expire Certificates", + supportedTasks: "Overview of Supported Deployment Tasks", + }, + steps: { + createPipeline: "Create Certificate Pipeline", + addTask: "Add Deployment Task", + scheduledRun: "Scheduled Run", + }, + customPipeline: "Custom Pipeline", + createCertdPipeline: "Create Certificate Pipeline", + commercialCertHosting: "Commercial Certificate Hosting", + tooltip: { + manualUploadOwnCert: "Manually upload your own certificate for automatic deployment", + noAutoApplyCommercialCert: "Does not automatically apply for commercial certificates", + manualUploadOnUpdate: "Must manually upload once when the certificate is updated", + }, + table: { + confirmDeleteTitle: "Are you sure you want to delete?", + confirmDeleteMessage: "This will delete all data related to the pipeline, including execution history, certificate files, and certificate repository records.", + }, + play: { + runPipeline: "Run Pipeline", + confirm: "Confirm", + confirmTrigger: "Are you sure you want to trigger the run?", + pipelineStarted: "Pipeline has started running", + }, + actions: { + editPipeline: "Edit Pipeline", + editConfigGroup: "Modify Configuration/Group", + viewCertificate: "View Certificate", + downloadCertificate: "Download Certificate", + }, + fields: { + userId: "User ID", + pipelineName: "Pipeline Name", + keyword: "Keyword", + required: "This field is required", + pipelineContent: "Pipeline Content", + scheduledTaskCount: "Scheduled Task Count", + deployTaskCount: "Deployment Task Count", + remainingValidity: "Remaining Validity", + expiryTime: "Expiry Time", + status: "Status", + lastRun: "Last Run", + enabled: "Enabled", + enabledLabel: "Enabled", + disabledLabel: "Disabled", + group: "Group", + type: "Type", + order: "Order Number", + keepHistoryCount: "History Record Retention Count", + keepHistoryHelper: "Number of history records to keep; excess will be deleted", + createTime: "Creation Time", + updateTime: "Update Time", + triggerType: "Trigger Type", + pipelineId: "Pipeline Id", + }, + types: { + certApply: "Certificate Application", + certUpload: "Certificate Upload", + custom: "Custom", + }, + myPipelines: "My Pipelines", + selectedCount: "Selected {count} items", + batchDelete: "Batch Delete", + batchForceRerun: "Force Rerun", + applyCertificate: "Apply for Certificate", + pipelineExecutionRecords: "Pipeline Execution Records", + confirm: "Confirm", + confirmBatchDeleteContent: "Are you sure you want to batch delete these {count} records?", + deleteSuccess: "Delete successful", + pleaseSelectRecords: "Please select records first", + triggerTypes: { + manual: "Manual Execution", + timer: "Scheduled Execution", + }, + sysResources: { + sysRoot: "System Management", + sysConsole: "Console", + sysSettings: "System Settings", + cnameSetting: "CNAME Service Settings", + emailSetting: "Email Server Settings", + siteSetting: "Site Personalization", + headerMenus: "Top Menu Settings", + sysAccess: "System-level Authorization", + sysPlugin: "Plugin Management", + sysPluginEdit: "Edit Plugin", + sysPluginConfig: "Certificate Plugin Configuration", + accountBind: "Account Binding", + permissionManager: "Permission Management", + roleManager: "Role Management", + userManager: "User Management", + suiteManager: "Suite Management", + suiteSetting: "Suite Settings", + orderManager: "Order Management", + userSuites: "User Suites", + }, + certificateRepo: { + title: "Certificate Repository", + sub: "Certificates generated from pipeline", + }, + + certificateNotGenerated: "Certificate not yet generated, please run the pipeline first", + viewCertificateTitle: "View Certificate", + close: "Close", + viewCert: { + title: "View Certificate", + }, + download: { + title: "Download Certificate", + }, + source: "Source Code", + github: "GitHub", + gitee: "Gitee", + cron: { + clearTip: "Clear Selection", + nextTrigger: "Next Trigger Time", + tip: "Please set a valid cron expression first", + }, + cronForm: { + title: "Scheduled Script", + helper: "Click the button above to select the time for daily execution.\nIt is recommended to run once a day. Tasks will be skipped if the certificate is not expiring.", + required: "This field is required", + }, + email: { + title: "Recipient Email", + helper: "Enter your recipient email addresses. Multiple addresses are supported.", + required: "This field is required", + }, + plugin: { + selectTitle: "Certificate Apply Plugin", + jsAcme: "JS-ACME: Easy to use, powerful features [Recommended]", + legoAcme: "Lego-ACME: Based on Lego, supports a wide range of DNS providers, suitable for users familiar with Lego", + }, + pipelineForm: { + createTitle: "Create Certificate Pipeline", + moreParams: "More Parameters", + triggerCronTitle: "Scheduled Trigger", + triggerCronHelper: + "Click the button above to choose a daily execution time.\nIt is recommended to trigger once per day. The task will be skipped if the certificate has not expired and will not be executed repeatedly.", + notificationTitle: "Failure Notification", + notificationHelper: "Get real-time alerts when the task fails", + groupIdTitle: "Pipeline Group", + }, + notificationDefault: "Use Default Notification", + monitor: { + title: "Site Certificate Monitoring", + description: "Check website certificates' expiration at 0:00 daily; reminders sent 10 days before expiration (using default notification channel);", + settingLink: "Site Monitoring Settings", + limitInfo: "Basic edition limited to 1, professional and above unlimited, current", + checkAll: "Check All", + confirmTitle: "Confirm", + confirmContent: "Confirm to trigger check for all site certificates?", + checkSubmitted: "Check task submitted", + pleaseRefresh: "Please refresh the page later to see the results", + siteName: "Site Name", + enterSiteName: "Please enter the site name", + domain: "Domain", + enterDomain: "Please enter the domain", + enterValidDomain: "Please enter a valid domain", + httpsPort: "HTTPS Port", + enterPort: "Please enter the port", + certInfo: "Certificate Info", + issuer: "Issuer", + certDomains: "Certificate Domains", + certProvider: "Issuer", + certStatus: "Certificate Status", + status: { + ok: "Valid", + expired: "Expired", + }, + certExpiresTime: "Certificate Expiration", + expired: "expired", + days: "days", + lastCheckTime: "Last Check Time", + disabled: "Enable/Disable", + ipCheck: "Enable IP Check", + selectRequired: "Please select", + ipCheckConfirm: "Are you sure to {status} IP check?", + ipCount: "IP Count", + checkStatus: "Check Status", + pipelineId: "Linked Pipeline ID", + certInfoId: "Certificate ID", + checkSubmittedRefresh: "Check task submitted. Please refresh later to view the result.", + ipManagement: "IP Management", + bulkImport: "Bulk Import", + basicLimitError: "Basic version allows only one monitoring site. Please upgrade to the Pro version.", + limitExceeded: "Sorry, you can only create up to {max} monitoring records. Please purchase or upgrade your plan.", + }, + checkStatus: { + success: "Success", + checking: "Checking", + error: "Error", + }, + domainList: { + title: "Domain List", + helper: "Format: domain:port:name, one per line. Port and name are optional.\nExamples:\nwww.baidu.com:443:Baidu\nwww.taobao.com::Taobao\nwww.google.com", + required: "Please enter domains to import", + placeholder: "www.baidu.com:443:Baidu\nwww.taobao.com::Taobao\nwww.google.com\n", + }, + accountInfo: "Account Information", + securitySettings: "Security & Settings", + confirmDisable2FA: "Are you sure you want to disable two-factor authentication login?", + disabledSuccess: "Disabled successfully", + saveSuccess: "Saved successfully", + twoFactorAuth: "2FA Two-Factor Authentication Login", + rebind: "Rebind", + twoFactorAuthHelper: "Enable or disable two-factor authentication login", + bindDevice: "Bind Device", + step1: "1. Install any authenticator app, for example:", + tooltipGoogleServiceError: "If you get a Google service not found error, you can install KK Google Assistant", + step2: "2. Scan the QR code to add the account", + step3: "3. Enter the verification code", + inputVerifyCode: "Please enter the verification code", + cancel: "Cancel", + authorizationManagement: "Authorization Management", + manageThirdPartyAuth: "Manage third-party system authorization information", + name: "Name", + pleaseEnterName: "Please enter the name", + nameHelper: "Fill in as you like, useful to distinguish when multiple authorizations of the same type exist", + level: "Level", + system: "System", + usera: "User", + nickName: "Nickname", + max50Chars: "Maximum 50 characters", + myInfo: "My Information", + siteMonitorSettings: "Site Monitor Settings", + notificationChannel: "Notification Channel", + setNotificationChannel: "Set the notification channel", + retryTimes: "Retry Times", + monitorRetryTimes: "Number of retry attempts for monitoring requests", + monitorCronSetting: "Monitoring Schedule", + cronTrigger: "Scheduled trigger for monitoring", + save: "Save", + editSchedule: "Edit Schedule", + timerTrigger: "Timer Trigger", + schedule: "Schedule", + selectCron: "Please select a schedule Cron", + batchEditSchedule: "Batch Edit Schedule", + editTrigger: "Edit Trigger", + triggerName: "Trigger Name", + requiredField: "This field is required", + type: "Type", + enterName: "Please enter a name", + confirmDeleteTrigger: "Are you sure you want to delete this trigger?", + notificationType: "Notification Type", + selectNotificationType: "Please select a notification type", + notificationName: "Notification Name", + helperNotificationName: "Fill freely, helps to distinguish when multiple notifications of the same type exist", + isDefault: "Is Default", + yes: "Yes", + no: "No", + selectIsDefault: "Please select if default", + prompt: "Prompt", + confirmSetDefaultNotification: "Are you sure to set as default notification?", + test: "Test", + scope: "Scope", + scopeOpenApiOnly: "Open API Only", + scopeFullAccount: "Full Account Permissions", + required: "This field is required", + scopeHelper: "Open API only allows access to open APIs; full account permissions allow access to all APIs", + add: "Generate New Key", + gen: { + text: "API Test", + title: "x-certd-token", + okText: "Confirm", + contentPart1: "Test the x-certd-token below, you can use it within 3 minutes to test ", + openApi: "Open API", + contentPart2: " request testing", + }, + pending_cname_setup: "Pending CNAME setup", + validating: "Validating", + validation_successful: "Validation successful", + validation_failed: "Validation failed", + validation_timed_out: "Validation timed out", + proxied_domain: "Proxied Domain", + host_record: "Host Record", + please_set_cname: "Please set CNAME", + cname_service: "CNAME Service", + default_public_cname: "Default public CNAME service, you can also ", + customize_cname: "Customize CNAME Service", + public_cname: "Public CNAME", + custom_cname: "Custom CNAME", + validate: "Validate", + validation_started: "Validation started, please wait patiently", + click_to_validate: "Click to Validate", + all: "All", + cname_feature_guide: "CNAME feature principle and usage guide", + batch_delete: "Batch Delete", + confirm_delete_count: "Are you sure to delete these {count} records in batch?", + delete_successful: "Delete successful", + please_select_records: "Please select records first", + edit_notification: "Edit Notification", + other_notification_method: "Other Notification Method", + trigger_time: "Trigger Time", + start_time: "At Start", + success_time: "On Success", + fail_to_success_time: "Fail to Success", + fail_time: "On Failure", + helper_suggest_fail_only: "It is recommended to select only 'On Failure' and 'Fail to Success'", + notification_config: "Notification Configuration", + please_select_notification: "Please select a notification method", + please_select_type: "Please select type", + please_select_trigger_time: "Please select notification trigger time", + please_select_notification_config: "Please select notification configuration", + confirm_delete_trigger: "Are you sure you want to delete this trigger?", + gift_package: "Gift Package", + package_name: "Package Name", + click_to_select: "Click to select", + please_select_package: "Please select a package", + package: "Package", + addon_package: "Addon Package", + domain_count: "Domain Count", + unit_count: "pcs", + field_required: "This field is required", + pipeline_count: "Pipeline Count", + unit_item: "items", + deploy_count: "Deploy Count", + unit_times: "times", + monitor_count: "Certificate Monitor Count", + duration: "Duration", + status: "Status", + active_time: "Activation Time", + expires_time: "Expiration Time", + is_present: "Is Present", + is_present_yes: "Yes", + is_present_no: "No", + basicInfo: "Basic Information", + titlea: "Title", + disabled: "Disabled", + ordera: "Order", + supportBuy: "Support Purchase", + intro: "Introduction", + packageContent: "Package Content", + maxDomainCount: "Max Domain Count", + maxPipelineCount: "Max Pipeline Count", + maxDeployCount: "Max Deploy Count", + maxMonitorCount: "Max Monitor Count", + price: "Price", + durationPrices: "Duration Prices", + packageName: "Package Name", + addon: "Addon", + typeHelper: "Suite: Only the most recently purchased one is active at a time\nAddon: Multiple can be purchased, effective immediately without affecting the suite\nThe quantities of suite and addon can be accumulated", + domainCount: "Domain Count", + pipelineCount: "Pipeline Count", + unitPipeline: "pipelines", + deployCount: "Deployment Count", + unitDeploy: "times", + monitorCount: "Certificate Monitor Count", + unitCount: "pcs", + durationPriceTitle: "Duration and Price", + selectDuration: "Select Duration", + supportPurchase: "Support Purchase", + cannotPurchase: "Cannot Purchase", + shelfStatus: "Shelf Status", + onShelf: "On Shelf", + offShelf: "Off Shelf", + orderHelper: "Smaller values appear first", + description: "Description", + createTime: "Creation Time", + updateTime: "Update Time", + edit: "Edit", + groupName: "Group Name", + enterGroupName: "Please enter group name", + subdomainHosting: "Subdomain Hosting", + subdomainHostingHint: "When your domain has subdomain hosting set, you need to create records here, otherwise certificate application will fail", + batchDeleteConfirm: "Are you sure to batch delete these {count} records?", + selectRecordFirst: "Please select records first", + subdomainHosted: "Hosted Subdomain", + subdomainHelpText: "If you don't understand what subdomain hosting is, please refer to the documentation ", + subdomainManagement: "Subdomain Management", + isDisabled: "Is Disabled", + enabled: "Enabled", + uploadCustomCert: "Upload Custom Certificate", + sourcee: "Source", + sourcePipeline: "Pipeline", + sourceManualUpload: "Manual Upload", + domains: "Domains", + enterDomain: "Please enter domain", + validDays: "Valid Days", + expires: " expires", + days: " days", + expireTime: "Expiration Time", + certIssuer: "Certificate Issuer", + applyTime: "Application Time", + relatedPipeline: "Related Pipeline", + statusSuccess: "Success", + statusChecking: "Checking", + statusError: "Error", + actionImportBatch: "Batch Import", + actionSyncIp: "Sync IP", + modalTitleSyncIp: "Sync IP", + modalContentSyncIp: "Are you sure to sync IP?", + notificationSyncComplete: "Sync Complete", + actionCheckAll: "Check All", + modalTitleConfirm: "Confirm", + modalContentCheckAll: "Confirm to trigger checking all IP site's certificates?", + notificationCheckSubmitted: "Check task submitted", + notificationCheckDescription: "Please refresh later to see results", + tooltipCheckNow: "Check Now", + notificationCheckSubmittedPleaseRefresh: "Check task submitted, please refresh later", + columnId: "ID", + columnIp: "IP", + helperIpCname: "Supports entering CNAME domain name", + ruleIpRequired: "Please enter IP", + columnCertDomains: "Certificate Domains", + columnCertProvider: "Issuer", + columnCertStatus: "Certificate Status", + statusNormal: "Normal", + statusExpired: "Expired", + columnCertExpiresTime: "Certificate Expiration Time", + expired: "expired", + columnCheckStatus: "Check Status", + columnLastCheckTime: "Last Check Time", + columnSource: "Source", + sourceSync: "Sync", + sourceManual: "Manual", + sourceImport: "Import", + columnDisabled: "Enabled/Disabled", + columnRemark: "Remark", + pluginFile: "Plugin File", + selectPluginFile: "Select plugin file", + overrideSameName: "Override same name", + override: "Override", + noOverride: "No override", + overrideHelper: "If a plugin with the same name exists, override it directly", + importPlugin: "Import Plugin", + operationSuccess: "Operation successful", + customPlugin: "Custom Plugin", + import: "Import", + export: "Export", + pluginType: "Plugin Type", + auth: "Authorization", + dns: "DNS", + deployPlugin: "Deploy Plugin", + icon: "Icon", + pluginName: "Plugin Name", + pluginNameHelper: "Must be English letters or digits, camelCase with type prefix\nExample: AliyunDeployToCDN\nDo not modify name once plugin is used", + pluginNameRuleMsg: "Must be English letters or digits, camelCase with type prefix", + author: "Author", + authorHelper: "Used as prefix when uploading to plugin store, e.g., greper/pluginName", + authorRuleMsg: "Must be English letters or digits", + titleHelper: "Plugin name in Chinese", + descriptionHelper: "Description of the plugin", + builtIn: "Built-in", + custom: "Custom", + store: "Store", + version: "Version", + pluginDependencies: "Plugin Dependencies", + pluginDependenciesHelper: "Dependencies to install first in format: [author/]pluginName[:version]", + editableRunStrategy: "Editable Run Strategy", + editable: "Editable", + notEditable: "Not Editable", + runStrategy: "Run Strategy", + normalRun: "Normal Run", + skipOnSuccess: "Skip on success (Deploy task)", + defaultRunStrategyHelper: "Default run strategy", + enableDisable: "Enable/Disable", + clickToToggle: "Click to toggle enable/disable", + confirmToggle: "Are you sure to", + disable: "disable", + enable: "enable", + pluginGroup: "Plugin Group", + icpRegistrationNumber: "ICP Registration Number", + icpPlaceholder: "Guangdong ICP xxxxxxx Number", + publicSecurityRegistrationNumber: "Public Security Registration Number", + publicSecurityPlaceholder: "Beijing Public Security xxxxxxx Number", + enableAssistant: "Enable Assistant", + allowCrawlers: "Allow Crawlers", + httpProxy: "HTTP Proxy", + httpProxyPlaceholder: "http://192.168.1.2:18010/", + httpProxyHelper: "Configure when some websites are blocked", + httpsProxy: "HTTPS Proxy", + httpsProxyPlaceholder: "http://192.168.1.2:18010/", + saveThenTestTitle: "Save first, then click test", + testButton: "Test", + httpsProxyHelper: "Usually both proxies are the same, save first then test", + dualStackNetwork: "Dual Stack Network", + default: "Default", + ipv4Priority: "IPv4 Priority", + ipv6Priority: "IPv6 Priority", + dualStackNetworkHelper: "If IPv6 priority is selected, enable IPv6 in docker-compose.yaml", + enableCommonCnameService: "Enable Public CNAME Service", + commonCnameHelper: "Allow use of public CNAME service. If disabled and no custom CNAME service is set, CNAME proxy certificate application will not work.", + saveButton: "Save", + stopSuccess: "Stopped successfully", + google: "Google", + baidu: "Baidu", + success: "Success", + testFailed: "Test Failed", + testCompleted: "Test Completed", + manageOtherUserPipeline: "Manage other users' pipelines", + limitUserPipelineCount: "Limit user pipeline count", + limitUserPipelineCountHelper: "0 means no limit", + enableSelfRegistration: "Enable self-registration", + enableUserValidityPeriod: "Enable user validity period", + userValidityPeriodHelper: "Users can use normally within validity; pipelines disabled after expiry", + enableUsernameRegistration: "Enable username registration", + enableEmailRegistration: "Enable email registration", + proFeature: "Pro feature", + emailServerSetup: "Set up email server", + enableSmsLoginRegister: "Enable SMS login and registration", + commFeature: "Commercial feature", + smsProvider: "SMS provider", + aliyunSms: "Aliyun SMS", + yfySms: "YFY SMS", + smsTest: "SMS test", + testMobilePlaceholder: "Enter test mobile number", + saveThenTest: "Save first then test", + enterTestMobile: "Please enter test mobile number", + sendSuccess: "Sent successfully", + atLeastOneLoginRequired: "At least one of password login or SMS login must be enabled", + fieldRequired: "This field is required", + siteHide: "Site Hide", + enableSiteHide: "Enable Site Hide", + siteHideDescription: "You can disable site accessibility normally and enable it when needed to enhance site security", + helpDoc: "Help Document", + randomAddress: "Random Address", + siteHideUrlHelper: "After the site is hidden, you need to visit this URL to unlock to access normally", + fullUnlockUrl: "Full Unlock URL", + saveThisUrl: "Please save this URL carefully", + unlockPassword: "Unlock Password", + unlockPasswordHelper: "Password needed to unlock the hide; set on first time or reset when filled", + autoHideTime: "Auto Hide Time", + autoHideTimeHelper: "Minutes without requests before auto hiding", + hideOpenApi: "Hide Open API", + hideOpenApiHelper: "Whether to hide open APIs; whether to expose /api/v1 prefixed endpoints", + hideSiteImmediately: "Hide Site Immediately", + hideImmediately: "Hide Immediately", + confirmHideSiteTitle: "Are you sure to hide the site immediately?", + confirmHideSiteContent: "After hiding, the site will be inaccessible. Please operate cautiously.", + siteHiddenSuccess: "Site has been hidden", + emailServerSettings: "Email Server Settings", + setEmailSendingServer: "Set the email sending server", + useCustomEmailServer: "Use Custom Email Server", + smtpDomain: "SMTP Domain", + pleaseEnterSmtpDomain: "Please enter SMTP domain or IP", + smtpPort: "SMTP Port", + pleaseEnterSmtpPort: "Please enter SMTP port", + username: "Username", + pleaseEnterUsername: "Please enter username", + password: "Password", + pleaseEnterPassword: "Please enter password", + qqEmailAuthCodeHelper: "If using QQ email, get an authorization code in QQ email settings as the password", + senderEmail: "Sender Email", + pleaseEnterSenderEmail: "Please enter sender email", + useSsl: "Use SSL", + sslPortNote: "SSL and non-SSL SMTP ports are different, please adjust port accordingly", + ignoreCertValidation: "Ignore Certificate Validation", + useOfficialEmailServer: "Use Official Email Server", + useOfficialEmailServerHelper: "Send emails directly using the official server to avoid complicated setup", + testReceiverEmail: "Test Receiver Email", + pleaseEnterTestReceiverEmail: "Please enter test receiver email", + saveBeforeTest: "Save before testing", + sendFailHelpDoc: "Failed to send??? ", + emailConfigHelpDoc: "Email configuration help document", + tryOfficialEmailServer: "You can also try using the official email server ↗↗↗↗↗↗↗↗", + pluginManagement: "Plugin Management", + pluginBetaWarning: "Custom plugins are in BETA and may have breaking changes in future", + pleaseSelectRecord: "Please select records first", + permissionManagement: "Permission Management", + adda: "Add", + rootNode: "Root Node", + permissionName: "Permission Name", + enterPermissionName: "Please enter permission name", + permissionCode: "Permission Code", + enterPermissionCode: "Please enter permission code", + max100Chars: "Maximum 100 characters", + examplePermissionCode: "e.g.: sys:user:view", + sortOrder: "Sort Order", + sortRequired: "Sort order is required", + parentNode: "Parent Node", + roleManagement: "Role Management", + assignPermissions: "Assign Permissions", + roleName: "Role Name", + enterRoleName: "Please enter role name", + unlockLogin: "Unlock Login", + notice: "Notice", + confirmUnlock: "Are you sure you want to unlock this user's login?", + unlockSuccess: "Unlock successful", + enterUsername: "Please enter username", + modifyPasswordIfFilled: "Fill in to change the password", + emaila: "Email", + mobile: "Mobile", + avatar: "Avatar", + validTime: "Valid Time", + remark: "Remark", + roles: "Roles", + cnameTitle: "CNAME Service Configuration", + cnameDescription: + "The domain name configured here serves as a proxy for verifying other domains. When other domains apply for certificates, they map to this domain via CNAME for ownership verification. The advantage is that any domain can apply for a certificate this way without providing an AccessSecret.", + cnameLinkText: "CNAME principle and usage instructions", + confirmTitle: "Confirm", + confirmDeleteBatch: "Are you sure you want to delete these {count} records?", + selectRecordsFirst: "Please select records first", + cnameDomain: "CNAME Domain", + cnameDomainPlaceholder: "cname.handsfree.work", + cnameDomainHelper: + "Requires a domain registered with a DNS provider on the right (or you can transfer other domain DNS servers here).\nOnce the CNAME domain is set, it cannot be changed. It is recommended to use a first-level subdomain.", + dnsProvider: "DNS Provider", + dnsProviderAuthorization: "DNS Provider Authorization", + setDefault: "Set Default", + confirmSetDefault: "Are you sure to set as default?", + setAsDefault: "Set as Default", + disabledLabel: "Disabled", + confirmToggleStatus: "Are you sure to {action}?", +}; diff --git a/packages/ui/certd-client/src/locales/langs/en-US/common.ts b/packages/ui/certd-client/src/locales/langs/en-US/common.ts new file mode 100644 index 00000000..d461d909 --- /dev/null +++ b/packages/ui/certd-client/src/locales/langs/en-US/common.ts @@ -0,0 +1,22 @@ +export default { + back: "Back", + backToHome: "Back To Home", + login: "Login", + logout: "Logout", + prompt: "Prompt", + cancel: "Cancel", + confirm: "Confirm", + reset: "Reset", + noData: "No Data", + refresh: "Refresh", + loadingMenu: "Loading Menu", + query: "Search", + search: "Search", + enabled: "Enabled", + disabled: "Disabled", + edit: "Edit", + delete: "Delete", + create: "Create", + yes: "Yes", + no: "No", +}; diff --git a/packages/ui/certd-client/src/locales/langs/en-US/guide.ts b/packages/ui/certd-client/src/locales/langs/en-US/guide.ts new file mode 100644 index 00000000..97ac56a2 --- /dev/null +++ b/packages/ui/certd-client/src/locales/langs/en-US/guide.ts @@ -0,0 +1,71 @@ +export default { + createCertPipeline: { + title: "Create Certificate Application Pipeline", + description: "Demonstrate how to configure a certificate application task", + items: { + tutorialTitle: "Tutorial Demo Content", + tutorialDesc1: "This tutorial demonstrates how to automatically apply for a certificate and deploy it to Nginx", + tutorialDesc2: "Only 3 steps, fully automatic application and deployment", + createTitle: "Create Certificate Pipeline", + createDesc: "Click to add a certificate pipeline and fill in the certificate application information", + successTitle: "Pipeline Created Successfully", + successDesc: "Click manual trigger to apply for the certificate", + nextTitle: "Next, demonstrate how to automatically deploy the certificate", + nextDesc: "If you only need to apply for a certificate, you can stop here", + }, + }, + buttons: { + prev: "Previous Step", + next: "Next Step", + }, + addDeployTask: { + title: "Add Deployment Certificate Task", + description: "Demonstrate deployment of certificate to Nginx", + items: { + addTaskTitle: "Add Certificate Deployment Task", + addTaskDesc1: "Demonstrate automatic deployment of certificate to nginx", + addTaskDesc2: "Our system provides numerous deployment plugins to meet your needs", + fillParamsTitle: "Fill Task Parameters", + fillParamsDesc1: "Fill in the certificate file path on the host", + fillParamsDesc2: "Select SSH login authorization for the host", + activateCertTitle: "Make New Certificate Effective", + activateCertDesc1: "Execute restart script", + activateCertDesc2: "Make the certificate effective", + taskSuccessTitle: "Deployment Task Added Successfully", + taskSuccessDesc: "Now you can run it", + pluginsTitle: "Our System Provides Numerous Deployment Plugins", + pluginsDesc: "You can deploy certificates to various applications and platforms according to your needs", + }, + }, + runAndTestTask: { + runAndTestTitle: "Run and Test", + runAndTestDescription: "Demonstrate pipeline running, view logs, skip on success, etc.", + runTestOnce: "Run a Test", + clickManualTriggerToTest: "Click the manual trigger button to test the run", + viewLogs: "View Logs", + clickTaskToViewStatusAndLogs: "Click the task to view status and logs", + howToTroubleshootFailure: "How to Troubleshoot Failure", + viewErrorLogs: "View error logs", + nginxContainerNotExistFix: "Shows nginx container not found error, fix by changing to correct nginx container name", + executionSuccess: "Execution Success", + retryAfterFix: "After fixing, click manual trigger again to rerun successfully", + autoSkipAfterSuccess: "Auto Skip After Success", + successSkipExplanation: "Successful runs will be skipped automatically, rerun only if parameters or certificates update", + viewCertDeploymentSuccess: "View Certificate Deployment Success", + visitNginxToSeeCert: "Visit website on nginx to see certificate deployed successfully", + downloadCertManualDeploy: "Download Certificate for Manual Deployment", + downloadIfNoAutoDeployPlugin: "If no deployment plugin available, download certificate for manual deployment", + }, + scheduleAndEmailTask: { + title: "Set Scheduled Execution and Email Notifications", + description: "Automatic running", + setSchedule: "Set Scheduled Execution", + pipelineSuccessThenSchedule: "Pipeline tests succeed, then configure scheduled triggers so it runs automatically daily", + recommendDailyRun: "Recommend configuring to run once daily; new certs requested 35 days before expiry and auto-skipped otherwise", + setEmailNotification: "Set Email Notifications", + suggestErrorAndRecoveryEmails: "Suggest listening for 'On Error' and 'Error to Success' to quickly troubleshoot failures (basic version requires mail server setup)", + basicVersionNeedsMailServer: "(basic version requires configuring mail server)", + tutorialEndTitle: "Tutorial End", + thanksForWatching: "Thank you for watching, hope it helps you", + }, +}; diff --git a/packages/ui/certd-client/src/locales/langs/en-US/index.ts b/packages/ui/certd-client/src/locales/langs/en-US/index.ts new file mode 100644 index 00000000..8e1e40be --- /dev/null +++ b/packages/ui/certd-client/src/locales/langs/en-US/index.ts @@ -0,0 +1,19 @@ +import certd from "./certd"; +import authentication from "./authentication"; +import vip from "./vip"; +import tutorial from "./tutorial"; +import preferences from "./preferences"; +import ui from "./ui"; +import guide from "./guide"; +import common from "./common"; + +export default { + certd, + authentication, + vip, + ui, + tutorial, + preferences, + guide, + common, +}; diff --git a/packages/ui/certd-client/src/locales/langs/en-US/preferences.ts b/packages/ui/certd-client/src/locales/langs/en-US/preferences.ts new file mode 100644 index 00000000..669a1e77 --- /dev/null +++ b/packages/ui/certd-client/src/locales/langs/en-US/preferences.ts @@ -0,0 +1,186 @@ +export default { + title: "Preferences", + subtitle: "Customize Preferences & Preview in Real Time", + resetTip: "Data has changed, click to reset", + resetTitle: "Reset Preferences", + resetSuccess: "Preferences reset successfully", + appearance: "Appearance", + layout: "Layout", + content: "Content", + other: "Other", + wide: "Wide", + compact: "Fixed", + followSystem: "Follow System", + vertical: "Vertical", + verticalTip: "Side vertical menu mode", + horizontal: "Horizontal", + horizontalTip: "Horizontal menu mode, all menus displayed at the top", + twoColumn: "Two Column", + twoColumnTip: "Vertical Two Column Menu Mode", + headerSidebarNav: "Header Vertical", + headerSidebarNavTip: "Header Full Width, Sidebar Navigation Mode", + headerTwoColumn: "Header Two Column", + headerTwoColumnTip: "Header Navigation & Sidebar Two Column co-exists", + mixedMenu: "Mixed Menu", + mixedMenuTip: "Vertical & Horizontal Menu Co-exists", + fullContent: "Full Content", + fullContentTip: "Only display content body, hide all menus", + normal: "Normal", + plain: "Plain", + rounded: "Rounded", + copyPreferences: "Copy Preferences", + copyPreferencesSuccessTitle: "Copy successful", + copyPreferencesSuccess: "Copy successful, please override in `src/preferences.ts` under app", + clearAndLogout: "Clear Cache & Logout", + mode: "Mode", + general: "General", + language: "Language", + dynamicTitle: "Dynamic Title", + watermark: "Watermark", + checkUpdates: "Periodic update check", + position: { + title: "Preferences Postion", + header: "Header", + auto: "Auto", + fixed: "Fixed", + }, + sidebar: { + title: "Sidebar", + width: "Width", + visible: "Show Sidebar", + collapsed: "Collpase Menu", + collapsedShowTitle: "Show Menu Title", + autoActivateChild: "Auto Activate SubMenu", + autoActivateChildTip: "`Enabled` to automatically activate the submenu while click menu.", + expandOnHover: "Expand On Hover", + expandOnHoverTip: "When the mouse hovers over menu, \n `Enabled` to expand children menus \n `Disabled` to expand whole sidebar.", + }, + tabbar: { + title: "Tabbar", + enable: "Enable Tab Bar", + icon: "Show Tabbar Icon", + showMore: "Show More Button", + showMaximize: "Show Maximize Button", + persist: "Persist Tabs", + maxCount: "Max Count of Tabs", + maxCountTip: "When the number of tabs exceeds the maximum,\nthe oldest tab will be closed.\n Set to 0 to disable count checking.", + draggable: "Enable Draggable Sort", + wheelable: "Support Mouse Wheel", + middleClickClose: "Close Tab when Mouse Middle Button Click", + wheelableTip: "When enabled, the Tabbar area responds to vertical scrolling events of the scroll wheel.", + styleType: { + title: "Tabs Style", + chrome: "Chrome", + card: "Card", + plain: "Plain", + brisk: "Brisk", + }, + contextMenu: { + reload: "Reload", + close: "Close", + pin: "Pin", + unpin: "Unpin", + closeLeft: "Close Left Tabs", + closeRight: "Close Right Tabs", + closeOther: "Close Other Tabs", + closeAll: "Close All Tabs", + openInNewWindow: "Open in New Window", + maximize: "Maximize", + restoreMaximize: "Restore", + }, + }, + navigationMenu: { + title: "Navigation Menu", + style: "Navigation Menu Style", + accordion: "Sidebar Accordion Menu", + split: "Navigation Menu Separation", + splitTip: "When enabled, the sidebar displays the top bar's submenu", + }, + breadcrumb: { + title: "Breadcrumb", + home: "Show Home Button", + enable: "Enable Breadcrumb", + icon: "Show Breadcrumb Icon", + background: "background", + style: "Breadcrumb Style", + hideOnlyOne: "Hidden when only one", + }, + animation: { + title: "Animation", + loading: "Page Loading", + transition: "Page Transition", + progress: "Page Progress", + }, + theme: { + title: "Theme", + radius: "Radius", + light: "Light", + dark: "Dark", + darkSidebar: "Semi Dark Sidebar", + darkHeader: "Semi Dark Header", + weakMode: "Weak Mode", + grayMode: "Gray Mode", + builtin: { + title: "Built-in", + default: "Default", + violet: "Violet", + pink: "Pink", + rose: "Rose", + skyBlue: "Sky Blue", + deepBlue: "Deep Blue", + green: "Green", + deepGreen: "Deep Green", + orange: "Orange", + yellow: "Yellow", + zinc: "Zinc", + neutral: "Neutral", + slate: "Slate", + gray: "Gray", + custom: "Custom", + }, + }, + header: { + title: "Header", + visible: "Show Header", + modeStatic: "Static", + modeFixed: "Fixed", + modeAuto: "Auto hide & Show", + modeAutoScroll: "Scroll to Hide & Show", + menuAlign: "Menu Align", + menuAlignStart: "Start", + menuAlignEnd: "End", + menuAlignCenter: "Center", + }, + footer: { + title: "Footer", + visible: "Show Footer", + fixed: "Fixed at Bottom", + }, + copyright: { + title: "Copyright", + enable: "Enable Copyright", + companyName: "Company Name", + companySiteLink: "Company Site Link", + date: "Date", + icp: "ICP License Number", + icpLink: "ICP Site Link", + }, + shortcutKeys: { + title: "Shortcut Keys", + global: "Global", + search: "Global Search", + logout: "Logout", + preferences: "Preferences", + }, + widget: { + title: "Widget", + globalSearch: "Enable Global Search", + fullscreen: "Enable Fullscreen", + themeToggle: "Enable Theme Toggle", + languageToggle: "Enable Language Toggle", + notification: "Enable Notification", + sidebarToggle: "Enable Sidebar Toggle", + lockScreen: "Enable Lock Screen", + refresh: "Enable Refresh", + }, +}; diff --git a/packages/ui/certd-client/src/locales/langs/en-US/tutorial.ts b/packages/ui/certd-client/src/locales/langs/en-US/tutorial.ts new file mode 100644 index 00000000..3f5ade3f --- /dev/null +++ b/packages/ui/certd-client/src/locales/langs/en-US/tutorial.ts @@ -0,0 +1,3 @@ +export default { + title: "Tutorial", +}; diff --git a/packages/ui/certd-client/src/locales/langs/en-US/ui.ts b/packages/ui/certd-client/src/locales/langs/en-US/ui.ts new file mode 100644 index 00000000..d9d5efc2 --- /dev/null +++ b/packages/ui/certd-client/src/locales/langs/en-US/ui.ts @@ -0,0 +1,104 @@ +export default { + formRules: { + required: "Please enter {0}", + selectRequired: "Please select {0}", + minLength: "{0} must be at least {1} characters", + maxLength: "{0} can be at most {1} characters", + length: "{0} must be {1} characters long", + alreadyExists: "{0} `{1}` already exists", + startWith: "{0} must start with `{1}`", + invalidURL: "Please input a valid URL", + }, + actionTitle: { + edit: "Modify {0}", + create: "Create {0}", + delete: "Delete {0}", + view: "View {0}", + }, + actionMessage: { + deleteConfirm: "Are you sure to delete {0}?", + deleting: "Deleting {0} ...", + deleteSuccess: "{0} deleted successfully", + operationSuccess: "Operation succeeded", + operationFailed: "Operation failed", + }, + placeholder: { + input: "Please enter", + select: "Please select", + }, + captcha: { + title: "Please complete the security verification", + sliderSuccessText: "Passed", + sliderDefaultText: "Slider and drag", + alt: "Supports img tag src attribute value", + sliderRotateDefaultTip: "Click picture to refresh", + sliderRotateFailTip: "Validation failed", + sliderRotateSuccessTip: "Validation successful, time {0} seconds", + refreshAriaLabel: "Refresh captcha", + confirmAriaLabel: "Confirm selection", + confirm: "Confirm", + pointAriaLabel: "Click point", + clickInOrder: "Please click in order", + }, + iconPicker: { + placeholder: "Select an icon", + search: "Search icon...", + }, + jsonViewer: { + copy: "Copy", + copied: "Copied", + }, + fallback: { + pageNotFound: "Oops! Page Not Found", + pageNotFoundDesc: "Sorry, we couldn't find the page you were looking for.", + forbidden: "Oops! Access Denied", + forbiddenDesc: "Sorry, but you don't have permission to access this page.", + internalError: "Oops! Something Went Wrong", + internalErrorDesc: "Sorry, but the server encountered an error.", + offline: "Offline Page", + offlineError: "Oops! Network Error", + offlineErrorDesc: "Sorry, can't connect to the internet. Check your connection.", + comingSoon: "Coming Soon", + http: { + requestTimeout: "The request timed out. Please try again later.", + networkError: "A network error occurred. Please check your internet connection and try again.", + badRequest: "Bad Request. Please check your input and try again.", + unauthorized: "Unauthorized. Please log in to continue.", + forbidden: "Forbidden. You do not have permission to access this resource.", + notFound: "Not Found. The requested resource could not be found.", + internalServerError: "Internal Server Error. Something went wrong on our end. Please try again later.", + }, + }, + widgets: { + document: "Document", + qa: "Q&A", + setting: "Settings", + logoutTip: "Do you want to logout?", + viewAll: "View All Messages", + notifications: "Notifications", + markAllAsRead: "Make All as Read", + clearNotifications: "Clear", + checkUpdatesTitle: "New Version Available", + checkUpdatesDescription: "Click to refresh and get the latest version", + search: { + title: "Search", + searchNavigate: "Search Navigation", + select: "Select", + navigate: "Navigate", + close: "Close", + noResults: "No Search Results Found", + noRecent: "No Search History", + recent: "Search History", + }, + lockScreen: { + title: "Lock Screen", + screenButton: "Locking", + password: "Password", + placeholder: "Please enter password", + unlock: "Click to unlock", + errorPasswordTip: "Password error, please re-enter", + backToLogin: "Back to login", + entry: "Enter the system", + }, + }, +}; diff --git a/packages/ui/certd-client/src/locales/langs/en-US/vip.ts b/packages/ui/certd-client/src/locales/langs/en-US/vip.ts new file mode 100644 index 00000000..b5658b02 --- /dev/null +++ b/packages/ui/certd-client/src/locales/langs/en-US/vip.ts @@ -0,0 +1,86 @@ +export default { + comm: { + name: "{vipLabel} Activated", + title: "Expires on: {expire}", + nav: "{vipLabel}", + }, + plus: { + name: "Pro Features", + title: "Upgrade to Pro for commercial license", + }, + free: { + comm: { + name: "Pro Features", + title: "Upgrade to Pro for commercial license", + }, + button: { + name: "Advanced Features", + title: "Upgrade to Advanced for more VIP privileges", + }, + nav: { + name: "Basic Version", + title: "Upgrade to Advanced for more VIP privileges", + }, + }, + enterCode: "Please enter the activation code", + successTitle: "Activation Successful", + successContent: "You have successfully activated {vipLabel}, valid until: {expireDate}", + bindAccountTitle: "Bind Your Account", + bindAccountContent: "Binding your account helps prevent license loss. Strongly recommended.", + congratulations_vip_trial: "Congratulations, you have received a Pro version {duration} days trial", + trial_modal_title: "7-day Pro version trial acquisition", + trial_modal_ok_text: "Get now", + trial_modal_thanks: "Thank you for supporting the open source project", + trial_modal_click_confirm: "Click confirm to get a 7-day Pro version trial", + get_7_day_pro_trial: "7-day professional version trial", + star_now: "Star Now", + please_help_star: "Could you please help by starring? Thanks a lot!", + admin_only_operation: "Admin operation only", + enter_activation_code: "Please enter the activation code", + activate_pro_business: "Activate Professional/Business Edition", + renew_business: "Renew Business Edition", + renew_pro_upgrade_business: "Renew Professional Edition / Upgrade to Business Edition", + basic_edition: "Basic Edition", + community_free_version: "Community Free Version", + unlimited_certificate_application: "Unlimited certificate applications", + unlimited_domain_count: "Unlimited domain count", + unlimited_certificate_pipelines: "Unlimited certificate pipelines", + common_deployment_plugins: "Common host, cloud platform, CDN, Baota, 1Panel deployment plugins", + email_webhook_notifications: "Email, webhook notification methods", + + professional_edition: "Professional Edition", + open_source_support: "Open source requires your sponsorship support", + vip_group_priority: "Access to VIP group, your requests will have priority", + unlimited_site_certificate_monitoring: "Unlimited site certificate monitoring", + more_notification_methods: "More notification methods", + plugins_fully_open: "All plugins open, including Synology and more", + click_to_get_7_day_trial: "Click to get 7-day trial", + years: "years", + afdian_support_vip: 'Get a one-year professional activation code after supporting "VIP membership" on Afdian, open source needs your support', + get_after_support: "Get after sponsoring", + + business_edition: "Business Edition", + commercial_license: "Commercial license, allowed for external operation", + all_pro_privileges: "All professional edition privileges", + allow_commercial_use_modify_logo_title: "Allows commercial use, can modify logo and title", + data_statistics: "Data statistics", + plugin_management: "Plugin management", + unlimited_multi_users: "Unlimited multi-users", + support_user_payment: "Supports user payments", + contact_author_for_trial: "Please contact the author for trial", + activate: "Activate", + get_pro_code_after_support: 'Get a one-year professional activation code after supporting "VIP membership" on Afdian', + business_contact_author: "Business edition please contact the author directly", + year: "year", + freee: "Free", + renew: "Renew", + activate_immediately: "Activate Immediately", + current: "Current", + activated_expire_time: " activated, expiration date: ", + site_id: "Site ID", + invite_code_optional: "Invite code [optional], can get extra 30 days for Professional / 15 days for Business", + no_activation_code: "No activation code?", + activation_code_one_use: "Activation code can only be used once. To change site, please ", + bind_account: "bind account", + transfer_vip: ' then "Transfer VIP"', +}; diff --git a/packages/ui/certd-client/src/locales/langs/zh-CN/authentication.ts b/packages/ui/certd-client/src/locales/langs/zh-CN/authentication.ts new file mode 100644 index 00000000..2bd39d05 --- /dev/null +++ b/packages/ui/certd-client/src/locales/langs/zh-CN/authentication.ts @@ -0,0 +1,85 @@ +export default { + welcomeBack: "欢迎回来", + pageTitle: "开箱即用的大型中后台管理系统", + pageDesc: "工程化、高性能、跨组件库的前端模版", + loginSuccess: "登录成功", + loginSuccessDesc: "欢迎回来", + loginSubtitle: "请输入您的帐户信息以开始管理您的项目", + selectAccount: "快速选择账号", + username: "账号", + password: "密码", + usernameTip: "请输入用户名", + passwordTip: "请输入密码", + verifyRequiredTip: "请先完成验证", + passwordErrorTip: "密码错误", + rememberMe: "记住账号", + createAnAccount: "创建一个账号", + createAccount: "创建账号", + alreadyHaveAccount: "已经有账号了?", + accountTip: "还没有账号?", + signUp: "注册", + signUpSubtitle: "让您的应用程序管理变得简单而有趣", + confirmPassword: "确认密码", + confirmPasswordTip: "两次输入的密码不一致", + agree: "我同意", + privacyPolicy: "隐私政策", + terms: "条款", + agreeTip: "请同意隐私政策和条款", + goToLogin: "去登录", + passwordStrength: "使用 8 个或更多字符,混合字母、数字和符号", + forgetPassword: "忘记密码?", + forgetPasswordSubtitle: "输入您的电子邮件,我们将向您发送重置密码的连接", + emailTip: "请输入邮箱", + emailValidErrorTip: "你输入的邮箱格式不正确", + sendResetLink: "发送重置链接", + email: "邮箱", + qrcodeSubtitle: "请用手机扫描二维码登录", + qrcodePrompt: "扫码后点击 '确认',即可完成登录", + qrcodeLogin: "扫码登录", + codeSubtitle: "请输入您的手机号码以开始管理您的项目", + code: "验证码", + codeTip: "请输入{0}位验证码", + mobile: "手机号码", + mobileTip: "请输入手机号", + mobileErrortip: "手机号码格式错误", + mobileLogin: "手机号登录", + sendCode: "获取验证码", + sendText: "{0}秒后重新获取", + thirdPartyLogin: "其他登录方式", + loginAgainTitle: "重新登录", + loginAgainSubTitle: "您的登录状态已过期,请重新登录以继续。", + layout: { + center: "居中", + alignLeft: "居左", + alignRight: "居右", + }, + usernamePlaceholder: "请输入用户名/邮箱/手机号", + passwordPlaceholder: "请输入密码", + mobilePlaceholder: "请输入手机号", + loginButton: "登录", + forgotAdminPassword: "忘记管理员密码?", + registerLink: "注册", + + smsTab: "短信验证码登录", + passwordTab: "密码登录", + + title: "修改密码", + weakPasswordWarning: "为了您的账户安全,请立即修改密码", + changeNow: "立即修改", + successMessage: "修改成功", + oldPassword: "旧密码", + oldPasswordRequired: "请输入旧密码", + newPassword: "新密码", + newPasswordRequired: "请输入新密码", + confirmNewPassword: "确认新密码", + confirmNewPasswordRequired: "请输入确认密码", + changePasswordButton: "修改密码", + enterPassword: "请输入密码", + newPasswordNotSameOld: "新密码不能和旧密码相同", + enterPasswordAgain: "请再次输入密码", + passwordsNotMatch: "两次输入密码不一致!", + avatar: "头像", + nickName: "昵称", + phoneNumber: "手机号", + changePassword: "修改密码", +}; diff --git a/packages/ui/certd-client/src/locales/langs/zh-CN/certd.ts b/packages/ui/certd-client/src/locales/langs/zh-CN/certd.ts new file mode 100644 index 00000000..7721d4da --- /dev/null +++ b/packages/ui/certd-client/src/locales/langs/zh-CN/certd.ts @@ -0,0 +1,688 @@ +export default { + app: { + crud: { + i18n: { + name: "姓名", + city: "城市", + status: "状态", + }, + }, + login: { + logoutTip: "确认", + logoutMessage: "确定要注销登录吗?", + }, + }, + fs: { + rowHandle: { + title: "操作列", + }, + }, + order: { + confirmTitle: "订单确认", + package: "套餐", + description: "说明", + specifications: "规格", + pipeline: "流水线", + domain: "域名", + deployTimes: "部署次数", + duration: "时长", + price: "价格", + paymentMethod: "支付方式", + free: "免费", + unit: { + pieces: "条", + count: "个", + times: "次", + }, + }, + framework: { + title: "框架", + home: "首页", + }, + + title: "证书自动化", + pipeline: "证书自动化流水线", + pipelineEdit: "编辑流水线", + history: "执行历史记录", + certStore: "证书仓库", + siteMonitor: "站点证书监控", + settings: "设置", + accessManager: "授权管理", + cnameRecord: "CNAME记录管理", + subDomain: "子域名托管设置", + pipelineGroup: "流水线分组管理", + openKey: "开放接口密钥", + notification: "通知设置", + siteMonitorSetting: "站点监控设置", + userSecurity: "认证安全设置", + userProfile: "账号信息", + suite: "套餐", + mySuite: "我的套餐", + suiteBuy: "套餐购买", + myTrade: "我的订单", + paymentReturn: "支付返回", + + user: { + greeting: "您好", + profile: "账号信息", + logout: "注销登录", + }, + dashboard: { + greeting: "您好,{name},欢迎使用 【{site}】", + latestVersion: "最新版本: {version}", + validUntil: "账户有效期:", + tutorialTooltip: "点击查看详细教程", + tutorialText: "仅需3步,全自动申请部署证书", + alertMessage: "证书和授权为敏感信息,不要使用来历不明的在线Certd服务和镜像,以免泄露;请务必私有化部署使用,认准官方版本发布渠道:", + helpDoc: "帮助文档", + pipelineCount: "证书流水线数量", + noPipeline: "您还没有证书流水线", + createNow: "立即创建", + managePipeline: "管理流水线", + pipelineStatus: "流水线状态", + recentRun: "最近运行统计", + runCount: "运行次数", + expiringCerts: "最快到期证书", + supportedTasks: "已支持的部署任务总览", + }, + steps: { + createPipeline: "创建证书流水线", + addTask: "添加部署任务", + scheduledRun: "定时运行", + }, + customPipeline: "自定义流水线", + createCertdPipeline: "创建证书流水线", + commercialCertHosting: "商用证书托管", + tooltip: { + manualUploadOwnCert: "手动上传自有证书,执行自动部署", + noAutoApplyCommercialCert: "并不能自动申请商业证书", + manualUploadOnUpdate: "证书有更新时,都需要手动上传一次", + }, + table: { + confirmDeleteTitle: "确定要删除吗?", + confirmDeleteMessage: "将删除该流水线相关的所有数据,包括执行历史、证书文件、证书仓库记录等", + }, + play: { + runPipeline: "运行流水线", + confirm: "确认", + confirmTrigger: "确定要触发运行吗?", + pipelineStarted: "管道已经开始运行", + }, + actions: { + editPipeline: "编辑流水线", + editConfigGroup: "修改配置/分组", + viewCertificate: "查看证书", + downloadCertificate: "下载证书", + }, + fields: { + userId: "用户Id", + pipelineName: "流水线名称", + keyword: "关键字", + required: "此项必填", + pipelineContent: "流水线内容", + scheduledTaskCount: "定时任务数", + deployTaskCount: "部署任务数", + remainingValidity: "到期剩余", + expiryTime: "过期时间", + status: "状态", + lastRun: "最后运行", + enabled: "启用", + enabledLabel: "启用", + disabledLabel: "禁用", + group: "分组", + type: "类型", + order: "排序号", + keepHistoryCount: "历史记录保持数", + keepHistoryHelper: "历史记录保持条数,多余的会被删除", + createTime: "创建时间", + updateTime: "更新时间", + triggerType: "触发类型", + pipelineId: "流水线Id", + }, + types: { + certApply: "证书申请", + certUpload: "证书上传", + custom: "自定义", + }, + myPipelines: "我的流水线", + selectedCount: "已选择 {count} 项", + batchDelete: "批量删除", + batchForceRerun: "强制重新运行", + applyCertificate: "申请证书", + pipelineExecutionRecords: "流水线执行记录", + confirm: "确认", + confirmBatchDeleteContent: "确定要批量删除这{count}条记录吗", + deleteSuccess: "删除成功", + pleaseSelectRecords: "请先勾选记录", + triggerTypes: { + manual: "手动执行", + timer: "定时执行", + }, + sysResources: { + sysRoot: "系统管理", + sysConsole: "控制台", + sysSettings: "系统设置", + cnameSetting: "CNAME服务设置", + emailSetting: "邮件服务器设置", + siteSetting: "站点个性化", + headerMenus: "顶部菜单设置", + sysAccess: "系统级授权", + sysPlugin: "插件管理", + sysPluginEdit: "编辑插件", + sysPluginConfig: "证书插件配置", + accountBind: "账号绑定", + permissionManager: "权限管理", + roleManager: "角色管理", + userManager: "用户管理", + suiteManager: "套餐管理", + suiteSetting: "套餐设置", + orderManager: "订单管理", + userSuites: "用户套餐", + }, + certificateRepo: { + title: "证书仓库", + sub: "从流水线生成的证书", + }, + + certificateNotGenerated: "证书还未生成,请先运行流水线", + viewCertificateTitle: "查看证书", + close: "关闭", + viewCert: { + title: "查看证书", + }, + download: { + title: "下载证书", + }, + source: "源码", + github: "github", + gitee: "gitee", + cron: { + clearTip: "清除选择", + nextTrigger: "下次触发时间", + tip: "请先设置正确的cron表达式", + }, + cronForm: { + title: "定时脚本", + helper: "点击上面的按钮,选择每天几点定时执行。\n建议设置为每天触发一次,证书未到期之前任务会跳过,不会重复执行", + required: "此项必填", + }, + email: { + title: "收件邮箱", + helper: "输入你的收件邮箱地址,支持多个邮箱", + required: "此项必填", + }, + plugin: { + selectTitle: "证书申请插件", + jsAcme: "JS-ACME:使用简单方便,功能强大【推荐】", + legoAcme: "Lego-ACME:基于Lego实现,支持海量DNS提供商,熟悉LEGO的用户可以使用", + }, + pipelineForm: { + createTitle: "创建证书流水线", + moreParams: "更多参数", + triggerCronTitle: "定时触发", + triggerCronHelper: "点击上面的按钮,选择每天几点定时执行。\n建议设置为每天触发一次,证书未到期之前任务会跳过,不会重复执行", + notificationTitle: "失败通知", + notificationHelper: "任务执行失败实时提醒", + groupIdTitle: "流水线分组", + }, + notificationDefault: "使用默认通知", + monitor: { + title: "站点证书监控", + description: "每天0点,检查网站证书的过期时间,到期前10天时将发出提醒(使用默认通知渠道);", + settingLink: "站点监控设置", + limitInfo: "基础版限制1条,专业版以上无限制,当前", + checkAll: "检查全部", + confirmTitle: "确认", + confirmContent: "确认触发检查全部站点证书吗?", + checkSubmitted: "检查任务已提交", + pleaseRefresh: "请稍后刷新页面查看结果", + siteName: "站点名称", + enterSiteName: "请输入站点名称", + domain: "网站域名", + enterDomain: "请输入域名", + enterValidDomain: "请输入正确的域名", + httpsPort: "HTTPS端口", + enterPort: "请输入端口", + certInfo: "证书信息", + issuer: "证书颁发机构", + certDomains: "证书域名", + certProvider: "颁发机构", + certStatus: "证书状态", + status: { + ok: "正常", + expired: "过期", + }, + certExpiresTime: "证书到期时间", + expired: "过期", + days: "天", + lastCheckTime: "上次检查时间", + disabled: "禁用启用", + ipCheck: "开启IP检查", + selectRequired: "请选择", + ipCheckConfirm: "确定{status}IP检查?", + ipCount: "IP数量", + checkStatus: "检查状态", + pipelineId: "关联流水线ID", + certInfoId: "证书ID", + checkSubmittedRefresh: "检查任务已提交,请稍后刷新查看结果", + ipManagement: "IP管理", + bulkImport: "批量导入", + basicLimitError: "基础版只能添加一个监控站点,请赞助升级专业版", + limitExceeded: "对不起,您最多只能创建条{max}监控记录,请购买或升级套餐", + }, + checkStatus: { + success: "成功", + checking: "检查中", + error: "异常", + }, + domainList: { + title: "域名列表", + helper: "格式【域名:端口:名称】,一行一个,其中端口、名称可以省略\n比如:\nwww.baidu.com:443:百度\nwww.taobao.com::淘宝\nwww.google.com", + required: "请输入要导入的域名", + placeholder: "www.baidu.com:443:百度\nwww.taobao.com::淘宝\nwww.google.com\n", + }, + accountInfo: "账号信息", + securitySettings: "认证安全设置", + confirmDisable2FA: "确定要关闭多重验证登录吗?", + disabledSuccess: "关闭成功", + saveSuccess: "保存成功", + twoFactorAuth: "2FA多重验证登录", + rebind: "重新绑定", + twoFactorAuthHelper: "是否开启多重验证登录", + bindDevice: "绑定设备", + step1: "1. 安装任意一款支持Authenticator的验证APP,比如:", + tooltipGoogleServiceError: "如果报没有找到谷歌服务的错误,您可以安装KK谷歌助手", + step2: "2. 扫描二维码添加账号", + step3: "3. 输入验证码", + inputVerifyCode: "请输入验证码", + cancel: "取消", + authorizationManagement: "授权管理", + manageThirdPartyAuth: "管理第三方系统授权信息", + name: "名称", + pleaseEnterName: "请填写名称", + nameHelper: "随便填,当多个相同类型的授权时,便于区分", + level: "级别", + system: "系统", + usera: "用户", + nickName: "昵称", + max50Chars: "最大50个字符", + myInfo: "我的信息", + siteMonitorSettings: "站点监控设置", + notificationChannel: "通知渠道", + setNotificationChannel: "设置通知渠道", + retryTimes: "重试次数", + monitorRetryTimes: "监控请求重试次数", + monitorCronSetting: "监控定时设置", + cronTrigger: "定时触发监控", + save: "保存", + editSchedule: "修改定时", + timerTrigger: "定时触发", + schedule: "定时", + selectCron: "请选择定时Cron", + batchEditSchedule: "批量修改定时", + editTrigger: "编辑触发器", + triggerName: "触发器名称", + requiredField: "此项必填", + type: "类型", + enterName: "请输入名称", + confirmDeleteTrigger: "确定要删除此触发器吗?", + notificationType: "通知类型", + selectNotificationType: "请选择通知类型", + notificationName: "通知名称", + helperNotificationName: "随便填,当多个相同类型的通知时,便于区分", + isDefault: "是否默认", + yes: "是", + no: "否", + selectIsDefault: "请选择是否默认", + prompt: "提示", + confirmSetDefaultNotification: "确定设置为默认通知?", + test: "测试", + scope: "权限范围", + scopeOpenApiOnly: "仅开放接口", + scopeFullAccount: "账户所有权限", + required: "此项必填", + scopeHelper: "仅开放接口只可以访问开放接口,账户所有权限可以访问所有接口", + add: "生成新的Key", + gen: { + text: "接口测试", + title: "x-certd-token", + okText: "确定", + contentPart1: "测试x-certd-token如下,您可以在3分钟内使用它进行", + openApi: "开放接口", + contentPart2: "请求测试", + }, + pending_cname_setup: "待设置CNAME", + validating: "验证中", + validation_successful: "验证成功", + validation_failed: "验证失败", + validation_timed_out: "验证超时", + proxied_domain: "被代理域名", + host_record: "主机记录", + please_set_cname: "请设置CNAME", + cname_service: "CNAME服务", + default_public_cname: "默认提供公共CNAME服务,您还可以", + customize_cname: "自定义CNAME服务", + public_cname: "公共CNAME", + custom_cname: "自定义CNAME", + validate: "验证", + validation_started: "开始验证,请耐心等待", + click_to_validate: "点击验证", + all: "全部", + cname_feature_guide: "CNAME功能原理及使用说明", + batch_delete: "批量删除", + confirm_delete_count: "确定要批量删除这{count}条记录吗", + delete_successful: "删除成功", + please_select_records: "请先勾选记录", + edit_notification: "编辑通知", + other_notification_method: "其他通知方式", + trigger_time: "触发时机", + start_time: "开始时", + success_time: "成功时", + fail_to_success_time: "失败转成功时", + fail_time: "失败时", + helper_suggest_fail_only: "建议仅选择'失败时'和'失败转成功'两种即可", + notification_config: "通知配置", + please_select_notification: "请选择通知方式", + please_select_type: "请选择类型", + please_select_trigger_time: "请选择通知时机", + please_select_notification_config: "请选择通知配置", + confirm_delete_trigger: "确定要删除此触发器吗?", + gift_package: "赠送套餐", + package_name: "套餐名称", + click_to_select: "点击选择", + please_select_package: "请选择套餐", + package: "套餐", + addon_package: "加量包", + domain_count: "域名数量", + unit_count: "个", + field_required: "此项必填", + pipeline_count: "流水线数量", + unit_item: "条", + deploy_count: "部署次数", + unit_times: "次", + monitor_count: "证书监控数量", + duration: "时长", + status: "状态", + active_time: "激活时间", + expires_time: "过期时间", + is_present: "是否赠送", + is_present_yes: "是", + is_present_no: "否", + basicInfo: "基础信息", + titlea: "名称", + disabled: "是否禁用", + ordera: "排序", + supportBuy: "支持购买", + intro: "介绍", + packageContent: "套餐内容", + maxDomainCount: "最大域名数", + maxPipelineCount: "最大流水线数", + maxDeployCount: "最大部署数", + maxMonitorCount: "最大监控数", + price: "价格", + durationPrices: "时长价格", + packageName: "套餐名称", + addon: "加量包", + typeHelper: "套餐:同一时间只有最新购买的一个生效\n加量包:可购买多个,购买后立即生效,不影响套餐\n套餐和加量包数量可叠加", + domainCount: "域名数量", + pipelineCount: "流水线数量", + unitPipeline: "条", + deployCount: "部署次数", + unitDeploy: "次", + monitorCount: "证书监控数量", + unitCount: "个", + durationPriceTitle: "时长及价格", + selectDuration: "选择时长", + supportPurchase: "支持购买", + cannotPurchase: "不能购买", + shelfStatus: "上下架", + onShelf: "上架", + offShelf: "下架", + orderHelper: "越小越靠前", + description: "说明", + createTime: "创建时间", + updateTime: "更新时间", + edit: "编辑", + groupName: "分组名称", + enterGroupName: "请输入分组名称", + subdomainHosting: "子域名托管", + subdomainHostingHint: "当你的域名设置了子域名托管,需要在此处创建记录,否则申请证书将失败", + batchDeleteConfirm: "确定要批量删除这{count}条记录吗", + selectRecordFirst: "请先勾选记录", + subdomainHosted: "托管的子域名", + subdomainHelpText: "如果您不理解什么是子域托管,可以参考文档", + subdomainManagement: "子域管理", + isDisabled: "是否禁用", + enabled: "启用", + uploadCustomCert: "上传自定义证书", + sourcee: "来源", + sourcePipeline: "流水线", + sourceManualUpload: "手动上传", + domains: "域名", + enterDomain: "请输入域名", + validDays: "有效天数", + expires: "过期", + days: "天", + expireTime: "过期时间", + certIssuer: "证书颁发机构", + applyTime: "申请时间", + relatedPipeline: "关联流水线", + statusSuccess: "成功", + statusChecking: "检查中", + statusError: "异常", + actionImportBatch: "批量导入", + actionSyncIp: "同步IP", + modalTitleSyncIp: "同步IP", + modalContentSyncIp: "确定要同步IP吗?", + notificationSyncComplete: "同步完成", + actionCheckAll: "检查全部", + modalTitleConfirm: "确认", + modalContentCheckAll: "确认触发检查全部IP站点的证书吗?", + notificationCheckSubmitted: "检查任务已提交", + notificationCheckDescription: "请稍后刷新页面查看结果", + tooltipCheckNow: "立即检查", + notificationCheckSubmittedPleaseRefresh: "检查任务已提交,请稍后刷新查看结果", + columnId: "ID", + columnIp: "IP", + helperIpCname: "也支持填写CNAME域名", + ruleIpRequired: "请输入IP", + columnCertDomains: "证书域名", + columnCertProvider: "颁发机构", + columnCertStatus: "证书状态", + statusNormal: "正常", + statusExpired: "过期", + columnCertExpiresTime: "证书到期时间", + expired: "过期", + columnCheckStatus: "检查状态", + columnLastCheckTime: "上次检查时间", + columnSource: "来源", + sourceSync: "同步", + sourceManual: "手动", + sourceImport: "导入", + columnDisabled: "禁用启用", + columnRemark: "备注", + pluginFile: "插件文件", + selectPluginFile: "选择插件文件", + overrideSameName: "同名覆盖", + override: "覆盖", + noOverride: "不覆盖", + overrideHelper: "如果已有相同名称插件,直接覆盖", + importPlugin: "导入插件", + operationSuccess: "操作成功", + customPlugin: "自定义插件", + import: "导入", + export: "导出", + pluginType: "插件类型", + auth: "授权", + dns: "DNS", + deployPlugin: "部署插件", + icon: "图标", + pluginName: "插件名称", + pluginNameHelper: "必须为英文或数字,驼峰命名,类型作为前缀\n示例:AliyunDeployToCDN\n插件使用后,名称不可修改", + pluginNameRuleMsg: "必须为英文或数字,驼峰命名,类型作为前缀", + author: "作者", + authorHelper: "上传插件市场时作为前缀,如 greper/pluginName", + authorRuleMsg: "必须为英文或数字", + titleHelper: "插件中文名称", + descriptionHelper: "插件描述", + builtIn: "内置", + custom: "自定义", + store: "市场", + version: "版本", + pluginDependencies: "插件依赖", + pluginDependenciesHelper: "格式: [作者/]插件名[:版本],需先安装依赖插件", + editableRunStrategy: "可编辑运行策略", + editable: "可编辑", + notEditable: "不可编辑", + runStrategy: "运行策略", + normalRun: "正常运行", + skipOnSuccess: "成功跳过(部署任务)", + defaultRunStrategyHelper: "默认运行策略", + enableDisable: "启用/禁用", + clickToToggle: "点击切换启用/禁用", + confirmToggle: "确认要", + disable: "禁用", + enable: "启用", + pluginGroup: "插件分组", + icpRegistrationNumber: "ICP备案号", + icpPlaceholder: "粤ICP备xxxxxxx号", + publicSecurityRegistrationNumber: "网安备案号", + publicSecurityPlaceholder: "京公网安备xxxxxxx号", + enableAssistant: "开启小助手", + allowCrawlers: "允许爬虫", + httpProxy: "HTTP代理", + httpProxyPlaceholder: "http://192.168.1.2:18010/", + httpProxyHelper: "当某些网站被墙时可以配置", + httpsProxy: "HTTPS代理", + httpsProxyPlaceholder: "http://192.168.1.2:18010/", + saveThenTestTitle: "保存后,再点击测试", + testButton: "测试", + httpsProxyHelper: "一般这两个代理填一样的,保存后再测试", + dualStackNetwork: "双栈网络", + default: "默认", + ipv4Priority: "IPV4优先", + ipv6Priority: "IPV6优先", + dualStackNetworkHelper: "如果选择IPv6优先,需要在docker-compose.yaml中启用ipv6", + enableCommonCnameService: "启用公共CNAME服务", + commonCnameHelper: "是否可以使用公共CNAME服务,如果禁用,且没有设置自定义CNAME服务,则无法使用CNAME代理方式申请证书", + saveButton: "保存", + stopSuccess: "停止成功", + google: "Google", + baidu: "百度", + success: "成功", + testFailed: "测试失败", + testCompleted: "测试完成", + manageOtherUserPipeline: "管理其他用户流水线", + limitUserPipelineCount: "限制用户流水线数量", + limitUserPipelineCountHelper: "0为不限制", + enableSelfRegistration: "开启自助注册", + enableUserValidityPeriod: "开启用户有效期", + userValidityPeriodHelper: "有效期内用户可正常使用,失效后流水线将被停用", + enableUsernameRegistration: "开启用户名注册", + enableEmailRegistration: "开启邮箱注册", + proFeature: "专业版功能", + emailServerSetup: "设置邮箱服务器", + enableSmsLoginRegister: "开启手机号登录、注册", + commFeature: "商业版功能", + smsProvider: "短信提供商", + aliyunSms: "阿里云短信", + yfySms: "易发云短信", + smsTest: "短信测试", + testMobilePlaceholder: "输入测试手机号", + saveThenTest: "保存后再点击测试", + enterTestMobile: "请输入测试手机号", + sendSuccess: "发送成功", + atLeastOneLoginRequired: "密码登录和手机号登录至少开启一个", + fieldRequired: "此项必填", + siteHide: "站点隐藏", + enableSiteHide: "启用站点隐藏", + siteHideDescription: "可以在平时关闭站点的可访问性,需要时再打开,增强站点安全性", + helpDoc: "帮助说明", + randomAddress: "随机地址", + siteHideUrlHelper: "站点被隐藏后,需要访问此URL解锁,才能正常访问", + fullUnlockUrl: "完整解除隐藏地址", + saveThisUrl: "请保存好此地址", + unlockPassword: "解除密码", + unlockPasswordHelper: "解除隐藏时需要输入密码,第一次需要设置密码,填写则重置密码", + autoHideTime: "自动隐藏时间", + autoHideTimeHelper: "多少分钟内无请求自动隐藏", + hideOpenApi: "隐藏开放接口", + hideOpenApiHelper: "是否隐藏开放接口,是否放开/api/v1开头的接口", + hideSiteImmediately: "立即隐藏站点", + hideImmediately: "立即隐藏", + confirmHideSiteTitle: "确定要立即隐藏站点吗?", + confirmHideSiteContent: "隐藏后,将无法访问站点,请谨慎操作", + siteHiddenSuccess: "站点已隐藏", + emailServerSettings: "邮件服务器设置", + setEmailSendingServer: "设置邮件发送服务器", + useCustomEmailServer: "使用自定义邮件服务器", + smtpDomain: "SMTP域名", + pleaseEnterSmtpDomain: "请输入smtp域名或ip", + smtpPort: "SMTP端口", + pleaseEnterSmtpPort: "请输入smtp端口号", + username: "用户名", + pleaseEnterUsername: "请输入用户名", + password: "密码", + pleaseEnterPassword: "请输入密码", + qqEmailAuthCodeHelper: "如果是qq邮箱,需要到qq邮箱的设置里面申请授权码作为密码", + senderEmail: "发件邮箱", + pleaseEnterSenderEmail: "请输入发件邮箱", + useSsl: "是否ssl", + sslPortNote: "ssl和非ssl的smtp端口是不一样的,注意修改端口", + ignoreCertValidation: "忽略证书校验", + useOfficialEmailServer: "使用官方邮件服务器", + useOfficialEmailServerHelper: "使用官方邮箱服务器直接发邮件,免除繁琐的配置", + testReceiverEmail: "测试收件邮箱", + pleaseEnterTestReceiverEmail: "请输入测试收件邮箱", + saveBeforeTest: "保存后再点击测试", + sendFailHelpDoc: "发送失败???", + emailConfigHelpDoc: "邮件配置帮助文档", + tryOfficialEmailServer: "您还可以试试使用官方邮件服务器↗↗↗↗↗↗↗↗", + pluginManagement: "插件管理", + pluginBetaWarning: "自定义插件处于BETA测试版,后续可能会有破坏性变更", + pleaseSelectRecord: "请先勾选记录", + permissionManagement: "权限管理", + adda: "添加", + rootNode: "根节点", + permissionName: "权限名称", + enterPermissionName: "请输入权限名称", + permissionCode: "权限代码", + enterPermissionCode: "请输入权限代码", + max100Chars: "最大100个字符", + examplePermissionCode: "例如:sys:user:view", + sortOrder: "排序", + sortRequired: "排序号必填", + parentNode: "父节点", + roleManagement: "角色管理", + assignPermissions: "分配权限", + roleName: "角色名称", + enterRoleName: "请输入角色名称", + unlockLogin: "解除登录锁定", + notice: "提示", + confirmUnlock: "确定要解除该用户的登录锁定吗?", + unlockSuccess: "解除成功", + enterUsername: "请输入用户名", + modifyPasswordIfFilled: "填写则修改密码", + emaila: "邮箱", + mobile: "手机号", + avatar: "头像", + validTime: "有效期", + remark: "备注", + roles: "角色", + cnameTitle: "CNAME服务配置", + cnameDescription: "此处配置的域名作为其他域名校验的代理,当别的域名需要申请证书时,通过CNAME映射到此域名上来验证所有权。好处是任何域名都可以通过此方式申请证书,也无需填写AccessSecret。", + cnameLinkText: "CNAME功能原理及使用说明", + confirmTitle: "确认", + confirmDeleteBatch: "确定要批量删除这{count}条记录吗", + selectRecordsFirst: "请先勾选记录", + cnameDomain: "CNAME域名", + cnameDomainPlaceholder: "cname.handsfree.work", + cnameDomainHelper: "需要一个右边DNS提供商注册的域名(也可以将其他域名的dns服务器转移到这几家来)。\nCNAME域名一旦确定不可修改,建议使用一级子域名", + dnsProvider: "DNS提供商", + dnsProviderAuthorization: "DNS提供商授权", + setDefault: "设置默认", + confirmSetDefault: "确定要设置为默认吗?", + setAsDefault: "设为默认", + disabledLabel: "禁用", + confirmToggleStatus: "确定要{action}吗?", +}; diff --git a/packages/ui/certd-client/src/locales/langs/zh-CN/common.ts b/packages/ui/certd-client/src/locales/langs/zh-CN/common.ts new file mode 100644 index 00000000..e9ba4012 --- /dev/null +++ b/packages/ui/certd-client/src/locales/langs/zh-CN/common.ts @@ -0,0 +1,22 @@ +export default { + back: "返回", + backToHome: "返回首页", + login: "登录", + logout: "退出登录", + prompt: "提示", + cancel: "取消", + confirm: "确认", + reset: "重置", + noData: "暂无数据", + refresh: "刷新", + loadingMenu: "加载菜单中", + query: "查询", + search: "搜索", + enabled: "已启用", + disabled: "已禁用", + edit: "修改", + delete: "删除", + create: "新增", + yes: "是", + no: "否", +}; diff --git a/packages/ui/certd-client/src/locales/langs/zh-CN/guide.ts b/packages/ui/certd-client/src/locales/langs/zh-CN/guide.ts new file mode 100644 index 00000000..07389477 --- /dev/null +++ b/packages/ui/certd-client/src/locales/langs/zh-CN/guide.ts @@ -0,0 +1,71 @@ +export default { + createCertPipeline: { + title: "创建证书申请流水线", + description: "演示证书申请任务如何配置", + items: { + tutorialTitle: "教程演示内容", + tutorialDesc1: "本教程演示如何自动申请证书并部署到Nginx上", + tutorialDesc2: "仅需3步,全自动申请部署证书", + createTitle: "创建证书流水线", + createDesc: "点击添加证书流水线,填写证书申请信息", + successTitle: "流水线创建成功", + successDesc: "点击手动触发即可申请证书", + nextTitle: "接下来演示如何自动部署证书", + nextDesc: "如果您只需要申请证书,那么到这一步就可以了", + }, + }, + buttons: { + prev: "上一步", + next: "下一步", + }, + addDeployTask: { + title: "添加部署证书任务", + description: "这里演示部署证书到Nginx", + items: { + addTaskTitle: "添加证书部署任务", + addTaskDesc1: "这里演示自动部署证书到nginx", + addTaskDesc2: "本系统提供海量部署插件,满足您的各种部署需求", + fillParamsTitle: "填写任务参数", + fillParamsDesc1: "填写主机上证书文件的路径", + fillParamsDesc2: "选择主机ssh登录授权", + activateCertTitle: "让新证书生效", + activateCertDesc1: "执行重启脚本", + activateCertDesc2: "让证书生效", + taskSuccessTitle: "部署任务添加成功", + taskSuccessDesc: "现在可以运行", + pluginsTitle: "本系统提供茫茫多的部署插件", + pluginsDesc: "您可以根据自身需求将证书部署到各种应用和平台", + }, + }, + runAndTestTask: { + runAndTestTitle: "运行与测试", + runAndTestDescription: "演示流水线运行,查看日志,成功后跳过等", + runTestOnce: "运行测试一下", + clickManualTriggerToTest: "点击手动触发按钮,即可测试运行", + viewLogs: "查看日志", + clickTaskToViewStatusAndLogs: "点击任务可以查看状态和日志", + howToTroubleshootFailure: "执行失败如何排查", + viewErrorLogs: "查看错误日志", + nginxContainerNotExistFix: "这里报的是nginx容器不存在,修改命令,改成正确的nginx容器名称即可", + executionSuccess: "执行成功", + retryAfterFix: "修改正确后,重新点击手动触发,重新运行一次,执行成功", + autoSkipAfterSuccess: "成功后自动跳过", + successSkipExplanation: "可以看到成功过的将会自动跳过,不会重复执行,只有当参数变更或者证书更新了,才会重新运行", + viewCertDeploymentSuccess: "查看证书部署成功", + visitNginxToSeeCert: "访问nginx上的网站,可以看到证书已经部署成功", + downloadCertManualDeploy: "还可以下载证书,手动部署", + downloadIfNoAutoDeployPlugin: "如果还没有好用的部署插件,没办法自动部署,你还可以下载证书,手动部署", + }, + scheduleAndEmailTask: { + title: "设置定时执行和邮件通知", + description: "自动运行", + setSchedule: "设置定时执行", + pipelineSuccessThenSchedule: "流水线测试成功,接下来配置定时触发,以后每天定时执行就不用管了", + recommendDailyRun: "推荐配置每天运行一次,在到期前35天才会重新申请新证书并部署,没到期前会自动跳过,不会重复申请。", + setEmailNotification: "设置邮件通知", + suggestErrorAndRecoveryEmails: "建议选择监听'错误时'和'错误转成功'两种即可,在意外失败时可以尽快去排查问题,(基础版需要配置邮件服务器)", + basicVersionNeedsMailServer: "(基础版需要配置邮件服务器)", + tutorialEndTitle: "教程结束", + thanksForWatching: "感谢观看,希望对你有所帮助", + }, +}; diff --git a/packages/ui/certd-client/src/locales/langs/zh-CN/index.ts b/packages/ui/certd-client/src/locales/langs/zh-CN/index.ts new file mode 100644 index 00000000..8e1e40be --- /dev/null +++ b/packages/ui/certd-client/src/locales/langs/zh-CN/index.ts @@ -0,0 +1,19 @@ +import certd from "./certd"; +import authentication from "./authentication"; +import vip from "./vip"; +import tutorial from "./tutorial"; +import preferences from "./preferences"; +import ui from "./ui"; +import guide from "./guide"; +import common from "./common"; + +export default { + certd, + authentication, + vip, + ui, + tutorial, + preferences, + guide, + common, +}; diff --git a/packages/ui/certd-client/src/locales/langs/zh-CN/preferences.ts b/packages/ui/certd-client/src/locales/langs/zh-CN/preferences.ts new file mode 100644 index 00000000..404ec2e7 --- /dev/null +++ b/packages/ui/certd-client/src/locales/langs/zh-CN/preferences.ts @@ -0,0 +1,186 @@ +export default { + title: "偏好设置", + subtitle: "自定义偏好设置 & 实时预览", + resetTitle: "重置偏好设置", + resetTip: "数据有变化,点击可进行重置", + resetSuccess: "重置偏好设置成功", + appearance: "外观", + layout: "布局", + content: "内容", + other: "其它", + wide: "流式", + compact: "定宽", + followSystem: "跟随系统", + vertical: "垂直", + verticalTip: "侧边垂直菜单模式", + horizontal: "水平", + horizontalTip: "水平菜单模式,菜单全部显示在顶部", + twoColumn: "双列菜单", + twoColumnTip: "垂直双列菜单模式", + headerSidebarNav: "侧边导航", + headerSidebarNavTip: "顶部通栏,侧边导航模式", + headerTwoColumn: "混合双列", + headerTwoColumnTip: "双列、水平菜单共存模式", + mixedMenu: "混合垂直", + mixedMenuTip: "垂直水平菜单共存", + fullContent: "内容全屏", + fullContentTip: "不显示任何菜单,只显示内容主体", + normal: "常规", + plain: "朴素", + rounded: "圆润", + copyPreferences: "复制偏好设置", + copyPreferencesSuccessTitle: "复制成功", + copyPreferencesSuccess: "复制成功,请在 app 下的 `src/preferences.ts`内进行覆盖", + clearAndLogout: "清空缓存 & 退出登录", + mode: "模式", + general: "通用", + language: "语言", + dynamicTitle: "动态标题", + watermark: "水印", + checkUpdates: "定时检查更新", + position: { + title: "偏好设置位置", + header: "顶栏", + auto: "自动", + fixed: "固定", + }, + sidebar: { + title: "侧边栏", + width: "宽度", + visible: "显示侧边栏", + collapsed: "折叠菜单", + collapsedShowTitle: "折叠显示菜单名", + autoActivateChild: "自动激活子菜单", + autoActivateChildTip: "点击顶层菜单时,自动激活第一个子菜单或者上一次激活的子菜单", + expandOnHover: "鼠标悬停展开", + expandOnHoverTip: "鼠标在折叠区域悬浮时,`启用`则展开当前子菜单,`禁用`则展开整个侧边栏", + }, + tabbar: { + title: "标签栏", + enable: "启用标签栏", + icon: "显示标签栏图标", + showMore: "显示更多按钮", + showMaximize: "显示最大化按钮", + persist: "持久化标签页", + maxCount: "最大标签数", + maxCountTip: "每次打开新的标签时如果超过最大标签数,\n会自动关闭一个最先打开的标签\n设置为 0 则不限制", + draggable: "启动拖拽排序", + wheelable: "启用纵向滚轮响应", + middleClickClose: "点击鼠标中键关闭标签页", + wheelableTip: "开启后,标签栏区域可以响应滚轮的纵向滚动事件。\n关闭时,只能响应系统的横向滚动事件(需要按下Shift再滚动滚轮)", + styleType: { + title: "标签页风格", + chrome: "谷歌", + card: "卡片", + plain: "朴素", + brisk: "轻快", + }, + contextMenu: { + reload: "重新加载", + close: "关闭", + pin: "固定", + unpin: "取消固定", + closeLeft: "关闭左侧标签页", + closeRight: "关闭右侧标签页", + closeOther: "关闭其它标签页", + closeAll: "关闭全部标签页", + openInNewWindow: "在新窗口打开", + maximize: "最大化", + restoreMaximize: "还原", + }, + }, + navigationMenu: { + title: "导航菜单", + style: "导航菜单风格", + accordion: "侧边导航菜单手风琴模式", + split: "导航菜单分离", + splitTip: "开启时,侧边栏显示顶栏对应菜单的子菜单", + }, + breadcrumb: { + title: "面包屑导航", + enable: "开启面包屑导航", + icon: "显示面包屑图标", + home: "显示首页按钮", + style: "面包屑风格", + hideOnlyOne: "仅有一个时隐藏", + background: "背景", + }, + animation: { + title: "动画", + loading: "页面切换 Loading", + transition: "页面切换动画", + progress: "页面切换进度条", + }, + theme: { + title: "主题", + radius: "圆角", + light: "浅色", + dark: "深色", + darkSidebar: "深色侧边栏", + darkHeader: "深色顶栏", + weakMode: "色弱模式", + grayMode: "灰色模式", + builtin: { + title: "内置主题", + default: "默认", + violet: "紫罗兰", + pink: "樱花粉", + rose: "玫瑰红", + skyBlue: "天蓝色", + deepBlue: "深蓝色", + green: "浅绿色", + deepGreen: "深绿色", + orange: "橙黄色", + yellow: "柠檬黄", + zinc: "锌色灰", + neutral: "中性色", + slate: "石板灰", + gray: "中灰色", + custom: "自定义", + }, + }, + header: { + title: "顶栏", + modeStatic: "静止", + modeFixed: "固定", + modeAuto: "自动隐藏和显示", + modeAutoScroll: "滚动隐藏和显示", + visible: "显示顶栏", + menuAlign: "菜单位置", + menuAlignStart: "左侧", + menuAlignEnd: "右侧", + menuAlignCenter: "居中", + }, + footer: { + title: "底栏", + visible: "显示底栏", + fixed: "固定在底部", + }, + copyright: { + title: "版权", + enable: "启用版权", + companyName: "公司名", + companySiteLink: "公司主页", + date: "日期", + icp: "ICP 备案号", + icpLink: "ICP 网站链接", + }, + shortcutKeys: { + title: "快捷键", + global: "全局", + search: "全局搜索", + logout: "退出登录", + preferences: "偏好设置", + }, + widget: { + title: "小部件", + globalSearch: "启用全局搜索", + fullscreen: "启用全屏", + themeToggle: "启用主题切换", + languageToggle: "启用语言切换", + notification: "启用通知", + sidebarToggle: "启用侧边栏切换", + lockScreen: "启用锁屏", + refresh: "启用刷新", + }, +}; diff --git a/packages/ui/certd-client/src/locales/langs/zh-CN/tutorial.ts b/packages/ui/certd-client/src/locales/langs/zh-CN/tutorial.ts new file mode 100644 index 00000000..dab6b1bd --- /dev/null +++ b/packages/ui/certd-client/src/locales/langs/zh-CN/tutorial.ts @@ -0,0 +1,3 @@ +export default { + title: "使用教程", +}; diff --git a/packages/ui/certd-client/src/locales/langs/zh-CN/ui.ts b/packages/ui/certd-client/src/locales/langs/zh-CN/ui.ts new file mode 100644 index 00000000..83c76776 --- /dev/null +++ b/packages/ui/certd-client/src/locales/langs/zh-CN/ui.ts @@ -0,0 +1,104 @@ +export default { + formRules: { + required: "请输入{0}", + selectRequired: "请选择{0}", + minLength: "{0}至少{1}个字符", + maxLength: "{0}最多{1}个字符", + length: "{0}长度必须为{1}个字符", + alreadyExists: "{0} `{1}` 已存在", + startWith: "{0}必须以 {1} 开头", + invalidURL: "请输入有效的链接", + }, + actionTitle: { + edit: "修改{0}", + create: "新增{0}", + delete: "删除{0}", + view: "查看{0}", + }, + actionMessage: { + deleteConfirm: "确定删除 {0} 吗?", + deleting: "正在删除 {0} ...", + deleteSuccess: "{0} 删除成功", + operationSuccess: "操作成功", + operationFailed: "操作失败", + }, + placeholder: { + input: "请输入", + select: "请选择", + }, + captcha: { + title: "请完成安全验证", + sliderSuccessText: "验证通过", + sliderDefaultText: "请按住滑块拖动", + sliderRotateDefaultTip: "点击图片可刷新", + sliderRotateFailTip: "验证失败", + sliderRotateSuccessTip: "验证成功,耗时{0}秒", + alt: "支持img标签src属性值", + refreshAriaLabel: "刷新验证码", + confirmAriaLabel: "确认选择", + confirm: "确认", + pointAriaLabel: "点击点", + clickInOrder: "请依次点击", + }, + iconPicker: { + placeholder: "选择一个图标", + search: "搜索图标...", + }, + jsonViewer: { + copy: "复制", + copied: "已复制", + }, + fallback: { + pageNotFound: "哎呀!未找到页面", + pageNotFoundDesc: "抱歉,我们无法找到您要找的页面。", + forbidden: "哎呀!访问被拒绝", + forbiddenDesc: "抱歉,您没有权限访问此页面。", + internalError: "哎呀!出错了", + internalErrorDesc: "抱歉,服务器遇到错误。", + offline: "离线页面", + offlineError: "哎呀!网络错误", + offlineErrorDesc: "抱歉,无法连接到互联网,请检查您的网络连接并重试。", + comingSoon: "即将推出", + http: { + requestTimeout: "请求超时,请稍后再试。", + networkError: "网络异常,请检查您的网络连接后重试。", + badRequest: "请求错误。请检查您的输入并重试。", + unauthorized: "登录认证过期,请重新登录后继续。", + forbidden: "禁止访问, 您没有权限访问此资源。", + notFound: "未找到, 请求的资源不存在。", + internalServerError: "内部服务器错误,请稍后再试。", + }, + }, + widgets: { + document: "文档", + qa: "问题 & 帮助", + setting: "设置", + logoutTip: "是否退出登录?", + viewAll: "查看所有消息", + notifications: "通知", + markAllAsRead: "全部标记为已读", + clearNotifications: "清空", + checkUpdatesTitle: "新版本可用", + checkUpdatesDescription: "点击刷新以获取最新版本", + search: { + title: "搜索", + searchNavigate: "搜索导航菜单", + select: "选择", + navigate: "导航", + close: "关闭", + noResults: "未找到搜索结果", + noRecent: "没有搜索历史", + recent: "搜索历史", + }, + lockScreen: { + title: "锁定屏幕", + screenButton: "锁定", + password: "密码", + placeholder: "请输入锁屏密码", + unlock: "点击解锁", + errorPasswordTip: "密码错误,请重新输入", + backToLogin: "返回登录", + entry: "进入系统", + }, + }, +}; diff --git a/packages/ui/certd-client/src/locales/langs/zh-CN/vip.ts b/packages/ui/certd-client/src/locales/langs/zh-CN/vip.ts new file mode 100644 index 00000000..b056b9a4 --- /dev/null +++ b/packages/ui/certd-client/src/locales/langs/zh-CN/vip.ts @@ -0,0 +1,86 @@ +export default { + comm: { + name: "{vipLabel}已开通", + title: "到期时间:{expire}", + nav: "{vipLabel}", + }, + plus: { + name: "商业版功能", + title: "升级商业版,获取商业授权", + }, + free: { + comm: { + name: "商业版功能", + title: "升级商业版,获取商业授权", + }, + button: { + name: "专业版功能", + title: "升级专业版,享受更多VIP特权", + }, + nav: { + name: "基础版", + title: "升级专业版,享受更多VIP特权", + }, + }, + enterCode: "请输入激活码", + successTitle: "激活成功", + successContent: "您已成功激活{vipLabel},有效期至:{expireDate}", + bindAccountTitle: "是否绑定袖手账号", + bindAccountContent: "绑定账号后,可以避免License丢失,强烈建议绑定", + congratulations_vip_trial: "恭喜,您已获得专业版{duration}天试用", + trial_modal_title: "7天专业版试用获取", + trial_modal_ok_text: "立即获取", + trial_modal_thanks: "感谢您对开源项目的支持", + trial_modal_click_confirm: "点击确认,即可获取7天专业版试用", + get_7_day_pro_trial: "7天专业版试用获取", + star_now: "立即去Star", + please_help_star: "可以先请您帮忙点个star吗?感谢感谢", + admin_only_operation: "仅限管理员操作", + enter_activation_code: "请输入激活码", + activate_pro_business: "激活专业版/商业版", + renew_business: "续期商业版", + renew_pro_upgrade_business: "续期专业版/升级商业版", + basic_edition: "基础版", + community_free_version: "社区免费版", + unlimited_certificate_application: "证书申请无限制", + unlimited_domain_count: "域名数量无限制", + unlimited_certificate_pipelines: "证书流水线数量无限制", + common_deployment_plugins: "常用的主机、云平台、cdn、宝塔、1Panel等部署插件", + email_webhook_notifications: "邮件、webhook通知方式", + + professional_edition: "专业版", + open_source_support: "开源需要您的赞助支持", + vip_group_priority: "可加VIP群,您的需求将优先实现", + unlimited_site_certificate_monitoring: "站点证书监控无限制", + more_notification_methods: "更多通知方式", + plugins_fully_open: "插件全开放,群辉等更多插件", + click_to_get_7_day_trial: "点击获取7天试用", + years: "年", + afdian_support_vip: "爱发电赞助“VIP会员”后获取一年期专业版激活码,开源需要您的支持", + get_after_support: "爱发电赞助后获取", + + business_edition: "商业版", + commercial_license: "商业授权,可对外运营", + all_pro_privileges: "拥有专业版所有特权", + allow_commercial_use_modify_logo_title: "允许商用,可修改logo、标题", + data_statistics: "数据统计", + plugin_management: "插件管理", + unlimited_multi_users: "多用户无限制", + support_user_payment: "支持用户支付", + contact_author_for_trial: "请联系作者获取试用", + activate: "激活", + get_pro_code_after_support: "爱发电赞助“VIP会员”后获取一年期专业版激活码", + business_contact_author: "商业版请直接联系作者", + year: "年", + freee: "免费", + renew: "续期", + activate_immediately: "立刻激活", + current: "当前", + activated_expire_time: "已激活,到期时间:", + site_id: "站点ID", + invite_code_optional: "邀请码【选填】,可额外获得专业版30天/商业版15天时长", + no_activation_code: "没有激活码?", + activation_code_one_use: "激活码使用过一次之后,不可再次使用,如果要更换站点,请", + bind_account: "绑定账号", + transfer_vip: ',然后"转移VIP"即可', +}; diff --git a/packages/ui/certd-client/src/vben/locales/typing.ts b/packages/ui/certd-client/src/locales/typing.ts similarity index 100% rename from packages/ui/certd-client/src/vben/locales/typing.ts rename to packages/ui/certd-client/src/locales/typing.ts diff --git a/packages/ui/certd-client/src/main.ts b/packages/ui/certd-client/src/main.ts index e7fba4bf..bf2d5db6 100644 --- a/packages/ui/certd-client/src/main.ts +++ b/packages/ui/certd-client/src/main.ts @@ -3,8 +3,7 @@ import App from "./App.vue"; // import Antd from "ant-design-vue"; import Antd from "./plugin/antdv-async/index"; import "./style/common.less"; -import { loadMessages } from "./i18n"; -import { i18n } from "/@/vben/locales"; +import { i18n, loadLocaleMessages } from "/@/locales" import components from "./components"; import router from "./router"; import plugin from "./plugin/"; @@ -16,15 +15,15 @@ import { initPreferences } from "/@/vben/preferences"; // import "./components/code-editor/import-works"; // @ts-ignore async function bootstrap() { - const app = createApp(App); - // app.use(Antd); - app.use(Antd); - await setupVben(app, { loadMessages, router }); - app.use(router); - // app.use(i18n); - // app.use(store); - app.use(components); - app.use(plugin, { i18n }); + const app = createApp(App); + // app.use(Antd); + app.use(Antd); + await setupVben(app, { loadLocaleMessages, router }); + app.use(router); + // app.use(i18n); + // app.use(store); + app.use(components); + app.use(plugin, { i18n }); const envMode = util.env.MODE; const namespace = `${import.meta.env.VITE_APP_NAMESPACE}-${envMode}`; diff --git a/packages/ui/certd-client/src/router/access.ts b/packages/ui/certd-client/src/router/access.ts index d96657fd..376bde71 100644 --- a/packages/ui/certd-client/src/router/access.ts +++ b/packages/ui/certd-client/src/router/access.ts @@ -12,7 +12,7 @@ async function generateAccess(options: GenerateMenuAndRoutesOptions) { const layoutMap: ComponentRecordType = { BasicLayout, - IFrameView + IFrameView, } as any; return await generateAccessible(preferences.app.accessMode, { @@ -28,7 +28,7 @@ async function generateAccess(options: GenerateMenuAndRoutesOptions) { forbiddenComponent, // 如果 route.meta.menuVisibleWithForbidden = true layoutMap, - pageMap + pageMap, }); } diff --git a/packages/ui/certd-client/src/router/source/framework.ts b/packages/ui/certd-client/src/router/source/framework.ts index 5805f9d4..29560b4b 100644 --- a/packages/ui/certd-client/src/router/source/framework.ts +++ b/packages/ui/certd-client/src/router/source/framework.ts @@ -1,6 +1,7 @@ import LayoutBasic from "/@/layout/layout-basic.vue"; import type { RouteRecordRaw } from "vue-router"; +import i18n from "/@/locales/i18n"; import { mergeRouteModules } from "/@/vben/utils"; const dynamicRouteFiles = import.meta.glob("./modules/**/*.ts*", { @@ -11,7 +12,7 @@ const dynamicRouteFiles = import.meta.glob("./modules/**/*.ts*", { const dynamicRoutes: RouteRecordRaw[] = mergeRouteModules(dynamicRouteFiles); export const frameworkResource = [ { - title: "框架", + title: "certd.framework.title", name: "root", path: "/", redirect: "/index", @@ -22,7 +23,7 @@ export const frameworkResource = [ }, children: [ { - title: "首页", + title: "certd.framework.home", name: "index", path: "/index", component: "/framework/home/index.vue", @@ -39,4 +40,5 @@ export const frameworkResource = [ ], }, ]; + console.assert(frameworkResource.length === 1, "frameworkResource数组长度只能为1,你只能配置framework路由的子路由"); diff --git a/packages/ui/certd-client/src/router/source/header.ts b/packages/ui/certd-client/src/router/source/header.ts index 26b96220..0debcd8c 100644 --- a/packages/ui/certd-client/src/router/source/header.ts +++ b/packages/ui/certd-client/src/router/source/header.ts @@ -1,33 +1,35 @@ +import i18n from "/@/locales/i18n"; + export const headerResource = [ { - title: "文档", + title: "certd.helpDoc", path: "https://certd.docmirror.cn", meta: { - icon: "ion:document-text-outline" - } + icon: "ion:document-text-outline", + }, }, { - title: "源码", + title: "certd.source", name: "source", key: "source", meta: { - icon: "ion:git-branch-outline" + icon: "ion:git-branch-outline", }, children: [ { - title: "github", + title: "certd.github", path: "https://github.com/certd/certd", meta: { - icon: "ion:logo-github" - } + icon: "ion:logo-github", + }, }, { - title: "gitee", + title: "certd.gitee", path: "https://gitee.com/certd/certd", meta: { - icon: "ion:logo-octocat" - } - } - ] - } + icon: "ion:logo-octocat", + }, + }, + ], + }, ]; diff --git a/packages/ui/certd-client/src/router/source/modules/about.tsx b/packages/ui/certd-client/src/router/source/modules/about.tsx index a2ca004e..e4c40d41 100644 --- a/packages/ui/certd-client/src/router/source/modules/about.tsx +++ b/packages/ui/certd-client/src/router/source/modules/about.tsx @@ -2,16 +2,18 @@ import { IFrameView } from "/@/vben/layouts"; import { useSettingStore } from "/@/store/settings"; import { computed } from "vue"; import TutorialButton from "/@/components/tutorial/index.vue"; +import i18n from "/@/locales/i18n"; + export const aboutResource = [ { - title: "文档", + title: "certd.dashboard.helpDoc", name: "document", path: "/about/doc", component: IFrameView, meta: { icon: "lucide:book-open-text", link: "https://certd.docmirror.cn", - title: "文档", + title: "certd.dashboard.helpDoc", order: 9999, show: () => { const settingStore = useSettingStore(); diff --git a/packages/ui/certd-client/src/router/source/modules/certd.ts b/packages/ui/certd-client/src/router/source/modules/certd.ts index 46b42361..cc255f8b 100644 --- a/packages/ui/certd-client/src/router/source/modules/certd.ts +++ b/packages/ui/certd-client/src/router/source/modules/certd.ts @@ -1,9 +1,10 @@ import { useSettingStore } from "/@/store/settings"; import aboutResource from "/@/router/source/modules/about"; +import i18n from "/@/locales/i18n"; export const certdResources = [ { - title: "证书自动化", + title: "certd.title", name: "CertdRoot", path: "/certd", redirect: "/certd/pipeline", @@ -14,7 +15,7 @@ export const certdResources = [ }, children: [ { - title: "证书自动化流水线", + title: "certd.pipeline", name: "PipelineManager", path: "/certd/pipeline", component: "/certd/pipeline/index.vue", @@ -24,7 +25,7 @@ export const certdResources = [ }, }, { - title: "编辑流水线", + title: "certd.pipelineEdit", name: "PipelineEdit", path: "/certd/pipeline/detail", component: "/certd/pipeline/detail.vue", @@ -33,7 +34,7 @@ export const certdResources = [ }, }, { - title: "执行历史记录", + title: "certd.history", name: "PipelineHistory", path: "/certd/history", component: "/certd/history/index.vue", @@ -71,7 +72,7 @@ export const certdResources = [ }, }, { - title: "证书仓库", + title: "certd.certStore", name: "CertStore", path: "/certd/monitor/cert", component: "/certd/monitor/cert/index.vue", @@ -83,7 +84,7 @@ export const certdResources = [ }, }, { - title: "站点证书监控", + title: "certd.siteMonitor", name: "SiteCertMonitor", path: "/certd/monitor/site", component: "/certd/monitor/site/index.vue", @@ -94,7 +95,7 @@ export const certdResources = [ }, }, { - title: "设置", + title: "certd.settings", name: "MineSetting", path: "/certd/setting", redirect: "/certd/access", @@ -105,7 +106,7 @@ export const certdResources = [ }, children: [ { - title: "授权管理", + title: "certd.accessManager", name: "AccessManager", path: "/certd/access", component: "/certd/access/index.vue", @@ -116,7 +117,7 @@ export const certdResources = [ }, }, { - title: "CNAME记录管理", + title: "certd.cnameRecord", name: "CnameRecord", path: "/certd/cname/record", component: "/certd/cname/record/index.vue", @@ -127,7 +128,7 @@ export const certdResources = [ }, }, { - title: "子域名托管设置", + title: "certd.subDomain", name: "SubDomain", path: "/certd/pipeline/subDomain", component: "/certd/pipeline/sub-domain/index.vue", @@ -138,7 +139,7 @@ export const certdResources = [ }, }, { - title: "流水线分组管理", + title: "certd.pipelineGroup", name: "PipelineGroupManager", path: "/certd/pipeline/group", component: "/certd/pipeline/group/index.vue", @@ -148,9 +149,8 @@ export const certdResources = [ keepAlive: true, }, }, - { - title: "开放接口密钥", + title: "certd.openKey", name: "OpenKey", path: "/certd/open/openkey", component: "/certd/open/openkey/index.vue", @@ -161,7 +161,7 @@ export const certdResources = [ }, }, { - title: "通知设置", + title: "certd.notification", name: "NotificationManager", path: "/certd/notification", component: "/certd/notification/index.vue", @@ -172,7 +172,7 @@ export const certdResources = [ }, }, { - title: "站点监控设置", + title: "certd.siteMonitorSetting", name: "SiteMonitorSetting", path: "/certd/monitor/setting", component: "/certd/monitor/site/setting/index.vue", @@ -183,7 +183,7 @@ export const certdResources = [ }, }, { - title: "认证安全设置", + title: "certd.userSecurity", name: "UserSecurity", path: "/certd/mine/security", component: "/certd/mine/security/index.vue", @@ -194,7 +194,7 @@ export const certdResources = [ }, }, { - title: "账号信息", + title: "certd.userProfile", name: "UserProfile", path: "/certd/mine/user-profile", component: "/certd/mine/user-profile.vue", @@ -206,9 +206,8 @@ export const certdResources = [ }, ], }, - { - title: "套餐", + title: "certd.suite", name: "SuiteProduct", path: "/certd/suite", redirect: "/certd/suite/mine", @@ -222,7 +221,7 @@ export const certdResources = [ }, children: [ { - title: "我的套餐", + title: "certd.mySuite", name: "MySuite", path: "/certd/suite/mine", component: "/certd/suite/mine/index.vue", @@ -236,7 +235,7 @@ export const certdResources = [ }, }, { - title: "套餐购买", + title: "certd.suiteBuy", name: "SuiteProductBuy", path: "/certd/suite/buy", component: "/certd/suite/buy.vue", @@ -250,7 +249,7 @@ export const certdResources = [ }, }, { - title: "我的订单", + title: "certd.myTrade", name: "MyTrade", path: "/certd/trade", component: "/certd/trade/index.vue", @@ -265,7 +264,7 @@ export const certdResources = [ }, }, { - title: "支付返回", + title: "certd.paymentReturn", name: "PaymentReturn", path: "/certd/payment/return/:type", component: "/certd/payment/return.vue", @@ -277,17 +276,6 @@ export const certdResources = [ }, ], }, - - // { - // title: "邮箱设置", - // name: "EmailSetting", - // path: "/sys/settings/email", - // component: "/sys/settings/email-setting.vue", - // meta: { - // icon: "ion:mail-outline", - // auth: true - // } - // }, ], }, ]; diff --git a/packages/ui/certd-client/src/router/source/modules/sys.ts b/packages/ui/certd-client/src/router/source/modules/sys.ts index fe290277..22937dd4 100644 --- a/packages/ui/certd-client/src/router/source/modules/sys.ts +++ b/packages/ui/certd-client/src/router/source/modules/sys.ts @@ -1,10 +1,11 @@ import LayoutPass from "/@/layout/layout-pass.vue"; import { useSettingStore } from "/@/store/settings"; import aboutResource from "/@/router/source/modules/about"; +import i18n from "/@/locales/i18n"; export const sysResources = [ { - title: "系统管理", + title: "certd.sysResources.sysRoot", name: "SysRoot", path: "/sys", redirect: "/sys/settings", @@ -15,7 +16,7 @@ export const sysResources = [ }, children: [ { - title: "控制台", + title: "certd.sysResources.sysConsole", name: "SysConsole", path: "/sys/console", component: "/sys/console/index.vue", @@ -30,7 +31,7 @@ export const sysResources = [ }, { - title: "系统设置", + title: "certd.sysResources.sysSettings", name: "SysSettings", path: "/sys/settings", component: "/sys/settings/index.vue", @@ -40,7 +41,7 @@ export const sysResources = [ }, }, { - title: "CNAME服务设置", + title: "certd.sysResources.cnameSetting", name: "CnameSetting", path: "/sys/cname/provider", component: "/sys/cname/provider/index.vue", @@ -51,7 +52,7 @@ export const sysResources = [ }, }, { - title: "邮件服务器设置", + title: "certd.sysResources.emailSetting", name: "EmailSetting", path: "/sys/settings/email", component: "/sys/settings/email/index.vue", @@ -62,7 +63,7 @@ export const sysResources = [ }, }, { - title: "站点个性化", + title: "certd.sysResources.siteSetting", name: "SiteSetting", path: "/sys/site", component: "/sys/site/index.vue", @@ -76,7 +77,7 @@ export const sysResources = [ }, }, { - title: "顶部菜单设置", + title: "certd.sysResources.headerMenus", name: "HeaderMenus", path: "/sys/settings/header-menus", component: "/sys/settings/header-menus/index.vue", @@ -91,7 +92,7 @@ export const sysResources = [ }, }, { - title: "系统级授权", + title: "certd.sysResources.sysAccess", name: "SysAccess", path: "/sys/access", component: "/sys/access/index.vue", @@ -106,7 +107,7 @@ export const sysResources = [ }, }, { - title: "插件管理", + title: "certd.sysResources.sysPlugin", name: "SysPlugin", path: "/sys/plugin", component: "/sys/plugin/index.vue", @@ -117,7 +118,7 @@ export const sysResources = [ }, }, { - title: "编辑插件", + title: "certd.sysResources.sysPluginEdit", name: "SysPluginEdit", path: "/sys/plugin/edit", component: "/sys/plugin/edit.vue", @@ -129,7 +130,7 @@ export const sysResources = [ }, }, { - title: "证书插件配置", + title: "certd.sysResources.sysPluginConfig", name: "SysPluginConfig", path: "/sys/plugin/config", component: "/sys/plugin/config.vue", @@ -143,7 +144,7 @@ export const sysResources = [ }, }, { - title: "账号绑定", + title: "certd.sysResources.accountBind", name: "AccountBind", path: "/sys/account", component: "/sys/account/index.vue", @@ -154,19 +155,18 @@ export const sysResources = [ }, }, { - title: "权限管理", + title: "certd.sysResources.permissionManager", name: "PermissionManager", path: "/sys/authority/permission", component: "/sys/authority/permission/index.vue", meta: { icon: "ion:list-outline", - //需要校验权限 permission: "sys:auth:per:view", keepAlive: true, }, }, { - title: "角色管理", + title: "certd.sysResources.roleManager", name: "RoleManager", path: "/sys/authority/role", component: "/sys/authority/role/index.vue", @@ -177,7 +177,7 @@ export const sysResources = [ }, }, { - title: "用户管理", + title: "certd.sysResources.userManager", name: "UserManager", path: "/sys/authority/user", component: "/sys/authority/user/index.vue", @@ -189,7 +189,7 @@ export const sysResources = [ }, { - title: "套餐管理", + title: "certd.sysResources.suiteManager", name: "SuiteManager", path: "/sys/suite", redirect: "/sys/suite/setting", @@ -204,7 +204,7 @@ export const sysResources = [ }, children: [ { - title: "套餐设置", + title: "certd.sysResources.suiteSetting", name: "SuiteSetting", path: "/sys/suite/setting", component: "/sys/suite/setting/index.vue", @@ -218,7 +218,7 @@ export const sysResources = [ }, }, { - title: "订单管理", + title: "certd.sysResources.orderManager", name: "OrderManager", path: "/sys/suite/trade", component: "/sys/suite/trade/index.vue", @@ -233,7 +233,7 @@ export const sysResources = [ }, }, { - title: "用户套餐", + title: "certd.sysResources.userSuites", name: "UserSuites", path: "/sys/suite/user-suite", component: "/sys/suite/user-suite/index.vue", diff --git a/packages/ui/certd-client/src/router/source/outside.ts b/packages/ui/certd-client/src/router/source/outside.ts index 2f521ab0..b86ddbdb 100644 --- a/packages/ui/certd-client/src/router/source/outside.ts +++ b/packages/ui/certd-client/src/router/source/outside.ts @@ -10,21 +10,21 @@ export const outsideResource = [ children: [ { meta: { - title: "登录" + title: "登录", }, name: "login", path: "/login", - component: "/framework/login/index.vue" + component: "/framework/login/index.vue", }, { meta: { - title: "注册" + title: "注册", }, name: "register", path: "/register", - component: "/framework/register/index.vue" - } - ] + component: "/framework/register/index.vue", + }, + ], }, - ...errorPage + ...errorPage, ]; diff --git a/packages/ui/certd-client/src/store/plugin/index.ts b/packages/ui/certd-client/src/store/plugin/index.ts index 17de169b..58cffb3b 100644 --- a/packages/ui/certd-client/src/store/plugin/index.ts +++ b/packages/ui/certd-client/src/store/plugin/index.ts @@ -1,7 +1,7 @@ import { defineStore } from "pinia"; import * as api from "./api.plugin"; import { DynamicType, FormItemProps } from "@fast-crud/fast-crud"; - +import { i18n } from "/src/locales/i18n"; interface PluginState { group?: PluginGroups; } @@ -31,8 +31,10 @@ export type PluginDefine = { export class PluginGroups { groups!: { [key: string]: PluginGroup }; map!: { [key: string]: PluginDefine }; + t: any; constructor(groups: { [key: string]: PluginGroup }) { this.groups = groups; + this.t = i18n.global.t; this.initGroup(groups); this.initMap(); } @@ -40,7 +42,7 @@ export class PluginGroups { private initGroup(groups: { [p: string]: PluginGroup }) { const all: PluginGroup = { key: "all", - title: "全部", + title: this.t("certd.all"), order: 0, plugins: [], icon: "material-symbols:border-all-rounded", diff --git a/packages/ui/certd-client/src/vben/common-ui/components/captcha/point-selection-captcha/index.vue b/packages/ui/certd-client/src/vben/common-ui/components/captcha/point-selection-captcha/index.vue index 3a8d2a38..19e032e0 100644 --- a/packages/ui/certd-client/src/vben/common-ui/components/captcha/point-selection-captcha/index.vue +++ b/packages/ui/certd-client/src/vben/common-ui/components/captcha/point-selection-captcha/index.vue @@ -2,7 +2,7 @@ import type { CaptchaPoint, PointSelectionCaptchaProps } from '../types'; import { RotateCw } from '/@/vben/icons'; -import { $t } from '/@/vben/locales'; +import { $t } from '/@/locales'; import { VbenButton, VbenIconButton } from '/@/vben/shadcn-ui'; diff --git a/packages/ui/certd-client/src/vben/common-ui/components/captcha/point-selection-captcha/point-selection-captcha-card.vue b/packages/ui/certd-client/src/vben/common-ui/components/captcha/point-selection-captcha/point-selection-captcha-card.vue index 9bdf7d9c..27f2fd23 100644 --- a/packages/ui/certd-client/src/vben/common-ui/components/captcha/point-selection-captcha/point-selection-captcha-card.vue +++ b/packages/ui/certd-client/src/vben/common-ui/components/captcha/point-selection-captcha/point-selection-captcha-card.vue @@ -3,7 +3,7 @@ import type { PointSelectionCaptchaCardProps } from '../types'; import { computed } from 'vue'; -import { $t } from '/@/vben/locales'; +import { $t } from '/@/locales'; import { Card, diff --git a/packages/ui/certd-client/src/vben/common-ui/components/captcha/slider-captcha/index.vue b/packages/ui/certd-client/src/vben/common-ui/components/captcha/slider-captcha/index.vue index 5757fcb6..806702e6 100644 --- a/packages/ui/certd-client/src/vben/common-ui/components/captcha/slider-captcha/index.vue +++ b/packages/ui/certd-client/src/vben/common-ui/components/captcha/slider-captcha/index.vue @@ -7,7 +7,7 @@ import type { import { reactive, unref, useTemplateRef, watch, watchEffect } from 'vue'; -import { $t } from '/@/vben/locales'; +import { $t } from '/@/locales'; import { cn } from '/@/vben/shared/utils'; diff --git a/packages/ui/certd-client/src/vben/common-ui/components/captcha/slider-rotate-captcha/index.vue b/packages/ui/certd-client/src/vben/common-ui/components/captcha/slider-rotate-captcha/index.vue index 06f118ff..c5b899be 100644 --- a/packages/ui/certd-client/src/vben/common-ui/components/captcha/slider-rotate-captcha/index.vue +++ b/packages/ui/certd-client/src/vben/common-ui/components/captcha/slider-rotate-captcha/index.vue @@ -8,7 +8,7 @@ import type { import { computed, reactive, unref, useTemplateRef, watch } from 'vue'; -import { $t } from '/@/vben/locales'; +import { $t } from '/@/locales'; import { useTimeoutFn } from '@vueuse/core'; diff --git a/packages/ui/certd-client/src/vben/common-ui/components/icon-picker/icon-picker.vue b/packages/ui/certd-client/src/vben/common-ui/components/icon-picker/icon-picker.vue index cfc5152f..80f26c6e 100644 --- a/packages/ui/certd-client/src/vben/common-ui/components/icon-picker/icon-picker.vue +++ b/packages/ui/certd-client/src/vben/common-ui/components/icon-picker/icon-picker.vue @@ -5,7 +5,7 @@ import { computed, ref, watch, watchEffect } from 'vue'; import { usePagination } from '/@/vben/hooks'; import { EmptyIcon, Grip, listIcons } from '/@/vben/icons'; -import { $t } from '/@/vben/locales'; +import { $t } from '/@/locales'; import { Button, diff --git a/packages/ui/certd-client/src/vben/common-ui/components/json-viewer/index.vue b/packages/ui/certd-client/src/vben/common-ui/components/json-viewer/index.vue index 1ef6b833..cc3fe7af 100644 --- a/packages/ui/certd-client/src/vben/common-ui/components/json-viewer/index.vue +++ b/packages/ui/certd-client/src/vben/common-ui/components/json-viewer/index.vue @@ -14,7 +14,7 @@ import { computed, useAttrs } from 'vue'; // @ts-ignore import VueJsonViewer from 'vue-json-viewer'; -import { $t } from '/@/vben/locales'; +import { $t } from '/@/locales'; import { isBoolean } from '/@/vben/shared/utils'; diff --git a/packages/ui/certd-client/src/vben/common-ui/ui/authentication/code-login.vue b/packages/ui/certd-client/src/vben/common-ui/ui/authentication/code-login.vue index e2e8b865..54b16f99 100644 --- a/packages/ui/certd-client/src/vben/common-ui/ui/authentication/code-login.vue +++ b/packages/ui/certd-client/src/vben/common-ui/ui/authentication/code-login.vue @@ -6,7 +6,7 @@ import type { VbenFormSchema } from '/@/vben/form-ui'; import { computed, reactive } from 'vue'; import { useRouter } from 'vue-router'; -import { $t } from '/@/vben/locales'; +import { $t } from '/@/locales'; import { useVbenForm } from '/@/vben/form-ui'; import { VbenButton } from '/@/vben/shadcn-ui'; diff --git a/packages/ui/certd-client/src/vben/common-ui/ui/authentication/forget-password.vue b/packages/ui/certd-client/src/vben/common-ui/ui/authentication/forget-password.vue index c1554a65..355fcc53 100644 --- a/packages/ui/certd-client/src/vben/common-ui/ui/authentication/forget-password.vue +++ b/packages/ui/certd-client/src/vben/common-ui/ui/authentication/forget-password.vue @@ -1,15 +1,15 @@ diff --git a/packages/ui/certd-client/src/vben/layouts/widgets/lock-screen/lock-screen-modal.vue b/packages/ui/certd-client/src/vben/layouts/widgets/lock-screen/lock-screen-modal.vue index b676a6f7..15fa089e 100644 --- a/packages/ui/certd-client/src/vben/layouts/widgets/lock-screen/lock-screen-modal.vue +++ b/packages/ui/certd-client/src/vben/layouts/widgets/lock-screen/lock-screen-modal.vue @@ -3,7 +3,7 @@ import type { Recordable } from "../../../types"; import { computed, reactive } from "vue"; -import { $t } from "../../../locales"; +import { $t } from "/@/locales"; import { useVbenForm, z } from "../../../form-ui"; import { useVbenModal } from "../../../popup-ui"; diff --git a/packages/ui/certd-client/src/vben/layouts/widgets/lock-screen/lock-screen.vue b/packages/ui/certd-client/src/vben/layouts/widgets/lock-screen/lock-screen.vue index d2a78bd8..fede0024 100644 --- a/packages/ui/certd-client/src/vben/layouts/widgets/lock-screen/lock-screen.vue +++ b/packages/ui/certd-client/src/vben/layouts/widgets/lock-screen/lock-screen.vue @@ -2,7 +2,7 @@ import { computed, reactive, ref } from "vue"; import { LockKeyhole } from "../../../icons"; -import { $t, useI18n } from "../../../locales"; +import { $t, useI18n } from "/@/locales"; import { storeToRefs, useLockStore } from "../../../stores"; import { useScrollLock } from "../../../composables"; diff --git a/packages/ui/certd-client/src/vben/layouts/widgets/notification/notification.vue b/packages/ui/certd-client/src/vben/layouts/widgets/notification/notification.vue index 02deffe0..e1e21f37 100644 --- a/packages/ui/certd-client/src/vben/layouts/widgets/notification/notification.vue +++ b/packages/ui/certd-client/src/vben/layouts/widgets/notification/notification.vue @@ -2,7 +2,7 @@ import type { NotificationItem } from "./types"; import { Bell, MailCheck } from "/@/vben/icons"; -import { $t } from "/@/vben/locales"; +import { $t } from "/@/locales"; import { VbenButton, VbenIconButton, VbenPopover, VbenScrollbar } from "/@/vben//shadcn-ui"; diff --git a/packages/ui/certd-client/src/vben/layouts/widgets/preferences/blocks/general/animation.vue b/packages/ui/certd-client/src/vben/layouts/widgets/preferences/blocks/general/animation.vue index 4652ada6..96d72488 100644 --- a/packages/ui/certd-client/src/vben/layouts/widgets/preferences/blocks/general/animation.vue +++ b/packages/ui/certd-client/src/vben/layouts/widgets/preferences/blocks/general/animation.vue @@ -1,5 +1,5 @@ - - - - - - - - {{ theme.name }} - - - - - {{ theme.tip }} - - - - - + + + + + + + + {{ theme.name }} + + + + + {{ theme.tip }} + + + + + diff --git a/packages/ui/certd-client/src/vben/layouts/widgets/preferences/blocks/layout/navigation.vue b/packages/ui/certd-client/src/vben/layouts/widgets/preferences/blocks/layout/navigation.vue index 9ffe3a18..2f8e8906 100644 --- a/packages/ui/certd-client/src/vben/layouts/widgets/preferences/blocks/layout/navigation.vue +++ b/packages/ui/certd-client/src/vben/layouts/widgets/preferences/blocks/layout/navigation.vue @@ -1,7 +1,7 @@ - {{ $t('preferences.widget.globalSearch') }} + {{ $t("preferences.widget.globalSearch") }} - {{ $t('preferences.widget.themeToggle') }} + {{ $t("preferences.widget.themeToggle") }} - {{ $t('preferences.widget.languageToggle') }} + {{ $t("preferences.widget.languageToggle") }} - {{ $t('preferences.widget.fullscreen') }} + {{ $t("preferences.widget.fullscreen") }} - {{ $t('preferences.widget.notification') }} + {{ $t("preferences.widget.notification") }} - {{ $t('preferences.widget.lockScreen') }} + {{ $t("preferences.widget.lockScreen") }} - {{ $t('preferences.widget.sidebarToggle') }} + {{ $t("preferences.widget.sidebarToggle") }} - {{ $t('preferences.widget.refresh') }} + {{ $t("preferences.widget.refresh") }} - {{ $t('preferences.position.title') }} + {{ $t("preferences.position.title") }} diff --git a/packages/ui/certd-client/src/vben/layouts/widgets/preferences/blocks/shortcut-keys/global.vue b/packages/ui/certd-client/src/vben/layouts/widgets/preferences/blocks/shortcut-keys/global.vue index 1845971b..f3dd8f16 100644 --- a/packages/ui/certd-client/src/vben/layouts/widgets/preferences/blocks/shortcut-keys/global.vue +++ b/packages/ui/certd-client/src/vben/layouts/widgets/preferences/blocks/shortcut-keys/global.vue @@ -1,7 +1,7 @@ diff --git a/packages/ui/certd-client/src/views/certd/history/crud.tsx b/packages/ui/certd-client/src/views/certd/history/crud.tsx index 8b1c1abb..04bc04e6 100644 --- a/packages/ui/certd-client/src/views/certd/history/crud.tsx +++ b/packages/ui/certd-client/src/views/certd/history/crud.tsx @@ -8,209 +8,209 @@ import { useSettingStore } from "/@/store/settings"; import { statusUtil } from "/@/views/certd/pipeline/pipeline/utils/util.status"; export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet { - const router = useRouter(); - const { t } = useI18n(); - const pageRequest = async (query: UserPageQuery): Promise => { - return await api.GetList(query); - }; - const editRequest = async ({ form, row }: EditReq) => { - form.id = row.id; - const res = await api.UpdateObj(form); - return res; - }; - const delRequest = async ({ row }: DelReq) => { - return await api.DelObj(row.id); - }; + const router = useRouter(); + const { t } = useI18n(); + const pageRequest = async (query: UserPageQuery): Promise => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }: EditReq) => { + form.id = row.id; + const res = await api.UpdateObj(form); + return res; + }; + const delRequest = async ({ row }: DelReq) => { + return await api.DelObj(row.id); + }; - const addRequest = async ({ form }: AddReq) => { - const res = await api.AddObj(form); - return res; - }; + const addRequest = async ({ form }: AddReq) => { + const res = await api.AddObj(form); + return res; + }; - const userStore = useUserStore(); - const settingStore = useSettingStore(); - const selectedRowKeys: Ref = ref([]); - context.selectedRowKeys = selectedRowKeys; + const userStore = useUserStore(); + const settingStore = useSettingStore(); + const selectedRowKeys: Ref = ref([]); + context.selectedRowKeys = selectedRowKeys; - return { - crudOptions: { - settings: { - plugins: { - //这里使用行选择插件,生成行选择crudOptions配置,最终会与crudOptions合并 - rowSelection: { - enabled: true, - order: -2, - before: true, - // handle: (pluginProps,useCrudProps)=>CrudOptions, - props: { - multiple: true, - crossPage: true, - selectedRowKeys, - }, - }, - }, - }, - request: { - pageRequest, - addRequest, - editRequest, - delRequest, - }, - actionbar: { - buttons: { - add: { - show: false, - }, - }, - }, - search: { - formItem: { - labelCol: { - style: { - // width: "100px" - }, - }, - wrapperCol: { - style: { - width: "50%", - }, - }, - }, - }, - rowHandle: { - minWidth: 200, - fixed: "right", - buttons: { - edit: { - show: false, - }, - }, - }, - columns: { - id: { - title: "ID", - key: "id", - type: "number", - column: { - width: 100, - }, - form: { - show: false, - }, - }, - userId: { - title: "用户Id", - type: "number", - search: { - show: computed(() => { - return userStore.isAdmin && settingStore.sysPublic.managerOtherUserPipeline; - }), - }, - form: { - show: false, - }, - column: { - show: computed(() => { - return userStore.isAdmin && settingStore.sysPublic.managerOtherUserPipeline; - }), - width: 100, - }, - }, - pipelineId: { - title: "流水线Id", - type: "number", - search: { - show: true, - }, - form: { - show: false, - }, - column: { - width: 100, - }, - }, - pipelineTitle: { - title: "流水线名称", - type: "text", - search: { - show: true, - }, - column: { - width: 300, - tooltip: true, - ellipsis: true, - cellRender: ({ row, value }) => { - return {value}; - }, - }, - }, - triggerType: { - title: "触发类型", - type: "dict-select", - search: { - show: true, - }, - dict: dict({ - data: [ - { value: "user", label: "手动执行" }, - { value: "timer", label: "定时执行" }, - ], - }), - form: { - show: false, - value: "custom", - }, - column: { - sorter: true, - width: 90, - align: "center", - show: true, - component: { - color: "auto", - }, - }, - }, - status: { - title: "状态", - type: "dict-select", - search: { - show: true, - }, - dict: dict({ - data: statusUtil.getOptions(), - }), - form: { - show: false, - }, - column: { - sorter: true, - width: 120, - align: "center", - }, - }, - createTime: { - title: "创建时间", - type: "datetime", - form: { - show: false, - }, - column: { - sorter: true, - width: 160, - align: "center", - }, - }, - updateTime: { - title: "更新时间", - type: "datetime", - form: { - show: false, - }, - column: { - show: true, - }, - }, - }, - }, - }; + return { + crudOptions: { + settings: { + plugins: { + //这里使用行选择插件,生成行选择crudOptions配置,最终会与crudOptions合并 + rowSelection: { + enabled: true, + order: -2, + before: true, + // handle: (pluginProps,useCrudProps)=>CrudOptions, + props: { + multiple: true, + crossPage: true, + selectedRowKeys, + }, + }, + }, + }, + request: { + pageRequest, + addRequest, + editRequest, + delRequest, + }, + actionbar: { + buttons: { + add: { + show: false, + }, + }, + }, + search: { + formItem: { + labelCol: { + style: { + // width: "100px" + }, + }, + wrapperCol: { + style: { + width: "50%", + }, + }, + }, + }, + rowHandle: { + minWidth: 200, + fixed: "right", + buttons: { + edit: { + show: false, + }, + }, + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 100, + }, + form: { + show: false, + }, + }, + userId: { + title: t("certd.fields.userId"), + type: "number", + search: { + show: computed(() => { + return userStore.isAdmin && settingStore.sysPublic.managerOtherUserPipeline; + }), + }, + form: { + show: false, + }, + column: { + show: computed(() => { + return userStore.isAdmin && settingStore.sysPublic.managerOtherUserPipeline; + }), + width: 100, + }, + }, + pipelineId: { + title: t("certd.fields.pipelineId"), + type: "number", + search: { + show: true, + }, + form: { + show: false, + }, + column: { + width: 100, + }, + }, + pipelineTitle: { + title: t('certd.fields.pipelineName'), + type: "text", + search: { + show: true, + }, + column: { + width: 300, + tooltip: true, + ellipsis: true, + cellRender: ({ row, value }) => { + return {value}; + }, + }, + }, + triggerType: { + title: t("certd.fields.triggerType"), + type: "dict-select", + search: { + show: true, + }, + dict: dict({ + data: [ + { value: "user", label: t("certd.triggerTypes.manual") }, + { value: "timer", label: t("certd.triggerTypes.timer") }, + ], + }), + form: { + show: false, + value: "custom", + }, + column: { + sorter: true, + width: 90, + align: "center", + show: true, + component: { + color: "auto", + }, + }, + }, + status: { + title: t("certd.fields.status"), + type: "dict-select", + search: { + show: true, + }, + dict: dict({ + data: statusUtil.getOptions(), + }), + form: { + show: false, + }, + column: { + sorter: true, + width: 120, + align: "center", + }, + }, + createTime: { + title: t("certd.fields.createTime"), + type: "datetime", + form: { + show: false, + }, + column: { + sorter: true, + width: 160, + align: "center", + }, + }, + updateTime: { + title: t("certd.fields.updateTime"), + type: "datetime", + form: { + show: false, + }, + column: { + show: true, + }, + }, + }, + }, + }; } diff --git a/packages/ui/certd-client/src/views/certd/history/index.vue b/packages/ui/certd-client/src/views/certd/history/index.vue index 6df7ed42..e6232d26 100644 --- a/packages/ui/certd-client/src/views/certd/history/index.vue +++ b/packages/ui/certd-client/src/views/certd/history/index.vue @@ -1,11 +1,11 @@ - 流水线执行记录 + {{ t("certd.pipelineExecutionRecords") }} - + @@ -13,12 +13,16 @@ + diff --git a/packages/ui/certd-client/src/views/certd/mine/user-profile.vue b/packages/ui/certd-client/src/views/certd/mine/user-profile.vue index 72d048bd..244db826 100644 --- a/packages/ui/certd-client/src/views/certd/mine/user-profile.vue +++ b/packages/ui/certd-client/src/views/certd/mine/user-profile.vue @@ -1,41 +1,50 @@ - - - 我的信息 - - - - {{ userInfo.username }} - - - - {{ userInfo.username }} - - - {{ userInfo.nickName }} - {{ userInfo.email }} - {{ userInfo.phoneCode }}{{ userInfo.mobile }} - - - - - - + + + {{ t("certd.myInfo") }} + + + + {{ userInfo.username }} + + + + + {{ userInfo.username }} + + + {{ userInfo.nickName }} + {{ userInfo.email }} + {{ userInfo.phoneCode }}{{ userInfo.mobile + }} + + + + + + + + + diff --git a/packages/ui/certd-client/src/views/certd/monitor/cert/crud.tsx b/packages/ui/certd-client/src/views/certd/monitor/cert/crud.tsx index 65dc4f9c..88f3bce9 100644 --- a/packages/ui/certd-client/src/views/certd/monitor/cert/crud.tsx +++ b/packages/ui/certd-client/src/views/certd/monitor/cert/crud.tsx @@ -1,5 +1,6 @@ // @ts-ignore import { useI18n } from "vue-i18n"; +// import { AddReq, compute, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, useFormWrapper, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud"; import { certInfoApi } from "./api"; import dayjs from "dayjs"; @@ -10,285 +11,288 @@ import CertView from "/@/views/certd/pipeline/cert-view.vue"; import { useCertUpload } from "/@/views/certd/pipeline/cert-upload/use"; export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet { - const api = certInfoApi; - const pageRequest = async (query: UserPageQuery): Promise => { - return await api.GetList(query); - }; - const editRequest = async (req: EditReq) => { - const { form, row } = req; - form.id = row.id; - const res = await api.UpdateObj(form); - return res; - }; - const delRequest = async (req: DelReq) => { - const { row } = req; - return await api.DelObj(row.id); - }; + const { t } = useI18n(); + const api = certInfoApi; + const pageRequest = async (query: UserPageQuery): Promise => { + return await api.GetList(query); + }; + const editRequest = async (req: EditReq) => { + const { form, row } = req; + form.id = row.id; + const res = await api.UpdateObj(form); + return res; + }; + const delRequest = async (req: DelReq) => { + const { row } = req; + return await api.DelObj(row.id); + }; - const addRequest = async (req: AddReq) => { - const { form } = req; - const res = await api.AddObj(form); - return res; - }; - const { openCrudFormDialog } = useFormWrapper(); - const router = useRouter(); + const addRequest = async (req: AddReq) => { + const { form } = req; + const res = await api.AddObj(form); + return res; + }; + const { openCrudFormDialog } = useFormWrapper(); + const router = useRouter(); - const model = useModal(); - const viewCert = async (row: any) => { - const cert = await api.GetCert(row.id); - if (!cert) { - notification.error({ message: "证书还未生成,请先运行流水线" }); - return; - } + const model = useModal(); + const viewCert = async (row: any) => { + const cert = await api.GetCert(row.id); + if (!cert) { + notification.error({ message: t("certd.certificateNotGenerated") }); + return; + } - model.success({ - title: "查看证书", - maskClosable: true, - okText: "关闭", - width: 800, - content: () => { - return ; - }, - }); - }; + model.success({ + title: t("certd.modal.viewCertificateTitle"), + maskClosable: true, + okText: t("certd.modal.close"), + width: 800, + content: () => { + return ; + }, + }); + }; - const { openUploadCreateDialog, openUpdateCertDialog } = useCertUpload(); - return { - crudOptions: { - request: { - pageRequest, - addRequest, - editRequest, - delRequest, - }, - form: { - labelCol: { - //固定label宽度 - span: null, - style: { - width: "100px", - }, - }, - col: { - span: 22, - }, - wrapper: { - width: 600, - }, - }, - actionbar: { - show: true, - buttons: { - add: { - text: "上传自定义证书", - type: "primary", - show: false, - async click() { - await openUploadCreateDialog(); - }, - }, - }, - }, - tabs: { - name: "fromType", - show: true, - }, - rowHandle: { - width: 100, - fixed: "right", - buttons: { - view: { show: false }, - viewCert: { - order: 3, - title: "查看证书", - type: "link", - icon: "ph:certificate", - async click({ row }) { - await viewCert(row); - }, - }, - copy: { show: false }, - edit: { show: false }, - remove: { - order: 10, - show: false, - }, - download: { - order: 9, - title: "下载证书", - type: "link", - icon: "ant-design:download-outlined", - async click({ row }) { - if (!row.certFile) { - notification.error({ message: "证书还未生成,请先运行流水线" }); - return; - } - window.open("/api/monitor/cert/download?id=" + row.id); - }, - }, - }, - }, - columns: { - id: { - title: "ID", - key: "id", - type: "number", - search: { - show: false, - }, - column: { - width: 100, - editable: { - disabled: true, - }, - }, - form: { - show: false, - }, - }, - fromType: { - title: "来源", - search: { - show: true, - }, - type: "dict-select", - dict: dict({ - data: [ - { label: "流水线", value: "pipeline" }, - { label: "手动上传", value: "upload" }, - ], - }), - form: { - show: false, - }, - column: { - width: 100, - sorter: true, - component: { - color: "auto", - }, - conditionalRender: false, - }, - valueBuilder({ value, row, key }) { - if (!value) { - row[key] = "pipeline"; - } - }, - }, - domains: { - title: "域名", - search: { - show: true, - }, - type: "text", - form: { - rules: [{ required: true, message: "请输入域名" }], - }, - column: { - width: 450, - sorter: true, - component: { - name: "fs-values-format", - color: "auto", - }, - }, - }, - domainCount: { - title: "域名数量", - type: "number", - form: { - show: false, - }, - column: { - width: 120, - sorter: true, - show: false, - }, - }, - expiresLeft: { - title: "有效天数", - search: { - show: false, - }, - type: "date", - form: { - show: false, - }, - column: { - sorter: true, - conditionalRender: false, - cellRender({ row }) { - const value = row.expiresTime; - if (!value) { - return "-"; - } - const expireDate = dayjs(value).format("YYYY-MM-DD"); - const leftDays = dayjs(value).diff(dayjs(), "day"); - const color = leftDays < 20 ? "red" : "#389e0d"; - const percent = (leftDays / 90) * 100; - return `${leftDays}天`} />; - }, - }, - }, - expiresTime: { - title: "过期时间", - search: { - show: false, - }, - type: "datetime", - form: { - show: false, - }, - column: { - sorter: true, - }, - }, - certProvider: { - title: "证书颁发机构", - search: { - show: false, - }, - type: "text", - form: { - show: false, - }, - column: { - width: 200, - }, - }, - applyTime: { - title: "申请时间", - search: { - show: false, - }, - type: "datetime", - form: { - show: false, - }, - column: { - sorter: true, - }, - }, - "pipeline.title": { - title: "关联流水线", - search: { show: false }, - type: "link", - form: { - show: false, - }, - column: { - width: 350, - sorter: true, - component: { - on: { - onClick({ row }) { - router.push({ path: "/certd/pipeline/detail", query: { id: row.pipelineId, editMode: "false" } }); - }, - }, - }, - }, - }, - }, - }, - }; + const { openUploadCreateDialog, openUpdateCertDialog } = useCertUpload(); + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest, + }, + form: { + labelCol: { + //固定label宽度 + span: null, + style: { + width: "100px", + }, + }, + col: { + span: 22, + }, + wrapper: { + width: 600, + }, + }, + actionbar: { + show: true, + buttons: { + add: { + text: t('certd.uploadCustomCert'), + type: "primary", + show: false, + async click() { + await openUploadCreateDialog(); + }, + }, + }, + }, + tabs: { + name: "fromType", + show: true, + }, + rowHandle: { + width: 100, + fixed: "right", + buttons: { + view: { show: false }, + viewCert: { + order: 3, + title: t("certd.viewCert.title"), + type: "link", + icon: "ph:certificate", + async click({ row }) { + await viewCert(row); + }, + }, + copy: { show: false }, + edit: { show: false }, + remove: { + order: 10, + show: false, + }, + download: { + order: 9, + title: t("certd.download.title"), + type: "link", + icon: "ant-design:download-outlined", + async click({ row }) { + if (!row.certFile) { + notification.error({ message: t("certd.certificateNotGenerated") }); + return; + } + window.open("/api/monitor/cert/download?id=" + row.id); + }, + }, + + }, + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + search: { + show: false, + }, + column: { + width: 100, + editable: { + disabled: true, + }, + }, + form: { + show: false, + }, + }, + fromType: { + title: t('certd.sourcee'), + search: { + show: true, + }, + type: "dict-select", + dict: dict({ + data: [ + { label: t('certd.sourcePipeline'), value: "pipeline" }, + { label: t('certd.sourceManualUpload'), value: "upload" }, + ], + }), + form: { + show: false, + }, + column: { + width: 100, + sorter: true, + component: { + color: "auto", + }, + conditionalRender: false, + }, + valueBuilder({ value, row, key }) { + if (!value) { + row[key] = "pipeline"; + } + }, + }, + domains: { + title: t('certd.domains'), + search: { + show: true, + }, + type: "text", + form: { + rules: [{ required: true, message: t('certd.enterDomain') }], + }, + column: { + width: 450, + sorter: true, + component: { + name: "fs-values-format", + color: "auto", + }, + }, + }, + domainCount: { + title: t('certd.domainCount'), + type: "number", + form: { + show: false, + }, + column: { + width: 120, + sorter: true, + show: false, + }, + }, + expiresLeft: { + title: t('certd.validDays'), + search: { + show: false, + }, + type: "date", + form: { + show: false, + }, + column: { + sorter: true, + conditionalRender: false, + cellRender({ row }) { + const value = row.expiresTime; + if (!value) { + return "-"; + } + const expireDate = dayjs(value).format("YYYY-MM-DD"); + const leftDays = dayjs(value).diff(dayjs(), "day"); + const color = leftDays < 20 ? "red" : "#389e0d"; + const percent = (leftDays / 90) * 100; + return `${leftDays}${t('certd.days')}`} />; + }, + }, + }, + expiresTime: { + title: t('certd.expireTime'), + search: { + show: false, + }, + type: "datetime", + form: { + show: false, + }, + column: { + sorter: true, + }, + }, + certProvider: { + title: t('certd.certIssuer'), + search: { + show: false, + }, + type: "text", + form: { + show: false, + }, + column: { + width: 200, + }, + }, + applyTime: { + title: t('certd.applyTime'), + search: { + show: false, + }, + type: "datetime", + form: { + show: false, + }, + column: { + sorter: true, + }, + }, + "pipeline.title": { + title: t('certd.relatedPipeline'), + search: { show: false }, + type: "link", + form: { + show: false, + }, + column: { + width: 350, + sorter: true, + component: { + on: { + onClick({ row }) { + router.push({ path: "/certd/pipeline/detail", query: { id: row.pipelineId, editMode: "false" } }); + }, + }, + }, + }, + }, + + }, + }, + }; } diff --git a/packages/ui/certd-client/src/views/certd/monitor/cert/index.vue b/packages/ui/certd-client/src/views/certd/monitor/cert/index.vue index ad696cb4..d2bf6ea7 100644 --- a/packages/ui/certd-client/src/views/certd/monitor/cert/index.vue +++ b/packages/ui/certd-client/src/views/certd/monitor/cert/index.vue @@ -1,30 +1,33 @@ - - - - 证书仓库 - 从流水线生成的证书 - - - - + + + + {{ t("certd.certificateRepo.title") }} + {{ t("certd.certificateRepo.sub") }} + + + + diff --git a/packages/ui/certd-client/src/views/certd/monitor/site/crud.tsx b/packages/ui/certd-client/src/views/certd/monitor/site/crud.tsx index 5607d9c9..27b1fab3 100644 --- a/packages/ui/certd-client/src/views/certd/monitor/site/crud.tsx +++ b/packages/ui/certd-client/src/views/certd/monitor/site/crud.tsx @@ -11,505 +11,513 @@ import { useSiteIpMonitor } from "./ip/use"; import { useSiteImport } from "/@/views/certd/monitor/site/use"; export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet { - const { t } = useI18n(); - const api = siteInfoApi; - const { crudBinding } = crudExpose; - const pageRequest = async (query: UserPageQuery): Promise => { - return await api.GetList(query); - }; - const editRequest = async (req: EditReq) => { - const { form, row } = req; - form.id = row.id; - const res = await api.UpdateObj(form); - return res; - }; - const delRequest = async (req: DelReq) => { - const { row } = req; - return await api.DelObj(row.id); - }; + const { t } = useI18n(); + const api = siteInfoApi; + const { crudBinding } = crudExpose; + const pageRequest = async (query: UserPageQuery): Promise => { + return await api.GetList(query); + }; + const editRequest = async (req: EditReq) => { + const { form, row } = req; + form.id = row.id; + const res = await api.UpdateObj(form); + return res; + }; + const delRequest = async (req: DelReq) => { + const { row } = req; + return await api.DelObj(row.id); + }; - const addRequest = async (req: AddReq) => { - const { form } = req; - const res = await api.AddObj(form); - return res; - }; + const addRequest = async (req: AddReq) => { + const { form } = req; + const res = await api.AddObj(form); + return res; + }; - const settingsStore = useSettingStore(); + const settingsStore = useSettingStore(); - const checkStatusDict = dict({ - data: [ - { label: "成功", value: "ok", color: "green" }, - { label: "检查中", value: "checking", color: "blue" }, - { label: "异常", value: "error", color: "red" }, - ], - }); + const checkStatusDict = dict({ + data: [ + { label: t("checkStatus.success"), value: "ok", color: "green" }, + { label: t("checkStatus.checking"), value: "checking", color: "blue" }, + { label: t("checkStatus.error"), value: "error", color: "red" }, + ], + }); - const { openSiteIpMonitorDialog } = useSiteIpMonitor(); - const { openSiteImportDialog } = useSiteImport(); - return { - crudOptions: { - request: { - pageRequest, - addRequest, - editRequest, - delRequest, - }, - form: { - labelCol: { - //固定label宽度 - span: null, - style: { - width: "100px", - }, - }, - col: { - span: 22, - }, - wrapper: { - width: 600, - }, - }, - actionbar: { - buttons: { - add: { - async click() { - if (!settingsStore.isPlus) { - //非plus - if (crudBinding.value.data.length >= 1) { - notification.error({ - message: "基础版只能添加一个监控站点,请赞助升级专业版", - }); - mitter.emit("openVipModal"); - return; - } - } - //检查是否监控站点数量超出限制 - if (settingsStore.isComm && settingsStore.suiteSetting.enabled) { - //检查数量是否超限 - const suiteDetail = await mySuiteApi.SuiteDetailGet(); - const max = suiteDetail.monitorCount.max; - if (max != -1 && max <= suiteDetail.monitorCount.used) { - notification.error({ - message: `对不起,您最多只能创建条${max}监控记录,请购买或升级套餐`, - }); - return; - } - } - await crudExpose.openAdd({}); - }, - }, - //导入按钮 - import: { - show: true, - text: "批量导入", - type: "primary", - async click() { - openSiteImportDialog({ - afterSubmit() { - crudExpose.doRefresh(); - }, - }); - }, - }, - }, - }, - rowHandle: { - fixed: "right", - width: 240, - buttons: { - check: { - order: 0, - type: "link", - text: null, - tooltip: { - title: "立即检查", - }, - icon: "ion:play-sharp", - click: async ({ row }) => { - await api.DoCheck(row.id); - await crudExpose.doRefresh(); - notification.success({ - message: "检查任务已提交,请稍后刷新查看结果", - }); - }, - }, - ipCheck: { - order: 10, - type: "link", - text: null, - show: compute(({ row }) => { - return row.ipCheck === true; - }), - tooltip: { - title: "IP管理", - }, - icon: "entypo:address", - click: async ({ row }) => { - openSiteIpMonitorDialog({ siteId: row.id }); - }, - }, - }, - }, - tabs: { - name: "disabled", - show: true, - }, - columns: { - id: { - title: "ID", - key: "id", - type: "number", - search: { - show: false, - }, - column: { - width: 80, - align: "center", - }, - form: { - show: false, - }, - }, - name: { - title: "站点名称", - search: { - show: true, - }, - type: "text", - form: { - rules: [{ required: true, message: "请输入站点名称" }], - }, - column: { - width: 160, - }, - }, - domain: { - title: "网站域名", - search: { - show: true, - }, - type: "text", - form: { - rules: [ - { required: true, message: "请输入域名" }, - //@ts-ignore - { type: "domains", message: "请输入正确的域名" }, - ], - }, - column: { - width: 230, - sorter: true, - cellRender({ value, row }) { - const url = `https://${value}:${row.httpsPort}`; - return ( - - - - {value}:{row.httpsPort} - - - - ); - }, - }, - }, - httpsPort: { - title: "HTTPS端口", - search: { - show: false, - }, - type: "number", - form: { - value: 443, - rules: [{ required: true, message: "请输入端口" }], - }, - column: { - width: 100, - show: false, - }, - }, - certInfo: { - title: "证书信息", - type: "text", - form: { show: false }, - column: { - width: 200, - sorter: false, - show: true, - conditionalRender: false, - cellRender({ value, row }) { - const slots = { - content() { - return ( - - 证书颁发机构:{row.certProvider} - 证书域名:{row.certDomains} - - ); - }, - }; - return ( - - {row.certDomains} - - ); - }, - }, - }, - certDomains: { - title: "证书域名", - search: { - show: true, - }, - type: "text", - form: { - show: false, - }, - column: { - width: 200, - sorter: true, - show: false, - cellRender({ value }) { - return ( - - {value} - - ); - }, - }, - }, - certProvider: { - title: "颁发机构", - search: { - show: false, - }, - type: "text", - form: { - show: false, - }, - column: { - width: 200, - sorter: true, - show: false, - cellRender({ value }) { - return {value}; - }, - }, - }, - certStatus: { - title: "证书状态", - search: { - show: true, - }, - type: "dict-select", - dict: dict({ - data: [ - { label: "正常", value: "ok", color: "green" }, - { label: "过期", value: "expired", color: "red" }, - ], - }), - form: { - show: false, - }, - column: { - width: 100, - sorter: true, - show: true, - align: "center", - }, - }, - certExpiresTime: { - title: "证书到期时间", - search: { - show: false, - }, - type: "date", - form: { - show: false, - }, - column: { - sorter: true, - cellRender({ value }) { - if (!value) { - return "-"; - } - const expireDate = dayjs(value).format("YYYY-MM-DD"); - const leftDays = dayjs(value).diff(dayjs(), "day"); - const color = leftDays < 20 ? "red" : "#389e0d"; - const percent = (leftDays / 90) * 100; - return `${leftDays}天`} />; - }, - }, - }, - lastCheckTime: { - title: "上次检查时间", - search: { - show: false, - }, - type: "datetime", - form: { - show: false, - }, - column: { - sorter: true, - width: 155, - }, - }, - disabled: { - title: "禁用启用", - search: { - show: false, - }, - type: "dict-switch", - dict: dict({ - data: [ - { label: "启用", value: false, color: "green" }, - { label: "禁用", value: true, color: "red" }, - ], - }), - form: { - value: false, - }, - column: { - width: 100, - sorter: true, - align: "center", - component: { - name: "fs-dict-switch", - vModel: "checked", - on: { - async change({ row, $event }) { - await api.DisabledChange(row.id, $event); - await crudExpose.doRefresh(); - }, - }, - }, - }, - }, - ipCheck: { - title: "开启IP检查", - type: "dict-switch", - dict: dict({ - data: [ - { label: "启用", value: true, color: "green" }, - { label: "禁用", value: false, color: "gray" }, - ], - }), - form: { - value: false, - rules: [{ required: true, message: "请选择" }], - }, - column: { - align: "center", - width: 100, - conditionalRender: false, - component: { - name: "fs-dict-switch", - vModel: "checked", - on: { - change({ row, $event }) { - Modal.confirm({ - title: "提示", - content: `确定${$event ? "开启" : "关闭"}IP检查?`, - onOk: async () => { - await api.IpCheckChange(row.id, $event); - await crudExpose.doRefresh(); - if ($event) { - openSiteIpMonitorDialog({ siteId: row.id }); - } - }, - onCancel: async () => { - await crudExpose.doRefresh(); - }, - }); - }, - }, - }, - }, - } as ColumnCompositionProps, - ipCount: { - title: "IP数量", - search: { - show: false, - }, - type: "text", - form: { - show: false, - }, - column: { - width: 100, - sorter: true, - align: "center", - }, - }, - checkStatus: { - title: "检查状态", - search: { - show: false, - }, - type: "dict-select", - dict: checkStatusDict, - form: { - show: false, - }, - column: { - width: 100, - align: "center", - sorter: true, - cellRender({ value, row, key }) { - return ( - - - - ); - }, - }, - }, - // error: { - // title: "错误信息", - // search: { - // show: false - // }, - // type: "text", - // form: { - // show: false - // }, - // column: { - // width: 200, - // sorter: true, - // cellRender({ value }) { - // return {value}; - // } - // } - // }, - pipelineId: { - title: "关联流水线id", - search: { - show: false, - }, - form: { show: false }, - type: "number", - column: { - width: 200, - sorter: true, - show: false, - }, - }, - certInfoId: { - title: "证书id", - search: { - show: false, - }, - type: "number", - form: { show: false }, - column: { - width: 100, - sorter: true, - show: false, - }, - }, - }, - }, - }; + const { openSiteIpMonitorDialog } = useSiteIpMonitor(); + const { openSiteImportDialog } = useSiteImport(); + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest, + }, + form: { + labelCol: { + //固定label宽度 + span: null, + style: { + width: "100px", + }, + }, + col: { + span: 22, + }, + wrapper: { + width: 600, + }, + }, + actionbar: { + buttons: { + add: { + async click() { + if (!settingsStore.isPlus) { + // 非plus + if (crudBinding.value.data.length >= 1) { + notification.error({ + message: t("certd.monitor.basicLimitError"), + }); + mitter.emit("openVipModal"); + return; + } + } + + + //检查是否监控站点数量超出限制 + if (settingsStore.isComm && settingsStore.suiteSetting.enabled) { + // 检查数量是否超限 + const suiteDetail = await mySuiteApi.SuiteDetailGet(); + const max = suiteDetail.monitorCount.max; + if (max != -1 && max <= suiteDetail.monitorCount.used) { + notification.error({ + message: t("certd.monitor.limitExceeded", { max }), + }); + return; + } + } + + await crudExpose.openAdd({}); + }, + }, + //导入按钮 + import: { + show: true, + text: t("certd.monitor.bulkImport"), + type: "primary", + async click() { + openSiteImportDialog({ + afterSubmit() { + crudExpose.doRefresh(); + }, + }); + }, + }, + }, + }, + rowHandle: { + fixed: "right", + width: 240, + buttons: { + check: { + order: 0, + type: "link", + text: null, + tooltip: { + title: "立即检查", + }, + icon: "ion:play-sharp", + click: async ({ row }) => { + await api.DoCheck(row.id); + await crudExpose.doRefresh(); + notification.success({ + message: t("certd.monitor.checkSubmittedRefresh"), + }); + }, + }, + ipCheck: { + order: 10, + type: "link", + text: null, + show: compute(({ row }) => row.ipCheck === true), + tooltip: { + title: t("certd.monitor.ipManagement"), + }, + icon: "entypo:address", + click: async ({ row }) => { + openSiteIpMonitorDialog({ siteId: row.id }); + }, + }, + }, + }, + tabs: { + name: "disabled", + show: true, + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + search: { + show: false, + }, + column: { + width: 80, + align: "center", + }, + form: { + show: false, + }, + }, + name: { + title: t("certd.monitor.siteName"), + search: { + show: true, + }, + type: "text", + form: { + rules: [{ required: true, message: t("certd.monitor.enterSiteName") }], + }, + column: { + width: 160, + }, + }, + domain: { + title: t("certd.monitor.domain"), + search: { + show: true, + }, + type: "text", + form: { + rules: [ + { required: true, message: t("certd.monitor.enterDomain") }, + // @ts-ignore + { type: "domains", message: t("certd.monitor.enterValidDomain") }, + ], + }, + column: { + width: 230, + sorter: true, + cellRender({ value, row }) { + const url = `https://${value}:${row.httpsPort}`; + return ( + + + + {value}:{row.httpsPort} + + + + ); + }, + }, + }, + httpsPort: { + title: t("certd.monitor.httpsPort"), + search: { + show: false, + }, + type: "number", + form: { + value: 443, + rules: [{ required: true, message: t("certd.monitor.enterPort") }], + }, + column: { + width: 100, + show: false, + }, + }, + certInfo: { + title: t("certd.monitor.certInfo"), + type: "text", + form: { show: false }, + column: { + width: 200, + sorter: false, + show: true, + conditionalRender: false, + cellRender({ value, row }) { + const slots = { + content() { + return ( + + {t("certd.monitor.issuer")}: {row.certProvider} + {t("certd.monitor.certDomains")}: {row.certDomains} + + ); + }, + }; + return ( + + {row.certDomains} + + ); + }, + }, + }, + certDomains: { + title: t("certd.monitor.certDomains"), + search: { + show: true, + }, + type: "text", + form: { + show: false, + }, + column: { + width: 200, + sorter: true, + show: false, + cellRender({ value }) { + return ( + + {value} + + ); + }, + }, + }, + certProvider: { + title: t("certd.monitor.certProvider"), + search: { + show: false, + }, + type: "text", + form: { + show: false, + }, + column: { + width: 200, + sorter: true, + show: false, + cellRender({ value }) { + return {value}; + }, + }, + }, + certStatus: { + title: t("certd.monitor.certStatus"), + search: { + show: true, + }, + type: "dict-select", + dict: dict({ + data: [ + { label: t("certd.monitor.status.ok"), value: "ok", color: "green" }, + { label: t("certd.monitor.status.expired"), value: "expired", color: "red" }, + ], + }), + form: { + show: false, + }, + column: { + width: 100, + sorter: true, + show: true, + align: "center", + }, + }, + certExpiresTime: { + title: t("certd.monitor.certExpiresTime"), + search: { + show: false, + }, + type: "date", + form: { + show: false, + }, + column: { + sorter: true, + cellRender({ value }) { + if (!value) { + return "-"; + } + const expireDate = dayjs(value).format("YYYY-MM-DD"); + const leftDays = dayjs(value).diff(dayjs(), "day"); + const color = leftDays < 20 ? "red" : "#389e0d"; + const percent = (leftDays / 90) * 100; + return ( + `${leftDays}${t("certd.monitor.days")}`} + /> + ); + }, + }, + }, + lastCheckTime: { + title: t("certd.monitor.lastCheckTime"), + search: { + show: false, + }, + type: "datetime", + form: { + show: false, + }, + column: { + sorter: true, + width: 155, + }, + }, + disabled: { + title: t("certd.monitor.disabled"), + search: { + show: false, + }, + type: "dict-switch", + dict: dict({ + data: [ + { label: t("common.enabled"), value: false, color: "green" }, + { label: t("common.disabled"), value: true, color: "red" }, + ], + }), + form: { + value: false, + }, + column: { + width: 100, + sorter: true, + align: "center", + component: { + name: "fs-dict-switch", + vModel: "checked", + on: { + async change({ row, $event }) { + await api.DisabledChange(row.id, $event); + await crudExpose.doRefresh(); + }, + }, + }, + }, + }, + ipCheck: { + title: t("certd.monitor.ipCheck"), + type: "dict-switch", + dict: dict({ + data: [ + { label: t("common.enabled"), value: true, color: "green" }, + { label: t("common.disabled"), value: false, color: "gray" }, + ], + }), + form: { + value: false, + rules: [{ required: true, message: t("certd.monitor.selectRequired") }], + }, + column: { + align: "center", + width: 100, + conditionalRender: false, + component: { + name: "fs-dict-switch", + vModel: "checked", + on: { + change({ row, $event }) { + Modal.confirm({ + title: t("common.confirm"), + content: t("certd.monitor.ipCheckConfirm", { status: $event ? t("common.enabled") : t("common.disabled") }), + onOk: async () => { + await api.IpCheckChange(row.id, $event); + await crudExpose.doRefresh(); + if ($event) { + openSiteIpMonitorDialog({ siteId: row.id }); + } + }, + onCancel: async () => { + await crudExpose.doRefresh(); + }, + }); + }, + }, + }, + } + } as ColumnCompositionProps, + ipCount: { + title: t("certd.monitor.ipCount"), + search: { + show: false, + }, + type: "text", + form: { + show: false, + }, + column: { + width: 100, + sorter: true, + align: "center", + }, + }, + checkStatus: { + title: t("certd.monitor.checkStatus"), + search: { + show: false, + }, + type: "dict-select", + dict: checkStatusDict, + form: { + show: false, + }, + column: { + width: 100, + align: "center", + sorter: true, + cellRender({ value, row }) { + return ( + + + + ); + }, + }, + }, + // error: { + // title: "错误信息", + // search: { + // show: false + // }, + // type: "text", + // form: { + // show: false + // }, + // column: { + // width: 200, + // sorter: true, + // cellRender({ value }) { + // return {value}; + // } + // } + // }, + pipelineId: { + title: t("certd.monitor.pipelineId"), + search: { + show: false, + }, + form: { show: false }, + type: "number", + column: { + width: 200, + sorter: true, + show: false, + }, + }, + certInfoId: { + title: t("certd.monitor.certInfoId"), + search: { + show: false, + }, + type: "number", + form: { show: false }, + column: { + width: 100, + sorter: true, + show: false, + }, + }, + }, + }, + }; } diff --git a/packages/ui/certd-client/src/views/certd/monitor/site/index.vue b/packages/ui/certd-client/src/views/certd/monitor/site/index.vue index b3521df3..1da7404d 100644 --- a/packages/ui/certd-client/src/views/certd/monitor/site/index.vue +++ b/packages/ui/certd-client/src/views/certd/monitor/site/index.vue @@ -1,53 +1,61 @@ - - - - 站点证书监控 - - - 每天0点,检查网站证书的过期时间,到期前10天时将发出提醒(使用默认通知渠道); - 站点监控设置 - - 基础版限制1条,专业版以上无限制,当前 - - - - 检查全部 - - - - + + + + {{ t("certd.monitor.title") }} + + + {{ t("certd.monitor.description") }} + {{ t("certd.monitor.settingLink") }} + + + {{ t("certd.monitor.limitInfo") }} + + + + + + {{ t("certd.monitor.checkAll") }} + + + + + diff --git a/packages/ui/certd-client/src/views/certd/monitor/site/ip/crud.tsx b/packages/ui/certd-client/src/views/certd/monitor/site/ip/crud.tsx index 1fb77a2a..f600adb4 100644 --- a/packages/ui/certd-client/src/views/certd/monitor/site/ip/crud.tsx +++ b/packages/ui/certd-client/src/views/certd/monitor/site/ip/crud.tsx @@ -7,353 +7,354 @@ import { Modal, notification } from "ant-design-vue"; import { useSiteIpMonitor } from "/@/views/certd/monitor/site/ip/use"; export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet { - const api = siteIpApi; + const { t } = useI18n(); + const api = siteIpApi; - const pageRequest = async (query: UserPageQuery): Promise => { - if (!query.query) { - query.query = {}; - } - query.query.siteId = context.props.siteId; - return await api.GetList(query); - }; - const editRequest = async (req: EditReq) => { - const { form, row } = req; - form.id = row.id; - const res = await api.UpdateObj(form); - return res; - }; - const delRequest = async (req: DelReq) => { - const { row } = req; - return await api.DelObj(row.id); - }; + const pageRequest = async (query: UserPageQuery): Promise => { + if (!query.query) { + query.query = {}; + } + query.query.siteId = context.props.siteId; + return await api.GetList(query); + }; + const editRequest = async (req: EditReq) => { + const { form, row } = req; + form.id = row.id; + const res = await api.UpdateObj(form); + return res; + }; + const delRequest = async (req: DelReq) => { + const { row } = req; + return await api.DelObj(row.id); + }; - const addRequest = async (req: AddReq) => { - const { form } = req; - form.siteId = context.props.siteId; - const res = await api.AddObj(form); - return res; - }; + const addRequest = async (req: AddReq) => { + const { form } = req; + form.siteId = context.props.siteId; + const res = await api.AddObj(form); + return res; + }; - const checkStatusDict = dict({ - data: [ - { label: "成功", value: "ok", color: "green" }, - { label: "检查中", value: "checking", color: "blue" }, - { label: "异常", value: "error", color: "red" }, - ], - }); - const { openSiteIpImportDialog } = useSiteIpMonitor(); - return { - crudOptions: { - request: { - pageRequest, - addRequest, - editRequest, - delRequest, - }, - form: { - labelCol: { - //固定label宽度 - span: null, - style: { - width: "100px", - }, - }, - col: { - span: 22, - }, - wrapper: { - width: 600, - }, - }, - actionbar: { - buttons: { - add: { - async click() { - await crudExpose.openAdd({}); - }, - }, - import: { - show: true, - text: "批量导入", - type: "primary", - async click() { - openSiteIpImportDialog({ - siteId: context.props.siteId, - afterSubmit() { - crudExpose.doRefresh(); - }, - }); - }, - }, - load: { - text: "同步IP", - type: "primary", - async click() { - Modal.confirm({ - title: "同步IP", - content: "确定要同步IP吗?", - onOk: async () => { - await api.DoSync(context.props.siteId); - await crudExpose.doRefresh(); - notification.success({ - message: "同步完成", - }); - }, - }); - }, - }, - checkAll: { - text: "检查全部", - type: "primary", - click: () => { - Modal.confirm({ - title: "确认", - content: "确认触发检查全部IP站点的证书吗?", - onOk: async () => { - await siteIpApi.CheckAll(context.props.siteId); - notification.success({ - message: "检查任务已提交", - description: "请稍后刷新页面查看结果", - }); - }, - }); - }, - }, - }, - }, - rowHandle: { - fixed: "right", - width: 240, - buttons: { - check: { - order: 0, - type: "link", - text: null, - tooltip: { - title: "立即检查", - }, - icon: "ion:play-sharp", - click: async ({ row }) => { - await api.DoCheck(row.id); - await crudExpose.doRefresh(); - notification.success({ - message: "检查任务已提交,请稍后刷新查看结果", - }); - }, - }, - }, - }, - columns: { - id: { - title: "ID", - key: "id", - type: "number", - search: { - show: false, - }, - column: { - width: 80, - align: "center", - }, - form: { - show: false, - }, - }, - ipAddress: { - title: "IP", - search: { - show: true, - }, - type: "text", - helper: "也支持填写CNAME域名", - form: { - rules: [{ required: true, message: "请输入IP" }], - }, - column: { - width: 160, - }, - }, - certDomains: { - title: "证书域名", - search: { - show: false, - }, - type: "text", - form: { - show: false, - }, - column: { - width: 200, - sorter: true, - show: false, - cellRender({ value }) { - return ( - - {value} - - ); - }, - }, - }, - certProvider: { - title: "颁发机构", - search: { - show: false, - }, - type: "text", - form: { - show: false, - }, - column: { - width: 200, - show: false, - sorter: true, - cellRender({ value }) { - return {value}; - }, - }, - }, - certStatus: { - title: "证书状态", - search: { - show: true, - }, - type: "dict-select", - dict: dict({ - data: [ - { label: "正常", value: "ok", color: "green" }, - { label: "过期", value: "expired", color: "red" }, - ], - }), - form: { - show: false, - }, - column: { - width: 100, - sorter: true, - show: true, - align: "center", - }, - }, - certExpiresTime: { - title: "证书到期时间", - search: { - show: false, - }, - type: "date", - form: { - show: false, - }, - column: { - sorter: true, - cellRender({ value }) { - if (!value) { - return "-"; - } - const expireDate = dayjs(value).format("YYYY-MM-DD"); - const leftDays = dayjs(value).diff(dayjs(), "day"); - const color = leftDays < 20 ? "red" : "#389e0d"; - const percent = (leftDays / 90) * 100; - return `${leftDays}天`} />; - }, - }, - }, - checkStatus: { - title: "检查状态", - search: { - show: false, - }, - type: "dict-select", - dict: checkStatusDict, - form: { - show: false, - }, - column: { - width: 100, - align: "center", - sorter: true, - cellRender({ value, row, key }) { - return ( - - - - ); - }, - }, - }, - lastCheckTime: { - title: "上次检查时间", - search: { - show: false, - }, - type: "datetime", - form: { - show: false, - }, - column: { - sorter: true, - width: 155, - }, - }, - from: { - title: "来源", - search: { - show: false, - }, - type: "dict-switch", - dict: dict({ - data: [ - { label: "同步", value: "sync", color: "green" }, - { label: "手动", value: "manual", color: "blue" }, - { label: "导入", value: "import", color: "blue" }, - ], - }), - form: { - value: false, - }, - column: { - width: 100, - sorter: true, - align: "center", - }, - }, - disabled: { - title: "禁用启用", - search: { - show: false, - }, - type: "dict-switch", - dict: dict({ - data: [ - { label: "启用", value: false, color: "green" }, - { label: "禁用", value: true, color: "red" }, - ], - }), - form: { - value: false, - }, - column: { - width: 100, - sorter: true, - align: "center", - }, - }, - remark: { - title: "备注", - search: { - show: false, - }, - type: "text", - form: { - show: false, - }, - column: { - width: 200, - sorter: true, - tooltip: true, - }, - }, - }, - }, - }; + const checkStatusDict = dict({ + data: [ + { label: t("certd.statusSuccess"), value: "ok", color: "green" }, + { label: t("certd.statusChecking"), value: "checking", color: "blue" }, + { label: t("certd.statusError"), value: "error", color: "red" }, + ], + }); + const { openSiteIpImportDialog } = useSiteIpMonitor(); + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest, + }, + form: { + labelCol: { + //固定label宽度 + span: null, + style: { + width: "100px", + }, + }, + col: { + span: 22, + }, + wrapper: { + width: 600, + }, + }, + actionbar: { + buttons: { + add: { + async click() { + await crudExpose.openAdd({}); + }, + }, + import: { + show: true, + text: t("certd.actionImportBatch"), + type: "primary", + async click() { + openSiteIpImportDialog({ + siteId: context.props.siteId, + afterSubmit() { + crudExpose.doRefresh(); + }, + }); + }, + }, + load: { + text: t("certd.actionSyncIp"), + type: "primary", + async click() { + Modal.confirm({ + title: t("certd.modalTitleSyncIp"), + content: t("certd.modalContentSyncIp"), + onOk: async () => { + await api.DoSync(context.props.siteId); + await crudExpose.doRefresh(); + notification.success({ + message: t("certd.notificationSyncComplete"), + }); + }, + }); + }, + }, + checkAll: { + text: t("certd.actionCheckAll"), + type: "primary", + click: () => { + Modal.confirm({ + title: t("certd.modalTitleConfirm"), + content: t("certd.modalContentCheckAll"), + onOk: async () => { + await siteIpApi.CheckAll(context.props.siteId); + notification.success({ + message: t("certd.notificationCheckSubmitted"), + description: t("certd.notificationCheckDescription"), + }); + }, + }); + }, + }, + }, + }, + rowHandle: { + fixed: "right", + width: 240, + buttons: { + check: { + order: 0, + type: "link", + text: null, + tooltip: { + title: t("certd.tooltipCheckNow"), + }, + icon: "ion:play-sharp", + click: async ({ row }) => { + await api.DoCheck(row.id); + await crudExpose.doRefresh(); + notification.success({ + message: t("certd.notificationCheckSubmittedPleaseRefresh"), + }); + }, + }, + }, + }, + columns: { + id: { + title: t("certd.columnId"), + key: "id", + type: "number", + search: { + show: false, + }, + column: { + width: 80, + align: "center", + }, + form: { + show: false, + }, + }, + ipAddress: { + title: t("certd.columnIp"), + search: { + show: true, + }, + type: "text", + helper: t("certd.helperIpCname"), + form: { + rules: [{ required: true, message: t("certd.ruleIpRequired") }], + }, + column: { + width: 160, + }, + }, + certDomains: { + title: t("certd.columnCertDomains"), + search: { + show: false, + }, + type: "text", + form: { + show: false, + }, + column: { + width: 200, + sorter: true, + show: false, + cellRender({ value }) { + return ( + + {value} + + ); + }, + }, + }, + certProvider: { + title: t("certd.columnCertProvider"), + search: { + show: false, + }, + type: "text", + form: { + show: false, + }, + column: { + width: 200, + show: false, + sorter: true, + cellRender({ value }) { + return {value}; + }, + }, + }, + certStatus: { + title: t("certd.columnCertStatus"), + search: { + show: true, + }, + type: "dict-select", + dict: dict({ + data: [ + { label: t("certd.statusNormal"), value: "ok", color: "green" }, + { label: t("certd.statusExpired"), value: "expired", color: "red" }, + ], + }), + form: { + show: false, + }, + column: { + width: 100, + sorter: true, + show: true, + align: "center", + }, + }, + certExpiresTime: { + title: t("certd.columnCertExpiresTime"), + search: { + show: false, + }, + type: "date", + form: { + show: false, + }, + column: { + sorter: true, + cellRender({ value }) { + if (!value) { + return "-"; + } + const expireDate = dayjs(value).format("YYYY-MM-DD"); + const leftDays = dayjs(value).diff(dayjs(), "day"); + const color = leftDays < 20 ? "red" : "#389e0d"; + const percent = (leftDays / 90) * 100; + return `${leftDays} ${t("certd.days")}`} />; + }, + }, + }, + checkStatus: { + title: t("certd.columnCheckStatus"), + search: { + show: false, + }, + type: "dict-select", + dict: checkStatusDict, + form: { + show: false, + }, + column: { + width: 100, + align: "center", + sorter: true, + cellRender({ value, row, key }) { + return ( + + + + ); + }, + }, + }, + lastCheckTime: { + title: t("certd.columnLastCheckTime"), + search: { + show: false, + }, + type: "datetime", + form: { + show: false, + }, + column: { + sorter: true, + width: 155, + }, + }, + from: { + title: t("certd.columnSource"), + search: { + show: false, + }, + type: "dict-switch", + dict: dict({ + data: [ + { label: t("certd.sourceSync"), value: "sync", color: "green" }, + { label: t("certd.sourceManual"), value: "manual", color: "blue" }, + { label: t("certd.sourceImport"), value: "import", color: "blue" }, + ], + }), + form: { + value: false, + }, + column: { + width: 100, + sorter: true, + align: "center", + }, + }, + disabled: { + title: t("certd.columnDisabled"), + search: { + show: false, + }, + type: "dict-switch", + dict: dict({ + data: [ + { label: t("certd.enabled"), value: false, color: "green" }, + { label: t("certd.disabled"), value: true, color: "red" }, + ], + }), + form: { + value: false, + }, + column: { + width: 100, + sorter: true, + align: "center", + }, + }, + remark: { + title: t("certd.columnRemark"), + search: { + show: false, + }, + type: "text", + form: { + show: false, + }, + column: { + width: 200, + sorter: true, + tooltip: true, + }, + }, + }, + }, + }; } diff --git a/packages/ui/certd-client/src/views/certd/monitor/site/setting/index.vue b/packages/ui/certd-client/src/views/certd/monitor/site/setting/index.vue index 57a6bc2d..0c5c3934 100644 --- a/packages/ui/certd-client/src/views/certd/monitor/site/setting/index.vue +++ b/packages/ui/certd-client/src/views/certd/monitor/site/setting/index.vue @@ -1,37 +1,41 @@ - - - 站点监控设置 - - - - - - - - 设置通知渠道 - - - - - - 监控请求重试次数 - - - - - - - 定时触发监控 - - - 保存 - - - - + + + {{ t("certd.siteMonitorSettings") }} + + + + + + + + {{ t('certd.setNotificationChannel') }} + + + + + + {{ t('certd.monitorRetryTimes') }} + + + + + + + {{ t('certd.cronTrigger') }} + + + {{ t('certd.save') + }} + + + + + diff --git a/packages/ui/certd-client/src/views/certd/monitor/site/use.tsx b/packages/ui/certd-client/src/views/certd/monitor/site/use.tsx index a4b913b7..32b0efc3 100644 --- a/packages/ui/certd-client/src/views/certd/monitor/site/use.tsx +++ b/packages/ui/certd-client/src/views/certd/monitor/site/use.tsx @@ -1,41 +1,44 @@ import { useFormWrapper } from "@fast-crud/fast-crud"; import { siteInfoApi } from "./api"; +import { useI18n } from "vue-i18n"; export function useSiteImport() { - const { openCrudFormDialog } = useFormWrapper(); + const { t } = useI18n(); + const { openCrudFormDialog } = useFormWrapper(); - async function openSiteImportDialog(opts: { afterSubmit: any }) { - const { afterSubmit } = opts; - await openCrudFormDialog({ - crudOptions: { - columns: { - text: { - type: "textarea", - title: "域名列表", - form: { - helper: "格式【域名:端口:名称】,一行一个,其中端口、名称可以省略\n比如:\nwww.baidu.com:443:百度\nwww.taobao.com::淘宝\nwww.google.com", - rules: [{ required: true, message: "请输入要导入的域名" }], - component: { - placeholder: "www.baidu.com:443:百度\nwww.taobao.com::淘宝\nwww.google.com\n", - rows: 8, - }, - col: { - span: 24, - }, - }, - }, - }, - form: { - async doSubmit({ form }) { - return siteInfoApi.Import(form); - }, - afterSubmit, - }, - }, - }); - } + async function openSiteImportDialog(opts: { afterSubmit: any }) { + const { afterSubmit } = opts; + await openCrudFormDialog({ + crudOptions: { + columns: { + text: { + type: "textarea", + title: t("certd.domainList.title"), // 域名列表 + form: { + helper: t("certd.domainList.helper"), + rules: [{ required: true, message: t("certd.domainList.required") }], + component: { + placeholder: t("certd.domainList.placeholder"), + rows: 8, + }, + col: { + span: 24, + }, + }, + }, + }, - return { - openSiteImportDialog, - }; + form: { + async doSubmit({ form }) { + return siteInfoApi.Import(form); + }, + afterSubmit, + }, + }, + }); + } + + return { + openSiteImportDialog, + }; } diff --git a/packages/ui/certd-client/src/views/certd/notification/common.tsx b/packages/ui/certd-client/src/views/certd/notification/common.tsx index 18e061cd..196adcdd 100644 --- a/packages/ui/certd-client/src/views/certd/notification/common.tsx +++ b/packages/ui/certd-client/src/views/certd/notification/common.tsx @@ -5,238 +5,241 @@ import { forEach, get, merge, set } from "lodash-es"; import { Modal } from "ant-design-vue"; import * as api from "/@/views/sys/cname/provider/api"; import { mitter } from "/@/utils/util.mitt"; +import { useI18n } from "vue-i18n"; export function notificationProvide(api: any) { - provide("notificationApi", api); - provide("get:plugin:type", () => { - return "notification"; - }); + provide("notificationApi", api); + provide("get:plugin:type", () => { + return "notification"; + }); } export function getCommonColumnDefine(crudExpose: any, typeRef: any, api: any) { - const notificationTypeDictRef = dict({ - url: "/pi/notification/getTypeDict", - }); - const defaultPluginConfig = { - component: { - name: "a-input", - vModel: "value", - }, - }; + const { t } = useI18n(); - function buildDefineFields(define: any, form: any, mode: string) { - const formWrapperRef = crudExpose.getFormWrapperRef(); - const columnsRef = toRef(formWrapperRef.formOptions, "columns"); + const notificationTypeDictRef = dict({ + url: "/pi/notification/getTypeDict", + }); + const defaultPluginConfig = { + component: { + name: "a-input", + vModel: "value", + }, + }; - for (const key in columnsRef.value) { - if (key.indexOf(".") >= 0) { - delete columnsRef.value[key]; - } - } - console.log('crudBinding.value[mode + "Form"].columns', columnsRef.value); - forEach(define.input, (value: any, mapKey: any) => { - const key = "body." + mapKey; - const field = { - ...value, - key, - }; - const column = merge({ title: key }, defaultPluginConfig, field); - //eval - useReference(column); + function buildDefineFields(define: any, form: any, mode: string) { + const formWrapperRef = crudExpose.getFormWrapperRef(); + const columnsRef = toRef(formWrapperRef.formOptions, "columns"); - if (column.required) { - if (!column.rules) { - column.rules = []; - } - column.rules.push({ required: true, message: "此项必填" }); - } + for (const key in columnsRef.value) { + if (key.indexOf(".") >= 0) { + delete columnsRef.value[key]; + } + } + console.log('crudBinding.value[mode + "Form"].columns', columnsRef.value); + forEach(define.input, (value: any, mapKey: any) => { + const key = "body." + mapKey; + const field = { + ...value, + key, + }; + const column = merge({ title: key }, defaultPluginConfig, field); + //eval + useReference(column); - //设置默认值 - if (column.value != null && get(form, key) == null) { - set(form, key, column.value); - } - //字段配置赋值 - columnsRef.value[key] = column; - console.log("form", columnsRef.value, form); - }); - } + if (column.required) { + if (!column.rules) { + column.rules = []; + } + column.rules.push({ required: true, message: t("certd.requiredField") }); + } - const currentDefine = ref(); + //设置默认值 + if (column.value != null && get(form, key) == null) { + set(form, key, column.value); + } + //字段配置赋值 + columnsRef.value[key] = column; + console.log("form", columnsRef.value, form); + }); + } - return { - id: { - title: "ID", - key: "id", - type: "number", - column: { - width: 100, - }, - form: { - show: false, - }, - }, - type: { - title: "通知类型", - type: "dict-select", - dict: notificationTypeDictRef, - search: { - show: false, - }, - column: { - width: 200, - component: { - color: "auto", - }, - }, - editForm: { - component: { - disabled: false, - }, - }, - form: { - component: { - disabled: false, - showSearch: true, - filterOption: (input: string, option: any) => { - input = input?.toLowerCase(); - return option.value.toLowerCase().indexOf(input) >= 0 || option.label.toLowerCase().indexOf(input) >= 0; - }, - renderLabel(item: any) { - return ( - - {item.label} - {item.needPlus && } - - ); - }, - }, - rules: [{ required: true, message: "请选择通知类型" }], - valueChange: { - immediate: true, - async handle({ value, mode, form, immediate }) { - if (value == null) { - return; - } - const lastTitle = currentDefine.value?.title; - const define = await api.GetProviderDefine(value); - currentDefine.value = define; - console.log("define", define); + const currentDefine = ref(); - if (!immediate) { - form.body = {}; - if (define.needPlus) { - mitter.emit("openVipModal"); - } - } + return { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 100, + }, + form: { + show: false, + }, + }, + type: { + title: t("certd.notificationType"), + type: "dict-select", + dict: notificationTypeDictRef, + search: { + show: false, + }, + column: { + width: 200, + component: { + color: "auto", + }, + }, + editForm: { + component: { + disabled: false, + }, + }, + form: { + component: { + disabled: false, + showSearch: true, + filterOption: (input: string, option: any) => { + input = input?.toLowerCase(); + return option.value.toLowerCase().indexOf(input) >= 0 || option.label.toLowerCase().indexOf(input) >= 0; + }, + renderLabel(item: any) { + return ( + + {item.label} + {item.needPlus && } + + ); + }, + }, + rules: [{ required: true, message: t("certd.selectNotificationType") }], + valueChange: { + immediate: true, + async handle({ value, mode, form, immediate }) { + if (value == null) { + return; + } + const lastTitle = currentDefine.value?.title; + const define = await api.GetProviderDefine(value); + currentDefine.value = define; + console.log("define", define); - if (!form.name || form.name === lastTitle) { - form.name = define.title; - } - buildDefineFields(define, form, mode); - }, - }, - helper: computed(() => { - const define = currentDefine.value; - if (define == null) { - return ""; - } - return define.desc; - }), - }, - } as ColumnCompositionProps, - name: { - title: "通知名称", - search: { - show: true, - }, - type: ["text"], - form: { - rules: [{ required: true, message: "请填写名称" }], - helper: "随便填,当多个相同类型的通知时,便于区分", - }, - column: { - width: 200, - }, - }, - isDefault: { - title: "是否默认", - type: "dict-switch", - dict: dict({ - data: [ - { label: "是", value: true, color: "success" }, - { label: "否", value: false, color: "default" }, - ], - }), - form: { - value: false, - rules: [{ required: true, message: "请选择是否默认" }], - order: 999, - }, - column: { - align: "center", - width: 100, - component: { - name: "a-switch", - vModel: "checked", - disabled: compute(({ value }) => { - return value === true; - }), - on: { - change({ row }) { - Modal.confirm({ - title: "提示", - content: "确定设置为默认通知?", - onOk: async () => { - await api.SetDefault(row.id); - await crudExpose.doRefresh(); - }, - onCancel: async () => { - await crudExpose.doRefresh(); - }, - }); - }, - }, - }, - }, - } as ColumnCompositionProps, - test: { - title: "测试", - form: { - show: compute(({ form }) => { - return !!form.type; - }), - component: { - name: "api-test", - action: "TestRequest", - }, - order: 990, - col: { - span: 24, - }, - }, - column: { - show: false, - }, - }, - setting: { - column: { show: false }, - form: { - show: false, - valueBuilder({ value, form }) { - form.body = {}; - if (!value) { - return; - } - const setting = JSON.parse(value); - for (const key in setting) { - form.body[key] = setting[key]; - } - }, - valueResolve({ form }) { - const setting = form.body; - form.setting = JSON.stringify(setting); - }, - }, - } as ColumnCompositionProps, - }; + if (!immediate) { + form.body = {}; + if (define.needPlus) { + mitter.emit("openVipModal"); + } + } + + if (!form.name || form.name === lastTitle) { + form.name = define.title; + } + buildDefineFields(define, form, mode); + }, + }, + helper: computed(() => { + const define = currentDefine.value; + if (define == null) { + return ""; + } + return define.desc; + }), + }, + } as ColumnCompositionProps, + name: { + title: t("certd.notificationName"), + search: { + show: true, + }, + type: ["text"], + form: { + rules: [{ required: true, message: t("certd.enterName") }], + helper: t("certd.helperNotificationName"), + }, + column: { + width: 200, + }, + }, + isDefault: { + title: t("certd.isDefault"), + type: "dict-switch", + dict: dict({ + data: [ + { label: t("certd.yes"), value: true, color: "success" }, + { label: t("certd.no"), value: false, color: "default" }, + ], + }), + form: { + value: false, + rules: [{ required: true, message: t("certd.selectIsDefault") }], + order: 999, + }, + column: { + align: "center", + width: 100, + component: { + name: "a-switch", + vModel: "checked", + disabled: compute(({ value }) => { + return value === true; + }), + on: { + change({ row }) { + Modal.confirm({ + title: t("certd.prompt"), + content: t("certd.confirmSetDefaultNotification"), + onOk: async () => { + await api.SetDefault(row.id); + await crudExpose.doRefresh(); + }, + onCancel: async () => { + await crudExpose.doRefresh(); + }, + }); + }, + }, + }, + }, + } as ColumnCompositionProps, + test: { + title: t("certd.test"), + form: { + show: compute(({ form }) => { + return !!form.type; + }), + component: { + name: "api-test", + action: "TestRequest", + }, + order: 990, + col: { + span: 24, + }, + }, + column: { + show: false, + }, + }, + setting: { + column: { show: false }, + form: { + show: false, + valueBuilder({ value, form }) { + form.body = {}; + if (!value) { + return; + } + const setting = JSON.parse(value); + for (const key in setting) { + form.body[key] = setting[key]; + } + }, + valueResolve({ form }) { + const setting = form.body; + form.setting = JSON.stringify(setting); + }, + }, + } as ColumnCompositionProps, + }; } diff --git a/packages/ui/certd-client/src/views/certd/notification/notification-selector/index.vue b/packages/ui/certd-client/src/views/certd/notification/notification-selector/index.vue index 1ac58c86..cc10c45b 100644 --- a/packages/ui/certd-client/src/views/certd/notification/notification-selector/index.vue +++ b/packages/ui/certd-client/src/views/certd/notification/notification-selector/index.vue @@ -1,36 +1,26 @@ - - - - - - - - - - + + + + + + + + + + diff --git a/packages/ui/certd-client/src/views/certd/open/openkey/crud.tsx b/packages/ui/certd-client/src/views/certd/open/openkey/crud.tsx index 040624e0..a98133ed 100644 --- a/packages/ui/certd-client/src/views/certd/open/openkey/crud.tsx +++ b/packages/ui/certd-client/src/views/certd/open/openkey/crud.tsx @@ -5,181 +5,182 @@ import { OPEN_API_DOC, openkeyApi } from "./api"; import { useModal } from "/@/use/use-modal"; export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet { - const { t } = useI18n(); - const api = openkeyApi; - const pageRequest = async (query: UserPageQuery): Promise => { - return await api.GetList(query); - }; - const editRequest = async (req: EditReq) => { - const { form, row } = req; - form.id = row.id; - const res = await api.UpdateObj(form); - return res; - }; - const delRequest = async (req: DelReq) => { - const { row } = req; - return await api.DelObj(row.id); - }; + const { t } = useI18n(); + const api = openkeyApi; + const pageRequest = async (query: UserPageQuery): Promise => { + return await api.GetList(query); + }; + const editRequest = async (req: EditReq) => { + const { form, row } = req; + form.id = row.id; + const res = await api.UpdateObj(form); + return res; + }; + const delRequest = async (req: DelReq) => { + const { row } = req; + return await api.DelObj(row.id); + }; - const addRequest = async (req: AddReq) => { - const { form } = req; - const res = await api.AddObj(form); - return res; - }; - const model = useModal(); - return { - crudOptions: { - request: { - pageRequest, - addRequest, - editRequest, - delRequest, - }, - search: { - show: false, - }, - form: { - labelCol: { - //固定label宽度 - span: null, - style: { - width: "100px", - }, - }, - col: { - span: 22, - }, - wrapper: { - width: 600, - }, - }, - actionbar: { - buttons: { - add: { - text: "生成新的Key", - }, - }, - }, - rowHandle: { - width: 300, - fixed: "right", - buttons: { - view: { show: true }, - copy: { show: false }, - edit: { show: false }, - remove: { show: true }, - gen: { - text: "接口测试", - size: "mini", - icon: "devicon-plain:vitest", - type: "primary", - async click({ row }) { - const apiToken = await api.GetApiToken(row.id); + const addRequest = async (req: AddReq) => { + const { form } = req; + const res = await api.AddObj(form); + return res; + }; + const model = useModal(); + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest, + }, + search: { + show: false, + }, + form: { + labelCol: { + //固定label宽度 + span: null, + style: { + width: "100px", + }, + }, + col: { + span: 22, + }, + wrapper: { + width: 600, + }, + }, + actionbar: { + buttons: { + add: { + text: t("certd.actionbar.add"), + }, + }, + }, + rowHandle: { + width: 300, + fixed: "right", + buttons: { + view: { show: true }, + copy: { show: false }, + edit: { show: false }, + remove: { show: true }, + gen: { + text: t("certd.gen.text"), + size: "mini", + icon: "devicon-plain:vitest", + type: "primary", + async click({ row }) { + const apiToken = await api.GetApiToken(row.id); - model.success({ - title: "x-certd-token", - maskClosable: true, - okText: "确定", - width: 600, - content: () => { - return ( - - - 测试x-certd-token如下,您可以在3分钟内使用它进行 - - 开放接口 - - 请求测试 - - - - - - ); - }, - }); - }, - }, - }, - }, - columns: { - id: { - title: "ID", - key: "id", - type: "number", - search: { - show: false, - }, - column: { - width: 100, - editable: { - disabled: true, - }, - }, - form: { - show: false, - }, - }, - keyId: { - title: "KeyId", - type: ["text", "copyable"], - search: { - show: true, - }, - form: { - show: false, - }, - column: { - width: 250, - sorter: true, - }, - }, - keySecret: { - title: "KeySecret", - type: ["text", "copyable"], - form: { - show: false, - }, - column: { - width: 580, - sorter: true, - }, - }, - scope: { - title: "权限范围", - type: "dict-radio", - dict: dict({ - data: [ - { label: "仅开放接口", value: "open", color: "blue" }, - { label: "账户所有权限", value: "user", color: "red" }, - ], - }), - form: { - value: "open", - show: true, - rules: [{ required: true, message: "此项必填" }], - helper: "仅开放接口只可以访问开放接口,账户所有权限可以访问所有接口", - component: { - vModel: "value", - }, - }, - column: { - width: 120, - align: "center", - sorter: true, - }, - }, - createTime: { - title: "创建时间", - type: "datetime", - search: { - show: false, - }, - form: { - show: false, - }, - }, - }, - }, - }; + model.success({ + title: t("certd.gen.title"), + maskClosable: true, + okText: t("certd.gen.okText"), + width: 600, + content: () => { + return ( + + + {t("certd.gen.contentPart1")} + + {t("certd.gen.openApi")} + + {t("certd.gen.contentPart2")} + + + + + + ); + }, + }); + }, + }, + }, + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + search: { + show: false, + }, + column: { + width: 100, + editable: { + disabled: true, + }, + }, + form: { + show: false, + }, + }, + keyId: { + title: "KeyId", + type: ["text", "copyable"], + search: { + show: true, + }, + form: { + show: false, + }, + column: { + width: 250, + sorter: true, + }, + }, + keySecret: { + title: "KeySecret", + type: ["text", "copyable"], + form: { + show: false, + }, + column: { + width: 580, + sorter: true, + }, + }, + scope: { + title: t("certd.scope"), + type: "dict-radio", + dict: dict({ + data: [ + { label: t("certd.scopeOpenApiOnly"), value: "open", color: "blue" }, + { label: t("certd.scopeFullAccount"), value: "user", color: "red" }, + ], + }), + form: { + value: "open", + show: true, + rules: [{ required: true, message: t("certd.required") }], + helper: t("certd.scopeHelper"), + component: { + vModel: "value", + }, + }, + column: { + width: 120, + align: "center", + sorter: true, + }, + }, + createTime: { + title: t("certd.fields.createTime"), + type: "datetime", + search: { + show: false, + }, + form: { + show: false, + }, + }, + + }, + }, + }; } diff --git a/packages/ui/certd-client/src/views/certd/pipeline/certd-form/use.tsx b/packages/ui/certd-client/src/views/certd/pipeline/certd-form/use.tsx index 9238e2fd..a02dbfc8 100644 --- a/packages/ui/certd-client/src/views/certd/pipeline/certd-form/use.tsx +++ b/packages/ui/certd-client/src/views/certd/pipeline/certd-form/use.tsx @@ -11,6 +11,8 @@ import * as api from "../api"; import { PluginGroup, usePluginStore } from "/@/store/plugin"; import { createNotificationApi } from "/@/views/certd/notification/api"; import GroupSelector from "../group/group-selector.vue"; +import { useI18n } from "vue-i18n"; + export function fillPipelineByDefaultForm(pipeline: any, form: any) { const triggers = []; @@ -32,238 +34,241 @@ export function fillPipelineByDefaultForm(pipeline: any, form: any) { } export function setRunnableIds(pipeline: any) { - const idMap: any = {}; - function createId(oldId: any) { - if (oldId == null) { - return nanoid(); - } - const newId = nanoid(); - idMap[oldId] = newId; - return newId; - } - if (pipeline.stages) { - for (const stage of pipeline.stages) { - stage.id = createId(stage.id); - if (stage.tasks) { - for (const task of stage.tasks) { - task.id = createId(task.id); - if (task.steps) { - for (const step of task.steps) { - step.id = createId(step.id); - } - } - } - } - } - } + const { t } = useI18n(); + const idMap: any = {}; + function createId(oldId: any) { + if (oldId == null) { + return nanoid(); + } + const newId = nanoid(); + idMap[oldId] = newId; + return newId; + } + if (pipeline.stages) { + for (const stage of pipeline.stages) { + stage.id = createId(stage.id); + if (stage.tasks) { + for (const task of stage.tasks) { + task.id = createId(task.id); + if (task.steps) { + for (const step of task.steps) { + step.id = createId(step.id); + } + } + } + } + } + } - for (const trigger of pipeline.triggers) { - trigger.id = nanoid(); - } - for (const notification of pipeline.notifications) { - notification.id = nanoid(); - } + for (const trigger of pipeline.triggers) { + trigger.id = nanoid(); + } + for (const notification of pipeline.notifications) { + notification.id = nanoid(); + } - let content = JSON.stringify(pipeline); - for (const key in idMap) { - content = content.replaceAll(key, idMap[key]); - } - return JSON.parse(content); + let content = JSON.stringify(pipeline); + for (const key in idMap) { + content = content.replaceAll(key, idMap[key]); + } + return JSON.parse(content); } export function useCertPipelineCreator() { - const { openCrudFormDialog } = useFormWrapper(); + const { t } = useI18n(); + const { openCrudFormDialog } = useFormWrapper(); - const pluginStore = usePluginStore(); - const router = useRouter(); + const pluginStore = usePluginStore(); + const router = useRouter(); - function createCrudOptions(certPlugins: any[], getFormData: any, doSubmit: any): CreateCrudOptionsRet { - const inputs: any = {}; - const moreParams = []; - for (const plugin of certPlugins) { - for (const inputKey in plugin.input) { - if (inputs[inputKey]) { - //如果两个插件有的字段,直接显示 - inputs[inputKey].form.show = true; - continue; - } - const inputDefine = cloneDeep(plugin.input[inputKey]); - if (!inputDefine.required && !inputDefine.maybeNeed) { - moreParams.push(inputKey); - // continue; - } - useReference(inputDefine); - inputs[inputKey] = { - title: inputDefine.title, - form: { - ...inputDefine, - show: compute(ctx => { - const form = getFormData(); - if (!form) { - return false; - } + function createCrudOptions(certPlugins: any[], getFormData: any, doSubmit: any): CreateCrudOptionsRet { + const inputs: any = {}; + const moreParams = []; + for (const plugin of certPlugins) { + for (const inputKey in plugin.input) { + if (inputs[inputKey]) { + //如果两个插件有的字段,直接显示 + inputs[inputKey].form.show = true; + continue; + } + const inputDefine = cloneDeep(plugin.input[inputKey]); + if (!inputDefine.required && !inputDefine.maybeNeed) { + moreParams.push(inputKey); + // continue; + } + useReference(inputDefine); + inputs[inputKey] = { + title: inputDefine.title, + form: { + ...inputDefine, + show: compute(ctx => { + const form = getFormData(); + if (!form) { + return false; + } - let inputDefineShow = true; - if (inputDefine.show != null) { - const computeShow = inputDefine.show as any; - if (computeShow === false) { - inputDefineShow = false; - } else if (computeShow && computeShow.computeFn) { - inputDefineShow = computeShow.computeFn({ form }); - } - } - return form?.certApplyPlugin === plugin.name && inputDefineShow; - }), - }, - }; - } - } + let inputDefineShow = true; + if (inputDefine.show != null) { + const computeShow = inputDefine.show as any; + if (computeShow === false) { + inputDefineShow = false; + } else if (computeShow && computeShow.computeFn) { + inputDefineShow = computeShow.computeFn({ form }); + } + } + return form?.certApplyPlugin === plugin.name && inputDefineShow; + }), + }, + }; + } + } - const pluginStore = usePluginStore(); - const randomHour = Math.floor(Math.random() * 6); - const randomMin = Math.floor(Math.random() * 60); + const pluginStore = usePluginStore(); + const randomHour = Math.floor(Math.random() * 6); + const randomMin = Math.floor(Math.random() * 60); - const groupDictRef = dict({ - url: "/pi/pipeline/group/all", - value: "id", - label: "name", - }); + const groupDictRef = dict({ + url: "/pi/pipeline/group/all", + value: "id", + label: "name", + }); - return { - crudOptions: { - form: { - doSubmit, - wrapper: { - width: 1350, - saveRemind: false, - title: "创建证书流水线", - }, - group: { - groups: { - more: { - header: "更多参数", - columns: moreParams, - collapsed: true, - }, - }, - }, - }, - columns: { - certApplyPlugin: { - title: "证书申请插件", - type: "dict-select", - dict: dict({ - data: [ - { value: "CertApply", label: "JS-ACME" }, - { value: "CertApplyLego", label: "Lego-ACME" }, - ], - }), - form: { - order: 0, - value: "CertApply", - helper: { - render: () => { - return ( - - JS-ACME:使用简单方便,功能强大【推荐】 - Lego-ACME:基于Lego实现,支持海量DNS提供商,熟悉LEGO的用户可以使用 - - ); - }, - }, - valueChange: { - handle: async ({ form, value }) => { - const config = await pluginStore.getPluginConfig({ - name: value, - type: "builtIn", - }); - if (config.sysSetting?.input) { - merge(form, config.sysSetting.input); - } - }, - immediate: true, - }, - }, - }, - ...inputs, - triggerCron: { - title: "定时触发", - type: "text", - form: { - value: `0 ${randomMin} ${randomHour} * * *`, - component: { - name: "cron-editor", - vModel: "modelValue", - placeholder: "0 0 4 * * *", - }, - helper: "点击上面的按钮,选择每天几点定时执行。\n建议设置为每天触发一次,证书未到期之前任务会跳过,不会重复执行", - order: 100, - }, - }, - notification: { - title: "失败通知", - type: "text", - form: { - value: 0, - component: { - name: NotificationSelector, - vModel: "modelValue", - on: { - selectedChange({ $event, form }) { - form.notificationTarget = $event; - }, - }, - }, - order: 101, - helper: "任务执行失败实时提醒", - }, - }, - groupId: { - title: "流水线分组", - type: "dict-select", - dict: groupDictRef, - form: { - component: { - name: GroupSelector, - vModel: "modelValue", - }, - order: 9999, - }, - }, - }, - }, - }; - } + return { + crudOptions: { + form: { + doSubmit, + wrapper: { + width: 1350, + saveRemind: false, + title: t("certd.pipelineForm.createTitle"), + }, + group: { + groups: { + more: { + header: t("certd.pipelineForm.moreParams"), + columns: moreParams, + collapsed: true, + }, + }, + }, + }, + columns: { + certApplyPlugin: { + title: t("certd.plugin.selectTitle"), + type: "dict-select", + dict: dict({ + data: [ + { value: "CertApply", label: "JS-ACME" }, + { value: "CertApplyLego", label: "Lego-ACME" }, + ], + }), + form: { + order: 0, + value: "CertApply", + helper: { + render: () => { + return ( + + {t("certd.plugin.jsAcme")} + {t("certd.plugin.legoAcme")} + + ); + }, + }, + valueChange: { + handle: async ({ form, value }) => { + const config = await pluginStore.getPluginConfig({ + name: value, + type: "builtIn", + }); + if (config.sysSetting?.input) { + merge(form, config.sysSetting.input); + } + }, + immediate: true, + }, + }, + }, + ...inputs, + triggerCron: { + title: t("certd.pipelineForm.triggerCronTitle"), + type: "text", + form: { + value: `0 ${randomMin} ${randomHour} * * *`, + component: { + name: "cron-editor", + vModel: "modelValue", + placeholder: "0 0 4 * * *", + }, + helper: t("certd.pipelineForm.triggerCronHelper"), + order: 100, + }, + }, + notification: { + title: t("certd.pipelineForm.notificationTitle"), + type: "text", + form: { + value: 0, + component: { + name: NotificationSelector, + vModel: "modelValue", + on: { + selectedChange({ $event, form }) { + form.notificationTarget = $event; + }, + }, + }, + order: 101, + helper: t("certd.pipelineForm.notificationHelper"), + }, + }, + groupId: { + title: t("certd.pipelineForm.groupIdTitle"), + type: "dict-select", + dict: groupDictRef, + form: { + component: { + name: GroupSelector, + vModel: "modelValue", + }, + order: 9999, + }, + } - async function getCertPlugins() { - const pluginGroup = await pluginStore.getGroups(); - const pluginGroups: { [key: string]: PluginGroup } = pluginGroup.groups; - const certPluginGroup = pluginGroups.cert; + }, + }, + }; + } - const certPlugins = []; - for (const plugin of certPluginGroup.plugins) { - const detail: any = await pluginStore.getPluginDefine(plugin.name); - certPlugins.push(detail); - } - return certPlugins; - } + async function getCertPlugins() { + const pluginGroup = await pluginStore.getGroups(); + const pluginGroups: { [key: string]: PluginGroup } = pluginGroup.groups; + const certPluginGroup = pluginGroups.cert; - async function openAddCertdPipelineDialog(req: { defaultGroupId?: number }) { - //检查是否流水线数量超出限制 - await checkPipelineLimit(); + const certPlugins = []; + for (const plugin of certPluginGroup.plugins) { + const detail: any = await pluginStore.getPluginDefine(plugin.name); + certPlugins.push(detail); + } + return certPlugins; + } - const wrapperRef = ref(); - function getFormData() { - if (!wrapperRef.value) { - return null; - } - return wrapperRef.value.getFormData(); - } + async function openAddCertdPipelineDialog(req: { defaultGroupId?: number }) { + //检查是否流水线数量超出限制 + await checkPipelineLimit(); - async function doSubmit({ form }: any) { - // const certDetail = readCertDetail(form.cert.crt); - // 添加certd pipeline - const pluginInput = omit(form, ["triggerCron", "notification", "notificationTarget", "certApplyPlugin", "groupId"]); + const wrapperRef = ref(); + function getFormData() { + if (!wrapperRef.value) { + return null; + } + return wrapperRef.value.getFormData(); + } + + async function doSubmit({ form }: any) { + // const certDetail = readCertDetail(form.cert.crt); + // 添加certd pipeline + const pluginInput = omit(form, ["triggerCron", "notification", "notificationTarget", "certApplyPlugin", "groupId"]); let pipeline: any = { title: form.domains[0] + "证书自动化", runnableType: "pipeline", @@ -296,38 +301,38 @@ export function useCertPipelineCreator() { ], }; - pipeline = fillPipelineByDefaultForm(pipeline, form); + pipeline = fillPipelineByDefaultForm(pipeline, form); pipeline = setRunnableIds(pipeline); - const groupId = form.groupId; - const id = await api.Save({ - title: pipeline.title, - content: JSON.stringify(pipeline), - keepHistoryCount: 30, - type: "cert", - groupId, - }); - if (form.email) { - try { - //创建一个默认的邮件通知 - const notificationApi = createNotificationApi(); - await notificationApi.GetOrCreateDefault({ email: form.email }); - } catch (e) { - console.error(e); - } - } - message.success("创建成功,请添加证书部署任务"); - router.push({ path: "/certd/pipeline/detail", query: { id, editMode: "true" } }); - } - const certPlugins = await getCertPlugins(); - const { crudOptions } = createCrudOptions(certPlugins, getFormData, doSubmit); - //@ts-ignore - crudOptions.columns.groupId.form.value = req.defaultGroupId || undefined; - const wrapper = await openCrudFormDialog({ crudOptions }); - wrapperRef.value = wrapper; - } + const groupId = form.groupId; + const id = await api.Save({ + title: pipeline.title, + content: JSON.stringify(pipeline), + keepHistoryCount: 30, + type: "cert", + groupId, + }); + if (form.email) { + try { + //创建一个默认的邮件通知 + const notificationApi = createNotificationApi(); + await notificationApi.GetOrCreateDefault({ email: form.email }); + } catch (e) { + console.error(e); + } + } + message.success("创建成功,请添加证书部署任务"); + router.push({ path: "/certd/pipeline/detail", query: { id, editMode: "true" } }); + } + const certPlugins = await getCertPlugins(); + const { crudOptions } = createCrudOptions(certPlugins, getFormData, doSubmit); + //@ts-ignore + crudOptions.columns.groupId.form.value = req.defaultGroupId || undefined; + const wrapper = await openCrudFormDialog({ crudOptions }); + wrapperRef.value = wrapper; + } - return { - openAddCertdPipelineDialog, - }; + return { + openAddCertdPipelineDialog, + }; } diff --git a/packages/ui/certd-client/src/views/certd/pipeline/components/change-trigger.vue b/packages/ui/certd-client/src/views/certd/pipeline/components/change-trigger.vue index 8f754712..4e4fb599 100644 --- a/packages/ui/certd-client/src/views/certd/pipeline/components/change-trigger.vue +++ b/packages/ui/certd-client/src/views/certd/pipeline/components/change-trigger.vue @@ -1,62 +1,70 @@ - + + + diff --git a/packages/ui/certd-client/src/views/certd/pipeline/crud.tsx b/packages/ui/certd-client/src/views/certd/pipeline/crud.tsx index 1ee1864a..3a82cd4d 100644 --- a/packages/ui/certd-client/src/views/certd/pipeline/crud.tsx +++ b/packages/ui/certd-client/src/views/certd/pipeline/crud.tsx @@ -16,470 +16,469 @@ import GroupSelector from "/@/views/certd/pipeline/group/group-selector.vue"; import { useCertViewer } from "/@/views/certd/pipeline/use"; export default function ({ crudExpose, context: { groupDictRef, selectedRowKeys } }: CreateCrudOptionsProps): CreateCrudOptionsRet { - const router = useRouter(); - const lastResRef = ref(); + const router = useRouter(); + const lastResRef = ref(); - const { openAddCertdPipelineDialog } = useCertPipelineCreator(); - const { openUploadCreateDialog } = useCertUpload(); + const { openAddCertdPipelineDialog } = useCertPipelineCreator(); + const { openUploadCreateDialog } = useCertUpload(); - const pageRequest = async (query: UserPageQuery): Promise => { - return await api.GetList(query); - }; - const editRequest = async ({ form, row }: EditReq) => { - form.id = row.id; - const res = await api.UpdateObj(form); - lastResRef.value = res; - return res; - }; - const delRequest = async ({ row }: DelReq) => { - return await api.DelObj(row.id); - }; + const pageRequest = async (query: UserPageQuery): Promise => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }: EditReq) => { + form.id = row.id; + const res = await api.UpdateObj(form); + lastResRef.value = res; + return res; + }; + const delRequest = async ({ row }: DelReq) => { + return await api.DelObj(row.id); + }; - const addRequest = async ({ form }: AddReq) => { - if (form.content == null) { - form.content = JSON.stringify({ - title: form.title, - }); - } else { - //复制的流水线 - delete form.status; - delete form.lastHistoryTime; - delete form.lastVars; - delete form.createTime; - delete form.id; - let pipeline = JSON.parse(form.content); - pipeline.title = form.title; - pipeline = setRunnableIds(pipeline); - form.content = JSON.stringify(pipeline); - } + const addRequest = async ({ form }: AddReq) => { + if (form.content == null) { + form.content = JSON.stringify({ + title: form.title, + }); + } else { + //复制的流水线 + delete form.status; + delete form.lastHistoryTime; + delete form.lastVars; + delete form.createTime; + delete form.id; + let pipeline = JSON.parse(form.content); + pipeline.title = form.title; + pipeline = setRunnableIds(pipeline); + form.content = JSON.stringify(pipeline); + } - const res = await api.AddObj(form); - lastResRef.value = res; - return res; - }; + const res = await api.AddObj(form); + lastResRef.value = res; + return res; + }; - const { viewCert, downloadCert } = useCertViewer(); - const userStore = useUserStore(); - const settingStore = useSettingStore(); + const { viewCert, downloadCert } = useCertViewer(); + const userStore = useUserStore(); + const settingStore = useSettingStore(); - function onDialogOpen(opt: any) { - const searchForm = crudExpose.getSearchValidatedFormData(); - opt.initialForm = { - ...opt.initialForm, - groupId: searchForm.groupId, - }; - } + function onDialogOpen(opt: any) { + const searchForm = crudExpose.getSearchValidatedFormData(); + opt.initialForm = { + ...opt.initialForm, + groupId: searchForm.groupId, + }; + } - return { - crudOptions: { - request: { - pageRequest, - addRequest, - editRequest, - delRequest, - }, - settings: { - plugins: { - //行选择插件,内置插件 - rowSelection: { - //是否启用本插件 - enabled: true, - order: -2, - //合并在用户配置crudOptions之前还是之后 - before: true, - props: { - multiple: true, - crossPage: false, - selectedRowKeys, - onSelectedChanged(selected) { - console.log("已选择变化:", selected); - }, - }, - }, - }, - }, - actionbar: { - buttons: { - add: { - order: 5, - icon: "ion:ios-add-circle-outline", - text: "自定义流水线", - }, - addCertd: { - order: 1, - text: "创建证书流水线", - type: "primary", - icon: "ion:ios-add-circle-outline", - click() { - const searchForm = crudExpose.getSearchValidatedFormData(); - const defaultGroupId = searchForm.groupId; - openAddCertdPipelineDialog({ defaultGroupId }); - }, - }, - uploadCert: { - order: 2, - text: "商用证书托管", - type: "primary", - tooltip: { - slots: { - title() { - return ( - - 手动上传自有证书,执行自动部署 - 并不能自动申请商业证书 - 证书有更新时,都需要手动上传一次 - - ); - }, - }, - }, - icon: "ion:cloud-upload-outline", - click() { - const searchForm = crudExpose.getSearchValidatedFormData(); - openUploadCreateDialog({ defaultGroupId: searchForm.groupId }); - }, - }, - }, - }, - form: { - afterSubmit({ form, res, mode }) { - if (mode === "add") { - router.push({ path: "/certd/pipeline/detail", query: { id: res.id, editMode: "true" } }); - } - }, - wrapper: { - onOpen: onDialogOpen, - }, - }, - table: { - scroll: { x: 1500 }, - remove: { - confirmTitle: "确定要删除吗?", - confirmMessage: "将删除该流水线相关的所有数据,包括执行历史、证书文件、证书仓库记录等", - }, - }, - tabs: { - name: "groupId", - show: true, - }, - rowHandle: { - width: 200, - fixed: "right", - dropdown: { - show: true, - }, - buttons: { - play: { - order: -999, - title: "运行流水线", - tooltip: { title: "运行流水线" }, - type: "link", - icon: "ant-design:play-circle-outlined", - click({ row }) { - Modal.confirm({ - title: "确认", - content: `确定要触发运行吗?`, - async onOk() { - await api.Trigger(row.id); - notification.success({ message: "管道已经开始运行" }); - }, - }); - }, - }, - view: { - show: false, - click({ row }) { - router.push({ path: "/certd/pipeline/detail", query: { id: row.id, editMode: "false" } }); - }, - }, - copy: { - click: async context => { - settingStore.checkPlus(); - const { ui } = useUi(); - // @ts-ignore - let row = context[ui.tableColumn.row]; - row = cloneDeep(row); - row.title = row.title + "_copy"; - await crudExpose.openCopy({ - row: row, - index: context.index, - }); - }, - class: "need-plus", - }, - config: { - order: 1, - title: "编辑流水线", - type: "link", - dropdown: true, - icon: "ant-design:edit-outlined", - click({ row }) { - router.push({ path: "/certd/pipeline/detail", query: { id: row.id, editMode: "true" } }); - }, - }, - edit: { - order: 2, - title: "修改配置/分组", - icon: "ant-design:setting-outlined", - dropdown: true, - }, - viewCert: { - order: 3, - title: "查看证书", - tooltip: { title: "查看证书" }, - type: "link", - icon: "ph:certificate", - async click({ row }) { - await viewCert(row.id); - }, - }, - download: { - order: 4, - type: "link", - title: "下载证书", - tooltip: { title: "下载证书" }, - icon: "ant-design:download-outlined", - async click({ row }) { - await downloadCert(row.id); - }, - }, - remove: { - order: 5, - dropdown: true, - }, - }, - }, - columns: { - id: { - title: "ID", - key: "id", - type: "number", - search: { - show: true, - }, - column: { - width: 100, - }, - form: { - show: false, - }, - }, - userId: { - title: "用户Id", - type: "number", - search: { - show: computed(() => { - return userStore.isAdmin && settingStore.sysPublic.managerOtherUserPipeline; - }), - }, - form: { - show: false, - }, - column: { - show: computed(() => { - return userStore.isAdmin && settingStore.sysPublic.managerOtherUserPipeline; - }), - width: 100, - }, - }, - title: { - title: "流水线名称", - type: "link", - search: { - show: true, - title: "关键字", - component: { - name: "a-input", - }, - }, - form: { - rules: [{ required: true, message: "此项必填" }], - }, - column: { - width: 350, - // tooltip: true, - ellipsis: true, - sorter: true, - showTitle: true, - cellRender: ({ row, value }) => { - return {value}; - }, - }, - }, - content: { - title: "流水线内容", - form: { show: false }, - column: { - show: false, - }, - valueBuilder({ row }) { - if (row.content) { - row.content = JSON.parse(row.content); - const pipeline = row.content; - let stepCount = 0; - eachStages(pipeline.stages, (item, runnableType) => { - if (runnableType === "step") { - stepCount++; - } - }); - row._stepCount = stepCount; - if (pipeline.triggers) { - row._triggerCount = pipeline.triggers?.length > 0 ? pipeline.triggers.length : "-"; - } - } - }, - valueResolve({ row }) { - if (row.content) { - row.content = JSON.stringify(row.content); - } - }, - }, - _triggerCount: { - title: "定时任务数", - type: "number", - column: { - align: "center", - width: 100, - }, - form: { - show: false, - }, - }, - _stepCount: { - title: "部署任务数", - type: "number", - form: { show: false }, - column: { - align: "center", - width: 100, - }, - }, - lastVars: { - title: "到期剩余", - type: "number", - form: { - show: false, - }, - column: { - cellRender({ row }) { - if (!row.lastVars?.certExpiresTime) { - return "-"; - } - const leftDays = dayjs(row.lastVars.certExpiresTime).diff(dayjs(), "day"); - const color = leftDays < 20 ? "red" : "#389e0d"; - const percent = (leftDays / 90) * 100; - return `${leftDays} 天`} />; - }, - width: 150, - }, - }, - "lastVars.certExpiresTime": { - title: "过期时间", - search: { - show: false, - }, - type: "datetime", - form: { - show: false, - }, - column: { - sorter: false, - width: 150, - align: "center", - }, - }, - status: { - title: "状态", - type: "dict-select", - search: { - show: true, - }, - dict: dict({ - data: statusUtil.getOptions(), - }), - form: { - show: false, - }, - column: { - sorter: true, - width: 120, - align: "center", - }, - }, - lastHistoryTime: { - title: "最后运行", - type: "datetime", - form: { - show: false, - }, - column: { - sorter: true, - width: 150, - align: "center", - }, - }, - disabled: { - title: "启用", - type: "dict-switch", - dict: dict({ - data: [ - { value: false, label: "启用" }, - { value: true, label: "禁用" }, - ], - }), - form: { - value: false, - show: false, - }, - column: { - sorter: true, - width: 80, - align: "center", - component: { - name: "fs-dict-switch", - vModel: "checked", - }, - async valueChange({ row, key, value }) { - return await api.UpdateObj({ - id: row.id, - disabled: row[key], - }); - }, - }, - }, - groupId: { - title: "分组", - type: "dict-select", - search: { - show: true, - }, - dict: groupDictRef, - form: { - component: { - name: GroupSelector, - vModel: "modelValue", - }, - }, - column: { - width: 130, - align: "center", - component: { - color: "auto", - }, - sorter: true, - }, - }, - type: { - title: "类型", - type: "dict-select", - search: { - show: true, - }, - dict: dict({ - data: [ - { value: "cert", label: "证书申请" }, - { value: "cert_upload", label: "证书上传" }, - { value: "custom", label: "自定义" }, + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest, + }, + settings: { + plugins: { + //行选择插件,内置插件 + rowSelection: { + //是否启用本插件 + enabled: true, + order: -2, + //合并在用户配置crudOptions之前还是之后 + before: true, + props: { + multiple: true, + crossPage: false, + selectedRowKeys, + onSelectedChanged(selected) { + console.log("已选择变化:", selected); + }, + }, + }, + }, + }, + actionbar: { + buttons: { + add: { + order: 5, + icon: "ion:ios-add-circle-outline", + text: t("certd.customPipeline"), + }, + addCertd: { + order: 1, + text: t("certd.createCertdPipeline"), + type: "primary", + icon: "ion:ios-add-circle-outline", + click() { + const searchForm = crudExpose.getSearchValidatedFormData(); + const defaultGroupId = searchForm.groupId; + openAddCertdPipelineDialog({ defaultGroupId }); + }, + }, + uploadCert: { + order: 2, + text: t("certd.commercialCertHosting"), + type: "primary", + tooltip: { + slots: { + title() { + return ( + + {t("certd.tooltip.manualUploadOwnCert")} + {t("certd.tooltip.noAutoApplyCommercialCert")} + {t("certd.tooltip.manualUploadOnUpdate")} + + ); + }, + }, + }, + icon: "ion:cloud-upload-outline", + click() { + const searchForm = crudExpose.getSearchValidatedFormData(); + openUploadCreateDialog({ defaultGroupId: searchForm.groupId }); + }, + }, + }, + }, + form: { + afterSubmit({ form, res, mode }) { + if (mode === "add") { + router.push({ path: "/certd/pipeline/detail", query: { id: res.id, editMode: "true" } }); + } + }, + wrapper: { + onOpen: onDialogOpen, + }, + }, + table: { + scroll: { x: 1500 }, + remove: { + confirmTitle: t("certd.table.confirmDeleteTitle"), + confirmMessage: t("certd.table.confirmDeleteMessage"), + }, + }, + tabs: { + name: "groupId", + show: true, + }, + rowHandle: { + width: 200, + fixed: "right", + dropdown: { + show: true, + }, + buttons: { + play: { + order: -999, + title: t("certd.play.runPipeline"), + tooltip: { title: t("certd.play.runPipeline") }, + type: "link", + icon: "ant-design:play-circle-outlined", + click({ row }) { + Modal.confirm({ + title: t("certd.play.confirm"), + content: t("certd.play.confirmTrigger"), + async onOk() { + await api.Trigger(row.id); + notification.success({ message: t("certd.play.pipelineStarted") }); + }, + }); + }, + }, + view: { + show: false, + click({ row }) { + router.push({ path: "/certd/pipeline/detail", query: { id: row.id, editMode: "false" } }); + }, + }, + copy: { + click: async context => { + settingStore.checkPlus(); + const { ui } = useUi(); + // @ts-ignore + let row = context[ui.tableColumn.row]; + row = cloneDeep(row); + row.title = row.title + "_copy"; + await crudExpose.openCopy({ + row: row, + index: context.index, + }); + }, + class: "need-plus", + }, + config: { + order: 1, + title: t("certd.actions.editPipeline"), + type: "link", + dropdown: true, + icon: "ant-design:edit-outlined", + click({ row }) { + router.push({ path: "/certd/pipeline/detail", query: { id: row.id, editMode: "true" } }); + }, + }, + edit: { + order: 2, + title: t("certd.actions.editConfigGroup"), + icon: "ant-design:setting-outlined", + dropdown: true, + }, + viewCert: { + order: 3, + title: t("certd.actions.viewCertificate"), + tooltip: { title: t("certd.actions.viewCertificate") }, + type: "link", + icon: "ph:certificate", + async click({ row }) { + await viewCert(row.id); + }, + }, + download: { + order: 4, + type: "link", + title: t("certd.actions.downloadCertificate"), + tooltip: { title: t("certd.actions.downloadCertificate") }, + icon: "ant-design:download-outlined", + async click({ row }) { + await downloadCert(row.id); + }, + }, + remove: { + order: 5, + dropdown: true, + }, + }, + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + search: { + show: true, + }, + column: { + width: 100, + }, + form: { + show: false, + }, + }, + userId: { + title: t("certd.fields.userId"), + type: "number", + search: { + show: computed(() => { + return userStore.isAdmin && settingStore.sysPublic.managerOtherUserPipeline; + }), + }, + form: { + show: false, + }, + column: { + show: computed(() => { + return userStore.isAdmin && settingStore.sysPublic.managerOtherUserPipeline; + }), + width: 100, + }, + }, + title: { + title: t("certd.fields.pipelineName"), + type: "link", + search: { + show: true, + title: t("certd.fields.keyword"), + component: { + name: "a-input", + }, + }, + form: { + rules: [{ required: true, message: t("certd.fields.required") }], + }, + column: { + width: 350, + ellipsis: true, + sorter: true, + showTitle: true, + cellRender: ({ row, value }) => { + return {value}; + }, + }, + }, + content: { + title: t("certd.fields.pipelineContent"), + form: { show: false }, + column: { + show: false, + }, + valueBuilder({ row }) { + if (row.content) { + row.content = JSON.parse(row.content); + const pipeline = row.content; + let stepCount = 0; + eachStages(pipeline.stages, (item, runnableType) => { + if (runnableType === "step") { + stepCount++; + } + }); + row._stepCount = stepCount; + if (pipeline.triggers) { + row._triggerCount = pipeline.triggers?.length > 0 ? pipeline.triggers.length : "-"; + } + } + }, + valueResolve({ row }) { + if (row.content) { + row.content = JSON.stringify(row.content); + } + }, + }, + _triggerCount: { + title: t("certd.fields.scheduledTaskCount"), + type: "number", + column: { + align: "center", + width: 100, + }, + form: { + show: false, + }, + }, + _stepCount: { + title: t("certd.fields.deployTaskCount"), + type: "number", + form: { show: false }, + column: { + align: "center", + width: 100, + }, + }, + lastVars: { + title: t("certd.fields.remainingValidity"), + type: "number", + form: { + show: false, + }, + column: { + cellRender({ row }) { + if (!row.lastVars?.certExpiresTime) { + return "-"; + } + const leftDays = dayjs(row.lastVars.certExpiresTime).diff(dayjs(), "day"); + const color = leftDays < 20 ? "red" : "#389e0d"; + const percent = (leftDays / 90) * 100; + return `${leftDays} 天`} />; + }, + width: 150, + }, + }, + "lastVars.certExpiresTime": { + title: t("certd.fields.expiryTime"), + search: { + show: false, + }, + type: "datetime", + form: { + show: false, + }, + column: { + sorter: false, + width: 150, + align: "center", + }, + }, + status: { + title: t("certd.fields.status"), + type: "dict-select", + search: { + show: true, + }, + dict: dict({ + data: statusUtil.getOptions(), + }), + form: { + show: false, + }, + column: { + sorter: true, + width: 120, + align: "center", + }, + }, + lastHistoryTime: { + title: t("certd.fields.lastRun"), + type: "datetime", + form: { + show: false, + }, + column: { + sorter: true, + width: 150, + align: "center", + }, + }, + disabled: { + title: t("certd.fields.enabled"), + type: "dict-switch", + dict: dict({ + data: [ + { value: false, label: t("certd.fields.enabledLabel") }, + { value: true, label: t("certd.fields.disabledLabel") }, + ], + }), + form: { + value: false, + show: false, + }, + column: { + sorter: true, + width: 80, + align: "center", + component: { + name: "fs-dict-switch", + vModel: "checked", + }, + async valueChange({ row, key, value }) { + return await api.UpdateObj({ + id: row.id, + disabled: row[key], + }); + }, + }, + }, + groupId: { + title: t("certd.fields.group"), + type: "dict-select", + search: { + show: true, + }, + dict: groupDictRef, + form: { + component: { + name: GroupSelector, + vModel: "modelValue", + }, + }, + column: { + width: 130, + align: "center", + component: { + color: "auto", + }, + sorter: true, + }, + }, + type: { + title: t("certd.fields.type"), + type: "dict-select", + search: { + show: true, + }, + dict: dict({ + data: [ + { value: "cert", label: t("certd.types.certApply") }, + { value: "cert_upload", label: t("certd.types.certUpload") }, + { value: "custom", label: t("certd.types.custom") }, { value: "template", label: "模版" }, ], }), @@ -498,55 +497,56 @@ export default function ({ crudExpose, context: { groupDictRef, selectedRowKeys }, }, order: { - title: "排序号", - type: "number", - column: { - sorter: true, - align: "center", - width: 80, - }, - form: { - value: 0, - }, - }, - keepHistoryCount: { - title: "历史记录保持数", - type: "number", - form: { - value: 20, - helper: "历史记录保持条数,多余的会被删除", - }, - column: { - width: 130, - show: false, - sorter: true, - }, - }, - createTime: { - title: "创建时间", - type: "datetime", - form: { - show: false, - }, - column: { - sorter: true, - width: 155, - align: "center", - }, - }, - updateTime: { - title: "更新时间", - type: "datetime", - form: { - show: false, - }, - column: { - width: 125, - show: false, - sorter: true, - }, - }, - }, - }, - }; + title: t("certd.fields.order"), + type: "number", + column: { + sorter: true, + align: "center", + width: 80, + }, + form: { + value: 0, + }, + }, + keepHistoryCount: { + title: t("certd.fields.keepHistoryCount"), + type: "number", + form: { + value: 20, + helper: t("certd.fields.keepHistoryHelper"), + }, + column: { + width: 130, + show: false, + sorter: true, + }, + }, + createTime: { + title: t("certd.fields.createTime"), + type: "datetime", + form: { + show: false, + }, + column: { + sorter: true, + width: 155, + align: "center", + }, + }, + updateTime: { + title: t("certd.fields.updateTime"), + type: "datetime", + form: { + show: false, + }, + column: { + width: 125, + show: false, + sorter: true, + }, + }, + + }, + }, + }; } diff --git a/packages/ui/certd-client/src/views/certd/pipeline/group/crud.tsx b/packages/ui/certd-client/src/views/certd/pipeline/group/crud.tsx index 225b1b42..8392b54d 100644 --- a/packages/ui/certd-client/src/views/certd/pipeline/group/crud.tsx +++ b/packages/ui/certd-client/src/views/certd/pipeline/group/crud.tsx @@ -5,130 +5,131 @@ import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, Edi import { pipelineGroupApi } from "./api"; export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet { - const api = pipelineGroupApi; - const pageRequest = async (query: UserPageQuery): Promise => { - return await api.GetList(query); - }; - const editRequest = async (req: EditReq) => { - const { form, row } = req; - form.id = row.id; - const res = await api.UpdateObj(form); - return res; - }; - const delRequest = async (req: DelReq) => { - const { row } = req; - return await api.DelObj(row.id); - }; + const { t } = useI18n(); + const api = pipelineGroupApi; + const pageRequest = async (query: UserPageQuery): Promise => { + return await api.GetList(query); + }; + const editRequest = async (req: EditReq) => { + const { form, row } = req; + form.id = row.id; + const res = await api.UpdateObj(form); + return res; + }; + const delRequest = async (req: DelReq) => { + const { row } = req; + return await api.DelObj(row.id); + }; - const addRequest = async (req: AddReq) => { - const { form } = req; - const res = await api.AddObj(form); - return res; - }; + const addRequest = async (req: AddReq) => { + const { form } = req; + const res = await api.AddObj(form); + return res; + }; - return { - crudOptions: { - settings: { - plugins: { - mobile: { - props: { - rowHandle: { - width: 160, - }, - }, - }, - }, - }, - request: { - pageRequest, - addRequest, - editRequest, - delRequest, - }, - form: { - labelCol: { - //固定label宽度 - span: null, - style: { - width: "100px", - }, - }, - col: { - span: 22, - }, - wrapper: { - width: 600, - }, - }, - rowHandle: { - width: 200, - group: { - editable: { - edit: { - text: "编辑", - order: -1, - type: "primary", - click({ row, index }) { - crudExpose.openEdit({ - index, - row, - }); - }, - }, - }, - }, - }, - table: { - editable: { - enabled: true, - mode: "cell", - exclusive: true, - //排他式激活效果,将其他行的编辑状态触发保存 - exclusiveEffect: "save", //自动保存其他行编辑状态,cancel = 自动关闭其他行编辑状态 - async updateCell(opts) { - const { row, key, value } = opts; - //如果是添加,需要返回{[rowKey]:xxx},比如:{id:2} - return await api.UpdateObj({ id: row.id, [key]: value }); - }, - }, - }, - columns: { - id: { - title: "ID", - key: "id", - type: "number", - search: { - show: true, - }, - column: { - width: 100, - editable: { - disabled: true, - }, - }, - form: { - show: false, - }, - }, - name: { - title: "分组名称", - search: { - show: true, - }, - type: "text", - form: { - rules: [ - { - required: true, - message: "请输入分组名称", - }, - ], - }, - column: { - width: 400, - }, - }, - }, - }, - }; + return { + crudOptions: { + settings: { + plugins: { + mobile: { + props: { + rowHandle: { + width: 160, + }, + }, + }, + }, + }, + request: { + pageRequest, + addRequest, + editRequest, + delRequest, + }, + form: { + labelCol: { + //固定label宽度 + span: null, + style: { + width: "100px", + }, + }, + col: { + span: 22, + }, + wrapper: { + width: 600, + }, + }, + rowHandle: { + width: 200, + group: { + editable: { + edit: { + text: t('certd.edit'), + order: -1, + type: "primary", + click({ row, index }) { + crudExpose.openEdit({ + index, + row, + }); + }, + }, + }, + }, + }, + table: { + editable: { + enabled: true, + mode: "cell", + exclusive: true, + //排他式激活效果,将其他行的编辑状态触发保存 + exclusiveEffect: "save", //自动保存其他行编辑状态,cancel = 自动关闭其他行编辑状态 + async updateCell(opts) { + const { row, key, value } = opts; + //如果是添加,需要返回{[rowKey]:xxx},比如:{id:2} + return await api.UpdateObj({ id: row.id, [key]: value }); + }, + }, + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + search: { + show: true, + }, + column: { + width: 100, + editable: { + disabled: true, + }, + }, + form: { + show: false, + }, + }, + name: { + title: t('certd.groupName'), + search: { + show: true, + }, + type: "text", + form: { + rules: [ + { + required: true, + message: t('certd.enterGroupName'), + }, + ], + }, + column: { + width: 400, + }, + } + }, + }, + }; } diff --git a/packages/ui/certd-client/src/views/certd/pipeline/index.vue b/packages/ui/certd-client/src/views/certd/pipeline/index.vue index 727d7f19..01a77c1c 100644 --- a/packages/ui/certd-client/src/views/certd/pipeline/index.vue +++ b/packages/ui/certd-client/src/views/certd/pipeline/index.vue @@ -1,14 +1,14 @@ - 我的流水线 + {{ t("certd.myPipelines") }} - 已选择 {{ selectedRowKeys.length }} 项 - - + {{ t("certd.selectedCount", { count: selectedRowKeys.length }) }} + + @@ -16,12 +16,13 @@ - 申请证书 + {{ t("certd.applyCertificate") }} + diff --git a/packages/ui/certd-client/src/views/certd/suite/mine/crud.tsx b/packages/ui/certd-client/src/views/certd/suite/mine/crud.tsx index a2948e00..295ff2fc 100644 --- a/packages/ui/certd-client/src/views/certd/suite/mine/crud.tsx +++ b/packages/ui/certd-client/src/views/certd/suite/mine/crud.tsx @@ -7,310 +7,310 @@ import DurationValue from "/@/views/sys/suite/product/duration-value.vue"; import UserSuiteStatus from "/@/views/certd/suite/mine/user-suite-status.vue"; import dayjs from "dayjs"; export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet { - const pageRequest = async (query: UserPageQuery): Promise => { - return await api.GetList(query); - }; - const editRequest = async (req: EditReq) => { - const { form, row } = req; - form.id = row.id; - const res = await api.UpdateObj(form); - return res; - }; - const delRequest = async (req: DelReq) => { - const { row } = req; - return await api.DelObj(row.id); - }; + const pageRequest = async (query: UserPageQuery): Promise => { + return await api.GetList(query); + }; + const editRequest = async (req: EditReq) => { + const { form, row } = req; + form.id = row.id; + const res = await api.UpdateObj(form); + return res; + }; + const delRequest = async (req: DelReq) => { + const { row } = req; + return await api.DelObj(row.id); + }; - const addRequest = async (req: AddReq) => { - const { form } = req; - const res = await api.AddObj(form); - return res; - }; + const addRequest = async (req: AddReq) => { + const { form } = req; + const res = await api.AddObj(form); + return res; + }; - const router = useRouter(); + const router = useRouter(); - return { - crudOptions: { - request: { - pageRequest, - addRequest, - editRequest, - delRequest - }, - form: { - labelCol: { - //固定label宽度 - span: null, - style: { - width: "100px" - } - }, - col: { - span: 22 - }, - wrapper: { - width: 600 - } - }, - actionbar: { - buttons: { - add: { show: false }, - buy: { - text: "购买", - type: "primary", - click() { - router.push({ - path: "/certd/suite/buy" - }); - } - } - } - }, - rowHandle: { - width: 200, - fixed: "right", - buttons: { - view: { show: false }, - copy: { show: false }, - edit: { show: false }, - remove: { show: false } - // continue:{ - // text:"续期", - // type:"link", - // click(){ - // console.log("续期"); - // } - // } - } - }, - columns: { - id: { - title: "ID", - key: "id", - type: "number", - search: { - show: false - }, - column: { - width: 100, - editable: { - disabled: true - } - }, - form: { - show: false - } - }, - title: { - title: "套餐名称", - type: "text", - search: { - show: true - }, - form: { - rules: [{ required: true, message: "此项必填" }] - }, - column: { - width: 200 - } - }, - productType: { - title: "类型", - type: "dict-select", - editForm: { - component: { - disabled: true - } - }, - dict: dict({ - data: [ - { label: "套餐", value: "suite", color: "green" }, - { label: "加量包", value: "addon", color: "blue" } - ] - }), - form: { - rules: [{ required: true, message: "此项必填" }] - }, - column: { - width: 80, - align: "center" - }, - valueBuilder: ({ row }) => { - if (row.content) { - row.content = JSON.parse(row.content); - } - }, - valueResolve: ({ form }) => { - if (form.content) { - form.content = JSON.stringify(form.content); - } - } - }, - "content.maxDomainCount": { - title: "域名数量", - type: "text", - form: { - key: ["content", "maxDomainCount"], - component: { - name: SuiteValueEdit, - vModel: "modelValue", - unit: "个" - }, - rules: [{ required: true, message: "此项必填" }] - }, - column: { - width: 100, - component: { - name: SuiteValue, - vModel: "modelValue", - unit: "个" - }, - align: "center" - } - }, - "content.maxPipelineCount": { - title: "流水线数量", - type: "text", - form: { - key: ["content", "maxPipelineCount"], - component: { - name: SuiteValueEdit, - vModel: "modelValue", - unit: "条" - }, - rules: [{ required: true, message: "此项必填" }] - }, - column: { - width: 100, - component: { - name: SuiteValue, - vModel: "modelValue", - unit: "条" - }, - align: "center" - } - }, - "content.maxDeployCount": { - title: "部署次数", - type: "text", - form: { - key: ["content", "maxDeployCount"], - component: { - name: SuiteValueEdit, - vModel: "modelValue", - unit: "次" - }, - rules: [{ required: true, message: "此项必填" }] - }, - column: { - width: 100, - component: { - name: SuiteValue, - vModel: "modelValue", - unit: "次", - used: compute(({ row }) => { - return row.deployCountUsed; - }) - }, - align: "center" - } - }, - "content.maxMonitorCount": { - title: "证书监控数量", - type: "text", - form: { - key: ["content", "maxMonitorCount"], - component: { - name: SuiteValueEdit, - vModel: "modelValue", - unit: "个" - }, - rules: [{ required: true, message: "此项必填" }] - }, - column: { - width: 120, - component: { - name: SuiteValue, - vModel: "modelValue", - unit: "个" - }, - align: "center" - } - }, - duration: { - title: "时长", - type: "text", - form: {}, - column: { - component: { - name: DurationValue, - vModel: "modelValue" - }, - width: 100, - align: "center" - } - }, - status: { - title: "状态", - type: "text", - form: { show: false }, - column: { - width: 100, - align: "center", - component: { - name: UserSuiteStatus, - userSuite: compute(({ row }) => { - return row; - }), - currentSuite: context.currentSuite - }, - conditionalRender: { - match() { - return false; - } - } - } - }, - activeTime: { - title: "激活时间", - type: "date", - column: { - width: 150 - } - }, - expiresTime: { - title: "过期时间", - type: "date", - column: { - width: 150, - component: { - name: "expires-time-text", - vModel: "value", - mode: "tag", - title: compute(({ value }) => { - return dayjs(value).format("YYYY-MM-DD HH:mm:ss"); - }) - } - } - }, - isPresent: { - title: "是否赠送", - type: "dict-switch", - dict: dict({ - data: [ - { label: "是", value: true, color: "success" }, - { label: "否", value: false, color: "blue" } - ] - }), - form: { - value: true - }, - column: { - width: 100, - align: "center" - } - } - } - } - }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + form: { + labelCol: { + //固定label宽度 + span: null, + style: { + width: "100px" + } + }, + col: { + span: 22 + }, + wrapper: { + width: 600 + } + }, + actionbar: { + buttons: { + add: { show: false }, + buy: { + text: "购买", + type: "primary", + click() { + router.push({ + path: "/certd/suite/buy" + }); + } + } + } + }, + rowHandle: { + width: 200, + fixed: "right", + buttons: { + view: { show: false }, + copy: { show: false }, + edit: { show: false }, + remove: { show: false } + // continue:{ + // text:"续期", + // type:"link", + // click(){ + // console.log("续期"); + // } + // } + } + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + search: { + show: false + }, + column: { + width: 100, + editable: { + disabled: true + } + }, + form: { + show: false + } + }, + title: { + title: "套餐名称", + type: "text", + search: { + show: true + }, + form: { + rules: [{ required: true, message: "此项必填" }] + }, + column: { + width: 200 + } + }, + productType: { + title: "类型", + type: "dict-select", + editForm: { + component: { + disabled: true + } + }, + dict: dict({ + data: [ + { label: "套餐", value: "suite", color: "green" }, + { label: "加量包", value: "addon", color: "blue" } + ] + }), + form: { + rules: [{ required: true, message: "此项必填" }] + }, + column: { + width: 80, + align: "center" + }, + valueBuilder: ({ row }) => { + if (row.content) { + row.content = JSON.parse(row.content); + } + }, + valueResolve: ({ form }) => { + if (form.content) { + form.content = JSON.stringify(form.content); + } + } + }, + "content.maxDomainCount": { + title: "域名数量", + type: "text", + form: { + key: ["content", "maxDomainCount"], + component: { + name: SuiteValueEdit, + vModel: "modelValue", + unit: "个" + }, + rules: [{ required: true, message: "此项必填" }] + }, + column: { + width: 100, + component: { + name: SuiteValue, + vModel: "modelValue", + unit: "个" + }, + align: "center" + } + }, + "content.maxPipelineCount": { + title: "流水线数量", + type: "text", + form: { + key: ["content", "maxPipelineCount"], + component: { + name: SuiteValueEdit, + vModel: "modelValue", + unit: "条" + }, + rules: [{ required: true, message: "此项必填" }] + }, + column: { + width: 100, + component: { + name: SuiteValue, + vModel: "modelValue", + unit: "条" + }, + align: "center" + } + }, + "content.maxDeployCount": { + title: "部署次数", + type: "text", + form: { + key: ["content", "maxDeployCount"], + component: { + name: SuiteValueEdit, + vModel: "modelValue", + unit: "次" + }, + rules: [{ required: true, message: "此项必填" }] + }, + column: { + width: 100, + component: { + name: SuiteValue, + vModel: "modelValue", + unit: "次", + used: compute(({ row }) => { + return row.deployCountUsed; + }) + }, + align: "center" + } + }, + "content.maxMonitorCount": { + title: "证书监控数量", + type: "text", + form: { + key: ["content", "maxMonitorCount"], + component: { + name: SuiteValueEdit, + vModel: "modelValue", + unit: "个" + }, + rules: [{ required: true, message: "此项必填" }] + }, + column: { + width: 120, + component: { + name: SuiteValue, + vModel: "modelValue", + unit: "个" + }, + align: "center" + } + }, + duration: { + title: "时长", + type: "text", + form: {}, + column: { + component: { + name: DurationValue, + vModel: "modelValue" + }, + width: 100, + align: "center" + } + }, + status: { + title: "状态", + type: "text", + form: { show: false }, + column: { + width: 100, + align: "center", + component: { + name: UserSuiteStatus, + userSuite: compute(({ row }) => { + return row; + }), + currentSuite: context.currentSuite + }, + conditionalRender: { + match() { + return false; + } + } + } + }, + activeTime: { + title: "激活时间", + type: "date", + column: { + width: 150 + } + }, + expiresTime: { + title: "过期时间", + type: "date", + column: { + width: 150, + component: { + name: "expires-time-text", + vModel: "value", + mode: "tag", + title: compute(({ value }) => { + return dayjs(value).format("YYYY-MM-DD HH:mm:ss"); + }) + } + } + }, + isPresent: { + title: "是否赠送", + type: "dict-switch", + dict: dict({ + data: [ + { label: "是", value: true, color: "success" }, + { label: "否", value: false, color: "blue" } + ] + }), + form: { + value: true + }, + column: { + width: 100, + align: "center" + } + } + } + } + }; } diff --git a/packages/ui/certd-client/src/views/certd/suite/order-modal.vue b/packages/ui/certd-client/src/views/certd/suite/order-modal.vue index 46f53f32..7f474847 100644 --- a/packages/ui/certd-client/src/views/certd/suite/order-modal.vue +++ b/packages/ui/certd-client/src/views/certd/suite/order-modal.vue @@ -1,32 +1,46 @@ - + - 套餐:{{ product.title }} - 说明:{{ product.intro }} - 规格: + {{$t('order.package')}}:{{ product.title }} + + + {{$t('order.description')}}:{{ product.intro }} + + + {{$t('order.specifications')}}: - 流水线; - 域名; - 部署次数; + + {{$t('order.pipeline')}}; + + + {{$t('order.domain')}}; + + + {{$t('order.deployTimes')}}; + - 时长: + {{$t('order.duration')}}: - 价格: + + {{$t('order.price')}}: + + - 支付方式: - 免费 + {{$t('order.paymentMethod')}}: + {{$t('order.free')}} + diff --git a/packages/ui/certd-client/src/views/sys/authority/permission/index.vue b/packages/ui/certd-client/src/views/sys/authority/permission/index.vue index 9e07ade2..1d5be571 100644 --- a/packages/ui/certd-client/src/views/sys/authority/permission/index.vue +++ b/packages/ui/certd-client/src/views/sys/authority/permission/index.vue @@ -1,76 +1,81 @@ - - - 权限管理 - - - - - 添加 - - - - + + + {{ t("certd.permissionManagement") }} + + + + + {{ t("certd.adda") }} + + + + + diff --git a/packages/ui/certd-client/src/views/sys/authority/role/crud.tsx b/packages/ui/certd-client/src/views/sys/authority/role/crud.tsx index 026746c6..22535080 100644 --- a/packages/ui/certd-client/src/views/sys/authority/role/crud.tsx +++ b/packages/ui/certd-client/src/views/sys/authority/role/crud.tsx @@ -1,84 +1,86 @@ import * as api from "./api"; import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud"; +import { useI18n } from "vue-i18n"; export default function ({ crudExpose, context: { authz } }: CreateCrudOptionsProps): CreateCrudOptionsRet { - const pageRequest = async (query: UserPageQuery): Promise => { - return await api.GetList(query); - }; - const editRequest = async ({ form, row }: EditReq) => { - form.id = row.id; - return await api.UpdateObj(form); - }; - const delRequest = async ({ row }: DelReq) => { - return await api.DelObj(row.id); - }; + const { t } = useI18n(); + const pageRequest = async (query: UserPageQuery): Promise => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }: EditReq) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }: DelReq) => { + return await api.DelObj(row.id); + }; - const addRequest = async ({ form }: AddReq) => { - return await api.AddObj(form); - }; - return { - crudOptions: { - request: { - pageRequest, - addRequest, - editRequest, - delRequest - }, - rowHandle: { - width: 300, - buttons: { - authz: { - type: "link", - text: "授权", - async click(context) { - await authz.authzOpen(context.record.id); - } - } - } - }, - columns: { - id: { - title: "id", - type: "text", - form: { show: false }, // 表单配置 - column: { - width: 70, - sorter: true - } - }, - name: { - title: "角色名称", - type: "text", - search: { show: true }, - form: { - rules: [ - { required: true, message: "请输入角色名称" }, - { max: 50, message: "最大50个字符" } - ] - }, // 表单配置 - column: { - sorter: true - } - }, - createTime: { - title: "创建时间", - type: "datetime", - column: { - sorter: true - }, - form: { - show: false - } - }, - updateTime: { - title: "更新时间", - type: "datetime", - column: { - sorter: true - }, - form: { show: false } // 表单配置 - } - } - } - }; + const addRequest = async ({ form }: AddReq) => { + return await api.AddObj(form); + }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + rowHandle: { + width: 300, + buttons: { + authz: { + type: "link", + text: "授权", + async click(context) { + await authz.authzOpen(context.record.id); + } + } + } + }, + columns: { + id: { + title: "id", + type: "text", + form: { show: false }, // 表单配置 + column: { + width: 70, + sorter: true + } + }, + name: { + title: t("certd.roleName"), + type: "text", + search: { show: true }, + form: { + rules: [ + { required: true, message: t("certd.enterRoleName") }, + { max: 50, message: t("certd.max50Chars") } + ] + }, // 表单配置 + column: { + sorter: true + } + }, + createTime: { + title: t("certd.createTime"), + type: "datetime", + column: { + sorter: true + }, + form: { + show: false + } + }, + updateTime: { + title: t("certd.updateTime"), + type: "datetime", + column: { + sorter: true + }, + form: { show: false } // 表单配置 + } + } + } + }; } diff --git a/packages/ui/certd-client/src/views/sys/authority/role/index.vue b/packages/ui/certd-client/src/views/sys/authority/role/index.vue index 487b8ff4..4b868295 100644 --- a/packages/ui/certd-client/src/views/sys/authority/role/index.vue +++ b/packages/ui/certd-client/src/views/sys/authority/role/index.vue @@ -1,15 +1,18 @@ - - - 角色管理 - - - - - - + + + {{ t("certd.roleManagement") }} + + + + + + + diff --git a/packages/ui/certd-client/src/views/sys/authority/user/crud.tsx b/packages/ui/certd-client/src/views/sys/authority/user/crud.tsx index 25ff879d..0bc8ff66 100644 --- a/packages/ui/certd-client/src/views/sys/authority/user/crud.tsx +++ b/packages/ui/certd-client/src/views/sys/authority/user/crud.tsx @@ -4,282 +4,284 @@ import { useUserStore } from "/@/store/user"; import { Modal, notification } from "ant-design-vue"; import dayjs from "dayjs"; import { useSettingStore } from "/@/store/settings"; +import { useI18n } from "vue-i18n"; + export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet { - const pageRequest = async (query: UserPageQuery): Promise => { - return await api.GetList(query); - }; - const editRequest = async ({ form, row }: EditReq) => { - form.id = row.id; - return await api.UpdateObj(form); - }; - const delRequest = async ({ row }: DelReq) => { - return await api.DelObj(row.id); - }; + const { t } = useI18n(); + const pageRequest = async (query: UserPageQuery): Promise => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }: EditReq) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }: DelReq) => { + return await api.DelObj(row.id); + }; - const addRequest = async ({ form }: AddReq) => { - return await api.AddObj(form); - }; + const addRequest = async ({ form }: AddReq) => { + return await api.AddObj(form); + }; - const userStore = useUserStore(); + const userStore = useUserStore(); - const settingStore = useSettingStore(); - const userValidTimeEnabled = compute(() => { - return settingStore.sysPublic.userValidTimeEnabled === true; - }); - return { - crudOptions: { - request: { - pageRequest, - addRequest, - editRequest, - delRequest, - }, - rowHandle: { - fixed: "right", - buttons: { - unlock: { - title: "解除登录锁定", - text: null, - type: "link", - icon: "ion:lock-open-outline", - click: async ({ row }) => { - Modal.confirm({ - title: "提示", - content: "确定要解除该用户的登录锁定吗?", - onOk: async () => { - await api.Unlock(row.id); - notification.success({ - message: "解除成功", - }); - }, - }); - }, - }, - }, - }, - table: { - scroll: { - //使用固定列时需要设置此值,并且大于等于列宽度之和的值 - x: 1400, - }, - }, - columns: { - id: { - title: "id", - type: "text", - form: { show: false }, // 表单配置 - column: { - width: 100, - sorter: true, - }, - }, - createTime: { - title: "创建时间", - type: "datetime", - form: { show: false }, // 表单配置 - column: { - width: 180, - sorter: true, - }, - }, - // updateTime: { - // title: "修改时间", - // type: "datetime", - // form: { show: false }, // 表单配置 - // column: { - // sortable: "update_time", - // width: 180 - // } - // }, - username: { - title: "用户名", - type: "text", - search: { show: true }, // 开启查询 - form: { - rules: [ - { required: true, message: "请输入用户名" }, - { max: 50, message: "最大50个字符" }, - ], - }, - editForm: { component: { disabled: false } }, - column: { - sorter: true, - width: 200, - }, - }, - password: { - title: "密码", - type: "text", - key: "password", - column: { - show: false, - }, - form: { - rules: [{ max: 50, message: "最大50个字符" }], - component: { - showPassword: true, - }, - helper: "填写则修改密码", - }, - }, - nickName: { - title: "昵称", - type: "text", - search: { show: true }, // 开启查询 - form: { - rules: [{ max: 50, message: "最大50个字符" }], - }, - column: { - sorter: true, - }, - }, - email: { - title: "邮箱", - type: "text", - search: { show: true }, // 开启查询 - form: { - rules: [{ max: 50, message: "最大50个字符" }], - }, - column: { - sorter: true, - width: 160, - }, - }, - mobile: { - title: "手机号", - type: "text", - search: { show: true }, // 开启查询 - form: { - rules: [{ max: 50, message: "最大50个字符" }], - }, - column: { - sorter: true, - width: 130, - }, - }, - avatar: { - title: "头像", - type: "cropper-uploader", - column: { - width: 70, - component: { - //设置高度,修复操作列错位的问题 - style: { - height: "30px", - width: "auto", - }, - buildUrl(key: string) { - return `api/basic/file/download?&key=` + key; - }, - }, - }, - form: { - component: { - vModel: "modelValue", - valueType: "key", - cropper: { - aspectRatio: 1, - autoCropArea: 1, - viewMode: 0, - }, - onReady: null, - uploader: { - type: "form", - action: "/basic/file/upload", - name: "file", - headers: { - Authorization: "Bearer " + userStore.getToken, - }, - successHandle(res: any) { - return res; - }, - }, - buildUrl(key: string) { - return `api/basic/file/download?&key=` + key; - }, - }, - }, - }, - status: { - title: "状态", - type: "dict-switch", - dict: dict({ - data: [ - { label: "启用", value: 1, color: "green" }, - { label: "禁用", value: 0, color: "red" }, - ], - }), - column: { - align: "center", - sorter: true, - width: 100, - }, - }, - validTime: { - title: "有效期", - type: "date", - form: { - show: userValidTimeEnabled, - }, - column: { - align: "center", - sorter: true, - width: 100, - show: userValidTimeEnabled, - cellRender({ value }) { - if (value == null || value === 0) { - return ""; - } - if (value < dayjs().valueOf()) { - return 已过期; - } - const date = dayjs(value).format("YYYY-MM-DD"); - return ( - - - - ); - }, - }, - valueBuilder({ value, row, key }) { - if (value != null) { - row[key] = dayjs(value); - } - }, - valueResolve({ value, row, key }) { - if (value != null) { - row[key] = value.valueOf(); - } - }, - }, - remark: { - title: "备注", - type: "text", - column: { - sorter: true, - }, - form: { - rules: [{ max: 100, message: "最大100个字符" }], - }, - }, - roles: { - title: "角色", - type: "dict-select", - dict: dict({ - url: "/sys/authority/role/list", - value: "id", - label: "name", - }), // 数据字典 - form: { - component: { mode: "multiple" }, - }, - column: { - width: 250, - sortable: true, - }, - }, - }, - }, - }; + const settingStore = useSettingStore(); + const userValidTimeEnabled = compute(() => { + return settingStore.sysPublic.userValidTimeEnabled === true; + }); + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest, + }, + rowHandle: { + fixed: "right", + buttons: { + unlock: { + title: t("certd.unlockLogin"), + text: null, + type: "link", + icon: "ion:lock-open-outline", + click: async ({ row }) => { + Modal.confirm({ + title: t("certd.notice"), + content: t("certd.confirmUnlock"), + onOk: async () => { + await api.Unlock(row.id); + notification.success({ + message: t("certd.unlockSuccess"), + }); + }, + }); + }, + }, + }, + }, + table: { + scroll: { + //使用固定列时需要设置此值,并且大于等于列宽度之和的值 + x: 1400, + }, + }, + columns: { + id: { + title: "id", + type: "text", + form: { show: false }, // 表单配置 + column: { + width: 100, + sorter: true, + }, + }, + createTime: { + title: t("certd.createTime"), + type: "datetime", + form: { show: false }, // 表单配置 + column: { + width: 180, + sorter: true, + }, + }, + // updateTime: { + // title: "修改时间", + // type: "datetime", + // form: { show: false }, // 表单配置 + // column: { + // sortable: "update_time", + // width: 180 + // } + // }, + username: { + title: t("certd.username"), + type: "text", + search: { show: true }, // 开启查询 + form: { + rules: [ + { required: true, message: t("certd.enterUsername") }, + { max: 50, message: t("certd.max50Chars") }, + ], + }, + editForm: { component: { disabled: false } }, + column: { + sorter: true, + width: 200, + }, + }, + password: { + title: t("certd.password"), + type: "text", + key: "password", + column: { + show: false, + }, + form: { + rules: [{ max: 50, message: t("certd.max50Chars") }], + component: { + showPassword: true, + }, + helper: t("certd.modifyPasswordIfFilled"), + }, + }, + nickName: { + title: t("certd.nickName"), + type: "text", + search: { show: true }, // 开启查询 + form: { + rules: [{ max: 50, message: t("certd.max50Chars") }], + }, + column: { + sorter: true, + }, + }, + email: { + title: t("certd.emaila"), + type: "text", + search: { show: true }, // 开启查询 + form: { + rules: [{ max: 50, message: t("certd.max50Chars") }], + }, + column: { + sorter: true, + width: 160, + }, + }, + mobile: { + title: t("certd.mobile"), + type: "text", + search: { show: true }, // 开启查询 + form: { + rules: [{ max: 50, message: t("certd.max50Chars") }], + }, + column: { + sorter: true, + width: 130, + }, + }, + avatar: { + title: t("certd.avatar"), + type: "cropper-uploader", + column: { + width: 70, + component: { + style: { + height: "30px", + width: "auto", + }, + buildUrl(key: string) { + return `api/basic/file/download?&key=` + key; + }, + }, + }, + form: { + component: { + vModel: "modelValue", + valueType: "key", + cropper: { + aspectRatio: 1, + autoCropArea: 1, + viewMode: 0, + }, + onReady: null, + uploader: { + type: "form", + action: "/basic/file/upload", + name: "file", + headers: { + Authorization: "Bearer " + userStore.getToken, + }, + successHandle(res: any) { + return res; + }, + }, + buildUrl(key: string) { + return `api/basic/file/download?&key=` + key; + }, + }, + }, + }, + status: { + title: t("certd.status"), + type: "dict-switch", + dict: dict({ + data: [ + { label: t("certd.enabled"), value: 1, color: "green" }, + { label: t("certd.disabled"), value: 0, color: "red" }, + ], + }), + column: { + align: "center", + sorter: true, + width: 100, + }, + }, + validTime: { + title: t("certd.validTime"), + type: "date", + form: { + show: userValidTimeEnabled, + }, + column: { + align: "center", + sorter: true, + width: 100, + show: userValidTimeEnabled, + cellRender({ value }) { + if (value == null || value === 0) { + return ""; + } + if (value < dayjs().valueOf()) { + return {t("certd.expired")}; + } + const date = dayjs(value).format("YYYY-MM-DD"); + return ( + + + + ); + }, + }, + valueBuilder({ value, row, key }) { + if (value != null) { + row[key] = dayjs(value); + } + }, + valueResolve({ value, row, key }) { + if (value != null) { + row[key] = value.valueOf(); + } + }, + }, + remark: { + title: t("certd.remark"), + type: "text", + column: { + sorter: true, + }, + form: { + rules: [{ max: 100, message: t("certd.max100Chars") }], + }, + }, + roles: { + title: t("certd.roles"), + type: "dict-select", + dict: dict({ + url: "/sys/authority/role/list", + value: "id", + label: "name", + }), // 数据字典 + form: { + component: { mode: "multiple" }, + }, + column: { + width: 250, + sortable: true, + }, + }, + }, + }, + }; } diff --git a/packages/ui/certd-client/src/views/sys/cname/provider/crud.tsx b/packages/ui/certd-client/src/views/sys/cname/provider/crud.tsx index 269f819c..dda10e7c 100644 --- a/packages/ui/certd-client/src/views/sys/cname/provider/crud.tsx +++ b/packages/ui/certd-client/src/views/sys/cname/provider/crud.tsx @@ -8,244 +8,244 @@ import { useSettingStore } from "/@/store/settings"; import { Modal } from "ant-design-vue"; export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet { - const router = useRouter(); - const { t } = useI18n(); - const pageRequest = async (query: UserPageQuery): Promise => { - return await api.GetList(query); - }; - const editRequest = async ({ form, row }: EditReq) => { - form.id = row.id; - const res = await api.UpdateObj(form); - return res; - }; - const delRequest = async ({ row }: DelReq) => { - return await api.DelObj(row.id); - }; + const router = useRouter(); + const { t } = useI18n(); + const pageRequest = async (query: UserPageQuery): Promise => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }: EditReq) => { + form.id = row.id; + const res = await api.UpdateObj(form); + return res; + }; + const delRequest = async ({ row }: DelReq) => { + return await api.DelObj(row.id); + }; - const addRequest = async ({ form }: AddReq) => { - const res = await api.AddObj(form); - return res; - }; + const addRequest = async ({ form }: AddReq) => { + const res = await api.AddObj(form); + return res; + }; - const userStore = useUserStore(); - const settingStore = useSettingStore(); - const selectedRowKeys: Ref = ref([]); - context.selectedRowKeys = selectedRowKeys; + const userStore = useUserStore(); + const settingStore = useSettingStore(); + const selectedRowKeys: Ref = ref([]); + context.selectedRowKeys = selectedRowKeys; - return { - crudOptions: { - settings: { - plugins: { - //这里使用行选择插件,生成行选择crudOptions配置,最终会与crudOptions合并 - rowSelection: { - enabled: true, - order: -2, - before: true, - // handle: (pluginProps,useCrudProps)=>CrudOptions, - props: { - multiple: true, - crossPage: true, - selectedRowKeys - } - } - } - }, - request: { - pageRequest, - addRequest, - editRequest, - delRequest - }, - rowHandle: { - minWidth: 200, - fixed: "right" - }, - columns: { - id: { - title: "ID", - key: "id", - type: "number", - column: { - width: 100 - }, - form: { - show: false - } - }, - domain: { - title: "CNAME域名", - type: "text", - editForm: { - component: { - disabled: true - } - }, - search: { - show: true - }, - form: { - component: { - placeholder: "cname.handsfree.work" - }, - helper: "需要一个右边DNS提供商注册的域名(也可以将其他域名的dns服务器转移到这几家来)。\nCNAME域名一旦确定不可修改,建议使用一级子域名", - rules: [{ required: true, message: "此项必填" }] - }, - column: { - width: 200 - } - }, - dnsProviderType: { - title: "DNS提供商", - type: "dict-select", - search: { - show: true - }, - dict: dict({ - url: "pi/dnsProvider/list", - value: "key", - label: "title" - }), - form: { - rules: [{ required: true, message: "此项必填" }] - }, - column: { - width: 150, - component: { - color: "auto" - } - } - }, - accessId: { - title: "DNS提供商授权", - type: "dict-select", - dict: dict({ - url: "/pi/access/list", - value: "id", - label: "name" - }), - form: { - component: { - name: "access-selector", - vModel: "modelValue", - type: compute(({ form }) => { - return form.dnsProviderType; - }) - }, - rules: [{ required: true, message: "此项必填" }] - }, - column: { - width: 150, - component: { - color: "auto" - } - } - }, - isDefault: { - title: "是否默认", - type: "dict-switch", - dict: dict({ - data: [ - { label: "是", value: true, color: "success" }, - { label: "否", value: false, color: "default" } - ] - }), - form: { - value: false, - rules: [{ required: true, message: "请选择是否默认" }] - }, - column: { - align: "center", - width: 100 - } - }, - setDefault: { - title: "设置默认", - type: "text", - form: { - show: false - }, - column: { - width: 100, - align: "center", - conditionalRenderDisabled: true, - cellRender: ({ row }) => { - if (row.isDefault) { - return; - } - const onClick = async () => { - Modal.confirm({ - title: "提示", - content: `确定要设置为默认吗?`, - onOk: async () => { - await api.SetDefault(row.id); - await crudExpose.doRefresh(); - } - }); - }; + return { + crudOptions: { + settings: { + plugins: { + //这里使用行选择插件,生成行选择crudOptions配置,最终会与crudOptions合并 + rowSelection: { + enabled: true, + order: -2, + before: true, + // handle: (pluginProps,useCrudProps)=>CrudOptions, + props: { + multiple: true, + crossPage: true, + selectedRowKeys + } + } + } + }, + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + rowHandle: { + minWidth: 200, + fixed: "right" + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 100 + }, + form: { + show: false + } + }, + domain: { + title: t("certd.cnameDomain"), + type: "text", + editForm: { + component: { + disabled: true, + }, + }, + search: { + show: true, + }, + form: { + component: { + placeholder: t("certd.cnameDomainPlaceholder"), + }, + helper: t("certd.cnameDomainHelper"), + rules: [{ required: true, message: t("certd.requiredField") }], + }, + column: { + width: 200, + }, + }, + dnsProviderType: { + title: t("certd.dnsProvider"), + type: "dict-select", + search: { + show: true, + }, + dict: dict({ + url: "pi/dnsProvider/list", + value: "key", + label: "title", + }), + form: { + rules: [{ required: true, message: t("certd.requiredField") }], + }, + column: { + width: 150, + component: { + color: "auto", + }, + }, + }, + accessId: { + title: t("certd.dnsProviderAuthorization"), + type: "dict-select", + dict: dict({ + url: "/pi/access/list", + value: "id", + label: "name", + }), + form: { + component: { + name: "access-selector", + vModel: "modelValue", + type: compute(({ form }) => { + return form.dnsProviderType; + }), + }, + rules: [{ required: true, message: t("certd.requiredField") }], + }, + column: { + width: 150, + component: { + color: "auto", + }, + }, + }, + isDefault: { + title: t("certd.isDefault"), + type: "dict-switch", + dict: dict({ + data: [ + { label: t("certd.yes"), value: true, color: "success" }, + { label: t("certd.no"), value: false, color: "default" }, + ], + }), + form: { + value: false, + rules: [{ required: true, message: t("certd.selectIsDefault") }], + }, + column: { + align: "center", + width: 100, + }, + }, + setDefault: { + title: t("certd.setDefault"), + type: "text", + form: { + show: false, + }, + column: { + width: 100, + align: "center", + conditionalRenderDisabled: true, + cellRender: ({ row }) => { + if (row.isDefault) { + return; + } + const onClick = async () => { + Modal.confirm({ + title: t("certd.prompt"), + content: t("certd.confirmSetDefault"), + onOk: async () => { + await api.SetDefault(row.id); + await crudExpose.doRefresh(); + }, + }); + }; - return ( - - 设为默认 - - ); - } - } - }, - disabled: { - title: "禁用/启用", - type: "dict-switch", - dict: dict({ - data: [ - { label: "启用", value: false, color: "success" }, - { label: "禁用", value: true, color: "error" } - ] - }), - form: { - value: false - }, - column: { - width: 100, - component: { - title: "点击可禁用/启用", - on: { - async click({ value, row }) { - Modal.confirm({ - title: "提示", - content: `确定要${!value ? "禁用" : "启用"}吗?`, - onOk: async () => { - await api.SetDisabled(row.id, !value); - await crudExpose.doRefresh(); - } - }); - } - } - } - } - }, - createTime: { - title: "创建时间", - type: "datetime", - form: { - show: false - }, - column: { - sorter: true, - width: 160, - align: "center" - } - }, - updateTime: { - title: "更新时间", - type: "datetime", - form: { - show: false - }, - column: { - show: true, - width: 160 - } - } - } - } - }; + return ( + + {t("certd.setAsDefault")} + + ); + }, + }, + }, + disabled: { + title: t("certd.disabled"), + type: "dict-switch", + dict: dict({ + data: [ + { label: t("certd.enabled"), value: false, color: "success" }, + { label: t("certd.disabledLabel"), value: true, color: "error" }, + ], + }), + form: { + value: false, + }, + column: { + width: 100, + component: { + title: t("certd.clickToToggle"), + on: { + async click({ value, row }) { + Modal.confirm({ + title: t("certd.prompt"), + content: t("certd.confirmToggleStatus", { action: !value ? t("certd.disable") : t("certd.enable") }), + onOk: async () => { + await api.SetDisabled(row.id, !value); + await crudExpose.doRefresh(); + }, + }); + }, + }, + }, + }, + }, + createTime: { + title: t("certd.createTime"), + type: "datetime", + form: { + show: false, + }, + column: { + sorter: true, + width: 160, + align: "center", + }, + }, + updateTime: { + title: t("certd.updateTime"), + type: "datetime", + form: { + show: false, + }, + column: { + show: true, + width: 160, + }, + }, + } + } + }; } diff --git a/packages/ui/certd-client/src/views/sys/cname/provider/index.vue b/packages/ui/certd-client/src/views/sys/cname/provider/index.vue index 15ef5aa6..858da32d 100644 --- a/packages/ui/certd-client/src/views/sys/cname/provider/index.vue +++ b/packages/ui/certd-client/src/views/sys/cname/provider/index.vue @@ -1,60 +1,67 @@ - - - - CNAME服务配置 - - 此处配置的域名作为其他域名校验的代理,当别的域名需要申请证书时,通过CNAME映射到此域名上来验证所有权。好处是任何域名都可以通过此方式申请证书,也无需填写AccessSecret。 - CNAME功能原理及使用说明 - - - - - - - - - - - + + + + {{ t("certd.cnameTitle") }} + + {{ t("certd.cnameDescription") }} + + {{ t("certd.cnameLinkText") }} + + + + + + + + + + + + + diff --git a/packages/ui/certd-client/src/views/sys/plugin/crud.tsx b/packages/ui/certd-client/src/views/sys/plugin/crud.tsx index 938a9dd4..01d33229 100644 --- a/packages/ui/certd-client/src/views/sys/plugin/crud.tsx +++ b/packages/ui/certd-client/src/views/sys/plugin/crud.tsx @@ -8,559 +8,527 @@ import { Modal, notification } from "ant-design-vue"; import yaml from "js-yaml"; export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet { - const router = useRouter(); - const { t } = useI18n(); + const router = useRouter(); + const { t } = useI18n(); - let lastType = ""; - const pageRequest = async (query: UserPageQuery): Promise => { - if (lastType && lastType != query?.query?.type) { - //lastType有变化 - query.page.offset = 0; - } - lastType = query?.query?.type; - return await api.GetList(query); - }; - const editRequest = async ({ form, row }: EditReq) => { - form.id = row.id; - const res = await api.UpdateObj(form); - return res; - }; - const delRequest = async ({ row }: DelReq) => { - return await api.DelObj(row.id); - }; + let lastType = ""; + const pageRequest = async (query: UserPageQuery): Promise => { + if (lastType && lastType != query?.query?.type) { + query.page.offset = 0; + } + lastType = query?.query?.type; + return await api.GetList(query); + }; + const editRequest = async ({ form, row }: EditReq) => { + form.id = row.id; + const res = await api.UpdateObj(form); + return res; + }; + const delRequest = async ({ row }: DelReq) => { + return await api.DelObj(row.id); + }; - const addRequest = async ({ form }: AddReq) => { - const res = await api.AddObj(form); - return res; - }; + const addRequest = async ({ form }: AddReq) => { + const res = await api.AddObj(form); + return res; + }; - const selectedRowKeys: Ref = ref([]); - context.selectedRowKeys = selectedRowKeys; - const { openCrudFormDialog } = useFormWrapper(); + const selectedRowKeys: Ref = ref([]); + context.selectedRowKeys = selectedRowKeys; + const { openCrudFormDialog } = useFormWrapper(); - async function openImportDialog() { - function createCrudOptions() { - return { - crudOptions: { - columns: { - content: { - title: "插件文件", - type: "text", - form: { - component: { - name: "pem-input", - vModel: "modelValue", - textarea: { - rows: 8, - }, - }, - col: { - span: 24, - }, - helper: "选择插件文件", - }, - }, - override: { - title: "同名覆盖", - type: "dict-switch", - dict: dict({ - data: [ - { - value: true, - label: "覆盖", - }, - { - value: false, - label: "不覆盖", - }, - ], - }), - form: { - value: false, - col: { - span: 24, - }, - helper: "如果已有相同名称插件,直接覆盖", - }, - }, - }, - form: { - wrapper: { - title: "导入插件", - saveRemind: false, - }, - afterSubmit() { - notification.success({ message: "操作成功" }); - crudExpose.doRefresh(); - }, - async doSubmit({ form }: any) { - return await api.ImportPlugin({ - ...form, - }); - }, - }, - }, - }; - } - const { crudOptions } = createCrudOptions(); - await openCrudFormDialog({ crudOptions }); - } - return { - crudOptions: { - settings: { - plugins: { - //这里使用行选择插件,生成行选择crudOptions配置,最终会与crudOptions合并 - rowSelection: { - enabled: true, - order: -2, - before: true, - // handle: (pluginProps,useCrudProps)=>CrudOptions, - props: { - multiple: true, - crossPage: true, - selectedRowKeys, - }, - }, - }, - }, - request: { - pageRequest, - addRequest, - editRequest, - delRequest, - }, - actionbar: { - buttons: { - add: { - show: true, - icon: "ion:ios-add-circle-outline", - text: "自定义插件", - }, - import: { - show: true, - icon: "ion:cloud-upload-outline", - text: "导入", - type: "primary", - async click() { - await openImportDialog(); - }, - }, - }, - }, - rowHandle: { - show: true, - minWidth: 200, - fixed: "right", - buttons: { - edit: { - show: compute(({ row }) => { - return row.type === "custom"; - }), - }, - copy: { - show: compute(({ row }) => { - return row.type === "custom"; - }), - }, - remove: { - order: 999, - show: compute(({ row }) => { - return row.type === "custom"; - }), - }, - export: { - text: null, - icon: "ion:cloud-download-outline", - title: "导出", - type: "link", - show: compute(({ row }) => { - return row.type === "custom"; - }), - async click({ row }) { - //将文本内容,作为文件下载 - const content = await api.ExportPlugin(row.id); - if (content) { - const blob = new Blob([content], { type: "text/plain;charset=utf-8" }); - const url = URL.createObjectURL(blob); - const link = document.createElement("a"); - link.href = url; - link.download = `${row.name}.yaml`; - link.click(); - URL.revokeObjectURL(url); - } - }, - }, - }, - }, - table: { - rowKey: "name", - }, - tabs: { - name: "type", - show: true, - defaultOption: { - show: false, - }, - }, - form: { - onSuccess(opts: any) { - if (opts.res?.id) { - router.push({ - name: "SysPluginEdit", - query: { - id: opts.res.id, - }, - }); - } - }, - }, - columns: { - // id: { - // title: "ID", - // key: "id", - // type: "number", - // column: { - // width: 100 - // }, - // form: { - // show: false - // } - // }, - pluginType: { - title: "插件类型", - type: "dict-select", - search: { - show: true, - }, - form: { - order: 0, - rules: [{ required: true }], - component: { - disabled: true, - }, - }, - addForm: { - component: { - disabled: false, - }, - }, - dict: dict({ - data: [ - { label: "授权", value: "access" }, - { label: "DNS", value: "dnsProvider" }, - { label: "部署插件", value: "deploy" }, - ], - }), - column: { - width: 100, - align: "center", - component: { - color: "auto", - }, - }, - }, - icon: { - title: "图标", - type: "icon", - form: { - rules: [{ required: true }], - }, - column: { - width: 70, - align: "center", - component: { - name: "fs-icon", - vModel: "icon", - style: { - fontSize: "22px", - }, - }, - }, - }, - name: { - title: "插件名称", - type: "text", - search: { - show: true, - }, - form: { - show: true, - helper: "必须为英文或数字,驼峰命名,类型作为前缀\n例如AliyunDeployToCDN\n插件一旦被使用,不要修改名称", - rules: [ - { required: true }, - { - type: "regexp", - pattern: /^[a-zA-Z][a-zA-Z0-9]+$/, - message: "必须为英文或数字,驼峰命名,类型作为前缀", - }, - ], - }, - column: { - width: 250, - cellRender({ row }) { - if (row.author) { - return ; - } else { - return ; - } - }, - }, - }, - author: { - title: "作者", - type: "text", - search: { - show: true, - }, - form: { - show: true, - helper: "上传到插件商店时,将作为插件名称前缀,例如:greper/pluginName", - rules: [ - { required: true }, - { - type: "regexp", - pattern: /^[a-zA-Z][a-zA-Z0-9]+$/, - message: "必须为英文字母或数字", - }, - ], - }, - column: { - width: 200, - show: false, - }, - }, - title: { - title: "标题", - type: "text", - form: { - helper: "插件中文名称", - rules: [{ required: true }], - }, - column: { - width: 300, - cellRender({ row }) { - if (row.type === "custom") { - return {row.title}; - } - return {row.title}; - }, - }, - }, - desc: { - title: "描述", - type: "textarea", - helper: "插件的描述", - column: { - width: 300, - show: false, - }, - }, - - type: { - title: "来源", - type: "dict-select", - search: { - show: true, - }, - form: { - value: "custom", - component: { - disabled: true, - }, - }, - dict: dict({ - data: [ - { label: "内置", value: "builtIn" }, - { label: "自建", value: "custom" }, - { label: "商店", value: "store" }, - ], - }), - column: { - width: 70, - align: "center", - component: { - color: "auto", - }, - }, - }, - version: { - title: "版本", - type: "text", - column: { - width: 100, - align: "center", - }, - }, - // "extra.dependLibs": { - // title: "第三方依赖", - // type: "text", - // form: { - // helper: "依赖的第三方库,package.dependencies的格式:name[:^version]", - // component: { - // name: "a-select", - // mode: "tags", - // allowClear: true, - // open: false, - // }, - // }, - // column: { - // show: false, - // }, - // }, - "extra.dependPlugins": { - title: "插件依赖", - type: "text", - form: { - component: { - name: "a-select", - mode: "tags", - open: false, - allowClear: true, - }, - helper: "安装时会先安装依赖的插件,格式:[author/]pluginName[:version]", - }, - column: { - show: false, - }, - }, - "extra.showRunStrategy": { - title: "可修改运行策略", - type: "dict-switch", - dict: dict({ - data: [ - { value: true, label: "可修改" }, - { value: false, label: "不可修改" }, - ], - }), - form: { - value: false, - rules: [{ required: true }], - }, - column: { - width: 100, - align: "left", - show: false, - }, - }, - "extra.default.strategy.runStrategy": { - title: "运行策略", - type: "dict-select", - dict: dict({ - data: [ - { value: 0, label: "正常运行" }, - { value: 1, label: "成功后跳过(部署任务)" }, - ], - }), - form: { - value: 1, - rules: [{ required: true }], - helper: "默认运行策略", - show: compute(({ form }) => { - return form.extra.showRunStrategy; - }), - }, - column: { - width: 100, - align: "left", - component: { - color: "auto", - }, - show: false, - }, - valueBuilder({ row }) { - if (row.extra) { - row.extra = yaml.load(row.extra); - } - }, - valueResolve({ row }) { - if (row.extra) { - row.extra = yaml.dump(row.extra); - } - }, - }, - disabled: { - title: "点击禁用/启用", - type: "dict-switch", - dict: dict({ - data: [ - { label: "启用", value: false, color: "success" }, - { label: "禁用", value: true, color: "error" }, - ], - }), - form: { - title: "禁用/启用", - value: false, - }, - column: { - width: 120, - align: "center", - component: { - title: "点击可禁用/启用", - on: { - async click({ value, row }) { - Modal.confirm({ - title: "提示", - content: `确定要${!value ? "禁用" : "启用"}吗?`, - onOk: async () => { - await api.SetDisabled({ - id: row.id, - name: row.name, - type: row.type, - disabled: !value, - }); - await crudExpose.doRefresh(); - }, - }); - }, - }, - }, - }, - }, - group: { - title: "插件分组", - type: "dict-select", - dict: dict({ - url: "/pi/plugin/groupsList", - label: "title", - value: "key", - }), - form: { - rules: [{ required: true }], - show: compute(({ form }) => { - return form.pluginType === "deploy"; - }), - }, - column: { - width: 100, - align: "left", - component: { - color: "auto", - }, - }, - }, - createTime: { - title: "创建时间", - type: "datetime", - form: { - show: false, - }, - column: { - sorter: true, - width: 160, - align: "center", - }, - }, - updateTime: { - title: "更新时间", - type: "datetime", - form: { - show: false, - }, - column: { - show: true, - }, - }, - }, - }, - }; + async function openImportDialog() { + function createCrudOptions() { + return { + crudOptions: { + columns: { + content: { + title: t("certd.pluginFile"), + type: "text", + form: { + component: { + name: "pem-input", + vModel: "modelValue", + textarea: { + rows: 8, + }, + }, + col: { + span: 24, + }, + helper: t("certd.selectPluginFile"), + }, + }, + override: { + title: t("certd.overrideSameName"), + type: "dict-switch", + dict: dict({ + data: [ + { + value: true, + label: t("certd.override"), + }, + { + value: false, + label: t("certd.noOverride"), + }, + ], + }), + form: { + value: false, + col: { + span: 24, + }, + helper: t("certd.overrideHelper"), + }, + }, + }, + form: { + wrapper: { + title: t("certd.importPlugin"), + saveRemind: false, + }, + afterSubmit() { + notification.success({ message: t("certd.operationSuccess") }); + crudExpose.doRefresh(); + }, + async doSubmit({ form }: any) { + return await api.ImportPlugin({ + ...form, + }); + }, + }, + }, + }; + } + const { crudOptions } = createCrudOptions(); + await openCrudFormDialog({ crudOptions }); + } + return { + crudOptions: { + settings: { + plugins: { + rowSelection: { + enabled: true, + order: -2, + before: true, + props: { + multiple: true, + crossPage: true, + selectedRowKeys, + }, + }, + }, + }, + request: { + pageRequest, + addRequest, + editRequest, + delRequest, + }, + actionbar: { + buttons: { + add: { + show: true, + icon: "ion:ios-add-circle-outline", + text: t("certd.customPlugin"), + }, + import: { + show: true, + icon: "ion:cloud-upload-outline", + text: t("certd.import"), + type: "primary", + async click() { + await openImportDialog(); + }, + }, + }, + }, + rowHandle: { + show: true, + minWidth: 200, + fixed: "right", + buttons: { + edit: { + show: compute(({ row }) => { + return row.type === "custom"; + }), + }, + copy: { + show: compute(({ row }) => { + return row.type === "custom"; + }), + }, + remove: { + order: 999, + show: compute(({ row }) => { + return row.type === "custom"; + }), + }, + export: { + text: null, + icon: "ion:cloud-download-outline", + title: t("certd.export"), + type: "link", + show: compute(({ row }) => { + return row.type === "custom"; + }), + async click({ row }) { + const content = await api.ExportPlugin(row.id); + if (content) { + const blob = new Blob([content], { type: "text/plain;charset=utf-8" }); + const url = URL.createObjectURL(blob); + const link = document.createElement("a"); + link.href = url; + link.download = `${row.name}.yaml`; + link.click(); + URL.revokeObjectURL(url); + } + }, + }, + }, + }, + table: { + rowKey: "name", + }, + tabs: { + name: "type", + show: true, + defaultOption: { + show: false, + }, + }, + form: { + onSuccess(opts: any) { + if (opts.res?.id) { + router.push({ + name: "SysPluginEdit", + query: { + id: opts.res.id, + }, + }); + } + }, + }, + columns: { + pluginType: { + title: t("certd.pluginType"), + type: "dict-select", + search: { + show: true, + }, + form: { + order: 0, + rules: [{ required: true }], + component: { + disabled: true, + }, + }, + addForm: { + component: { + disabled: false, + }, + }, + dict: dict({ + data: [ + { label: t("certd.auth"), value: "access" }, + { label: t("certd.dns"), value: "dnsProvider" }, + { label: t("certd.deployPlugin"), value: "deploy" }, + ], + }), + column: { + width: 100, + align: "center", + component: { + color: "auto", + }, + }, + }, + icon: { + title: t("certd.icon"), + type: "icon", + form: { + rules: [{ required: true }], + }, + column: { + width: 70, + align: "center", + component: { + name: "fs-icon", + vModel: "icon", + style: { + fontSize: "22px", + }, + }, + }, + }, + name: { + title: t("certd.pluginName"), + type: "text", + search: { + show: true, + }, + form: { + show: true, + helper: t("certd.pluginNameHelper"), + rules: [ + { required: true }, + { + type: "regexp", + pattern: /^[a-zA-Z][a-zA-Z0-9]+$/, + message: t("certd.pluginNameRuleMsg"), + }, + ], + }, + column: { + width: 250, + cellRender({ row }) { + if (row.author) { + return ; + } else { + return ; + } + }, + }, + }, + author: { + title: t("certd.author"), + type: "text", + search: { + show: true, + }, + form: { + show: true, + helper: t("certd.authorHelper"), + rules: [ + { required: true }, + { + type: "regexp", + pattern: /^[a-zA-Z][a-zA-Z0-9]+$/, + message: t("certd.authorRuleMsg"), + }, + ], + }, + column: { + width: 200, + show: false, + }, + }, + title: { + title: t("certd.titlea"), + type: "text", + form: { + helper: t("certd.titleHelper"), + rules: [{ required: true }], + }, + column: { + width: 300, + cellRender({ row }) { + if (row.type === "custom") { + return {row.title}; + } + return {row.title}; + }, + }, + }, + desc: { + title: t("certd.description"), + type: "textarea", + helper: t("certd.descriptionHelper"), + column: { + width: 300, + show: false, + }, + }, + type: { + title: t("certd.sourcee"), + type: "dict-select", + search: { + show: true, + }, + form: { + value: "custom", + component: { + disabled: true, + }, + }, + dict: dict({ + data: [ + { label: t("certd.builtIn"), value: "builtIn" }, + { label: t("certd.custom"), value: "custom" }, + { label: t("certd.store"), value: "store" }, + ], + }), + column: { + width: 70, + align: "center", + component: { + color: "auto", + }, + }, + }, + version: { + title: t("certd.version"), + type: "text", + column: { + width: 100, + align: "center", + }, + }, + "extra.dependPlugins": { + title: t("certd.pluginDependencies"), + type: "text", + form: { + component: { + name: "a-select", + mode: "tags", + open: false, + allowClear: true, + }, + helper: t("certd.pluginDependenciesHelper"), + }, + column: { + show: false, + }, + }, + "extra.showRunStrategy": { + title: t("certd.editableRunStrategy"), + type: "dict-switch", + dict: dict({ + data: [ + { value: true, label: t("certd.editable") }, + { value: false, label: t("certd.notEditable") }, + ], + }), + form: { + value: false, + rules: [{ required: true }], + }, + column: { + width: 100, + align: "left", + show: false, + }, + }, + "extra.default.strategy.runStrategy": { + title: t("certd.runStrategy"), + type: "dict-select", + dict: dict({ + data: [ + { value: 0, label: t("certd.normalRun") }, + { value: 1, label: t("certd.skipOnSuccess") }, + ], + }), + form: { + value: 1, + rules: [{ required: true }], + helper: t("certd.defaultRunStrategyHelper"), + show: compute(({ form }) => { + return form.extra.showRunStrategy; + }), + }, + column: { + width: 100, + align: "left", + component: { + color: "auto", + }, + show: false, + }, + valueBuilder({ row }) { + if (row.extra) { + row.extra = yaml.load(row.extra); + } + }, + valueResolve({ row }) { + if (row.extra) { + row.extra = yaml.dump(row.extra); + } + }, + }, + disabled: { + title: t("certd.enableDisable"), + type: "dict-switch", + dict: dict({ + data: [ + { label: t("certd.enabled"), value: false, color: "success" }, + { label: t("certd.disabled"), value: true, color: "error" }, + ], + }), + form: { + title: t("certd.enableDisable"), + value: false, + }, + column: { + width: 120, + align: "center", + component: { + title: t("certd.clickToToggle"), + on: { + async click({ value, row }) { + Modal.confirm({ + title: t("certd.confirm"), + content: `${t("certd.confirmToggle")} ${!value ? t("certd.disable") : t("certd.enable")}?`, + onOk: async () => { + await api.SetDisabled({ + id: row.id, + name: row.name, + type: row.type, + disabled: !value, + }); + await crudExpose.doRefresh(); + }, + }); + }, + }, + }, + }, + }, + group: { + title: t("certd.pluginGroup"), + type: "dict-select", + dict: dict({ + url: "/pi/plugin/groupsList", + label: "title", + value: "key", + }), + form: { + rules: [{ required: true }], + show: compute(({ form }) => { + return form.pluginType === "deploy"; + }), + }, + column: { + width: 100, + align: "left", + component: { + color: "auto", + }, + }, + }, + createTime: { + title: t("certd.createTime"), + type: "datetime", + form: { + show: false, + }, + column: { + sorter: true, + width: 160, + align: "center", + }, + }, + updateTime: { + title: t("certd.updateTime"), + type: "datetime", + form: { + show: false, + }, + column: { + show: true, + }, + }, + }, + }, + }; } diff --git a/packages/ui/certd-client/src/views/sys/plugin/index.vue b/packages/ui/certd-client/src/views/sys/plugin/index.vue index 80e0c21c..c16ab966 100644 --- a/packages/ui/certd-client/src/views/sys/plugin/index.vue +++ b/packages/ui/certd-client/src/views/sys/plugin/index.vue @@ -1,58 +1,63 @@ - - - - 插件管理 - 自定义插件处于BETA测试版,后续可能会有破坏性变更 - - - - - - - - - - + + + + {{ t("certd.pluginManagement") }} + {{ t("certd.pluginBetaWarning") }} + + + + + + + + + + + diff --git a/packages/ui/certd-client/src/views/sys/settings/email/index.vue b/packages/ui/certd-client/src/views/sys/settings/email/index.vue index df834df8..9c6843d1 100644 --- a/packages/ui/certd-client/src/views/sys/settings/email/index.vue +++ b/packages/ui/certd-client/src/views/sys/settings/email/index.vue @@ -1,73 +1,86 @@ - - - - 邮件服务器设置 - 设置邮件发送服务器 - - + + + + {{ t('certd.emailServerSettings') }} + {{ t('certd.setEmailSendingServer') }} + + - - - - - - - + + + + + + + - - - + + + - - - - - - 如果是qq邮箱,需要到qq邮箱的设置里面申请授权码作为密码 - - - - - - - ssl和非ssl的smtp端口是不一样的,注意修改端口 - - - - + + + + + + {{ t('certd.qqEmailAuthCodeHelper') }} + + + + + + + {{ t('certd.sslPortNote') }} + + + + - - 保存 - - - - - - - - - 使用官方邮箱服务器直接发邮件,免除繁琐的配置 - - - - - - - - - 保存后再点击测试 - 发送失败???邮件配置帮助文档 - 您还可以试试使用官方邮件服务器↗↗↗↗↗↗↗↗ - - - 测试 - - - - + + {{ t('certd.save') }} + + + + + + + + + {{ t('certd.useOfficialEmailServerHelper') }} + + + + + + + + + {{ t('certd.saveBeforeTest') }} + {{ t('certd.sendFailHelpDoc') }}{{ + t('certd.emailConfigHelpDoc') }} + {{ t('certd.tryOfficialEmailServer') }} + + + {{ t('certd.test') + }} + + + + + diff --git a/packages/ui/certd-client/src/views/sys/settings/tabs/base.vue b/packages/ui/certd-client/src/views/sys/settings/tabs/base.vue index 1b6ec753..e1982e61 100644 --- a/packages/ui/certd-client/src/views/sys/settings/tabs/base.vue +++ b/packages/ui/certd-client/src/views/sys/settings/tabs/base.vue @@ -1,54 +1,59 @@ - - - - - - - - + + + + + + + + - - - - - - + + + + + + - - - 当某些网站被墙时可以配置 - + + + {{ t('certd.httpProxyHelper') }} + - - - - 测试 - - 一般这两个代理填一样的,保存后再测试 - + + + + {{ t('certd.testButton') }} + + {{ t('certd.httpsProxyHelper') }} + - - - 默认 - IPV4优先 - IPV6优先 - - 如果选择IPv6优先,需要在docker-compose.yaml中启用ipv6 - + + + {{ t('certd.default') }} + {{ t('certd.ipv4Priority') }} + {{ t('certd.ipv6Priority') }} + + {{ t('certd.dualStackNetworkHelper') }} + - - - 是否可以使用公共CNAME服务,如果禁用,且没有设置自定义CNAME服务,则无法使用CNAME代理方式申请证书 - + + + + - - 保存 - - - + + {{ t('certd.saveButton') + }} + + + + diff --git a/packages/ui/certd-client/src/views/sys/settings/tabs/register.vue b/packages/ui/certd-client/src/views/sys/settings/tabs/register.vue index 8f395eaa..a0464054 100644 --- a/packages/ui/certd-client/src/views/sys/settings/tabs/register.vue +++ b/packages/ui/certd-client/src/views/sys/settings/tabs/register.vue @@ -1,67 +1,78 @@ - - - - - - - - 0为不限制 - - - - - - - - - - 有效期内用户可正常使用,失效后流水线将被停用 - - - - - + + + + + + + + {{ t('certd.limitUserPipelineCountHelper') }} + + + + + + + + + + {{ t('certd.userValidityPeriodHelper') }} + + + + + - - - - - - 需要设置邮箱服务器 - - - - - - - - - - - 阿里云短信 - 易发云短信 - - - - - + + + + + + + {{ t('certd.emailServerSetup') }} + + + + + + + + + + + + {{ t('certd.aliyunSms') }} + {{ t('certd.yfySms') }} + + + + + - - - - 测试 - - 保存后再点击测试 - - - + + + + {{ + t('certd.testButton') }} + + {{ t('certd.saveThenTest') }} + + + - - 保存 - - - + + {{ t('certd.saveButton') + }} + + + diff --git a/packages/ui/certd-client/src/views/sys/settings/tabs/safe.vue b/packages/ui/certd-client/src/views/sys/settings/tabs/safe.vue index f850c9c9..5e3966f7 100644 --- a/packages/ui/certd-client/src/views/sys/settings/tabs/safe.vue +++ b/packages/ui/certd-client/src/views/sys/settings/tabs/safe.vue @@ -1,54 +1,63 @@ - - - 站点隐藏 - - - - + + + {{ t('certd.siteHide') }} + + + + - - 可以在平时关闭站点的可访问性,需要时再打开,增强站点安全性 - - 帮助说明 - - - - - - - - - - 站点被隐藏后,需要访问此URL解锁,才能正常访问 - - - - 请保存好此地址 - - - - 解除隐藏时需要输入密码,第一次需要设置密码,填写则重置密码 - - - - 多少分钟内无请求自动隐藏 - - - - 是否隐藏开放接口,是否放开/api/v1开头的接口 - - - 立即隐藏 - - - 保存 - - - + + {{ t('certd.siteHideDescription') }} + + {{ t('certd.helpDoc') }} + + + + + + + + + + {{ t('certd.siteHideUrlHelper') }} + + + + {{ t('certd.saveThisUrl') }} + + + + {{ t('certd.unlockPasswordHelper') }} + + + + {{ t('certd.autoHideTimeHelper') }} + + + + {{ t('certd.hideOpenApiHelper') }} + + + {{ + t('certd.hideImmediately') }} + + + {{ t('certd.save') + }} + + + + diff --git a/packages/ui/certd-client/src/views/sys/suite/product/crud.tsx b/packages/ui/certd-client/src/views/sys/suite/product/crud.tsx index 97e6a73c..1b8bb1e1 100644 --- a/packages/ui/certd-client/src/views/sys/suite/product/crud.tsx +++ b/packages/ui/certd-client/src/views/sys/suite/product/crud.tsx @@ -4,317 +4,333 @@ import SuiteValue from "./suite-value.vue"; import SuiteValueEdit from "./suite-value-edit.vue"; import PriceEdit from "./price-edit.vue"; import DurationPriceValue from "/@/views/sys/suite/product/duration-price-value.vue"; +import { useI18n } from "vue-i18n"; export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet { - const emit = context.emit; - const pageRequest = async (query: UserPageQuery): Promise => { - return await api.GetList(query); - }; - const editRequest = async ({ form, row }: EditReq) => { - form.id = row.id; - const res = await api.UpdateObj(form); - return res; - }; - const delRequest = async ({ row }: DelReq) => { - return await api.DelObj(row.id); - }; + const { t } = useI18n(); + const emit = context.emit; + const pageRequest = async (query: UserPageQuery): Promise => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }: EditReq) => { + form.id = row.id; + const res = await api.UpdateObj(form); + return res; + }; + const delRequest = async ({ row }: DelReq) => { + return await api.DelObj(row.id); + }; - const addRequest = async ({ form }: AddReq) => { - const res = await api.AddObj(form); - return res; - }; + const addRequest = async ({ form }: AddReq) => { + const res = await api.AddObj(form); + return res; + }; - return { - crudOptions: { - table: { - onRefreshed: () => { - emit("refreshed"); - } - }, - search: { - show: false - }, - request: { - pageRequest, - addRequest, - editRequest, - delRequest - }, - pagination: { - show: false, - pageSize: 999999 - }, - rowHandle: { - minWidth: 200, - fixed: "right" - }, - form: { - group: { - groups: { - base: { - header: "基础信息", - columns: ["title", "type", "disabled", "order", "supportBuy", "intro"] - }, - content: { - header: "套餐内容", - columns: ["content.maxDomainCount", "content.maxPipelineCount", "content.maxDeployCount", "content.maxMonitorCount"] - }, - price: { - header: "价格", - columns: ["durationPrices"] - } - } - } - }, - columns: { - // id: { - // title: "ID", - // key: "id", - // type: "number", - // column: { - // width: 100 - // }, - // form: { - // show: false - // } - // }, - title: { - title: "套餐名称", - type: "text", - search: { - show: true - }, - form: { - rules: [{ required: true, message: "此项必填" }] - }, - column: { - width: 200 - } - }, - type: { - title: "类型", - type: "dict-select", - editForm: { - component: { - disabled: true - } - }, - dict: dict({ - data: [ - { label: "套餐", value: "suite" }, - { label: "加量包", value: "addon" } - ] - }), - form: { - value: "suite", - rules: [{ required: true, message: "此项必填" }], - helper: "套餐:同一时间只有最新购买的一个生效\n加量包:可购买多个,购买后立即生效,不影响套餐\n套餐和加量包数量可叠加" - }, - column: { - width: 80, - align: "center" - }, - valueBuilder: ({ row }) => { - if (row.content) { - row.content = JSON.parse(row.content); - } - if (row.durationPrices) { - row.durationPrices = JSON.parse(row.durationPrices); - } - }, - valueResolve: ({ form }) => { - if (form.content) { - form.content = JSON.stringify(form.content); - } - if (form.durationPrices) { - form.durationPrices = JSON.stringify(form.durationPrices); - } - } - }, - "content.maxDomainCount": { - title: "域名数量", - type: "text", - form: { - key: ["content", "maxDomainCount"], - component: { - name: SuiteValueEdit, - vModel: "modelValue", - unit: "个" - }, - rules: [{ required: true, message: "此项必填" }] - }, - column: { - width: 100, - component: { - name: SuiteValue, - vModel: "modelValue", - unit: "个" - } - } - }, - "content.maxPipelineCount": { - title: "流水线数量", - type: "text", - form: { - key: ["content", "maxPipelineCount"], - component: { - name: SuiteValueEdit, - vModel: "modelValue", - unit: "条" - }, - rules: [{ required: true, message: "此项必填" }] - }, - column: { - width: 100, - component: { - name: SuiteValue, - vModel: "modelValue", - unit: "条" - } - } - }, - "content.maxDeployCount": { - title: "部署次数", - type: "text", - form: { - key: ["content", "maxDeployCount"], - component: { - name: SuiteValueEdit, - vModel: "modelValue", - unit: "次" - }, - rules: [{ required: true, message: "此项必填" }] - }, - column: { - width: 100, - component: { - name: SuiteValue, - vModel: "modelValue", - unit: "次" - } - } - }, - "content.maxMonitorCount": { - title: "证书监控数量", - type: "text", - form: { - key: ["content", "maxMonitorCount"], - component: { - name: SuiteValueEdit, - vModel: "modelValue", - unit: "个" - }, - rules: [{ required: true, message: "此项必填" }] - }, - column: { - width: 120, - component: { - name: SuiteValue, - vModel: "modelValue", - unit: "个" - } - } - }, - durationPrices: { - title: "时长及价格", - type: "text", - form: { - title: "选择时长", - component: { - name: PriceEdit, - vModel: "modelValue", - edit: true, - style: { - minHeight: "120px" - } - }, - col: { - span: 24 - }, - rules: [{ required: true, message: "此项必填" }] - }, - column: { - component: { - name: DurationPriceValue, - vModel: "modelValue" - }, - width: 350 - } - }, - supportBuy: { - title: "支持购买", - type: "dict-switch", - dict: dict({ - data: [ - { label: "支持购买", value: true, color: "success" }, - { label: "不能购买", value: false, color: "gray" } - ] - }), - form: { - value: true - }, - column: { - width: 120 - } - }, - disabled: { - title: "上下架", - type: "dict-radio", - dict: dict({ - data: [ - { value: false, label: "上架", color: "green" }, - { value: true, label: "下架", color: "gray" } - ] - }), - form: { - value: false - }, - column: { - width: 100 - } - }, - order: { - title: "排序", - type: "number", - form: { - helper: "越小越靠前", - value: 0 - }, - column: { - width: 100 - } - }, - intro: { - title: "说明", - type: "textarea", - column: { - width: 200 - } - }, - createTime: { - title: "创建时间", - type: "datetime", - form: { - show: false - }, - column: { - sorter: true, - width: 160, - align: "center" - } - }, - updateTime: { - title: "更新时间", - type: "datetime", - form: { - show: false - }, - column: { - show: true, - width: 160 - } - } - } - } - }; + return { + crudOptions: { + table: { + onRefreshed: () => { + emit("refreshed"); + } + }, + search: { + show: false + }, + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + pagination: { + show: false, + pageSize: 999999 + }, + rowHandle: { + minWidth: 200, + fixed: "right" + }, + form: { + group: { + groups: { + base: { + header: t('certd.basicInfo'), + columns: [ + t('certd.titlea'), + t('certd.type'), + t('certd.disabled'), + t('certd.ordera'), + t('certd.supportBuy'), + t('certd.intro') + ] + }, + content: { + header: t('certd.packageContent'), + columns: [ + t('certd.maxDomainCount'), + t('certd.maxPipelineCount'), + t('certd.maxDeployCount'), + t('certd.maxMonitorCount') + ] + }, + price: { + header: t('certd.price'), + columns: [ + t('certd.durationPrices') + ] + } + } + } + } + columns: { + // id: { + // title: "ID", + // key: "id", + // type: "number", + // column: { + // width: 100 + // }, + // form: { + // show: false + // } + // }, + title: { + title: t('certd.packageName'), + type: "text", + search: { + show: true + }, + form: { + rules: [{ required: true, message: t('certd.requiredField') }] + }, + column: { + width: 200 + } + }, + type: { + title: t('certd.type'), + type: "dict-select", + editForm: { + component: { + disabled: true + } + }, + dict: dict({ + data: [ + { label: t('certd.suite'), value: "suite" }, + { label: t('certd.addon'), value: "addon" } + ] + }), + form: { + value: "suite", + rules: [{ required: true, message: t('certd.requiredField') }], + helper: t('certd.typeHelper') + }, + column: { + width: 80, + align: "center" + }, + valueBuilder: ({ row }) => { + if (row.content) { + row.content = JSON.parse(row.content); + } + if (row.durationPrices) { + row.durationPrices = JSON.parse(row.durationPrices); + } + }, + valueResolve: ({ form }) => { + if (form.content) { + form.content = JSON.stringify(form.content); + } + if (form.durationPrices) { + form.durationPrices = JSON.stringify(form.durationPrices); + } + } + }, + "content.maxDomainCount": { + title: t('certd.domainCount'), + type: "text", + form: { + key: ["content", "maxDomainCount"], + component: { + name: SuiteValueEdit, + vModel: "modelValue", + unit: t('certd.unitCount') + }, + rules: [{ required: true, message: t('certd.requiredField') }] + }, + column: { + width: 100, + component: { + name: SuiteValue, + vModel: "modelValue", + unit: t('certd.unitCount') + } + } + }, + "content.maxPipelineCount": { + title: t('certd.pipelineCount'), + type: "text", + form: { + key: ["content", "maxPipelineCount"], + component: { + name: SuiteValueEdit, + vModel: "modelValue", + unit: t('certd.unitPipeline') + }, + rules: [{ required: true, message: t('certd.requiredField') }] + }, + column: { + width: 100, + component: { + name: SuiteValue, + vModel: "modelValue", + unit: t('certd.unitPipeline') + } + } + }, + "content.maxDeployCount": { + title: t('certd.deployCount'), + type: "text", + form: { + key: ["content", "maxDeployCount"], + component: { + name: SuiteValueEdit, + vModel: "modelValue", + unit: t('certd.unitDeploy') + }, + rules: [{ required: true, message: t('certd.requiredField') }] + }, + column: { + width: 100, + component: { + name: SuiteValue, + vModel: "modelValue", + unit: t('certd.unitDeploy') + } + } + }, + "content.maxMonitorCount": { + title: t('certd.monitorCount'), + type: "text", + form: { + key: ["content", "maxMonitorCount"], + component: { + name: SuiteValueEdit, + vModel: "modelValue", + unit: t('certd.unitCount') + }, + rules: [{ required: true, message: t('certd.requiredField') }] + }, + column: { + width: 120, + component: { + name: SuiteValue, + vModel: "modelValue", + unit: t('certd.unitCount') + } + } + }, + durationPrices: { + title: t('certd.durationPriceTitle'), + type: "text", + form: { + title: t('certd.selectDuration'), + component: { + name: PriceEdit, + vModel: "modelValue", + edit: true, + style: { + minHeight: "120px" + } + }, + col: { + span: 24 + }, + rules: [{ required: true, message: t('certd.requiredField') }] + }, + column: { + component: { + name: DurationPriceValue, + vModel: "modelValue" + }, + width: 350 + } + }, + supportBuy: { + title: t('certd.supportBuy'), + type: "dict-switch", + dict: dict({ + data: [ + { label: t('certd.supportPurchase'), value: true, color: "success" }, + { label: t('certd.cannotPurchase'), value: false, color: "gray" } + ] + }), + form: { + value: true + }, + column: { + width: 120 + } + }, + disabled: { + title: t('certd.shelfStatus'), + type: "dict-radio", + dict: dict({ + data: [ + { value: false, label: t('certd.onShelf'), color: "green" }, + { value: true, label: t('certd.offShelf'), color: "gray" } + ] + }), + form: { + value: false + }, + column: { + width: 100 + } + }, + order: { + title: t('certd.ordera'), + type: "number", + form: { + helper: t('certd.orderHelper'), + value: 0 + }, + column: { + width: 100 + } + }, + intro: { + title: t('certd.description'), + type: "textarea", + column: { + width: 200 + } + }, + createTime: { + title: t('certd.createTime'), + type: "datetime", + form: { + show: false + }, + column: { + sorter: true, + width: 160, + align: "center" + } + }, + updateTime: { + title: t('certd.updateTime'), + type: "datetime", + form: { + show: false + }, + column: { + show: true, + width: 160 + } + } + } + } + }; } diff --git a/packages/ui/certd-client/src/views/sys/suite/user-suite/crud.tsx b/packages/ui/certd-client/src/views/sys/suite/user-suite/crud.tsx index 0ea1a77a..c5b7058c 100644 --- a/packages/ui/certd-client/src/views/sys/suite/user-suite/crud.tsx +++ b/packages/ui/certd-client/src/views/sys/suite/user-suite/crud.tsx @@ -8,392 +8,397 @@ import createCrudOptionsUser from "/@/views/sys/authority/user/crud"; import UserSuiteStatus from "/@/views/certd/suite/mine/user-suite-status.vue"; import SuiteDurationSelector from "../setting/suite-duration-selector.vue"; import dayjs from "dayjs"; +import { useI18n } from "vue-i18n"; + + export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet { - const api = sysUserSuiteApi; - const pageRequest = async (query: UserPageQuery): Promise => { - return await api.GetList(query); - }; - const editRequest = async (req: EditReq) => { - const { form, row } = req; - form.id = row.id; - const res = await api.UpdateObj(form); - return res; - }; - const delRequest = async (req: DelReq) => { - const { row } = req; - return await api.DelObj(row.id); - }; + const { t } = useI18n(); + const api = sysUserSuiteApi; + const pageRequest = async (query: UserPageQuery): Promise => { + return await api.GetList(query); + }; + const editRequest = async (req: EditReq) => { + const { form, row } = req; + form.id = row.id; + const res = await api.UpdateObj(form); + return res; + }; + const delRequest = async (req: DelReq) => { + const { row } = req; + return await api.DelObj(row.id); + }; - const addRequest = async (req: AddReq) => { - const { form } = req; - const res = await api.PresentSuite(form); - return res; - }; + const addRequest = async (req: AddReq) => { + const { form } = req; + const res = await api.PresentSuite(form); + return res; + }; - const router = useRouter(); + const router = useRouter(); - return { - crudOptions: { - request: { - pageRequest, - addRequest, - editRequest, - delRequest - }, - form: { - labelCol: { - //固定label宽度 - span: null, - style: { - width: "100px" - } - }, - col: { - span: 22 - }, - wrapper: { - width: 600 - } - }, - actionbar: { - buttons: { - add: { text: "赠送套餐" } - } - }, - toolbar: { show: false }, - rowHandle: { - width: 200, - fixed: "right", - buttons: { - view: { show: true }, - copy: { show: false }, - edit: { show: false }, - remove: { show: true } - // continue:{ - // text:"续期", - // type:"link", - // click(){ - // console.log("续期"); - // } - // } - } - }, - columns: { - id: { - title: "ID", - key: "id", - type: "number", - search: { - show: false - }, - column: { - width: 100, - editable: { - disabled: true - } - }, - form: { - show: false - } - }, - title: { - title: "套餐名称", - type: "text", - search: { - show: true - }, - form: { - show: false - }, - column: { - width: 200 - } - }, - userId: { - title: "用户", - type: "table-select", - search: { - show: true - }, - dict: dict({ - async getNodesByValues(ids: number[]) { - return await api.GetSimpleUserByIds(ids); - }, - value: "id", - label: "nickName" - }), - form: { - component: { - crossPage: true, - multiple: false, - select: { - placeholder: "点击选择" - }, - createCrudOptions: createCrudOptionsUser - // crudOptionsOverride: crudOptionsOverride - } - } - }, - //赠送 - presentSuiteId: { - title: "赠送套餐", - type: "dict-select", - column: { show: false }, - addForm: { - show: true, - component: { - name: SuiteDurationSelector, - vModel: "modelValue" - }, - rules: [ - { - validator: async (rule, value) => { - if (value && value.productId) { - return true; - } - throw new Error("请选择套餐"); - } - } - ] - }, - valueResolve({ form, value }) { - if (value && value.productId) { - form.productId = value.productId; - form.duration = value.duration; - } - }, - form: { show: false } - }, - productType: { - title: "类型", - type: "dict-select", - editForm: { - component: { - disabled: true - } - }, - dict: dict({ - data: [ - { label: "套餐", value: "suite", color: "green" }, - { label: "加量包", value: "addon", color: "blue" } - ] - }), - form: { - show: false - }, - column: { - width: 80, - align: "center" - }, - valueBuilder: ({ row }) => { - if (row.content) { - row.content = JSON.parse(row.content); - } - }, - valueResolve: ({ form }) => { - if (form.content) { - form.content = JSON.stringify(form.content); - } - } - }, - "content.maxDomainCount": { - title: "域名数量", - type: "text", - form: { - show: false, - key: ["content", "maxDomainCount"], - component: { - name: SuiteValueEdit, - vModel: "modelValue", - unit: "个" - }, - rules: [{ required: true, message: "此项必填" }] - }, - column: { - width: 100, - component: { - name: SuiteValue, - vModel: "modelValue", - unit: "个" - }, - align: "center" - } - }, - "content.maxPipelineCount": { - title: "流水线数量", - type: "text", - form: { - show: false, - key: ["content", "maxPipelineCount"], - component: { - name: SuiteValueEdit, - vModel: "modelValue", - unit: "条" - }, - rules: [{ required: true, message: "此项必填" }] - }, - column: { - width: 100, - component: { - name: SuiteValue, - vModel: "modelValue", - unit: "条" - }, - align: "center" - } - }, - "content.maxDeployCount": { - title: "部署次数", - type: "text", - form: { - show: false, - key: ["content", "maxDeployCount"], - component: { - name: SuiteValueEdit, - vModel: "modelValue", - unit: "次" - }, - rules: [{ required: true, message: "此项必填" }] - }, - column: { - width: 100, - component: { - name: SuiteValue, - vModel: "modelValue", - unit: "次", - used: compute(({ row }) => { - return row.deployCountUsed; - }) - }, - align: "center" - } - }, - "content.maxMonitorCount": { - title: "证书监控数量", - type: "text", - form: { - show: false, - key: ["content", "maxMonitorCount"], - component: { - name: SuiteValueEdit, - vModel: "modelValue", - unit: "个" - }, - rules: [{ required: true, message: "此项必填" }] - }, - column: { - width: 120, - component: { - name: SuiteValue, - vModel: "modelValue", - unit: "个" - }, - align: "center" - } - }, - duration: { - title: "时长", - type: "text", - form: { show: false }, - column: { - component: { - name: DurationValue, - vModel: "modelValue" - }, - width: 100, - align: "center" - } - }, - status: { - title: "状态", - type: "text", - form: { show: false }, - column: { - width: 100, - align: "center", - component: { - name: UserSuiteStatus, - userSuite: compute(({ row }) => { - return row; - }) - }, - conditionalRender: { - match() { - return false; - } - } - } - }, - activeTime: { - title: "激活时间", - type: "date", - column: { - width: 150 - }, - form: { - show: false - } - }, - expiresTime: { - title: "过期时间", - type: "date", - form: { - show: false - }, - column: { - width: 150, - component: { - name: "expires-time-text", - vModel: "value", - mode: "tag", - title: compute(({ value }) => { - return dayjs(value).format("YYYY-MM-DD HH:mm:ss"); - }) - } - } - }, - isPresent: { - title: "是否赠送", - type: "dict-switch", - dict: dict({ - data: [ - { label: "是", value: true, color: "success" }, - { label: "否", value: false, color: "blue" } - ] - }), - form: { - value: true, - show: false - }, - column: { - width: 100, - align: "center" - } - }, - createTime: { - title: "创建时间", - type: "datetime", - form: { - show: false - }, - column: { - sorter: true, - width: 160, - align: "center" - } - }, - updateTime: { - title: "更新时间", - type: "datetime", - form: { - show: false - }, - column: { - show: true, - width: 160 - } - } - } - } - }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest + }, + form: { + labelCol: { + //固定label宽度 + span: null, + style: { + width: "100px" + } + }, + col: { + span: 22 + }, + wrapper: { + width: 600 + } + }, + actionbar: { + buttons: { + add: { text: t('certd.gift_package') } + } + }, + + toolbar: { show: false }, + rowHandle: { + width: 200, + fixed: "right", + buttons: { + view: { show: true }, + copy: { show: false }, + edit: { show: false }, + remove: { show: true } + // continue:{ + // text:"续期", + // type:"link", + // click(){ + // console.log("续期"); + // } + // } + } + }, + columns: { + id: { + title: "ID", + key: "id", + type: "number", + search: { + show: false + }, + column: { + width: 100, + editable: { + disabled: true + } + }, + form: { + show: false + } + }, + title: { + title: t('certd.package_name'), + type: "text", + search: { + show: true + }, + form: { + show: false + }, + column: { + width: 200 + } + }, + userId: { + title: t('certd.usera'), + type: "table-select", + search: { + show: true + }, + dict: dict({ + async getNodesByValues(ids: number[]) { + return await api.GetSimpleUserByIds(ids); + }, + value: "id", + label: "nickName" + }), + form: { + component: { + crossPage: true, + multiple: false, + select: { + placeholder: t('certd.click_to_select') + }, + createCrudOptions: createCrudOptionsUser + // crudOptionsOverride: crudOptionsOverride + } + } + }, + //赠送 + presentSuiteId: { + title: t('certd.gift_package'), + type: "dict-select", + column: { show: false }, + addForm: { + show: true, + component: { + name: SuiteDurationSelector, + vModel: "modelValue" + }, + rules: [ + { + validator: async (rule, value) => { + if (value && value.productId) { + return true; + } + throw new Error(t('certd.please_select_package')); + } + } + ] + }, + valueResolve({ form, value }) { + if (value && value.productId) { + form.productId = value.productId; + form.duration = value.duration; + } + }, + form: { show: false } + }, + productType: { + title: t('certd.type'), + type: "dict-select", + editForm: { + component: { + disabled: true + } + }, + dict: dict({ + data: [ + { label: t('certd.package'), value: "suite", color: "green" }, + { label: t('certd.addon_package'), value: "addon", color: "blue" } + ] + }), + form: { + show: false + }, + column: { + width: 80, + align: "center" + }, + valueBuilder: ({ row }) => { + if (row.content) { + row.content = JSON.parse(row.content); + } + }, + valueResolve: ({ form }) => { + if (form.content) { + form.content = JSON.stringify(form.content); + } + } + }, + "content.maxDomainCount": { + title: t('certd.domain_count'), + type: "text", + form: { + show: false, + key: ["content", "maxDomainCount"], + component: { + name: SuiteValueEdit, + vModel: "modelValue", + unit: t('certd.unit_count') + }, + rules: [{ required: true, message: t('certd.field_required') }] + }, + column: { + width: 100, + component: { + name: SuiteValue, + vModel: "modelValue", + unit: t('certd.unit_count') + }, + align: "center" + } + }, + "content.maxPipelineCount": { + title: t('certd.pipeline_count'), + type: "text", + form: { + show: false, + key: ["content", "maxPipelineCount"], + component: { + name: SuiteValueEdit, + vModel: "modelValue", + unit: t('certd.unit_item') + }, + rules: [{ required: true, message: t('certd.field_required') }] + }, + column: { + width: 100, + component: { + name: SuiteValue, + vModel: "modelValue", + unit: t('certd.unit_item') + }, + align: "center" + } + }, + "content.maxDeployCount": { + title: t('certd.deploy_count'), + type: "text", + form: { + show: false, + key: ["content", "maxDeployCount"], + component: { + name: SuiteValueEdit, + vModel: "modelValue", + unit: t('certd.unit_times') + }, + rules: [{ required: true, message: t('certd.field_required') }] + }, + column: { + width: 100, + component: { + name: SuiteValue, + vModel: "modelValue", + unit: t('certd.unit_times'), + used: compute(({ row }) => { + return row.deployCountUsed; + }) + }, + align: "center" + } + }, + "content.maxMonitorCount": { + title: t('certd.monitor_count'), + type: "text", + form: { + show: false, + key: ["content", "maxMonitorCount"], + component: { + name: SuiteValueEdit, + vModel: "modelValue", + unit: t('certd.unit_count') + }, + rules: [{ required: true, message: t('certd.field_required') }] + }, + column: { + width: 120, + component: { + name: SuiteValue, + vModel: "modelValue", + unit: t('certd.unit_count') + }, + align: "center" + } + }, + duration: { + title: t('certd.duration'), + type: "text", + form: { show: false }, + column: { + component: { + name: DurationValue, + vModel: "modelValue" + }, + width: 100, + align: "center" + } + }, + status: { + title: t('certd.status'), + type: "text", + form: { show: false }, + column: { + width: 100, + align: "center", + component: { + name: UserSuiteStatus, + userSuite: compute(({ row }) => { + return row; + }) + }, + conditionalRender: { + match() { + return false; + } + } + } + }, + activeTime: { + title: t('certd.active_time'), + type: "date", + column: { + width: 150 + }, + form: { + show: false + } + }, + expiresTime: { + title: t('certd.expires_time'), + type: "date", + form: { + show: false + }, + column: { + width: 150, + component: { + name: "expires-time-text", + vModel: "value", + mode: "tag", + title: compute(({ value }) => { + return dayjs(value).format("YYYY-MM-DD HH:mm:ss"); + }) + } + } + }, + isPresent: { + title: t('certd.is_present'), + type: "dict-switch", + dict: dict({ + data: [ + { label: t('certd.is_present_yes'), value: true, color: "success" }, + { label: t('certd.is_present_no'), value: false, color: "blue" } + ] + }), + form: { + value: true, + show: false + }, + column: { + width: 100, + align: "center" + } + }, + createTime: { + title: t('certd.create_time'), + type: "datetime", + form: { + show: false + }, + column: { + sorter: true, + width: 160, + align: "center" + } + }, + updateTime: { + title: t('certd.update_time'), + type: "datetime", + form: { + show: false + }, + column: { + show: true, + width: 160 + } + } + } + } + }; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index eac58e7b..25727fb1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -46,7 +46,7 @@ importers: packages/core/acme-client: dependencies: '@certd/basic': - specifier: ^1.35.2 + specifier: ^1.35.5 version: link:../basic '@peculiar/x509': specifier: ^1.11.0 @@ -207,10 +207,10 @@ importers: packages/core/pipeline: dependencies: '@certd/basic': - specifier: ^1.35.2 + specifier: ^1.35.5 version: link:../basic '@certd/plus-core': - specifier: ^1.35.2 + specifier: ^1.35.5 version: link:../../pro/plus-core dayjs: specifier: ^1.11.7 @@ -415,7 +415,7 @@ importers: packages/libs/lib-k8s: dependencies: '@certd/basic': - specifier: ^1.35.2 + specifier: ^1.35.5 version: link:../../core/basic '@kubernetes/client-node': specifier: 0.21.0 @@ -455,16 +455,16 @@ importers: packages/libs/lib-server: dependencies: '@certd/acme-client': - specifier: ^1.35.2 + specifier: ^1.35.5 version: link:../../core/acme-client '@certd/basic': - specifier: ^1.35.2 + specifier: ^1.35.5 version: link:../../core/basic '@certd/pipeline': - specifier: ^1.35.2 + specifier: ^1.35.5 version: link:../../core/pipeline '@certd/plus-core': - specifier: ^1.35.2 + specifier: ^1.35.5 version: link:../../pro/plus-core '@midwayjs/cache': specifier: ~3.14.0 @@ -607,16 +607,16 @@ importers: packages/plugins/plugin-cert: dependencies: '@certd/acme-client': - specifier: ^1.35.2 + specifier: ^1.35.5 version: link:../../core/acme-client '@certd/basic': - specifier: ^1.35.2 + specifier: ^1.35.5 version: link:../../core/basic '@certd/pipeline': - specifier: ^1.35.2 + specifier: ^1.35.5 version: link:../../core/pipeline '@certd/plugin-lib': - specifier: ^1.35.2 + specifier: ^1.35.5 version: link:../plugin-lib '@google-cloud/publicca': specifier: ^1.3.0 @@ -698,10 +698,10 @@ importers: specifier: ^3.787.0 version: 3.810.0(aws-crt@1.26.2) '@certd/basic': - specifier: ^1.35.2 + specifier: ^1.35.5 version: link:../../core/basic '@certd/pipeline': - specifier: ^1.35.2 + specifier: ^1.35.5 version: link:../../core/pipeline '@kubernetes/client-node': specifier: 0.21.0 @@ -789,19 +789,19 @@ importers: packages/pro/commercial-core: dependencies: '@certd/basic': - specifier: ^1.35.2 + specifier: ^1.35.5 version: link:../../core/basic '@certd/lib-server': - specifier: ^1.35.2 + specifier: ^1.35.5 version: link:../../libs/lib-server '@certd/pipeline': - specifier: ^1.35.2 + specifier: ^1.35.5 version: link:../../core/pipeline '@certd/plugin-plus': - specifier: ^1.35.2 + specifier: ^1.35.5 version: link:../plugin-plus '@certd/plus-core': - specifier: ^1.35.2 + specifier: ^1.35.5 version: link:../plus-core '@midwayjs/core': specifier: ~3.20.3 @@ -886,22 +886,22 @@ importers: specifier: ^1.0.2 version: 1.0.3 '@certd/basic': - specifier: ^1.35.2 + specifier: ^1.35.5 version: link:../../core/basic '@certd/lib-k8s': - specifier: ^1.35.2 + specifier: ^1.35.5 version: link:../../libs/lib-k8s '@certd/pipeline': - specifier: ^1.35.2 + specifier: ^1.35.5 version: link:../../core/pipeline '@certd/plugin-cert': - specifier: ^1.35.2 + specifier: ^1.35.5 version: link:../../plugins/plugin-cert '@certd/plugin-lib': - specifier: ^1.35.2 + specifier: ^1.35.5 version: link:../../plugins/plugin-lib '@certd/plus-core': - specifier: ^1.35.2 + specifier: ^1.35.5 version: link:../plus-core ali-oss: specifier: ^6.21.0 @@ -1004,7 +1004,7 @@ importers: packages/pro/plus-core: dependencies: '@certd/basic': - specifier: ^1.35.2 + specifier: ^1.35.5 version: link:../../core/basic dayjs: specifier: ^1.11.7 @@ -1294,10 +1294,10 @@ importers: version: 0.1.3(zod@3.24.4) devDependencies: '@certd/lib-iframe': - specifier: ^1.35.2 + specifier: ^1.35.5 version: link:../../libs/lib-iframe '@certd/pipeline': - specifier: ^1.35.2 + specifier: ^1.35.5 version: link:../../core/pipeline '@rollup/plugin-commonjs': specifier: ^25.0.7 @@ -1480,46 +1480,46 @@ importers: specifier: ^3.705.0 version: 3.810.0(aws-crt@1.26.2) '@certd/acme-client': - specifier: ^1.35.2 + specifier: ^1.35.5 version: link:../../core/acme-client '@certd/basic': - specifier: ^1.35.2 + specifier: ^1.35.5 version: link:../../core/basic '@certd/commercial-core': - specifier: ^1.35.2 + specifier: ^1.35.5 version: link:../../pro/commercial-core '@certd/cv4pve-api-javascript': specifier: ^8.4.1 version: 8.4.1 '@certd/jdcloud': - specifier: ^1.35.2 + specifier: ^1.35.5 version: link:../../libs/lib-jdcloud '@certd/lib-huawei': - specifier: ^1.35.2 + specifier: ^1.35.5 version: link:../../libs/lib-huawei '@certd/lib-k8s': - specifier: ^1.35.2 + specifier: ^1.35.5 version: link:../../libs/lib-k8s '@certd/lib-server': - specifier: ^1.35.2 + specifier: ^1.35.5 version: link:../../libs/lib-server '@certd/midway-flyway-js': - specifier: ^1.35.2 + specifier: ^1.35.5 version: link:../../libs/midway-flyway-js '@certd/pipeline': - specifier: ^1.35.2 + specifier: ^1.35.5 version: link:../../core/pipeline '@certd/plugin-cert': - specifier: ^1.35.2 + specifier: ^1.35.5 version: link:../../plugins/plugin-cert '@certd/plugin-lib': - specifier: ^1.35.2 + specifier: ^1.35.5 version: link:../../plugins/plugin-lib '@certd/plugin-plus': - specifier: ^1.35.2 + specifier: ^1.35.5 version: link:../../pro/plugin-plus '@certd/plus-core': - specifier: ^1.35.2 + specifier: ^1.35.5 version: link:../../pro/plus-core '@huaweicloud/huaweicloud-sdk-cdn': specifier: ^3.1.120