Junjie
2 天以前 5447ff1e5abeb1d45c9e331f78a0e523695b2561
#WCS系统自定义上传LOGO、标题、名称V3.0.1.8
2个文件已添加
8个文件已修改
171 ■■■■■ 已修改文件
src/main/java/com/zy/asrs/controller/OpenController.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/system/controller/ConfigController.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/sql/20260505_add_branding_config.sql 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/sql/20260505_alter_sys_config_value_to_mediumtext.sql 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/static/js/config/config.js 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/static/js/login/login.js 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/config/config.html 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/index.html 34 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/login.html 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/controller/OpenController.java
@@ -391,6 +391,15 @@
        return R.ok().add(map);
    }
    @GetMapping("/getBranding")
    public R getBranding() {
        HashMap<String, Object> map = new HashMap<>();
        map.put("logo", configService.getConfigValue("system.logo", "/static/images/zy-logo.png"));
        map.put("title", configService.getConfigValue("system.title", "浙江中扬 - 自动化立体仓库 - WCS"));
        map.put("copyright", configService.getConfigValue("system.copyright", "© 2026 浙江中扬立库技术有限公司"));
        return R.ok().add(map);
    }
    @PostMapping("/runtimeConfig/update")
    @OpenApiLog(memo = "修改运行参数")
    public R updateRuntimeConfig(@RequestBody RuntimeConfigUpdateParam param) {
src/main/java/com/zy/system/controller/ConfigController.java
@@ -17,7 +17,9 @@
import com.zy.core.enums.RedisKeyType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.*;
@RestController
@@ -226,6 +228,38 @@
        return R.ok(types);
    }
    @PostMapping("/config/uploadLogo/auth")
    @ManagerAuth
    public R uploadLogo(@RequestParam("file") MultipartFile file) {
        if (file == null || file.isEmpty()) {
            return R.error("请选择文件");
        }
        String originalName = file.getOriginalFilename();
        if (originalName == null) {
            return R.error("文件名为空");
        }
        String ext = originalName.contains(".") ? originalName.substring(originalName.lastIndexOf(".") + 1).toLowerCase() : "";
        String mimeType;
        switch (ext) {
            case "png": mimeType = "image/png"; break;
            case "jpg":
            case "jpeg": mimeType = "image/jpeg"; break;
            case "svg": mimeType = "image/svg+xml"; break;
            case "ico": mimeType = "image/x-icon"; break;
            default: return R.error("仅支持 png/jpg/jpeg/svg/ico 格式");
        }
        try {
            String base64 = Base64.getEncoder().encodeToString(file.getBytes());
            String dataUri = "data:" + mimeType + ";base64," + base64;
            configService.saveConfigValue("system.logo", dataUri);
            Parameter.reset();
            configService.refreshSystemConfigCache();
            return R.ok().add(dataUri);
        } catch (IOException e) {
            return R.error("上传失败:" + e.getMessage());
        }
    }
    private static boolean checkJson(String val){
        Object parse = null;
src/main/resources/application.yml
@@ -1,6 +1,6 @@
# 系统版本信息
app:
  version: 3.0.1.7
  version: 3.0.1.8
  version-type: prd  # prd 或 dev
  i18n:
    default-locale: zh-CN
src/main/resources/sql/20260505_add_branding_config.sql
New file
@@ -0,0 +1,24 @@
-- 新增系统外观配置:Logo、标题、版权信息
-- 说明:执行本脚本后,请在"角色授权"里给对应角色勾选"Config 管理"菜单权限。
--       配置可在 Config 管理页面通过 select_type = 'system' 筛选后编辑。
INSERT INTO sys_config(name, code, value, type, status, select_type)
SELECT '系统Logo', 'system.logo', '/static/images/zy-logo.png', 1, 1, 'system'
FROM dual
WHERE NOT EXISTS (
    SELECT 1 FROM sys_config WHERE code = 'system.logo'
);
INSERT INTO sys_config(name, code, value, type, status, select_type)
SELECT '系统标题', 'system.title', 'WCS系统V3.0', 1, 1, 'system'
FROM dual
WHERE NOT EXISTS (
    SELECT 1 FROM sys_config WHERE code = 'system.title'
);
INSERT INTO sys_config(name, code, value, type, status, select_type)
SELECT '版权信息', 'system.copyright', '© 2026 浙江中扬立库技术有限公司', 1, 1, 'system'
FROM dual
WHERE NOT EXISTS (
    SELECT 1 FROM sys_config WHERE code = 'system.copyright'
);
src/main/resources/sql/20260505_alter_sys_config_value_to_mediumtext.sql
New file
@@ -0,0 +1,2 @@
-- 扩大 sys_config.value 列容量,支持存储 base64 编码的 Logo 等大数据
ALTER TABLE sys_config MODIFY COLUMN value MEDIUMTEXT;
src/main/webapp/static/js/config/config.js
@@ -2036,6 +2036,39 @@
                            }
                        });
                    }).catch(function () {});
                },
                triggerLogoUpload: function () {
                    this.$refs.logoFileInput.click();
                },
                handleLogoUpload: function (e) {
                    var file = e.target.files[0];
                    if (!file) return;
                    var self = this;
                    var formData = new FormData();
                    formData.append("file", file);
                    $.ajax({
                        url: baseUrl + "/config/uploadLogo/auth",
                        method: "POST",
                        headers: self.authHeaders(),
                        data: formData,
                        processData: false,
                        contentType: false,
                        success: function (res) {
                            if (self.handleForbidden(res)) {
                                return;
                            }
                            if (res && res.code === 200) {
                                self.$message.success("Logo上传成功");
                                self.loadTable();
                            } else {
                                self.$message.error((res && res.msg) ? res.msg : "上传失败");
                            }
                        },
                        error: function () {
                            self.$message.error("上传失败");
                        }
                    });
                    e.target.value = "";
                }
            })
        });
