Compare commits
35 Commits
585e7eb3a6
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| d2622ac85b | |||
| 4ab4cc39d2 | |||
| 7be5057d5b | |||
| bc19f9a638 | |||
| 498fa8519e | |||
| e7f6738a27 | |||
| 036d98dbd4 | |||
| 8b07e121a5 | |||
| 9cf9745d49 | |||
| c323698b2a | |||
| 5d4d388fe8 | |||
| 9e33f42d34 | |||
| 033b124b90 | |||
| 955bca434c | |||
| cf9e51611a | |||
| 1146f3b180 | |||
| 5131b9396b | |||
| a8f0a86e07 | |||
| 3ed03f0496 | |||
| 05c1b0d29b | |||
| 2f54cf304a | |||
| 109357e8e2 | |||
| 8563930e68 | |||
| 8d07d60566 | |||
| 4cdce770d8 | |||
| b87816c5eb | |||
| 3deefdccff | |||
| a643397716 | |||
| 740fc03778 | |||
| ed1a6fe662 | |||
| 37f035709d | |||
| b39c3499c3 | |||
| cb7ce1361d | |||
| 2cb3299402 | |||
| 6127d5f7ed |
9
.gitignore
vendored
9
.gitignore
vendored
@@ -23,5 +23,10 @@ pnpm-debug.log*
|
|||||||
# jetbrains setting folder
|
# jetbrains setting folder
|
||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
# test articles
|
# ignore test articles except for article definitions
|
||||||
src/content/
|
src/content/*
|
||||||
|
!src/content/config.ts
|
||||||
|
|
||||||
|
# ignore generated html from makeinfo
|
||||||
|
src/pages/article/translation/*.html
|
||||||
|
|
||||||
|
|||||||
12
.prettierrc.mjs
Normal file
12
.prettierrc.mjs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/** @type {import("prettier").Config} */
|
||||||
|
export default {
|
||||||
|
plugins: ['prettier-plugin-astro'],
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: '*.astro',
|
||||||
|
options: {
|
||||||
|
parser: 'astro',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
11
.vscode/settings.json
vendored
Normal file
11
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"[json]": {
|
||||||
|
"editor.tabSize": 2
|
||||||
|
},
|
||||||
|
"[javascript]": {
|
||||||
|
"editor.tabSize": 2
|
||||||
|
},
|
||||||
|
"[typescript]": {
|
||||||
|
"editor.tabSize": 2
|
||||||
|
}
|
||||||
|
}
|
||||||
11
README.md
11
README.md
@@ -1 +1,12 @@
|
|||||||
|
# 李守中的个人站
|
||||||
|
|
||||||
个人资料、文章与工具站点。由 Astro 框架构建,使用 typescript 编写。
|
个人资料、文章与工具站点。由 Astro 框架构建,使用 typescript 编写。
|
||||||
|
|
||||||
|
## 关于翻译类文章的发布方式
|
||||||
|
|
||||||
|
翻译类文章不由 Astro 生成,而是预先使用 texinfo 编写后编译到 HTML 文件,将其放于固定位置,再在 Astro 页面中引用这些文件。
|
||||||
|
|
||||||
|
这意味着每有一个新的翻译类文章要发布时,需要:
|
||||||
|
|
||||||
|
1. 更新 pages/article/translation/index.astro 文件中翻译类文章的列表;
|
||||||
|
2. 更新 astro.config.mjs 文件中的 sitemap 插件配置,填入 texinfo 编译出的 HTML 文件的 URL 以使这些文章可以进入 sitemap.xml 中,从而可以被搜索引擎收录。
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import { defineConfig } from 'astro/config';
|
import { defineConfig } from 'astro/config';
|
||||||
|
|
||||||
import sitemap from "@astrojs/sitemap";
|
import sitemap from "@astrojs/sitemap";
|
||||||
|
import vue from "@astrojs/vue";
|
||||||
|
import remarkToc from 'remark-toc';
|
||||||
|
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
markdown: {
|
markdown: {
|
||||||
|
smartypants: false,
|
||||||
syntaxHighlight: 'shiki',
|
syntaxHighlight: 'shiki',
|
||||||
shikiConfig: {
|
shikiConfig: {
|
||||||
// Choose from Shiki's built-in themes (or add your own)
|
// Choose from Shiki's built-in themes (or add your own)
|
||||||
@@ -25,16 +27,38 @@ export default defineConfig({
|
|||||||
// Add custom transformers: https://shiki.style/guide/transformers
|
// Add custom transformers: https://shiki.style/guide/transformers
|
||||||
// Find common transformers: https://shiki.style/packages/transformers
|
// Find common transformers: https://shiki.style/packages/transformers
|
||||||
transformers: []
|
transformers: []
|
||||||
}
|
},
|
||||||
|
remarkPlugins: [
|
||||||
|
[remarkToc, { heading: "(table[ -]of[ -])?contents?|toc|目录" }]
|
||||||
|
],
|
||||||
},
|
},
|
||||||
site: 'https://lishouzhong.com',
|
site: 'https://lishouzhong.com',
|
||||||
integrations: [sitemap({
|
integrations: [
|
||||||
// entryLimit: 10000,
|
sitemap({
|
||||||
// customPages: [],
|
customPages: [
|
||||||
// filter: (page) => { },
|
"https://lishouzhong.com/file_share/translation/FreeBSD 13 NFS exports man(5) page 2021 中文译本.html",
|
||||||
// lastmod: new Date('2022-02-24'),
|
"https://lishouzhong.com/file_share/translation/FreeBSD 13 NFS mountd man(8) page 2020 中文译本.html",
|
||||||
// serialize(item) {
|
"https://lishouzhong.com/file_share/translation/FreeBSD 13 NFS nfsd man(8) page 2019 中文译本.html",
|
||||||
// return item;
|
"https://lishouzhong.com/file_share/translation/FreeBSD 13 NFS showmount man(8) page 2016 中文译本.html",
|
||||||
// }
|
"https://lishouzhong.com/file_share/translation/FreeBSD 13 NFSv4 man(4) page 2019 中文译本.html",
|
||||||
})]
|
"https://lishouzhong.com/file_share/translation/FreeBSD 13 NFSv4 nfscbd man(8) page 2009 中文译本.html",
|
||||||
|
"https://lishouzhong.com/file_share/translation/FreeBSD 13 NFSv4 nfsrevoke man(8) page 2009 中文译本.html",
|
||||||
|
"https://lishouzhong.com/file_share/translation/FreeBSD 13 NFSv4 nfsuserd man(8) page 2019 中文译本.html",
|
||||||
|
"https://lishouzhong.com/file_share/translation/FreeBSD 13 rpcbind man(8) page 2017 中文译本.html",
|
||||||
|
"https://lishouzhong.com/file_share/translation/EasyRSA Intro-To-PKI v3.08 中文译本.html",
|
||||||
|
"https://lishouzhong.com/file_share/translation/EasyRSA-Advanced v3.08 中文译本.html",
|
||||||
|
"https://lishouzhong.com/file_share/translation/EasyRSA-Readme v3.08 中文译本.html",
|
||||||
|
"https://lishouzhong.com/file_share/translation/EasyRSA-Upgrade-Notes v3.08 中文译本.html",
|
||||||
|
"https://lishouzhong.com/file_share/translation/iperf3 v3.9 man page 中文译本.html",
|
||||||
|
"https://lishouzhong.com/file_share/translation/PostgreSQL Don't Do This 中文译本.html",
|
||||||
|
"https://lishouzhong.com/file_share/translation/rsync v3.2.7 man(1) page 中文译本.html",
|
||||||
|
],
|
||||||
|
// entryLimit: 10000,
|
||||||
|
// filter: (page) => { },
|
||||||
|
// lastmod: new Date('2022-02-24'),
|
||||||
|
// serialize(item) {
|
||||||
|
// return item;
|
||||||
|
// }
|
||||||
|
}),
|
||||||
|
vue()]
|
||||||
});
|
});
|
||||||
36
content.config.ts
Normal file
36
content.config.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import { z, defineCollection } from 'astro:content';
|
||||||
|
import { glob } from 'astro/loaders';
|
||||||
|
|
||||||
|
const blogCollection = defineCollection({
|
||||||
|
loader: glob({ pattern: '**/[^_]*.{md,mdx}', base: "./src/content/blog" }),
|
||||||
|
schema: z.object({
|
||||||
|
title: z.string(),
|
||||||
|
description: z.string(),
|
||||||
|
category: z.string(),
|
||||||
|
lastUpdate: z.string()
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
const noteCollection = defineCollection({
|
||||||
|
loader: glob({ pattern: '**/[^_]*.{md,mdx}', base: "./src/content/note" }),
|
||||||
|
schema: z.object({
|
||||||
|
title: z.string(),
|
||||||
|
description: z.string(),
|
||||||
|
category: z.string(),
|
||||||
|
lastUpdate: z.string()
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
const translationCollection = defineCollection({
|
||||||
|
loader: glob({ pattern: '**/[^_]*.{md,mdx}', base: "./src/content/translation" }),
|
||||||
|
schema: z.object({
|
||||||
|
title: z.string(),
|
||||||
|
description: z.string(),
|
||||||
|
category: z.string(),
|
||||||
|
lastUpdate: z.string()
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const collections = {
|
||||||
|
'blog': blogCollection,
|
||||||
|
'note': noteCollection,
|
||||||
|
'translation': translationCollection,
|
||||||
|
};
|
||||||
7241
package-lock.json
generated
7241
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
19
package.json
19
package.json
@@ -3,16 +3,23 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "astro dev",
|
"dev": "astro dev --host",
|
||||||
"start": "astro dev",
|
"start": "astro dev --host",
|
||||||
"build": "astro check && astro build",
|
"build": "astro check && astro build",
|
||||||
"preview": "astro preview",
|
"preview": "astro preview",
|
||||||
"astro": "astro"
|
"astro": "astro"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/check": "^0.7.0",
|
"@astrojs/check": "^0.9.4",
|
||||||
"@astrojs/sitemap": "^3.1.5",
|
"@astrojs/sitemap": "^3.6.0",
|
||||||
"astro": "^4.9.2",
|
"@astrojs/vue": "^5.1.3",
|
||||||
"typescript": "^5.4.5"
|
"astro": "^5.15.9",
|
||||||
|
"bignumber.js": "^9.3.1",
|
||||||
|
"pinyin-pro": "^3.27.0",
|
||||||
|
"prettier": "^3.7.4",
|
||||||
|
"prettier-plugin-astro": "^0.14.1",
|
||||||
|
"remark-toc": "^9.0.0",
|
||||||
|
"typescript": "^5.8.2",
|
||||||
|
"vue": "^3.5.13"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,27 @@
|
|||||||
# cron
|
# cron
|
||||||
# 0 4 * * * . /etc/profile; /bin/sh /opt/ld-site/script/build_on_linux.sh >/dev/null 2>&1
|
# 0 4 * * * . /etc/profile; /bin/sh /opt/ld-site/script/build_on_linux.sh >/dev/null 2>&1
|
||||||
|
|
||||||
export PATH=$PATH:/opt/dev_tools/node-v20.12.2-linux-x64/bin
|
NVM_DIR="/opt/dev_tools/nvm"
|
||||||
|
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
|
||||||
|
|
||||||
script_path="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
|
script_path="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
|
||||||
original_article_home="/home/ld/Documents/ld_article"
|
original_article_home="/home/ld/Documents/ld_article"
|
||||||
original_article_destination="/opt/ld-site/src/content"
|
original_article_destination="/opt/ld-site/src/content"
|
||||||
ld_site_dist_target="/opt/nginx_targets/ld_site_dist"
|
ld_site_dist_target="/opt/nginx_targets/ld_site_dist"
|
||||||
|
translation_article_path="/opt/nginx_targets/file_share/translation"
|
||||||
|
|
||||||
rsync -a "${original_article_home}/" "${original_article_destination}/"
|
rsync -a --delete-after -f"P config.ts" \
|
||||||
|
"${original_article_home}/" \
|
||||||
|
"${original_article_destination}/"
|
||||||
|
|
||||||
|
cd "${original_article_destination}/translation"
|
||||||
|
makeinfo --html --no-split --css-include=theme.css ./*.texi
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "cannot generate translation article, check 'makeinfo' command output."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
rm -rf "${translation_article_path}/*.html"
|
||||||
|
mv ./*.html "${translation_article_path}/"
|
||||||
|
|
||||||
cd "${script_path}/../"
|
cd "${script_path}/../"
|
||||||
npm run build
|
npm run build
|
||||||
@@ -19,6 +32,6 @@ if [ $? -ne 0 ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
mv ${ld_site_dist_target} "${ld_site_dist_target}-old"
|
mv ${ld_site_dist_target} "${ld_site_dist_target}_old"
|
||||||
mv dist ${ld_site_dist_target}
|
mv dist ${ld_site_dist_target}
|
||||||
rm -rf "${ld_site_dist_target}-old" ${original_article_destination}
|
rm -rf "${ld_site_dist_target}_old"
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
---
|
---
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
@@ -10,8 +11,8 @@
|
|||||||
<div>
|
<div>
|
||||||
<small class="licence">若正文中无特殊说明,本站内容遵循:</small>
|
<small class="licence">若正文中无特殊说明,本站内容遵循:</small>
|
||||||
<small class="licence">
|
<small class="licence">
|
||||||
<a href="http://creativecommons.org/licenses/by-nc-sa/4.0/">
|
<a href="https://creativecommons.org/licenses/by-nc/4.0/" target="_blank">
|
||||||
署名-非商业使用-相同方式共享 4.0 国际许可协议
|
署名-非商业性使用 4.0 协议国际版
|
||||||
</a>
|
</a>
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ const { title } = Astro.props;
|
|||||||
|
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width" />
|
||||||
<link rel="icon" href="/favicon.ico" type="image/x-icon">
|
<link rel="icon" href="/favicon.ico" type="image/x-icon" />
|
||||||
<meta name="generator" content={Astro.generator} />
|
<meta name="generator" content={Astro.generator} />
|
||||||
<link rel="sitemap" href="/sitemap-index.xml" />
|
<link rel="sitemap" href="/sitemap-index.xml" />
|
||||||
<title>{title}</title>
|
<title>{title}</title>
|
||||||
|
|||||||
@@ -1,48 +1,37 @@
|
|||||||
---
|
---
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<div class="nav" id="nav">
|
<div class="nav" id="nav">
|
||||||
<span class="nav-item site-title" id="site-title">
|
<div class="global-nav">
|
||||||
<a class="nav-link" href="/">李守中</a>
|
<span class="site-title"><a class="nav-link" href="/">李守中</a></span>
|
||||||
</span>
|
<span class="category-item">
|
||||||
<span class="nav-item" id="category">
|
<a class="nav-link" href="/article/blog/">博客</a>
|
||||||
<ul class="nav-list">
|
</span>
|
||||||
<li class="nav-list-item">
|
<span class="category-item">
|
||||||
<a class="nav-link" href="/article/blog/">博客</a>
|
<a class="nav-link" href="/article/note/">笔记</a>
|
||||||
</li>
|
</span>
|
||||||
<li class="nav-list-item">
|
<span class="category-item">
|
||||||
<a class="nav-link" href="/article/note/">笔记</a>
|
<a class="nav-link" href="/article/translation/">翻译</a>
|
||||||
</li>
|
</span>
|
||||||
<li class="nav-list-item">
|
<span class="category-item">
|
||||||
<a class="nav-link" href="/article/translation/">翻译</a>
|
<a class="nav-link" href="/tool/">工具</a>
|
||||||
</li>
|
</span>
|
||||||
</ul>
|
</div>
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.nav {
|
.nav {
|
||||||
font-size: 1em;
|
margin: 0.2em 0 0 0;
|
||||||
margin: 10px 0 0 0;
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-end;
|
|
||||||
}
|
}
|
||||||
#site-title {
|
.site-title {
|
||||||
font-size: 1.8em;
|
font-size: 1.8em;
|
||||||
margin: 0 20px 0 0;
|
margin: 0 10px 0 0;
|
||||||
padding: 0 0 2px 0;
|
|
||||||
line-height: 100%;
|
|
||||||
}
|
}
|
||||||
.nav-list {
|
.category-item {
|
||||||
padding: 0;
|
margin: 0 10px 0 0;
|
||||||
margin: 6px 0 0 0;
|
|
||||||
}
|
}
|
||||||
.nav-list .nav-list-item {
|
.global-nav:last-child {
|
||||||
float: left;
|
|
||||||
list-style-type: none;
|
|
||||||
margin: 0 20px 0 0;
|
|
||||||
}
|
|
||||||
.nav-list .nav-list-item:last-child {
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
.nav-link {
|
.nav-link {
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ const { collectionName, categoryName, posts } = Astro.props;
|
|||||||
{
|
{
|
||||||
posts.map((post) => (
|
posts.map((post) => (
|
||||||
<li>
|
<li>
|
||||||
<a href={`/article/${collectionName}/${post.slug}`} target="_blank">
|
{/* Hide .md extension from URL */}
|
||||||
|
<a href={`/article/${collectionName}/${post.id.replace(".md", "")}`} target="_blank">
|
||||||
{post.data.title}
|
{post.data.title}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -3,11 +3,14 @@ import type { CollectionEntry } from "astro:content";
|
|||||||
import { getCollection, type CollectionKey } from "astro:content";
|
import { getCollection, type CollectionKey } from "astro:content";
|
||||||
import CategorySummary from "./CategorySummary.astro";
|
import CategorySummary from "./CategorySummary.astro";
|
||||||
|
|
||||||
|
import * as pinyinpro from "pinyin-pro";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
collectionName: CollectionKey;
|
collectionName: CollectionKey;
|
||||||
sortedByTimeline: boolean;
|
sortByTimeline: boolean;
|
||||||
|
sortByArticleTitle: boolean;
|
||||||
}
|
}
|
||||||
const { collectionName, sortedByTimeline } = Astro.props;
|
const { collectionName, sortByTimeline, sortByArticleTitle } = Astro.props;
|
||||||
|
|
||||||
type CategoriedPosts = {
|
type CategoriedPosts = {
|
||||||
[categoryName: string]: Array<CollectionEntry<CollectionKey>>;
|
[categoryName: string]: Array<CollectionEntry<CollectionKey>>;
|
||||||
@@ -20,33 +23,57 @@ const allCategories: Array<string> = Array.from(
|
|||||||
new Set(
|
new Set(
|
||||||
allPosts.map((entry) => {
|
allPosts.map((entry) => {
|
||||||
return entry.data.category;
|
return entry.data.category;
|
||||||
})
|
}),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
const categoriedPostList: Array<CategoriedPosts> = allCategories.map((key) => {
|
const categoriedPostList: Array<CategoriedPosts> = allCategories.map((key) => {
|
||||||
let object: CategoriedPosts = {};
|
let object: CategoriedPosts = {};
|
||||||
object[key] = allPosts.filter((post) => post.data.category === key);
|
object[key] = allPosts.filter((post) => post.data.category === key);
|
||||||
sortedByTimeline
|
|
||||||
? object[key].sort(
|
// article sort function by article title
|
||||||
(
|
function sortPostByTitlePinyin(
|
||||||
postA: CollectionEntry<CollectionKey>,
|
postA: CollectionEntry<CollectionKey>,
|
||||||
postB: CollectionEntry<CollectionKey>
|
postB: CollectionEntry<CollectionKey>,
|
||||||
) => {
|
): number {
|
||||||
if (postA.data.lastUpdate !== postB.data.lastUpdate) {
|
let postATitle = pinyinpro.convert(pinyinpro.pinyin(postA.data.title), {
|
||||||
return postA.data.lastUpdate < postB.data.lastUpdate ? 1 : -1;
|
format: "symbolToNum",
|
||||||
}
|
});
|
||||||
return postA.id.localeCompare(postB.id, 'zh-Hans-CN', { sensitivity: 'accent' });
|
let postBTitle = pinyinpro.convert(pinyinpro.pinyin(postB.data.title), {
|
||||||
}
|
format: "symbolToNum",
|
||||||
)
|
});
|
||||||
: object[key].sort(
|
return postATitle.localeCompare(postBTitle, "en");
|
||||||
(
|
}
|
||||||
postA: CollectionEntry<CollectionKey>,
|
|
||||||
postB: CollectionEntry<CollectionKey>
|
if (sortByTimeline) {
|
||||||
) => {
|
// sort article by update date
|
||||||
return postA.id.localeCompare(postB.id);
|
object[key].sort((postA, postB) => {
|
||||||
}
|
// if two articles is updated on the same day, sort them by title
|
||||||
);
|
if (postA.data.lastUpdate !== postB.data.lastUpdate) {
|
||||||
|
return postA.data.lastUpdate < postB.data.lastUpdate ? 1 : -1;
|
||||||
|
}
|
||||||
|
return sortPostByTitlePinyin(postA, postB);
|
||||||
|
});
|
||||||
|
} else if (sortByArticleTitle) {
|
||||||
|
// sort article by title
|
||||||
|
// get articles with titles started in Chinese or the alphabet
|
||||||
|
let postWithAlphabetStartedTitle: CollectionEntry<CollectionKey>[] =
|
||||||
|
object[key].filter((post) => /^[a-zA-Z]/.test(post.data.title));
|
||||||
|
let postWithChineseStartedTitle: CollectionEntry<CollectionKey>[] =
|
||||||
|
object[key].filter((post) => /^[^a-zA-Z]/.test(post.data.title));
|
||||||
|
// sort two Array by article title
|
||||||
|
postWithAlphabetStartedTitle.sort((postA, postB) =>
|
||||||
|
sortPostByTitlePinyin(postA, postB),
|
||||||
|
);
|
||||||
|
postWithChineseStartedTitle.sort((postA, postB) =>
|
||||||
|
sortPostByTitlePinyin(postA, postB),
|
||||||
|
);
|
||||||
|
// articles that have title start with Chinese prior to
|
||||||
|
// those which have titles start with the alphabet
|
||||||
|
object[key] = postWithChineseStartedTitle.concat(
|
||||||
|
postWithAlphabetStartedTitle,
|
||||||
|
);
|
||||||
|
}
|
||||||
return object;
|
return object;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -56,7 +83,7 @@ const categoriedPostListSortedByArticleAmount: Array<CategoriedPosts> =
|
|||||||
const aKey: string = Object.keys(a)[0];
|
const aKey: string = Object.keys(a)[0];
|
||||||
const bKey: string = Object.keys(b)[0];
|
const bKey: string = Object.keys(b)[0];
|
||||||
return b[bKey].length - a[aKey].length;
|
return b[bKey].length - a[aKey].length;
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -75,17 +102,6 @@ const categoriedPostListSortedByArticleAmount: Array<CategoriedPosts> =
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@media screen and (max-width: 125ex) {
|
|
||||||
.collection-desc-item {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media screen and (min-width: 125ex) {
|
|
||||||
.collection-desc-item {
|
|
||||||
width: 50%;
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.collection-desc::after {
|
.collection-desc::after {
|
||||||
display: block;
|
display: block;
|
||||||
content: "";
|
content: "";
|
||||||
|
|||||||
79
src/components/article/article_body/ArticleBody.astro
Normal file
79
src/components/article/article_body/ArticleBody.astro
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
---
|
||||||
|
// get rendered ArticleBodyContent HTML
|
||||||
|
const html = await Astro.slots.render("default");
|
||||||
|
|
||||||
|
// rule:
|
||||||
|
// 1. If a line belongs to the area where the line break should be kept,
|
||||||
|
// do not process this line.
|
||||||
|
// 2. If a line ends with [a-zA-Z], add a space to the end of this line.
|
||||||
|
// 3. If a line ends without [a-zA-Z] and its next line starts with [a-zA-Z],
|
||||||
|
// add a heading space to the next line.
|
||||||
|
// 4. If a line does not start in ASCII chars, join it to its previous line.
|
||||||
|
// 5. If a line starts with <strong> or <em> or <a>, add a heading space to
|
||||||
|
// this line.
|
||||||
|
// 6. If a line ends with </strong> or </em> or </a>, add a tailing space to
|
||||||
|
// this line.
|
||||||
|
// This is important in using one newline character to wrap Chinese.
|
||||||
|
const arr = html.split("\n");
|
||||||
|
let articleHTMLFinal = arr[0];
|
||||||
|
let remainIntactArea = false;
|
||||||
|
// The first line of the HTML string is a table-of-content head recognized by
|
||||||
|
// remark-toc so it can be ignored.
|
||||||
|
for (let i = 1; i < arr.length; i++) {
|
||||||
|
// rule 1
|
||||||
|
// Check if the current line belongs to some block area.
|
||||||
|
if (arr[i].match(/^(<pre|<code|<blockquote|<table)/) !== null) {
|
||||||
|
remainIntactArea = true;
|
||||||
|
} else {
|
||||||
|
remainIntactArea = false;
|
||||||
|
}
|
||||||
|
// -------- add space
|
||||||
|
// rule 2
|
||||||
|
// If the last character of the current line is [a-zA-Z], add a space to the
|
||||||
|
// end of the line.
|
||||||
|
if (arr[i].charAt(arr[i].length - 1).match(/[a-zA-Z]/) !== null) {
|
||||||
|
arr[i] += " ";
|
||||||
|
}
|
||||||
|
// rule 3
|
||||||
|
// The current is not the last line.
|
||||||
|
// AND
|
||||||
|
// The current line ends without [a-zA-Z].
|
||||||
|
// AND
|
||||||
|
// The next line starts with [a-zA-Z].
|
||||||
|
if (
|
||||||
|
i + 1 < arr.length &&
|
||||||
|
arr[i].charAt(arr[i].length - 1).match(/[a-zA-Z]/) === null &&
|
||||||
|
arr[i + 1].charAt(0).match(/[a-zA-Z]/) !== null
|
||||||
|
) {
|
||||||
|
arr[i] += " ";
|
||||||
|
}
|
||||||
|
// rule 5
|
||||||
|
if (arr[i].match(/^(<em>|<strong>|<a>)/) !== null) {
|
||||||
|
arr[i] = " " + arr[i];
|
||||||
|
}
|
||||||
|
// rule 6
|
||||||
|
if (arr[i].match(/(<\/em>|<\/strong>|<\/a>)$/) !== null) {
|
||||||
|
arr[i] = arr[i] + " ";
|
||||||
|
}
|
||||||
|
// -------- combine lines
|
||||||
|
// rule 4
|
||||||
|
if (
|
||||||
|
// (
|
||||||
|
// If the first character is not ascii character,
|
||||||
|
// OR
|
||||||
|
// the final character of previous line is not ascii character.
|
||||||
|
// ) AND
|
||||||
|
// Current line should not belong to area that remains intact.
|
||||||
|
(arr[i].charAt(0).match(/[ -~]/) === null ||
|
||||||
|
arr[i - 1].charAt(arr[i - 1].length - 1).match(/[ -~]/) === null) &&
|
||||||
|
!remainIntactArea
|
||||||
|
) {
|
||||||
|
articleHTMLFinal += arr[i];
|
||||||
|
} else {
|
||||||
|
articleHTMLFinal += "\n";
|
||||||
|
articleHTMLFinal += arr[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
<Fragment set:html={articleHTMLFinal} />
|
||||||
@@ -1,15 +1,19 @@
|
|||||||
---
|
---
|
||||||
import { getEntry, type CollectionKey } from "astro:content";
|
import { getCollection, type CollectionEntry } from "astro:content";
|
||||||
|
import { render, type CollectionKey } from "astro:content";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
articleSlug: string;
|
articleId: string;
|
||||||
collectionName: CollectionKey;
|
collectionName: CollectionKey;
|
||||||
}
|
}
|
||||||
const { articleSlug, collectionName } = Astro.props;
|
const { articleId, collectionName } = Astro.props;
|
||||||
|
|
||||||
const article = await getEntry(collectionName, articleSlug);
|
const articles = await getCollection(collectionName);
|
||||||
const { Content } = await article!.render();
|
const article = articles.find(
|
||||||
const articlePrettyName = article!.id.split("/").pop()!.replace(".md", "");
|
(post: CollectionEntry<CollectionKey>) => post.id === articleId
|
||||||
|
);
|
||||||
|
const { Content } = await render(article!);
|
||||||
|
const articlePrettyName = article!.data.title;
|
||||||
---
|
---
|
||||||
|
|
||||||
<div class="article">
|
<div class="article">
|
||||||
@@ -45,10 +49,14 @@ const articlePrettyName = article!.id.split("/").pop()!.replace(".md", "");
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
/* reduce indent of TOC list */
|
/* reduce indent of TOC list */
|
||||||
:global(.article-title + ul) {
|
:global(.article-title ~ ul:nth-of-type(1)) {
|
||||||
margin: 1em 0 1em 0;
|
margin: 1em 0 1em 0;
|
||||||
padding: 0 0 0 20px;
|
padding: 0 0 0 20px;
|
||||||
}
|
}
|
||||||
|
/* hide TOC headint */
|
||||||
|
:global(.article-title ~ h2:nth-of-type(1)) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
/* font for all code */
|
/* font for all code */
|
||||||
:global(.article code) {
|
:global(.article code) {
|
||||||
font-family: "Consolas", "Tahoma", sans-serif;
|
font-family: "Consolas", "Tahoma", sans-serif;
|
||||||
@@ -68,4 +76,15 @@ const articlePrettyName = article!.id.split("/").pop()!.replace(".md", "");
|
|||||||
:global(.article li > code) {
|
:global(.article li > code) {
|
||||||
color: #be4750;
|
color: #be4750;
|
||||||
}
|
}
|
||||||
|
:global(.article table) {
|
||||||
|
margin: 1em 0;
|
||||||
|
}
|
||||||
|
:global(.article blockquote) {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0 1em;
|
||||||
|
background: rgb(246, 246, 246);
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
18
src/components/article/article_body/ArticleBodyWrapper.astro
Normal file
18
src/components/article/article_body/ArticleBodyWrapper.astro
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
import type { CollectionKey } from "astro:content";
|
||||||
|
import ArticleBody from "./ArticleBody.astro";
|
||||||
|
import ArticleBodyContent from "./ArticleBodyContent.astro";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
articleId: string;
|
||||||
|
collectionName: CollectionKey;
|
||||||
|
}
|
||||||
|
const { articleId, collectionName } = Astro.props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<ArticleBody>
|
||||||
|
<ArticleBodyContent
|
||||||
|
articleId={articleId}
|
||||||
|
collectionName={collectionName}
|
||||||
|
/>
|
||||||
|
</ArticleBody>
|
||||||
20
src/components/tool/ToolNav.astro
Normal file
20
src/components/tool/ToolNav.astro
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<ul class="tool-list">
|
||||||
|
<li class="tool-item"><a href="/tool/unit_conversion">单位换算</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.tool-list {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0.5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-list li {
|
||||||
|
float: left;
|
||||||
|
margin: 0 16px 0 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
231
src/components/tool/unit_conversion/DataUnitConversion.vue
Normal file
231
src/components/tool/unit_conversion/DataUnitConversion.vue
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
<script setup>
|
||||||
|
import { computed, ref } from "vue";
|
||||||
|
import BigNumber from "bignumber.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {string} UnitNameStr
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @enum {UnitNameStr}
|
||||||
|
*/
|
||||||
|
const UnitName = Object.freeze({
|
||||||
|
KBYTE: "unit-k-byte",
|
||||||
|
MBYTE: "unit-m-byte",
|
||||||
|
GBYTE: "unit-g-byte",
|
||||||
|
TBYTE: "unit-t-byte",
|
||||||
|
KIBYTE: "unit-ki-byte",
|
||||||
|
MIBYTE: "unit-mi-byte",
|
||||||
|
GIBYTE: "unit-gi-byte",
|
||||||
|
TIBYTE: "unit-ti-byte",
|
||||||
|
BYTE: "unit-byte",
|
||||||
|
BIT: "unit-bit",
|
||||||
|
KBIT: "unit-k-bit",
|
||||||
|
MBIT: "unit-m-bit",
|
||||||
|
GBIT: "unit-g-bit",
|
||||||
|
TBIT: "unit-t-bit",
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @enum {number}
|
||||||
|
*/
|
||||||
|
const UnitNameToSize = Object.freeze({
|
||||||
|
[UnitName.KBYTE]: 1000,
|
||||||
|
[UnitName.MBYTE]: 1000 * 1000,
|
||||||
|
[UnitName.GBYTE]: 1000 * 1000 * 1000,
|
||||||
|
[UnitName.TBYTE]: 1000 * 1000 * 1000 * 1000,
|
||||||
|
[UnitName.KIBYTE]: 1024,
|
||||||
|
[UnitName.MIBYTE]: 1024 * 1024,
|
||||||
|
[UnitName.GIBYTE]: 1024 * 1024 * 1024,
|
||||||
|
[UnitName.TIBYTE]: 1024 * 1024 * 1024 * 1024,
|
||||||
|
[UnitName.BYTE]: 1,
|
||||||
|
[UnitName.BIT]: 1 / 8,
|
||||||
|
[UnitName.KBIT]: 1000 / 8,
|
||||||
|
[UnitName.MBIT]: 1000000 / 8,
|
||||||
|
[UnitName.GBIT]: 1000000000 / 8,
|
||||||
|
[UnitName.TBIT]: 1000000000000 / 8,
|
||||||
|
});
|
||||||
|
|
||||||
|
const size = ref({
|
||||||
|
[UnitName.KBYTE]: "",
|
||||||
|
[UnitName.MBYTE]: "",
|
||||||
|
[UnitName.GBYTE]: "",
|
||||||
|
[UnitName.TBYTE]: "",
|
||||||
|
[UnitName.KIBYTE]: "",
|
||||||
|
[UnitName.MIBYTE]: "",
|
||||||
|
[UnitName.GIBYTE]: "",
|
||||||
|
[UnitName.TIBYTE]: "",
|
||||||
|
[UnitName.BYTE]: "",
|
||||||
|
[UnitName.BIT]: "",
|
||||||
|
[UnitName.KBIT]: "",
|
||||||
|
[UnitName.MBIT]: "",
|
||||||
|
[UnitName.GBIT]: "",
|
||||||
|
[UnitName.TBIT]: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change ref variable when user inputs some characters.
|
||||||
|
* @param {UnitNameStr} unitName
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
function sizeChanged(unitName) {
|
||||||
|
/**
|
||||||
|
* @type {object.<UnitNameStr, string>}
|
||||||
|
*/
|
||||||
|
let result = {};
|
||||||
|
// assign 0 to current input if nothing is passed
|
||||||
|
if (size.value[unitName].length === 0) result[unitName] = "0";
|
||||||
|
let inputedBytes = new BigNumber(UnitNameToSize[unitName]).multipliedBy(
|
||||||
|
size.value[unitName],
|
||||||
|
);
|
||||||
|
for (const key in UnitNameToSize) {
|
||||||
|
result[key] = inputedBytes.div(UnitNameToSize[key]).toString();
|
||||||
|
}
|
||||||
|
result[unitName] = size.value[unitName];
|
||||||
|
for (const key in UnitNameToSize) {
|
||||||
|
if (result[key] !== "NaN") {
|
||||||
|
size.value[key] = result[key];
|
||||||
|
} else if (result[key] === "NaN") {
|
||||||
|
size.value[key] = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const isMultipleOf4KiB = computed(() => {
|
||||||
|
let userInput = new BigNumber(size.value[UnitName.KIBYTE]);
|
||||||
|
if (userInput.toNumber() === 0) return false;
|
||||||
|
return userInput.mod(4).toNumber() === 0 ? true : false;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<h1>数据单位换算</h1>
|
||||||
|
<div class="unit-conversion-computation-area 1000-in-byte">
|
||||||
|
<p>1000 进制并以 Byte 计:</p>
|
||||||
|
<div class="unit-conversion-computation-node">
|
||||||
|
<input class="size-text" :id="UnitName.KBYTE" type="text" v-model="size[UnitName.KBYTE]"
|
||||||
|
@input="sizeChanged(UnitName.KBYTE)" />
|
||||||
|
<span class="unit-name">KB</span>
|
||||||
|
</div>
|
||||||
|
<div class="unit-conversion-computation-node">
|
||||||
|
<input class="size-text" :id="UnitName.MBYTE" type="text" v-model="size[UnitName.MBYTE]"
|
||||||
|
@input="sizeChanged(UnitName.MBYTE)" />
|
||||||
|
<span class="unit-name">MB</span>
|
||||||
|
</div>
|
||||||
|
<div class="unit-conversion-computation-node">
|
||||||
|
<input class="size-text" :id="UnitName.GBYTE" type="text" v-model="size[UnitName.GBYTE]"
|
||||||
|
@input="sizeChanged(UnitName.GBYTE)" />
|
||||||
|
<span class="unit-name">GB</span>
|
||||||
|
</div>
|
||||||
|
<div class="unit-conversion-computation-node">
|
||||||
|
<input class="size-text" :id="UnitName.TBYTE" type="text" v-model="size[UnitName.TBYTE]"
|
||||||
|
@input="sizeChanged(UnitName.TBYTE)" />
|
||||||
|
<span class="unit-name">TB</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="unit-conversion-computation-area 1024-in-byte">
|
||||||
|
<p>1024 进制并以 Byte 计:</p>
|
||||||
|
<div class="unit-conversion-computation-node">
|
||||||
|
<input class="size-text" :id="UnitName.KIBYTE" type="text" v-model="size[UnitName.KIBYTE]"
|
||||||
|
@input="sizeChanged(UnitName.KIBYTE)" />
|
||||||
|
<span class="unit-name">KiB</span>
|
||||||
|
</div>
|
||||||
|
<div class="unit-conversion-computation-node">
|
||||||
|
<input class="size-text" :id="UnitName.MIBYTE" type="text" v-model="size[UnitName.MIBYTE]"
|
||||||
|
@input="sizeChanged(UnitName.MIBYTE)" />
|
||||||
|
<span class="unit-name">MiB</span>
|
||||||
|
</div>
|
||||||
|
<div class="unit-conversion-computation-node">
|
||||||
|
<input class="size-text" :id="UnitName.GIBYTE" type="text" v-model="size[UnitName.GIBYTE]"
|
||||||
|
@input="sizeChanged(UnitName.GIBYTE)" />
|
||||||
|
<span class="unit-name">GiB</span>
|
||||||
|
</div>
|
||||||
|
<div class="unit-conversion-computation-node">
|
||||||
|
<input class="size-text" :id="UnitName.TIBYTE" type="text" v-model="size[UnitName.TIBYTE]"
|
||||||
|
@input="sizeChanged(UnitName.TIBYTE)" />
|
||||||
|
<span class="unit-name">TiB</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="unit-conversion-computation-area byte-bit">
|
||||||
|
<p>以 Byte (B) / bit (b) 计:</p>
|
||||||
|
<div class="unit-conversion-computation-node">
|
||||||
|
<input class="size-text" :id="UnitName.BYTE" type="text" v-model="size[UnitName.BYTE]"
|
||||||
|
@input="sizeChanged(UnitName.BYTE)" />
|
||||||
|
<span class="unit-name">B</span>
|
||||||
|
</div>
|
||||||
|
<div class="unit-conversion-computation-node">
|
||||||
|
<input class="size-text" :id="UnitName.BIT" type="text" v-model="size[UnitName.BIT]"
|
||||||
|
@input="sizeChanged(UnitName.BIT)" />
|
||||||
|
<span class="unit-name">b</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="unit-conversion-computation-area 1000-in-bit">
|
||||||
|
<p>1000 进制并以 bit 计:</p>
|
||||||
|
<div class="unit-conversion-computation-node">
|
||||||
|
<input class="size-text" :id="UnitName.KBIT" type="text" v-model="size[UnitName.KBIT]"
|
||||||
|
@input="sizeChanged(UnitName.KBIT)" />
|
||||||
|
<span class="unit-name">Kb</span>
|
||||||
|
</div>
|
||||||
|
<div class="unit-conversion-computation-node">
|
||||||
|
<input class="size-text" :id="UnitName.MBIT" type="text" v-model="size[UnitName.MBIT]"
|
||||||
|
@input="sizeChanged(UnitName.MBIT)" />
|
||||||
|
<span class="unit-name">Mb</span>
|
||||||
|
</div>
|
||||||
|
<div class="unit-conversion-computation-node">
|
||||||
|
<input class="size-text" :id="UnitName.GBIT" type="text" v-model="size[UnitName.GBIT]"
|
||||||
|
@input="sizeChanged(UnitName.GBIT)" />
|
||||||
|
<span class="unit-name">Gb</span>
|
||||||
|
</div>
|
||||||
|
<div class="unit-conversion-computation-node">
|
||||||
|
<input class="size-text" :id="UnitName.TBIT" type="text" v-model="size[UnitName.TBIT]"
|
||||||
|
@input="sizeChanged(UnitName.TBIT)" />
|
||||||
|
<span class="unit-name">Tb</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="unit-conversion-computation-result">
|
||||||
|
<p v-show="isMultipleOf4KiB">该结果值是 4 KiB 的倍数</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
h1,
|
||||||
|
p {
|
||||||
|
margin: 0.5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.unit-conversion-computation-node {
|
||||||
|
margin: 0.5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.unit-conversion-computation-node {
|
||||||
|
float: left;
|
||||||
|
width: 170px;
|
||||||
|
margin: 0 8px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.unit-conversion-computation-node .size-text {
|
||||||
|
margin: 0 6px 0 0;
|
||||||
|
width: 125px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 395px) {
|
||||||
|
.unit-conversion-computation-node {
|
||||||
|
width: 140px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.unit-conversion-computation-node .size-text {
|
||||||
|
width: 95px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.unit-conversion-computation-area::after {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.unit-conversion-computation-result {
|
||||||
|
margin: 0.5em 0 0.5em 0;
|
||||||
|
height: 1em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
import DataUnitConversion from "./DataUnitConversion.vue";
|
||||||
|
import WeightUnitConversion from "./WeightUnitConversion.vue";
|
||||||
|
---
|
||||||
|
|
||||||
|
<WeightUnitConversion client:load />
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<DataUnitConversion client:load />
|
||||||
152
src/components/tool/unit_conversion/WeightUnitConversion.vue
Normal file
152
src/components/tool/unit_conversion/WeightUnitConversion.vue
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
<script setup>
|
||||||
|
import { computed, ref } from "vue";
|
||||||
|
import BigNumber from "bignumber.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {string} UnitNameStr
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @enum {UnitNameStr}
|
||||||
|
*/
|
||||||
|
const twToMetricUnitName = Object.freeze({
|
||||||
|
JIN: "jin",
|
||||||
|
LIANG: "liang",
|
||||||
|
QIAN: "qian",
|
||||||
|
FEN: "fen",
|
||||||
|
GRAM: "gram",
|
||||||
|
KILOGRAM: "kilogram",
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @enum {number}
|
||||||
|
*/
|
||||||
|
const twToMetricUnitNameToWeight = Object.freeze({
|
||||||
|
[twToMetricUnitName.JIN]: 600,
|
||||||
|
[twToMetricUnitName.LIANG]: 600 / 16,
|
||||||
|
[twToMetricUnitName.QIAN]: 600 / 16 / 10,
|
||||||
|
[twToMetricUnitName.FEN]: 600 / 16 / 10 / 10,
|
||||||
|
[twToMetricUnitName.GRAM]: 1,
|
||||||
|
[twToMetricUnitName.KILOGRAM]: 1 * 1000,
|
||||||
|
});
|
||||||
|
|
||||||
|
const weight = ref({
|
||||||
|
[twToMetricUnitName.JIN]: "",
|
||||||
|
[twToMetricUnitName.LIANG]: "",
|
||||||
|
[twToMetricUnitName.QIAN]: "",
|
||||||
|
[twToMetricUnitName.FEN]: "",
|
||||||
|
[twToMetricUnitName.GRAM]: "",
|
||||||
|
[twToMetricUnitName.KILOGRAM]: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change ref variable when user inputs some characters.
|
||||||
|
* @param {UnitNameStr} unitName
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
function weightChanged(unitName) {
|
||||||
|
/**
|
||||||
|
* @type {object.<UnitNameStr, string>}
|
||||||
|
*/
|
||||||
|
let result = {};
|
||||||
|
// assign 0 to current input if nothing is passed
|
||||||
|
if (weight.value[unitName].length === 0) result[unitName] = "0";
|
||||||
|
let inputedGram = new BigNumber(
|
||||||
|
twToMetricUnitNameToWeight[unitName],
|
||||||
|
).multipliedBy(weight.value[unitName]);
|
||||||
|
for (const key in twToMetricUnitNameToWeight) {
|
||||||
|
result[key] = inputedGram.div(twToMetricUnitNameToWeight[key]).toString();
|
||||||
|
}
|
||||||
|
result[unitName] = weight.value[unitName];
|
||||||
|
for (const key in twToMetricUnitNameToWeight) {
|
||||||
|
if (result[key] !== "NaN") {
|
||||||
|
weight.value[key] = result[key];
|
||||||
|
} else if (result[key] === "NaN") {
|
||||||
|
weight.value[key] = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<h1>重量换算</h1>
|
||||||
|
<div class="weight-conversion-computation-area metric">
|
||||||
|
<p>公制重量:</p>
|
||||||
|
<div class="weight-conversion-computation-node">
|
||||||
|
<input class="size-text" :id="twToMetricUnitName.KILOGRAM" type="text"
|
||||||
|
v-model="weight[twToMetricUnitName.KILOGRAM]" @input="weightChanged(twToMetricUnitName.KILOGRAM)" />
|
||||||
|
<span class="unit-name">千克</span>
|
||||||
|
</div>
|
||||||
|
<div class="weight-conversion-computation-node">
|
||||||
|
<input class="size-text" :id="twToMetricUnitName.GRAM" type="text" v-model="weight[twToMetricUnitName.GRAM]"
|
||||||
|
@input="weightChanged(twToMetricUnitName.GRAM)" />
|
||||||
|
<span class="unit-name">克</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="weight-conversion-computation-area tw">
|
||||||
|
<p>台湾制重量:</p>
|
||||||
|
<div class="weight-conversion-computation-node">
|
||||||
|
<input class="size-text" :id="twToMetricUnitName.JIN" type="text" v-model="weight[twToMetricUnitName.JIN]"
|
||||||
|
@input="weightChanged(twToMetricUnitName.JIN)" />
|
||||||
|
<span class="unit-name">斤</span>
|
||||||
|
</div>
|
||||||
|
<div class="weight-conversion-computation-node">
|
||||||
|
<input class="size-text" :id="twToMetricUnitName.LIANG" type="text" v-model="weight[twToMetricUnitName.LIANG]"
|
||||||
|
@input="weightChanged(twToMetricUnitName.LIANG)" />
|
||||||
|
<span class="unit-name">两</span>
|
||||||
|
</div>
|
||||||
|
<div class="weight-conversion-computation-node">
|
||||||
|
<input class="size-text" :id="twToMetricUnitName.QIAN" type="text" v-model="weight[twToMetricUnitName.QIAN]"
|
||||||
|
@input="weightChanged(twToMetricUnitName.QIAN)" />
|
||||||
|
<span class="unit-name">钱</span>
|
||||||
|
</div>
|
||||||
|
<div class="weight-conversion-computation-node">
|
||||||
|
<input class="size-text" :id="twToMetricUnitName.FEN" type="text" v-model="weight[twToMetricUnitName.FEN]"
|
||||||
|
@input="weightChanged(twToMetricUnitName.FEN)" />
|
||||||
|
<span class="unit-name">分</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
h1,
|
||||||
|
p {
|
||||||
|
margin: 0.5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weight-conversion-computation-node {
|
||||||
|
margin: 0.5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weight-conversion-computation-node {
|
||||||
|
float: left;
|
||||||
|
width: 170px;
|
||||||
|
margin: 0 8px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weight-conversion-computation-node .size-text {
|
||||||
|
margin: 0 6px 0 0;
|
||||||
|
width: 115px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 395px) {
|
||||||
|
.weight-conversion-computation-node {
|
||||||
|
width: 140px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weight-conversion-computation-node .size-text {
|
||||||
|
width: 85px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.weight-conversion-computation-area::after {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weight-conversion-computation-result {
|
||||||
|
margin: 0.5em 0 0.5em 0;
|
||||||
|
height: 1em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
import HTMLCommonHead from "../components/HTMLCommonHead.astro";
|
import HTMLCommonHead from "../components/HTMLCommonHead.astro";
|
||||||
import "../styles/global.css"
|
import "../styles/global.css";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title: string;
|
title: string;
|
||||||
@@ -11,7 +11,7 @@ const { title } = Astro.props;
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="zh">
|
<html lang="zh">
|
||||||
<head>
|
<head>
|
||||||
<HTMLCommonHead title={title}/>
|
<HTMLCommonHead title={title} />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<slot />
|
<slot />
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
import DefaultLayout from "../layouts/DefaultLayout.astro";
|
import DefaultLayout from "../layouts/DefaultLayout.astro";
|
||||||
import Nav from "../components/Nav.astro";
|
import Nav from "../components/Nav.astro";
|
||||||
import Footer from "../components/Footer.astro";
|
import Footer from "../components/Footer.astro";
|
||||||
import ArticleBody from "../components/article/ArticleBody.astro";
|
import ArticleBodyWrapper from "../components/article/article_body/ArticleBodyWrapper.astro";
|
||||||
---
|
---
|
||||||
|
|
||||||
<DefaultLayout title="404">
|
<DefaultLayout title="404">
|
||||||
<Nav />
|
<Nav />
|
||||||
<ArticleBody collectionName="site" articleSlug="404" />
|
<ArticleBodyWrapper collectionName="site" articleId="404.md" />
|
||||||
<Footer />
|
<Footer />
|
||||||
</DefaultLayout>
|
</DefaultLayout>
|
||||||
|
|||||||
@@ -1,34 +1,31 @@
|
|||||||
---
|
---
|
||||||
import { getCollection } from "astro:content";
|
import { getCollection } from "astro:content";
|
||||||
import type {
|
import type { CollectionEntry, CollectionKey } from "astro:content";
|
||||||
CollectionEntry,
|
|
||||||
CollectionKey,
|
|
||||||
ContentEntryMap,
|
|
||||||
} from "astro:content";
|
|
||||||
import Footer from "../../../components/Footer.astro";
|
import Footer from "../../../components/Footer.astro";
|
||||||
import Nav from "../../../components/Nav.astro";
|
import Nav from "../../../components/Nav.astro";
|
||||||
import ArticleBody from "../../../components/article/ArticleBody.astro";
|
import ArticleBodyWrapper from "../../../components/article/article_body/ArticleBodyWrapper.astro";
|
||||||
import DefaultLayout from "../../../layouts/DefaultLayout.astro";
|
import DefaultLayout from "../../../layouts/DefaultLayout.astro";
|
||||||
|
|
||||||
export async function getStaticPaths() {
|
export async function getStaticPaths() {
|
||||||
const collectionName: CollectionKey = "blog";
|
const collectionName: CollectionKey = "blog";
|
||||||
const blogEntries = await getCollection(collectionName);
|
const blogEntries = await getCollection(collectionName);
|
||||||
return blogEntries.map((entry) => ({
|
return blogEntries.map((post: CollectionEntry<CollectionKey>) => ({
|
||||||
params: { blog: entry.slug },
|
// Hide .md extension from URL
|
||||||
props: { entry },
|
params: { blog: post.id.replace(".md", "") },
|
||||||
|
props: { post },
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
entry: CollectionEntry<keyof ContentEntryMap>;
|
post: CollectionEntry<CollectionKey>;
|
||||||
}
|
}
|
||||||
const { entry } = Astro.props;
|
const { post } = Astro.props;
|
||||||
|
|
||||||
const articlePrettyName:string = entry.id.split("/").pop()!.replace(".md", "");
|
const articlePrettyName: string = post.data.title;
|
||||||
---
|
---
|
||||||
|
|
||||||
<DefaultLayout title=`${articlePrettyName} - 李守中`>
|
<DefaultLayout title=`${articlePrettyName} - 李守中`>
|
||||||
<Nav />
|
<Nav />
|
||||||
<ArticleBody collectionName="blog" articleSlug={entry.slug} />
|
<ArticleBodyWrapper collectionName="blog" articleId={post.id} />
|
||||||
<Footer />
|
<Footer />
|
||||||
</DefaultLayout>
|
</DefaultLayout>
|
||||||
|
|||||||
@@ -1,21 +1,37 @@
|
|||||||
---
|
---
|
||||||
import Footer from '../../../components/Footer.astro';
|
import Footer from "../../../components/Footer.astro";
|
||||||
import Nav from '../../../components/Nav.astro';
|
import Nav from "../../../components/Nav.astro";
|
||||||
import CollectionSummary from '../../../components/article/CollectionSummary.astro';
|
import CollectionSummary from "../../../components/article/CollectionSummary.astro";
|
||||||
import DefaultLayout from '../../../layouts/DefaultLayout.astro';
|
import DefaultLayout from "../../../layouts/DefaultLayout.astro";
|
||||||
---
|
---
|
||||||
|
|
||||||
<DefaultLayout title="博客 - 李守中">
|
<DefaultLayout title="博客 - 李守中">
|
||||||
<Nav />
|
<Nav />
|
||||||
<div class="options">
|
<div class="options">
|
||||||
<span>当前: [按日期降序] <a href="/article/blog/sort_by_name">按名字排序</a></span>
|
<span>当前: [按日期降序]</span>
|
||||||
|
<a href="/article/blog/sort_by_name">按名字排序</a>
|
||||||
</div>
|
</div>
|
||||||
<CollectionSummary collectionName="blog" sortedByTimeline={true}/>
|
<CollectionSummary
|
||||||
|
collectionName="blog"
|
||||||
|
sortByTimeline={true}
|
||||||
|
sortByArticleTitle={false}
|
||||||
|
/>
|
||||||
<Footer />
|
<Footer />
|
||||||
</DefaultLayout>
|
</DefaultLayout>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
:global(@media screen and (max-width: 768px)) {
|
||||||
|
.collection-desc-item {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:global(@media screen and (min-width: 768px)) {
|
||||||
|
.collection-desc-item {
|
||||||
|
width: 50%;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
.options {
|
.options {
|
||||||
margin: 1em 0 0 0;
|
margin: 0.5em 0 0 0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,21 +1,37 @@
|
|||||||
---
|
---
|
||||||
import Footer from '../../../components/Footer.astro';
|
import Footer from "../../../components/Footer.astro";
|
||||||
import Nav from '../../../components/Nav.astro';
|
import Nav from "../../../components/Nav.astro";
|
||||||
import CollectionSummary from '../../../components/article/CollectionSummary.astro';
|
import CollectionSummary from "../../../components/article/CollectionSummary.astro";
|
||||||
import DefaultLayout from '../../../layouts/DefaultLayout.astro';
|
import DefaultLayout from "../../../layouts/DefaultLayout.astro";
|
||||||
---
|
---
|
||||||
|
|
||||||
<DefaultLayout title="博客 - 李守中">
|
<DefaultLayout title="博客 - 李守中">
|
||||||
<Nav />
|
<Nav />
|
||||||
<div class="options">
|
<div class="options">
|
||||||
<span>当前: [按名字排序] <a href="/article/blog/">按日期降序</a></span>
|
<span>当前: [按名字排序]</span>
|
||||||
|
<a href="/article/blog/">按日期降序</a>
|
||||||
</div>
|
</div>
|
||||||
<CollectionSummary collectionName="blog" sortedByTimeline={false}/>
|
<CollectionSummary
|
||||||
|
collectionName="blog"
|
||||||
|
sortByTimeline={false}
|
||||||
|
sortByArticleTitle={true}
|
||||||
|
/>
|
||||||
<Footer />
|
<Footer />
|
||||||
</DefaultLayout>
|
</DefaultLayout>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
:global(@media screen and (max-width: 768px)) {
|
||||||
|
.collection-desc-item {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:global(@media screen and (min-width: 768px)) {
|
||||||
|
.collection-desc-item {
|
||||||
|
width: 50%;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
.options {
|
.options {
|
||||||
margin: 1em 0 0 0;
|
margin: 0.5em 0 0 0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,34 +1,30 @@
|
|||||||
---
|
---
|
||||||
import { getCollection } from "astro:content";
|
import { getCollection } from "astro:content";
|
||||||
import type {
|
import type { CollectionEntry, CollectionKey } from "astro:content";
|
||||||
CollectionEntry,
|
|
||||||
CollectionKey,
|
|
||||||
ContentEntryMap,
|
|
||||||
} from "astro:content";
|
|
||||||
import Footer from "../../../components/Footer.astro";
|
import Footer from "../../../components/Footer.astro";
|
||||||
import Nav from "../../../components/Nav.astro";
|
import Nav from "../../../components/Nav.astro";
|
||||||
import ArticleBody from "../../../components/article/ArticleBody.astro";
|
import ArticleBodyWrapper from "../../../components/article/article_body/ArticleBodyWrapper.astro";
|
||||||
import DefaultLayout from "../../../layouts/DefaultLayout.astro";
|
import DefaultLayout from "../../../layouts/DefaultLayout.astro";
|
||||||
|
|
||||||
export async function getStaticPaths() {
|
export async function getStaticPaths() {
|
||||||
const collectionName: CollectionKey = "note";
|
const collectionName: CollectionKey = "note";
|
||||||
const blogEntries = await getCollection(collectionName);
|
const blogEntries = await getCollection(collectionName);
|
||||||
return blogEntries.map((entry) => ({
|
return blogEntries.map((post: CollectionEntry<CollectionKey>) => ({
|
||||||
params: { note: entry.slug },
|
params: { note: post.id.replace(".md", "") },
|
||||||
props: { entry },
|
props: { post },
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
entry: CollectionEntry<keyof ContentEntryMap>;
|
post: CollectionEntry<CollectionKey>;
|
||||||
}
|
}
|
||||||
const { entry } = Astro.props;
|
const { post } = Astro.props;
|
||||||
|
|
||||||
const articlePrettyName:string = entry.id.split("/").pop()!.replace(".md", "");
|
const articlePrettyName: string = post.data.title;
|
||||||
---
|
---
|
||||||
|
|
||||||
<DefaultLayout title=`${articlePrettyName} - 李守中`>
|
<DefaultLayout title=`${articlePrettyName} - 李守中`>
|
||||||
<Nav />
|
<Nav />
|
||||||
<ArticleBody collectionName="note" articleSlug={entry.slug} />
|
<ArticleBodyWrapper collectionName="note" articleId={post.id} />
|
||||||
<Footer />
|
<Footer />
|
||||||
</DefaultLayout>
|
</DefaultLayout>
|
||||||
|
|||||||
@@ -1,21 +1,37 @@
|
|||||||
---
|
---
|
||||||
import Footer from '../../../components/Footer.astro';
|
import Footer from "../../../components/Footer.astro";
|
||||||
import Nav from '../../../components/Nav.astro';
|
import Nav from "../../../components/Nav.astro";
|
||||||
import CollectionSummary from '../../../components/article/CollectionSummary.astro';
|
import CollectionSummary from "../../../components/article/CollectionSummary.astro";
|
||||||
import DefaultLayout from '../../../layouts/DefaultLayout.astro';
|
import DefaultLayout from "../../../layouts/DefaultLayout.astro";
|
||||||
---
|
---
|
||||||
|
|
||||||
<DefaultLayout title="笔记 - 李守中">
|
<DefaultLayout title="笔记 - 李守中">
|
||||||
<Nav />
|
<Nav />
|
||||||
<div class="options">
|
<div class="options">
|
||||||
<span>当前: [按名字排序] <a href="/article/note/sort_by_timeline">按日期降序</a></span>
|
<span>当前: [按名字排序]</span>
|
||||||
|
<a href="/article/note/sort_by_timeline">按日期降序</a>
|
||||||
</div>
|
</div>
|
||||||
<CollectionSummary collectionName="note" sortedByTimeline={false}/>
|
<CollectionSummary
|
||||||
|
collectionName="note"
|
||||||
|
sortByTimeline={false}
|
||||||
|
sortByArticleTitle={true}
|
||||||
|
/>
|
||||||
<Footer />
|
<Footer />
|
||||||
</DefaultLayout>
|
</DefaultLayout>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
:global(@media screen and (max-width: 768px)) {
|
||||||
|
.collection-desc-item {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:global(@media screen and (min-width: 768px)) {
|
||||||
|
.collection-desc-item {
|
||||||
|
width: 50%;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
.options {
|
.options {
|
||||||
margin: 1em 0 0 0;
|
margin: 0.5em 0 0 0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,21 +1,37 @@
|
|||||||
---
|
---
|
||||||
import Footer from '../../../components/Footer.astro';
|
import Footer from "../../../components/Footer.astro";
|
||||||
import Nav from '../../../components/Nav.astro';
|
import Nav from "../../../components/Nav.astro";
|
||||||
import CollectionSummary from '../../../components/article/CollectionSummary.astro';
|
import CollectionSummary from "../../../components/article/CollectionSummary.astro";
|
||||||
import DefaultLayout from '../../../layouts/DefaultLayout.astro';
|
import DefaultLayout from "../../../layouts/DefaultLayout.astro";
|
||||||
---
|
---
|
||||||
|
|
||||||
<DefaultLayout title="笔记 - 李守中">
|
<DefaultLayout title="笔记 - 李守中">
|
||||||
<Nav />
|
<Nav />
|
||||||
<div class="options">
|
<div class="options">
|
||||||
<span>当前: [按日期降序] <a href="/article/note/">按名字排序</a></span>
|
<span>当前: [按日期降序]</span>
|
||||||
|
<a href="/article/note/">按名字排序</a>
|
||||||
</div>
|
</div>
|
||||||
<CollectionSummary collectionName="note" sortedByTimeline={true}/>
|
<CollectionSummary
|
||||||
|
collectionName="note"
|
||||||
|
sortByTimeline={true}
|
||||||
|
sortByArticleTitle={false}
|
||||||
|
/>
|
||||||
<Footer />
|
<Footer />
|
||||||
</DefaultLayout>
|
</DefaultLayout>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
:global(@media screen and (max-width: 768px)) {
|
||||||
|
.collection-desc-item {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:global(@media screen and (min-width: 768px)) {
|
||||||
|
.collection-desc-item {
|
||||||
|
width: 50%;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
.options {
|
.options {
|
||||||
margin: 1em 0 0 0;
|
margin: 0.5em 0 0 0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
---
|
|
||||||
import { getCollection } from "astro:content";
|
|
||||||
import type {
|
|
||||||
CollectionEntry,
|
|
||||||
CollectionKey,
|
|
||||||
ContentEntryMap,
|
|
||||||
} from "astro:content";
|
|
||||||
import Footer from "../../../components/Footer.astro";
|
|
||||||
import Nav from "../../../components/Nav.astro";
|
|
||||||
import ArticleBody from "../../../components/article/ArticleBody.astro";
|
|
||||||
import DefaultLayout from "../../../layouts/DefaultLayout.astro";
|
|
||||||
|
|
||||||
export async function getStaticPaths() {
|
|
||||||
const collectionName: CollectionKey = "translation";
|
|
||||||
const blogEntries = await getCollection(collectionName);
|
|
||||||
return blogEntries.map((entry) => ({
|
|
||||||
params: { translation: entry.slug },
|
|
||||||
props: { entry },
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
entry: CollectionEntry<keyof ContentEntryMap>;
|
|
||||||
}
|
|
||||||
const { entry } = Astro.props;
|
|
||||||
|
|
||||||
const articlePrettyName:string = entry.id.split("/").pop()!.replace(".md", "");
|
|
||||||
---
|
|
||||||
|
|
||||||
<DefaultLayout title=`${articlePrettyName} - 李守中`>
|
|
||||||
<Nav />
|
|
||||||
<ArticleBody collectionName="translation" articleSlug={entry.slug} />
|
|
||||||
<Footer />
|
|
||||||
</DefaultLayout>
|
|
||||||
@@ -1,21 +1,172 @@
|
|||||||
---
|
---
|
||||||
import Footer from '../../../components/Footer.astro';
|
import Footer from "../../../components/Footer.astro";
|
||||||
import Nav from '../../../components/Nav.astro';
|
import Nav from "../../../components/Nav.astro";
|
||||||
import CollectionSummary from '../../../components/article/CollectionSummary.astro';
|
import DefaultLayout from "../../../layouts/DefaultLayout.astro";
|
||||||
import DefaultLayout from '../../../layouts/DefaultLayout.astro';
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<DefaultLayout title="翻译 - 李守中">
|
<DefaultLayout title="翻译 - 李守中">
|
||||||
<Nav />
|
<Nav />
|
||||||
<div class="options">
|
<div class="collection-desc">
|
||||||
<span>当前: [按名字排序] <a href="/article/translation/sort_by_timeline">按日期降序</a></span>
|
<div class="collection-desc-item">
|
||||||
|
<h1>freebsd</h1>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="/file_share/translation/FreeBSD 13 NFS exports man(5) page 2021 中文译本.html"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
FreeBSD 13 NFS exports man(5) 2021 中文译本
|
||||||
|
</a>
|
||||||
|
</li><li>
|
||||||
|
<a
|
||||||
|
href="/file_share/translation/FreeBSD 13 NFS mountd man(8) page 2020 中文译本.html"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
FreeBSD 13 NFS mountd man(8) 2020 中文译本
|
||||||
|
</a>
|
||||||
|
</li><li>
|
||||||
|
<a
|
||||||
|
href="/file_share/translation/FreeBSD 13 NFS nfsd man(8) page 2019 中文译本.html"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
FreeBSD 13 NFS nfsd man(8) 2019 中文译本
|
||||||
|
</a>
|
||||||
|
</li><li>
|
||||||
|
<a
|
||||||
|
href="/file_share/translation/FreeBSD 13 NFS showmount man(8) page 2016 中文译本.html"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
FreeBSD 13 NFS showmount man(8) 2016 中文译本
|
||||||
|
</a>
|
||||||
|
</li><li>
|
||||||
|
<a
|
||||||
|
href="/file_share/translation/FreeBSD 13 NFSv4 man(4) page 2019 中文译本.html"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
FreeBSD 13 NFSv4 man(4) 2019 中文译本
|
||||||
|
</a>
|
||||||
|
</li><li>
|
||||||
|
<a
|
||||||
|
href="/file_share/translation/FreeBSD 13 NFSv4 nfscbd man(8) page 2009 中文译本.html"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
FreeBSD 13 NFSv4 nfscbd man(8) 2009 中文译本
|
||||||
|
</a>
|
||||||
|
</li><li>
|
||||||
|
<a
|
||||||
|
href="/file_share/translation/FreeBSD 13 NFSv4 nfsrevoke man(8) page 2009 中文译本.html"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
FreeBSD 13 NFSv4 nfsrevoke man(8) 2020 中文译本
|
||||||
|
</a>
|
||||||
|
</li><li>
|
||||||
|
<a
|
||||||
|
href="/file_share/translation/FreeBSD 13 NFSv4 nfsuserd man(8) page 2019 中文译本.html"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
FreeBSD 13 NFSv4 nfsuserd man(8) 2019 中文译本
|
||||||
|
</a>
|
||||||
|
</li><li>
|
||||||
|
<a
|
||||||
|
href="/file_share/translation/FreeBSD 13 rpcbind man(8) page 2017 中文译本.html"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
FreeBSD 13 rpcbind man(8) 2017 中文译本
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div><div class="collection-desc-item">
|
||||||
|
<h1>easyrsa</h1>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="/file_share/translation/EasyRSA Intro-To-PKI v3.08 中文译本.html"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
EasyRSA Intro-To-PKI v3.08 中文译本
|
||||||
|
</a>
|
||||||
|
</li><li>
|
||||||
|
<a
|
||||||
|
href="/file_share/translation/EasyRSA-Advanced v3.08 中文译本.html"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
EasyRSA-Advanced v3.08 中文译本
|
||||||
|
</a>
|
||||||
|
</li><li>
|
||||||
|
<a
|
||||||
|
href="/file_share/translation/EasyRSA-Readme v3.08 中文译本.html"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
EasyRSA-Readme v3.08 中文译本
|
||||||
|
</a>
|
||||||
|
</li><li>
|
||||||
|
<a
|
||||||
|
href="/file_share/translation/EasyRSA-Upgrade-Notes v3.08 中文译本.html"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
EasyRSA-Upgrade-Notes v3.08 中文译本
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div><div class="collection-desc-item">
|
||||||
|
<h1>uncategorized</h1>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="/file_share/translation/iperf3 v3.9 man page 中文译本.html"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
iperf3 v3.9 man(1) 中文译本
|
||||||
|
</a>
|
||||||
|
</li><li>
|
||||||
|
<a
|
||||||
|
href="/file_share/translation/PostgreSQL Don't Do This 中文译本.html"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
PostgreSQL Don't Do This 中文译本
|
||||||
|
</a>
|
||||||
|
</li><li>
|
||||||
|
<a
|
||||||
|
href="/file_share/translation/rsync v3.2.7 man(1) page 中文译本.html"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
rsync v3.2.7 man(1) 中文译本
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<CollectionSummary collectionName="translation" sortedByTimeline={false}/>
|
|
||||||
<Footer />
|
<Footer />
|
||||||
</DefaultLayout>
|
</DefaultLayout>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
:global(@media screen and (max-width: 1024px)) {
|
||||||
|
.collection-desc-item {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:global(@media screen and (min-width: 1024px)) {
|
||||||
|
.collection-desc-item {
|
||||||
|
width: 50%;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
.options {
|
.options {
|
||||||
margin: 1em 0 0 0;
|
margin: 0.5em 0 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collection-desc::after {
|
||||||
|
display: block;
|
||||||
|
content: "";
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
padding: 0;
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
margin: 0.5em 0 0 0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
---
|
|
||||||
import Footer from '../../../components/Footer.astro';
|
|
||||||
import Nav from '../../../components/Nav.astro';
|
|
||||||
import CollectionSummary from '../../../components/article/CollectionSummary.astro';
|
|
||||||
import DefaultLayout from '../../../layouts/DefaultLayout.astro';
|
|
||||||
---
|
|
||||||
|
|
||||||
<DefaultLayout title="翻译 - 李守中">
|
|
||||||
<Nav />
|
|
||||||
<div class="options">
|
|
||||||
<span>当前: [按日期降序] <a href="/article/translation/">按名字排序</a></span>
|
|
||||||
</div>
|
|
||||||
<CollectionSummary collectionName="translation" sortedByTimeline={true}/>
|
|
||||||
<Footer />
|
|
||||||
</DefaultLayout>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.options {
|
|
||||||
margin: 1em 0 0 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -2,11 +2,11 @@
|
|||||||
import DefaultLayout from "../layouts/DefaultLayout.astro";
|
import DefaultLayout from "../layouts/DefaultLayout.astro";
|
||||||
import Nav from "../components/Nav.astro";
|
import Nav from "../components/Nav.astro";
|
||||||
import Footer from "../components/Footer.astro";
|
import Footer from "../components/Footer.astro";
|
||||||
import ArticleBody from "../components/article/ArticleBody.astro";
|
import ArticleBodyWrapper from "../components/article/article_body/ArticleBodyWrapper.astro";
|
||||||
---
|
---
|
||||||
|
|
||||||
<DefaultLayout title="李守中">
|
<DefaultLayout title="李守中">
|
||||||
<Nav />
|
<Nav />
|
||||||
<ArticleBody collectionName="site" articleSlug="关于本站" />
|
<ArticleBodyWrapper collectionName="site" articleId="关于本站.md" />
|
||||||
<Footer />
|
<Footer />
|
||||||
</DefaultLayout>
|
</DefaultLayout>
|
||||||
|
|||||||
14
src/pages/tool/index.astro
Normal file
14
src/pages/tool/index.astro
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
import DefaultLayout from "../../layouts/DefaultLayout.astro";
|
||||||
|
import Footer from "../../components/Footer.astro";
|
||||||
|
import Nav from "../../components/Nav.astro";
|
||||||
|
import UnitConversionWrapper from "../../components/tool/unit_conversion/UnitConversionWrapper.astro";
|
||||||
|
import ToolNav from "../../components/tool/ToolNav.astro";
|
||||||
|
---
|
||||||
|
|
||||||
|
<DefaultLayout title="工具 - 李守中">
|
||||||
|
<Nav />
|
||||||
|
<ToolNav />
|
||||||
|
<UnitConversionWrapper />
|
||||||
|
<Footer />
|
||||||
|
</DefaultLayout>
|
||||||
14
src/pages/tool/unit_conversion.astro
Normal file
14
src/pages/tool/unit_conversion.astro
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
import DefaultLayout from "../../layouts/DefaultLayout.astro";
|
||||||
|
import Footer from "../../components/Footer.astro";
|
||||||
|
import Nav from "../../components/Nav.astro";
|
||||||
|
import UnitConversionWrapper from "../../components/tool/unit_conversion/UnitConversionWrapper.astro";
|
||||||
|
import ToolNav from "../../components/tool/ToolNav.astro";
|
||||||
|
---
|
||||||
|
|
||||||
|
<DefaultLayout title="单位换算 - 李守中">
|
||||||
|
<Nav />
|
||||||
|
<ToolNav />
|
||||||
|
<UnitConversionWrapper />
|
||||||
|
<Footer />
|
||||||
|
</DefaultLayout>
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
@media screen and (max-width: 125ex) {
|
@media screen and (max-width: 1024px) {
|
||||||
body {
|
body {
|
||||||
width: 95%;
|
width: 95%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 125ex) {
|
@media screen and (min-width: 1024px) {
|
||||||
body {
|
body {
|
||||||
width: 125ex;
|
width: 954px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -17,13 +17,22 @@ html {
|
|||||||
body {
|
body {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
font-family: "Consolas", "Hack", -apple-system,
|
font-family:
|
||||||
BlinkMacSystemFont, Tahoma, Arial, "Hiragino Sans GB", "Microsoft YaHei",
|
"Consolas",
|
||||||
"WenQuanYi Micro Hei", sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
|
"Hack",
|
||||||
"Segoe UI Symbol", "Noto Color Emoji";
|
Tahoma,
|
||||||
|
Arial,
|
||||||
|
"Hiragino Sans GB",
|
||||||
|
"Microsoft YaHei",
|
||||||
|
"WenQuanYi Micro Hei",
|
||||||
|
sans-serif,
|
||||||
|
"Apple Color Emoji",
|
||||||
|
"Segoe UI Emoji",
|
||||||
|
"Segoe UI Symbol",
|
||||||
|
"Noto Color Emoji";
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
overflow-y: scroll;
|
padding: 0 8px 0 8px;
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
{
|
{
|
||||||
"extends": "astro/tsconfigs/strict"
|
"extends": "astro/tsconfigs/strict",
|
||||||
|
"include": [".astro/types.d.ts", "**/*"],
|
||||||
|
"exclude": ["dist"],
|
||||||
|
"compilerOptions": {
|
||||||
|
"jsx": "preserve"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user