perf: 优化中英文翻译与切换

pull/453/head
xiaojunnuo 2025-06-28 23:57:01 +08:00
parent 082f47663d
commit acaa8b1731
43 changed files with 4121 additions and 4175 deletions

View File

@ -1,5 +1,5 @@
<template> <template>
<AConfigProvider :locale="locale" :theme="tokenTheme"> <AConfigProvider :locale="antdvLocale" :theme="tokenTheme">
<FsFormProvider> <FsFormProvider>
<contextHolder /> <contextHolder />
<router-view /> <router-view />
@ -8,24 +8,22 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, onMounted, provide, ref } from "vue"; import { computed, provide, ref } from "vue";
import "dayjs/locale/zh-cn"; import { preferences, usePreferences } from "/@/vben/preferences";
import "dayjs/locale/en";
import dayjs from "dayjs";
import { usePreferences, preferences } from "/@/vben/preferences";
import { useAntdDesignTokens } from "/@/vben/hooks"; import { useAntdDesignTokens } from "/@/vben/hooks";
import { theme } from "ant-design-vue"; import { Modal, theme } from "ant-design-vue";
import AConfigProvider from "ant-design-vue/es/config-provider"; import AConfigProvider from "ant-design-vue/es/config-provider";
import { Modal } from "ant-design-vue"; import { antdvLocale } from "./locales/antdv";
import MaxKBChat from "/@/components/ai/index.vue"; import { setI18nLanguage } from "/@/locales";
import { util } from "/@/utils";
import { useSettingStore } from "/@/store/settings";
defineOptions({ defineOptions({
name: "App", name: "App",
}); });
const [modal, contextHolder] = Modal.useModal(); const [modal, contextHolder] = Modal.useModal();
provide("modal", modal); provide("modal", modal);
const locale = preferences.app.locale;
setI18nLanguage(locale);
const { isDark } = usePreferences(); const { isDark } = usePreferences();
const { tokens } = useAntdDesignTokens(); const { tokens } = useAntdDesignTokens();
@ -43,5 +41,4 @@ const tokenTheme = computed(() => {
token: tokens, token: tokens,
}; };
}); });
</script> </script>

View File

@ -17,95 +17,91 @@ const userStore = useUserStore();
const router = useRouter(); const router = useRouter();
const menus = computed(() => [ const menus = computed(() => [
{ {
handler: () => { handler: () => {
router.push("/certd/mine/user-profile"); router.push("/certd/mine/user-profile");
}, },
icon: "fa-solid:book", icon: "fa-solid:book",
text: t("certd.accountInfo"), text: t("certd.accountInfo"),
}, },
{ {
handler: () => { handler: () => {
router.push("/certd/mine/security"); router.push("/certd/mine/security");
}, },
icon: "fluent:shield-keyhole-16-regular", icon: "fluent:shield-keyhole-16-regular",
text: t("certd.securitySettings"), text: t("certd.securitySettings"),
}, },
]); ]);
const avatar = computed(() => { const avatar = computed(() => {
const avt = userStore.getUserInfo?.avatar; const avt = userStore.getUserInfo?.avatar;
return avt ? `/api/basic/file/download?key=${avt}` : ""; return avt ? `/api/basic/file/download?key=${avt}` : "";
}); });
async function handleLogout() { async function handleLogout() {
await userStore.logout(true); await userStore.logout(true);
} }
const settingStore = useSettingStore(); const settingStore = useSettingStore();
const sysPublic = computed(() => { const sysPublic = computed(() => {
return settingStore.sysPublic; return settingStore.sysPublic;
}); });
const siteInfo = computed(() => { const siteInfo = computed(() => {
return settingStore.siteInfo; return settingStore.siteInfo;
}); });
onErrorCaptured(e => { onErrorCaptured(e => {
console.error("ErrorCaptured:", e); console.error("ErrorCaptured:", e);
// notification.error({ message: e.message }); // notification.error({ message: e.message });
// //
return false; return false;
}); });
onMounted(async () => { onMounted(async () => {
await settingStore.checkUrlBound(); await settingStore.checkUrlBound();
}); });
function goGithub() { function goGithub() {
window.open("https://github.com/certd/certd"); window.open("https://github.com/certd/certd");
} }
const settingsStore = useSettingStore(); const settingsStore = useSettingStore();
const chatBox = ref(); const chatBox = ref();
const openChat = (q: string) => { const openChat = (q: string) => {
chatBox.value.openChat({ q }); chatBox.value.openChat({ q });
}; };
provide("fn:ai.open", openChat); provide("fn:ai.open", openChat);
</script> </script>
<template> <template>
<BasicLayout @clear-preferences-and-logout="handleLogout"> <BasicLayout @clear-preferences-and-logout="handleLogout">
<template #user-dropdown> <template #user-dropdown>
<UserDropdown :avatar="avatar" :menus="menus" <UserDropdown :avatar="avatar" :menus="menus" :text="userStore.userInfo?.nickName || userStore.userInfo?.username" description="" tag-text="" @logout="handleLogout" />
:text="userStore.userInfo?.nickName || userStore.userInfo?.username" description="" tag-text="" </template>
@logout="handleLogout" /> <template #lock-screen>
</template> <LockScreen :avatar @to-login="handleLogout" />
<template #lock-screen> </template>
<LockScreen :avatar @to-login="handleLogout" /> <template #header-right-0>
</template> <div v-if="!settingStore.isComm" class="hover:bg-accent ml-1 mr-2 cursor-pointer rounded-full hidden md:block">
<template #header-right-0> <tutorial-button class="flex-center header-btn" />
<div v-if="!settingStore.isComm" </div>
class="hover:bg-accent ml-1 mr-2 cursor-pointer rounded-full hidden md:block"> <div class="hover:bg-accent ml-1 mr-2 cursor-pointer rounded-full">
<tutorial-button class="flex-center header-btn" /> <vip-button class="flex-center header-btn" mode="nav" />
</div> </div>
<div class="hover:bg-accent ml-1 mr-2 cursor-pointer rounded-full"> <div v-if="!settingStore.isComm" class="hover:bg-accent ml-1 mr-2 cursor-pointer rounded-full">
<vip-button class="flex-center header-btn" mode="nav" /> <fs-button shape="circle" type="text" icon="ion:logo-github" :text="null" @click="goGithub" />
</div> </div>
<div v-if="!settingStore.isComm" class="hover:bg-accent ml-1 mr-2 cursor-pointer rounded-full"> </template>
<fs-button shape="circle" type="text" icon="ion:logo-github" :text="null" @click="goGithub" /> <template #footer>
</div> <PageFooter></PageFooter>
</template> <MaxKBChat v-if="settingsStore.sysPublic.aiChatEnabled !== false" ref="chatBox" />
<template #footer> </template>
<PageFooter></PageFooter> </BasicLayout>
<MaxKBChat v-if="settingsStore.sysPublic.aiChatEnabled !== false" ref="chatBox" />
</template>
</BasicLayout>
</template> </template>
<style lang="less"> <style lang="less">
.header-btn { .header-btn {
font-size: 14px; font-size: 14px;
padding: 5px; padding: 5px;
} }
</style> </style>

View File

@ -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;
}
}

View File

@ -1,24 +1,24 @@
import type { App } from "vue"; import type { App } from "vue";
import type { Locale } from "vue-i18n"; import type { Locale } from "vue-i18n";
import { setAntdvLocale } from "./antdv";
import type { ImportLocaleFn, LoadMessageFn, LocaleSetupOptions, SupportedLanguagesType } from "./typing"; import type { ImportLocaleFn, LoadMessageFn, LocaleSetupOptions, SupportedLanguagesType } from "./typing";
import { unref } from "vue"; import { unref } from "vue";
import { createI18n } from "vue-i18n"; import { createI18n } from "vue-i18n";
import en_US from './langs/en-US/index'; import en_US from "./langs/en-US/index";
import zh_CH from './langs/zh-CN/index'; import zh_CN from "./langs/zh-CN/index";
import { useSimpleLocale } from "/@/vben/composables"; import { useSimpleLocale } from "/@/vben/composables";
const i18n = createI18n({ const i18n = createI18n({
globalInjection: true, globalInjection: true,
legacy: false, legacy: false,
fallbackLocale: 'en_US', fallbackLocale: "en-US",
locale: 'en_US', locale: "en-US",
messages: { messages: {
zh_CH: zh_CH, "zh-CN": zh_CN,
en_US: en_US "en-US": en_US,
} },
}); });
const modules = import.meta.glob("./langs/**/*.json"); const modules = import.meta.glob("./langs/**/*.json");
@ -33,15 +33,15 @@ let loadMessages: LoadMessageFn;
* @param modules * @param modules
*/ */
function loadLocalesMap(modules: Record<string, () => Promise<unknown>>) { function loadLocalesMap(modules: Record<string, () => Promise<unknown>>) {
const localesMap: Record<Locale, ImportLocaleFn> = {}; const localesMap: Record<Locale, ImportLocaleFn> = {};
for (const [path, loadLocale] of Object.entries(modules)) { for (const [path, loadLocale] of Object.entries(modules)) {
const key = path.match(/([\w-]*)\.(json)/)?.[1]; const key = path.match(/([\w-]*)\.(json)/)?.[1];
if (key) { if (key) {
localesMap[key] = loadLocale as ImportLocaleFn; localesMap[key] = loadLocale as ImportLocaleFn;
} }
} }
return localesMap; return localesMap;
} }
/** /**
@ -51,37 +51,37 @@ function loadLocalesMap(modules: Record<string, () => Promise<unknown>>) {
* @returns A map of locales to their corresponding import functions * @returns A map of locales to their corresponding import functions
*/ */
function loadLocalesMapFromDir(regexp: RegExp, modules: Record<string, () => Promise<unknown>>): Record<Locale, ImportLocaleFn> { function loadLocalesMapFromDir(regexp: RegExp, modules: Record<string, () => Promise<unknown>>): Record<Locale, ImportLocaleFn> {
const localesRaw: Record<Locale, Record<string, () => Promise<unknown>>> = {}; const localesRaw: Record<Locale, Record<string, () => Promise<unknown>>> = {};
const localesMap: Record<Locale, ImportLocaleFn> = {}; const localesMap: Record<Locale, ImportLocaleFn> = {};
// Iterate over the modules to extract language and file names // Iterate over the modules to extract language and file names
for (const path in modules) { for (const path in modules) {
const match = path.match(regexp); const match = path.match(regexp);
if (match) { if (match) {
const [_, locale, fileName] = match; const [_, locale, fileName] = match;
if (locale && fileName) { if (locale && fileName) {
if (!localesRaw[locale]) { if (!localesRaw[locale]) {
localesRaw[locale] = {}; localesRaw[locale] = {};
} }
if (modules[path]) { if (modules[path]) {
localesRaw[locale][fileName] = modules[path]; localesRaw[locale][fileName] = modules[path];
} }
} }
} }
} }
// Convert raw locale data into async import functions // Convert raw locale data into async import functions
for (const [locale, files] of Object.entries(localesRaw)) { for (const [locale, files] of Object.entries(localesRaw)) {
localesMap[locale] = async () => { localesMap[locale] = async () => {
const messages: Record<string, any> = {}; const messages: Record<string, any> = {};
for (const [fileName, importFn] of Object.entries(files)) { for (const [fileName, importFn] of Object.entries(files)) {
messages[fileName] = ((await importFn()) as any)?.default; messages[fileName] = ((await importFn()) as any)?.default;
} }
return { default: messages }; return { default: messages };
}; };
} }
return localesMap; return localesMap;
} }
/** /**
@ -89,24 +89,26 @@ function loadLocalesMapFromDir(regexp: RegExp, modules: Record<string, () => Pro
* @param locale * @param locale
*/ */
function setI18nLanguage(locale: Locale) { function setI18nLanguage(locale: Locale) {
i18n.global.locale.value = locale; // setAntdvLocale(locale);
//@ts-ignore
i18n.global.locale.value = locale;
document?.querySelector("html")?.setAttribute("lang", locale); document?.querySelector("html")?.setAttribute("lang", locale);
} }
async function setupI18n(app: App, options: LocaleSetupOptions = {}) { async function setupI18n(app: App, options: LocaleSetupOptions = {}) {
const { defaultLocale = "en-US" } = options; const { defaultLocale = "en-US" } = options;
// app可以自行扩展一些第三方库和组件库的国际化 // app可以自行扩展一些第三方库和组件库的国际化
loadMessages = options.loadMessages || (async () => ({})); loadMessages = options.loadMessages || (async () => ({}));
app.use(i18n); app.use(i18n);
await loadLocaleMessages(defaultLocale); await loadLocaleMessages(defaultLocale);
// 在控制台打印警告 // 在控制台打印警告
i18n.global.setMissingHandler((locale, key) => { i18n.global.setMissingHandler((locale, key) => {
if (options.missingWarn && key.includes(".")) { if (options.missingWarn && key.includes(".")) {
console.warn(`[intlify] Not found '${key}' key in '${locale}' locale messages.`); console.warn(`[intlify] Not found '${key}' key in '${locale}' locale messages.`);
} }
}); });
} }
/** /**
@ -114,22 +116,22 @@ async function setupI18n(app: App, options: LocaleSetupOptions = {}) {
* @param lang * @param lang
*/ */
async function loadLocaleMessages(lang: SupportedLanguagesType) { async function loadLocaleMessages(lang: SupportedLanguagesType) {
if (unref(i18n.global.locale) === lang) { if (unref(i18n.global.locale) === lang) {
return setI18nLanguage(lang); return setI18nLanguage(lang);
} }
setSimpleLocale(lang); setSimpleLocale(lang);
const message = await localesMap[lang]?.(); const message = await localesMap[lang]?.();
if (message?.default) { if (message?.default) {
i18n.global.setLocaleMessage(lang, message.default); //@ts-ignore
} i18n.global.setLocaleMessage(lang, message.default);
}
const mergeMessage = await loadMessages(lang); const mergeMessage = await loadMessages(lang);
i18n.global.mergeLocaleMessage(lang, mergeMessage); i18n.global.mergeLocaleMessage(lang, mergeMessage);
return setI18nLanguage(lang); return setI18nLanguage(lang);
} }
export { i18n, loadLocaleMessages, loadLocalesMap, loadLocalesMapFromDir, setupI18n, setI18nLanguage };
export { i18n, loadLocaleMessages, loadLocalesMap, loadLocalesMapFromDir, setupI18n };
export default i18n; export default i18n;

View File

@ -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 $t = i18n.global.t;
const $te = i18n.global.te; 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 ImportLocaleFn, type LocaleSetupOptions, type SupportedLanguagesType } from "./typing";
// export type { CompileError } from "@intlify/core-base"; // export type { CompileError } from "@intlify/core-base";

View File

@ -1,84 +1,84 @@
export default { export default {
"welcomeBack": "Welcome Back", welcomeBack: "Welcome Back",
"pageTitle": "Plug-and-play Admin system", pageTitle: "Plug-and-play Admin system",
"pageDesc": "Efficient, versatile frontend template", pageDesc: "Efficient, versatile frontend template",
"loginSuccess": "Login Successful", loginSuccess: "Login Successful",
"loginSuccessDesc": "Welcome Back", loginSuccessDesc: "Welcome Back",
"loginSubtitle": "Enter your account details to manage your projects", loginSubtitle: "Enter your account details to manage your projects",
"selectAccount": "Quick Select Account", selectAccount: "Quick Select Account",
"username": "Username", username: "Username",
"password": "Password", password: "Password",
"usernameTip": "Please enter username", usernameTip: "Please enter username",
"passwordErrorTip": "Password is incorrect", passwordErrorTip: "Password is incorrect",
"passwordTip": "Please enter password", passwordTip: "Please enter password",
"verifyRequiredTip": "Please complete the verification first", verifyRequiredTip: "Please complete the verification first",
"rememberMe": "Remember Me", rememberMe: "Remember Me",
"createAnAccount": "Create an Account", createAnAccount: "Create an Account",
"createAccount": "Create Account", createAccount: "Create Account",
"alreadyHaveAccount": "Already have an account?", alreadyHaveAccount: "Already have an account?",
"accountTip": "Don't have an account?", accountTip: "Don't have an account?",
"signUp": "Sign Up", signUp: "Sign Up",
"signUpSubtitle": "Make managing your applications simple and fun", signUpSubtitle: "Make managing your applications simple and fun",
"confirmPassword": "Confirm Password", confirmPassword: "Confirm Password",
"confirmPasswordTip": "The passwords do not match", confirmPasswordTip: "The passwords do not match",
"agree": "I agree to", agree: "I agree to",
"privacyPolicy": "Privacy-policy", privacyPolicy: "Privacy-policy",
"terms": "Terms", terms: "Terms",
"agreeTip": "Please agree to the Privacy Policy and Terms", agreeTip: "Please agree to the Privacy Policy and Terms",
"goToLogin": "Login instead", goToLogin: "Login instead",
"passwordStrength": "Use 8 or more characters with a mix of letters, numbers & symbols", passwordStrength: "Use 8 or more characters with a mix of letters, numbers & symbols",
"forgetPassword": "Forget Password?", forgetPassword: "Forget Password?",
"forgetPasswordSubtitle": "Enter your email and we'll send you instructions to reset your password", forgetPasswordSubtitle: "Enter your email and we'll send you instructions to reset your password",
"emailTip": "Please enter email", emailTip: "Please enter email",
"emailValidErrorTip": "The email format you entered is incorrect", emailValidErrorTip: "The email format you entered is incorrect",
"sendResetLink": "Send Reset Link", sendResetLink: "Send Reset Link",
"email": "Email", email: "Email",
"qrcodeSubtitle": "Scan the QR code with your phone to login", qrcodeSubtitle: "Scan the QR code with your phone to login",
"qrcodePrompt": "Click 'Confirm' after scanning to complete login", qrcodePrompt: "Click 'Confirm' after scanning to complete login",
"qrcodeLogin": "QR Code Login", qrcodeLogin: "QR Code Login",
"codeSubtitle": "Enter your phone number to start managing your project", codeSubtitle: "Enter your phone number to start managing your project",
"code": "Security code", code: "Security code",
"codeTip": "Security code required {0} characters", codeTip: "Security code required {0} characters",
"mobile": "Mobile", mobile: "Mobile",
"mobileLogin": "Mobile Login", mobileLogin: "Mobile Login",
"mobileTip": "Please enter mobile number", mobileTip: "Please enter mobile number",
"mobileErrortip": "The phone number format is incorrect", mobileErrortip: "The phone number format is incorrect",
"sendCode": "Get Security code", sendCode: "Get Security code",
"sendText": "Resend in {0}s", sendText: "Resend in {0}s",
"thirdPartyLogin": "Or continue with", thirdPartyLogin: "Or continue with",
"loginAgainTitle": "Please Log In Again", loginAgainTitle: "Please Log In Again",
"loginAgainSubTitle": "Your login session has expired. Please log in again to continue.", loginAgainSubTitle: "Your login session has expired. Please log in again to continue.",
"layout": { layout: {
"center": "Align Center", center: "Align Center",
"alignLeft": "Align Left", alignLeft: "Align Left",
"alignRight": "Align Right" alignRight: "Align Right",
}, },
usernamePlaceholder: 'Please enter username/email/phone number', usernamePlaceholder: "Please enter username/email/phone number",
passwordPlaceholder: 'Please enter your password', passwordPlaceholder: "Please enter your password",
mobilePlaceholder: 'Please enter your mobile number', mobilePlaceholder: "Please enter your mobile number",
loginButton: 'Log In', loginButton: "Log In",
forgotAdminPassword: 'Forgot admin password?', forgotAdminPassword: "Forgot admin password?",
registerLink: 'Register', registerLink: "Register",
smsTab: 'Login via SMS code', smsTab: "Login via SMS code",
passwordTab: 'Password login', passwordTab: "Password login",
title: 'Change Password', title: "Change Password",
weakPasswordWarning: 'For your account security, please change your password immediately', weakPasswordWarning: "For your account security, please change your password immediately",
changeNow: 'Change Now', changeNow: "Change Now",
successMessage: 'Changed successfully', successMessage: "Changed successfully",
oldPassword: 'Old Password', oldPassword: "Old Password",
oldPasswordRequired: 'Please enter the old password', oldPasswordRequired: "Please enter the old password",
newPassword: 'New Password', newPassword: "New Password",
newPasswordRequired: 'Please enter the new password', newPasswordRequired: "Please enter the new password",
confirmNewPassword: 'Confirm New Password', confirmNewPassword: "Confirm New Password",
confirmNewPasswordRequired: 'Please confirm the new password', confirmNewPasswordRequired: "Please confirm the new password",
changePasswordButton: 'Change Password', changePasswordButton: "Change Password",
enterPassword: "Please enter the password", enterPassword: "Please enter the password",
newPasswordNotSameOld: "The new password cannot be the same as the old password", newPasswordNotSameOld: "The new password cannot be the same as the old password",
enterPasswordAgain: "Please enter the password again", enterPasswordAgain: "Please enter the password again",
passwordsNotMatch: "The two passwords do not match!", passwordsNotMatch: "The two passwords do not match!",
avatar: "Avatar", avatar: "Avatar",
nickName: "Nickname", nickName: "Nickname",
phoneNumber: "Phone Number", phoneNumber: "Phone Number",
changePassword: "Change Password", changePassword: "Change Password",
} };

File diff suppressed because it is too large Load Diff

View File

@ -1,22 +1,22 @@
export default { export default {
"back": "Back", back: "Back",
"backToHome": "Back To Home", backToHome: "Back To Home",
"login": "Login", login: "Login",
"logout": "Logout", logout: "Logout",
"prompt": "Prompt", prompt: "Prompt",
"cancel": "Cancel", cancel: "Cancel",
"confirm": "Confirm", confirm: "Confirm",
"reset": "Reset", reset: "Reset",
"noData": "No Data", noData: "No Data",
"refresh": "Refresh", refresh: "Refresh",
"loadingMenu": "Loading Menu", loadingMenu: "Loading Menu",
"query": "Search", query: "Search",
"search": "Search", search: "Search",
"enabled": "Enabled", enabled: "Enabled",
"disabled": "Disabled", disabled: "Disabled",
"edit": "Edit", edit: "Edit",
"delete": "Delete", delete: "Delete",
"create": "Create", create: "Create",
"yes": "Yes", yes: "Yes",
"no": "No" no: "No",
} };

View File

