feat: 更新文档,添加项目架构和界面预览部分
This commit is contained in:
@@ -1,2 +1,3 @@
|
||||
dist/
|
||||
cache/
|
||||
.temp/
|
||||
|
||||
+255
@@ -31,6 +31,261 @@ features:
|
||||
link: /operations
|
||||
---
|
||||
|
||||
## 项目架构
|
||||
|
||||
<div class="home-architecture">
|
||||
<img
|
||||
src="./screenshots/metapi-architecture.png"
|
||||
alt="Metapi Federated AI Model Aggregation Gateway Architecture"
|
||||
/>
|
||||
</div>
|
||||
|
||||
## 项目界面预览
|
||||
|
||||
<div class="home-carousel" aria-label="项目界面自动轮播预览">
|
||||
<button class="home-carousel-nav home-carousel-prev" type="button" aria-label="上一张">
|
||||
<span aria-hidden="true">‹</span>
|
||||
</button>
|
||||
<button class="home-carousel-nav home-carousel-next" type="button" aria-label="下一张">
|
||||
<span aria-hidden="true">›</span>
|
||||
</button>
|
||||
<div class="home-carousel-track">
|
||||
<figure class="home-carousel-slide">
|
||||
<img src="./screenshots/dashboard.png" alt="仪表盘" />
|
||||
<figcaption>仪表盘</figcaption>
|
||||
</figure>
|
||||
<figure class="home-carousel-slide">
|
||||
<img src="./screenshots/model-marketplace.png" alt="模型广场" />
|
||||
<figcaption>模型广场</figcaption>
|
||||
</figure>
|
||||
<figure class="home-carousel-slide">
|
||||
<img src="./screenshots/routes.png" alt="智能路由" />
|
||||
<figcaption>智能路由</figcaption>
|
||||
</figure>
|
||||
<figure class="home-carousel-slide">
|
||||
<img src="./screenshots/accounts.png" alt="账号管理" />
|
||||
<figcaption>账号管理</figcaption>
|
||||
</figure>
|
||||
<figure class="home-carousel-slide">
|
||||
<img src="./screenshots/sites.png" alt="站点管理" />
|
||||
<figcaption>站点管理</figcaption>
|
||||
</figure>
|
||||
<figure class="home-carousel-slide">
|
||||
<img src="./screenshots/tokens.png" alt="令牌管理" />
|
||||
<figcaption>令牌管理</figcaption>
|
||||
</figure>
|
||||
<figure class="home-carousel-slide">
|
||||
<img src="./screenshots/playground.png" alt="模型操练场" />
|
||||
<figcaption>模型操练场</figcaption>
|
||||
</figure>
|
||||
<figure class="home-carousel-slide">
|
||||
<img src="./screenshots/monitor.png" alt="可用性监控" />
|
||||
<figcaption>可用性监控</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.home-architecture {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.home-architecture img {
|
||||
width: 100%;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
box-shadow: 0 12px 28px rgba(2, 8, 20, 0.08);
|
||||
}
|
||||
|
||||
.home-carousel {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 16px;
|
||||
background: var(--vp-c-bg-soft);
|
||||
box-shadow: 0 10px 24px rgba(2, 8, 20, 0.06);
|
||||
}
|
||||
|
||||
.home-carousel-track {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
transform: translateX(0%);
|
||||
transition: transform 420ms ease;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.home-carousel-slide {
|
||||
flex: 0 0 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.home-carousel-slide img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
max-height: min(78vh, 980px);
|
||||
object-fit: contain;
|
||||
background: var(--vp-c-bg);
|
||||
}
|
||||
|
||||
.home-carousel-slide figcaption {
|
||||
padding: 12px 14px;
|
||||
font-size: 22px;
|
||||
line-height: 1.2;
|
||||
font-weight: 600;
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.home-carousel-nav {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 14%;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: #fff;
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.home-carousel-nav span {
|
||||
display: inline-flex;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 999px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(17, 24, 39, 0.56);
|
||||
font-size: 28px;
|
||||
line-height: 1;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.home-carousel:hover .home-carousel-nav {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.home-carousel-prev {
|
||||
left: 0;
|
||||
justify-content: flex-start;
|
||||
padding-left: 12px;
|
||||
background: linear-gradient(90deg, rgba(15, 23, 42, 0.2), transparent);
|
||||
}
|
||||
|
||||
.home-carousel-next {
|
||||
right: 0;
|
||||
justify-content: flex-end;
|
||||
padding-right: 12px;
|
||||
background: linear-gradient(270deg, rgba(15, 23, 42, 0.2), transparent);
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.home-carousel-slide figcaption {
|
||||
font-size: 18px;
|
||||
padding: 10px 12px;
|
||||
}
|
||||
|
||||
.home-carousel-nav {
|
||||
opacity: 1;
|
||||
width: 18%;
|
||||
}
|
||||
|
||||
.home-carousel-nav span {
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script setup>
|
||||
import { onBeforeUnmount, onMounted } from 'vue';
|
||||
|
||||
let disposeCarousel = null;
|
||||
|
||||
onMounted(() => {
|
||||
const root = document.querySelector('.home-carousel');
|
||||
if (!root) return;
|
||||
|
||||
const track = root.querySelector('.home-carousel-track');
|
||||
const slides = root.querySelectorAll('.home-carousel-slide');
|
||||
const prevBtn = root.querySelector('.home-carousel-prev');
|
||||
const nextBtn = root.querySelector('.home-carousel-next');
|
||||
|
||||
if (!track || slides.length === 0) return;
|
||||
|
||||
const total = slides.length;
|
||||
let index = 0;
|
||||
let timerId = null;
|
||||
|
||||
const render = () => {
|
||||
track.style.transform = `translateX(-${index * 100}%)`;
|
||||
};
|
||||
|
||||
const goNext = () => {
|
||||
index = (index + 1) % total;
|
||||
render();
|
||||
};
|
||||
|
||||
const goPrev = () => {
|
||||
index = (index - 1 + total) % total;
|
||||
render();
|
||||
};
|
||||
|
||||
const stopAutoPlay = () => {
|
||||
if (timerId !== null) {
|
||||
window.clearInterval(timerId);
|
||||
timerId = null;
|
||||
}
|
||||
};
|
||||
|
||||
const startAutoPlay = () => {
|
||||
stopAutoPlay();
|
||||
timerId = window.setInterval(goNext, 3000);
|
||||
};
|
||||
|
||||
const onMouseEnter = () => stopAutoPlay();
|
||||
const onMouseLeave = () => startAutoPlay();
|
||||
const onPrevClick = () => {
|
||||
goPrev();
|
||||
startAutoPlay();
|
||||
};
|
||||
const onNextClick = () => {
|
||||
goNext();
|
||||
startAutoPlay();
|
||||
};
|
||||
|
||||
root.addEventListener('mouseenter', onMouseEnter);
|
||||
root.addEventListener('mouseleave', onMouseLeave);
|
||||
prevBtn?.addEventListener('click', onPrevClick);
|
||||
nextBtn?.addEventListener('click', onNextClick);
|
||||
|
||||
render();
|
||||
startAutoPlay();
|
||||
|
||||
disposeCarousel = () => {
|
||||
stopAutoPlay();
|
||||
root.removeEventListener('mouseenter', onMouseEnter);
|
||||
root.removeEventListener('mouseleave', onMouseLeave);
|
||||
prevBtn?.removeEventListener('click', onPrevClick);
|
||||
nextBtn?.removeEventListener('click', onNextClick);
|
||||
};
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (typeof disposeCarousel === 'function') {
|
||||
disposeCarousel();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
## 文档定位
|
||||
|
||||
- 面向新用户:快速搭建和接入
|
||||
|
||||
Reference in New Issue
Block a user