前端项目结构
This commit is contained in:
21
urbanLifelineWeb/packages/portal/Dockerfile.dev
Normal file
21
urbanLifelineWeb/packages/portal/Dockerfile.dev
Normal file
@@ -0,0 +1,21 @@
|
||||
FROM node:20-alpine
|
||||
|
||||
RUN npm install -g pnpm@latest
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# 复制依赖文件
|
||||
COPY packages/portal/package.json ./
|
||||
COPY pnpm-lock.yaml pnpm-workspace.yaml ./
|
||||
|
||||
# 安装依赖
|
||||
RUN pnpm install --frozen-lockfile
|
||||
|
||||
# 复制源代码
|
||||
COPY packages/portal/ ./
|
||||
|
||||
# 暴露端口
|
||||
EXPOSE 3000
|
||||
|
||||
# 开发模式(HMR)
|
||||
CMD ["pnpm", "dev", "--host", "0.0.0.0"]
|
||||
32
urbanLifelineWeb/packages/portal/index.html
Normal file
32
urbanLifelineWeb/packages/portal/index.html
Normal file
@@ -0,0 +1,32 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<title>泰豪电源 AI 数智化平台</title>
|
||||
|
||||
<!-- Import Maps 配置 - 只配置共享模块 -->
|
||||
<script type="importmap">
|
||||
{
|
||||
"imports": {
|
||||
"@shared/components": "/shared/components.js",
|
||||
"@shared/utils": "/shared/utils.js",
|
||||
"@shared/api": "/shared/api.js",
|
||||
"@shared/composables": "/shared/composables.js",
|
||||
"@shared/types": "/shared/types.js"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- 预加载关键模块 -->
|
||||
<link rel="modulepreload" href="/shared/components.js">
|
||||
<link rel="modulepreload" href="/shared/utils.js">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
<!-- ES Module 入口 -->
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
156
urbanLifelineWeb/packages/portal/src/views/ImportMapsExample.vue
Normal file
156
urbanLifelineWeb/packages/portal/src/views/ImportMapsExample.vue
Normal file
@@ -0,0 +1,156 @@
|
||||
<script setup lang="ts">
|
||||
/**
|
||||
* Import Maps 使用示例
|
||||
*
|
||||
* 通过 HTML 中的 <script type="importmap"> 配置,
|
||||
* 可以直接从 HTTP URL 导入共享组件
|
||||
*/
|
||||
|
||||
// ✅ 直接导入!浏览器会自动从 http://localhost/shared/components.js 加载
|
||||
import { UlTable } from '@shared/components'
|
||||
import { http } from '@shared/utils'
|
||||
import { authApi } from '@shared/api'
|
||||
import { useTable } from '@shared/composables'
|
||||
|
||||
// 类型定义
|
||||
interface User {
|
||||
id: string
|
||||
username: string
|
||||
email: string
|
||||
createTime: string
|
||||
}
|
||||
|
||||
// 使用共享的 useTable 组合式函数
|
||||
const {
|
||||
loading,
|
||||
tableData,
|
||||
pagination,
|
||||
handlePageChange,
|
||||
handleSizeChange,
|
||||
refresh
|
||||
} = useTable<User>({
|
||||
fetchData: async (params) => {
|
||||
// 使用共享的 API 函数
|
||||
return await authApi.getUserList(params)
|
||||
}
|
||||
})
|
||||
|
||||
// 表格列配置
|
||||
const columns = [
|
||||
{ prop: 'username', label: '用户名', minWidth: 150 },
|
||||
{ prop: 'email', label: '邮箱', minWidth: 200 },
|
||||
{ prop: 'createTime', label: '创建时间', width: 180 }
|
||||
]
|
||||
|
||||
// 测试 HTTP 请求
|
||||
const testRequest = async () => {
|
||||
try {
|
||||
// 使用共享的 http 工具
|
||||
const result = await http.get('/api/test')
|
||||
console.log('请求结果:', result)
|
||||
} catch (error) {
|
||||
console.error('请求失败:', error)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="import-maps-example">
|
||||
<div class="header">
|
||||
<h1>Import Maps 示例</h1>
|
||||
<p class="description">
|
||||
✅ 共享组件从 <code>http://localhost/shared/</code> 动态加载<br>
|
||||
✅ 无需打包,浏览器原生 ES Module 支持<br>
|
||||
✅ 真正的运行时共享,所有应用使用同一份代码
|
||||
</p>
|
||||
<el-button type="primary" @click="testRequest">
|
||||
测试 HTTP 请求
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 使用从 HTTP 加载的共享组件 -->
|
||||
<UlTable
|
||||
:data="tableData"
|
||||
:columns="columns"
|
||||
:loading="loading"
|
||||
:pagination="pagination"
|
||||
@page-change="handlePageChange"
|
||||
@size-change="handleSizeChange"
|
||||
/>
|
||||
|
||||
<div class="info">
|
||||
<h3>📦 当前加载的模块:</h3>
|
||||
<ul>
|
||||
<li><code>@shared/components</code> → http://localhost/shared/components.js</li>
|
||||
<li><code>@shared/utils</code> → http://localhost/shared/utils.js</li>
|
||||
<li><code>@shared/api</code> → http://localhost/shared/api.js</li>
|
||||
<li><code>@shared/composables</code> → http://localhost/shared/composables.js</li>
|
||||
</ul>
|
||||
|
||||
<h3>🔍 如何查看?</h3>
|
||||
<ol>
|
||||
<li>打开浏览器开发者工具 (F12)</li>
|
||||
<li>切换到 Network 标签页</li>
|
||||
<li>筛选 JS 类型</li>
|
||||
<li>刷新页面,可以看到从 /shared/ 加载的模块</li>
|
||||
</ol>
|
||||
|
||||
<h3>✨ 优势:</h3>
|
||||
<ul>
|
||||
<li>✅ 真正的代码共享(所有应用共用一份)</li>
|
||||
<li>✅ 支持热更新(修改共享组件,所有应用自动更新)</li>
|
||||
<li>✅ 减小构建体积(共享代码不打包到业务应用)</li>
|
||||
<li>✅ 浏览器缓存(共享模块只下载一次)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.import-maps-example {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.header {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.description {
|
||||
color: #666;
|
||||
line-height: 1.8;
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
.description code {
|
||||
background: #f0f0f0;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
font-family: 'Courier New', monospace;
|
||||
}
|
||||
|
||||
.info {
|
||||
margin-top: 32px;
|
||||
padding: 20px;
|
||||
background: #f5f7fa;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.info h3 {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 12px;
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.info ul, .info ol {
|
||||
line-height: 2;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.info code {
|
||||
background: #fff;
|
||||
padding: 2px 8px;
|
||||
border-radius: 3px;
|
||||
font-family: 'Courier New', monospace;
|
||||
color: #e6a23c;
|
||||
}
|
||||
</style>
|
||||
22
urbanLifelineWeb/packages/shared/Dockerfile.dev
Normal file
22
urbanLifelineWeb/packages/shared/Dockerfile.dev
Normal file
@@ -0,0 +1,22 @@
|
||||
FROM node:20-alpine
|
||||
|
||||
# 安装 pnpm
|
||||
RUN npm install -g pnpm@latest
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# 复制 package.json
|
||||
COPY packages/shared/package.json ./
|
||||
COPY pnpm-lock.yaml pnpm-workspace.yaml ./
|
||||
|
||||
# 安装依赖
|
||||
RUN pnpm install --frozen-lockfile
|
||||
|
||||
# 复制源代码
|
||||
COPY packages/shared/ ./
|
||||
|
||||
# 暴露端口
|
||||
EXPOSE 5000
|
||||
|
||||
# 开发模式启动(支持热更新)
|
||||
CMD ["pnpm", "dev", "--host", "0.0.0.0"]
|
||||
28
urbanLifelineWeb/packages/shared/package.json
Normal file
28
urbanLifelineWeb/packages/shared/package.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "@shared/all",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite --port 5000 --host",
|
||||
"build": "run-p build:*",
|
||||
"build:esm": "vite build --mode esm",
|
||||
"build:federation": "vite build --mode federation",
|
||||
"preview": "vite preview --port 5000"
|
||||
},
|
||||
"dependencies": {
|
||||
"vue": "^3.5.13",
|
||||
"vue-router": "^4.5.0",
|
||||
"pinia": "^2.2.8",
|
||||
"element-plus": "^2.9.1",
|
||||
"@vueuse/core": "^11.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
"@vitejs/plugin-vue-jsx": "^4.1.1",
|
||||
"typescript": "^5.7.2",
|
||||
"vite": "^6.0.3",
|
||||
"@originjs/vite-plugin-federation": "^1.3.6",
|
||||
"npm-run-all": "^4.1.5"
|
||||
}
|
||||
}
|
||||
93
urbanLifelineWeb/packages/shared/vite.config.esm.ts
Normal file
93
urbanLifelineWeb/packages/shared/vite.config.esm.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import vueJsx from '@vitejs/plugin-vue-jsx'
|
||||
import { resolve } from 'path'
|
||||
|
||||
/**
|
||||
* ES Module 构建配置
|
||||
* 用于 Import Maps 方案
|
||||
*
|
||||
* 策略:将 Vue、Element Plus 等依赖打包进共享模块
|
||||
* 业务应用只需引入 @shared/*,无需关心底层依赖
|
||||
*/
|
||||
export default defineConfig({
|
||||
plugins: [vue(), vueJsx()],
|
||||
|
||||
build: {
|
||||
lib: {
|
||||
entry: {
|
||||
components: resolve(__dirname, 'src/components/index.ts'),
|
||||
utils: resolve(__dirname, 'src/utils/index.ts'),
|
||||
api: resolve(__dirname, 'src/api/index.ts'),
|
||||
composables: resolve(__dirname, 'src/composables/index.ts'),
|
||||
types: resolve(__dirname, 'src/types/index.ts')
|
||||
},
|
||||
formats: ['es'], // 仅构建 ES Module
|
||||
fileName: (format, entryName) => `${entryName}.js`
|
||||
},
|
||||
|
||||
rollupOptions: {
|
||||
// ⚠️ 不外部化依赖,将它们打包进共享模块
|
||||
// 这样业务应用只需引入 @shared/* 即可
|
||||
external: [],
|
||||
|
||||
output: {
|
||||
// 保持 ES Module 格式
|
||||
format: 'es',
|
||||
// 导出命名导出
|
||||
exports: 'named',
|
||||
// 生成 sourcemap
|
||||
sourcemap: true,
|
||||
// 分块策略:将大的依赖分离出来
|
||||
manualChunks(id) {
|
||||
// Vue 核心
|
||||
if (id.includes('node_modules/vue/') ||
|
||||
id.includes('node_modules/@vue/')) {
|
||||
return 'vue-core'
|
||||
}
|
||||
// Vue Router
|
||||
if (id.includes('node_modules/vue-router/')) {
|
||||
return 'vue-router'
|
||||
}
|
||||
// Pinia
|
||||
if (id.includes('node_modules/pinia/')) {
|
||||
return 'pinia'
|
||||
}
|
||||
// Element Plus
|
||||
if (id.includes('node_modules/element-plus/')) {
|
||||
return 'element-plus'
|
||||
}
|
||||
// VueUse
|
||||
if (id.includes('node_modules/@vueuse/')) {
|
||||
return 'vueuse'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 输出目录
|
||||
outDir: 'dist/esm',
|
||||
emptyOutDir: true,
|
||||
|
||||
// 目标浏览器
|
||||
target: 'esnext',
|
||||
|
||||
// 不压缩(开发环境)
|
||||
minify: false,
|
||||
|
||||
// 启用代码分割
|
||||
cssCodeSplit: true
|
||||
},
|
||||
|
||||
// 开发服务器配置
|
||||
server: {
|
||||
port: 5000,
|
||||
host: true,
|
||||
cors: true,
|
||||
headers: {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Methods': 'GET, OPTIONS',
|
||||
'Content-Type': 'application/javascript; charset=utf-8'
|
||||
}
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user