@ -1,71 +1,71 @@
export default { export default {
createCertPipeline: { createCertPipeline: {
title: "Create Certificate Application Pipeline", title: "Create Certificate Application Pipeline",
description: "Demonstrate how to configure a certificate application task", description: "Demonstrate how to configure a certificate application task",
items: { items: {
tutorialTitle: "Tutorial Demo Content", tutorialTitle: "Tutorial Demo Content",
tutorialDesc1: "This tutorial demonstrates how to automatically apply for a certificate and deploy it to Nginx", 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", tutorialDesc2: "Only 3 steps, fully automatic application and deployment",
createTitle: "Create Certificate Pipeline", createTitle: "Create Certificate Pipeline",
createDesc: "Click to add a certificate pipeline and fill in the certificate application information", createDesc: "Click to add a certificate pipeline and fill in the certificate application information",
successTitle: "Pipeline Created Successfully", successTitle: "Pipeline Created Successfully",
successDesc: "Click manual trigger to apply for the certificate", successDesc: "Click manual trigger to apply for the certificate",
nextTitle: "Next, demonstrate how to automatically deploy 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", nextDesc: "If you only need to apply for a certificate, you can stop here",
}, },
}, },
buttons: { buttons: {
prev: "Previous Step", prev: "Previous Step",
next: "Next Step", next: "Next Step",
}, },
addDeployTask: { addDeployTask: {
title: "Add Deployment Certificate Task", title: "Add Deployment Certificate Task",
description: "Demonstrate deployment of certificate to Nginx", description: "Demonstrate deployment of certificate to Nginx",
items: { items: {
addTaskTitle: "Add Certificate Deployment Task", addTaskTitle: "Add Certificate Deployment Task",
addTaskDesc1: "Demonstrate automatic deployment of certificate to nginx", addTaskDesc1: "Demonstrate automatic deployment of certificate to nginx",
addTaskDesc2: "Our system provides numerous deployment plugins to meet your needs", addTaskDesc2: "Our system provides numerous deployment plugins to meet your needs",
fillParamsTitle: "Fill Task Parameters", fillParamsTitle: "Fill Task Parameters",
fillParamsDesc1: "Fill in the certificate file path on the host", fillParamsDesc1: "Fill in the certificate file path on the host",
fillParamsDesc2: "Select SSH login authorization for the host", fillParamsDesc2: "Select SSH login authorization for the host",
activateCertTitle: "Make New Certificate Effective", activateCertTitle: "Make New Certificate Effective",
activateCertDesc1: "Execute restart script", activateCertDesc1: "Execute restart script",
activateCertDesc2: "Make the certificate effective", activateCertDesc2: "Make the certificate effective",
taskSuccessTitle: "Deployment Task Added Successfully", taskSuccessTitle: "Deployment Task Added Successfully",
taskSuccessDesc: "Now you can run it", taskSuccessDesc: "Now you can run it",
pluginsTitle: "Our System Provides Numerous Deployment Plugins", pluginsTitle: "Our System Provides Numerous Deployment Plugins",
pluginsDesc: "You can deploy certificates to various applications and platforms according to your needs", pluginsDesc: "You can deploy certificates to various applications and platforms according to your needs",
}, },
}, },
runAndTestTask: { runAndTestTask: {
runAndTestTitle: "Run and Test", runAndTestTitle: "Run and Test",
runAndTestDescription: "Demonstrate pipeline running, view logs, skip on success, etc.", runAndTestDescription: "Demonstrate pipeline running, view logs, skip on success, etc.",
runTestOnce: "Run a Test", runTestOnce: "Run a Test",
clickManualTriggerToTest: "Click the manual trigger button to test the run", clickManualTriggerToTest: "Click the manual trigger button to test the run",
viewLogs: "View Logs", viewLogs: "View Logs",
clickTaskToViewStatusAndLogs: "Click the task to view status and logs", clickTaskToViewStatusAndLogs: "Click the task to view status and logs",
howToTroubleshootFailure: "How to Troubleshoot Failure", howToTroubleshootFailure: "How to Troubleshoot Failure",
viewErrorLogs: "View error logs", viewErrorLogs: "View error logs",
nginxContainerNotExistFix: "Shows nginx container not found error, fix by changing to correct nginx container name", nginxContainerNotExistFix: "Shows nginx container not found error, fix by changing to correct nginx container name",
executionSuccess: "Execution Success", executionSuccess: "Execution Success",
retryAfterFix: "After fixing, click manual trigger again to rerun successfully", retryAfterFix: "After fixing, click manual trigger again to rerun successfully",
autoSkipAfterSuccess: "Auto Skip After Success", autoSkipAfterSuccess: "Auto Skip After Success",
successSkipExplanation: "Successful runs will be skipped automatically, rerun only if parameters or certificates update", successSkipExplanation: "Successful runs will be skipped automatically, rerun only if parameters or certificates update",
viewCertDeploymentSuccess: "View Certificate Deployment Success", viewCertDeploymentSuccess: "View Certificate Deployment Success",
visitNginxToSeeCert: "Visit website on nginx to see certificate deployed successfully", visitNginxToSeeCert: "Visit website on nginx to see certificate deployed successfully",
downloadCertManualDeploy: "Download Certificate for Manual Deployment", downloadCertManualDeploy: "Download Certificate for Manual Deployment",
downloadIfNoAutoDeployPlugin: "If no deployment plugin available, download certificate for manual deployment", downloadIfNoAutoDeployPlugin: "If no deployment plugin available, download certificate for manual deployment",
}, },
scheduleAndEmailTask: { scheduleAndEmailTask: {
title: "Set Scheduled Execution and Email Notifications", title: "Set Scheduled Execution and Email Notifications",
description: "Automatic running", description: "Automatic running",
setSchedule: "Set Scheduled Execution", setSchedule: "Set Scheduled Execution",
pipelineSuccessThenSchedule: "Pipeline tests succeed, then configure scheduled triggers so it runs automatically daily", 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", recommendDailyRun: "Recommend configuring to run once daily; new certs requested 35 days before expiry and auto-skipped otherwise",
setEmailNotification: "Set Email Notifications", setEmailNotification: "Set Email Notifications",
suggestErrorAndRecoveryEmails: "Suggest listening for 'On Error' and 'Error to Success' to quickly troubleshoot failures (basic version requires mail server setup)", 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)", basicVersionNeedsMailServer: "(basic version requires configuring mail server)",
tutorialEndTitle: "Tutorial End", tutorialEndTitle: "Tutorial End",
thanksForWatching: "Thank you for watching, hope it helps you", thanksForWatching: "Thank you for watching, hope it helps you",
} },
} };

View File

@ -1,19 +1,19 @@
import certd from './certd'; import certd from "./certd";
import authentication from './authentication'; import authentication from "./authentication";
import vip from './vip'; import vip from "./vip";
import tutorial from './tutorial'; import tutorial from "./tutorial";
import preferences from './preferences'; import preferences from "./preferences";
import ui from './ui'; import ui from "./ui";
import guide from './guide'; import guide from "./guide";
import common from './common'; import common from "./common";
export default { export default {
certd, certd,
authentication, authentication,
vip, vip,
ui, ui,
tutorial, tutorial,
preferences, preferences,
guide, guide,
common common,
}; };

View File

@ -1,186 +1,186 @@
export default { export default {
"title": "Preferences", title: "Preferences",
"subtitle": "Customize Preferences & Preview in Real Time", subtitle: "Customize Preferences & Preview in Real Time",
"resetTip": "Data has changed, click to reset", resetTip: "Data has changed, click to reset",
"resetTitle": "Reset Preferences", resetTitle: "Reset Preferences",
"resetSuccess": "Preferences reset successfully", resetSuccess: "Preferences reset successfully",
"appearance": "Appearance", appearance: "Appearance",
"layout": "Layout", layout: "Layout",
"content": "Content", content: "Content",
"other": "Other", other: "Other",
"wide": "Wide", wide: "Wide",
"compact": "Fixed", compact: "Fixed",
"followSystem": "Follow System", followSystem: "Follow System",
"vertical": "Vertical", vertical: "Vertical",
"verticalTip": "Side vertical menu mode", verticalTip: "Side vertical menu mode",
"horizontal": "Horizontal", horizontal: "Horizontal",
"horizontalTip": "Horizontal menu mode, all menus displayed at the top", horizontalTip: "Horizontal menu mode, all menus displayed at the top",
"twoColumn": "Two Column", twoColumn: "Two Column",
"twoColumnTip": "Vertical Two Column Menu Mode", twoColumnTip: "Vertical Two Column Menu Mode",
"headerSidebarNav": "Header Vertical", headerSidebarNav: "Header Vertical",
"headerSidebarNavTip": "Header Full Width, Sidebar Navigation Mode", headerSidebarNavTip: "Header Full Width, Sidebar Navigation Mode",
"headerTwoColumn": "Header Two Column", headerTwoColumn: "Header Two Column",
"headerTwoColumnTip": "Header Navigation & Sidebar Two Column co-exists", headerTwoColumnTip: "Header Navigation & Sidebar Two Column co-exists",
"mixedMenu": "Mixed Menu", mixedMenu: "Mixed Menu",
"mixedMenuTip": "Vertical & Horizontal Menu Co-exists", mixedMenuTip: "Vertical & Horizontal Menu Co-exists",
"fullContent": "Full Content", fullContent: "Full Content",
"fullContentTip": "Only display content body, hide all menus", fullContentTip: "Only display content body, hide all menus",
"normal": "Normal", normal: "Normal",
"plain": "Plain", plain: "Plain",
"rounded": "Rounded", rounded: "Rounded",
"copyPreferences": "Copy Preferences", copyPreferences: "Copy Preferences",
"copyPreferencesSuccessTitle": "Copy successful", copyPreferencesSuccessTitle: "Copy successful",
"copyPreferencesSuccess": "Copy successful, please override in `src/preferences.ts` under app", copyPreferencesSuccess: "Copy successful, please override in `src/preferences.ts` under app",
"clearAndLogout": "Clear Cache & Logout", clearAndLogout: "Clear Cache & Logout",
"mode": "Mode", mode: "Mode",
"general": "General", general: "General",
"language": "Language", language: "Language",
"dynamicTitle": "Dynamic Title", dynamicTitle: "Dynamic Title",
"watermark": "Watermark", watermark: "Watermark",
"checkUpdates": "Periodic update check", checkUpdates: "Periodic update check",
"position": { position: {
"title": "Preferences Postion", title: "Preferences Postion",
"header": "Header", header: "Header",
"auto": "Auto", auto: "Auto",
"fixed": "Fixed" fixed: "Fixed",
}, },
"sidebar": { sidebar: {
"title": "Sidebar", title: "Sidebar",
"width": "Width", width: "Width",
"visible": "Show Sidebar", visible: "Show Sidebar",
"collapsed": "Collpase Menu", collapsed: "Collpase Menu",
"collapsedShowTitle": "Show Menu Title", collapsedShowTitle: "Show Menu Title",
"autoActivateChild": "Auto Activate SubMenu", autoActivateChild: "Auto Activate SubMenu",
"autoActivateChildTip": "`Enabled` to automatically activate the submenu while click menu.", autoActivateChildTip: "`Enabled` to automatically activate the submenu while click menu.",
"expandOnHover": "Expand On Hover", expandOnHover: "Expand On Hover",
"expandOnHoverTip": "When the mouse hovers over menu, \n `Enabled` to expand children menus \n `Disabled` to expand whole sidebar." expandOnHoverTip: "When the mouse hovers over menu, \n `Enabled` to expand children menus \n `Disabled` to expand whole sidebar.",
}, },
"tabbar": { tabbar: {
"title": "Tabbar", title: "Tabbar",
"enable": "Enable Tab Bar", enable: "Enable Tab Bar",
"icon": "Show Tabbar Icon", icon: "Show Tabbar Icon",
"showMore": "Show More Button", showMore: "Show More Button",
"showMaximize": "Show Maximize Button", showMaximize: "Show Maximize Button",
"persist": "Persist Tabs", persist: "Persist Tabs",
"maxCount": "Max Count of 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.", 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", draggable: "Enable Draggable Sort",
"wheelable": "Support Mouse Wheel", wheelable: "Support Mouse Wheel",
"middleClickClose": "Close Tab when Mouse Middle Button Click", middleClickClose: "Close Tab when Mouse Middle Button Click",
"wheelableTip": "When enabled, the Tabbar area responds to vertical scrolling events of the scroll wheel.", wheelableTip: "When enabled, the Tabbar area responds to vertical scrolling events of the scroll wheel.",
"styleType": { styleType: {
"title": "Tabs Style", title: "Tabs Style",
"chrome": "Chrome", chrome: "Chrome",
"card": "Card", card: "Card",
"plain": "Plain", plain: "Plain",
"brisk": "Brisk" brisk: "Brisk",
}, },
"contextMenu": { contextMenu: {
"reload": "Reload", reload: "Reload",
"close": "Close", close: "Close",
"pin": "Pin", pin: "Pin",
"unpin": "Unpin", unpin: "Unpin",
"closeLeft": "Close Left Tabs", closeLeft: "Close Left Tabs",
"closeRight": "Close Right Tabs", closeRight: "Close Right Tabs",
"closeOther": "Close Other Tabs", closeOther: "Close Other Tabs",
"closeAll": "Close All Tabs", closeAll: "Close All Tabs",
"openInNewWindow": "Open in New Window", openInNewWindow: "Open in New Window",
"maximize": "Maximize", maximize: "Maximize",
"restoreMaximize": "Restore" restoreMaximize: "Restore",
} },
}, },
"navigationMenu": { navigationMenu: {
"title": "Navigation Menu", title: "Navigation Menu",
"style": "Navigation Menu Style", style: "Navigation Menu Style",
"accordion": "Sidebar Accordion Menu", accordion: "Sidebar Accordion Menu",
"split": "Navigation Menu Separation", split: "Navigation Menu Separation",
"splitTip": "When enabled, the sidebar displays the top bar's submenu" splitTip: "When enabled, the sidebar displays the top bar's submenu",
}, },
"breadcrumb": { breadcrumb: {
"title": "Breadcrumb", title: "Breadcrumb",
"home": "Show Home Button", home: "Show Home Button",
"enable": "Enable Breadcrumb", enable: "Enable Breadcrumb",
"icon": "Show Breadcrumb Icon", icon: "Show Breadcrumb Icon",
"background": "background", background: "background",
"style": "Breadcrumb Style", style: "Breadcrumb Style",
"hideOnlyOne": "Hidden when only one" hideOnlyOne: "Hidden when only one",
}, },
"animation": { animation: {
"title": "Animation", title: "Animation",
"loading": "Page Loading", loading: "Page Loading",
"transition": "Page Transition", transition: "Page Transition",
"progress": "Page Progress" progress: "Page Progress",
}, },
"theme": { theme: {
"title": "Theme", title: "Theme",
"radius": "Radius", radius: "Radius",
"light": "Light", light: "Light",
"dark": "Dark", dark: "Dark",
"darkSidebar": "Semi Dark Sidebar", darkSidebar: "Semi Dark Sidebar",
"darkHeader": "Semi Dark Header", darkHeader: "Semi Dark Header",
"weakMode": "Weak Mode", weakMode: "Weak Mode",
"grayMode": "Gray Mode", grayMode: "Gray Mode",
"builtin": { builtin: {
"title": "Built-in", title: "Built-in",
"default": "Default", default: "Default",
"violet": "Violet", violet: "Violet",
"pink": "Pink", pink: "Pink",
"rose": "Rose", rose: "Rose",
"skyBlue": "Sky Blue", skyBlue: "Sky Blue",
"deepBlue": "Deep Blue", deepBlue: "Deep Blue",
"green": "Green", green: "Green",
"deepGreen": "Deep Green", deepGreen: "Deep Green",
"orange": "Orange", orange: "Orange",
"yellow": "Yellow", yellow: "Yellow",
"zinc": "Zinc", zinc: "Zinc",
"neutral": "Neutral", neutral: "Neutral",
"slate": "Slate", slate: "Slate",
"gray": "Gray", gray: "Gray",
"custom": "Custom" custom: "Custom",
} },
}, },
"header": { header: {
"title": "Header", title: "Header",
"visible": "Show Header", visible: "Show Header",
"modeStatic": "Static", modeStatic: "Static",
"modeFixed": "Fixed", modeFixed: "Fixed",
"modeAuto": "Auto hide & Show", modeAuto: "Auto hide & Show",
"modeAutoScroll": "Scroll to Hide & Show", modeAutoScroll: "Scroll to Hide & Show",
"menuAlign": "Menu Align", menuAlign: "Menu Align",
"menuAlignStart": "Start", menuAlignStart: "Start",
"menuAlignEnd": "End", menuAlignEnd: "End",
"menuAlignCenter": "Center" menuAlignCenter: "Center",
}, },
"footer": { footer: {
"title": "Footer", title: "Footer",
"visible": "Show Footer", visible: "Show Footer",
"fixed": "Fixed at Bottom" fixed: "Fixed at Bottom",
}, },
"copyright": { copyright: {
"title": "Copyright", title: "Copyright",
"enable": "Enable Copyright", enable: "Enable Copyright",
"companyName": "Company Name", companyName: "Company Name",
"companySiteLink": "Company Site Link", companySiteLink: "Company Site Link",
"date": "Date", date: "Date",
"icp": "ICP License Number", icp: "ICP License Number",
"icpLink": "ICP Site Link" icpLink: "ICP Site Link",
}, },
"shortcutKeys": { shortcutKeys: {
"title": "Shortcut Keys", title: "Shortcut Keys",
"global": "Global", global: "Global",
"search": "Global Search", search: "Global Search",
"logout": "Logout", logout: "Logout",
"preferences": "Preferences" preferences: "Preferences",
}, },
"widget": { widget: {
"title": "Widget", title: "Widget",
"globalSearch": "Enable Global Search", globalSearch: "Enable Global Search",
"fullscreen": "Enable Fullscreen", fullscreen: "Enable Fullscreen",
"themeToggle": "Enable Theme Toggle", themeToggle: "Enable Theme Toggle",
"languageToggle": "Enable Language Toggle", languageToggle: "Enable Language Toggle",
"notification": "Enable Notification", notification: "Enable Notification",
"sidebarToggle": "Enable Sidebar Toggle", sidebarToggle: "Enable Sidebar Toggle",
"lockScreen": "Enable Lock Screen", lockScreen: "Enable Lock Screen",
"refresh": "Enable Refresh" refresh: "Enable Refresh",
} },
} };

View File

@ -1,3 +1,3 @@
export default { export default {
title: 'Tutorial', title: "Tutorial",
} };

View File

@ -1,104 +1,104 @@
export default { export default {
"formRules": { formRules: {
"required": "Please enter {0}", required: "Please enter {0}",
"selectRequired": "Please select {0}", selectRequired: "Please select {0}",
"minLength": "{0} must be at least {1} characters", minLength: "{0} must be at least {1} characters",
"maxLength": "{0} can be at most {1} characters", maxLength: "{0} can be at most {1} characters",
"length": "{0} must be {1} characters long", length: "{0} must be {1} characters long",
"alreadyExists": "{0} `{1}` already exists", alreadyExists: "{0} `{1}` already exists",
"startWith": "{0} must start with `{1}`", startWith: "{0} must start with `{1}`",
"invalidURL": "Please input a valid URL" invalidURL: "Please input a valid URL",
}, },
"actionTitle": { actionTitle: {
"edit": "Modify {0}", edit: "Modify {0}",
"create": "Create {0}", create: "Create {0}",
"delete": "Delete {0}", delete: "Delete {0}",
"view": "View {0}" view: "View {0}",
}, },
"actionMessage": { actionMessage: {
"deleteConfirm": "Are you sure to delete {0}?", deleteConfirm: "Are you sure to delete {0}?",
"deleting": "Deleting {0} ...", deleting: "Deleting {0} ...",
"deleteSuccess": "{0} deleted successfully", deleteSuccess: "{0} deleted successfully",
"operationSuccess": "Operation succeeded", operationSuccess: "Operation succeeded",
"operationFailed": "Operation failed" operationFailed: "Operation failed",
}, },
"placeholder": { placeholder: {
"input": "Please enter", input: "Please enter",
"select": "Please select" select: "Please select",
}, },
"captcha": { captcha: {
"title": "Please complete the security verification", title: "Please complete the security verification",
"sliderSuccessText": "Passed", sliderSuccessText: "Passed",
"sliderDefaultText": "Slider and drag", sliderDefaultText: "Slider and drag",
"alt": "Supports img tag src attribute value", alt: "Supports img tag src attribute value",
"sliderRotateDefaultTip": "Click picture to refresh", sliderRotateDefaultTip: "Click picture to refresh",
"sliderRotateFailTip": "Validation failed", sliderRotateFailTip: "Validation failed",
"sliderRotateSuccessTip": "Validation successful, time {0} seconds", sliderRotateSuccessTip: "Validation successful, time {0} seconds",
"refreshAriaLabel": "Refresh captcha", refreshAriaLabel: "Refresh captcha",
"confirmAriaLabel": "Confirm selection", confirmAriaLabel: "Confirm selection",
"confirm": "Confirm", confirm: "Confirm",
"pointAriaLabel": "Click point", pointAriaLabel: "Click point",
"clickInOrder": "Please click in order" clickInOrder: "Please click in order",
}, },
"iconPicker": { iconPicker: {
"placeholder": "Select an icon", placeholder: "Select an icon",
"search": "Search icon..." search: "Search icon...",
}, },
"jsonViewer": { jsonViewer: {
"copy": "Copy", copy: "Copy",
"copied": "Copied" copied: "Copied",
}, },
"fallback": { fallback: {
"pageNotFound": "Oops! Page Not Found", pageNotFound: "Oops! Page Not Found",
"pageNotFoundDesc": "Sorry, we couldn't find the page you were looking for.", pageNotFoundDesc: "Sorry, we couldn't find the page you were looking for.",
"forbidden": "Oops! Access Denied", forbidden: "Oops! Access Denied",
"forbiddenDesc": "Sorry, but you don't have permission to access this page.", forbiddenDesc: "Sorry, but you don't have permission to access this page.",
"internalError": "Oops! Something Went Wrong", internalError: "Oops! Something Went Wrong",
"internalErrorDesc": "Sorry, but the server encountered an error.", internalErrorDesc: "Sorry, but the server encountered an error.",
"offline": "Offline Page", offline: "Offline Page",
"offlineError": "Oops! Network Error", offlineError: "Oops! Network Error",
"offlineErrorDesc": "Sorry, can't connect to the internet. Check your connection.", offlineErrorDesc: "Sorry, can't connect to the internet. Check your connection.",
"comingSoon": "Coming Soon", comingSoon: "Coming Soon",
"http": { http: {
"requestTimeout": "The request timed out. Please try again later.", requestTimeout: "The request timed out. Please try again later.",
"networkError": "A network error occurred. Please check your internet connection and try again.", networkError: "A network error occurred. Please check your internet connection and try again.",
"badRequest": "Bad Request. Please check your input and try again.", badRequest: "Bad Request. Please check your input and try again.",
"unauthorized": "Unauthorized. Please log in to continue.", unauthorized: "Unauthorized. Please log in to continue.",
"forbidden": "Forbidden. You do not have permission to access this resource.", forbidden: "Forbidden. You do not have permission to access this resource.",
"notFound": "Not Found. The requested resource could not be found.", notFound: "Not Found. The requested resource could not be found.",
"internalServerError": "Internal Server Error. Something went wrong on our end. Please try again later." internalServerError: "Internal Server Error. Something went wrong on our end. Please try again later.",
} },
}, },
"widgets": { widgets: {
"document": "Document", document: "Document",
"qa": "Q&A", qa: "Q&A",
"setting": "Settings", setting: "Settings",
"logoutTip": "Do you want to logout?", logoutTip: "Do you want to logout?",
"viewAll": "View All Messages", viewAll: "View All Messages",
"notifications": "Notifications", notifications: "Notifications",
"markAllAsRead": "Make All as Read", markAllAsRead: "Make All as Read",
"clearNotifications": "Clear", clearNotifications: "Clear",
"checkUpdatesTitle": "New Version Available", checkUpdatesTitle: "New Version Available",
"checkUpdatesDescription": "Click to refresh and get the latest version", checkUpdatesDescription: "Click to refresh and get the latest version",
"search": { search: {
"title": "Search", title: "Search",
"searchNavigate": "Search Navigation", searchNavigate: "Search Navigation",
"select": "Select", select: "Select",
"navigate": "Navigate", navigate: "Navigate",
"close": "Close", close: "Close",
"noResults": "No Search Results Found", noResults: "No Search Results Found",
"noRecent": "No Search History", noRecent: "No Search History",
"recent": "Search History" recent: "Search History",
}, },
"lockScreen": { lockScreen: {
"title": "Lock Screen", title: "Lock Screen",
"screenButton": "Locking", screenButton: "Locking",
"password": "Password", password: "Password",
"placeholder": "Please enter password", placeholder: "Please enter password",
"unlock": "Click to unlock", unlock: "Click to unlock",
"errorPasswordTip": "Password error, please re-enter", errorPasswordTip: "Password error, please re-enter",
"backToLogin": "Back to login", backToLogin: "Back to login",
"entry": "Enter the system" entry: "Enter the system",
} },
} },
} };

