From f2d39112664de2ddbf345a8c1ba0cfa3aeaadebf Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Fri, 9 Jan 2026 05:49:09 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E9=9A=90=E8=97=8F?= =?UTF-8?q?=E8=8F=9C=E5=8D=95=E5=92=8C=E8=87=AA=E5=AE=9A=E4=B9=89Logo?= =?UTF-8?q?=E9=95=BF=E6=9C=9F=E4=BF=9D=E5=AD=98=20(#1200)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Initial plan * feat: 支持隐藏菜单和自定义Logo长期保存 - 后端:在 SettingPanel 结构体中添加 HiddenMenu 和 CustomLogo 字段 - 后端:在 GetPanel 和 UpdatePanel 方法中处理新字段的获取和保存 - 后端:修改 Panel 接口返回 hidden_menu 和 custom_logo 给前端初始化 - 前端:在基本设置页面添加隐藏菜单和自定义 Logo 设置项 - 前端:从侧边栏设置组件中移除弹窗,只保留菜单折叠按钮 - 前端:初始化时从服务端获取并应用隐藏菜单和自定义 Logo 设置 - 前端:调整 store 的 persist 配置,不再将这两个设置保存到本地存储 Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com> * 代码审查完成 Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com> * feat: 优化样式 --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com> Co-authored-by: 耗子 --- go.sum | 7 -- internal/data/setting.go | 18 +++- internal/http/request/setting.go | 2 + internal/service/home.go | 8 +- .../layout/sidebar/components/SideSetting.vue | 101 +----------------- web/src/main.ts | 10 +- web/src/store/modules/permission/index.ts | 7 +- web/src/store/modules/theme/index.ts | 8 +- web/src/views/setting/IndexView.vue | 9 +- web/src/views/setting/SettingBase.vue | 59 ++++++++++ 10 files changed, 110 insertions(+), 119 deletions(-) diff --git a/go.sum b/go.sum index 72e7e4b4..2b43e86e 100644 --- a/go.sum +++ b/go.sum @@ -118,8 +118,6 @@ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXi github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE= -github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/wire v0.7.0 h1:JxUKI6+CVBgCO2WToKy/nQk0sS+amI9z9EjVmdaocj4= github.com/google/wire v0.7.0/go.mod h1:n6YbUQD9cPKTnHXEBN2DXlOp/mVADhVErcMFb0v3J18= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= @@ -269,7 +267,6 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/pquerna/otp v1.5.0 h1:NMMR+WrmaqXU4EzdGJEE1aUUI0AMRzsp96fFFWNPwxs= @@ -378,8 +375,6 @@ golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI= -golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -452,8 +447,6 @@ golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA= -golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= diff --git a/internal/data/setting.go b/internal/data/setting.go index ee9bb695..ec1fe8f3 100644 --- a/internal/data/setting.go +++ b/internal/data/setting.go @@ -221,6 +221,14 @@ func (r *settingRepo) GetPanel() (*request.SettingPanel, error) { if err != nil { return nil, err } + hiddenMenu, err := r.GetSlice(biz.SettingHiddenMenu) + if err != nil { + return nil, err + } + customLogo, err := r.Get(biz.SettingKeyCustomLogo) + if err != nil { + return nil, err + } ip, err := r.Get(biz.SettingKeyPublicIPs) if err != nil { return nil, err @@ -253,6 +261,8 @@ func (r *settingRepo) GetPanel() (*request.SettingPanel, error) { BindUA: r.conf.HTTP.BindUA, WebsitePath: websitePath, BackupPath: backupPath, + HiddenMenu: hiddenMenu, + CustomLogo: customLogo, Port: r.conf.HTTP.Port, HTTPS: r.conf.HTTP.TLS, ACME: r.conf.HTTP.ACME, @@ -281,11 +291,13 @@ func (r *settingRepo) UpdatePanel(req *request.SettingPanel) (bool, error) { if err := r.Set(biz.SettingKeyBackupPath, req.BackupPath); err != nil { return false, err } - publicIPBytes, err := json.Marshal(req.PublicIP) - if err != nil { + if err := r.SetSlice(biz.SettingHiddenMenu, req.HiddenMenu); err != nil { return false, err } - if err = r.Set(biz.SettingKeyPublicIPs, string(publicIPBytes)); err != nil { + if err := r.Set(biz.SettingKeyCustomLogo, req.CustomLogo); err != nil { + return false, err + } + if err := r.SetSlice(biz.SettingKeyPublicIPs, req.PublicIP); err != nil { return false, err } diff --git a/internal/http/request/setting.go b/internal/http/request/setting.go index 87324c3a..e7821a76 100644 --- a/internal/http/request/setting.go +++ b/internal/http/request/setting.go @@ -17,6 +17,8 @@ type SettingPanel struct { BindUA []string `json:"bind_ua"` WebsitePath string `json:"website_path" validate:"required"` BackupPath string `json:"backup_path" validate:"required"` + HiddenMenu []string `json:"hidden_menu"` // 隐藏的菜单项 + CustomLogo string `json:"custom_logo" validate:"isFullURL"` // 自定义 Logo URL Port uint `json:"port" validate:"required|min:1|max:65535"` HTTPS bool `json:"https"` ACME bool `json:"acme"` diff --git a/internal/service/home.go b/internal/service/home.go index 059e84af..61756ceb 100644 --- a/internal/service/home.go +++ b/internal/service/home.go @@ -57,10 +57,14 @@ func (s *HomeService) Panel(w http.ResponseWriter, r *http.Request) { if name == "" { name = s.t.Get("AcePanel") } + hiddenMenu, _ := s.settingRepo.GetSlice(biz.SettingHiddenMenu) + customLogo, _ := s.settingRepo.Get(biz.SettingKeyCustomLogo) Success(w, chix.M{ - "name": name, - "locale": s.conf.App.Locale, + "name": name, + "locale": s.conf.App.Locale, + "hidden_menu": hiddenMenu, + "custom_logo": customLogo, }) } diff --git a/web/src/layout/sidebar/components/SideSetting.vue b/web/src/layout/sidebar/components/SideSetting.vue index aceb80e9..35638aae 100644 --- a/web/src/layout/sidebar/components/SideSetting.vue +++ b/web/src/layout/sidebar/components/SideSetting.vue @@ -1,108 +1,9 @@ diff --git a/web/src/main.ts b/web/src/main.ts index 429ad179..c1afb4a0 100644 --- a/web/src/main.ts +++ b/web/src/main.ts @@ -6,7 +6,7 @@ import { createApp } from 'vue' import App from './App.vue' import { setupRouter } from '@/router' -import { setupStore, useThemeStore } from '@/store' +import { setupStore, usePermissionStore, useThemeStore } from '@/store' import { gettext, setCurrent, setupNaiveDiscreteApi } from '@/utils' import home from '@/api/panel/home' @@ -26,18 +26,24 @@ async function setupApp() { const setupPanel = async () => { const themeStore = useThemeStore() + const permissionStore = usePermissionStore() setCurrent(themeStore.locale) return new Promise((resolve) => { useRequest(home.panel, { initialData: { name: import.meta.env.VITE_APP_TITLE, - locale: 'en' + locale: 'en', + hidden_menu: [], + custom_logo: '' } }).onSuccess(async ({ data }: { data: any }) => { setCurrent(data.locale) themeStore.setLocale(data.locale) themeStore.setName(data.name) + // 设置隐藏菜单和自定义 Logo + themeStore.setLogo(data.custom_logo || '') + permissionStore.setHiddenRoutes(data.hidden_menu || []) resolve() }) diff --git a/web/src/store/modules/permission/index.ts b/web/src/store/modules/permission/index.ts index bd67771a..33f1cc5f 100644 --- a/web/src/store/modules/permission/index.ts +++ b/web/src/store/modules/permission/index.ts @@ -32,9 +32,10 @@ export const usePermissionStore = defineStore('permission', { }, resetPermission() { this.$reset() + }, + /** 设置隐藏的菜单 */ + setHiddenRoutes(hiddenRoutes: string[]) { + this.hiddenRoutes = hiddenRoutes } - }, - persist: { - pick: ['hiddenRoutes'] } }) diff --git a/web/src/store/modules/theme/index.ts b/web/src/store/modules/theme/index.ts index eea3c062..ac69d419 100644 --- a/web/src/store/modules/theme/index.ts +++ b/web/src/store/modules/theme/index.ts @@ -53,7 +53,13 @@ export const useThemeStore = defineStore('theme', { /** 设置名称 */ setName(name: string) { this.name = name + }, + /** 设置 Logo */ + setLogo(logo: string) { + this.logo = logo } }, - persist: true + persist: { + pick: ['isMobile', 'darkMode', 'sider', 'header', 'tab', 'locale', 'name'] + } }) diff --git a/web/src/views/setting/IndexView.vue b/web/src/views/setting/IndexView.vue index 3f3c7543..eecea77d 100644 --- a/web/src/views/setting/IndexView.vue +++ b/web/src/views/setting/IndexView.vue @@ -7,7 +7,7 @@ import { NButton } from 'naive-ui' import { useGettext } from 'vue3-gettext' import setting from '@/api/panel/setting' -import { useThemeStore } from '@/store' +import { usePermissionStore, useThemeStore } from '@/store' import CreateModal from '@/views/setting/CreateModal.vue' import SettingBase from '@/views/setting/SettingBase.vue' import SettingSafe from '@/views/setting/SettingSafe.vue' @@ -15,6 +15,7 @@ import SettingUser from '@/views/setting/SettingUser.vue' const { $gettext } = useGettext() const themeStore = useThemeStore() +const permissionStore = usePermissionStore() const currentTab = ref('base') const createModal = ref(false) @@ -34,6 +35,8 @@ const { data: model } = useRequest(setting.list, { bind_ua: [], website_path: '', backup_path: '', + hidden_menu: [], + custom_logo: '', https: false, acme: false, public_ip: [], @@ -54,6 +57,10 @@ const handleSave = () => { themeStore.setLocale(model.value.locale) } + // 更新隐藏菜单和自定义 Logo + themeStore.setLogo(model.value.custom_logo || '') + permissionStore.setHiddenRoutes(model.value.hidden_menu || []) + // 如果需要重启,则自动刷新页面 if (data.restart) { window.$message.info($gettext('Panel is restarting, page will refresh in 5 seconds')) diff --git a/web/src/views/setting/SettingBase.vue b/web/src/views/setting/SettingBase.vue index 8372d1cb..daf39ec0 100644 --- a/web/src/views/setting/SettingBase.vue +++ b/web/src/views/setting/SettingBase.vue @@ -1,8 +1,14 @@