Compare commits
6 Commits
78cc77e918
...
8b07e121a5
Author | SHA1 | Date | |
---|---|---|---|
8b07e121a5 | |||
9cf9745d49 | |||
c323698b2a | |||
5d4d388fe8 | |||
9e33f42d34 | |||
033b124b90 |
4
.gitignore
vendored
4
.gitignore
vendored
@ -26,3 +26,7 @@ pnpm-debug.log*
|
||||
# ignore test articles except for article definitions
|
||||
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 生成,而是预先使用 texinfo 编写后编译到 HTML 文件,将其放于固定位置,再在 Astro 页面中引用这些文件。
|
||||
|
||||
这意味着每有一个新的翻译类文章要发布时,需要:
|
||||
|
||||
1. 更新 pages/article/translation/index.astro 文件中翻译类文章的列表;
|
||||
2. 更新 astro.config.mjs 文件中的 sitemap 插件配置,填入 texinfo 编译出的 HTML 文件的 URL 以使这些文章可以进入 sitemap.xml 中,从而可以被搜索引擎收录。
|
||||
|
@ -35,8 +35,25 @@ export default defineConfig({
|
||||
site: 'https://lishouzhong.com',
|
||||
integrations: [
|
||||
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,
|
||||
// customPages: [],
|
||||
// filter: (page) => { },
|
||||
// lastmod: new Date('2022-02-24'),
|
||||
// serialize(item) {
|
||||
|
62
package-lock.json
generated
62
package-lock.json
generated
@ -14,6 +14,8 @@
|
||||
"astro": "^4.9.2",
|
||||
"bignumber.js": "^9.1.2",
|
||||
"pinyin-pro": "^3.22.0",
|
||||
"prettier": "^3.3.3",
|
||||
"prettier-plugin-astro": "^0.14.1",
|
||||
"remark-toc": "^9.0.0",
|
||||
"typescript": "^5.4.5",
|
||||
"vue": "^3.4.29"
|
||||
@ -58,9 +60,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@astrojs/compiler": {
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmmirror.com/@astrojs/compiler/-/compiler-2.8.0.tgz",
|
||||
"integrity": "sha512-yrpD1WRGqsJwANaDIdtHo+YVjvIOFAjC83lu5qENIgrafwZcJgSXDuwVMXOgok4tFzpeKLsFQ6c3FoUdloLWBQ=="
|
||||
"version": "2.10.3",
|
||||
"resolved": "https://registry.npmmirror.com/@astrojs/compiler/-/compiler-2.10.3.tgz",
|
||||
"integrity": "sha512-bL/O7YBxsFt55YHU021oL+xz+B/9HvGNId3F9xURN16aeqDK9juHGktdkCSXz+U4nqFACq6ZFvWomOzhV+zfPw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@astrojs/internal-helpers": {
|
||||
"version": "0.4.0",
|
||||
@ -5400,6 +5403,35 @@
|
||||
"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": {
|
||||
"version": "1.29.0",
|
||||
"resolved": "https://registry.npmmirror.com/prismjs/-/prismjs-1.29.0.tgz",
|
||||
@ -6114,6 +6146,21 @@
|
||||
"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": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmmirror.com/sax/-/sax-1.4.1.tgz",
|
||||
@ -6395,6 +6442,15 @@
|
||||
"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": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmmirror.com/superjson/-/superjson-2.2.1.tgz",
|
||||
|
@ -16,6 +16,8 @@
|
||||
"astro": "^4.9.2",
|
||||
"bignumber.js": "^9.1.2",
|
||||
"pinyin-pro": "^3.22.0",
|
||||
"prettier": "^3.3.3",
|
||||
"prettier-plugin-astro": "^0.14.1",
|
||||
"remark-toc": "^9.0.0",
|
||||
"typescript": "^5.4.5",
|
||||
"vue": "^3.4.29"
|
||||
|
@ -8,11 +8,21 @@ script_path="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
|
||||
original_article_home="/home/ld/Documents/ld_article"
|
||||
original_article_destination="/opt/ld-site/src/content"
|
||||
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" \
|
||||
"${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}/../"
|
||||
npm run build
|
||||
|
||||
@ -21,6 +31,6 @@ if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
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}
|
||||
rm -rf "${ld_site_dist_target}-old"
|
||||
rm -rf "${ld_site_dist_target}_old"
|
||||
|
@ -1,4 +1,5 @@
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
<div class="footer">
|
||||
|
@ -7,7 +7,7 @@ const { title } = Astro.props;
|
||||
|
||||
<meta charset="UTF-8" />
|
||||
<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} />
|
||||
<link rel="sitemap" href="/sitemap-index.xml" />
|
||||
<title>{title}</title>
|
||||
|
@ -1,4 +1,5 @@
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
<div class="nav" id="nav">
|
||||
|
@ -23,8 +23,8 @@ const allCategories: Array<string> = Array.from(
|
||||
new Set(
|
||||
allPosts.map((entry) => {
|
||||
return entry.data.category;
|
||||
})
|
||||
)
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
const categoriedPostList: Array<CategoriedPosts> = allCategories.map((key) => {
|
||||
@ -34,7 +34,7 @@ const categoriedPostList: Array<CategoriedPosts> = allCategories.map((key) => {
|
||||
// article sort function by article title
|
||||
function sortPostByTitlePinyin(
|
||||
postA: CollectionEntry<CollectionKey>,
|
||||
postB: CollectionEntry<CollectionKey>
|
||||
postB: CollectionEntry<CollectionKey>,
|
||||
): number {
|
||||
let postATitle = pinyinpro.convert(pinyinpro.pinyin(postA.data.title), {
|
||||
format: "symbolToNum",
|
||||
@ -63,15 +63,15 @@ const categoriedPostList: Array<CategoriedPosts> = allCategories.map((key) => {
|
||||
object[key].filter((post) => /^[^a-zA-Z]/.test(post.data.title));
|
||||
// sort two Array by article title
|
||||
postWithAlphabetStartedTitle.sort((postA, postB) =>
|
||||
sortPostByTitlePinyin(postA, postB)
|
||||
sortPostByTitlePinyin(postA, postB),
|
||||
);
|
||||
postWithChineseStartedTitle.sort((postA, postB) =>
|
||||
sortPostByTitlePinyin(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
|
||||
postWithAlphabetStartedTitle,
|
||||
);
|
||||
}
|
||||
return object;
|
||||
@ -83,7 +83,7 @@ const categoriedPostListSortedByArticleAmount: Array<CategoriedPosts> =
|
||||
const aKey: string = Object.keys(a)[0];
|
||||
const bKey: string = Object.keys(b)[0];
|
||||
return b[bKey].length - a[aKey].length;
|
||||
}
|
||||
},
|
||||
);
|
||||
---
|
||||
|
||||
|
@ -2,25 +2,67 @@
|
||||
// get rendered ArticleBodyContent HTML
|
||||
const html = await Astro.slots.render("default");
|
||||
|
||||
// If some lines are not started with ASCII character,
|
||||
// join them to their previous line.
|
||||
// This is important.
|
||||
// Because I add one newline character to wrap Chinese.
|
||||
// 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];
|
||||
// Whether the current line belongs to the area where
|
||||
// the line break should be kept.
|
||||
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;
|
||||
}
|
||||
if (
|
||||
// If the first character is not ascii character,
|
||||
// or the final character of previous line is not character.
|
||||
// -------- 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) &&
|
||||
|
@ -1,4 +1,5 @@
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
<ul class="tool-list">
|
||||
|
@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { computed, ref } from 'vue';
|
||||
import { computed, ref } from "vue";
|
||||
import BigNumber from "bignumber.js";
|
||||
|
||||
/**
|
||||
@ -24,7 +24,7 @@ const UnitName = Object.freeze({
|
||||
MBIT: "unit-m-bit",
|
||||
GBIT: "unit-g-bit",
|
||||
TBIT: "unit-t-bit",
|
||||
})
|
||||
});
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
@ -47,20 +47,20 @@ const UnitNameToSize = Object.freeze({
|
||||
});
|
||||
|
||||
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]: '',
|
||||
[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]: "",
|
||||
});
|
||||
|
||||
/**
|
||||
@ -74,17 +74,19 @@ function sizeChanged(unitName) {
|
||||
*/
|
||||
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]);
|
||||
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') {
|
||||
if (result[key] !== "NaN") {
|
||||
size.value[key] = result[key];
|
||||
} else if (result[key] === 'NaN') {
|
||||
size.value[key] = '';
|
||||
} else if (result[key] === "NaN") {
|
||||
size.value[key] = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -93,7 +95,7 @@ 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>
|
||||
@ -102,22 +104,22 @@ const isMultipleOf4KiB = computed(() => {
|
||||
<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)">
|
||||
@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)">
|
||||
@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)">
|
||||
@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)">
|
||||
@input="sizeChanged(UnitName.TBYTE)" />
|
||||
<span class="unit-name">TB</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -125,22 +127,22 @@ const isMultipleOf4KiB = computed(() => {
|
||||
<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)">
|
||||
@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)">
|
||||
@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)">
|
||||
@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)">
|
||||
@input="sizeChanged(UnitName.TIBYTE)" />
|
||||
<span class="unit-name">TiB</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -148,12 +150,12 @@ const isMultipleOf4KiB = computed(() => {
|
||||
<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)">
|
||||
@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)">
|
||||
@input="sizeChanged(UnitName.BIT)" />
|
||||
<span class="unit-name">b</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -161,22 +163,22 @@ const isMultipleOf4KiB = computed(() => {
|
||||
<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)">
|
||||
@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)">
|
||||
@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)">
|
||||
@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)">
|
||||
@input="sizeChanged(UnitName.TBIT)" />
|
||||
<span class="unit-name">Tb</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -203,7 +205,7 @@ p {
|
||||
|
||||
.unit-conversion-computation-node .size-text {
|
||||
margin: 0 6px 0 0;
|
||||
width: 125px
|
||||
width: 125px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 395px) {
|
||||
@ -217,7 +219,7 @@ p {
|
||||
}
|
||||
|
||||
.unit-conversion-computation-area::after {
|
||||
content: '';
|
||||
content: "";
|
||||
display: block;
|
||||
clear: both;
|
||||
}
|
||||
|
@ -5,6 +5,6 @@ import WeightUnitConversion from "./WeightUnitConversion.vue";
|
||||
|
||||
<WeightUnitConversion client:load />
|
||||
|
||||
<br>
|
||||
<br />
|
||||
|
||||
<DataUnitConversion client:load />
|
||||
|
@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { computed, ref } from 'vue';
|
||||
import { computed, ref } from "vue";
|
||||
import BigNumber from "bignumber.js";
|
||||
|
||||
/**
|
||||
@ -15,8 +15,8 @@ const twToMetricUnitName = Object.freeze({
|
||||
QIAN: "qian",
|
||||
FEN: "fen",
|
||||
GRAM: "gram",
|
||||
KILOGRAM: "kilogram"
|
||||
})
|
||||
KILOGRAM: "kilogram",
|
||||
});
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
@ -28,15 +28,15 @@ const twToMetricUnitNameToWeight = Object.freeze({
|
||||
[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]: '',
|
||||
[twToMetricUnitName.JIN]: "",
|
||||
[twToMetricUnitName.LIANG]: "",
|
||||
[twToMetricUnitName.QIAN]: "",
|
||||
[twToMetricUnitName.FEN]: "",
|
||||
[twToMetricUnitName.GRAM]: "",
|
||||
[twToMetricUnitName.KILOGRAM]: "",
|
||||
});
|
||||
|
||||
/**
|
||||
@ -50,17 +50,19 @@ function weightChanged(unitName) {
|
||||
*/
|
||||
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]);
|
||||
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') {
|
||||
if (result[key] !== "NaN") {
|
||||
weight.value[key] = result[key];
|
||||
} else if (result[key] === 'NaN') {
|
||||
weight.value[key] = '';
|
||||
} else if (result[key] === "NaN") {
|
||||
weight.value[key] = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -72,12 +74,12 @@ function weightChanged(unitName) {
|
||||
<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)">
|
||||
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)">
|
||||
@input="weightChanged(twToMetricUnitName.GRAM)" />
|
||||
<span class="unit-name">克</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -85,22 +87,22 @@ function weightChanged(unitName) {
|
||||
<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)">
|
||||
@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)">
|
||||
@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)">
|
||||
@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)">
|
||||
@input="weightChanged(twToMetricUnitName.FEN)" />
|
||||
<span class="unit-name">分</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -124,7 +126,7 @@ p {
|
||||
|
||||
.weight-conversion-computation-node .size-text {
|
||||
margin: 0 6px 0 0;
|
||||
width: 115px
|
||||
width: 115px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 395px) {
|
||||
@ -138,7 +140,7 @@ p {
|
||||
}
|
||||
|
||||
.weight-conversion-computation-area::after {
|
||||
content: '';
|
||||
content: "";
|
||||
display: block;
|
||||
clear: both;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
---
|
||||
import HTMLCommonHead from "../components/HTMLCommonHead.astro";
|
||||
import "../styles/global.css"
|
||||
import "../styles/global.css";
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
|
@ -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 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>
|
@ -1,21 +1,141 @@
|
||||
---
|
||||
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/sort_by_timeline">按日期降序</a>
|
||||
<div class="collection-desc">
|
||||
<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>
|
||||
<CollectionSummary
|
||||
collectionName="translation"
|
||||
sortByTimeline={false}
|
||||
sortByArticleTitle={true}
|
||||
/>
|
||||
<Footer />
|
||||
</DefaultLayout>
|
||||
|
||||
@ -34,4 +154,19 @@ import DefaultLayout from "../../../layouts/DefaultLayout.astro";
|
||||
.options {
|
||||
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>
|
||||
|
@ -1,37 +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>当前: [按日期降序]</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>
|
@ -17,10 +17,19 @@ html {
|
||||
body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-family: "Consolas", "Hack", -apple-system,
|
||||
BlinkMacSystemFont, 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-family:
|
||||
"Consolas",
|
||||
"Hack",
|
||||
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;
|
||||
margin: auto;
|
||||
overflow-y: scroll;
|
||||
|
Loading…
x
Reference in New Issue
Block a user