View File

@ -1,86 +1,86 @@
export default { export default {
comm: { comm: {
name: "{vipLabel} Activated", name: "{vipLabel} Activated",
title: "Expires on: {expire}", title: "Expires on: {expire}",
nav: "{vipLabel}", nav: "{vipLabel}",
}, },
plus: { plus: {
name: "Pro Features", name: "Pro Features",
title: "Upgrade to Pro for commercial license", title: "Upgrade to Pro for commercial license",
}, },
free: { free: {
comm: { comm: {
name: "Pro Features", name: "Pro Features",
title: "Upgrade to Pro for commercial license", title: "Upgrade to Pro for commercial license",
}, },
button: { button: {
name: "Advanced Features", name: "Advanced Features",
title: "Upgrade to Advanced for more VIP privileges", title: "Upgrade to Advanced for more VIP privileges",
}, },
nav: { nav: {
name: "Basic Version", name: "Basic Version",
title: "Upgrade to Advanced for more VIP privileges", title: "Upgrade to Advanced for more VIP privileges",
}, },
}, },
enterCode: "Please enter the activation code", enterCode: "Please enter the activation code",
successTitle: "Activation Successful", successTitle: "Activation Successful",
successContent: "You have successfully activated {vipLabel}, valid until: {expireDate}", successContent: "You have successfully activated {vipLabel}, valid until: {expireDate}",
bindAccountTitle: "Bind Your Account", bindAccountTitle: "Bind Your Account",
bindAccountContent: "Binding your account helps prevent license loss. Strongly recommended.", bindAccountContent: "Binding your account helps prevent license loss. Strongly recommended.",
congratulations_vip_trial: 'Congratulations, you have received a Pro version {duration} days trial', congratulations_vip_trial: "Congratulations, you have received a Pro version {duration} days trial",
trial_modal_title: '7-day Pro version trial acquisition', trial_modal_title: "7-day Pro version trial acquisition",
trial_modal_ok_text: 'Get now', trial_modal_ok_text: "Get now",
trial_modal_thanks: 'Thank you for supporting the open source project', 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', trial_modal_click_confirm: "Click confirm to get a 7-day Pro version trial",
get_7_day_pro_trial: "7-day professional version trial", get_7_day_pro_trial: "7-day professional version trial",
star_now: "Star Now", star_now: "Star Now",
please_help_star: "Could you please help by starring? Thanks a lot!", please_help_star: "Could you please help by starring? Thanks a lot!",
admin_only_operation: "Admin operation only", admin_only_operation: "Admin operation only",
enter_activation_code: "Please enter the activation code", enter_activation_code: "Please enter the activation code",
activate_pro_business: "Activate Professional/Business Edition", activate_pro_business: "Activate Professional/Business Edition",
renew_business: "Renew Business Edition", renew_business: "Renew Business Edition",
renew_pro_upgrade_business: "Renew Professional Edition / Upgrade to Business Edition", renew_pro_upgrade_business: "Renew Professional Edition / Upgrade to Business Edition",
basic_edition: "Basic Edition", basic_edition: "Basic Edition",
community_free_version: "Community Free Version", community_free_version: "Community Free Version",
unlimited_certificate_application: "Unlimited certificate applications", unlimited_certificate_application: "Unlimited certificate applications",
unlimited_domain_count: "Unlimited domain count", unlimited_domain_count: "Unlimited domain count",
unlimited_certificate_pipelines: "Unlimited certificate pipelines", unlimited_certificate_pipelines: "Unlimited certificate pipelines",
common_deployment_plugins: "Common host, cloud platform, CDN, Baota, 1Panel deployment plugins", common_deployment_plugins: "Common host, cloud platform, CDN, Baota, 1Panel deployment plugins",
email_webhook_notifications: "Email, webhook notification methods", email_webhook_notifications: "Email, webhook notification methods",
professional_edition: "Professional Edition", professional_edition: "Professional Edition",
open_source_support: "Open source requires your sponsorship support", open_source_support: "Open source requires your sponsorship support",
vip_group_priority: "Access to VIP group, your requests will have priority", vip_group_priority: "Access to VIP group, your requests will have priority",
unlimited_site_certificate_monitoring: "Unlimited site certificate monitoring", unlimited_site_certificate_monitoring: "Unlimited site certificate monitoring",
more_notification_methods: "More notification methods", more_notification_methods: "More notification methods",
plugins_fully_open: "All plugins open, including Synology and more", plugins_fully_open: "All plugins open, including Synology and more",
click_to_get_7_day_trial: "Click to get 7-day trial", click_to_get_7_day_trial: "Click to get 7-day trial",
years: "years", years: "years",
afdian_support_vip: 'Get a one-year professional activation code after supporting "VIP membership" on Afdian, open source needs your support', 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", get_after_support: "Get after sponsoring",
business_edition: "Business Edition", business_edition: "Business Edition",
commercial_license: "Commercial license, allowed for external operation", commercial_license: "Commercial license, allowed for external operation",
all_pro_privileges: "All professional edition privileges", all_pro_privileges: "All professional edition privileges",
allow_commercial_use_modify_logo_title: "Allows commercial use, can modify logo and title", allow_commercial_use_modify_logo_title: "Allows commercial use, can modify logo and title",
data_statistics: "Data statistics", data_statistics: "Data statistics",
plugin_management: "Plugin management", plugin_management: "Plugin management",
unlimited_multi_users: "Unlimited multi-users", unlimited_multi_users: "Unlimited multi-users",
support_user_payment: "Supports user payments", support_user_payment: "Supports user payments",
contact_author_for_trial: "Please contact the author for trial", contact_author_for_trial: "Please contact the author for trial",
activate: "Activate", activate: "Activate",
get_pro_code_after_support: 'Get a one-year professional activation code after supporting "VIP membership" on Afdian', 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", business_contact_author: "Business edition please contact the author directly",
year: "year", year: "year",
freee: "Free", freee: "Free",
renew: "Renew", renew: "Renew",
activate_immediately: "Activate Immediately", activate_immediately: "Activate Immediately",
current: "Current", current: "Current",
activated_expire_time: " activated, expiration date: ", activated_expire_time: " activated, expiration date: ",
site_id: "Site ID", site_id: "Site ID",
invite_code_optional: "Invite code [optional], can get extra 30 days for Professional / 15 days for Business", invite_code_optional: "Invite code [optional], can get extra 30 days for Professional / 15 days for Business",
no_activation_code: "No activation code?", no_activation_code: "No activation code?",
activation_code_one_use: "Activation code can only be used once. To change site, please ", activation_code_one_use: "Activation code can only be used once. To change site, please ",
bind_account: "bind account", bind_account: "bind account",
transfer_vip: ' then "Transfer VIP"', transfer_vip: ' then "Transfer VIP"',
} };

View File

@ -1,85 +1,85 @@
export default { export default {
"welcomeBack": "欢迎回来", welcomeBack: "欢迎回来",
"pageTitle": "开箱即用的大型中后台管理系统", pageTitle: "开箱即用的大型中后台管理系统",
"pageDesc": "工程化、高性能、跨组件库的前端模版", pageDesc: "工程化、高性能、跨组件库的前端模版",
"loginSuccess": "登录成功", loginSuccess: "登录成功",
"loginSuccessDesc": "欢迎回来", loginSuccessDesc: "欢迎回来",
"loginSubtitle": "请输入您的帐户信息以开始管理您的项目", loginSubtitle: "请输入您的帐户信息以开始管理您的项目",
"selectAccount": "快速选择账号", selectAccount: "快速选择账号",
"username": "账号", username: "账号",
"password": "密码", password: "密码",
"usernameTip": "请输入用户名", usernameTip: "请输入用户名",
"passwordTip": "请输入密码", passwordTip: "请输入密码",
"verifyRequiredTip": "请先完成验证", verifyRequiredTip: "请先完成验证",
"passwordErrorTip": "密码错误", passwordErrorTip: "密码错误",
"rememberMe": "记住账号", rememberMe: "记住账号",
"createAnAccount": "创建一个账号", createAnAccount: "创建一个账号",
"createAccount": "创建账号", createAccount: "创建账号",
"alreadyHaveAccount": "已经有账号了?", alreadyHaveAccount: "已经有账号了?",
"accountTip": "还没有账号?", accountTip: "还没有账号?",
"signUp": "注册", signUp: "注册",
"signUpSubtitle": "让您的应用程序管理变得简单而有趣", signUpSubtitle: "让您的应用程序管理变得简单而有趣",
"confirmPassword": "确认密码", confirmPassword: "确认密码",
"confirmPasswordTip": "两次输入的密码不一致", confirmPasswordTip: "两次输入的密码不一致",
"agree": "我同意", agree: "我同意",
"privacyPolicy": "隐私政策", privacyPolicy: "隐私政策",
"terms": "条款", terms: "条款",
"agreeTip": "请同意隐私政策和条款", agreeTip: "请同意隐私政策和条款",
"goToLogin": "去登录", goToLogin: "去登录",
"passwordStrength": "使用 8 个或更多字符,混合字母、数字和符号", passwordStrength: "使用 8 个或更多字符,混合字母、数字和符号",
"forgetPassword": "忘记密码?", forgetPassword: "忘记密码?",
"forgetPasswordSubtitle": "输入您的电子邮件,我们将向您发送重置密码的连接", forgetPasswordSubtitle: "输入您的电子邮件,我们将向您发送重置密码的连接",
"emailTip": "请输入邮箱", emailTip: "请输入邮箱",
"emailValidErrorTip": "你输入的邮箱格式不正确", emailValidErrorTip: "你输入的邮箱格式不正确",
"sendResetLink": "发送重置链接", sendResetLink: "发送重置链接",
"email": "邮箱", email: "邮箱",
"qrcodeSubtitle": "请用手机扫描二维码登录", qrcodeSubtitle: "请用手机扫描二维码登录",
"qrcodePrompt": "扫码后点击 '确认',即可完成登录", qrcodePrompt: "扫码后点击 '确认',即可完成登录",
"qrcodeLogin": "扫码登录", qrcodeLogin: "扫码登录",
"codeSubtitle": "请输入您的手机号码以开始管理您的项目", codeSubtitle: "请输入您的手机号码以开始管理您的项目",
"code": "验证码", code: "验证码",
"codeTip": "请输入{0}位验证码", codeTip: "请输入{0}位验证码",
"mobile": "手机号码", mobile: "手机号码",
"mobileTip": "请输入手机号", mobileTip: "请输入手机号",
"mobileErrortip": "手机号码格式错误", mobileErrortip: "手机号码格式错误",
"mobileLogin": "手机号登录", mobileLogin: "手机号登录",
"sendCode": "获取验证码", sendCode: "获取验证码",
"sendText": "{0}秒后重新获取", sendText: "{0}秒后重新获取",
"thirdPartyLogin": "其他登录方式", thirdPartyLogin: "其他登录方式",
"loginAgainTitle": "重新登录", loginAgainTitle: "重新登录",
"loginAgainSubTitle": "您的登录状态已过期,请重新登录以继续。", loginAgainSubTitle: "您的登录状态已过期,请重新登录以继续。",
"layout": { layout: {
"center": "居中", center: "居中",
"alignLeft": "居左", alignLeft: "居左",
"alignRight": "居右" alignRight: "居右",
}, },
usernamePlaceholder: '请输入用户名/邮箱/手机号', usernamePlaceholder: "请输入用户名/邮箱/手机号",
passwordPlaceholder: '请输入密码', passwordPlaceholder: "请输入密码",
mobilePlaceholder: '请输入手机号', mobilePlaceholder: "请输入手机号",
loginButton: '登录', loginButton: "登录",
forgotAdminPassword: '忘记管理员密码?', forgotAdminPassword: "忘记管理员密码?",
registerLink: '注册', registerLink: "注册",
smsTab: '短信验证码登录', smsTab: "短信验证码登录",
passwordTab: '密码登录', passwordTab: "密码登录",
title: '修改密码', title: "修改密码",
weakPasswordWarning: '为了您的账户安全,请立即修改密码', weakPasswordWarning: "为了您的账户安全,请立即修改密码",
changeNow: '立即修改', changeNow: "立即修改",
successMessage: '修改成功', successMessage: "修改成功",
oldPassword: '旧密码', oldPassword: "旧密码",
oldPasswordRequired: '请输入旧密码', oldPasswordRequired: "请输入旧密码",
newPassword: '新密码', newPassword: "新密码",
newPasswordRequired: '请输入新密码', newPasswordRequired: "请输入新密码",
confirmNewPassword: '确认新密码', confirmNewPassword: "确认新密码",
confirmNewPasswordRequired: '请输入确认密码', confirmNewPasswordRequired: "请输入确认密码",
changePasswordButton: '修改密码', changePasswordButton: "修改密码",
enterPassword: "请输入密码", enterPassword: "请输入密码",
newPasswordNotSameOld: "新密码不能和旧密码相同", newPasswordNotSameOld: "新密码不能和旧密码相同",
enterPasswordAgain: "请再次输入密码", enterPasswordAgain: "请再次输入密码",
passwordsNotMatch: "两次输入密码不一致!", passwordsNotMatch: "两次输入密码不一致!",
avatar: "头像", avatar: "头像",
nickName: "昵称", nickName: "昵称",
phoneNumber: "手机号", phoneNumber: "手机号",
changePassword: "修改密码", changePassword: "修改密码",
} };

File diff suppressed because it is too large Load Diff

View File

@ -1,22 +1,22 @@
export default { export default {
"back": "返回", back: "返回",
"backToHome": "返回首页", backToHome: "返回首页",
"login": "登录", login: "登录",
"logout": "退出登录", logout: "退出登录",
"prompt": "提示", prompt: "提示",
"cancel": "取消", cancel: "取消",
"confirm": "确认", confirm: "确认",
"reset": "重置", reset: "重置",
"noData": "暂无数据", noData: "暂无数据",
"refresh": "刷新", refresh: "刷新",
"loadingMenu": "加载菜单中", loadingMenu: "加载菜单中",
"query": "查询", query: "查询",
"search": "搜索", search: "搜索",
"enabled": "已启用", enabled: "已启用",
"disabled": "已禁用", disabled: "已禁用",
"edit": "修改", edit: "修改",
"delete": "删除", delete: "删除",
"create": "新增", create: "新增",
"yes": "是", yes: "是",
"no": "否" no: "否",
} };

View File

@ -1,71 +1,71 @@
export default { export default {
createCertPipeline: { createCertPipeline: {
title: "创建证书申请流水线", title: "创建证书申请流水线",
description: "演示证书申请任务如何配置", description: "演示证书申请任务如何配置",
items: { items: {
tutorialTitle: "教程演示内容", tutorialTitle: "教程演示内容",
tutorialDesc1: "本教程演示如何自动申请证书并部署到Nginx上", tutorialDesc1: "本教程演示如何自动申请证书并部署到Nginx上",
tutorialDesc2: "仅需3步全自动申请部署证书", tutorialDesc2: "仅需3步全自动申请部署证书",
createTitle: "创建证书流水线", createTitle: "创建证书流水线",
createDesc: "点击添加证书流水线,填写证书申请信息", createDesc: "点击添加证书流水线,填写证书申请信息",
successTitle: "流水线创建成功", successTitle: "流水线创建成功",
successDesc: "点击手动触发即可申请证书", successDesc: "点击手动触发即可申请证书",
nextTitle: "接下来演示如何自动部署证书", nextTitle: "接下来演示如何自动部署证书",
nextDesc: "如果您只需要申请证书,那么到这一步就可以了", nextDesc: "如果您只需要申请证书,那么到这一步就可以了",
}, },
}, },
buttons: { buttons: {
prev: "上一步", prev: "上一步",
next: "下一步", next: "下一步",
}, },
addDeployTask: { addDeployTask: {
title: "添加部署证书任务", title: "添加部署证书任务",
description: "这里演示部署证书到Nginx", description: "这里演示部署证书到Nginx",
items: { items: {
addTaskTitle: "添加证书部署任务", addTaskTitle: "添加证书部署任务",
addTaskDesc1: "这里演示自动部署证书到nginx", addTaskDesc1: "这里演示自动部署证书到nginx",
addTaskDesc2: "本系统提供海量部署插件,满足您的各种部署需求", addTaskDesc2: "本系统提供海量部署插件,满足您的各种部署需求",
fillParamsTitle: "填写任务参数", fillParamsTitle: "填写任务参数",
fillParamsDesc1: "填写主机上证书文件的路径", fillParamsDesc1: "填写主机上证书文件的路径",
fillParamsDesc2: "选择主机ssh登录授权", fillParamsDesc2: "选择主机ssh登录授权",
activateCertTitle: "让新证书生效", activateCertTitle: "让新证书生效",
activateCertDesc1: "执行重启脚本", activateCertDesc1: "执行重启脚本",
activateCertDesc2: "让证书生效", activateCertDesc2: "让证书生效",
taskSuccessTitle: "部署任务添加成功", taskSuccessTitle: "部署任务添加成功",
taskSuccessDesc: "现在可以运行", taskSuccessDesc: "现在可以运行",
pluginsTitle: "本系统提供茫茫多的部署插件", pluginsTitle: "本系统提供茫茫多的部署插件",
pluginsDesc: "您可以根据自身需求将证书部署到各种应用和平台", pluginsDesc: "您可以根据自身需求将证书部署到各种应用和平台",
}, },
}, },
runAndTestTask: { runAndTestTask: {
runAndTestTitle: "运行与测试", runAndTestTitle: "运行与测试",
runAndTestDescription: "演示流水线运行,查看日志,成功后跳过等", runAndTestDescription: "演示流水线运行,查看日志,成功后跳过等",
runTestOnce: "运行测试一下", runTestOnce: "运行测试一下",
clickManualTriggerToTest: "点击手动触发按钮,即可测试运行", clickManualTriggerToTest: "点击手动触发按钮,即可测试运行",
viewLogs: "查看日志", viewLogs: "查看日志",
clickTaskToViewStatusAndLogs: "点击任务可以查看状态和日志", clickTaskToViewStatusAndLogs: "点击任务可以查看状态和日志",
howToTroubleshootFailure: "执行失败如何排查", howToTroubleshootFailure: "执行失败如何排查",
viewErrorLogs: "查看错误日志", viewErrorLogs: "查看错误日志",
nginxContainerNotExistFix: "这里报的是nginx容器不存在修改命令改成正确的nginx容器名称即可", nginxContainerNotExistFix: "这里报的是nginx容器不存在修改命令改成正确的nginx容器名称即可",
executionSuccess: "执行成功", executionSuccess: "执行成功",
retryAfterFix: "修改正确后,重新点击手动触发,重新运行一次,执行成功", retryAfterFix: "修改正确后,重新点击手动触发,重新运行一次,执行成功",
autoSkipAfterSuccess: "成功后自动跳过", autoSkipAfterSuccess: "成功后自动跳过",
successSkipExplanation: "可以看到成功过的将会自动跳过,不会重复执行,只有当参数变更或者证书更新了,才会重新运行", successSkipExplanation: "可以看到成功过的将会自动跳过,不会重复执行,只有当参数变更或者证书更新了,才会重新运行",
viewCertDeploymentSuccess: "查看证书部署成功", viewCertDeploymentSuccess: "查看证书部署成功",
visitNginxToSeeCert: "访问nginx上的网站可以看到证书已经部署成功", visitNginxToSeeCert: "访问nginx上的网站可以看到证书已经部署成功",
downloadCertManualDeploy: "还可以下载证书,手动部署", downloadCertManualDeploy: "还可以下载证书,手动部署",
downloadIfNoAutoDeployPlugin: "如果还没有好用的部署插件,没办法自动部署,你还可以下载证书,手动部署", downloadIfNoAutoDeployPlugin: "如果还没有好用的部署插件,没办法自动部署,你还可以下载证书,手动部署",
}, },
scheduleAndEmailTask: { scheduleAndEmailTask: {
title: "设置定时执行和邮件通知", title: "设置定时执行和邮件通知",
description: "自动运行", description: "自动运行",
setSchedule: "设置定时执行", setSchedule: "设置定时执行",
pipelineSuccessThenSchedule: "流水线测试成功,接下来配置定时触发,以后每天定时执行就不用管了", pipelineSuccessThenSchedule: "流水线测试成功,接下来配置定时触发,以后每天定时执行就不用管了",
recommendDailyRun: "推荐配置每天运行一次在到期前35天才会重新申请新证书并部署没到期前会自动跳过不会重复申请。", recommendDailyRun: "推荐配置每天运行一次在到期前35天才会重新申请新证书并部署没到期前会自动跳过不会重复申请。",
setEmailNotification: "设置邮件通知", setEmailNotification: "设置邮件通知",
suggestErrorAndRecoveryEmails: "建议选择监听'错误时'和'错误转成功'两种即可,在意外失败时可以尽快去排查问题,(基础版需要配置邮件服务器)", suggestErrorAndRecoveryEmails: "建议选择监听'错误时'和'错误转成功'两种即可,在意外失败时可以尽快去排查问题,(基础版需要配置邮件服务器)",
basicVersionNeedsMailServer: "(基础版需要配置邮件服务器)", basicVersionNeedsMailServer: "(基础版需要配置邮件服务器)",
tutorialEndTitle: "教程结束", tutorialEndTitle: "教程结束",
thanksForWatching: "感谢观看,希望对你有所帮助", thanksForWatching: "感谢观看,希望对你有所帮助",
} },
} };

View File

@ -1,19 +1,19 @@
import certd from './certd'; import certd from "./certd";
import authentication from './authentication'; import authentication from "./authentication";
import vip from './vip'; import vip from "./vip";
import tutorial from './tutorial'; import tutorial from "./tutorial";
import preferences from './preferences'; import preferences from "./preferences";
import ui from './ui'; import ui from "./ui";
import guide from './guide'; import guide from "./guide";
import common from './common'; import common from "./common";
export default { export default {
certd, certd,
authentication, authentication,
vip, vip,
ui, ui,
tutorial, tutorial,
preferences, preferences,
guide, guide,
common common,
}; };

View File

