共享组件启动

This commit is contained in:
2025-12-06 17:04:49 +08:00
parent 39579ff75f
commit fd02caf921
14 changed files with 296 additions and 4 deletions

View File

@@ -3493,7 +3493,8 @@
"version": "1.0.0", "version": "1.0.0",
"dependencies": { "dependencies": {
"ofetch": "^1.4.1", "ofetch": "^1.4.1",
"vue": "^3.5.13" "vue": "^3.5.13",
"vue-router": "^4.2.0"
}, },
"devDependencies": { "devDependencies": {
"@originjs/vite-plugin-federation": "^1.3.6", "@originjs/vite-plugin-federation": "^1.3.6",

View File

@@ -0,0 +1,12 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Shared Components Demo</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

View File

@@ -5,6 +5,7 @@
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"dev:demo": "vite",
"build": "run-p build:*", "build": "run-p build:*",
"build:esm": "vite build --mode esm", "build:esm": "vite build --mode esm",
"build:federation": "vite build --mode federation", "build:federation": "vite build --mode federation",
@@ -12,7 +13,8 @@
}, },
"dependencies": { "dependencies": {
"vue": "^3.5.13", "vue": "^3.5.13",
"ofetch": "^1.4.1" "ofetch": "^1.4.1",
"vue-router": "^4.2.0"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^20.10.0", "@types/node": "^20.10.0",

View File

@@ -5,9 +5,15 @@ settings:
excludeLinksFromLockfile: false excludeLinksFromLockfile: false
dependencies: dependencies:
ofetch:
specifier: ^1.4.1
version: 1.5.1
vue: vue:
specifier: ^3.5.13 specifier: ^3.5.13
version: 3.5.25(typescript@5.9.3) version: 3.5.25(typescript@5.9.3)
vue-router:
specifier: ^4.2.0
version: 4.6.3(vue@3.5.25)
devDependencies: devDependencies:
'@originjs/vite-plugin-federation': '@originjs/vite-plugin-federation':
@@ -863,6 +869,10 @@ packages:
'@vue/compiler-dom': 3.5.25 '@vue/compiler-dom': 3.5.25
'@vue/shared': 3.5.25 '@vue/shared': 3.5.25
/@vue/devtools-api@6.6.4:
resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==}
dev: false
/@vue/reactivity@3.5.25: /@vue/reactivity@3.5.25:
resolution: {integrity: sha512-5xfAypCQepv4Jog1U4zn8cZIcbKKFka3AgWHEFQeK65OW+Ys4XybP6z2kKgws4YB43KGpqp5D/K3go2UPPunLA==} resolution: {integrity: sha512-5xfAypCQepv4Jog1U4zn8cZIcbKKFka3AgWHEFQeK65OW+Ys4XybP6z2kKgws4YB43KGpqp5D/K3go2UPPunLA==}
dependencies: dependencies:
@@ -1090,6 +1100,10 @@ packages:
object-keys: 1.1.1 object-keys: 1.1.1
dev: true dev: true
/destr@2.0.5:
resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==}
dev: false
/dunder-proto@1.0.1: /dunder-proto@1.0.1:
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@@ -1679,6 +1693,10 @@ packages:
resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==}
dev: true dev: true
/node-fetch-native@1.6.7:
resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==}
dev: false
/node-releases@2.0.27: /node-releases@2.0.27:
resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==}
dev: true dev: true
@@ -1730,6 +1748,14 @@ packages:
object-keys: 1.1.1 object-keys: 1.1.1
dev: true dev: true
/ofetch@1.5.1:
resolution: {integrity: sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==}
dependencies:
destr: 2.0.5
node-fetch-native: 1.6.7
ufo: 1.6.1
dev: false
/own-keys@1.0.1: /own-keys@1.0.1:
resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@@ -2149,6 +2175,10 @@ packages:
engines: {node: '>=14.17'} engines: {node: '>=14.17'}
hasBin: true hasBin: true
/ufo@1.6.1:
resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==}
dev: false
/unbox-primitive@1.1.0: /unbox-primitive@1.1.0:
resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@@ -2232,6 +2262,15 @@ packages:
fsevents: 2.3.3 fsevents: 2.3.3
dev: true dev: true
/vue-router@4.6.3(vue@3.5.25):
resolution: {integrity: sha512-ARBedLm9YlbvQomnmq91Os7ck6efydTSpRP3nuOKCvgJOHNrhRoJDSKtee8kcL1Vf7nz6U+PMBL+hTvR3bTVQg==}
peerDependencies:
vue: ^3.5.0
dependencies:
'@vue/devtools-api': 6.6.4
vue: 3.5.25(typescript@5.9.3)
dev: false
/vue@3.5.25(typescript@5.9.3): /vue@3.5.25(typescript@5.9.3):
resolution: {integrity: sha512-YLVdgv2K13WJ6n+kD5owehKtEXwdwXuj2TTyJMsO7pSeKw2bfRNZGjhB7YzrpbMYj5b5QsUebHpOqR3R3ziy/g==} resolution: {integrity: sha512-YLVdgv2K13WJ6n+kD5owehKtEXwdwXuj2TTyJMsO7pSeKw2bfRNZGjhB7YzrpbMYj5b5QsUebHpOqR3R3ziy/g==}
peerDependencies: peerDependencies:

View File

