测试-02151304
This commit is contained in:
parent
49f21b9573
commit
5b5ed67e64
167
src/components/Examples_box.vue
Normal file
167
src/components/Examples_box.vue
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
<script setup>
|
||||||
|
defineProps({
|
||||||
|
example: Object,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<router-link :to="'/examples/' + example.id">
|
||||||
|
<div class="example-box" :class="{ 'no-image': !example.image }">
|
||||||
|
<img v-if="example.image" :src="example.image" alt="Example image" class="example-image" />
|
||||||
|
<div class="example-details">
|
||||||
|
<h3>{{ example.name }}</h3>
|
||||||
|
<p class="author">{{ example.author.join("、") }}</p>
|
||||||
|
<div class="tags">
|
||||||
|
<span v-for="tag in example.tags" :key="tag" class="tag">{{ tag }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</router-link>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* 基础布局 */
|
||||||
|
.example-box {
|
||||||
|
display: flex;
|
||||||
|
gap: 20px;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 20px;
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
min-height: 180px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 有图片时的布局 */
|
||||||
|
.example-image {
|
||||||
|
width: 240px;
|
||||||
|
height: 180px;
|
||||||
|
border-radius: 8px;
|
||||||
|
object-fit: cover;
|
||||||
|
transform-origin: center;
|
||||||
|
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 无图片时的布局 */
|
||||||
|
.example-box.no-image {
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-details {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 鼠标悬停动画 */
|
||||||
|
.example-box:hover {
|
||||||
|
transform: translateY(-4px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-box:hover .example-image {
|
||||||
|
transform: scale(1.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-box:hover h3 {
|
||||||
|
transform: translateX(8px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 文字动画 */
|
||||||
|
h3 {
|
||||||
|
font-size: 1.4rem;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.author {
|
||||||
|
opacity: 0.8;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-box:hover .author {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tags {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag {
|
||||||
|
padding: 4px 12px;
|
||||||
|
border-radius: 20px;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 暗色主题 (默认) */
|
||||||
|
.example-box {
|
||||||
|
background: #1a1a1a;
|
||||||
|
border: 1px solid #2d2d2d;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.author {
|
||||||
|
color: #a0a0a0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag {
|
||||||
|
background: #2d3a4a;
|
||||||
|
color: #7fadf2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 亮色主题 */
|
||||||
|
.theme-light .example-box {
|
||||||
|
background: #ffffff;
|
||||||
|
border: 1px solid #e0e0e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light h3 {
|
||||||
|
color: #1a1a1a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .author {
|
||||||
|
color: #666666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .tag {
|
||||||
|
background: #fff3cd;
|
||||||
|
color: #856404;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 无图片时的特殊样式 */
|
||||||
|
.example-box.no-image {
|
||||||
|
padding: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-box.no-image .example-details {
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-box.no-image:hover h3 {
|
||||||
|
transform: scale(1.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* 响应式处理 */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.example-box {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-image {
|
||||||
|
width: 100%;
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -53,6 +53,7 @@ const navItems = [
|
|||||||
'divider',
|
'divider',
|
||||||
{name: '博客', link: '/blog'},
|
{name: '博客', link: '/blog'},
|
||||||
{name: '项目', link: '/projects'},
|
{name: '项目', link: '/projects'},
|
||||||
|
{name: '实例', link: '/examples'},
|
||||||
{name: '小工具', link: '/tools'},
|
{name: '小工具', link: '/tools'},
|
||||||
'divider',
|
'divider',
|
||||||
{name: '关于', link: '/about'}
|
{name: '关于', link: '/about'}
|
||||||
|
@ -44,20 +44,25 @@ const getStatusColor = (status) => {
|
|||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.2); /* 更强的阴影效果 */
|
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.2); /* 更强的阴影效果 */
|
||||||
transition: transform 0.1s ease;
|
transition: transform 0.2s ease;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-box:hover {
|
.project-box:hover {
|
||||||
transform: scale(1.01); /* 鼠标悬停时放大盒子 */
|
transform: scale(1.05); /* 鼠标悬停时放大盒子 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-image {
|
.project-image {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 150px; /* 增加图片高度 */
|
height: 150px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.project-box:hover .project-image {
|
||||||
|
transform: scale(1.1); /* 鼠标悬停时放大盒子 */
|
||||||
|
}
|
||||||
|
|
||||||
.project-details {
|
.project-details {
|
||||||
padding: 10px 15px 15px;
|
padding: 10px 15px 15px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
139
src/pages/Examples_home.vue
Normal file
139
src/pages/Examples_home.vue
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import Examples_box from '../components/Examples_box.vue'
|
||||||
|
|
||||||
|
// 搜索输入
|
||||||
|
const searchQuery = ref('')
|
||||||
|
|
||||||
|
// 用于生成随机图片尺寸
|
||||||
|
const imageURL = (r1, r2) => {
|
||||||
|
return `https://picsum.photos/${220 + Math.floor(r1 * 500)}/${220 + Math.floor(r2 * 500)}`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 示例项目数据
|
||||||
|
const examples = ref([
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: '智能养殖系统',
|
||||||
|
author: ["张三", "李四"],
|
||||||
|
image: imageURL(Math.random(), Math.random()),
|
||||||
|
status: '进行中',
|
||||||
|
tags: ['智能养殖', '数据分析', '物联网']
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析搜索栏的输入,将其按空格拆分为多个关键字 tokens
|
||||||
|
* 例如输入 "Vue3 应用" => ["Vue3", "应用"]
|
||||||
|
*/
|
||||||
|
const parseSearchInput = (input) => {
|
||||||
|
return input
|
||||||
|
.trim()
|
||||||
|
.split(/\s+/)
|
||||||
|
.filter(Boolean) // 过滤空字符串
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据搜索栏输入,过滤项目
|
||||||
|
* 需求:搜索关键字既可以匹配项目名称,也可以匹配项目的 tags
|
||||||
|
* 并且多个关键字之间使用 "AND" 逻辑
|
||||||
|
*/
|
||||||
|
const filterExamples = () => {
|
||||||
|
// 如果没有输入任何搜索,直接返回全部
|
||||||
|
if (!searchQuery.value.trim()) {
|
||||||
|
return examples.value
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取用户输入的关键词数组
|
||||||
|
const tokens = parseSearchInput(searchQuery.value.toLowerCase())
|
||||||
|
|
||||||
|
return examples.value.filter((example) => {
|
||||||
|
// 将项目名称和标签都转换为小写,用于匹配
|
||||||
|
const nameLower = example.name.toLowerCase()
|
||||||
|
const tagsLower = example.tags.map((tag) => tag.toLowerCase())
|
||||||
|
|
||||||
|
// 对于每个 token,都要求能在名称或标签中找到
|
||||||
|
return tokens.every((token) => {
|
||||||
|
// 名称包含 token,或某个标签包含 token
|
||||||
|
return (
|
||||||
|
nameLower.includes(token) ||
|
||||||
|
tagsLower.some((tag) => tag.includes(token))
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="container">
|
||||||
|
<div class="filters">
|
||||||
|
<!-- 将标签的功能合并到搜索栏内 -->
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
v-model="searchQuery"
|
||||||
|
placeholder="搜索(多个标签或关键词,空格分割)"
|
||||||
|
class="search-bar"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 按照搜索结果展示项目 -->
|
||||||
|
<div class="examples">
|
||||||
|
<Examples_box
|
||||||
|
v-for="example in filterExamples()"
|
||||||
|
:key="example.name"
|
||||||
|
:example="example"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.container {
|
||||||
|
height: calc(100vh - 100px);
|
||||||
|
width: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 筛选区域 */
|
||||||
|
.filters {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 搜索栏 */
|
||||||
|
.search-bar {
|
||||||
|
padding: 10px;
|
||||||
|
margin: 20px 0;
|
||||||
|
width: 50%;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 项目区域 */
|
||||||
|
.examples {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 20px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 隐藏滚动条,可按需保留 */
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 亮色模式下可适当调整样式 */
|
||||||
|
.theme-light .search-bar {
|
||||||
|
border: 1px solid #ffb74d;
|
||||||
|
}
|
||||||
|
</style>
|
@ -12,6 +12,7 @@ import Account_draft from "../pages/accountPages/Account_draft.vue";
|
|||||||
import Account_userInfo from "../pages/accountPages/Account_userInfo.vue";
|
import Account_userInfo from "../pages/accountPages/Account_userInfo.vue";
|
||||||
import Account_admin_uploadLog from "../pages/accountPages/Account_admin_uploadLog.vue";
|
import Account_admin_uploadLog from "../pages/accountPages/Account_admin_uploadLog.vue";
|
||||||
import Projects from "../pages/Projects_home.vue";
|
import Projects from "../pages/Projects_home.vue";
|
||||||
|
import Examples_home from "../pages/Examples_home.vue";
|
||||||
import Tools_home from "../pages/Tools_home.vue";
|
import Tools_home from "../pages/Tools_home.vue";
|
||||||
import About from "../pages/About.vue";
|
import About from "../pages/About.vue";
|
||||||
|
|
||||||
@ -32,6 +33,10 @@ const routes = [
|
|||||||
path: '/projects',
|
path: '/projects',
|
||||||
name: 'Projects',
|
name: 'Projects',
|
||||||
component: Projects
|
component: Projects
|
||||||
|
}, {
|
||||||
|
path: '/examples',
|
||||||
|
name: 'Examples',
|
||||||
|
component: Examples_home
|
||||||
}, {
|
}, {
|
||||||
path: '/tools',
|
path: '/tools',
|
||||||
name: 'Tools',
|
name: 'Tools',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user