mirror of
https://github.com/pengzhanbo/vuepress-theme-plume.git
synced 2026-04-23 10:58:13 +08:00
feat(theme): add support for article copyright (#345)
This commit is contained in:
parent
bdcb49661c
commit
6e037dcdc0
@ -113,6 +113,7 @@ export const themeGuide = defineNoteConfig({
|
||||
'加密',
|
||||
'文章贡献者',
|
||||
'文章变更历史',
|
||||
'文章版权所有',
|
||||
'文章水印',
|
||||
'友情链接页',
|
||||
'seo',
|
||||
|
||||
@ -199,6 +199,16 @@ permalink: /config/frontmatter/basic/
|
||||
|
||||
当前页面是否显示页面变更历史。
|
||||
|
||||
### copyright
|
||||
|
||||
- 类型: `boolean | CopyrightLicense | CopyrightFrontmatter`
|
||||
- 默认值: `false`
|
||||
- 详情:
|
||||
|
||||
当前文章是否 显示 版权信息。
|
||||
|
||||
完整信息请查看 [copyright](../../guide/功能/文章版权所有.md)
|
||||
|
||||
### editLink
|
||||
|
||||
- 类型: `boolean`
|
||||
|
||||
@ -838,6 +838,50 @@ interface SidebarItem {
|
||||
- 默认值: `'View All Changelog'`
|
||||
- 详情: 变更记录的按钮文本
|
||||
|
||||
### copyright
|
||||
|
||||
- 类型: `boolean | CopyrightLicense | CopyrightOptions`
|
||||
- 默认值: `false`
|
||||
- 详情: 版权配置
|
||||
|
||||
详情请参考 [版权所有](../guide/功能/文章版权所有.md)
|
||||
|
||||
### copyrightText
|
||||
|
||||
- 类型: `string`
|
||||
- 默认值: `'Copyright'`
|
||||
- 详情: 版权所有的文本
|
||||
|
||||
### copyrightAuthorText
|
||||
|
||||
- 类型: `string`
|
||||
- 默认值: `'Copyright Ownership:'`
|
||||
- 详情: 版权所有者的文本
|
||||
|
||||
### copyrightCreationOriginalText
|
||||
|
||||
- 类型: `string`
|
||||
- 默认值: `'This article link:'`
|
||||
- 详情: 本文链接的文本
|
||||
|
||||
### copyrightCreationTranslateText
|
||||
|
||||
- 类型: `string`
|
||||
- 默认值: `'This article translated from:'`
|
||||
- 详情: 本文翻译的文本
|
||||
|
||||
### copyrightCreationReprintText
|
||||
|
||||
- 类型: `string`
|
||||
- 默认值: `'This article reprint from:'`
|
||||
- 详情: 本文转载的文本
|
||||
|
||||
### copyrightLicenseText
|
||||
|
||||
- 类型: `string`
|
||||
- 默认值: `'License under:'`
|
||||
- 详情: 版权许可证的文本
|
||||
|
||||
### prevPage
|
||||
|
||||
- 类型: `boolean`
|
||||
|
||||
254
docs/notes/theme/guide/功能/文章版权所有.md
Normal file
254
docs/notes/theme/guide/功能/文章版权所有.md
Normal file
@ -0,0 +1,254 @@
|
||||
---
|
||||
title: 文章版权所有
|
||||
icon: lucide:creative-commons
|
||||
badge:
|
||||
type: tip
|
||||
text: v1.0.0-rc.118 +
|
||||
createTime: 2024/11/20 10:52:49
|
||||
permalink: /guide/features/copyright/
|
||||
---
|
||||
|
||||
<script setup>
|
||||
import VPCopyright from '@theme/VPCopyright.vue'
|
||||
</script>
|
||||
|
||||
## 概述
|
||||
|
||||
主题支持为文章添加 文章 **版权所有** 声明。
|
||||
|
||||
文章通常来源于 原创、转载、翻译等。针对于不同的来源,添加版权声明信息能够更好地保护知识产权,
|
||||
以及避免产生版权纠纷。
|
||||
|
||||
### Creative Commons
|
||||
|
||||
主题默认支持 [Creative Commons](https://creativecommons.org/) 许可协议的版权声明,包括:
|
||||
|
||||
<style>
|
||||
.doc-cc-list [class^="vpi-license-"] {
|
||||
margin-left: 8px;
|
||||
width: 1.4em;
|
||||
height: 1.4em;
|
||||
color: var(--vp-c-text-2);
|
||||
transition: color var(--vp-t-color);
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="doc-cc-list">
|
||||
|
||||
- [CC0 1.0 通用 (CC0)](https://creativecommons.org/publicdomain/zero/1.0/)
|
||||
<span class="vpi-license-zero" />
|
||||
- [署名 4.0 国际 (CC-BY-4.0)](https://creativecommons.org/licenses/by/4.0/)
|
||||
<span class="vpi-license-cc" /><span class="vpi-license-by" />
|
||||
- [署名-相同方式共享 4.0 国际 (CC-BY-SA-4.0)](https://creativecommons.org/licenses/by-sa/4.0/)
|
||||
<span class="vpi-license-cc" /><span class="vpi-license-by" /><span class="vpi-license-sa" />
|
||||
- [署名-非商业性 4.0 国际 (CC-BY-NC-4.0)](https://creativecommons.org/licenses/by-nc/4.0/)
|
||||
<span class="vpi-license-cc" /><span class="vpi-license-by" /><span class="vpi-license-nc" />
|
||||
- [署名-禁止演绎 4.0 国际 (CC-BY-ND-4.0)](https://creativecommons.org/licenses/by-nd/4.0/)
|
||||
<span class="vpi-license-cc" /><span class="vpi-license-by" /><span class="vpi-license-nd" />
|
||||
- [署名-非商业性-相同方式共享 4.0 国际 (CC-BY-NC-SA-4.0)](https://creativecommons.org/licenses/by-nc-sa/4.0/)
|
||||
<span class="vpi-license-cc" /><span class="vpi-license-by" /><span class="vpi-license-nc" /><span class="vpi-license-sa" />
|
||||
- [署名-非商业性-禁止演绎 4.0 国际 (CC-BY-NC-ND-4.0)](https://creativecommons.org/licenses/by-nc-nd/4.0/)
|
||||
<span class="vpi-license-cc" /><span class="vpi-license-by" /><span class="vpi-license-nc" /><span class="vpi-license-nd" />
|
||||
|
||||
</div>
|
||||
|
||||
您可以根据需要选择不同的许可协议,或者自定义许可协议。
|
||||
|
||||
### 版权信息
|
||||
|
||||
版权信息包括:
|
||||
|
||||
- 版权所有者,版权所有者链接
|
||||
- 版权许可证,版权许可证链接
|
||||
- 作品原文链接
|
||||
|
||||
这些信息将显示在文章的底部。
|
||||
|
||||
::: tip 使用此功能建议同时启用 [贡献者](./文章贡献者.md) 功能。对于原创文章,主题会自动将文章的第一位贡献者作为版权所有者。你也可以在文章 frontmatter 中手动指定版权所有者。
|
||||
:::
|
||||
|
||||
## 全局配置
|
||||
|
||||
您可以通过以下配置为您的站点的所有文章,声明版权许可证为 `CC-BY-4.0`:
|
||||
|
||||
::: code-tabs
|
||||
@tab .vuepress/config.ts
|
||||
|
||||
```ts
|
||||
import { defineUserConfig } from 'vuepress'
|
||||
import { plumeTheme } from 'vuepress-theme-plume'
|
||||
|
||||
export default defineUserConfig({
|
||||
theme: plumeTheme({
|
||||
copyright: 'CC-BY-4.0' // [!code hl]
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
您可以通过以下配置为您的站点的所有文章 声明自定义的版权许可证:
|
||||
|
||||
::: code-tabs
|
||||
@tab .vuepress/config.ts
|
||||
|
||||
```ts :no-line-numbers
|
||||
import { defineUserConfig } from 'vuepress'
|
||||
import { plumeTheme } from 'vuepress-theme-plume'
|
||||
|
||||
export default defineUserConfig({
|
||||
theme: plumeTheme({
|
||||
copyright: { // [!code hl:6]
|
||||
license: {
|
||||
name: 'MIT', // 许可证名称
|
||||
url: 'https://your-license-url' // 许可证地址
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
**配置类型:**
|
||||
|
||||
```ts
|
||||
export type CopyrightLicense =
|
||||
| 'CC-BY-4.0'
|
||||
| 'CC-BY-SA-4.0'
|
||||
| 'CC-BY-NC-4.0'
|
||||
| 'CC-BY-NC-SA-4.0'
|
||||
| 'CC-BY-ND-4.0'
|
||||
| 'CC-BY-NC-ND-4.0'
|
||||
| 'CC0'
|
||||
| string
|
||||
|
||||
/**
|
||||
* - 配置为 `true` 时,默认为 `CC-BY-4.0`
|
||||
* - 配置为 `false` 时,不显示版权,但可以在文章 frontmatter.copyright 中覆盖配置
|
||||
*/
|
||||
type CopyrightOptions = boolean | string | CopyrightLicense | {
|
||||
/**
|
||||
* 许可证
|
||||
*/
|
||||
license: CopyrightLicense | {
|
||||
name: CopyrightLicense | string
|
||||
url: string
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
::: warning 全局配置只适用于 原创文章,对于非原创文章,您应该在文章 frontmatter 中配置版权信息。
|
||||
:::
|
||||
|
||||
## 文章 frontmatter 配置
|
||||
|
||||
您可以在文章 frontmatter 中为单个文章配置版权信息,以覆盖全局配置:
|
||||
|
||||
```md
|
||||
---
|
||||
title: 我的文章
|
||||
copyright: CC-BY-4.0
|
||||
---
|
||||
```
|
||||
|
||||
**配置类型:**
|
||||
|
||||
```ts
|
||||
/**
|
||||
* 配置为 `false` 时,不显示版权
|
||||
* 配置为 `true` 时,则默认为 全局配置的 copyright
|
||||
*/
|
||||
export type CopyrightFrontmatter = boolean | string | CopyrightLicense | {
|
||||
/**
|
||||
* 版权许可
|
||||
*/
|
||||
license?: CopyrightLicense | { name: string, url: string }
|
||||
|
||||
/**
|
||||
* 版权所有者
|
||||
* - 原创文章时默认为文章的第一位贡献者
|
||||
* - 非原创文章时需要声明版权所有者
|
||||
*/
|
||||
author?: string | { name: string, url?: string }
|
||||
|
||||
/**
|
||||
* 作品的创作方式, 原创、翻译、转载
|
||||
* @default 'original'
|
||||
*/
|
||||
creation?: 'original' | 'translate' | 'reprint'
|
||||
|
||||
/**
|
||||
* 原文地址,非原创作品时需要声明原文地址
|
||||
* @default ''
|
||||
*/
|
||||
source?: string
|
||||
}
|
||||
```
|
||||
|
||||
## 文章配置示例
|
||||
|
||||
### 原创文章
|
||||
|
||||
```md
|
||||
---
|
||||
title: 我的文章
|
||||
copyright: CC-BY-4.0
|
||||
---
|
||||
```
|
||||
|
||||
<VPCopyright license="CC-BY-4.0" />
|
||||
|
||||
### 转载文章
|
||||
|
||||
```md
|
||||
---
|
||||
title: 转载的文章
|
||||
copyright:
|
||||
creation: reprint
|
||||
license: CC-BY-4.0
|
||||
source: https://example.com/origin
|
||||
author:
|
||||
name: 转载者
|
||||
url: https://example.com/author
|
||||
---
|
||||
```
|
||||
|
||||
<VPCopyright
|
||||
license="CC-BY-4.0" source="https://example.com/origin" creation="reprint"
|
||||
:author="{name: '转载者', url: 'https://example.com/author'}"
|
||||
/>
|
||||
|
||||
### 翻译文章
|
||||
|
||||
```md
|
||||
---
|
||||
title: 翻译的文章
|
||||
copyright:
|
||||
creation: translate
|
||||
license: CC-BY-4.0
|
||||
source: https://example.com/origin
|
||||
author:
|
||||
name: 原文作者
|
||||
url: https://example.com/author
|
||||
---
|
||||
```
|
||||
|
||||
<VPCopyright
|
||||
license="CC-BY-4.0" source="https://example.com/origin" creation="translate"
|
||||
:author="{name: '原文作者', url: 'https://example.com/author'}"
|
||||
/>
|
||||
|
||||
### 自定义许可证
|
||||
|
||||
```md
|
||||
---
|
||||
title: 我的文章
|
||||
copyright:
|
||||
license:
|
||||
name: MIT
|
||||
url: https://example.com/mit
|
||||
---
|
||||
```
|
||||
|
||||
<VPCopyright :license="{name: 'MIT', url: 'https://example.com/mit'}" />
|
||||
92
theme/src/client/components/VPCopyright.vue
Normal file
92
theme/src/client/components/VPCopyright.vue
Normal file
@ -0,0 +1,92 @@
|
||||
<script setup lang="ts">
|
||||
import type { CopyrightFrontmatter } from '../../shared/index.js'
|
||||
import VPLink from '@theme/VPLink.vue'
|
||||
import { computed } from 'vue'
|
||||
import { useCopyright, useData } from '../composables/index.js'
|
||||
|
||||
const props = defineProps<CopyrightFrontmatter>()
|
||||
|
||||
const { theme } = useData()
|
||||
const {
|
||||
author,
|
||||
creation,
|
||||
creationText,
|
||||
license,
|
||||
sourceUrl,
|
||||
} = useCopyright(computed(() => props))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="hint-container tip copyright-container">
|
||||
<p v-if="author">
|
||||
<span>{{ theme.copyrightAuthorText || 'Copyright Ownership:' }}</span>
|
||||
<VPLink :href="author.url" no-icon>
|
||||
{{ author.name }}
|
||||
</VPLink>
|
||||
</p>
|
||||
<p v-if="sourceUrl">
|
||||
<span>{{ creationText }}</span>
|
||||
<VPLink :href="sourceUrl" :no-icon="creation === 'original'" data-allow-mismatch>
|
||||
{{ sourceUrl }}
|
||||
</VPLink>
|
||||
</p>
|
||||
<p v-if="license">
|
||||
<span>{{ theme.copyrightLicenseText || 'License under' }}</span>
|
||||
<VPLink :href="license.url" no-icon>
|
||||
{{ license.name }}
|
||||
</VPLink>
|
||||
<template v-if="license.icons">
|
||||
<span v-for="icon in license.icons" :key="icon" :class="`vpi-license-${icon}`" />
|
||||
</template>
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.vp-doc .copyright-container p {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
gap: 4px;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
margin: 8px 0;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.vp-doc .copyright-container p span:first-of-type {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.vp-doc .copyright-container [class*="vpi-"] {
|
||||
width: 1.2em;
|
||||
height: 1.2em;
|
||||
color: var(--vp-c-text-2);
|
||||
transition: color var(--vp-t-color);
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
.vpi-license-cc {
|
||||
--icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1em' height='1em' viewBox='0 0 24 24'%3E%3Cpath fill='%23000' d='M9 8c1.104 0 2.105.448 2.829 1.173l-1.414 1.413a2 2 0 1 0 0 2.828l1.413 1.414A4.001 4.001 0 0 1 5 12c0-2.208 1.792-4 4-4m9.829 1.173A4.001 4.001 0 0 0 12 12a4.001 4.001 0 0 0 6.828 2.828l-1.414-1.414a2 2 0 1 1 0-2.828zM2 12C2 6.477 6.477 2 12 2s10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12m10-8a8 8 0 1 0 0 16a8 8 0 0 0 0-16'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.vpi-license-by {
|
||||
--icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1em' height='1em' viewBox='0 0 24 24'%3E%3Cpath fill='%23000' d='M14 7a2 2 0 1 1-4 0a2 2 0 0 1 4 0m1 4a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v4h1.5v4h3v-4H15zm-3-9C6.477 2 2 6.477 2 12s4.477 10 10 10s10-4.477 10-10S17.523 2 12 2M4 12a8 8 0 1 1 16 0a8 8 0 0 1-16 0'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.vpi-license-nc {
|
||||
--icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1em' height='1em' viewBox='0 0 24 24'%3E%3Cpath fill='%23000' d='M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10s10-4.477 10-10S17.523 2 12 2M7.094 5.68A8 8 0 0 1 18.32 16.905l-2.154-2.154A2.5 2.5 0 0 0 14 11h-4a.5.5 0 0 1 0-1.001h5.5V8H13V6h-2v2h-1q-.273.001-.53.056zM5.68 7.094L7.835 9.25A2.5 2.5 0 0 0 10 13h4a.5.5 0 0 1 0 1.001H8.5v2H11v2h2v-2h1q.273-.001.53-.056l2.376 2.376A8 8 0 0 1 5.68 7.095'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.vpi-license-nd {
|
||||
--icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1em' height='1em' viewBox='0 0 24 24'%3E%3Cpath fill='%23000' d='M8 9h8v2H8zm0 6v-2h8v2zm-6-3C2 6.477 6.477 2 12 2s10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12m10-8a8 8 0 1 0 0 16a8 8 0 0 0 0-16'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.vpi-license-sa {
|
||||
--icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1em' height='1em' viewBox='0 0 24 24'%3E%3Cpath fill='%23000' d='M17 12c0-3.314-2.238-6-5-6c-2.177 0-4.03 1.67-4.716 4H6l2.5 3l2.5-3H9.401C9.92 8.805 10.89 8 12 8c1.657 0 3 1.79 3 4s-1.343 4-3 4c-1.11 0-2.08-.804-2.598-1.999H7.285C7.97 16.33 9.823 18 12 18c2.762 0 5-2.686 5-6M12 2a9.97 9.97 0 0 0-7.07 2.93A9.97 9.97 0 0 0 2 12a9.97 9.97 0 0 0 2.93 7.07A9.97 9.97 0 0 0 12 22a9.97 9.97 0 0 0 7.07-2.93A9.97 9.97 0 0 0 22 12a9.97 9.97 0 0 0-2.93-7.07A9.97 9.97 0 0 0 12 2M6.344 6.344A7.97 7.97 0 0 1 12 4c2.208 0 4.206.895 5.656 2.344A7.97 7.97 0 0 1 20 12a7.97 7.97 0 0 1-2.344 5.656A7.97 7.97 0 0 1 12 20a7.97 7.97 0 0 1-5.656-2.344A7.97 7.97 0 0 1 4 12c0-2.208.895-4.206 2.344-5.656'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.vpi-license-zero {
|
||||
--icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1em' height='1em' viewBox='0 0 24 24'%3E%3Cpath fill='%23000' d='M17 12c0-3.314-2.238-6-5-6s-5 2.686-5 6s2.239 6 5 6s5-2.686 5-6m-6.237 3.645l3.562-6.173c.422.69.675 1.57.675 2.528c0 2.21-1.343 4-3 4c-.441 0-.86-.127-1.237-.355M9 12c0-2.21 1.343-4 3-4c.441 0 .86.127 1.237.355l-3.562 6.173C9.253 13.838 9 12.958 9 12m3-10a9.97 9.97 0 0 0-7.07 2.93A9.97 9.97 0 0 0 2 12a9.97 9.97 0 0 0 2.93 7.07A9.97 9.97 0 0 0 12 22a9.97 9.97 0 0 0 7.07-2.93A9.97 9.97 0 0 0 22 12a9.97 9.97 0 0 0-2.93-7.07A9.97 9.97 0 0 0 12 2M6.344 6.344A7.97 7.97 0 0 1 12 4c2.208 0 4.206.895 5.656 2.344A7.97 7.97 0 0 1 20 12a7.97 7.97 0 0 1-2.344 5.656A7.97 7.97 0 0 1 12 20a7.97 7.97 0 0 1-5.656-2.344A7.97 7.97 0 0 1 4 12c0-2.208.895-4.206 2.344-5.656'/%3E%3C/svg%3E");
|
||||
}
|
||||
</style>
|
||||
@ -3,6 +3,7 @@ import VPDocAside from '@theme/VPDocAside.vue'
|
||||
import VPDocBreadcrumbs from '@theme/VPDocBreadcrumbs.vue'
|
||||
import VPDocChangelog from '@theme/VPDocChangelog.vue'
|
||||
import VPDocContributor from '@theme/VPDocContributor.vue'
|
||||
import VPDocCopyright from '@theme/VPDocCopyright.vue'
|
||||
import VPDocFooter from '@theme/VPDocFooter.vue'
|
||||
import VPDocMeta from '@theme/VPDocMeta.vue'
|
||||
import VPEncryptPage from '@theme/VPEncryptPage.vue'
|
||||
@ -129,6 +130,7 @@ watch(
|
||||
<Content />
|
||||
<VPDocContributor />
|
||||
<VPDocChangelog />
|
||||
<VPDocCopyright />
|
||||
</div>
|
||||
</main>
|
||||
<VPDocFooter v-if="isPageDecrypted">
|
||||
|
||||
@ -101,6 +101,12 @@ function resolveSidebar(
|
||||
transition: border-left var(--vp-t-color);
|
||||
}
|
||||
|
||||
@media print {
|
||||
.vp-breadcrumb {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.vp-breadcrumb ol {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
@ -69,6 +69,12 @@ const hasChangelog = computed(() =>
|
||||
display: block;
|
||||
}
|
||||
|
||||
@media print {
|
||||
.vp-doc-changelog {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.vp-doc-changelog .changelog-header > span {
|
||||
display: block;
|
||||
}
|
||||
|
||||
40
theme/src/client/components/VPDocCopyright.vue
Normal file
40
theme/src/client/components/VPDocCopyright.vue
Normal file
@ -0,0 +1,40 @@
|
||||
<script setup lang="ts">
|
||||
import type { CopyrightFrontmatter } from '../../shared/index.js'
|
||||
import VPCopyright from '@theme/VPCopyright.vue'
|
||||
import VPDocHeader from '@theme/VPDocHeader.vue'
|
||||
import { computed } from 'vue'
|
||||
import { isPlainObject } from 'vuepress/shared'
|
||||
import { useData } from '../composables/index.js'
|
||||
|
||||
const { theme, frontmatter } = useData()
|
||||
|
||||
const copyright = computed<CopyrightFrontmatter | null>(() => {
|
||||
if ((frontmatter.value.copyright ?? theme.value.copyright ?? false) === false) {
|
||||
return null
|
||||
}
|
||||
const docCopyright = (isPlainObject(frontmatter.value.copyright)
|
||||
? frontmatter.value.copyright
|
||||
: { license: frontmatter.value.copyright === true ? '' : frontmatter.value.copyright }) as CopyrightFrontmatter
|
||||
if (!theme.value.copyright)
|
||||
return docCopyright
|
||||
|
||||
const themeCopyright = (isPlainObject(theme.value.copyright)
|
||||
? theme.value.copyright
|
||||
: { license: theme.value.copyright === true ? '' : theme.value.copyright }) as CopyrightFrontmatter
|
||||
|
||||
return {
|
||||
...themeCopyright,
|
||||
...docCopyright,
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="copyright" class="vp-doc-copyright">
|
||||
<VPDocHeader anchor="doc-copyright">
|
||||
{{ theme.copyrightText || 'Copyright' }}
|
||||
</VPDocHeader>
|
||||
|
||||
<VPCopyright v-bind="copyright" />
|
||||
</div>
|
||||
</template>
|
||||
113
theme/src/client/composables/copyright.ts
Normal file
113
theme/src/client/composables/copyright.ts
Normal file
@ -0,0 +1,113 @@
|
||||
import type { CopyrightFrontmatter, CopyrightLicense, CopyrightOptions, GitContributor, KnownCopyrightLicense } from '../../shared/index.js'
|
||||
import { computed, type ComputedRef } from 'vue'
|
||||
import { useRoute, useRouteLocale } from 'vuepress/client'
|
||||
import { useContributors } from './contributors.js'
|
||||
import { useData } from './data.js'
|
||||
import { getPresetLocaleData } from './preset-locales.js'
|
||||
|
||||
const LICENSE_URL: Record<KnownCopyrightLicense, { url: string, icons: string[] }> = {
|
||||
'CC0': {
|
||||
url: 'https://creativecommons.org/publicdomain/zero/1.0/',
|
||||
icons: ['zero'],
|
||||
},
|
||||
'CC-BY-4.0': {
|
||||
url: 'https://creativecommons.org/licenses/by/4.0/',
|
||||
icons: ['cc', 'by'],
|
||||
},
|
||||
'CC-BY-NC-4.0': {
|
||||
url: 'https://creativecommons.org/licenses/by-nc/4.0/',
|
||||
icons: ['cc', 'by', 'nc'],
|
||||
},
|
||||
'CC-BY-NC-SA-4.0': {
|
||||
url: 'https://creativecommons.org/licenses/by-nc-sa/4.0/',
|
||||
icons: ['cc', 'by', 'nc', 'sa'],
|
||||
},
|
||||
'CC-BY-NC-ND-4.0': {
|
||||
url: 'https://creativecommons.org/licenses/by-nc-nd/4.0/',
|
||||
icons: ['cc', 'by', 'nc', 'nd'],
|
||||
},
|
||||
'CC-BY-ND-4.0': {
|
||||
url: 'https://creativecommons.org/licenses/by-nd/4.0/',
|
||||
icons: ['cc', 'by', 'nd'],
|
||||
},
|
||||
'CC-BY-SA-4.0': {
|
||||
url: 'https://creativecommons.org/licenses/by-sa/4.0/',
|
||||
icons: ['cc', 'by', 'sa'],
|
||||
},
|
||||
}
|
||||
|
||||
export function useCopyright(copyright: ComputedRef<CopyrightFrontmatter>) {
|
||||
const { theme } = useData<'post'>()
|
||||
const route = useRoute()
|
||||
const routeLocale = useRouteLocale()
|
||||
const { contributors } = useContributors()
|
||||
|
||||
const hasCopyright = computed(() => Boolean(copyright.value))
|
||||
|
||||
const creation = computed(() => copyright.value.creation || 'original')
|
||||
|
||||
const license = computed(() => resolveLicense(copyright.value.license, routeLocale.value))
|
||||
|
||||
const author = computed(() => resolveAuthor(copyright.value.author, creation.value, contributors.value))
|
||||
|
||||
const sourceUrl = computed(() => {
|
||||
if (creation.value === 'original')
|
||||
return __VUEPRESS_SSR__ ? route.fullPath : location.href.split('#')[0]
|
||||
|
||||
return copyright.value.source
|
||||
})
|
||||
|
||||
const creationText = computed(() => {
|
||||
const creation = copyright.value.creation
|
||||
if (creation === 'translate') {
|
||||
return theme.value.copyrightCreationTranslateText || 'This article is translated from'
|
||||
}
|
||||
else if (creation === 'reprint') {
|
||||
return theme.value.copyrightCreationReprintText || 'This article is reprint from'
|
||||
}
|
||||
return theme.value.copyrightCreationOriginalText || 'This article link: '
|
||||
})
|
||||
|
||||
return { license, author, hasCopyright, creation, creationText, sourceUrl }
|
||||
}
|
||||
|
||||
interface License {
|
||||
name: string
|
||||
url?: string
|
||||
icons?: string[]
|
||||
}
|
||||
|
||||
function resolveLicense(
|
||||
license: CopyrightOptions['license'] = 'CC-BY-4.0',
|
||||
locale: string,
|
||||
): License {
|
||||
const result: License = typeof license === 'string' ? { name: license } : { ...license }
|
||||
const fallback = LICENSE_URL[result.name]
|
||||
const name = getPresetLocaleData(locale, result.name as CopyrightLicense)
|
||||
if (name) {
|
||||
result.name = `${name} (${result.name})`
|
||||
}
|
||||
result.url ||= fallback?.url
|
||||
result.icons = fallback?.icons
|
||||
return result
|
||||
}
|
||||
|
||||
function resolveAuthor(
|
||||
author: CopyrightFrontmatter['author'],
|
||||
creation: CopyrightFrontmatter['creation'],
|
||||
contributors: GitContributor[],
|
||||
): { name: string, url?: string } | undefined {
|
||||
const contributor = contributors[0]
|
||||
|
||||
const options = typeof author === 'string' ? { name: author } : author
|
||||
|
||||
if (options && !options.url) {
|
||||
const contributor = contributors.find(c => c.name === options.name)
|
||||
if (contributor)
|
||||
options.url = contributor.url
|
||||
}
|
||||
if (creation === 'original' && contributor)
|
||||
return contributor
|
||||
|
||||
return options
|
||||
}
|
||||
@ -7,6 +7,7 @@ export * from './blog-post-list.js'
|
||||
export * from './blog-tags.js'
|
||||
export * from './bulletin.js'
|
||||
export * from './contributors.js'
|
||||
export * from './copyright.js'
|
||||
export * from './dark-mode.js'
|
||||
export * from './data.js'
|
||||
export * from './edit-link.js'
|
||||
|
||||
@ -12,11 +12,17 @@ export const enLocale: PlumeThemeLocaleData = {
|
||||
editLinkText: 'Edit this page',
|
||||
contributorsText: 'Contributors',
|
||||
lastUpdatedText: 'Last Updated',
|
||||
|
||||
changelogText: 'Changelog',
|
||||
changelogOnText: 'On',
|
||||
changelogButtonText: 'View All Changelog',
|
||||
|
||||
copyrightText: 'Copyright',
|
||||
copyrightAuthorText: 'Copyright Ownership:',
|
||||
copyrightCreationOriginalText: 'This article link:',
|
||||
copyrightCreationTranslateText: 'This article is translated from:',
|
||||
copyrightCreationReprintText: 'This article is reprint from:',
|
||||
copyrightLicenseText: 'License under:',
|
||||
|
||||
encryptButtonText: 'Confirm',
|
||||
encryptPlaceholder: 'Enter password',
|
||||
encryptGlobalText: 'Only password can access this site',
|
||||
@ -29,12 +35,21 @@ export const enLocale: PlumeThemeLocaleData = {
|
||||
}
|
||||
|
||||
export const enPresetLocale: PresetLocale = {
|
||||
home: 'Home',
|
||||
blog: 'Blog',
|
||||
tag: 'Tags',
|
||||
archive: 'Archives',
|
||||
category: 'Categories',
|
||||
archiveTotal: '{count} articles',
|
||||
'home': 'Home',
|
||||
'blog': 'Blog',
|
||||
'tag': 'Tags',
|
||||
'archive': 'Archives',
|
||||
'category': 'Categories',
|
||||
'archiveTotal': '{count} articles',
|
||||
|
||||
// ------ copyright license ------
|
||||
'CC0': 'CC0 1.0 Universal',
|
||||
'CC-BY-4.0': 'Attribution 4.0 International',
|
||||
'CC-BY-NC-4.0': 'Attribution-NonCommercial 4.0 International',
|
||||
'CC-BY-NC-SA-4.0': 'Attribution-NonCommercial-ShareAlike 4.0 International',
|
||||
'CC-BY-NC-ND-4.0': 'Attribution-NonCommercial-NoDerivatives 4.0 International',
|
||||
'CC-BY-ND-4.0': 'Attribution-NoDerivatives 4.0 International',
|
||||
'CC-BY-SA-4.0': 'Attribution-ShareAlike 4.0 International',
|
||||
}
|
||||
|
||||
export const enSearchLocale: Partial<SearchLocaleOptions> = {
|
||||
|
||||
@ -22,6 +22,13 @@ export const zhLocale: PlumeThemeLocaleData = {
|
||||
changelogOnText: '于',
|
||||
changelogButtonText: '查看全部变更历史',
|
||||
|
||||
copyrightText: '版权所有',
|
||||
copyrightAuthorText: '版权归属于:',
|
||||
copyrightCreationOriginalText: '本文链接:',
|
||||
copyrightCreationTranslateText: '本文翻译自:',
|
||||
copyrightCreationReprintText: '本文转载自:',
|
||||
copyrightLicenseText: '许可证:',
|
||||
|
||||
notFound: {
|
||||
code: '404',
|
||||
title: '页面未找到',
|
||||
@ -41,12 +48,21 @@ export const zhLocale: PlumeThemeLocaleData = {
|
||||
}
|
||||
|
||||
export const zhPresetLocale: PresetLocale = {
|
||||
home: '首页',
|
||||
blog: '博客',
|
||||
tag: '标签',
|
||||
archive: '归档',
|
||||
category: '分类',
|
||||
archiveTotal: '{count} 篇',
|
||||
'home': '首页',
|
||||
'blog': '博客',
|
||||
'tag': '标签',
|
||||
'archive': '归档',
|
||||
'category': '分类',
|
||||
'archiveTotal': '{count} 篇',
|
||||
|
||||
// ------ copyright license ------
|
||||
'CC0': 'CC0 1.0 通用',
|
||||
'CC-BY-4.0': '署名 4.0 国际',
|
||||
'CC-BY-NC-4.0': '署名-非商业性 4.0 国际',
|
||||
'CC-BY-NC-SA-4.0': '署名-非商业性-相同方式共享 4.0 国际',
|
||||
'CC-BY-NC-ND-4.0': '署名-非商业性-禁止演绎 4.0 国际',
|
||||
'CC-BY-ND-4.0': '署名-禁止演绎 4.0 国际',
|
||||
'CC-BY-SA-4.0': '署名-相同方式共享 4.0 国际',
|
||||
}
|
||||
|
||||
export const zhDocsearchLocale: DocSearchLocaleOptions = {
|
||||
|
||||
@ -1,3 +1,12 @@
|
||||
/**
|
||||
* A literal type that supports custom further strings but preserves autocompletion in IDEs.
|
||||
*
|
||||
* @see [copied from issue](https://github.com/microsoft/TypeScript/issues/29729#issuecomment-471566609)
|
||||
*/
|
||||
export type LiteralUnion<Union extends Base, Base = string> =
|
||||
| Union
|
||||
| (Base & { zz_IGNORE_ME?: never })
|
||||
|
||||
export type ThemeImage =
|
||||
| string
|
||||
| { src: string, alt?: string }
|
||||
@ -41,7 +50,7 @@ export type SocialLinkIconUnion =
|
||||
|
||||
export type SocialLinkIcon = SocialLinkIconUnion | { svg: string, name?: string }
|
||||
|
||||
export interface PresetLocale {
|
||||
export interface PresetLocale extends Record<CopyrightLicense, string> {
|
||||
home: string
|
||||
blog: string
|
||||
tag: string
|
||||
@ -67,3 +76,45 @@ export interface ThemeTransition {
|
||||
*/
|
||||
appearance?: boolean | 'fade' | 'circle-clip' | 'horizontal-clip' | 'vertical-clip' | 'skew-clip'
|
||||
}
|
||||
|
||||
export type KnownCopyrightLicense =
|
||||
| 'CC-BY-4.0'
|
||||
| 'CC-BY-SA-4.0'
|
||||
| 'CC-BY-NC-4.0'
|
||||
| 'CC-BY-NC-SA-4.0'
|
||||
| 'CC-BY-ND-4.0'
|
||||
| 'CC-BY-NC-ND-4.0'
|
||||
| 'CC0'
|
||||
|
||||
export type CopyrightLicense = LiteralUnion<KnownCopyrightLicense>
|
||||
|
||||
export interface CopyrightOptions {
|
||||
/**
|
||||
* 版权信息
|
||||
* @see https://creativecommons.org/share-your-work/cclicenses/
|
||||
* @default 'CC-BY-4.0'
|
||||
*/
|
||||
license?: CopyrightLicense | { name: string, url: string }
|
||||
}
|
||||
|
||||
export interface CopyrightFrontmatter extends CopyrightOptions {
|
||||
/**
|
||||
* 作品的作者
|
||||
*
|
||||
* 如果是 原创,则默认为 contributors 中的第一个,否则需要手动指定
|
||||
* @default ''
|
||||
*/
|
||||
author?: string | { name: string, url?: string }
|
||||
|
||||
/**
|
||||
* 作品的创作方式
|
||||
* @default 'original'
|
||||
*/
|
||||
creation?: 'original' | 'translate' | 'reprint'
|
||||
|
||||
/**
|
||||
* 原文地址,非 原创 作品时需要声明原文地址
|
||||
* @default ''
|
||||
*/
|
||||
source?: string
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import type { CopyrightFrontmatter, CopyrightLicense } from '../base.js'
|
||||
import type { BlogPostCover } from '../blog.js'
|
||||
import type { PlumeThemePageFrontmatter } from './page.js'
|
||||
|
||||
@ -38,4 +39,9 @@ export interface PlumeThemePostFrontmatter extends PlumeThemePageFrontmatter {
|
||||
* 是否展示文章摘要,传入 string 时为自定义摘要,此时 `<!-- more -->` 无效
|
||||
*/
|
||||
excerpt?: boolean | string
|
||||
|
||||
/**
|
||||
* 版权信息
|
||||
*/
|
||||
copyright?: boolean | CopyrightLicense | CopyrightFrontmatter
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { LocaleData } from 'vuepress/core'
|
||||
import type { SocialLink, SocialLinkIconUnion, ThemeOutline, ThemeTransition } from '../base.js'
|
||||
import type { CopyrightLicense, CopyrightOptions, SocialLink, SocialLinkIconUnion, ThemeOutline, ThemeTransition } from '../base.js'
|
||||
import type { PlumeThemeBlog } from '../blog.js'
|
||||
import type { NavItem } from '../navbar.js'
|
||||
import type { NotesOptions } from '../notes.js'
|
||||
@ -115,6 +115,48 @@ export interface PlumeThemeLocaleData extends LocaleData {
|
||||
*/
|
||||
bulletin?: true | BulletinOptions
|
||||
|
||||
/**
|
||||
* 配置版权信息
|
||||
* @default false
|
||||
*/
|
||||
copyright?: boolean | CopyrightLicense | CopyrightOptions
|
||||
|
||||
/**
|
||||
* 版权所有的文本
|
||||
* @default 'Copyright'
|
||||
*/
|
||||
copyrightText?: string
|
||||
|
||||
/**
|
||||
* 版权归属者的文本
|
||||
* @default 'Copyright Ownership:'
|
||||
*/
|
||||
copyrightAuthorText?: string
|
||||
|
||||
/**
|
||||
* 本文链接的文本
|
||||
* @default 'This article link:'
|
||||
*/
|
||||
copyrightCreationOriginalText?: string
|
||||
|
||||
/**
|
||||
* 本文翻译链接的文本
|
||||
* @default 'This article is translated from:'
|
||||
*/
|
||||
copyrightCreationTranslateText?: string
|
||||
|
||||
/**
|
||||
* 转载链接的文本
|
||||
* @default 'This article is reprint from:'
|
||||
*/
|
||||
copyrightCreationReprintText?: string
|
||||
|
||||
/**
|
||||
* 许可证的文本
|
||||
* @default 'License under:'
|
||||
*/
|
||||
copyrightLicenseText?: string
|
||||
|
||||
/**
|
||||
* 是否启用过渡动画效果
|
||||
*
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user