Compare commits

..

1 Commits

Author SHA1 Message Date
78cc77e918 Update article license. 2024-10-27 13:06:54 +08:00
24 changed files with 186 additions and 430 deletions

4
.gitignore vendored
View File

@ -26,7 +26,3 @@ pnpm-debug.log*
# ignore test articles except for article definitions # ignore test articles except for article definitions
src/content/* src/content/*
!src/content/config.ts !src/content/config.ts
# ignore generated html from makeinfo
src/pages/article/translation/*.html

View File

@ -1,12 +0,0 @@
/** @type {import("prettier").Config} */
export default {
plugins: ['prettier-plugin-astro'],
overrides: [
{
files: '*.astro',
options: {
parser: 'astro',
},
},
],
};

11
.vscode/settings.json vendored
View File

@ -1,11 +0,0 @@
{
"[json]": {
"editor.tabSize": 2
},
"[javascript]": {
"editor.tabSize": 2
},
"[typescript]": {
"editor.tabSize": 2
}
}

View File

@ -1,12 +1 @@
# 李守中的个人站
个人资料、文章与工具站点。由 Astro 框架构建,使用 typescript 编写。 个人资料、文章与工具站点。由 Astro 框架构建,使用 typescript 编写。
## 关于翻译类文章的发布方式
翻译类文章不由 Astro 生成,而是预先使用 texinfo 编写后编译到 HTML 文件,将其放于固定位置,再在 Astro 页面中引用这些文件。
这意味着每有一个新的翻译类文章要发布时,需要:
1. 更新 pages/article/translation/index.astro 文件中翻译类文章的列表;
2. 更新 astro.config.mjs 文件中的 sitemap 插件配置,填入 texinfo 编译出的 HTML 文件的 URL 以使这些文章可以进入 sitemap.xml 中,从而可以被搜索引擎收录。

View File

