init: 初始化 H5 和 大屏
This commit is contained in:
commit
ffaaa56af7
4
.env.development
Normal file
4
.env.development
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# VITE环境变量
|
||||||
|
|
||||||
|
# 开发环境
|
||||||
|
VITE_API_BASE_URL=http://localhost:3000/api
|
||||||
4
.env.production
Normal file
4
.env.production
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# VITE环境变量
|
||||||
|
|
||||||
|
# 生产环境
|
||||||
|
VITE_API_BASE_URL=https://api.example.com
|
||||||
22
.eslintrc.cjs
Normal file
22
.eslintrc.cjs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
module.exports = {
|
||||||
|
env: {
|
||||||
|
browser: true,
|
||||||
|
es2021: true,
|
||||||
|
node: true
|
||||||
|
},
|
||||||
|
extends: [
|
||||||
|
'eslint:recommended',
|
||||||
|
'plugin:vue/vue3-recommended'
|
||||||
|
],
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 'latest',
|
||||||
|
sourceType: 'module'
|
||||||
|
},
|
||||||
|
plugins: ['vue'],
|
||||||
|
rules: {
|
||||||
|
'vue/multi-word-component-names': 'off',
|
||||||
|
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||||
|
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||||
|
'vue/no-v-html': 'off'
|
||||||
|
}
|
||||||
|
}
|
||||||
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
.DS_Store
|
||||||
|
*.log
|
||||||
|
.vscode
|
||||||
|
.idea
|
||||||
|
*.local
|
||||||
276
README.md
Normal file
276
README.md
Normal file
@ -0,0 +1,276 @@
|
|||||||
|
# H5 Monorepo 工程
|
||||||
|
|
||||||
|
基于 pnpm workspace 的 Monorepo 项目,包含 Vue3 大屏和 Vue3 移动端两个子项目。
|
||||||
|
|
||||||
|
## 项目结构
|
||||||
|
|
||||||
|
```
|
||||||
|
H5/
|
||||||
|
├── packages/
|
||||||
|
│ ├── screen/ # Vue3 大屏项目
|
||||||
|
│ │ ├── src/
|
||||||
|
│ │ │ ├── views/
|
||||||
|
│ │ │ ├── components/
|
||||||
|
│ │ │ ├── router/
|
||||||
|
│ │ │ ├── store/
|
||||||
|
│ │ │ └── styles/
|
||||||
|
│ │ ├── vite.config.js
|
||||||
|
│ │ └── package.json
|
||||||
|
│ ├── mobile/ # Vue3 移动端项目
|
||||||
|
│ │ ├── src/
|
||||||
|
│ │ │ ├── views/
|
||||||
|
│ │ │ ├── components/
|
||||||
|
│ │ │ ├── router/
|
||||||
|
│ │ │ └── styles/
|
||||||
|
│ │ ├── vite.config.js
|
||||||
|
│ │ └── package.json
|
||||||
|
│ └── shared/ # 共享代码库
|
||||||
|
│ ├── api/ # 接口封装
|
||||||
|
│ ├── utils/ # 工具函数
|
||||||
|
│ ├── config/ # 配置文件
|
||||||
|
│ └── package.json
|
||||||
|
├── pnpm-workspace.yaml
|
||||||
|
├── package.json
|
||||||
|
└── README.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## 技术栈
|
||||||
|
|
||||||
|
### 大屏项目 (screen)
|
||||||
|
- Vue 3
|
||||||
|
- Vite 5
|
||||||
|
- Vue Router 4
|
||||||
|
- Pinia
|
||||||
|
- ECharts 5
|
||||||
|
- Vue-ECharts
|
||||||
|
- Sass
|
||||||
|
|
||||||
|
### 移动端项目 (mobile)
|
||||||
|
- Vue 3
|
||||||
|
- Vite 5
|
||||||
|
- Vue Router 4
|
||||||
|
- Pinia
|
||||||
|
- Vant 4(移动端 UI 组件库)
|
||||||
|
- 自动按需引入组件
|
||||||
|
- Sass
|
||||||
|
|
||||||
|
### 共享库 (shared)
|
||||||
|
- Axios(请求封装)
|
||||||
|
- 工具函数(防抖/节流/日期格式化/深拷贝等)
|
||||||
|
- 统一配置管理
|
||||||
|
|
||||||
|
## 快速开始
|
||||||
|
|
||||||
|
### 安装依赖
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 安装 pnpm (如果还没安装)
|
||||||
|
npm install -g pnpm
|
||||||
|
|
||||||
|
# 安装所有依赖
|
||||||
|
pnpm install
|
||||||
|
```
|
||||||
|
|
||||||
|
### 开发命令
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 启动大屏项目(端口:3000)
|
||||||
|
pnpm dev:screen
|
||||||
|
|
||||||
|
# 启动 H5 移动端(端口:8080)
|
||||||
|
pnpm dev:mobile
|
||||||
|
|
||||||
|
# 构建大屏项目
|
||||||
|
pnpm build:screen
|
||||||
|
|
||||||
|
# 构建 H5 移动端
|
||||||
|
pnpm build:mobile
|
||||||
|
|
||||||
|
# 构建所有项目
|
||||||
|
pnpm build:all
|
||||||
|
|
||||||
|
# 代码检查
|
||||||
|
pnpm lint
|
||||||
|
```
|
||||||
|
|
||||||
|
## 项目说明
|
||||||
|
|
||||||
|
### 1. 大屏项目 (screen)
|
||||||
|
|
||||||
|
**访问地址:** `http://localhost:3000`
|
||||||
|
|
||||||
|
**特点:**
|
||||||
|
- 适配大屏分辨率 (1920x1080)
|
||||||
|
- 数据可视化(ECharts)
|
||||||
|
- 暗色主题设计
|
||||||
|
- 响应式布局
|
||||||
|
|
||||||
|
**目录结构:**
|
||||||
|
```
|
||||||
|
packages/screen/src/
|
||||||
|
├── views/ # 页面组件
|
||||||
|
├── components/ # 通用组件
|
||||||
|
├── router/ # 路由配置
|
||||||
|
├── store/ # Pinia 状态管理
|
||||||
|
├── assets/ # 静态资源
|
||||||
|
└── styles/ # 全局样式
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 移动端项目 (mobile)
|
||||||
|
|
||||||
|
**访问地址:** `http://localhost:8080`
|
||||||
|
|
||||||
|
**特点:**
|
||||||
|
- 响应式设计,适配移动端
|
||||||
|
- Vant UI 组件库
|
||||||
|
- 自动按需引入,减小包体积
|
||||||
|
- 支持 PWA
|
||||||
|
- 适合嵌入第三方 APP
|
||||||
|
|
||||||
|
**可用组件:**
|
||||||
|
- NavBar(导航栏)
|
||||||
|
- Tabbar(底部导航)
|
||||||
|
- Cell/CellGroup(单元格列表)
|
||||||
|
- Grid(宫格)
|
||||||
|
- Button(按钮)
|
||||||
|
- Toast(轻提示)
|
||||||
|
- Dialog(弹窗)
|
||||||
|
- Image(图片)
|
||||||
|
- 等 60+ 组件
|
||||||
|
|
||||||
|
**目录结构:**
|
||||||
|
```
|
||||||
|
packages/mobile/src/
|
||||||
|
├── views/ # 页面组件
|
||||||
|
├── components/ # 通用组件
|
||||||
|
├── router/ # 路由配置
|
||||||
|
├── store/ # Pinia 状态管理
|
||||||
|
├── assets/ # 静态资源
|
||||||
|
└── styles/ # 全局样式
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 共享库 (shared)
|
||||||
|
|
||||||
|
共享代码可在两个项目中复用:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// 在项目中引入 API
|
||||||
|
import { getUserInfo, getDataList } from '@shared/api'
|
||||||
|
|
||||||
|
// 引入工具函数
|
||||||
|
import { formatDate, debounce, throttle } from '@shared/utils'
|
||||||
|
|
||||||
|
// 引入配置
|
||||||
|
import { API_CONFIG, APP_CONFIG } from '@shared/config'
|
||||||
|
```
|
||||||
|
|
||||||
|
**可用工具函数:**
|
||||||
|
- `formatDate()` - 日期格式化
|
||||||
|
- `debounce()` - 防抖
|
||||||
|
- `throttle()` - 节流
|
||||||
|
- `deepClone()` - 深拷贝
|
||||||
|
|
||||||
|
## 环境变量
|
||||||
|
|
||||||
|
项目使用 `.env` 文件管理环境变量:
|
||||||
|
|
||||||
|
- `.env.development` - 开发环境
|
||||||
|
- `.env.production` - 生产环境
|
||||||
|
|
||||||
|
**示例:**
|
||||||
|
```bash
|
||||||
|
# .env.development
|
||||||
|
VITE_API_BASE_URL=http://localhost:3000/api
|
||||||
|
```
|
||||||
|
|
||||||
|
## 开发建议
|
||||||
|
|
||||||
|
### 1. 共享代码复用
|
||||||
|
|
||||||
|
将通用的业务逻辑、API 接口、工具函数放在 `packages/shared` 中,避免重复代码。
|
||||||
|
|
||||||
|
### 2. 组件开发
|
||||||
|
|
||||||
|
- **大屏项目**:使用 ECharts 开发数据可视化组件
|
||||||
|
- **移动端项目**:优先使用 Vant 组件,减少自定义开发
|
||||||
|
|
||||||
|
### 3. 样式规范
|
||||||
|
|
||||||
|
- 使用 Sass 预处理器
|
||||||
|
- 遵循 BEM 命名规范
|
||||||
|
- 组件样式使用 `scoped`
|
||||||
|
|
||||||
|
### 4. API 请求
|
||||||
|
|
||||||
|
统一使用 `@shared/api` 中封装的请求方法,自动处理 token、错误等。
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. 本项目使用 **JavaScript**,未使用 TypeScript
|
||||||
|
2. 使用 **pnpm** 作为包管理工具,不要使用 npm 或 yarn
|
||||||
|
3. 大屏和移动端项目独立运行,通过 shared 包共享代码
|
||||||
|
4. 移动端项目基于 Vue3 + Vant,**不是 uni-app**,仅支持 H5
|
||||||
|
5. 如需扩展到小程序,建议使用 uni-app 或 Taro 重新搭建
|
||||||
|
|
||||||
|
## 部署说明
|
||||||
|
|
||||||
|
### 大屏项目部署
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 构建
|
||||||
|
pnpm build:screen
|
||||||
|
|
||||||
|
# 构建产物在 packages/screen/dist
|
||||||
|
# 部署到 Nginx 或其他静态服务器
|
||||||
|
```
|
||||||
|
|
||||||
|
### 移动端部署
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 构建
|
||||||
|
pnpm build:mobile
|
||||||
|
|
||||||
|
# 构建产物在 packages/mobile/dist
|
||||||
|
# 可以嵌入到第三方 APP 的 WebView 中
|
||||||
|
```
|
||||||
|
|
||||||
|
## 浏览器支持
|
||||||
|
|
||||||
|
### 大屏项目
|
||||||
|
- Chrome >= 87
|
||||||
|
- Firefox >= 78
|
||||||
|
- Safari >= 14
|
||||||
|
- Edge >= 88
|
||||||
|
|
||||||
|
### 移动端项目
|
||||||
|
- iOS Safari >= 10
|
||||||
|
- Android Chrome >= 5.0
|
||||||
|
- 微信浏览器
|
||||||
|
|
||||||
|
## 常见问题
|
||||||
|
|
||||||
|
### 1. 端口被占用
|
||||||
|
|
||||||
|
修改对应项目的 `vite.config.js` 中的 `server.port` 配置。
|
||||||
|
|
||||||
|
### 2. 组件按需引入不生效
|
||||||
|
|
||||||
|
移动端项目使用 `unplugin-vue-components` 自动按需引入 Vant 组件,无需手动引入。
|
||||||
|
|
||||||
|
### 3. 如何添加新页面?
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. 在 src/views 中创建页面组件
|
||||||
|
# 2. 在 src/router/index.js 中添加路由配置
|
||||||
|
```
|
||||||
|
|
||||||
|
## 技术支持
|
||||||
|
|
||||||
|
- Vue 3 文档:https://cn.vuejs.org/
|
||||||
|
- Vant 文档:https://vant-ui.github.io/vant/
|
||||||
|
- ECharts 文档:https://echarts.apache.org/zh/index.html
|
||||||
|
- Pinia 文档:https://pinia.vuejs.org/zh/
|
||||||
|
|
||||||
|
## 许可证
|
||||||
|
|
||||||
|
MIT
|
||||||
21
package.json
Normal file
21
package.json
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"name": "h5-workspace",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"description": "大屏 + H5 Monorepo 工程",
|
||||||
|
"scripts": {
|
||||||
|
"dev:screen": "pnpm --filter @h5/screen dev",
|
||||||
|
"dev:mobile": "pnpm --filter @h5/mobile dev",
|
||||||
|
"build:screen": "pnpm --filter @h5/screen build",
|
||||||
|
"build:mobile": "pnpm --filter @h5/mobile build",
|
||||||
|
"build:all": "pnpm -r build",
|
||||||
|
"lint": "pnpm -r lint"
|
||||||
|
},
|
||||||
|
"keywords": ["monorepo", "vue3", "vant", "大屏", "h5"],
|
||||||
|
"author": "",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.0.0",
|
||||||
|
"pnpm": ">=8.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
14
packages/mobile/index.html
Normal file
14
packages/mobile/index.html
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, viewport-fit=cover" />
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||||
|
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
|
||||||
|
<title>H5移动端</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
25
packages/mobile/package.json
Normal file
25
packages/mobile/package.json
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"name": "@h5/mobile",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"vue": "^3.4.0",
|
||||||
|
"vue-router": "^4.2.0",
|
||||||
|
"pinia": "^2.1.0",
|
||||||
|
"vant": "^4.8.0",
|
||||||
|
"axios": "^1.6.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitejs/plugin-vue": "^5.0.0",
|
||||||
|
"vite": "^5.0.0",
|
||||||
|
"sass": "^1.70.0",
|
||||||
|
"unplugin-vue-components": "^0.26.0",
|
||||||
|
"unplugin-auto-import": "^0.17.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
17
packages/mobile/src/App.vue
Normal file
17
packages/mobile/src/App.vue
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<template>
|
||||||
|
<div id="app">
|
||||||
|
<router-view />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
// App 根组件
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#app {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 100vh;
|
||||||
|
background: #f7f8fa;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
13
packages/mobile/src/main.js
Normal file
13
packages/mobile/src/main.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { createApp } from 'vue'
|
||||||
|
import { createPinia } from 'pinia'
|
||||||
|
import router from './router'
|
||||||
|
import App from './App.vue'
|
||||||
|
import 'vant/lib/index.css'
|
||||||
|
import './styles/index.css'
|
||||||
|
|
||||||
|
const app = createApp(App)
|
||||||
|
|
||||||
|
app.use(createPinia())
|
||||||
|
app.use(router)
|
||||||
|
|
||||||
|
app.mount('#app')
|
||||||
21
packages/mobile/src/router/index.js
Normal file
21
packages/mobile/src/router/index.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
|
|
||||||
|
const routes = [
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
name: 'Home',
|
||||||
|
component: () => import('../views/Home.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/user',
|
||||||
|
name: 'User',
|
||||||
|
component: () => import('../views/User.vue')
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHistory(),
|
||||||
|
routes
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router
|
||||||
16
packages/mobile/src/styles/index.css
Normal file
16
packages/mobile/src/styles/index.css
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
#app {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
58
packages/mobile/src/views/Home.vue
Normal file
58
packages/mobile/src/views/Home.vue
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<template>
|
||||||
|
<div class="home">
|
||||||
|
<van-nav-bar title="首页" fixed placeholder />
|
||||||
|
|
||||||
|
<div class="content">
|
||||||
|
<van-cell-group inset>
|
||||||
|
<van-cell title="欢迎使用" value="H5移动端" />
|
||||||
|
</van-cell-group>
|
||||||
|
|
||||||
|
<van-grid :column-num="2" class="grid">
|
||||||
|
<van-grid-item icon="photo-o" text="功能1" @click="showToast('功能1')" />
|
||||||
|
<van-grid-item icon="chat-o" text="功能2" @click="showToast('功能2')" />
|
||||||
|
<van-grid-item icon="setting-o" text="功能3" @click="showToast('功能3')" />
|
||||||
|
<van-grid-item icon="star-o" text="功能4" @click="showToast('功能4')" />
|
||||||
|
</van-grid>
|
||||||
|
|
||||||
|
<van-button type="primary" block class="btn" @click="goToUser">
|
||||||
|
进入个人中心
|
||||||
|
</van-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<van-tabbar v-model="active" route>
|
||||||
|
<van-tabbar-item icon="home-o" to="/">首页</van-tabbar-item>
|
||||||
|
<van-tabbar-item icon="user-o" to="/user">我的</van-tabbar-item>
|
||||||
|
</van-tabbar>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import { showToast } from 'vant'
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const active = ref(0)
|
||||||
|
|
||||||
|
const goToUser = () => {
|
||||||
|
router.push('/user')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.home {
|
||||||
|
padding-bottom: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
78
packages/mobile/src/views/User.vue
Normal file
78
packages/mobile/src/views/User.vue
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
<template>
|
||||||
|
<div class="user">
|
||||||
|
<van-nav-bar title="我的" fixed placeholder />
|
||||||
|
|
||||||
|
<div class="user-header">
|
||||||
|
<van-image
|
||||||
|
round
|
||||||
|
width="80"
|
||||||
|
height="80"
|
||||||
|
src="https://fastly.jsdelivr.net/npm/@vant/assets/cat.jpeg"
|
||||||
|
/>
|
||||||
|
<div class="user-name">用户名</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<van-cell-group inset class="menu-group">
|
||||||
|
<van-cell title="个人信息" is-link @click="showToast('个人信息')" />
|
||||||
|
<van-cell title="我的订单" is-link @click="showToast('我的订单')" />
|
||||||
|
<van-cell title="收货地址" is-link @click="showToast('收货地址')" />
|
||||||
|
<van-cell title="设置" is-link @click="showToast('设置')" />
|
||||||
|
</van-cell-group>
|
||||||
|
|
||||||
|
<van-button type="danger" block class="logout-btn" @click="handleLogout">
|
||||||
|
退出登录
|
||||||
|
</van-button>
|
||||||
|
|
||||||
|
<van-tabbar v-model="active" route>
|
||||||
|
<van-tabbar-item icon="home-o" to="/">首页</van-tabbar-item>
|
||||||
|
<van-tabbar-item icon="user-o" to="/user">我的</van-tabbar-item>
|
||||||
|
</van-tabbar>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import { showToast, showDialog } from 'vant'
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const active = ref(1)
|
||||||
|
|
||||||
|
const handleLogout = () => {
|
||||||
|
showDialog({
|
||||||
|
title: '提示',
|
||||||
|
message: '确定要退出登录吗?'
|
||||||
|
}).then(() => {
|
||||||
|
showToast('已退出')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.user {
|
||||||
|
padding-bottom: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 40px 0;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-name {
|
||||||
|
margin-top: 16px;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #323233;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-group {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logout-btn {
|
||||||
|
margin: 24px 16px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
30
packages/mobile/vite.config.js
Normal file
30
packages/mobile/vite.config.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
import { resolve } from 'path'
|
||||||
|
import Components from 'unplugin-vue-components/vite'
|
||||||
|
import { VantResolver } from 'unplugin-vue-components/resolvers'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [
|
||||||
|
vue(),
|
||||||
|
Components({
|
||||||
|
resolvers: [VantResolver()]
|
||||||
|
})
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': resolve(__dirname, 'src'),
|
||||||
|
'@shared': resolve(__dirname, '../shared')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
port: 8080,
|
||||||
|
host: '0.0.0.0',
|
||||||
|
open: true
|
||||||
|
},
|
||||||
|
build: {
|
||||||
|
outDir: 'dist',
|
||||||
|
assetsDir: 'assets',
|
||||||
|
sourcemap: false
|
||||||
|
}
|
||||||
|
})
|
||||||
12
packages/screen/index.html
Normal file
12
packages/screen/index.html
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>数据大屏</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
24
packages/screen/package.json
Normal file
24
packages/screen/package.json
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"name": "@h5/screen",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"vue": "^3.4.0",
|
||||||
|
"vue-router": "^4.2.0",
|
||||||
|
"pinia": "^2.1.0",
|
||||||
|
"axios": "^1.6.0",
|
||||||
|
"echarts": "^5.5.0",
|
||||||
|
"vue-echarts": "^6.6.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitejs/plugin-vue": "^5.0.0",
|
||||||
|
"vite": "^5.0.0",
|
||||||
|
"sass": "^1.70.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
19
packages/screen/src/App.vue
Normal file
19
packages/screen/src/App.vue
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<template>
|
||||||
|
<div id="app">
|
||||||
|
<router-view />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'App'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
#app {
|
||||||
|
width: 100%;
|
||||||
|
height: 100vh;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
12
packages/screen/src/main.js
Normal file
12
packages/screen/src/main.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { createApp } from 'vue'
|
||||||
|
import { createPinia } from 'pinia'
|
||||||
|
import router from './router'
|
||||||
|
import App from './App.vue'
|
||||||
|
import './styles/index.scss'
|
||||||
|
|
||||||
|
const app = createApp(App)
|
||||||
|
|
||||||
|
app.use(createPinia())
|
||||||
|
app.use(router)
|
||||||
|
|
||||||
|
app.mount('#app')
|
||||||
16
packages/screen/src/router/index.js
Normal file
16
packages/screen/src/router/index.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
|
|
||||||
|
const routes = [
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
name: 'Home',
|
||||||
|
component: () => import('../views/Home.vue')
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHistory(),
|
||||||
|
routes
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router
|
||||||
16
packages/screen/src/styles/index.scss
Normal file
16
packages/screen/src/styles/index.scss
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Microsoft YaHei', Arial, sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
#app {
|
||||||
|
width: 100%;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
102
packages/screen/src/views/Home.vue
Normal file
102
packages/screen/src/views/Home.vue
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
<template>
|
||||||
|
<div class="screen-container">
|
||||||
|
<header class="screen-header">
|
||||||
|
<h1>数据大屏</h1>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="screen-content">
|
||||||
|
<div class="screen-left">
|
||||||
|
<div class="chart-box">
|
||||||
|
<h3>左侧图表1</h3>
|
||||||
|
</div>
|
||||||
|
<div class="chart-box">
|
||||||
|
<h3>左侧图表2</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="screen-center">
|
||||||
|
<div class="chart-box center-main">
|
||||||
|
<h3>中间主图表</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="screen-right">
|
||||||
|
<div class="chart-box">
|
||||||
|
<h3>右侧图表1</h3>
|
||||||
|
</div>
|
||||||
|
<div class="chart-box">
|
||||||
|
<h3>右侧图表2</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'Home',
|
||||||
|
setup() {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.screen-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100vh;
|
||||||
|
background: #0a1e3e;
|
||||||
|
color: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.screen-header {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 36px;
|
||||||
|
font-weight: bold;
|
||||||
|
background: linear-gradient(to right, #4facfe, #00f2fe);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.screen-content {
|
||||||
|
display: flex;
|
||||||
|
gap: 20px;
|
||||||
|
height: calc(100% - 80px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.screen-left,
|
||||||
|
.screen-right {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.screen-center {
|
||||||
|
flex: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-box {
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 20px;
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 0 0 15px 0;
|
||||||
|
font-size: 18px;
|
||||||
|
color: #4facfe;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.center-main {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
31
packages/screen/vite.config.js
Normal file
31
packages/screen/vite.config.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
import { resolve } from 'path'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [vue()],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': resolve(__dirname, 'src'),
|
||||||
|
'@shared': resolve(__dirname, '../shared')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
port: 3000,
|
||||||
|
open: true,
|
||||||
|
cors: true
|
||||||
|
},
|
||||||
|
build: {
|
||||||
|
outDir: 'dist',
|
||||||
|
assetsDir: 'assets',
|
||||||
|
sourcemap: false,
|
||||||
|
minify: 'terser',
|
||||||
|
rollupOptions: {
|
||||||
|
output: {
|
||||||
|
chunkFileNames: 'js/[name]-[hash].js',
|
||||||
|
entryFileNames: 'js/[name]-[hash].js',
|
||||||
|
assetFileNames: '[ext]/[name]-[hash].[ext]'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
17
packages/shared/api/index.js
Normal file
17
packages/shared/api/index.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import request from './request'
|
||||||
|
|
||||||
|
// 示例API接口
|
||||||
|
export const getUserInfo = () => {
|
||||||
|
return request({
|
||||||
|
url: '/user/info',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getDataList = (params) => {
|
||||||
|
return request({
|
||||||
|
url: '/data/list',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
42
packages/shared/api/request.js
Normal file
42
packages/shared/api/request.js
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
// 创建axios实例
|
||||||
|
const request = axios.create({
|
||||||
|
baseURL: import.meta.env.VITE_API_BASE_URL || '/api',
|
||||||
|
timeout: 10000
|
||||||
|
})
|
||||||
|
|
||||||
|
// 请求拦截器
|
||||||
|
request.interceptors.request.use(
|
||||||
|
config => {
|
||||||
|
// 可以在这里添加token等
|
||||||
|
const token = localStorage.getItem('token')
|
||||||
|
if (token) {
|
||||||
|
config.headers.Authorization = `Bearer ${token}`
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
console.error('请求错误:', error)
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// 响应拦截器
|
||||||
|
request.interceptors.response.use(
|
||||||
|
response => {
|
||||||
|
const res = response.data
|
||||||
|
// 根据实际情况处理响应
|
||||||
|
if (res.code !== 200) {
|
||||||
|
console.error('接口错误:', res.message)
|
||||||
|
return Promise.reject(new Error(res.message || 'Error'))
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
console.error('响应错误:', error)
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export default request
|
||||||
18
packages/shared/config/index.js
Normal file
18
packages/shared/config/index.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// 环境配置
|
||||||
|
export const ENV = import.meta.env.MODE || 'development'
|
||||||
|
|
||||||
|
// API配置
|
||||||
|
export const API_CONFIG = {
|
||||||
|
development: {
|
||||||
|
baseURL: 'http://localhost:3000/api'
|
||||||
|
},
|
||||||
|
production: {
|
||||||
|
baseURL: 'https://api.example.com'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 应用配置
|
||||||
|
export const APP_CONFIG = {
|
||||||
|
title: '数据大屏',
|
||||||
|
version: '1.0.0'
|
||||||
|
}
|
||||||
3
packages/shared/index.js
Normal file
3
packages/shared/index.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export * from './api'
|
||||||
|
export * from './utils'
|
||||||
|
export * from './config'
|
||||||
11
packages/shared/package.json
Normal file
11
packages/shared/package.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "@h5/shared",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {},
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^1.6.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
75
packages/shared/utils/index.js
Normal file
75
packages/shared/utils/index.js
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/**
|
||||||
|
* 格式化日期
|
||||||
|
* @param {Date|string|number} date
|
||||||
|
* @param {string} format
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function formatDate(date, format = 'YYYY-MM-DD HH:mm:ss') {
|
||||||
|
const d = new Date(date)
|
||||||
|
const year = d.getFullYear()
|
||||||
|
const month = String(d.getMonth() + 1).padStart(2, '0')
|
||||||
|
const day = String(d.getDate()).padStart(2, '0')
|
||||||
|
const hours = String(d.getHours()).padStart(2, '0')
|
||||||
|
const minutes = String(d.getMinutes()).padStart(2, '0')
|
||||||
|
const seconds = String(d.getSeconds()).padStart(2, '0')
|
||||||
|
|
||||||
|
return format
|
||||||
|
.replace('YYYY', year)
|
||||||
|
.replace('MM', month)
|
||||||
|
.replace('DD', day)
|
||||||
|
.replace('HH', hours)
|
||||||
|
.replace('mm', minutes)
|
||||||
|
.replace('ss', seconds)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 防抖函数
|
||||||
|
* @param {Function} fn
|
||||||
|
* @param {number} delay
|
||||||
|
* @returns {Function}
|
||||||
|
*/
|
||||||
|
export function debounce(fn, delay = 300) {
|
||||||
|
let timer = null
|
||||||
|
return function(...args) {
|
||||||
|
if (timer) clearTimeout(timer)
|
||||||
|
timer = setTimeout(() => {
|
||||||
|
fn.apply(this, args)
|
||||||
|
}, delay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 节流函数
|
||||||
|
* @param {Function} fn
|
||||||
|
* @param {number} delay
|
||||||
|
* @returns {Function}
|
||||||
|
*/
|
||||||
|
export function throttle(fn, delay = 300) {
|
||||||
|
let last = 0
|
||||||
|
return function(...args) {
|
||||||
|
const now = Date.now()
|
||||||
|
if (now - last >= delay) {
|
||||||
|
last = now
|
||||||
|
fn.apply(this, args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 深拷贝
|
||||||
|
* @param {any} obj
|
||||||
|
* @returns {any}
|
||||||
|
*/
|
||||||
|
export function deepClone(obj) {
|
||||||
|
if (obj === null || typeof obj !== 'object') return obj
|
||||||
|
if (obj instanceof Date) return new Date(obj)
|
||||||
|
if (obj instanceof Array) return obj.map(item => deepClone(item))
|
||||||
|
|
||||||
|
const clonedObj = {}
|
||||||
|
for (const key in obj) {
|
||||||
|
if (obj.hasOwnProperty(key)) {
|
||||||
|
clonedObj[key] = deepClone(obj[key])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return clonedObj
|
||||||
|
}
|
||||||
1867
pnpm-lock.yaml
generated
Normal file
1867
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
2
pnpm-workspace.yaml
Normal file
2
pnpm-workspace.yaml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
packages:
|
||||||
|
- 'packages/*'
|
||||||
Loading…
x
Reference in New Issue
Block a user