@@ -0,0 +1,34 @@
<template>
<router-view />
</template>
<script setup lang="ts">
// 根组件只包含路由视图,布局和样式由 DefaultLayout 组件处理
</script>
<style>
/* 全局样式 */
body {
margin: 0;
padding: 0;
font-family: Arial, sans-serif;
line-height: 1.6;
}
/* 全局链接样式 */
a {
color: #2c3e50;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
/* 响应式设计 */
@media (max-width: 768px) {
.app-layout {
padding: 0 12px;
}
}
</style>

View File

@@ -0,0 +1,12 @@
<template>
<div>
</div>
</template>
<script setup lang="ts">
</script>
<style lang="scss" scoped>
</style>

View File

@@ -0,0 +1,9 @@
<template>
</template>
<script setup lang="ts">
</script>
<style lang="scss" scoped>
</style>

View File

@@ -0,0 +1 @@
export { default as FileUpload } from './FileUpload.vue'

View File

@@ -0,0 +1 @@
export * from './fileupload'

View File

@@ -0,0 +1,39 @@
<template>
<div class="app-layout">
<header>
<h1>Shared Components Demo</h1>
<nav>
<router-link to="/">Home</router-link> |
<router-link to="/fileupload">FileUpload Demo</router-link>
</nav>
</header>
<main>
<router-view />
</main>
</div>
</template>
<style scoped>
.app-layout {
max-width: 1200px;
margin: 0 auto;
padding: 0 16px;
}
header {
margin-bottom: 24px;
padding-bottom: 16px;
border-bottom: 1px solid #eaeaea;
}
nav {
margin: 12px 0;
}
nav a {
margin-right: 16px;
color: #2c3e50;
text-decoration: none;
}
nav a.router-link-exact-active {
color: #42b983;
font-weight: bold;
}
</style>

View File

@@ -0,0 +1,5 @@
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
createApp(App).use(router).mount('#app')

View File

@@ -0,0 +1,121 @@
import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router'
import { defineAsyncComponent } from 'vue'
// 扩展 ImportMeta 接口
declare global {
interface ImportMeta {
glob: (pattern: string) => Record<string, () => Promise<any>>
}
}
// 定义路由项类型
interface RouteItem {
path: string
name: string
}
// 布局组件
const DefaultLayout = defineAsyncComponent(
() => import('../layouts/DefaultLayout.vue')
)
// 首页组件
const Home = {
props: {
routes: {
type: Array as () => RouteItem[],
required: true
}
},
template: `
<div>
<h2>Welcome</h2>
<p>This is the shared components demo home.</p>
<ul>
<li v-for="route in routes" :key="route.path">
<router-link :to="route.path">
{{ route.path === '/' ? 'Home' : route.path.slice(1) }}
</router-link>
</li>
</ul>
</div>
`
}
// 生成路由配置
const routes: RouteRecordRaw[] = [
{
path: '/',
component: DefaultLayout,
children: [
{
path: '',
component: Home,
props: {
routes: [] as RouteItem[]
}
}
]
}
]
// 自动导入所有以 Test.vue 结尾的组件
const testComponents = import.meta.glob('../components/**/*Test.vue')
// 为每个测试组件创建路由
Object.entries(testComponents).forEach(([path, component]) => {
// 从路径中提取路由路径
// 例如: ../components/fileupload/FileUploadTest.vue -> /fileupload
const routePath = path
.replace('../components/', '/') // 移除相对路径
.replace(/\/\w+Test\.vue$/, '') // 移除 Test.vue 部分
.toLowerCase() // 统一小写
const routeName = routePath.slice(1) || 'home'
const routeConfig: RouteRecordRaw = {
path: routePath,
name: routeName,
component: DefaultLayout,
children: [
{
path: '',
name: `${routeName}-content`,
component: defineAsyncComponent(component as any),
meta: { title: `${routeName} Demo` }
}
]
}
// 添加路由
if (routePath === '/') {
// 如果是根路径,合并到现有路由
const rootRoute = routes[0]
if (rootRoute.children) {
rootRoute.children.push(...routeConfig.children!)
}
} else {
routes.push(routeConfig)
}
// 将路由添加到首页的 props 中
const homeRoute = routes[0]
if (homeRoute.props && typeof homeRoute.props === 'object' && !Array.isArray(homeRoute.props)) {
const props = homeRoute.props as { routes: RouteItem[] }
props.routes.push({
path: routePath,
name: routeName
})
}
})
const router = createRouter({
history: createWebHistory(),
routes
})
// 设置页面标题
router.beforeEach((to) => {
document.title = (to.meta.title as string) || 'Component Demo'
})
export default router

View File

@@ -0,0 +1,12 @@
/* eslint-disable */
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}
declare module '*.md' {
import type { ComponentOptions } from 'vue'
const Component: ComponentOptions
export default Component
}

View File

@@ -17,7 +17,11 @@ const __dirname = dirname(__filename)
*/ */
export default defineConfig({ export default defineConfig({
plugins: [vue(), vueJsx()], plugins: [vue(), vueJsx()],
define: {
__VUE_PROD_DEVTOOLS__: true,
// __VUE_OPTIONS_API__: true, // 确保启用 Options API
// __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: true
},
resolve: { resolve: {
alias: [ alias: [
{ find: '@', replacement: resolve(__dirname, 'src') } { find: '@', replacement: resolve(__dirname, 'src') }
@@ -98,7 +102,7 @@ export default defineConfig({
headers: { headers: {
'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, OPTIONS', 'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Content-Type': 'application/javascript; charset=utf-8' // 'Content-Type': 'application/javascript; charset=utf-8'
} }
} }
}) })