@ -1,186 +1,186 @@
export default { export default {
"title": "偏好设置", title: "偏好设置",
"subtitle": "自定义偏好设置 & 实时预览", subtitle: "自定义偏好设置 & 实时预览",
"resetTitle": "重置偏好设置", resetTitle: "重置偏好设置",
"resetTip": "数据有变化,点击可进行重置", resetTip: "数据有变化,点击可进行重置",
"resetSuccess": "重置偏好设置成功", resetSuccess: "重置偏好设置成功",
"appearance": "外观", appearance: "外观",
"layout": "布局", layout: "布局",
"content": "内容", content: "内容",
"other": "其它", other: "其它",
"wide": "流式", wide: "流式",
"compact": "定宽", compact: "定宽",
"followSystem": "跟随系统", followSystem: "跟随系统",
"vertical": "垂直", vertical: "垂直",
"verticalTip": "侧边垂直菜单模式", verticalTip: "侧边垂直菜单模式",
"horizontal": "水平", horizontal: "水平",
"horizontalTip": "水平菜单模式,菜单全部显示在顶部", horizontalTip: "水平菜单模式,菜单全部显示在顶部",
"twoColumn": "双列菜单", twoColumn: "双列菜单",
"twoColumnTip": "垂直双列菜单模式", twoColumnTip: "垂直双列菜单模式",
"headerSidebarNav": "侧边导航", headerSidebarNav: "侧边导航",
"headerSidebarNavTip": "顶部通栏,侧边导航模式", headerSidebarNavTip: "顶部通栏,侧边导航模式",
"headerTwoColumn": "混合双列", headerTwoColumn: "混合双列",
"headerTwoColumnTip": "双列、水平菜单共存模式", headerTwoColumnTip: "双列、水平菜单共存模式",
"mixedMenu": "混合垂直", mixedMenu: "混合垂直",
"mixedMenuTip": "垂直水平菜单共存", mixedMenuTip: "垂直水平菜单共存",
"fullContent": "内容全屏", fullContent: "内容全屏",
"fullContentTip": "不显示任何菜单,只显示内容主体", fullContentTip: "不显示任何菜单,只显示内容主体",
"normal": "常规", normal: "常规",
"plain": "朴素", plain: "朴素",
"rounded": "圆润", rounded: "圆润",
"copyPreferences": "复制偏好设置", copyPreferences: "复制偏好设置",
"copyPreferencesSuccessTitle": "复制成功", copyPreferencesSuccessTitle: "复制成功",
"copyPreferencesSuccess": "复制成功,请在 app 下的 `src/preferences.ts`内进行覆盖", copyPreferencesSuccess: "复制成功,请在 app 下的 `src/preferences.ts`内进行覆盖",
"clearAndLogout": "清空缓存 & 退出登录", clearAndLogout: "清空缓存 & 退出登录",
"mode": "模式", mode: "模式",
"general": "通用", general: "通用",
"language": "语言", language: "语言",
"dynamicTitle": "动态标题", dynamicTitle: "动态标题",
"watermark": "水印", watermark: "水印",
"checkUpdates": "定时检查更新", checkUpdates: "定时检查更新",
"position": { position: {
"title": "偏好设置位置", title: "偏好设置位置",
"header": "顶栏", header: "顶栏",
"auto": "自动", auto: "自动",
"fixed": "固定" fixed: "固定",
}, },
"sidebar": { sidebar: {
"title": "侧边栏", title: "侧边栏",
"width": "宽度", width: "宽度",
"visible": "显示侧边栏", visible: "显示侧边栏",
"collapsed": "折叠菜单", collapsed: "折叠菜单",
"collapsedShowTitle": "折叠显示菜单名", collapsedShowTitle: "折叠显示菜单名",
"autoActivateChild": "自动激活子菜单", autoActivateChild: "自动激活子菜单",
"autoActivateChildTip": "点击顶层菜单时,自动激活第一个子菜单或者上一次激活的子菜单", autoActivateChildTip: "点击顶层菜单时,自动激活第一个子菜单或者上一次激活的子菜单",
"expandOnHover": "鼠标悬停展开", expandOnHover: "鼠标悬停展开",
"expandOnHoverTip": "鼠标在折叠区域悬浮时,`启用`则展开当前子菜单,`禁用`则展开整个侧边栏" expandOnHoverTip: "鼠标在折叠区域悬浮时,`启用`则展开当前子菜单,`禁用`则展开整个侧边栏",
}, },
"tabbar": { tabbar: {
"title": "标签栏", title: "标签栏",
"enable": "启用标签栏", enable: "启用标签栏",
"icon": "显示标签栏图标", icon: "显示标签栏图标",
"showMore": "显示更多按钮", showMore: "显示更多按钮",
"showMaximize": "显示最大化按钮", showMaximize: "显示最大化按钮",
"persist": "持久化标签页", persist: "持久化标签页",
"maxCount": "最大标签数", maxCount: "最大标签数",
"maxCountTip": "每次打开新的标签时如果超过最大标签数,\n会自动关闭一个最先打开的标签\n设置为 0 则不限制", maxCountTip: "每次打开新的标签时如果超过最大标签数,\n会自动关闭一个最先打开的标签\n设置为 0 则不限制",
"draggable": "启动拖拽排序", draggable: "启动拖拽排序",
"wheelable": "启用纵向滚轮响应", wheelable: "启用纵向滚轮响应",
"middleClickClose": "点击鼠标中键关闭标签页", middleClickClose: "点击鼠标中键关闭标签页",
"wheelableTip": "开启后,标签栏区域可以响应滚轮的纵向滚动事件。\n关闭时只能响应系统的横向滚动事件需要按下Shift再滚动滚轮", wheelableTip: "开启后,标签栏区域可以响应滚轮的纵向滚动事件。\n关闭时只能响应系统的横向滚动事件需要按下Shift再滚动滚轮",
"styleType": { styleType: {
"title": "标签页风格", title: "标签页风格",
"chrome": "谷歌", chrome: "谷歌",
"card": "卡片", card: "卡片",
"plain": "朴素", plain: "朴素",
"brisk": "轻快" brisk: "轻快",
}, },
"contextMenu": { contextMenu: {
"reload": "重新加载", reload: "重新加载",
"close": "关闭", close: "关闭",
"pin": "固定", pin: "固定",
"unpin": "取消固定", unpin: "取消固定",
"closeLeft": "关闭左侧标签页", closeLeft: "关闭左侧标签页",
"closeRight": "关闭右侧标签页", closeRight: "关闭右侧标签页",
"closeOther": "关闭其它标签页", closeOther: "关闭其它标签页",
"closeAll": "关闭全部标签页", closeAll: "关闭全部标签页",
"openInNewWindow": "在新窗口打开", openInNewWindow: "在新窗口打开",
"maximize": "最大化", maximize: "最大化",
"restoreMaximize": "还原" restoreMaximize: "还原",
} },
}, },
"navigationMenu": { navigationMenu: {
"title": "导航菜单", title: "导航菜单",
"style": "导航菜单风格", style: "导航菜单风格",
"accordion": "侧边导航菜单手风琴模式", accordion: "侧边导航菜单手风琴模式",
"split": "导航菜单分离", split: "导航菜单分离",
"splitTip": "开启时,侧边栏显示顶栏对应菜单的子菜单" splitTip: "开启时,侧边栏显示顶栏对应菜单的子菜单",
}, },
"breadcrumb": { breadcrumb: {
"title": "面包屑导航", title: "面包屑导航",
"enable": "开启面包屑导航", enable: "开启面包屑导航",
"icon": "显示面包屑图标", icon: "显示面包屑图标",
"home": "显示首页按钮", home: "显示首页按钮",
"style": "面包屑风格", style: "面包屑风格",
"hideOnlyOne": "仅有一个时隐藏", hideOnlyOne: "仅有一个时隐藏",
"background": "背景" background: "背景",
}, },
"animation": { animation: {
"title": "动画", title: "动画",
"loading": "页面切换 Loading", loading: "页面切换 Loading",
"transition": "页面切换动画", transition: "页面切换动画",
"progress": "页面切换进度条" progress: "页面切换进度条",
}, },
"theme": { theme: {
"title": "主题", title: "主题",
"radius": "圆角", radius: "圆角",
"light": "浅色", light: "浅色",
"dark": "深色", dark: "深色",
"darkSidebar": "深色侧边栏", darkSidebar: "深色侧边栏",
"darkHeader": "深色顶栏", darkHeader: "深色顶栏",
"weakMode": "色弱模式", weakMode: "色弱模式",
"grayMode": "灰色模式", grayMode: "灰色模式",
"builtin": { builtin: {
"title": "内置主题", title: "内置主题",
"default": "默认", default: "默认",
"violet": "紫罗兰", violet: "紫罗兰",
"pink": "樱花粉", pink: "樱花粉",
"rose": "玫瑰红", rose: "玫瑰红",
"skyBlue": "天蓝色", skyBlue: "天蓝色",
"deepBlue": "深蓝色", deepBlue: "深蓝色",
"green": "浅绿色", green: "浅绿色",
"deepGreen": "深绿色", deepGreen: "深绿色",
"orange": "橙黄色", orange: "橙黄色",
"yellow": "柠檬黄", yellow: "柠檬黄",
"zinc": "锌色灰", zinc: "锌色灰",
"neutral": "中性色", neutral: "中性色",
"slate": "石板灰", slate: "石板灰",
"gray": "中灰色", gray: "中灰色",
"custom": "自定义" custom: "自定义",
} },
}, },
"header": { header: {
"title": "顶栏", title: "顶栏",
"modeStatic": "静止", modeStatic: "静止",
"modeFixed": "固定", modeFixed: "固定",
"modeAuto": "自动隐藏和显示", modeAuto: "自动隐藏和显示",
"modeAutoScroll": "滚动隐藏和显示", modeAutoScroll: "滚动隐藏和显示",
"visible": "显示顶栏", visible: "显示顶栏",
"menuAlign": "菜单位置", menuAlign: "菜单位置",
"menuAlignStart": "左侧", menuAlignStart: "左侧",
"menuAlignEnd": "右侧", menuAlignEnd: "右侧",
"menuAlignCenter": "居中" menuAlignCenter: "居中",
}, },
"footer": { footer: {
"title": "底栏", title: "底栏",
"visible": "显示底栏", visible: "显示底栏",
"fixed": "固定在底部" fixed: "固定在底部",
}, },
"copyright": { copyright: {
"title": "版权", title: "版权",
"enable": "启用版权", enable: "启用版权",
"companyName": "公司名", companyName: "公司名",
"companySiteLink": "公司主页", companySiteLink: "公司主页",
"date": "日期", date: "日期",
"icp": "ICP 备案号", icp: "ICP 备案号",
"icpLink": "ICP 网站链接" icpLink: "ICP 网站链接",
}, },
"shortcutKeys": { shortcutKeys: {
"title": "快捷键", title: "快捷键",
"global": "全局", global: "全局",
"search": "全局搜索", search: "全局搜索",
"logout": "退出登录", logout: "退出登录",
"preferences": "偏好设置" preferences: "偏好设置",
}, },
"widget": { widget: {
"title": "小部件", title: "小部件",
"globalSearch": "启用全局搜索", globalSearch: "启用全局搜索",
"fullscreen": "启用全屏", fullscreen: "启用全屏",
"themeToggle": "启用主题切换", themeToggle: "启用主题切换",
"languageToggle": "启用语言切换", languageToggle: "启用语言切换",
"notification": "启用通知", notification: "启用通知",
"sidebarToggle": "启用侧边栏切换", sidebarToggle: "启用侧边栏切换",
"lockScreen": "启用锁屏", lockScreen: "启用锁屏",
"refresh": "启用刷新" refresh: "启用刷新",
} },
} };

View File

@ -1,3 +1,3 @@
export default { export default {
title: '使用教程', title: "使用教程",
} };

View File

@ -1,104 +1,104 @@
export default { export default {
"formRules": { formRules: {
"required": "请输入{0}", required: "请输入{0}",
"selectRequired": "请选择{0}", selectRequired: "请选择{0}",
"minLength": "{0}至少{1}个字符", minLength: "{0}至少{1}个字符",
"maxLength": "{0}最多{1}个字符", maxLength: "{0}最多{1}个字符",
"length": "{0}长度必须为{1}个字符", length: "{0}长度必须为{1}个字符",
"alreadyExists": "{0} `{1}` 已存在", alreadyExists: "{0} `{1}` 已存在",
"startWith": "{0}必须以 {1} 开头", startWith: "{0}必须以 {1} 开头",
"invalidURL": "请输入有效的链接" invalidURL: "请输入有效的链接",
}, },
"actionTitle": { actionTitle: {
"edit": "修改{0}", edit: "修改{0}",
"create": "新增{0}", create: "新增{0}",
"delete": "删除{0}", delete: "删除{0}",
"view": "查看{0}" view: "查看{0}",
}, },
"actionMessage": { actionMessage: {
"deleteConfirm": "确定删除 {0} 吗?", deleteConfirm: "确定删除 {0} 吗?",
"deleting": "正在删除 {0} ...", deleting: "正在删除 {0} ...",
"deleteSuccess": "{0} 删除成功", deleteSuccess: "{0} 删除成功",
"operationSuccess": "操作成功", operationSuccess: "操作成功",
"operationFailed": "操作失败" operationFailed: "操作失败",
}, },
"placeholder": { placeholder: {
"input": "请输入", input: "请输入",
"select": "请选择" select: "请选择",
}, },
"captcha": { captcha: {
"title": "请完成安全验证", title: "请完成安全验证",
"sliderSuccessText": "验证通过", sliderSuccessText: "验证通过",
"sliderDefaultText": "请按住滑块拖动", sliderDefaultText: "请按住滑块拖动",
"sliderRotateDefaultTip": "点击图片可刷新", sliderRotateDefaultTip: "点击图片可刷新",
"sliderRotateFailTip": "验证失败", sliderRotateFailTip: "验证失败",
"sliderRotateSuccessTip": "验证成功,耗时{0}秒", sliderRotateSuccessTip: "验证成功,耗时{0}秒",
"alt": "支持img标签src属性值", alt: "支持img标签src属性值",
"refreshAriaLabel": "刷新验证码", refreshAriaLabel: "刷新验证码",
"confirmAriaLabel": "确认选择", confirmAriaLabel: "确认选择",
"confirm": "确认", confirm: "确认",
"pointAriaLabel": "点击点", pointAriaLabel: "点击点",
"clickInOrder": "请依次点击" clickInOrder: "请依次点击",
}, },
"iconPicker": { iconPicker: {
"placeholder": "选择一个图标", placeholder: "选择一个图标",
"search": "搜索图标..." search: "搜索图标...",
}, },
"jsonViewer": { jsonViewer: {
"copy": "复制", copy: "复制",
"copied": "已复制" copied: "已复制",
}, },
"fallback": { fallback: {
"pageNotFound": "哎呀!未找到页面", pageNotFound: "哎呀!未找到页面",
"pageNotFoundDesc": "抱歉,我们无法找到您要找的页面。", pageNotFoundDesc: "抱歉,我们无法找到您要找的页面。",
"forbidden": "哎呀!访问被拒绝", forbidden: "哎呀!访问被拒绝",
"forbiddenDesc": "抱歉,您没有权限访问此页面。", forbiddenDesc: "抱歉,您没有权限访问此页面。",
"internalError": "哎呀!出错了", internalError: "哎呀!出错了",
"internalErrorDesc": "抱歉,服务器遇到错误。", internalErrorDesc: "抱歉,服务器遇到错误。",
"offline": "离线页面", offline: "离线页面",
"offlineError": "哎呀!网络错误", offlineError: "哎呀!网络错误",
"offlineErrorDesc": "抱歉,无法连接到互联网,请检查您的网络连接并重试。", offlineErrorDesc: "抱歉,无法连接到互联网,请检查您的网络连接并重试。",
"comingSoon": "即将推出", comingSoon: "即将推出",
"http": { http: {
"requestTimeout": "请求超时,请稍后再试。", requestTimeout: "请求超时,请稍后再试。",
"networkError": "网络异常,请检查您的网络连接后重试。", networkError: "网络异常,请检查您的网络连接后重试。",
"badRequest": "请求错误。请检查您的输入并重试。", badRequest: "请求错误。请检查您的输入并重试。",
"unauthorized": "登录认证过期,请重新登录后继续。", unauthorized: "登录认证过期,请重新登录后继续。",
"forbidden": "禁止访问, 您没有权限访问此资源。", forbidden: "禁止访问, 您没有权限访问此资源。",
"notFound": "未找到, 请求的资源不存在。", notFound: "未找到, 请求的资源不存在。",
"internalServerError": "内部服务器错误,请稍后再试。" internalServerError: "内部服务器错误,请稍后再试。",
} },
}, },
"widgets": { widgets: {
"document": "文档", document: "文档",
"qa": "问题 & 帮助", qa: "问题 & 帮助",
"setting": "设置", setting: "设置",
"logoutTip": "是否退出登录?", logoutTip: "是否退出登录?",
"viewAll": "查看所有消息", viewAll: "查看所有消息",
"notifications": "通知", notifications: "通知",
"markAllAsRead": "全部标记为已读", markAllAsRead: "全部标记为已读",
"clearNotifications": "清空", clearNotifications: "清空",
"checkUpdatesTitle": "新版本可用", checkUpdatesTitle: "新版本可用",
"checkUpdatesDescription": "点击刷新以获取最新版本", checkUpdatesDescription: "点击刷新以获取最新版本",
"search": { search: {
"title": "搜索", title: "搜索",
"searchNavigate": "搜索导航菜单", searchNavigate: "搜索导航菜单",
"select": "选择", select: "选择",
"navigate": "导航", navigate: "导航",
"close": "关闭", close: "关闭",
"noResults": "未找到搜索结果", noResults: "未找到搜索结果",
"noRecent": "没有搜索历史", noRecent: "没有搜索历史",
"recent": "搜索历史" recent: "搜索历史",
}, },
"lockScreen": { lockScreen: {
"title": "锁定屏幕", title: "锁定屏幕",
"screenButton": "锁定", screenButton: "锁定",
"password": "密码", password: "密码",
"placeholder": "请输入锁屏密码", placeholder: "请输入锁屏密码",
"unlock": "点击解锁", unlock: "点击解锁",
"errorPasswordTip": "密码错误,请重新输入", errorPasswordTip: "密码错误,请重新输入",
"backToLogin": "返回登录", backToLogin: "返回登录",
"entry": "进入系统" entry: "进入系统",
} },
} },
} };

View File

@ -1,86 +1,86 @@
export default { export default {
comm: { comm: {
name: "{vipLabel}已开通", name: "{vipLabel}已开通",
title: "到期时间:{expire}", title: "到期时间:{expire}",
nav: "{vipLabel}", nav: "{vipLabel}",
}, },
plus: { plus: {
name: "商业版功能", name: "商业版功能",
title: "升级商业版,获取商业授权", title: "升级商业版,获取商业授权",
}, },
free: { free: {
comm: { comm: {
name: "商业版功能", name: "商业版功能",
title: "升级商业版,获取商业授权", title: "升级商业版,获取商业授权",
}, },
button: { button: {
name: "专业版功能", name: "专业版功能",
title: "升级专业版享受更多VIP特权", title: "升级专业版享受更多VIP特权",
}, },
nav: { nav: {
name: "基础版", name: "基础版",
title: "升级专业版享受更多VIP特权", title: "升级专业版享受更多VIP特权",
}, },
}, },
enterCode: "请输入激活码", enterCode: "请输入激活码",
successTitle: "激活成功", successTitle: "激活成功",
successContent: "您已成功激活{vipLabel},有效期至:{expireDate}", successContent: "您已成功激活{vipLabel},有效期至:{expireDate}",
bindAccountTitle: "是否绑定袖手账号", bindAccountTitle: "是否绑定袖手账号",
bindAccountContent: "绑定账号后可以避免License丢失强烈建议绑定", bindAccountContent: "绑定账号后可以避免License丢失强烈建议绑定",
congratulations_vip_trial: '恭喜,您已获得专业版{duration}天试用', congratulations_vip_trial: "恭喜,您已获得专业版{duration}天试用",
trial_modal_title: '7天专业版试用获取', trial_modal_title: "7天专业版试用获取",
trial_modal_ok_text: '立即获取', trial_modal_ok_text: "立即获取",
trial_modal_thanks: '感谢您对开源项目的支持', trial_modal_thanks: "感谢您对开源项目的支持",
trial_modal_click_confirm: '点击确认即可获取7天专业版试用', trial_modal_click_confirm: "点击确认即可获取7天专业版试用",
get_7_day_pro_trial: "7天专业版试用获取", get_7_day_pro_trial: "7天专业版试用获取",
star_now: "立即去Star", star_now: "立即去Star",
please_help_star: "可以先请您帮忙点个star吗感谢感谢", please_help_star: "可以先请您帮忙点个star吗感谢感谢",
admin_only_operation: "仅限管理员操作", admin_only_operation: "仅限管理员操作",
enter_activation_code: "请输入激活码", enter_activation_code: "请输入激活码",
activate_pro_business: "激活专业版/商业版", activate_pro_business: "激活专业版/商业版",
renew_business: "续期商业版", renew_business: "续期商业版",
renew_pro_upgrade_business: "续期专业版/升级商业版", renew_pro_upgrade_business: "续期专业版/升级商业版",
basic_edition: "基础版", basic_edition: "基础版",
community_free_version: "社区免费版", community_free_version: "社区免费版",
unlimited_certificate_application: "证书申请无限制", unlimited_certificate_application: "证书申请无限制",
unlimited_domain_count: "域名数量无限制", unlimited_domain_count: "域名数量无限制",
unlimited_certificate_pipelines: "证书流水线数量无限制", unlimited_certificate_pipelines: "证书流水线数量无限制",
common_deployment_plugins: "常用的主机、云平台、cdn、宝塔、1Panel等部署插件", common_deployment_plugins: "常用的主机、云平台、cdn、宝塔、1Panel等部署插件",
email_webhook_notifications: "邮件、webhook通知方式", email_webhook_notifications: "邮件、webhook通知方式",
professional_edition: "专业版", professional_edition: "专业版",
open_source_support: "开源需要您的赞助支持", open_source_support: "开源需要您的赞助支持",
vip_group_priority: "可加VIP群您的需求将优先实现", vip_group_priority: "可加VIP群您的需求将优先实现",
unlimited_site_certificate_monitoring: "站点证书监控无限制", unlimited_site_certificate_monitoring: "站点证书监控无限制",
more_notification_methods: "更多通知方式", more_notification_methods: "更多通知方式",
plugins_fully_open: "插件全开放,群辉等更多插件", plugins_fully_open: "插件全开放,群辉等更多插件",
click_to_get_7_day_trial: "点击获取7天试用", click_to_get_7_day_trial: "点击获取7天试用",
years: "年", years: "年",
afdian_support_vip: '爱发电赞助“VIP会员”后获取一年期专业版激活码开源需要您的支持', afdian_support_vip: "爱发电赞助“VIP会员”后获取一年期专业版激活码开源需要您的支持",
get_after_support: "爱发电赞助后获取", get_after_support: "爱发电赞助后获取",
business_edition: "商业版", business_edition: "商业版",
commercial_license: "商业授权,可对外运营", commercial_license: "商业授权,可对外运营",
all_pro_privileges: "拥有专业版所有特权", all_pro_privileges: "拥有专业版所有特权",
allow_commercial_use_modify_logo_title: "允许商用可修改logo、标题", allow_commercial_use_modify_logo_title: "允许商用可修改logo、标题",
data_statistics: "数据统计", data_statistics: "数据统计",
plugin_management: "插件管理", plugin_management: "插件管理",
unlimited_multi_users: "多用户无限制", unlimited_multi_users: "多用户无限制",
support_user_payment: "支持用户支付", support_user_payment: "支持用户支付",
contact_author_for_trial: "请联系作者获取试用", contact_author_for_trial: "请联系作者获取试用",
activate: "激活", activate: "激活",
get_pro_code_after_support: '爱发电赞助“VIP会员”后获取一年期专业版激活码', get_pro_code_after_support: "爱发电赞助“VIP会员”后获取一年期专业版激活码",
business_contact_author: "商业版请直接联系作者", business_contact_author: "商业版请直接联系作者",
year: "年", year: "年",
freee: "免费", freee: "免费",
renew: "续期", renew: "续期",
activate_immediately: "立刻激活", activate_immediately: "立刻激活",
current: "当前", current: "当前",
activated_expire_time: "已激活,到期时间:", activated_expire_time: "已激活,到期时间:",
site_id: "站点ID", site_id: "站点ID",
invite_code_optional: "邀请码【选填】可额外获得专业版30天/商业版15天时长", invite_code_optional: "邀请码【选填】可额外获得专业版30天/商业版15天时长",
no_activation_code: "没有激活码?", no_activation_code: "没有激活码?",
activation_code_one_use: "激活码使用过一次之后,不可再次使用,如果要更换站点,请", activation_code_one_use: "激活码使用过一次之后,不可再次使用,如果要更换站点,请",
bind_account: "绑定账号", bind_account: "绑定账号",
transfer_vip: ',然后"转移VIP"即可', transfer_vip: ',然后"转移VIP"即可',
} };

