xto-fronted 0.8.2 → 0.8.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -43,6 +43,11 @@ export declare function getApiBaseUrl(): string;
43
43
  * 获取应用基础路径(用于路由 base)
44
44
  */
45
45
  export declare function getBasePath(): string;
46
+ /**
47
+ * 拼接部署路径的静态资源地址(public 目录资源,如 logo)
48
+ * 部署在子路径(如 /lending)时,绝对路径 /vite.svg 会落到域名根路径 404,需自动补前缀
49
+ */
50
+ export declare function withBasePath(path: string): string;
46
51
  export declare const appConfig: {
47
52
  appId: import('vue').Ref<string, string>;
48
53
  clientId: import('vue').Ref<string, string>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xto-fronted",
3
- "version": "0.8.2",
3
+ "version": "0.8.4",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "XTO 前端应用框架",
@@ -1,5 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import { computed, ref, onMounted, onUnmounted, watch } from 'vue'
3
+ import { withBasePath } from '@/utils/config'
3
4
  import { useRoute, useRouter } from 'vue-router'
4
5
  import { useMenuStore } from '@/stores/menu'
5
6
  import { useAppStore } from '@/stores/app'
@@ -13,9 +14,11 @@ import { Drawer } from '@xto/feedback'
13
14
  const props = withDefaults(defineProps<{
14
15
  logoSrc?: string
15
16
  }>(), {
16
- logoSrc: '/vite.svg'
17
17
  })
18
18
 
19
+ // logo 地址在渲染期解析:模块加载早于 createXtoApp 的运行时配置,不能作为默认值立即求值
20
+ const resolvedLogoSrc = computed(() => props.logoSrc || withBasePath('/vite.svg'))
21
+
19
22
  const route = useRoute()
20
23
  const router = useRouter()
21
24
  const menuStore = useMenuStore()
@@ -359,7 +362,7 @@ onUnmounted(() => {
359
362
  <div class="mix-top-menu">
360
363
  <!-- 左侧 Logo -->
361
364
  <div class="mix-top-menu__logo">
362
- <img :src="props.logoSrc" alt="Logo" class="mix-top-menu__logo-img" />
365
+ <img :src="resolvedLogoSrc" alt="Logo" class="mix-top-menu__logo-img" />
363
366
  <span class="mix-top-menu__logo-text">{{ appStore.appName }}</span>
364
367
  </div>
365
368
 
@@ -1,5 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import { computed, ref, watch } from 'vue'
3
+ import { withBasePath } from '@/utils/config'
3
4
  import { useRoute, useRouter } from 'vue-router'
4
5
  import { useMenuStore } from '@/stores/menu'
5
6
  import { useUserStore } from '@/stores/user'
@@ -20,9 +21,11 @@ const props = withDefaults(defineProps<{
20
21
  menuList: () => [],
21
22
  showLogo: true,
22
23
  showUser: true,
23
- logoSrc: '/vite.svg'
24
24
  })
25
25
 
26
+ // logo 地址在渲染期解析:模块加载早于 createXtoApp 的运行时配置,不能作为默认值立即求值
27
+ const resolvedLogoSrc = computed(() => props.logoSrc || withBasePath('/vite.svg'))
28
+
26
29
  const route = useRoute()
27
30
  const router = useRouter()
28
31
  const menuStore = useMenuStore()
@@ -129,7 +132,7 @@ const handleLogout = () => {
129
132
  <div class="sidebar" :class="{ 'sidebar--collapsed': isCollapsed }">
130
133
  <!-- Logo -->
131
134
  <div v-if="props.showLogo" class="sidebar__logo">
132
- <img :src="props.logoSrc" alt="Logo" class="sidebar__logo-img" />
135
+ <img :src="resolvedLogoSrc" alt="Logo" class="sidebar__logo-img" />
133
136
  <span v-show="!isCollapsed" class="sidebar__logo-text">{{ appStore.appName }}</span>
134
137
  </div>
135
138
 
@@ -1,5 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import { computed, ref, onMounted, onUnmounted } from 'vue'
3
+ import { withBasePath } from '@/utils/config'
3
4
  import { useRoute, useRouter } from 'vue-router'
4
5
  import { useMenuStore } from '@/stores/menu'
5
6
  import { useAppStore } from '@/stores/app'
@@ -13,9 +14,11 @@ import { Drawer } from '@xto/feedback'
13
14
  const props = withDefaults(defineProps<{
14
15
  logoSrc?: string
15
16
  }>(), {
16
- logoSrc: '/vite.svg'
17
17
  })
18
18
 
19
+ // logo 地址在渲染期解析:模块加载早于 createXtoApp 的运行时配置,不能作为默认值立即求值
20
+ const resolvedLogoSrc = computed(() => props.logoSrc || withBasePath('/vite.svg'))
21
+
19
22
  const route = useRoute()
20
23
  const router = useRouter()
21
24
  const menuStore = useMenuStore()
@@ -302,7 +305,7 @@ onUnmounted(() => {
302
305
  <div class="top-menu">
303
306
  <!-- 左侧 Logo -->
304
307
  <div class="top-menu__logo">
305
- <img :src="props.logoSrc" alt="Logo" class="top-menu__logo-img" />
308
+ <img :src="resolvedLogoSrc" alt="Logo" class="top-menu__logo-img" />
306
309
  <span class="top-menu__logo-text">{{ appStore.appName }}</span>
307
310
  </div>
308
311
 
@@ -1,5 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import { computed } from 'vue'
3
+ import { withBasePath } from '@/utils/config'
3
4
  import { useAppStore } from '@/stores/app'
4
5
  import { useMenuStore } from '@/stores/menu'
5
6
  import Sidebar from './Sidebar.vue'
@@ -11,9 +12,11 @@ import MixTopMenu from './MixTopMenu.vue'
11
12
  const props = withDefaults(defineProps<{
12
13
  logoSrc?: string
13
14
  }>(), {
14
- logoSrc: '/vite.svg'
15
15
  })
16
16
 
17
+ // logo 地址在渲染期解析:模块加载早于 createXtoApp 的运行时配置,不能作为默认值立即求值
18
+ const resolvedLogoSrc = computed(() => props.logoSrc || withBasePath('/vite.svg'))
19
+
17
20
  const appStore = useAppStore()
18
21
  const menuStore = useMenuStore()
19
22
 
@@ -51,7 +54,7 @@ const showMixSidebar = computed(() => {
51
54
  <!-- sidebar模式:左侧菜单 + 顶部Header + 主内容 -->
52
55
  <div v-if="layoutMode === 'sidebar'" class="layout layout--sidebar">
53
56
  <aside class="layout__aside" :style="{ width: sidebarWidth }">
54
- <Sidebar :menu-list="sidebarMenuList" :show-logo="true" :show-user="true" :logo-src="props.logoSrc" />
57
+ <Sidebar :menu-list="sidebarMenuList" :show-logo="true" :show-user="true" :logo-src="resolvedLogoSrc" />
55
58
  </aside>
56
59
  <div class="layout__main">
57
60
  <header class="layout__header">
@@ -66,7 +69,7 @@ const showMixSidebar = computed(() => {
66
69
  <!-- top模式:顶部菜单 + 主内容 -->
67
70
  <div v-if="layoutMode === 'top'" class="layout layout--top">
68
71
  <div class="layout__top-menu">
69
- <TopMenu :logo-src="props.logoSrc" />
72
+ <TopMenu :logo-src="resolvedLogoSrc" />
70
73
  </div>
71
74
  <div class="layout__main">
72
75
  <main class="layout__content">
@@ -79,7 +82,7 @@ const showMixSidebar = computed(() => {
79
82
  <div v-if="layoutMode === 'mix'" class="layout layout--mix">
80
83
  <!-- 顶部一级菜单 -->
81
84
  <div class="layout__mix-top-menu">
82
- <MixTopMenu :logo-src="props.logoSrc" />
85
+ <MixTopMenu :logo-src="resolvedLogoSrc" />
83
86
  </div>
84
87
  <!-- 下方:左侧子菜单 + 主内容 -->
85
88
  <div class="layout__mix-body">
@@ -6,7 +6,7 @@ import { createRouter as vueCreateRouter, createWebHistory } from 'vue-router'
6
6
  import type { RouteRecordRaw, Router } from 'vue-router'
7
7
  import { h, defineComponent } from 'vue'
8
8
  import Layout from '@/components/Layout/index.vue'
9
- import { getBasePath } from '@/utils/config'
9
+ import { getBasePath, withBasePath } from '@/utils/config'
10
10
 
11
11
  interface LayoutRouteOptions {
12
12
  indexPath?: string
@@ -28,7 +28,9 @@ export function createLayoutRoute(
28
28
  options: LayoutRouteOptions = {}
29
29
  ): RouteRecordRaw {
30
30
  const indexPath = options.indexPath || '/dashboard'
31
- const logoSrc = options.logoSrc || '/vite.svg'
31
+ // 注意:createLayoutRoute 常在模块顶层被调用(早于 createXtoApp 初始化运行时配置),
32
+ // logo 地址必须延迟到渲染时解析,不能在此处求值
33
+ const logoSrc = options.logoSrc
32
34
 
33
35
  // 添加 catch-all 路由,让404在Layout内部渲染,保持左侧菜单显示
34
36
  const catchAllRoute: RouteRecordRaw = {
@@ -44,7 +46,7 @@ export function createLayoutRoute(
44
46
  const LayoutWrapper = defineComponent({
45
47
  name: 'LayoutWrapper',
46
48
  render() {
47
- return h(Layout, { logoSrc })
49
+ return h(Layout, { logoSrc: logoSrc || withBasePath('/vite.svg') })
48
50
  }
49
51
  })
50
52
 
@@ -152,6 +152,16 @@ export function getBasePath(): string {
152
152
  return basePath.value
153
153
  }
154
154
 
155
+ /**
156
+ * 拼接部署路径的静态资源地址(public 目录资源,如 logo)
157
+ * 部署在子路径(如 /lending)时,绝对路径 /vite.svg 会落到域名根路径 404,需自动补前缀
158
+ */
159
+ export function withBasePath(path: string): string {
160
+ if (!path) return path
161
+ const base = getBasePath().replace(/\/$/, '')
162
+ return path.startsWith('/') ? `${base}${path}` : `${base}/${path}`
163
+ }
164
+
155
165
  // 导出响应式引用(供特殊场景直接使用)
156
166
  export const appConfig = {
157
167
  appId,