20260526152617
This commit is contained in:
1 parent
843529e57a
commit
223435efa6
10 files changed
+1143
-1
No files matched your search
@@ -0,0 +1,48 @@
|
||||
# 书签导入与整理计划
|
||||
|
||||
## 需求分析
|
||||
用户希望将浏览器书签文件 `D:\bookmarks_2026_5_26.html` 中的内容导入并整理到 `docs/resources.mdx` 文件中。
|
||||
|
||||
## 书签文件结构分析
|
||||
|
||||
从读取的书签文件中识别出以下主要分组:
|
||||
|
||||
### 第一层分组
|
||||
1. **AI 智能助手** - 包含 ChatGPT、腾讯元宝、v0 等
|
||||
2. **前端开发** - 包含多个子文件夹:
|
||||
- 图标库 (expo icons, iconfont, icones, lucide)
|
||||
- 前端模板库 (uiverse, codepen, htmlrev, lottiefiles, mockuphone)
|
||||
- TinyMCE 相关
|
||||
- Uniapp 相关
|
||||
- 前端组件库 (Arco Design)
|
||||
- React Native 第三方库
|
||||
- 插画库 (undraw, storyset, manypixels)
|
||||
- React Native 开发相关
|
||||
3. **Python** - PaddlePaddle 相关
|
||||
|
||||
## 实现方案
|
||||
|
||||
### 步骤
|
||||
1. 创建一个 Node.js 脚本来解析书签 HTML 文件
|
||||
2. 将解析结果转换为资源导航所需的分组格式
|
||||
3. 更新 `docs/resources.mdx` 文件
|
||||
|
||||
### 修改文件
|
||||
- `docs/resources.mdx` - 更新为解析后的书签内容
|
||||
|
||||
## 输出格式
|
||||
使用现有的 `SearchableLinkGrid` 组件格式,保持分组结构清晰。
|
||||
|
||||
## 过滤规则
|
||||
1. 跳过重复链接
|
||||
2. 清理过长的标题(超过50字符截断)
|
||||
3. 提取有意义的描述信息
|
||||
|
||||
## 风险评估
|
||||
- 文件较大(50KB+),需要处理大量数据
|
||||
- 部分链接可能失效,建议用户后续验证
|
||||
|
||||
## 测试计划
|
||||
1. 解析书签文件
|
||||
2. 生成新的 resources.mdx
|
||||
3. 启动开发服务器验证显示效果
|
||||
@@ -0,0 +1,102 @@
|
||||
# 资源导航搜索过滤功能实现计划
|
||||
|
||||
## 需求分析
|
||||
|
||||
用户希望在 `docs/resources.mdx` 文件中添加一个搜索框,实现本地过滤功能,可以根据关键词过滤显示的资源卡片。
|
||||
|
||||
## 现有代码结构分析
|
||||
|
||||
### 当前文件结构
|
||||
|
||||
* `docs/resources.mdx` - 资源导航页面,使用 LinkGrid 和 LinkCard 组件
|
||||
|
||||
* `src/components/Link/LinkCard.tsx` - 单个链接卡片组件
|
||||
|
||||
* `src/components/Link/LinkGrid.tsx` - 卡片网格容器组件
|
||||
|
||||
### 当前实现方式
|
||||
|
||||
`LinkGrid` 组件直接渲染所有子元素,没有过滤功能。
|
||||
|
||||
## 实现方案
|
||||
|
||||
### 方案选择
|
||||
|
||||
创建一个新的带搜索功能的组件 `SearchableLinkGrid`,包含:
|
||||
|
||||
1. 搜索输入框
|
||||
2. 过滤逻辑(基于 title 和 description)
|
||||
3. 集成现有 LinkCard 组件
|
||||
|
||||
### 修改文件列表
|
||||
|
||||
| 文件 | 操作 | 说明 |
|
||||
| -------------------------------------------- | -- | ------------ |
|
||||
| `src/components/Link/SearchableLinkGrid.tsx` | 新建 | 带搜索过滤的网格组件 |
|
||||
| `docs/resources.mdx` | 修改 | 引入新组件并重构内容结构 |
|
||||
|
||||
## 实现步骤
|
||||
|
||||
### 步骤 1: 创建 SearchableLinkGrid 组件
|
||||
|
||||
* 使用 React useState 管理搜索关键词
|
||||
|
||||
* 使用 useMemo 进行过滤计算
|
||||
|
||||
* 支持按 title 和 description 进行模糊搜索
|
||||
|
||||
### 步骤 2: 修改 resources.mdx
|
||||
|
||||
* 引入新组件
|
||||
|
||||
* 将现有 LinkCard 包装在 SearchableLinkGrid 中
|
||||
|
||||
## 技术实现细节
|
||||
|
||||
### 搜索过滤逻辑
|
||||
|
||||
```typescript
|
||||
// 过滤逻辑
|
||||
const filteredCards = useMemo(() => {
|
||||
if (!searchTerm) return children
|
||||
const lowerTerm = searchTerm.toLowerCase()
|
||||
return // 过滤匹配的卡片
|
||||
}, [searchTerm, children])
|
||||
```
|
||||
|
||||
### 组件设计
|
||||
|
||||
* 搜索框位于网格上方
|
||||
|
||||
* 实时过滤,无需点击搜索按钮
|
||||
|
||||
* 支持中英文搜索
|
||||
|
||||
## 风险评估
|
||||
|
||||
### 潜在风险
|
||||
|
||||
1. **性能问题**:如果卡片数量很大,过滤可能有性能问题
|
||||
|
||||
* 缓解:使用 useMemo 缓存过滤结果
|
||||
|
||||
2. **样式冲突**:新组件可能与现有样式冲突
|
||||
|
||||
* 缓解:使用独立的 CSS 模块
|
||||
|
||||
3. **React 版本兼容性**:需要确保使用 React 19 兼容的写法
|
||||
|
||||
* 缓解:使用标准 React hooks
|
||||
|
||||
## 依赖检查
|
||||
|
||||
* 项目已安装 React 19 和相关类型定义
|
||||
|
||||
* 使用现有样式系统 (CSS Modules)
|
||||
|
||||
## 测试计划
|
||||
|
||||
1. 启动开发服务器验证功能
|
||||
2. 测试搜索功能是否正常工作
|
||||
3. 验证样式是否正确显示
|
||||
|
||||
@@ -0,0 +1,333 @@
|
||||
---
|
||||
id: resources
|
||||
title: 资源导航
|
||||
sidebar_position: false
|
||||
hide_table_of_contents: true
|
||||
---
|
||||
|
||||
import SearchableLinkGrid from '@site/src/components/Link/SearchableLinkGrid';
|
||||
|
||||
<SearchableLinkGrid
|
||||
groups={[
|
||||
{
|
||||
groupName: 'AI 助手',
|
||||
items: [
|
||||
{
|
||||
title: 'ChatGPT',
|
||||
description: 'OpenAI 推出的智能对话助手',
|
||||
href: 'https://chatgpt.com/',
|
||||
},
|
||||
{
|
||||
title: '腾讯元宝',
|
||||
description: '腾讯混元大模型智能助手',
|
||||
href: 'https://yuanbao.tencent.com/chat/naQivTmsDa',
|
||||
},
|
||||
{
|
||||
title: 'v0.dev',
|
||||
description: 'AI 代码生成工具',
|
||||
href: 'http://v0.dev/',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
groupName: '大模型与AI',
|
||||
items: [
|
||||
{
|
||||
title: 'Ollama',
|
||||
description: '本地大模型运行工具',
|
||||
href: 'https://ollama.com/',
|
||||
},
|
||||
{
|
||||
title: 'OpenCompass',
|
||||
description: '大模型评测榜单',
|
||||
href: 'https://rank.opencompass.org.cn/leaderboard-multimodal/',
|
||||
},
|
||||
{
|
||||
title: 'InternVL',
|
||||
description: '多模态大模型',
|
||||
href: 'https://internvl.readthedocs.io/en/latest/internvl3.0/quick_start.html',
|
||||
},
|
||||
{
|
||||
title: 'Qwen',
|
||||
description: '阿里云通义千问',
|
||||
href: 'https://mcp.csdn.net/683559a9606a8318e85a74e7.html',
|
||||
},
|
||||
{
|
||||
title: '腾讯混元',
|
||||
description: '腾讯大模型',
|
||||
href: 'https://github.com/Tencent-Hunyuan/Tencent-Hunyuan-Large',
|
||||
},
|
||||
{
|
||||
title: 'MinerU',
|
||||
description: '文档解析工具',
|
||||
href: 'https://opendatalab.github.io/MinerU/zh/',
|
||||
},
|
||||
{
|
||||
title: 'PaddleOCR',
|
||||
description: 'OCR 识别工具',
|
||||
href: 'https://paddlepaddle.github.io/PaddleOCR/latest/version3.x/pipeline_usage/OCR.html',
|
||||
},
|
||||
{
|
||||
title: 'PaddleNLP',
|
||||
description: '自然语言处理框架',
|
||||
href: 'https://paddlenlp.readthedocs.io/zh/latest/advanced_guide/distributed_training.html',
|
||||
},
|
||||
{
|
||||
title: 'PaddleServing',
|
||||
description: '模型部署服务',
|
||||
href: 'https://github.com/PaddlePaddle/Serving/blob/v0.9.0/examples/Pipeline/PaddleOCR/ocr/README_CN.md',
|
||||
},
|
||||
{
|
||||
title: '免费 AI 接口整理',
|
||||
description: '4种免费 AI 接口',
|
||||
href: 'https://www.cnblogs.com/vipstone/p/19142030',
|
||||
},
|
||||
{
|
||||
title: 'HuggingFace 模型转换',
|
||||
description: 'GGUF 格式转换教程',
|
||||
href: 'https://zhuanlan.zhihu.com/p/721617987',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
groupName: '计算机视觉',
|
||||
items: [
|
||||
{
|
||||
title: 'YOLOv11',
|
||||
description: '目标检测模型',
|
||||
href: 'https://modelers.csdn.net/683fe614606a8318e85b0e13.html',
|
||||
},
|
||||
{
|
||||
title: 'X-AnyLabeling',
|
||||
description: '标注工具',
|
||||
href: 'https://github.com/CVHub520/X-AnyLabeling',
|
||||
},
|
||||
{
|
||||
title: 'GeoLayoutLM',
|
||||
description: '文档智能模型',
|
||||
href: 'https://mp.weixin.qq.com/s?__biz=Mzg4NjI0NDg0Ng==',
|
||||
},
|
||||
{
|
||||
title: 'LayoutReader',
|
||||
description: '文档阅读顺序模型',
|
||||
href: 'https://developer.volcengine.com/articles/7385010087594721319',
|
||||
},
|
||||
{
|
||||
title: 'PubLayNet',
|
||||
description: '文档布局数据集',
|
||||
href: 'https://opendatalab.com/OpenDataLab/PubLayNet/tree/main',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
groupName: '前端开发',
|
||||
items: [
|
||||
{
|
||||
title: 'Tauri',
|
||||
description: '跨平台桌面应用框架',
|
||||
href: 'https://v2.tauri.app/zh-cn/',
|
||||
},
|
||||
{
|
||||
title: 'Arco Design Vue',
|
||||
description: '字节跳动前端组件库',
|
||||
href: 'https://arco.design/vue/docs/start',
|
||||
},
|
||||
{
|
||||
title: 'gluestack-ui',
|
||||
description: 'React Native UI 套件',
|
||||
href: 'https://gluestack.io/ui/docs/home/overview/quick-start',
|
||||
},
|
||||
{
|
||||
title: 'React Native Directory',
|
||||
description: '第三方库目录',
|
||||
href: 'https://reactnative.directory/packages',
|
||||
},
|
||||
{
|
||||
title: 'Awesome RN',
|
||||
description: '学习资源精选',
|
||||
href: 'https://github.com/wabg/awesome-react-native',
|
||||
},
|
||||
{
|
||||
title: 'RN UI Datepicker',
|
||||
description: '日期选择组件',
|
||||
href: 'https://farhoudshapouran.github.io/react-native-ui-datepicker/',
|
||||
},
|
||||
{
|
||||
title: 'Victory Charts',
|
||||
description: '数据可视化库',
|
||||
href: 'https://nearform.com/open-source/victory/docs/introduction/',
|
||||
},
|
||||
{
|
||||
title: 'React Native 第三方库',
|
||||
description: '常用库汇总',
|
||||
href: 'https://www.cnblogs.com/youhui/articles/11771865.html',
|
||||
},
|
||||
{
|
||||
title: 'Expo 热更新',
|
||||
description: '不使用 EAS 的热更新方案',
|
||||
href: 'https://juejin.cn/post/7390659669113929779',
|
||||
},
|
||||
{
|
||||
title: '原生插件集成指南',
|
||||
description: 'uni-app 原生插件文档',
|
||||
href: 'https://nativesupport.dcloud.net.cn/NativePlugin/offline_package/android.html',
|
||||
},
|
||||
{
|
||||
title: '离线打包教程',
|
||||
description: 'Android 离线打包制作',
|
||||
href: 'https://ask.dcloud.net.cn/article/35482',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
groupName: 'UI 资源',
|
||||
items: [
|
||||
{
|
||||
title: 'Expo Vector Icons',
|
||||
description: 'React Native 图标库',
|
||||
href: 'https://icons.expo.fyi/Index',
|
||||
},
|
||||
{
|
||||
title: 'IconFont',
|
||||
description: '阿里巴巴矢量图标库',
|
||||
href: 'https://www.iconfont.cn/',
|
||||
},
|
||||
{
|
||||
title: 'Icônes',
|
||||
description: '开源图标库集合',
|
||||
href: 'https://icones.js.org/collection/all',
|
||||
},
|
||||
{
|
||||
title: 'Lucide',
|
||||
description: '精美的开源图标库',
|
||||
href: 'https://lucide.dev/icons/',
|
||||
},
|
||||
{
|
||||
title: 'SVG Repo',
|
||||
description: '免费 SVG 矢量图标',
|
||||
href: 'https://www.svgrepo.com/',
|
||||
},
|
||||
{
|
||||
title: 'UIverse',
|
||||
description: 'CSS & Tailwind UI 组件',
|
||||
href: 'https://uiverse.io/elements',
|
||||
},
|
||||
{
|
||||
title: 'CodePen',
|
||||
description: '前端代码分享社区',
|
||||
href: 'https://codepen.io/trending',
|
||||
},
|
||||
{
|
||||
title: 'HTML Rev',
|
||||
description: '免费 HTML 网站模板',
|
||||
href: 'https://htmlrev.com/',
|
||||
},
|
||||
{
|
||||
title: 'LottieFiles',
|
||||
description: '动画资源平台',
|
||||
href: 'https://app.lottiefiles.com/search',
|
||||
},
|
||||
{
|
||||
title: 'MockUPhone',
|
||||
description: '设备样机展示',
|
||||
href: 'https://mockuphone.com/model/iphone-15/',
|
||||
},
|
||||
{
|
||||
title: 'unDraw',
|
||||
description: '开源插画库',
|
||||
href: 'https://undraw.co/',
|
||||
},
|
||||
{
|
||||
title: 'Storyset',
|
||||
description: '可自定义插画',
|
||||
href: 'https://storyset.com/',
|
||||
},
|
||||
{
|
||||
title: 'ManyPixels',
|
||||
description: '免费插画素材',
|
||||
href: 'https://www.manypixels.co/gallery',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
groupName: '开发工具',
|
||||
items: [
|
||||
{
|
||||
title: 'NestJS',
|
||||
description: 'Node.js 企业级框架',
|
||||
href: 'https://nest.nodejs.cn/',
|
||||
},
|
||||
{
|
||||
title: 'JDK 8',
|
||||
description: 'Java 开发工具包',
|
||||
href: 'https://adoptium.net/zh-CN/temurin/releases?version=8',
|
||||
},
|
||||
{
|
||||
title: 'Go',
|
||||
description: 'Golang 下载',
|
||||
href: 'https://go.dev/dl/',
|
||||
},
|
||||
{
|
||||
title: 'nvm-windows',
|
||||
description: 'Node 版本管理',
|
||||
href: 'https://github.com/coreybutler/nvm-windows/releases',
|
||||
},
|
||||
{
|
||||
title: 'URL Encoder',
|
||||
description: 'SVG URL 编码工具',
|
||||
href: 'https://yoksel.github.io/url-encoder/zh-cn/',
|
||||
},
|
||||
{
|
||||
title: 'Dependency Walker',
|
||||
description: '依赖分析工具',
|
||||
href: 'https://www.dependencywalker.com/',
|
||||
},
|
||||
{
|
||||
title: '天地图 API',
|
||||
description: '地理编码服务',
|
||||
href: 'http://lbs.tianditu.gov.cn/server/geocoding.html',
|
||||
},
|
||||
{
|
||||
title: '天地图示例',
|
||||
description: 'JS API 示例',
|
||||
href: 'http://lbs.tianditu.gov.cn/api/js4.0/examples.html',
|
||||
},
|
||||
{
|
||||
title: '天地图 UniApp',
|
||||
description: 'UniApp 插件',
|
||||
href: 'https://ext.dcloud.net.cn/plugin?id=22630',
|
||||
},
|
||||
{
|
||||
title: 'Ollama 完整教程',
|
||||
description: '本地 LLM 管理',
|
||||
href: 'https://www.cnblogs.com/obullxl/p/18295202',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
groupName: '效率工具',
|
||||
items: [
|
||||
{
|
||||
title: 'DeepL翻译',
|
||||
description: 'AI 驱动的翻译工具',
|
||||
href: 'https://www.deepl.com/zh/translator',
|
||||
},
|
||||
{
|
||||
title: 'Snipaste',
|
||||
description: '截图工具',
|
||||
href: 'https://www.snipaste.com/',
|
||||
},
|
||||
{
|
||||
title: '腾讯桌面整理',
|
||||
description: '桌面文件整理工具',
|
||||
href: 'https://guanjia.qq.com/product/zmzl/',
|
||||
},
|
||||
{
|
||||
title: 'Twinkle Tray',
|
||||
description: '屏幕亮度调节',
|
||||
href: 'https://twinkletray.com/',
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
@@ -108,6 +108,11 @@ const config: Config = {
|
||||
position: 'left',
|
||||
sidebarId: 'DatabaseSidebar',
|
||||
},
|
||||
{
|
||||
to: '/docs/resources',
|
||||
label: '资源导航',
|
||||
position: 'left',
|
||||
},
|
||||
{
|
||||
to: '/blog',
|
||||
label: '博客',
|
||||
@@ -160,6 +165,9 @@ const config: Config = {
|
||||
maxHeadingLevel: 5,
|
||||
},
|
||||
} satisfies Preset.ThemeConfig,
|
||||
plugins: [
|
||||
'docusaurus-plugin-sass',
|
||||
],
|
||||
}
|
||||
|
||||
export default config
|
||||
@@ -25,9 +25,11 @@
|
||||
"@docusaurus/theme-common": "^3.9.2",
|
||||
"@mdx-js/react": "^3.0.0",
|
||||
"clsx": "^2.0.0",
|
||||
"docusaurus-plugin-sass": "^0.2.6",
|
||||
"prism-react-renderer": "^2.3.0",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0"
|
||||
"react-dom": "^19.0.0",
|
||||
"sass": "^1.100.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "^6.7.3",
|
||||
|
||||
Generated
+245
@@ -29,6 +29,9 @@ importers:
|
||||
clsx:
|
||||
specifier: ^2.0.0
|
||||
version: 2.1.1
|
||||
docusaurus-plugin-sass:
|
||||
specifier: ^0.2.6
|
||||
version: 0.2.6(@docusaurus/core@3.9.2(@mdx-js/react@3.1.1(@types/react@19.2.7)(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.6.3))(sass@1.100.0)(webpack@5.104.1)
|
||||
prism-react-renderer:
|
||||
specifier: ^2.3.0
|
||||
version: 2.4.1(react@19.2.3)
|
||||
@@ -38,6 +41,9 @@ importers:
|
||||
react-dom:
|
||||
specifier: ^19.0.0
|
||||
version: 19.2.3(react@19.2.3)
|
||||
sass:
|
||||
specifier: ^1.100.0
|
||||
version: 1.100.0
|
||||
devDependencies:
|
||||
'@antfu/eslint-config':
|
||||
specifier: ^6.7.3
|
||||
@@ -1556,6 +1562,94 @@ packages:
|
||||
resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==}
|
||||
engines: {node: '>=8.0.0'}
|
||||
|
||||
'@parcel/watcher-android-arm64@2.5.6':
|
||||
resolution: {integrity: sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@parcel/watcher-darwin-arm64@2.5.6':
|
||||
resolution: {integrity: sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@parcel/watcher-darwin-x64@2.5.6':
|
||||
resolution: {integrity: sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@parcel/watcher-freebsd-x64@2.5.6':
|
||||
resolution: {integrity: sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
'@parcel/watcher-linux-arm-glibc@2.5.6':
|
||||
resolution: {integrity: sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@parcel/watcher-linux-arm-musl@2.5.6':
|
||||
resolution: {integrity: sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@parcel/watcher-linux-arm64-glibc@2.5.6':
|
||||
resolution: {integrity: sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@parcel/watcher-linux-arm64-musl@2.5.6':
|
||||
resolution: {integrity: sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@parcel/watcher-linux-x64-glibc@2.5.6':
|
||||
resolution: {integrity: sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@parcel/watcher-linux-x64-musl@2.5.6':
|
||||
resolution: {integrity: sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@parcel/watcher-win32-arm64@2.5.6':
|
||||
resolution: {integrity: sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@parcel/watcher-win32-ia32@2.5.6':
|
||||
resolution: {integrity: sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@parcel/watcher-win32-x64@2.5.6':
|
||||
resolution: {integrity: sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@parcel/watcher@2.5.6':
|
||||
resolution: {integrity: sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
|
||||
'@pkgr/core@0.2.9':
|
||||
resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==}
|
||||
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
|
||||
@@ -2346,6 +2440,10 @@ packages:
|
||||
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
|
||||
engines: {node: '>= 8.10.0'}
|
||||
|
||||
chokidar@5.0.0:
|
||||
resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==}
|
||||
engines: {node: '>= 20.19.0'}
|
||||
|
||||
chrome-trace-event@1.0.4:
|
||||
resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==}
|
||||
engines: {node: '>=6.0'}
|
||||
@@ -2727,6 +2825,10 @@ packages:
|
||||
resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==}
|
||||
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
|
||||
|
||||
detect-libc@2.1.2:
|
||||
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
detect-node@2.1.0:
|
||||
resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==}
|
||||
|
||||
@@ -2750,6 +2852,12 @@ packages:
|
||||
resolution: {integrity: sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
docusaurus-plugin-sass@0.2.6:
|
||||
resolution: {integrity: sha512-2hKQQDkrufMong9upKoG/kSHJhuwd+FA3iAe/qzS/BmWpbIpe7XKmq5wlz4J5CJaOPu4x+iDJbgAxZqcoQf0kg==}
|
||||
peerDependencies:
|
||||
'@docusaurus/core': ^2.0.0-beta || ^3.0.0-alpha
|
||||
sass: ^1.30.0
|
||||
|
||||
dom-converter@0.2.0:
|
||||
resolution: {integrity: sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==}
|
||||
|
||||
@@ -3622,6 +3730,9 @@ packages:
|
||||
engines: {node: '>=16.x'}
|
||||
hasBin: true
|
||||
|
||||
immutable@5.1.5:
|
||||
resolution: {integrity: sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==}
|
||||
|
||||
import-fresh@3.3.1:
|
||||
resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -4312,6 +4423,9 @@ packages:
|
||||
no-case@3.0.4:
|
||||
resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==}
|
||||
|
||||
node-addon-api@7.1.1:
|
||||
resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
|
||||
|
||||
node-emoji@2.2.0:
|
||||
resolution: {integrity: sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -5082,6 +5196,10 @@ packages:
|
||||
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
|
||||
engines: {node: '>=8.10.0'}
|
||||
|
||||
readdirp@5.0.0:
|
||||
resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==}
|
||||
engines: {node: '>= 20.19.0'}
|
||||
|
||||
recma-build-jsx@1.0.0:
|
||||
resolution: {integrity: sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==}
|
||||
|
||||
@@ -5241,6 +5359,32 @@ packages:
|
||||
safer-buffer@2.1.2:
|
||||
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
|
||||
|
||||
sass-loader@16.0.8:
|
||||
resolution: {integrity: sha512-hcov4ZwZJIGbEuyNr9EmiTmZueyrxSToE6GOzoZnq5JM7ecRO7ttyvilPn+VmRsqiP16+VYZzVnGZj/hzZgKBA==}
|
||||
engines: {node: '>= 18.12.0'}
|
||||
peerDependencies:
|
||||
'@rspack/core': 0.x || ^1.0.0 || ^2.0.0-0
|
||||
node-sass: ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0
|
||||
sass: ^1.3.0
|
||||
sass-embedded: '*'
|
||||
webpack: ^5.0.0
|
||||
peerDependenciesMeta:
|
||||
'@rspack/core':
|
||||
optional: true
|
||||
node-sass:
|
||||
optional: true
|
||||
sass:
|
||||
optional: true
|
||||
sass-embedded:
|
||||
optional: true
|
||||
webpack:
|
||||
optional: true
|
||||
|
||||
sass@1.100.0:
|
||||
resolution: {integrity: sha512-B5j0rYMlinhhOo9tjQebMVVn0TfyXAF+wB3b2ggZUuJ/is/Y+7+JGjirAMxHZ9Z3hIP98NPfamlAkBHa1lAaXQ==}
|
||||
engines: {node: '>=20.19.0'}
|
||||
hasBin: true
|
||||
|
||||
sax@1.4.3:
|
||||
resolution: {integrity: sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==}
|
||||
|
||||
@@ -8344,6 +8488,67 @@ snapshots:
|
||||
|
||||
'@opentelemetry/api@1.9.0': {}
|
||||
|
||||
'@parcel/watcher-android-arm64@2.5.6':
|
||||
optional: true
|
||||
|
||||
'@parcel/watcher-darwin-arm64@2.5.6':
|
||||
optional: true
|
||||
|
||||
'@parcel/watcher-darwin-x64@2.5.6':
|
||||
optional: true
|
||||
|
||||
'@parcel/watcher-freebsd-x64@2.5.6':
|
||||
optional: true
|
||||
|
||||
'@parcel/watcher-linux-arm-glibc@2.5.6':
|
||||
optional: true
|
||||
|
||||
'@parcel/watcher-linux-arm-musl@2.5.6':
|
||||
optional: true
|
||||
|
||||
'@parcel/watcher-linux-arm64-glibc@2.5.6':
|
||||
optional: true
|
||||
|
||||
'@parcel/watcher-linux-arm64-musl@2.5.6':
|
||||
optional: true
|
||||
|
||||
'@parcel/watcher-linux-x64-glibc@2.5.6':
|
||||
optional: true
|
||||
|
||||
'@parcel/watcher-linux-x64-musl@2.5.6':
|
||||
optional: true
|
||||
|
||||
'@parcel/watcher-win32-arm64@2.5.6':
|
||||
optional: true
|
||||
|
||||
'@parcel/watcher-win32-ia32@2.5.6':
|
||||
optional: true
|
||||
|
||||
'@parcel/watcher-win32-x64@2.5.6':
|
||||
optional: true
|
||||
|
||||
'@parcel/watcher@2.5.6':
|
||||
dependencies:
|
||||
detect-libc: 2.1.2
|
||||
is-glob: 4.0.3
|
||||
node-addon-api: 7.1.1
|
||||
picomatch: 4.0.3
|
||||
optionalDependencies:
|
||||
'@parcel/watcher-android-arm64': 2.5.6
|
||||
'@parcel/watcher-darwin-arm64': 2.5.6
|
||||
'@parcel/watcher-darwin-x64': 2.5.6
|
||||
'@parcel/watcher-freebsd-x64': 2.5.6
|
||||
'@parcel/watcher-linux-arm-glibc': 2.5.6
|
||||
'@parcel/watcher-linux-arm-musl': 2.5.6
|
||||
'@parcel/watcher-linux-arm64-glibc': 2.5.6
|
||||
'@parcel/watcher-linux-arm64-musl': 2.5.6
|
||||
'@parcel/watcher-linux-x64-glibc': 2.5.6
|
||||
'@parcel/watcher-linux-x64-musl': 2.5.6
|
||||
'@parcel/watcher-win32-arm64': 2.5.6
|
||||
'@parcel/watcher-win32-ia32': 2.5.6
|
||||
'@parcel/watcher-win32-x64': 2.5.6
|
||||
optional: true
|
||||
|
||||
'@pkgr/core@0.2.9': {}
|
||||
|
||||
'@pnpm/config.env-replace@1.1.0': {}
|
||||
@@ -9301,6 +9506,10 @@ snapshots:
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.3
|
||||
|
||||
chokidar@5.0.0:
|
||||
dependencies:
|
||||
readdirp: 5.0.0
|
||||
|
||||
chrome-trace-event@1.0.4: {}
|
||||
|
||||
ci-info@3.9.0: {}
|
||||
@@ -9652,6 +9861,9 @@ snapshots:
|
||||
|
||||
destroy@1.2.0: {}
|
||||
|
||||
detect-libc@2.1.2:
|
||||
optional: true
|
||||
|
||||
detect-node@2.1.0: {}
|
||||
|
||||
detect-port@1.6.1:
|
||||
@@ -9675,6 +9887,17 @@ snapshots:
|
||||
dependencies:
|
||||
'@leichtgewicht/ip-codec': 2.0.5
|
||||
|
||||
docusaurus-plugin-sass@0.2.6(@docusaurus/core@3.9.2(@mdx-js/react@3.1.1(@types/react@19.2.7)(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.6.3))(sass@1.100.0)(webpack@5.104.1):
|
||||
dependencies:
|
||||
'@docusaurus/core': 3.9.2(@mdx-js/react@3.1.1(@types/react@19.2.7)(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.6.3)
|
||||
sass: 1.100.0
|
||||
sass-loader: 16.0.8(sass@1.100.0)(webpack@5.104.1)
|
||||
transitivePeerDependencies:
|
||||
- '@rspack/core'
|
||||
- node-sass
|
||||
- sass-embedded
|
||||
- webpack
|
||||
|
||||
dom-converter@0.2.0:
|
||||
dependencies:
|
||||
utila: 0.4.0
|
||||
@@ -10804,6 +11027,8 @@ snapshots:
|
||||
|
||||
image-size@2.0.2: {}
|
||||
|
||||
immutable@5.1.5: {}
|
||||
|
||||
import-fresh@3.3.1:
|
||||
dependencies:
|
||||
parent-module: 1.0.1
|
||||
@@ -11703,6 +11928,9 @@ snapshots:
|
||||
lower-case: 2.0.2
|
||||
tslib: 2.8.1
|
||||
|
||||
node-addon-api@7.1.1:
|
||||
optional: true
|
||||
|
||||
node-emoji@2.2.0:
|
||||
dependencies:
|
||||
'@sindresorhus/is': 4.6.0
|
||||
@@ -12532,6 +12760,8 @@ snapshots:
|
||||
dependencies:
|
||||
picomatch: 2.3.1
|
||||
|
||||
readdirp@5.0.0: {}
|
||||
|
||||
recma-build-jsx@1.0.0:
|
||||
dependencies:
|
||||
'@types/estree': 1.0.8
|
||||
@@ -12743,6 +12973,21 @@ snapshots:
|
||||
|
||||
safer-buffer@2.1.2: {}
|
||||
|
||||
sass-loader@16.0.8(sass@1.100.0)(webpack@5.104.1):
|
||||
dependencies:
|
||||
neo-async: 2.6.2
|
||||
optionalDependencies:
|
||||
sass: 1.100.0
|
||||
webpack: 5.104.1
|
||||
|
||||
sass@1.100.0:
|
||||
dependencies:
|
||||
chokidar: 5.0.0
|
||||
immutable: 5.1.5
|
||||
source-map-js: 1.2.1
|
||||
optionalDependencies:
|
||||
'@parcel/watcher': 2.5.6
|
||||
|
||||
sax@1.4.3: {}
|
||||
|
||||
scheduler@0.27.0: {}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import React from 'react'
|
||||
import styles from './styles.module.css'
|
||||
|
||||
export interface LinkCardProps {
|
||||
title: string
|
||||
description: string
|
||||
href: string
|
||||
icon?: string
|
||||
}
|
||||
|
||||
export default function LinkCard({
|
||||
title,
|
||||
description,
|
||||
href,
|
||||
icon,
|
||||
}: LinkCardProps) {
|
||||
let favicon = icon
|
||||
|
||||
if (!favicon) {
|
||||
try {
|
||||
const hostname = new URL(href).hostname
|
||||
favicon = `https://www.google.com/s2/favicons?domain=${hostname}&sz=128`
|
||||
}
|
||||
catch {
|
||||
favicon = undefined
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<a
|
||||
className={styles.card}
|
||||
href={href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<div className={styles.header}>
|
||||
{favicon
|
||||
? (
|
||||
<img
|
||||
src={favicon}
|
||||
alt={title}
|
||||
className={styles.icon}
|
||||
/>
|
||||
)
|
||||
: (
|
||||
<div className={styles.placeholder}>
|
||||
{title.charAt(0)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<span className={styles.title}>
|
||||
{title}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className={styles.description}>
|
||||
{description}
|
||||
</div>
|
||||
</a>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import React from 'react'
|
||||
import styles from './styles.module.css'
|
||||
|
||||
interface Props {
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
export default function LinkGrid({ children }: Props) {
|
||||
return (
|
||||
<div className={styles.grid}>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
import React, { useState, useMemo } from 'react'
|
||||
import LinkCard, { LinkCardProps } from './LinkCard'
|
||||
import styles from './styles.module.css'
|
||||
|
||||
export interface GroupedLinkCardProps {
|
||||
groupName: string
|
||||
items: LinkCardProps[]
|
||||
}
|
||||
|
||||
interface SearchableLinkGridProps {
|
||||
groups: GroupedLinkCardProps[]
|
||||
}
|
||||
|
||||
export default function SearchableLinkGrid({ groups }: SearchableLinkGridProps) {
|
||||
const [searchTerm, setSearchTerm] = useState('')
|
||||
|
||||
const filteredGroups = useMemo(() => {
|
||||
if (!searchTerm.trim()) return groups
|
||||
|
||||
const lowerTerm = searchTerm.toLowerCase()
|
||||
return groups.map(group => ({
|
||||
...group,
|
||||
items: group.items.filter(
|
||||
item =>
|
||||
item.title.toLowerCase().includes(lowerTerm) ||
|
||||
item.description.toLowerCase().includes(lowerTerm),
|
||||
),
|
||||
})).filter(group => group.items.length > 0)
|
||||
}, [searchTerm, groups])
|
||||
|
||||
const totalItems = groups.reduce((sum, group) => sum + group.items.length, 0)
|
||||
const filteredTotalItems = filteredGroups.reduce((sum, group) => sum + group.items.length, 0)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={styles.searchContainer}>
|
||||
<div className={styles.searchWrapper}>
|
||||
<svg
|
||||
className={styles.searchIcon}
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<circle cx="11" cy="11" r="8" />
|
||||
<path d="m21 21-4.35-4.35" />
|
||||
</svg>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="搜索资源..."
|
||||
value={searchTerm}
|
||||
onChange={e => setSearchTerm(e.target.value)}
|
||||
className={styles.searchInput}
|
||||
/>
|
||||
</div>
|
||||
<span className={styles.searchCount}>
|
||||
{filteredTotalItems} / {totalItems} 项
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{filteredGroups.length > 0 ? (
|
||||
filteredGroups.map((group, groupIndex) => (
|
||||
<div key={groupIndex} className={styles.group}>
|
||||
<h3 className={styles.groupTitle}>{group.groupName}</h3>
|
||||
<div className={styles.grid}>
|
||||
{group.items.map((item, itemIndex) => (
|
||||
<LinkCard key={itemIndex} {...item} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<div className={styles.noResults}>
|
||||
<svg
|
||||
className={styles.noResultsIcon}
|
||||
width="48"
|
||||
height="48"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<circle cx="11" cy="11" r="8" />
|
||||
<path d="m21 21-4.35-4.35" />
|
||||
<path d="M8 11s1.5-2 4-2 4 2 4 2" />
|
||||
</svg>
|
||||
<p>未找到匹配的资源</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
.grid {
|
||||
display: grid;
|
||||
gap: 16px;
|
||||
margin-top: 16px;
|
||||
|
||||
/* 默认桌面4列 */
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
/* 平板 */
|
||||
@media (max-width: 1200px) {
|
||||
.grid {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
/* 小平板 */
|
||||
@media (max-width: 996px) {
|
||||
.grid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
/* 手机 */
|
||||
@media (max-width: 576px) {
|
||||
.grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
/* 卡片 */
|
||||
|
||||
.card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
padding: 18px;
|
||||
|
||||
border-radius: 14px;
|
||||
border: 1px solid var(--ifm-color-emphasis-200);
|
||||
|
||||
background: var(--ifm-card-background-color);
|
||||
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
|
||||
transition:
|
||||
transform 0.2s ease,
|
||||
border-color 0.2s ease,
|
||||
box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
|
||||
transform: translateY(-3px);
|
||||
|
||||
border-color: var(--ifm-color-primary);
|
||||
|
||||
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
/* 标题区域 */
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
/* 图标 */
|
||||
|
||||
.icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
|
||||
object-fit: contain;
|
||||
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
|
||||
border-radius: 10px;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
background: var(--ifm-color-primary);
|
||||
color: white;
|
||||
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* 标题 */
|
||||
|
||||
.title {
|
||||
font-size: 17px;
|
||||
font-weight: 600;
|
||||
line-height: 1.4;
|
||||
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* 描述 */
|
||||
|
||||
.description {
|
||||
margin-top: 14px;
|
||||
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
|
||||
color: var(--ifm-color-emphasis-700);
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
/* 搜索容器 */
|
||||
|
||||
.searchContainer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
margin-bottom: 24px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.searchWrapper {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: var(--ifm-card-background-color);
|
||||
border: 1px solid var(--ifm-color-emphasis-200);
|
||||
border-radius: 12px;
|
||||
padding: 2px 2px 2px 16px;
|
||||
transition:
|
||||
border-color 0.2s ease,
|
||||
box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.searchWrapper:focus-within {
|
||||
border-color: var(--ifm-color-primary);
|
||||
box-shadow: 0 0 0 3px var(--ifm-color-primary-alpha-10);
|
||||
}
|
||||
|
||||
.searchIcon {
|
||||
color: var(--ifm-color-emphasis-400);
|
||||
margin-right: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.searchInput {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
padding: 12px 16px 12px 0;
|
||||
border: none;
|
||||
border-radius: 0 10px 10px 0;
|
||||
font-size: 15px;
|
||||
background: transparent;
|
||||
color: var(--ifm-font-color-base);
|
||||
}
|
||||
|
||||
.searchInput:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.searchInput::placeholder {
|
||||
color: var(--ifm-color-emphasis-500);
|
||||
}
|
||||
|
||||
.searchCount {
|
||||
font-size: 13px;
|
||||
color: var(--ifm-color-emphasis-500);
|
||||
white-space: nowrap;
|
||||
padding: 6px 12px;
|
||||
background: var(--ifm-color-emphasis-100);
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
/* 分组样式 */
|
||||
|
||||
.group {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.group:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.groupTitle {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: var(--ifm-color-emphasis-800);
|
||||
margin-bottom: 20px;
|
||||
padding-left: 14px;
|
||||
border-left: 4px solid var(--ifm-color-primary);
|
||||
}
|
||||
|
||||
/* 无结果状态 */
|
||||
|
||||
.noResults {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 60px 20px;
|
||||
color: var(--ifm-color-emphasis-600);
|
||||
}
|
||||
|
||||
.noResultsIcon {
|
||||
color: var(--ifm-color-emphasis-400);
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.noResults p {
|
||||
margin: 0;
|
||||
font-size: 15px;
|
||||
}
|
||||
Reference in new issue
Block a user