View File

@ -12,7 +12,7 @@ async function generateAccess(options: GenerateMenuAndRoutesOptions) {
const layoutMap: ComponentRecordType = { const layoutMap: ComponentRecordType = {
BasicLayout, BasicLayout,
IFrameView IFrameView,
} as any; } as any;
return await generateAccessible(preferences.app.accessMode, { return await generateAccessible(preferences.app.accessMode, {
@ -28,7 +28,7 @@ async function generateAccess(options: GenerateMenuAndRoutesOptions) {
forbiddenComponent, forbiddenComponent,
// 如果 route.meta.menuVisibleWithForbidden = true // 如果 route.meta.menuVisibleWithForbidden = true
layoutMap, layoutMap,
pageMap pageMap,
}); });
} }

View File

@ -1,44 +1,44 @@
import LayoutBasic from "/@/layout/layout-basic.vue"; import LayoutBasic from "/@/layout/layout-basic.vue";
import type { RouteRecordRaw } from "vue-router"; import type { RouteRecordRaw } from "vue-router";
import i18n from '/@/locales/i18n'; import i18n from "/@/locales/i18n";
import { mergeRouteModules } from "/@/vben/utils"; import { mergeRouteModules } from "/@/vben/utils";
const dynamicRouteFiles = import.meta.glob("./modules/**/*.ts*", { const dynamicRouteFiles = import.meta.glob("./modules/**/*.ts*", {
eager: true, eager: true,
}); });
/** 动态路由 */ /** 动态路由 */
const dynamicRoutes: RouteRecordRaw[] = mergeRouteModules(dynamicRouteFiles); const dynamicRoutes: RouteRecordRaw[] = mergeRouteModules(dynamicRouteFiles);
export const frameworkResource = [ export const frameworkResource = [
{ {
title: i18n.global.t("certd.framework.title"), title: "certd.framework.title",
name: "root", name: "root",
path: "/", path: "/",
redirect: "/index", redirect: "/index",
component: LayoutBasic, component: LayoutBasic,
meta: { meta: {
icon: "ion:accessibility", icon: "ion:accessibility",
hideInBreadcrumb: true, hideInBreadcrumb: true,
}, },
children: [ children: [
{ {
title: i18n.global.t("certd.framework.home"), title: "certd.framework.home",
name: "index", name: "index",
path: "/index", path: "/index",
component: "/framework/home/index.vue", component: "/framework/home/index.vue",
meta: { meta: {
fixedAside: true, fixedAside: true,
showOnHeader: false, showOnHeader: false,
icon: "ion:home-outline", icon: "ion:home-outline",
auth: true, auth: true,
}, },
}, },
// @ts-ignore // @ts-ignore
...dynamicRoutes, ...dynamicRoutes,
], ],
}, },
]; ];
console.assert(frameworkResource.length === 1, "frameworkResource数组长度只能为1你只能配置framework路由的子路由"); console.assert(frameworkResource.length === 1, "frameworkResource数组长度只能为1你只能配置framework路由的子路由");

View File

@ -1,36 +1,35 @@
import i18n from '/@/locales/i18n'; import i18n from "/@/locales/i18n";
export const headerResource = [ export const headerResource = [
{ {
title: i18n.global.t("certd.helpDoc"), title: "certd.helpDoc",
path: "https://certd.docmirror.cn", path: "https://certd.docmirror.cn",
meta: { meta: {
icon: "ion:document-text-outline" icon: "ion:document-text-outline",
} },
}, },
{ {
title: i18n.global.t("certd.source"), title: "certd.source",
name: "source", name: "source",
key: "source", key: "source",
meta: { meta: {
icon: "ion:git-branch-outline" icon: "ion:git-branch-outline",
}, },
children: [ children: [
{ {
title: i18n.global.t("certd.github"), title: "certd.github",
path: "https://github.com/certd/certd", path: "https://github.com/certd/certd",
meta: { meta: {
icon: "ion:logo-github" icon: "ion:logo-github",
} },
}, },
{ {
title: i18n.global.t("certd.gitee"), title: "certd.gitee",
path: "https://gitee.com/certd/certd", path: "https://gitee.com/certd/certd",
meta: { meta: {
icon: "ion:logo-octocat" icon: "ion:logo-octocat",
} },
} },
] ],
} },
]; ];

View File

@ -2,25 +2,25 @@ import { IFrameView } from "/@/vben/layouts";
import { useSettingStore } from "/@/store/settings"; import { useSettingStore } from "/@/store/settings";
import { computed } from "vue"; import { computed } from "vue";
import TutorialButton from "/@/components/tutorial/index.vue"; import TutorialButton from "/@/components/tutorial/index.vue";
import i18n from '/@/locales/i18n'; import i18n from "/@/locales/i18n";
export const aboutResource = [ export const aboutResource = [
{ {
title: i18n.global.t("certd.dashboard.helpDoc"), title: "certd.dashboard.helpDoc",
name: "document", name: "document",
path: "/about/doc", path: "/about/doc",
component: IFrameView, component: IFrameView,
meta: { meta: {
icon: "lucide:book-open-text", icon: "lucide:book-open-text",
link: "https://certd.docmirror.cn", link: "https://certd.docmirror.cn",
title: i18n.global.t("certd.dashboard.helpDoc"), title: "certd.dashboard.helpDoc",
order: 9999, order: 9999,
show: () => { show: () => {
const settingStore = useSettingStore(); const settingStore = useSettingStore();
return !settingStore.isComm; return !settingStore.isComm;
}, },
}, },
}, },
]; ];
export default aboutResource; export default aboutResource;

View File

@ -1,256 +1,255 @@
import { useSettingStore } from "/@/store/settings"; import { useSettingStore } from "/@/store/settings";
import aboutResource from "/@/router/source/modules/about"; import aboutResource from "/@/router/source/modules/about";
import i18n from '/@/locales/i18n'; import i18n from "/@/locales/i18n";
export const certdResources = [ export const certdResources = [
{ {
title: i18n.global.t("certd.title"), title: "certd.title",
name: "CertdRoot", name: "CertdRoot",
path: "/certd", path: "/certd",
redirect: "/certd/pipeline", redirect: "/certd/pipeline",
meta: { meta: {
icon: "ion:key-outline", icon: "ion:key-outline",
auth: true, auth: true,
order: 0, order: 0,
}, },
children: [ children: [
{ {
title: i18n.global.t("certd.pipeline"), title: "certd.pipeline",
name: "PipelineManager", name: "PipelineManager",
path: "/certd/pipeline", path: "/certd/pipeline",
component: "/certd/pipeline/index.vue", component: "/certd/pipeline/index.vue",
meta: { meta: {
icon: "ion:analytics-sharp", icon: "ion:analytics-sharp",
keepAlive: true, keepAlive: true,
}, },
}, },
{ {
title: i18n.global.t("certd.pipelineEdit"), title: "certd.pipelineEdit",
name: "PipelineEdit", name: "PipelineEdit",
path: "/certd/pipeline/detail", path: "/certd/pipeline/detail",
component: "/certd/pipeline/detail.vue", component: "/certd/pipeline/detail.vue",
meta: { meta: {
isMenu: false, isMenu: false,
}, },
}, },
{ {
title: i18n.global.t("certd.history"), title: "certd.history",
name: "PipelineHistory", name: "PipelineHistory",
path: "/certd/history", path: "/certd/history",
component: "/certd/history/index.vue", component: "/certd/history/index.vue",
meta: { meta: {
icon: "ion:timer-outline", icon: "ion:timer-outline",
keepAlive: true, keepAlive: true,
}, },
}, },
{ {
title: i18n.global.t("certd.certStore"), title: "certd.certStore",
name: "CertStore", name: "CertStore",
path: "/certd/monitor/cert", path: "/certd/monitor/cert",
component: "/certd/monitor/cert/index.vue", component: "/certd/monitor/cert/index.vue",
meta: { meta: {
icon: "ion:shield-checkmark-outline", icon: "ion:shield-checkmark-outline",
auth: true, auth: true,
isMenu: true, isMenu: true,
keepAlive: true, keepAlive: true,
}, },
}, },
{ {
title: i18n.global.t("certd.siteMonitor"), title: "certd.siteMonitor",
name: "SiteCertMonitor", name: "SiteCertMonitor",
path: "/certd/monitor/site", path: "/certd/monitor/site",
component: "/certd/monitor/site/index.vue", component: "/certd/monitor/site/index.vue",
meta: { meta: {
icon: "ion:videocam-outline", icon: "ion:videocam-outline",
auth: true, auth: true,
keepAlive: true, keepAlive: true,
}, },
}, },
{ {
title: i18n.global.t("certd.settings"), title: "certd.settings",
name: "MineSetting", name: "MineSetting",
path: "/certd/setting", path: "/certd/setting",
redirect: "/certd/access", redirect: "/certd/access",
meta: { meta: {
icon: "ion:settings-outline", icon: "ion:settings-outline",
auth: true, auth: true,
keepAlive: true, keepAlive: true,
}, },
children: [ children: [
{ {
title: i18n.global.t("certd.accessManager"), title: "certd.accessManager",
name: "AccessManager", name: "AccessManager",
path: "/certd/access", path: "/certd/access",
component: "/certd/access/index.vue", component: "/certd/access/index.vue",
meta: { meta: {
icon: "ion:disc-outline", icon: "ion:disc-outline",
auth: true, auth: true,
keepAlive: true, keepAlive: true,
}, },
}, },
{ {
title: i18n.global.t("certd.cnameRecord"), title: "certd.cnameRecord",
name: "CnameRecord", name: "CnameRecord",
path: "/certd/cname/record", path: "/certd/cname/record",
component: "/certd/cname/record/index.vue", component: "/certd/cname/record/index.vue",
meta: { meta: {
icon: "ion:link-outline", icon: "ion:link-outline",
auth: true, auth: true,
keepAlive: true, keepAlive: true,
}, },
}, },
{ {
title: i18n.global.t("certd.subDomain"), title: "certd.subDomain",
name: "SubDomain", name: "SubDomain",
path: "/certd/pipeline/subDomain", path: "/certd/pipeline/subDomain",
component: "/certd/pipeline/sub-domain/index.vue", component: "/certd/pipeline/sub-domain/index.vue",
meta: { meta: {
icon: "material-symbols:approval-delegation-outline", icon: "material-symbols:approval-delegation-outline",
auth: true, auth: true,
keepAlive: true, keepAlive: true,
}, },
}, },
{ {
title: i18n.global.t("certd.pipelineGroup"), title: "certd.pipelineGroup",
name: "PipelineGroupManager", name: "PipelineGroupManager",
path: "/certd/pipeline/group", path: "/certd/pipeline/group",
component: "/certd/pipeline/group/index.vue", component: "/certd/pipeline/group/index.vue",
meta: { meta: {
icon: "mdi:format-list-group", icon: "mdi:format-list-group",
auth: true, auth: true,
keepAlive: true, keepAlive: true,
}, },
}, },
{ {
title: i18n.global.t("certd.openKey"), title: "certd.openKey",
name: "OpenKey", name: "OpenKey",
path: "/certd/open/openkey", path: "/certd/open/openkey",
component: "/certd/open/openkey/index.vue", component: "/certd/open/openkey/index.vue",
meta: { meta: {
icon: "hugeicons:api", icon: "hugeicons:api",
auth: true, auth: true,
keepAlive: true, keepAlive: true,
}, },
}, },
{ {
title: i18n.global.t("certd.notification"), title: "certd.notification",
name: "NotificationManager", name: "NotificationManager",
path: "/certd/notification", path: "/certd/notification",
component: "/certd/notification/index.vue", component: "/certd/notification/index.vue",
meta: { meta: {
icon: "ion:megaphone-outline", icon: "ion:megaphone-outline",
auth: true, auth: true,
keepAlive: true, keepAlive: true,
}, },
}, },
{ {
title: i18n.global.t("certd.siteMonitorSetting"), title: "certd.siteMonitorSetting",
name: "SiteMonitorSetting", name: "SiteMonitorSetting",
path: "/certd/monitor/setting", path: "/certd/monitor/setting",
component: "/certd/monitor/site/setting/index.vue", component: "/certd/monitor/site/setting/index.vue",
meta: { meta: {
icon: "ion:videocam-outline", icon: "ion:videocam-outline",
auth: true, auth: true,
isMenu: true, isMenu: true,
}, },
}, },
{ {
title: i18n.global.t("certd.userSecurity"), title: "certd.userSecurity",
name: "UserSecurity", name: "UserSecurity",
path: "/certd/mine/security", path: "/certd/mine/security",
component: "/certd/mine/security/index.vue", component: "/certd/mine/security/index.vue",
meta: { meta: {
icon: "fluent:shield-keyhole-16-regular", icon: "fluent:shield-keyhole-16-regular",
auth: true, auth: true,
isMenu: true, isMenu: true,
}, },
}, },
{ {
title: i18n.global.t("certd.userProfile"), title: "certd.userProfile",
name: "UserProfile", name: "UserProfile",
path: "/certd/mine/user-profile", path: "/certd/mine/user-profile",
component: "/certd/mine/user-profile.vue", component: "/certd/mine/user-profile.vue",
meta: { meta: {
icon: "ion:person-outline", icon: "ion:person-outline",
auth: true, auth: true,
isMenu: false, isMenu: false,
}, },
}, },
], ],
}, },
{ {
title: i18n.global.t("certd.suite"), title: "certd.suite",
name: "SuiteProduct", name: "SuiteProduct",
path: "/certd/suite", path: "/certd/suite",
redirect: "/certd/suite/mine", redirect: "/certd/suite/mine",
meta: { meta: {
show: () => { show: () => {
const settingStore = useSettingStore(); const settingStore = useSettingStore();
return settingStore.isComm && settingStore.isSuiteEnabled; return settingStore.isComm && settingStore.isSuiteEnabled;
}, },
icon: "ion:cart-outline", icon: "ion:cart-outline",
auth: true, auth: true,
}, },
children: [ children: [
{ {
title: i18n.global.t("certd.mySuite"), title: "certd.mySuite",
name: "MySuite", name: "MySuite",
path: "/certd/suite/mine", path: "/certd/suite/mine",
component: "/certd/suite/mine/index.vue", component: "/certd/suite/mine/index.vue",
meta: { meta: {
show: () => { show: () => {
const settingStore = useSettingStore(); const settingStore = useSettingStore();
return settingStore.isComm; return settingStore.isComm;
}, },
icon: "ion:gift-outline", icon: "ion:gift-outline",
auth: true, auth: true,
}, },
}, },
{ {
title: i18n.global.t("certd.suiteBuy"), title: "certd.suiteBuy",
name: "SuiteProductBuy", name: "SuiteProductBuy",
path: "/certd/suite/buy", path: "/certd/suite/buy",
component: "/certd/suite/buy.vue", component: "/certd/suite/buy.vue",
meta: { meta: {
show: () => { show: () => {
const settingStore = useSettingStore(); const settingStore = useSettingStore();
return settingStore.isComm; return settingStore.isComm;
}, },
icon: "ion:cart-outline", icon: "ion:cart-outline",
auth: true, auth: true,
}, },
}, },
{ {
title: i18n.global.t("certd.myTrade"), title: "certd.myTrade",
name: "MyTrade", name: "MyTrade",
path: "/certd/trade", path: "/certd/trade",
component: "/certd/trade/index.vue", component: "/certd/trade/index.vue",
meta: { meta: {
show: () => { show: () => {
const settingStore = useSettingStore(); const settingStore = useSettingStore();
return settingStore.isComm; return settingStore.isComm;
}, },
icon: "ion:bag-check-outline", icon: "ion:bag-check-outline",
auth: true, auth: true,
keepAlive: true, keepAlive: true,
}, },
}, },
{ {
title: i18n.global.t("certd.paymentReturn"), title: "certd.paymentReturn",
name: "PaymentReturn", name: "PaymentReturn",
path: "/certd/payment/return/:type", path: "/certd/payment/return/:type",
component: "/certd/payment/return.vue", component: "/certd/payment/return.vue",
meta: { meta: {
icon: "ant-design:pay-circle-outlined", icon: "ant-design:pay-circle-outlined",
auth: false, auth: false,
isMenu: false, isMenu: false,
}, },
}, },
], ],
}, },
], ],
}, },
]; ];
export default certdResources; export default certdResources;

View File

@ -1,256 +1,256 @@
import LayoutPass from "/@/layout/layout-pass.vue"; import LayoutPass from "/@/layout/layout-pass.vue";
import { useSettingStore } from "/@/store/settings"; import { useSettingStore } from "/@/store/settings";
import aboutResource from "/@/router/source/modules/about"; import aboutResource from "/@/router/source/modules/about";
import i18n from '/@/locales/i18n'; import i18n from "/@/locales/i18n";
export const sysResources = [ export const sysResources = [
{ {
title: i18n.global.t('certd.sysResources.sysRoot'), title: "certd.sysResources.sysRoot",
name: "SysRoot", name: "SysRoot",
path: "/sys", path: "/sys",
redirect: "/sys/settings", redirect: "/sys/settings",
meta: { meta: {
icon: "ion:settings-outline", icon: "ion:settings-outline",
permission: "sys:settings:view", permission: "sys:settings:view",
order: 10, order: 10,
}, },
children: [ children: [
{ {
title: i18n.global.t('certd.sysResources.sysConsole'), title: "certd.sysResources.sysConsole",
name: "SysConsole", name: "SysConsole",
path: "/sys/console", path: "/sys/console",
component: "/sys/console/index.vue", component: "/sys/console/index.vue",
meta: { meta: {
show: () => { show: () => {
const settingStore = useSettingStore(); const settingStore = useSettingStore();
return settingStore.isComm; return settingStore.isComm;
}, },
icon: "ion:speedometer-outline", icon: "ion:speedometer-outline",
permission: "sys:auth:user:view", permission: "sys:auth:user:view",
}, },
}, },
{ {
title: i18n.global.t('certd.sysResources.sysSettings'), title: "certd.sysResources.sysSettings",
name: "SysSettings", name: "SysSettings",
path: "/sys/settings", path: "/sys/settings",
component: "/sys/settings/index.vue", component: "/sys/settings/index.vue",
meta: { meta: {
icon: "ion:settings-outline", icon: "ion:settings-outline",
permission: "sys:settings:view", permission: "sys:settings:view",
}, },
}, },
{ {
title: i18n.global.t('certd.sysResources.cnameSetting'), title: "certd.sysResources.cnameSetting",
name: "CnameSetting", name: "CnameSetting",
path: "/sys/cname/provider", path: "/sys/cname/provider",
component: "/sys/cname/provider/index.vue", component: "/sys/cname/provider/index.vue",
meta: { meta: {
icon: "ion:earth-outline", icon: "ion:earth-outline",
permission: "sys:settings:view", permission: "sys:settings:view",
keepAlive: true, keepAlive: true,
}, },
}, },
{ {
title: i18n.global.t('certd.sysResources.emailSetting'), title: "certd.sysResources.emailSetting",
name: "EmailSetting", name: "EmailSetting",
path: "/sys/settings/email", path: "/sys/settings/email",
component: "/sys/settings/email/index.vue", component: "/sys/settings/email/index.vue",
meta: { meta: {
permission: "sys:settings:view", permission: "sys:settings:view",
icon: "ion:mail-outline", icon: "ion:mail-outline",
auth: true, auth: true,
}, },
}, },
{ {
title: i18n.global.t('certd.sysResources.siteSetting'), title: "certd.sysResources.siteSetting",
name: "SiteSetting", name: "SiteSetting",
path: "/sys/site", path: "/sys/site",
component: "/sys/site/index.vue", component: "/sys/site/index.vue",
meta: { meta: {
show: () => { show: () => {
const settingStore = useSettingStore(); const settingStore = useSettingStore();
return settingStore.isComm; return settingStore.isComm;
}, },
icon: "ion:document-text-outline", icon: "ion:document-text-outline",
permission: "sys:settings:view", permission: "sys:settings:view",
}, },
}, },
{ {
title: i18n.global.t('certd.sysResources.headerMenus'), title: "certd.sysResources.headerMenus",
name: "HeaderMenus", name: "HeaderMenus",
path: "/sys/settings/header-menus", path: "/sys/settings/header-menus",
component: "/sys/settings/header-menus/index.vue", component: "/sys/settings/header-menus/index.vue",
meta: { meta: {
show: () => { show: () => {
const settingStore = useSettingStore(); const settingStore = useSettingStore();
return settingStore.isComm; return settingStore.isComm;
}, },
icon: "ion:menu", icon: "ion:menu",
permission: "sys:settings:view", permission: "sys:settings:view",
keepAlive: true, keepAlive: true,
}, },
}, },
{ {
title: i18n.global.t('certd.sysResources.sysAccess'), title: "certd.sysResources.sysAccess",
name: "SysAccess", name: "SysAccess",
path: "/sys/access", path: "/sys/access",
component: "/sys/access/index.vue", component: "/sys/access/index.vue",
meta: { meta: {
show: () => { show: () => {
const settingStore = useSettingStore(); const settingStore = useSettingStore();
return settingStore.isComm; return settingStore.isComm;
}, },
icon: "ion:disc-outline", icon: "ion:disc-outline",
permission: "sys:settings:view", permission: "sys:settings:view",
keepAlive: true, keepAlive: true,
}, },
}, },
{ {
title: i18n.global.t('certd.sysResources.sysPlugin'), title: "certd.sysResources.sysPlugin",
name: "SysPlugin", name: "SysPlugin",
path: "/sys/plugin", path: "/sys/plugin",
component: "/sys/plugin/index.vue", component: "/sys/plugin/index.vue",
meta: { meta: {
icon: "ion:extension-puzzle-outline", icon: "ion:extension-puzzle-outline",
permission: "sys:settings:view", permission: "sys:settings:view",
keepAlive: true, keepAlive: true,
}, },
}, },
{ {
title: i18n.global.t('certd.sysResources.sysPluginEdit'), title: "certd.sysResources.sysPluginEdit",
name: "SysPluginEdit", name: "SysPluginEdit",
path: "/sys/plugin/edit", path: "/sys/plugin/edit",
component: "/sys/plugin/edit.vue", component: "/sys/plugin/edit.vue",
meta: { meta: {
isMenu: false, isMenu: false,
icon: "ion:extension-puzzle", icon: "ion:extension-puzzle",
permission: "sys:settings:view", permission: "sys:settings:view",
keepAlive: true, keepAlive: true,
}, },
}, },
{ {
title: i18n.global.t('certd.sysResources.sysPluginConfig'), title: "certd.sysResources.sysPluginConfig",
name: "SysPluginConfig", name: "SysPluginConfig",
path: "/sys/plugin/config", path: "/sys/plugin/config",
component: "/sys/plugin/config.vue", component: "/sys/plugin/config.vue",
meta: { meta: {
show: () => { show: () => {
const settingStore = useSettingStore(); const settingStore = useSettingStore();
return settingStore.isComm; return settingStore.isComm;
}, },
icon: "ion:extension-puzzle", icon: "ion:extension-puzzle",
permission: "sys:settings:view", permission: "sys:settings:view",
}, },
}, },
{ {
title: i18n.global.t('certd.sysResources.accountBind'), title: "certd.sysResources.accountBind",
name: "AccountBind", name: "AccountBind",
path: "/sys/account", path: "/sys/account",
component: "/sys/account/index.vue", component: "/sys/account/index.vue",
meta: { meta: {
icon: "ion:golf-outline", icon: "ion:golf-outline",
permission: "sys:settings:view", permission: "sys:settings:view",
keepAlive: true, keepAlive: true,
}, },
}, },
{ {
title: i18n.global.t('certd.sysResources.permissionManager'), title: "certd.sysResources.permissionManager",
name: "PermissionManager", name: "PermissionManager",
path: "/sys/authority/permission", path: "/sys/authority/permission",
component: "/sys/authority/permission/index.vue", component: "/sys/authority/permission/index.vue",
meta: { meta: {
icon: "ion:list-outline", icon: "ion:list-outline",
permission: "sys:auth:per:view", permission: "sys:auth:per:view",
keepAlive: true, keepAlive: true,
}, },
}, },
{ {
title: i18n.global.t('certd.sysResources.roleManager'), title: "certd.sysResources.roleManager",
name: "RoleManager", name: "RoleManager",
path: "/sys/authority/role", path: "/sys/authority/role",
component: "/sys/authority/role/index.vue", component: "/sys/authority/role/index.vue",
meta: { meta: {
icon: "ion:people-outline", icon: "ion:people-outline",
permission: "sys:auth:role:view", permission: "sys:auth:role:view",
keepAlive: true, keepAlive: true,
}, },
}, },
{ {
title: i18n.global.t('certd.sysResources.userManager'), title: "certd.sysResources.userManager",
name: "UserManager", name: "UserManager",
path: "/sys/authority/user", path: "/sys/authority/user",
component: "/sys/authority/user/index.vue", component: "/sys/authority/user/index.vue",
meta: { meta: {
icon: "ion:person-outline", icon: "ion:person-outline",
permission: "sys:auth:user:view", permission: "sys:auth:user:view",
keepAlive: true, keepAlive: true,
}, },
}, },
{ {
title: i18n.global.t('certd.sysResources.suiteManager'), title: "certd.sysResources.suiteManager",
name: "SuiteManager", name: "SuiteManager",
path: "/sys/suite", path: "/sys/suite",
redirect: "/sys/suite/setting", redirect: "/sys/suite/setting",
meta: { meta: {
icon: "ion:cart-outline", icon: "ion:cart-outline",
permission: "sys:settings:edit", permission: "sys:settings:edit",
show: () => { show: () => {
const settingStore = useSettingStore(); const settingStore = useSettingStore();
return settingStore.isComm; return settingStore.isComm;
}, },
keepAlive: true, keepAlive: true,
}, },
children: [ children: [
{ {
title: i18n.global.t('certd.sysResources.suiteSetting'), title: "certd.sysResources.suiteSetting",
name: "SuiteSetting", name: "SuiteSetting",
path: "/sys/suite/setting", path: "/sys/suite/setting",
component: "/sys/suite/setting/index.vue", component: "/sys/suite/setting/index.vue",
meta: { meta: {
show: () => { show: () => {
const settingStore = useSettingStore(); const settingStore = useSettingStore();
return settingStore.isComm; return settingStore.isComm;
}, },
icon: "ion:cart", icon: "ion:cart",
permission: "sys:settings:edit", permission: "sys:settings:edit",
}, },
}, },
{ {
title: i18n.global.t('certd.sysResources.orderManager'), title: "certd.sysResources.orderManager",
name: "OrderManager", name: "OrderManager",
path: "/sys/suite/trade", path: "/sys/suite/trade",
component: "/sys/suite/trade/index.vue", component: "/sys/suite/trade/index.vue",
meta: { meta: {
show: () => { show: () => {
const settingStore = useSettingStore(); const settingStore = useSettingStore();
return settingStore.isComm; return settingStore.isComm;
}, },
icon: "ion:bag-check", icon: "ion:bag-check",
permission: "sys:settings:edit", permission: "sys:settings:edit",
keepAlive: true, keepAlive: true,
}, },
}, },
{ {
title: i18n.global.t('certd.sysResources.userSuites'), title: "certd.sysResources.userSuites",
name: "UserSuites", name: "UserSuites",
path: "/sys/suite/user-suite", path: "/sys/suite/user-suite",
component: "/sys/suite/user-suite/index.vue", component: "/sys/suite/user-suite/index.vue",
meta: { meta: {
show: () => { show: () => {
const settingStore = useSettingStore(); const settingStore = useSettingStore();
return settingStore.isComm; return settingStore.isComm;
}, },
icon: "ion:gift-outline", icon: "ion:gift-outline",
auth: true, auth: true,
keepAlive: true, keepAlive: true,
}, },
}, },
], ],
}, },
], ],
}, },
]; ];
export default sysResources; export default sysResources;