@ -35,25 +35,8 @@ export default defineConfig({
site: 'https://lishouzhong.com', site: 'https://lishouzhong.com',
integrations: [ integrations: [
sitemap({ sitemap({
customPages: [
"https://lishouzhong.com/file_share/translation/FreeBSD 13 NFS exports man(5) page 2021 中文译本.html",
"https://lishouzhong.com/file_share/translation/FreeBSD 13 NFS mountd man(8) page 2020 中文译本.html",
"https://lishouzhong.com/file_share/translation/FreeBSD 13 NFS nfsd man(8) page 2019 中文译本.html",
"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, // entryLimit: 10000,
// customPages: [],
// filter: (page) => { }, // filter: (page) => { },
// lastmod: new Date('2022-02-24'), // lastmod: new Date('2022-02-24'),
// serialize(item) { // serialize(item) {

62
package-lock.json generated
View File

@ -14,8 +14,6 @@
"astro": "^4.9.2", "astro": "^4.9.2",
"bignumber.js": "^9.1.2", "bignumber.js": "^9.1.2",
"pinyin-pro": "^3.22.0", "pinyin-pro": "^3.22.0",
"prettier": "^3.3.3",
"prettier-plugin-astro": "^0.14.1",
"remark-toc": "^9.0.0", "remark-toc": "^9.0.0",
"typescript": "^5.4.5", "typescript": "^5.4.5",
"vue": "^3.4.29" "vue": "^3.4.29"
@ -60,10 +58,9 @@
} }
}, },
"node_modules/@astrojs/compiler": { "node_modules/@astrojs/compiler": {
"version": "2.10.3", "version": "2.8.0",
"resolved": "https://registry.npmmirror.com/@astrojs/compiler/-/compiler-2.10.3.tgz", "resolved": "https://registry.npmmirror.com/@astrojs/compiler/-/compiler-2.8.0.tgz",
"integrity": "sha512-bL/O7YBxsFt55YHU021oL+xz+B/9HvGNId3F9xURN16aeqDK9juHGktdkCSXz+U4nqFACq6ZFvWomOzhV+zfPw==", "integrity": "sha512-yrpD1WRGqsJwANaDIdtHo+YVjvIOFAjC83lu5qENIgrafwZcJgSXDuwVMXOgok4tFzpeKLsFQ6c3FoUdloLWBQ=="
"license": "MIT"
}, },
"node_modules/@astrojs/internal-helpers": { "node_modules/@astrojs/internal-helpers": {
"version": "0.4.0", "version": "0.4.0",
@ -5403,35 +5400,6 @@
"node": ">=8.15" "node": ">=8.15"
} }
}, },
"node_modules/prettier": {
"version": "3.3.3",
"resolved": "https://registry.npmmirror.com/prettier/-/prettier-3.3.3.tgz",
"integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==",
"license": "MIT",
"bin": {
"prettier": "bin/prettier.cjs"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/prettier-plugin-astro": {
"version": "0.14.1",
"resolved": "https://registry.npmmirror.com/prettier-plugin-astro/-/prettier-plugin-astro-0.14.1.tgz",
"integrity": "sha512-RiBETaaP9veVstE4vUwSIcdATj6dKmXljouXc/DDNwBSPTp8FRkLGDSGFClKsAFeeg+13SB0Z1JZvbD76bigJw==",
"license": "MIT",
"dependencies": {
"@astrojs/compiler": "^2.9.1",
"prettier": "^3.0.0",
"sass-formatter": "^0.7.6"
},
"engines": {
"node": "^14.15.0 || >=16.0.0"
}
},
"node_modules/prismjs": { "node_modules/prismjs": {
"version": "1.29.0", "version": "1.29.0",
"resolved": "https://registry.npmmirror.com/prismjs/-/prismjs-1.29.0.tgz", "resolved": "https://registry.npmmirror.com/prismjs/-/prismjs-1.29.0.tgz",
@ -6146,21 +6114,6 @@
"queue-microtask": "^1.2.2" "queue-microtask": "^1.2.2"
} }
}, },
"node_modules/s.color": {
"version": "0.0.15",
"resolved": "https://registry.npmmirror.com/s.color/-/s.color-0.0.15.tgz",
"integrity": "sha512-AUNrbEUHeKY8XsYr/DYpl+qk5+aM+DChopnWOPEzn8YKzOhv4l2zH6LzZms3tOZP3wwdOyc0RmTciyi46HLIuA==",
"license": "MIT"
},
"node_modules/sass-formatter": {
"version": "0.7.9",
"resolved": "https://registry.npmmirror.com/sass-formatter/-/sass-formatter-0.7.9.tgz",
"integrity": "sha512-CWZ8XiSim+fJVG0cFLStwDvft1VI7uvXdCNJYXhDvowiv+DsbD1nXLiQ4zrE5UBvj5DWZJ93cwN0NX5PMsr1Pw==",
"license": "MIT",
"dependencies": {
"suf-log": "^2.5.3"
}
},
"node_modules/sax": { "node_modules/sax": {
"version": "1.4.1", "version": "1.4.1",
"resolved": "https://registry.npmmirror.com/sax/-/sax-1.4.1.tgz", "resolved": "https://registry.npmmirror.com/sax/-/sax-1.4.1.tgz",
@ -6442,15 +6395,6 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/suf-log": {
"version": "2.5.3",
"resolved": "https://registry.npmmirror.com/suf-log/-/suf-log-2.5.3.tgz",
"integrity": "sha512-KvC8OPjzdNOe+xQ4XWJV2whQA0aM1kGVczMQ8+dStAO6KfEB140JEVQ9dE76ONZ0/Ylf67ni4tILPJB41U0eow==",
"license": "MIT",
"dependencies": {
"s.color": "0.0.15"
}
},
"node_modules/superjson": { "node_modules/superjson": {
"version": "2.2.1", "version": "2.2.1",
"resolved": "https://registry.npmmirror.com/superjson/-/superjson-2.2.1.tgz", "resolved": "https://registry.npmmirror.com/superjson/-/superjson-2.2.1.tgz",

View File

@ -16,8 +16,6 @@
"astro": "^4.9.2", "astro": "^4.9.2",
"bignumber.js": "^9.1.2", "bignumber.js": "^9.1.2",
"pinyin-pro": "^3.22.0", "pinyin-pro": "^3.22.0",
"prettier": "^3.3.3",
"prettier-plugin-astro": "^0.14.1",
"remark-toc": "^9.0.0", "remark-toc": "^9.0.0",
"typescript": "^5.4.5", "typescript": "^5.4.5",
"vue": "^3.4.29" "vue": "^3.4.29"

View File

@ -8,21 +8,11 @@ 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 --delete-after -f"P config.ts" \ rsync -a --delete-after -f"P config.ts" \
"${original_article_home}/" \ "${original_article_home}/" \
"${original_article_destination}/" "${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
@ -31,6 +21,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" rm -rf "${ld_site_dist_target}-old"

View File

@ -11,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>

View File

@ -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>

View File

@ -1,5 +1,4 @@
--- ---
--- ---
<div class="nav" id="nav"> <div class="nav" id="nav">

View File

@ -23,8 +23,8 @@ 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) => {
@ -34,7 +34,7 @@ const categoriedPostList: Array<CategoriedPosts> = allCategories.map((key) => {
// article sort function by article title // article sort function by article title
function sortPostByTitlePinyin( function sortPostByTitlePinyin(
postA: CollectionEntry<CollectionKey>, postA: CollectionEntry<CollectionKey>,
postB: CollectionEntry<CollectionKey>, postB: CollectionEntry<CollectionKey>
): number { ): number {
let postATitle = pinyinpro.convert(pinyinpro.pinyin(postA.data.title), { let postATitle = pinyinpro.convert(pinyinpro.pinyin(postA.data.title), {
format: "symbolToNum", format: "symbolToNum",
@ -63,15 +63,15 @@ const categoriedPostList: Array<CategoriedPosts> = allCategories.map((key) => {
object[key].filter((post) => /^[^a-zA-Z]/.test(post.data.title)); object[key].filter((post) => /^[^a-zA-Z]/.test(post.data.title));
// sort two Array by article title // sort two Array by article title
postWithAlphabetStartedTitle.sort((postA, postB) => postWithAlphabetStartedTitle.sort((postA, postB) =>
sortPostByTitlePinyin(postA, postB), sortPostByTitlePinyin(postA, postB)
); );
postWithChineseStartedTitle.sort((postA, postB) => postWithChineseStartedTitle.sort((postA, postB) =>
sortPostByTitlePinyin(postA, postB), sortPostByTitlePinyin(postA, postB)
); );
// articles that have title start with Chinese prior to // articles that have title start with Chinese prior to
// those which have titles start with the alphabet // those which have titles start with the alphabet
object[key] = postWithChineseStartedTitle.concat( object[key] = postWithChineseStartedTitle.concat(
postWithAlphabetStartedTitle, postWithAlphabetStartedTitle
); );
} }
return object; return object;
@ -83,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;
}, }
); );
--- ---

View File

@ -2,67 +2,25 @@
// get rendered ArticleBodyContent HTML // get rendered ArticleBodyContent HTML
const html = await Astro.slots.render("default"); const html = await Astro.slots.render("default");
// rule: // If some lines are not started with ASCII character,
// 1. If a line belongs to the area where the line break should be kept, // join them to their previous line.
// do not process this line. // This is important.
// 2. If a line ends with [a-zA-Z], add a space to the end of this line. // Because I add one newline character to wrap Chinese.
// 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"); const arr = html.split("\n");
let articleHTMLFinal = arr[0]; let articleHTMLFinal = arr[0];
// Whether the current line belongs to the area where
// the line break should be kept.
let remainIntactArea = false; 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++) { 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) { if (arr[i].match(/^(<pre|<code|<blockquote|<table)/) !== null) {
remainIntactArea = true; remainIntactArea = true;
} else { } else {
remainIntactArea = false; 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 ( if (
i + 1 < arr.length && // If the first character is not ascii character,
arr[i].charAt(arr[i].length - 1).match(/[a-zA-Z]/) === null && // or the final character of previous line is not character.
arr[i + 1].charAt(0).match(/[a-zA-Z]/) !== null // AND
) {
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. // Current line should not belong to area that remains intact.
(arr[i].charAt(0).match(/[ -~]/) === null || (arr[i].charAt(0).match(/[ -~]/) === null ||
arr[i - 1].charAt(arr[i - 1].length - 1).match(/[ -~]/) === null) && arr[i - 1].charAt(arr[i - 1].length - 1).match(/[ -~]/) === null) &&

View File

@ -1,5 +1,4 @@
--- ---
--- ---
<ul class="tool-list"> <ul class="tool-list">
@ -7,14 +6,14 @@
</ul> </ul>
<style> <style>
.tool-list { .tool-list {
list-style: none; list-style: none;
padding: 0; padding: 0;
margin: 0.5em 0; margin: 0.5em 0;
} }
.tool-list li { .tool-list li {
float: left; float: left;
margin: 0 16px 0 0; margin: 0 16px 0 0;
} }
</style> </style>

View File

@ -1,5 +1,5 @@
<script setup> <script setup>
import { computed, ref } from "vue"; import { computed, ref } from 'vue';
import BigNumber from "bignumber.js"; import BigNumber from "bignumber.js";
/** /**
@ -24,7 +24,7 @@ const UnitName = Object.freeze({
MBIT: "unit-m-bit", MBIT: "unit-m-bit",
GBIT: "unit-g-bit", GBIT: "unit-g-bit",
TBIT: "unit-t-bit", TBIT: "unit-t-bit",
}); })
/** /**
* @enum {number} * @enum {number}
@ -47,25 +47,25 @@ const UnitNameToSize = Object.freeze({
}); });
const size = ref({ const size = ref({
[UnitName.KBYTE]: "", [UnitName.KBYTE]: '',
[UnitName.MBYTE]: "", [UnitName.MBYTE]: '',
[UnitName.GBYTE]: "", [UnitName.GBYTE]: '',
[UnitName.TBYTE]: "", [UnitName.TBYTE]: '',
[UnitName.KIBYTE]: "", [UnitName.KIBYTE]: '',
[UnitName.MIBYTE]: "", [UnitName.MIBYTE]: '',
[UnitName.GIBYTE]: "", [UnitName.GIBYTE]: '',
[UnitName.TIBYTE]: "", [UnitName.TIBYTE]: '',
[UnitName.BYTE]: "", [UnitName.BYTE]: '',
[UnitName.BIT]: "", [UnitName.BIT]: '',
[UnitName.KBIT]: "", [UnitName.KBIT]: '',
[UnitName.MBIT]: "", [UnitName.MBIT]: '',
[UnitName.GBIT]: "", [UnitName.GBIT]: '',
[UnitName.TBIT]: "", [UnitName.TBIT]: '',
}); });
/** /**
* Change ref variable when user inputs some characters. * Change ref variable when user inputs some characters.
* @param {UnitNameStr} unitName * @param {UnitNameStr} unitName
* @returns {void} * @returns {void}
*/ */
function sizeChanged(unitName) { function sizeChanged(unitName) {
@ -74,19 +74,17 @@ function sizeChanged(unitName) {
*/ */
let result = {}; let result = {};
// assign 0 to current input if nothing is passed // assign 0 to current input if nothing is passed
if (size.value[unitName].length === 0) result[unitName] = "0"; if (size.value[unitName].length === 0) result[unitName] = '0';
let inputedBytes = new BigNumber(UnitNameToSize[unitName]).multipliedBy( let inputedBytes = new BigNumber(UnitNameToSize[unitName]).multipliedBy(size.value[unitName]);
size.value[unitName],
);
for (const key in UnitNameToSize) { for (const key in UnitNameToSize) {
result[key] = inputedBytes.div(UnitNameToSize[key]).toString(); result[key] = inputedBytes.div(UnitNameToSize[key]).toString();
} }
result[unitName] = size.value[unitName]; result[unitName] = size.value[unitName];
for (const key in UnitNameToSize) { for (const key in UnitNameToSize) {
if (result[key] !== "NaN") { if (result[key] !== 'NaN') {
size.value[key] = result[key]; size.value[key] = result[key];
} else if (result[key] === "NaN") { } else if (result[key] === 'NaN') {
size.value[key] = ""; size.value[key] = '';
} }
} }
} }
@ -95,7 +93,7 @@ const isMultipleOf4KiB = computed(() => {
let userInput = new BigNumber(size.value[UnitName.KIBYTE]); let userInput = new BigNumber(size.value[UnitName.KIBYTE]);
if (userInput.toNumber() === 0) return false; if (userInput.toNumber() === 0) return false;
return userInput.mod(4).toNumber() === 0 ? true : false; return userInput.mod(4).toNumber() === 0 ? true : false;
}); })
</script> </script>
<template> <template>
@ -104,22 +102,22 @@ const isMultipleOf4KiB = computed(() => {
<p>1000 进制并以 Byte :</p> <p>1000 进制并以 Byte :</p>
<div class="unit-conversion-computation-node"> <div class="unit-conversion-computation-node">
<input class="size-text" :id="UnitName.KBYTE" type="text" v-model="size[UnitName.KBYTE]" <input class="size-text" :id="UnitName.KBYTE" type="text" v-model="size[UnitName.KBYTE]"
@input="sizeChanged(UnitName.KBYTE)" /> @input="sizeChanged(UnitName.KBYTE)">
<span class="unit-name">KB</span> <span class="unit-name">KB</span>
</div> </div>
<div class="unit-conversion-computation-node"> <div class="unit-conversion-computation-node">
<input class="size-text" :id="UnitName.MBYTE" type="text" v-model="size[UnitName.MBYTE]" <input class="size-text" :id="UnitName.MBYTE" type="text" v-model="size[UnitName.MBYTE]"
@input="sizeChanged(UnitName.MBYTE)" /> @input="sizeChanged(UnitName.MBYTE)">
<span class="unit-name">MB</span> <span class="unit-name">MB</span>
</div> </div>
<div class="unit-conversion-computation-node"> <div class="unit-conversion-computation-node">
<input class="size-text" :id="UnitName.GBYTE" type="text" v-model="size[UnitName.GBYTE]" <input class="size-text" :id="UnitName.GBYTE" type="text" v-model="size[UnitName.GBYTE]"
@input="sizeChanged(UnitName.GBYTE)" /> @input="sizeChanged(UnitName.GBYTE)">
<span class="unit-name">GB</span> <span class="unit-name">GB</span>
</div> </div>
<div class="unit-conversion-computation-node"> <div class="unit-conversion-computation-node">
<input class="size-text" :id="UnitName.TBYTE" type="text" v-model="size[UnitName.TBYTE]" <input class="size-text" :id="UnitName.TBYTE" type="text" v-model="size[UnitName.TBYTE]"
@input="sizeChanged(UnitName.TBYTE)" /> @input="sizeChanged(UnitName.TBYTE)">
<span class="unit-name">TB</span> <span class="unit-name">TB</span>
</div> </div>
</div> </div>
@ -127,22 +125,22 @@ const isMultipleOf4KiB = computed(() => {
<p>1024 进制并以 Byte :</p> <p>1024 进制并以 Byte :</p>
<div class="unit-conversion-computation-node"> <div class="unit-conversion-computation-node">
<input class="size-text" :id="UnitName.KIBYTE" type="text" v-model="size[UnitName.KIBYTE]" <input class="size-text" :id="UnitName.KIBYTE" type="text" v-model="size[UnitName.KIBYTE]"
@input="sizeChanged(UnitName.KIBYTE)" /> @input="sizeChanged(UnitName.KIBYTE)">
<span class="unit-name">KiB</span> <span class="unit-name">KiB</span>
</div> </div>
<div class="unit-conversion-computation-node"> <div class="unit-conversion-computation-node">
<input class="size-text" :id="UnitName.MIBYTE" type="text" v-model="size[UnitName.MIBYTE]" <input class="size-text" :id="UnitName.MIBYTE" type="text" v-model="size[UnitName.MIBYTE]"
@input="sizeChanged(UnitName.MIBYTE)" /> @input="sizeChanged(UnitName.MIBYTE)">
<span class="unit-name">MiB</span> <span class="unit-name">MiB</span>
</div> </div>
<div class="unit-conversion-computation-node"> <div class="unit-conversion-computation-node">
<input class="size-text" :id="UnitName.GIBYTE" type="text" v-model="size[UnitName.GIBYTE]" <input class="size-text" :id="UnitName.GIBYTE" type="text" v-model="size[UnitName.GIBYTE]"
@input="sizeChanged(UnitName.GIBYTE)" /> @input="sizeChanged(UnitName.GIBYTE)">
<span class="unit-name">GiB</span> <span class="unit-name">GiB</span>
</div> </div>
<div class="unit-conversion-computation-node"> <div class="unit-conversion-computation-node">
<input class="size-text" :id="UnitName.TIBYTE" type="text" v-model="size[UnitName.TIBYTE]" <input class="size-text" :id="UnitName.TIBYTE" type="text" v-model="size[UnitName.TIBYTE]"
@input="sizeChanged(UnitName.TIBYTE)" /> @input="sizeChanged(UnitName.TIBYTE)">
<span class="unit-name">TiB</span> <span class="unit-name">TiB</span>
</div> </div>
</div> </div>
@ -150,12 +148,12 @@ const isMultipleOf4KiB = computed(() => {
<p> Byte (B) / bit (b) :</p> <p> Byte (B) / bit (b) :</p>
<div class="unit-conversion-computation-node"> <div class="unit-conversion-computation-node">
<input class="size-text" :id="UnitName.BYTE" type="text" v-model="size[UnitName.BYTE]" <input class="size-text" :id="UnitName.BYTE" type="text" v-model="size[UnitName.BYTE]"
@input="sizeChanged(UnitName.BYTE)" /> @input="sizeChanged(UnitName.BYTE)">
<span class="unit-name">B</span> <span class="unit-name">B</span>
</div> </div>
<div class="unit-conversion-computation-node"> <div class="unit-conversion-computation-node">
<input class="size-text" :id="UnitName.BIT" type="text" v-model="size[UnitName.BIT]" <input class="size-text" :id="UnitName.BIT" type="text" v-model="size[UnitName.BIT]"
@input="sizeChanged(UnitName.BIT)" /> @input="sizeChanged(UnitName.BIT)">
<span class="unit-name">b</span> <span class="unit-name">b</span>
</div> </div>
</div> </div>
@ -163,22 +161,22 @@ const isMultipleOf4KiB = computed(() => {
<p>1000 进制并以 bit :</p> <p>1000 进制并以 bit :</p>
<div class="unit-conversion-computation-node"> <div class="unit-conversion-computation-node">
<input class="size-text" :id="UnitName.KBIT" type="text" v-model="size[UnitName.KBIT]" <input class="size-text" :id="UnitName.KBIT" type="text" v-model="size[UnitName.KBIT]"
@input="sizeChanged(UnitName.KBIT)" /> @input="sizeChanged(UnitName.KBIT)">
<span class="unit-name">Kb</span> <span class="unit-name">Kb</span>
</div> </div>
<div class="unit-conversion-computation-node"> <div class="unit-conversion-computation-node">
<input class="size-text" :id="UnitName.MBIT" type="text" v-model="size[UnitName.MBIT]" <input class="size-text" :id="UnitName.MBIT" type="text" v-model="size[UnitName.MBIT]"
@input="sizeChanged(UnitName.MBIT)" /> @input="sizeChanged(UnitName.MBIT)">
<span class="unit-name">Mb</span> <span class="unit-name">Mb</span>
</div> </div>
<div class="unit-conversion-computation-node"> <div class="unit-conversion-computation-node">
<input class="size-text" :id="UnitName.GBIT" type="text" v-model="size[UnitName.GBIT]" <input class="size-text" :id="UnitName.GBIT" type="text" v-model="size[UnitName.GBIT]"
@input="sizeChanged(UnitName.GBIT)" /> @input="sizeChanged(UnitName.GBIT)">
<span class="unit-name">Gb</span> <span class="unit-name">Gb</span>
</div> </div>
<div class="unit-conversion-computation-node"> <div class="unit-conversion-computation-node">
<input class="size-text" :id="UnitName.TBIT" type="text" v-model="size[UnitName.TBIT]" <input class="size-text" :id="UnitName.TBIT" type="text" v-model="size[UnitName.TBIT]"
@input="sizeChanged(UnitName.TBIT)" /> @input="sizeChanged(UnitName.TBIT)">
<span class="unit-name">Tb</span> <span class="unit-name">Tb</span>
</div> </div>
</div> </div>
@ -205,7 +203,7 @@ p {
.unit-conversion-computation-node .size-text { .unit-conversion-computation-node .size-text {
margin: 0 6px 0 0; margin: 0 6px 0 0;
width: 125px; width: 125px
} }
@media screen and (max-width: 395px) { @media screen and (max-width: 395px) {
@ -219,7 +217,7 @@ p {
} }
.unit-conversion-computation-area::after { .unit-conversion-computation-area::after {
content: ""; content: '';
display: block; display: block;
clear: both; clear: both;
} }

View File

@ -5,6 +5,6 @@ import WeightUnitConversion from "./WeightUnitConversion.vue";
<WeightUnitConversion client:load /> <WeightUnitConversion client:load />
<br /> <br>
<DataUnitConversion client:load /> <DataUnitConversion client:load />

View File

@ -1,5 +1,5 @@
<script setup> <script setup>
import { computed, ref } from "vue"; import { computed, ref } from 'vue';
import BigNumber from "bignumber.js"; import BigNumber from "bignumber.js";
/** /**
@ -15,8 +15,8 @@ const twToMetricUnitName = Object.freeze({
QIAN: "qian", QIAN: "qian",
FEN: "fen", FEN: "fen",
GRAM: "gram", GRAM: "gram",
KILOGRAM: "kilogram", KILOGRAM: "kilogram"
}); })
/** /**
* @enum {number} * @enum {number}
@ -28,20 +28,20 @@ const twToMetricUnitNameToWeight = Object.freeze({
[twToMetricUnitName.FEN]: 600 / 16 / 10 / 10, [twToMetricUnitName.FEN]: 600 / 16 / 10 / 10,
[twToMetricUnitName.GRAM]: 1, [twToMetricUnitName.GRAM]: 1,
[twToMetricUnitName.KILOGRAM]: 1 * 1000, [twToMetricUnitName.KILOGRAM]: 1 * 1000,
}); })
const weight = ref({ const weight = ref({
[twToMetricUnitName.JIN]: "", [twToMetricUnitName.JIN]: '',
[twToMetricUnitName.LIANG]: "", [twToMetricUnitName.LIANG]: '',
[twToMetricUnitName.QIAN]: "", [twToMetricUnitName.QIAN]: '',
[twToMetricUnitName.FEN]: "", [twToMetricUnitName.FEN]: '',
[twToMetricUnitName.GRAM]: "", [twToMetricUnitName.GRAM]: '',
[twToMetricUnitName.KILOGRAM]: "", [twToMetricUnitName.KILOGRAM]: '',
}); });
/** /**
* Change ref variable when user inputs some characters. * Change ref variable when user inputs some characters.
* @param {UnitNameStr} unitName * @param {UnitNameStr} unitName
* @returns {void} * @returns {void}
*/ */
function weightChanged(unitName) { function weightChanged(unitName) {
@ -50,19 +50,17 @@ function weightChanged(unitName) {
*/ */
let result = {}; let result = {};
// assign 0 to current input if nothing is passed // assign 0 to current input if nothing is passed
if (weight.value[unitName].length === 0) result[unitName] = "0"; if (weight.value[unitName].length === 0) result[unitName] = '0';
let inputedGram = new BigNumber( let inputedGram = new BigNumber(twToMetricUnitNameToWeight[unitName]).multipliedBy(weight.value[unitName]);
twToMetricUnitNameToWeight[unitName],
).multipliedBy(weight.value[unitName]);
for (const key in twToMetricUnitNameToWeight) { for (const key in twToMetricUnitNameToWeight) {
result[key] = inputedGram.div(twToMetricUnitNameToWeight[key]).toString(); result[key] = inputedGram.div(twToMetricUnitNameToWeight[key]).toString();
} }
result[unitName] = weight.value[unitName]; result[unitName] = weight.value[unitName];
for (const key in twToMetricUnitNameToWeight) { for (const key in twToMetricUnitNameToWeight) {
if (result[key] !== "NaN") { if (result[key] !== 'NaN') {
weight.value[key] = result[key]; weight.value[key] = result[key];
} else if (result[key] === "NaN") { } else if (result[key] === 'NaN') {
weight.value[key] = ""; weight.value[key] = '';
} }
} }
} }
@ -74,12 +72,12 @@ function weightChanged(unitName) {
<p>公制重量:</p> <p>公制重量:</p>
<div class="weight-conversion-computation-node"> <div class="weight-conversion-computation-node">
<input class="size-text" :id="twToMetricUnitName.KILOGRAM" type="text" <input class="size-text" :id="twToMetricUnitName.KILOGRAM" type="text"
v-model="weight[twToMetricUnitName.KILOGRAM]" @input="weightChanged(twToMetricUnitName.KILOGRAM)" /> v-model="weight[twToMetricUnitName.KILOGRAM]" @input="weightChanged(twToMetricUnitName.KILOGRAM)">
<span class="unit-name">千克</span> <span class="unit-name">千克</span>
</div> </div>
<div class="weight-conversion-computation-node"> <div class="weight-conversion-computation-node">
<input class="size-text" :id="twToMetricUnitName.GRAM" type="text" v-model="weight[twToMetricUnitName.GRAM]" <input class="size-text" :id="twToMetricUnitName.GRAM" type="text" v-model="weight[twToMetricUnitName.GRAM]"
@input="weightChanged(twToMetricUnitName.GRAM)" /> @input="weightChanged(twToMetricUnitName.GRAM)">
<span class="unit-name"></span> <span class="unit-name"></span>
</div> </div>
</div> </div>
@ -87,22 +85,22 @@ function weightChanged(unitName) {
<p>台湾制重量:</p> <p>台湾制重量:</p>
<div class="weight-conversion-computation-node"> <div class="weight-conversion-computation-node">
<input class="size-text" :id="twToMetricUnitName.JIN" type="text" v-model="weight[twToMetricUnitName.JIN]" <input class="size-text" :id="twToMetricUnitName.JIN" type="text" v-model="weight[twToMetricUnitName.JIN]"
@input="weightChanged(twToMetricUnitName.JIN)" /> @input="weightChanged(twToMetricUnitName.JIN)">
<span class="unit-name"></span> <span class="unit-name"></span>
</div> </div>
<div class="weight-conversion-computation-node"> <div class="weight-conversion-computation-node">
<input class="size-text" :id="twToMetricUnitName.LIANG" type="text" v-model="weight[twToMetricUnitName.LIANG]" <input class="size-text" :id="twToMetricUnitName.LIANG" type="text" v-model="weight[twToMetricUnitName.LIANG]"
@input="weightChanged(twToMetricUnitName.LIANG)" /> @input="weightChanged(twToMetricUnitName.LIANG)">
<span class="unit-name"></span> <span class="unit-name"></span>
</div> </div>
<div class="weight-conversion-computation-node"> <div class="weight-conversion-computation-node">
<input class="size-text" :id="twToMetricUnitName.QIAN" type="text" v-model="weight[twToMetricUnitName.QIAN]" <input class="size-text" :id="twToMetricUnitName.QIAN" type="text" v-model="weight[twToMetricUnitName.QIAN]"
@input="weightChanged(twToMetricUnitName.QIAN)" /> @input="weightChanged(twToMetricUnitName.QIAN)">
<span class="unit-name"></span> <span class="unit-name"></span>
</div> </div>
<div class="weight-conversion-computation-node"> <div class="weight-conversion-computation-node">
<input class="size-text" :id="twToMetricUnitName.FEN" type="text" v-model="weight[twToMetricUnitName.FEN]" <input class="size-text" :id="twToMetricUnitName.FEN" type="text" v-model="weight[twToMetricUnitName.FEN]"
@input="weightChanged(twToMetricUnitName.FEN)" /> @input="weightChanged(twToMetricUnitName.FEN)">
<span class="unit-name"></span> <span class="unit-name"></span>
</div> </div>
</div> </div>
@ -126,7 +124,7 @@ p {
.weight-conversion-computation-node .size-text { .weight-conversion-computation-node .size-text {
margin: 0 6px 0 0; margin: 0 6px 0 0;
width: 115px; width: 115px
} }
@media screen and (max-width: 395px) { @media screen and (max-width: 395px) {
@ -140,7 +138,7 @@ p {
} }
.weight-conversion-computation-area::after { .weight-conversion-computation-area::after {
content: ""; content: '';
display: block; display: block;
clear: both; clear: both;
} }

View File

@ -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 />

View File

@ -24,7 +24,7 @@ interface Props {
} }
const { entry } = Astro.props; const { entry } = Astro.props;
const articlePrettyName: string = entry.id.split("/").pop()!.replace(".md", ""); const articlePrettyName:string = entry.id.split("/").pop()!.replace(".md", "");
--- ---
<DefaultLayout title=`${articlePrettyName} - 李守中`> <DefaultLayout title=`${articlePrettyName} - 李守中`>

View File

@ -24,7 +24,7 @@ interface Props {
} }
const { entry } = Astro.props; const { entry } = Astro.props;
const articlePrettyName: string = entry.id.split("/").pop()!.replace(".md", ""); const articlePrettyName:string = entry.id.split("/").pop()!.replace(".md", "");
--- ---
<DefaultLayout title=`${articlePrettyName} - 李守中`> <DefaultLayout title=`${articlePrettyName} - 李守中`>

View File

@ -0,0 +1,34 @@
---
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 ArticleBodyWrapper from "../../../components/article/article_body/ArticleBodyWrapper.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 />
<ArticleBodyWrapper collectionName="translation" articleSlug={entry.slug} />
<Footer />
</DefaultLayout>

View File

@ -1,141 +1,21 @@
--- ---
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="collection-desc"> <div class="options">
<div class="collection-desc-item"> <span>当前: [按名字排序]</span>
<h1>freebsd</h1> <a href="/article/translation/sort_by_timeline">按日期降序</a>
<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"
sortByTimeline={false}
sortByArticleTitle={true}
/>
<Footer /> <Footer />
</DefaultLayout> </DefaultLayout>
@ -154,19 +34,4 @@ import DefaultLayout from "../../../layouts/DefaultLayout.astro";
.options { .options {
margin: 0.5em 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>

View File

@ -0,0 +1,37 @@
---
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>当前: [按日期降序]</span>
<a href="/article/translation/">按名字排序</a>
</div>
<CollectionSummary
collectionName="translation"
sortByTimeline={true}
sortByArticleTitle={false}
/>
<Footer />
</DefaultLayout>
<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 {
margin: 0.5em 0 0 0;
}
</style>

View File

@ -17,19 +17,10 @@ html {
body { body {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
font-family: font-family: "Consolas", "Hack", -apple-system,
"Consolas", BlinkMacSystemFont, Tahoma, Arial, "Hiragino Sans GB", "Microsoft YaHei",
"Hack", "WenQuanYi Micro Hei", sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
Tahoma, "Segoe UI Symbol", "Noto Color Emoji";
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; overflow-y: scroll;