src/main/webapp/static/js/login/login.js
@@ -18,6 +18,9 @@
                uploadDialogVisible: false,
                mfaDialogVisible: false,
                licenseBase64: "",
                brandLogo: "",
                brandTitle: "",
                brandCopyright: "",
                titleClickCount: 0,
                titleClickTimer: null,
                loginForm: {
@@ -64,6 +67,7 @@
        },
        created: function () {
            this.initLanguageSwitch();
            this.loadBranding();
        },
        methods: {
            text: function (key, fallback) {
@@ -77,6 +81,27 @@
                }
                return fallback || key;
            },
            loadBranding: function () {
                var that = this;
                $.ajax({
                    url: baseUrl + "/openapi/getBranding",
                    method: "GET",
                    success: function (res) {
                        if (res.code === 200 && res.data) {
                            that.brandLogo = res.data.logo || "";
                            that.brandTitle = res.data.title || "";
                            that.brandCopyright = res.data.copyright || "";
                            if (res.data.title) {
                                document.title = res.data.title + " - 登录";
                            }
                            if (res.data.logo) {
                                var link = document.querySelector('link[rel="icon"]');
                                if (link) link.href = res.data.logo.indexOf("data:") === 0 ? res.data.logo : res.data.logo + "?v=" + Date.now();
                            }
                        }
                    }
                });
            },
            refreshRuleMessages: function () {
                var vm = this;
                vm.loginRules = {
src/main/webapp/views/config/config.html
@@ -453,6 +453,8 @@
                    <div class="toolbar-ops">
                        <el-button size="small" type="primary" plain icon="el-icon-plus" @click="openCreateDialog">新增</el-button>
                        <el-button size="small" type="danger" plain icon="el-icon-delete" :disabled="selection.length === 0" @click="removeSelection">删除</el-button>
                        <el-button size="small" plain icon="el-icon-upload2" @click="triggerLogoUpload">上传Logo</el-button>
                        <input ref="logoFileInput" type="file" accept="image/png,image/jpeg,image/svg+xml,image/x-icon" style="display:none" @change="handleLogoUpload">
                        <el-popover
                            placement="bottom"
                            width="320"
src/main/webapp/views/index.html
@@ -638,7 +638,7 @@
        :class="{ 'is-animating': collapseAnimating }"
        :width="asideWidth">
      <div class="aside-logo" :class="{ 'is-collapse': asideCompact }">
        <img class="aside-logo-image" src="../static/images/zy-logo.png" alt="浙江中扬">
        <img class="aside-logo-image" :src="brandLogo || '../static/images/zy-logo.png'" alt="Logo">
      </div>
      <transition name="aside-fade">
@@ -703,7 +703,7 @@
      <transition name="aside-fade">
        <div class="aside-footer" v-show="showAsideExtras">
          <div class="aside-footer-copy">© 2026 {{ t('app.company') }}</div>
          <div class="aside-footer-copy">{{ brandCopyright || ('© 2026 ' + t('app.company')) }}</div>
          <div class="aside-footer-version">
            <span class="aside-footer-version-text">{{ versionText }}</span>
            <el-tag
@@ -964,7 +964,10 @@
        userName: localStorage.getItem(USER_STORAGE_KEY) || (window.WCS_I18N ? window.WCS_I18N.tl("管理员") : "管理员"),
        aiAssistantVisible: false,
        aiAssistantMounted: false,
        aiAssistantSrc: baseUrl + "/views/ai/diagnosis.html"
        aiAssistantSrc: baseUrl + "/views/ai/diagnosis.html",
        brandLogo: "",
        brandTitle: "",
        brandCopyright: ""
      };
    },
    computed: {
@@ -1081,6 +1084,7 @@
      this.bindI18n();
      this.installCompatBridge();
      this.loadSystemVersion();
      this.loadBranding();
      this.loadMenu();
      this.loadLicenseDays();
      this.checkFakeStatus();
@@ -1288,7 +1292,7 @@
        tab.group = this.tl(tab.group);
      },
      updateDocumentTitle: function (title) {
        document.title = title + " - " + this.t("app.title");
        document.title = title + " - " + (this.brandTitle || this.t("app.title"));
      },
      resolveMenuIcon: function (code) {
        var iconMap = {
@@ -1893,6 +1897,28 @@
          }
        });
      },
      loadBranding: function () {
        var that = this;
        $.ajax({
          url: baseUrl + "/openapi/getBranding",
          headers: { token: localStorage.getItem("token") },
          method: "GET",
          success: function (res) {
            if (res.code === 200 && res.data) {
              that.brandLogo = res.data.logo || "";
              that.brandTitle = res.data.title || "";
              that.brandCopyright = res.data.copyright || "";
              if (res.data.title) {
                document.title = that.activeTabTitle + " - " + res.data.title;
              }
              if (res.data.logo) {
                var link = document.querySelector('link[rel="icon"]');
                if (link) link.href = res.data.logo.indexOf("data:") === 0 ? res.data.logo : res.data.logo + "?v=" + Date.now();
              }
            }
          }
        });
      },
      loadLicenseDays: function () {
        var that = this;
        $.ajax({
src/main/webapp/views/login.html
@@ -481,7 +481,7 @@
    <div class="login-layout">
        <section class="hero-panel animate__animated animate__fadeInLeft">
            <div class="brand-chip">Zoneyung WCS</div>
            <div class="brand-chip">{{ brandTitle || 'Zoneyung WCS' }}</div>
            <div class="hero-title">{{ text('login.hero.title', 'WCS系统让设备调度、任务执行与现场监控保持在同一套业务链路中。') }}</div>
            <div class="hero-subtitle">
                {{ text('login.hero.subtitle', 'Warehouse Control System 面向自动化立体仓库现场执行层,围绕堆垛机、RGV、输送站台、任务指令、库位状态和日志追踪进行统一调度与可视化管理,帮助仓储系统实现稳定、可追溯、可联动的作业控制。浙江中扬立库技术有限公司长期专注于自动化立体仓库与智能物流系统建设,覆盖方案设计、软件控制、设备集成与项目实施交付。') }}
@@ -501,14 +501,14 @@
                </div>
            </div>
            <div class="hero-footer">
                <span>{{ text('login.hero.company.name', '浙江中扬立库技术有限公司') }}</span>
                <span>{{ brandCopyright || text('login.hero.company.name', '浙江中扬立库技术有限公司') }}</span>
                <span>{{ text('login.hero.company.solution', '自动化立体仓库与智能物流系统解决方案') }}</span>
            </div>
        </section>
        <section class="login-card animate__animated animate__fadeInUp">
            <div class="login-head">
                <h1 class="login-title" @click="handleTitleClick">{{ text('login.title', 'WCS系统V3.0') }}</h1>
                <h1 class="login-title" @click="handleTitleClick">{{ brandTitle || text('login.title', 'WCS系统V3.0') }}</h1>
                <div class="login-subtitle">{{ text('login.subtitle', '请输入账号和密码进入系统。') }}</div>
            </div>
            <div class="login-body">