优化:初步优化全局代码样式

This commit is contained in:
2023-02-25 20:36:27 +08:00
parent a0358e1079
commit 57e1051a01
26 changed files with 433 additions and 453 deletions

View File

@@ -13,6 +13,6 @@ English | [中文](./README.md)
</a> </a>
📝 **This is the charles's personal knowledge repositories website.** 📝 **This is the charles's personal technology knowledge repositories website.**
🐢 [GitHub Pages](https://blog.charles7c.top) | 🐇 [Gitee Pages](https://charles7c.gitee.io) 🐢 [GitHub Pages](https://blog.charles7c.top) | 🐇 [Gitee Pages](https://charles7c.gitee.io)

View File

@@ -13,7 +13,7 @@
</a> </a>
📝 **查尔斯的个人知识库,记录 & 分享个人碎片化、结构化、体系化的知识内容。** 📝 **查尔斯的个人技术知识库,记录 & 分享个人碎片化、结构化、体系化的技术知识内容。**
🐢 [GitHub Pages完整体验](https://blog.charles7c.top) | 🐇 [Gitee Pages无法评论](https://charles7c.gitee.io) 🐢 [GitHub Pages完整体验](https://blog.charles7c.top) | 🐇 [Gitee Pages无法评论](https://charles7c.gitee.io)

View File

@@ -1,9 +1,9 @@
import { defineConfig } from 'vitepress' import { defineConfig } from 'vitepress';
import { metaData } from './config/constants' import { withMermaid } from 'vitepress-plugin-mermaid';
import { head } from './config/head' import { metaData } from './config/constants';
import { markdown } from './config/markdown' import { head } from './config/head';
import { themeConfig } from './config/theme' import { markdown } from './config/markdown';
import { withMermaid } from 'vitepress-plugin-mermaid' import { themeConfig } from './config/theme';
export default withMermaid( export default withMermaid(
defineConfig({ defineConfig({
@@ -11,11 +11,11 @@ export default withMermaid(
title: metaData.title, title: metaData.title,
description: metaData.description, description: metaData.description,
cleanUrls: 'without-subfolders', cleanUrls: true,
lastUpdated: true, // 显示最后更新时间 lastUpdated: true, // 显示最后更新时间
head, // <head>内标签配置 head, // <head>内标签配置
markdown: markdown, // Markdown配置 markdown: markdown, // Markdown配置
themeConfig // 主题配置 themeConfig, // 主题配置
}) }),
) );

View File

@@ -1,10 +1,10 @@
const site = 'https://blog.charles7c.top' const site = 'https://blog.charles7c.top';
export const metaData = { export const metaData = {
lang: 'zh-CN', lang: 'zh-CN',
locale: 'zh_CN', locale: 'zh_CN',
title: '查尔斯的知识库', title: '查尔斯的知识库',
description: '个人知识库,记录 & 分享个人碎片化、结构化、体系化的知识内容。', description: '个人技术知识库,记录 & 分享个人碎片化、结构化、体系化的技术知识内容。',
site, site,
image: `${site}/logo.jpg` image: `${site}/logo.jpg`,
} };

View File

@@ -1,5 +1,5 @@
import type { HeadConfig } from 'vitepress' import type { HeadConfig } from 'vitepress';
import { metaData } from './constants' import { metaData } from './constants';
export const head: HeadConfig[] = [ export const head: HeadConfig[] = [
['link', { rel: 'icon', href: '/favicon.ico' }], ['link', { rel: 'icon', href: '/favicon.ico' }],
@@ -56,4 +56,4 @@ export const head: HeadConfig[] = [
xhr.open('GET', 'https://api.charles7c.top/blog/pv?pageUrl=' + location.href); xhr.open('GET', 'https://api.charles7c.top/blog/pv?pageUrl=' + location.href);
xhr.send(); xhr.send();
}`] }`]
] ];

View File

@@ -1,4 +1,4 @@
import type { MarkdownOptions } from 'vitepress' import type { MarkdownOptions } from 'vitepress';
export const markdown: MarkdownOptions = { export const markdown: MarkdownOptions = {
// Shiki主题, 所有主题参见: https://github.com/shikijs/shiki/blob/main/docs/themes.md // Shiki主题, 所有主题参见: https://github.com/shikijs/shiki/blob/main/docs/themes.md
@@ -11,9 +11,9 @@ export const markdown: MarkdownOptions = {
// 在所有文档的<h1>标签后添加<ArticleMetadata/>组件 // 在所有文档的<h1>标签后添加<ArticleMetadata/>组件
config: (md) => { config: (md) => {
md.renderer.rules.heading_close = (tokens, idx, options, env, slf) => { md.renderer.rules.heading_close = (tokens, idx, options, env, slf) => {
let htmlResult = slf.renderToken(tokens, idx, options, env, slf) let htmlResult = slf.renderToken(tokens, idx, options);
if (tokens[idx].tag === 'h1') htmlResult += `\n<ClientOnly><ArticleMetadata v-if="($frontmatter?.aside ?? true) && ($frontmatter?.showArticleMetadata ?? true)" :article="$frontmatter" /></ClientOnly>` if (tokens[idx].tag === 'h1') htmlResult += `\n<ClientOnly><ArticleMetadata v-if="($frontmatter?.aside ?? true) && ($frontmatter?.showArticleMetadata ?? true)" :article="$frontmatter" /></ClientOnly>`;
return htmlResult return htmlResult;
} }
} },
} };

View File

@@ -1,4 +1,4 @@
import DefaultTheme from 'vitepress/theme' import type { DefaultTheme } from 'vitepress';
export const nav: DefaultTheme.Config['nav'] = [ export const nav: DefaultTheme.Config['nav'] = [
{ {
@@ -37,5 +37,5 @@ export const nav: DefaultTheme.Config['nav'] = [
{ text: '关于我', link: '/about/me', activeMatch: '/about/me' } { text: '关于我', link: '/about/me', activeMatch: '/about/me' }
], ],
activeMatch: '/about/' // // 当前页面处于匹配路径下时, 对应导航菜单将突出显示 activeMatch: '/about/' // // 当前页面处于匹配路径下时, 对应导航菜单将突出显示
} },
] ];

View File

@@ -1,6 +1,6 @@
import DefaultTheme from 'vitepress/theme' import type { DefaultTheme } from 'vitepress';
import { sync } from 'fast-glob' import { sync } from 'fast-glob';
import matter from 'gray-matter' import * as matter from 'gray-matter';
export const sidebar: DefaultTheme.Config['sidebar'] = { export const sidebar: DefaultTheme.Config['sidebar'] = {
'/categories/issues/': getItemsByDate("categories/issues"), '/categories/issues/': getItemsByDate("categories/issues"),
@@ -10,7 +10,7 @@ export const sidebar: DefaultTheme.Config['sidebar'] = {
'/courses/java/': getItems("courses/java"), '/courses/java/': getItems("courses/java"),
'/courses/mysql/': getItems("courses/mysql"), '/courses/mysql/': getItems("courses/mysql"),
'/courses/mybatis/': getItems("courses/mybatis") '/courses/mybatis/': getItems("courses/mybatis"),
} }
/** /**
@@ -19,57 +19,56 @@ export const sidebar: DefaultTheme.Config['sidebar'] = {
* /categories/issues/2022/07/20/xxx.md * /categories/issues/2022/07/20/xxx.md
* *
* @param path 扫描基础路径 * @param path 扫描基础路径
* @returns {DefaultTheme.SidebarGroup[]} * @returns {DefaultTheme.SidebarItem[]}
*/ */
function getItemsByDate (path: string) { function getItemsByDate (path: string) {
// 侧边栏年份分组数组 // 侧边栏年份分组数组
let yearGroups: DefaultTheme.SidebarGroup[] = [] let yearGroups: DefaultTheme.SidebarItem[] = [];
// 置顶数组 // 置顶数组
let topArticleItems: DefaultTheme.SidebarItem[] = [] let topArticleItems: DefaultTheme.SidebarItem[] = [];
// 1.获取所有年份目录 // 1.获取所有年份目录
sync(`docs/${path}/*`, { sync(`docs/${path}/*`, {
onlyDirectories: true, onlyDirectories: true,
objectMode: true objectMode: true,
}).forEach(({ name }) => { }).forEach(({ name }) => {
let year = name let year = name;
// 年份数组 // 年份数组
let articleItems: DefaultTheme.SidebarItem[] = [] let articleItems: DefaultTheme.SidebarItem[] = [];
// 2.获取所有月份目录 // 2.获取所有月份目录
sync(`docs/${path}/${year}/*`, { sync(`docs/${path}/${year}/*`, {
onlyDirectories: true, onlyDirectories: true,
objectMode: true objectMode: true,
}).forEach(({ name }) => { }).forEach(({ name }) => {
let month = name let month = name
// 3.获取所有日期目录 // 3.获取所有日期目录
sync(`docs/${path}/${year}/${month}/*`, { sync(`docs/${path}/${year}/${month}/*`, {
onlyDirectories: true, onlyDirectories: true,
objectMode: true objectMode: true,
}).forEach(({ name }) => { }).forEach(({ name }) => {
let day = name let day = name;
// 4.获取日期目录下的所有文章 // 4.获取日期目录下的所有文章
sync(`docs/${path}/${year}/${month}/${day}/*`, { sync(`docs/${path}/${year}/${month}/${day}/*`, {
onlyFiles: true, onlyFiles: true,
objectMode: true objectMode: true,
}).forEach((article) => { }).forEach((article) => {
const articleFile = matter.read(`${article.path}`) const articleFile = matter.read(`${article.path}`);
const { data } = articleFile const { data } = articleFile;
if (data.isTop) { if (data.isTop) {
// 向置顶分组前追加标题 // 向置顶分组前追加标题
topArticleItems.unshift({ topArticleItems.unshift({
text: data.title, text: data.title,
link: `/${path}/${year}/${month}/${day}/${article.name.replace('.md', '')}` link: `/${path}/${year}/${month}/${day}/${article.name.replace('.md', '')}`,
}) });
} }
// 向年份分组前追加标题 // 向年份分组前追加标题
articleItems.unshift({ articleItems.unshift({
text: data.title, text: data.title,
link: `/${path}/${year}/${month}/${day}/${article.name.replace('.md', '')}` link: `/${path}/${year}/${month}/${day}/${article.name.replace('.md', '')}`,
}) });
}) })
}) })
}) })
@@ -77,29 +76,29 @@ function getItemsByDate (path: string) {
// 添加年份分组 // 添加年份分组
yearGroups.unshift({ yearGroups.unshift({
text: `${year}年 (${articleItems.length}篇)`, text: `${year}年 (${articleItems.length}篇)`,
items: articleItems,
collapsed: true, collapsed: true,
items: articleItems });
})
}) })
if (topArticleItems.length > 0) { if (topArticleItems.length > 0) {
// 添加置顶分组 // 添加置顶分组
yearGroups.unshift({ yearGroups.unshift({
text: `📑 我的置顶 (${topArticleItems.length}篇)`, text: `📑 我的置顶 (${topArticleItems.length}篇)`,
items: topArticleItems,
collapsed: false, collapsed: false,
items: topArticleItems });
})
// 将最近年份分组展开 // 将最近年份分组展开
yearGroups[1].collapsed = false yearGroups[1].collapsed = false;
} else { } else {
// 将最近年份分组展开 // 将最近年份分组展开
yearGroups[0].collapsed = false yearGroups[0].collapsed = false;
} }
// 添加序号 // 添加序号
addOrderNumber(yearGroups) addOrderNumber(yearGroups);
return yearGroups return yearGroups;
} }
/** /**
@@ -108,54 +107,54 @@ function getItemsByDate (path: string) {
* courses/mybatis/01-MyBatis基础/01-xxx.md * courses/mybatis/01-MyBatis基础/01-xxx.md
* *
* @param path 扫描基础路径 * @param path 扫描基础路径
* @returns {DefaultTheme.SidebarGroup[]} * @returns {DefaultTheme.SidebarItem[]}
*/ */
function getItems (path: string) { function getItems (path: string) {
// 侧边栏分组数组 // 侧边栏分组数组
let groups: DefaultTheme.SidebarGroup[] = [] let groups: DefaultTheme.SidebarItem[] = [];
// 侧边栏分组下标题数组 // 侧边栏分组下标题数组
let items: DefaultTheme.SidebarItem[] = [] let items: DefaultTheme.SidebarItem[] = [];
let total = 0 let total = 0;
// 当分组内文章数量少于 2 篇或文章总数显示超过 20 篇时,自动折叠分组 // 当分组内文章数量少于 2 篇或文章总数显示超过 20 篇时,自动折叠分组
const groupCollapsedSize = 2 const groupCollapsedSize = 2;
const titleCollapsedSize = 20 const titleCollapsedSize = 20;
// 1.获取所有分组目录 // 1.获取所有分组目录
sync(`docs/${path}/*`, { sync(`docs/${path}/*`, {
onlyDirectories: true, onlyDirectories: true,
objectMode: true objectMode: true,
}).forEach(({ name }) => { }).forEach(({ name }) => {
let groupName = name let groupName = name;
// 2.获取分组下的所有文章 // 2.获取分组下的所有文章
sync(`docs/${path}/${groupName}/*`, { sync(`docs/${path}/${groupName}/*`, {
onlyFiles: true, onlyFiles: true,
objectMode: true objectMode: true,
}).forEach((article) => { }).forEach((article) => {
const articleFile = matter.read(`${article.path}`) const articleFile = matter.read(`${article.path}`);
const { data } = articleFile const { data } = articleFile;
// 向前追加标题 // 向前追加标题
items.push({ items.push({
text: data.title, text: data.title,
link: `/${path}/${groupName}/${article.name.replace('.md', '')}` link: `/${path}/${groupName}/${article.name.replace('.md', '')}`,
}) });
total ++ total += 1;
}) })
// 3.向前追加到分组 // 3.向前追加到分组
// 当分组内文章数量少于 A 篇或文章总数显示超过 B 篇时,自动折叠分组 // 当分组内文章数量少于 A 篇或文章总数显示超过 B 篇时,自动折叠分组
groups.push({ groups.push({
text: `${groupName.substring(groupName.indexOf('-') + 1)} (${items.length}篇)`, text: `${groupName.substring(groupName.indexOf('-') + 1)} (${items.length}篇)`,
items: items,
collapsed: items.length < groupCollapsedSize || total > titleCollapsedSize, collapsed: items.length < groupCollapsedSize || total > titleCollapsedSize,
items: items
}) })
// 4.清空侧边栏分组下标题数组 // 4.清空侧边栏分组下标题数组
items = [] items = [];
}) })
// 添加序号 // 添加序号
addOrderNumber(groups) addOrderNumber(groups);
return groups return groups;
} }
/** /**
@@ -164,10 +163,10 @@ function getItems (path: string) {
* @param groups 分组数据 * @param groups 分组数据
*/ */
function addOrderNumber(groups) { function addOrderNumber(groups) {
for (var i = 0; i < groups.length; i++) { for (let i = 0; i < groups.length; i++) {
for (var j = 0; j < groups[i].items.length; j++) { for (let j = 0; j < groups[i].items.length; j++) {
var items = groups[i].items const items = groups[i].items;
items[j].text = `[${j + 1}] ${items[j].text}` items[j].text = `[${j + 1}] ${items[j].text}`;
} }
} }
} }

View File

@@ -1,15 +1,20 @@
import DefaultTheme from 'vitepress/theme' import type { DefaultTheme } from 'vitepress';
import { nav } from './nav' import { nav } from './nav';
import { sidebar } from './sidebar' import { sidebar } from './sidebar';
export const themeConfig: DefaultTheme.Config = { export const themeConfig: DefaultTheme.Config = {
nav, // 导航栏配置 nav, // 导航栏配置
sidebar, // 侧边栏配置 sidebar, // 侧边栏配置
logo: '/logo.png', logo: '/logo.png',
outline: 'deep', // 右侧大纲标题层级 outline: {
outlineTitle: '目录', // 右侧大纲标题文本配置 level: 'deep', // 右侧大纲标题层级
label: '目录', // 右侧大纲标题文本配置
},
outlineBadges: false, // 是否在大纲中显示 Badge 文本 outlineBadges: false, // 是否在大纲中显示 Badge 文本
darkModeSwitchLabel: '切换日光/暗黑模式',
sidebarMenuLabel: '文章',
returnToTopLabel: '返回顶部',
lastUpdatedText: '最后更新', // 最后更新时间文本配置, 需先配置lastUpdated为true lastUpdatedText: '最后更新', // 最后更新时间文本配置, 需先配置lastUpdated为true
// 文档页脚文本配置 // 文档页脚文本配置
docFooter: { docFooter: {
@@ -60,10 +65,11 @@ export const themeConfig: DefaultTheme.Config = {
], ],
// 自定义扩展: 文章元数据配置 // 自定义扩展: 文章元数据配置
// @ts-ignore
articleMetadataConfig: { articleMetadataConfig: {
author: '查尔斯', // 文章全局默认作者名称 author: '查尔斯', // 文章全局默认作者名称
authorLink: '/about/me', // 点击作者名时默认跳转的链接 authorLink: '/about/me', // 点击作者名时默认跳转的链接
showViewCount: true // 是否显示文章阅读数, 需要在 docs/.vitepress/theme/api/config.js 及 interface.js 配置好相应 API 接口 showViewCount: true, // 是否显示文章阅读数, 需要在 docs/.vitepress/theme/api/config.js 及 interface.js 配置好相应 API 接口
}, },
// 自定义扩展: 文章版权配置 // 自定义扩展: 文章版权配置
copyrightConfig: { copyrightConfig: {

View File

@@ -15,23 +15,21 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from 'vue' import { computed } from 'vue';
import DefaultTheme from 'vitepress/theme' import DefaultTheme from 'vitepress/theme';
import { useData } from 'vitepress' import { useData } from 'vitepress';
import md5 from 'blueimp-md5' import md5 from 'blueimp-md5';
import Copyright from './components/layout/Copyright.vue' import Copyright from './components/layout/Copyright.vue';
import Comment from './components/layout/Comment.vue' import Comment from './components/layout/Comment.vue';
import Footer from './components/layout/Footer.vue' import Footer from './components/layout/Footer.vue';
const { Layout } = DefaultTheme const { Layout } = DefaultTheme;
const { page, theme, frontmatter } = useData() const { page, theme, frontmatter } = useData();
const hasSidebar = computed(() => { const hasSidebar = computed(() => {
return ( return (
frontmatter.value.aside !== false && frontmatter.value.aside !== false && frontmatter.value.layout !== 'home'
frontmatter.value.layout !== 'home' )
) });
})
</script> </script>
<style scoped> <style scoped></style>
</style>

View File

@@ -1,24 +1,24 @@
import axios from 'axios' import axios from 'axios';
const createBaseInstance = () => { const createBaseInstance = () => {
const instance = axios.create({ const instance = axios.create({
baseURL: 'https://api.charles7c.top/blog', baseURL: 'https://api.charles7c.top/blog',
timeout: 3000 timeout: 3000,
}) });
instance.interceptors.request.use(handleRequest, handleError) instance.interceptors.request.use(handleRequest, handleError);
instance.interceptors.response.use(handleResponse, handleError) instance.interceptors.response.use(handleResponse, handleError);
return instance return instance;
} }
export const request = createBaseInstance() export const request = createBaseInstance();
function handleError(e) { function handleError(e) {
throw e throw e;
} }
function handleRequest(request) { function handleRequest(request) {
return request; return request;
} }
function handleResponse(response) { function handleResponse(response) {
return response.data return response.data;
} }

View File

@@ -1,3 +1,3 @@
export * from './interface' export * from './interface';
export { default } from './interface' export { default } from './interface';

View File

@@ -1,17 +1,17 @@
import { request } from './config' import { request } from './config';
export const getArticleViewCount = (id, pageUrl, call) => { export const getArticleViewCount = (id, pageUrl, call) => {
request.get(`/article/view/${id}?pageUrl=${pageUrl}`, {}).then(result => { request.get(`/article/view/${id}?pageUrl=${pageUrl}`, {}).then(result => {
call(process(result)) call(process(result));
}) });
} };
function process(result) { function process(result) {
if (result.code === 200) { if (result.code === 200) {
return result.data return result.data;
} else { } else {
console.log(result.msg) console.log(result.msg);
} }
} }
export default { getArticleViewCount } export default { getArticleViewCount }

View File

@@ -79,142 +79,97 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import articleData from '../../../../article-data.json' import articleData from '../../../../article-data.json';
import { formatDate, getQueryParam, goToLink } from '../utils.ts' import { getQueryParam, goToLink } from '../utils.ts';
// 文章原始数据和归档数据 // 文章原始数据和归档数据
let $articleData let $articleData;
let archiveData let archiveData;
// 要筛选的分类、标签、年份 // 要筛选的分类、标签、年份
let $category let $category;
let $tag let $tag;
let $year let $year;
initTimeline() /**
* 初始化时间轴
*/
function initTimeline() {
$articleData = [];
archiveData = {};
/** // 如果URL路径有category或tag或year参数, 默认筛选出指定category或tag或year的文章数据
* 初始化时间轴 // 例如: /archives?category=Bug万象集
*/ // 例如: /archives?tag=JVM
function initTimeline() { // 例如: /archives?year=2020
$articleData = [] $category = getQueryParam('category');
archiveData = {} $tag = getQueryParam('tag');
$year = getQueryParam('year');
// 如果URL路径有category或tag或year参数, 默认筛选出指定category或tag或year的文章数据 if ($category && $category.trim() != '') {
// 例如: /archives?category=Bug万象集 for (let i = 0; i < articleData.length; i++) {
// 例如: /archives?tag=JVM let article = articleData[i];
// 例如: /archives?year=2020 if (article.categories && article.categories.includes($category)) {
$category = getQueryParam('category') $articleData.push(article);
$tag = getQueryParam('tag') }
$year = getQueryParam('year')
if ($category && $category.trim() != '') {
for (let i = 0; i < articleData.length; i++) {
let article = articleData[i]
if (article.categories && article.categories.includes($category)) {
$articleData.push(article)
} }
} } else if ($tag && $tag.trim() != '') {
} else if ($tag && $tag.trim() != '') { for (let i = 0; i < articleData.length; i++) {
for (let i = 0; i < articleData.length; i++) { let article = articleData[i];
let article = articleData[i] if (article.tags && article.tags.includes($tag)) {
if (article.tags && article.tags.includes($tag)) { $articleData.push(article);
$articleData.push(article) }
} }
} } else if ($year && $year.trim() != '') {
} else if ($year && $year.trim() != '') { for (let i = 0; i < articleData.length; i++) {
for (let i = 0; i < articleData.length; i++) { let article = articleData[i];
let article = articleData[i] if (article.date && new Date(article.date).getFullYear() == $year) {
if (article.date && new Date(article.date).getFullYear() == $year) { $articleData.push(article);
$articleData.push(article) }
} }
} else {
$articleData.push(...articleData);
} }
} else {
$articleData.push(...articleData) // 文章数据归档处理
// 1.对文章数据进行降序排序
$articleData.sort((a, b) => b.date.localeCompare(a.date));
// 2.按年、月进行归档
for (let i = 0; i < $articleData.length; i++) {
const article = $articleData[i];
let year = (new Date(article.date).getFullYear()) + '年';
let month = (new Date(article.date).getMonth() + 1) + '月';
if (!archiveData[year]) {
archiveData[year] = {};
}
if (!(archiveData[year][month])) {
archiveData[year][month] = [];
}
archiveData[year][month].push(article);
}
}
initTimeline();
/**
* 获取生肖图标
*
* @param year 年份
*/
function getChineseZodiac(year) {
const arr = ['monkey', 'rooster', 'dog', 'pig', 'rat', 'ox', 'tiger', 'rabbit', 'dragon', 'snake', 'horse', 'goat'];
return arr[year % 12];
} }
// 文章数据归档处理 /**
// 1.对文章数据进行降序排序 * 获取生肖名称
$articleData.sort((a, b) => b.date.localeCompare(a.date)) *
// 2.按年、月进行归档 * @param year 年份
for (let i = 0; i < $articleData.length; i++) { */
const article = $articleData[i] function getChineseZodiacAlias(year) {
let year = (new Date(article.date).getFullYear()) + '年' const arr = ['猴年', '鸡年', '狗年', '猪年', '鼠年', '牛年', '虎年', '兔年', '龙年', '蛇年', '马年', '年'];
let month = (new Date(article.date).getMonth() + 1) + '月' return arr[year % 12];
if (!archiveData[year]) {
archiveData[year] = {}
}
if (!(archiveData[year][month])) {
archiveData[year][month] = []
}
archiveData[year][month].push(article)
}
}
/**
* 获取生肖
*/
function getChineseZodiac(year) {
switch(year % 12){
case 0:
return 'monkey'
case 1:
return 'rooster'
case 2:
return 'dog'
case 3:
return 'pig'
case 4:
return 'rat'
case 5:
return 'ox'
case 6:
return 'tiger'
case 7:
return 'rabbit'
case 8:
return 'dragon'
case 9:
return 'snake'
case 10:
return 'horse'
case 11:
return 'goat'
} }
}
/**
* 获取生肖名称
*/
function getChineseZodiacAlias(year) {
switch(year % 12){
case 0:
return '猴年'
case 1:
return '鸡年'
case 2:
return '狗年'
case 3:
return '猪年'
case 4:
return '鼠年'
case 5:
return '牛年'
case 6:
return '虎年'
case 7:
return '兔年'
case 8:
return '龙年'
case 9:
return '蛇年'
case 10:
return '马年'
case 11:
return '羊年'
}
}
</script> </script>
<style scoped> <style scoped>

View File

@@ -68,49 +68,49 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { reactive, toRefs, onMounted } from 'vue' import { reactive, toRefs, onMounted } from 'vue';
import { useData } from 'vitepress' import { useData } from 'vitepress';
import md5 from 'blueimp-md5' import md5 from 'blueimp-md5';
import dayjs from 'dayjs' import dayjs from 'dayjs';
import 'dayjs/locale/zh-cn' import 'dayjs/locale/zh-cn';
import relativeTime from 'dayjs/plugin/relativeTime' import relativeTime from 'dayjs/plugin/relativeTime';
import { goToLink } from '../utils.ts' import { goToLink } from '../utils.ts';
dayjs.extend(relativeTime) dayjs.extend(relativeTime);
dayjs.locale('zh-cn') dayjs.locale('zh-cn');
// 定义文章属性 // 定义文章属性
const props = defineProps({ const props = defineProps({
article: Object, article: Object,
showCategory: { showCategory: {
type: Boolean, type: Boolean,
default: true default: true,
},
});
// 初始化文章元数据信息
const { theme, page } = useData();
const data = reactive({
isOriginal: props.article?.isOriginal ?? true,
author: props.article?.author ?? theme.value.articleMetadataConfig.author,
authorLink: props.article?.authorLink ?? theme.value.articleMetadataConfig.authorLink,
showViewCount: theme.value.articleMetadataConfig?.showViewCount ?? false,
viewCount: 0,
date: new Date(props.article.date),
categories: props.article?.categories ?? [],
tags: props.article?.tags ?? [],
showCategory: props.showCategory
});
const { isOriginal, author, authorLink, showViewCount, viewCount, date, toDate, categories, tags, showCategory } = toRefs(data);
if (data.showViewCount) {
// 记录并获取文章阅读数(使用文章标题 + 发布时间生成 MD5 值,作为文章的唯一标识)
onMounted(() => {
$api.getArticleViewCount(md5(props.article.title + props.article.date), location.href, function(viewCountData) {
data.viewCount = viewCountData;
});
});
} }
})
// 初始化文章元数据信息
const { theme, page } = useData()
const data = reactive({
isOriginal: props.article?.isOriginal ?? true,
author: props.article?.author ?? theme.value.articleMetadataConfig.author,
authorLink: props.article?.authorLink ?? theme.value.articleMetadataConfig.authorLink,
showViewCount: theme.value.articleMetadataConfig?.showViewCount ?? false,
viewCount: 0,
date: new Date(props.article.date),
categories: props.article?.categories ?? [],
tags: props.article?.tags ?? [],
showCategory: props.showCategory
})
const { isOriginal, author, authorLink, showViewCount, viewCount, date, toDate, categories, tags, showCategory } = toRefs(data)
if (data.showViewCount) {
// 记录并获取文章阅读数(使用文章标题 + 发布时间生成 MD5 值,作为文章的唯一标识)
onMounted(() => {
$api.getArticleViewCount(md5(props.article.title + props.article.date), location.href, function(viewCountData) {
data.viewCount = viewCountData
})
})
}
</script> </script>
<style scoped> <style scoped>

View File

@@ -58,63 +58,63 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, ref, onMounted } from 'vue' import { computed, ref } from 'vue';
import md5 from 'blueimp-md5' import md5 from 'blueimp-md5';
import articleData from '../../../../article-data.json' import articleData from '../../../../article-data.json';
import { formatDate, getQueryParam } from '../utils.ts' import { getQueryParam } from '../utils.ts';
const tags = computed(() => initTags(articleData)) const tags = computed(() => initTags(articleData));
/** /**
* 初始化标签数据 * 初始化标签数据
* {tagTitle1: [article1, article2, ...} * {tagTitle1: [article1, article2, ...}
*/ */
function initTags(articleData) { function initTags(articleData) {
const tags: any = {} const tags: any = {};
for (let i = 0; i < articleData.length; i++) { for (let i = 0; i < articleData.length; i++) {
const article = articleData[i] const article = articleData[i];
const articleTags = article.tags const articleTags = article.tags;
if (Array.isArray(articleTags)) { if (Array.isArray(articleTags)) {
articleTags.forEach((articleTag) => { articleTags.forEach((articleTag) => {
if (!tags[articleTag]) { if (!tags[articleTag]) {
tags[articleTag] = [] tags[articleTag] = [];
} }
tags[articleTag].push(article) tags[articleTag].push(article);
// 文章按发布时间降序排序 // 文章按发布时间降序排序
tags[articleTag].sort((a, b) => b.date.localeCompare(a.date)) tags[articleTag].sort((a, b) => b.date.localeCompare(a.date));
}) });
}
}
return tags;
}
// 点击指定Tag后进行选中
let selectTag = ref('');
const toggleTag = (tagTitle: string) => {
if (selectTag.value && selectTag.value == tagTitle) {
selectTag.value = null;
} else {
selectTag.value = tagTitle;
} }
} }
return tags
}
// 点击指定Tag后进行选中 // 如果URL路径有tag参数, 默认选中指定Tag, 例如: /tags?tag=Git
let selectTag = ref('') let tag = getQueryParam('tag');
const toggleTag = (tagTitle: string) => { if (tag && tag.trim() != '') {
if (selectTag.value && selectTag.value == tagTitle) { toggleTag(tag);
selectTag.value = null
} else {
selectTag.value = tagTitle
} }
}
// 如果URL路径有tag参数, 默认选中指定Tag, 例如: /tags?tag=Git const dataList = computed(() => initWordCloud(tags));
let tag = getQueryParam('tag') /**
if (tag && tag.trim() != '') { * 初始化词云数据
toggleTag(tag) * [{"name": xx, "value": xx}]
} */
function initWordCloud(tags) {
const dataList = computed(() => initWordCloud(tags)) const dataList = [];
/** for (let tag in tags.value) {
* 初始化词云数据 dataList.push({"name": tag, "value": tags.value[tag].length});
* [{"name": xx, "value": xx}] }
*/ return dataList;
function initWordCloud(tags) {
const dataList = []
for (let tag in tags.value) {
dataList.push({"name": tag, "value": tags.value[tag].length})
} }
return dataList
}
</script> </script>
<style scoped> <style scoped>

View File

@@ -3,79 +3,78 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { reactive, toRefs, onMounted } from 'vue' import { reactive, toRefs, onMounted } from 'vue';
import { useData } from 'vitepress' import { useData } from 'vitepress';
import md5 from 'blueimp-md5' import md5 from 'blueimp-md5';
import $ from 'jquery' import $ from 'jquery';
import { Message } from '@arco-design/web-vue' import { Message } from '@arco-design/web-vue';
import '@arco-design/web-vue/es/message/style/css.js' import '@arco-design/web-vue/es/message/style/css.js';
import Gitalk from 'gitalk' import Gitalk from 'gitalk';
import '../../styles/components/gitalk.css' import '../../styles/components/gitalk.css';
// 定义属性 // 定义属性
const props = defineProps({ const props = defineProps({
commentConfig: Object commentConfig: Object,
}) });
const data = reactive({ const data = reactive({
type: props.commentConfig?.type ?? 'gitalk' type: props.commentConfig?.type ?? 'gitalk',
})
const { type } = toRefs(data)
// 初始化评论组件配置
const { page } = useData()
let gitalk
if (type.value && type.value == 'gitalk') {
gitalk = new Gitalk({
clientID: '1de126ce1fbdbe049709',
clientSecret: '035fe49874a43e5cefc28a99b7e40b1925319c62',
repo: 'charles7c.github.io',
owner: 'Charles7c',
admin: ['Charles7c'],
id: md5(page.value.relativePath),
language: 'zh-CN',
distractionFreeMode: false,
// 默认: https://cors-anywhere.azm.workers.dev/https://github.com/login/oauth/access_token
proxy: 'https://vercel.charles7c.top/github_access_token'
}) })
} const { type } = toRefs(data);
// 渲染评论组件 // 初始化评论组件配置
onMounted(() => { const { page } = useData();
let gitalk;
if (type.value && type.value == 'gitalk') { if (type.value && type.value == 'gitalk') {
gitalk.render('comment-container') gitalk = new Gitalk({
clientID: '1de126ce1fbdbe049709',
// 如果点赞,先判断有没有登录 clientSecret: '035fe49874a43e5cefc28a99b7e40b1925319c62',
let $gc = $('#comment-container'); repo: 'charles7c.github.io',
$gc.on('click', '.gt-comment-like', function () { owner: 'Charles7c',
if (!window.localStorage.getItem('GT_ACCESS_TOKEN')) { admin: ['Charles7c'],
Message.warning({ id: md5(page.value.relativePath),
content:'点赞前,请您先进行登录', language: 'zh-CN',
closable: true distractionFreeMode: false,
}) // 默认: https://cors-anywhere.azm.workers.dev/https://github.com/login/oauth/access_token
proxy: 'https://vercel.charles7c.top/github_access_token',
return false });
}
return true
})
// 提交评论后输入框高度没有重置bug
$gc.on('click', '.gt-header-controls .gt-btn-public', function () {
let $gt = $('.gt-header-textarea')
$gt.css('height', '72px')
})
// 点击预览时,隐藏评论按钮
$gc.on('click', '.gt-header-controls .gt-btn-preview', function () {
let pl = $('.gt-header-controls .gt-btn-public');
if (pl.hasClass('hide')) {
pl.removeClass('hide')
} else {
// 隐藏
pl.addClass('hide')
}
})
} }
})
// 渲染评论组件
onMounted(() => {
if (type.value && type.value == 'gitalk') {
gitalk.render('comment-container')
// 如果点赞,先判断有没有登录
let $gc = $('#comment-container');
$gc.on('click', '.gt-comment-like', function () {
if (!window.localStorage.getItem('GT_ACCESS_TOKEN')) {
Message.warning({
content: '点赞前,请您先进行登录',
closable: true
})
return false
}
return true
})
// 提交评论后输入框高度没有重置bug
$gc.on('click', '.gt-header-controls .gt-btn-public', function () {
let $gt = $('.gt-header-textarea')
$gt.css('height', '72px')
})
// 点击预览时,隐藏评论按钮
$gc.on('click', '.gt-header-controls .gt-btn-preview', function () {
let pl = $('.gt-header-controls .gt-btn-public');
if (pl.hasClass('hide')) {
pl.removeClass('hide')
} else {
// 隐藏
pl.addClass('hide')
}
})
}
})
</script> </script>
<style scoped> <style scoped></style>
</style>

View File

@@ -43,18 +43,18 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { reactive, toRefs } from 'vue' import { reactive, toRefs } from 'vue';
import { useData } from 'vitepress' import { useData } from 'vitepress';
const { theme, frontmatter } = useData() const { theme, frontmatter } = useData();
const data = reactive({ const data = reactive({
isOriginal: frontmatter.value?.isOriginal ?? true, isOriginal: frontmatter.value?.isOriginal ?? true,
author: frontmatter.value?.author ?? theme.value.articleMetadataConfig.author, author: frontmatter.value?.author ?? theme.value.articleMetadataConfig.author,
authorLink: frontmatter.value?.authorLink ?? theme.value.articleMetadataConfig.authorLink, authorLink: frontmatter.value?.authorLink ?? theme.value.articleMetadataConfig.authorLink,
articleTitle: frontmatter.value?.articleTitle ?? frontmatter.value.title, articleTitle: frontmatter.value?.articleTitle ?? frontmatter.value.title,
articleLink: frontmatter.value?.articleLink ?? decodeURI(window.location.href) articleLink: frontmatter.value?.articleLink ?? decodeURI(window.location.href),
}) });
const { isOriginal, author, authorLink, articleTitle, articleLink } = toRefs(data) const { isOriginal, author, authorLink, articleTitle, articleLink } = toRefs(data);
</script> </script>
<style scoped> <style scoped>
@@ -68,7 +68,7 @@ const { isOriginal, author, authorLink, articleTitle, articleLink } = toRefs(dat
} }
.copyright .content { .copyright .content {
padding: 13px 16px; padding: 13px 16px;
} }

View File

@@ -23,8 +23,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { useData } from 'vitepress' import { useData } from 'vitepress';
const { theme } = useData() const { theme } = useData();
</script> </script>
<style scoped> <style scoped>

View File

@@ -1,21 +1,21 @@
import DefaultTheme from 'vitepress/theme' import DefaultTheme from 'vitepress/theme'
import MyLayout from './MyLayout.vue' import MyLayout from './MyLayout.vue';
import './styles/vars.css' import './styles/vars.css';
import './styles/custom.css' import './styles/custom.css';
import axios from 'axios' import axios from 'axios';
import api from './api/index' import api from './api/index';
export default { export default {
...DefaultTheme, ...DefaultTheme,
Layout: MyLayout, Layout: MyLayout,
enhanceApp(ctx) { enhanceApp(ctx) {
// extend default theme custom behaviour. // extend default theme custom behaviour.
DefaultTheme.enhanceApp(ctx) DefaultTheme.enhanceApp(ctx);
// 全局挂载 API 接口 // 全局挂载 API 接口
ctx.app.config.globalProperties.$http = axios ctx.app.config.globalProperties.$http = axios
if (typeof window !== 'undefined') { if (typeof window !== 'undefined') {
window.$api = api window.$api = api;
} }
// register your custom global components // register your custom global components

View File

@@ -1,36 +1,39 @@
/** /**
* 格式化时间 * 格式化时间
*
* @param date 待格式化时间 * @param date 待格式化时间
* @returns 格式化后的时间(YYYY/MM/dd AM hh:mm) * @returns 格式化后的时间(YYYY/MM/dd AM hh:mm)
*/ */
export function formatDate(date) { export function formatDate(date) {
const formatDate = new Date(date) const formatDate = new Date(date);
return formatDate.toLocaleString('zh', {year: 'numeric', month: 'numeric', day: 'numeric', hour: 'numeric', minute: 'numeric'}) return formatDate.toLocaleString('zh', {year: 'numeric', month: 'numeric', day: 'numeric', hour: 'numeric', minute: 'numeric'});
} }
/** /**
* 获取URL路径中的指定参数 * 获取 URL 路径中的指定参数
*
* @param paramName 参数名 * @param paramName 参数名
* @returns 参数值 * @returns 参数值
*/ */
export function getQueryParam(paramName) { export function getQueryParam(paramName) {
const reg = new RegExp("(^|&)"+ paramName +"=([^&]*)(&|$)") const reg = new RegExp("(^|&)"+ paramName +"=([^&]*)(&|$)");
let value = decodeURIComponent(window.location.search.substr(1)).match(reg) let value = decodeURIComponent(window.location.search.substr(1)).match(reg);
if (value != null) { if (value != null) {
return unescape(value[2]) return unescape(value[2]);
} }
return null return null;
} }
/** /**
* 跳转到指定链接 * 跳转到指定链接
*
* @param paramName 参数名 * @param paramName 参数名
* @param paramValue 参数值 * @param paramValue 参数值
*/ */
export function goToLink(url, paramName, paramValue) { export function goToLink(url, paramName, paramValue) {
if (paramName) { if (paramName) {
window.location.href = url + '?' + paramName + '=' + paramValue window.location.href = url + '?' + paramName + '=' + paramValue;
} else { } else {
window.location.href = url window.location.href = url;
} }
} }

View File

@@ -8,7 +8,7 @@ lastUpdated: false
<img width=100% src="../public/img/svg/about-repos-header.svg" alt="头部图" /> <img width=100% src="../public/img/svg/about-repos-header.svg" alt="头部图" />
<div align="center"> <div align="center">
📝 <strong>查尔斯的个人知识库,记录 & 分享个人碎片化、结构化、体系化的知识内容。</strong> 📝 <strong>查尔斯的个人技术知识库,记录 & 分享个人碎片化、结构化、体系化的技术知识内容。</strong>
</div> </div>
--- ---

View File

@@ -2,21 +2,21 @@
layout: home layout: home
title: 查尔斯的知识库 title: 查尔斯的知识库
titleTemplate: 个人知识库,记录和分享个人碎片化、结构化、体系化的知识内容 titleTemplate: 个人技术知识库,记录和分享个人碎片化、结构化、体系化的技术知识内容
hero: hero:
name: 查尔斯的知识库 name: 查尔斯的知识库
text: 专注 & 洞察 & 分享 text: 专注 & 洞察 & 分享
tagline: 个人知识库,记录 & 分享个人碎片化、结构化、体系化的知识内容。 tagline: 个人技术知识库,记录 & 分享个人碎片化、结构化、体系化的技术知识内容。
image: image:
src: /logo.png src: /logo.png
alt: Logo alt: Logo
actions: actions:
- theme: brand - theme: brand
text: Get Started text: 快速开始
link: /categories/issues/index link: /categories/issues/index
- theme: alt - theme: alt
text: View on GitHub text: GitHub 查看
link: https://github.com/Charles7c/charles7c.github.io link: https://github.com/Charles7c/charles7c.github.io
features: features:

View File

@@ -1,19 +1,19 @@
import { defineConfig } from 'vite' import { defineConfig } from 'vite';
//import { SearchPlugin } from 'vitepress-plugin-search' import Components from 'unplugin-vue-components/vite';
import Components from 'unplugin-vue-components/vite' import { ArcoResolver } from 'unplugin-vue-components/resolvers';
import { ArcoResolver } from 'unplugin-vue-components/resolvers' // import { SearchPlugin } from 'vitepress-plugin-search';
export default defineConfig({ export default defineConfig({
plugins: [ plugins: [
//SearchPlugin({ // SearchPlugin({
//encode: false, // encode: false,
//tokenize: 'full' // tokenize: 'full',
//}), // }),
Components({ Components({
dirs: ['.vitepress/theme/components'], dirs: ['.vitepress/theme/components'],
include: [/\.vue$/, /\.vue\?vue/, /\.md$/], include: [/\.vue$/, /\.vue\?vue/, /\.md$/],
resolvers: [ArcoResolver({ sideEffect: true, resolveIcons: true })] resolvers: [ArcoResolver({ sideEffect: true, resolveIcons: true })]
}) }),
], ],
ssr: { noExternal: ['@arco-design/web-vue'] }, ssr: { noExternal: ['@arco-design/web-vue'] },
resolve: { resolve: {

View File

@@ -11,6 +11,8 @@
}, },
"devDependencies": { "devDependencies": {
"@arco-design/web-vue": "^2.43.2", "@arco-design/web-vue": "^2.43.2",
"@types/blueimp-md5": "^2.18.0",
"@types/jquery": "^3.5.16",
"flexsearch": "^0.7.31", "flexsearch": "^0.7.31",
"markdown-it": "^13.0.1", "markdown-it": "^13.0.1",
"mermaid": "9.3.0", "mermaid": "9.3.0",

18
pnpm-lock.yaml generated
View File

@@ -7,6 +7,8 @@ overrides:
specifiers: specifiers:
'@antv/g2plot': ^2.4.25 '@antv/g2plot': ^2.4.25
'@arco-design/web-vue': ^2.43.2 '@arco-design/web-vue': ^2.43.2
'@types/blueimp-md5': ^2.18.0
'@types/jquery': ^3.5.16
axios: ^1.3.4 axios: ^1.3.4
blueimp-md5: ^2.19.0 blueimp-md5: ^2.19.0
dayjs: ^1.11.7 dayjs: ^1.11.7
@@ -36,6 +38,8 @@ dependencies:
devDependencies: devDependencies:
'@arco-design/web-vue': 2.43.2_vue@3.2.47 '@arco-design/web-vue': 2.43.2_vue@3.2.47
'@types/blueimp-md5': 2.18.0
'@types/jquery': 3.5.16
flexsearch: 0.7.31 flexsearch: 0.7.31
markdown-it: 13.0.1 markdown-it: 13.0.1
mermaid: 9.3.0 mermaid: 9.3.0
@@ -931,6 +935,10 @@ packages:
picomatch: 2.3.1 picomatch: 2.3.1
dev: true dev: true
/@types/blueimp-md5/2.18.0:
resolution: {integrity: sha512-f4A+++lGZGJvVSgeyMkqA7BEf2BVQli6F+qEykKb49c5ieWQBkfpn6CP5c1IZr2Yi2Ofl6Fj+v0e1fN18Z8Cnw==}
dev: true
/@types/d3-timer/2.0.1: /@types/d3-timer/2.0.1:
resolution: {integrity: sha512-TF8aoF5cHcLO7W7403blM7L1T+6NF3XMyN3fxyUolq2uOcFeicG/khQg/dGxiCJWoAcmYulYN7LYSRKO54IXaA==} resolution: {integrity: sha512-TF8aoF5cHcLO7W7403blM7L1T+6NF3XMyN3fxyUolq2uOcFeicG/khQg/dGxiCJWoAcmYulYN7LYSRKO54IXaA==}
dev: false dev: false
@@ -943,6 +951,12 @@ packages:
resolution: {integrity: sha512-HXwADeHEP4exXkCIwy2n1+i0f1ilP1ETQOH5KDOugjkTFZPntWo0Gr8stZOaebkxsdx+k0X/K6obU/+it07ocg==} resolution: {integrity: sha512-HXwADeHEP4exXkCIwy2n1+i0f1ilP1ETQOH5KDOugjkTFZPntWo0Gr8stZOaebkxsdx+k0X/K6obU/+it07ocg==}
dev: true dev: true
/@types/jquery/3.5.16:
resolution: {integrity: sha512-bsI7y4ZgeMkmpG9OM710RRzDFp+w4P1RGiIt30C1mSBT+ExCleeh4HObwgArnDFELmRrOpXgSYN9VF1hj+f1lw==}
dependencies:
'@types/sizzle': 2.3.3
dev: true
/@types/linkify-it/3.0.2: /@types/linkify-it/3.0.2:
resolution: {integrity: sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==} resolution: {integrity: sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==}
dev: true dev: true
@@ -958,6 +972,10 @@ packages:
resolution: {integrity: sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==} resolution: {integrity: sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==}
dev: true dev: true
/@types/sizzle/2.3.3:
resolution: {integrity: sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==}
dev: true
/@types/web-bluetooth/0.0.16: /@types/web-bluetooth/0.0.16:
resolution: {integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==} resolution: {integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==}
dev: true dev: true