Skip to content

Commit 2953c59

Browse files
committed
feat: 项目介绍组件(有bug跳外源链接会卡死)
1 parent 576bc5e commit 2953c59

3 files changed

Lines changed: 138 additions & 170 deletions

File tree

app/components/ProjectIntro.vue

Lines changed: 87 additions & 168 deletions
Original file line numberDiff line numberDiff line change
@@ -1,176 +1,95 @@
1+
<!-- eslint-disable vue/attribute-hyphenation -->
12
<template>
2-
<section class="project-intro">
3-
<div class="container">
4-
<h2 class="title">
5-
{{ title }}
6-
</h2>
7-
<div class="project-list">
8-
<div
9-
v-for="(item, index) in projectList"
10-
:key="item.id"
11-
class="project-card"
12-
:style="{ animationDelay: `${index * 0.1}s` }"
13-
>
14-
<div class="project-img">
15-
<img :src="item.cover" :alt="item.name" loading="lazy">
16-
</div>
17-
<div class="project-info">
18-
<h3 class="project-name">
19-
{{ item.name }}
20-
</h3>
21-
<p class="project-desc">
22-
{{ item.desc }}
23-
</p>
24-
<p v-if="item.developTime" class="project-meta">
25-
开发时间:{{ item.developTime }}
26-
</p>
27-
<span class="project-tag">
28-
{{ item.tag }}
29-
</span>
30-
</div>
3+
<motion.div
4+
class="w-full flex flex-col md:flex-row gap-6 md:gap-8 p-6 bg-blue-100 rounded-2xl shadow-lg"
5+
:initial="{ x: -50, opacity: 0 }"
6+
:whileInView="{ x: 0, opacity: 1 }"
7+
:transition="{ duration: 1, type: 'spring' }"
8+
>
9+
<!-- 左侧图片区域 -->
10+
<motion.div class="md:w-1/2 flex-shrink-0">
11+
<!-- 图片容器,用于获取宽度 -->
12+
<div ref="imgContainer">
13+
<motion.img
14+
class="w-full h-48 object-cover rounded-2xl shadow-md text-center border border-dashed border-gray-300"
15+
:initial="{ opacity: 0, y: 40 }"
16+
:whileInView="{ opacity: 1, y: 0 }"
17+
:transition="{ duration: 1, delay: 0.6, type: 'spring' }"
18+
src="/images/test.png"
19+
alt="占位图片"
20+
/>
21+
</div>
22+
23+
<!-- 滑轨和滑块 -->
24+
<div class="mt-4 w-full">
25+
<div class="relative h-12 w-full bg-white/40 backdrop-blur-sm rounded-full shadow-inner border border-white/30">
26+
<motion.h4
27+
class="absolute top-1/2 -translate-y-1/2 right-15 text-sm font-medium tracking-tight text-blue-700/80"
28+
:style="{ opacity: textOpacity }"
29+
>
30+
访问github
31+
</motion.h4>
32+
<motion.div
33+
class="absolute top-1/2 -translate-y-1/2 w-15 h-10 bg-gradient-to-r from-white to-blue-50 rounded-full shadow-lg cursor-grab active:cursor-grabbing flex items-center justify-center gap-1 border border-blue-200/50"
34+
drag="x"
35+
:dragSnapToOrigin="isToOrigin"
36+
:dragElastic="0.1"
37+
:dragConstraints="dragConstraints"
38+
:style="{ x: sliderX }"
39+
>
40+
<!-- 装饰小圆点,表示可拖拽 -->
41+
<span class="ml-2 text-lg font-medium text-blue-600">></span>
42+
</motion.div>
3143
</div>
3244
</div>
33-
</div>
34-
</section>
45+
</motion.div>
46+
47+
<!-- 右侧 Markdown 内容区域(原样) -->
48+
<motion.div
49+
class="md:w-1/2 prose prose-lg max-w-none"
50+
:initial="{ opacity: 0, x: -40 }"
51+
:whileInView="{ opacity: 1, x: 0 }"
52+
:transition="{ duration: 0.8, delay: 1, type: 'spring' }"
53+
>
54+
<ContentRenderer v-if="markdownContent" :value="markdownContent" />
55+
<div v-else class="text-gray-400 italic">
56+
暂无内容
57+
</div>
58+
</motion.div>
59+
</motion.div>
3560
</template>
3661

