Преглед изворни кода

品牌信息(logo / 顶部标题 / 公司名)改为运行期可配

  通过 public/config.js 暴露 window.__APP_CONFIG__,部署后可直接编辑
  dist/config.js 切换品牌,无需重新构建。Logo 和登录页标题图改放在
  public/brand/ 下,覆盖文件即生效,便于多地区/多租户挂载不同卷复用
  同一份 dist。

  优先级:window.__APP_CONFIG__ > VUE_APP_BRAND_* > 代码默认值
画安 пре 22 часа
родитељ
комит
3a27fe8396

+ 9 - 0
.env.development

@@ -12,3 +12,12 @@ VUE_APP_AMAP_SECURITY_CODE=a7413c674852c5eaf01d90813c5b7ef6
 
 # Mock 开关:开发期默认开启
 VUE_APP_USE_MOCK=true
+
+# ─────────────────────────────────────────────────
+# 品牌信息(构建期默认值;部署后可改 dist/config.js 覆盖)
+# 留空则回退到 src/utils/brand.js 中的代码默认值
+# ─────────────────────────────────────────────────
+VUE_APP_BRAND_TITLE=交通信号控制平台—灵•智
+VUE_APP_BRAND_COMPANY=北京东土正创科技有限公司
+VUE_APP_BRAND_LOGO_URL=brand/logo.png
+VUE_APP_BRAND_LOGIN_TITLE_URL=brand/login-title.png

+ 10 - 0
.env.production

@@ -17,3 +17,13 @@ VUE_APP_USE_MOCK=true
 
 # WebSocket 推送地址(预留,未启用前可留空)
 # VUE_APP_WS_URL=wss://dtscreen.your-domain.com
+
+# ─────────────────────────────────────────────────
+# 品牌信息(构建期默认值)
+# 部署后无需重新构建——直接编辑 dist/config.js 中的 window.__APP_CONFIG__
+# 即可热替换标题/公司名/logo(logo 文件放在 dist/brand/ 目录下)
+# ─────────────────────────────────────────────────
+VUE_APP_BRAND_TITLE=交通信号控制平台—灵•智
+VUE_APP_BRAND_COMPANY=北京东土正创科技有限公司
+VUE_APP_BRAND_LOGO_URL=brand/logo.png
+VUE_APP_BRAND_LOGIN_TITLE_URL=brand/login-title.png

BIN
public/brand/login-title.png


BIN
public/brand/logo.png


+ 17 - 0
public/config.js

@@ -0,0 +1,17 @@
+/**
+ * 运行期品牌配置(不进 webpack 打包,部署后可直接编辑 dist/config.js 切换品牌)
+ *
+ * 优先级:window.__APP_CONFIG__ > VUE_APP_BRAND_* (.env) > 代码默认值
+ *
+ * 字段说明:
+ *  - brandTitle:          顶部大标题文字
+ *  - brandCompany:        登录页底部公司名
+ *  - brandLogoUrl:        Logo 图片路径(相对站点根,默认指向 brand/logo.png)
+ *  - brandLoginTitleUrl:  登录页标题图(如不需要可置空字符串,会回退为文字标题)
+ */
+window.__APP_CONFIG__ = {
+  brandTitle:         '交通信号控制平台—灵•智',
+  brandCompany:       '北京东土正创科技有限公司',
+  brandLogoUrl:       'brand/logo.png',
+  brandLoginTitleUrl: 'brand/login-title.png'
+};

+ 3 - 1
public/index.html

@@ -7,7 +7,9 @@
     <link rel="icon" href="<%= BASE_URL %>favicon.ico">
     
     <link rel="preconnect" href="https://cdn.staticfile.org">
-    
+
+    <script src="<%= BASE_URL %>config.js"></script>
+
     <title><%= htmlWebpackPlugin.options.title %></title>
     
     <!-- <script>

+ 4 - 3
src/layouts/DashboardLayout.vue

@@ -2,9 +2,9 @@
     <div class="fluid-dashboard">
         <div class="frame-top">
             <div class="top-logo">
-                <img src="@/assets/images/logo.png" />
+                <img :src="brand.logo" />
             </div>
-            <div class="title glow-text" :data-text="title">{{ title }}</div>
+            <div class="title glow-text" :data-text="brand.title">{{ brand.title }}</div>
             <div class="top-right-actions">
                 <FullscreenToggle />
                 <UserProfile />
@@ -108,6 +108,7 @@ import ChangePassword from '@/components/ui/ChangePassword.vue';
 import CrossingMultiView from '@/components/ui/CrossingMultiView.vue';
 import CrossingDetailHeader from '@/components/ui/CrossingDetailHeader.vue';
 import OfflineTip from '@/components/ui/OfflineTip.vue';
