mirror of
https://github.com/pengzhanbo/vuepress-theme-plume.git
synced 2026-04-23 10:58:13 +08:00
commit
3a07d55e75
@ -2,6 +2,7 @@ import * as path from 'node:path'
|
||||
import { type UserConfig, defineUserConfig } from 'vuepress'
|
||||
import { viteBundler } from '@vuepress/bundler-vite'
|
||||
import { addViteOptimizeDepsInclude, addViteSsrExternal } from '@vuepress/helper'
|
||||
import { peerDependencies } from '../package.json'
|
||||
import { theme } from './theme.js'
|
||||
|
||||
export default defineUserConfig({
|
||||
@ -27,6 +28,10 @@ export default defineUserConfig({
|
||||
addViteSsrExternal(bundlerOptions, app, '@simonwep/pickr')
|
||||
},
|
||||
|
||||
define: {
|
||||
__VUEPRESS_VERSION__: peerDependencies.vuepress,
|
||||
},
|
||||
|
||||
bundler: viteBundler(),
|
||||
|
||||
theme,
|
||||
|
||||
@ -16,8 +16,10 @@ const { lightColors, darkColors, css, reset } = useThemeColors()
|
||||
<h4>{{ name }}</h4>
|
||||
<section v-for="color in group" :key="color.key" class="theme-color">
|
||||
<ColorPick v-model="color.value" />
|
||||
<h5>{{ color.name }}</h5>
|
||||
<span class="desc">{{ color.desc }}</span>
|
||||
<div>
|
||||
<h5>{{ color.name }}</h5>
|
||||
<span class="desc">{{ color.desc }}</span>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
@ -27,8 +29,10 @@ const { lightColors, darkColors, css, reset } = useThemeColors()
|
||||
<h4>{{ name }}</h4>
|
||||
<section v-for="color in group" :key="color.key" class="theme-color">
|
||||
<ColorPick v-model="color.value" />
|
||||
<h5>{{ color.name }}</h5>
|
||||
<span class="desc">{{ color.desc }}</span>
|
||||
<div>
|
||||
<h5>{{ color.name }}</h5>
|
||||
<span class="desc">{{ color.desc }}</span>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
70
docs/1.示例/照片类作品示例.md
Normal file
70
docs/1.示例/照片类作品示例.md
Normal file
@ -0,0 +1,70 @@
|
||||
---
|
||||
title: 照片类作品示例
|
||||
author: Plume Theme
|
||||
createTime: 2024/08/17 14:30:23
|
||||
permalink: /article/30995vcd/
|
||||
---
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
|
||||
const list = ref([])
|
||||
|
||||
onMounted(async () => {
|
||||
const res = await fetch('https://api.pengzhanbo.cn/wallpaper/bing/list/zh/').then((res) => res.json())
|
||||
list.value = res.map(item => ({
|
||||
title: item.title,
|
||||
image: item.url,
|
||||
author: item.copyright.replace('© ', '').split('/')?.[0].trim(),
|
||||
description: item.description,
|
||||
date: item.ssd.replace(/(\d{4})(\d{2})(\d{2})_(\d{2})(\d{2})/, (_, y, m, d, h, mm) => `${y}/${m}/${d} ${h}:${mm}`),
|
||||
href: item.url
|
||||
}))
|
||||
})
|
||||
</script>
|
||||
|
||||
## 单列照片
|
||||
|
||||
```md :no-line-numbers
|
||||
<ImageCard v-for="item in list" :key="item.image" v-bind="item" />
|
||||
```
|
||||
|
||||
<ImageCard v-for="item in list" :key="item.image" v-bind="item" />
|
||||
|
||||
## 双列照片
|
||||
|
||||
```md :no-line-numbers
|
||||
<CardGrid cols="2">
|
||||
<ImageCard v-for="item in list" :key="item.image" v-bind="item" />
|
||||
</CardGrid>
|
||||
```
|
||||
|
||||
<CardGrid cols="2">
|
||||
<ImageCard v-for="item in list" :key="item.image" v-bind="item" />
|
||||
</CardGrid>
|
||||
|
||||
## 三列照片
|
||||
|
||||
```md :no-line-numbers
|
||||
<CardGrid cols="3">
|
||||
<ImageCard v-for="item in list" :key="item.image" v-bind="item" />
|
||||
</CardGrid>
|
||||
```
|
||||
|
||||
<CardGrid cols="3">
|
||||
<ImageCard v-for="item in list" :key="item.image" v-bind="item" />
|
||||
</CardGrid>
|
||||
|
||||
## 不同尺寸设备适配
|
||||
|
||||
调整窗口大小以观察效果
|
||||
|
||||
```md :no-line-numbers
|
||||
<CardGrid :cols="{ sm: 2, md: 3, lg: 3 }">
|
||||
<ImageCard v-for="item in list" :key="item.image" v-bind="item" />
|
||||
</CardGrid>
|
||||
```
|
||||
|
||||
<CardGrid :cols="{ sm: 2, md: 3, lg: 3 }">
|
||||
<ImageCard v-for="item in list" :key="item.image" v-bind="item" />
|
||||
</CardGrid>
|
||||
@ -28,7 +28,7 @@ VuePress 支持在 Markdown 文件中使用 组件。
|
||||
|
||||
**输入:**
|
||||
|
||||
```md
|
||||
```md :no-line-numbers
|
||||
- VuePress - <Badge type="info" text="v2" />
|
||||
- VuePress - <Badge type="tip" text="v2" />
|
||||
- VuePress - <Badge type="warning" text="v2" />
|
||||
@ -58,7 +58,7 @@ VuePress 支持在 Markdown 文件中使用 组件。
|
||||
|
||||
**输入:**
|
||||
|
||||
```md
|
||||
```md :no-line-numbers
|
||||
- home - <Icon name="material-symbols:home" color="currentColor" size="1em" />
|
||||
- vscode - <Icon name="skill-icons:vscode-dark" size="2em" />
|
||||
- twitter - <Icon name="skill-icons:twitter" size="2em" />
|
||||
@ -79,7 +79,7 @@ VuePress 支持在 Markdown 文件中使用 组件。
|
||||
::: code-tabs
|
||||
@tab .vuepress/config.ts
|
||||
|
||||
```ts
|
||||
```ts :no-line-numbers
|
||||
export default defineUserConfig({
|
||||
theme: plumeTheme({
|
||||
plugins: {
|
||||
@ -103,7 +103,7 @@ export default defineUserConfig({
|
||||
|
||||
**输入:**
|
||||
|
||||
```md
|
||||
```md :no-line-numbers
|
||||
- 鼠标悬停 - <Plot>悬停时可见</Plot>
|
||||
- 点击 - <Plot trigger="click">点击时可见</Plot>
|
||||
```
|
||||
@ -135,7 +135,7 @@ export default defineUserConfig({
|
||||
|
||||
**输入:**
|
||||
|
||||
```md
|
||||
```md :no-line-numbers
|
||||
<Card title="卡片标题" icon="twemoji:astonished-face">
|
||||
这里是卡片内容。
|
||||
</Card>
|
||||
@ -183,7 +183,7 @@ export default defineUserConfig({
|
||||
|
||||
**输入:**
|
||||
|
||||
```md
|
||||
```md :no-line-numbers
|
||||
<LinkCard title="卡片标题" href="/" description="这里是卡片内容" />
|
||||
<LinkCard icon="twemoji:astonished-face" title="卡片标题" href="/" />
|
||||
```
|
||||
@ -193,13 +193,109 @@ export default defineUserConfig({
|
||||
<LinkCard title="卡片标题" href="/" description="这里是卡片内容" />
|
||||
<LinkCard icon="twemoji:astonished-face" title="卡片标题" href="/" />
|
||||
|
||||
## 图片卡片
|
||||
|
||||
使用 `<ImageCard>` 组件在页面中显示图片卡片。
|
||||
|
||||
图片卡片 有别于 markdown 的 普通插入图片方式,它展示与图片相关的更多信息,包括标题、描述、作者、链接等。
|
||||
适用于如 摄影作品、设计作品、宣传海报 等场景。
|
||||
|
||||
### Props
|
||||
|
||||
| 名称 | 类型 | 默认值 | 说明 |
|
||||
| ----------- | -------------------------- | ------ | --------------------------------------- |
|
||||
| image | `string` | `''` | 必填,图片链接 |
|
||||
| title | `string` | `''` | 可选,标题 (展示其它信息需要依赖此属性) |
|
||||
| description | `string` | `''` | 可选,描述 |
|
||||
| author | `string` | `''` | 可选,作者 |
|
||||
| href | `string` | `''` | 可选,链接 |
|
||||
| date | `string \| Date \| number` | `''` | 可选,日期 |
|
||||
|
||||
**输入:**
|
||||
|
||||
```md :no-line-numbers
|
||||
<ImageCard
|
||||
image="https://cn.bing.com/th?id=OHR.AlfanzinaLighthouse_ZH-CN9704515669_1920x1080.webp"
|
||||
title="阿尔凡齐纳灯塔,阿尔加维,葡萄牙"
|
||||
description="今天照片中的灯塔位于葡萄牙南部海岸阿尔加维的卡沃埃罗。阿尔凡齐纳灯塔建于1919年,照耀着大海,帮助船只在该地区周围危险的水域航行。这座灯塔是著名的旅游胜地,同时也是该地区与海洋紧密联系的象征。如果你有幸住在灯塔附近,那么本周末就是拜访灯塔的最佳时机。"
|
||||
href="/"
|
||||
author="Andreas Kunz"
|
||||
date="2024/08/16"
|
||||
/>
|
||||
```
|
||||
|
||||
**输出:**
|
||||
|
||||
<ImageCard
|
||||
image="https://cn.bing.com/th?id=OHR.AlfanzinaLighthouse_ZH-CN9704515669_1920x1080.webp"
|
||||
title="阿尔凡齐纳灯塔,阿尔加维,葡萄牙"
|
||||
description="今天照片中的灯塔位于葡萄牙南部海岸阿尔加维的卡沃埃罗。阿尔凡齐纳灯塔建于1919年,照耀着大海,帮助船只在该地区周围危险的水域航行。这座灯塔是著名的旅游胜地,同时也是该地区与海洋紧密联系的象征。如果你有幸住在灯塔附近,那么本周末就是拜访灯塔的最佳时机。"
|
||||
href="/"
|
||||
author="Andreas Kunz"
|
||||
date="2024/08/16"
|
||||
/>
|
||||
|
||||
还可以放到 `<CardGrid>` 组件中。
|
||||
|
||||
**输入:**
|
||||
|
||||
```md :no-line-numbers
|
||||
<CardGrid>
|
||||
<ImageCard
|
||||
image="https://cn.bing.com/th?id=OHR.AlfanzinaLighthouse_ZH-CN9704515669_1920x1080.webp"
|
||||
title="阿尔凡齐纳灯塔,阿尔加维,葡萄牙"
|
||||
description="..."
|
||||
href="/"
|
||||
author="Andreas Kunz"
|
||||
date="2024/08/16"
|
||||
/>
|
||||
<ImageCard
|
||||
image="https://cn.bing.com/th?id=OHR.AlfanzinaLighthouse_ZH-CN9704515669_1920x1080.webp"
|
||||
title="阿尔凡齐纳灯塔,阿尔加维,葡萄牙"
|
||||
description="..."
|
||||
href="/"
|
||||
author="Andreas Kunz"
|
||||
date="2024/08/16"
|
||||
/>
|
||||
</CardGrid>
|
||||
```
|
||||
|
||||
**输出:**
|
||||
|
||||
<CardGrid>
|
||||
<ImageCard
|
||||
image="https://cn.bing.com/th?id=OHR.AlfanzinaLighthouse_ZH-CN9704515669_1920x1080.webp"
|
||||
title="阿尔凡齐纳灯塔,阿尔加维,葡萄牙"
|
||||
description="今天照片中的灯塔位于葡萄牙南部海岸阿尔加维的卡沃埃罗。阿尔凡齐纳灯塔建于1919年,照耀着大海,帮助船只在该地区周围危险的水域航行。这座灯塔是著名的旅游胜地,同时也是该地区与海洋紧密联系的象征。如果你有幸住在灯塔附近,那么本周末就是拜访灯塔的最佳时机。"
|
||||
href="/"
|
||||
author="Andreas Kunz"
|
||||
date="2024/08/16"
|
||||
/>
|
||||
<ImageCard
|
||||
image="https://cn.bing.com/th?id=OHR.AlfanzinaLighthouse_ZH-CN9704515669_1920x1080.webp"
|
||||
title="阿尔凡齐纳灯塔,阿尔加维,葡萄牙"
|
||||
description="今天照片中的灯塔位于葡萄牙南部海岸阿尔加维的卡沃埃罗。阿尔凡齐纳灯塔建于1919年,照耀着大海,帮助船只在该地区周围危险的水域航行。这座灯塔是著名的旅游胜地,同时也是该地区与海洋紧密联系的象征。如果你有幸住在灯塔附近,那么本周末就是拜访灯塔的最佳时机。"
|
||||
href="/"
|
||||
author="Andreas Kunz"
|
||||
date="2024/08/16"
|
||||
/>
|
||||
</CardGrid>
|
||||
|
||||
[查看 照片类作品示例](../../../../1.示例/照片类作品示例.md)
|
||||
|
||||
## 卡片排列容器
|
||||
|
||||
当你需要将多个卡片排列,可以使用 `<CardGrid>` 组件。在空间足够时,多个卡片会自动排列。
|
||||
|
||||
### Props
|
||||
|
||||
| 名称 | 类型 | 默认值 | 说明 |
|
||||
| :--- | :----------------------------------------------- | :----- | :------------- |
|
||||
| cols | `number \| Record<'sm' \| 'md' \| 'lg', number>` | `2` | 卡片排列列数。 |
|
||||
|
||||
**输入:**
|
||||
|
||||
```md
|
||||
```md :no-line-numbers
|
||||
<CardGrid>
|
||||
<Card title="卡片标题" icon="twemoji:astonished-face">
|
||||
这里是卡片内容。
|
||||
|
||||
@ -9,6 +9,10 @@ tags:
|
||||
- 快速开始
|
||||
---
|
||||
|
||||
<script setup>
|
||||
const vuepressVersion = __VUEPRESS_VERSION__
|
||||
</script>
|
||||
|
||||
## 依赖环境
|
||||
|
||||
- [Node.js v18.20.0+](https://nodejs.org/)
|
||||
@ -94,7 +98,7 @@ tags:
|
||||
:::
|
||||
|
||||
:::warning
|
||||
主题当前版本 已适配至 `vuepress@2.0.0-rc.14`,你应该安装这个版本的 VuePress。
|
||||
主题当前版本 已适配至 <code>vuepress@{{ vuepressVersion }}</code>,你应该安装这个版本的 VuePress。
|
||||
高于或低于这个版本,可能会存在潜在的兼容性问题。
|
||||
:::
|
||||
|
||||
@ -160,7 +164,7 @@ tags:
|
||||
:::
|
||||
|
||||
:::warning
|
||||
无论是否需要使用 **多语言** ,你都应该为 VuePress 配置 正确 `lang` 选项值。
|
||||
无论是否需要使用 __多语言__ ,你都应该为 VuePress 配置 正确 `lang` 选项值。
|
||||
主题需要根据 `lang` 选项来确定语言环境文本。
|
||||
:::
|
||||
|
||||
|
||||
@ -22,13 +22,13 @@ tags:
|
||||
|
||||
示例:
|
||||
|
||||
```
|
||||
``` :no-line-numbers
|
||||
{sourceDir}/
|
||||
├─ notes/
|
||||
├─ typescript/
|
||||
│ ├─ foo.md
|
||||
├─ rust/
|
||||
│ ├─ foo.md
|
||||
│ ├─ typescript/
|
||||
│ │ └─ foo.md
|
||||
│ └─ rust/
|
||||
│ └─ foo.md
|
||||
```
|
||||
|
||||
其中,`typescript` 和 `rust` 为目录名,各自独立保存与之相关的 markdown 文件。
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
"vuepress": "2.0.0-rc.14"
|
||||
},
|
||||
"dependencies": {
|
||||
"@iconify/json": "^2.2.235",
|
||||
"@iconify/json": "^2.2.238",
|
||||
"@simonwep/pickr": "^1.9.1",
|
||||
"@vuepress/bundler-vite": "2.0.0-rc.14",
|
||||
"chart.js": "^4.4.3",
|
||||
@ -20,7 +20,7 @@
|
||||
"flowchart.ts": "^3.0.0",
|
||||
"http-server": "^14.1.1",
|
||||
"mermaid": "^10.9.1",
|
||||
"vue": "^3.4.37",
|
||||
"vue": "^3.4.38",
|
||||
"vuepress-theme-plume": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
12
package.json
12
package.json
@ -3,7 +3,7 @@
|
||||
"type": "module",
|
||||
"version": "1.0.0-rc.86",
|
||||
"private": true,
|
||||
"packageManager": "pnpm@9.7.0",
|
||||
"packageManager": "pnpm@9.7.1",
|
||||
"author": "pengzhanbo <q942450674@outlook.com> (https://github.com/pengzhanbo/)",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
@ -44,17 +44,17 @@
|
||||
"@types/lodash.merge": "^4.6.9",
|
||||
"@types/node": "20.12.10",
|
||||
"@types/webpack-env": "^1.18.5",
|
||||
"bumpp": "^9.4.2",
|
||||
"bumpp": "^9.5.1",
|
||||
"commitizen": "^4.3.0",
|
||||
"conventional-changelog-cli": "^5.0.0",
|
||||
"cpx2": "^7.0.1",
|
||||
"cz-conventional-changelog": "^3.3.0",
|
||||
"eslint": "^9.8.0",
|
||||
"eslint": "^9.9.0",
|
||||
"husky": "^9.1.4",
|
||||
"lint-staged": "^15.2.8",
|
||||
"lint-staged": "^15.2.9",
|
||||
"rimraf": "^6.0.1",
|
||||
"stylelint": "^16.8.1",
|
||||
"tsconfig-vuepress": "^4.5.0",
|
||||
"stylelint": "^16.8.2",
|
||||
"tsconfig-vuepress": "^5.0.0",
|
||||
"tsup": "^8.2.4",
|
||||
"typescript": "^5.5.4",
|
||||
"wait-on": "^7.2.0"
|
||||
|
||||
@ -40,7 +40,7 @@
|
||||
"vuepress": "2.0.0-rc.14"
|
||||
},
|
||||
"dependencies": {
|
||||
"vue": "^3.4.37"
|
||||
"vue": "^3.4.38"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
@ -41,13 +41,13 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@vuepress/helper": "2.0.0-rc.40",
|
||||
"@vueuse/core": "^10.11.1",
|
||||
"@vueuse/core": "^11.0.0",
|
||||
"markdown-it-container": "^4.0.0",
|
||||
"nanoid": "^5.0.7",
|
||||
"shiki": "^1.12.1",
|
||||
"tm-grammars": "^1.16.3",
|
||||
"tm-themes": "^1.6.2",
|
||||
"vue": "^3.4.37"
|
||||
"shiki": "^1.14.1",
|
||||
"tm-grammars": "^1.17.2",
|
||||
"tm-themes": "^1.7.1",
|
||||
"vue": "^3.4.38"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/markdown-it": "^14.1.2"
|
||||
|
||||
@ -41,14 +41,14 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@vuepress/helper": "2.0.0-rc.40",
|
||||
"@vueuse/core": "^10.11.1",
|
||||
"@vueuse/integrations": "^10.11.1",
|
||||
"@vueuse/core": "^11.0.0",
|
||||
"@vueuse/integrations": "^11.0.0",
|
||||
"chokidar": "^3.6.0",
|
||||
"focus-trap": "^7.5.4",
|
||||
"mark.js": "^8.11.1",
|
||||
"minisearch": "^7.1.0",
|
||||
"p-map": "^7.0.2",
|
||||
"vue": "^3.4.37"
|
||||
"vue": "^3.4.38"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
@ -36,17 +36,17 @@
|
||||
"vuepress": "2.0.0-rc.14"
|
||||
},
|
||||
"dependencies": {
|
||||
"@shikijs/transformers": "^1.12.1",
|
||||
"@shikijs/twoslash": "^1.12.1",
|
||||
"@shikijs/transformers": "^1.14.1",
|
||||
"@shikijs/twoslash": "^1.14.1",
|
||||
"@types/hast": "^3.0.4",
|
||||
"@vuepress/helper": "2.0.0-rc.40",
|
||||
"@vueuse/core": "^10.11.1",
|
||||
"@vueuse/core": "^11.0.0",
|
||||
"floating-vue": "^5.2.2",
|
||||
"mdast-util-from-markdown": "^2.0.1",
|
||||
"mdast-util-gfm": "^3.0.0",
|
||||
"mdast-util-to-hast": "^13.2.0",
|
||||
"nanoid": "^5.0.7",
|
||||
"shiki": "^1.12.1",
|
||||
"shiki": "^1.14.1",
|
||||
"twoslash": "^0.2.9",
|
||||
"twoslash-vue": "^0.2.9"
|
||||
},
|
||||
|
||||
1349
pnpm-lock.yaml
generated
1349
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -91,24 +91,24 @@
|
||||
"@vuepress/plugin-seo": "2.0.0-rc.40",
|
||||
"@vuepress/plugin-sitemap": "2.0.0-rc.40",
|
||||
"@vuepress/plugin-watermark": "2.0.0-rc.40",
|
||||
"@vueuse/core": "^10.11.1",
|
||||
"@vueuse/core": "^11.0.0",
|
||||
"bcrypt-ts": "^5.0.2",
|
||||
"chokidar": "^3.6.0",
|
||||
"create-filter": "^1.1.0",
|
||||
"date-fns": "^3.6.0",
|
||||
"esbuild": "^0.23.0",
|
||||
"esbuild": "^0.23.1",
|
||||
"fast-glob": "^3.3.2",
|
||||
"gray-matter": "^4.0.3",
|
||||
"json2yaml": "^1.1.0",
|
||||
"katex": "^0.16.11",
|
||||
"local-pkg": "^0.5.0",
|
||||
"nanoid": "^5.0.7",
|
||||
"vue": "^3.4.37",
|
||||
"vue": "^3.4.38",
|
||||
"vue-router": "^4.4.3",
|
||||
"vuepress-plugin-md-enhance": "2.0.0-rc.52",
|
||||
"vuepress-plugin-md-power": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iconify/json": "^2.2.235"
|
||||
"@iconify/json": "^2.2.238"
|
||||
}
|
||||
}
|
||||
|
||||
@ -86,7 +86,6 @@ watchPostEffect(() => {
|
||||
.vp-navbar.screen-open {
|
||||
background-color: var(--vp-nav-bg-color);
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.vp-navbar:not(.home) {
|
||||
|
||||
@ -26,7 +26,15 @@ const hasComments = computed(() => {
|
||||
return page.value.frontmatter.comments !== false && isPageDecrypted.value
|
||||
})
|
||||
|
||||
const enableAside = computed(() => hasAside.value && headers.value.length)
|
||||
const enableAside = computed(() => {
|
||||
if (!hasAside.value)
|
||||
return false
|
||||
|
||||
if (isBlogPost.value)
|
||||
return headers.value.length > 0
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
const pageName = computed(() =>
|
||||
route.path.replace(/[./]+/g, '_').replace(/_html$/, ''),
|
||||
|
||||
@ -1,5 +1,41 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useMediaQuery } from '@vueuse/core'
|
||||
|
||||
const props = defineProps<{
|
||||
cols?: string | number | { sm?: number, md?: number, lg?: number }
|
||||
}>()
|
||||
|
||||
const md = useMediaQuery('(min-width: 768px)')
|
||||
const lg = useMediaQuery('(min-width: 960px)')
|
||||
|
||||
const cols = computed(() => {
|
||||
const reset = { sm: 1, md: 2, lg: 2 }
|
||||
if (!props.cols)
|
||||
return reset
|
||||
if (typeof props.cols === 'number' || typeof props.cols === 'string') {
|
||||
const cols = Number(props.cols)
|
||||
return { sm: cols, md: cols, lg: cols }
|
||||
}
|
||||
return { ...reset, ...props.cols }
|
||||
})
|
||||
|
||||
const repeat = computed(() => {
|
||||
if (lg.value)
|
||||
return cols.value.lg
|
||||
else if (md.value)
|
||||
return cols.value.md
|
||||
else
|
||||
return cols.value.sm
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="vp-card-grid">
|
||||
<div
|
||||
class="vp-card-grid" :class="[`cols-${repeat}`]" :style="{
|
||||
gridTemplateColumns: `repeat(${repeat}, 1fr)`,
|
||||
}"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
@ -14,10 +50,4 @@
|
||||
.vp-card-grid > * {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.vp-card-grid {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
191
theme/src/client/components/global/VPImageCard.vue
Normal file
191
theme/src/client/components/global/VPImageCard.vue
Normal file
@ -0,0 +1,191 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { usePageLang } from 'vuepress/client'
|
||||
|
||||
const props = defineProps<{
|
||||
image: string
|
||||
title?: string
|
||||
description?: string
|
||||
href?: string
|
||||
author?: string
|
||||
date?: string | Date | number
|
||||
}>()
|
||||
|
||||
const lang = usePageLang()
|
||||
|
||||
const title = computed(() => {
|
||||
if (props.title)
|
||||
return props.title
|
||||
const image = props.image || ''
|
||||
const dirs = image.split('/')
|
||||
return dirs[dirs.length - 1]
|
||||
})
|
||||
|
||||
const date = computed(() => {
|
||||
if (!props.date)
|
||||
return ''
|
||||
const date = props.date instanceof Date ? props.date : new Date(props.date)
|
||||
const intl = new Intl.DateTimeFormat(
|
||||
lang.value,
|
||||
{ year: 'numeric', month: 'short', day: 'numeric' },
|
||||
)
|
||||
|
||||
return intl.format(date)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="vp-image-card">
|
||||
<div class="image-container">
|
||||
<img :src="image" :alt="title" loading="lazy">
|
||||
<div class="image-info">
|
||||
<h3 class="title">
|
||||
<a v-if="href" :href="href" target="_blank" rel="noopener noreferrer">{{ title }}</a>
|
||||
<span v-else>{{ title }}</span>
|
||||
</h3>
|
||||
<p v-if="author || date" class="copyright">
|
||||
<span v-if="author">{{ author }}</span>
|
||||
<span v-if="author && date"> | </span>
|
||||
<span v-if="date">{{ date }}</span>
|
||||
</p>
|
||||
<p v-if="description" class="description">
|
||||
{{ description }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.vp-image-card {
|
||||
margin: 16px 0;
|
||||
box-shadow: var(--vp-shadow-2);
|
||||
transition: var(--t-color);
|
||||
transition-property: box-shadow;
|
||||
}
|
||||
|
||||
.vp-image-card:hover {
|
||||
box-shadow: var(--vp-shadow-4);
|
||||
}
|
||||
|
||||
.vp-image-card .image-container {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
font-size: 0;
|
||||
line-height: 1;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.image-info {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
max-height: 100%;
|
||||
padding: 16px 20px 0;
|
||||
overflow-y: hidden;
|
||||
font-size: 14px;
|
||||
color: var(--vp-c-white);
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
border-top-left-radius: 8px;
|
||||
border-top-right-radius: 8px;
|
||||
transition: transform var(--t-color);
|
||||
transform: translateY(calc(100% - 60px));
|
||||
}
|
||||
|
||||
:where(.vp-card-grid.cols-3) .image-info {
|
||||
padding: 8px 8px 0;
|
||||
font-size: 12px;
|
||||
transform: translateY(calc(100% - 36px));
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
:where(.vp-card-grid.cols-2) .image-info {
|
||||
padding: 8px 8px 0;
|
||||
font-size: 12px;
|
||||
transform: translateY(calc(100% - 36px));
|
||||
}
|
||||
}
|
||||
|
||||
.image-info:hover {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.image-info .title {
|
||||
min-height: 28px;
|
||||
margin: 0 0 16px;
|
||||
overflow: hidden;
|
||||
font-size: 18px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
:where(.vp-card-grid.cols-3) .image-info .title {
|
||||
min-height: 20px;
|
||||
margin: 0 0 8px;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
:where(.vp-card-grid.cols-2) .image-info .title {
|
||||
min-height: 20px;
|
||||
margin: 0 0 8px;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.image-info .title a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.image-info p {
|
||||
margin: 0;
|
||||
line-height: 24px;
|
||||
color: var(--vp-c-white);
|
||||
}
|
||||
|
||||
:where(.vp-card-grid.cols-3) .image-info p {
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
:where(.vp-card-grid.cols-2) .image-info p {
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.image-info p:last-child {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.image-info .copyright {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.image-info .copyright span:first-child {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-align: right;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.image-info .description {
|
||||
flex: 1;
|
||||
height: 1px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.image-info .description::-webkit-scrollbar {
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
</style>
|
||||
@ -5,6 +5,7 @@ import VPCard from '@theme/global/VPCard.vue'
|
||||
import VPLinkCard from '@theme/global/VPLinkCard.vue'
|
||||
import VPBadge from '@theme/global/VPBadge.vue'
|
||||
import VPCardGrid from '@theme/global/VPCardGrid.vue'
|
||||
import VPImageCard from '@theme/global/VPImageCard.vue'
|
||||
import VPIcon from '@theme/VPIcon.vue'
|
||||
|
||||
export function globalComponents(app: App) {
|
||||
@ -20,6 +21,9 @@ export function globalComponents(app: App) {
|
||||
app.component('VPLinkCard', VPLinkCard)
|
||||
app.component('LinkCard', VPLinkCard)
|
||||
|
||||
app.component('VPImageCard', VPImageCard)
|
||||
app.component('ImageCard', VPImageCard)
|
||||
|
||||
app.component('DocSearch', () => {
|
||||
const SearchComponent
|
||||
= app.component('Docsearch') || app.component('SearchBox')
|
||||
@ -40,7 +44,6 @@ export function globalComponents(app: App) {
|
||||
app.component('Icon', VPIcon)
|
||||
app.component('VPIcon', VPIcon)
|
||||
|
||||
/** @deprecated */
|
||||
app.component('HomeBox', VPHomeBox)
|
||||
app.component('VPHomeBox', VPHomeBox)
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user