Compare commits
25 Commits
main
..
9f28803260
| Author | SHA1 | Date | |
|---|---|---|---|
| 9f28803260 | |||
| e2c883233c | |||
| 564040b8c3 | |||
| 44689736d5 | |||
| cb3747d38c | |||
| eeb0031bf5 | |||
| 3130dcbbc3 | |||
| 056e58c5bd | |||
| 89a3f2182a | |||
| 8eb832de6c | |||
| d831135553 | |||
| 7ac31a3984 | |||
| 3bb7ee6796 | |||
| 12fdf56539 | |||
| 173a99a626 | |||
| 99a79b4af6 | |||
| 1f02282ad6 | |||
| 218c43abd9 | |||
| 48130b5d8f | |||
| cc419e6541 | |||
| 7e9b7e6a2a | |||
| d2513b2158 | |||
| 38d8fccaa0 | |||
| 3029cc0fdf | |||
| 6465c714e0 |
@@ -1,31 +0,0 @@
|
|||||||
---
|
|
||||||
description: 每次站点相关修改后须本地构建/启动并验证;Git 仅在被用户明确要求时提交
|
|
||||||
alwaysApply: true
|
|
||||||
---
|
|
||||||
|
|
||||||
# Agent 工作流:验证站点与 Git 提交
|
|
||||||
|
|
||||||
## 修改后必须自行验证(硬性要求)
|
|
||||||
|
|
||||||
凡改动 **VuePress / Plume 配置、`docs/` 下内容与主题相关代码** 等会影响站点展示或 dev/build 的行为时,**在本轮任务结束前必须**完成下列至少一项,并确认 **首页等主要页面非白屏**、**无控制台致命报错**(开发模式下可 F12 查看):
|
|
||||||
|
|
||||||
1. **推荐(与线上最接近)**:在项目根执行
|
|
||||||
`npm run docs:build`
|
|
||||||
然后执行
|
|
||||||
`npm run docs:verify`
|
|
||||||
(会检查 `docs/.vuepress/dist/index.html` 是否包含首页 hero 结构。)
|
|
||||||
|
|
||||||
2. **若重点验证热更新 / 开发体验**:执行
|
|
||||||
`npm run docs:dev-clean`
|
|
||||||
(`--clean-cache --clean-temp` 可避免 **Vite 预构建缓存** 仍指向旧版 `@vuepress/helper`,从而触发 **`encodeSVG` 导出缺失、整站白屏**。)
|
|
||||||
在浏览器打开本地地址,确认首页 hero 与导航正常。
|
|
||||||
|
|
||||||
**白屏常见原因速记**:`@vuepress/plugin-markdown-chart` 等依赖需要 `@vuepress/helper/client` 导出 `encodeSVG`;若 `docs/.vuepress/.cache` 里预打包了过旧的 helper,会出现 **仅 dev 白屏、build 正常**。处理:执行 **`npm run docs:clear-cache`** 后重启 dev,或 **`npm run docs:dev-clean`** / **`npm run docs:build`**(后两者会 clean)。项目已在 `config.ts` 的 Vite `optimizeDeps.include` 中强制包含 `@vuepress/helper/client`,以降低复发概率。
|
|
||||||
|
|
||||||
## Git 提交策略
|
|
||||||
|
|
||||||
- **禁止自作主张提交**:**仅当用户在本轮对话中明确说出要提交**(例如「提交一下」「帮我 commit」「推到 git」等)时,才执行 `git add` / `git commit` / `git push`。
|
|
||||||
- **未要求提交时**:改完代码、做完验证即可结束;**不要**替用户 commit。
|
|
||||||
- **用户若说不要提交**:同样不执行任何 git 写入操作。
|
|
||||||
|
|
||||||
**注意**:构建产物目录 **`docs/.vuepress/dist`**、缓存 **`.cache` / `.temp`** 已在 `.gitignore` 中,**不要**将编译输出提交进源码仓。
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
---
|
|
||||||
description: RAGFlow 检索 Plume 主题时必须使用 VuePress2-Plume 数据集,避免选错知识库
|
|
||||||
alwaysApply: true
|
|
||||||
---
|
|
||||||
|
|
||||||
# RAGFlow 与 Plume 文档
|
|
||||||
|
|
||||||
当任务涉及 **vuepress-theme-plume**、**Plume 主题**、**主题配置 / 博客 / 分类 / 标签 / 侧边栏** 等与 Plume 相关的问题,需要通过 **RAGFlow MCP**(`ragflow_retrieval`)查官方文档时:
|
|
||||||
|
|
||||||
- **必须使用数据集**:**VuePress2-Plume**(名称)。
|
|
||||||
- 若接口需要 `dataset_ids`:当前知识库 ID 为 **`e57440b52aa711f1845c9627700ac9a4`**(与名称对应;若后台重建数据集请以维护者为准更新本条)。
|
|
||||||
- **禁止**在未确认数据集的情况下用其它知识库代替 Plume 文档检索,以免污染答案或引发环境异常。
|
|
||||||
|
|
||||||
与 Plume 无关的问题(例如通用 VuePress 2 核心、本项目业务逻辑)再按需选择其它数据集或不指定 `dataset_ids`。
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
---
|
|
||||||
description: 双仓库结构、本地构建与发布流程;启动时加载项目上下文
|
|
||||||
alwaysApply: true
|
|
||||||
---
|
|
||||||
|
|
||||||
# SiMengWebSite_Notes — 仓库与部署上下文
|
|
||||||
|
|
||||||
在开始涉及 **构建、发布、静态资源路径、`_publish`** 或 **多仓库** 的任务前,请先阅读仓库根目录的 **`PROJECT_BRIEF.local.md`**(若存在)。该文件为本地说明,不进入 Git,含完整背景与命令示例。
|
|
||||||
|
|
||||||
## 速记(无本地文件时的兜底)
|
|
||||||
|
|
||||||
- **源码仓**:仓库根目录 → 远程示例 `SiMengWebSite_Notes.git`;日常开发与提交 Markdown / VuePress 配置。
|
|
||||||
- **构建仓**:**`_publish/`** 内为 **独立 Git 仓库** → 远程示例 `build_notes_simengweb.git`;**只提交** `npm run docs:build` 产出(`docs/.vuepress/dist` 的内容同步至此)。
|
|
||||||
- **原因**:业务服务器性能不足,**不在线上构建**;本地构建后推送构建仓,**服务器拉取更新**即可换页。
|
|
||||||
- **`_publish/`** 与 **`PROJECT_BRIEF.local.md`** 已被根目录 `.gitignore` 忽略,勿把构建产物或该简介误提交进源码仓。
|
|
||||||
|
|
||||||
若缺少 `PROJECT_BRIEF.local.md`,可向维护者索取模板或根据上述速记操作,避免在源码仓中提交 `dist` 或误改发布仓历史。
|
|
||||||
@@ -9,6 +9,3 @@
|
|||||||
*.tff binary
|
*.tff binary
|
||||||
*.woff binary
|
*.woff binary
|
||||||
*.woff2 binary
|
*.woff2 binary
|
||||||
*.mp4 binary
|
|
||||||
*.webm binary
|
|
||||||
*.mov binary
|
|
||||||
|
|||||||
@@ -7,15 +7,3 @@ docs/.vuepress/dist
|
|||||||
.trae/
|
.trae/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
*.log
|
*.log
|
||||||
|
|
||||||
# Local project brief for agents (do not commit)
|
|
||||||
PROJECT_BRIEF.local.md
|
|
||||||
|
|
||||||
# Build and Publish output
|
|
||||||
_publish/
|
|
||||||
|
|
||||||
# Local resources storage (not uploaded)
|
|
||||||
resources/
|
|
||||||
|
|
||||||
# Ensure .gitkeep files are always tracked
|
|
||||||
!**/.gitkeep
|
|
||||||
|
|||||||
@@ -22,43 +22,11 @@
|
|||||||
|
|
||||||
## 安装
|
## 安装
|
||||||
|
|
||||||
- 环境要求:
|
确保您的 Node.js 版本符合要求(^20.6.0 || >=22.0.0),然后执行以下命令:
|
||||||
- Node.js:`^20.6.0` 或 `>=22.0.0`(推荐使用当前 LTS 或稳定版)
|
|
||||||
- npm:`>=10`
|
|
||||||
|
|
||||||
- 安装步骤:
|
```sh
|
||||||
1. 在项目根目录执行依赖安装:
|
npm i
|
||||||
```sh
|
```
|
||||||
npm install
|
|
||||||
```
|
|
||||||
2. 启动开发服务器(任选其一):
|
|
||||||
- 常规方式:
|
|
||||||
```sh
|
|
||||||
npm run docs:dev
|
|
||||||
```
|
|
||||||
- Windows 快捷方式(尝试监听 `0.0.0.0` 以便局域网访问):
|
|
||||||
```bat
|
|
||||||
.\start.bat
|
|
||||||
```
|
|
||||||
- 若端口或权限受限,使用备用命令(绑定到本机并更换端口):
|
|
||||||
```sh
|
|
||||||
npx vuepress@2.0.0-rc.24 dev docs --port 5173 --host localhost
|
|
||||||
```
|
|
||||||
3. 打开浏览器访问:
|
|
||||||
- `http://localhost:4567/`(默认)或
|
|
||||||
- `http://localhost:5173/`(如果使用备用命令)
|
|
||||||
|
|
||||||
- 常见问题与排查:
|
|
||||||
- “`vuepress` 不是内部或外部命令”:通常为依赖未安装或安装失败,执行 `npm install` 后重试。
|
|
||||||
- “listen EACCES: permission denied 0.0.0.0:4567”:端口或绑定权限受限,改用 `--host localhost` 或更换端口(如 `--port 5173`)。
|
|
||||||
- 需要清理缓存后重试:
|
|
||||||
```sh
|
|
||||||
npm run docs:dev-clean
|
|
||||||
```
|
|
||||||
- 更换端口示例(以 8080 为例):
|
|
||||||
```sh
|
|
||||||
npx vuepress dev docs --port 8080 --host localhost
|
|
||||||
```
|
|
||||||
|
|
||||||
## 使用
|
## 使用
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,22 @@
|
|||||||
import { defineClientConfig } from 'vuepress/client'
|
import { defineClientConfig } from 'vuepress/client'
|
||||||
import RImg from './theme/components/RImg.vue'
|
// import RepoCard from 'vuepress-theme-plume/features/RepoCard.vue'
|
||||||
import './theme/styles/custom.css'
|
// import NpmBadge from 'vuepress-theme-plume/features/NpmBadge.vue'
|
||||||
|
// import NpmBadgeGroup from 'vuepress-theme-plume/features/NpmBadgeGroup.vue'
|
||||||
|
// import Swiper from 'vuepress-theme-plume/features/Swiper.vue'
|
||||||
|
|
||||||
|
// import CustomComponent from './theme/components/Custom.vue'
|
||||||
|
|
||||||
|
// import './theme/styles/custom.css'
|
||||||
|
|
||||||
export default defineClientConfig({
|
export default defineClientConfig({
|
||||||
enhance({ app }) {
|
enhance({ app }) {
|
||||||
app.component('RImg', RImg)
|
// built-in components
|
||||||
|
// app.component('RepoCard', RepoCard)
|
||||||
|
// app.component('NpmBadge', NpmBadge)
|
||||||
|
// app.component('NpmBadgeGroup', NpmBadgeGroup)
|
||||||
|
// app.component('Swiper', Swiper) // you should install `swiper`
|
||||||
|
|
||||||
|
// your custom components
|
||||||
|
// app.component('CustomComponent', CustomComponent)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,272 +0,0 @@
|
|||||||
import { defineCollection, defineCollections } from 'vuepress-theme-plume'
|
|
||||||
|
|
||||||
/** 博客目录名 → 中文分类展示 */
|
|
||||||
const BLOG_CATEGORY_LABEL_ZH: Record<string, string> = {
|
|
||||||
blog: '博客',
|
|
||||||
competition: '竞赛',
|
|
||||||
technology: '技术',
|
|
||||||
website: '网站',
|
|
||||||
elysia: '爱莉希雅',
|
|
||||||
collect: '合集',
|
|
||||||
ai: 'AI',
|
|
||||||
}
|
|
||||||
|
|
||||||
export default defineCollections([
|
|
||||||
defineCollection({
|
|
||||||
type: 'post',
|
|
||||||
dir: 'blog',
|
|
||||||
title: '博客',
|
|
||||||
link: '/blog/',
|
|
||||||
linkPrefix: '/article/',
|
|
||||||
postCover: {
|
|
||||||
layout: 'left',
|
|
||||||
compact: true,
|
|
||||||
},
|
|
||||||
categoriesTransform: (categories) =>
|
|
||||||
categories.map((c) => ({
|
|
||||||
...c,
|
|
||||||
name: BLOG_CATEGORY_LABEL_ZH[c.name] ?? c.name,
|
|
||||||
})),
|
|
||||||
}),
|
|
||||||
|
|
||||||
defineCollection({
|
|
||||||
type: 'doc',
|
|
||||||
dir: 'notes/ai',
|
|
||||||
title: '模型',
|
|
||||||
linkPrefix: '/ai/',
|
|
||||||
sidebar: [
|
|
||||||
{ text: '模型', link: '/ai/' },
|
|
||||||
{
|
|
||||||
text: '基础概念',
|
|
||||||
collapsed: false,
|
|
||||||
items: [
|
|
||||||
{ text: 'MCP 与 Skills 详解', link: '/article/mcp-and-skills/' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '部署与工具链',
|
|
||||||
collapsed: false,
|
|
||||||
items: [
|
|
||||||
{ text: 'RAGFlow 与 MCP', link: '/article/windows11-ragflow-deployment-mcp/' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '模型与 API',
|
|
||||||
collapsed: false,
|
|
||||||
items: [{ text: '大模型 API 整合', link: '/article/free_model_api/' }],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
|
|
||||||
defineCollection({
|
|
||||||
type: 'doc',
|
|
||||||
dir: 'notes/subject/english',
|
|
||||||
title: '英语学习',
|
|
||||||
linkPrefix: '/subject/english/',
|
|
||||||
sidebar: [
|
|
||||||
{ text: '英语学习笔记', link: '/subject/english/' },
|
|
||||||
{
|
|
||||||
text: '学习环境与工具',
|
|
||||||
prefix: '/basis',
|
|
||||||
items: [{ text: '英语环境配置', link: '/subject/english/basis/' }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '词汇与记忆',
|
|
||||||
prefix: '/vocabulary',
|
|
||||||
items: [{ text: '词汇学习与记忆法', link: '/subject/english/vocabulary/' }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '语法与句法',
|
|
||||||
prefix: '/grammar',
|
|
||||||
items: [{ text: '核心语法与句型', link: '/subject/english/grammar/' }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '听力与口语',
|
|
||||||
prefix: '/listening-speaking',
|
|
||||||
items: [{ text: '听力口语训练', link: '/subject/english/listening-speaking/' }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '阅读与写作',
|
|
||||||
prefix: '/reading-writing',
|
|
||||||
items: [{ text: '阅读提升', link: '/subject/english/reading-writing/' }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '写译',
|
|
||||||
prefix: '/writing-translation',
|
|
||||||
items: [{ text: '写作与翻译指南', link: '/subject/english/writing-translation/' }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '考试与备考',
|
|
||||||
prefix: '/exam',
|
|
||||||
items: [{ text: '英语四级 (CET-4) 备考指南', link: '/subject/english/exam/cet-4/' }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '资源与工具',
|
|
||||||
prefix: '/resources',
|
|
||||||
items: [{ text: '学习资源与工具', link: '/subject/english/resources/' }],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
|
|
||||||
defineCollection({
|
|
||||||
type: 'doc',
|
|
||||||
dir: 'notes/subject/certification',
|
|
||||||
title: '系统分析师',
|
|
||||||
linkPrefix: '/subject/certification/',
|
|
||||||
sidebar: [
|
|
||||||
{ text: '系统分析师简介', link: '/subject/certification/' },
|
|
||||||
{
|
|
||||||
text: '第一篇 基础知识',
|
|
||||||
prefix: '/certification/sys-analyst',
|
|
||||||
items: [
|
|
||||||
{ text: '第一章 绪论', link: '/subject/certification/sys-analyst/' },
|
|
||||||
{ text: '第二章 数学与工程基础', link: '/subject/certification/sys-analyst/math/' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '第二篇 关键技术',
|
|
||||||
prefix: '/certification/sys-analyst',
|
|
||||||
items: [
|
|
||||||
{ text: '第十章 系统规划与分析', link: '/subject/certification/sys-analyst/planning/' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
|
|
||||||
defineCollection({
|
|
||||||
type: 'doc',
|
|
||||||
dir: 'notes/programming/solidity',
|
|
||||||
title: 'Solidity',
|
|
||||||
linkPrefix: '/programming/solidity/',
|
|
||||||
sidebar: [
|
|
||||||
{ text: 'Solidity 学习笔记', link: '/programming/solidity/' },
|
|
||||||
{
|
|
||||||
text: '基础语法',
|
|
||||||
prefix: '/basic-syntax',
|
|
||||||
items: [
|
|
||||||
{ text: 'Solidity 基础语法与数据类型', link: '/programming/solidity/basic-syntax/' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '项目实例',
|
|
||||||
prefix: '/basic-syntax',
|
|
||||||
items: [{ text: 'Solidity 代码实例', link: '/programming/solidity/analysis/case-analysis/' }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '杂项',
|
|
||||||
prefix: '/other',
|
|
||||||
items: [
|
|
||||||
{ text: 'Hardhat 相关知识', link: '/programming/solidity/other/hardhat/' },
|
|
||||||
{ text: '一些没分类的小知识', link: '/programming/solidity/other/miscellaneous/' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
|
|
||||||
defineCollection({
|
|
||||||
type: 'doc',
|
|
||||||
dir: 'notes/theory/cryptography',
|
|
||||||
title: '密码学基础',
|
|
||||||
linkPrefix: '/theory/cryptography/',
|
|
||||||
sidebar: [
|
|
||||||
{ text: '密码学基础', link: '/theory/cryptography/' },
|
|
||||||
{
|
|
||||||
text: '古典加密算法',
|
|
||||||
prefix: '/theory',
|
|
||||||
items: [
|
|
||||||
{ text: '替换密码', link: '/theory/cryptography/substitution-ciphers/' },
|
|
||||||
{ text: '置换密码', link: '/theory/cryptography/permutation-encryption/' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
|
|
||||||
defineCollection({
|
|
||||||
type: 'doc',
|
|
||||||
dir: 'notes/ops/blockchain',
|
|
||||||
title: '区块链运维',
|
|
||||||
linkPrefix: '/ops/blockchain/',
|
|
||||||
sidebar: [
|
|
||||||
{ text: '区块链运维指南', link: '/ops/blockchain/' },
|
|
||||||
{
|
|
||||||
text: '区块链理论基础',
|
|
||||||
prefix: '/theory',
|
|
||||||
items: [
|
|
||||||
{ text: '区块链的基本原理', link: '/ops/blockchain/theory/basic-principles/' },
|
|
||||||
{ text: 'FISCO-BCOS 节点类型', link: '/ops/blockchain/theory/fisco-bcos-node-type/' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '区块链运维',
|
|
||||||
items: [
|
|
||||||
{ text: '区块链产品设计和基本部署', link: '/ops/blockchain/practice/basic-deployment/' },
|
|
||||||
{ text: '区块链网络部署与管理', link: '/ops/blockchain/practice/node-deployment/' },
|
|
||||||
{ text: 'Console 控制台操作', link: '/ops/blockchain/practice/console-operator/' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
|
|
||||||
defineCollection({
|
|
||||||
type: 'doc',
|
|
||||||
dir: 'notes/ops/linux',
|
|
||||||
title: 'Linux 运维',
|
|
||||||
linkPrefix: '/ops/linux/',
|
|
||||||
sidebar: [
|
|
||||||
{ text: 'Linux 运维笔记', link: '/ops/linux/' },
|
|
||||||
{
|
|
||||||
text: 'Linux 基础',
|
|
||||||
prefix: '/linux',
|
|
||||||
items: [{ text: 'Linux 基础命令详解', link: '/ops/linux/basic-commands/' }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '其他',
|
|
||||||
prefix: '/linux',
|
|
||||||
items: [{ text: '一些零散的命令', link: '/ops/linux/other/' }],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
|
|
||||||
defineCollection({
|
|
||||||
type: 'doc',
|
|
||||||
dir: 'notes/ops/docker',
|
|
||||||
title: 'Docker',
|
|
||||||
linkPrefix: '/ops/docker/',
|
|
||||||
sidebar: [
|
|
||||||
{ text: 'Docker 运维笔记', link: '/ops/docker/' },
|
|
||||||
{
|
|
||||||
text: '数据库相关',
|
|
||||||
prefix: '/ops/docker/db/',
|
|
||||||
items: [
|
|
||||||
{ text: 'MongoDB 部署', link: '/ops/docker/db/mongodb/' },
|
|
||||||
{ text: 'Postgres 部署', link: '/ops/docker/db/postgres/' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '其他服务',
|
|
||||||
prefix: '/ops/docker/other/',
|
|
||||||
items: [{ text: 'ETLCloud 部署', link: '/ops/docker/other/ETLCloud/' }],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
|
|
||||||
defineCollection({
|
|
||||||
type: 'doc',
|
|
||||||
dir: 'notes/programming/web',
|
|
||||||
title: 'Web 开发',
|
|
||||||
linkPrefix: '/programming/web/',
|
|
||||||
sidebar: [
|
|
||||||
{ text: 'Web 开发学习笔记', link: '/programming/web/' },
|
|
||||||
{
|
|
||||||
text: '基础知识',
|
|
||||||
prefix: '/basic-syntax',
|
|
||||||
items: [
|
|
||||||
{ text: 'Web 前端基础讲解', link: '/programming/web/basic-syntax/html-css-js/' },
|
|
||||||
{ text: 'HTML 常用标签与属性', link: '/programming/web/basic-syntax/html-tags-attributes/' },
|
|
||||||
{ text: 'HTML 列表与语义布局', link: '/programming/web/basic-syntax/html-lists-and-semantic-layout/' },
|
|
||||||
{ text: 'JavaScript 基础知识', link: '/programming/web/basic-syntax/javascript-basics/' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
])
|
|
||||||
@@ -3,53 +3,25 @@ import { plumeTheme } from 'vuepress-theme-plume'
|
|||||||
import { viteBundler } from '@vuepress/bundler-vite'
|
import { viteBundler } from '@vuepress/bundler-vite'
|
||||||
import { commentPlugin } from '@vuepress/plugin-comment'
|
import { commentPlugin } from '@vuepress/plugin-comment'
|
||||||
import { umamiAnalyticsPlugin } from '@vuepress/plugin-umami-analytics'
|
import { umamiAnalyticsPlugin } from '@vuepress/plugin-umami-analytics'
|
||||||
import { enrichBlogReadingTimePlugin } from './plugins/enrichBlogReadingTime'
|
|
||||||
import path from 'node:path'
|
|
||||||
import { fileURLToPath } from 'node:url'
|
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url)
|
|
||||||
const __dirname = path.dirname(__filename)
|
|
||||||
|
|
||||||
export default defineUserConfig({
|
export default defineUserConfig({
|
||||||
base: '/',
|
base: '/',
|
||||||
lang: 'zh-CN',
|
lang: 'zh-CN',
|
||||||
title: '仲夏夜之梦',
|
title: '祀梦的花园',
|
||||||
description: '爱与回忆的小世界,记录生活中的每一份温暖与感动',
|
description: '爱与回忆的小世界,记录生活中的每一份温暖与感动',
|
||||||
|
|
||||||
head: [
|
head: [
|
||||||
|
// 配置站点图标
|
||||||
['link', { rel: 'icon', type: 'image/png', href: 'https://theme-plume.vuejs.press/favicon-32x32.png' }],
|
['link', { rel: 'icon', type: 'image/png', href: 'https://theme-plume.vuejs.press/favicon-32x32.png' }],
|
||||||
],
|
],
|
||||||
|
|
||||||
bundler: viteBundler({
|
bundler: viteBundler(),
|
||||||
viteOptions: {
|
shouldPrefetch: false, // 站点较大,页面数量较多时,不建议启用
|
||||||
resolve: {
|
|
||||||
alias: {
|
|
||||||
'@theme/Blog/VPPostItem.vue': path.resolve(__dirname, './theme/Blog/VPPostItem.vue'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* 强制预构建含 encodeSVG 的 client 入口,避免 docs/.vuepress/.cache/deps 里残留旧版
|
|
||||||
* @vuepress/helper 导致 dev 下「does not provide an export named encodeSVG」整页白屏。
|
|
||||||
*/
|
|
||||||
optimizeDeps: {
|
|
||||||
include: ['@vuepress/helper/client'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
shouldPrefetch: false,
|
|
||||||
|
|
||||||
theme: plumeTheme({
|
theme: plumeTheme({
|
||||||
/* 站点域名,启动 SEO 优化 */
|
/* 博客文章页面链接前缀 */
|
||||||
hostname: 'https://www.simengweb.com',
|
article: '/article/',
|
||||||
|
|
||||||
/* 启用数学公式支持和Mermaid图表 */
|
|
||||||
markdown: {
|
|
||||||
math: {
|
|
||||||
type: 'katex',
|
|
||||||
},
|
|
||||||
mermaid: true,
|
|
||||||
demo: true,
|
|
||||||
},
|
|
||||||
/**
|
/**
|
||||||
* 编译缓存,加快编译速度
|
* 编译缓存,加快编译速度
|
||||||
*/
|
*/
|
||||||
@@ -57,14 +29,22 @@ export default defineUserConfig({
|
|||||||
|
|
||||||
/* 本地搜索, 默认启用 */
|
/* 本地搜索, 默认启用 */
|
||||||
search: { provider: 'local' },
|
search: { provider: 'local' },
|
||||||
footer: {
|
|
||||||
message: '愿每一份温柔都被世界珍藏 ✨',
|
/**
|
||||||
copyright: '<a href="https://beian.miit.gov.cn/" target="_blank" aria-label="gongan filing address">沪ICP备2023010022号-1</a>©2025祀梦的个人博客'
|
* 博客相关配置
|
||||||
|
*/
|
||||||
|
blog: {
|
||||||
|
postCover: {
|
||||||
|
layout: 'left',
|
||||||
|
width: 300,
|
||||||
|
compact: true
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 文章贡献者配置
|
* 文章贡献者配置
|
||||||
*/
|
*/
|
||||||
|
// 修改contributors配置
|
||||||
contributors: {
|
contributors: {
|
||||||
mode: 'inline',
|
mode: 'inline',
|
||||||
info: [
|
info: [
|
||||||
@@ -84,10 +64,12 @@ export default defineUserConfig({
|
|||||||
git: true
|
git: true
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 代码块配置
|
||||||
|
*/
|
||||||
codeHighlighter: {
|
codeHighlighter: {
|
||||||
lineNumbers: true,
|
lineNumbers: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
plugins: [
|
plugins: [
|
||||||
@@ -98,11 +80,10 @@ export default defineUserConfig({
|
|||||||
requiredMeta: ['nick']
|
requiredMeta: ['nick']
|
||||||
}),
|
}),
|
||||||
umamiAnalyticsPlugin({
|
umamiAnalyticsPlugin({
|
||||||
id: '2114ab32-5543-4be1-b5e8-c4f2c9269d0d',
|
id: 'a4f0ca65-2da6-4e6b-a01b-f3b3157d05a3',
|
||||||
link: 'https://umami.simengweb.com/script.js',
|
link: 'https://umami.simengweb.com/script.js',
|
||||||
domains: ['www.simengweb.com'],
|
domains: ['notes.simengweb.com'],
|
||||||
cache: true
|
cache: true
|
||||||
}),
|
}),
|
||||||
enrichBlogReadingTimePlugin(),
|
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -9,45 +9,23 @@ export default defineNavbarConfig([
|
|||||||
text: '博客',
|
text: '博客',
|
||||||
link: '/blog/',
|
link: '/blog/',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
text: '模型',
|
|
||||||
link: '/ai/',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '学科知识',
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
text: '英语学习笔记',
|
|
||||||
link: '/subject/english/',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '系统分析师',
|
|
||||||
link: '/subject/certification/',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
text: '编程笔记',
|
text: '编程笔记',
|
||||||
items: [
|
items: [
|
||||||
|
{
|
||||||
|
text: 'LeetCode',
|
||||||
|
link: '/programming/leetcode/',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'C++',
|
||||||
|
link: '/programming/cplusplus/',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
text: 'Solidity',
|
text: 'Solidity',
|
||||||
link: '/programming/solidity/',
|
link: '/programming/solidity/',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
text: 'Web 开发',
|
|
||||||
link: '/programming/web/',
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
text:"技术理论",
|
|
||||||
items:[
|
|
||||||
{
|
|
||||||
text: '密码学基础',
|
|
||||||
link: '/theory/cryptography/',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
text: '运维',
|
text: '运维',
|
||||||
items: [
|
items: [
|
||||||
@@ -59,10 +37,6 @@ export default defineNavbarConfig([
|
|||||||
text: 'Linux 运维',
|
text: 'Linux 运维',
|
||||||
link: '/ops/linux/',
|
link: '/ops/linux/',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
text: 'Docker 运维',
|
|
||||||
link: '/ops/docker/',
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
import { defineNoteConfig, defineNotesConfig } from 'vuepress-theme-plume'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置编程笔记
|
||||||
|
*/
|
||||||
|
const LeetCode = defineNoteConfig({
|
||||||
|
dir: 'programming',
|
||||||
|
link: '/programming/leetcode/',
|
||||||
|
sidebar: [
|
||||||
|
{ text: "LeetCode 指南", link: "/programming/leetcode/" },
|
||||||
|
{
|
||||||
|
text: "刷题题单", prefix: "/question_sheet", items: [
|
||||||
|
{ text: "LeetCode 入门题单~(≧∇≦)ノ", link: "/programming/leetcode/question_sheet/beginner/" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
const cPlusPlus = defineNoteConfig({
|
||||||
|
dir: 'programming',
|
||||||
|
link: '/programming/cplusplus/',
|
||||||
|
sidebar: [
|
||||||
|
{ text: "C++ 学习笔记", link: "/programming/cplusplus/" },
|
||||||
|
{
|
||||||
|
text: "基础配置与开发工具", prefix: "/basis", items: [
|
||||||
|
{ text: "C++ 环境配置", link: "/programming/cplusplus/basis/" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
const solidity = defineNoteConfig({
|
||||||
|
dir: 'programming',
|
||||||
|
link: '/programming/solidity/',
|
||||||
|
sidebar: [
|
||||||
|
{ text: "Solidity 学习笔记", link: "/programming/solidity/" },
|
||||||
|
{
|
||||||
|
text: "基础语法", prefix: "/basic-syntax", items: [
|
||||||
|
{ text: "Solidity 基础语法与数据类型", link: "/programming/solidity/basic-syntax/" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
const blockchain = defineNoteConfig({
|
||||||
|
dir: 'ops',
|
||||||
|
link: '/ops/blockchain',
|
||||||
|
sidebar: [
|
||||||
|
{ text: "区块链运维指南", link: "/ops/blockchain/" },
|
||||||
|
{
|
||||||
|
text: "区块链理论基础", items: [
|
||||||
|
{ text: "区块链的基本原理", link: "/ops/blockchain/theory/basic-principles/" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "区块链运维", items: [
|
||||||
|
{ text: "区块链产品设计和基本部署", link: "/ops/blockchain/practice/basic-deployment/" },
|
||||||
|
{ text: "区块链网络部署与管理", link: "/ops/blockchain/practice/node-deployment/" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
const linux = defineNoteConfig({
|
||||||
|
dir: 'ops',
|
||||||
|
link: '/ops/',
|
||||||
|
sidebar: [
|
||||||
|
{ text: "Linux 运维笔记", link: "/ops/linux/" },
|
||||||
|
{
|
||||||
|
text: "Linux 基础", prefix: "/linux", items: [
|
||||||
|
{ text: "Linux 基础命令详解", link: "/ops/linux/basic-commands/" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
/**
|
||||||
|
* 导出所有的 note
|
||||||
|
*/
|
||||||
|
export default defineNotesConfig({
|
||||||
|
dir: 'notes',
|
||||||
|
link: '/',
|
||||||
|
notes: [LeetCode, cPlusPlus, solidity, blockchain, linux],
|
||||||
|
})
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
import fs from 'node:fs'
|
|
||||||
import { pathToFileURL } from 'node:url'
|
|
||||||
import type { App, PluginFunction } from 'vuepress'
|
|
||||||
|
|
||||||
const HMR_TAIL = `if (import.meta.webpackHot) {
|
|
||||||
import.meta.webpackHot.accept()
|
|
||||||
if (__VUE_HMR_RUNTIME__.updateBlogPostData) {
|
|
||||||
__VUE_HMR_RUNTIME__.updateBlogPostData(blogPostData)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (import.meta.hot) {
|
|
||||||
import.meta.hot.accept(({ blogPostData }) => {
|
|
||||||
__VUE_HMR_RUNTIME__.updateBlogPostData(blogPostData)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
async function enrichBlogData(app: App): Promise<void> {
|
|
||||||
const tempFile = app.dir.temp('internal/blogData.js')
|
|
||||||
if (!fs.existsSync(tempFile)) return
|
|
||||||
|
|
||||||
let blogPostData: Record<string, unknown>[]
|
|
||||||
try {
|
|
||||||
const url = `${pathToFileURL(tempFile).href}?t=${Date.now()}`
|
|
||||||
const mod = await import(url) as { blogPostData: Record<string, unknown>[] }
|
|
||||||
blogPostData = mod.blogPostData.map(p => ({ ...p }))
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const byPath = new Map<string, { minutes: number; words: number }>()
|
|
||||||
for (const page of app.pages) {
|
|
||||||
const rt = page.data.readingTime as { minutes: number; words: number } | undefined
|
|
||||||
if (rt && typeof rt.words === 'number') byPath.set(page.path, rt)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const post of blogPostData) {
|
|
||||||
const rt = byPath.get(post.path as string)
|
|
||||||
if (rt) post.readingTime = rt
|
|
||||||
}
|
|
||||||
|
|
||||||
await app.writeTemp(
|
|
||||||
'internal/blogData.js',
|
|
||||||
`export const blogPostData = ${JSON.stringify(blogPostData)}\n\n${HMR_TAIL}\n`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 在主题写入 blogData 之后,把各页的 readingTime(字数、分钟)并入博客列表数据,
|
|
||||||
* 供自定义 VPPostItem 在 /blog/ 列表展示。
|
|
||||||
*/
|
|
||||||
export function enrichBlogReadingTimePlugin(): PluginFunction {
|
|
||||||
return () => ({
|
|
||||||
name: 'enrich-blog-reading-time',
|
|
||||||
onPrepared: async (app: App) => {
|
|
||||||
await enrichBlogData(app)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { defineThemeConfig } from 'vuepress-theme-plume'
|
import { defineThemeConfig } from 'vuepress-theme-plume'
|
||||||
import collections from './collections'
|
|
||||||
import navbar from './navbar'
|
import navbar from './navbar'
|
||||||
|
import notes from './notes'
|
||||||
|
|
||||||
export default defineThemeConfig({
|
export default defineThemeConfig({
|
||||||
logo: '/plume.svg',
|
logo: '/plume.svg',
|
||||||
@@ -21,5 +21,5 @@ export default defineThemeConfig({
|
|||||||
},
|
},
|
||||||
|
|
||||||
navbar,
|
navbar,
|
||||||
collections,
|
notes,
|
||||||
})
|
})
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.0 MiB |
|
Before Width: | Height: | Size: 629 KiB |
|
Before Width: | Height: | Size: 384 KiB |
|
Before Width: | Height: | Size: 933 KiB |
|
Before Width: | Height: | Size: 600 KiB |
|
Before Width: | Height: | Size: 614 KiB |
|
Before Width: | Height: | Size: 509 KiB |
|
Before Width: | Height: | Size: 492 KiB |
|
Before Width: | Height: | Size: 676 KiB |
|
Before Width: | Height: | Size: 592 KiB |
|
Before Width: | Height: | Size: 658 KiB |
@@ -1,416 +0,0 @@
|
|||||||
<script lang="ts" setup>
|
|
||||||
import type { ReadingTime } from '@vuepress/plugin-reading-time/client'
|
|
||||||
import { getReadingTimeLocale, useReadingTimeLocaleConfig } from '@vuepress/plugin-reading-time/client'
|
|
||||||
import type { BlogPostCoverStyle, ThemeBlogPostItem } from 'vuepress-theme-plume/shared'
|
|
||||||
import VPLink from '@theme/VPLink.vue'
|
|
||||||
import { isMobile as _isMobile } from '@vuepress/helper/client'
|
|
||||||
import { computed, onMounted, ref } from 'vue'
|
|
||||||
import { withBase } from 'vuepress/client'
|
|
||||||
import { useData, useInternalLink, useTagColors } from 'vuepress-theme-plume/composables'
|
|
||||||
|
|
||||||
type PostItem = ThemeBlogPostItem & { readingTime?: ReadingTime }
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
post: PostItem
|
|
||||||
index: number
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const isMobile = ref(false)
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
isMobile.value = _isMobile(navigator.userAgent)
|
|
||||||
window.addEventListener('resize', () => {
|
|
||||||
isMobile.value = _isMobile(navigator.userAgent)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const { blog } = useData()
|
|
||||||
const colors = useTagColors()
|
|
||||||
const { categories: categoriesLink, tags: tagsLink } = useInternalLink()
|
|
||||||
const readingLocale = useReadingTimeLocaleConfig()
|
|
||||||
|
|
||||||
const createTime = computed(() => props.post.createTime?.split(/\s|T/)[0].replace(/\//g, '-'))
|
|
||||||
const categoryList = computed(() => props.post.categoryList ?? [])
|
|
||||||
|
|
||||||
const readingTimeText = computed(() => {
|
|
||||||
const rt = props.post.readingTime
|
|
||||||
const loc = readingLocale.value
|
|
||||||
if (!rt || !loc) return null
|
|
||||||
return getReadingTimeLocale(rt, loc)
|
|
||||||
})
|
|
||||||
|
|
||||||
const sticky = computed(() => {
|
|
||||||
if (typeof props.post.sticky === 'boolean') {
|
|
||||||
return props.post.sticky
|
|
||||||
}
|
|
||||||
else if (typeof props.post.sticky === 'number') {
|
|
||||||
return props.post.sticky >= 0
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
|
|
||||||
const tags = computed(() => {
|
|
||||||
const tagTheme = blog.value.tagsTheme ?? 'colored'
|
|
||||||
|
|
||||||
return (props.post.tags ?? [])
|
|
||||||
.slice(0, 4)
|
|
||||||
.map(tag => ({
|
|
||||||
name: tag,
|
|
||||||
className: colors.value[tag] ? `vp-tag-${colors.value[tag]}` : `tag-${tagTheme}`,
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
|
|
||||||
const cover = computed<BlogPostCoverStyle | null>(() => {
|
|
||||||
if (!props.post.cover)
|
|
||||||
return null
|
|
||||||
const opt = blog.value.postCover ?? 'right'
|
|
||||||
const options = typeof opt === 'string' ? { layout: opt } : opt
|
|
||||||
return { layout: 'right', ratio: '4:3', ...options, ...props.post.coverStyle }
|
|
||||||
})
|
|
||||||
|
|
||||||
const coverLayout = computed(() => {
|
|
||||||
if (isMobile.value)
|
|
||||||
return 'top'
|
|
||||||
const layout = cover.value?.layout ?? 'right'
|
|
||||||
const odd = (props.index + 1) % 2 === 1
|
|
||||||
if (layout === 'odd-left')
|
|
||||||
return odd ? 'left' : 'right'
|
|
||||||
if (layout === 'odd-right')
|
|
||||||
return odd ? 'right' : 'left'
|
|
||||||
return layout
|
|
||||||
})
|
|
||||||
|
|
||||||
const coverCompact = computed(() => {
|
|
||||||
if (props.post.excerpt || coverLayout.value === 'top')
|
|
||||||
return false
|
|
||||||
return cover.value?.compact ?? false
|
|
||||||
})
|
|
||||||
|
|
||||||
const coverStyles = computed(() => {
|
|
||||||
if (!cover.value)
|
|
||||||
return null
|
|
||||||
let ratio: number
|
|
||||||
if (typeof cover.value.ratio === 'number') {
|
|
||||||
ratio = cover.value.ratio
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
const [w, h] = cover.value.ratio!.split(/[:/]/).map(Number)
|
|
||||||
ratio = h / w
|
|
||||||
}
|
|
||||||
if (coverLayout.value === 'left' || coverLayout.value === 'right') {
|
|
||||||
const w = cover.value.width ?? 240
|
|
||||||
return { width: `${w}px`, height: `${w * ratio}px` }
|
|
||||||
}
|
|
||||||
|
|
||||||
return { height: 0, paddingBottom: `${ratio * 100}%` }
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div
|
|
||||||
class="vp-blog-post-item" data-allow-mismatch
|
|
||||||
:class="{ 'has-cover': post.cover, [coverLayout]: cover, 'draft': post.draft }"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
v-if="post.cover" class="post-cover" data-allow-mismatch
|
|
||||||
:class="{ compact: coverCompact }" :style="coverStyles"
|
|
||||||
>
|
|
||||||
<img :src="withBase(post.cover)" :alt="post.title" loading="lazy">
|
|
||||||
</div>
|
|
||||||
<div class="blog-post-item-content">
|
|
||||||
<h3>
|
|
||||||
<span v-if="sticky" class="sticky">TOP</span>
|
|
||||||
<span v-if="post.draft" class="draft">DRAFT</span>
|
|
||||||
<span v-if="post.encrypt" class="icon-lock vpi-lock" />
|
|
||||||
<VPLink :href="post.path" :text="post.title" />
|
|
||||||
</h3>
|
|
||||||
<div class="post-meta">
|
|
||||||
<div v-if="categoryList.length" class="category-list">
|
|
||||||
<span class="icon vpi-folder" />
|
|
||||||
<template v-for="(cate, i) in categoryList" :key="i">
|
|
||||||
<VPLink :href="categoriesLink ? `${categoriesLink.link}?id=${cate.id}` : undefined">
|
|
||||||
{{ cate.name }}
|
|
||||||
</VPLink>
|
|
||||||
<span v-if="i !== categoryList.length - 1">/</span>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
<div v-if="tags.length" class="tag-list">
|
|
||||||
<span class="icon vpi-tag" />
|
|
||||||
<template v-for="tag in tags" :key="tag.name">
|
|
||||||
<VPLink
|
|
||||||
class="tag"
|
|
||||||
:class="tag.className"
|
|
||||||
:href="tagsLink ? `${tagsLink.link}?tag=${tag.name}` : undefined"
|
|
||||||
>
|
|
||||||
{{ tag.name }}
|
|
||||||
</VPLink>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
<div v-if="readingTimeText" class="reading-time">
|
|
||||||
<span class="icon vpi-books" />
|
|
||||||
<span>{{ readingTimeText.words }}</span>
|
|
||||||
<span>{{ readingTimeText.time }}</span>
|
|
||||||
</div>
|
|
||||||
<div v-if="createTime" class="create-time">
|
|
||||||
<span class="icon vpi-clock" />
|
|
||||||
<span>{{ createTime }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="post.excerpt" class="vp-doc excerpt" v-html="post.excerpt" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.vp-blog-post-item {
|
|
||||||
padding: 16px;
|
|
||||||
margin: 0 -16px;
|
|
||||||
background-color: var(--vp-c-bg);
|
|
||||||
transition: background-color var(--vp-t-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.vp-blog-post-item.draft {
|
|
||||||
background-color: var(--vp-c-warning-soft);
|
|
||||||
}
|
|
||||||
|
|
||||||
.vp-blog-post-item.has-cover:where(.left, .right) {
|
|
||||||
display: flex;
|
|
||||||
gap: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 419px) {
|
|
||||||
.vp-blog-post-item.has-cover:where(.left, .right) {
|
|
||||||
display: block;
|
|
||||||
gap: unset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.vp-blog-post-item.has-cover.right {
|
|
||||||
flex-direction: row-reverse;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-cover {
|
|
||||||
position: relative;
|
|
||||||
align-self: center;
|
|
||||||
overflow: hidden;
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vp-blog-post-item.has-cover.left .post-cover.compact {
|
|
||||||
margin: -24px 0 -24px -20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vp-blog-post-item.has-cover.right .post-cover.compact {
|
|
||||||
margin: -24px -20px -24px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vp-blog-post-item.has-cover.top .post-cover {
|
|
||||||
margin: -16px -16px 16px;
|
|
||||||
border-radius: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 419px) {
|
|
||||||
.vp-blog-post-item.has-cover.top .post-cover {
|
|
||||||
width: calc(100% + 40px);
|
|
||||||
margin: -24px -20px 24px;
|
|
||||||
border-top-left-radius: 8px;
|
|
||||||
border-top-right-radius: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-cover img {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
object-fit: cover;
|
|
||||||
transition: transform 0.5s;
|
|
||||||
transform: scale(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.vp-blog-post-item.has-cover:hover .post-cover img {
|
|
||||||
transform: scale(1.02);
|
|
||||||
}
|
|
||||||
|
|
||||||
.vp-blog-post-item.has-cover.left .post-cover.compact {
|
|
||||||
border-top-right-radius: 0;
|
|
||||||
border-bottom-right-radius: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vp-blog-post-item.has-cover.right .post-cover.compact {
|
|
||||||
border-top-left-radius: 0;
|
|
||||||
border-bottom-left-radius: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.blog-post-item-content {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vp-blog-post-item.has-cover .blog-post-item-content {
|
|
||||||
flex: 1 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.blog-post-item-content .sticky,
|
|
||||||
.blog-post-item-content .draft {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 3px 6px;
|
|
||||||
margin-right: 0.5rem;
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: 600;
|
|
||||||
line-height: 1;
|
|
||||||
color: var(--vp-c-text-2);
|
|
||||||
background-color: var(--vp-c-brand-soft);
|
|
||||||
border-radius: 4px;
|
|
||||||
transition: var(--vp-t-color);
|
|
||||||
transition-property: color, background-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.blog-post-item-content .draft {
|
|
||||||
color: var(--vp-c-warning-1);
|
|
||||||
background-color: var(--vp-c-warning-soft);
|
|
||||||
}
|
|
||||||
|
|
||||||
.blog-post-item-content .icon-lock {
|
|
||||||
width: 1em;
|
|
||||||
height: 1em;
|
|
||||||
margin-right: 8px;
|
|
||||||
margin-left: 3px;
|
|
||||||
color: var(--vp-c-text-3);
|
|
||||||
transition: var(--vp-t-color);
|
|
||||||
transition-property: color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.blog-post-item-content h3 {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin: 0;
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--vp-c-text-1);
|
|
||||||
transition: color var(--vp-t-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.blog-post-item-content h3 a {
|
|
||||||
color: inherit;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.blog-post-item-content h3:hover {
|
|
||||||
color: var(--vp-c-brand-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.blog-post-item-content h3:hover .sticky {
|
|
||||||
color: var(--vp-c-text-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.blog-post-item-content .excerpt {
|
|
||||||
margin-top: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
|
||||||
.vp-blog-post-item {
|
|
||||||
padding: 24px 20px;
|
|
||||||
margin: 0;
|
|
||||||
border-radius: 8px;
|
|
||||||
box-shadow: var(--vp-shadow-1);
|
|
||||||
transition: var(--vp-t-color);
|
|
||||||
transition-property: background-color, color, box-shadow;
|
|
||||||
will-change: transform;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vp-blog-post-item:hover {
|
|
||||||
box-shadow: var(--vp-shadow-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.blog-post-item-content .post-meta {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.blog-post-item-content .post-meta {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 16px;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: flex-start;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 400;
|
|
||||||
color: var(--vp-c-text-2);
|
|
||||||
transition: color var(--vp-t-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.blog-post-item-content .post-meta > div {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.blog-post-item-content .post-meta .reading-time {
|
|
||||||
gap: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.blog-post-item-content .post-meta .tag-list {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.blog-post-item-content .post-meta .tag-list .tag {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 3px 5px;
|
|
||||||
margin-right: 6px;
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 1;
|
|
||||||
color: var(--vp-tag-color);
|
|
||||||
background-color: var(--vp-tag-bg);
|
|
||||||
border-radius: 3px;
|
|
||||||
transition: color var(--vp-t-color), background-color var(--vp-t-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.blog-post-item-content .post-meta .tag-list .tag:last-of-type {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.blog-post-item-content .post-meta .icon {
|
|
||||||
width: 14px;
|
|
||||||
height: 14px;
|
|
||||||
margin-right: 0.3rem;
|
|
||||||
color: var(--vp-c-text-3);
|
|
||||||
transition: color var(--vp-t-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.blog-post-item-content .post-meta a {
|
|
||||||
font-weight: normal;
|
|
||||||
color: inherit;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.excerpt.vp-doc :deep(p) {
|
|
||||||
margin: 0.5rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.excerpt.vp-doc :deep(p:first-of-type) {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.excerpt.vp-doc :deep(p:last-of-type) {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.excerpt.vp-doc :deep(p strong) {
|
|
||||||
color: var(--vp-c-text-2);
|
|
||||||
transition: color var(--vp-t-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.excerpt.vp-doc :deep(div[class*="language-"]) {
|
|
||||||
margin: 16px -16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 496px) {
|
|
||||||
.excerpt.vp-doc :deep(div[class*="language-"]) {
|
|
||||||
margin: 16px 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
const message = ref('Hello World!')
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="my-custom-content">
|
||||||
|
{{ message }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
<template>
|
|
||||||
<img :src="imageSrc" :alt="alt" v-bind="$attrs" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { computed } from 'vue'
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
src: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
alt: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// 使用 Vite 的 import.meta.glob 或简单字符串拼接
|
|
||||||
// 由于别名在运行时不可直接动态拼接,我们这里使用一个更稳妥的方法
|
|
||||||
// 映射到我们配置好的别名
|
|
||||||
const imageSrc = computed(() => {
|
|
||||||
// 处理路径,确保指向别名
|
|
||||||
const path = props.src.startsWith('/') ? props.src.slice(1) : props.src
|
|
||||||
return new URL(`../../../resources/${path}`, import.meta.url).href
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
@@ -1,31 +1,3 @@
|
|||||||
/**
|
|
||||||
* 博客:分类(中文展示名)与标签(英文)之间留出间距;分类路径本身前后略松一些
|
|
||||||
*/
|
|
||||||
.vp-blog-post-item .post-meta .category-list {
|
|
||||||
padding: 0.3em 0.65em;
|
|
||||||
margin-right: 0.75rem;
|
|
||||||
margin-left: 0.15rem;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vp-blog-post-item .post-meta .category-list .icon {
|
|
||||||
margin-right: 0.45rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vp-blog-post-item .post-meta .category-list a + span {
|
|
||||||
margin-inline: 0.2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 文章页面包屑:各级目录名前后略留白 */
|
|
||||||
.vp-breadcrumb ol {
|
|
||||||
gap: 6px 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vp-breadcrumb ol li .breadcrumb:not(.current) {
|
|
||||||
padding: 0.15em 0.5em;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
/** 主题颜色 */
|
/** 主题颜色 */
|
||||||
|
|
||||||
|
|||||||
@@ -7,12 +7,16 @@ config:
|
|||||||
full: true
|
full: true
|
||||||
background: tint-plate
|
background: tint-plate
|
||||||
hero:
|
hero:
|
||||||
name: 仲夏夜之梦
|
name: 祀梦的花园
|
||||||
tagline: 祀梦和小小夏的花园
|
tagline: 爱与回忆的小世界
|
||||||
text: 在这里,每一个文字都承载着温暖与美好 🌸
|
text: 在这里,每一个文字都承载着温暖与美好 🌸
|
||||||
actions:
|
actions:
|
||||||
-
|
-
|
||||||
theme: brand
|
theme: brand
|
||||||
text: 博客
|
text: 博客
|
||||||
link: /blog/
|
link: /blog/
|
||||||
|
-
|
||||||
|
theme: alt
|
||||||
|
text: 探索笔记 →
|
||||||
|
link: /notes/
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
title: 关于祀梦与他的花园
|
title: 关于祀梦的花园
|
||||||
description: 这里是爱与回忆的小世界~
|
description: 这里是爱与回忆的小世界~
|
||||||
hideInBlogList: true
|
hideInBlogList: true
|
||||||
article: false
|
article: false
|
||||||
@@ -7,59 +7,36 @@ createTime: 2024/10/29 12:30:00
|
|||||||
permalink: /about/
|
permalink: /about/
|
||||||
---
|
---
|
||||||
|
|
||||||
# 👋 嗨呀!这里是祀梦的小窝~
|
# 👋 这里是祀梦的小窝!
|
||||||
|
|
||||||
欢迎来到我的数字花园!这里不仅是笔记的存放处,更是我思维流转、技术成长与生活感悟的小小交汇点。希望能在这里和你分享温暖与美好~ (๑>◡<๑)
|
泥嚎~ 欢迎来到祀梦的个人博客,这是我在这个小站发布的第一篇文章哦~ (✧ω✧)
|
||||||
|
|
||||||
## 🏗️ 正在努力构建的未来~
|
## 关于站长我呀~ 🌸
|
||||||
|
|
||||||
目前我是一名大三的学生,主修**信息安全**专业。虽然专业背景在“守”,但我对“建”有着更浓厚的兴趣呢!
|
我是祀梦,现在在上海某大专读书,是个可爱的男孩子~ 目前是信息安全专业的学生,但是超——级喜欢编程!(๑•̀ㅂ•́)و✧
|
||||||
|
|
||||||
- **职业坐标**:目前正全力投入**后端开发**的学习中,正在努力准备实习,期待能在真实的代码世界里磨砺自己,成为厉害的后端大牛!(๑•̀ㅂ•́)و✧
|
## 我的兴趣爱好~ 🌈
|
||||||
- **技术理念**:我崇尚简洁、高效的代码,同时也相信技术的最终目的是为了传递温暖和解决问题。
|
|
||||||
|
|
||||||
## 🪴 为什么会有这个“花园”?
|
我喜欢好多好多东西呀!编程、摄像、美食、旅游,还有画画~ 假期闲下来的时候,我会画一些自己喜欢的角色呢!(๑˘ᴗ˘๑)
|
||||||
|
|
||||||
你可能会发现这个站点的结构非常清晰(或者说,我在努力让它变得整齐一些~)。
|
## 技术方面~ 💻
|
||||||
|
|
||||||
在 2025 年末,我决定将原有的博客迁移到这个新家。原因很简单:**我需要一个更具结构化的空间来安放我的笔记**。从密码学理论到后端开发实战,从英语四级备考到算法练习,我希望知识能够像小植物一样,在合适的土壤里有序生长,方便自己翻阅,也能给路过的你一点点参考呀~ 🌸
|
在技术方面,Python 是我最熟练的编程语言,Java 和 C 也懂一些,前后端开发和 Linux 运维我也会一点哦~ 虽然什么都感兴趣,什么都想学,但是总觉得自己不够聪明,学杂而不精呢... (╥ω╥)
|
||||||
|
|
||||||
## 🚀 个人项目展示~
|
## 为什么叫"祀梦"? 🌙
|
||||||
|
|
||||||
除了这个笔记站,我还维护着以下这些心血之作:
|
这个名字的由来超有趣!初中的时候我很喜欢写小说,"祀梦"是我其中一本小书的主角名字~ 当时我的初中同学聊天的时候说这个名字很好听,我就一直沿用下来啦!(๑´ㅂ`๑)
|
||||||
|
|
||||||
- [**祀梦的个人博客**](https://blog.simengweb.com/):我的感性领地,记录着生活故事与长长的技术思考。
|
## 联系方式~ 📱
|
||||||
- [**算法练习平台**](https://algorithmplatform.simengweb.com/):为了提升算法能力而开发的小平台,欢迎来一起刷题呀!(≧∇≦)ノ
|
|
||||||
|
|
||||||
## 🛠️ 祀梦的小技能树~ (〃'▽'〃)
|
如果您在网页访问过程中遇到什么问题,或者想要交个朋友的话,欢迎随时来找我玩哦~
|
||||||
|
- 邮箱:meng_si@proton.me
|
||||||
|
- B站:[66ccff色的薰依草](https://space.bilibili.com/361714249)
|
||||||
|
|
||||||
虽然总觉得自己学得杂杂的,但每一项技能都是我用心收集的“宝藏”哦!
|
## 网站的成长记录~ 📝
|
||||||
|
|
||||||
- **常用的魔法咒语**:Python (最熟练啦~), C++, Java, Solidity, JavaScript
|
- 2025年9月21日:部署完网站
|
||||||
- **探索过的奇幻领地**:后端开发、信息安全、区块链运维、Linux 系统管理 (。・ω・。)
|
|
||||||
- **随身携带的口袋工具**:Docker, Git, WSL2, Hardhat
|
|
||||||
|
|
||||||
## 📸 生活的彩色一角
|
## 写在最后~ 💌
|
||||||
|
|
||||||
在代码之外,我的世界也是五颜六色的:
|
很早之前我就总是喜欢写点东西,但是又不敢发到大的平台上,做一个属于自己的互联网小窝感觉真的超——级棒!希望大家能在这里找到有用的内容,也欢迎大家常来玩哦~ (づ ̄ 3 ̄)づ
|
||||||
|
|
||||||
- **超棒的朋友**:首页提到的“小小夏”是我非常要好的朋友。我们共同守护着这个数字花园的宁静。
|
|
||||||
- **我的小爱好**:我热爱摄像、美食与旅游。假期闲下来的时候,也会拿起画笔,勾勒出心里的角色呢~ (๑˘ᴗ˘๑)
|
|
||||||
- **关于我呀**:一个喜欢美好事物、偶尔有点小社恐、但在技术面前总能保持好奇心的男孩子。
|
|
||||||
|
|
||||||
## 🕰️ 网站的成长足迹
|
|
||||||
|
|
||||||
- **2025-12-15**:完成结构化迁移,给笔记们找了个舒服的新家。
|
|
||||||
- **2025-09-21**:笔记站初次部署。
|
|
||||||
- **2024-10-29**:在这个小站写下了第一段关于花园的文字。
|
|
||||||
|
|
||||||
## 💌 找我玩呀~
|
|
||||||
|
|
||||||
如果你对我的项目感兴趣,或者只是想聊聊技术与生活,欢迎随时联系:
|
|
||||||
|
|
||||||
- **邮箱**:[meng_si@proton.me](mailto:meng_si@proton.me)
|
|
||||||
- **B站**:[66ccff色的薰依草](https://space.bilibili.com/361714249)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
> “在每一个文字里,都承载着温暖与美好。” 🌸
|
|
||||||
@@ -1,266 +0,0 @@
|
|||||||
---
|
|
||||||
title: 第十四届蓝桥杯大赛软件赛国赛 Python 研究生组(正在更新)
|
|
||||||
createTime: 2026/01/09 15:57:22
|
|
||||||
permalink: /archives/b1c77a1d-d402-4788-8049-fa3aeb12ebd0/
|
|
||||||
tags:
|
|
||||||
- contest
|
|
||||||
---
|
|
||||||
## 一、X 质数
|
|
||||||
|
|
||||||
### 题目
|
|
||||||
|
|
||||||
题目链接:0X质数 - 蓝桥云课
|
|
||||||
|
|
||||||
### 问题描述
|
|
||||||
|
|
||||||
对于一个含有 M 个数位的正整数 N ,任意选中其中 K 个不同的数位(0≤K<M),将这些选中的数位删除之后,余下的数位按照原来的顺序组成了一个新的数字 P 。如果至少存在一个 P 是质数,我们就称 N 是一个 X 质数。例如,对于整数 7869 ,我们可以删去 7 和 6 ,得到一个新的数字 89 ,由于 89 是一个质数,因此 7869 是一个 X 质数。又如,对于整数 77 ,可以删去一个 7 后变为质数 7 ,因此 77 也是一个 X 质数。
|
|
||||||
|
|
||||||
请问 1 (含)至 1000000(含)中一共有多少个不同的 X 质数。
|
|
||||||
|
|
||||||
### 解析
|
|
||||||
|
|
||||||
先通过埃氏筛,获取到范围内的所有质数。然后遍历每个数字的所有子串,查看是否满足 X 质数的定义,我们只要找到一个符合条件的子串即可退出循环,只要子串是质数或者 X 质数,我们都可以认定当前数字为 X 质数。
|
|
||||||
|
|
||||||
这里在寻找每个数的子串的时候,可以采用二进制的方式,详情查看:通过位运算快速生成所有的子序列|祀梦的个人博客
|
|
||||||
|
|
||||||
### 答案
|
|
||||||
|
|
||||||
```python
|
|
||||||
# import os
|
|
||||||
# import sys
|
|
||||||
|
|
||||||
# def is_prime(n):
|
|
||||||
# _nums = [True] * (n + 1)
|
|
||||||
# _nums[0] = _nums[1] = False
|
|
||||||
# for i in range(2, n):
|
|
||||||
# if _nums[i]:
|
|
||||||
# for j in range(i ** 2, n + 1, i):
|
|
||||||
# _nums[j] = False
|
|
||||||
# return _nums
|
|
||||||
|
|
||||||
# N = 1000000
|
|
||||||
# nums = is_prime(N)
|
|
||||||
# ans = 0
|
|
||||||
|
|
||||||
# for num in range(N + 1):
|
|
||||||
# digits = [int(n) for n in str(num)]
|
|
||||||
# length = len(str(num))
|
|
||||||
# for mask in range(1,1 << length):
|
|
||||||
# subseq = 0
|
|
||||||
# for i in range(length):
|
|
||||||
# if mask & ( 1 << i ):
|
|
||||||
# subseq = subseq * 10 + digits[i]
|
|
||||||
# if nums[subseq]:
|
|
||||||
# nums[num] = True
|
|
||||||
# ans += 1
|
|
||||||
# break
|
|
||||||
|
|
||||||
# print(ans) # 989457
|
|
||||||
print(989457)
|
|
||||||
```
|
|
||||||
|
|
||||||
## 二、顶板上的正方形
|
|
||||||
|
|
||||||
### 题目
|
|
||||||
|
|
||||||
题目链接:0钉板上的正方形 - 蓝桥云课
|
|
||||||
|
|
||||||
### 问题描述
|
|
||||||
|
|
||||||
小蓝有一个大小为 10×1010×10 钉板,如下图所示,上面等距分布着 10×1010×10 个钉槽(上下/左右相邻的钉槽之间的距离均相同)。其中有一些钉槽上面有钉子, 在图中用红色来进行标注,白色则表示此处没有钉子。小蓝喜欢用橡皮筋在钉 子上围出正方形图案,即选择钉板上的四个钉子,将其作为正方形的四个顶点, 从而把橡皮筋拉伸为一个正方形。
|
|
||||||
|
|
||||||
现在,小蓝想要知道在这个钉板上,可以围出多少种边长不同的正方 形?(相同边长的正方形均视作是同一种,不用考虑正方形的位置)。图中展示 了三种不同边长的正方形,其中蓝色线条表示橡皮筋。
|
|
||||||
|
|
||||||
### 解析
|
|
||||||
|
|
||||||
参考了评论区的大哥(马国良的个人主页 - 蓝桥云课)的思路,如下图所示
|
|
||||||
|
|
||||||
dr 为行之间的差距
|
|
||||||
|
|
||||||
dc 为列之间的差距
|
|
||||||
|
|
||||||
我们遍历 A 和 B,计算 C 和 D 的坐标,分别为 ( x2 + dr , y2 - dc ),( x1 + dr , y1 - dc )
|
|
||||||
|
|
||||||
然后确保CD没有超过棋盘范围并且都有钉子即可
|
|
||||||
|
|
||||||
### 答案
|
|
||||||
|
|
||||||
```python
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
from itertools import combinations
|
|
||||||
|
|
||||||
arr = [
|
|
||||||
[1, 1, 0, 1, 0, 1, 1, 1, 1, 1],
|
|
||||||
[1, 1, 1, 0, 0, 1, 1, 1, 1, 0],
|
|
||||||
[1, 1, 0, 0, 1, 0, 1, 1, 1, 1],
|
|
||||||
[1, 0, 1, 1, 0, 1, 1, 1, 1, 0],
|
|
||||||
[1, 0, 1, 0, 1, 1, 1, 1, 0, 0],
|
|
||||||
[1, 0, 0, 1, 0, 1, 0, 1, 0, 1],
|
|
||||||
[1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
|
|
||||||
[0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
|
|
||||||
[0, 1, 1, 0, 1, 0, 1, 1, 1, 1],
|
|
||||||
[1, 0, 1, 0, 0, 1, 0, 1, 0, 0]
|
|
||||||
]
|
|
||||||
points = [(i, j) for i in range(10) for j in range(10) if arr[i][j]]
|
|
||||||
squares = set()
|
|
||||||
|
|
||||||
for (x1, y1), (x2, y2) in combinations(points, 2):
|
|
||||||
dx, dy = x1 - x2, y1 - y2
|
|
||||||
x3 = x2 + dy
|
|
||||||
y3 = y2 - dx
|
|
||||||
x4 = x1 + dy
|
|
||||||
y4 = y1 - dx
|
|
||||||
if all(0 <= coord < 10 for coord in [x3, y3, x4, y4]):
|
|
||||||
if arr[x3][y3] and arr[x4][y4]:
|
|
||||||
squares.add(dx*dx + dy*dy)
|
|
||||||
print(len(squares))
|
|
||||||
```
|
|
||||||
|
|
||||||
## 三、整数变换
|
|
||||||
|
|
||||||
### 题目
|
|
||||||
|
|
||||||
题目链接:0整数变换 - 蓝桥云课
|
|
||||||
|
|
||||||
### 问题描述
|
|
||||||
|
|
||||||
小蓝有一个整数 n。每分钟,小蓝的数都会发生变化,变为上一分钟的数减去上一分钟的数的各个数位和。
|
|
||||||
|
|
||||||
例如,如果小蓝开始时的数为 23,则下一分钟变为 23−(2+3)=18,再下一分钟变为 18−(1+8)=9,再下一分钟变为 9−9=0,共经过了 3 分钟变为 0。
|
|
||||||
|
|
||||||
给定一个正整数,请问这个数多少分钟后变为 0。
|
|
||||||
|
|
||||||
### 解析
|
|
||||||
|
|
||||||
这题直接暴力就过了。
|
|
||||||
|
|
||||||
Python3 跑出来只能通过 60% 的测试用例,用 PyPy3 就好了。
|
|
||||||
|
|
||||||
### 答案
|
|
||||||
|
|
||||||
```python
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
num = int(input())
|
|
||||||
count = 0
|
|
||||||
while num != 0:
|
|
||||||
num -= sum(map(int,str(num)))
|
|
||||||
count += 1
|
|
||||||
print(count)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 整数变换的第二种做法
|
|
||||||
|
|
||||||
### 答案
|
|
||||||
|
|
||||||
```python
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
# 对 1000 以内的数进行预处理
|
|
||||||
# difference[i][j] 代表每次减去 sum_digits(i) + j 之后,剩余的值,可能为负数
|
|
||||||
# step[i][j] 记录最终结果
|
|
||||||
# 题目中数字的最大值范围为 10的9 次方,我们每一步多减去的 j 值代表的其实是前六位的和
|
|
||||||
# j 最大也就是 54( 前六位都是9 )
|
|
||||||
|
|
||||||
difference = [[0 for _ in range(55)] for _ in range(1000)]
|
|
||||||
step = [[0 for _ in range(55)] for _ in range(1000)]
|
|
||||||
|
|
||||||
# 这里要考虑一下边界情况,有可能后三位为 0 前六位还有
|
|
||||||
# 这个时候要减去前六位的值
|
|
||||||
# 并且步数需要加一步
|
|
||||||
difference[0] = [-num for num in range(55)]
|
|
||||||
step[0] = [0] + [1 for num in range(54)]
|
|
||||||
|
|
||||||
# 先计算出 0 ~ 999 以内的数的值
|
|
||||||
for i in range(1,1000):
|
|
||||||
# 遍历所有可能的 j 值
|
|
||||||
for j in range(55):
|
|
||||||
ans,num = 0,i
|
|
||||||
while num > 0:
|
|
||||||
num -= ( sum(map(int,str(num))) + j )
|
|
||||||
ans += 1
|
|
||||||
difference[i][j] = num
|
|
||||||
step[i][j] = ans
|
|
||||||
|
|
||||||
# 当 num 小于 1000 的时候直接返回
|
|
||||||
num = int(input())
|
|
||||||
if num < 1000:
|
|
||||||
print(step[num][0])
|
|
||||||
else:
|
|
||||||
ans = 0
|
|
||||||
while num:
|
|
||||||
if num < 1000:
|
|
||||||
ans += step[num][0]
|
|
||||||
break
|
|
||||||
# x 为前六位
|
|
||||||
# y 为后三位
|
|
||||||
x = num // 1000
|
|
||||||
y = num % 1000
|
|
||||||
# 直接加上步数,以及溢出的值即可
|
|
||||||
ans += step[y][sum(map(int,str(x)))]
|
|
||||||
num = x * 1000 + difference[y][sum(map(int,str(x)))]
|
|
||||||
print(ans)
|
|
||||||
```
|
|
||||||
|
|
||||||
## 四、火车运输
|
|
||||||
|
|
||||||
## 五、最大区间
|
|
||||||
|
|
||||||
### 题目
|
|
||||||
|
|
||||||
题目链接:0最大区间 - 蓝桥云课
|
|
||||||
|
|
||||||
### 问题描述
|
|
||||||
|
|
||||||
给定一个长度为 n 的序列 Ai,求 L,R 使 (R−L+1)⋅min(AL,AL+1,…,AR) 尽可能大,其中 min 表示最小值。
|
|
||||||
|
|
||||||
你只需要输出最大的值即可,不需要输出具体的 L,R。
|
|
||||||
|
|
||||||
### 解析
|
|
||||||
|
|
||||||
这道题要求给定字符串中的 (R−L+1)⋅min(AL,AL+1,…,AR) 的最大值,每次计算区间内的最小值再扩展区间很麻烦,我们可以转换一下思路以当前元素为区间内的最小元素,视为右边界,然后不断向左边扩展,一直到小于当前元素的位置停下,就可以得到当前区间的结果。(这里为什么不用向右边扩展,因为如果存在这种情况的话,右边向左扩展的时候会包括这一段)我们可以采用单调递增栈来加快寻找的速度,0 是为了确保清空栈。
|
|
||||||
|
|
||||||
可以用下面这个例子 debug 一下程序查看运行过程。
|
|
||||||
|
|
||||||
n = 6
|
|
||||||
|
|
||||||
A = [1,2,3,2,3,2]
|
|
||||||
|
|
||||||
(感觉解释的还是不太够清楚,有啥问题可以直接评论)
|
|
||||||
|
|
||||||
### 答案
|
|
||||||
|
|
||||||
```python
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
n = int(input())
|
|
||||||
A = list(map(int,input().split()))
|
|
||||||
A.append(0)
|
|
||||||
|
|
||||||
stack = []
|
|
||||||
ans = 0
|
|
||||||
|
|
||||||
for i in range(n+1):
|
|
||||||
while stack and A[i] <= A[stack[-1]]:
|
|
||||||
prev_i = stack.pop()
|
|
||||||
L = stack[-1] if stack else -1
|
|
||||||
ans = max( ( i - L - 1 ) * A[prev_i] ,ans)
|
|
||||||
stack.append(i)
|
|
||||||
|
|
||||||
print(ans)
|
|
||||||
```
|
|
||||||
|
|
||||||
## 六、等腰三角形
|
|
||||||
|
|
||||||
## 七、连续数组
|
|
||||||
|
|
||||||
## 八、质数排序
|
|
||||||
|
|
||||||
## 九、选段排序
|
|
||||||
|
|
||||||
## 十、最长同类子串
|
|
||||||
|
|
||||||
@@ -0,0 +1,149 @@
|
|||||||
|
---
|
||||||
|
title: 2025 一带一路金砖 - 区块链应用开发与运维 样题详解
|
||||||
|
createTime: 2025/09/30 19:57:14
|
||||||
|
permalink: /article/2025-BR-BRICS-BC-App-Develop-Op-Sample-Q&A/
|
||||||
|
# password: simeng
|
||||||
|
---
|
||||||
|
|
||||||
|
## 第一部分:区块链平台运维
|
||||||
|
### 第1题:完成 FISCO BCOS 区块链系统部署并验证
|
||||||
|
**【要求】**
|
||||||
|
|
||||||
|
登录 Linux 服务器,完成节点部署、启动、将过程结果截图,保存至做答题并点击提交。
|
||||||
|
|
||||||
|
**【任务】**
|
||||||
|
|
||||||
|
登录 Linux 服务器,进入指定操作目录(/root/tools)基于开发部署工具 build_chain.sh 脚本在本地搭建一条 4 节点的 FISCO BCOS 链。起始端口号为:30300,20200,8545。将节点 2 的端口号改为 30500,20400,8675。确保搭建的区块链系统能正常运行,并将执行的命令和完整的命令执行结果截图保存至作答区并点击提交。具体任务如下:
|
||||||
|
|
||||||
|
**【参考答案】**
|
||||||
|
|
||||||
|
使用 build_chain.sh 脚本搭建 FISCO BCOS 链:
|
||||||
|
|
||||||
|
默认的 -v 会从网络中下载 FISCO BCOS,这里本地已经有了,比赛的时候肯定是断网的,这里直接调用本地的即可
|
||||||
|
```shell
|
||||||
|
bash build_chain.sh -l "127.0.0.1:4" -p 30300,20200,8545 -e ./fisco-bcos
|
||||||
|
```
|
||||||
|

|
||||||
|
在创建完成之后,当前目录会出现一个 nodes 文件夹,这里面就是我们的每个节点的具体配置信息等等
|
||||||
|
|
||||||
|
这里推荐下载一个 tree 用来查看目录结构
|
||||||
|
```shell
|
||||||
|
apt install tree
|
||||||
|
tree nodes
|
||||||
|
```
|
||||||
|
目录结构应该大概如下:
|
||||||
|
```
|
||||||
|
nodes
|
||||||
|
├── 127.0.0.1
|
||||||
|
│ ├── node0
|
||||||
|
│ │ ├── ...
|
||||||
|
│ ├── node1
|
||||||
|
│ │ ├── ...
|
||||||
|
│ ├── node2
|
||||||
|
│ │ ├── ...
|
||||||
|
│ ├── node3
|
||||||
|
│ │ ├── ...
|
||||||
|
```
|
||||||
|
每个 node 文件夹都包含 `conf/`(配置文件),`log/`(日志文件),`fisco-bcos/`(二进制副本)
|
||||||
|
|
||||||
|
修改节点2的端口,根据要求修改为 30500,20400,8675
|
||||||
|
|
||||||
|
编辑配置文件:`vim nodes/127.0.0.1/node2/config.ini`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
[rpc]
|
||||||
|
channel_listen_ip=0.0.0.0
|
||||||
|
channel_listen_port=20202 # [!code --]
|
||||||
|
channel_listen_port=20400 # [!code ++]
|
||||||
|
jsonrpc_listen_ip=127.0.0.1
|
||||||
|
jsonrpc_listen_port=8547 # [!code --]
|
||||||
|
jsonrpc_listen_port=8675 # [!code ++]
|
||||||
|
disable_dynamic_group=false
|
||||||
|
[p2p]
|
||||||
|
listen_ip=0.0.0.0
|
||||||
|
listen_port=30302 # [!code --]
|
||||||
|
listen_port=30500 # [!code ++]
|
||||||
|
; nodes to connect
|
||||||
|
node.0=127.0.0.1:30300
|
||||||
|
node.1=127.0.0.1:30301
|
||||||
|
node.2=127.0.0.1:30302 # [!code --]
|
||||||
|
node.2=127.0.0.1:30500 # [!code ++]
|
||||||
|
node.3=127.0.0.1:30303
|
||||||
|
```
|
||||||
|
不光节点2的 \[p2p\] 的 `node.2` 需要修改,其他几个节点的都需要进行修改
|
||||||
|
|
||||||
|
切换到 `nodes/127.0.0.1/` 目录下,通过 `bash start_all.sh` 启动所有节点
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
验证端口是否生效:`ss -tuln | grep -E '30500|20400|8675'`
|
||||||
|
|
||||||
|
预期输出如下:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
tcp LISTEN 0 4096 127.0.0.1:8675 0.0.0.0:*
|
||||||
|
tcp LISTEN 0 4096 0.0.0.0:30500 0.0.0.0:*
|
||||||
|
tcp LISTEN 0 4096 0.0.0.0:20400 0.0.0.0:*
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
主要参考文章如下:
|
||||||
|
|
||||||
|
1. [搭建第一个区块链网络](https://fisco-doc.readthedocs.io/zh/latest/docs/installation.html)
|
||||||
|
2. [开发部署工具(build_chain.sh)](https://fisco-bcos-documentation.readthedocs.io/zh-cn/latest/docs/manual/build_chain.html)
|
||||||
|
|
||||||
|
### 第2题:按要求完成WeBASE-Node-Manager的安装与部署
|
||||||
|
**【要求】**
|
||||||
|
|
||||||
|
登录 Linux 服务器,部署节点管理平台,并将部署、启动、应用过程结果截图,保存至作答区并点击提交。
|
||||||
|
|
||||||
|
**【任务】**
|
||||||
|
|
||||||
|
登录 linux 服务器,进入指定操作目录(/root/tools/webase)中完成区块链一体化管理平台的配置部署,并检查是否安装成功,具体操作任务如下:
|
||||||
|
1. 进入 WeBASE-Node-Manager 目录,完成数据库初始化操作;
|
||||||
|
2. 修改 application.yml 配置文件,进行 WeBASE-Node-Manager 的服务配置,包括数据库名称,数据库用户,数据库密码等;
|
||||||
|
3. 使用命令启动 WeBASE-Node-Manager 管理平台服务,并检查节点管理是否正常启动;
|
||||||
|
4. 使用 swagger 对节点管理服务的 API 接口测试。
|
||||||
|
|
||||||
|
**【参考答案】**
|
||||||
|
|
||||||
|
切换到 WeBASE-Front 目录下,将 `dist/conf_template` 配置文件目录复制一份作为配置文件,命名为 `dist/conf`
|
||||||
|
```bash
|
||||||
|
# 当前工作目录为:/home/simeng/blockchain/WeBASE-Front
|
||||||
|
cp -r dist/conf_template dist/conf
|
||||||
|
```
|
||||||
|
|
||||||
|
将节点所在目录 `nodes/{$ip}/sdk` 下的所有文件拷贝到当前的 `dist/conf` 目录下
|
||||||
|
```bash
|
||||||
|
cp ../nodes/127.0.0.1/sdk/* ./dist/conf/
|
||||||
|
```
|
||||||
|
|
||||||
|
编辑配置文件 : `vim dist/conf/application.yml`
|
||||||
|
```bash
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
配置环境变量 JAVA_HOME
|
||||||
|
编辑配置文件:`~/.bashrc`,添加如下内容
|
||||||
|
```bash
|
||||||
|
export JAVA_HOME="/usr/lib/jvm/java-1.8.0-openjdk-amd64"
|
||||||
|
export PATH=$JAVA_HOME/bin:$PATH
|
||||||
|
```
|
||||||
|
刷新环境变量
|
||||||
|
```bash
|
||||||
|
source ~/.bashrc
|
||||||
|
```
|
||||||
|
|
||||||
|
切换到 `dist` 目录下,拉取 Solidity 开发工具 v0.8.11.js(这一步可能会很慢,建议直接把 v0.8.11.js 下载到本地,然后直接放到对应目录,WSL2 的机器是直接挂载在 Windows 上的,Windows 可以直接操作)
|
||||||
|
```bash
|
||||||
|
wget https://github.com/WeBankBlockchain/WeBASELargeFiles/releases/download/v3.0.0/v0.8.11.js -O static/static/js/v0.8.11.js
|
||||||
|
```
|
||||||
|
|
||||||
|
运行 `bash start.sh` 启动 WeBASE-Node-Manager 管理平台服务
|
||||||
|
```bash
|
||||||
|
# 当前工作目录为:/home/simeng/blockchain/WeBASE-Front/dist
|
||||||
|
bash start.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
由于 localhostForwarding 机制(具体参考:[配置WSL2资源](https://notes.simengweb.com/article/operate-wsl2/#%E7%BD%91%E7%BB%9C%E7%9B%B8%E5%85%B3%E9%85%8D%E7%BD%AE)),我们可以直接通过localhost来访问WSL2容器的端口的服务
|
||||||
|
访问:[http://localhost:5002/WeBASE-Front](http://localhost:5002/WeBASE-Front)
|
||||||
@@ -1,296 +0,0 @@
|
|||||||
---
|
|
||||||
title: 爱莉希雅语录
|
|
||||||
createTime: 2026/01/08 15:39:17
|
|
||||||
permalink: /archives/a5b3ea8e-7c3c-40a1-a737-26e911623da8/
|
|
||||||
tags:
|
|
||||||
- honkai-impact-3rd
|
|
||||||
---
|
|
||||||
嗨,亲爱的来访者♪ 欢迎来到这片收集了“真我”与“美丽”碎片的园圃。在这里,你会读到执拗花朵在暴雨中的坚持,也会听见逐火英桀们为文明奏响的最后颂歌。请怀着期待慢慢翻阅吧,愿这些如星光般的文字能陪你开启属于自己的闪耀旅程,毕竟……你本身就是这世间最瑰丽的馈赠呢♪
|
|
||||||
<!-- more -->
|
|
||||||
1. 执拗的花朵永远不会因暴雨而褪去颜色,你的决心也一定能在绝境中绽放真我。
|
|
||||||
|
|
||||||
2. 愿你前行的道路有群星闪耀。愿你留下的足迹有百花绽放。你即是上帝的馈赠,世界因你而瑰丽。
|
|
||||||
|
|
||||||
3. 悲剧并非终结,而是希望的起始。
|
|
||||||
|
|
||||||
4. 此后,将有群星闪耀,因为我如今来过。此后,将有百花绽放,因为我从未离去。
|
|
||||||
|
|
||||||
5. 告别过去,是为了走向未来
|
|
||||||
|
|
||||||
6. 我名为爱莉希雅……最初的律者,人之律者。
|
|
||||||
|
|
||||||
7. 某一日,祂从天坠落。人们抬头仰望,于是看见了星空。星月送来神的女儿,她愿成为人的伴侣。长风化作她的轺车,四海落成她的园圃。鸟雀衔来善的种子,百花编织爱的颂歌。她便是这样降生于世,行于大地,与人类一同长大,与世界一起发芽。
|
|
||||||
|
|
||||||
8. 亲爱的山雀,请将我的箭,我的花,与我的爱,带给那子然独行的旅人。然后,便让它开出永恒而无瑕的…人性之华吧。
|
|
||||||
|
|
||||||
9. 压力之下的选择才能揭示一个人的真我,也将决定他最终会成为怎样的人。
|
|
||||||
|
|
||||||
10. 即使未来不能改变,我也要自己决定到达那个结果的过程。
|
|
||||||
|
|
||||||
11. 这是段漫长的路途,你或许会停滞不前,甚至在很长一段时间里一无所获。但这未必是件坏事,并不是一定要做到了什么,达成了什么才算前进。空虚、迷茫、犹豫、自否,对于一个人的一生,这些过程同样拥有它们的意义——就像这里的十三个人曾经历的那样。
|
|
||||||
|
|
||||||
12. 天色暗了,接下来就是调皮捣蛋的时间了呢。
|
|
||||||
|
|
||||||
13. 白天的我也很可爱,晚上的我也很可爱,你更喜欢哪个我呢?
|
|
||||||
|
|
||||||
14. 说起粉色头发的可爱女孩,你第一个会想到谁?321回答!
|
|
||||||
|
|
||||||
15. 你好像有不少问题想问我呢,别心急,我们还有很多很多时间。
|
|
||||||
|
|
||||||
16. 你喜欢脚踩在落叶或是新雪上的感觉吗?我很喜欢哦。
|
|
||||||
|
|
||||||
17. 你比我想象中还可爱许多呢。是不是很在意我想象了什么?
|
|
||||||
|
|
||||||
18. 今天的任务都完成啦?真棒,夸夸你哦。
|
|
||||||
|
|
||||||
19. 嗨!今天天气真好,和我一样闪闪发光呢。
|
|
||||||
|
|
||||||
20. 嗨,早上好!一天的好心情,从见到你开始。
|
|
||||||
|
|
||||||
21. 嗨,早上好呀!看见我,有没有很开心呢?
|
|
||||||
|
|
||||||
22. 你该休息啦,约好了,我们明天再见哦。
|
|
||||||
|
|
||||||
23. 天冷了,小心别着凉哦。我?美少女怎么会冷呢。
|
|
||||||
|
|
||||||
24. 今天是我的生日哦。要一起庆祝吗?就我们两个人。
|
|
||||||
|
|
||||||
25. 哎呀,你也睡不着吗?那我们来聊聊天,好不好?
|
|
||||||
|
|
||||||
26. 有些事不用太在意,美丽的少女总有些小秘密,不是吗?
|
|
||||||
|
|
||||||
27. 这么晚了还不睡吗?是在想我,对不对?
|
|
||||||
|
|
||||||
28. 知道吗,今天是个很特别的日子。对啦,就是你的生日♪
|
|
||||||
|
|
||||||
29. 好啦,冷静一下♪
|
|
||||||
|
|
||||||
30. 以律者的标准,你的样子还真有些......别致呢♪
|
|
||||||
|
|
||||||
31. 哎呀,别生气嘛♪
|
|
||||||
|
|
||||||
32. 真是的......我会哭的哦......
|
|
||||||
|
|
||||||
33. 抱歉......得让你失望啦♪
|
|
||||||
|
|
||||||
34. 英桀们的谢幕,必须盛大而壮丽
|
|
||||||
|
|
||||||
35. 只是你我二人独处,显然有些寂寞了♪
|
|
||||||
|
|
||||||
36. 你说对不对?我的好·伊·甸♪
|
|
||||||
|
|
||||||
37. 你明明也很开心嘛♪
|
|
||||||
|
|
||||||
38. 英桀们也不过是心绪复杂,带有缺陷的普通人,就和当时的每个普通人一样。有自己的私心,也多半谈不上有什么大爱,但也会因为一时感动,或是心血来潮,去做一些冲动,又了不起的事。他们从来不是完美的英雄,只是因为在那个时间,出现在那里的是他们而已。
|
|
||||||
|
|
||||||
39. 我相信换做其他人,也一定会做出小异大同的选择,甚至做得更好也说不定?这就是[美丽]的永恒,我所相信的[真我]。十三个人生命的全部就是如此纷繁而又美妙的事物,意义在于在这个过程中我想要相信什么。那会决定在抵达终点时你能得到什么。
|
|
||||||
|
|
||||||
40. 你看,他们曾如此骄傲地活过,贯彻始终,以生命奏响了文明的颂歌,这是被称作英桀的人们的故事,是十三位逐火者未尽的旅途,但来访者,你们的道路仍将延续,不是吗,那就听凭心意前进吧,嘻,都说了要前进啦,沿着脚下的足迹,去见证这段逐火的征程,最后跨越逝者们的终幕,去创造我们所未能迎接的未来吧。
|
|
||||||
|
|
||||||
41. 要流传给后世的,绝不应该只有憎恨和使命。只有领略过这个时代的光辉和灿烂,未来的人们才能理解我们为之而战的意义。
|
|
||||||
|
|
||||||
42. 由英桀们来传承的,应当是身为英桀的荣耀,战斗的理由。是在这段逐火的旅途中,我们最终的灵魂所托。
|
|
||||||
|
|
||||||
43. 而后来的你们,将见证我们生命的足迹,灵魂的刻印,并在其中找到自己寻求的答案。
|
|
||||||
|
|
||||||
44. 但无论如何 我们的时代已经结束了,而我们也努力完成了各自的使命。
|
|
||||||
|
|
||||||
45. 不是被推搡,被裹挟着向前,也不是沿着被别人选定的道路行进。而是怀着这样的想法——我想要怎么做,我应该怎么做——去决定自己的命路,自己的刻印。
|
|
||||||
|
|
||||||
46. 以自我的意志,朝着自身所设下的目标,用自在的方式,竭尽全力活过“我”作为主角的一生。
|
|
||||||
|
|
||||||
47. 你看,我是这样的,大家也是这样的。我所知晓的那个时代,以及它所孕育出的这十三个人,都是如此。万千的轨迹交织、萦绕,由此诞下百花齐放的奇迹。这就是“美丽”的永恒,我所相信的“真我”。
|
|
||||||
|
|
||||||
48. 至此,我们的故事结束了。而从今以后,就是“你”的故事了。
|
|
||||||
|
|
||||||
49. 让无人传颂的歌谣,再度启唱。
|
|
||||||
|
|
||||||
50. 为历史尘封的故事,续写新章。
|
|
||||||
|
|
||||||
51. 高洁而纯真的灵魂,踏上旅途。
|
|
||||||
|
|
||||||
52. 愿掠集之兽爱佑其身,赐予「空梦」的自由。
|
|
||||||
|
|
||||||
53. 愿渡尘之羽爱佑其身,赐予「浮生」的坚忍。
|
|
||||||
|
|
||||||
54. 愿绘世之卷爱佑其身,赐予「繁星」的纯真。
|
|
||||||
|
|
||||||
55. 愿噬界之蛇爱佑其身,赐予「无限」的渴求。
|
|
||||||
|
|
||||||
56. 愿黎明之哨爱佑其身,赐予「旭光」的意志。
|
|
||||||
|
|
||||||
57. 愿寸断之刃爱佑其身,赐予「刹那」的决绝。
|
|
||||||
|
|
||||||
58. 愿善法之瞳爱佑其身,赐予「天慧」的智识。
|
|
||||||
|
|
||||||
59. 愿坏劫之焱爱佑其身,赐予「鏖灭」的赤诚。
|
|
||||||
|
|
||||||
60. 愿愚戏之匣爱佑其身,赐予「螺旋」的奇迹。
|
|
||||||
|
|
||||||
61. 愿璀耀之歌爱佑其身,赐予「黄金」的光辉。
|
|
||||||
|
|
||||||
62. 愿深罪之槛爱佑其身,赐予「戒律」的慈爱。
|
|
||||||
|
|
||||||
63. 愿无烬之剑爱佑其身,赐予「救世」的理想。
|
|
||||||
|
|
||||||
64. 愿无瑕之人爱佑其身,赐予「真我」的回归。
|
|
||||||
|
|
||||||
65. 我们于此赞颂,始源之律者的诞生
|
|
||||||
|
|
||||||
66. 我这支歌将抚摸你的前额,犹如那祝福的亲吻
|
|
||||||
|
|
||||||
67. 当你独自一人时,它会坐在身旁与你耳语
|
|
||||||
|
|
||||||
68. 当你陷入人群时,它又会保护你远离喧嚣。
|
|
||||||
|
|
||||||
69. 我的歌将成为你梦想的羽翼,它将载着你的心到那未知的边缘
|
|
||||||
|
|
||||||
70. 当黑夜遮蔽了你的路时,它又成为了照耀在你头顶的忠实星光
|
|
||||||
|
|
||||||
71. 于是,「始源」的故事迎来落幕
|
|
||||||
|
|
||||||
72. 于是,「始源」的故事迎来伊始
|
|
||||||
|
|
||||||
73. 以我为终……
|
|
||||||
|
|
||||||
74. 以我……为始。
|
|
||||||
|
|
||||||
75. 而今,终焉之时将至。而今,归去之时已至。就此告别吧,美丽的世界。
|
|
||||||
|
|
||||||
76. 不想前进的时候,就暂且停下脚步吧,我们永远都在这里,和当初一模一样,毕竟,美丽的女孩子什么都能做到嘛!
|
|
||||||
|
|
||||||
77. 游云拂风,花染湛空,与你并肩漫步在午后街头,携手美好此刻,共赏岁月温柔。
|
|
||||||
|
|
||||||
78. 经常会和别的女孩子谈论你哦。内容……是不是很想知道呀~
|
|
||||||
|
|
||||||
79. 有些事不用太在意,美丽的少女总有些小秘密,不是吗?
|
|
||||||
|
|
||||||
80. 这里有好多和我一样漂亮的女孩子呀,是天堂吗?
|
|
||||||
|
|
||||||
81. 你会不会嫌我话多呢?可我就是有好多话想对你说呀。
|
|
||||||
|
|
||||||
82. 不许叫错我的名字噢,不然……我会有小情绪的。
|
|
||||||
|
|
||||||
83. 如此绚丽的花朵,不该在绽放之前就枯萎。我会赠予你璀聚的祝福,而你的灵魂,也将会绽放更耀眼的光辉。
|
|
||||||
|
|
||||||
84. 嗨,想我了吗?
|
|
||||||
|
|
||||||
85. 不论何时何地,爱莉希雅都会回应你的期待
|
|
||||||
|
|
||||||
86. 嗨,我又来啦。多夸夸我好吗?我会很开心的~♪
|
|
||||||
|
|
||||||
87. 你好!新的一天,从一场美妙的邂逅开始。
|
|
||||||
|
|
||||||
88. 终于轮到我啦,这段时间我可是一直都在构思与你见面的开场白呢。
|
|
||||||
|
|
||||||
89. 你可以更光明正大的看向我噢,毕竟我也直在看着你嘛。来,让我们更深入地了解彼此吧?
|
|
||||||
|
|
||||||
90. 唉,要做的事好多~但焦虑可是女孩子的大敌,保持优雅得体,从容愉快地前进吧。
|
|
||||||
|
|
||||||
91. 别看我这样,其实我也是很忙的。不过,我的日程上永远有为你预留的时间。
|
|
||||||
|
|
||||||
92. 唉,时间真是个讨厌的东西,你不觉得吗?如果不是时间紧迫,我还想留下更多值得纪念的话语。
|
|
||||||
|
|
||||||
93. 有没有觉得我的话要比别人多一点?多就对啦,我可是有在很认真地准备这件事的。
|
|
||||||
|
|
||||||
94. 哇,你看那朵白白软软的云,是不是有点像我呢?
|
|
||||||
|
|
||||||
95. 可爱的少女心可是无所不能的噢~♪
|
|
||||||
|
|
||||||
96. 好啦可以啦,再说下去我就要哭了噢~♪
|
|
||||||
|
|
||||||
97. 这束鲜花,要心怀感激的收下哦~♪
|
|
||||||
|
|
||||||
98. 要好好看着我哦~♪
|
|
||||||
|
|
||||||
99. 这身衣服是伊甸做的噢,喜欢吗,还是说,喜欢的是我呢~♪
|
|
||||||
|
|
||||||
100. 别动噢,借你的眼睛照照镜子……好啦,我看起来怎么样?
|
|
||||||
|
|
||||||
101. 嗯~和女孩子独处时,可要好好看向对方的眼晴噢~♪
|
|
||||||
|
|
||||||
102. 有空多来陪陪我好吗,你一定不忍心让可爱的我孤独寂寞吧。
|
|
||||||
|
|
||||||
103. 这可是你选的衣服,要好好看着,不许移开视线噢。
|
|
||||||
|
|
||||||
104. 加点浪漫的气氛,如何?
|
|
||||||
|
|
||||||
105. 哇谢谢!我就知道你对我最好啦!
|
|
||||||
|
|
||||||
106. 爱莉希雅的贴心提示!你可以尽情的依赖爱莉希雅,而她,也会以全部的身心回应你!
|
|
||||||
|
|
||||||
107. 爱莉希雅的贴心提示!如果见到凯文在大厅里,多穿点衣服,他很冷的,各种意义上呢。
|
|
||||||
|
|
||||||
108. 在热情似火这件事上,我可不会输给千劫哦。
|
|
||||||
|
|
||||||
109. 这一次有你想要的东西吗?没有的话,我就可以再见你一面了。
|
|
||||||
|
|
||||||
110. 你应该不会嫌我话多吧?还是说,你更喜欢伊甸那种优雅文静的类型?
|
|
||||||
|
|
||||||
111. 如你所见,与那个凯文齐名的第二领袖,竟是一位如花朵般娇羞的少女也就是我啦。
|
|
||||||
|
|
||||||
112. 爱莉希雅的贴心提示!和樱说话时,别盯着耳朵看太久,她和我不一样,会害羞。
|
|
||||||
|
|
||||||
113. 想了想我毕竟算半个文职,说“武”也不太合适。那就去掉一个字,叫“女神”吧!你觉得呢?
|
|
||||||
|
|
||||||
114. 你说,我要是把头发留的和樱那么长,会不会更好看呀?嗯?
|
|
||||||
|
|
||||||
115. 你战斗的样子如此绚丽,就如同漫天的飞花一般。让我再为其添点色彩吧。
|
|
||||||
|
|
||||||
116. 这里埋藏着太多的历史、太多的秘密。但别担心,无论路有多长,我始终都会在你身边。
|
|
||||||
|
|
||||||
117. 如果我也成为律者,会是什么样子呢?头发会再变多些吗?还是说……裙摆会突然变长?
|
|
||||||
|
|
||||||
118. 说起来,为什么只有我的刻印是金色的伊甸不会生气吧?算啦,她那么好,一定不会生气的。
|
|
||||||
|
|
||||||
119. 咦,到我了吗?哎呀真可惜,这次的开场白我还没想好呢,原谅我吧,下次一定给你个惊喜。
|
|
||||||
|
|
||||||
120. 仔细想想,我是女孩子,战力比肩凯文,又有如神明般的聪慧与美貌。这么说来,我不也是“女武神”吗?
|
|
||||||
|
|
||||||
121. 爱莉希雅的贴心提示!如果遇到千劫,千万别问他有关面具的事哦。
|
|
||||||
|
|
||||||
122. 唉,不知道你见没见到这里的男孩们,他们都内向得很,还是我比较放得开吧?
|
|
||||||
|
|
||||||
123. 爱莉希雅的贴心提示!伊甸就像是天使一样——温柔,美丽,又总会予人以天使般的馈赠。
|
|
||||||
|
|
||||||
124. 这真是个神奇的地方,不是吗?它让我们的灵魂跨越时空于此相遇,多么浪漫呀。
|
|
||||||
|
|
||||||
125. 悄悄告诉你哦,我一共准备了40种不同的开场白。
|
|
||||||
|
|
||||||
126. 咦,你在看哪?哦......很在意这对耳朵?它们很漂亮对吗,我也这么觉得。
|
|
||||||
|
|
||||||
127. 你一定也在期待和我相遇吧?我知道,因为我的刻印总是很实用嘛。
|
|
||||||
|
|
||||||
128. 我的抉择与你的抉择,二者相遇会诞下怎样的奇迹,我想见证这一刻的到来。
|
|
||||||
|
|
||||||
129. 爱莉希雅的贴心提示!如果你不想听苏说话了,悄悄走开也行,他看不见。
|
|
||||||
|
|
||||||
130. 有我的刻印,你绝对不会输。输了也没关系,我会先一步去大厅等你回来。
|
|
||||||
|
|
||||||
131. 不要被三次元的女人骗了呀。
|
|
||||||
|
|
||||||
132. 英桀第二位,爱莉希雅,如你所见是一位像花朵般可爱的美丽少女。
|
|
||||||
|
|
||||||
133. 我们会留在过去,而你将走向未来。
|
|
||||||
|
|
||||||
134. 毕竟我向来不习惯太严肃的场合,也不喜欢过于拘谨的人际关系。
|
|
||||||
|
|
||||||
135. 再打下去我会哭的哦
|
|
||||||
|
|
||||||
136. 怎么样?喜欢我的记忆吗?有没有一种在和美丽的少女并肩作战的感觉?
|
|
||||||
|
|
||||||
137. 猜猜我为你带来了什么,金色的、闪闪发光的刻印,没有人能拒绝它,不是吗?
|
|
||||||
|
|
||||||
138. 见好就收哦!
|
|
||||||
|
|
||||||
139. 但我永远都会在这里,为你点亮前行的灯火。
|
|
||||||
|
|
||||||
140. 纵使知道文明已经覆灭,却依旧选择穷尽一切抗争到最后,只是为了将自己骄傲活过的样子永远铭刻在历史之中。
|
|
||||||
|
|
||||||
141. 悲伤不会凭空消失,但温暖的感情也会永远在心底珍藏。
|
|
||||||
|
|
||||||
142. 无论过去还是现在,可爱的少女心是无所不能的——只有这一点,无论如何都不会改变。
|
|
||||||
|
|
||||||
143. 那个白头发的漂亮女孩就是芽衣的...嗯哼哼,被我发现喽。
|
|
||||||
|
|
||||||
143.那是...华?唉,她交到了新的朋友,太好了。
|
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: 在 Windows10 上部署 WSL2 并启动 ubuntu 虚拟机
|
title: 在 Windows10 上部署 WSL2 并启动 ubuntu 虚拟机
|
||||||
createTime: 2025/09/29 07:13:17
|
createTime: 2025/09/29 07:13:17
|
||||||
|
cover: https://image.simengweb.com/elysia_cover_image/1pK7sI441p7x25YV.jpg
|
||||||
|
coverStyle:
|
||||||
|
layout: right
|
||||||
permalink: /article/deploying-wsl2-on-windows-10/
|
permalink: /article/deploying-wsl2-on-windows-10/
|
||||||
tags:
|
|
||||||
- wsl
|
|
||||||
---
|
---
|
||||||
|
|
||||||
嗨呀~让我们在 Windows10 专业版上部署 WSL2 并启动 ubuntu 20.04 虚拟机吧~
|
嗨呀~让我们在 Windows10 专业版上部署 WSL2 并启动 ubuntu 20.04 虚拟机吧~
|
||||||
@@ -112,6 +113,8 @@ Press any key to continue...
|
|||||||
#### 错误码 0x80370102 虚拟化技术没有开启
|
#### 错误码 0x80370102 虚拟化技术没有开启
|
||||||
如果遇到这个错误,可能是因为虚拟化技术没有开启哦~如果是在真实机上操作的话,需要进入BIOS开启虚拟化技术呢~开启之后,可以在任务管理器 -> 性能 -> CPU 的详情页面中,看到下方的虚拟化状态显示为「已启用」哦~
|
如果遇到这个错误,可能是因为虚拟化技术没有开启哦~如果是在真实机上操作的话,需要进入BIOS开启虚拟化技术呢~开启之后,可以在任务管理器 -> 性能 -> CPU 的详情页面中,看到下方的虚拟化状态显示为「已启用」哦~
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
如果是在虚拟机中操作的话,就需要启用嵌套虚拟化技术啦~以VMware为例:
|
如果是在虚拟机中操作的话,就需要启用嵌套虚拟化技术啦~以VMware为例:
|
||||||
|
|
||||||
先关闭虚拟机,然后找到虚拟机的.vmx配置文件(通常在虚拟机目录下),在文件末尾添加一行:`vhv.enable = "TRUE"`
|
先关闭虚拟机,然后找到虚拟机的.vmx配置文件(通常在虚拟机目录下),在文件末尾添加一行:`vhv.enable = "TRUE"`
|
||||||
@@ -142,4 +145,5 @@ wsl --terminate Ubuntu-20.04
|
|||||||
接下来,我们还可以通过VS Code来进行开发哦~不过需要先安装一些扩展呢:'Remote - SSH'和'WSL'
|
接下来,我们还可以通过VS Code来进行开发哦~不过需要先安装一些扩展呢:'Remote - SSH'和'WSL'
|
||||||
|
|
||||||
安装好扩展后,在VS Code左侧打开远程资源管理器,就可以看到Ubuntu20.04虚拟机啦~直接点击连接就可以了哦~不过第一次连接可能需要启动一下虚拟机,会有点慢呢,耐心等待一下吧~
|
安装好扩展后,在VS Code左侧打开远程资源管理器,就可以看到Ubuntu20.04虚拟机啦~直接点击连接就可以了哦~不过第一次连接可能需要启动一下虚拟机,会有点慢呢,耐心等待一下吧~
|
||||||
|

|
||||||
连接成功之后,就可以愉快地进行开发啦~是不是很简单呢~
|
连接成功之后,就可以愉快地进行开发啦~是不是很简单呢~
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
---
|
---
|
||||||
title: WSL2 核心操作指南
|
title: WSL2 核心操作指南
|
||||||
createTime: 2025/09/30 16:08:32
|
createTime: 2025/09/30 16:08:32
|
||||||
|
cover: https://image.simengweb.com/elysia_cover_image/2MEiWEhvsx8OtKeJ.png
|
||||||
permalink: /article/operate-wsl2/
|
permalink: /article/operate-wsl2/
|
||||||
tags:
|
|
||||||
- wsl
|
|
||||||
---
|
---
|
||||||
这篇文章主要讲WSL2虚拟机核心操作哦~它基于轻量级Hyper-V运行,像贴心小精灵默默工作~还能用命令行精细控制!接下来讲启动/关闭、实例管理、资源配置、网络操作、备份迁移这五大操作,是不是很期待呢~♪
|
这篇文章主要讲WSL2虚拟机核心操作哦~它基于轻量级Hyper-V运行,像贴心小精灵默默工作~还能用命令行精细控制!接下来讲启动/关闭、实例管理、资源配置、网络操作、备份迁移这五大操作,是不是很期待呢~♪
|
||||||
|
|
||||||
|
|||||||
@@ -1,84 +0,0 @@
|
|||||||
---
|
|
||||||
title: 通过位运算快速生成所有的子序列
|
|
||||||
createTime: 2026/01/09 16:15:00
|
|
||||||
permalink: /archives/ea20bdda-0d49-4472-a647-2e305a930d11/
|
|
||||||
tags:
|
|
||||||
- algorithms
|
|
||||||
---
|
|
||||||
## 一、子序列的本质
|
|
||||||
|
|
||||||
子序列是从原字符串中选择任意数量的字符(可以是 0 个、1 个、...、所有字符),且保持这些字符的原始顺序形成的字符串。
|
|
||||||
|
|
||||||
例如:原字符串 `"abc"` 的子序列包括 `"a"`, `"b"`, `"c"`, `"ab"`, `"ac"`, `"bc"`, `"abc"` 和空字符串 `""`(共 $2^3 = 8$ 种可能性)。
|
|
||||||
|
|
||||||
## 二、位掩码(Bitmask)的引入
|
|
||||||
|
|
||||||
假设字符串长度为 `n`,我们可以用一个 `n` 位的二进制数来表示是否选择某个字符:
|
|
||||||
- 二进制数的每一位对应原字符串的一个字符。
|
|
||||||
- 如果某一位是 `1`,表示选择对应的字符;如果是 `0`,表示不选择。
|
|
||||||
|
|
||||||
例如,对于字符串 `"abc"`(`n=3`):
|
|
||||||
|
|
||||||
| 二进制数 | 十进制 | 选择情况 | 生成子序列 |
|
|
||||||
| :--- | :--- | :--- | :--- |
|
|
||||||
| `101` | 5 | 选择第 1 个字符 "a" 和第 3 个字符 "c" | `"ac"` |
|
|
||||||
| `010` | 2 | 选择第 2 个字符 "b" | `"b"` |
|
|
||||||
|
|
||||||
## 三、遍历所有可能的二进制数
|
|
||||||
|
|
||||||
所有可能的二进制数范围是 `0`(全 0)到 $2^n - 1$(全 1)。例如,`n=3` 时:
|
|
||||||
- 范围是 `000` (0) 到 `111` (7),共 $2^3 = 8$ 种可能。
|
|
||||||
- 每个二进制数对应一种子序列选择方式。
|
|
||||||
- 如果包含 `000` (0),对应空字符串 `""`。若不需要空字符串,可以从 `1` 开始遍历。
|
|
||||||
|
|
||||||
## 四、如何将二进制数转换为子序列?
|
|
||||||
|
|
||||||
### 1. 外层循环:遍历所有可能的二进制数
|
|
||||||
对于 `n=3`,遍历 `1` (`001`) 到 `7` (`111`)。
|
|
||||||
|
|
||||||
### 2. 内层循环:检查每一位是否为 1
|
|
||||||
- 对于每个二进制数 `mask`,遍历其每一位 `i`(从 `0` 到 `n-1`)。
|
|
||||||
- 如果 `mask` 的第 `i` 位是 `1`(即 `mask & (1 << i)` 为真),则将原字符串的第 `i` 个字符加入子序列。
|
|
||||||
|
|
||||||
## 五、代码示例
|
|
||||||
|
|
||||||
```python
|
|
||||||
def get_all_subsequences(s):
|
|
||||||
n = len(s)
|
|
||||||
subsequences = []
|
|
||||||
# 遍历 1 到 2^n - 1 (排除空字符串)
|
|
||||||
for mask in range(1, 1 << n):
|
|
||||||
subseq = []
|
|
||||||
for i in range(n):
|
|
||||||
# 检查 mask 的第 i 位是否为 1
|
|
||||||
if mask & (1 << i):
|
|
||||||
subseq.append(s[i])
|
|
||||||
subsequences.append(''.join(subseq))
|
|
||||||
return subsequences
|
|
||||||
|
|
||||||
# 测试
|
|
||||||
print(get_all_subsequences("abc"))
|
|
||||||
# 输出: ['a', 'b', 'ab', 'c', 'ac', 'bc', 'abc']
|
|
||||||
```
|
|
||||||
|
|
||||||
## 六、应用场景
|
|
||||||
|
|
||||||
在处理类似 **X 质数** 的问题时,我们需要检查一个数字的所有数字子序列。通过这种位运算的方式,可以非常高效地生成所有组合并进行判断。
|
|
||||||
|
|
||||||
```python
|
|
||||||
def solve_x_prime(num):
|
|
||||||
s = str(num)
|
|
||||||
n = len(s)
|
|
||||||
for mask in range(1, 1 << n):
|
|
||||||
subseq_str = ""
|
|
||||||
for i in range(n):
|
|
||||||
if mask & (1 << i):
|
|
||||||
subseq_str += s[i]
|
|
||||||
|
|
||||||
# 将生成的子序列转换为整数并判断是否为质数
|
|
||||||
p = int(subseq_str)
|
|
||||||
if is_prime(p):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
```
|
|
||||||
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
---
|
|
||||||
title: 快速幂算法详解
|
|
||||||
createTime: 2026/01/09 16:05:00
|
|
||||||
permalink: /archives/1325a3bf-91d7-43ff-9630-e894549e12c1/
|
|
||||||
tags:
|
|
||||||
- algorithms
|
|
||||||
---
|
|
||||||
## 简介
|
|
||||||
|
|
||||||
快速幂算法是用于快速计算的算法,可以用于快速的处理大整数幂的场景。
|
|
||||||
|
|
||||||
最简单的 for 循环求数字的 n 次幂需要 $O(n)$ 的时间复杂度。快速幂方法可以达到 $O(\log N)$ 的时间复杂度。
|
|
||||||
|
|
||||||
快速幂的核心思想是将指数拆分,以达到快速计算的目的。
|
|
||||||
|
|
||||||
## 快速幂 - 二进制法
|
|
||||||
|
|
||||||
### 原理
|
|
||||||
|
|
||||||
二进制法的核心思想是将指数转换为二进制形式,通过逐位处理并结合平方运算减少乘法次数。
|
|
||||||
|
|
||||||
**二进制分解指数**
|
|
||||||
将指数 $n$ 表示为二进制形式,例如 $n=13$ 对应二进制为 `1101`,即 $13=8+4+1=2^3+2^2+2^0$。
|
|
||||||
|
|
||||||
**幂的拆分**
|
|
||||||
根据二进制分解,$a^n=a^{2^k} \times a^{2^{k-1}} \times \dots \times a^{2^0}$,其中仅当二进制位为 1 时,对应项被保留。
|
|
||||||
|
|
||||||
**逐位处理与平方加速**
|
|
||||||
- 从最低位到最高位依次处理二进制每一位。
|
|
||||||
- 若当前位为 1,则将当前的底数累乘到结果中。
|
|
||||||
- 每一步将底数平方,为处理更高位做准备。
|
|
||||||
|
|
||||||
### 代码示例
|
|
||||||
|
|
||||||
```python
|
|
||||||
def power(base, exp):
|
|
||||||
res = 1
|
|
||||||
while exp > 0:
|
|
||||||
if exp & 1:
|
|
||||||
res *= base
|
|
||||||
base *= base
|
|
||||||
exp >>= 1
|
|
||||||
return res
|
|
||||||
```
|
|
||||||
|
|
||||||
## 快速幂 - 折半法
|
|
||||||
|
|
||||||
### 原理
|
|
||||||
|
|
||||||
折半法的核心公式如下:
|
|
||||||
|
|
||||||
我们要快速计算 $a$ 的 $n$ 次方:
|
|
||||||
|
|
||||||
- 当 $n$ 为奇数的时候:$a^n = a \cdot (a^2)^{(n-1)/2}$
|
|
||||||
- 当 $n$ 为偶数的时候:$a^n = (a^2)^{n/2}$
|
|
||||||
- 当 $n$ 为 0 的时候,直接返回 1
|
|
||||||
|
|
||||||
通过递归或迭代,将大指数问题分解为小指数问题,最后合并结果。
|
|
||||||
|
|
||||||
### 代码示例
|
|
||||||
|
|
||||||
```python
|
|
||||||
def power(base, exp):
|
|
||||||
if exp == 0: return 1
|
|
||||||
half = power(base, exp // 2)
|
|
||||||
return half * half * (base if exp % 2 else 1)
|
|
||||||
```
|
|
||||||
|
|
||||||
## 两种方法对比
|
|
||||||
|
|
||||||
| 特性 | 二进制法 | 折半法 |
|
|
||||||
| :--- | :--- | :--- |
|
|
||||||
| **实现方式** | 迭代 + 位运算 | 递归/迭代 + 分治 |
|
|
||||||
| **时间复杂度** | $O(\log N)$ | $O(\log N)$ |
|
|
||||||
| **空间复杂度** | $O(1)$ | $O(\log N)$ (递归) |
|
|
||||||
|
|
||||||
两者都可以实现在指数时间解决问题,二进制方法比折半法更加的省空间。
|
|
||||||
@@ -1,300 +0,0 @@
|
|||||||
---
|
|
||||||
title: Python 字符串格式化全指南
|
|
||||||
createTime: 2026/01/09 14:00:48
|
|
||||||
permalink: /archives/56ea3081-9c69-43d7-96c8-2812ec08be2c/
|
|
||||||
tags:
|
|
||||||
- python
|
|
||||||
---
|
|
||||||
|
|
||||||
## 字符串格式化
|
|
||||||
|
|
||||||
我们经常会输出类似“亲爱的xxx你好!你xx月的话费是xx,余额是xx”之类的字符串,而xxx的内容都是根据变量变化的,所以需要一种简便的格式化字符串的方式。
|
|
||||||
|
|
||||||
在 Python 中可以使用很多种的字符串格式化方式,下面逐个列举。
|
|
||||||
|
|
||||||
### 字符串格式化的四种方式
|
|
||||||
|
|
||||||
#### 一、 %号
|
|
||||||
|
|
||||||
在Python中,可以采用%来实现字符串格式化,%号格式化字符串的方式是从Python诞生之处就已经存在,时至今日,Python官方也未放弃使用%号,但是也不推荐使用这种格式化方式。
|
|
||||||
|
|
||||||
在字符串内部,`%s`表示用字符串替换,`%d`表示用整数替换,有几个占位符,后面就跟几个变量或值,顺序要对应。如果只有一个`%?`,括号可以省略。如果不太确定应该用什么,可以使用`%s`,它会把任何数据类型转换为字符串。
|
|
||||||
|
|
||||||
有的时候,字符串里的%是一个普通的字符,这个时候就需要转义,用`%%`来表示一个%。
|
|
||||||
|
|
||||||
下面是常见的字符串格式化替代符:
|
|
||||||
|
|
||||||
- `%c`:格式化字符以及ASCII码
|
|
||||||
- `%s`:格式化字符串
|
|
||||||
- `%d`:格式化整数
|
|
||||||
- `%u`:格式化无符号整数
|
|
||||||
- `%o`:格式化无符号八进制数
|
|
||||||
- `%x`:格式化无符号十六进制数
|
|
||||||
- `%X`:格式化无符号十六进制数(大写)
|
|
||||||
- `%f`:格式化浮点数,可指定小数点后精度
|
|
||||||
- `%e`:用科学表示法格式化浮点数
|
|
||||||
- `%E`:同 %e
|
|
||||||
- `%g`:%f 和 %e 的简写
|
|
||||||
- `%G`:%f 和 %E 的简写
|
|
||||||
|
|
||||||
**Python 字符串格式化的代码示例:**
|
|
||||||
|
|
||||||
```python
|
|
||||||
a=15 #定义变量a并赋值
|
|
||||||
b=12345678.1234567 #定义变量b并赋值
|
|
||||||
strs="I love Python!" #定义字符串strs并赋值
|
|
||||||
print("a=%05d"%a) #使用m控制输出位数,空位补0
|
|
||||||
print("b=%8.3f"%b) #输出b,用m.n控制输出的长度和小数点位数
|
|
||||||
print("%s"%strs) #正常输出字符串,用于做比较
|
|
||||||
print("%17s"%strs) #使用m控制输出字符串长度
|
|
||||||
print("%r"%strs) #使用%r输出字符串
|
|
||||||
print("%-17.5s"%strs) #使用-m.n进行左对齐、限制字符串长度和字符位数
|
|
||||||
print("a={0:05}".format(a)) #输出5位数字空位补0
|
|
||||||
print("b={0:,.3f}".format(b)) #输出b,用千分位分隔并保留3位小数
|
|
||||||
print("{0:*^30}".format(strs)) #居中且使用*填充
|
|
||||||
```
|
|
||||||
|
|
||||||
**运行结果:**
|
|
||||||
|
|
||||||
```text
|
|
||||||
a=00015
|
|
||||||
b=12345678.123
|
|
||||||
I love Python!
|
|
||||||
I love Python!
|
|
||||||
'I love Python!'
|
|
||||||
I lov
|
|
||||||
a=00015
|
|
||||||
b=12,345,678.123
|
|
||||||
********I love Python!********
|
|
||||||
```
|
|
||||||
|
|
||||||
需要注意的一点是,Python中的中文大小为两字符,英文为一字符,所以说在写的时候要格外留意。
|
|
||||||
|
|
||||||
#### 二、 str.format 格式化
|
|
||||||
|
|
||||||
该format方法是在Python2.6中引入的,是字符串类型的内置方法。因为str.format的方式在性能(后面测试了一下,比%要慢)和使用的灵活性上都比%更胜一筹,所以推荐使用。
|
|
||||||
|
|
||||||
**1. 使用位置参数**
|
|
||||||
|
|
||||||
```python
|
|
||||||
# 按照位置一一对应
|
|
||||||
print('{} asked {} to do something'.format("egon","lili"))
|
|
||||||
print('{} asked {} to do something'.format("lili","egon"))
|
|
||||||
```
|
|
||||||
|
|
||||||
**2. 使用索引**
|
|
||||||
|
|
||||||
```python
|
|
||||||
# 使用索引取对应位置的值
|
|
||||||
print('{0}{0}{1}{0}'.format('x','y'))
|
|
||||||
```
|
|
||||||
|
|
||||||
**3. 使用关键字参数 or 字典**
|
|
||||||
|
|
||||||
```python
|
|
||||||
# 可以通过关键字or字典的方式格式化,打破了位置带来的限制与困扰
|
|
||||||
print('我的名字是 {name}, 我的年龄是 {age}.'.format(age=18, name='egon'))
|
|
||||||
|
|
||||||
kwargs = {'name': 'egon', 'age': 18}
|
|
||||||
print('我的名字是 {name}, 我的年龄是 {age}.'.format(**kwargs)) # 使用**进行解包操作
|
|
||||||
```
|
|
||||||
|
|
||||||
关于解包详解可以参看:<https://blog.csdn.net/qq_36667170/article/details/121401668>
|
|
||||||
|
|
||||||
**4. 填充和格式化**
|
|
||||||
|
|
||||||
```python
|
|
||||||
# 先取到值,然后在冒号后设定填充格式:[填充字符][对齐方式][宽度]
|
|
||||||
|
|
||||||
# *<10:左对齐,总共10个字符,不够的用*号填充
|
|
||||||
print('{0:*<10}'.format('开始执行')) # 开始执行******
|
|
||||||
|
|
||||||
# *>10:右对齐,总共10个字符,不够的用*号填充
|
|
||||||
print('{0:*>10}'.format('开始执行')) # ******开始执行
|
|
||||||
|
|
||||||
# *^10:居中显示,总共10个字符,不够的用*号填充
|
|
||||||
print('{0:*^10}'.format('开始执行')) # ***开始执行***
|
|
||||||
```
|
|
||||||
|
|
||||||
**5. 进度和进制**
|
|
||||||
|
|
||||||
```python
|
|
||||||
print('{salary:.3f}'.format(salary=1232132.12351)) #精确到小数点后3位,四舍五入
|
|
||||||
print('{0:b}'.format(123)) # 转成二进制,结果为:1111011
|
|
||||||
print('{0:o}'.format(9)) # 转成八进制,结果为:11
|
|
||||||
print('{0:x}'.format(15)) # 转成十六进制,结果为:f
|
|
||||||
print('{0:,}'.format(99812939393931)) # 千分位格式化
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 三、 f-Strings 格式化
|
|
||||||
|
|
||||||
虽然str.format()比%格式稍微高级了一些,但是它还是有自己的缺陷。当需要传入的字符串过多时,仍然会显得非常冗长。于是在Python3.6中引入了f-strings,不仅比str.format更简洁,性能上也更胜一筹。
|
|
||||||
|
|
||||||
f-string是以 f 或 F 开头的字符串,核心在于字符串中符号{}的使用。
|
|
||||||
|
|
||||||
**1. {} 中可以是变量名**
|
|
||||||
|
|
||||||
```python
|
|
||||||
name = 'egon'
|
|
||||||
age = 18
|
|
||||||
print(f'{name} {age}') # egon 18
|
|
||||||
print(F'{age} {name}') # 18 egon
|
|
||||||
```
|
|
||||||
|
|
||||||
**2. {} 中可以是表达式**
|
|
||||||
|
|
||||||
```python
|
|
||||||
# 可以在{}中放置任意合法的Python表达式,会在运行时计算
|
|
||||||
# 比如:数学表达式
|
|
||||||
print(f'{3*3/2}') # 4.5
|
|
||||||
|
|
||||||
# 比如:函数的调用
|
|
||||||
def foo(n):
|
|
||||||
print('foo say hello')
|
|
||||||
return n
|
|
||||||
|
|
||||||
print(f'{foo(10)}') # 会调用foo(10),然后打印其返回值
|
|
||||||
|
|
||||||
# 比如:调用对象的方法
|
|
||||||
name='EGON'
|
|
||||||
print(f'{name.lower()}') # egon
|
|
||||||
```
|
|
||||||
|
|
||||||
**3. 在类中的使用**
|
|
||||||
|
|
||||||
```python
|
|
||||||
class Person(object):
|
|
||||||
def __init__(self, name, age):
|
|
||||||
self.name = name
|
|
||||||
self.age = age
|
|
||||||
def __str__(self):
|
|
||||||
return f'{self.name}:{self.age}'
|
|
||||||
def __repr__(self):
|
|
||||||
return f'===>{self.name}:{self.age}<==='
|
|
||||||
|
|
||||||
obj=Person('egon',18)
|
|
||||||
print(obj) # 触发__str__
|
|
||||||
print(obj.__repr__()) # 触发__repr__
|
|
||||||
|
|
||||||
# 在f-Strings中的使用
|
|
||||||
print(f'{obj}') # 触发__str__
|
|
||||||
print(f'{obj!r}') # 触发__repr__
|
|
||||||
```
|
|
||||||
|
|
||||||
**4. 多行 f-Strings**
|
|
||||||
|
|
||||||
```python
|
|
||||||
# 当格式化字符串过长时,如下列表info
|
|
||||||
name = 'Egon'
|
|
||||||
age = 18
|
|
||||||
gender = 'male'
|
|
||||||
hobbie1='play'
|
|
||||||
hobbie2='music'
|
|
||||||
hobbie3='read'
|
|
||||||
info = [f'名字:{name}年龄:{age}性别:{gender}',
|
|
||||||
f'第一个爱好:{hobbie1}第二个爱好:{hobbie2}第三个爱好:{hobbie3}']
|
|
||||||
|
|
||||||
# 我们可以回车分隔到多行,注意每行前都有一个f
|
|
||||||
info = [
|
|
||||||
# 第一个元素
|
|
||||||
f'名字:{name}'
|
|
||||||
f' 年龄:{age}'
|
|
||||||
f' 性别:{gender}',
|
|
||||||
|
|
||||||
# 第二个元素
|
|
||||||
f'第一个爱好:{hobbie1}'
|
|
||||||
f' 第二个爱好:{hobbie2}'
|
|
||||||
f' 第三个爱好:{hobbie3}'
|
|
||||||
]
|
|
||||||
print(info)
|
|
||||||
```
|
|
||||||
|
|
||||||
**5. 引号的嵌套**
|
|
||||||
|
|
||||||
```python
|
|
||||||
# 当字符串嵌套发送冲突时,与正常的字符串处理方式是一样的
|
|
||||||
# 1、外层为单引号,内层嵌套也为单引号,并且想要输入的内容也为单引号,那么外层需要改用双引号
|
|
||||||
print("my name is 'egon'")
|
|
||||||
|
|
||||||
# 2、外层为单引号,内层嵌套也为单引号,并且想要输入的内容也为单引号,需要用到转义
|
|
||||||
print('my name is \'egon\'')
|
|
||||||
```
|
|
||||||
|
|
||||||
**6. 括号的处理**
|
|
||||||
|
|
||||||
需要注意的是,反斜杠可以用来进行字符转义,但不能用在 {} 的表达式中,注释 # 号也不能出现在 {} 的表达式中。
|
|
||||||
|
|
||||||
```python
|
|
||||||
f'{1\2}' # 语法错误
|
|
||||||
f'{x#}' # 语法错误
|
|
||||||
```
|
|
||||||
|
|
||||||
所以当我们的输出的结果中需要包含{}时,类似于之前输出%的做法,需要在原有的基础上再套一层。
|
|
||||||
|
|
||||||
```python
|
|
||||||
print(f'{{天王盖地虎}}') # {天王盖地虎}
|
|
||||||
print(f'{{{{天王盖地虎}}}}') # {{天王盖地虎}}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 四、 三种字符串格式化方式的性能对比
|
|
||||||
|
|
||||||
```python
|
|
||||||
from timeit import timeit
|
|
||||||
|
|
||||||
def test_s():
|
|
||||||
name = 'Egon'
|
|
||||||
age = 18
|
|
||||||
return '%s:%s.' % (name, age)
|
|
||||||
|
|
||||||
def test_format():
|
|
||||||
name = 'Egon'
|
|
||||||
age = 18
|
|
||||||
return '{}:{}.'.format(name, age)
|
|
||||||
|
|
||||||
def test_f_strings():
|
|
||||||
name = 'Egon'
|
|
||||||
age = 18
|
|
||||||
return f'{name}:{age}.'
|
|
||||||
|
|
||||||
res1 = timeit(test_s, number=1000000)
|
|
||||||
res2 = timeit(test_format, number=1000000)
|
|
||||||
res3 = timeit(test_f_strings, number=1000000)
|
|
||||||
print(res1) # 0.3338866999838501
|
|
||||||
print(res2) # 0.40734450006857514
|
|
||||||
print(res3) # 0.24273910000920296 最快
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 五、 标准库模版
|
|
||||||
|
|
||||||
从Python2.4起,Python标准库string引入了Template也可以用来格式化字符串,所以说,与前三种方式的一个显著的区别就是:Template属于Python语言的核心语法特征,使用方式如下。
|
|
||||||
|
|
||||||
```python
|
|
||||||
from string import Template
|
|
||||||
|
|
||||||
name='EGON'
|
|
||||||
t = Template('Hello $name!')
|
|
||||||
res=t.substitute(name=name)
|
|
||||||
|
|
||||||
print(res) # Hello EGON!
|
|
||||||
```
|
|
||||||
|
|
||||||
还有另外一个不同的地方是,这个模板字符串不支持类似str.format那样的进制转换,需要自己处理。
|
|
||||||
|
|
||||||
```python
|
|
||||||
from string import Template
|
|
||||||
|
|
||||||
name='EGON'
|
|
||||||
templ_string = 'Hello $name, there is a $error error!!!'
|
|
||||||
res=Template(templ_string).substitute(name=name, error=hex(12345))
|
|
||||||
|
|
||||||
print(res) # Hello EGON, there is a 0x3039 error!!!
|
|
||||||
```
|
|
||||||
|
|
||||||
另外稍微吐槽一下,这种方式真的慢。使用模板字符串Template的最佳的时机就是当你的程序需要处理由用户提供的输入内容时。模板字符串是最保险的选择,因为可以降低复杂性。其他一些复杂的字符串格式化技巧的可能会给你的程序带来安全漏洞。
|
|
||||||
|
|
||||||
### 总结
|
|
||||||
|
|
||||||
- 如果格式化的字符串是由用户输入的,那么基于安全性考虑,推荐使用Template。
|
|
||||||
- 如果使用的python3.6+版本的解释器,推荐使用f-Strings。
|
|
||||||
- 如果要兼容python2.x版本的python解释器,推荐使用str.format。
|
|
||||||
- 如果不是测试的代码,不推荐使用%。
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
---
|
|
||||||
title: 原码、反码、补码
|
|
||||||
createTime: 2026/01/08 16:34:05
|
|
||||||
permalink: /archives/6f41cabe-41e6-4a09-9f1c-af7dd709a35d/
|
|
||||||
tags:
|
|
||||||
- cs-fundamentals
|
|
||||||
---
|
|
||||||
|
|
||||||
欢迎来到 0 与 1 的魔法派对!这篇文章将带你揭开原码、反码与补码的奥秘,看计算机如何巧妙地用补码化减为加。让我们一起翻开这页,去捕捉二进制底层那份迷人的理性之美吧♪
|
|
||||||
|
|
||||||
<!-- more -->
|
|
||||||
|
|
||||||
## 一、 前置概念
|
|
||||||
|
|
||||||
计算机底层存储数据的时候使用的是二进制数字,但是计算机在存储一个数字的时候并不是直接存储该数字对应的二进制数,而是存储该数字对应的**二进制数的补码**。
|
|
||||||
|
|
||||||
在了解原码、反码和补码之前,我们要了解**机器数**和**真值**的概念。
|
|
||||||
|
|
||||||
### 1) 机器数
|
|
||||||
一个数在计算机的存储形式是二进制数,我们称这些二进制数为**机器数**。机器数是有符号的,在计算机中用机器数的最高位存放符号位,`0` 表示正数,`1` 表示负数。
|
|
||||||
|
|
||||||
### 2) 真值
|
|
||||||
因为机器数带有符号位,所以机器数的形式值不等于其真实表示的值(真值)。
|
|
||||||
- 以机器数 `1000 0001` 为例,其真正表示的值(首位为符号位)为 `-1`,而形式值(首位就是代表 1)为 `129`。
|
|
||||||
- 因此将带符号的机器数的真正表示的值称为机器数的**真值**。
|
|
||||||
|
|
||||||
## 二、 原码、反码与补码
|
|
||||||
|
|
||||||
### 1) 原码
|
|
||||||
原码的表示与机器数真值表示的一样,即用第一位表示符号,其余位表示数值。
|
|
||||||
- **正数**:就是它对应的二进制数。
|
|
||||||
- **负数**:将绝对值对应的二进制最左边位变为 `1`。
|
|
||||||
|
|
||||||
例如十进制的正负 1,用 8 位二进制的原码表示如下:
|
|
||||||
- `[+1]` = 原: `[ 0000 0001 ]`
|
|
||||||
- `[-1]` = 原: `[ 1000 0001 ]`
|
|
||||||
|
|
||||||
### 2) 反码
|
|
||||||
- **正数**:和原码相同。
|
|
||||||
- **负数**:在其原码的基础上,**符号位不变,其余各位取反**。
|
|
||||||
|
|
||||||
示例:
|
|
||||||
- `[+1]` = 原: `[ 0000 0001 ]` = 反: `[ 0000 0001 ]`
|
|
||||||
- `[-1]` = 原: `[ 1000 0001 ]` = 反: `[ 1111 1110 ]`
|
|
||||||
|
|
||||||
### 3) 补码
|
|
||||||
- **正数**:补码是其原码本身。
|
|
||||||
- **负数**:补码是在其原码的基础上,**符号位不变,其余各位取反后加 1**(即在反码的基础上加 1)。
|
|
||||||
|
|
||||||
示例:
|
|
||||||
- `[+1]` = 原: `[ 0000 0001 ]` = 反: `[ 0000 0001 ]` = 补: `[ 0000 0001 ]`
|
|
||||||
- `[-1]` = 原: `[ 1000 0001 ]` = 反: `[ 1111 1110 ]` = 补: `[ 1111 1111 ]`
|
|
||||||
|
|
||||||
## 三、 数据在计算机中的存储形式
|
|
||||||
|
|
||||||
计算机实际上只存储**补码**,所以说原码转换为补码的过程,也可以理解为数据存储到计算机内存中的过程。
|
|
||||||
|
|
||||||
在原、反、补码中,正数的表示是一模一样的,而负数的表示是不相同的。因此对于负数的补码来说,我们不能直接用进制转换将其转换为十进制数值,因为这样是得不到计算机真正存储的十进制数的。**应该将其转换为原码后,再将转换得到的原码进行进制转换为十进制数**(机器数包含符号位)。
|
|
||||||
|
|
||||||
## 四、 为什么会使用原码、反码、补码
|
|
||||||
|
|
||||||
对于人脑来说,知道机器数的第一位是符号位是一件很轻松的事情,但对于计算机基础电路设计来说判别第一位是符号位是非常难和复杂的事情。
|
|
||||||
|
|
||||||
为了让计算机底层设计更加简单,于是设计将符号位参与运算,并且**只保留加法**的方法,通过**加上一个负数的方式来实现减法**。这样让计算机运算更加简单,并且也让符号位参与到运算中去。
|
|
||||||
|
|
||||||
## 五、 使用原码、反码与补码进行运算
|
|
||||||
|
|
||||||
### 1) 使用原码运算
|
|
||||||
计算十进制表达式:`1 - 1 = 0`
|
|
||||||
```text
|
|
||||||
1 - 1 = 1 + (-1)
|
|
||||||
= 原:[ 0000 0001 ] + 原:[ 1000 0001 ]
|
|
||||||
= 原:[ 1000 0010 ] = -2
|
|
||||||
```
|
|
||||||
**结论**:如果用原码表示,让符号位也参与计算,对于减法来说,结果是不正确的。这也是计算机内部在存储数据时不使用原码的原因。为了解决这一问题,出现了反码。
|
|
||||||
|
|
||||||
### 2) 使用反码运算
|
|
||||||
计算十进制表达式:`1 - 1 = 0`
|
|
||||||
```text
|
|
||||||
1 - 1 = 1 + (-1)
|
|
||||||
= 原:[ 0000 0001 ] + 原:[ 1000 0001 ]
|
|
||||||
= 反:[ 0000 0001 ] + 反:[ 1111 1110 ]
|
|
||||||
= 反:[ 1111 1111 ] = 原:[ 1000 0000 ] = -0
|
|
||||||
```
|
|
||||||
**结论**:通过计算我们发现用反码计算减法,结果的真值部分是正确的。唯一的问题出现在 "0" 这个特殊的数值上。虽然人们理解上 `+0` 和 `-0` 是一样的,但是 0 带符号是没有任何意义的,而且会有 `[0000 0000]原` 和 `[1000 0000]原` 两个编码表示 0。为了解决这一问题,出现了补码。
|
|
||||||
|
|
||||||
### 3) 使用补码运算
|
|
||||||
计算十进制表达式:`1 - 1 = 0`
|
|
||||||
```text
|
|
||||||
1 - 1 = 1 + (-1)
|
|
||||||
= 原:[ 0000 0001 ] + 原:[ 1000 0001 ]
|
|
||||||
= 补:[ 0000 0001 ] + 补:[ 1111 1111 ]
|
|
||||||
= 补:[ 0000 0000 ] = 原:[ 0000 0000 ] = 0
|
|
||||||
```
|
|
||||||
**结论**:这样 0 用 `[0000 0000]` 表示,而以前出现问题的 `-0` 则不存在了。而且人们还发现可以用 `[1000 0000]` 表示 `-128`。
|
|
||||||
|
|
||||||
**-128 的推算过程如下**:
|
|
||||||
```text
|
|
||||||
(-1) + (-127) = -128
|
|
||||||
= 原:[ 1000 0001 ] + 原:[ 1111 1111 ]
|
|
||||||
= 补:[ 1111 1111 ] + 补:[ 1000 0001 ]
|
|
||||||
= 补:[ 1000 0000 ]
|
|
||||||
```
|
|
||||||
> **注意**:因为实际上是使用以前的 `-0` 的补码来表示 `-128`,所以 `-128` 并没有原码和反码表示。只要补码是 `[1000 0000]`,其十进制数值就为 `-128`。
|
|
||||||
|
|
||||||
### 4) 小结
|
|
||||||
因为补码能多存储一个 `-128`,而且在计算机底层中存储的是补码,所以在计算机中一个 8 位的二进制数的存储范围是用补码表示的 `[-128, 127]`,而不是用原码或反码表示的 `[-127, 127]`。这也可以解释为什么计算机中一个字节的取值范围是 `[-128, 127]`。
|
|
||||||
|
|
||||||
## 六、 总结(牢记)
|
|
||||||
|
|
||||||
- **二进制的最高位是符号位**:`0` 表示正数,`1` 表示负数。
|
|
||||||
- **正数三码合一**:正数的原码、反码、补码都一样。
|
|
||||||
- **负数反码**:它的原码符号位不变,其它位取反。
|
|
||||||
- **负数补码**:它的反码 + 1;反之,负数反码 = 负数补码 - 1。
|
|
||||||
- **0 的表示**:`0` 的反码、补码都是 `0`。
|
|
||||||
- **运算方式**:在计算机运算的时候都是以 **“补码”** 的方式来运算的。
|
|
||||||
- **查看结果**:当我们看运算结果的时候,要看它的**原码**(重点)。
|
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
---
|
---
|
||||||
title: 通过 EdgeOne Pages 搭建图床
|
title: 通过 EdgeOne Pages 搭建图床
|
||||||
createTime: 2025/09/29 02:28:17
|
createTime: 2025/09/29 02:28:17
|
||||||
|
cover: https://image.simengweb.com/elysia_cover_image/0lQviAbmMCVGN0Y6.png
|
||||||
permalink: /article/8gihio2v/
|
permalink: /article/8gihio2v/
|
||||||
tags:
|
|
||||||
- image-hosting
|
|
||||||
---
|
---
|
||||||
|
|
||||||
各位~今天要给大家介绍一个超棒的图床搭建方法哦!EdgeOne Pages 是腾讯云提供的静态网站托管服务,而且还有免费额度可以使用呢,对于日常需求来说完全足够啦~
|
各位~今天要给大家介绍一个超棒的图床搭建方法哦!EdgeOne Pages 是腾讯云提供的静态网站托管服务,而且还有免费额度可以使用呢,对于日常需求来说完全足够啦~
|
||||||
|
|||||||
@@ -22,9 +22,4 @@ list:
|
|||||||
link: https://www.ajohn.top/
|
link: https://www.ajohn.top/
|
||||||
avatar: https://github.com/zzyAJohn.png
|
avatar: https://github.com/zzyAJohn.png
|
||||||
desc: 我希望正在读这句话的人永远开心
|
desc: 我希望正在读这句话的人永远开心
|
||||||
-
|
|
||||||
name: 小小夏
|
|
||||||
link: https://notes.simengweb.com
|
|
||||||
avatar: https://image.simengweb.com/images/xxx.jpg
|
|
||||||
desc: Kawasaki天下第一!
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
---
|
|
||||||
title: 模型
|
|
||||||
createTime: 2026/03/29 20:00:00
|
|
||||||
permalink: /ai/
|
|
||||||
---
|
|
||||||
|
|
||||||
# 模型
|
|
||||||
|
|
||||||
这里是我和大模型、RAG、本地部署还有各种工具链打交道的地方~
|
|
||||||
|
|
||||||
和博客里的随想不同,这些笔记会更系统化一些,方便以后自己回来查的时候能一把抓到重点。左侧的目录会随着我记的东西越来越多而慢慢展开,就像「英语学习」「Web 开发」那些栏目一样,可以按专题慢慢逛。
|
|
||||||
|
|
||||||
当然啦,如果只是突然想到什么、想随手记几句碎碎念,还是会丢进 [博客](/blog/) 里,那里更适合发点动态更新的小东西~
|
|
||||||
@@ -1,252 +0,0 @@
|
|||||||
---
|
|
||||||
title: 便宜免费的大模型 API 整合 ( 2025年11月11日 )
|
|
||||||
createTime: 2025/11/11 13:54:02
|
|
||||||
permalink: /article/free_model_api/
|
|
||||||
sidebar: '/ai/'
|
|
||||||
---
|
|
||||||
|
|
||||||
百度千帆、讯飞星火、腾讯混元均有免费在线额度,SCNet 提供 0.1 元/百万 tokens 的超低价大模型,轻量任务先薅免费,量大了再掏 0.1 元,稳!
|
|
||||||
|
|
||||||
## 免费的大模型 API 整合
|
|
||||||
|
|
||||||
嗨~如果你在找既温柔又省荷包的小模型,就把它们悄悄收进这里吧!它们也许不是夜空最亮的那颗星,却能在摘要、划重点、轻声问答的小角落里,给你软软又稳稳的陪伴哦~
|
|
||||||
|
|
||||||
|
|
||||||
### 百度千帆大模型平台
|
|
||||||
|
|
||||||
先从千帆开始吧~下面是常用模型的参考价格:
|
|
||||||
|
|
||||||
| 模型名称 | 版本名称 | 服务内容 | 子项 | 在线推理 | 批量推理 | 单位 |
|
|
||||||
|---|---|---|---|---|---|---|
|
|
||||||
| ERNIE Speed | ernie-speed-128k | 推理服务 | 输入 | 0 | 0.00012 | 元/千tokens |
|
|
||||||
| ERNIE Speed | ernie-speed-128k | 推理服务 | 输出 | 0 | 0.00024 | 元/千tokens |
|
|
||||||
| ERNIE Lite | ernie-lite-8k | 推理服务 | 输入 | 0 | 0.0012 | 元/千tokens |
|
|
||||||
| ERNIE Lite | ernie-lite-8k | 推理服务 | 输出 | 0 | 0.0024 | 元/千tokens |
|
|
||||||
| ERNIE Tiny | ernie-tiny-8k | 推理服务 | 输入 | 0 | 0.00008 | 元/千tokens |
|
|
||||||
| ERNIE Tiny | ernie-tiny-8k | 推理服务 | 输出 | 0 | 0.00016 | 元/千tokens |
|
|
||||||
|
|
||||||
> 提示:单纯调用 API 接口属于在线推理,当前显示为 0 元;批量推理按量计费。
|
|
||||||
|
|
||||||
#### 快速上手
|
|
||||||
|
|
||||||
1. 访问控制台并获取 API Key。
|
|
||||||
2. 使用下方示例进行快速测试。
|
|
||||||
3. 若遇错误,请检查模型名称与凭证有效期。
|
|
||||||
|
|
||||||
控制台:[https://console.bce.baidu.com/qianfan/ais/console/apiKey](https://console.bce.baidu.com/qianfan/ais/console/apiKey)
|
|
||||||
|
|
||||||
```python
|
|
||||||
import requests
|
|
||||||
import json
|
|
||||||
|
|
||||||
def main():
|
|
||||||
url = "https://qianfan.baidubce.com/v2/chat/completions"
|
|
||||||
API_KEY = 'YOUR_API_KEY'
|
|
||||||
payload = json.dumps({
|
|
||||||
"model": "ernie-speed-128k",
|
|
||||||
"messages": [
|
|
||||||
{
|
|
||||||
"role": "system",
|
|
||||||
"content": "You are a helpful assistant."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"role": "user",
|
|
||||||
"content": "你好"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
headers = {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'Authorization': F'Bearer {API_KEY}'
|
|
||||||
}
|
|
||||||
response = requests.request("POST", url, headers=headers, data=payload)
|
|
||||||
print(response.text)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
||||||
```
|
|
||||||
|
|
||||||
> 小贴士:将 `YOUR_API_KEY` 替换为你的密钥,建议使用环境变量管理凭证;请勿在公共仓库提交 Key。
|
|
||||||
|
|
||||||
更多文档:[https://cloud.baidu.com/doc/qianfan-api/s/3m9b5lqft](https://cloud.baidu.com/doc/qianfan-api/s/3m9b5lqft)
|
|
||||||
|
|
||||||
### 讯飞星火大模型
|
|
||||||
|
|
||||||
接着,我们轻盈地来到星火~
|
|
||||||
|
|
||||||
- 官网:[https://xinghuo.xfyun.cn/sparkapi](https://xinghuo.xfyun.cn/sparkapi)
|
|
||||||
- 控制台:[https://console.xfyun.cn/services/cbm?from=desk](https://console.xfyun.cn/services/cbm?from=desk)
|
|
||||||
- 模型说明:可见 Spark Lite,Token 余量为无限。
|
|
||||||
|
|
||||||
#### 快速上手
|
|
||||||
|
|
||||||
> 小贴士:如需联网检索,启用 `web_search`;流式输出可以提升交互体验。
|
|
||||||
|
|
||||||
1. 在控制台获取授权凭证并替换到示例中。
|
|
||||||
2. 选择 `Lite` 模型,按需开启 `web_search` 与 `stream`。
|
|
||||||
3. 若需要长内容输出,注意 8K tokens 限制,合理裁剪上下文。
|
|
||||||
|
|
||||||
```python
|
|
||||||
# encoding:UTF-8
|
|
||||||
import json
|
|
||||||
import requests
|
|
||||||
|
|
||||||
|
|
||||||
# 请替换XXXXXXXXXX为您的 APIpassword, 获取地址:https://console.xfyun.cn/services/bmx1
|
|
||||||
api_key = "Bearer XXXXXXXXXX"
|
|
||||||
url = "https://spark-api-open.xf-yun.com/v1/chat/completions"
|
|
||||||
|
|
||||||
# 请求模型,并将结果输出
|
|
||||||
def get_answer(message):
|
|
||||||
#初始化请求体
|
|
||||||
headers = {
|
|
||||||
'Authorization':api_key,
|
|
||||||
'content-type': "application/json"
|
|
||||||
}
|
|
||||||
body = {
|
|
||||||
"model": "Lite",
|
|
||||||
"user": "user_id",
|
|
||||||
"messages": message,
|
|
||||||
# 下面是可选参数
|
|
||||||
"stream": True,
|
|
||||||
"tools": [
|
|
||||||
{
|
|
||||||
"type": "web_search",
|
|
||||||
"web_search": {
|
|
||||||
"enable": True,
|
|
||||||
"search_mode":"deep"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
full_response = "" # 存储返回结果
|
|
||||||
isFirstContent = True # 首帧标识
|
|
||||||
|
|
||||||
response = requests.post(url=url,json= body,headers= headers,stream= True)
|
|
||||||
# print(response)
|
|
||||||
for chunks in response.iter_lines():
|
|
||||||
# 打印返回的每帧内容
|
|
||||||
# print(chunks)
|
|
||||||
if (chunks and '[DONE]' not in str(chunks)):
|
|
||||||
data_org = chunks[6:]
|
|
||||||
|
|
||||||
# print(f"DEBUG: raw data_org: {data_org}")
|
|
||||||
chunk = json.loads(data_org)
|
|
||||||
text = chunk['choices'][0]['delta']
|
|
||||||
|
|
||||||
# 判断最终结果状态并输出
|
|
||||||
if ('content' in text and '' != text['content']):
|
|
||||||
content = text["content"]
|
|
||||||
if (True == isFirstContent):
|
|
||||||
isFirstContent = False
|
|
||||||
print(content, end="")
|
|
||||||
full_response += content
|
|
||||||
return full_response
|
|
||||||
|
|
||||||
|
|
||||||
# 管理对话历史,按序编为列表
|
|
||||||
def getText(text,role, content):
|
|
||||||
jsoncon = {}
|
|
||||||
jsoncon["role"] = role
|
|
||||||
jsoncon["content"] = content
|
|
||||||
text.append(jsoncon)
|
|
||||||
return text
|
|
||||||
|
|
||||||
# 获取对话中的所有角色的content长度
|
|
||||||
def getlength(text):
|
|
||||||
length = 0
|
|
||||||
for content in text:
|
|
||||||
temp = content["content"]
|
|
||||||
leng = len(temp)
|
|
||||||
length += leng
|
|
||||||
return length
|
|
||||||
|
|
||||||
# 判断长度是否超长,当前限制8K tokens
|
|
||||||
def checklen(text):
|
|
||||||
while (getlength(text) > 11000):
|
|
||||||
del text[0]
|
|
||||||
return text
|
|
||||||
|
|
||||||
|
|
||||||
#主程序入口
|
|
||||||
if __name__ =='__main__':
|
|
||||||
|
|
||||||
#对话历史存储列表
|
|
||||||
chatHistory = []
|
|
||||||
#循环对话轮次
|
|
||||||
while (1):
|
|
||||||
# 等待控制台输入
|
|
||||||
Input = input("\n" + "我:")
|
|
||||||
question = checklen(getText(chatHistory,"user", Input))
|
|
||||||
# 开始输出模型内容
|
|
||||||
print("星火:", end="")
|
|
||||||
getText(chatHistory,"assistant", get_answer(question))
|
|
||||||
```
|
|
||||||
|
|
||||||
### 腾讯混元大模型
|
|
||||||
|
|
||||||
- 计费与价格:[https://cloud.tencent.com/document/product/1729/97731](https://cloud.tencent.com/document/product/1729/97731)
|
|
||||||
- 文档与控制台:[https://cloud.tencent.com/document/product/1729/111008](https://cloud.tencent.com/document/product/1729/111008)
|
|
||||||
- 模型说明:`hunyuan-lite` 免费可用,适合轻量任务。
|
|
||||||
|
|
||||||
```python
|
|
||||||
import os
|
|
||||||
from openai import OpenAI
|
|
||||||
|
|
||||||
# 构造 client(建议使用环境变量管理密钥)
|
|
||||||
api_key = os.getenv("HUNYUAN_API_KEY", "YOUR_API_KEY")
|
|
||||||
client = OpenAI(
|
|
||||||
api_key=api_key, # 混元 APIKey
|
|
||||||
base_url="https://api.hunyuan.cloud.tencent.com/v1", # 混元 endpoint
|
|
||||||
)
|
|
||||||
|
|
||||||
completion = client.chat.completions.create(
|
|
||||||
model="hunyuan-lite",
|
|
||||||
messages=[
|
|
||||||
{
|
|
||||||
"role": "user",
|
|
||||||
"content": "泥嚎"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
extra_body={
|
|
||||||
"enable_enhancement": True,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
print(completion.choices[0].message.content)
|
|
||||||
```
|
|
||||||
|
|
||||||
> 小贴士:请勿在公共仓库提交任何真实密钥;使用环境变量或密钥管理服务更安全。
|
|
||||||
|
|
||||||
## 低价大模型
|
|
||||||
|
|
||||||
### SCNet 平台
|
|
||||||
|
|
||||||
#### 简介
|
|
||||||
SCNet 是一个面向人工智能和科学计算的一站式算力与 AI 平台,提供从数据管理、模型训练到部署的完整链路服务,同时结合国产超算硬件和多模态模型生态,让企业和开发者能更高效地使用 AI。
|
|
||||||
|
|
||||||
#### 链接与文档
|
|
||||||
- 官网:[https://www.scnet.cn/](https://www.scnet.cn/)
|
|
||||||
#### 价格一览
|
|
||||||
| 模型 | 上下文长度 | 百万 tokens 输入价格 | 百万 tokens 输出价格 |
|
|
||||||
|---|---|---|---|
|
|
||||||
| Qwen3-235B-A22B | 32K | 0.1 元 | 0.1 元 |
|
|
||||||
| DeepSeek-R1-Distill-Qwen-7B | 32K | 0.1 元 | 0.1 元 |
|
|
||||||
| DeepSeek-R1-Distill-Qwen-32B | 32K | 1 元 | 4 元 |
|
|
||||||
| DeepSeek-R1-Distill-Llama-70B | 32K | 0.1 元 | 6 元 |
|
|
||||||
| QwQ-32B | 32K | 1 元 | 4 元 |
|
|
||||||
| Qwen3-30B-A3B | 128K | 1 元 | 6 元 |
|
|
||||||
| Qwen3-Embedding-8B | - | 0.1 元 | - |
|
|
||||||
|
|
||||||
- 文档(计费与说明):[https://www.scnet.cn/ac/openapi/doc/2.0/moduleapi/tutorial/modulefee.html](https://www.scnet.cn/ac/openapi/doc/2.0/moduleapi/tutorial/modulefee.html)
|
|
||||||
|
|
||||||
目前看到的价格最低的 Qwen3-235B-A22B 模型的 API,相比前面的免费模型,性能更强。
|
|
||||||
- API 接口使用样例:[https://www.scnet.cn/ac/openapi/doc/2.0/moduleapi/tutorial/apicall.html](https://www.scnet.cn/ac/openapi/doc/2.0/moduleapi/tutorial/apicall.html)
|
|
||||||
|
|
||||||
## 小结
|
|
||||||
|
|
||||||
把上面这些“零钱包”级别的模型都翻完啦~
|
|
||||||
- 百度千帆、讯飞星火、腾讯混元都给出「免费在线额度」,日常轻量问答、摘要、润色完全够用;记得把 Key 藏进环境变量,别手滑推到 GitHub。
|
|
||||||
- 如果任务量突然暴涨,SCNet 的 Qwen3-235B-A22B 只要 0.1 元/百万 tokens,性价比直接拉满,当“备胎”也安心。
|
|
||||||
|
|
||||||
一句话:先薅免费的,薅不动再掏 0.1 元,让荷包和模型一起“稳稳幸福”吧!
|
|
||||||
@@ -1,188 +0,0 @@
|
|||||||
---
|
|
||||||
title: MCP 与 Skills:让 AI 助手更懂你的利器
|
|
||||||
createTime: 2026/03/29 14:00:00
|
|
||||||
permalink: /article/mcp-and-skills/
|
|
||||||
sidebar: '/ai/'
|
|
||||||
---
|
|
||||||
|
|
||||||
嗨~今天来聊聊两个让 AI 助手变得更聪明、更贴心的小魔法:**MCP** 和 **Skills** 🪄
|
|
||||||
|
|
||||||
它们就像是给 AI 装上了「外挂」和「说明书」,让它不仅能聊天,还能真正帮你干活、调用工具、甚至访问你的本地知识库~
|
|
||||||
|
|
||||||
|
|
||||||
## 一、MCP 是什么?
|
|
||||||
|
|
||||||
**MCP** 全称是 **Model Context Protocol**(模型上下文协议),是由 [Anthropic](https://www.anthropic.com/) 提出的一种开放协议。
|
|
||||||
|
|
||||||
简单来说,它就像是 AI 和外部世界之间的「通用翻译官」🌐
|
|
||||||
|
|
||||||
### 为什么需要 MCP?
|
|
||||||
|
|
||||||
想象一下:
|
|
||||||
- 你想让 AI 查一下你的本地数据库
|
|
||||||
- 你想让 AI 调用某个特定工具
|
|
||||||
- 你想让 AI 访问你的笔记知识库
|
|
||||||
|
|
||||||
以前,每个工具都要写一套单独的对接代码,很麻烦对吧?
|
|
||||||
|
|
||||||
MCP 的出现,就是为了让这些「对接」变得标准化——**一次配置,到处可用**。
|
|
||||||
|
|
||||||
### MCP 的工作原理
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
|
||||||
│ AI 助手 │ ◄──► │ MCP 协议 │ ◄──► │ 外部工具 │
|
|
||||||
│ (Cursor等) │ │ (标准化) │ │ (数据库/API) │
|
|
||||||
└─────────────┘ └─────────────┘ └─────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
AI 助手通过 MCP 协议,可以:
|
|
||||||
- 🔍 **检索**(Retrieval):查询知识库、数据库
|
|
||||||
- 🛠️ **调用工具**(Tools):执行特定功能
|
|
||||||
- 💾 **访问资源**(Resources):读取文件、配置等
|
|
||||||
|
|
||||||
|
|
||||||
## 二、Skills 是什么?
|
|
||||||
|
|
||||||
如果说 MCP 是「通信协议」,那 **Skills** 就是「技能说明书」📖
|
|
||||||
|
|
||||||
### Skills 的概念
|
|
||||||
|
|
||||||
Skills(技能)是封装好的、可复用的功能模块。每个 Skill 通常包含:
|
|
||||||
- **功能描述**:这个技能是干嘛的
|
|
||||||
- **调用方式**:需要哪些参数、返回什么结果
|
|
||||||
- **使用示例**:实际怎么调用
|
|
||||||
|
|
||||||
### MCP vs Skills 的关系
|
|
||||||
|
|
||||||
| 概念 | 比喻 | 作用 |
|
|
||||||
|------|------|------|
|
|
||||||
| **MCP** | 电话线/网络协议 📡 | 负责「能连上」 |
|
|
||||||
| **Skills** | 电话簿/功能菜单 📋 | 负责「知道能做什么」 |
|
|
||||||
|
|
||||||
MCP 让 AI 和工具**连得通**,Skills 让 AI**知道怎么用**。
|
|
||||||
|
|
||||||
|
|
||||||
## 三、实际应用场景
|
|
||||||
|
|
||||||
### 场景 1:本地知识库检索 🗃️
|
|
||||||
|
|
||||||
就像我在 [RAGFlow 部署文章](../windows11-ragflow-deployment-mcp/) 里写的,通过 MCP 把 RAGFlow 接到 Cursor 里:
|
|
||||||
|
|
||||||
```json
|
|
||||||
// Cursor 的 MCP 配置
|
|
||||||
{
|
|
||||||
"mcpServers": {
|
|
||||||
"RAGFlow": {
|
|
||||||
"url": "http://127.0.0.1:39382/mcp/"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
然后 AI 就能:
|
|
||||||
- 自动检索你的笔记
|
|
||||||
- 基于本地知识回答问题
|
|
||||||
- 不用把敏感文件上传到云端
|
|
||||||
|
|
||||||
### 场景 2:数据库查询 🗄️
|
|
||||||
|
|
||||||
配置一个数据库 MCP Server,AI 就能直接帮你:
|
|
||||||
|
|
||||||
```
|
|
||||||
用户:查一下上个月销售额最高的产品
|
|
||||||
AI:【通过 MCP 调用数据库查询工具】
|
|
||||||
SELECT product_name, SUM(sales)
|
|
||||||
FROM sales
|
|
||||||
WHERE date >= '2025-02-01'
|
|
||||||
GROUP BY product_name
|
|
||||||
ORDER BY SUM(sales) DESC
|
|
||||||
LIMIT 1;
|
|
||||||
|
|
||||||
结果是:产品 A,销售额 ¥123,456
|
|
||||||
```
|
|
||||||
|
|
||||||
### 场景 3:文件操作 📁
|
|
||||||
|
|
||||||
通过文件系统 MCP,AI 可以:
|
|
||||||
- 读取项目配置文件
|
|
||||||
- 批量重命名文件
|
|
||||||
- 生成代码并保存到指定目录
|
|
||||||
|
|
||||||
|
|
||||||
## 四、在 Cursor 中使用
|
|
||||||
|
|
||||||
### 配置 MCP Server
|
|
||||||
|
|
||||||
以 Cursor 为例,在 `~/.cursor/mcp.json` 中添加:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"mcpServers": {
|
|
||||||
"my-database": {
|
|
||||||
"url": "http://localhost:3000/mcp"
|
|
||||||
},
|
|
||||||
"file-system": {
|
|
||||||
"command": "npx",
|
|
||||||
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/allowed/dir"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 使用流程
|
|
||||||
|
|
||||||
1. **AI 发现技能**:启动时,AI 会自动获取所有可用的 Skills 列表
|
|
||||||
2. **意图识别**:当你提问时,AI 判断是否需要调用工具
|
|
||||||
3. **参数填充**:AI 自动提取所需参数
|
|
||||||
4. **执行并返回**:调用 MCP Server,获取结果后呈现给你
|
|
||||||
|
|
||||||
### 交互示例
|
|
||||||
|
|
||||||
```
|
|
||||||
你:帮我总结一下项目里的 API 接口
|
|
||||||
|
|
||||||
AI:我来帮你分析一下项目中的 API 接口。
|
|
||||||
【调用 file-system skill 读取项目文件】
|
|
||||||
【调用检索 skill 查找路由定义】
|
|
||||||
|
|
||||||
找到以下接口:
|
|
||||||
1. GET /api/users - 获取用户列表
|
|
||||||
2. POST /api/users - 创建用户
|
|
||||||
3. GET /api/users/:id - 获取单个用户
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## 五、MCP 生态一览
|
|
||||||
|
|
||||||
目前 MCP 生态正在快速发展,已有许多现成的 Server 可用:
|
|
||||||
|
|
||||||
| 类型 | 代表项目 | 用途 |
|
|
||||||
|------|---------|------|
|
|
||||||
| 文件系统 | `@modelcontextprotocol/server-filesystem` | 读写本地文件 |
|
|
||||||
| 数据库 | `@modelcontextprotocol/server-postgres` | PostgreSQL 查询 |
|
|
||||||
| GitHub | `@modelcontextprotocol/server-github` | 操作 GitHub |
|
|
||||||
| 浏览器 | `@browserbasehq/mcp-server-browserbase` | 自动化浏览器操作 |
|
|
||||||
| 知识库 | RAGFlow MCP | 本地文档检索 |
|
|
||||||
|
|
||||||
完整的官方列表可以在 [MCP Servers Repository](https://github.com/modelcontextprotocol/servers) 找到。
|
|
||||||
|
|
||||||
|
|
||||||
## 六、总结
|
|
||||||
|
|
||||||
| 要点 | 说明 |
|
|
||||||
|------|------|
|
|
||||||
| **MCP** | 让 AI 和工具「说同一种语言」的开放协议 |
|
|
||||||
| **Skills** | 封装好的功能模块,告诉 AI「我能做什么」 |
|
|
||||||
| **价值** | 打破信息孤岛,让 AI 真正连接你的数字世界 |
|
|
||||||
| **前景** | 越来越多的工具会支持 MCP,生态会越来越丰富 |
|
|
||||||
|
|
||||||
用一句话概括:**MCP 是桥梁,Skills 是地图,让 AI 从「聊天伙伴」变成「得力助手」** 🎯
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
> 💡 **延伸阅读**
|
|
||||||
> - [Windows 11 本地部署 RAGFlow 与 Cursor MCP](./windows11-ragflow-deployment-mcp.md)
|
|
||||||
> - [MCP 官方文档](https://modelcontextprotocol.io/)
|
|
||||||
> - [Anthropic MCP 介绍](https://www.anthropic.com/news/model-context-protocol)
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
---
|
|
||||||
title: Console 控制台操作
|
|
||||||
createTime: 2025/10/03 02:55:49
|
|
||||||
permalink: /ops/blockchain/practice/console-operator/
|
|
||||||
---
|
|
||||||
|
|
||||||
## Console 控制台是什么?
|
|
||||||
Console 是 FISCO BCOS 官方提供的一个交互式命令行工具,用于直接与区块链节点交互。
|
|
||||||
主要功能包括:
|
|
||||||
- 查询区块链状态(如区块链高度、节点列表、群组信息)
|
|
||||||
- 部署和调用智能合约(支持 Solidity)
|
|
||||||
- 管理节点(如查看节点共识状态、动态添加节点)
|
|
||||||
- 调试交易和查看回执
|
|
||||||
|
|
||||||
## 部署 Console 控制台
|
|
||||||
获取下载脚本:`wget https://gitee.com/FISCO-BCOS/console/releases/download/v2.8.0/console.tar.gz`
|
|
||||||
解压控制台安装包:`tar -zxf console.tar.gz console/`
|
|
||||||
|
|
||||||
复制配置文件:`cp conf/config-example.toml conf/config.toml`
|
|
||||||
编辑配置文件:`vim conf/config.toml`
|
|
||||||
```bash
|
|
||||||
# enSslKey = "conf/gm/gmensdk.key" # GM ssl cert file path
|
|
||||||
# default load the GM SSL encryption privateKey from ${certPath}/gm/gmensdk.key
|
|
||||||
|
|
||||||
[network]
|
|
||||||
# 请确保这里的连接地址都有节点
|
|
||||||
peers=["127.0.0.1:20200", "127.0.0.1:20201"] # The peer list to connect [!code warning]
|
|
||||||
|
|
||||||
# Configure a private topic as a topic message sender.
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
复制节点证书到 Console 的配置目录下
|
|
||||||
```bash
|
|
||||||
cp -r ../nodes/127.0.0.1/sdk/* conf/
|
|
||||||
```
|
|
||||||
|
|
||||||
通过 `bash start.sh` 就可以启动 Console 控制台啦!
|
|
||||||

|
|
||||||
|
|
||||||
## 常用控制台命令
|
|
||||||
|
|
||||||
这里的区块链的搭建是根据前一篇文章中的多机构多群组多节点的关系
|
|
||||||
|
|
||||||
### 合约相关命令
|
|
||||||
### 其他命令
|
|
||||||
- 查询区块高度:`getBlockNumber`
|
|
||||||
```bash title='output_example'
|
|
||||||
[group:1]> getBlockNumber
|
|
||||||
0
|
|
||||||
```
|
|
||||||
- 查看共识节点列表:`getSealerList`
|
|
||||||
```bash title='output_example'
|
|
||||||
[group:1]> getSealerList
|
|
||||||
[
|
|
||||||
d036a22f16909cba4c8521cd3c31db3be381e673c9749e3fd2848ab25eca134ef917cb9306f41893295c535078bce77bdc241eba3f92549a73efe4699d3c0df8,
|
|
||||||
f0535616759000b473a285d60814bc2c5927a9cb897644da46890de71e59df590cd6fe1b226a95e19a566b6fc127fd85032de0d9fdd3d529e5e50cd96ad51f28
|
|
||||||
]
|
|
||||||
```
|
|
||||||
@@ -76,19 +76,6 @@ info|2025-10-02 17:08:33.807504|[g:1][CONSENSUS][SEALER]++++++++++++++++ Generat
|
|||||||
```
|
```
|
||||||
不断输出带有 `++++Generating seal` 的日志,说明共识正常
|
不断输出带有 `++++Generating seal` 的日志,说明共识正常
|
||||||
|
|
||||||
检查群组之间的共识
|
|
||||||
```bash
|
|
||||||
tail -f nodes/127.0.0.1/node0/log/log* | grep 'g:1.*+++'
|
|
||||||
```
|
|
||||||
输出样例如下:
|
|
||||||
```bash
|
|
||||||
info|2025-10-02 17:59:59.194525|[g:1][CONSENSUS][SEALER]++++++++++++++++ Generating seal on,blkNum=1,tx=0,nodeIdx=1,hash=fdb2da54...
|
|
||||||
info|2025-10-02 18:59:59.225102|[g:1][CONSENSUS][SEALER]++++++++++++++++ Generating seal on,blkNum=1,tx=0,nodeIdx=1,hash=b7d87997...
|
|
||||||
info|2025-10-02 19:59:59.327677|[g:1][CONSENSUS][SEALER]++++++++++++++++ Generating seal on,blkNum=1,tx=0,nodeIdx=1,hash=38dea43f...
|
|
||||||
info|2025-10-02 20:59:59.462059|[g:1][CONSENSUS][SEALER]++++++++++++++++ Generating seal on,blkNum=1,tx=0,nodeIdx=1,hash=cb0e89fa...
|
|
||||||
info|2025-10-02 21:59:59.284218|[g:1][CONSENSUS][SEALER]++++++++++++++++ Generating seal on,blkNum=1,tx=0,nodeIdx=1,hash=721e8904...
|
|
||||||
```
|
|
||||||
|
|
||||||
### 部署多机构多群组多节点
|
### 部署多机构多群组多节点
|
||||||
|
|
||||||
在很多时候,我们需要部署多机构多群组多节点的联盟链,来满足不同业务场景下的需求。
|
在很多时候,我们需要部署多机构多群组多节点的联盟链,来满足不同业务场景下的需求。
|
||||||
|
|||||||
@@ -1,342 +0,0 @@
|
|||||||
---
|
|
||||||
title: 区块链的节点
|
|
||||||
createTime: 2025/10/05 13:07:05
|
|
||||||
permalink: /ops/blockchain/theory/fisco-bcos-node-type/
|
|
||||||
---
|
|
||||||
|
|
||||||
## 节点的基础概念
|
|
||||||
|
|
||||||
FISCO BCOS 引入了[游离节点、观察者节点和共识节点](https://fisco-bcos-documentation.readthedocs.io/zh-cn/latest/docs/design/security_control/node_management.html#id6),这三种节点类型可以通过控制台互相转换。
|
|
||||||
|
|
||||||
组员:
|
|
||||||
- 共识节点:参与共识的节点,拥有群组的所有数据(搭链时默认都生成共识节点)。
|
|
||||||
- 观察者节点:不参与共识,但能实时同步链上数据的节点。
|
|
||||||
|
|
||||||
非组员:
|
|
||||||
- 游离节点:已启动,等待加入群组的节点。处在一种暂时的节点状态,不能获取链上的数据。
|
|
||||||
|
|
||||||
## 节点配置文件详解
|
|
||||||
|
|
||||||
节点搭建完成之后会有`config.ini`、`conf/group.%group_id.genesis`、`conf/group.%group_id.ini`、`node.nodeid` 等配置文件
|
|
||||||
|
|
||||||
::: code-tabs
|
|
||||||
@tab config.ini
|
|
||||||
```bash
|
|
||||||
# RPC (Remote Proceduce Call 远程过程调用)
|
|
||||||
# 用于配置节点对外提供的 RPC 服务,包括 JSON-RPC ( 用于 Web3.js,控制台等 )和 Channel 通信( 用于 SDK,Java 应用等 )
|
|
||||||
[rpc]
|
|
||||||
# 允许外部访问
|
|
||||||
channel_listen_ip=0.0.0.0
|
|
||||||
channel_listen_port=20200
|
|
||||||
# 只允许内部访问
|
|
||||||
jsonrpc_listen_ip=127.0.0.1
|
|
||||||
jsonrpc_listen_port=8545
|
|
||||||
|
|
||||||
# P2P (Peer-to-Peer 点对点通信)
|
|
||||||
# 用于配置节点之间的 P2P 通信
|
|
||||||
[p2p]
|
|
||||||
listen_ip=0.0.0.0
|
|
||||||
listen_port=30300
|
|
||||||
; nodes to connect
|
|
||||||
node.0=127.0.0.1:30300
|
|
||||||
node.1=127.0.0.1:30301
|
|
||||||
node.2=127.0.0.1:30400
|
|
||||||
node.3=127.0.0.1:30401
|
|
||||||
node.4=127.0.0.1:30500
|
|
||||||
node.5=127.0.0.1:30501
|
|
||||||
node.6=127.0.0.1:30600
|
|
||||||
node.7=127.0.0.1:30601
|
|
||||||
|
|
||||||
# 证书的黑白名单控制
|
|
||||||
[certificate_blacklist]
|
|
||||||
; crl.0 should be nodeid, nodeid's length is 128
|
|
||||||
;crl.0=
|
|
||||||
|
|
||||||
[certificate_whitelist]
|
|
||||||
; cal.0 should be nodeid, nodeid's length is 128
|
|
||||||
;cal.0=
|
|
||||||
|
|
||||||
# 群组的配置目录和数据目录
|
|
||||||
# 启动的时候会自动加载 group_config_path 下的所有群组
|
|
||||||
[group]
|
|
||||||
group_data_path=data/
|
|
||||||
group_config_path=conf/
|
|
||||||
|
|
||||||
# 配置网络安全
|
|
||||||
[network_security]
|
|
||||||
; directory the certificates located in
|
|
||||||
# 网络部分的配置文件目录
|
|
||||||
data_path=conf/
|
|
||||||
; the node private key file
|
|
||||||
# 节点的私钥文件
|
|
||||||
key=node.key
|
|
||||||
; the node certificate file
|
|
||||||
# 节点的证书文件
|
|
||||||
cert=node.crt
|
|
||||||
; the ca certificate file
|
|
||||||
# CA 的证书文件
|
|
||||||
ca_cert=ca.crt
|
|
||||||
|
|
||||||
# 配置加密存储
|
|
||||||
# 用于对接 KMS ,实现落盘数据加密
|
|
||||||
# 具体的可以查看参考文献中的第2篇文章,落盘加密
|
|
||||||
[storage_security]
|
|
||||||
enable=false
|
|
||||||
key_manager_ip=
|
|
||||||
key_manager_port=
|
|
||||||
cipher_data_key=
|
|
||||||
|
|
||||||
# 链的基本信息
|
|
||||||
[chain]
|
|
||||||
# 链的 ID ,不同链ID的节点无法通信
|
|
||||||
id=1
|
|
||||||
; use SM crypto or not, should nerver be changed
|
|
||||||
# 是否启动国密算法,这一步在创建的时候就应该规划好
|
|
||||||
# 一旦设定,不可更改,否则无法和其他节点通信
|
|
||||||
sm_crypto=false
|
|
||||||
# cannel 是否启用国密,需和 SDK 一致
|
|
||||||
sm_crypto_channel=false
|
|
||||||
|
|
||||||
# 兼容性配置
|
|
||||||
[compatibility]
|
|
||||||
; supported_version should nerver be changed
|
|
||||||
# 指定节点兼容的 FISCO BCOS 的版本
|
|
||||||
# 默认值即可,不应手动修改
|
|
||||||
supported_version=2.8.0
|
|
||||||
|
|
||||||
# 日志配置
|
|
||||||
[log]
|
|
||||||
# 启用配置
|
|
||||||
enable=true
|
|
||||||
# 日志配置目录
|
|
||||||
log_path=./log
|
|
||||||
; enable/disable the statistics function
|
|
||||||
# 是否启用统计功能
|
|
||||||
enable_statistic=false
|
|
||||||
; network statistics interval, unit is second, default is 60s
|
|
||||||
# 统计信息刷新间隔(单位:秒)
|
|
||||||
stat_flush_interval=60
|
|
||||||
; info debug trace
|
|
||||||
# 日志级别,可选值(trace < debug < info < warning < error)
|
|
||||||
level=info
|
|
||||||
; MB
|
|
||||||
# 单个日志文件最大大小(单位:MB)
|
|
||||||
max_log_file_size=200
|
|
||||||
# 是否实时刷盘
|
|
||||||
flush=true
|
|
||||||
|
|
||||||
# 流量控制
|
|
||||||
[flow_control]
|
|
||||||
; restrict QPS of the node
|
|
||||||
# 限制 QPS 每秒请求数,防止 DDOS
|
|
||||||
;limit_req=1000
|
|
||||||
; restrict the outgoing bandwidth of the node
|
|
||||||
; Mb, can be a decimal
|
|
||||||
; when the outgoing bandwidth exceeds the limit, the block synchronization operation will not proceed
|
|
||||||
# 限制节点出带宽(单位Mbps)
|
|
||||||
# 超出之后会暂停区块同步,避免网络拥塞
|
|
||||||
;outgoing_bandwidth_limit=2
|
|
||||||
```
|
|
||||||
@tab group.1.genesis
|
|
||||||
```bash
|
|
||||||
# 共识机制配置
|
|
||||||
[consensus]
|
|
||||||
; consensus algorithm now support PBFT(consensus_type=pbft), Raft(consensus_type=raft)
|
|
||||||
; rpbft(consensus_type=rpbft)
|
|
||||||
# 共识算法
|
|
||||||
consensus_type=pbft
|
|
||||||
; the max number of transactions of a block
|
|
||||||
# 每个区块的最大打包量,超出的部分会留在交易池,等待下一次打包
|
|
||||||
max_trans_num=1000
|
|
||||||
; in seconds, block consensus timeout, at least 3s
|
|
||||||
# 共识算法的超时时间
|
|
||||||
consensus_timeout=3
|
|
||||||
; rpbft related configuration
|
|
||||||
; the working sealers num of each consensus epoch
|
|
||||||
# 仅在 rpbft 的时候生效,每轮共识 epoch 中参与打包的共识节点数。
|
|
||||||
epoch_sealer_num=2
|
|
||||||
; the number of generated blocks each epoch
|
|
||||||
# 仅在 rpbft 的时候生效,每轮 epoch 持续的区块数。
|
|
||||||
epoch_block_num=1000
|
|
||||||
; the node id of consensusers
|
|
||||||
# 群组中共识节点的列表
|
|
||||||
node.0=f0535616759000b473a285d60814bc2c5927a9cb897644da46890de71e59df590cd6fe1b226a95e19a566b6fc127fd85032de0d9fdd3d529e5e50cd96ad51f28
|
|
||||||
node.1=d036a22f16909cba4c8521cd3c31db3be381e673c9749e3fd2848ab25eca134ef917cb9306f41893295c535078bce77bdc241eba3f92549a73efe4699d3c0df8
|
|
||||||
|
|
||||||
# 存储区块链的状态信息
|
|
||||||
# 支持 storage state 和 MPT state
|
|
||||||
# storage state 效率高
|
|
||||||
# MPT state 效率低,但是包含完整的历史信息
|
|
||||||
[state]
|
|
||||||
type=storage
|
|
||||||
# transaction (交易)相关配置
|
|
||||||
# gas 是一种计算智能合约执行过程中消耗的计算和存储资源,包括交易的gas的最大值的限制和区块的gas最大值的限制
|
|
||||||
# 如果交易或区块执行消耗的 gas 超过限制,则会丢弃交易区块
|
|
||||||
# FISCO BCOS 是联盟链,简化了 gas 的设计,仅保留交易最大 gas 限制,区块最大 gas 是通过共识配置的
|
|
||||||
[tx]
|
|
||||||
; transaction gas limit
|
|
||||||
gas_limit=300000000
|
|
||||||
# 群组的配置
|
|
||||||
[group]
|
|
||||||
# 群组 ID
|
|
||||||
id=1
|
|
||||||
# 群组的创世区块的时间戳(ms)
|
|
||||||
timestamp=1759397321000
|
|
||||||
|
|
||||||
# 群组的 EVM 配置
|
|
||||||
# 是否开启 free storage
|
|
||||||
# 这里具体的内容,可以查看参考文献中的第4篇文章,【教程】如何修改FISCO-BCOS内的EVM的gas...
|
|
||||||
[evm]
|
|
||||||
enable_free_storage=false
|
|
||||||
```
|
|
||||||
@tab group.1.ini
|
|
||||||
```bash
|
|
||||||
# 共识运行参数(PBFT 优化)
|
|
||||||
[consensus]
|
|
||||||
; the ttl for broadcasting pbft message
|
|
||||||
# PBFT 消息在网络中最多转发 2 跳
|
|
||||||
ttl=2
|
|
||||||
; min block generation time(ms)
|
|
||||||
# 最小出块时间 500ms
|
|
||||||
min_block_generation_time=500
|
|
||||||
# 动态调整区块大小 genesis 中的 max_trans_num
|
|
||||||
enable_dynamic_block_size=true
|
|
||||||
# 优化 TLL 转发逻辑,提升 PBFT 在大规模网络中的效率
|
|
||||||
enable_ttl_optimization=true
|
|
||||||
# Prepare 消息中只包含交易哈希,减少网络传输量
|
|
||||||
# 大幅提示大区块场景下的共识效率
|
|
||||||
enable_prepare_with_txsHash=true
|
|
||||||
; The following is the relevant configuration of rpbft
|
|
||||||
; set true to enable broadcast prepare request by tree
|
|
||||||
# 使用树状拓扑广播 Prepare 广播(而非全网广播)
|
|
||||||
# 适合节点多的场景
|
|
||||||
# 仅在 rpbft 或优化版 PBFT 中有效
|
|
||||||
broadcast_prepare_by_tree=true
|
|
||||||
; percent of nodes that broadcast prepare status to, must be between 25 and 100
|
|
||||||
# 广播 Prepare 状态给33%的节点,必须在 25%~100% 之间
|
|
||||||
prepare_status_broadcast_percent=33
|
|
||||||
; max wait time before request missed transactions, ms, must be between 5ms and 1000ms
|
|
||||||
# 请求缺失交易前,最多等待 100ms(收集更多交易)
|
|
||||||
# 范围 5 ~ 1000ms
|
|
||||||
max_request_missedTxs_waitTime=100
|
|
||||||
; maximum wait time before requesting a prepare, ms, must be between 10ms and 1000ms
|
|
||||||
# 请求缺失 Prepare 消息前,最多等待 100ms
|
|
||||||
# 范围 10ms ~ 1000ms
|
|
||||||
max_request_prepare_waitTime=100
|
|
||||||
|
|
||||||
[storage]
|
|
||||||
; storage db type, rocksdb / mysql / scalable, rocksdb is recommended
|
|
||||||
# 存储引擎
|
|
||||||
# 推荐使用 rocksdb (高性能,嵌入式 KV 数据库)
|
|
||||||
# 支持 mysql ( 不推荐,性能差 ) , scalable (分布式存储,实验性)
|
|
||||||
type=rocksdb
|
|
||||||
; set true to turn on binary log
|
|
||||||
# 开启二进制日志,默认关闭,开启会增加IO
|
|
||||||
binary_log=false
|
|
||||||
; scroll_threshold=scroll_threshold_multiple*1000, only for scalable
|
|
||||||
# 仅在 scalable 存储引擎下生效
|
|
||||||
scroll_threshold_multiple=2
|
|
||||||
; set fasle to disable CachedStorage
|
|
||||||
# 启用缓存层(非常推荐)
|
|
||||||
cached_storage=true
|
|
||||||
; max cache memeory, MB
|
|
||||||
# 缓存最大内容,32MB
|
|
||||||
max_capacity=32
|
|
||||||
# 允许节点最多超前同步10个区块
|
|
||||||
max_forward_block=10
|
|
||||||
; only for external, deprecated in v2.3.0
|
|
||||||
# 最大重试次数,默认 60 次
|
|
||||||
max_retry=60
|
|
||||||
topic=DB
|
|
||||||
; only for mysql
|
|
||||||
db_ip=127.0.0.1
|
|
||||||
db_port=3306
|
|
||||||
db_username=
|
|
||||||
db_passwd=
|
|
||||||
db_name=
|
|
||||||
# 交易池
|
|
||||||
[tx_pool]
|
|
||||||
# 交易池最大容量,默认 150000
|
|
||||||
limit=150000
|
|
||||||
; transaction pool memory size limit, MB
|
|
||||||
# 交易池最大内存占用:512 MB
|
|
||||||
memory_limit=512
|
|
||||||
; number of threads responsible for transaction notification,
|
|
||||||
; default is 2, not recommended for more than 8
|
|
||||||
# 用于通知 SDK 交易上链结果的线程数
|
|
||||||
notify_worker_num=2
|
|
||||||
# 区块同步优化
|
|
||||||
[sync]
|
|
||||||
; max memory size used for block sync, must >= 32MB
|
|
||||||
# 区块同步最大内存:512MB,必须大于等于 32 MB
|
|
||||||
max_block_sync_memory_size=512
|
|
||||||
# 同步空闲时,等待 200ms 再检查新块
|
|
||||||
idle_wait_ms=200
|
|
||||||
; send block status by tree-topology, only supported when use pbft
|
|
||||||
# 使用树状拓扑同步区块
|
|
||||||
# 可以大幅度降低网络带宽
|
|
||||||
# 仅在 PBFT 下支持
|
|
||||||
sync_block_by_tree=true
|
|
||||||
; send transaction by tree-topology, only supported when use pbft
|
|
||||||
; recommend to use when deploy many consensus nodes
|
|
||||||
# 交易广播也使用树状拓扑
|
|
||||||
send_txs_by_tree=true
|
|
||||||
; must between 1000 to 3000
|
|
||||||
; only enabled when sync_by_tree is true
|
|
||||||
# Gossip 协议心跳间隔
|
|
||||||
gossip_interval_ms=1000
|
|
||||||
# 每个节点维护 3 个gossip邻居
|
|
||||||
gossip_peers_number=3
|
|
||||||
; max number of nodes that broadcast txs status to, recommended less than 5
|
|
||||||
# 交易状态最多广播给5个节点
|
|
||||||
txs_max_gossip_peers_num=5
|
|
||||||
|
|
||||||
# 群组级的流量控制
|
|
||||||
[flow_control]
|
|
||||||
; restrict QPS of the group
|
|
||||||
# 限制群组的 QPS 为 1000
|
|
||||||
;limit_req=1000
|
|
||||||
; restrict the outgoing bandwidth of the group
|
|
||||||
; Mb, can be a decimal
|
|
||||||
; when the outgoing bandwidth exceeds the limit, the block synchronization operation will not proceed
|
|
||||||
# 限制群组的出链带宽为 2Mbps/s
|
|
||||||
;outgoing_bandwidth_limit=2
|
|
||||||
|
|
||||||
# SDK 连接白名单
|
|
||||||
[sdk_allowlist]
|
|
||||||
; When sdk_allowlist is empty, all SDKs can connect to this node
|
|
||||||
; when sdk_allowlist is not empty, only the SDK in the allowlist can connect to this node
|
|
||||||
; public_key.0 should be nodeid, nodeid's length is 128
|
|
||||||
;public_key.0=
|
|
||||||
```
|
|
||||||
@tab node.nodeid
|
|
||||||
```bash
|
|
||||||
# 节点的 ID
|
|
||||||
f0535616759000b473a285d60814bc2c5927a9cb897644da46890de71e59df590cd6fe1b226a95e19a566b6fc127fd85032de0d9fdd3d529e5e50cd96ad51f28
|
|
||||||
```
|
|
||||||
:::
|
|
||||||
|
|
||||||
## 节点操作命令
|
|
||||||
|
|
||||||
- `addSealer`:将节点转换为共识节点。
|
|
||||||
- `addObserver`:将节点转换为观察节点。
|
|
||||||
- `removeNode`:将节点设置为游离节点。
|
|
||||||
|
|
||||||
- `getSealerList`:查询当前的共识节点的列表
|
|
||||||
- `getObserverList`:查询当前的观察者节点的列表·
|
|
||||||
- `getNodeList`:查询当前的所有节点的列表
|
|
||||||
|
|
||||||
操作节点的时候是通过节点ID进行操作的,节点ID可以在节点目录的`conf/node.nodeid`中获取
|
|
||||||
|
|
||||||
请确保节点加入的区块链所有节点共识正常:正常的节点会输出`+++`日志
|
|
||||||
|
|
||||||
|
|
||||||
## 参考文章:
|
|
||||||
|
|
||||||
1. [组员节点管理](https://fisco-bcos-documentation.readthedocs.io/zh-cn/latest/docs/manual/node_management.html)
|
|
||||||
|
|
||||||
2. [落盘加密](https://fisco-bcos-documentation.readthedocs.io/zh-cn/latest/docs/design/features/storage_security.html)
|
|
||||||
|
|
||||||
3. [【全网首发】FISCO-BCOS从底层讲起链从配置文件开始解析...](https://blog.csdn.net/LforikQ/article/details/130920092)
|
|
||||||
|
|
||||||
4. [【教程】如何修改FISCO-BCOS内的EVM的gas值,让区块链预防DOS攻击...](https://blog.csdn.net/qq_57309855/article/details/126334115)
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
---
|
|
||||||
title: ETLCloud
|
|
||||||
createTime: 2025/12/24 17:13:36
|
|
||||||
permalink: /ops/docker/other/ETLCloud/
|
|
||||||
---
|
|
||||||
|
|
||||||
## 前置环境
|
|
||||||
这里安装之前,需要先安装 mongodb 数据库,可以参考 [使用 Docker 部署 MongoDB](mongodb.md)
|
|
||||||
|
|
||||||
## 安装 ETLCloud
|
|
||||||
|
|
||||||
拉取社区版本镜像:
|
|
||||||
```bash
|
|
||||||
docker pull ccr.ccs.tencentyun.com/restcloud/restcloud-etl:V4.0
|
|
||||||
```
|
|
||||||
|
|
||||||
### 部署容器
|
|
||||||
|
|
||||||
如果您已经运行过同名容器,请先删除:
|
|
||||||
```bash
|
|
||||||
docker rm -f restcloud-etl-V4.0
|
|
||||||
```
|
|
||||||
|
|
||||||
使用以下命令进行部署(包含授权绑定、数据持久化和端口映射):
|
|
||||||
```bash
|
|
||||||
docker run -d \
|
|
||||||
--restart=always \
|
|
||||||
--privileged=true \
|
|
||||||
--name restcloud-etl-V4.0 \
|
|
||||||
--mac-address=01:D2:F4:18:B8:67 \
|
|
||||||
-v /data/tomcat:/usr/tomcat \
|
|
||||||
-p 9527:8080 \
|
|
||||||
ccr.ccs.tencentyun.com/restcloud/restcloud-etl:V4.0
|
|
||||||
```
|
|
||||||
|
|
||||||
**参数说明:**
|
|
||||||
- `--restart=always`: 容器退出时总是重启(如系统重启)。
|
|
||||||
- `--privileged=true`: 赋予容器特权模式,确保初始化正常。
|
|
||||||
- `--mac-address`: 固定 MAC 地址,用于绑定 License 授权(**注意:第一个字节必须为偶数,建议使用 02 开头**)。
|
|
||||||
- `-v /data/tomcat:/usr/tomcat`: 将 Tomcat 目录挂载到宿主机,实现数据持久化。
|
|
||||||
- `-p 9527:8080`: 访问端口映射,宿主机访问端口为 9527。
|
|
||||||
|
|
||||||
拷贝容器中的 tomcat 目录到主机上
|
|
||||||
```bash
|
|
||||||
docker cp e0419ad8a2bf31743ec17368cf4098e293c36761842a7dcff8280fbc34195fba:/usr/tomcat /data/
|
|
||||||
```
|
|
||||||
删除之前创建的简单容器
|
|
||||||
```bash
|
|
||||||
docker rm -f e0419ad8a2bf31743ec17368cf4098e293c36761842a7dcff8280fbc34195fba
|
|
||||||
```
|
|
||||||
在 /data/tomcat 路径下,编辑ETLCloud mongo相关的配置文件,这里的信息参考之前创建的 MongoDB 实例。
|
|
||||||
这里我额外修改了 server.port ,这里太容易和其他的服务端口冲突,所以这里修改为 9527。
|
|
||||||
```bash
|
|
||||||
sudo vim ./webapps/ROOT/WEB-INF/classes/application.properties
|
|
||||||
|
|
||||||
server.port=9527
|
|
||||||
...
|
|
||||||
#集群服务器之间同步用的公共配置数据库,空表示和配置库一起
|
|
||||||
spring.data.mongodb.host=127.0.0.1:27018
|
|
||||||
spring.data.mongodb.repositories.enabled=true
|
|
||||||
#MongoDb的数据库认证用户名及密码,没有可以为空
|
|
||||||
spring.data.mongodb.username=admin
|
|
||||||
spring.data.mongodb.password=admin
|
|
||||||
...
|
|
||||||
```
|
|
||||||
然后可以启动容器,样例如下:
|
|
||||||
```bash
|
|
||||||
docker run -d --restart=always --restart=on-failure:5 --privileged=true --name restcloud-etl-V4.0 --mac-address=自定义mac地址 -v /home/tomcat:/usr/tomcat -p 8080:8080 镜像id
|
|
||||||
```
|
|
||||||
具体命令,这里的MAC地址参数一定是要有的,不然每次重新创建之后MAC地址会变就需要重新生成SN码
|
|
||||||
|
|
||||||
这里的MAC地址不要和博主写一样的...自己随机一个或者随便写一个就行
|
|
||||||
```bash
|
|
||||||
docker run -d --restart=always --restart=on-failure:5 --privileged=true --name restcloud-etl-V4.0 --mac-address=02:D2:00:00:00:67 -v /data/tomcat:/usr/tomcat -p 9527:8080 4a5c0ed2a5b2
|
|
||||||
```
|
|
||||||
然后访问 服务器IP:9527/restcloud/admin/login 就可以啦
|
|
||||||
|
|
||||||
后面的话就是去官网申请一个SN码,然后就可以开始使用啦。
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
---
|
|
||||||
title: 使用 Docker 部署 Postgres
|
|
||||||
createTime: 2025/12/25 10:52:34
|
|
||||||
permalink: /ops/docker/db/postgres/
|
|
||||||
---
|
|
||||||
|
|
||||||
## 在 Ubuntu 上部署 Postgres 实例
|
|
||||||
|
|
||||||
拉取 Postgres 镜像:
|
|
||||||
```bash
|
|
||||||
docker pull postgres
|
|
||||||
```
|
|
||||||
|
|
||||||
> [!IMPORTANT] 注意事项
|
|
||||||
> 从 **PostgreSQL 18+** 版本开始,官方镜像建议将数据目录挂载到 `/var/lib/postgresql` 而非 `/var/lib/postgresql/data`。如果你是从旧版本升级镜像,直接挂载旧目录会导致启动失败。
|
|
||||||
|
|
||||||
### 极简运行 Postgres 实例
|
|
||||||
```bash
|
|
||||||
docker run --restart=always -itd --name postgres -p 5432:5432 -e POSTGRES_PASSWORD=postgres postgres
|
|
||||||
```
|
|
||||||
解析:
|
|
||||||
- `--restart=always`:容器重启时自动启动。
|
|
||||||
- `-itd`:交互式模式运行容器,后台模式。
|
|
||||||
- `--name postgres`:为容器命名为 postgres。
|
|
||||||
- `-p 5432:5432`:将主机的 5432 端口映射到容器的 5432 端口。
|
|
||||||
- `-e POSTGRES_PASSWORD=postgres`:设置 Postgres 数据库的密码为 postgres。
|
|
||||||
- `postgres`:使用 Postgres 镜像。
|
|
||||||
|
|
||||||
### 配置 Postgres 持久化实例
|
|
||||||
|
|
||||||
#### 方案一:使用最新版本 (18+)
|
|
||||||
创建一个目录用于存储 Postgres 数据:
|
|
||||||
```bash
|
|
||||||
mkdir -p /data/postgres/
|
|
||||||
```
|
|
||||||
运行 Postgres 容器:
|
|
||||||
```bash
|
|
||||||
docker run --restart=always -itd \
|
|
||||||
--name postgres \
|
|
||||||
-p 5432:5432 \
|
|
||||||
-e POSTGRES_PASSWORD=postgres \
|
|
||||||
-v /data/postgres/:/var/lib/postgresql \
|
|
||||||
postgres
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 方案二:固定使用旧版本 (如 17)
|
|
||||||
如果你有现存的 `/var/lib/postgresql/data` 格式数据,建议固定版本:
|
|
||||||
```bash
|
|
||||||
docker run --restart=always -itd \
|
|
||||||
--name postgres \
|
|
||||||
-p 5432:5432 \
|
|
||||||
-e POSTGRES_PASSWORD=postgres \
|
|
||||||
-v /data/postgres/:/var/lib/postgresql/data \
|
|
||||||
postgres:17
|
|
||||||
```
|
|
||||||
解析:
|
|
||||||
- `-v /data/postgres/:/var/lib/postgresql/data`:将主机的 /data/postgres/ 目录挂载到容器的 /var/lib/postgresql/data 目录,用于持久化存储数据。
|
|
||||||
|
|
||||||
#### 进入容器之后修改 postgres 用户密码
|
|
||||||
进入 Postgres 容器:
|
|
||||||
```bash
|
|
||||||
docker exec -it postgres psql -U postgres
|
|
||||||
```
|
|
||||||
解析:
|
|
||||||
- `docker exec -it postgres psql -U postgres`:进入 Postgres 容器的 psql 命令行界面,使用 postgres 用户进行登录。
|
|
||||||
修改 postgres 用户密码为 postgres:
|
|
||||||
```sql
|
|
||||||
ALTER USER postgres PASSWORD 'postgres';
|
|
||||||
```
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
---
|
|
||||||
title: Docker 部署运维
|
|
||||||
createTime: 2025/12/24 16:39:34
|
|
||||||
permalink: /ops/docker/
|
|
||||||
---
|
|
||||||
|
|
||||||
欢迎来到 Docker 运维笔记!✨
|
|
||||||
|
|
||||||
在这里,我们将一起探索那些神奇的“小盒子”——Docker 容器。它们就像是装满惊喜的礼盒,把复杂的程序都整整齐齐地收纳在一起,是不是很有趣呢?♪
|
|
||||||
|
|
||||||
不管是部署数据库,还是搭建各种好玩的应用,只要有了这些小盒子,一切都会变得像魔法一样简单又优雅呢~真期待看到你在运维的道路上,也能像花朵一样绽放光彩呢。🌸
|
|
||||||
|
|
||||||
好啦,让我们开始这段美妙的旅程吧~如果遇到困难,这里会一直提供帮助的哦~
|
|
||||||
|
|
||||||
## 常用基础命令 🛠️
|
|
||||||
|
|
||||||
为了更顺畅地开启这段旅程,这里准备了一些常用的魔法指令:
|
|
||||||
|
|
||||||
### 容器管理
|
|
||||||
- `docker ps`:查看正在运行的小盒子。
|
|
||||||
- `docker ps -a`:查看所有的小盒子(包括休息中的)。
|
|
||||||
- `docker stop <ID>`:让小盒子暂时休息一下。
|
|
||||||
- `docker start <ID>`:唤醒休息中的小盒子。
|
|
||||||
- `docker rm <ID>`:送走不再需要的小盒子。
|
|
||||||
|
|
||||||
### 镜像管理
|
|
||||||
- `docker images`:查看我们收集的所有魔法镜像。
|
|
||||||
- `docker pull <name>`:从云端下载新的魔法镜像。
|
|
||||||
- `docker rmi <ID>`:清理不再需要的魔法镜像。
|
|
||||||
|
|
||||||
### 日志与调试
|
|
||||||
- `docker logs -f <ID>`:实时查看小盒子里的悄悄话。
|
|
||||||
- `docker exec -it <ID> /bin/bash`:亲自钻进小盒子里面看看。
|
|
||||||
|
|
||||||
## 部署清单 📋
|
|
||||||
|
|
||||||
- [MongoDB 部署魔法](./mongodb.md)
|
|
||||||
- 更多惊喜,敬请期待~✨
|
|
||||||
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
---
|
|
||||||
title: 使用 Docker 部署 MongoDB
|
|
||||||
createTime: 2025/12/24 16:34:00
|
|
||||||
permalink: /ops/docker/db/mongodb/
|
|
||||||
---
|
|
||||||
|
|
||||||
本教程将介绍如何使用 Docker 快速部署一个 MongoDB 实例。
|
|
||||||
|
|
||||||
## 在 Ubuntu 上部署 MongoDB:4.2 实例
|
|
||||||
|
|
||||||
我们先拉取 MongoDB:4.2 镜像:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker pull mongo:4.2
|
|
||||||
```
|
|
||||||
|
|
||||||
创建一个目录用于存储 MongoDB 数据:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mkdir -p /data/mongo/
|
|
||||||
```
|
|
||||||
|
|
||||||
启动 MongoDB 容器:
|
|
||||||
```bash :wrap
|
|
||||||
sudo docker run --restart=always -itd --name mongo -p 27018:27017 -v /data/mongo/:/data/db mongo:4.2 --auth
|
|
||||||
```
|
|
||||||
命令详解:
|
|
||||||
- `--restart=always`:容器重启时自动启动。
|
|
||||||
- `-itd`:交互式模式运行容器,后台模式。
|
|
||||||
- `--name mongo`:为容器命名为 mongo。
|
|
||||||
- `-p 27018:27017`:将主机的 27018 端口映射到容器的 27017 端口。
|
|
||||||
- `-v /data/mongo/:/data/db`:将主机的 /data/mongo/ 目录挂载到容器的 /data/db 目录,用于持久化存储数据。
|
|
||||||
- `mongo:4.2`:使用 MongoDB:4.2 镜像。
|
|
||||||
- `--auth`:启用身份验证功能。
|
|
||||||
|
|
||||||
进入 MongoDB 容器:
|
|
||||||
```bash
|
|
||||||
docker exec -it mongo mongo admin
|
|
||||||
```
|
|
||||||
|
|
||||||
### 在 MongoDB 容器中创建管理员用户
|
|
||||||
先进入 admin 数据库
|
|
||||||
```bash
|
|
||||||
use admin;
|
|
||||||
```
|
|
||||||
创建管理员用户 admin, 密码 root, 角色为 root。
|
|
||||||
```bash
|
|
||||||
db.createUser( { user: "root", pwd: "root", roles:['root'] });
|
|
||||||
```
|
|
||||||
退出 MongoDB 容器:
|
|
||||||
```bash
|
|
||||||
exit
|
|
||||||
```
|
|
||||||
使用 root 用户进行登录
|
|
||||||
```bash
|
|
||||||
docker exec -it mongo mongo admin -u root -p root
|
|
||||||
```
|
|
||||||
这里还能创建一个 admin 用户,用密码 admin,角色为 readWriteAnyDatabase, dbAdminAnyDatabase。
|
|
||||||
```bash
|
|
||||||
db.createUser( { user: "admin", pwd: "admin", roles:['readWriteAnyDatabase','dbAdminAnyDatabase'] } );
|
|
||||||
```
|
|
||||||
验证用户,这里会登录到 admin
|
|
||||||
```bash
|
|
||||||
db.auth("admin","admin");
|
|
||||||
```
|
|
||||||
删除用户的话可以使用,注意这里要切换回 root 用户
|
|
||||||
```bash
|
|
||||||
db.dropUser("admin");
|
|
||||||
```
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
---
|
|
||||||
title: 一些零散的命令
|
|
||||||
createTime: 2025/12/31 10:00:30
|
|
||||||
permalink: /ops/linux/other/
|
|
||||||
---
|
|
||||||
|
|
||||||
## 文件处理相关
|
|
||||||
|
|
||||||
统计当前目录下的所有文件加起来有多少行
|
|
||||||
```
|
|
||||||
wc -l * | tail -n 1
|
|
||||||
```
|
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
---
|
||||||
|
title: C++ 学习笔记
|
||||||
|
description: 记录C++编程语言的学习心得和基础知识
|
||||||
|
createTime: 2025/09/22 09:00:00
|
||||||
|
permalink: /programming/cplusplus/
|
||||||
|
---
|
||||||
|
|
||||||
|
# C++ 学习笔记
|
||||||
|
|
||||||
|
这里是我在学习C++编程语言过程中记录的学习心得、基础知识和实战经验。通过整理这些笔记,希望能够帮助自己和他人更好地掌握C++这门强大的编程语言。
|
||||||
|
|
||||||
|
## 学习路径
|
||||||
|
|
||||||
|
C++是一门复杂而强大的编程语言,学习路径建议如下:
|
||||||
|
|
||||||
|
1. **基础语法** - 掌握C++的基本语法和编程概念
|
||||||
|
2. **面向对象编程** - 学习C++的类、继承、多态等OOP特性
|
||||||
|
3. **STL标准库** - 熟悉常用的容器和算法
|
||||||
|
4. **高级特性** - 模板、异常处理、智能指针等
|
||||||
|
5. **实战项目** - 通过实际项目巩固所学知识
|
||||||
|
|
||||||
|
## 笔记分类
|
||||||
|
|
||||||
|
### 基础语法
|
||||||
|
|
||||||
|
- 数据类型
|
||||||
|
- 运算符和表达式
|
||||||
|
- 控制流语句
|
||||||
|
- 函数
|
||||||
|
|
||||||
|
### 面向对象编程
|
||||||
|
|
||||||
|
- 类和对象
|
||||||
|
- 继承与多态
|
||||||
|
- 封装与抽象
|
||||||
|
- 构造函数与析构函数
|
||||||
|
|
||||||
|
### STL标准库
|
||||||
|
|
||||||
|
- 容器(vector, list, map等)
|
||||||
|
- 迭代器
|
||||||
|
- 算法
|
||||||
|
- 函数对象
|
||||||
|
|
||||||
|
## 学习资源推荐
|
||||||
|
|
||||||
|
- [C++ Primer Plus:中文版(第六版)](https://101su.ru/book/5442395/430bc1/c-primer-plus%E4%B8%AD%E6%96%87%E7%89%88%E7%AC%AC%E5%85%AD%E7%89%88.html) - 很好的C++入门教材
|
||||||
|
|
||||||
|
开始你的C++学习之旅吧!坚持学习,你会发现C++的魅力所在!
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
title: C++ 简介与环境配置
|
||||||
|
createTime: 2025/09/22 09:06:00
|
||||||
|
permalink: /programming/cplusplus/basis/
|
||||||
|
---
|
||||||
|
|
||||||
|
## C++的简介
|
||||||
|
欢迎来到C++的奇妙世界~(๑>◡<๑) 如果你之前接触过C语言的话,那真是太棒啦!C++就像是C语言穿上了漂亮的新衣裳,不仅保留了原来的优点,还添加了面向对象和泛型编程的魔法哦~不过呢,可能需要稍微调整一下之前的编程习惯呢。就算你是第一次接触C++也完全不用担心,我会用最简单可爱的方式,带着你一步一步探索这个神奇的编程世界哒!
|
||||||
|
|
||||||
|
## 环境配置
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
---
|
||||||
|
title: LeetCode 题解笔记
|
||||||
|
description: 记录LeetCode算法题的解题思路和代码实现
|
||||||
|
createTime: 2025/09/22 08:09:52
|
||||||
|
permalink: /programming/leetcode/
|
||||||
|
---
|
||||||
|
|
||||||
|
# LeetCode 题解笔记
|
||||||
|
|
||||||
|
这里是我在LeetCode刷题过程中记录的解题思路和代码实现。通过整理这些题解,希望能够提高自己的算法水平和编程能力。
|
||||||
|
|
||||||
|
## 题解分类
|
||||||
|
|
||||||
|
### 数组与字符串
|
||||||
|
|
||||||
|
- [两数之和](https://leetcode-cn.com/problems/two-sum/)
|
||||||
|
- [三数之和](https://leetcode-cn.com/problems/3sum/)
|
||||||
|
|
||||||
|
### 链表
|
||||||
|
|
||||||
|
- [反转链表](https://leetcode-cn.com/problems/reverse-linked-list/)
|
||||||
|
- [合并两个有序链表](https://leetcode-cn.com/problems/merge-two-sorted-lists/)
|
||||||
|
|
||||||
|
### 动态规划
|
||||||
|
|
||||||
|
- [爬楼梯](https://leetcode-cn.com/problems/climbing-stairs/)
|
||||||
|
- [最长回文子串](https://leetcode-cn.com/problems/longest-palindromic-substring/)
|
||||||
|
|
||||||
|
## 解题技巧
|
||||||
|
|
||||||
|
1. **双指针法**:常用于数组、链表问题,如两数之和、反转链表等
|
||||||
|
2. **滑动窗口**:处理字符串子串问题
|
||||||
|
3. **动态规划**:将原问题分解为子问题,自底向上求解
|
||||||
|
4. **分治法**:将问题分成若干子问题,分别求解后合并结果
|
||||||
|
|
||||||
|
## 学习资源
|
||||||
|
|
||||||
|
- [LeetCode官方题解](https://leetcode-cn.com/problemset/all/)
|
||||||
|
- [代码随想录](https://programmercarl.com/)
|
||||||
|
- [算法导论](https://mitpress.mit.edu/books/introduction-algorithms)
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
title: LeetCode 入门题单~(≧∇≦)ノ
|
||||||
|
description: 给算法初学者的题目推荐和学习路径
|
||||||
|
createTime: 2025年9月22日 08:52:52
|
||||||
|
permalink: /programming/leetcode/question_sheet/beginner/
|
||||||
|
---
|
||||||
|
# LeetCode 入门题单~(๑>◡<๑)
|
||||||
|
|
||||||
|
作为算法初学者,选择合适的题目开始练习真的超~级重要哦!这里给大家推荐一份超棒的入门题单——[「新」动计划 · 编程入门](https://leetcode.cn/studyplan/primers-list/) ~这份题单就像给小树苗精心准备的阳光和雨露一样,特别适合刚开始算法之旅的你~(悄悄告诉你,里面有两题是数据库基础,暂时可以跳过哦~)
|
||||||
|
|
||||||
|
我的小建议是:先自己试着挑战一下,如果遇到困难了,再来看题解也没关系~重要的是享受这个成长的过程呀~(♡˙︶˙♡)
|
||||||
|
|
||||||
|
## 基础语法 & 数据类型 ٩(๑>◡<๑)۶
|
||||||
@@ -7,15 +7,3 @@ permalink: /programming/solidity/
|
|||||||
|
|
||||||
## Solidity 智能合约概述
|
## Solidity 智能合约概述
|
||||||
|
|
||||||
Solidity 是一种用于编写智能合约的静态类型编程语言,它运行在以太坊虚拟机(EVM)上。
|
|
||||||
|
|
||||||
## 推荐的资料
|
|
||||||
推荐的编辑器
|
|
||||||
|
|
||||||
Remix IDE:[https://remix.ethereum.org/](https://remix.ethereum.org/)
|
|
||||||
|
|
||||||
有在线版本,也可以下载之后使用,而且可以通过 Docker 部署,很方便
|
|
||||||
|
|
||||||
Solidity 学习资料:
|
|
||||||
- [Solidity 官方文档](https://docs.soliditylang.org/zh-cn/latest/index.html)
|
|
||||||
- [cryptozombies](https://cryptozombies.io/zh/course)
|
|
||||||
|
|||||||
@@ -6,83 +6,180 @@ permalink: /programming/solidity/basic-syntax/
|
|||||||
|
|
||||||
# Solidity 基础语法与数据类型~(≧∇≦)ノ
|
# Solidity 基础语法与数据类型~(≧∇≦)ノ
|
||||||
|
|
||||||
## Solidity 文件基础框架
|
嗨~欢迎来到 Solidity 基础语法的学习时间!今天我们要一起探索 Solidity 这门神奇语言的基本结构和数据类型哦~准备好了吗?Let's go!(๑>◡<๑)
|
||||||
|
|
||||||
Solidity 文件的基础框架通常包含以下几个部分:
|
## 智能合约的基本结构 🏗️
|
||||||
|
|
||||||
1. **SPDX 许可证标识符 (SPDX License Identifier)**:为了避免法律问题和明确智能合约的开源许可证,建议在合约的开头添加 SPDX 许可证标识符。
|
让我们先来看一个最简单的 Solidity 智能合约是什么样子的吧:
|
||||||
2. **Solidity 版本声明 (Pragma)**:这会告诉编译器您希望使用哪个版本的 Solidity 来编译您的代码。
|
|
||||||
3. **ABI编码编译指示**:通过`pragma abicoder v1`或`pragma abicoder v2`来指定ABI编码版本。
|
|
||||||
4. **合约定义 (Contract Definition)**:这是您编写智能合约代码的主体部分。
|
|
||||||
|
|
||||||
下面是一个基础的 Solidity 文件框架示例,您可以直接使用:
|
|
||||||
|
|
||||||
```solidity
|
```solidity
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
pragma solidity ^0.8.20;
|
pragma solidity ^0.8.0;
|
||||||
pragma experimental ABIEncoderV2;
|
|
||||||
|
|
||||||
contract MyContract {
|
contract HelloWorld {
|
||||||
// 在这里编写您的合约代码
|
// 状态变量
|
||||||
|
string public greeting = "Hello, World!";
|
||||||
|
|
||||||
|
// 函数
|
||||||
|
function setGreeting(string memory _greeting) public {
|
||||||
|
greeting = _greeting;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGreeting() public view returns (string memory) {
|
||||||
|
return greeting;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Solidity 基础数据类型
|
是不是看起来有点像其他编程语言呀?让我们来一点一点地了解它吧~
|
||||||
|
|
||||||
- 布尔类型(bool):值为 `true` 和 `false`
|
## 版本声明和许可证 📜
|
||||||
- 运算符:`!`(逻辑非)、`&&`(逻辑与)、`||`(逻辑或)、`==`(等于)、`!=`(不等于)
|
|
||||||
- 整形(int/uint):
|
|
||||||
- 有符号整数(int):可以表示负数和零。
|
|
||||||
- 无符号整数(uint):只能表示非负数。
|
|
||||||
- 整数类型可以是8位、16位、32位、64位或256位。(只要是八的倍数都可以)
|
|
||||||
- 例如:`int8`、`uint256`等。
|
|
||||||
- 运算符:
|
|
||||||
- 比较运算符:`<=`,`<`,`>=`,`>`
|
|
||||||
- 位运算符:`&`(按位与)、`|`(按位或)、`^`(按位异或)、`~`(按位取反)
|
|
||||||
- 移位运算符:`<<`(左移)、`>>`(右移)
|
|
||||||
- 算数运算符:`+`(加法)、`-`(减法)、`*`(乘法)、`/`(除法)、`%`(取余)
|
|
||||||
- 对于一个整数类型`X`,可以使用`type(X).min`和`type(X).max`来获取其最小值和最大值。
|
|
||||||
|
|
||||||
|
在 Solidity 合约的最开始,我们需要添加两行重要的声明:
|
||||||
|
|
||||||
## Solidity 基础语法结构
|
|
||||||
|
|
||||||
## Solidity 基础操作
|
|
||||||
|
|
||||||
### 导入其他源文件
|
|
||||||
|
|
||||||
Solidity 支持导入其他源文件,使用`import`语句。例如:
|
|
||||||
|
|
||||||
```solidity
|
```solidity
|
||||||
import "filename.sol";
|
// SPDX-License-Identifier: MIT
|
||||||
|
pragma solidity ^0.8.0;
|
||||||
```
|
```
|
||||||
|
|
||||||
这将导入`filename.sol`文件中的合约代码。
|
- **许可证声明**:告诉别人你的代码使用什么许可证,可以自由使用吗?
|
||||||
|
- **版本声明**:指定合约使用的 Solidity 编译器版本,`^0.8.0` 表示可以使用 0.8.0 及以上的版本哦~
|
||||||
|
|
||||||
还可以创建一个新的全局符号
|
## 数据类型 🌈
|
||||||
|
|
||||||
|
Solidity 有很多有趣的数据类型,让我们来认识一下它们吧~
|
||||||
|
|
||||||
|
### 值类型
|
||||||
|
|
||||||
|
这些类型在赋值时会直接复制值哦~
|
||||||
|
|
||||||
|
#### 布尔型 (Boolean)
|
||||||
|
|
||||||
```solidity
|
```solidity
|
||||||
import * as NewName from "filename.sol"
|
bool public isActive = true;
|
||||||
|
bool public isPaused = false;
|
||||||
```
|
```
|
||||||
|
|
||||||
这将创建一个新的全局符号`NewName`,您可以在合约中使用它来引用`filename.sol`文件中的合约,例如`NewName.ContractName`。
|
布尔型只有两个值:`true` 和 `false`,就像开关一样简单!(๑˘ᴗ˘๑)
|
||||||
|
|
||||||
还可以在导入的同时重命名符号:`import {symbol1 as alias, symbol2} from "filename";`
|
#### 整型 (Integer)
|
||||||
|
|
||||||
### 注释
|
|
||||||
|
|
||||||
Solidity 支持两种类型的注释:
|
|
||||||
|
|
||||||
1. **行注释**:使用`//`来注释单行代码。
|
|
||||||
2. **块注释**:使用`/*`和`*/`来注释多行代码。
|
|
||||||
|
|
||||||
例如:
|
|
||||||
|
|
||||||
```solidity
|
```solidity
|
||||||
// 这是一个行注释
|
int public negativeNumber = -42;
|
||||||
|
uint public positiveNumber = 42;
|
||||||
|
|
||||||
/*
|
// 你还可以指定位数哦~
|
||||||
这是一个
|
uint8 public smallNumber = 255; // 0-255
|
||||||
块注释
|
uint256 public bigNumber = 1000000000000000000; // 这是 1 以太币的 wei 值哦~
|
||||||
*/
|
```
|
||||||
```
|
|
||||||
|
整型分为有符号(`int`)和无符号(`uint`)两种,可以存储正数和负数呢~
|
||||||
|
|
||||||
|
#### 地址型 (Address)
|
||||||
|
|
||||||
|
```solidity
|
||||||
|
address public owner = 0x71C7656EC7ab88b098defB751B7401B5f6d8976F;
|
||||||
|
|
||||||
|
// 可以转账的地址类型
|
||||||
|
address payable public recipient = payable(0x71C7656EC7ab88b098defB751B7401B5f6d8976F);
|
||||||
|
```
|
||||||
|
|
||||||
|
地址类型非常特别,它用于存储以太坊账户的地址,就像是账户的身份证号码一样~`address payable` 还可以接收以太币转账呢!
|
||||||
|
|
||||||
|
### 引用类型
|
||||||
|
|
||||||
|
这些类型在赋值时只会传递引用,而不是复制整个值哦~
|
||||||
|
|
||||||
|
#### 字符串 (String)
|
||||||
|
|
||||||
|
```solidity
|
||||||
|
string public message = "Hello, Solidity!";
|
||||||
|
string public name = "祀梦";
|
||||||
|
```
|
||||||
|
|
||||||
|
字符串用于存储文本数据,就像你平时写的便签一样~
|
||||||
|
|
||||||
|
#### 数组 (Array)
|
||||||
|
|
||||||
|
```solidity
|
||||||
|
// 固定大小的数组
|
||||||
|
uint[5] public fixedArray = [1, 2, 3, 4, 5];
|
||||||
|
|
||||||
|
// 动态大小的数组
|
||||||
|
uint[] public dynamicArray;
|
||||||
|
|
||||||
|
// 你还可以在函数中这样使用哦~
|
||||||
|
function addNumber(uint _number) public {
|
||||||
|
dynamicArray.push(_number);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
数组就像是一个收纳盒,可以按顺序存放多个相同类型的数据呢~
|
||||||
|
|
||||||
|
#### 映射 (Mapping)
|
||||||
|
|
||||||
|
```solidity
|
||||||
|
// 映射:地址 => 余额
|
||||||
|
mapping(address => uint) public balances;
|
||||||
|
|
||||||
|
// 使用方式
|
||||||
|
function deposit() public payable {
|
||||||
|
balances[msg.sender] += msg.value;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
映射是 Solidity 中非常强大的数据结构,就像是一个魔法字典,可以通过键快速找到对应的值~
|
||||||
|
|
||||||
|
## 变量作用域 🔍
|
||||||
|
|
||||||
|
在 Solidity 中,变量有不同的作用域哦:
|
||||||
|
|
||||||
|
### 状态变量
|
||||||
|
|
||||||
|
```solidity
|
||||||
|
contract MyContract {
|
||||||
|
// 状态变量:存储在区块链上的
|
||||||
|
uint public myStateVariable = 42;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
状态变量是存储在区块链上的,所有合约函数都可以访问它们,就像是合约的公共记忆一样~
|
||||||
|
|
||||||
|
### 局部变量
|
||||||
|
|
||||||
|
```solidity
|
||||||
|
function myFunction() public {
|
||||||
|
// 局部变量:只在函数内部可见
|
||||||
|
uint myLocalVariable = 100;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
局部变量只在定义它们的函数内部可见,函数执行结束后就会消失哦~
|
||||||
|
|
||||||
|
### 全局变量
|
||||||
|
|
||||||
|
```solidity
|
||||||
|
function getSender() public view returns (address) {
|
||||||
|
// msg.sender 是一个全局变量,包含当前调用者的地址
|
||||||
|
return msg.sender;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
全局变量是 Solidity 内置的特殊变量,可以在任何地方使用,它们提供了关于区块链和交易的重要信息呢~
|
||||||
|
|
||||||
|
## 小练习 🏋️♀️
|
||||||
|
|
||||||
|
现在让我们来做一个小练习吧!试着编写一个简单的计数器合约,包含以下功能:
|
||||||
|
|
||||||
|
1. 一个状态变量来存储计数值
|
||||||
|
2. 一个函数来增加计数
|
||||||
|
3. 一个函数来减少计数
|
||||||
|
4. 一个函数来获取当前计数值
|
||||||
|
|
||||||
|
你可以做到吗?相信自己,你一定可以的!(๑•̀ㅂ•́)و✧
|
||||||
|
|
||||||
|
## 写在最后~💌
|
||||||
|
|
||||||
|
今天我们学习了 Solidity 的基础语法和数据类型,这些都是构建智能合约的基石哦~虽然一开始可能会觉得有点复杂,但只要多练习,你一定会越来越熟练的!下节课我们将学习 Solidity 的函数和修饰器,敬请期待吧~
|
||||||
|
|
||||||
|
如果有任何问题,欢迎随时提问哦~让我们一起在区块链的世界里快乐地探索吧!(づ ̄ 3 ̄)づ
|
||||||
|
|||||||
@@ -1,922 +0,0 @@
|
|||||||
---
|
|
||||||
title: 十份代码实例
|
|
||||||
createTime: 2025/10/10 11:07:11
|
|
||||||
permalink: /programming/solidity/analysis/case-analysis/
|
|
||||||
password: simeng
|
|
||||||
---
|
|
||||||
|
|
||||||
## 题目一
|
|
||||||
|
|
||||||
原题:
|
|
||||||
|
|
||||||
1.食品信息(FoodInfoItem)的接口编码
|
|
||||||
|
|
||||||
(1)编写食品信息实体的接口,完成可溯源食品信息初始化,实现可追溯的原始生产商食品信息上链功能
|
|
||||||
|
|
||||||
(2)编写分销商食品上链信息接口,根据食品溯源智能合约地址获取分销商上链食品的信息
|
|
||||||
|
|
||||||
(3)编写超市进行食品上链信息的接口,根据食品溯源智能合约地址获取超市上链食品信息。
|
|
||||||
|
|
||||||
2.食品溯源(Trace)的接口编码
|
|
||||||
|
|
||||||
(1)编写食品溯源智能合约生产商Producer添加食品接口,必须生产商才能添加可溯源的食品,实现溯源功能
|
|
||||||
|
|
||||||
(2)编写食品溯源智能合约分销商Distributor添加食品接口,必须分销商才能添加可溯源的食品,实现溯源功能
|
|
||||||
|
|
||||||
(3)编写食品溯源智能合约超市Retailer添加食品接口,必须超市才能添加可溯源的食品,实现溯源功能
|
|
||||||
|
|
||||||
3.角色(Role)管理的接口编码
|
|
||||||
|
|
||||||
(1)编写食品溯源增加角色接口,必须是未增加的角色才能被添加,实现添加角色的功能
|
|
||||||
|
|
||||||
(2)编写食品溯源移除角色接口,必须是已增加的角色才能被移除,实现移除角色的功能
|
|
||||||
|
|
||||||
(3)编写食品溯源角色授权接口,必须是授权的角色地址,实现角色权限管理功能
|
|
||||||
|
|
||||||
::: code-tabs
|
|
||||||
@tab Distributor.sol
|
|
||||||
```solidity
|
|
||||||
pragma solidity ^0.4.25;
|
|
||||||
|
|
||||||
import "./Roles.sol";
|
|
||||||
|
|
||||||
//中间商角色
|
|
||||||
contract Distributor {
|
|
||||||
using Roles for Roles.Role;
|
|
||||||
|
|
||||||
event DistributorAdded(address indexed account);
|
|
||||||
event DistributorRemoved(address indexed account);
|
|
||||||
|
|
||||||
Roles.Role private _distributors;
|
|
||||||
|
|
||||||
constructor (address distributor ) public {
|
|
||||||
_addDistributor(distributor);
|
|
||||||
}
|
|
||||||
|
|
||||||
modifier onlyDistributor() {
|
|
||||||
require(isDistributor(msg.sender), "DistributorRole: caller does not have the Distributor role");
|
|
||||||
_;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isDistributor(address account) public view returns (bool) {
|
|
||||||
return _distributors.has(account);
|
|
||||||
}
|
|
||||||
|
|
||||||
function addDistributor(address account) public onlyDistributor {
|
|
||||||
_addDistributor(account);
|
|
||||||
}
|
|
||||||
|
|
||||||
function renounceDistributor() public {
|
|
||||||
_removeDistributor(msg.sender);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _addDistributor(address account) internal {
|
|
||||||
_distributors.add(account);
|
|
||||||
emit DistributorAdded(account);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _removeDistributor(address account) internal {
|
|
||||||
_distributors.remove(account);
|
|
||||||
emit DistributorRemoved(account);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
@tab FoodInfoItem.sol
|
|
||||||
```solidity
|
|
||||||
pragma solidity >=0.4.22 <0.5.0;
|
|
||||||
pragma experimental ABIEncoderV2;
|
|
||||||
|
|
||||||
//食品信息管理合约
|
|
||||||
// 1. 保存食品基本信息:时间戳(流转过程中),用户名(流转过程中),用户地址信息(流转过程中),食品质量(流转过程中),食物名称,当前用户名称,质量,状态.
|
|
||||||
// 2. 对食品基本信息进行初始化
|
|
||||||
// 3. 实现两个方法:中间商添加食品信息;超市添加食品信息
|
|
||||||
// 4. 实现显示食品信息的方法
|
|
||||||
|
|
||||||
contract FoodInfoItem{
|
|
||||||
uint[] _timestamp;//①保存食品流转过程中各个阶段的时间戳
|
|
||||||
string[] _traceName;//②保存食品流转过程各个阶段的用户名
|
|
||||||
address[] _traceAddress;//③保存食品流转过程各个阶段的用户地址信息(和用户一一对应)
|
|
||||||
uint8[] _traceQuality;//④保存食品流转过程中各个阶段的质量
|
|
||||||
string _name;//⑤食品名称
|
|
||||||
string _currentTraceName;//⑥当前用户名称
|
|
||||||
uint8 _quality;//⑦质量(0=优质 1=合格 2=不合格)
|
|
||||||
uint8 _status;//⑧状态(0:生产 1:分销 2:出售)
|
|
||||||
address _owner;//⑨初始化owner
|
|
||||||
|
|
||||||
constructor (string name, string traceName, uint8 quality, address producer) public {
|
|
||||||
_timestamp.push(now);
|
|
||||||
_traceName.push(traceName);
|
|
||||||
_traceAddress.push(producer);
|
|
||||||
_traceQuality.push(quality);
|
|
||||||
_name = name;
|
|
||||||
_currentTraceName = traceName;
|
|
||||||
_quality = quality;
|
|
||||||
_status = 0;
|
|
||||||
_owner = msg.sender;
|
|
||||||
}
|
|
||||||
|
|
||||||
function addTraceInfoByDistributor(string traceName,address distributor, uint8 quality) public returns(bool) {
|
|
||||||
require(_status == 0 , "status must be producing");
|
|
||||||
require(_owner == msg.sender,"only trace contract can invoke")
|
|
||||||
//②
|
|
||||||
_timestamp.push(now);
|
|
||||||
_traceName.push(traceName);
|
|
||||||
_currentTraceName = traceName;
|
|
||||||
_traceAddress.push(distributor);
|
|
||||||
_quality = quality;
|
|
||||||
//③
|
|
||||||
//④
|
|
||||||
_traceQuality.push(_quality);
|
|
||||||
_status = 1;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function addTraceInfoByRetailer(string traceName,address retailer, uint8 quality) public returns(bool) {
|
|
||||||
require(_status == 1 , "status must be distributing");
|
|
||||||
require(_owner == msg.sender,"only trace contract can invoke")
|
|
||||||
//②
|
|
||||||
_timestamp.push(now);
|
|
||||||
_traceName.push(traceName);
|
|
||||||
_currentTraceName = traceName;
|
|
||||||
_traceAddress.push(retailer);
|
|
||||||
_quality = quality;
|
|
||||||
//③
|
|
||||||
//④
|
|
||||||
_traceQuality.push(_quality);
|
|
||||||
_status = 2;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
function getTraceInfo() public constant returns(uint[], string[], address[], uint8[]) {
|
|
||||||
return(_timestamp, _traceName, _traceAddress, _traceQuality);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFood() public constant returns(uint, string, string, string, address, uint8) {
|
|
||||||
return(_timestamp[0], _traceName[0], _name, _currentTraceName, _traceAddress[0], _quality);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
```
|
|
||||||
@tab Producer.sol
|
|
||||||
```solidity
|
|
||||||
pragma solidity ^0.4.25;
|
|
||||||
|
|
||||||
import "./Roles.sol";
|
|
||||||
|
|
||||||
//生产者角色
|
|
||||||
contract Producer {
|
|
||||||
using Roles for Roles.Role;
|
|
||||||
|
|
||||||
event ProducerAdded(address indexed account);
|
|
||||||
event ProducerRemoved(address indexed account);
|
|
||||||
|
|
||||||
Roles.Role private _producers;
|
|
||||||
|
|
||||||
constructor (address producer) public {
|
|
||||||
_addProducer(producer);
|
|
||||||
}
|
|
||||||
|
|
||||||
modifier onlyProducer() {
|
|
||||||
require(isProducer(msg.sender), "ProducerRole: caller does not have the Producer role");
|
|
||||||
_;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isProducer(address account) public view returns (bool) {
|
|
||||||
return _producers.has(account);
|
|
||||||
}
|
|
||||||
|
|
||||||
function addProducer(address account) public onlyProducer {
|
|
||||||
_addProducer(account);
|
|
||||||
}
|
|
||||||
|
|
||||||
function renounceProducer() public {
|
|
||||||
_removeProducer(msg.sender);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _addProducer(address account) internal {
|
|
||||||
_producers.add(account);
|
|
||||||
emit ProducerAdded(account);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _removeProducer(address account) internal {
|
|
||||||
_producers.remove(account);
|
|
||||||
emit ProducerRemoved(account);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
@tab Retailer.sol
|
|
||||||
```solidity
|
|
||||||
pragma solidity ^0.4.25;
|
|
||||||
|
|
||||||
import "./Roles.sol";
|
|
||||||
|
|
||||||
//零售商角色(超市)
|
|
||||||
contract Retailer {
|
|
||||||
using Roles for Roles.Role;
|
|
||||||
|
|
||||||
event RetailerAdded(address indexed account);
|
|
||||||
event RetailerRemoved(address indexed account);
|
|
||||||
|
|
||||||
Roles.Role private _retailers;
|
|
||||||
|
|
||||||
constructor (address retailer) public {
|
|
||||||
_addRetailer(retailer);
|
|
||||||
}
|
|
||||||
|
|
||||||
modifier onlyRetailer() {
|
|
||||||
require(isRetailer(msg.sender), "RetailerRole: caller does not have the Retailer role");
|
|
||||||
_;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isRetailer(address account) public view returns (bool) {
|
|
||||||
return _retailers.has(account);
|
|
||||||
}
|
|
||||||
|
|
||||||
function addRetailer(address account) public onlyRetailer {
|
|
||||||
_addRetailer(account);
|
|
||||||
}
|
|
||||||
|
|
||||||
function renounceRetailer() public {
|
|
||||||
_removeRetailer(msg.sender);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _addRetailer(address account) internal {
|
|
||||||
_retailers.add(account);
|
|
||||||
emit RetailerAdded(account);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _removeRetailer(address account) internal {
|
|
||||||
_retailers.remove(account);
|
|
||||||
emit RetailerRemoved(account);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
@tab Roles.sol
|
|
||||||
```solidity
|
|
||||||
pragma solidity ^0.4.25;
|
|
||||||
//角色库(管理所有角色地址)
|
|
||||||
// 1. 实现增加角色地址
|
|
||||||
// 2. 移除角色地址
|
|
||||||
// 3. 判断角色地址是否被授权
|
|
||||||
library Roles {
|
|
||||||
struct Role {
|
|
||||||
mapping (address => bool) bearer;
|
|
||||||
}
|
|
||||||
function add(Role storage role, address account) internal {
|
|
||||||
require(!has(role, account), "Roles: account already has role");
|
|
||||||
role.bearer[account] = true;
|
|
||||||
}
|
|
||||||
function remove(Role storage role, address account) internal {
|
|
||||||
require(has(role, account), "Roles: account does not have role");
|
|
||||||
role.bearer[account] = false;
|
|
||||||
}
|
|
||||||
function has(Role storage role, address account) internal returns (bool) {
|
|
||||||
require(account != address(0), "Roles: account is the zero address");
|
|
||||||
return role.bearer[account];
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
```
|
|
||||||
@tab Trace.sol
|
|
||||||
```solidity
|
|
||||||
pragma solidity >=0.4.22 <0.7.0;
|
|
||||||
pragma experimental ABIEncoderV2;
|
|
||||||
import "./FoodInfoItem.sol";
|
|
||||||
import "./Distributor.sol";
|
|
||||||
import "./Producer.sol";
|
|
||||||
import "./Retailer.sol";
|
|
||||||
|
|
||||||
//食品溯源合约(负责具体食品溯源信息的生成)
|
|
||||||
// 1.实现生产食品的方法(新建食品信息)
|
|
||||||
// 2.实现食品分销过程中增加溯源信息的接口
|
|
||||||
// 3.实现食品出售过程中增加溯源信息的接口
|
|
||||||
// 4.实现获取食品溯源信息接口
|
|
||||||
|
|
||||||
contract Trace is Producer, Distributor, Retailer{
|
|
||||||
|
|
||||||
mapping (uint256 => address) foods;//食品溯源id到具体食品溯源合约的映射表
|
|
||||||
uint[] foodList;
|
|
||||||
|
|
||||||
//构造函数
|
|
||||||
constructor(address producer, address distributor, address retailer)
|
|
||||||
public Producer(producer)
|
|
||||||
Distributor(distributor)
|
|
||||||
Retailer(retailer){
|
|
||||||
|
|
||||||
}
|
|
||||||
//生成食品溯源信息接口
|
|
||||||
//只有Producer能调用
|
|
||||||
//name 食品名称
|
|
||||||
//traceNumber 食品溯源id
|
|
||||||
//traceName 当前用户名称
|
|
||||||
//quality 当前食品质量
|
|
||||||
function newFood(string name,uint256 traceNumber, string traceName, uint8 quality)
|
|
||||||
public onlyProducer returns(uint)
|
|
||||||
{
|
|
||||||
require(foods[traceNumber] == address(0),"traceName already exist");//④
|
|
||||||
FoodInfoItem food = new FoodInfoItem(name,traceName,quality,msg.sender);//⑤
|
|
||||||
foods[traceNumber] = food;//⑥
|
|
||||||
foodList.push(traceNumber);//⑦
|
|
||||||
retur food;//⑧
|
|
||||||
}
|
|
||||||
|
|
||||||
//食品分销过程中增加溯源信息的接口
|
|
||||||
//只有Distributor能调用
|
|
||||||
//traceNumber 食品溯源id
|
|
||||||
//traceName 当前用户名称
|
|
||||||
//quality 当前食品质量
|
|
||||||
function addTraceInfoByDistributor(uint256 traceNumber,string traceName, uint8 quality)
|
|
||||||
public onlyRetailer returns(bool) {
|
|
||||||
require(foods[traceNumber] != address(0),"traceNumber does not exist");
|
|
||||||
//③
|
|
||||||
return FoodInfoItem(foods[traceNumber]).addTraceInfoByDistributor(traceName,msg.sender, quality);
|
|
||||||
}
|
|
||||||
|
|
||||||
//食品出售过程中增加溯源信息的接口
|
|
||||||
//只有Retailer能调用
|
|
||||||
//traceNumber 食品溯源id
|
|
||||||
//traceName 当前用户名称
|
|
||||||
//quality 当前食品质量
|
|
||||||
function addTraceInfoByRetailer(uint256 traceNumber,string traceName, uint8 quality)
|
|
||||||
public onlyRetailer returns(bool) {
|
|
||||||
require(foods[traceNumber != address(0)], "traceNumber does not exist");
|
|
||||||
return FoodInfoItem(foods[traceNumber]).addTraceInfoByRetailer(traceName,msg.sender, quality);
|
|
||||||
}
|
|
||||||
|
|
||||||
//获取食品溯源信息接口
|
|
||||||
//string[] 保存食品流转过程中各个阶段的相关信息
|
|
||||||
//address[] 保存食品流转过程各个阶段的用户地址信息(和用户一一对应)
|
|
||||||
//uint8[] 保存食品流转过程中各个阶段的状态变化
|
|
||||||
function getTraceInfo(uint256 traceNumber) public constant returns(uint[], string[], address[], uint8[]) {
|
|
||||||
require(foods[traceNumber] != address(0), "traceNumber does not exist");
|
|
||||||
return FoodInfoItem(foods[traceNumber]).getTraceInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFood(uint256 traceNumber) public constant returns(uint, string, string, string, address, uint8) {
|
|
||||||
require(foods[traceNumber] != address(0), "traceNumber does not exist");
|
|
||||||
return FoodInfoItem(foods[traceNumber]).getFood();
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAllFood() public constant returns (uint[]) {
|
|
||||||
return foodList;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
:::
|
|
||||||
## 题目二
|
|
||||||
|
|
||||||
题目:
|
|
||||||
|
|
||||||
1.供应链金融实体信息编码(6分)
|
|
||||||
|
|
||||||
(1)编写供应链金融智能合约的实体接口,完成实体通用数据的初始化,实现企业和票据实体信息上链的功能;(2分)
|
|
||||||
|
|
||||||
(2)编写企业上链信息接口,实现供应链金融的企业信息上链;(2分)
|
|
||||||
|
|
||||||
(3)基于给定的智能合约代码以及注释,完成银行向企业交易的接口函数;(2分)
|
|
||||||
|
|
||||||
2.供应链金融公司与公司接口编码(6分)
|
|
||||||
|
|
||||||
(1)编写公司与公司之间进行交易的历史存证上链接口,实现公司与公司之间的交易功能;(2分)
|
|
||||||
|
|
||||||
(2)编写创建存证的接口,实现创建存证的功能;(2分)
|
|
||||||
|
|
||||||
(3)编写交易金额数量变化的接口,实现凭证交易双方资金的变化功能;(2分)
|
|
||||||
|
|
||||||
3.供应链金融公司与银行交易的接口编码(4分)
|
|
||||||
|
|
||||||
(1)编写公司与银行之间进行交易的历史存证上链接口,实现公司与银行之间的交易功能;(2分)
|
|
||||||
|
|
||||||
(2)编写创建存证的接口,实现创建存证的功能;(1分)
|
|
||||||
|
|
||||||
(3)编写交易金额数量变化的接口,实现凭证交易双方资金的变化功能;(1分)
|
|
||||||
|
|
||||||
::: code-tabs
|
|
||||||
@tab xxx.sol
|
|
||||||
```solidity
|
|
||||||
```
|
|
||||||
@tab xxx.sol
|
|
||||||
```solidity
|
|
||||||
```
|
|
||||||
@tab xxx.sol
|
|
||||||
```solidity
|
|
||||||
```
|
|
||||||
:::
|
|
||||||
## 题目三
|
|
||||||
|
|
||||||
题目:
|
|
||||||
|
|
||||||
子任务2-2-1:太阳能板管理接口编码
|
|
||||||
|
|
||||||
1. 根据文档要求,编写太阳能板新增接口功能,必须将新增太阳能板数据存入指定表中,在存储完成后需触发后事件并返回存储与否的标识;
|
|
||||||
|
|
||||||
2. 根据文档要求,编写太阳能板修改接口,必须通过指定表修改完成数据更新,在完成更新后需触发事件并返回更新与否的标识。
|
|
||||||
|
|
||||||
子任务2-2-2:能源管理接口编码
|
|
||||||
|
|
||||||
1. 根据文档要求,编写能源新增接口功能,必须将新增能源数据存入指定表中,在存储完成后需触发后事件并返回存储与否的标识;
|
|
||||||
|
|
||||||
2. 根据文档要求,编写能源修改接口,必须通过指定表修改完成数据更新,在完成更新后需触发事件并返回更新与否的标识。
|
|
||||||
|
|
||||||
子任务2-2-3:合约部署和调用
|
|
||||||
|
|
||||||
1. 解决代码错误和警告,正确编译并部署合约,成功获取部署的合约地址和ABI;
|
|
||||||
|
|
||||||
2. 调用太阳能板查询合约接口,完整验证业务流程;
|
|
||||||
|
|
||||||
3. 调用能源查询合约接口,完整验证业务流程。
|
|
||||||
|
|
||||||
::: code-tabs
|
|
||||||
@tab xxx.sol
|
|
||||||
```solidity
|
|
||||||
```
|
|
||||||
@tab xxx.sol
|
|
||||||
```solidity
|
|
||||||
```
|
|
||||||
@tab xxx.sol
|
|
||||||
```solidity
|
|
||||||
```
|
|
||||||
:::
|
|
||||||
## 题目四
|
|
||||||
|
|
||||||
原题:
|
|
||||||
|
|
||||||
1.编写原材料接口newMaterial,初始化原材料信息,返回合约地址,并实现原材料信息上链功能。
|
|
||||||
|
|
||||||
2.编写获取存原材料接口getMaterial,根据合约地址获取原材料信息
|
|
||||||
|
|
||||||
3.编写食品物流上链接口addLogistic,实现食品物流信息上链功能
|
|
||||||
|
|
||||||
4.编写获取食品物流信息的接口getLogistics,根据食品产品编号获取物流信息
|
|
||||||
|
|
||||||
::: code-tabs
|
|
||||||
@tab Logistics.sol
|
|
||||||
```solidity
|
|
||||||
pragma solidity 0.6.10;
|
|
||||||
|
|
||||||
pragma experimental ABIEncoderV2;
|
|
||||||
|
|
||||||
contract Logistics {
|
|
||||||
|
|
||||||
struct LogisticsData {
|
|
||||||
address cargo; //货物合约地址
|
|
||||||
address orgin; //货物上级合约地址
|
|
||||||
address destination; //货物下级合约地址
|
|
||||||
string memo; //备注信息
|
|
||||||
uint createdAt; //创建时间
|
|
||||||
uint queryCount; //已查询次数
|
|
||||||
}
|
|
||||||
|
|
||||||
LogisticsData[] private _logisticsData; //全部物流信息数组
|
|
||||||
|
|
||||||
uint public recordCount;//所有记录条数
|
|
||||||
uint public queryCount;//所有查询次数
|
|
||||||
|
|
||||||
//可自行添加形参和返回值
|
|
||||||
function addLogistics(address _cargo,address _orgin,【请补充】) public {
|
|
||||||
//TODO:”请补充缺失代码
|
|
||||||
}
|
|
||||||
|
|
||||||
//可自行添加形参和返回值
|
|
||||||
function getLogisticsInfo(address _cargo) public returns(LogisticsData[] memory _cargoLogisticsData) {
|
|
||||||
|
|
||||||
//TODO:”请补充缺失代码
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
```
|
|
||||||
@tab Material.sol
|
|
||||||
```solidity
|
|
||||||
pragma solidity 0.6.10;
|
|
||||||
|
|
||||||
contract Material {
|
|
||||||
struct Material {
|
|
||||||
address owner;
|
|
||||||
string name;
|
|
||||||
string id;
|
|
||||||
string memo;
|
|
||||||
uint createdAt;
|
|
||||||
bool exist;
|
|
||||||
}
|
|
||||||
|
|
||||||
mapping(string => Material) public materials;
|
|
||||||
//可自行添加形参和返回值
|
|
||||||
function newMaterial(string memory _name,string memory _id,【请补充】) public {
|
|
||||||
|
|
||||||
//TODO:”请补充缺失代码
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//可自行添加形参和返回值
|
|
||||||
function getMaterial(string memory id) public view returns(【请补充】) {
|
|
||||||
|
|
||||||
//TODO:”请补充缺失代码
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
```
|
|
||||||
:::
|
|
||||||
|
|
||||||
参考答案:
|
|
||||||
|
|
||||||
::: code-tabs
|
|
||||||
@tab Logistics.sol
|
|
||||||
```solidity
|
|
||||||
pragma solidity 0.6.10;
|
|
||||||
|
|
||||||
pragma experimental ABIEncoderV2;
|
|
||||||
|
|
||||||
contract Logistics {
|
|
||||||
|
|
||||||
struct LogisticsData {
|
|
||||||
address cargo; //货物合约地址
|
|
||||||
address orgin; //货物上级合约地址
|
|
||||||
address destination; //货物下级合约地址
|
|
||||||
string memo; //备注信息
|
|
||||||
uint createdAt; //创建时间
|
|
||||||
uint queryCount; //已查询次数
|
|
||||||
}
|
|
||||||
|
|
||||||
LogisticsData[] private _logisticsData; //全部物流信息数组
|
|
||||||
|
|
||||||
uint public recordCount;//所有记录条数
|
|
||||||
uint public queryCount;//所有查询次数
|
|
||||||
|
|
||||||
//可自行添加形参和返回值
|
|
||||||
function addLogistics(address _cargo,address _orgin,【请补充】) public {
|
|
||||||
//TODO:”请补充缺失代码
|
|
||||||
}
|
|
||||||
|
|
||||||
//可自行添加形参和返回值
|
|
||||||
function getLogisticsInfo(address _cargo) public returns(LogisticsData[] memory _cargoLogisticsData) {
|
|
||||||
|
|
||||||
//TODO:”请补充缺失代码
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
```
|
|
||||||
@tab Material.sol
|
|
||||||
```solidity
|
|
||||||
pragma solidity 0.6.10;
|
|
||||||
|
|
||||||
contract Material {
|
|
||||||
struct Material {
|
|
||||||
address owner;
|
|
||||||
string name;
|
|
||||||
string id;
|
|
||||||
string memo;
|
|
||||||
uint createdAt;
|
|
||||||
bool exist;
|
|
||||||
}
|
|
||||||
|
|
||||||
mapping(string => Material) public materials;
|
|
||||||
//可自行添加形参和返回值
|
|
||||||
function newMaterial(string memory _name,string memory _id,【请补充】) public {
|
|
||||||
|
|
||||||
//TODO:”请补充缺失代码
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//可自行添加形参和返回值
|
|
||||||
function getMaterial(string memory id) public view returns(【请补充】) {
|
|
||||||
|
|
||||||
//TODO:”请补充缺失代码
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
```
|
|
||||||
:::
|
|
||||||
|
|
||||||
## 题目五
|
|
||||||
|
|
||||||
题目:
|
|
||||||
|
|
||||||
1.个人签章信息接口编码
|
|
||||||
|
|
||||||
(1)编写个人签章智能合约的实体接口,完成实体通用数据的初始化,实现签章和用户实体信息上链的功能
|
|
||||||
|
|
||||||
(2)编写签章信息上链的接口,实现Seal合约的构造函数;
|
|
||||||
|
|
||||||
(3)基于给定的智能合约代码以及注释,完成ElectronicSeal合约判断多人签章文件编号是否存在的函数。
|
|
||||||
|
|
||||||
2.电子印章接口编码
|
|
||||||
|
|
||||||
(1)基于给定的智能合约代码以及注释,完成ElectronicSeal合约获取多人签章信息函数;
|
|
||||||
|
|
||||||
(2)基于给定的智能合约代码以及注释,完成ElectronicSeal合约多人签章函数。
|
|
||||||
|
|
||||||
::: code-tabs
|
|
||||||
@tab xxx.sol
|
|
||||||
```solidity
|
|
||||||
```
|
|
||||||
:::
|
|
||||||
## 题目六
|
|
||||||
|
|
||||||
题目:
|
|
||||||
|
|
||||||
1.食品信息(Food)、成员信息(Member)、生产订单信息(Productions)的结构体功能编码(6分)
|
|
||||||
|
|
||||||
(1)编写食品信息实体功能。(2分)
|
|
||||||
|
|
||||||
(2)完善智能合约中用户结构体内容(2分)
|
|
||||||
|
|
||||||
(3)编写生产订单(Productions)结构体信息。(2分)
|
|
||||||
|
|
||||||
2.食品溯源(Trace)的接口编码(6分)
|
|
||||||
|
|
||||||
(1)根据食品信息结构体,完成食品信息添加相应功能(2分)
|
|
||||||
|
|
||||||
(2)编写食品溯源收购商创建收购订单功能。
|
|
||||||
|
|
||||||
(3)编写食品溯源创建运输订单功能。
|
|
||||||
|
|
||||||
3.角色(Role)管理的接口编码(4分)
|
|
||||||
|
|
||||||
(1)编写食品溯源增加角色接口,实现添加角色的功能。(1分)
|
|
||||||
|
|
||||||
(2)编写食品溯源获取角色功能。(1分)
|
|
||||||
|
|
||||||
(3)编写食品溯源修改角色功能。(2分)
|
|
||||||
|
|
||||||
::: code-tabs
|
|
||||||
@tab xxx.sol
|
|
||||||
```
|
|
||||||
```
|
|
||||||
:::
|
|
||||||
## 题目七
|
|
||||||
|
|
||||||
原题:
|
|
||||||
**子任务2-2-1:航班延误保险购买合约编码(5分)**
|
|
||||||
|
|
||||||
(1)编写航班保险购买上链接口,完成只有购买机票的用户可以购买保险、必须缴纳正确的保费金额、购买保险的时间不能超过购买机票后的0.5小时的功能,符合条件则用户可以购买保险,将用户购买保险状态上链;(3分)
|
|
||||||
|
|
||||||
(2)编写退保接口,完成保险公司预存赔偿金后,用户无法退保,反之用户可退保的功能,将用户退保状态上链,并退还用户保费功能。(2分)
|
|
||||||
|
|
||||||
**子任务2-2-2:航班信息存证上链合约编码**
|
|
||||||
|
|
||||||
(1)编写获取航班信息接口,实现依据航班号获得航班号对应的计划起飞时间、实际起飞时间、到达时间、是否延误状态的功能;
|
|
||||||
|
|
||||||
(2)编写判断航班是否延误接口,实现依据航班号获得航班号对应的航班是否延误,得到航班是否延误的结果功能。
|
|
||||||
|
|
||||||
**子任务2-2-3:航班延误险理赔合约编码**
|
|
||||||
|
|
||||||
(1)编写客户理赔接口,实现如果航班延误超过4小时,将赔偿金赔偿给乘客的功能;
|
|
||||||
|
|
||||||
(2)编写保险公司收取保费接口,实现如果航班没有延误或者延误时间少于4小时,将保费转账给保险公司,并退还赔偿金的功能。
|
|
||||||
|
|
||||||
::: code-tabs
|
|
||||||
@tab Claims.sol
|
|
||||||
```solidity
|
|
||||||
pragma solidity ^0.6.10;
|
|
||||||
|
|
||||||
import "./FlightDelayInsurance.sol";
|
|
||||||
|
|
||||||
contract Claims is FlightDelayInsurance{
|
|
||||||
|
|
||||||
// 定义航班信息结构体
|
|
||||||
struct Flight {
|
|
||||||
uint256 departureTime; // 航班出发时间
|
|
||||||
uint256 delayTime; // 航班延误时间
|
|
||||||
bool isDelayed; // 航班是否延误
|
|
||||||
bool isInsured; // 航班是否购买保险
|
|
||||||
}
|
|
||||||
|
|
||||||
// 定义保险公司地址
|
|
||||||
address public insuranceCompany;
|
|
||||||
|
|
||||||
// 定义航班信息映射
|
|
||||||
mapping(bytes32 => Flight) public flights;
|
|
||||||
|
|
||||||
// 定义购买保险事件
|
|
||||||
event BuyInsurance(bytes32 flightKey, address passenger, uint256 premium);
|
|
||||||
|
|
||||||
// 定义航班延误事件
|
|
||||||
event FlightDelay(bytes32 flightKey, uint256 delayTime);
|
|
||||||
|
|
||||||
// 定义理赔事件
|
|
||||||
event Claim(bytes32 flightKey, address passenger, uint256 amount);
|
|
||||||
|
|
||||||
// 定义保险公司预存的赔偿金
|
|
||||||
uint256 public compensationNew;
|
|
||||||
|
|
||||||
// 构造函数,初始化保险公司地址和赔偿金
|
|
||||||
constructor() FlightDelayInsurance(platformS,airlineV,insuranceCompanyC,premium,compensation) public {
|
|
||||||
insuranceCompany = msg.sender;
|
|
||||||
compensationNew = compensation;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新航班信息函数
|
|
||||||
function updateFlight(bytes32 flightKey, uint256 departureTime, uint256 delayTime, bool isDelayed) payable public {
|
|
||||||
// 判断调用者是否为保险公司
|
|
||||||
require(msg.sender == insuranceCompany, "Only insurance company can update flight information");
|
|
||||||
// 更新航班信息
|
|
||||||
flights[flightKey].departureTime = departureTime;
|
|
||||||
flights[flightKey].isDelayed = isDelayed;
|
|
||||||
|
|
||||||
/*********** 客户理赔接口 **********/
|
|
||||||
//任务2-2-3,请编写合约代码
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/********** 客户理赔接口 ***********/
|
|
||||||
|
|
||||||
/*********** 保险公司收取保费接口开发 **********/
|
|
||||||
//任务2-2-3,请编写合约代码
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/********** 保险公司收取保费接口开发 ***********/
|
|
||||||
// 触发航班延误事件
|
|
||||||
emit FlightDelay(flightKey, delayTime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
@tab FlightDelayInsurance.sol
|
|
||||||
```solidity
|
|
||||||
pragma solidity ^0.6.10;
|
|
||||||
|
|
||||||
contract FlightDelayInsurance {
|
|
||||||
address public platformS; // 平台S的地址
|
|
||||||
address public airlineV; // 航空公司V的地址
|
|
||||||
address public insuranceCompanyC; // 保险公司C的地址
|
|
||||||
uint public premium; // 保险费
|
|
||||||
uint public compensation; // 赔偿金额
|
|
||||||
uint public purchaseTime; // 购买保险的时间
|
|
||||||
uint public depositTime; // 存入赔偿金额的时间
|
|
||||||
bool public purchased; // 是否购买了保险
|
|
||||||
bool public deposited; // 是否存入了赔偿金额
|
|
||||||
mapping(address => bool) public insured; // 已退保的用户
|
|
||||||
mapping(address => bool) public policy; // 已生成保单的用户
|
|
||||||
mapping(address => bool) public purchasedTicket; // 已购买机票的用户
|
|
||||||
|
|
||||||
constructor(address _platformS, address _airlineV, address _insuranceCompanyC, uint _premium, uint _compensation) public {
|
|
||||||
platformS = _platformS; // 初始化平台S的地址
|
|
||||||
airlineV = _airlineV; // 初始化航空公司V的地址
|
|
||||||
insuranceCompanyC = _insuranceCompanyC; // 初始化保险公司C的地址
|
|
||||||
premium = _premium; // 初始化保险费
|
|
||||||
compensation = _compensation; // 初始化赔偿金额
|
|
||||||
}
|
|
||||||
|
|
||||||
function purchaseTicket() public {
|
|
||||||
require(!purchasedTicket[msg.sender], "Ticket has already been purchased"); // 该用户已购买机票
|
|
||||||
purchasedTicket[msg.sender] = true; // 标记该用户已购买机票
|
|
||||||
purchaseTime = block.timestamp;
|
|
||||||
}
|
|
||||||
/*********** 航班保险购买上链接口开发 **********/
|
|
||||||
//任务2-2-1,请编写合约代码
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/********** 航班保险购买上链接口开发 ***********/
|
|
||||||
function depositCompensation() public payable {
|
|
||||||
require(msg.sender == insuranceCompanyC, "Only insurance company C can deposit compensation"); // 只有保险公司C可以存入赔偿金额
|
|
||||||
require(msg.value == compensation, "Compensation amount is incorrect"); // 赔偿金额不正确
|
|
||||||
require(block.timestamp < depositTime + 2 hours, "Deposit time has expired"); // 存入赔偿金额的时间已过期
|
|
||||||
deposited = true; // 标记已存入赔偿金额
|
|
||||||
}
|
|
||||||
|
|
||||||
/*********** 退保接口开发 **********/
|
|
||||||
//任务2-2-1,请编写合约代码
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/********** 退保接口开发 ***********/
|
|
||||||
|
|
||||||
function generatePolicy() public {
|
|
||||||
require(deposited, "Compensation has not been deposited"); // 赔偿金额未存入,无法生成保单
|
|
||||||
require(msg.sender == platformS, "Only platform S can generate policy"); // 只有平台S可以生成保单
|
|
||||||
require(!policy[msg.sender], "Policy has already been generated"); // 该用户已生成保单
|
|
||||||
policy[msg.sender] = true; // 标记该用户已生成保单
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
@tab FlightManagement.sol
|
|
||||||
```solidity
|
|
||||||
pragma solidity ^0.6.10;
|
|
||||||
|
|
||||||
contract FlightManagement {
|
|
||||||
|
|
||||||
// 航班结构体
|
|
||||||
struct Flight {
|
|
||||||
string flightNumber; // 航班号
|
|
||||||
uint256 scheduledDepartureTime; // 计划起飞时间
|
|
||||||
uint256 actualDepartureTime; // 实际起飞时间
|
|
||||||
uint256 arrivalTime; // 到达时间
|
|
||||||
bool delayed; // 是否延误
|
|
||||||
}
|
|
||||||
|
|
||||||
// 航班号到航班信息的映射
|
|
||||||
mapping(string => Flight) flights;
|
|
||||||
|
|
||||||
// 添加航班
|
|
||||||
function addFlight(string memory _flightNumber, uint256 _scheduledDepartureTime, uint256 _arrivalTime) public {
|
|
||||||
flights[_flightNumber] = Flight(_flightNumber, _scheduledDepartureTime, 0, _arrivalTime, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新实际起飞时间
|
|
||||||
function updateActualDepartureTime(string memory _flightNumber, uint256 _actualDepartureTime) public {
|
|
||||||
flights[_flightNumber].actualDepartureTime = _actualDepartureTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*********** 获取航班信息接口开发 **********/
|
|
||||||
//任务2-2-2,请编写合约代码
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/********** 获取航班信息接口开发 ***********/
|
|
||||||
|
|
||||||
/*********** 编写判断航班是否延误接口开发 **********/
|
|
||||||
//任务2-2-2,请编写合约代码
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/********** 编写判断航班是否延误接口开发 ***********/
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
:::
|
|
||||||
## 题目八
|
|
||||||
|
|
||||||
题目:
|
|
||||||
子任务2-2-1:信息管理合约编码
|
|
||||||
1. 编写检索个人信息接口,完成患者通过身份证号检索其姓名、性别、年龄的功能;
|
|
||||||
2. 编写信息管理接口,完成允许患者与医院和科室进行信息管理,通过身份证号检索到患者的个人信息,将预约信息显示给患者,并发送到患者的账户地址中的功能;
|
|
||||||
子任务2-2-2:病历管理合约编码
|
|
||||||
1. 编写新建病历接口,实现检索病人对应科室既往病历,授权医生查看,如果没有既往病历则创建一个新的病历功能;
|
|
||||||
2. 编写结束就诊接口,实现检查病历是否已经填写,并结束病历咨询的功能。
|
|
||||||
子任务2-2-3:病历查看合约编码
|
|
||||||
根据需求用例文档在待补充源码中完成病历查看合约的编码,解决代码错误和警告,正确编译合约,功能调试正确,运行合约中的检查退款请求状态、自动批准退款请求接口功能。
|
|
||||||
1.编写查看病人个人信息接口,实现获取指定病人个人信息功能;
|
|
||||||
2.编写查看病人病情描述接口,实现获取指定病人病情描述功能。
|
|
||||||
::: code-tabs
|
|
||||||
@tab xxx.sol
|
|
||||||
```
|
|
||||||
```
|
|
||||||
:::
|
|
||||||
## 题目九
|
|
||||||
|
|
||||||
题目:
|
|
||||||
|
|
||||||
子任务2-2-1:合同管理合约编码
|
|
||||||
|
|
||||||
(1)编写房东签署合同接口,完成本合同位置只允许房东签署,通过合同中的信息生成租赁合同的链上哈希,触发协议签署合同的功能,其中合同中的信息包括房东链上账户、租客链上账户、租赁开始时间、租赁结束时间、月租金额、押金金额、交租时间;
|
|
||||||
|
|
||||||
(2)编写租金支付接口,完成只允许租客支付租金的规则,检查支付的租金金额是否正确,触发记录租金支付情况的功能。
|
|
||||||
|
|
||||||
子任务2-2-2:违约管理合约编码
|
|
||||||
|
|
||||||
(1)编写房东终止合同接口,实现房东终止合同判断,如果租客已经终止合同则合同无效,如果合同有效,对合同终止状态进行标记,将剩余押金退还给租客的功能;
|
|
||||||
|
|
||||||
(2)编写租客终止合同接口,实现租客终止合同判断,如果房东已经终止合同则合同无效,如果合同有效,对合同终止状态进行标记,将剩余押金退还给房东的功能。
|
|
||||||
|
|
||||||
子任务2-2-3:押金管理合约编码
|
|
||||||
|
|
||||||
根据需求用例文档在待补充源码中完成押金管理合约的编码,解决代码错误和警告,正确编译合约,功能调试正确,运行合约中的租客缴纳押金情况查询、房东收取押金情况查询接口功能。
|
|
||||||
|
|
||||||
(1)编写租客缴纳押金情况查询接口,实现查询租客是狗已缴纳押金功能;
|
|
||||||
|
|
||||||
(2)编写房东收取押金情况查询接口,实现房东是否已收到押金的功能。
|
|
||||||
|
|
||||||
::: code-tabs
|
|
||||||
@tab xxx.sol
|
|
||||||
```
|
|
||||||
```
|
|
||||||
:::
|
|
||||||
## 题目十
|
|
||||||
|
|
||||||
题目:
|
|
||||||
|
|
||||||
子任务2-2-1:账户管理合约编码
|
|
||||||
|
|
||||||
根据需求用例文档在待补充源码中完成账户管理合约的编码,解决代码错误和警告,正确编译合约,功能调试正确。需要编写生成账户接口,完成从外部部门检索姓名、年龄、雇主、开始日期、工资、缴费基数,将人员信息进行综合存储功能。
|
|
||||||
|
|
||||||
子任务2-2-2:费用管理合约编码
|
|
||||||
|
|
||||||
根据需求用例文档在待补充源码中完成费用管理合约的编码,解决代码错误和警告,正确编译合约,功能调试正确,运行添加新职工账户、添加新雇主账户。
|
|
||||||
|
|
||||||
1.编写添加新职工账户接口,实现当账户不存在,只有管理员可以添加职工账户,职工新账户中账户余额为0,未被赞助的功能;
|
|
||||||
|
|
||||||
2.编写添加新雇主账户接口,实现当账户不存在,只有管理员可以添加雇主账户,雇主新账户中账户余额为0,已被赞助的功能;
|
|
||||||
|
|
||||||
子任务2-2-3:保险转移合约编码
|
|
||||||
|
|
||||||
根据需求用例文档在待补充源码中完成保险转移合约的编码,解决代码错误和警告,正确编译合约,功能调试正确,运行合约中的申请转移关系、接收账户转移接口。
|
|
||||||
|
|
||||||
1.编写申请转移关系接口,实现创建申请、添加到申请列表功能,其中创建申请需要设置申请人地址、原城市、目标城市、停缴状态、批准状态;
|
|
||||||
|
|
||||||
2.编写接收账户转移接口,实现获取账户,进行账户授权状态、接收状态、个人账户基金、统筹账户基金、养老保险账户的信息设置;
|
|
||||||
|
|
||||||
::: code-tabs
|
|
||||||
@tab xxx.sol
|
|
||||||
```
|
|
||||||
```
|
|
||||||
:::
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
---
|
|
||||||
title: Hardhat 相关知识
|
|
||||||
createTime: 2025/10/14 14:57:06
|
|
||||||
permalink: /programming/solidity/other/hardhat/
|
|
||||||
---
|
|
||||||
|
|
||||||
## HardHat2 部署
|
|
||||||
### Node.js v16 安装
|
|
||||||
HardHat2 需要 Node.js v16 及以上的版本
|
|
||||||
这里给出的是通过 npm 来将旧版本升级到 Node.js v16 的,如果想要直接安装的话通过 `yum install nodejs` 或者 `apt install nodejs` 来即可。
|
|
||||||
```bash
|
|
||||||
npm config set registry https://registry.npmmirror.com
|
|
||||||
sudo npm install -g n
|
|
||||||
sudo n 16
|
|
||||||
```
|
|
||||||
安装完成之后可以查看版本,如果没有更新可以重新刷新一下环境(直接退出重连最简单)
|
|
||||||
```bash
|
|
||||||
node -v
|
|
||||||
# v16.20.2
|
|
||||||
npm -v
|
|
||||||
# 6.14.15
|
|
||||||
```
|
|
||||||
### 安装 Hardhat2
|
|
||||||
创建一个 hardhat2-project 目录,初始化 npm 项目,注意这里不要用 hardhat 作为项目名
|
|
||||||
```bash
|
|
||||||
mkdir ~/hardhat2-project
|
|
||||||
cd ~/hardhat2-project
|
|
||||||
npm init -y
|
|
||||||
```
|
|
||||||
安装 Hardhat 2
|
|
||||||
```bash
|
|
||||||
npm install --save-dev hardhat@^2.23.0
|
|
||||||
```
|
|
||||||
初始化 Hardhat2 项目
|
|
||||||
```bash
|
|
||||||
npx hardhat init
|
|
||||||
```
|
|
||||||
选择
|
|
||||||
```bash
|
|
||||||
✔ What do you want to do? · Create a JavaScript project
|
|
||||||
✔ Hardhat project root: · /root/hardhat2-project
|
|
||||||
✔ Do you want to add a .gitignore? (Y/n) · y
|
|
||||||
✔ Help us improve Hardhat with anonymous crash reports & basic usage data? (Y/n) · y
|
|
||||||
✔ Do you want to install this sample project's dependencies with npm (@nomicfoundation/hardhat-toolbox)? (Y/n) · y
|
|
||||||
```
|
|
||||||
## 使用 Hardhat2 编译测试合约
|
|
||||||
在 hardhat2-project 目录下,执行以下命令来编译合约
|
|
||||||
合约都会放在 `contracts` 目录下,这里我们选择了一个默认的合约,因此可以直接编译
|
|
||||||
```bash
|
|
||||||
# 测试编译
|
|
||||||
npx hardhat compile
|
|
||||||
# 运行测试
|
|
||||||
npx hardhat test
|
|
||||||
# 查看可用任务
|
|
||||||
npx hardhat help
|
|
||||||
```
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
---
|
|
||||||
title: 一些没分类的小知识
|
|
||||||
createTime: 2025/10/12 15:34:38
|
|
||||||
permalink: /programming/solidity/other/miscellaneous/
|
|
||||||
---
|
|
||||||
|
|
||||||
## 关于 memory 和 storage 存储类型
|
|
||||||
|
|
||||||
- `storage`:合约的持久化状态数据,保存在链上状态。对 `storage` 的写入最昂贵,读取也比内存贵;修改会永久生效。
|
|
||||||
- `memory`:函数调用期间的临时数据,函数返回后即释放。对 `memory` 的更改不会持久化。
|
|
||||||
- (补充)`calldata`:外部函数参数的只读数据位置,零拷贝、不可修改,用于节省 gas。
|
|
||||||
|
|
||||||
### 生命周期与成本
|
|
||||||
- `storage` 写入昂贵、读取较贵;适合保存需要长期存在的状态。
|
|
||||||
- `memory` 在函数结束时释放,读取/写入相对便宜;适合临时计算与返回值。
|
|
||||||
- 复杂引用类型(数组、`struct`、`mapping`、`string`、`bytes`)在函数参数或局部变量处通常必须显式标注数据位置。
|
|
||||||
|
|
||||||
### 默认与必须声明
|
|
||||||
- 状态变量总是位于 `storage`(例如 `User[] public users;`)。
|
|
||||||
- 外部函数(`external`)的复杂类型参数默认是 `calldata`;内部/公共函数需要显式标注 `memory` 或 `storage`。
|
|
||||||
- 局部变量的复杂类型必须指定数据位置,否则编译报错。
|
|
||||||
|
|
||||||
### 拷贝与引用语义
|
|
||||||
- 从 `storage` 读取到 `memory` 会“复制”数据;修改 `memory` 副本不影响原始 `storage`。
|
|
||||||
- 使用 `storage` 局部变量可以得到对状态数据的“引用”,对其赋值会持久化。
|
|
||||||
|
|
||||||
```solidity
|
|
||||||
pragma solidity ^0.8.20;
|
|
||||||
|
|
||||||
contract Users {
|
|
||||||
struct User { string name; uint age; }
|
|
||||||
User[] public users;
|
|
||||||
|
|
||||||
function add(string memory name, uint age) external {
|
|
||||||
users.push(User(name, age)); // 写入 storage
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateName(uint i, string memory newName) external {
|
|
||||||
User storage u = users[i]; // storage 引用(指向链上状态)
|
|
||||||
u.name = newName; // 修改持久化生效
|
|
||||||
}
|
|
||||||
|
|
||||||
function tryUpdate(uint i) external {
|
|
||||||
User memory u = users[i]; // 从 storage 复制到 memory
|
|
||||||
u.age = 99; // 仅修改副本,不会影响链上状态
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 在内部函数传递 storage 引用
|
|
||||||
- 仅内部/私有函数可以接收 `storage` 引用参数,从而直接修改状态;外部函数参数不能是 `storage`。
|
|
||||||
|
|
||||||
```solidity
|
|
||||||
pragma solidity ^0.8.20;
|
|
||||||
|
|
||||||
contract Users2 {
|
|
||||||
struct User { string name; uint age; }
|
|
||||||
User[] public users;
|
|
||||||
|
|
||||||
function _inc(User storage u) internal { u.age += 1; }
|
|
||||||
|
|
||||||
function birthday(uint i) external {
|
|
||||||
_inc(users[i]); // 传递 storage 引用,持久化修改
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 动态 memory 数组与限制
|
|
||||||
- 可在 `memory` 中构造动态数组:`new uint[](n)`;适合作为返回值或临时计算。
|
|
||||||
- `mapping` 只能存在于 `storage`,不能在 `memory` 中创建或拷贝。
|
|
||||||
|
|
||||||
```solidity
|
|
||||||
pragma solidity ^0.8.20;
|
|
||||||
|
|
||||||
contract Arrays {
|
|
||||||
function make(uint n) external pure returns (uint[] memory a) {
|
|
||||||
a = new uint[](n);
|
|
||||||
for (uint i = 0; i < n; i++) a[i] = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 常见坑与实践建议
|
|
||||||
- 给 `storage` 变量整体赋值会进行深拷贝或引用变更(依据类型),要明确拷贝成本与语义。
|
|
||||||
- 修改 `memory` 副本不会持久化;要修改链上状态请使用 `storage` 引用。
|
|
||||||
- 大型 `string/bytes/数组` 在 `memory↔storage` 间复制成本高,尽量减少不必要的复制。
|
|
||||||
- 外部函数能用 `calldata` 的地方尽量使用(只读参数),节省 gas。
|
|
||||||
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
---
|
|
||||||
title: Web 概览与学习路径
|
|
||||||
permalink: /programming/web/
|
|
||||||
createTime: 2025/10/28 22:20:00
|
|
||||||
---
|
|
||||||
|
|
||||||
# Web 是什么?
|
|
||||||
|
|
||||||
Web(万维网)是一个基于浏览器与服务器的“信息与应用平台”。用户通过浏览器访问网站,浏览器通过 **HTTP/HTTPS** 与服务器交互,服务器返回页面或数据,最终在浏览器中呈现并产生交互。
|
|
||||||
|
|
||||||
- 浏览器:渲染页面(HTML/CSS/JS)、执行脚本、发起网络请求。
|
|
||||||
- 服务器:处理业务逻辑、读写数据库、对外提供 API。
|
|
||||||
- 数据库:持久化存储(MySQL/PostgreSQL/MongoDB 等)。
|
|
||||||
|
|
||||||
简而言之:浏览器负责“看与用”,服务器负责“算与存”。
|
|
||||||
|
|
||||||
## 前端基础概念
|
|
||||||
|
|
||||||
- HTML:结构与语义(页面“骨架”)。
|
|
||||||
- CSS:表现与布局(页面“外观”)。
|
|
||||||
- JavaScript:交互与逻辑(页面“大脑”)。
|
|
||||||
- 现代前端:模块化、构建工具、组件化框架(Vue/React/Svelte 等)。
|
|
||||||
|
|
||||||
入门建议:先掌握原生 HTML/CSS/JS,再学习框架。你可以从本站文章开始:
|
|
||||||
- 基础教程 → `/programming/web/basic-syntax/html-css-js/`
|
|
||||||
|
|
||||||
## 后端基础概念
|
|
||||||
|
|
||||||
- 服务器与框架:Node.js(Express/NestJS)、Python(Flask/Django/FastAPI)、Java(Spring Boot)、Go(Gin/Fiber)。
|
|
||||||
- API 风格:REST(常见)、GraphQL(灵活查询)。
|
|
||||||
- 数据库与 ORM:MySQL/PostgreSQL(关系型)、MongoDB(文档型);ORM 如 Prisma/TypeORM/SQLAlchemy。
|
|
||||||
- 常见能力:认证授权(Session/JWT/OAuth)、文件上传、任务队列、缓存、日志与监控。
|
|
||||||
|
|
||||||
## 前后端如何协作
|
|
||||||
|
|
||||||
- 约定接口:路径、方法(GET/POST/PUT/DELETE)、参数与返回 JSON。
|
|
||||||
- 跨域与安全:CORS、CSRF/XSS/SQL 注入防护、HTTPS。
|
|
||||||
- 开发流程:
|
|
||||||
1) 需求与原型 → 2) API 设计 → 3) 前端页面与交互 → 4) 后端实现与测试 → 5) 联调与验收 → 6) 部署与监控。
|
|
||||||
|
|
||||||
## 学习路径(从 0 到 1)
|
|
||||||
|
|
||||||
1. 基础三件套:HTML + CSS + JavaScript(建议用 VSCode + Live Server)。
|
|
||||||
2. 工具与方法:Git/GitHub、浏览器开发者工具、HTTP/REST、请求调试(Postman/Insomnia)。
|
|
||||||
3. 进阶前端:布局(Flex/Grid)、响应式、ES6+、模块化、打包与构建(Vite/Webpack)。
|
|
||||||
4. 后端入门:选择一种语言与框架(如 Node.js + Express),完成 CRUD 与认证。
|
|
||||||
5. 数据库:会建表、会写基本查询;理解事务与索引。
|
|
||||||
6. 部署与上线:Nginx 反向代理、Docker、环境变量、日志与监控。
|
|
||||||
|
|
||||||
建议开发一个“待办清单 + 登录 + 数据持久化”的完整小项目,贯穿前后端与部署。
|
|
||||||
|
|
||||||
## 推荐学习资源
|
|
||||||
|
|
||||||
通用与入门:
|
|
||||||
- [MDN Web Docs(HTML/CSS/JS 全面权威)](https://developer.mozilla.org/)
|
|
||||||
- [freeCodeCamp(系统课程与练习)](https://www.freecodecamp.org/)
|
|
||||||
- [Roadmap.sh(前端/后端学习路径图)](https://roadmap.sh/)
|
|
||||||
|
|
||||||
前端:
|
|
||||||
- [HTML/CSS 基础(MDN)](https://developer.mozilla.org/en-US/docs/Learn)
|
|
||||||
- [CSS-Tricks(样式与布局技巧)](https://css-tricks.com/)
|
|
||||||
- [JavaScript 基础与进阶(MDN)](https://developer.mozilla.org/en-US/docs/Web/JavaScript)
|
|
||||||
- [Vue 官方文档](https://vuejs.org/)
|
|
||||||
|
|
||||||
后端:
|
|
||||||
- [Node.js 文档](https://nodejs.org/en/docs)
|
|
||||||
- [Express](https://expressjs.com/)
|
|
||||||
- [NestJS](https://docs.nestjs.com/)
|
|
||||||
- [Spring Boot](https://spring.io/projects/spring-boot)
|
|
||||||
- [Django](https://docs.djangoproject.com/)
|
|
||||||
- [Flask](https://flask.palletsprojects.com/)
|
|
||||||
- [FastAPI](https://fastapi.tiangolo.com/)
|
|
||||||
|
|
||||||
数据库与 ORM:
|
|
||||||
- [MySQL](https://dev.mysql.com/doc/)
|
|
||||||
- [PostgreSQL](https://www.postgresql.org/docs/)
|
|
||||||
- [MongoDB](https://www.mongodb.com/docs/)
|
|
||||||
- [Prisma(Node.js ORM)](https://www.prisma.io/docs)
|
|
||||||
- [TypeORM](https://typeorm.io/)
|
|
||||||
|
|
||||||
工具:
|
|
||||||
- [VSCode](https://code.visualstudio.com/)
|
|
||||||
- [Postman](https://www.postman.com/)
|
|
||||||
- [Insomnia](https://insomnia.rest/)
|
|
||||||
- [Docker](https://docs.docker.com/)
|
|
||||||
|
|
||||||
## 本站相关内容
|
|
||||||
|
|
||||||
- Web 前端基础讲解 → `/programming/web/basic-syntax/html-css-js/`
|
|
||||||
- 后续将补充:前端工程化、接口联调、部署与运维实战等专题。
|
|
||||||
@@ -1,535 +0,0 @@
|
|||||||
---
|
|
||||||
title: Web 前端基础讲解
|
|
||||||
createTime: 2025/10/28 21:23:48
|
|
||||||
permalink: /programming/web/basic-syntax/html-css-js/
|
|
||||||
---
|
|
||||||
|
|
||||||
## 前端开发到底是什么?
|
|
||||||
|
|
||||||
前端开发,简单来说,就是**你能在网页上看到和互动的一切**。
|
|
||||||
|
|
||||||
想象一下你正在看一个网站:
|
|
||||||
|
|
||||||
* **文字、图片、按钮、菜单**:这些都是前端开发人员用代码“画”出来的。
|
|
||||||
* **点击按钮会发生什么、输入框能打字、图片能放大缩小**:这些互动效果也是前端开发人员用代码实现的。
|
|
||||||
|
|
||||||
所以,前端开发就是负责把设计师画好的网站样子,用代码变成用户可以在浏览器里真实看到、摸到、用的东西。它主要涉及三种技术:
|
|
||||||
|
|
||||||
1. **HTML**:就像网页的“骨架”,决定了网页上有什么内容(标题、段落、图片等)。
|
|
||||||
2. **CSS**:就像网页的“衣服”,决定了网页看起来怎么样(颜色、字体、布局等)。
|
|
||||||
3. **JavaScript**:就像网页的“大脑”,决定了网页能做什么(动画、交互、数据处理等)。
|
|
||||||
|
|
||||||
总而言之,前端开发就是让网站变得好看、好用、能互动。
|
|
||||||
|
|
||||||
## 开发工具
|
|
||||||
|
|
||||||
进行前端开发,我们需要一个好用的“工具箱”。其中,**Visual Studio Code (VS Code)** 是一个非常受欢迎且功能强大的选择。
|
|
||||||
|
|
||||||
VsCode 官网:[https://code.visualstudio.com/](https://code.visualstudio.com/)
|
|
||||||
|
|
||||||
**为什么推荐 VS Code?**
|
|
||||||
|
|
||||||
* **免费且开源**:你可以免费使用它,并且它的代码是公开的,有很多人一起维护和改进。
|
|
||||||
* **轻量级但功能强大**:它启动速度快,占用资源少,但通过安装各种“插件”,可以实现非常多的功能,比如代码高亮、智能提示、调试、版本控制等等。
|
|
||||||
* **支持多种语言**:不仅对 HTML、CSS、JavaScript 有很好的支持,也支持其他很多编程语言。
|
|
||||||
* **丰富的生态系统**:有大量的插件可以帮助你提高开发效率,比如 Live Server(实时预览网页)、Prettier(代码格式化)等。
|
|
||||||
* **跨平台**:无论你使用 Windows、macOS 还是 Linux,都可以安装和使用 VS Code。
|
|
||||||
|
|
||||||
### VsCode 前端开发插件推荐
|
|
||||||
- **Chinese (Simplified) Language Pack for Visual Studio Code**:中文语言包,方便中文用户使用。
|
|
||||||
- **Live Server**:实时预览网页,修改代码后自动刷新。
|
|
||||||
- **Prettier**:代码格式化,保持代码风格统一。
|
|
||||||
- **HTML CSS Support**:HTML 和 CSS 代码智能提示。
|
|
||||||
|
|
||||||
参考文章:
|
|
||||||
1. [VSCode安装配置使用教程(最新版超详细保姆级含插件)一文就够了](https://blog.csdn.net/2303_82176667/article/details/137193809)
|
|
||||||
2. [快速上手web前端开发(超详细教程)](https://blog.csdn.net/2303_82176667/article/details/137193809)
|
|
||||||
|
|
||||||
## HTML5 基础知识讲解
|
|
||||||
|
|
||||||
HTML 是一种用来描述网页的语言,用于描述网页的结构和内容。
|
|
||||||
|
|
||||||
HTML 并不是一种编程语言,而是一种标记语言。它使用标签来描述网页上的不同元素,比如标题、段落、图片、链接等。
|
|
||||||
|
|
||||||
标签指的是由尖括号 `<` 和 `>` 包围的关键词,通常成对出现,用来表示元素的开始和结束。
|
|
||||||
|
|
||||||
一个简单的 HTML 实例,用于初步的了解效果:
|
|
||||||
```html title='index.html'
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>仲夏夜之梦(notes.simengweb.com)</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>我的第一个标题</h1>
|
|
||||||
<p>我的第一个段落。</p>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
```
|
|
||||||
|
|
||||||
小提示:可以将代码复制到 VsCode 中,然后使用 Live Server 插件就可以实时预览效果啦。
|
|
||||||
|
|
||||||
### HTML5 文档分析
|
|
||||||
我们以前面的样例代码为例,来对 HTML 文档进行讲解
|
|
||||||
|
|
||||||
第一行:`<!DOCTYPE html>`,这是一个文档类型声明,用于告诉浏览器这是一个 HTML5 文档。
|
|
||||||
|
|
||||||
第二行:`<html>`,这是 HTML 文档的根元素,所有其他元素都必须嵌套在这个元素中,`<html>` 和 `</html>` 之间的内容就是文档的主体。
|
|
||||||
|
|
||||||
第三行到第六行:`<head>`,这是文档的头部元素,用于包含文档的元数据(比如标题、字符编码、外部资源引用等)。
|
|
||||||
|
|
||||||
第四行:`<meta charset="utf-8">`,这是一个元数据标签,用于指定文档的字符编码为 UTF-8。UTF-8 是一种常用的字符编码,能够表示世界上几乎所有的字符。如果不写的话可能会导致中文乱码的问题。
|
|
||||||
|
|
||||||
第五行:`<title>仲夏夜之梦(notes.simengweb.com)</title>`,这是文档的标题,会显示在浏览器的标题栏或标签页上。
|
|
||||||
|
|
||||||
第七行:`<body>`,这是文档的主体元素,用于包含文档的可见内容(比如文字、图片、按钮等),`<body>` 和 `</body>` 之间的内容就是文档的主体内容。
|
|
||||||
|
|
||||||
第八行:`<h1>我的第一个标题</h1>`,这是一个一级标题元素,用于表示文档的主要主题。`h1` 元素的内容会显示为较大的字体,通常用于页面的主标题。
|
|
||||||
|
|
||||||
第九行:`<p>我的第一个段落。</p>`,这是一个段落元素,用于表示文档中的一个段落。`p` 元素的内容会显示为普通的段落文本。
|
|
||||||
|
|
||||||
小提示:在浏览器中打开 F12 开启开发者工具,就可以查看和调试 HTML 文档啦。
|
|
||||||
|
|
||||||
## CSS3 基础知识讲解
|
|
||||||
|
|
||||||
CSS 是一种用来描述 HTML 文档“长什么样”的语言,专门负责网页的样式、排版与视觉效果。
|
|
||||||
|
|
||||||
CSS 也不是编程语言,而是一种“样式表”语言。它通过“选择器”找到 HTML 元素,再给它挂上一条或多条“声明”,告诉浏览器“这个元素应该是什么颜色、多大字号、放在哪里”。
|
|
||||||
|
|
||||||
一条 CSS 声明由“属性”和“值”组成,用冒号分隔,以分号结尾;多条声明放在花括号里,就组成了一条“样式规则”。
|
|
||||||
|
|
||||||
一个最简单的 CSS 示例,让页面里的标题变成红色、居中显示:
|
|
||||||
::: code-tabs
|
|
||||||
@tab index.html
|
|
||||||
```html
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>仲夏夜之梦(notes.simengweb.com)</title>
|
|
||||||
<link rel="stylesheet" href="style.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>我的第一个标题</h1>
|
|
||||||
<p>我的第一个段落。</p>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
```
|
|
||||||
@tab style.css
|
|
||||||
```css
|
|
||||||
body {
|
|
||||||
background-color:#d0e4fe;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
color:orange;
|
|
||||||
text-align:center;
|
|
||||||
}
|
|
||||||
p {
|
|
||||||
font-family:"Times New Roman";
|
|
||||||
font-size:20px;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
:::
|
|
||||||
这里的 index.html 中需要添加一条语句,用于引入 CSS 样式表。
|
|
||||||
```html
|
|
||||||
<link rel="stylesheet" href="style.css">
|
|
||||||
```
|
|
||||||
然后通过浏览器打开 index.html 就可以看到效果啦。
|
|
||||||
|
|
||||||
### CSS 文档分析
|
|
||||||
|
|
||||||
我们以前面的样例代码为例,来对 CSS 文档进行讲解
|
|
||||||
|
|
||||||
第一到三行:`body { background-color:#d0e4fe; }`,这是一条针对 `<body>` 元素的样式规则。
|
|
||||||
- `body` 是“选择器”,表示要把样式应用到整个网页主体。
|
|
||||||
- `background-color` 是属性,用来设置背景颜色;`#d0e4fe` 是一个淡蓝色色值,因此整个页面会呈现淡蓝色背景。
|
|
||||||
|
|
||||||
第四行到第七行:`h1 { color:orange; text-align:center; }`,这是一条针对 `<h1>` 元素的样式规则。
|
|
||||||
- `h1` 是“选择器”,对应 HTML 中的一级标题。
|
|
||||||
- `color:orange;` 把标题文字设为橙色。
|
|
||||||
- `text-align:center;` 让标题在水平方向上居中显示。
|
|
||||||
|
|
||||||
第八行到第十一行:`p { font-family:"Times New Roman"; font-size:20px; }`,这是一条针对 `<p>` 元素的样式规则。
|
|
||||||
- `p` 是“选择器”,对应 HTML 中的段落。
|
|
||||||
- `font-family:"Times New Roman";` 指定段落使用 Times New Roman 字体。
|
|
||||||
- `font-size:20px;` 把段落文字大小设为 20 像素,让文字看起来更大、更清晰。
|
|
||||||
|
|
||||||
小提示:在 VsCode 中安装“Live Server”插件后,修改并保存 CSS 文件,浏览器会自动刷新,立即看到样式变化,方便调试。
|
|
||||||
|
|
||||||
## JavaScript 基础知识讲解
|
|
||||||
|
|
||||||
JavaScript(简称 JS)是让网页“动起来、能互动”的语言。它可以:
|
|
||||||
- 响应用户操作(点击、输入、滚动等)
|
|
||||||
- 修改页面内容与样式(增删节点、改文字/颜色)
|
|
||||||
- 与服务器通信、处理数据与状态
|
|
||||||
- 实现动画与复杂交互逻辑
|
|
||||||
|
|
||||||
下面用一个小示例,演示 JS 如何让页面产生交互效果:
|
|
||||||
::: code-tabs
|
|
||||||
@tab index.html
|
|
||||||
```html
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>仲夏夜之梦(notes.simengweb.com)</title>
|
|
||||||
<link rel="stylesheet" href="style.css">
|
|
||||||
<!-- 样式写在外部 CSS 中 -->
|
|
||||||
<!-- JavaScript 推荐放在 body 末尾,或使用 defer 属性 -->
|
|
||||||
<!-- 这里在 body 末尾通过 src 引入外部脚本 -->
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>我的第一个标题</h1>
|
|
||||||
<p id="message">我的第一个段落。</p>
|
|
||||||
|
|
||||||
<button id="btn">点我试试</button>
|
|
||||||
<input id="name" placeholder="输入你的名字" />
|
|
||||||
|
|
||||||
<script src="script.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
```
|
|
||||||
@tab script.js
|
|
||||||
```js
|
|
||||||
// 等页面元素加载完成后再执行(避免找不到节点)
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
|
||||||
const msg = document.getElementById('message'); // 选择段落节点
|
|
||||||
const btn = document.getElementById('btn'); // 选择按钮
|
|
||||||
const nameInput = document.getElementById('name'); // 选择输入框
|
|
||||||
|
|
||||||
// 点击按钮,修改段落文本与颜色
|
|
||||||
btn.addEventListener('click', () => {
|
|
||||||
msg.textContent = '你点击了按钮!';
|
|
||||||
msg.style.color = 'green';
|
|
||||||
console.log('按钮被点击'); // 打开浏览器控制台可见
|
|
||||||
});
|
|
||||||
|
|
||||||
// 输入时,实时更新欢迎语
|
|
||||||
nameInput.addEventListener('input', (e) => {
|
|
||||||
const name = e.target.value.trim();
|
|
||||||
msg.textContent = name ? `你好,${name}!` : '我的第一个段落。';
|
|
||||||
});
|
|
||||||
});
|
|
||||||
```
|
|
||||||
:::
|
|
||||||
|
|
||||||
### JavaScript 文档分析
|
|
||||||
我们以前面的样例代码为例,来对 JS 的关键点进行讲解:
|
|
||||||
|
|
||||||
- `<script src="script.js"></script>`:在 HTML 中通过 `script` 标签引入外部 JS 文件。常见放置位置是 `</body>` 之前,避免脚本执行时页面元素尚未加载。
|
|
||||||
- `document.addEventListener('DOMContentLoaded', ...)`:等待文档结构加载完成再运行 JS,确保能获取到页面元素。
|
|
||||||
- `document.getElementById('...')`:选择页面中的元素节点,便于后续读写或绑定事件。
|
|
||||||
- `addEventListener('click'/'input', ...)`:给元素绑定事件监听器,描述“发生什么事时要做什么”。
|
|
||||||
- `textContent / style`:修改文本与内联样式,从而即时改变页面显示效果。
|
|
||||||
- `console.log(...)`:在开发者工具控制台输出调试信息,便于观察程序执行。
|
|
||||||
|
|
||||||
### 小提示
|
|
||||||
- 打开浏览器开发者工具(F12),切到“Console”能查看 `console.log` 输出;在 “Elements”/“Inspector” 面板能查看并调试 DOM 结构与样式。
|
|
||||||
- 在 VSCode 中使用 “Live Server”,保存 `script.js` 后浏览器会自动刷新,方便快速迭代。
|
|
||||||
- 现代 JS 推荐使用 `const` 与 `let` 替代 `var`;多用箭头函数与模板字符串(如 `` `你好,${name}` ``)让代码更简洁。
|
|
||||||
|
|
||||||
到这里,我们已经分别了解了 HTML(结构)、CSS(样式)与 JavaScript(交互)三者如何协同工作。接下来可以尝试把它们组合起来,做一个小页面练手。
|
|
||||||
|
|
||||||
## 动手练习:小作业(只改 HTML/CSS,JS 已提供)
|
|
||||||
|
|
||||||
目标:做一个“欢迎卡片”,包含标题、说明文字、输入框与按钮。
|
|
||||||
- 点击按钮时切换页面主题(浅色/深色),JS 会自动完成;
|
|
||||||
- 输入名字时实时显示欢迎语;
|
|
||||||
- 你只需要修改 HTML 和 CSS 来达到视觉与布局效果。
|
|
||||||
|
|
||||||
### 作业要求(验收标准)
|
|
||||||
- 卡片居中显示,具有圆角、阴影与内边距;
|
|
||||||
- 标题居中,段落有合适的行高与间距;
|
|
||||||
- 按钮有悬停(hover)与按下(active)的视觉反馈;
|
|
||||||
- 输入框获得焦点(focus)时有明显强调样式;
|
|
||||||
- 在移动端(窄屏)不溢出,布局能自适应;
|
|
||||||
- 深色主题下文字可读、对比度合理。提示:JS 会在 `<body>` 上切换 `theme-dark` 类,你可以在 CSS 中基于该类进行主题覆盖。
|
|
||||||
|
|
||||||
### 起步模板
|
|
||||||
将以下三份代码保存为同目录下的 `index.html`、`style.css` 与 `script.js`,用 VSCode 的 Live Server 打开 `index.html` 进行预览。完成作业时,请只修改 HTML 和 CSS(保留 JS 的 id 与类名依赖)。
|
|
||||||
|
|
||||||
::: code-tabs
|
|
||||||
@tab index.html
|
|
||||||
```html
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="zh-CN">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>小作业:欢迎卡片</title>
|
|
||||||
<link rel="stylesheet" href="style.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="page">
|
|
||||||
<div class="card" id="card">
|
|
||||||
<h1 class="card__title">欢迎来到我的页面</h1>
|
|
||||||
<p class="card__text" id="message">请点击按钮或输入你的名字</p>
|
|
||||||
<div class="card__actions">
|
|
||||||
<input id="name" class="input" placeholder="输入你的名字" />
|
|
||||||
<button id="btn" class="btn btn--primary">点我切换主题</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<script src="script.js" defer></script>
|
|
||||||
<!-- 注意:JS 依赖 #message、#btn、#name 这几个 id,请不要改动它们的命名 -->
|
|
||||||
<!-- 你可以新增或调整 class 名,或增删结构,来实现更好的视觉与布局 -->
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
```
|
|
||||||
@tab style.css
|
|
||||||
```css
|
|
||||||
/* 目标:只通过 CSS 实现卡片的视觉与响应式,并支持深色主题 */
|
|
||||||
/* 建议:使用 CSS 变量管理主题;在 .theme-dark 上覆盖变量 */
|
|
||||||
|
|
||||||
:root {
|
|
||||||
--bg: #f7f9fc;
|
|
||||||
--text: #222;
|
|
||||||
--primary: #ff7a45; /* 可调整为你的品牌色 */
|
|
||||||
--card-bg: #fff;
|
|
||||||
--card-shadow: 0 10px 20px rgba(0,0,0,0.08);
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
background: var(--bg);
|
|
||||||
color: var(--text);
|
|
||||||
font-family: system-ui, -apple-system, Segoe UI, Roboto, 'Helvetica Neue', Arial;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page {
|
|
||||||
min-height: 100vh;
|
|
||||||
display: grid;
|
|
||||||
place-items: center;
|
|
||||||
padding: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
width: min(640px, 100%);
|
|
||||||
background: var(--card-bg);
|
|
||||||
border-radius: 16px;
|
|
||||||
box-shadow: var(--card-shadow);
|
|
||||||
padding: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card__title {
|
|
||||||
margin: 0 0 12px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card__text {
|
|
||||||
margin: 0 0 16px;
|
|
||||||
line-height: 1.6;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card__actions {
|
|
||||||
display: flex;
|
|
||||||
gap: 12px;
|
|
||||||
justify-content: center;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input {
|
|
||||||
padding: 10px 12px;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
border-radius: 8px;
|
|
||||||
outline: none;
|
|
||||||
min-width: 220px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input:focus {
|
|
||||||
border-color: var(--primary);
|
|
||||||
box-shadow: 0 0 0 3px rgba(255, 122, 69, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
padding: 10px 16px;
|
|
||||||
border: none;
|
|
||||||
border-radius: 999px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: transform .08s ease, box-shadow .2s ease, background-color .2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn--primary {
|
|
||||||
background: var(--primary);
|
|
||||||
color: #fff;
|
|
||||||
box-shadow: 0 6px 12px rgba(255, 122, 69, .3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn--primary:hover {
|
|
||||||
transform: translateY(-1px);
|
|
||||||
box-shadow: 0 8px 16px rgba(255, 122, 69, .4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn--primary:active {
|
|
||||||
transform: translateY(0);
|
|
||||||
box-shadow: 0 4px 8px rgba(255, 122, 69, .3);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 任务:为深色主题定义样式。JS 会在 <body> 上切换 `.theme-dark` 类。*/
|
|
||||||
body.theme-dark {
|
|
||||||
--bg: #0f172a;
|
|
||||||
--text: #e5e7eb;
|
|
||||||
--primary: #60a5fa; /* 深色下可更柔和 */
|
|
||||||
--card-bg: #1f2937;
|
|
||||||
--card-shadow: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 可选:为移动端优化 */
|
|
||||||
@media (max-width: 480px) {
|
|
||||||
.card { padding: 18px; }
|
|
||||||
.input { min-width: 100%; }
|
|
||||||
}
|
|
||||||
```
|
|
||||||
@tab script.js
|
|
||||||
```js
|
|
||||||
// JS 已提供:负责交互逻辑,切换主题与显示欢迎语
|
|
||||||
// 你只需要改 HTML 和 CSS 即可达成作业要求
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
|
||||||
const msg = document.getElementById('message');
|
|
||||||
const btn = document.getElementById('btn');
|
|
||||||
const nameInput = document.getElementById('name');
|
|
||||||
|
|
||||||
// 切换深色主题(在 <body> 上切换 theme-dark 类)
|
|
||||||
btn.addEventListener('click', () => {
|
|
||||||
document.body.classList.toggle('theme-dark');
|
|
||||||
const dark = document.body.classList.contains('theme-dark');
|
|
||||||
msg.textContent = dark ? '深色主题已开启' : '浅色主题已开启';
|
|
||||||
});
|
|
||||||
|
|
||||||
// 实时欢迎语
|
|
||||||
nameInput.addEventListener('input', (e) => {
|
|
||||||
const name = e.target.value.trim();
|
|
||||||
msg.textContent = name ? `你好,${name}!` : '请点击按钮或输入你的名字';
|
|
||||||
});
|
|
||||||
});
|
|
||||||
```
|
|
||||||
:::
|
|
||||||
|
|
||||||
### 提示与加分项
|
|
||||||
- 可以用 `:root`+CSS 变量统一管理主题色,在 `.theme-dark` 中覆盖变量即可完成切换;
|
|
||||||
- 尝试为按钮增加平滑过渡与阴影层次感;
|
|
||||||
- 在移动端下让输入框占满一行,按钮在下一行显示;
|
|
||||||
- 可自行优化字体、字号与间距,让视觉更加舒适;
|
|
||||||
- 加分:实现卡片在深色主题下的微动效(例如背景色淡入)。
|
|
||||||
|
|
||||||
## 入门版(更简单,建议先做这个)
|
|
||||||
|
|
||||||
目标:做一个“欢迎卡片”,内容包括标题、说明文字、一个输入框和一个按钮。
|
|
||||||
- 点击按钮时高亮卡片(JS 已完成,你只需让 `.card.highlight` 在 CSS 里更显眼)。
|
|
||||||
- 输入名字时实时显示欢迎语(JS 已完成)。
|
|
||||||
- 你只需要修改 HTML 和 CSS,让页面好看、布局合理即可。
|
|
||||||
|
|
||||||
验收标准(入门)
|
|
||||||
- 卡片居中、圆角、阴影、内边距,整体观感舒适。
|
|
||||||
- 按钮有 `hover/active` 反馈,输入框 `focus` 有强调样式。
|
|
||||||
- 窄屏下不溢出,内容能自适应。
|
|
||||||
|
|
||||||
::: code-tabs
|
|
||||||
@tab index.html
|
|
||||||
```html
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="zh-CN">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>入门版小作业</title>
|
|
||||||
<link rel="stylesheet" href="style.css">
|
|
||||||
<!-- 保留以下 id:message / btn / name / card -->
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="page">
|
|
||||||
<div class="card" id="card">
|
|
||||||
<h1>欢迎</h1>
|
|
||||||
<p id="message">在输入框里写下你的名字</p>
|
|
||||||
<input id="name" placeholder="输入你的名字">
|
|
||||||
<button id="btn">高亮卡片</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<script src="script.js"></script>
|
|
||||||
<!-- 用 VSCode Live Server 打开 index.html 预览效果 -->
|
|
||||||
<!-- 完成作业时,请只修改 HTML 和 CSS -->
|
|
||||||
<!-- 你可以自由调整颜色、间距、字体与布局,使页面更美观 -->
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
```
|
|
||||||
@tab style.css
|
|
||||||
```css
|
|
||||||
/* 极简样式:你可以在此基础上美化与调整 */
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
background: #f5f7fa;
|
|
||||||
font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page {
|
|
||||||
min-height: 100vh;
|
|
||||||
display: grid;
|
|
||||||
place-items: center;
|
|
||||||
padding: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
width: min(360px, 100%);
|
|
||||||
background: #fff;
|
|
||||||
border-radius: 12px;
|
|
||||||
box-shadow: 0 6px 12px rgba(0,0,0,0.08);
|
|
||||||
padding: 16px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
input {
|
|
||||||
width: 100%;
|
|
||||||
padding: 8px 10px;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
border-radius: 8px;
|
|
||||||
outline: none;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
input:focus {
|
|
||||||
border-color: #1677ff;
|
|
||||||
box-shadow: 0 0 0 2px rgba(22,119,255,.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
padding: 8px 12px;
|
|
||||||
border: none;
|
|
||||||
border-radius: 8px;
|
|
||||||
background: #1677ff;
|
|
||||||
color: #fff;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:hover { filter: brightness(1.05); }
|
|
||||||
button:active { filter: brightness(0.95); }
|
|
||||||
|
|
||||||
/* JS 点击按钮时会在卡片上切换 .highlight 类 */
|
|
||||||
.card.highlight {
|
|
||||||
background: #fffbe6; /* 浅黄色高亮 */
|
|
||||||
box-shadow: 0 8px 16px rgba(0,0,0,0.10);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
@tab script.js
|
|
||||||
```js
|
|
||||||
// JS 已提供:负责简单交互。请只改 HTML/CSS。
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
|
||||||
const msg = document.getElementById('message');
|
|
||||||
const btn = document.getElementById('btn');
|
|
||||||
const nameInput = document.getElementById('name');
|
|
||||||
const card = document.getElementById('card');
|
|
||||||
|
|
||||||
// 实时欢迎语
|
|
||||||
nameInput.addEventListener('input', (e) => {
|
|
||||||
const name = e.target.value.trim();
|
|
||||||
msg.textContent = name ? `你好,${name}!` : '在输入框里写下你的名字';
|
|
||||||
});
|
|
||||||
|
|
||||||
// 切换卡片高亮
|
|
||||||
btn.addEventListener('click', () => {
|
|
||||||
card.classList.toggle('highlight');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
```
|
|
||||||
:::
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,468 +0,0 @@
|
|||||||
---
|
|
||||||
title: html 列表与语义布局
|
|
||||||
createTime: 2025/11/02 20:47:50
|
|
||||||
permalink: /programming/web/basic-syntax/html-lists-and-semantic-layout/
|
|
||||||
---
|
|
||||||
## 四、列表(UL/OL/LI)
|
|
||||||
|
|
||||||
无序列表(圆点):
|
|
||||||
```html title="index.html"
|
|
||||||
<ul>
|
|
||||||
<li>学习笔记</li>
|
|
||||||
<li>工具推荐</li>
|
|
||||||
<li>友情链接</li>
|
|
||||||
</ul>
|
|
||||||
```
|
|
||||||
|
|
||||||
有序列表(数字):
|
|
||||||
```html title="index.html"
|
|
||||||
<ol>
|
|
||||||
<li>需求与原型</li>
|
|
||||||
<li>API 设计</li>
|
|
||||||
<li>联调与验收</li>
|
|
||||||
</ol>
|
|
||||||
```
|
|
||||||
|
|
||||||
**小案例:展示清单与步骤**
|
|
||||||
::: code-tabs
|
|
||||||
@tab index.html
|
|
||||||
```html
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="zh-CN">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>列表案例</title>
|
|
||||||
<link rel="stylesheet" href="style.css">
|
|
||||||
<script defer src="app.js"></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h2>我的清单</h2>
|
|
||||||
<ul>
|
|
||||||
<li>学习笔记</li>
|
|
||||||
<li>工具推荐</li>
|
|
||||||
<li>友情链接</li>
|
|
||||||
</ul>
|
|
||||||
<h2>项目步骤</h2>
|
|
||||||
<ol>
|
|
||||||
<li>需求与原型</li>
|
|
||||||
<li>API 设计</li>
|
|
||||||
<li>联调与验收</li>
|
|
||||||
</ol>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
```
|
|
||||||
@tab style.css
|
|
||||||
```css
|
|
||||||
ul, ol { margin: .5rem 0 1rem; padding-left: 1.5rem; }
|
|
||||||
li { margin: .25rem 0; }
|
|
||||||
```
|
|
||||||
@tab app.js
|
|
||||||
```js
|
|
||||||
document.querySelectorAll('li').forEach(li => li.addEventListener('click', () => li.classList.toggle('done')));
|
|
||||||
```
|
|
||||||
:::
|
|
||||||
|
|
||||||
**小作业:做一个“今日任务清单”**
|
|
||||||
::: code-tabs
|
|
||||||
@tab index.html
|
|
||||||
```html
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="zh-CN">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>今日任务清单</title>
|
|
||||||
<link rel="stylesheet" href="style.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>今天要做的事</h1>
|
|
||||||
<ul>
|
|
||||||
<li>阅读 30 分钟</li>
|
|
||||||
<li>练习 1 个算法题</li>
|
|
||||||
<li>整理房间</li>
|
|
||||||
</ul>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
```
|
|
||||||
@tab style.css
|
|
||||||
```css
|
|
||||||
body { font-family: system-ui; margin: 2rem; }
|
|
||||||
li { cursor: pointer; }
|
|
||||||
.done { text-decoration: line-through; color: #888; }
|
|
||||||
```
|
|
||||||
:::
|
|
||||||
|
|
||||||
## 五、语义化布局(Header/Nav/Main/Section/Article/Aside/Footer)
|
|
||||||
|
|
||||||
语义化标签让结构更清晰,搜索引擎更友好:
|
|
||||||
```html title="index.html"
|
|
||||||
<header>
|
|
||||||
<h1>我的网站</h1>
|
|
||||||
<nav>
|
|
||||||
<a href="/blog/">博客</a>
|
|
||||||
<a href="/notes/">笔记</a>
|
|
||||||
</nav>
|
|
||||||
<!-- 注意:更复杂的导航通常配合 CSS/JS 做响应式菜单 -->
|
|
||||||
<!-- 小提示:块级容器的外层仍可使用 <div> 来做网格或栅格布局 -->
|
|
||||||
<!-- 例如 <div class="container"> 包裹全站 -->
|
|
||||||
<!-- 只是这些语义标签会让搜索引擎更理解结构 -->
|
|
||||||
<!-- 语义标签与 <div> 并不冲突,可以配合使用 -->
|
|
||||||
<!-- 选择语义标签能让你的 HTML 更“有含义”而非只是分区 -->
|
|
||||||
<!-- 如果仅用于布局、无明确语义,用 <div> 即可 -->
|
|
||||||
<!-- 这段说明旨在帮助你建立语义化与布局之间的直觉 -->
|
|
||||||
<!-- 逐步养成好的结构化写法 -->
|
|
||||||
<!-- 让内容更可维护,更易被机器理解 -->
|
|
||||||
<!-- (继续往下看,还会介绍 Article/Section 等) -->
|
|
||||||
```
|
|
||||||
|
|
||||||
> 语义化标签的要点:这些标签表达了“这是什么内容”,而不是“怎么显示”。更容易被搜索引擎和读屏工具理解。
|
|
||||||
|
|
||||||
**小案例:语义化布局入门**
|
|
||||||
::: code-tabs
|
|
||||||
@tab index.html
|
|
||||||
```html
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="zh-CN">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>语义化布局案例</title>
|
|
||||||
<link rel="stylesheet" href="style.css">
|
|
||||||
<script defer src="app.js"></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<header>
|
|
||||||
<h1>我的网站</h1>
|
|
||||||
<nav>
|
|
||||||
<a href="#home">首页</a>
|
|
||||||
<a href="#blog">博客</a>
|
|
||||||
</nav>
|
|
||||||
</header>
|
|
||||||
<main>
|
|
||||||
<section>
|
|
||||||
<h2>最新文章</h2>
|
|
||||||
<article>
|
|
||||||
<h3>用语义标签改造你的页面</h3>
|
|
||||||
<p>示例段落:用 header/nav/main/section/article/aside/footer 组织结构。</p>
|
|
||||||
</article>
|
|
||||||
</section>
|
|
||||||
<aside>
|
|
||||||
<h2>侧栏</h2>
|
|
||||||
<p>这里可以放导航、标签云或广告位。</p>
|
|
||||||
</aside>
|
|
||||||
</main>
|
|
||||||
<footer>© 2025 祀梦</footer>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
```
|
|
||||||
@tab style.css
|
|
||||||
```css
|
|
||||||
body { font-family: system-ui; margin: 0; }
|
|
||||||
header, footer { padding: 1rem 2rem; background: #f7f7f7; }
|
|
||||||
nav { display: flex; gap: 1rem; }
|
|
||||||
main { display: grid; grid-template-columns: 1fr 240px; gap: 1rem; padding: 1rem 2rem; }
|
|
||||||
article { background: #fff; padding: 1rem; border: 1px solid #eee; border-radius: 8px; }
|
|
||||||
aside { background: #fff; padding: 1rem; border: 1px solid #eee; border-radius: 8px; }
|
|
||||||
```
|
|
||||||
@tab app.js
|
|
||||||
```js
|
|
||||||
document.querySelectorAll('nav a').forEach(a => a.addEventListener('click', () => {
|
|
||||||
document.querySelectorAll('nav a').forEach(x => x.classList.remove('active'));
|
|
||||||
a.classList.add('active');
|
|
||||||
}));
|
|
||||||
```
|
|
||||||
:::
|
|
||||||
|
|
||||||
**小作业:用语义标签搭一个博客首页**
|
|
||||||
::: code-tabs
|
|
||||||
@tab index.html
|
|
||||||
```html
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="zh-CN">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>博客首页</title>
|
|
||||||
<link rel="stylesheet" href="style.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<header>
|
|
||||||
<h1>博客名称</h1>
|
|
||||||
<nav>
|
|
||||||
<a href="#">首页</a>
|
|
||||||
<a href="#">归档</a>
|
|
||||||
<a href="#">关于</a>
|
|
||||||
</nav>
|
|
||||||
</header>
|
|
||||||
<main>
|
|
||||||
<section>
|
|
||||||
<article>
|
|
||||||
<h2>第一篇文章标题</h2>
|
|
||||||
<p>摘要内容……</p>
|
|
||||||
</article>
|
|
||||||
<article>
|
|
||||||
<h2>第二篇文章标题</h2>
|
|
||||||
<p>摘要内容……</p>
|
|
||||||
</article>
|
|
||||||
</section>
|
|
||||||
<aside>
|
|
||||||
<h2>侧栏</h2>
|
|
||||||
<p>分类、标签或个人简介。</p>
|
|
||||||
</aside>
|
|
||||||
</main>
|
|
||||||
<footer>© 2025 你的名字</footer>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
```
|
|
||||||
@tab style.css
|
|
||||||
```css
|
|
||||||
/* 与案例类似的栅格布局样式 */
|
|
||||||
main { display: grid; grid-template-columns: 1fr 240px; gap: 1rem; padding: 1rem 2rem; }
|
|
||||||
article { background: #fff; padding: 1rem; border: 1px solid #eee; border-radius: 8px; }
|
|
||||||
aside { background: #fff; padding: 1rem; border: 1px solid #eee; border-radius: 8px; }
|
|
||||||
```
|
|
||||||
:::
|
|
||||||
|
|
||||||
## 六、表格(Table/TR/TH/TD)
|
|
||||||
|
|
||||||
```html title="index.html"
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>姓名</th>
|
|
||||||
<th>职业</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>祀梦</td>
|
|
||||||
<td>开发者</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
```
|
|
||||||
小提示:表格样式通常用 CSS 来美化(边框、间距、对齐等)。
|
|
||||||
|
|
||||||
**小案例:信息表格**
|
|
||||||
::: code-tabs
|
|
||||||
@tab index.html
|
|
||||||
```html
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="zh-CN">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>信息表格案例</title>
|
|
||||||
<link rel="stylesheet" href="style.css">
|
|
||||||
<script defer src="app.js"></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr><th>姓名</th><th>职业</th><th>城市</th></tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr><td>祀梦</td><td>开发者</td><td>上海</td></tr>
|
|
||||||
<tr><td>小李</td><td>产品经理</td><td>杭州</td></tr>
|
|
||||||
<tr><td>小王</td><td>设计师</td><td>北京</td></tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
```
|
|
||||||
@tab style.css
|
|
||||||
```css
|
|
||||||
table { border-collapse: collapse; width: 100%; }
|
|
||||||
th, td { border: 1px solid #ddd; padding: .5rem .75rem; text-align: left; }
|
|
||||||
thead th { background: #f7f7f7; }
|
|
||||||
tbody tr:nth-child(odd) { background: #fafafa; }
|
|
||||||
```
|
|
||||||
@tab app.js
|
|
||||||
```js
|
|
||||||
document.querySelectorAll('tbody tr').forEach(tr => tr.addEventListener('click', () => tr.classList.toggle('highlight')));
|
|
||||||
```
|
|
||||||
:::
|
|
||||||
|
|
||||||
**小作业:制作“课程表”**
|
|
||||||
::: code-tabs
|
|
||||||
@tab index.html
|
|
||||||
```html
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="zh-CN">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>课程表</title>
|
|
||||||
<link rel="stylesheet" href="style.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr><th>时间</th><th>课程</th><th>教室</th></tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr><td>周一 9:00</td><td>数学</td><td>A101</td></tr>
|
|
||||||
<tr><td>周三 14:00</td><td>英语</td><td>B302</td></tr>
|
|
||||||
<tr><td>周五 10:00</td><td>计算机</td><td>C210</td></tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
```
|
|
||||||
@tab style.css
|
|
||||||
```css
|
|
||||||
table { border-collapse: collapse; width: 100%; }
|
|
||||||
th, td { border: 1px solid #ddd; padding: .5rem .75rem; }
|
|
||||||
thead th { background: #f0f0f0; }
|
|
||||||
```
|
|
||||||
:::
|
|
||||||
|
|
||||||
|
|
||||||
## 七、媒体(Audio/Video/Source)
|
|
||||||
|
|
||||||
```html title="index.html"
|
|
||||||
<audio src="bgm.mp3" controls loop></audio>
|
|
||||||
|
|
||||||
<video src="intro.mp4" controls width="480" poster="cover.jpg" muted></video>
|
|
||||||
<!-- 常用属性:controls / autoplay / loop / muted / poster(封面) -->
|
|
||||||
```
|
|
||||||
|
|
||||||
补充说明:
|
|
||||||
- `controls` 提供播放控制;一般不建议隐藏,保证可用性。
|
|
||||||
- `autoplay` 可能被浏览器限制;若需自动播放,通常需同时设置 `muted`。
|
|
||||||
- `preload` 控制预加载策略(`none`/`metadata`/`auto`),根据页面性能需求选择。
|
|
||||||
- `poster` 为视频未播放时的封面图;音频没有封面属性。
|
|
||||||
- 使用 `<source>` 指定多种格式与 MIME 类型,提升兼容性:`<source src="xxx.mp4" type="video/mp4">`。
|
|
||||||
|
|
||||||
**小案例:嵌入音视频**
|
|
||||||
::: code-tabs
|
|
||||||
@tab index.html
|
|
||||||
```html
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="zh-CN">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>媒体案例</title>
|
|
||||||
<link rel="stylesheet" href="style.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h2>背景音乐</h2>
|
|
||||||
<audio src="bgm.mp3" controls loop></audio>
|
|
||||||
<h2>介绍视频</h2>
|
|
||||||
<video src="intro.mp4" controls width="480" poster="cover.jpg" muted></video>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
```
|
|
||||||
@tab style.css
|
|
||||||
```css
|
|
||||||
video { display: block; margin-top: .5rem; }
|
|
||||||
```
|
|
||||||
:::
|
|
||||||
|
|
||||||
**小作业:添加一段你喜欢的音乐与视频**
|
|
||||||
::: code-tabs
|
|
||||||
@tab index.html
|
|
||||||
```html
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="zh-CN">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>我的媒体</title>
|
|
||||||
<link rel="stylesheet" href="style.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<audio src="music.mp3" controls></audio>
|
|
||||||
<video src="movie.mp4" controls width="480" poster="cover.jpg"></video>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
```
|
|
||||||
@tab style.css
|
|
||||||
```css
|
|
||||||
audio, video { display: block; margin: .5rem 0; }
|
|
||||||
```
|
|
||||||
:::
|
|
||||||
|
|
||||||
|
|
||||||
## 八、全局常用属性(任何标签几乎都能用)
|
|
||||||
|
|
||||||
- `id`:唯一标识,用于 JS/CSS 精确选择。
|
|
||||||
- `class`:分组与样式选择(上一课已讲)。
|
|
||||||
- `style`:内联样式(不推荐大量使用,建议写到 CSS 文件)。
|
|
||||||
- `title`:悬停提示文字。
|
|
||||||
- `data-*`:自定义数据属性(如 `data-user-id="42"`)。
|
|
||||||
- `aria-*`:无障碍相关属性,帮助读屏工具理解(如 `aria-label`)。
|
|
||||||
|
|
||||||
**小案例:使用 id/class/data/title/aria**
|
|
||||||
::: code-tabs
|
|
||||||
@tab index.html
|
|
||||||
```html
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="zh-CN">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>全局属性案例</title>
|
|
||||||
<link rel="stylesheet" href="style.css">
|
|
||||||
<script defer src="app.js"></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<button id="likeBtn" class="btn" title="点赞一下" data-count="0" aria-label="点赞">👍 喜欢</button>
|
|
||||||
<p id="msg" class="note" aria-live="polite">点击按钮试试。</p>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
```
|
|
||||||
@tab style.css
|
|
||||||
```css
|
|
||||||
.btn { padding: .5rem .75rem; border: 1px solid #ccc; border-radius: 6px; background: #fff; }
|
|
||||||
.note { color: #555; }
|
|
||||||
```
|
|
||||||
@tab app.js
|
|
||||||
```js
|
|
||||||
const btn = document.getElementById('likeBtn');
|
|
||||||
const msg = document.getElementById('msg');
|
|
||||||
btn.addEventListener('click', () => {
|
|
||||||
const count = Number(btn.dataset.count || 0) + 1;
|
|
||||||
btn.dataset.count = String(count);
|
|
||||||
msg.textContent = `已点赞 ${count} 次`;
|
|
||||||
});
|
|
||||||
```
|
|
||||||
:::
|
|
||||||
|
|
||||||
**小作业:做一个带计数的按钮**
|
|
||||||
::: code-tabs
|
|
||||||
@tab index.html
|
|
||||||
```html
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="zh-CN">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>计数按钮</title>
|
|
||||||
<link rel="stylesheet" href="style.css">
|
|
||||||
<script defer src="app.js"></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<button id="counter" class="btn" title="点击增加" data-count="0">点击我</button>
|
|
||||||
<p id="status" class="note">当前计数:0</p>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
```
|
|
||||||
@tab style.css
|
|
||||||
```css
|
|
||||||
.btn { padding: .5rem .75rem; border: 1px solid #ccc; border-radius: 6px; background: #fff; }
|
|
||||||
.note { margin-top: .5rem; }
|
|
||||||
```
|
|
||||||
@tab app.js
|
|
||||||
```js
|
|
||||||
const counter = document.getElementById('counter');
|
|
||||||
const statusEl = document.getElementById('status');
|
|
||||||
counter.addEventListener('click', () => {
|
|
||||||
const count = Number(counter.dataset.count || 0) + 1;
|
|
||||||
counter.dataset.count = String(count);
|
|
||||||
statusEl.textContent = `当前计数:${count}`;
|
|
||||||
});
|
|
||||||
```
|
|
||||||
:::
|
|
||||||
|
|
||||||
## 结尾:先结构清晰,再上样式与交互
|
|
||||||
|
|
||||||
写网页像搭房子:先把房间(结构标签)安排好,再选家具颜色(CSS),最后加智能设备(JS)。
|
|
||||||
|
|
||||||
建议你先用这些常用标签做一个“个人名片页”,含标题、段落、头像图片、导航链接和一个简单表单。练熟后再加样式与交互。
|
|
||||||
|
|
||||||
**延伸阅读**:
|
|
||||||
- HTML 规范与参考(MDN):https://developer.mozilla.org/en-US/docs/Web/HTML
|
|
||||||
- 无障碍与语义化:https://developer.mozilla.org/en-US/docs/Glossary/Semantics
|
|
||||||
@@ -1,424 +0,0 @@
|
|||||||
---
|
|
||||||
title: HTML 常用标签与属性
|
|
||||||
createTime: 2025/11/2 19:30:00
|
|
||||||
permalink: /programming/web/basic-syntax/html-tags-attributes/
|
|
||||||
---
|
|
||||||
|
|
||||||
## 文本与标题(H/P/Span/Strong/Em/Div)
|
|
||||||
|
|
||||||
**标题**:从重要到不重要,`<h1>` ~ `<h6>`。
|
|
||||||
```html title="index.html"
|
|
||||||
<h1>我的网站</h1>
|
|
||||||
<h2>关于我</h2>
|
|
||||||
<h3>联系方式</h3>
|
|
||||||
```
|
|
||||||
|
|
||||||
### 认识div标签
|
|
||||||
|
|
||||||
**什么是 `<div>`?**
|
|
||||||
|
|
||||||
`<div>` 是 "division"(分区)的缩写,可以理解为网页中的"容器"或"盒子"。
|
|
||||||
|
|
||||||
想象一下搬家时的纸箱:
|
|
||||||
|
|
||||||
* 网页 = 整个房间
|
|
||||||
* `<div>` = 一个个纸箱
|
|
||||||
* 箱子里 = 可以放各种物品(文字、图片、按钮等)
|
|
||||||
|
|
||||||
### `<div>` 的基本特点
|
|
||||||
|
|
||||||
1. 块级元素
|
|
||||||
`<div>` 是块级元素,这意味着:
|
|
||||||
|
|
||||||
* 默认会占据整行的宽度
|
|
||||||
* 前后会自动换行
|
|
||||||
* 就像段落一样,每个`<div>`都会从新的一行开始
|
|
||||||
|
|
||||||
**`<div>` 本身没有特定含义,它只是用来分组和布局。**
|
|
||||||
|
|
||||||
### 为什么要使用 `<div>`?
|
|
||||||
|
|
||||||
没有`<div>`的情况:
|
|
||||||
```html title='index.html'
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>仲夏夜之梦</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>我的网站</h1>
|
|
||||||
<p>欢迎来到我的个人网站!</p>
|
|
||||||
<img src="photo.jpg" alt="我的照片">
|
|
||||||
<p>这是我的个人介绍...</p>
|
|
||||||
<button>联系我</button>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
```
|
|
||||||
|
|
||||||
所有元素都堆在一起,很难分别控制样式。
|
|
||||||
|
|
||||||
使用 `<div>` 的情况:
|
|
||||||
```html title='index.html'
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>仲夏夜之梦</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="header">
|
|
||||||
<h1>我的网站</h1>
|
|
||||||
<p>欢迎来到我的个人网站!</p>
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<img src="photo.jpg" alt="我的照片">
|
|
||||||
<p>这是我的个人介绍...</p>
|
|
||||||
</div>
|
|
||||||
<div class="footer">
|
|
||||||
<button>联系我</button>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
```
|
|
||||||
这样我就可以分别控制每个部分的样式啦!
|
|
||||||
|
|
||||||
这个时候又有聪明的小朋友问了:这个class是什么呀?难道说?!是**起的名字!!**
|
|
||||||
|
|
||||||
太好了,恭喜你答对了,那么我们为什么要用class呢?
|
|
||||||
|
|
||||||
### Class
|
|
||||||
|
|
||||||
Class 可以理解为给 HTML 元素起的"组名"或"类别名",让 CSS 能够精确地找到并美化特定的元素。
|
|
||||||
|
|
||||||
想象一个学校:
|
|
||||||
* HTML 元素 = 学生
|
|
||||||
* Class = 学生的身份(如"三年级一班"、"篮球队员")
|
|
||||||
* CSS = 老师,根据身份给学生安排不同的任务和服装
|
|
||||||
|
|
||||||
class基本用法此处就不举例了,详情参照上面的代码。
|
|
||||||
|
|
||||||
如果没有class的情况:
|
|
||||||
```html title='index.html'
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>仲夏夜之梦</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>我的网站</h1>
|
|
||||||
<p>普通段落</p>
|
|
||||||
<p>个人介绍</p>
|
|
||||||
<p>重要提示</p>
|
|
||||||
<button>普通按钮</button>
|
|
||||||
<button>重要按钮</button>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
```
|
|
||||||
|
|
||||||
如果我们想给"重要提示"和"重要按钮"设置特殊样式,很难精确选择!像之前我教的一样css直接用p或者h1来选择的话就无法区分具体每一段的区别了。
|
|
||||||
|
|
||||||
这时候就可以用class了!
|
|
||||||
::: code-tabs
|
|
||||||
@tab index.html
|
|
||||||
```html
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>仲夏夜之梦</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>我的网站</h1>
|
|
||||||
<p class="normal">普通段落</p>
|
|
||||||
<p class="intro">个人介绍</p>
|
|
||||||
<p class="warning">重要提示</p>
|
|
||||||
<button class="normal-btn">普通按钮</button>
|
|
||||||
<button class="important-btn">重要按钮</button>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
```
|
|
||||||
@tab style.css
|
|
||||||
```css
|
|
||||||
.warning {
|
|
||||||
color: red;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
.important-btn {
|
|
||||||
background-color: red;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
:::
|
|
||||||
**现在自己动手尝试一下**
|
|
||||||
|
|
||||||
## 动手练习:小作业
|
|
||||||
|
|
||||||
运用所学的 HTML 和 CSS 知识,创建一个美观的个人名片页面。不确定的时候翻翻文档
|
|
||||||
|
|
||||||
```html title='index.html'
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="zh-CN">
|
|
||||||
<head>
|
|
||||||
<!-- 在这里添加字符编码和标题 -->
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<!-- 创建一个名片容器 div,class 为 "card" -->
|
|
||||||
|
|
||||||
<!-- 在卡片内部分为三个区域: -->
|
|
||||||
|
|
||||||
<!-- 1. 头部区域:包含姓名和职位 -->
|
|
||||||
<div class="card-header">
|
|
||||||
<!-- 添加 h1 标题显示你的姓名 -->
|
|
||||||
<!-- 添加 p 段落显示你的职位或专业 -->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 2. 主体区域:包含联系信息 -->
|
|
||||||
<div class="card-body">
|
|
||||||
<!-- 添加至少3个联系信息,使用 p 标签 -->
|
|
||||||
<!-- 例如:电话、邮箱、地址等 -->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 3. 底部区域:包含个人简介 -->
|
|
||||||
<div class="card-footer">
|
|
||||||
<!-- 添加一个个人简介段落 -->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
```
|
|
||||||
|
|
||||||
css样式自定,发挥自己的创作力,创建一个独特的个人名片页面。
|
|
||||||
|
|
||||||
**段落与换行**:
|
|
||||||
```html title="index.html"
|
|
||||||
<p>这是一个段落,里面可以有<strong>加粗</strong>和<em>强调</em>。</p>
|
|
||||||
<p>这是另一个段落。<br>需要换行时用 <code><br></code>。</p>
|
|
||||||
<hr> <!-- 水平分割线 -->
|
|
||||||
```
|
|
||||||
|
|
||||||
**行内 vs 块级**:
|
|
||||||
- `<div>` 是块级元素(换行占整行),用于分区布局;
|
|
||||||
- `<span>` 是行内元素(不换行),用于强调局部文字。
|
|
||||||
|
|
||||||
使用建议与解释:
|
|
||||||
- 语义优先:`<strong>` 表示“语义上的重点”,`<b>` 仅表示“加粗外观”;`<em>` 表示“语气强调”,`<i>` 仅表示“斜体外观”。优先使用语义标签,样式交给 CSS。
|
|
||||||
- 标题层级:通常一个页面只有一个 `<h1>`,下面按层级组织为 `<h2>/<h3>...`。不要为求大小随意跳级或用标题替代普通文本。
|
|
||||||
- 段落与换行:换行请优先使用分段(`<p>`),只有在同段内需要视觉换行时使用 `<br>`。`<hr>` 适合用于内容分隔或主题切换。
|
|
||||||
|
|
||||||
**小案例:文本与标题**
|
|
||||||
::: code-tabs
|
|
||||||
@tab index.html
|
|
||||||
```html
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="zh-CN">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>文本与标题案例</title>
|
|
||||||
<link rel="stylesheet" href="style.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>我的网站</h1>
|
|
||||||
<h2>关于我</h2>
|
|
||||||
<p>我是一名<strong>前端开发者</strong>,喜欢<em>简洁的设计</em>与清晰的结构。</p>
|
|
||||||
<hr>
|
|
||||||
<h3>联系方式</h3>
|
|
||||||
<p>Email: hello@example.com</p>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
```
|
|
||||||
@tab style.css
|
|
||||||
```css
|
|
||||||
body { font-family: system-ui; margin: 2rem; }
|
|
||||||
h1 { font-size: 2rem; margin-bottom: .5rem; }
|
|
||||||
h2 { margin-top: 1.5rem; }
|
|
||||||
p { margin: .5rem 0; }
|
|
||||||
```
|
|
||||||
:::
|
|
||||||
|
|
||||||
**小作业:写一段个人简介**
|
|
||||||
::: code-tabs
|
|
||||||
@tab index.html
|
|
||||||
```html
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="zh-CN">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>个人简介</title>
|
|
||||||
<link rel="stylesheet" href="style.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>你的名字</h1>
|
|
||||||
<h2>一句话介绍</h2>
|
|
||||||
<p>用两段文字,分别写你现在在做什么、你感兴趣的方向。</p>
|
|
||||||
<p>使用 <strong>strong</strong> 与 <em>em</em> 做重点强调。</p>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
```
|
|
||||||
@tab style.css
|
|
||||||
```css
|
|
||||||
body { font-family: system-ui; margin: 2rem; line-height: 1.8; }
|
|
||||||
p { max-width: 60ch; }
|
|
||||||
```
|
|
||||||
:::
|
|
||||||
|
|
||||||
## 三、链接与图片(A/IMG)
|
|
||||||
|
|
||||||
### 什么是 `<img>` 标签
|
|
||||||
|
|
||||||
`<img>` 标签用于在网页中插入图片,它是自闭合标签(不需要结束标签)。
|
|
||||||
|
|
||||||
### `<img>` 标签的基本属性
|
|
||||||
|
|
||||||
* src:指定图片的路径(必填)
|
|
||||||
* alt:指定图片的替代文本(必填,用于图片加载失败时显示)
|
|
||||||
* width:指定图片的宽度(可选)
|
|
||||||
* height:指定图片的高度(可选)
|
|
||||||
|
|
||||||
基本语法:
|
|
||||||
```html title='index.html'
|
|
||||||
<img src="photo.jpg" alt="我的照片" width="200" height="300">
|
|
||||||
```
|
|
||||||
|
|
||||||
### HTML 超链接
|
|
||||||
|
|
||||||
HTML 链接(Anchor)是网页之间跳转的核心部分。
|
|
||||||
|
|
||||||
HTML 使用链接与网络上的另一个文档相连。
|
|
||||||
|
|
||||||
HTML中的链接是一种用于在不同网页之间导航的元素。
|
|
||||||
|
|
||||||
链接通常用于将一个网页与另一个网页或资源(如文档、图像、音频文件等)相关联。
|
|
||||||
|
|
||||||
链接允许用户在浏览网页时单击文本或图像来跳转到其他位置,从而实现网页之间的互联。
|
|
||||||
|
|
||||||
HTML 链接 通过 `<a>` 标签创建,通常用于将用户从一个页面导航到另一个页面、从一个部分跳转到页面中的另一个部分、下载文件、打开电子邮件应用程序或执行 JavaScript 函数等。
|
|
||||||
|
|
||||||
超链接可以是一个字,一个词,或者一组词,也可以是一幅图像,可以点击这些内容来跳转到新的文档或者当前文档中的某个部分。
|
|
||||||
|
|
||||||
当把鼠标指针移动到网页中的某个链接上时,箭头会变为**一只小手**。
|
|
||||||
|
|
||||||
## `<a>` 标签的基本属性
|
|
||||||
|
|
||||||
* href:指定链接的目标 URL(必填)
|
|
||||||
* target:指定链接在何处打开(可选)
|
|
||||||
* _blank:在新窗口或标签页中打开链接
|
|
||||||
* _self:在当前窗口或标签页中打开链接(默认)
|
|
||||||
* _parent:在父框架中打开链接
|
|
||||||
* _top:在顶部框架中打开链接
|
|
||||||
|
|
||||||
基本语法:
|
|
||||||
```html title='index.html'
|
|
||||||
<a href="https://www.example.com">链接文本</a>
|
|
||||||
```
|
|
||||||
- `<a>` 标签:定义了一个超链接(anchor)。它是 HTML 中用来创建可点击链接的主要标签。
|
|
||||||
- href 属性:指定目标 URL,当点击链接时,浏览器将导航到此 URL。
|
|
||||||
|
|
||||||
**这里还有一个target 属性**:
|
|
||||||
- _blank:在新窗口或标签页中打开链接
|
|
||||||
- _self:在当前窗口或标签页中打开链接(默认)
|
|
||||||
- _parent:在父框架中打开链接
|
|
||||||
- _top:在顶部框架中打开链接
|
|
||||||
|
|
||||||
```html title='index.html'
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>target属性示例</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h2>target属性示例</h2>
|
|
||||||
|
|
||||||
<!-- 在当前窗口打开(默认) -->
|
|
||||||
<a href="https://www.baidu.com" target="_self">当前窗口打开</a>
|
|
||||||
|
|
||||||
<!-- 在新窗口打开 -->
|
|
||||||
<a href="https://www.baidu.com" target="_blank">新窗口打开</a>
|
|
||||||
|
|
||||||
<!-- 在父窗口打开 -->
|
|
||||||
<a href="https://www.baidu.com" target="_parent">父窗口打开</a>
|
|
||||||
|
|
||||||
<!-- 建议:新窗口打开外部链接 -->
|
|
||||||
<a href="https://www.example.com" target="_blank" rel="noopener">
|
|
||||||
外部网站(安全的新窗口)
|
|
||||||
</a>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
```
|
|
||||||
复制代码到自己的html文件中,尝试一下不同的target属性值。
|
|
||||||
|
|
||||||
常用属性:
|
|
||||||
- `href`:目标地址;
|
|
||||||
- `target="_blank"`:新窗口打开;
|
|
||||||
- `rel="noopener"`:安全与性能(避免旧窗口被新页面控制)。
|
|
||||||
|
|
||||||
补充说明:
|
|
||||||
- 链接安全:外链新窗口打开时同时设置 `rel="noopener"` 或 `rel="noreferrer"`,避免安全与性能问题。
|
|
||||||
- 图片可使用 `loading="lazy"` 懒加载,减少首屏资源压力;`alt` 请写出图片用途或内容摘要。
|
|
||||||
- 设定 `width/height` 可以预留占位,减少页面布局抖动(CLS)。复杂场景可考虑 `<picture>` + `<source>` 做响应式图片。
|
|
||||||
- 与图片相关的配套标签:`<figure>` + `<figcaption>` 用于图片与说明文字的组合。
|
|
||||||
|
|
||||||
**小案例:链接与图片**
|
|
||||||
::: code-tabs
|
|
||||||
@tab index.html
|
|
||||||
```html
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="zh-CN">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>链接与图片案例</title>
|
|
||||||
<link rel="stylesheet" href="style.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<nav>
|
|
||||||
<a href="/" >首页</a>
|
|
||||||
<a href="https://developer.mozilla.org/" target="_blank" rel="noopener">MDN</a>
|
|
||||||
</nav>
|
|
||||||
<img src="avatar.jpg" alt="我的头像" width="160" height="160">
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
```
|
|
||||||
@tab style.css
|
|
||||||
```css
|
|
||||||
nav { display: flex; gap: 1rem; margin-bottom: 1rem; }
|
|
||||||
nav a { color: #06c; text-decoration: none; }
|
|
||||||
nav a:hover { text-decoration: underline; }
|
|
||||||
img { border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,.1); }
|
|
||||||
```
|
|
||||||
:::
|
|
||||||
|
|
||||||
**小作业:做一个“友链”与头像区块**
|
|
||||||
::: code-tabs
|
|
||||||
@tab index.html
|
|
||||||
```html
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="zh-CN">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>友链与头像</title>
|
|
||||||
<link rel="stylesheet" href="style.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<section>
|
|
||||||
<h2>友情链接</h2>
|
|
||||||
<p>
|
|
||||||
<a href="https://notes.simengweb.com" target="_blank" rel="noopener">祀梦笔记</a>
|
|
||||||
·
|
|
||||||
<a href="https://vuejs.org/" target="_blank" rel="noopener">Vue.js</a>
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h2>头像</h2>
|
|
||||||
<img src="avatar.jpg" alt="你的头像" width="160" height="160">
|
|
||||||
</section>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
```
|
|
||||||
@tab style.css
|
|
||||||
```css
|
|
||||||
section { margin-bottom: 1rem; }
|
|
||||||
img { border: 2px solid #eee; border-radius: 50%; }
|
|
||||||
```
|
|
||||||
:::
|
|
||||||
|
|
||||||
@@ -1,606 +0,0 @@
|
|||||||
---
|
|
||||||
title: JavaScript 基础知识
|
|
||||||
createTime: 2025/11/2 21:30:00
|
|
||||||
permalink: /programming/web/basic-syntax/javascript-basics/
|
|
||||||
---
|
|
||||||
|
|
||||||
## JavaScript 是什么?
|
|
||||||
|
|
||||||
JavaScript 是一种广泛用于网页开发的脚本语言,它使网页能够实现交互式功能。与 HTML(负责结构)和 CSS(负责样式)不同,JavaScript 专注于**行为**,可以让网页变得动态和响应式。
|
|
||||||
|
|
||||||
|
|
||||||
## 第一个 JavaScript 程序
|
|
||||||
|
|
||||||
JavaScript 代码可以直接写在 HTML 文件中,通常放在 `<body>` 标签的底部,使用 `<script>` 标签包裹。
|
|
||||||
|
|
||||||
```html title="index.html"
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="zh-CN">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>我的第一个 JavaScript 程序</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>你好,世界!</h1>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// 这是注释,不会被执行
|
|
||||||
console.log('Hello, JavaScript!'); // 在控制台输出文本
|
|
||||||
alert('欢迎学习 JavaScript!'); // 弹出提示框
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
```
|
|
||||||
|
|
||||||
**提示**:你可以在浏览器中按下 F12 打开开发者工具,然后切换到 "Console" 标签查看 `console.log()` 的输出。
|
|
||||||
|
|
||||||
`console.log()` 是 JavaScript 中最常用、最重要的调试工具,可以说是每个开发者的"最佳朋友"!
|
|
||||||
|
|
||||||
### 基本输出 ###
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// 输出字符串
|
|
||||||
console.log("Hello World!");
|
|
||||||
|
|
||||||
// 输出变量
|
|
||||||
const name = "小明";
|
|
||||||
console.log(name);
|
|
||||||
|
|
||||||
// 输出多个值
|
|
||||||
const age = 18;
|
|
||||||
const isStudent = true;
|
|
||||||
console.log("学生信息:", name, age, isStudent);
|
|
||||||
```
|
|
||||||
|
|
||||||
### 输出不同类型的数据 ###
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// 各种数据类型
|
|
||||||
console.log("字符串:", "Hello");
|
|
||||||
console.log("数字:", 42);
|
|
||||||
console.log("布尔值:", true);
|
|
||||||
console.log("数组:", [1, 2, 3]);
|
|
||||||
console.log("对象:", {name: "李华", age: 20});
|
|
||||||
console.log("函数:", function() {});
|
|
||||||
console.log("undefined:", undefined);
|
|
||||||
console.log("null:", null);
|
|
||||||
```
|
|
||||||
|
|
||||||
### 字符串插值 ###
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const userName = "张三";
|
|
||||||
const userAge = 25;
|
|
||||||
const score = 95.5;
|
|
||||||
|
|
||||||
// 传统方式
|
|
||||||
console.log("用户 " + userName + " 年龄 " + userAge + " 分数 " + score);
|
|
||||||
|
|
||||||
// 模板字符串(推荐)
|
|
||||||
console.log(`用户 ${userName} 年龄 ${userAge} 分数 ${score}`);
|
|
||||||
|
|
||||||
// 带表达式的插值
|
|
||||||
console.log(`${userName} 是 ${userAge >= 18 ? "成年人" : "未成年人"}`);
|
|
||||||
```
|
|
||||||
|
|
||||||
### 格式化输出 ###
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const product = {
|
|
||||||
name: "笔记本电脑",
|
|
||||||
price: 5999,
|
|
||||||
brand: "Dell",
|
|
||||||
inStock: true
|
|
||||||
};
|
|
||||||
|
|
||||||
// %s - 字符串
|
|
||||||
console.log("产品名称: %s", product.name);
|
|
||||||
|
|
||||||
// %d - 数字
|
|
||||||
console.log("价格: %d 元", product.price);
|
|
||||||
|
|
||||||
// %f - 浮点数
|
|
||||||
console.log("折扣价: %f", product.price * 0.9);
|
|
||||||
|
|
||||||
// %o - 对象
|
|
||||||
console.log("完整产品信息: %o", product);
|
|
||||||
|
|
||||||
// %c - CSS样式
|
|
||||||
console.log("%c重要信息!", "color: red; font-size: 20px; font-weight: bold;");
|
|
||||||
```
|
|
||||||
|
|
||||||
## JavaScript 变量
|
|
||||||
|
|
||||||
变量是用来存储信息的容器。在 JavaScript 中,我们使用 `let`、`const` 或 `var` 关键字来声明变量。
|
|
||||||
|
|
||||||
### 变量声明方式
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// 使用 let 声明可变变量
|
|
||||||
let name = "小明";
|
|
||||||
name = "小红"; // 可以修改
|
|
||||||
|
|
||||||
// 使用 const 声明常量(不可变)
|
|
||||||
const PI = 3.14159;
|
|
||||||
// PI = 3.14; // 错误!常量不能修改
|
|
||||||
|
|
||||||
// 使用 var 声明变量(旧方式,现在推荐使用 let 和 const)
|
|
||||||
var age = 25;
|
|
||||||
```
|
|
||||||
|
|
||||||
**注意**:
|
|
||||||
- 使用 `let` 声明的变量可以重新赋值
|
|
||||||
- 使用 `const` 声明的变量不能重新赋值(常量)
|
|
||||||
- 尽量避免使用 `var`,因为它有一些奇怪的作用域规则
|
|
||||||
|
|
||||||
|
|
||||||
## 数据类型
|
|
||||||
|
|
||||||
JavaScript 有几种基本数据类型:
|
|
||||||
|
|
||||||
### 1. 字符串(String)
|
|
||||||
|
|
||||||
用于表示文本,可以使用单引号或双引号。
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const greeting = "你好";
|
|
||||||
const name = 'JavaScript';
|
|
||||||
const message = `${greeting}, ${name}!`; // 使用模板字符串(ES6 特性)
|
|
||||||
console.log(message); // 输出:你好, JavaScript!
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 数字(Number)
|
|
||||||
|
|
||||||
用于表示数值。
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const age = 25;
|
|
||||||
const price = 99.99;
|
|
||||||
const PI = 3.14159;
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. 布尔值(Boolean)
|
|
||||||
|
|
||||||
用于表示真或假,只有两个值:`true` 和 `false`。
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const isStudent = true;
|
|
||||||
const hasGraduated = false;
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. 数组(Array)
|
|
||||||
|
|
||||||
用于存储多个值的集合。
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const fruits = ["苹果", "香蕉", "橙子"];
|
|
||||||
console.log(fruits[0]); // 输出:苹果(数组索引从0开始)
|
|
||||||
|
|
||||||
// 添加元素
|
|
||||||
fruits.push("葡萄");
|
|
||||||
console.log(fruits); // 输出:["苹果", "香蕉", "橙子", "葡萄"]
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5. 对象(Object)
|
|
||||||
|
|
||||||
用于存储键值对集合。
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const person = {
|
|
||||||
name: "小明",
|
|
||||||
age: 25,
|
|
||||||
isStudent: true,
|
|
||||||
greet: function() {
|
|
||||||
console.log(`你好,我是${this.name}!`);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log(person.name); // 输出:小明
|
|
||||||
person.greet(); // 输出:你好,我是小明!
|
|
||||||
```
|
|
||||||
|
|
||||||
## 运算符
|
|
||||||
|
|
||||||
### 算术运算符
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const a = 10;
|
|
||||||
const b = 5;
|
|
||||||
|
|
||||||
console.log(a + b); // 15 加法
|
|
||||||
console.log(a - b); // 5 减法
|
|
||||||
console.log(a * b); // 50 乘法
|
|
||||||
console.log(a / b); // 2 除法
|
|
||||||
console.log(a % b); // 0 取余
|
|
||||||
console.log(a ** b); // 100000 幂运算(ES6)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 赋值运算符
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const x = 10;
|
|
||||||
|
|
||||||
x += 5; // 等同于 x = x + 5
|
|
||||||
console.log(x); // 15
|
|
||||||
|
|
||||||
x -= 3; // 等同于 x = x - 3
|
|
||||||
console.log(x); // 12
|
|
||||||
```
|
|
||||||
|
|
||||||
### 比较运算符
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const a = 10;
|
|
||||||
const b = 5;
|
|
||||||
|
|
||||||
console.log(a > b); // true
|
|
||||||
console.log(a < b); // false
|
|
||||||
console.log(a >= b); // true
|
|
||||||
console.log(a <= b); // false
|
|
||||||
console.log(a === b); // false(严格相等,比较值和类型)
|
|
||||||
console.log(a == b); // false(宽松相等,只比较值)
|
|
||||||
console.log(a !== b); // true(严格不相等)
|
|
||||||
```
|
|
||||||
|
|
||||||
## 条件语句
|
|
||||||
|
|
||||||
### if 语句
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const age = 18;
|
|
||||||
|
|
||||||
if (age >= 18) {
|
|
||||||
console.log("你已经成年了!");
|
|
||||||
} else if (age >= 13) {
|
|
||||||
console.log("你是青少年。");
|
|
||||||
} else {
|
|
||||||
console.log("你还是个孩子。");
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## 循环
|
|
||||||
|
|
||||||
### for 循环
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// 打印1到5
|
|
||||||
for (const i = 1; i <= 5; i++) {
|
|
||||||
console.log(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 遍历数组
|
|
||||||
const fruits = ["苹果", "香蕉", "橙子"];
|
|
||||||
for (const i = 0; i < fruits.length; i++) {
|
|
||||||
console.log(fruits[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 使用 for...of 遍历(ES6)
|
|
||||||
for (const fruit of fruits) {
|
|
||||||
console.log(fruit);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### while 循环
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const count = 1;
|
|
||||||
while (count <= 5) {
|
|
||||||
console.log(count);
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 函数
|
|
||||||
|
|
||||||
函数是一段可重复使用的代码块,可以接受输入(参数),执行操作,并返回输出(返回值)。
|
|
||||||
|
|
||||||
### 函数声明
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
function greet(name) {
|
|
||||||
return `你好,${name}!`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const message = greet("小明");
|
|
||||||
console.log(message); // 输出:你好,小明!
|
|
||||||
```
|
|
||||||
|
|
||||||
格式如下
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
function 函数名(参数1, 参数2, ...参数N) {
|
|
||||||
// 函数体:要执行的代码
|
|
||||||
return 返回值; // 可选
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 箭头函数(ES6)
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const sum = (a, b) => {
|
|
||||||
return a + b;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 简化写法(当只有一行返回语句时)
|
|
||||||
const multiply = (a, b) => a * b;
|
|
||||||
|
|
||||||
console.log(sum(3, 4)); // 7
|
|
||||||
console.log(multiply(3, 4)); // 12
|
|
||||||
```
|
|
||||||
|
|
||||||
## DOM 操作
|
|
||||||
|
|
||||||
DOM(文档对象模型)是 HTML 和 XML 文档的编程接口。JavaScript 可以通过 DOM 来操作网页元素。
|
|
||||||
|
|
||||||
### 选择元素
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// 通过 id 选择元素
|
|
||||||
const title = document.getElementById("title");
|
|
||||||
|
|
||||||
// 通过 class 选择元素(返回元素集合)
|
|
||||||
const items = document.getElementsByClassName("item");
|
|
||||||
|
|
||||||
// 通过标签名选择元素
|
|
||||||
const paragraphs = document.getElementsByTagName("p");
|
|
||||||
|
|
||||||
// 通过 CSS 选择器选择元素(ES5+)
|
|
||||||
const header = document.querySelector("header"); // 选择第一个匹配的元素
|
|
||||||
const allLinks = document.querySelectorAll("a"); // 选择所有匹配的元素
|
|
||||||
```
|
|
||||||
|
|
||||||
### 修改元素内容
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// 修改文本内容
|
|
||||||
const title = document.getElementById("title");
|
|
||||||
title.textContent = "新标题";
|
|
||||||
|
|
||||||
// 修改 HTML 内容
|
|
||||||
title.innerHTML = "<strong>加粗的新标题</strong>";
|
|
||||||
```
|
|
||||||
|
|
||||||
### 修改元素样式
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const element = document.getElementById("box");
|
|
||||||
element.style.color = "red";
|
|
||||||
element.style.fontSize = "20px";
|
|
||||||
element.style.backgroundColor = "#f0f0f0";
|
|
||||||
```
|
|
||||||
|
|
||||||
### 添加事件监听器
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const button = document.getElementById("myButton");
|
|
||||||
|
|
||||||
button.addEventListener("click", function() {
|
|
||||||
console.log("按钮被点击了!");
|
|
||||||
alert("你好,欢迎使用!");
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
## 小案例:交互式计算器
|
|
||||||
|
|
||||||
下面是一个简单的交互式计算器示例,演示如何结合 HTML、CSS 和 JavaScript。
|
|
||||||
|
|
||||||
::: code-tabs
|
|
||||||
@tab index.html
|
|
||||||
```html
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="zh-CN">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>简易计算器</title>
|
|
||||||
<link rel="stylesheet" href="style.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="calculator">
|
|
||||||
<h2>简易计算器</h2>
|
|
||||||
<input type="number" id="num1" placeholder="输入第一个数字">
|
|
||||||
<select id="operation">
|
|
||||||
<option value="add">+</option>
|
|
||||||
<option value="subtract">-</option>
|
|
||||||
<option value="multiply">×</option>
|
|
||||||
<option value="divide">÷</option>
|
|
||||||
</select>
|
|
||||||
<input type="number" id="num2" placeholder="输入第二个数字">
|
|
||||||
<button id="calculate">计算</button>
|
|
||||||
<div id="result">结果将显示在这里</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="script.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
```
|
|
||||||
@tab style.css
|
|
||||||
```css
|
|
||||||
.calculator {
|
|
||||||
max-width: 300px;
|
|
||||||
margin: 2rem auto;
|
|
||||||
padding: 1.5rem;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
border-radius: 8px;
|
|
||||||
text-align: center;
|
|
||||||
font-family: system-ui;
|
|
||||||
}
|
|
||||||
|
|
||||||
input, select, button {
|
|
||||||
margin: 0.5rem 0;
|
|
||||||
padding: 0.5rem;
|
|
||||||
width: 100%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
background: #007bff;
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:hover {
|
|
||||||
background: #0056b3;
|
|
||||||
}
|
|
||||||
|
|
||||||
#result {
|
|
||||||
margin-top: 1rem;
|
|
||||||
padding: 1rem;
|
|
||||||
background: #f8f9fa;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
@tab script.js
|
|
||||||
```javascript
|
|
||||||
// 获取DOM元素
|
|
||||||
const num1Input = document.getElementById('num1');
|
|
||||||
const num2Input = document.getElementById('num2');
|
|
||||||
const operationSelect = document.getElementById('operation');
|
|
||||||
const calculateButton = document.getElementById('calculate');
|
|
||||||
const resultDiv = document.getElementById('result');
|
|
||||||
|
|
||||||
// 添加点击事件监听器
|
|
||||||
calculateButton.addEventListener('click', function() {
|
|
||||||
// 获取输入值
|
|
||||||
const num1 = parseFloat(num1Input.value);
|
|
||||||
const num2 = parseFloat(num2Input.value);
|
|
||||||
const operation = operationSelect.value;
|
|
||||||
const result;
|
|
||||||
|
|
||||||
// 检查输入是否有效
|
|
||||||
if (isNaN(num1) || isNaN(num2)) {
|
|
||||||
resultDiv.textContent = '请输入有效的数字!';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 执行计算
|
|
||||||
switch (operation) {
|
|
||||||
case 'add':
|
|
||||||
result = num1 + num2;
|
|
||||||
break;
|
|
||||||
case 'subtract':
|
|
||||||
result = num1 - num2;
|
|
||||||
break;
|
|
||||||
case 'multiply':
|
|
||||||
result = num1 * num2;
|
|
||||||
break;
|
|
||||||
case 'divide':
|
|
||||||
// 检查除数是否为0
|
|
||||||
if (num2 === 0) {
|
|
||||||
resultDiv.textContent = '除数不能为0!';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
result = num1 / num2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 显示结果
|
|
||||||
resultDiv.textContent = `计算结果:${result}`;
|
|
||||||
});
|
|
||||||
```
|
|
||||||
:::
|
|
||||||
|
|
||||||
## JavaScript 的异步编程
|
|
||||||
|
|
||||||
JavaScript 是单线程的,但它可以通过异步编程模型来处理耗时操作,如网络请求、定时器等。
|
|
||||||
|
|
||||||
### setTimeout 和 setInterval
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// setTimeout - 延迟执行一次
|
|
||||||
setTimeout(function() {
|
|
||||||
console.log('2秒后执行');
|
|
||||||
}, 2000);
|
|
||||||
|
|
||||||
// setInterval - 定期重复执行
|
|
||||||
const count = 0;
|
|
||||||
const timer = setInterval(function() {
|
|
||||||
count++;
|
|
||||||
console.log(`执行第 ${count} 次`);
|
|
||||||
|
|
||||||
if (count >= 5) {
|
|
||||||
clearInterval(timer); // 清除定时器
|
|
||||||
console.log('定时器已停止');
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Promise(ES6)
|
|
||||||
|
|
||||||
Promise 是异步编程的一种解决方案,用于处理异步操作。
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// 创建一个Promise
|
|
||||||
const fetchData = new Promise((resolve, reject) => {
|
|
||||||
// 模拟网络请求
|
|
||||||
setTimeout(() => {
|
|
||||||
const success = true;
|
|
||||||
if (success) {
|
|
||||||
resolve('数据获取成功');
|
|
||||||
} else {
|
|
||||||
reject('数据获取失败');
|
|
||||||
}
|
|
||||||
}, 2000);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 使用Promise
|
|
||||||
fetchData
|
|
||||||
.then(data => {
|
|
||||||
console.log(data);
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error(error);
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
## 常见错误和调试技巧
|
|
||||||
|
|
||||||
### 常见错误
|
|
||||||
|
|
||||||
1. **语法错误**:代码不符合 JavaScript 语法规则
|
|
||||||
2. **引用错误**:使用了未定义的变量或函数
|
|
||||||
3. **类型错误**:对错误类型的值进行操作
|
|
||||||
4. **范围错误**:数值超出有效范围
|
|
||||||
|
|
||||||
### 调试技巧
|
|
||||||
|
|
||||||
1. **使用 console.log()**:在控制台输出变量值或执行流程
|
|
||||||
2. **使用断点**:在浏览器开发者工具中设置断点,逐步执行代码
|
|
||||||
3. **检查错误信息**:仔细阅读错误提示,找出问题所在
|
|
||||||
4. **检查变量类型**:使用 `typeof` 操作符检查变量类型
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
console.log(typeof "hello"); // "string"
|
|
||||||
console.log(typeof 42); // "number"
|
|
||||||
console.log(typeof true); // "boolean"
|
|
||||||
console.log(typeof {}); // "object"
|
|
||||||
console.log(typeof []); // "object"(数组也是对象的一种)
|
|
||||||
```
|
|
||||||
|
|
||||||
## 实践练习
|
|
||||||
|
|
||||||
### 练习1:创建一个简单的待办事项列表
|
|
||||||
|
|
||||||
使用 HTML、CSS 和 JavaScript 创建一个待办事项列表,包含添加、删除和标记完成功能。
|
|
||||||
|
|
||||||
提示:
|
|
||||||
- 使用数组存储待办事项
|
|
||||||
- 使用 DOM 操作动态更新列表
|
|
||||||
- 为按钮添加事件监听器
|
|
||||||
|
|
||||||
### 练习2:实现一个数字猜谜游戏
|
|
||||||
|
|
||||||
计算机随机生成一个1到100之间的数字,玩家通过输入框猜测,程序提示"猜大了"或"猜小了",直到猜对为止。
|
|
||||||
|
|
||||||
提示:
|
|
||||||
- 使用 `Math.random()` 生成随机数
|
|
||||||
- 使用条件语句判断猜测结果
|
|
||||||
- 记录并显示猜测次数
|
|
||||||
|
|
||||||
## 总结
|
|
||||||
|
|
||||||
JavaScript 是现代 web 开发的核心技术之一,它可以让网页变得动态和交互。通过学习变量、数据类型、运算符、条件语句、循环、函数和 DOM 操作等基础知识,你已经迈出了学习 JavaScript 的第一步。
|
|
||||||
|
|
||||||
继续练习和探索,你会发现 JavaScript 的强大功能和灵活性!
|
|
||||||
@@ -1,202 +0,0 @@
|
|||||||
---
|
|
||||||
title: 系统分析师
|
|
||||||
createTime: 2026/03/01 12:40:00
|
|
||||||
permalink: /subject/certification/
|
|
||||||
---
|
|
||||||
|
|
||||||
# 系统分析师
|
|
||||||
|
|
||||||
软考高级资格考证笔记~
|
|
||||||
|
|
||||||
> 官网:https://www.ruankao.org.cn/
|
|
||||||
|
|
||||||
## 考试科目
|
|
||||||
|
|
||||||
| 科目 | 题型 | 分值 | 时长 |
|
|
||||||
| :--- | :--- | :--- | :--- |
|
|
||||||
| 综合知识 | 选择题 (75道) | 75分 | 150分钟 |
|
|
||||||
| 案例分析 | 问答题 (5道选3道) | 75分 | 90分钟 |
|
|
||||||
| 论文写作 | 论文题 (4选1,2500字) | 75分 | 120分钟 |
|
|
||||||
|
|
||||||
**及格标准**:各科目均需达到 45 分
|
|
||||||
|
|
||||||
## 考试要求
|
|
||||||
|
|
||||||
1. 具有系统工程的基础知识
|
|
||||||
2. 掌握开发信息系统的综合技术知识(硬件、软件、网络、数据库)
|
|
||||||
3. 熟悉企业和政府信息化建设,并具有组织信息化战略规划的知识
|
|
||||||
4. 熟练掌握信息系统开发过程和方法
|
|
||||||
5. 熟悉信息系统开发标准
|
|
||||||
6. 掌握信息安全的相关知识与技术
|
|
||||||
7. 理解软件质量保证的手段
|
|
||||||
8. 具有经济与管理科学的相关基础知识,熟悉有关的法律法规
|
|
||||||
9. 具有大学本科的数学基础
|
|
||||||
10. 熟练阅读和正确理解相关领域的英文文献
|
|
||||||
|
|
||||||
## 综合知识考点分布
|
|
||||||
|
|
||||||
| 知识点 | 分数 | 比例 |
|
|
||||||
| :--- | :--- | :--- |
|
|
||||||
| 信息化战略规划 | 6-9 | 8-12% |
|
|
||||||
| 需求工程 | 4-8 | 5.33-10.67% |
|
|
||||||
| 系统设计 | 2-8 | 2.67-10.67% |
|
|
||||||
| 信息安全 | 4-9 | 5.33-12% |
|
|
||||||
| 数据库系统 | 6-8 | 8-10.67% |
|
|
||||||
| 计算机网络 | 5-8 | 6.67-10.67% |
|
|
||||||
| 操作系统 | 4-6 | 5.33-8% |
|
|
||||||
| 软件工程 | 2-5 | 2.6-6.67% |
|
|
||||||
| 法律法规与标准化 | 4 | 5.33% |
|
|
||||||
| 专业英语 | 5 | 6.67% |
|
|
||||||
|
|
||||||
## 案例分析考查主题
|
|
||||||
|
|
||||||
- 系统规划
|
|
||||||
- 开发方法与开发模型
|
|
||||||
- 需求工程
|
|
||||||
- 系统分析
|
|
||||||
- 系统设计
|
|
||||||
- 系统运行和维护
|
|
||||||
- 数据库方向
|
|
||||||
- 项目管理
|
|
||||||
- 企业信息化战略与实施
|
|
||||||
- 嵌入式方向
|
|
||||||
- Web技术
|
|
||||||
- 新技术的应用
|
|
||||||
|
|
||||||
## 论文写作主题
|
|
||||||
|
|
||||||
- 系统规划与战略规划
|
|
||||||
- 需求工程
|
|
||||||
- 系统开发方法
|
|
||||||
- 系统维护和系统测试
|
|
||||||
- 项目管理
|
|
||||||
- 面向对象技术
|
|
||||||
- 应用系统分析与设计
|
|
||||||
- 数据库工程
|
|
||||||
- 应用系统集成
|
|
||||||
- 新技术的应用
|
|
||||||
|
|
||||||
## 备考攻略
|
|
||||||
|
|
||||||
### 备考周期
|
|
||||||
建议 **4-6个月**,高级科目难度较大,需尽早准备!
|
|
||||||
|
|
||||||
### 第一阶段:基础构建(1-2个月)
|
|
||||||
- 研究考试大纲,明确考试范围和重点
|
|
||||||
- 购买官方推荐教材
|
|
||||||
- 制定详细的学习计划
|
|
||||||
- 从基础知识点开始,逐步构建知识体系
|
|
||||||
|
|
||||||
### 第二阶段:深入学习(1-2个月)
|
|
||||||
- 细致阅读教材,做好学习笔记
|
|
||||||
- 加入在线课程或学习小组交流
|
|
||||||
- 实践应用所学知识
|
|
||||||
- 定期自我测试
|
|
||||||
|
|
||||||
### 第三阶段:强化训练(1-2个月)
|
|
||||||
- 大量做历年真题
|
|
||||||
- 查漏补缺,强化记忆
|
|
||||||
- 提高答题速度
|
|
||||||
- 论文写作:看范文,写框架
|
|
||||||
|
|
||||||
### 第四阶段:冲刺复习(考前1个月)
|
|
||||||
- 总结各章节重点和易错点
|
|
||||||
- 全真模拟考试
|
|
||||||
- 调整心态,保持状态
|
|
||||||
|
|
||||||
### 第五阶段:考前准备(考前1周)
|
|
||||||
- 复习笔记和提纲
|
|
||||||
- 检查考试资料(身份证、准考证)
|
|
||||||
- 调整作息,确保精力充沛
|
|
||||||
|
|
||||||
## 答题技巧
|
|
||||||
|
|
||||||
### 综合知识
|
|
||||||
- 先看问题,再看选项
|
|
||||||
- 排除干扰项,选择相对稳妥的说法
|
|
||||||
- 当两个选项截然相反时,必有一个正确答案
|
|
||||||
- 可以用选项代入计算的方式走捷径
|
|
||||||
|
|
||||||
### 案例分析
|
|
||||||
- 带着问题去看题干
|
|
||||||
- 顺势而为答题,根据题干倾向分析优劣
|
|
||||||
- 分条目答题,方便老师查看得分点
|
|
||||||
- 根据分值决定条目数量和简略程度
|
|
||||||
|
|
||||||
### 论文写作
|
|
||||||
- 提前准备 2-3 个主题的框架
|
|
||||||
- 关注论文结构:背景、问题、方案、效果、总结
|
|
||||||
- 结合实际项目经验
|
|
||||||
- 字数控制在 2500 字左右
|
|
||||||
|
|
||||||
## 2026年考试时间安排
|
|
||||||
|
|
||||||
| 时间节点 | 说明 |
|
|
||||||
| :--- | :--- |
|
|
||||||
| 报名时间 | 2026年3月中旬至4月中旬(各省差异大) |
|
|
||||||
| 考试时间 | 2026年5月23日-26日 |
|
|
||||||
| 准考证打印 | 考前一周左右(5月中旬) |
|
|
||||||
|
|
||||||
**注意**:各地报名时间差异极大,部分省份窗口期可能短至一周,请务必关注当地通知!
|
|
||||||
|
|
||||||
## 报名流程与注意事项
|
|
||||||
|
|
||||||
### 报名官网
|
|
||||||
- 中国计算机技术职业资格网:https://www.ruankao.org.cn/
|
|
||||||
- 报名平台:https://bm.ruankao.org.cn/
|
|
||||||
|
|
||||||
### 报名流程
|
|
||||||
1. **注册登录**:使用"国家网络身份认证"APP扫码登录
|
|
||||||
2. **填报信息**:选择考区、填写个人信息,报考级别选"高级",资格选"系统分析师"
|
|
||||||
3. **上传照片**:必须使用官方"照片验证工具"处理白底彩色证件照
|
|
||||||
4. **审核缴费**:等待审核(1-3个工作日),审核通过后完成网上缴费
|
|
||||||
|
|
||||||
### 注意事项
|
|
||||||
- **属地原则**:仅接受在工作地、居住地或学籍所在地报考
|
|
||||||
- **异地报考**:需提供当地社保缴纳证明(连续6个月)或有效期内居住证
|
|
||||||
- **实名认证**:需提前下载"国家网络身份认证"APP完成认证
|
|
||||||
- **警惕骗子**:勿信"代报名"、"包过"、"异地代报"等诈骗信息
|
|
||||||
|
|
||||||
## 核心能力考查
|
|
||||||
|
|
||||||
系统分析师考试强调综合分析与解决实际问题的能力:
|
|
||||||
|
|
||||||
| 能力 | 说明 |
|
|
||||||
| :--- | :--- |
|
|
||||||
| 系统思维 | 从全局视角看待信息系统与组织战略的关系 |
|
|
||||||
| 需求分析建模 | 能使用DFD、用例图、类图、活动图等工具进行系统建模 |
|
|
||||||
| 方案设计与权衡 | 分析C/S vs B/S、单体 vs 微服务等方案的优劣 |
|
|
||||||
| 项目管理与风险控制 | 制定项目计划(WBS、甘特图)、估算成本、识别风险 |
|
|
||||||
| 技术趋势理解 | 对云计算、大数据、AI、物联网等新技术有基本理解 |
|
|
||||||
| 表达与写作 | 撰写结构清晰、逻辑严谨、结合实际的论文 |
|
|
||||||
|
|
||||||
**三大核心模块**:软件工程 + 系统分析设计 + 项目管理,合计占 **45%以上**!
|
|
||||||
|
|
||||||
## 论文评分标准
|
|
||||||
|
|
||||||
| 评分项 | 占比 | 档次 |
|
|
||||||
| :--- | :--- | :--- |
|
|
||||||
| 切合题意 | 30% | 非常切合、较好地切合、基本上切合 |
|
|
||||||
| 应用深度与水平 | 20% | 很强、较强、一般、较差 |
|
|
||||||
| 实践性 | 20% | 很强、较强、一般、较差 |
|
|
||||||
| 表达能力 | 15% | 很强、较强、一般、较差 |
|
|
||||||
| 书面能力 | 15% | 很强、较强、一般、较差 |
|
|
||||||
|
|
||||||
## 推荐资料
|
|
||||||
|
|
||||||
### 官方教材
|
|
||||||
- 《系统分析师教程(第2版)》(清华大学出版社,2024年10月出版)
|
|
||||||
|
|
||||||
### 备考资料
|
|
||||||
- 近5年软考系统分析师真题
|
|
||||||
- 《系统分析师考试全程指导》
|
|
||||||
- 《5天修炼》
|
|
||||||
- 在线资源:希赛网、软考真题网、B站相关课程
|
|
||||||
|
|
||||||
## 参考资料
|
|
||||||
|
|
||||||
- [中国计算机技术职业资格网](https://www.ruankao.org.cn/)
|
|
||||||
- [软考高级系统分析师备考攻略 - 知乎](https://zhuanlan.zhihu.com/p/14782824101)
|
|
||||||
- [新手一次过软考高级系统分析师 - CSDN](https://blog.csdn.net/winin2024/article/details/141386702)
|
|
||||||
- [系统分析师考试备考全面解析 - CSDN](https://blog.csdn.net/summer_fish/article/details/151296190)
|
|
||||||
- [2026上半年软考系统分析师报考指南 - 希赛网](https://www.educity.cn/rk/5454826.html)
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
---
|
|
||||||
title: 第一章 绪论
|
|
||||||
createTime: 2026/03/01 14:00:00
|
|
||||||
permalink: /subject/certification/sys-analyst/
|
|
||||||
---
|
|
||||||
|
|
||||||
# 第一章 绪论
|
|
||||||
|
|
||||||
> 还没有开始写呢,先占个位置~ ✨
|
|
||||||
|
|
||||||
敬请期待...
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
---
|
|
||||||
title: 第二章 数学与工程基础
|
|
||||||
createTime: 2026/03/01 14:00:00
|
|
||||||
permalink: /subject/certification/sys-analyst/math/
|
|
||||||
---
|
|
||||||
|
|
||||||
# 第二章 数学与工程基础
|
|
||||||
|
|
||||||
> 还没有开始写呢,先占个位置~ ✨
|
|
||||||
|
|
||||||
敬请期待...
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
---
|
|
||||||
title: 第十章 系统规划与分析
|
|
||||||
createTime: 2026/03/01 14:00:00
|
|
||||||
permalink: /subject/certification/sys-analyst/planning/
|
|
||||||
---
|
|
||||||
|
|
||||||
# 第十章 系统规划与分析
|
|
||||||
|
|
||||||
> 还没有开始写呢,先占个位置~ ✨
|
|
||||||
|
|
||||||
敬请期待...
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
---
|
|
||||||
title: 英语学习笔记
|
|
||||||
createTime: 2025/10/20 16:34:20
|
|
||||||
permalink: /subject/english/
|
|
||||||
---
|
|
||||||
|
|
||||||
# 英语学习笔记
|
|
||||||
|
|
||||||
这是一个英语学习笔记的总览页。下面是目录与分区入口:
|
|
||||||
|
|
||||||
## 目录
|
|
||||||
- 学习环境与工具 → `/subject/english/basis/`
|
|
||||||
- 词汇与记忆 → `/subject/english/vocabulary/`
|
|
||||||
- 语法与句法 → `/subject/english/grammar/`
|
|
||||||
- 听力与口语 → `/subject/english/listening-speaking/`
|
|
||||||
- 阅读 → `/subject/english/reading-writing/`
|
|
||||||
- 考试与备考(含四级) → `/subject/english/exam/cet-4/`
|
|
||||||
- 学习资源与工具 → `/subject/english/resources/`
|
|
||||||
- 写译 → `/subject/english/writing-translation/`
|
|
||||||
|
|
||||||
|
|
||||||
你可以从以上入口进入对应的专题,随时扩展各章节内容。
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
---
|
|
||||||
title: 英语环境配置
|
|
||||||
createTime: 2025/10/20 16:34:20
|
|
||||||
permalink: /subject/english/basis/
|
|
||||||
---
|
|
||||||
|
|
||||||
# 英语环境配置
|
|
||||||
|
|
||||||
这里是英语学习的入门环境配置示例模版:
|
|
||||||
|
|
||||||
- 推荐词典与工具
|
|
||||||
- 输入法与语音资源
|
|
||||||
- 常用学习网站与 App
|
|
||||||
|
|
||||||
你可以按需补充具体内容。
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
---
|
|
||||||
title: 英语四级 (CET-4) 备考指南
|
|
||||||
createTime: 2025/10/20 16:42:00
|
|
||||||
permalink: /subject/english/exam/cet-4/
|
|
||||||
---
|
|
||||||
|
|
||||||
# 英语四级 (CET-4) 备考指南
|
|
||||||
|
|
||||||
## 考试结构概览
|
|
||||||
- 听力 (25%):新闻、长对话、讲座/采访
|
|
||||||
- 阅读 (35%):仔细阅读、选词填空
|
|
||||||
- 翻译 (15%):汉译英
|
|
||||||
- 写作 (25%):应用文与观点写作
|
|
||||||
|
|
||||||
## 核心备考策略
|
|
||||||
- 词汇:高频词与真题语块,使用 SRS 间隔重复
|
|
||||||
- 语法:时态、非谓语、从句,针对写作与翻译的准确表达
|
|
||||||
- 听力:精听 + 跟读(shadowing),积累场景表达
|
|
||||||
- 阅读:结构化分析段落逻辑,题型定位与干扰项识别
|
|
||||||
- 写作:模板+素材库+改写练习,关注连贯与准确
|
|
||||||
|
|
||||||
## 时间分配建议
|
|
||||||
- 写作 30 分钟:审题→列纲→写作→快速校对
|
|
||||||
- 阅读 40 分钟:定位→分析→作答→标记不确定
|
|
||||||
- 听力 按机考节奏:提前熟悉说明与做题界面
|
|
||||||
- 翻译 20 分钟:分句翻译→语法检查→润色
|
|
||||||
|
|
||||||
## 真题与模拟
|
|
||||||
- 使用近 3-5 年真题按套训练;分析错因并归档到词汇/语法/方法类别
|
|
||||||
- 每周至少一次全真模拟,记录分配与状态调整
|
|
||||||
|
|
||||||
## 考场技巧
|
|
||||||
- 先易后难;不确定题目标记后回看
|
|
||||||
- 注意题干限制词与语义线索(especially/only/not/except 等)
|
|
||||||
- 写作与翻译建议使用简单准确句式,避免长难句错误
|
|
||||||
|
|
||||||
## 推荐资源
|
|
||||||
- 真题:教育部考试中心官方题库与解析
|
|
||||||
- 听力:BBC 6 Minute English、VOA Learning English
|
|
||||||
- 写作:常用应用文格式与常见开头结尾句
|
|
||||||
|
|
||||||
> 后续我会补充各模块的详细练习清单与示例。
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
---
|
|
||||||
title: 核心语法与句型
|
|
||||||
createTime: 2025/10/20 16:40:00
|
|
||||||
permalink: /subject/english/grammar/
|
|
||||||
---
|
|
||||||
|
|
||||||
# 核心语法与句型
|
|
||||||
|
|
||||||
## 时态与语态
|
|
||||||
- 16 种时态的构成与用法(一般/进行/完成/完成进行 × 现在/过去/将来/过去将来)
|
|
||||||
- 被动语态的转换规则与适用场景
|
|
||||||
- 时态一致原则(主将从现、过去时一致等)
|
|
||||||
- 情态动词 + 完成体(must have done / should have done 等)的推测与虚拟含义
|
|
||||||
|
|
||||||
## 从句
|
|
||||||
### 名词性从句
|
|
||||||
- 主语从句(That he failed surprised us. / Whether she comes matters.)
|
|
||||||
- 宾语从句(I think (that) you are right.)
|
|
||||||
- 表语从句(The truth is that he lied.)
|
|
||||||
- 同位语从句(The fact that he lied shocked us.)
|
|
||||||
|
|
||||||
### 定语从句
|
|
||||||
- 限定性 vs. 非限定性
|
|
||||||
- 关系代词(who / whom / whose / which / that)与关系副词(when / where / why)
|
|
||||||
- 介词 + 关系代词(the house in which I live)
|
|
||||||
- 省略与替代(the book (that) I bought; the place (where) we met)
|
|
||||||
|
|
||||||
### 状语从句
|
|
||||||
- 时间(when / while / before / after / as soon as / until)
|
|
||||||
- 条件(if / unless / provided that / on condition that)
|
|
||||||
- 让步(though / although / even if / while)
|
|
||||||
- 原因(because / since / as / now that)
|
|
||||||
- 结果(so...that / such...that)
|
|
||||||
- 目的(so that / in order that)
|
|
||||||
- 比较(than / as...as / the more...the more)
|
|
||||||
|
|
||||||
## 非谓语动词
|
|
||||||
- 不定式(to do):作主语、宾语、定语、状语、补语
|
|
||||||
- 动名词(doing):作主语、宾语、表语、定语
|
|
||||||
- 现在分词(doing)与过去分词(done):作定语、状语、补语、表语
|
|
||||||
- 独立主格结构(with + O + OC; N + doing / done...)
|
|
||||||
- 非谓语动词的时态与语态(to be doing / to have done / being done / having been done)
|
|
||||||
|
|
||||||
## 倒装与虚拟
|
|
||||||
### 倒装
|
|
||||||
- 否定副词置前(Never have I seen...)
|
|
||||||
- Only + 状语置前(Only then did I realize...)
|
|
||||||
- So / Such 置前(So fast did he run that...)
|
|
||||||
- 方位/时间副词置前(Out rushed the children.)
|
|
||||||
- 虚拟条件句省略 if(Were I you... / Had I known...)
|
|
||||||
|
|
||||||
### 虚拟语气
|
|
||||||
- 与现在事实相反(If I were you, I would...)
|
|
||||||
- 与过去事实相反(If I had known, I would have...)
|
|
||||||
- 与将来事实相反(If it should rain tomorrow...)
|
|
||||||
- 省略 if 的倒装(Were / Should / Had...)
|
|
||||||
- 含蓄虚拟(without / but for / otherwise)
|
|
||||||
- 名词性从句中的虚拟(insist that... (should) do; It is important that... (should) do)
|
|
||||||
|
|
||||||
## 句式与信息焦点
|
|
||||||
### 强调结构
|
|
||||||
- It is / was...that / who...(It was John that/who broke the window.)
|
|
||||||
- 助动词 do / does / did(She did tell me the truth.)
|
|
||||||
- 副词强调(absolutely / definitely / simply)
|
|
||||||
|
|
||||||
### 分裂句(Cleft Sentence)
|
|
||||||
- 主语分裂(What I need is time.)
|
|
||||||
- 宾语分裂(What he bought was a Ferrari.)
|
|
||||||
- 状语分裂(Where we met was in Paris.)
|
|
||||||
|
|
||||||
### 平行与省略
|
|
||||||
- 并列结构中的省略(He likes tea and she coffee.)
|
|
||||||
- 比较结构中的省略(I like her better than him.)
|
|
||||||
- 替代(do / so / one / that / those)
|
|
||||||
|
|
||||||
### 插入语与同位语
|
|
||||||
- 破折号、括号、逗号插入(The book—which I bought yesterday—is amazing.)
|
|
||||||
- 同位语短语(Mr. Smith, CEO of the company, will attend.)
|
|
||||||
|
|
||||||
### 长难句拆解策略
|
|
||||||
- 找谓语,定主干
|
|
||||||
- 划从句,标连接词
|
|
||||||
- 去插入,还省略
|
|
||||||
- 调语序,还原文
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
---
|
|
||||||
title: 听力口语训练
|
|
||||||
createTime: 2025/10/20 16:40:00
|
|
||||||
permalink: /subject/english/listening-speaking/
|
|
||||||
---
|
|
||||||
|
|
||||||
# 听力口语训练
|
|
||||||
|
|
||||||
- 音素与连读弱读
|
|
||||||
- 听力材料选择与精听泛听
|
|
||||||
- 跟读与复述(shadowing & retelling)
|
|
||||||
- 场景对话与表达
|
|
||||||
|
|
||||||
## 自然拼读法Phonics
|
|
||||||
|
|
||||||
Phonics自然拼音,或叫“英语自然拼读法”更容易理解。拼读什么?就是看到英文字母或字母的组合能自然地读出、读对它的发音。这里,首先要区分“读音”(Name)与“发音”(Sound)。A-Z的26个字母几乎大家都认识、都能念出来,而念出来的就是字母本身的“读音”;而自然发音指的是字母的“发音”。发音不同于读音。看到字母后,不管读音,找对发音。这就Phonics要学的。理论可以讲很多很深,但Phonics注重的是方法与实践。因此,知道怎么正确发音就行了。
|
|
||||||
|
|
||||||
下面给你放一个基本发音规则的表,虽然看不到完整版但是足够了也足够大致理解发音规律了
|
|
||||||
|
|
||||||
```
|
|
||||||
https://wenku.so.com/tfd/b97611df865df13967a3b93a66d9e904?src=360ss&ocpc_id=139916&plan_id=2327941041&group_id=687803892&keyword=%E8%8B%B1%E8%AF%AD%E8%87%AA%E7%84%B6%E6%8B%BC%E8%AF%BB%E5%AD%97%E6%AF%8D%E7%BB%84%E5%90%88%E5%8F%91%E9%9F%B3&qhclickid=2ee0322320520786
|
|
||||||
```
|
|
||||||
|
|
||||||
英文的26个字母,分为元音和辅音,其中元音只有5个,即a,e,i,o,u,其余字母均为辅音。
|
|
||||||
|
|
||||||
对于大多数辅音来说,它们的发音是一对一的,而有少数几个辅音字母,每个字母有两个或以上的发音。
|
|
||||||
|
|
||||||
### 二、有多个发音的辅音
|
|
||||||
|
|
||||||
#### c 字母
|
|
||||||
c后面接a,o,u的时候,c的发音与字母k发音相同,叫做“hard c sound”:
|
|
||||||
```
|
|
||||||
cat, cap,call,coat,cup
|
|
||||||
```
|
|
||||||
|
|
||||||
当字母c后面接e,i,或y的时候,通常c的发音与字母s发音相同,叫做“soft c sound” :
|
|
||||||
```
|
|
||||||
city, ice, rice, face, cell, cent, voice, pencil, juice
|
|
||||||
```
|
|
||||||
|
|
||||||
有时在e或i前面,c会发/sh/音:
|
|
||||||
```
|
|
||||||
ocean, racial, social
|
|
||||||
```
|
|
||||||
|
|
||||||
#### g 字母
|
|
||||||
g后面接a,o,u的时候,所发的音叫做“hard g sound”:
|
|
||||||
```
|
|
||||||
girl, gas, get, give, go
|
|
||||||
```
|
|
||||||
|
|
||||||
当字母g后面接e,i,或y的时候,有时g的发音与字母j的发音相同,叫做“soft g sound”
|
|
||||||
```
|
|
||||||
age, change, ginger, Egypt, gentle, giraffe, badge
|
|
||||||
```
|
|
||||||
|
|
||||||
特例:forget, give, girl中的g发hard sound。
|
|
||||||
|
|
||||||
#### x 字母
|
|
||||||
x在单词中间或结尾时发/ks/音:
|
|
||||||
```
|
|
||||||
box, next, fix, mix, tax
|
|
||||||
```
|
|
||||||
|
|
||||||
x在单词中间时有时发/gz/音:
|
|
||||||
```
|
|
||||||
exit, exam, exact
|
|
||||||
```
|
|
||||||
|
|
||||||
x在单词起始位置时发/z/音:
|
|
||||||
```
|
|
||||||
xylophone, xerox。
|
|
||||||
```
|
|
||||||
|
|
||||||
#### y 字母
|
|
||||||
y在单词起始位置时发音为辅音:
|
|
||||||
```
|
|
||||||
yes, you, yard, year, yell
|
|
||||||
```
|
|
||||||
|
|
||||||
y在单词或音节中间或结尾时被当做元音;
|
|
||||||
|
|
||||||
y在结尾,单词只有一个音节时y发长/i/音,y在结尾,单词有两个或以上音节时y发长/e/音,y在单词或音节中间时,发短/i/音。
|
|
||||||
|
|
||||||
- 小测试:
|
|
||||||
- my, cry, fly, sky, baby, happy
|
|
||||||
- puppy, hurrygym, nymph
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
---
|
|
||||||
title: 阅读提升
|
|
||||||
createTime: 2025/10/20 16:40:00
|
|
||||||
permalink: /subject/english/reading-writing/
|
|
||||||
---
|
|
||||||
|
|
||||||
# 阅读提升
|
|
||||||
|
|
||||||
- 阅读策略(略读、扫读、精读)
|
|
||||||
- 篇章结构与逻辑(指代、连接、修饰)
|
|
||||||
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
---
|
|
||||||
title: 词汇学习与记忆法
|
|
||||||
createTime: 2025/10/20 16:40:00
|
|
||||||
permalink: /subject/english/vocabulary/
|
|
||||||
---
|
|
||||||
|
|
||||||
# 词汇学习与记忆法
|
|
||||||
|
|
||||||
- 高频词与词根词缀
|
|
||||||
- 主题词汇(校园、科技、社会等)
|
|
||||||
- 间隔重复(SRS)与记忆曲线
|
|
||||||
- 固定搭配与语块(collocations & chunks)
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
---
|
|
||||||
title: 写译
|
|
||||||
createTime: 2025/10/22 10:26:07
|
|
||||||
permalink: /subject/english/writing-translation/
|
|
||||||
---
|
|
||||||
|
|
||||||
# 写译
|
|
||||||
|
|
||||||
- 写作模板与常用句式
|
|
||||||
- 改写与润色(句式多样化、语法准确性)
|
|
||||||
- 翻译(语法、词汇、上下文)
|
|
||||||
@@ -1,193 +0,0 @@
|
|||||||
---
|
|
||||||
title: 密码学基础
|
|
||||||
createTime: 2025/10/27 10:38:57
|
|
||||||
permalink: /theory/cryptography/
|
|
||||||
---
|
|
||||||
|
|
||||||
# 密码学基础
|
|
||||||
|
|
||||||
## 1. 密码学的定义
|
|
||||||
|
|
||||||
### 1.1 基本概念
|
|
||||||
|
|
||||||
**密码学(Cryptography)** 是一门研究信息安全的学科,主要关注如何在不安全的环境中实现安全通信。其核心是通过数学方法对信息进行变换,使得只有授权方能够理解信息内容。
|
|
||||||
|
|
||||||
### 1.2 核心目标
|
|
||||||
|
|
||||||
密码学追求以下四个主要安全目标:
|
|
||||||
|
|
||||||
- **机密性(Confidentiality)**:确保信息只能被授权的人访问
|
|
||||||
- **完整性(Integrity)**:确保信息在传输过程中不被篡改
|
|
||||||
- **认证性(Authentication)**:确认通信双方的身份真实性
|
|
||||||
- **不可否认性(Non-repudiation)**:防止发送方事后否认发送过信息
|
|
||||||
|
|
||||||
### 1.3 重要作用
|
|
||||||
|
|
||||||
密码学在现代信息安全中扮演着至关重要的角色:
|
|
||||||
|
|
||||||
- 保护个人隐私和商业机密
|
|
||||||
- 确保金融交易的安全性
|
|
||||||
- 维护国家安全和军事通信
|
|
||||||
- 支撑互联网基础设施的安全运行
|
|
||||||
|
|
||||||
### 1.4 主要应用场景
|
|
||||||
|
|
||||||
- **网络安全**:HTTPS、VPN、SSL/TLS协议
|
|
||||||
- **数字身份认证**:数字证书、数字签名、双因素认证
|
|
||||||
- **区块链技术**:加密货币、智能合约、分布式账本
|
|
||||||
- **移动通信**:SIM卡加密、移动支付安全
|
|
||||||
- **物联网安全**:设备身份认证、数据传输加密
|
|
||||||
|
|
||||||
### 1.5 基础概念与术语(入门)
|
|
||||||
|
|
||||||
为方便初学者快速建立直觉,先认识密码学中最核心的几个概念:
|
|
||||||
|
|
||||||
**明文(Plaintext)与密文(Ciphertext)**
|
|
||||||
- 明文:未加密的原始消息,例如“HELLO”。
|
|
||||||
- 密文:加密后的消息,人类或未授权系统难以直接理解。
|
|
||||||
|
|
||||||
**加密(Encryption)与解密(Decryption)**
|
|
||||||
- 加密:用密钥将明文转换为密文,记为:
|
|
||||||
|
|
||||||
$$
|
|
||||||
C = E_k(P)
|
|
||||||
$$
|
|
||||||
|
|
||||||
- 解密:用密钥将密文还原为明文,记为:
|
|
||||||
|
|
||||||
$$
|
|
||||||
P = D_k(C)
|
|
||||||
$$
|
|
||||||
|
|
||||||
其中,$P$ 表示明文,$C$ 表示密文,$k$ 表示密钥,$E$ 为加密算法,$D$ 为解密算法。
|
|
||||||
|
|
||||||
**密钥(Key):对称密钥 vs 非对称密钥**
|
|
||||||
- 对称密钥:加密和解密使用相同的密钥,速度快,但密钥分发与管理是难点。
|
|
||||||
- 非对称密钥(公钥密码):加密使用“公钥”,解密使用“私钥”,便于密钥分发,还能支持数字签名。
|
|
||||||
|
|
||||||
对称加密流程示意(同一把密钥):
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
flowchart LR
|
|
||||||
S[发送者] -- 使用共享密钥 K 加密 --> C[(密文)]
|
|
||||||
C -- 使用共享密钥 K 解密 --> R[接收者]
|
|
||||||
```
|
|
||||||
|
|
||||||
非对称加密流程示意(公钥/私钥):
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
flowchart LR
|
|
||||||
S[发送者] -- 使用接收者公钥加密 --> C[(密文)]
|
|
||||||
C -- 使用接收者私钥解密 --> R[接收者]
|
|
||||||
```
|
|
||||||
|
|
||||||
在典型的 RSA 公钥体制中,还可以用一个简洁的数学表达式表示加解密:
|
|
||||||
|
|
||||||
$$
|
|
||||||
\begin{aligned}
|
|
||||||
c &= m^{e} \bmod n,\\
|
|
||||||
m &= c^{d} \bmod n,
|
|
||||||
\end{aligned}
|
|
||||||
$$
|
|
||||||
|
|
||||||
其中 $(e, n)$ 为公钥,$(d, n)$ 为私钥,$m$ 为明文,$c$ 为密文。
|
|
||||||
|
|
||||||
**常见攻击模型简介(只需直观理解)**
|
|
||||||
- 唯密文攻击(COA):攻击者只有密文,尝试恢复明文或密钥。
|
|
||||||
- 已知明文攻击(KPA):攻击者拥有部分“明文-密文”对,用于分析算法或密钥。
|
|
||||||
- 选择明文攻击(CPA):攻击者可选择明文并获取其密文,用于推断密钥或算法结构。
|
|
||||||
- 选择密文攻击(CCA):攻击者可选择密文并得到其解密结果,进一步分析系统弱点。
|
|
||||||
|
|
||||||
直观结论:设计良好的现代密码系统,应当在这些攻击模型下仍保持安全(在合理的参数与假设下)。
|
|
||||||
|
|
||||||
## 2. 密码学历史简述
|
|
||||||
|
|
||||||
### 2.1 古代密码学(公元前-15世纪)
|
|
||||||
|
|
||||||
**凯撒密码(Caesar Cipher)**
|
|
||||||
- 时间:公元前1世纪
|
|
||||||
- 原理:字母移位加密
|
|
||||||
- 示例:将字母向后移动3位,A→D,B→E
|
|
||||||
|
|
||||||
**斯巴达密码棒(Scytale)**
|
|
||||||
- 时间:公元前5世纪
|
|
||||||
- 原理:缠绕在特定直径木棒上的皮条
|
|
||||||
|
|
||||||
古典密码简述:
|
|
||||||
- 核心思路:替换或移位(重新排列)字符。
|
|
||||||
- 代表示例:凯撒(替换)、栅栏(移位)、维吉尼亚(多表替换)。
|
|
||||||
- 直觉目标:混淆结构、增加猜测难度;但易受频率分析。
|
|
||||||
|
|
||||||
### 2.2 文艺复兴时期(15-18世纪)
|
|
||||||
|
|
||||||
**维吉尼亚密码(Vigenère Cipher)**
|
|
||||||
- 时间:16世纪
|
|
||||||
- 原理:多表替换密码
|
|
||||||
- 特点:比单表替换更安全
|
|
||||||
|
|
||||||
**博福特密码(Beaufort Cipher)**
|
|
||||||
- 时间:18世纪
|
|
||||||
- 原理:改进的维吉尼亚密码
|
|
||||||
|
|
||||||
### 2.3 近代密码学(19-20世纪中期)
|
|
||||||
|
|
||||||
**恩尼格玛密码机(Enigma)**
|
|
||||||
- 时间:二战时期
|
|
||||||
- 原理:机械转子密码机
|
|
||||||
- 重要性:推动了现代密码分析的发展
|
|
||||||
|
|
||||||
**香农的信息论**
|
|
||||||
- 时间:1949年
|
|
||||||
- 贡献:为密码学奠定了数学理论基础
|
|
||||||
|
|
||||||
### 2.4 现代密码学(1970年代至今)
|
|
||||||
|
|
||||||
**DES算法**
|
|
||||||
- 时间:1977年
|
|
||||||
- 意义:第一个公开的加密标准
|
|
||||||
|
|
||||||
**RSA算法**
|
|
||||||
- 时间:1977年
|
|
||||||
- 意义:第一个实用的公钥密码系统
|
|
||||||
|
|
||||||
**AES算法**
|
|
||||||
- 时间:2001年
|
|
||||||
- 意义:取代DES的新一代加密标准
|
|
||||||
|
|
||||||
现代密码简述:
|
|
||||||
- 对称加密:同一密钥加解密,适合大量数据(示例:AES/DES/3DES)。
|
|
||||||
|
|
||||||
$$
|
|
||||||
C = E_k(P), \quad P = D_k(C)
|
|
||||||
$$
|
|
||||||
|
|
||||||
- 非对称加密:公钥加密、私钥解密,便于密钥分发与数字签名(示例:RSA/ECC)。
|
|
||||||
|
|
||||||
$$
|
|
||||||
c = m^{e} \bmod n, \quad m = c^{d} \bmod n
|
|
||||||
$$
|
|
||||||
|
|
||||||
- 密钥交换:Diffie–Hellman 在不安全信道建立共享密钥。
|
|
||||||
- 数字签名:私钥签名、公钥验证,保障真实性与不可否认性。
|
|
||||||
|
|
||||||
### 2.5 关键历史时间线
|
|
||||||
|
|
||||||
```
|
|
||||||
公元前5世纪:斯巴达密码棒
|
|
||||||
公元前1世纪:凯撒密码
|
|
||||||
16世纪:维吉尼亚密码
|
|
||||||
1918年:一次一密密码本
|
|
||||||
1949年:香农信息论
|
|
||||||
1977年:DES和RSA算法
|
|
||||||
2001年:AES标准
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 总结
|
|
||||||
|
|
||||||
密码学作为信息安全的基石,经历了从简单替换到复杂数学算法的漫长发展历程。现代密码学建立在严格的数学基础之上,通过对称加密、非对称加密等多种技术手段,为数字世界提供了可靠的安全保障。
|
|
||||||
|
|
||||||
理解密码学的基本原理和分类,有助于我们更好地应用这些技术来保护信息安全,同时也为深入学习更高级的密码学概念奠定基础。
|
|
||||||
|
|
||||||
本篇笔记的所有代码开源于:[https://gitea.simengweb.com/si-meng-spec/cryptography-example-code](https://gitea.simengweb.com/si-meng-spec/cryptography-example-code)
|
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
---
|
|
||||||
title: 置换密码 - 等待完善
|
|
||||||
createTime: 2025/10/29 13:50:49
|
|
||||||
permalink: /theory/cryptography/permutation-encryption/
|
|
||||||
---
|
|
||||||
|
|
||||||
# 置换密码(Permutation / Transposition Ciphers)
|
|
||||||
|
|
||||||
置换密码的核心思想不是“把字母换成别的字母”(替换),而是**重新排列明文字符的位置**。也就是说:
|
|
||||||
- 明文字母的集合不变,顺序发生了改变;
|
|
||||||
- 由于字母频率不变,置换密码依然会暴露统计特征,但单词的结构与位置模式被打散。
|
|
||||||
|
|
||||||
与“替换密码”相比,置换密码更像是“洗牌”:把原本顺序排列的牌重新打乱。单独使用时并不安全,但与替换联合使用(乘积密码)能显著增强安全性。
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
flowchart LR
|
|
||||||
P[明文] --> A{根据密钥生成位置}
|
|
||||||
A --> B[重新排列字符]
|
|
||||||
B --> C[密文]
|
|
||||||
```
|
|
||||||
|
|
||||||
## 一、栅栏密码(Rail Fence Cipher)
|
|
||||||
|
|
||||||
**工作原理**:
|
|
||||||
将明文按“Z字形”写入若干行(称为“栅栏/轨道”),再按行依次读出即得到密文。轨道数即为密钥。
|
|
||||||
|
|
||||||
**示意**(以 3 轨为例):
|
|
||||||
```
|
|
||||||
轨1: 0 4 8 ...
|
|
||||||
轨2: 1 3 5 7 9 ...
|
|
||||||
轨3: 2 6 ...
|
|
||||||
```
|
|
||||||
|
|
||||||
**示例**:
|
|
||||||
明文:`HELLOWORLD`
|
|
||||||
轨道数:`3`
|
|
||||||
- 轨1(索引 0,4,8):`H O L`
|
|
||||||
- 轨2(索引 1,3,5,7,9):`E L W R D`
|
|
||||||
- 轨3(索引 2,6):`L O`
|
|
||||||
|
|
||||||
密文为各轨串联:`HOL` + `ELWRD` + `LO` → `HOLELWRDLO`
|
|
||||||
|
|
||||||
**数学表示**:
|
|
||||||
设明文 $P = p_0 p_1 \dots p_{n-1}$,根据密钥生成一个位置序列 $s_0, s_1, \dots, s_{n-1}$(即置换次序),则:
|
|
||||||
$$
|
|
||||||
C_j = p_{s_j}, \quad j = 0,1,\dots,n-1
|
|
||||||
$$
|
|
||||||
解密使用逆序列 $t = s^{-1}$:
|
|
||||||
$$
|
|
||||||
p_i = C_{t_i}, \quad i = 0,1,\dots,n-1
|
|
||||||
$$
|
|
||||||
|
|
||||||
**特点**:
|
|
||||||
- 实现简单,直观“打乱顺序”
|
|
||||||
- 频率不变,难以抵抗纯统计分析;但位置模式被破坏,较难直接猜词
|
|
||||||
- 作为教学与与替换密码的组合(乘积密码)更有价值
|
|
||||||
|
|
||||||
## 二、列移位置换(Columnar Transposition)
|
|
||||||
|
|
||||||
**工作原理**:
|
|
||||||
选择一个关键词,将明文按列填入表格,再按关键词的字母排序对列进行重排,最终按列或按行读出密文。
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
flowchart LR
|
|
||||||
A[明文填入表格] --> B{按关键词排序列}
|
|
||||||
B --> C[重排读取]
|
|
||||||
C --> D[密文]
|
|
||||||
```
|
|
||||||
|
|
||||||
**简例(概念演示)**:
|
|
||||||
明文:`ATTACKATDAWN`
|
|
||||||
关键词:`ZEBRA`(按字母表排序为 `A B E R Z`)
|
|
||||||
1) 将明文逐行填入 5 列表格;
|
|
||||||
2) 按关键词排序(A→B→E→R→Z)重排列;
|
|
||||||
3) 按重排后的列依次读出密文。
|
|
||||||
|
|
||||||
(实际实现时需要处理明文长度不足一整行的填充策略,如使用 `X` 或留空。)
|
|
||||||
|
|
||||||
**数学表示(一般置换模型)**:
|
|
||||||
关键词决定一个列置换 $\pi$,其作用是重新排列列索引。若把明文按列读取为序列 $P$,加密可抽象为:
|
|
||||||
$$
|
|
||||||
C = \operatorname{Permute}_{\pi}(P), \quad P = \operatorname{Permute}_{\pi^{-1}}(C)
|
|
||||||
$$
|
|
||||||
|
|
||||||
**特点**:
|
|
||||||
- 比栅栏更灵活,关键词让置换更“难猜”
|
|
||||||
- 仍保留频率分布,易受已知明文/选择明文的结构分析攻击
|
|
||||||
- 常与替换结合形成更强的乘积密码(如 ADFGX/ADFGVX 密码)
|
|
||||||
|
|
||||||
## 三、联合与加固:置换 × 替换
|
|
||||||
|
|
||||||
将“替换”与“置换”组合(先替换后置换,或多轮交替)能显著增强安全性:
|
|
||||||
- 替换打乱统计特征(字母频率分布变平)
|
|
||||||
- 置换打乱位置结构(模式与相邻关系被破坏)
|
|
||||||
|
|
||||||
这种思路在现代密码设计中仍然常见(“混淆与扩散”理念),尽管算法形式已经大为不同。
|
|
||||||
|
|
||||||
## 四、安全性与弱点(直观理解)
|
|
||||||
- 单独的置换密码不改变字母频率,抵抗统计攻击能力有限
|
|
||||||
- 容易受到已知明文/选择明文攻击(通过结构猜测置换)
|
|
||||||
- 多轮、复杂置换能提高攻击成本,但不建议单独用于实际安全场景
|
|
||||||
|
|
||||||
## 五、小练习(可选)
|
|
||||||
试着把你自己的名字用 3 轨栅栏加密;然后写出解密过程(先确定轨道索引,再按逆序重建原文)。
|
|
||||||
|
|
||||||
## 附件:
|
|
||||||
具体的使用样例代码请参考:[https://gitea.simengweb.com/si-meng-spec/cryptography-example-code](https://gitea.simengweb.com/si-meng-spec/cryptography-example-code)
|
|
||||||
@@ -1,170 +0,0 @@
|
|||||||
---
|
|
||||||
title: 替换密码
|
|
||||||
createTime: 2025/10/27 10:47:42
|
|
||||||
permalink: /theory/cryptography/substitution-ciphers/
|
|
||||||
---
|
|
||||||
|
|
||||||
# 替换密码(Substitution Ciphers)
|
|
||||||
|
|
||||||
我们一起来系统梳理古典加密算法(Classical Ciphers)。这些算法虽然在现代已不再安全,但它们是密码学发展的基石,蕴含了替换、置换、密钥等核心思想,非常适合理解密码学的基本原理。
|
|
||||||
|
|
||||||
替换密码的核心思想是“一对一”或“多对一”的字符映射:把明文中的每一个字母(或符号)按照事先约定好的规则,替换成另一个字母(或符号)。
|
|
||||||
|
|
||||||
这种映射可以是固定不变的(如凯撒密码的“统一移位”),也可以是依赖密钥动态变化的(如维吉尼亚密码的“周期移位”)。
|
|
||||||
|
|
||||||
由于密文保留了原始字母的出现频率,只是“换了一张皮”,所以替换密码在本质上没有改变字母的统计特性,这也为频率分析攻击留下了突破口。
|
|
||||||
|
|
||||||
替换操作可以手工完成,也可以通过查表、转盘、甚至机械电路实现,是后续更复杂多表替换与乘积密码的雏形。
|
|
||||||
|
|
||||||
## 一、凯撒密码(Caesar Cipher)
|
|
||||||
|
|
||||||
**工作原理**:
|
|
||||||
凯撒密码是一种循环移位密码,将字母表视为一个环形结构。加密时每个字母向后移动固定位置 $k$,解密时向前移动相同位置。
|
|
||||||
|
|
||||||
**数学表示**:
|
|
||||||
设字母 A-Z 对应数字 0-25,则:
|
|
||||||
|
|
||||||
加密公式:
|
|
||||||
$$E(x) = (x + k) \mod 26$$
|
|
||||||
|
|
||||||
解密公式:
|
|
||||||
$$D(x) = (x - k) \mod 26$$
|
|
||||||
|
|
||||||
其中 $x$ 是明文字母编号,$k$ 是密钥(0 ≤ k ≤ 25)。
|
|
||||||
|
|
||||||
**特点**:
|
|
||||||
- 实现简单,易于理解
|
|
||||||
- 密钥空间仅 $26$ 种可能,安全性极低
|
|
||||||
- 易受频率分析攻击
|
|
||||||
- 主要具有教学价值
|
|
||||||
|
|
||||||
|
|
||||||
## 二、单表替换密码(Simple Substitution Cipher)
|
|
||||||
|
|
||||||
**工作原理**:
|
|
||||||
单表替换密码是凯撒密码的泛化形式,它使用一个随机的字母替换表,而不是固定的移位。每个明文字母都被唯一地映射到一个密文字母,形成一对一的替换关系。
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
graph LR
|
|
||||||
A[明文字母] --> B{替换表}
|
|
||||||
B --> C[密文字母]
|
|
||||||
```
|
|
||||||
|
|
||||||
**数学表示**:
|
|
||||||
设字母表 $\Sigma = \{A,B,C,...,Z\}$,替换函数 $f: \Sigma \rightarrow \Sigma$ 是一个双射(一一对应),则:
|
|
||||||
|
|
||||||
加密公式:
|
|
||||||
$$E(x) = f(x)$$
|
|
||||||
|
|
||||||
解密公式:
|
|
||||||
$$D(y) = f^{-1}(y)$$
|
|
||||||
|
|
||||||
其中 $f^{-1}$ 是 $f$ 的逆函数。
|
|
||||||
|
|
||||||
**密钥空间**:
|
|
||||||
单表替换密码的密钥空间是所有可能的字母排列,大小为:
|
|
||||||
$$|K| = 26! \approx 4.03 \times 10^{26}$$
|
|
||||||
|
|
||||||
这个巨大的密钥空间使得暴力破解在计算上不可行。
|
|
||||||
|
|
||||||
**示例**:
|
|
||||||
假设替换表为:
|
|
||||||
```
|
|
||||||
A→Q, B→W, C→E, D→R, E→T, F→Y, G→U, H→I, I→O, J→P,
|
|
||||||
K→A, L→S, M→D, N→F, O→G, P→H, Q→J, R→K, S→L, T→Z,
|
|
||||||
U→X, V→C, W→V, X→B, Y→N, Z→M
|
|
||||||
```
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
flowchart LR
|
|
||||||
A[明文: HELLO] --> B[替换加密]
|
|
||||||
B --> C[密文: ITSSG]
|
|
||||||
C --> D[逆替换解密]
|
|
||||||
D --> E[明文: HELLO]
|
|
||||||
```
|
|
||||||
|
|
||||||
**安全性分析**:
|
|
||||||
虽然单表替换密码的密钥空间巨大,但它仍然易受**频率分析攻击**。因为:
|
|
||||||
|
|
||||||
1. **字母频率保留**:高频字母(如E、T、A)在密文中仍然是高频
|
|
||||||
2. **单词模式保留**:常见单词模式(如"THE"、"ING")在密文中保持相同模式
|
|
||||||
3. **双字母频率**:常见字母对(如"TH"、"ER")的频率特征仍然存在
|
|
||||||
|
|
||||||
**攻击方法**:
|
|
||||||
- 单字母频率分析
|
|
||||||
- 双字母频率分析
|
|
||||||
- 单词长度和模式分析
|
|
||||||
- 已知明文攻击
|
|
||||||
|
|
||||||
**特点**:
|
|
||||||
- 密钥空间巨大($26!$),理论上难以暴力破解
|
|
||||||
- 仍然易受统计攻击
|
|
||||||
- 是密码学历史上重要的里程碑
|
|
||||||
- 为现代密码学提供了重要启示
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 三、维吉尼亚密码(Vigenère Cipher)
|
|
||||||
|
|
||||||
**工作原理**:
|
|
||||||
维吉尼亚密码是一种多表替换密码,它使用一个关键词来决定每次替换的凯撒密码移位量。关键词的每个字母对应一个移位量,明文的每个字母根据关键词的循环使用进行替换。
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
graph LR
|
|
||||||
A[明文] --> B{关键词循环扩展}
|
|
||||||
B --> C[明文与关键词按位组合]
|
|
||||||
C --> D{多表替换}
|
|
||||||
D --> E[密文]
|
|
||||||
```
|
|
||||||
|
|
||||||
**数学表示**:
|
|
||||||
设字母 A-Z 对应数字 0-25。
|
|
||||||
明文 $P = p_0 p_1 ... p_{n-1}$
|
|
||||||
关键词 $K = k_0 k_1 ... k_{m-1}$ (长度为 $m$)
|
|
||||||
|
|
||||||
加密公式:
|
|
||||||
$$E(p_i) = (p_i + k_{i \pmod m}) \mod 26$$
|
|
||||||
|
|
||||||
解密公式:
|
|
||||||
$$D(c_i) = (c_i - k_{i \pmod m}) \mod 26$$
|
|
||||||
|
|
||||||
其中 $p_i$ 是明文第 $i$ 个字母的数字表示,$k_{i \pmod m}$ 是关键词循环后对应第 $i$ 个字母的数字表示,$c_i$ 是密文第 $i$ 个字母的数字表示。
|
|
||||||
|
|
||||||
**示例**:
|
|
||||||
明文:`ATTACKATDAWN`
|
|
||||||
关键词:`LEMON`
|
|
||||||
|
|
||||||
1. **关键词循环扩展**:
|
|
||||||
将关键词 `LEMON` 循环扩展至与明文等长:`LEMONLEMONLE`
|
|
||||||
|
|
||||||
2. **明文与关键词按位组合(数字表示)**:
|
|
||||||
将明文和扩展后的关键词转换为数字 (A=0, B=1, ..., Z=25)。
|
|
||||||
明文数字: `0 19 19 0 2 10 0 19 3 0 22 13`
|
|
||||||
关键词数字: `11 4 12 14 13 11 4 12 14 13 11 4`
|
|
||||||
|
|
||||||
3. **加密运算**:
|
|
||||||
对每对明文数字 $p_i$ 和关键词数字 $k_{i \pmod m}$ 执行 $(p_i + k_{i \pmod m}) \mod 26$ 运算。
|
|
||||||
例如:
|
|
||||||
- 第一个字母:明文 A (0) + 关键词 L (11) = $(0 + 11) \mod 26 = 11 \rightarrow L$
|
|
||||||
- 第二个字母:明文 T (19) + 关键词 E (4) = $(19 + 4) \mod 26 = 23 \rightarrow X$
|
|
||||||
- 第三个字母:明文 T (19) + 关键词 M (12) = $(19 + 12) \mod 26 = 5 \rightarrow F$
|
|
||||||
...
|
|
||||||
最终密文:`LXFOPVEFRNHR`
|
|
||||||
|
|
||||||
|
|
||||||
**安全性分析**:
|
|
||||||
维吉尼亚密码比单表替换密码更安全,因为它引入了**多表替换**,使得密文的字母频率分布趋于平坦,从而抵抗了简单的频率分析攻击。
|
|
||||||
|
|
||||||
然而,它并非绝对安全,主要弱点在于**关键词的周期性**:
|
|
||||||
1. **Kasiski 测试**:通过分析密文中重复出现的字母组,可以推断出关键词的长度。
|
|
||||||
2. **频率分析(针对子密码)**:一旦关键词长度确定,密文可以被分成若干个凯撒密码,然后对每个子密码进行频率分析。
|
|
||||||
|
|
||||||
**特点**:
|
|
||||||
- 多表替换,比单表替换密码更安全
|
|
||||||
- 引入了关键词的概念,增强了密钥的复杂性
|
|
||||||
- 易受Kasiski测试和频率分析的组合攻击
|
|
||||||
- 在历史上曾被认为是“牢不可破的密码”
|
|
||||||
|
|
||||||
|
|
||||||
## 附件:
|
|
||||||
具体的使用样例代码请参考:[https://gitea.simengweb.com/si-meng-spec/cryptography-example-code](https://gitea.simengweb.com/si-meng-spec/cryptography-example-code)
|
|
||||||
@@ -7,33 +7,24 @@
|
|||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20.6.0 || >=22.0.0"
|
"node": "^20.6.0 || >=22.0.0"
|
||||||
},
|
},
|
||||||
"overrides": {
|
|
||||||
"@vuepress/helper": "2.0.0-rc.123"
|
|
||||||
},
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"docs:dev": "vuepress dev docs",
|
"docs:dev": "vuepress dev docs",
|
||||||
"docs:dev-clean": "vuepress dev docs --clean-cache --clean-temp",
|
"docs:dev-clean": "vuepress dev docs --clean-cache --clean-temp",
|
||||||
"docs:clear-cache": "node scripts/clear-vuepress-cache.mjs",
|
|
||||||
"docs:build": "vuepress build docs --clean-cache --clean-temp",
|
"docs:build": "vuepress build docs --clean-cache --clean-temp",
|
||||||
"docs:verify": "node scripts/verify-site.mjs",
|
|
||||||
"docs:place-ragflow-guide": "node scripts/place-ragflow-guide.mjs",
|
|
||||||
"docs:preview": "http-server docs/.vuepress/dist",
|
"docs:preview": "http-server docs/.vuepress/dist",
|
||||||
"vp-update": "npx vp-update"
|
"vp-update": "npx vp-update"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vuepress/helper": "2.0.0-rc.123",
|
"@vuepress/bundler-vite": "2.0.0-rc.24",
|
||||||
"@vuepress/bundler-vite": "2.0.0-rc.26",
|
"@vuepress/plugin-umami-analytics": "^2.0.0-rc.112",
|
||||||
"@vuepress/plugin-umami-analytics": "2.0.0-rc.112",
|
|
||||||
"artalk": "^2.9.1",
|
"artalk": "^2.9.1",
|
||||||
"http-server": "^14.1.1",
|
"http-server": "^14.1.1",
|
||||||
"typescript": "^5.9.2",
|
"typescript": "^5.9.2",
|
||||||
"vue": "^3.5.21",
|
"vue": "^3.5.21",
|
||||||
"vuepress": "2.0.0-rc.26",
|
"vuepress": "2.0.0-rc.24",
|
||||||
"vuepress-theme-plume": "1.0.0-rc.192"
|
"vuepress-theme-plume": "1.0.0-rc.164"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vuepress/plugin-comment": "2.0.0-rc.123",
|
"@waline/client": "^3.6.0"
|
||||||
"@waline/client": "^3.13.0",
|
|
||||||
"mermaid": "^11.12.1"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,153 +0,0 @@
|
|||||||
@echo off
|
|
||||||
setlocal EnableExtensions EnableDelayedExpansion
|
|
||||||
|
|
||||||
REM ==========================================
|
|
||||||
REM Configuration
|
|
||||||
REM ==========================================
|
|
||||||
chcp 65001 >nul
|
|
||||||
|
|
||||||
REM Move to project root
|
|
||||||
cd /d "%~dp0.."
|
|
||||||
set "PROJECT_DIR=%CD%\"
|
|
||||||
|
|
||||||
set "DIST_DIR=%PROJECT_DIR%docs\.vuepress\dist"
|
|
||||||
set "CACHE_DIR=%PROJECT_DIR%docs\.vuepress\.cache"
|
|
||||||
set "TEMP_DIR=%PROJECT_DIR%docs\.vuepress\.temp"
|
|
||||||
set "PUBLISH_DIR=%PROJECT_DIR%_publish"
|
|
||||||
set "REMOTE_URL=https://gitea.simengweb.com/si-meng-spec/build_notes_simengweb.git"
|
|
||||||
set "BRANCH=main"
|
|
||||||
|
|
||||||
REM High memory limit for better performance/stability (8GB)
|
|
||||||
set "NODE_OPTIONS=--max-old-space-size=8192"
|
|
||||||
|
|
||||||
REM Disable Git SSL Verification for this session
|
|
||||||
git config --global http.sslVerify false
|
|
||||||
git config --global http.sslBackend openssl
|
|
||||||
|
|
||||||
REM Args
|
|
||||||
set "COMMIT_MSG=%~1"
|
|
||||||
if "%COMMIT_MSG%"=="" set "COMMIT_MSG=Build: %DATE% %TIME%"
|
|
||||||
|
|
||||||
echo ==========================================
|
|
||||||
echo Start Build and Publish
|
|
||||||
echo Project Dir: %PROJECT_DIR%
|
|
||||||
echo Memory Limit: 8192 MB
|
|
||||||
echo ==========================================
|
|
||||||
|
|
||||||
REM ==========================================
|
|
||||||
REM 1. Cleanup
|
|
||||||
REM ==========================================
|
|
||||||
echo [1/5] Cleaning up old artifacts...
|
|
||||||
|
|
||||||
if exist "%DIST_DIR%" (
|
|
||||||
echo - Removing %DIST_DIR%
|
|
||||||
rd /s /q "%DIST_DIR%"
|
|
||||||
)
|
|
||||||
if exist "%CACHE_DIR%" (
|
|
||||||
echo - Removing %CACHE_DIR%
|
|
||||||
rd /s /q "%CACHE_DIR%"
|
|
||||||
)
|
|
||||||
if exist "%TEMP_DIR%" (
|
|
||||||
echo - Removing %TEMP_DIR%
|
|
||||||
rd /s /q "%TEMP_DIR%"
|
|
||||||
)
|
|
||||||
REM Clean node_modules for a fresh install every time (Ensures clean state)
|
|
||||||
if exist "%PROJECT_DIR%node_modules" (
|
|
||||||
echo - Removing node_modules (Fresh Install Mode)
|
|
||||||
rd /s /q "%PROJECT_DIR%node_modules"
|
|
||||||
)
|
|
||||||
|
|
||||||
REM ==========================================
|
|
||||||
REM 2. Install Dependencies
|
|
||||||
REM ==========================================
|
|
||||||
echo [2/5] Installing dependencies (npm ci)...
|
|
||||||
call npm ci
|
|
||||||
if %ERRORLEVEL% NEQ 0 (
|
|
||||||
echo [Error] npm ci failed.
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
REM ==========================================
|
|
||||||
REM 3. Build
|
|
||||||
REM ==========================================
|
|
||||||
echo [3/5] Building docs...
|
|
||||||
call npm run docs:build
|
|
||||||
if %ERRORLEVEL% NEQ 0 (
|
|
||||||
echo [Error] Build failed.
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
if not exist "%DIST_DIR%" (
|
|
||||||
echo [Error] Dist directory not found after build.
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
REM ==========================================
|
|
||||||
REM 4. Prepare Publish Directory
|
|
||||||
REM ==========================================
|
|
||||||
echo [4/5] Preparing publish directory...
|
|
||||||
|
|
||||||
if not exist "%PUBLISH_DIR%\.git" (
|
|
||||||
echo - Cloning publish repo...
|
|
||||||
if exist "%PUBLISH_DIR%" rd /s /q "%PUBLISH_DIR%"
|
|
||||||
git clone "%REMOTE_URL%" "%PUBLISH_DIR%"
|
|
||||||
if !ERRORLEVEL! NEQ 0 exit /b 1
|
|
||||||
) else (
|
|
||||||
echo - Updating publish repo...
|
|
||||||
pushd "%PUBLISH_DIR%"
|
|
||||||
git fetch --all
|
|
||||||
git checkout "%BRANCH%"
|
|
||||||
git reset --hard "origin/%BRANCH%"
|
|
||||||
popd
|
|
||||||
)
|
|
||||||
|
|
||||||
echo - Cleaning publish directory content (preserving .git)...
|
|
||||||
pushd "%PUBLISH_DIR%"
|
|
||||||
for /f "delims=" %%F in ('dir /a /b') do (
|
|
||||||
if /I not "%%F"==".git" (
|
|
||||||
if exist "%%F" (
|
|
||||||
attrib -R "%%F" >nul 2>&1
|
|
||||||
rd /s /q "%%F" 2>nul
|
|
||||||
del /f /q "%%F" 2>nul
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
popd
|
|
||||||
|
|
||||||
echo - Copying new build artifacts...
|
|
||||||
robocopy "%DIST_DIR%" "%PUBLISH_DIR%" *.* /E /NFL /NDL /NP /NJH /NJS >nul
|
|
||||||
if %ERRORLEVEL% GEQ 8 (
|
|
||||||
echo [Error] Robocopy failed.
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
REM ==========================================
|
|
||||||
REM 5. Push to Remote
|
|
||||||
REM ==========================================
|
|
||||||
echo [5/5] Pushing changes...
|
|
||||||
pushd "%PUBLISH_DIR%"
|
|
||||||
|
|
||||||
if defined GIT_USERNAME git config user.name "%GIT_USERNAME%"
|
|
||||||
if defined GIT_EMAIL git config user.email "%GIT_EMAIL%"
|
|
||||||
|
|
||||||
git add -A
|
|
||||||
git diff --cached --quiet
|
|
||||||
if %ERRORLEVEL% EQU 0 (
|
|
||||||
echo [Info] No changes to commit.
|
|
||||||
) else (
|
|
||||||
git commit -m "%COMMIT_MSG%"
|
|
||||||
echo - Pushing to %BRANCH%...
|
|
||||||
git push origin "%BRANCH%"
|
|
||||||
if !ERRORLEVEL! NEQ 0 (
|
|
||||||
echo [Error] Git push failed.
|
|
||||||
popd
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
echo [Success] Pushed successfully.
|
|
||||||
)
|
|
||||||
popd
|
|
||||||
|
|
||||||
echo ==========================================
|
|
||||||
echo All Done!
|
|
||||||
echo ==========================================
|
|
||||||
exit /b 0
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
# 提交笔记站源码变更(尊重 .gitignore,不会加入 _publish、dist、PROJECT_BRIEF.local.md 等)
|
|
||||||
# 用法: .\script\commit-notes.ps1 -m "提交说明"
|
|
||||||
param(
|
|
||||||
[Parameter(Mandatory = $true)]
|
|
||||||
[string] $Message
|
|
||||||
)
|
|
||||||
$ErrorActionPreference = "Stop"
|
|
||||||
$root = Split-Path -Parent $PSScriptRoot
|
|
||||||
Set-Location $root
|
|
||||||
|
|
||||||
git add docs/.vuepress docs/blog .cursor/rules script/commit-notes.ps1
|
|
||||||
$staged = @(git diff --cached --name-only)
|
|
||||||
if ($staged.Count -eq 0) {
|
|
||||||
Write-Host "No staged changes."
|
|
||||||
exit 0
|
|
||||||
}
|
|
||||||
git commit -m $Message
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
@echo off
|
|
||||||
cd /d "%~dp0.."
|
|
||||||
npm run docs:dev -- --host 0.0.0.0 --port 8888
|
|
||||||
@@ -1,2 +1 @@
|
|||||||
cd "$(dirname "$0")/.."
|
|
||||||
npm run docs:dev -- --host 0.0.0.0 --port 8888
|
npm run docs:dev -- --host 0.0.0.0 --port 8888
|
||||||