From ca269ed71bcae5d2bc98f5778cc5d7ca6c014555 Mon Sep 17 00:00:00 2001 From: Hyouka Date: Wed, 21 Feb 2024 15:17:45 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=B9=E8=BF=9Bcard?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vitepress/config.ts | 8 +- .vitepress/theme/components/Card.ts | 146 ++++++++++------- .vitepress/theme/markdown/card.ts | 4 +- .vitepress/theme/styles/card.scss | 116 ++++++++++--- .vitepress/theme/styles/main.css | 3 + .vitepress/theme/styles/vars.css | 4 + package.json | 1 + pnpm-lock.yaml | 7 + src/download-client.md | 2 + src/md-enhance-guide.md | 245 +++++++++++++++++++++++++--- unocss.config.ts | 1 + 11 files changed, 430 insertions(+), 107 deletions(-) diff --git a/.vitepress/config.ts b/.vitepress/config.ts index b1b4047e6..c13cd289d 100644 --- a/.vitepress/config.ts +++ b/.vitepress/config.ts @@ -424,7 +424,13 @@ A Completionist's Interactive Map by Kongying Tavern`, md.use(obsidianImageSize) md.use(figure) md.use(timeline) - md.use(MarkdownItKbd) + md.use(MarkdownItKbd, { + presets: [ + { + name: 'icons', + }, + ], + }) }, }, }) diff --git a/.vitepress/theme/components/Card.ts b/.vitepress/theme/components/Card.ts index 5dc1152af..3e5cd6b9b 100644 --- a/.vitepress/theme/components/Card.ts +++ b/.vitepress/theme/components/Card.ts @@ -41,6 +41,13 @@ export interface CardProps { */ color?: string + /** + * Card cover + * + * 卡片封面,Only NormalTheme + */ + cover?: string + /** * Card hover shadow * @@ -69,88 +76,105 @@ const Card: FunctionalComponent = ({ logo = '', color = '', link = '', + cover = '', theme = 'normal', hoverShadow = true, shadow = true, }) => { let icon = '' + + const iconMap = { + 'bilibili.com': 'i-custom-bilibili', + 'txc.qq.com': 'i-custom-txc', + 'support.qq.com': 'i-custom-txc', + 'youtube.com': 'i-logos-youtube-icon', + 'twitter.com': 'i-logos-twitter', + 'discord': 'i-logos-discord-icon', + 'reddit.com': 'i-logos-reddit-icon', + 'baidu.com': 'i-custom-baidu', + 'qq.com': 'i-custom-qq', + 'weixitianli.com': 'i-custom-wxtl', + } + if (logo === '' && link !== '') { - if (link.includes('bilibili.com')) { - icon = 'i-custom-bilibili' - } else if (link.includes('txc.qq.com') || link.includes('support.qq.com')) { - icon = 'i-custom-txc' - } else if (link.includes('youtube.com')) { - icon = 'i-logos-youtube-icon' - } else if (link.includes('twitter.com')) { - icon = 'i-logos-twitter' - } else if (link.includes('discord')) { - icon = 'i-logos-discord-icon' - } else if (link.includes('reddit.com')) { - icon = 'i-logos-reddit-icon' - } else if (link.includes('baidu.com')) { - icon = 'i-custom-baidu' - } else if (link.includes('qq.com')) { - icon = 'i-custom-qq' - } else if (link.includes('weixitianli.com')) { - icon = `i-custom-wxtl` + const linkDomain = link.match(/(?:https?:\/\/)?(?:www\.)?([^\/]+)\//) + if (linkDomain && linkDomain[1]) { + const domain = linkDomain[1] + for (const key in iconMap) { + if (new RegExp(key).test(domain)) { + icon = iconMap[key] + break + } + } } } + const _Cover = () => { + return cover + ? h('div', { class: 'card-cover-contanier' }, [ + h('img', { + class: 'card-cover-img no-zoomable skeleton-animation', + onLoad: (e) => { + e.target!['classList'].remove('skeleton-animation') + }, + onError: (e) => { + e.target!['classList'].add('load-error') + e.target!['src'] = + 'https://assets.yuanshen.site/images/noImage.png' + }, + src: isRelativeLink(cover) ? withBase(cover) : cover, + }), + ]) + : '' + } + + const logoLink = (i) => { + if (i === 'self') return withBase('/imgs/logo_128.png') + if (isRelativeLink(i) && !logo && !icon) + return withBase('/imgs/logo_128.png') + return isRelativeLink(i) ? withBase(i) : i + } + const children = [ - icon === '' - ? h('img', { - class: 'vp-card-logo no-zoomable', - src: withBase(logo || '/imgs/logo_128.png'), - }) - : h('label', { - class: `vp-card-icon ${icon}`, + _Cover(), + h('div', { class: 'card-footer' }, [ + icon === '' + ? logo + ? h('img', { class: 'card-logo no-zoomable', src: logoLink(logo) }) + : '' + : h('label', { class: `card-icon ${icon}` }), + h('div', { class: 'card-content' }, [ + h('div', { class: 'card-title', innerHTML: title }), + h('hr'), + h('div', { + class: 'card-desc', + innerHTML: desc + ? desc + : isRelativeLink(link) + ? `https://yuanshen.site/docs/${link.substring(0, 3).replace(/(\.\/|\/)/g, '') + link.substring(3)}` + : link, }), - h('div', { class: 'vp-card-content' }, [ - h('div', { class: 'vp-card-title', innerHTML: title }), - h('hr'), - h('div', { - class: 'vp-card-desc', - innerHTML: desc - ? desc - : isRelativeLink(link) - ? `https://yuanshen.site/docs/${ - link.substring(0, 3).replace(/(\.\/|\/)/g, '') + - link.substring(3) - }` - : link, - }), + ]), ]), ] const props: Record = { - class: `vp-card vp-card-theme-${theme} ${ - hoverShadow ? 'vp-card-hover' : '' - }`, + class: `card card-theme-${theme} ${hoverShadow ? 'card-hover' : ''}`, title: title, } if (color) props['style'] = { background: color } if (shadow) props['style'] = { 'box-shadow': 'var(--vp-shadow-1)' } - return isLinkExternal(link) - ? h( - 'a', - { - href: link, - target: '_blank', - ...props, - }, - children, - ) - : h( - 'a', - { - href: withBase(link), - target: '_self', - ...props, - }, - children, - ) + return h( + 'a', + { + href: isLinkExternal(link) ? link : withBase(link), + target: isLinkExternal(link) ? '_blank' : '_self', + ...props, + }, + children, + ) } Card.displayName = 'Card' diff --git a/.vitepress/theme/markdown/card.ts b/.vitepress/theme/markdown/card.ts index 0af373817..f57062b57 100644 --- a/.vitepress/theme/markdown/card.ts +++ b/.vitepress/theme/markdown/card.ts @@ -18,6 +18,7 @@ export interface CardOptions { logo?: string link?: string color?: string + cover?: string theme?: 'normal' | 'medium' hoverShadow?: boolean shadow?: boolean @@ -29,6 +30,7 @@ const CARD_PROPS = [ 'logo', 'link', 'color', + 'cover', 'hoverShadow', 'shadow', 'theme', @@ -97,7 +99,7 @@ export const cardPlugin: PluginSimple = (md) => { name: 'card', openRender: () => `\ -
+
`, }) diff --git a/.vitepress/theme/styles/card.scss b/.vitepress/theme/styles/card.scss index 9b9703cb1..1cfee44ca 100644 --- a/.vitepress/theme/styles/card.scss +++ b/.vitepress/theme/styles/card.scss @@ -1,11 +1,14 @@ @use './config.scss'; -.vp-card { - display: inline-flex; - +.card { + display: flex; + flex-wrap: wrap; + flex-direction: row; + align-content: flex-start; + justify-content: flex-start; align-items: center; - border-radius: 0.5rem; - + border-radius: 0.75rem; + border: 1px solid var(--vp-c-divider); background-color: var(--vp-c-bg-alt); color: inherit; @@ -17,15 +20,70 @@ text-decoration: none !important; } -.vp-card-hover:hover { +.card-hover:hover { transform: translate3d(0, -4px, 0); + border-color: var(--vp-c-brand-1); box-shadow: var(--vp-shadow-2); } -.vp-card-theme-normal { +[card-grid] { + display: grid; + gap: 2rem; + grid-template-columns: repeat(1, minmax(0, 1fr)); +} + +@media (min-width: 640px) { + [card-grid] { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} + +.card-theme-normal { max-width: 360px; margin: 1rem 0.5rem; - padding: 1rem; + + .card-cover-contanier { + position: relative; + overflow: hidden; + width: 100%; + height: 160px; + border-radius: 0.75rem 0.75rem 0 0; + + .load-error::after { + content: '图片被派蒙吃掉了!' !important; + text-align: center; + line-height: 10; + background-color: var(--vp-c-bg-alt) !important; + animation: none !important; + } + + // 骨架屏动画 + .skeleton-animation::after { + content: ' '; + position: absolute; + bottom: 0; + left: 0; + right: 0; + top: 0; + background: linear-gradient( + 90deg, + rgba(255, 255, 255, 0), + var(--skeleton-animation-gradient), + rgba(255, 255, 255, 0) + ); + animation: t-skeleton--gradient 1.5s linear 2s infinite; + } + } + + .card-footer { + display: flex; + flex-wrap: nowrap; + flex-direction: row; + justify-content: center; + align-items: center; + padding: 1.25rem 1rem; + width: 100%; + } hr { margin: 0.25em 0; @@ -33,12 +91,12 @@ } @media (max-width: config.$mobile) { - .vp-card-theme-normal { + .card-theme-normal { max-width: 95%; } } -.vp-card-icon { +.card-icon { width: 2em !important; height: 2em !important; background-color: var(--vp-c-bg-soft); @@ -46,19 +104,20 @@ padding: 1em; } -.vp-card-logo { +.card-logo { width: 3em; height: 3em; margin-right: 1em; border-radius: 50%; } -.vp-card-content { +.card-content, +.card-footer { flex: 1; width: calc(100% - 6em); } -.vp-card-title { +.card-title { font-weight: 500; font-size: 1.1em; width: 95%; @@ -67,21 +126,21 @@ overflow: hidden; } -.vp-card-desc { +.card-desc { color: #888; font-size: 0.9em; line-height: 1.5; overflow-wrap: anywhere; } -.vp-card-container { +.card-container { display: flex; flex-wrap: wrap; align-content: stretch; align-items: stretch; justify-content: center; - .vp-card { + .card { flex-basis: calc(50% - 3rem); max-width: unset; margin: 0.5rem; @@ -100,25 +159,30 @@ } } -.vp-card-theme-medium { +.card-theme-medium { width: 100%; margin: 1rem 0.5rem; padding: 0.38rem; - .vp-card-logo { + .card-footer { + display: flex; + align-items: center; + } + + .card-logo { width: 2.5em; height: 2.5em; } - .vp-card-icon { + .card-icon { margin-left: 0.5em; } - .vp-card-title { + .card-title { font-size: 1em; } - .vp-card-desc { + .card-desc { width: 95%; text-overflow: ellipsis; white-space: nowrap; @@ -150,3 +214,13 @@ background-size: 100% 100%; background-color: transparent; } + +@keyframes t-skeleton--gradient { + 0% { + transform: translate(-100%) skew(-15deg); + } + + to { + transform: translate(100%) skew(-15deg); + } +} diff --git a/.vitepress/theme/styles/main.css b/.vitepress/theme/styles/main.css index c47b2c601..d705748af 100644 --- a/.vitepress/theme/styles/main.css +++ b/.vitepress/theme/styles/main.css @@ -127,6 +127,9 @@ html.dark .vp-doc figure figcaption { border: none; } +.vp-clear { +} + .medium-zoom-overlay { z-index: 999999999999; } diff --git a/.vitepress/theme/styles/vars.css b/.vitepress/theme/styles/vars.css index 8ba2d28a0..e34bcf403 100644 --- a/.vitepress/theme/styles/vars.css +++ b/.vitepress/theme/styles/vars.css @@ -36,6 +36,8 @@ --vp-custom-block-tip-text: #003100; --vp-custom-block-tip-bg: #e6f6e6; + + --skeleton-animation-gradient: rgba(0, 0, 0, 0.04); } .font-full { @@ -66,6 +68,8 @@ /* fix contrast on gray cards: check the same above (this is the default) */ --vp-c-text-dark-2: rgba(235, 235, 235, 0.6); --vp-c-tip-1: var(v--vp-c-tip-1) --nav-c-bg: rgba(30, 30, 32, 0.5); + + --skeleton-animation-gradient: rgba(255, 255, 255, 0.06); } /** diff --git a/package.json b/package.json index abdb9f827..9b2b00a79 100644 --- a/package.json +++ b/package.json @@ -77,6 +77,7 @@ "@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/parser": "^6.21.0", "@unocss/eslint-config": "^0.58.5", + "@unocss/extractor-mdc": "^0.58.5", "@vite-pwa/vitepress": "^0.3.1", "@vue/eslint-config-prettier": "^9.0.0", "@vue/eslint-config-typescript": "^12.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9aca6e2e0..4bd050dd6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -118,6 +118,9 @@ devDependencies: '@unocss/eslint-config': specifier: ^0.58.5 version: 0.58.5(eslint@8.56.0)(typescript@5.3.3) + '@unocss/extractor-mdc': + specifier: ^0.58.5 + version: 0.58.5 '@vite-pwa/vitepress': specifier: ^0.3.1 version: 0.3.1(vite-plugin-pwa@0.19.0) @@ -2947,6 +2950,10 @@ packages: '@unocss/core': 0.58.5 dev: false + /@unocss/extractor-mdc@0.58.5: + resolution: {integrity: sha512-8gsQ7xS9jy+hEARbMyhMGKH96U9YBdKw/F5qpBP63aaIRGQ7fXGtZGPlEGTpNchKV5NJY84zrVREFubZ8PXvdA==} + dev: true + /@unocss/inspector@0.58.5: resolution: {integrity: sha512-cbJlIHEZ14puTtttf7sl+VZFDscV1DJiSseh9sSe0xJ/1NVBT9Bvkm09/1tnpLYAgF5gfa1CaCcjKmURgYzKrA==} dependencies: diff --git a/src/download-client.md b/src/download-client.md index 5e23ae51b..3e45a85a6 100644 --- a/src/download-client.md +++ b/src/download-client.md @@ -19,6 +19,7 @@ comment: true ```card title: Windows 客户端使用手册 +logo: self link: ./manual/client-user-manual theme: medium ``` @@ -33,6 +34,7 @@ theme: medium ```card title: 网页版地图 V3 +logo: self link: https://v3.yuanshen.site theme: medium ``` diff --git a/src/md-enhance-guide.md b/src/md-enhance-guide.md index b531233dc..9d0ca8afd 100644 --- a/src/md-enhance-guide.md +++ b/src/md-enhance-guide.md @@ -1,35 +1,34 @@ --- -title: Markdown 增强语法 +title: Markdown 增强语法指南 +description: 本项目文档页面使用Markdown语法进行编写,除了 Vitepress 支持的 Markdown 基础组件之外我们额外添加和实现了一些特有语法和组件并在此演示 layout: doc +aside: false titleTemplate: 空荧酒馆 --- -## 上下角标 +## 目录 -站点中的 Markdown 文件支持上下角标 +[[TOC]] -### 语法 - -- 使用 ^ ^ 进行上角标标注。 -- 使用 ~ ~ 进行下角标标注。 - -:::tip -转义 +--- -你可以使用 \ 来转义 ^ 和 ~: +## Timeline 时间线 ```md -H\~2~O 19\^th^ -``` +::: timeline 2023-05-24 -会被渲染为 +- **do some thing1** +- do some thing2 + ::: -H\~2\~O 19\^th\^ -::: +::: timeline 2023-05-23 -## +- do some thing3 +- do some thing4 + ::: +``` ::: timeline 2023-05-24 @@ -38,13 +37,213 @@ H\~2\~O 19\^th\^ ::: ::: timeline 2023-05-23 -do some thing3 -do some thing4 -::: -则[[⌘+C]]Ac12321 +- do some thing3 +- do some thing4 + ::: + +--- -[[Copy]] -mac:cmd⌘copy⌘+Cwin:ctrl[[⊞+Ctrl]] +## Kbd 快捷键 +快捷方式组件建立在 Kbd 组件之上,可帮助您在内容中显示键盘快捷方式。 + +```md [[Meta]][[K]] +``` + +[[Meta]][[K]] + +--- + +## Footnote 脚注 + +- 在 Markdown 中使用 [^锚点文字] 来定义脚注。 +- 在之后的任何位置使用 [^锚点文字]: ... 来描述脚注内容。 +- 如果脚注包含多个段落,其后的段落应当保持双层缩进 + +#### 例子 + +脚注 1 链接[^first]。 + +脚注 2 链接[^second]。 + +行内的脚注^[行内脚注文本] 定义。 + +重复的页脚定义[^second]。 + +[^first]: 脚注 **可以包含特殊标记** + + 也可以由多个段落组成 + +[^second]: 脚注文字。 + +```md +脚注 1 链接[^first]。 + +脚注 2 链接[^second]。 + +行内的脚注^[行内脚注文本] 定义。 + +重复的页脚定义[^second]。 + +[^first]: 脚注 **可以包含特殊标记** + + 也可以由多个段落组成 + +[^second]: 脚注文字。 +``` + +--- + +## Mark 标记 + +使用 == == 进行标记。请注意两边需要有空格。 + +#### 例子 + +对于习惯了传统吟游诗人的蒙德来说, ==「偶像」== 是还不习惯的新生事物。但在蒙德,人人都爱芭芭拉。 + +```md +对于习惯了传统吟游诗人的蒙德来说, ==「偶像」== 是还不习惯的新生事物。但在蒙德,人人都爱芭芭拉。 +``` + +--- + +## Card 卡片 + +### Props + +| 接口 | 描述 | 默认值 | 类型 | +| ----------- | :----------------------------------------------- | :----: | :---------------: | +| title | 卡片标题,必填项 | - | `String` | +| desc | 卡片描述,为空时默认显示为 link | link | `String` | +| link | 卡片跳转链接,非必填 | - | `String` | +| logo | 卡片下方Logo的链接,非比填。填self默认为空荧logo | - | `Strong` | +| theme | 卡片主题,非必填 | normal | `normal`or`media` | +| color | 卡片链颜色,非必填 | normal | `String` | +| cover | 卡片封面链接,非必填。仅在normal主题生效 | - | `String` | +| hoverShadow | 是否启用卡片 hover 时阴影效果,默认启用 | true | `Boolean` | +| shadow | 是否启用卡片阴影效果,默认启用 | true | `Boolean` | + +> B站,百度,QQ,米游社,Youtube,X,Reddit,反馈平台的链接可以自动识别,无需手动填写logo + +### Example + +> Normal Theme + +```card +logo: self +title: This normal theme card +description: i am descrption +cover: https://upload-bbs.miyoushe.com/upload/2024/02/21/292762008/86d3c06e1a1adf7ef432cf838f7abb8c_7693471731342377565.png?x-oss-process=image/resize,s_500/quality,q_80/auto-orient,0/interlace,1/format,jpg +``` + +```card +logo: self +title: This normal theme card +description: i am descrption +``` + +````md +```card +logo: self +title: This normal theme card +description: i am descrption +cover: https://upload-bbs.miyoushe.com/upload/2024/02/21/292762008/86d3c06e1a1adf7ef432cf838f7abb8c_7693471731342377565.png?x-oss-process=image/resize,s_500/quality,q_80/auto-orient,0/interlace,1/format,jpg +``` + +```card +logo: self +title: This normal theme card +description: i am descrption +``` +```` + +> Medium Theme + +```card +title: 观看客户端基础使用教程 +link: https://www.bilibili.com/video/BV1uU4y157Te +theme: medium +``` + +```card +title: 网页版地图 +link: https://yuanshen.site/ +logo: self +desc: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +theme: medium +``` + +````md +```card +title: 观看客户端基础使用教程 +link: https://www.bilibili.com/video/BV1uU4y157Te +theme: medium +``` + +```card +title: 网页版地图 +link: https://yuanshen.site/ +desc: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +theme: medium +``` +```` + +--- + +## Frontmatter Config + +### footer + +- 类型:Boolean +- 默认:true + +是否显示页面的页脚 + +```yml +--- +footer: false #隐藏该页面的页脚 +--- +``` + +### aside + +- 类型:Boolean +- 默认:true + +是否显示页面的侧比栏 + +> 仅会在 `Layout: doc` 时自动启用 + +```yml +--- +footer: false #隐藏该页面的侧比栏 +--- +``` + +### banner + +- 类型:String + +配置页面的顶部的横幅内容,支持输入HTML不支持Markdown。默认隐藏 + +```yml +--- +banner: 我是Banner +--- +``` + +### bannerExpiryDate + +- 类型:Date + +配置页面顶部的横幅关闭日期,默认无 + +```yml +--- +banner: 服务器维护公告 +bannerExpiryDate: 2024-2-1 +--- +``` diff --git a/unocss.config.ts b/unocss.config.ts index 33ef077e9..bd1ee5d67 100644 --- a/unocss.config.ts +++ b/unocss.config.ts @@ -17,6 +17,7 @@ export default defineConfig({ 'icon-btn', 'inline-block cursor-pointer select-none opacity-75 scale-155 transition duration-200 ease-in-out hover:opacity-100 hover:text-teal-600', ], + ['card-grid', 'grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-8'], ], presets: [ presetUno(),