From 306082d625f7f007646ac5ce0e2d246620404e23 Mon Sep 17 00:00:00 2001 From: ssongliu <73214554+ssongliu@users.noreply.github.com> Date: Tue, 26 Mar 2024 16:44:07 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E5=96=84=E7=95=8C=E9=9D=A2?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E5=8A=9F=E8=83=BD=20(#4316)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/index.html | 1 - frontend/src/assets/images/favicon.png | Bin 0 -> 9330 bytes frontend/src/hooks/use-logo.ts | 36 ------------------ frontend/src/lang/modules/en.ts | 8 ++-- frontend/src/lang/modules/tw.ts | 9 ++--- frontend/src/lang/modules/zh.ts | 16 ++++---- .../components/Sidebar/components/Logo.vue | 13 +++++-- frontend/src/layout/index.vue | 10 +++++ frontend/src/views/login/entrance/index.vue | 2 +- frontend/src/views/login/index.vue | 2 +- frontend/src/views/setting/about/index.vue | 16 ++++++-- 11 files changed, 50 insertions(+), 63 deletions(-) create mode 100644 frontend/src/assets/images/favicon.png delete mode 100644 frontend/src/hooks/use-logo.ts diff --git a/frontend/index.html b/frontend/index.html index 2fd4c0993..f5c3d49d2 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -2,7 +2,6 @@ - loading... diff --git a/frontend/src/assets/images/favicon.png b/frontend/src/assets/images/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..6f82a12d56b666914ee686321551411e970d4db5 GIT binary patch literal 9330 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 { - const globalStore = GlobalStore(); - const themeConfig = computed(() => globalStore.themeConfig); - - if (!themeConfig.value.logo) { - const res = await searchXSetting(); - globalStore.setThemeConfig({ - ...themeConfig.value, - title: res.data.title, - logo: res.data.logo, - logoWithText: res.data.logoWithText, - favicon: res.data.favicon, - }); - } - let link = document.querySelector("link[rel*='icon']") as HTMLLinkElement | null; - if (link) { - if (globalStore.themeConfig.favicon) { - link.href = globalStore.themeConfig.favicon; - } else { - link.href = '/public/favicon.png'; - } - } else { - const newLink = document.createElement('link'); - newLink.rel = 'icon'; - if (globalStore.themeConfig.favicon) { - newLink.href = globalStore.themeConfig.favicon; - } else { - newLink.href = '/public/favicon.png'; - } - document.head.appendChild(newLink); - } -}; diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 6a34803e8..fc6eb8a2a 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -1445,7 +1445,7 @@ const message = { issue: 'Feedback', doc: 'Official document', star: 'Star', - description: '1Panel, Modern, open source Linux server operation and maintenance management panel.', + description: 'Linux server operation and maintenance management panel', forum: 'Forum Help', doc2: 'User Manual', currentVersion: 'Version', @@ -2325,11 +2325,9 @@ const message = { tamper: { tamper: 'Tamper', tamperHelper1: - 'One-click deployment type of website, it is recommended to enable the application directory anti-tampering function;', + 'For one-click deployment type websites, it is recommended to enable the application directory anti-tamper function; if the website cannot be used normally or backup and restore fail, please disable the anti-tamper function first;', tamperHelper2: - 'If the website cannot be used normally or backup and restore fail after enabling the anti-tampering function, please disable the anti-tampering function first;', - tamperHelper3: - 'Enabling tamper-proofing will restrict reading, writing, deleting, permission, and owner modification operations of protected files under non-excluded directories. Please choose carefully when setting [Exclude Directory] and [Protect].', + 'It will restrict the read, write, delete, permission, and owner modification operations of protected files under non-excluded directories', op: 'Operation', create: 'Create', file: 'File', diff --git a/frontend/src/lang/modules/tw.ts b/frontend/src/lang/modules/tw.ts index 55c5bca5f..adfa6b2e1 100644 --- a/frontend/src/lang/modules/tw.ts +++ b/frontend/src/lang/modules/tw.ts @@ -1346,7 +1346,7 @@ const message = { issue: '問題反饋', doc: '官方文檔', star: '點亮 Star', - description: '1Panel,現代化、開源的 Linux 服務器運維管理面板。', + description: 'Linux 服務器運維管理面板', forum: '論壇求助', doc2: '使用手冊', currentVersion: '當前運行版本:', @@ -2173,10 +2173,9 @@ const message = { }, tamper: { tamper: '防篡改', - tamperHelper1: '一鍵部署類型的網站,建議啟用應用目錄防篡改功能;', - tamperHelper2: '如果在啟用防篡改功能後出現網站無法正常使用或備份、恢復失敗的情況,請先關閉防篡改功能;', - tamperHelper3: - '啟用防篡改,將限制非排除目錄下受保護文件的讀寫、刪除、權限和所有者修改操作,請在設定 [排除目錄] 和 [保護] 時謹慎選擇。', + tamperHelper1: + '一鍵部署類型的網站,建議啟用應用目錄防篡改功能;如出現網站無法正常使用或備份、恢復失敗的情況,請先關閉防篡改功能;', + tamperHelper2: '將限制非排除目錄下受保護文件的讀寫、刪除、權限和所有者修改操作', op: '操作', create: '創建', file: '文件', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index b56bf3047..d7b2ce7bf 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -1347,7 +1347,7 @@ const message = { issue: '问题反馈', doc: '官方文档', star: '点亮 Star', - description: '1Panel,现代化、开源的 Linux 服务器运维管理面板。', + description: 'Linux 服务器运维管理面板', forum: '论坛求助', doc2: '使用手册', currentVersion: '当前运行版本:', @@ -2178,10 +2178,9 @@ const message = { }, tamper: { tamper: '防篡改', - tamperHelper1: '一键部署类型的网站,建议启用应用目录防篡改功能;', - tamperHelper2: '如果在启用防篡改功能后出现网站无法正常使用或备份、恢复失败的情况,请先关闭防篡改功能;', - tamperHelper3: - '启用防篡改,将限制非排除目录下受保护文件的读写、删除、权限和所有者修改操作,请在设定 [排除目录] 和 [保护] 时谨慎选择。', + tamperHelper1: + '一键部署类型的网站,建议启用应用目录防篡改功能;如出现网站无法正常使用或备份、恢复失败的情况,请先关闭防篡改功能;', + tamperHelper2: '将限制非排除目录下受保护文件的读写、删除、权限和所有者修改操作', op: '操作', create: '创建', file: '文件', @@ -2207,13 +2206,14 @@ const message = { title: '面板描述', titleHelper: '将会显示在用户登录页面 (例: Linux 服务器运维管理面板)', logo: 'Logo', - logoHelper: '将会显示在菜单收缩时管理页面左上方 (建议图片大小为: 82px*82px)', + logoHelper: '将会显示在菜单收缩时管理页面左上方 (建议图片大小为: 82px*82px,最大支持 15KB )', logoWithText: 'Logo (带文字)', - logoWithTextHelper: '将会显示在菜单展开时管理页面左上方 (建议图片大小为: 185px*55px)', + logoWithTextHelper: '将会显示在菜单展开时管理页面左上方 (建议图片大小为: 185px*55px,最大支持 30KB )', favicon: '网站图标', - faviconHelper: '网站图标 (建议图片大小为: 16px*16px)', + faviconHelper: '网站图标 (建议图片大小为: 16px*16px,最大支持 8KB )', reUpload: '重新上传', supportType: '只能上传 jpg/png/jpeg 文件!', + supportSize: '只能上传小于 {0}KB 的文件!', setDefault: '恢复默认', reset: '重置', }, diff --git a/frontend/src/layout/components/Sidebar/components/Logo.vue b/frontend/src/layout/components/Sidebar/components/Logo.vue index 3f95e9f09..ce6a636da 100644 --- a/frontend/src/layout/components/Sidebar/components/Logo.vue +++ b/frontend/src/layout/components/Sidebar/components/Logo.vue @@ -11,11 +11,18 @@ defineProps<{ isCollapse: boolean }>(); const globalStore = GlobalStore(); const getLogoUrl = (isCollapse: boolean) => { - const theme = globalStore.$state.themeConfig.theme || 'light'; if (isCollapse) { - return new URL(`../../../../assets/images/1panel-logo-${theme}.png`, import.meta.url).href; + if (globalStore.themeConfig.logo) { + return globalStore.themeConfig.logo; + } else { + return new URL(`../../../../assets/images/1panel-logo-light.png`, import.meta.url).href; + } } else { - return new URL(`../../../../assets/images/1panel-menu-${theme}.png`, import.meta.url).href; + if (globalStore.themeConfig.logoWithText) { + return globalStore.themeConfig.logoWithText; + } else { + return new URL(`../../../../assets/images/1panel-menu-light.png`, import.meta.url).href; + } } }; diff --git a/frontend/src/layout/index.vue b/frontend/src/layout/index.vue index a9511046f..7d989dbd6 100644 --- a/frontend/src/layout/index.vue +++ b/frontend/src/layout/index.vue @@ -88,6 +88,15 @@ const updateDarkMode = async (event: MediaQueryListEvent) => { switchDark(); }; +const initStatus = () => { + document.title = globalStore.themeConfig.panelName; + const link = (document.querySelector("link[rel*='icon']") || document.createElement('link')) as HTMLLinkElement; + link.type = 'image/x-icon'; + link.rel = 'shortcut icon'; + link.href = globalStore.themeConfig.favicon; + document.getElementsByTagName('head')[0].appendChild(link); +}; + const loadStatus = async () => { loading.value = globalStore.isLoading; loadingText.value = globalStore.loadingText; @@ -114,6 +123,7 @@ onBeforeUnmount(() => { timer = null; }); onMounted(() => { + initStatus(); loadStatus(); loadDataFromDB(); loadProductProFromDB(); diff --git a/frontend/src/views/login/entrance/index.vue b/frontend/src/views/login/entrance/index.vue index 0c2a851f5..c25589d3e 100644 --- a/frontend/src/views/login/entrance/index.vue +++ b/frontend/src/views/login/entrance/index.vue @@ -4,7 +4,7 @@