View File

@ -10,21 +10,21 @@ export const outsideResource = [
children: [ children: [
{ {
meta: { meta: {
title: "登录" title: "登录",
}, },
name: "login", name: "login",
path: "/login", path: "/login",
component: "/framework/login/index.vue" component: "/framework/login/index.vue",
}, },
{ {
meta: { meta: {
title: "注册" title: "注册",
}, },
name: "register", name: "register",
path: "/register", path: "/register",
component: "/framework/register/index.vue" component: "/framework/register/index.vue",
} },
] ],
}, },
...errorPage ...errorPage,
]; ];

View File

@ -1,161 +1,163 @@
import { defineStore } from "pinia"; import { defineStore } from "pinia";
import * as api from "./api.plugin"; import * as api from "./api.plugin";
import { DynamicType, FormItemProps } from "@fast-crud/fast-crud"; import { DynamicType, FormItemProps } from "@fast-crud/fast-crud";
import { i18n } from "/src/locales/i18n";
interface PluginState { interface PluginState {
group?: PluginGroups; group?: PluginGroups;
} }
export type PluginGroup = { export type PluginGroup = {
key: string; key: string;
title: string; title: string;
desc?: string; desc?: string;
order: number; order: number;
icon: string; icon: string;
plugins: any[]; plugins: any[];
}; };
export type PluginDefine = { export type PluginDefine = {
name: string; name: string;
title: string; title: string;
desc?: string; desc?: string;
shortcut: any; shortcut: any;
input: { input: {
[key: string]: DynamicType<FormItemProps>; [key: string]: DynamicType<FormItemProps>;
}; };
output: { output: {
[key: string]: any; [key: string]: any;
}; };
}; };
export class PluginGroups { export class PluginGroups {
groups!: { [key: string]: PluginGroup }; groups!: { [key: string]: PluginGroup };
map!: { [key: string]: PluginDefine }; map!: { [key: string]: PluginDefine };
constructor(groups: { [key: string]: PluginGroup }) { t: any;
this.groups = groups; constructor(groups: { [key: string]: PluginGroup }) {
this.initGroup(groups); this.groups = groups;
this.initMap(); this.t = i18n.global.t;
} this.initGroup(groups);
this.initMap();
}
private initGroup(groups: { [p: string]: PluginGroup }) { private initGroup(groups: { [p: string]: PluginGroup }) {
const all: PluginGroup = { const all: PluginGroup = {
key: "all", key: "all",
title: t('certd.all'), title: this.t("certd.all"),
order: 0, order: 0,
plugins: [], plugins: [],
icon: "material-symbols:border-all-rounded", icon: "material-symbols:border-all-rounded",
}; };
for (const key in groups) { for (const key in groups) {
all.plugins.push(...groups[key].plugins); all.plugins.push(...groups[key].plugins);
} }
this.groups = { this.groups = {
all, all,
...groups, ...groups,
}; };
} }
initMap() { initMap() {
const map: { [key: string]: PluginDefine } = {}; const map: { [key: string]: PluginDefine } = {};
for (const key in this.groups) { for (const key in this.groups) {
const group = this.groups[key]; const group = this.groups[key];
for (const plugin of group.plugins) { for (const plugin of group.plugins) {
map[plugin.name] = plugin; map[plugin.name] = plugin;
} }
} }
this.map = map; this.map = map;
} }
getGroups() { getGroups() {
return this.groups; return this.groups;
} }
get(name: string) { get(name: string) {
return this.map[name]; return this.map[name];
} }
getPreStepOutputOptions({ pipeline, currentStageIndex, currentTaskIndex, currentStepIndex, currentTask }: any) { getPreStepOutputOptions({ pipeline, currentStageIndex, currentTaskIndex, currentStepIndex, currentTask }: any) {
const steps = this.collectionPreStepOutputs({ const steps = this.collectionPreStepOutputs({
pipeline, pipeline,
currentStageIndex, currentStageIndex,
currentTaskIndex, currentTaskIndex,
currentStepIndex, currentStepIndex,
currentTask, currentTask,
}); });
const options: any[] = []; const options: any[] = [];
for (const step of steps) { for (const step of steps) {
const stepDefine = this.get(step.type); const stepDefine = this.get(step.type);
for (const key in stepDefine?.output) { for (const key in stepDefine?.output) {
options.push({ options.push({
value: `step.${step.id}.${key}`, value: `step.${step.id}.${key}`,
label: `${stepDefine.output[key].title}【from${step.title}`, label: `${stepDefine.output[key].title}【from${step.title}`,
type: step.type, type: step.type,
}); });
} }
} }
return options; return options;
} }
collectionPreStepOutputs({ pipeline, currentStageIndex, currentTaskIndex, currentStepIndex, currentTask }: any) { collectionPreStepOutputs({ pipeline, currentStageIndex, currentTaskIndex, currentStepIndex, currentTask }: any) {
const steps: any[] = []; const steps: any[] = [];
// 开始放step // 开始放step
for (let i = 0; i < currentStageIndex; i++) { for (let i = 0; i < currentStageIndex; i++) {
const stage = pipeline.stages[i]; const stage = pipeline.stages[i];
for (const task of stage.tasks) { for (const task of stage.tasks) {
for (const step of task.steps) { for (const step of task.steps) {
steps.push(step); steps.push(step);
} }
} }
} }
//当前阶段之前的task //当前阶段之前的task
const currentStage = pipeline.stages[currentStageIndex]; const currentStage = pipeline.stages[currentStageIndex];
for (let i = 0; i < currentTaskIndex; i++) { for (let i = 0; i < currentTaskIndex; i++) {
const task = currentStage.tasks[i]; const task = currentStage.tasks[i];
for (const step of task.steps) { for (const step of task.steps) {
steps.push(step); steps.push(step);
} }
} }
//放当前任务下的step //放当前任务下的step
for (let i = 0; i < currentStepIndex; i++) { for (let i = 0; i < currentStepIndex; i++) {
const step = currentTask.steps[i]; const step = currentTask.steps[i];
steps.push(step); steps.push(step);
} }
return steps; return steps;
} }
} }
export const usePluginStore = defineStore({ export const usePluginStore = defineStore({
id: "app.plugin", id: "app.plugin",
state: (): PluginState => ({ state: (): PluginState => ({
group: null, group: null,
}), }),
actions: { actions: {
async reload() { async reload() {
const groups = await api.GetGroups({}); const groups = await api.GetGroups({});
this.group = new PluginGroups(groups); this.group = new PluginGroups(groups);
}, },
async init() { async init() {
if (!this.group) { if (!this.group) {
await this.reload(); await this.reload();
} }
return this.group; return this.group;
}, },
async getGroups(): Promise<PluginGroups> { async getGroups(): Promise<PluginGroups> {
await this.init(); await this.init();
return this.group as PluginGroups; return this.group as PluginGroups;
}, },
async clear() { async clear() {
this.group = null; this.group = null;
}, },
async getList(): Promise<PluginDefine[]> { async getList(): Promise<PluginDefine[]> {
await this.init(); await this.init();
return this.group.groups.all.plugins; return this.group.groups.all.plugins;
}, },
async getPluginDefine(name: string): Promise<PluginDefine> { async getPluginDefine(name: string): Promise<PluginDefine> {
await this.init(); await this.init();
return this.group.get(name); return this.group.get(name);
}, },
async getPluginConfig(query: any) { async getPluginConfig(query: any) {
return await api.GetPluginConfig(query); return await api.GetPluginConfig(query);
}, },
}, },
}); });

View File