+import brand from '@/utils/brand';
 
 export default {
     name: 'DashboardLayout',
@@ -158,7 +159,7 @@ export default {
     },
     data() {
         return {
-            title: '交通信号控制平台—灵•智',
+            brand,
         }
     },
     methods: {

+ 3 - 2
src/layouts/LoginLayout.vue

@@ -1,7 +1,7 @@
 <template>
     <div class="fluid-dashboard">
         <div class="frame-top">
-            <div class="title glow-text" :data-text="title">{{ title }}</div>
+            <div class="title glow-text" :data-text="brand.title">{{ brand.title }}</div>
             <div class="top-right-actions">
                 <FullscreenToggle />
             </div>
@@ -25,6 +25,7 @@
 
 
 import FullscreenToggle from '@/components/ui/FullscreenToggle.vue';
+import brand from '@/utils/brand';
 
 export default {
     name: 'LoginLayout',
@@ -42,7 +43,7 @@ export default {
     },
     data() {
         return {
-            title: '交通信号控制平台—灵•智',
+            brand,
         }
     },
     methods: {

+ 37 - 0
src/utils/brand.js

@@ -0,0 +1,37 @@
+/**
+ * 品牌信息统一入口。
+ *
+ * 优先级:window.__APP_CONFIG__(部署后可改) > VUE_APP_BRAND_*(构建期 .env) > 默认值。
+ * 静态资源(logo / 标题图)通过 BASE_URL 拼接,指向 public/ 下的文件,因此 build 后亦可替换。
+ */
+
+const runtime = (typeof window !== 'undefined' && window.__APP_CONFIG__) || {};
+const env = process.env || {};
+const baseUrl = env.BASE_URL || '/';
+
+function pick(runtimeKey, envKey, fallback) {
+  const v = runtime[runtimeKey];
+  if (v !== undefined && v !== null && v !== '') return v;
+  const e = env[envKey];
+  if (e !== undefined && e !== null && e !== '') return e;
+  return fallback;
+}
+
+function asAssetUrl(p) {
+  if (!p) return '';
+  if (/^(https?:)?\/\//.test(p) || p.startsWith('data:')) return p;
+  return baseUrl.replace(/\/$/, '') + '/' + p.replace(/^\//, '');
+}
+
+export const brand = {
+  title:      pick('brandTitle',         'VUE_APP_BRAND_TITLE',           '交通信号控制平台—灵•智'),
+  company:    pick('brandCompany',       'VUE_APP_BRAND_COMPANY',         '北京东土正创科技有限公司'),
+  logo:       asAssetUrl(pick('brandLogoUrl',       'VUE_APP_BRAND_LOGO_URL',       'brand/logo.png')),
+  loginTitle: asAssetUrl(pick('brandLoginTitleUrl', 'VUE_APP_BRAND_LOGIN_TITLE_URL','brand/login-title.png')),
+};
+
+if (typeof document !== 'undefined' && brand.title) {
+  document.title = brand.title;
+}
+
+export default brand;

+ 16 - 4
src/views/Login.vue

@@ -7,9 +7,9 @@
     </video>
     <div v-if="videoBgFailed" class="bg-fallback"></div>
 
-    <!-- 顶部居中 KYLAND Logo -->
+    <!-- 顶部居中 Logo -->
     <header class="top-bar">
-      <img class="kyland-logo" src="@/assets/images/logo.png" alt="KYLAND" />
+      <img class="kyland-logo" :src="brand.logo" :alt="brand.title" />
     </header>
 
     <!-- 主内容区:左右两栏 -->
@@ -20,7 +20,8 @@
         <div class="login-box">
           <!-- 标题 -->
           <div class="title-wrap">
-            <img class="title-img" src="@/assets/login/title.png" alt="交通信号控制平台—灵·智" />
+            <img v-if="brand.loginTitle" class="title-img" :src="brand.loginTitle" :alt="brand.title" />
+            <div v-else class="title-text glow-text">{{ brand.title }}</div>
           </div>
 
           <!-- 表单 -->
@@ -61,7 +62,7 @@
     </div>
 
     <!-- 版权 -->
-    <footer class="copyright">北京东土正创科技有限公司</footer>
+    <footer class="copyright">{{ brand.company }}</footer>
 
   </div>
 </template>
@@ -69,6 +70,7 @@
 <script>
 import CaptchaCanvas from "@/components/CaptchaCanvas.vue";
 import { apiLogin } from "@/api";
+import brand from "@/utils/brand";
 
 export default {
   name: "LoginPage",
@@ -78,6 +80,7 @@ export default {
   },
   data() {
     return {
+      brand,
       videoBgFailed: false,
       username: "admin",
       password: "123456",
@@ -202,6 +205,15 @@ export default {
   width: auto;
   max-width: 100%;
 }
+.title-text {
+  font-family: var(--title-font-family);
+  font-size: 48px;
+  color: #ffffff;
+  line-height: 63px;
+  text-align: center;
+  font-style: normal;
+  text-transform: none;
+}
 
 .login-form {
   display: flex;

+ 11 - 0
vue.config.js

@@ -1,7 +1,18 @@
 const { defineConfig } = require('@vue/cli-service')
+
+// 首屏 <title> 取自 .env 的 VUE_APP_BRAND_TITLE(构建期);
+// 运行期会被 src/utils/brand.js 用 window.__APP_CONFIG__ 再次覆盖。
+const BRAND_TITLE = process.env.VUE_APP_BRAND_TITLE || '交通信号控制平台—灵•智'
+
 module.exports = defineConfig({
   transpileDependencies: true,
   lintOnSave: false,
+  chainWebpack: config => {
+    config.plugin('html').tap(args => {
+      args[0].title = BRAND_TITLE
+      return args
+    })
+  },
   // 【核心修复代码】:配置 Webpack fallback
   configureWebpack: {
     externals: {