template
This commit is contained in:
202
web/group-site/README.md
Normal file
202
web/group-site/README.md
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
# 科研课题组展示网站
|
||||||
|
|
||||||
|
## 📍 访问地址
|
||||||
|
|
||||||
|
```
|
||||||
|
https://amiap.hzau.edu.cn/group/
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🎨 Vue 项目配置(重要!)
|
||||||
|
|
||||||
|
### 1. Vue Router 配置
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// router/index.js
|
||||||
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
// ⚠️ 重要:base 设置为 /group/
|
||||||
|
history: createWebHistory('/group/'),
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
path: '/', // 实际访问: https://amiap.hzau.edu.cn/group/
|
||||||
|
name: 'Home',
|
||||||
|
component: () => import('@/views/Home.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/members', // 实际访问: https://amiap.hzau.edu.cn/group/members
|
||||||
|
name: 'Members',
|
||||||
|
component: () => import('@/views/Members.vue')
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Vite 配置
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// vite.config.js
|
||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [vue()],
|
||||||
|
// ⚠️ 重要:base 设置为 /group/
|
||||||
|
base: '/group/',
|
||||||
|
build: {
|
||||||
|
outDir: 'dist',
|
||||||
|
assetsDir: 'assets'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Vue CLI 配置(如果使用 Vue CLI)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// vue.config.js
|
||||||
|
module.exports = {
|
||||||
|
publicPath: process.env.NODE_ENV === 'production' ? '/group/' : '/',
|
||||||
|
outputDir: 'dist'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🚀 部署步骤
|
||||||
|
|
||||||
|
### 1. 构建 Vue 项目
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 在你的 Vue 项目根目录
|
||||||
|
cd your-vue-project
|
||||||
|
|
||||||
|
# 确保已配置 base: '/group/'
|
||||||
|
# 然后构建
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
# 或
|
||||||
|
pnpm build
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 复制构建产物
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 复制 dist 目录到部署位置
|
||||||
|
cp -r dist /vol1/1000/docker_server/traefik/web/group-site/
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 启动服务
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /vol1/1000/docker_server/traefik/web/group-site
|
||||||
|
|
||||||
|
# 启动
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
# 查看日志
|
||||||
|
docker compose logs -f
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 访问网站
|
||||||
|
|
||||||
|
```
|
||||||
|
https://amiap.hzau.edu.cn/group/
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📊 完整路由规划
|
||||||
|
|
||||||
|
```
|
||||||
|
https://amiap.hzau.edu.cn/
|
||||||
|
├── / → MinIO S3 API + 控制台(根路径)
|
||||||
|
│ ├── /stub/ → S3 bucket 访问
|
||||||
|
│ ├── /api/v1/ → MinIO Console API
|
||||||
|
│ └── /static/ → MinIO Console 静态资源
|
||||||
|
│
|
||||||
|
├── /group/ → 科研课题组网站(你的 Vue 项目)
|
||||||
|
│ ├── /group/ → 首页
|
||||||
|
│ ├── /group/members → 成员介绍
|
||||||
|
│ ├── /group/research → 研究方向
|
||||||
|
│ └── /group/... → 其他页面
|
||||||
|
│
|
||||||
|
├── /supa/ → Supabase API
|
||||||
|
└── /ABM/ → ABM 数据库
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔧 开发调试
|
||||||
|
|
||||||
|
### 本地开发
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 在 Vue 项目中本地运行
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
# Vite 会自动处理 /group/ base path
|
||||||
|
# 访问: http://localhost:5173/group/
|
||||||
|
```
|
||||||
|
|
||||||
|
### 生产环境测试
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 构建生产版本
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
# 预览
|
||||||
|
npm run preview
|
||||||
|
# 访问: http://localhost:4173/group/
|
||||||
|
```
|
||||||
|
|
||||||
|
## <20><> 示例项目结构
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// App.vue
|
||||||
|
<template>
|
||||||
|
<div id="app">
|
||||||
|
<nav>
|
||||||
|
<\!-- 路由链接会自动加上 /group/ 前缀 -->
|
||||||
|
<router-link to="/">首页</router-link>
|
||||||
|
<router-link to="/members">团队成员</router-link>
|
||||||
|
<router-link to="/research">研究方向</router-link>
|
||||||
|
</nav>
|
||||||
|
<router-view />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
// 图片引用示例
|
||||||
|
<template>
|
||||||
|
<img src="@/assets/logo.png" alt="Logo">
|
||||||
|
<\!-- Vite 会自动处理为: /group/assets/logo-xxx.png -->
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
## ⚠️ 常见问题
|
||||||
|
|
||||||
|
### Q: 刷新页面出现 404
|
||||||
|
A: 已在 nginx.conf 中配置 `try_files`,支持 Vue Router History 模式
|
||||||
|
|
||||||
|
### Q: 静态资源加载失败
|
||||||
|
A: 确保 Vite/Vue CLI 配置了正确的 `base: '/group/'`
|
||||||
|
|
||||||
|
### Q: 路由跳转到根路径
|
||||||
|
A: 检查 Vue Router 的 `history: createWebHistory('/group/')`
|
||||||
|
|
||||||
|
### Q: API 请求路径错误
|
||||||
|
A: 使用绝对路径或相对路径,例如:
|
||||||
|
```javascript
|
||||||
|
// 调用 Supabase API
|
||||||
|
fetch('https://amiap.hzau.edu.cn/supa/rest/v1/...')
|
||||||
|
|
||||||
|
// 或使用环境变量
|
||||||
|
const API_BASE = import.meta.env.VITE_API_URL
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔗 相关资源
|
||||||
|
|
||||||
|
- [Vue Router Base 配置](https://router.vuejs.org/guide/essentials/history-mode.html#html5-mode)
|
||||||
|
- [Vite Base 配置](https://vitejs.dev/config/shared-options.html#base)
|
||||||
|
- [主项目文档](../../README.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**部署路径**: `/group/`
|
||||||
|
**优先级**: 80(高于 MinIO S3 的 50)
|
||||||
|
**维护者**: Lab Admin
|
||||||
203
web/group-site/dist/index.html
vendored
Executable file
203
web/group-site/dist/index.html
vendored
Executable file
@@ -0,0 +1,203 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>实验室服务导航 - HZAU Lab</title>
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
background: rgba(255, 255, 255, 0.95);
|
||||||
|
border-radius: 24px;
|
||||||
|
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
||||||
|
padding: 3rem;
|
||||||
|
max-width: 800px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header h1 {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
color: #2d3748;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header p {
|
||||||
|
color: #718096;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.services {
|
||||||
|
display: grid;
|
||||||
|
gap: 1.5rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-card {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 1.5rem;
|
||||||
|
background: white;
|
||||||
|
border-radius: 16px;
|
||||||
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.07);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
text-decoration: none;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-card:hover {
|
||||||
|
transform: translateY(-4px);
|
||||||
|
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.15);
|
||||||
|
border-color: #667eea;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-icon {
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 12px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 2rem;
|
||||||
|
margin-right: 1.5rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-minio { background: linear-gradient(135deg, #ff6b6b 0%, #ee5a6f 100%); }
|
||||||
|
.icon-supabase { background: linear-gradient(135deg, #3ecf8e 0%, #2eb77a 100%); }
|
||||||
|
.icon-abm { background: linear-gradient(135deg, #4facfe 0%, #00a8ff 100%); }
|
||||||
|
|
||||||
|
.service-info {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-info h3 {
|
||||||
|
font-size: 1.3rem;
|
||||||
|
color: #2d3748;
|
||||||
|
margin-bottom: 0.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-info p {
|
||||||
|
color: #718096;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-badge {
|
||||||
|
padding: 0.25rem 0.75rem;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-left: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-internal {
|
||||||
|
background: #fef5e7;
|
||||||
|
color: #d68910;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-public {
|
||||||
|
background: #eaf7f0;
|
||||||
|
color: #27ae60;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
text-align: center;
|
||||||
|
padding-top: 2rem;
|
||||||
|
border-top: 1px solid #e2e8f0;
|
||||||
|
color: #a0aec0;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer strong {
|
||||||
|
color: #667eea;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.container {
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header h1 {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-card {
|
||||||
|
padding: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-icon {
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
margin-right: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="header">
|
||||||
|
<h1>🔬 实验室服务导航</h1>
|
||||||
|
<p>华中农业大学 · Lab Services Portal</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="services">
|
||||||
|
<!-- MinIO 对象存储 -->
|
||||||
|
<a href="http://100.64.0.2:9001" class="service-card" target="_blank">
|
||||||
|
<div class="service-icon icon-minio">📦</div>
|
||||||
|
<div class="service-info">
|
||||||
|
<h3>MinIO 对象存储</h3>
|
||||||
|
<p>S3 兼容对象存储管理控制台</p>
|
||||||
|
</div>
|
||||||
|
<span class="service-badge badge-internal">内网访问</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!-- Supabase Dashboard -->
|
||||||
|
<a href="http://100.64.0.2:18000" class="service-card" target="_blank">
|
||||||
|
<div class="service-icon icon-supabase">🗄️</div>
|
||||||
|
<div class="service-info">
|
||||||
|
<h3>Supabase Dashboard</h3>
|
||||||
|
<p>数据库管理和 API 配置面板</p>
|
||||||
|
</div>
|
||||||
|
<span class="service-badge badge-internal">内网访问</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!-- ABM 数据库 -->
|
||||||
|
<a href="https://amiap.hzau.edu.cn/ABM/" class="service-card" target="_blank">
|
||||||
|
<div class="service-icon icon-abm">🌐</div>
|
||||||
|
<div class="service-info">
|
||||||
|
<h3>ABM 数据库</h3>
|
||||||
|
<p>WebSocket 实时数据服务</p>
|
||||||
|
</div>
|
||||||
|
<span class="service-badge badge-public">公网访问</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
<p>⚠️ 这是临时导航页面</p>
|
||||||
|
<p style="margin-top: 0.5rem;">
|
||||||
|
后续将替换为 <strong>Vue 课题组网站</strong>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
39
web/group-site/docker-compose.yml
Normal file
39
web/group-site/docker-compose.yml
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# Vue 科研课题组展示网站
|
||||||
|
# 部署路径:https://amiap.hzau.edu.cn/group/
|
||||||
|
|
||||||
|
services:
|
||||||
|
group-site:
|
||||||
|
image: nginx:alpine
|
||||||
|
container_name: group-site
|
||||||
|
volumes:
|
||||||
|
# Vue 构建产物
|
||||||
|
- ./dist:/usr/share/nginx/html:ro
|
||||||
|
# Nginx 配置
|
||||||
|
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
|
||||||
|
networks:
|
||||||
|
- frontend
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
|
||||||
|
# ======================================
|
||||||
|
# 课题组网站路由 - /group/ 子路径
|
||||||
|
# ======================================
|
||||||
|
- "traefik.http.routers.group-site.rule=Host(`amiap.hzau.edu.cn`) && PathPrefix(`/group`)"
|
||||||
|
- "traefik.http.routers.group-site.priority=80" # 高于 MinIO S3 (50)
|
||||||
|
- "traefik.http.routers.group-site.entrypoints=websecure"
|
||||||
|
- "traefik.http.routers.group-site.tls.certresolver=myresolver"
|
||||||
|
|
||||||
|
# StripPrefix 中间件 - 去除 /group 前缀
|
||||||
|
- "traefik.http.middlewares.group-stripprefix.stripprefix.prefixes=/group"
|
||||||
|
|
||||||
|
# 压缩中间件
|
||||||
|
- "traefik.http.middlewares.group-compress.compress=true"
|
||||||
|
|
||||||
|
# 应用中间件
|
||||||
|
- "traefik.http.routers.group-site.middlewares=group-stripprefix,group-compress"
|
||||||
|
|
||||||
|
- "traefik.http.services.group-site-svc.loadbalancer.server.port=80"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
frontend:
|
||||||
|
external: true
|
||||||
30
web/group-site/nginx.conf
Normal file
30
web/group-site/nginx.conf
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# Nginx 配置 - Vue Router History 模式 + 子路径
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name _;
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
# Gzip 压缩
|
||||||
|
gzip on;
|
||||||
|
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml;
|
||||||
|
gzip_min_length 1000;
|
||||||
|
|
||||||
|
# Vue Router history 模式支持
|
||||||
|
# 重要:因为 Traefik 已经去除了 /group 前缀,这里直接处理根路径
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 静态资源缓存
|
||||||
|
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||||
|
expires 1y;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
}
|
||||||
|
|
||||||
|
# 安全头
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header X-XSS-Protection "1; mode=block" always;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user