first commit
This commit is contained in:
commit
9e8d0f0e11
2
.env
Normal file
2
.env
Normal file
@ -0,0 +1,2 @@
|
||||
VITE_APP_TITLE=美搭
|
||||
VITE_APP_API_URL=http://127.0.0.1:8000
|
||||
2
.env.production
Normal file
2
.env.production
Normal file
@ -0,0 +1,2 @@
|
||||
VITE_APP_TITLE=美搭
|
||||
VITE_APP_API_URL=https://meida-api.beefast.co
|
||||
38
.gitignore
vendored
Normal file
38
.gitignore
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
# 依赖包
|
||||
node_modules
|
||||
npm-debug.log
|
||||
yarn-debug.log
|
||||
yarn-error.log
|
||||
package-lock.json
|
||||
|
||||
# 打包输出
|
||||
/dist
|
||||
/build
|
||||
|
||||
# 本地环境文件
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# 日志文件
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# 编辑器目录和文件
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
# 操作系统文件
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# 测试覆盖率
|
||||
/coverage
|
||||
32
Dockerfile
Normal file
32
Dockerfile
Normal file
@ -0,0 +1,32 @@
|
||||
# 构建阶段
|
||||
FROM node:18-alpine as build-stage
|
||||
|
||||
# 设置工作目录
|
||||
WORKDIR /app
|
||||
|
||||
# 复制package.json和package-lock.json
|
||||
COPY package*.json ./
|
||||
|
||||
# 安装依赖
|
||||
RUN npm install
|
||||
|
||||
# 复制项目文件
|
||||
COPY . .
|
||||
|
||||
# 构建应用
|
||||
RUN npm run build
|
||||
|
||||
# 生产阶段
|
||||
FROM nginx:stable-alpine as production-stage
|
||||
|
||||
# 将构建好的文件复制到nginx目录
|
||||
COPY --from=build-stage /app/dist /usr/share/nginx/html
|
||||
|
||||
# 复制nginx配置文件
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
# 暴露80端口
|
||||
EXPOSE 80
|
||||
|
||||
# 启动nginx
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
67
README.md
Normal file
67
README.md
Normal file
@ -0,0 +1,67 @@
|
||||
# 美搭项目展示平台
|
||||
|
||||
这是一个基于Vue 3开发的美搭时尚穿搭项目展示平台,用于展示用户的穿搭历史和效果。
|
||||
|
||||
## 功能特点
|
||||
|
||||
- 展示用户的穿搭历史记录
|
||||
- 查看搭配效果和专业评分
|
||||
- 响应式设计,适配各种设备
|
||||
|
||||
## 开发技术
|
||||
|
||||
- Vue 3
|
||||
- Vue Router 4
|
||||
- Axios
|
||||
- Vite
|
||||
|
||||
## 安装与运行
|
||||
|
||||
### 安装依赖
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
### 启动开发服务器
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### 构建生产版本
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
### 预览生产构建
|
||||
|
||||
```bash
|
||||
npm run serve
|
||||
```
|
||||
|
||||
## 环境配置
|
||||
|
||||
项目配置了两个环境的API接口:
|
||||
|
||||
- 开发环境:http://127.0.0.1:8000
|
||||
- 生产环境:https://meida-api.beefast.co/
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
meida-portal/
|
||||
├── public/ # 静态资源
|
||||
├── src/ # 源代码
|
||||
│ ├── api/ # API接口
|
||||
│ ├── assets/ # 资源文件
|
||||
│ ├── components/ # 通用组件
|
||||
│ ├── router/ # 路由配置
|
||||
│ ├── views/ # 页面视图
|
||||
│ ├── App.vue # 根组件
|
||||
│ └── main.js # 入口文件
|
||||
├── index.html # HTML模板
|
||||
├── vite.config.js # Vite配置
|
||||
└── package.json # 项目配置
|
||||
```
|
||||
17
docker-compose.yml
Normal file
17
docker-compose.yml
Normal file
@ -0,0 +1,17 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
meida-portal:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
container_name: meida-portal
|
||||
ports:
|
||||
- "80:80"
|
||||
restart: always
|
||||
networks:
|
||||
- meida-network
|
||||
|
||||
networks:
|
||||
meida-network:
|
||||
driver: bridge
|
||||
13
index.html
Normal file
13
index.html
Normal file
@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<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>
|
||||
37
nginx.conf
Normal file
37
nginx.conf
Normal file
@ -0,0 +1,37 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
|
||||
#access_log /var/log/nginx/host.access.log main;
|
||||
|
||||
location / {
|
||||
root /usr/share/nginx/html;
|
||||
index index.html index.htm;
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
# 开发环境API代理配置
|
||||
# location /api/ {
|
||||
# proxy_pass http://127.0.0.1:8000/;
|
||||
# proxy_set_header Host $host;
|
||||
# proxy_set_header X-Real-IP $remote_addr;
|
||||
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
# proxy_set_header X-Forwarded-Proto $scheme;
|
||||
# }
|
||||
|
||||
# 生产环境API代理配置
|
||||
# 如需使用生产环境API,请取消下面的注释并注释上面的配置
|
||||
# location /api/ {
|
||||
# proxy_pass https://meida-api.beefast.co/;
|
||||
# proxy_set_header Host $host;
|
||||
# proxy_set_header X-Real-IP $remote_addr;
|
||||
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
# proxy_set_header X-Forwarded-Proto $scheme;
|
||||
# }
|
||||
|
||||
# redirect server error pages to the static page /50x.html
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
}
|
||||
21
package.json
Normal file
21
package.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "meida-portal",
|
||||
"version": "1.0.0",
|
||||
"description": "美搭项目展示平台",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"serve": "vite preview"
|
||||
},
|
||||
"keywords": ["vue", "meida", "fashion"],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@vitejs/plugin-vue": "^5.2.3",
|
||||
"axios": "^1.8.4",
|
||||
"vite": "^6.3.0",
|
||||
"vue": "^3.5.13",
|
||||
"vue-router": "^4.5.0"
|
||||
}
|
||||
}
|
||||
84
src/App.vue
Normal file
84
src/App.vue
Normal file
@ -0,0 +1,84 @@
|
||||
<template>
|
||||
<div class="app">
|
||||
<header class="header">
|
||||
<div class="container">
|
||||
<h1 class="logo">美搭</h1>
|
||||
<nav class="nav">
|
||||
<router-link to="/" class="nav-link">首页</router-link>
|
||||
<router-link to="/tryon-history" class="nav-link">穿搭历史</router-link>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="main">
|
||||
<div class="container">
|
||||
<router-view />
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer class="footer">
|
||||
<div class="container">
|
||||
<p>© {{ new Date().getFullYear() }} 美搭 - 时尚穿搭展示平台</p>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// App组件逻辑
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.header {
|
||||
background-color: #1a1a1a;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
||||
padding: 15px 0;
|
||||
}
|
||||
|
||||
.header .container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: #fe2c55;
|
||||
}
|
||||
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
text-decoration: none;
|
||||
color: #f0f0f0;
|
||||
font-weight: 500;
|
||||
padding: 5px 10px;
|
||||
border-radius: 4px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.nav-link:hover {
|
||||
background-color: #2a2a2a;
|
||||
}
|
||||
|
||||
.router-link-active {
|
||||
color: #fe2c55;
|
||||
background-color: #2a2a2a;
|
||||
}
|
||||
|
||||
.main {
|
||||
min-height: calc(100vh - 140px);
|
||||
padding: 30px 0;
|
||||
}
|
||||
|
||||
.footer {
|
||||
background-color: #1a1a1a;
|
||||
padding: 20px 0;
|
||||
text-align: center;
|
||||
color: #999;
|
||||
}
|
||||
</style>
|
||||
38
src/api/index.js
Normal file
38
src/api/index.js
Normal file
@ -0,0 +1,38 @@
|
||||
import axios from 'axios'
|
||||
|
||||
// 创建axios实例
|
||||
const api = axios.create({
|
||||
baseURL: import.meta.env.PROD ? 'https://meida-api.beefast.co/' : 'http://127.0.0.1:8000/',
|
||||
timeout: 10000,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
|
||||
// 响应拦截器
|
||||
api.interceptors.response.use(
|
||||
response => {
|
||||
// 如果返回的状态码为200,说明接口请求成功,可以正常拿到数据
|
||||
if (response.status === 200) {
|
||||
return response.data
|
||||
}
|
||||
// 否则的话抛出错误
|
||||
return Promise.reject(new Error('网络请求错误'))
|
||||
},
|
||||
error => {
|
||||
let message = error.message || '请求失败'
|
||||
if (error.response && error.response.data) {
|
||||
message = error.response.data.message || message
|
||||
}
|
||||
console.error(`API错误: ${message}`)
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
// 定义API方法
|
||||
export default {
|
||||
// 获取所有试穿历史
|
||||
getAllTryonHistories() {
|
||||
return api.get('/api/v1/tryon/histories/all')
|
||||
}
|
||||
}
|
||||
69
src/assets/main.css
Normal file
69
src/assets/main.css
Normal file
@ -0,0 +1,69 @@
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html, body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
font-size: 16px;
|
||||
color: #f0f0f0;
|
||||
line-height: 1.5;
|
||||
background-color: #181818;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.card {
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
||||
overflow: hidden;
|
||||
background-color: #252525;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 8px 16px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-weight: 500;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: #fe2c55;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background-color: #e6254d;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.mt-10 {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.mt-20 {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.mb-10 {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.mb-20 {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
9
src/main.js
Normal file
9
src/main.js
Normal file
@ -0,0 +1,9 @@
|
||||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
import './assets/main.css'
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
app.use(router)
|
||||
app.mount('#app')
|
||||
22
src/router/index.js
Normal file
22
src/router/index.js
Normal file
@ -0,0 +1,22 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import Home from '../views/Home.vue'
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/',
|
||||
name: 'home',
|
||||
component: Home
|
||||
},
|
||||
{
|
||||
path: '/tryon-history',
|
||||
name: 'tryon-history',
|
||||
component: () => import('../views/TryonHistory.vue')
|
||||
}
|
||||
]
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes
|
||||
})
|
||||
|
||||
export default router
|
||||
82
src/views/Home.vue
Normal file
82
src/views/Home.vue
Normal file
@ -0,0 +1,82 @@
|
||||
<template>
|
||||
<div class="home">
|
||||
<div class="hero">
|
||||
<h1>欢迎使用美搭</h1>
|
||||
<p>您的个人时尚穿搭助手</p>
|
||||
<router-link to="/tryon-history" class="btn btn-primary">查看穿搭历史</router-link>
|
||||
</div>
|
||||
|
||||
<div class="features mt-20">
|
||||
<h2 class="text-center mb-20">我们的特色服务</h2>
|
||||
<div class="feature-grid">
|
||||
<div class="feature-card card">
|
||||
<div class="feature-icon">🔍</div>
|
||||
<h3>智能搭配</h3>
|
||||
<p>AI智能分析您的身材特点,为您推荐最合适的穿搭方案</p>
|
||||
</div>
|
||||
<div class="feature-card card">
|
||||
<div class="feature-icon">👚</div>
|
||||
<h3>虚拟试穿</h3>
|
||||
<p>无需实际试穿,在线体验不同服装的效果</p>
|
||||
</div>
|
||||
<div class="feature-card card">
|
||||
<div class="feature-icon">💯</div>
|
||||
<h3>评分点评</h3>
|
||||
<p>专业评分系统,为您的穿搭提供专业意见</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// 首页逻辑
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.hero {
|
||||
text-align: center;
|
||||
padding: 60px 0;
|
||||
background-color: #252525;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.hero h1 {
|
||||
font-size: 36px;
|
||||
margin-bottom: 10px;
|
||||
color: #f0f0f0;
|
||||
}
|
||||
|
||||
.hero p {
|
||||
font-size: 18px;
|
||||
color: #bbb;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.feature-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
.feature-card {
|
||||
padding: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.feature-icon {
|
||||
font-size: 48px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.feature-card h3 {
|
||||
font-size: 20px;
|
||||
margin-bottom: 10px;
|
||||
color: #f0f0f0;
|
||||
}
|
||||
|
||||
.feature-card p {
|
||||
color: #bbb;
|
||||
}
|
||||
</style>
|
||||
236
src/views/TryonHistory.vue
Normal file
236
src/views/TryonHistory.vue
Normal file
@ -0,0 +1,236 @@
|
||||
<template>
|
||||
<div class="tryon-history">
|
||||
<div v-if="loading" class="loading">
|
||||
<p>加载中...</p>
|
||||
</div>
|
||||
|
||||
<div v-else-if="error" class="error">
|
||||
<p>{{ error }}</p>
|
||||
<button @click="fetchHistories" class="btn btn-primary">重试</button>
|
||||
</div>
|
||||
|
||||
<div v-else-if="histories.length === 0" class="empty">
|
||||
<p>暂无穿搭历史记录</p>
|
||||
</div>
|
||||
|
||||
<div v-else class="waterfall-container">
|
||||
<div v-for="history in histories" :key="history.id" class="waterfall-item">
|
||||
<div class="history-card card">
|
||||
<div class="result-image">
|
||||
<img :src="history.completion_url" alt="穿搭效果" />
|
||||
</div>
|
||||
|
||||
<div class="history-details">
|
||||
<div class="score">
|
||||
<span class="score-value" :class="getScoreClass(history.score)">{{ history.score }}分</span>
|
||||
</div>
|
||||
|
||||
<div class="comment">
|
||||
<p>{{ history.comment }}</p>
|
||||
</div>
|
||||
|
||||
<div class="source-images">
|
||||
<h4>搭配来源</h4>
|
||||
<div class="image-row">
|
||||
<div class="person-image">
|
||||
<img :src="history.person_image_url" alt="用户照片" />
|
||||
</div>
|
||||
<div v-if="history.top_clothing_url" class="top-image">
|
||||
<img :src="history.top_clothing_url" alt="上衣" />
|
||||
</div>
|
||||
<div v-if="history.bottom_clothing_url" class="bottom-image">
|
||||
<img :src="history.bottom_clothing_url" alt="下装" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="date">
|
||||
<small>{{ formatDate(history.create_time) }}</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import api from '../api'
|
||||
|
||||
const histories = ref([])
|
||||
const loading = ref(false)
|
||||
const error = ref(null)
|
||||
|
||||
// 获取试穿历史记录
|
||||
const fetchHistories = async () => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
const response = await api.getAllTryonHistories()
|
||||
if (response.code === 200) {
|
||||
histories.value = response.data
|
||||
} else {
|
||||
error.value = response.message || '获取试穿历史失败'
|
||||
}
|
||||
} catch (err) {
|
||||
error.value = '网络错误,请稍后重试'
|
||||
console.error('获取试穿历史出错:', err)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化日期
|
||||
const formatDate = (dateString) => {
|
||||
const date = new Date(dateString)
|
||||
return date.toLocaleString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
})
|
||||
}
|
||||
|
||||
// 根据分数获取评分CSS类
|
||||
const getScoreClass = (score) => {
|
||||
if (score >= 90) return 'score-excellent'
|
||||
if (score >= 80) return 'score-good'
|
||||
if (score >= 60) return 'score-average'
|
||||
return 'score-poor'
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchHistories()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-title {
|
||||
margin-bottom: 30px;
|
||||
color: #f0f0f0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.loading, .error, .empty {
|
||||
text-align: center;
|
||||
padding: 40px 0;
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
/* 小红书风格瀑布流布局 */
|
||||
.waterfall-container {
|
||||
column-count: 1;
|
||||
column-gap: 15px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.waterfall-item {
|
||||
break-inside: avoid;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.history-card {
|
||||
overflow: hidden;
|
||||
border-radius: 8px;
|
||||
background-color: #252525;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.result-image img {
|
||||
width: 100%;
|
||||
display: block;
|
||||
object-fit: cover;
|
||||
border-radius: 8px 8px 0 0;
|
||||
}
|
||||
|
||||
.history-details {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.score {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.score-value {
|
||||
display: inline-block;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.score-excellent {
|
||||
color: #fff;
|
||||
background-color: #fe2c55;
|
||||
}
|
||||
|
||||
.score-good {
|
||||
color: #fff;
|
||||
background-color: #fe6d42;
|
||||
}
|
||||
|
||||
.score-average {
|
||||
color: #fff;
|
||||
background-color: #fd9426;
|
||||
}
|
||||
|
||||
.score-poor {
|
||||
color: #fff;
|
||||
background-color: #b91c1c;
|
||||
}
|
||||
|
||||
.comment {
|
||||
margin-bottom: 15px;
|
||||
line-height: 1.5;
|
||||
color: #f0f0f0;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.source-images h4 {
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
.image-row {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.person-image img, .top-image img, .bottom-image img {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
object-fit: cover;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #333;
|
||||
}
|
||||
|
||||
.date {
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* 响应式布局 */
|
||||
@media (min-width: 576px) {
|
||||
.waterfall-container {
|
||||
column-count: 2;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.waterfall-container {
|
||||
column-count: 3;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
.waterfall-container {
|
||||
column-count: 4;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
25
vite.config.js
Normal file
25
vite.config.js
Normal file
@ -0,0 +1,25 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { dirname, resolve } from 'path';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': resolve(__dirname, './src'),
|
||||
},
|
||||
},
|
||||
server: {
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://127.0.0.1:8000',
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user