@ -1,15 +1,15 @@
<script setup lang="ts"> <script setup lang="ts">
import type { VbenFormSchema } from '/@/vben/form-ui'; import type { VbenFormSchema } from "/@/vben/form-ui";
import { computed, reactive } from 'vue'; import { computed, reactive } from "vue";
import { useRouter } from 'vue-router'; import { useRouter } from "vue-router";
import { $t } from '/@/locales'; import { $t } from "/@/locales";
import { useVbenForm } from '/@/vben/form-ui'; import { useVbenForm } from "/@/vben/form-ui";
import { VbenButton } from '/@/vben/shadcn-ui'; import { VbenButton } from "/@/vben/shadcn-ui";
import Title from './auth-title.vue'; import Title from "./auth-title.vue";
interface Props { interface Props {
formSchema: VbenFormSchema[]; formSchema: VbenFormSchema[];
@ -36,15 +36,15 @@ interface Props {
} }
defineOptions({ defineOptions({
name: 'ForgetPassword', name: "ForgetPassword",
}); });
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
loading: false, loading: false,
loginPath: '/auth/login', loginPath: "/auth/login",
submitButtonText: '', submitButtonText: "",
subTitle: '', subTitle: "",
title: '', title: "",
}); });
const emit = defineEmits<{ const emit = defineEmits<{
@ -59,7 +59,7 @@ const [Form, formApi] = useVbenForm(
}, },
schema: computed(() => props.formSchema), schema: computed(() => props.formSchema),
showDefaultActions: false, showDefaultActions: false,
}), })
); );
const router = useRouter(); const router = useRouter();
@ -68,7 +68,7 @@ async function handleSubmit() {
const { valid } = await formApi.validate(); const { valid } = await formApi.validate();
const values = await formApi.getValues(); const values = await formApi.getValues();
if (valid) { if (valid) {
emit('submit', values); emit("submit", values);
} }
} }
@ -84,12 +84,10 @@ defineExpose({
<template> <template>
<div> <div>
<Title> <Title>
<slot name="title"> <slot name="title"> {{ title || $t("authentication.forgetPassword") }} 🤦🏻 </slot>
{{ title || $t('authentication.forgetPassword') }} 🤦🏻
</slot>
<template #desc> <template #desc>
<slot name="subTitle"> <slot name="subTitle">
{{ subTitle || $t('authentication.forgetPasswordSubtitle') }} {{ subTitle || $t("authentication.forgetPasswordSubtitle") }}
</slot> </slot>
</template> </template>
</Title> </Title>
@ -105,11 +103,11 @@ defineExpose({
@click="handleSubmit" @click="handleSubmit"
> >
<slot name="submitButtonText"> <slot name="submitButtonText">
{{ submitButtonText || $t('authentication.sendResetLink') }} {{ submitButtonText || $t("authentication.sendResetLink") }}
</slot> </slot>
</VbenButton> </VbenButton>
<VbenButton class="mt-4 w-full" variant="outline" @click="goToLogin()"> <VbenButton class="mt-4 w-full" variant="outline" @click="goToLogin()">
{{ $t('common.back') }} {{ $t("common.back") }}
</VbenButton> </VbenButton>
</div> </div>
</div> </div>

View File

@ -1,11 +1,11 @@
export { default as Breadcrumb } from './breadcrumb.vue'; export { default as Breadcrumb } from "./breadcrumb.vue";
export * from './check-updates'; export * from "./check-updates";
export { default as AuthenticationColorToggle } from './color-toggle.vue'; export { default as AuthenticationColorToggle } from "./color-toggle.vue";
export * from './global-search'; export * from "./global-search";
export { default as LanguageToggle } from './language-toggle.vue'; export { default as LanguageToggle } from "./language-toggle.vue";
export { default as AuthenticationLayoutToggle } from './layout-toggle.vue'; export { default as AuthenticationLayoutToggle } from "./layout-toggle.vue";
export * from './lock-screen'; export * from "./lock-screen";
export * from './notification'; export * from "./notification";
export * from './preferences'; export * from "./preferences";
export * from './theme-toggle'; export * from "./theme-toggle";
export * from './user-dropdown'; export * from "./user-dropdown";

View File

@ -12,25 +12,25 @@ import { preferences, updatePreferences, usePreferences } from "/@/vben/preferen
import { VbenDropdownRadioMenu, VbenIconButton } from "/@/vben//shadcn-ui"; import { VbenDropdownRadioMenu, VbenIconButton } from "/@/vben//shadcn-ui";
defineOptions({ defineOptions({
name: "AuthenticationLayoutToggle" name: "AuthenticationLayoutToggle",
}); });
const menus = computed((): VbenDropdownMenuItem[] => [ const menus = computed((): VbenDropdownMenuItem[] => [
{ {
icon: PanelLeft, icon: PanelLeft,
label: $t("authentication.layout.alignLeft"), label: $t("authentication.layout.alignLeft"),
value: "panel-left" value: "panel-left",
}, },
{ {
icon: InspectionPanel, icon: InspectionPanel,
label: $t("authentication.layout.center"), label: $t("authentication.layout.center"),
value: "panel-center" value: "panel-center",
}, },
{ {
icon: PanelRight, icon: PanelRight,
label: $t("authentication.layout.alignRight"), label: $t("authentication.layout.alignRight"),
value: "panel-right" value: "panel-right",
} },
]); ]);
const { authPanelCenter, authPanelLeft, authPanelRight } = usePreferences(); const { authPanelCenter, authPanelLeft, authPanelRight } = usePreferences();
@ -38,8 +38,8 @@ const { authPanelCenter, authPanelLeft, authPanelRight } = usePreferences();
function handleUpdate(value: string) { function handleUpdate(value: string) {
updatePreferences({ updatePreferences({
app: { app: {
authPageLayout: value as AuthPageLayoutType authPageLayout: value as AuthPageLayoutType,
} },
}); });
} }
</script> </script>

View File

@ -1,71 +1,69 @@
<script setup lang="ts"> <script setup lang="ts">
import type { SelectOption } from '/@/vben/types'; import type { SelectOption } from "/@/vben/types";
import { computed } from 'vue'; import { computed } from "vue";
import { $t } from '/@/locales'; import { $t } from "/@/locales";
import SelectItem from '../select-item.vue'; import SelectItem from "../select-item.vue";
import SwitchItem from '../switch-item.vue'; import SwitchItem from "../switch-item.vue";
defineOptions({ defineOptions({
name: 'PreferenceInterfaceControl', name: "PreferenceInterfaceControl",
}); });
const widgetGlobalSearch = defineModel<boolean>('widgetGlobalSearch'); const widgetGlobalSearch = defineModel<boolean>("widgetGlobalSearch");
const widgetFullscreen = defineModel<boolean>('widgetFullscreen'); const widgetFullscreen = defineModel<boolean>("widgetFullscreen");
const widgetLanguageToggle = defineModel<boolean>('widgetLanguageToggle'); const widgetLanguageToggle = defineModel<boolean>("widgetLanguageToggle");
const widgetNotification = defineModel<boolean>('widgetNotification'); const widgetNotification = defineModel<boolean>("widgetNotification");
const widgetThemeToggle = defineModel<boolean>('widgetThemeToggle'); const widgetThemeToggle = defineModel<boolean>("widgetThemeToggle");
const widgetSidebarToggle = defineModel<boolean>('widgetSidebarToggle'); const widgetSidebarToggle = defineModel<boolean>("widgetSidebarToggle");
const widgetLockScreen = defineModel<boolean>('widgetLockScreen'); const widgetLockScreen = defineModel<boolean>("widgetLockScreen");
const appPreferencesButtonPosition = defineModel<string>( const appPreferencesButtonPosition = defineModel<string>("appPreferencesButtonPosition");
'appPreferencesButtonPosition', const widgetRefresh = defineModel<boolean>("widgetRefresh");
);
const widgetRefresh = defineModel<boolean>('widgetRefresh');
const positionItems = computed((): SelectOption[] => [ const positionItems = computed((): SelectOption[] => [
{ {
label: $t('preferences.position.auto'), label: $t("preferences.position.auto"),
value: 'auto', value: "auto",
}, },
{ {
label: $t('preferences.position.header'), label: $t("preferences.position.header"),
value: 'header', value: "header",
}, },
{ {
label: $t('preferences.position.fixed'), label: $t("preferences.position.fixed"),
value: 'fixed', value: "fixed",
}, },
]); ]);
</script> </script>
<template> <template>
<SwitchItem v-model="widgetGlobalSearch"> <SwitchItem v-model="widgetGlobalSearch">
{{ $t('preferences.widget.globalSearch') }} {{ $t("preferences.widget.globalSearch") }}
</SwitchItem> </SwitchItem>
<SwitchItem v-model="widgetThemeToggle"> <SwitchItem v-model="widgetThemeToggle">
{{ $t('preferences.widget.themeToggle') }} {{ $t("preferences.widget.themeToggle") }}
</SwitchItem> </SwitchItem>
<SwitchItem v-model="widgetLanguageToggle"> <SwitchItem v-model="widgetLanguageToggle">
{{ $t('preferences.widget.languageToggle') }} {{ $t("preferences.widget.languageToggle") }}
</SwitchItem> </SwitchItem>
<SwitchItem v-model="widgetFullscreen"> <SwitchItem v-model="widgetFullscreen">
{{ $t('preferences.widget.fullscreen') }} {{ $t("preferences.widget.fullscreen") }}
</SwitchItem> </SwitchItem>
<SwitchItem v-model="widgetNotification"> <SwitchItem v-model="widgetNotification">
{{ $t('preferences.widget.notification') }} {{ $t("preferences.widget.notification") }}
</SwitchItem> </SwitchItem>
<SwitchItem v-model="widgetLockScreen"> <SwitchItem v-model="widgetLockScreen">
{{ $t('preferences.widget.lockScreen') }} {{ $t("preferences.widget.lockScreen") }}
</SwitchItem> </SwitchItem>
<SwitchItem v-model="widgetSidebarToggle"> <SwitchItem v-model="widgetSidebarToggle">
{{ $t('preferences.widget.sidebarToggle') }} {{ $t("preferences.widget.sidebarToggle") }}
</SwitchItem> </SwitchItem>
<SwitchItem v-model="widgetRefresh"> <SwitchItem v-model="widgetRefresh">
{{ $t('preferences.widget.refresh') }} {{ $t("preferences.widget.refresh") }}
</SwitchItem> </SwitchItem>
<SelectItem v-model="appPreferencesButtonPosition" :items="positionItems"> <SelectItem v-model="appPreferencesButtonPosition" :items="positionItems">
{{ $t('preferences.position.title') }} {{ $t("preferences.position.title") }}
</SelectItem> </SelectItem>
</template> </template>

View File

@ -108,7 +108,7 @@ const defaultPreferences: Preferences = {
widget: { widget: {
fullscreen: true, fullscreen: true,
globalSearch: true, globalSearch: true,
languageToggle: false, languageToggle: true,
lockScreen: true, lockScreen: true,
notification: false, notification: false,
refresh: true, refresh: true,

View File

@ -1,35 +1,23 @@
import type { Preferences } from './types'; import type { Preferences } from "./types";
import { preferencesManager } from './preferences'; import { preferencesManager } from "./preferences";
// 偏好设置(带有层级关系) // 偏好设置(带有层级关系)
const preferences: Preferences = const preferences: Preferences = preferencesManager.getPreferences.apply(preferencesManager);
preferencesManager.getPreferences.apply(preferencesManager);
// 更新偏好设置 // 更新偏好设置
const updatePreferences = const updatePreferences = preferencesManager.updatePreferences.bind(preferencesManager);
preferencesManager.updatePreferences.bind(preferencesManager);
// 重置偏好设置 // 重置偏好设置
const resetPreferences = const resetPreferences = preferencesManager.resetPreferences.bind(preferencesManager);
preferencesManager.resetPreferences.bind(preferencesManager);
const clearPreferencesCache = const clearPreferencesCache = preferencesManager.clearCache.bind(preferencesManager);
preferencesManager.clearCache.bind(preferencesManager);
// 初始化偏好设置 // 初始化偏好设置
const initPreferences = const initPreferences = preferencesManager.initPreferences.bind(preferencesManager);
preferencesManager.initPreferences.bind(preferencesManager);
export { export { clearPreferencesCache, initPreferences, preferences, preferencesManager, resetPreferences, updatePreferences };
clearPreferencesCache,
initPreferences,
preferences,
preferencesManager,
resetPreferences,
updatePreferences,
};
export * from './constants'; export * from "./constants";
export type * from './types'; export type * from "./types";
export * from './use-preferences'; export * from "./use-preferences";

View File

@ -1,22 +1,18 @@
import type { DeepPartial } from '/@/vben/typings'; import type { DeepPartial } from "/@/vben/typings";
import type { InitialOptions, Preferences } from './types'; import type { InitialOptions, Preferences } from "./types";
import { markRaw, reactive, readonly, watch } from 'vue'; import { markRaw, reactive, readonly, watch } from "vue";
import { StorageManager } from '/@/vben/shared/cache'; import { StorageManager } from "/@/vben/shared/cache";
import { isMacOs, merge } from '/@/vben/shared/utils'; import { isMacOs, merge } from "/@/vben/shared/utils";
import { import { breakpointsTailwind, useBreakpoints, useDebounceFn } from "@vueuse/core";
breakpointsTailwind,
useBreakpoints,
useDebounceFn,
} from '@vueuse/core';
import { defaultPreferences } from './config'; import { defaultPreferences } from "./config";
import { updateCSSVariables } from './update-css-variables'; import { updateCSSVariables } from "./update-css-variables";
const STORAGE_KEY = 'preferences'; const STORAGE_KEY = "preferences";
const STORAGE_KEY_LOCALE = `${STORAGE_KEY}-locale`; const STORAGE_KEY_LOCALE = `${STORAGE_KEY}-locale`;
const STORAGE_KEY_THEME = `${STORAGE_KEY}-theme`; const STORAGE_KEY_THEME = `${STORAGE_KEY}-theme`;
@ -33,14 +29,11 @@ class PreferenceManager {
this.cache = new StorageManager(); this.cache = new StorageManager();
// 避免频繁的操作缓存 // 避免频繁的操作缓存
this.savePreferences = useDebounceFn( this.savePreferences = useDebounceFn((preference: Preferences) => this._savePreferences(preference), 150);
(preference: Preferences) => this._savePreferences(preference),
150,
);
} }
clearCache() { clearCache() {
[STORAGE_KEY, STORAGE_KEY_LOCALE, STORAGE_KEY_THEME].forEach((key) => { [STORAGE_KEY, STORAGE_KEY_LOCALE, STORAGE_KEY_THEME].forEach(key => {
this.cache?.removeItem(key); this.cache?.removeItem(key);
}); });
} }
@ -73,7 +66,7 @@ class PreferenceManager {
{}, {},
// overrides, // overrides,
this.loadCachedPreferences() || {}, this.loadCachedPreferences() || {},
this.initialPreferences, this.initialPreferences
); );
// 更新偏好设置 // 更新偏好设置
@ -103,7 +96,7 @@ class PreferenceManager {
// 保存重置后的偏好设置 // 保存重置后的偏好设置
this.savePreferences(this.state); this.savePreferences(this.state);
// 从存储中移除偏好设置项 // 从存储中移除偏好设置项
[STORAGE_KEY, STORAGE_KEY_THEME, STORAGE_KEY_LOCALE].forEach((key) => { [STORAGE_KEY, STORAGE_KEY_THEME, STORAGE_KEY_LOCALE].forEach(key => {
this.cache?.removeItem(key); this.cache?.removeItem(key);
}); });
this.updatePreferences(this.state); this.updatePreferences(this.state);
@ -145,17 +138,14 @@ class PreferenceManager {
updateCSSVariables(this.state); updateCSSVariables(this.state);
} }
if ( if (Reflect.has(appUpdates, "colorGrayMode") || Reflect.has(appUpdates, "colorWeakMode")) {
Reflect.has(appUpdates, 'colorGrayMode') ||
Reflect.has(appUpdates, 'colorWeakMode')
) {
this.updateColorMode(this.state); this.updateColorMode(this.state);
} }
} }
private initPlatform() { private initPlatform() {
const dom = document.documentElement; const dom = document.documentElement;
dom.dataset.platform = isMacOs() ? 'macOs' : 'window'; dom.dataset.platform = isMacOs() ? "macOs" : "window";
} }
/** /**
@ -183,25 +173,23 @@ class PreferenceManager {
// 监听断点,判断是否移动端 // 监听断点,判断是否移动端
const breakpoints = useBreakpoints(breakpointsTailwind); const breakpoints = useBreakpoints(breakpointsTailwind);
const isMobile = breakpoints.smaller('md'); const isMobile = breakpoints.smaller("md");
watch( watch(
() => isMobile.value, () => isMobile.value,
(val) => { val => {
this.updatePreferences({ this.updatePreferences({
app: { isMobile: val }, app: { isMobile: val },
}); });
}, },
{ immediate: true }, { immediate: true }
); );
// 监听系统主题偏好设置变化 // 监听系统主题偏好设置变化
window window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", ({ matches: isDark }) => {
.matchMedia('(prefers-color-scheme: dark)') this.updatePreferences({
.addEventListener('change', ({ matches: isDark }) => { theme: { mode: isDark ? "dark" : "light" },
this.updatePreferences({
theme: { mode: isDark ? 'dark' : 'light' },
});
}); });
});
} }
/** /**
@ -212,14 +200,10 @@ class PreferenceManager {
if (preference.app) { if (preference.app) {
const { colorGrayMode, colorWeakMode } = preference.app; const { colorGrayMode, colorWeakMode } = preference.app;
const dom = document.documentElement; const dom = document.documentElement;
const COLOR_WEAK = 'invert-mode'; const COLOR_WEAK = "invert-mode";
const COLOR_GRAY = 'grayscale-mode'; const COLOR_GRAY = "grayscale-mode";
colorWeakMode colorWeakMode ? dom.classList.add(COLOR_WEAK) : dom.classList.remove(COLOR_WEAK);
? dom.classList.add(COLOR_WEAK) colorGrayMode ? dom.classList.add(COLOR_GRAY) : dom.classList.remove(COLOR_GRAY);
: dom.classList.remove(COLOR_WEAK);
colorGrayMode
? dom.classList.add(COLOR_GRAY)
: dom.classList.remove(COLOR_GRAY);
} }
} }
} }

View File

@ -1,9 +1,9 @@
import type { Preferences } from './types'; import type { Preferences } from "./types";
import { generatorColorVariables } from '/@/vben/shared/color'; import { generatorColorVariables } from "/@/vben/shared/color";
import { updateCSSVariables as executeUpdateCSSVariables } from '/@/vben/shared/utils'; import { updateCSSVariables as executeUpdateCSSVariables } from "/@/vben/shared/utils";
import { BUILT_IN_THEME_PRESETS } from './constants'; import { BUILT_IN_THEME_PRESETS } from "./constants";
/** /**
* CSS CSS * CSS CSS
@ -21,13 +21,13 @@ function updateCSSVariables(preferences: Preferences) {
const { builtinType, mode, radius } = theme; const { builtinType, mode, radius } = theme;
// html 设置 dark 类 // html 设置 dark 类
if (Reflect.has(theme, 'mode')) { if (Reflect.has(theme, "mode")) {
const dark = isDarkTheme(mode); const dark = isDarkTheme(mode);
root.classList.toggle('dark', dark); root.classList.toggle("dark", dark);
} }
// html 设置 data-theme=[builtinType] // html 设置 data-theme=[builtinType]
if (Reflect.has(theme, 'builtinType')) { if (Reflect.has(theme, "builtinType")) {
const rootTheme = root.dataset.theme; const rootTheme = root.dataset.theme;
if (rootTheme !== builtinType) { if (rootTheme !== builtinType) {
root.dataset.theme = builtinType; root.dataset.theme = builtinType;
@ -35,36 +35,26 @@ function updateCSSVariables(preferences: Preferences) {
} }
// 获取当前的内置主题 // 获取当前的内置主题
const currentBuiltType = [...BUILT_IN_THEME_PRESETS].find( const currentBuiltType = [...BUILT_IN_THEME_PRESETS].find(item => item.type === builtinType);
(item) => item.type === builtinType,
);
let builtinTypeColorPrimary: string | undefined = ''; let builtinTypeColorPrimary: string | undefined = "";
if (currentBuiltType) { if (currentBuiltType) {
const isDark = isDarkTheme(preferences.theme.mode); const isDark = isDarkTheme(preferences.theme.mode);
// 设置不同主题的主要颜色 // 设置不同主题的主要颜色
const color = isDark const color = isDark ? currentBuiltType.darkPrimaryColor || currentBuiltType.primaryColor : currentBuiltType.primaryColor;
? currentBuiltType.darkPrimaryColor || currentBuiltType.primaryColor
: currentBuiltType.primaryColor;
builtinTypeColorPrimary = color || currentBuiltType.color; builtinTypeColorPrimary = color || currentBuiltType.color;
} }
// 如果内置主题颜色和自定义颜色都不存在,则不更新主题颜色 // 如果内置主题颜色和自定义颜色都不存在,则不更新主题颜色
if ( if (builtinTypeColorPrimary || Reflect.has(theme, "colorPrimary") || Reflect.has(theme, "colorDestructive") || Reflect.has(theme, "colorSuccess") || Reflect.has(theme, "colorWarning")) {
builtinTypeColorPrimary ||
Reflect.has(theme, 'colorPrimary') ||
Reflect.has(theme, 'colorDestructive') ||
Reflect.has(theme, 'colorSuccess') ||
Reflect.has(theme, 'colorWarning')
) {
// preferences.theme.colorPrimary = builtinTypeColorPrimary || colorPrimary; // preferences.theme.colorPrimary = builtinTypeColorPrimary || colorPrimary;
updateMainColorVariables(preferences); updateMainColorVariables(preferences);
} }
// 更新圆角 // 更新圆角
if (Reflect.has(theme, 'radius')) { if (Reflect.has(theme, "radius")) {
document.documentElement.style.setProperty('--radius', `${radius}rem`); document.documentElement.style.setProperty("--radius", `${radius}rem`);
} }
} }
@ -76,22 +66,21 @@ function updateMainColorVariables(preference: Preferences) {
if (!preference.theme) { if (!preference.theme) {
return; return;
} }
const { colorDestructive, colorPrimary, colorSuccess, colorWarning } = const { colorDestructive, colorPrimary, colorSuccess, colorWarning } = preference.theme;
preference.theme;
const colorVariables = generatorColorVariables([ const colorVariables = generatorColorVariables([
{ color: colorPrimary, name: 'primary' }, { color: colorPrimary, name: "primary" },
{ alias: 'warning', color: colorWarning, name: 'yellow' }, { alias: "warning", color: colorWarning, name: "yellow" },
{ alias: 'success', color: colorSuccess, name: 'green' }, { alias: "success", color: colorSuccess, name: "green" },
{ alias: 'destructive', color: colorDestructive, name: 'red' }, { alias: "destructive", color: colorDestructive, name: "red" },
]); ]);
// 要设置的 CSS 变量映射 // 要设置的 CSS 变量映射
const colorMappings = { const colorMappings = {
'--green-500': '--success', "--green-500": "--success",
'--primary-500': '--primary', "--primary-500": "--primary",
'--red-500': '--destructive', "--red-500": "--destructive",
'--yellow-500': '--warning', "--yellow-500": "--warning",
}; };
// 统一处理颜色变量的更新 // 统一处理颜色变量的更新
@ -106,9 +95,9 @@ function updateMainColorVariables(preference: Preferences) {
} }
function isDarkTheme(theme: string) { function isDarkTheme(theme: string) {
let dark = theme === 'dark'; let dark = theme === "dark";
if (theme === 'auto') { if (theme === "auto") {
dark = window.matchMedia('(prefers-color-scheme: dark)').matches; dark = window.matchMedia("(prefers-color-scheme: dark)").matches;
} }
return dark; return dark;
} }

View File

@ -166,7 +166,7 @@ function usePreferences() {
if (!enablePreferences) { if (!enablePreferences) {
return { return {
fixed: false, fixed: false,
header: false header: false,
}; };
} }
@ -182,7 +182,7 @@ function usePreferences() {
if (preferencesButtonPosition !== "auto") { if (preferencesButtonPosition !== "auto") {
return { return {
fixed: preferencesButtonPosition === "fixed", fixed: preferencesButtonPosition === "fixed",
header: isHeaderPosition header: isHeaderPosition,
}; };
} }
@ -191,7 +191,7 @@ function usePreferences() {
return { return {
fixed, fixed,
header: !fixed header: !fixed,
}; };
}); });
@ -219,7 +219,7 @@ function usePreferences() {
locale, locale,
preferencesButtonPosition, preferencesButtonPosition,
sidebarCollapsed, sidebarCollapsed,
theme theme,
}; };
} }

View File

@ -1,81 +1,89 @@
<template> <template>
<a-drawer v-model:open="notificationDrawerVisible" placement="right" :closable="true" width="600px" <a-drawer v-model:open="notificationDrawerVisible" placement="right" :closable="true" width="600px" class="pi-notification-form" @after-open-change="notificationDrawerOnAfterVisibleChange">
class="pi-notification-form" @after-open-change="notificationDrawerOnAfterVisibleChange"> <template #title>
<template #title> <div>
<div> {{ t("certd.edit_notification") }}
{{ t('certd.edit_notification') }} <a-button v-if="mode === 'edit'" @click="notificationDelete()">
<a-button v-if="mode === 'edit'" @click="notificationDelete()"> <template #icon>
<template #icon> <DeleteOutlined />
<DeleteOutlined /> </template>
</template> </a-button>
</a-button> </div>
</div> </template>
</template> <template v-if="currentNotification">
<template v-if="currentNotification"> <pi-container>
<pi-container> <a-form ref="notificationFormRef" class="notification-form" :model="currentNotification" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-form ref="notificationFormRef" class="notification-form" :model="currentNotification" <fs-form-item
:label-col="labelCol" :wrapper-col="wrapperCol"> v-if="currentNotification.type === 'email'"
<fs-form-item v-if="currentNotification.type === 'email'" v-model="currentNotification.type" :item="{ v-model="currentNotification.type"
title: t('certd.type'), :item="{
key: 'type', title: t('certd.type'),
value: 'email', key: 'type',
component: { value: 'email',
name: 'a-select', component: {
vModel: 'value', name: 'a-select',
disabled: !editMode, vModel: 'value',
options: [ disabled: !editMode,
{ value: 'email', label: t('certd.email') }, options: [
{ value: 'other', label: t('certd.other_notification_method') }, { value: 'email', label: t('certd.email') },
], { value: 'other', label: t('certd.other_notification_method') },
}, ],
rules: [{ required: true, message: t('certd.required') }], },
}" /> rules: [{ required: true, message: t('certd.required') }],
<fs-form-item v-model="currentNotification.when" :item="{ }"
title: t('certd.trigger_time'), />
key: 'when', <fs-form-item
value: ['error'], v-model="currentNotification.when"
component: { :item="{
name: 'a-select', title: t('certd.trigger_time'),
vModel: 'value', key: 'when',
disabled: !editMode, value: ['error'],
mode: 'multiple', component: {
options: [ name: 'a-select',
{ value: 'start', label: t('certd.start_time') }, vModel: 'value',
{ value: 'success', label: t('certd.success_time') }, disabled: !editMode,
{ value: 'turnToSuccess', label: t('certd.fail_to_success_time') }, mode: 'multiple',
{ value: 'error', label: t('certd.fail_time') }, options: [
], { value: 'start', label: t('certd.start_time') },
}, { value: 'success', label: t('certd.success_time') },
helper: t('certd.helper_suggest_fail_only'), { value: 'turnToSuccess', label: t('certd.fail_to_success_time') },
rules: [{ required: true, message: t('certd.required') }], { value: 'error', label: t('certd.fail_time') },
}" /> ],
<pi-notification-form-email v-if="currentNotification.type === 'email'" ref="optionsRef" },
v-model:options="currentNotification.options"></pi-notification-form-email> helper: t('certd.helper_suggest_fail_only'),
rules: [{ required: true, message: t('certd.required') }],
}"
/>
<pi-notification-form-email v-if="currentNotification.type === 'email'" ref="optionsRef" v-model:options="currentNotification.options"></pi-notification-form-email>
<fs-form-item v-else v-model="currentNotification.notificationId" :item="{ <fs-form-item
title: t('certd.notification_config'), v-else
key: 'notificationId', v-model="currentNotification.notificationId"
component: { :item="{
disabled: !editMode, title: t('certd.notification_config'),
name: NotificationSelector, key: 'notificationId',
onSelectedChange, component: {
}, disabled: !editMode,
helper: t('certd.please_select_notification'), name: NotificationSelector,
rules: [{ required: true, message: t('certd.required') }], onSelectedChange,
}" /> },
</a-form> helper: t('certd.please_select_notification'),
rules: [{ required: true, message: t('certd.required') }],
}"
/>
</a-form>
<template #footer> <template #footer>
<a-form-item v-if="editMode" :wrapper-col="{ span: 14, offset: 4 }"> <a-form-item v-if="editMode" :wrapper-col="{ span: 14, offset: 4 }">
<a-button type="primary" @click="notificationSave"> {{ t('certd.confirm') }} </a-button> <a-button type="primary" @click="notificationSave"> {{ t("certd.confirm") }} </a-button>
</a-form-item> </a-form-item>
</template> </template>
</pi-container> </pi-container>
</template> </template>
</a-drawer> </a-drawer>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { Modal } from "ant-design-vue"; import { Modal } from "ant-design-vue";
import { ref, Ref } from "vue"; import { ref, Ref } from "vue";
import * as _ from "lodash-es"; import * as _ from "lodash-es";
@ -86,157 +94,125 @@ import { useI18n } from "vue-i18n";
const { t } = useI18n(); const { t } = useI18n();
export default { defineOptions({
name: "PiNotificationForm", name: "PiNotificationForm",
// eslint-disable-next-line vue/no-unused-components });
components: { NotificationSelector, PiNotificationFormEmail },
props: {
editMode: {
type: Boolean,
default: true,
},
},
emits: ["update"],
setup(props: any, context: any) {
/**
* notification drawer
* @returns
*/
function useNotificationForm() {
const mode = ref("add");
const callback = ref();
const currentNotification: Ref<any> = ref({ type: undefined, when: [], options: {}, notificationId: undefined, title: "" });
const currentPlugin = ref({});
const notificationFormRef = ref(null);
const notificationDrawerVisible = ref(false);
const optionsRef = ref();
const rules = ref({
type: [
{
type: "string",
required: true,
message: t('certd.please_select_type'),
},
],
when: [
{
type: "string",
required: true,
message: t('certd.please_select_trigger_time'),
},
],
notificationId: [
{
type: "number",
required: true,
message: t('certd.please_select_notification_config'),
},
],
});
const notificationDrawerShow = () => {
notificationDrawerVisible.value = true;
};
const notificationDrawerClose = () => {
notificationDrawerVisible.value = false;
};
const notificationDrawerOnAfterVisibleChange = (val: any) => { const props = defineProps<{
console.log("notificationDrawerOnAfterVisibleChange", val); editMode: boolean;
}; }>();
const notificationOpen = (notification: any, emit: any) => { const emit = defineEmits(["update"]);
callback.value = emit;
currentNotification.value = _.cloneDeep(notification);
console.log("currentNotificationOpen", currentNotification.value);
notificationDrawerShow();
};
const notificationAdd = (emit: any) => { /**
mode.value = "add"; * notification drawer
const notification = { id: nanoid(), type: "custom", when: ["error", "turnToSuccess"] }; * @returns
notificationOpen(notification, emit); */
}; const mode = ref("add");
const callback = ref();
const notificationEdit = (notification: any, emit: any) => { const currentNotification: Ref<any> = ref({ type: undefined, when: [], options: {}, notificationId: undefined, title: "" });
mode.value = "edit"; const currentPlugin = ref({});
notificationOpen(notification, emit); const notificationFormRef = ref(null);
}; const notificationDrawerVisible = ref(false);
const optionsRef = ref();
const notificationView = (notification: any, emit: any) => { const rules = ref({
mode.value = "view"; type: [
notificationOpen(notification, emit); {
}; type: "string",
required: true,
const notificationSave = async (e: any) => { message: t("certd.please_select_type"),
if (optionsRef.value) { },
currentNotification.value.options = await optionsRef.value.getValue(); ],
} when: [
{
console.log("currentNotificationSave", currentNotification.value); type: "string",
try { required: true,
await notificationFormRef.value.validate(); message: t("certd.please_select_trigger_time"),
} catch (e) { },
console.error("表单验证失败:", e); ],
return; notificationId: [
} {
type: "number",
callback.value("save", currentNotification.value); required: true,
notificationDrawerClose(); message: t("certd.please_select_notification_config"),
}; },
],
const notificationDelete = () => { });
Modal.confirm({ const notificationDrawerShow = () => {
title: t('certd.confirm'), notificationDrawerVisible.value = true;
content: t('certd.confirm_delete_trigger'),
async onOk() {
callback.value("delete");
notificationDrawerClose();
},
});
};
const blankFn = () => {
return {};
};
function onSelectedChange(node: any) {
currentNotification.value.title = node?.name || null;
}
return {
notificationFormRef,
onSelectedChange,
mode,
notificationAdd,
notificationEdit,
notificationView,
notificationDrawerShow,
notificationDrawerVisible,
notificationDrawerOnAfterVisibleChange,
currentNotification,
currentPlugin,
notificationSave,
notificationDelete,
rules,
blankFn,
optionsRef,
};
}
return {
...useNotificationForm(),
labelCol: { span: 6 },
wrapperCol: { span: 16 },
};
},
computed: {
NotificationSelector() {
return NotificationSelector;
},
},
}; };
const notificationDrawerClose = () => {
notificationDrawerVisible.value = false;
};
const notificationDrawerOnAfterVisibleChange = (val: any) => {
console.log("notificationDrawerOnAfterVisibleChange", val);
};
const notificationOpen = (notification: any, emit: any) => {
callback.value = emit;
currentNotification.value = _.cloneDeep(notification);
console.log("currentNotificationOpen", currentNotification.value);
notificationDrawerShow();
};
const notificationAdd = (emit: any) => {
mode.value = "add";
const notification = { id: nanoid(), type: "custom", when: ["error", "turnToSuccess"] };
notificationOpen(notification, emit);
};
const notificationEdit = (notification: any, emit: any) => {
mode.value = "edit";
notificationOpen(notification, emit);
};
const notificationView = (notification: any, emit: any) => {
mode.value = "view";
notificationOpen(notification, emit);
};
const notificationSave = async (e: any) => {
if (optionsRef.value) {
currentNotification.value.options = await optionsRef.value.getValue();
}
console.log("currentNotificationSave", currentNotification.value);
try {
await notificationFormRef.value.validate();
} catch (e) {
console.error("表单验证失败:", e);
return;
}
callback.value("save", currentNotification.value);
notificationDrawerClose();
};
const notificationDelete = () => {
Modal.confirm({
title: t("certd.confirm"),
content: t("certd.confirm_delete_trigger"),
async onOk() {
callback.value("delete");
notificationDrawerClose();
},
});
};
const blankFn = () => {
return {};
};
function onSelectedChange(node: any) {
currentNotification.value.title = node?.name || null;
}
const labelCol = { span: 6 };
const wrapperCol = { span: 16 };
</script> </script>
<style lang="less"> <style lang="less">
.pi-notification-form {} .pi-notification-form {
}
</style> </style>

View File

@ -1,195 +1,201 @@
<template> <template>
<a-drawer v-model:open="triggerDrawerVisible" placement="right" :closable="true" width="650px" <a-drawer v-model:open="triggerDrawerVisible" placement="right" :closable="true" width="650px" class="pi-trigger-form" @after-open-change="triggerDrawerOnAfterVisibleChange">
class="pi-trigger-form" @after-open-change="triggerDrawerOnAfterVisibleChange"> <template #title>
<template #title> <div>
<div> {{ t("certd.editTrigger") }}
{{ t("certd.editTrigger") }} <a-button v-if="mode === 'edit'" @click="triggerDelete()">
<a-button v-if="mode === 'edit'" @click="triggerDelete()"> <template #icon>
<template #icon> <DeleteOutlined />
<DeleteOutlined /> </template>
</template> </a-button>
</a-button> </div>
</div> </template>
</template> <template v-if="currentTrigger">
<template v-if="currentTrigger"> <pi-container>
<pi-container> <a-form ref="triggerFormRef" class="trigger-form" :model="currentTrigger" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-form ref="triggerFormRef" class="trigger-form" :model="currentTrigger" :label-col="labelCol" <fs-form-item
:wrapper-col="wrapperCol"> v-model="currentTrigger.title"
<fs-form-item v-model="currentTrigger.title" :item="{ :item="{
title: t('certd.triggerName'), title: t('certd.triggerName'),
key: 'title', key: 'title',
component: { component: {
name: 'a-input', name: 'a-input',
vModel: 'value', vModel: 'value',
disabled: !editMode, disabled: !editMode,
}, },
rules: [{ required: true, message: t('certd.requiredField') }], rules: [{ required: true, message: t('certd.requiredField') }],
}" /> }"
/>
<fs-form-item
v-model="currentTrigger.type"
:item="{
title: t('certd.type'),
key: 'type',
value: 'timer',
component: {
name: 'a-select',
vModel: 'value',
disabled: !editMode,
options: [{ value: 'timer', label: t('certd.schedule') }],
},
rules: [{ required: true, message: t('certd.requiredField') }],
}"
/>
<fs-form-item v-model="currentTrigger.type" :item="{ <fs-form-item
title: t('certd.type'), v-model="currentTrigger.props.cron"
key: 'type', :item="{
value: 'timer', title: t('certd.cronForm.title'),
component: { key: 'props.cron',
name: 'a-select', component: {
vModel: 'value', disabled: !editMode,
disabled: !editMode, name: 'cron-editor',
options: [{ value: 'timer', label: t('certd.schedule') }], vModel: 'modelValue',
}, },
rules: [{ required: true, message: t('certd.requiredField') }], helper: t('certd.cronForm.helper'),
}" /> rules: [{ required: true, message: t('certd.cronForm.required') }],
}"
/>
</a-form>
<fs-form-item v-model="currentTrigger.props.cron" :item="{ <template #footer>
title: t('certd.cronForm.title'), <a-form-item v-if="editMode" :wrapper-col="{ span: 14, offset: 4 }">
key: 'props.cron', <a-button type="primary" @click="triggerSave"> </a-button>
component: { </a-form-item>
disabled: !editMode, </template>
name: 'cron-editor', </pi-container>
vModel: 'modelValue', </template>
}, </a-drawer>
helper: t('certd.cronForm.helper'),
rules: [{ required: true, message: t('certd.cronForm.required') }],
}" />
</a-form>
<template #footer>
<a-form-item v-if="editMode" :wrapper-col="{ span: 14, offset: 4 }">
<a-button type="primary" @click="triggerSave"> </a-button>
</a-form-item>
</template>
</pi-container>
</template>
</a-drawer>
</template> </template>
<script> <script>
import { message, Modal } from "ant-design-vue"; import { message, Modal } from "ant-design-vue";
import { inject, ref } from "vue"; import { inject, ref } from "vue";
import * as _ from "lodash-es"; import * as _ from "lodash-es";
import { useI18n } from "/src/locales/";
import { nanoid } from "nanoid"; import { nanoid } from "nanoid";
export default { export default {
name: "PiTriggerForm", name: "PiTriggerForm",
props: { props: {
editMode: { editMode: {
type: Boolean, type: Boolean,
default: true, default: true,
}, },
}, },
emits: ["update"], emits: ["update"],
setup(props, context) { setup(props, context) {
/** /**
* trigger drawer * trigger drawer
* @returns * @returns
*/ */
function useTriggerForm() { const { t } = useI18n();
const mode = ref("add"); function useTriggerForm() {
const callback = ref(); const mode = ref("add");
const currentTrigger = ref({ title: undefined, input: {} }); const callback = ref();
const currentPlugin = ref({}); const currentTrigger = ref({ title: undefined, input: {} });
const triggerFormRef = ref(null); const currentPlugin = ref({});
const triggerDrawerVisible = ref(false); const triggerFormRef = ref(null);
const rules = ref({ const triggerDrawerVisible = ref(false);
name: [ const rules = ref({
{ name: [
type: "string", {
required: true, type: "string",
message: t("certd.enterName"), required: true,
}, message: t("certd.enterName"),
], },
}); ],
});
const triggerDrawerShow = () => {
triggerDrawerVisible.value = true;
};
const triggerDrawerClose = () => {
triggerDrawerVisible.value = false;
};
const triggerDrawerShow = () => { const triggerDrawerOnAfterVisibleChange = val => {
triggerDrawerVisible.value = true; console.log("triggerDrawerOnAfterVisibleChange", val);
}; };
const triggerDrawerClose = () => {
triggerDrawerVisible.value = false;
};
const triggerDrawerOnAfterVisibleChange = val => { const triggerOpen = (trigger, emit) => {
console.log("triggerDrawerOnAfterVisibleChange", val); callback.value = emit;
}; currentTrigger.value = _.cloneDeep(trigger);
console.log("currentTriggerOpen", currentTrigger.value);
triggerDrawerShow();
};
const triggerOpen = (trigger, emit) => { const triggerAdd = emit => {
callback.value = emit; mode.value = "add";
currentTrigger.value = _.cloneDeep(trigger); const trigger = { id: nanoid(), title: t("certd.timerTrigger"), type: "timer", props: {} };
console.log("currentTriggerOpen", currentTrigger.value); triggerOpen(trigger, emit);
triggerDrawerShow(); };
};
const triggerAdd = emit => { const triggerEdit = (trigger, emit) => {
mode.value = "add"; mode.value = "edit";
const trigger = { id: nanoid(), title: t("certd.timerTrigger"), type: "timer", props: {} }; triggerOpen(trigger, emit);
triggerOpen(trigger, emit); };
};
const triggerView = (trigger, emit) => {
mode.value = "view";
triggerOpen(trigger, emit);
};
const triggerEdit = (trigger, emit) => { const triggerSave = async e => {
mode.value = "edit"; console.log("currentTriggerSave", currentTrigger.value);
triggerOpen(trigger, emit); try {
}; await triggerFormRef.value.validate();
} catch (e) {
console.error("表单验证失败:", e);
return;
}
const triggerView = (trigger, emit) => { callback.value("save", currentTrigger.value);
mode.value = "view"; triggerDrawerClose();
triggerOpen(trigger, emit); };
};
const triggerSave = async e => { const triggerDelete = () => {
console.log("currentTriggerSave", currentTrigger.value); Modal.confirm({
try { title: t("certd.confirm"),
await triggerFormRef.value.validate(); content: t("certd.confirmDeleteTrigger"),
} catch (e) { async onOk() {
console.error("表单验证失败:", e); callback.value("delete");
return; triggerDrawerClose();
} },
});
};
callback.value("save", currentTrigger.value); const blankFn = () => {
triggerDrawerClose(); return {};
}; };
return {
triggerFormRef,
mode,
triggerAdd,
triggerEdit,
triggerView,
triggerDrawerShow,
triggerDrawerVisible,
triggerDrawerOnAfterVisibleChange,
currentTrigger,
currentPlugin,
triggerSave,
triggerDelete,
rules,
blankFn,
};
}
const triggerDelete = () => { return {
Modal.confirm({ ...useTriggerForm(),
title: t("certd.confirm"), labelCol: { span: 6 },
content: t("certd.confirmDeleteTrigger"), wrapperCol: { span: 16 },
async onOk() { };
callback.value("delete"); },
triggerDrawerClose();
},
});
};
const blankFn = () => {
return {};
};
return {
triggerFormRef,
mode,
triggerAdd,
triggerEdit,
triggerView,
triggerDrawerShow,
triggerDrawerVisible,
triggerDrawerOnAfterVisibleChange,
currentTrigger,
currentPlugin,
triggerSave,
triggerDelete,
rules,
blankFn,
};
}
return {
...useTriggerForm(),
labelCol: { span: 6 },
wrapperCol: { span: 16 },
};
},
}; };
</script> </script>
<style lang="less"> <style lang="less">
.pi-trigger-form {} .pi-trigger-form {
}
</style> </style>