3762
<script setup lang="ts">
38-
interface ProjectItem {
39-
id: number;
40-
name: string;
41-
desc: string;
42-
tag: string;
43-
cover: string;
44-
developTime?: string;
45-
team?: string;
46-
detailLink?: string;
47-
}
48-
49-
withDefaults(
50-
defineProps<{
51-
title?: string;
52-
projectList: ProjectItem[];
53-
}>(),
54-
{
55-
title: 'COSMO 过往项目',
56-
},
57-
);
58-
</script>
59-
60-
<style scoped>
61-
.project-intro {
62-
padding: 60px 20px !important;
63-
background-color: #0a0a0f !important;
64-
color: #fff !important;
65-
width: 100%;
66-
min-height: 100vh;
67-
}
68-
69-
.project-intro .container {
70-
max-width: 1200px;
71-
margin: 0 auto;
72-
}
73-
74-
.project-intro .title {
75-
font-size: 32px;
76-
text-align: center;
77-
margin-bottom: 40px;
78-
font-weight: 600;
79-
color: #fff !important;
80-
}
81-
82-
.project-intro .project-list {
83-
display: grid;
84-
grid-template-columns: repeat(3, 1fr);
85-
gap: 24px;
86-
}
87-
88-
.project-intro .project-card {
89-
background: rgba(255, 255, 255, 0.05) !important;
90-
border-radius: 12px;
91-
overflow: hidden;
92-
transition: transform 0.3s ease !important;
93-
opacity: 0;
94-
transform: translateY(30px);
95-
animation: fadeInUp 0.6s ease forwards;
96-
}
97-
98-
@keyframes fadeInUp {
99-
to {
100-
opacity: 1;
101-
transform: translateY(0);
102-
}
103-
}
104-
105-
.project-intro .project-card:hover {
106-
transform: translateY(-6px) !important;
107-
}
108-
109-
.project-intro .project-img {
110-
width: 100%;
111-
height: 180px;
112-
overflow: hidden;
113-
background: rgba(255,255,255,0.1);
114-
}
115-
116-
.project-intro .project-img img {
117-
width: 100%;
118-
height: 100%;
119-
object-fit: cover;
120-
}
121-
122-
.project-intro .project-info {
123-
padding: 16px;
124-
}
125-
126-
.project-intro .project-name {
127-
font-size: 18px;
128-
margin-bottom: 8px;
129-
color: #fff !important;
130-
}
131-
132-
.project-intro .project-desc {
133-
font-size: 14px;
134-
color: #ccc !important;
135-
line-height: 1.5;
136-
margin-bottom: 12px;
137-
}
138-
139-
.project-intro .project-meta {
140-
font-size: 12px;
141-
color: #999 !important;
142-
margin-bottom: 8px;
143-
}
144-
145-
.project-intro .project-tag {
146-
display: inline-block;
147-
padding: 4px 10px;
148-
font-size: 12px;
149-
background: #4f46e5 !important;
150-
color: #fff !important;
151-
border-radius: 6px;
152-
}
153-
154-
@media (max-width: 1024px) {
155-
.project-intro .title {
156-
font-size: 28px;
157-
}
158-
.project-intro .project-list {
159-
grid-template-columns: repeat(2, 1fr);
160-
gap: 20px;
161-
}
162-
}
163-
164-
@media (max-width: 768px) {
165-
.project-intro .title {
166-
font-size: 24px;
167-
}
168-
.project-intro .project-list {
169-
grid-template-columns: 1fr;
170-
gap: 16px;
63+
import type { Collections } from '@nuxt/content';
64+
import { motion } from 'motion-v';
65+
66+
defineProps<{
67+
markdownContent: Collections['projects'] | null;
68+
}>();
69+
70+
const imgContainer = ref<HTMLDivElement | null>(null);
71+
const dragConstraints = ref({ left: 0, right: 300 }); // 初始默认
72+
const sliderX = useMotionValue(0);
73+
let hasNavigated = false; // 防止重复导航
74+
const isToOrigin = ref(true);
75+
const textOpacity = useTransform(sliderX, [0, 200], [1, 0.1]);
76+
77+
onMounted(() => {
78+
if (imgContainer.value) {
79+
const parentWidth = imgContainer.value.clientWidth;
80+
const thumbWidth = 80; // 滑块宽度 w-20 = 5rem = 80px
81+
dragConstraints.value = { left: 0, right: parentWidth - thumbWidth };
17182
}
172-
.project-intro .project-img {
173-
height: 160px;
83+
});
84+
85+
useMotionValueEvent(sliderX, 'change', (latest) => {
86+
if (hasNavigated)
87+
return; // 已经导航过一次,直接返回
88+
// 当滑块拖动到右侧边界时,打开GitHub链接
89+
if (latest >= dragConstraints.value.right - 2) {
90+
isToOrigin.value = false;
91+
hasNavigated = true;
92+
navigateTo('/');
17493
}
175-
}
176-
</style>
94+
});
95+
</script>

app/pages/history.vue

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@
5959
</div>
6060
</li>
6161
</ul>
62+
63+
<ProjectIntro class="mt-4" :markdown-content="project ?? null" />
64+
<ProjectIntro class="mt-4" :markdown-content="project ?? null" />
6265
</div>
6366

6467
<!-- 回到顶部按钮 -->
@@ -92,8 +95,11 @@ const showBackToTop = ref(false);
9295
const { data: history } = await useAsyncData(() => {
9396
return queryCollection('history').all();
9497
});
98+
const { data: project } = await useAsyncData(() => {
99+
return queryCollection('projects').first();
100+
});
95101
96-
if (!history.value) {
102+
if (!history.value && !project.value) {
97103
throw createError({
98104
statusCode: 404,
99105
statusMessage: 'Page not found',
@@ -196,6 +202,7 @@ function scrollToSection(id: string, event: Event) {
196202
}
197203
</script>
198204

205+
<!-- cosmo-only-tailwind-disable -->
199206
<style scoped>
200207
.nav-sidebar {
201208
background: linear-gradient(135deg, rgba(255, 255, 255, 0.25) 0%, rgba(255, 255, 255, 0.15) 100%);

content/projects/test.md

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Project
1+
# Markdown 基础文字样式示例
22

33
## 标题样式
44

@@ -30,6 +30,48 @@ ___这也是粗斜体文字___
3030
> > 这是嵌套引用
3131
> > 支持多层嵌套
3232
33+
## 列表样式
34+
35+
### 无序列表
36+
- 列表项一
37+
- 列表项二
38+
- 嵌套列表项
39+
- 另一个嵌套项
40+
- 列表项三
41+
42+
### 有序列表
43+
1. 第一项
44+
2. 第二项
45+
1. 嵌套有序项
46+
2. 另一个嵌套项
47+
3. 第三项
48+
49+
## 代码样式
50+
51+
`这是行内代码`
52+
53+
```
54+
这是代码块
55+
可以包含多行代码
56+
function hello() {
57+
console.log("Hello World!");
58+
}
59+
```
60+
61+
```javascript
62+
// 这是带语言标识的代码块
63+
const message = 'Hello Markdown';
64+
console.log(message);
65+
```
66+
67+
## 链接和图片
68+
69+
[这是文本链接](https://example.com)
70+
71+
![这是图片描述](https://example.com/image.jpg)
72+
73+
## 表格样式
74+
3375
| 表头1 | 表头2 | 表头3 |
3476
|-------|-------|-------|
3577
| 单元格1 | 单元格2 | 单元格3 |

0 commit comments

Comments
 (0)