View File

@ -1,96 +1,86 @@
<template> <template>
<div class="main login-page"> <div class="main login-page">
<a-form v-if="!twoFactor.loginId" ref="formRef" class="user-layout-login" name="custom-validation" <a-form v-if="!twoFactor.loginId" ref="formRef" class="user-layout-login" name="custom-validation" :model="formState" v-bind="layout" @finish="handleFinish" @finish-failed="handleFinishFailed">
:model="formState" v-bind="layout" @finish="handleFinish" @finish-failed="handleFinishFailed"> <!-- <div class="login-title">登录</div>-->
<!-- <div class="login-title">登录</div>--> <a-tabs v-model:active-key="formState.loginType" :tab-bar-style="{ textAlign: 'center', borderBottom: 'unset' }">
<a-tabs v-model:active-key="formState.loginType" <a-tab-pane key="password" :tab="t('authentication.passwordTab')" :disabled="sysPublicSettings.passwordLoginEnabled !== true">
:tab-bar-style="{ textAlign: 'center', borderBottom: 'unset' }"> <template v-if="formState.loginType === 'password'">
<a-tab-pane key="password" :tab="$t('authentication.passwordTab')" <!-- <div class="login-title">登录</div>-->
:disabled="sysPublicSettings.passwordLoginEnabled !== true"> <a-form-item required has-feedback name="username" :rules="rules.username">
<template v-if="formState.loginType === 'password'"> <a-input v-model:value="formState.username" :placeholder="t('authentication.usernamePlaceholder')" autocomplete="off">
<!-- <div class="login-title">登录</div>--> <template #prefix>
<a-form-item required has-feedback name="username" :rules="rules.username"> <fs-icon icon="ion:phone-portrait-outline"></fs-icon>
<a-input v-model:value="formState.username" </template>
:placeholder="$t('authentication.usernamePlaceholder')" autocomplete="off"> </a-input>
<template #prefix> </a-form-item>
<fs-icon icon="ion:phone-portrait-outline"></fs-icon> <a-form-item has-feedback name="password" :rules="rules.password">
</template> <a-input-password v-model:value="formState.password" :placeholder="t('authentication.passwordPlaceholder')" autocomplete="off">
</a-input> <template #prefix>
</a-form-item> <fs-icon icon="ion:lock-closed-outline"></fs-icon>
<a-form-item has-feedback name="password" :rules="rules.password"> </template>
<a-input-password v-model:value="formState.password" </a-input-password>
:placeholder="$t('authentication.passwordPlaceholder')" autocomplete="off"> </a-form-item>
<template #prefix> </template>
<fs-icon icon="ion:lock-closed-outline"></fs-icon> </a-tab-pane>
</template> <a-tab-pane v-if="sysPublicSettings.smsLoginEnabled === true" key="sms" :tab="t('authentication.smsTab')">
</a-input-password> <template v-if="formState.loginType === 'sms'">
</a-form-item> <a-form-item has-feedback name="mobile" :rules="rules.mobile">
</template> <a-input v-model:value="formState.mobile" :placeholder="t('authentication.mobilePlaceholder')" autocomplete="off">
</a-tab-pane> <template #prefix>
<a-tab-pane key="sms" :tab="$t('authentication.smsTab')" <fs-icon icon="ion:phone-portrait-outline"></fs-icon>
:disabled="sysPublicSettings.smsLoginEnabled !== true"> </template>
<template v-if="formState.loginType === 'sms'"> </a-input>
<a-form-item has-feedback name="mobile" :rules="rules.mobile"> </a-form-item>
<a-input v-model:value="formState.mobile"
:placeholder="$t('authentication.mobilePlaceholder')" autocomplete="off">
<template #prefix>
<fs-icon icon="ion:phone-portrait-outline"></fs-icon>
</template>
</a-input>
</a-form-item>
<a-form-item has-feedback name="imgCode"> <a-form-item has-feedback name="imgCode">
<image-code v-model:value="formState.imgCode" <image-code v-model:value="formState.imgCode" v-model:random-str="formState.randomStr"></image-code>
v-model:random-str="formState.randomStr"></image-code> </a-form-item>
</a-form-item>
<a-form-item name="smsCode" :rules="rules.smsCode"> <a-form-item name="smsCode" :rules="rules.smsCode">
<sms-code v-model:value="formState.smsCode" :img-code="formState.imgCode" <sms-code v-model:value="formState.smsCode" :img-code="formState.imgCode" :mobile="formState.mobile" :phone-code="formState.phoneCode" :random-str="formState.randomStr" />
:mobile="formState.mobile" :phone-code="formState.phoneCode" </a-form-item>
:random-str="formState.randomStr" /> </template>
</a-form-item> </a-tab-pane>
</template> </a-tabs>
</a-tab-pane> <a-form-item>
</a-tabs> <a-button type="primary" size="large" html-type="submit" :loading="loading" class="login-button">
<a-form-item> {{ t("authentication.loginButton") }}
<a-button type="primary" size="large" html-type="submit" :loading="loading" class="login-button"> </a-button>
{{ $t('authentication.loginButton') }}
</a-button>
<div v-if="!settingStore.isComm" class="mt-2"> <div v-if="!settingStore.isComm" class="mt-2">
<a href="https://certd.docmirror.cn/guide/use/forgotpasswd/" target="_blank"> <a href="https://certd.docmirror.cn/guide/use/forgotpasswd/" target="_blank">
{{ $t('authentication.forgotAdminPassword') }} {{ t("authentication.forgotAdminPassword") }}
</a> </a>
</div> </div>
</a-form-item> </a-form-item>
<a-form-item class="user-login-other"> <a-form-item class="user-login-other">
<router-link v-if="hasRegisterTypeEnabled()" class="register" :to="{ name: 'register' }"> <div class="flex flex-between justify-between items-center">
{{ $t('authentication.registerLink') }} <language-toggle class="color-blue"></language-toggle>
</router-link> <router-link v-if="hasRegisterTypeEnabled()" class="register" :to="{ name: 'register' }">
</a-form-item> {{ t("authentication.registerLink") }}
</router-link>
</div>
</a-form-item>
</a-form>
<a-form v-else ref="twoFactorFormRef" class="user-layout-login" :model="twoFactor" v-bind="layout">
<div class="mb-10 flex flex-center">请打开您的Authenticator APP获取动态验证码</div>
<a-form-item name="verifyCode">
<a-input ref="verifyCodeInputRef" v-model:value="twoFactor.verifyCode" placeholder="请输入动态验证码" allow-clear @keydown.enter="handleTwoFactorSubmit">
<template #prefix>
<fs-icon icon="ion:lock-closed-outline"></fs-icon>
</template>
</a-input>
</a-form-item>
<a-form-item>
<loading-button type="primary" size="large" html-type="button" class="login-button" :click="handleTwoFactorSubmit">OTP验证登录</loading-button>
</a-form-item>
</a-form> <a-form-item class="user-login-other">
<a-form v-else ref="twoFactorFormRef" class="user-layout-login" :model="twoFactor" v-bind="layout"> <a class="register" @click="twoFactor.loginId = null"> 返回 </a>
<div class="mb-10 flex flex-center">请打开您的Authenticator APP获取动态验证码</div> </a-form-item>
<a-form-item name="verifyCode"> </a-form>
<a-input ref="verifyCodeInputRef" v-model:value="twoFactor.verifyCode" placeholder="请输入动态验证码" </div>
allow-clear @keydown.enter="handleTwoFactorSubmit">
<template #prefix>
<fs-icon icon="ion:lock-closed-outline"></fs-icon>
</template>
</a-input>
</a-form-item>
<a-form-item>
<loading-button type="primary" size="large" html-type="button" class="login-button"
:click="handleTwoFactorSubmit">OTP验证登录</loading-button>
</a-form-item>
<a-form-item class="user-login-other">
<a class="register" @click="twoFactor.loginId = null"> 返回 </a>
</a-form-item>
</a-form>
</div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, nextTick, reactive, ref, toRaw } from "vue"; import { defineComponent, nextTick, reactive, ref, toRaw } from "vue";
@ -99,126 +89,130 @@ import { useSettingStore } from "/@/store/settings";
import { utils } from "@fast-crud/fast-crud"; import { utils } from "@fast-crud/fast-crud";
import ImageCode from "/@/views/framework/login/image-code.vue"; import ImageCode from "/@/views/framework/login/image-code.vue";
import SmsCode from "/@/views/framework/login/sms-code.vue"; import SmsCode from "/@/views/framework/login/sms-code.vue";
import { useI18n } from "/@/locales";
import { LanguageToggle } from "/@/vben/layouts";
export default defineComponent({ export default defineComponent({
name: "LoginPage", name: "LoginPage",
components: { SmsCode, ImageCode }, components: { LanguageToggle, SmsCode, ImageCode },
setup() { setup() {
const verifyCodeInputRef = ref(); const { t } = useI18n();
const loading = ref(false); const verifyCodeInputRef = ref();
const userStore = useUserStore(); const loading = ref(false);
const settingStore = useSettingStore(); const userStore = useUserStore();
const formRef = ref(); const settingStore = useSettingStore();
const formState = reactive({ const formRef = ref();
username: "", const formState = reactive({
phoneCode: "86", username: "",
mobile: "", phoneCode: "86",
password: "", mobile: "",
loginType: "password", //password password: "",
imgCode: "", loginType: "password", //password
smsCode: "", imgCode: "",
randomStr: "", smsCode: "",
}); randomStr: "",
});
const rules = { const rules = {
mobile: [ mobile: [
{ {
required: true, required: true,
message: "请输入手机号", message: "请输入手机号",
}, },
], ],
username: [ username: [
{ {
required: true, required: true,
message: "请输入用户名", message: "请输入用户名",
}, },
], ],
password: [ password: [
{ {
required: true, required: true,
message: "请输入登录密码", message: "请输入登录密码",
}, },
], ],
smsCode: [ smsCode: [
{ {
required: true, required: true,
message: "请输入短信验证码", message: "请输入短信验证码",
}, },
], ],
}; };
const layout = { const layout = {
labelCol: { labelCol: {
span: 0, span: 0,
}, },
wrapperCol: { wrapperCol: {
span: 24, span: 24,
}, },
}; };
const twoFactor = reactive({ const twoFactor = reactive({
loginId: "", loginId: "",
verifyCode: "", verifyCode: "",
}); });
const handleTwoFactorSubmit = async () => { const handleTwoFactorSubmit = async () => {
await userStore.loginByTwoFactor(twoFactor); await userStore.loginByTwoFactor(twoFactor);
}; };
const handleFinish = async (values: any) => { const handleFinish = async (values: any) => {
loading.value = true; loading.value = true;
try { try {
const loginType = formState.loginType; const loginType = formState.loginType;
await userStore.login(loginType, toRaw(formState)); await userStore.login(loginType, toRaw(formState));
} catch (e: any) { } catch (e: any) {
//@ts-ignore //@ts-ignore
if (e.code === 10020) { if (e.code === 10020) {
// //
//@ts-ignore //@ts-ignore
twoFactor.loginId = e.data; twoFactor.loginId = e.data;
await nextTick(); await nextTick();
verifyCodeInputRef.value.focus(); verifyCodeInputRef.value.focus();
} else { } else {
throw e; throw e;
} }
} finally { } finally {
loading.value = false; loading.value = false;
} }
}; };
const handleFinishFailed = (errors: any) => { const handleFinishFailed = (errors: any) => {
utils.logger.log(errors); utils.logger.log(errors);
}; };
const resetForm = () => { const resetForm = () => {
formRef.value.resetFields(); formRef.value.resetFields();
}; };
const isLoginError = ref(); const isLoginError = ref();
const sysPublicSettings = settingStore.getSysPublic; const sysPublicSettings = settingStore.getSysPublic;
function hasRegisterTypeEnabled() { function hasRegisterTypeEnabled() {
return sysPublicSettings.registerEnabled && (sysPublicSettings.usernameRegisterEnabled || sysPublicSettings.emailRegisterEnabled); return sysPublicSettings.registerEnabled && (sysPublicSettings.usernameRegisterEnabled || sysPublicSettings.emailRegisterEnabled);
} }
return { return {
loading, t,
formState, loading,
formRef, formState,
rules, formRef,
layout, rules,
handleFinishFailed, layout,
handleFinish, handleFinishFailed,
resetForm, handleFinish,
isLoginError, resetForm,
sysPublicSettings, isLoginError,
hasRegisterTypeEnabled, sysPublicSettings,
twoFactor, hasRegisterTypeEnabled,
handleTwoFactorSubmit, twoFactor,
verifyCodeInputRef, handleTwoFactorSubmit,
settingStore, verifyCodeInputRef,
}; settingStore,
}, };
},
}); });
</script> </script>
@ -226,83 +220,83 @@ export default defineComponent({
@import "../../../style/theme/index.less"; @import "../../../style/theme/index.less";
.login-page.main { .login-page.main {
//margin: 20px !important; //margin: 20px !important;
margin-bottom: 100px; margin-bottom: 100px;
.user-layout-login { .user-layout-login {
//label { //label {
// font-size: 14px; // font-size: 14px;
//} //}
.login-title { .login-title {
color: @primary-color; color: @primary-color;
font-size: 18px; font-size: 18px;
text-align: center; text-align: center;
margin: 20px; margin: 20px;
} }
.getCaptcha { .getCaptcha {
display: block; display: block;
width: 100%; width: 100%;
} }
.image-code { .image-code {
height: 34px; height: 34px;
} }
.input-right { .input-right {
width: 160px; width: 160px;
margin-left: 10px; margin-left: 10px;
} }
.forge-password { .forge-password {
font-size: 14px; font-size: 14px;
} }
button.login-button { button.login-button {
padding: 0 15px; padding: 0 15px;
font-size: 16px; font-size: 16px;
width: 100%; width: 100%;
} }
.user-login-other { .user-login-other {
text-align: left; text-align: left;
margin-top: 30px; margin-top: 30px;
margin-bottom: 30px; margin-bottom: 30px;
//line-height: 22px; //line-height: 22px;
.item-icon { .item-icon {
font-size: 24px; font-size: 24px;
color: rgba(0, 0, 0, 0.2); color: rgba(0, 0, 0, 0.2);
margin-left: 16px; margin-left: 16px;
vertical-align: middle; vertical-align: middle;
cursor: pointer; cursor: pointer;
transition: color 0.3s; transition: color 0.3s;
&:hover { &:hover {
color: @primary-color; color: @primary-color;
} }
} }
.register { .register {
float: right; float: right;
} }
} }
.fs-icon { .fs-icon {
color: rgba(0, 0, 0, 0.45); color: rgba(0, 0, 0, 0.45);
margin-right: 4px; margin-right: 4px;
} }
.ant-input-affix-wrapper { .ant-input-affix-wrapper {
line-height: 1.8 !important; line-height: 1.8 !important;
font-size: 14px !important; font-size: 14px !important;
>* { > * {
line-height: 1.8 !important; line-height: 1.8 !important;
font-size: 14px !important; font-size: 14px !important;
} }
} }
} }
} }
</style> </style>