0%

hexo博客next主题使用echarts图表

创建charts.js

在next/scripts/helpers下创建charts.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
const cheerio = require('cheerio')
const moment = require('moment')

hexo.extend.filter.register('after_render:html', function (locals) {
const $ = cheerio.load(locals)
const post = $('#posts-chart')
const tag = $('#tags-chart')
const category = $('#categories-chart')
const htmlEncode = false

if (post.length > 0 || tag.length > 0 || category.length > 0) {
if (post.length > 0 && $('#postsChart').length === 0) {
if (post.attr('data-encode') === 'true') htmlEncode = true
post.after(postsChart(post.attr('data-start')))
}
if (tag.length > 0 && $('#tagsChart').length === 0) {
if (tag.attr('data-encode') === 'true') htmlEncode = true
tag.after(tagsChart(tag.attr('data-length')))
}
if (category.length > 0 && $('#categoriesChart').length === 0) {
if (category.attr('data-encode') === 'true') htmlEncode = true
category.after(categoriesChart(category.attr('data-parent')))
}

if (htmlEncode) {
return $.root().html().replace(/&#/g, '&#')
} else {
return $.root().html()
}
} else {
return locals
}
}, 15)

function postsChart (startMonth) {
const startDate = moment(startMonth || '2020-01')
const endDate = moment()

const monthMap = new Map()
const dayTime = 3600 * 24 * 1000
for (let time = startDate; time <= endDate; time += dayTime) {
const month = moment(time).format('YYYY-MM')
if (!monthMap.has(month)) {
monthMap.set(month, 0)
}
}
hexo.locals.get('posts').forEach(function (post) {
const month = post.date.format('YYYY-MM')
if (monthMap.has(month)) {
monthMap.set(month, monthMap.get(month) + 1)
}
})
const monthArr = JSON.stringify([...monthMap.keys()])
const monthValueArr = JSON.stringify([...monthMap.values()])

return `
<script id="postsChart">
var color = document.documentElement.getAttribute('data-theme') === 'light' ? '#4c4948' : 'rgba(255,255,255,0.7)'
var postsChart = echarts.init(document.getElementById('posts-chart'), 'light');
var postsOption = {
title: {
text: '文章发布统计图',
x: 'center',
textStyle: {
color: color
}
},
tooltip: {
trigger: 'axis'
},
xAxis: {
name: '日期',
type: 'category',
boundaryGap: false,
nameTextStyle: {
color: color
},
axisTick: {
show: false
},
axisLabel: {
show: true,
color: color
},
axisLine: {
show: true,
lineStyle: {
color: color
}
},
data: ${monthArr}
},
yAxis: {
name: '文章篇数',
type: 'value',
nameTextStyle: {
color: color
},
splitLine: {
show: false
},
axisTick: {
show: false
},
axisLabel: {
show: true,
color: color
},
axisLine: {
show: true,
lineStyle: {
color: color
}
}
},
series: [{
name: '文章篇数',
type: 'line',
smooth: true,
lineStyle: {
width: 0
},
showSymbol: false,
itemStyle: {
opacity: 1,
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: 'rgba(128, 255, 165)'
},
{
offset: 1,
color: 'rgba(1, 191, 236)'
}])
},
areaStyle: {
opacity: 1,
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: 'rgba(128, 255, 165)'
}, {
offset: 1,
color: 'rgba(1, 191, 236)'
}])
},
data: ${monthValueArr},
markLine: {
data: [{
name: '平均值',
type: 'average',
label: {
color: color
}
}]
}
}]
};
postsChart.setOption(postsOption);
window.addEventListener('resize', () => {
postsChart.resize();
});
postsChart.on('click', 'series', (event) => {
if (event.componentType === 'series') window.location.href = '/archives/' + event.name.replace('-', '/');
});
</script>`
}

function tagsChart (len) {
const tagArr = []
hexo.locals.get('tags').map(function (tag) {
tagArr.push({ name: tag.name, value: tag.length, path: tag.path })
})
tagArr.sort((a, b) => { return b.value - a.value })

const dataLength = Math.min(tagArr.length, len) || tagArr.length
const tagNameArr = []
for (let i = 0; i < dataLength; i++) {
tagNameArr.push(tagArr[i].name)
}
const tagNameArrJson = JSON.stringify(tagNameArr)
const tagArrJson = JSON.stringify(tagArr)

return `
<script id="tagsChart">
var color = document.documentElement.getAttribute('data-theme') === 'light' ? '#4c4948' : 'rgba(255,255,255,0.7)'
var tagsChart = echarts.init(document.getElementById('tags-chart'), 'light');
var tagsOption = {
title: {
text: 'Top ${dataLength} 标签统计图',
x: 'center',
textStyle: {
color: color
}
},
tooltip: {},
xAxis: {
name: '标签',
type: 'category',
nameTextStyle: {
color: color
},
axisTick: {
show: false
},
axisLabel: {
show: true,
color: color,
interval: 0
},
axisLine: {
show: true,
lineStyle: {
color: color
}
},
data: ${tagNameArrJson}
},
yAxis: {
name: '文章篇数',
type: 'value',
splitLine: {
show: false
},
nameTextStyle: {
color: color
},
axisTick: {
show: false
},
axisLabel: {
show: true,
color: color
},
axisLine: {
show: true,
lineStyle: {
color: color
}
}
},
series: [{
name: '文章篇数',
type: 'bar',
data: ${tagArrJson},
itemStyle: {
borderRadius: [5, 5, 0, 0],
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: 'rgba(128, 255, 165)'
},
{
offset: 1,
color: 'rgba(1, 191, 236)'
}])
},
emphasis: {
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: 'rgba(128, 255, 195)'
},
{
offset: 1,
color: 'rgba(1, 211, 255)'
}])
}
},
markLine: {
data: [{
name: '平均值',
type: 'average',
label: {
color: color
}
}]
}
}]
};
tagsChart.setOption(tagsOption);
window.addEventListener('resize', () => {
tagsChart.resize();
});
tagsChart.on('click', 'series', (event) => {
if(event.data.path) window.location.href = '/' + event.data.path;
});
</script>`
}

function categoriesChart (dataParent) {
const categoryArr = []
let categoryParentFlag = false
hexo.locals.get('categories').map(function (category) {
if (category.parent) categoryParentFlag = true
categoryArr.push({
name: category.name,
value: category.length,
path: category.path,
id: category._id,
parentId: category.parent || '0'
})
})
categoryParentFlag = categoryParentFlag && dataParent === 'true'
categoryArr.sort((a, b) => { return b.value - a.value })
function translateListToTree (data, parent) {
let tree = []
let temp
data.forEach((item, index) => {
if (data[index].parentId == parent) {
let obj = data[index];
temp = translateListToTree(data, data[index].id);
if (temp.length > 0) {
obj.children = temp
}
if (tree.indexOf())
tree.push(obj)
}
})
return tree
}
const categoryNameJson = JSON.stringify(categoryArr.map(function (category) { return category.name }))
const categoryArrJson = JSON.stringify(categoryArr)
const categoryArrParentJson = JSON.stringify(translateListToTree(categoryArr, '0'))

return `
<script id="categoriesChart">
var color = document.documentElement.getAttribute('data-theme') === 'light' ? '#4c4948' : 'rgba(255,255,255,0.7)'
var categoriesChart = echarts.init(document.getElementById('categories-chart'), 'light');
var categoryParentFlag = ${categoryParentFlag}
var categoriesOption = {
title: {
text: '文章分类统计图',
x: 'center',
textStyle: {
color: color
}
},
legend: {
top: 'bottom',
data: ${categoryNameJson},
textStyle: {
color: color
}
},
tooltip: {
trigger: 'item'
},
series: []
};
categoriesOption.series.push(
categoryParentFlag ?
{
nodeClick :false,
name: '文章篇数',
type: 'sunburst',
radius: ['15%', '90%'],
center: ['50%', '55%'],
sort: 'desc',
data: ${categoryArrParentJson},
itemStyle: {
borderColor: '#fff',
borderWidth: 2,
emphasis: {
focus: 'ancestor',
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(255, 255, 255, 0.5)'
}
}
}
:
{
name: '文章篇数',
type: 'pie',
radius: [30, 80],
roseType: 'area',
label: {
color: color,
formatter: '{b} : {c} ({d}%)'
},
data: ${categoryArrJson},
itemStyle: {
emphasis: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(255, 255, 255, 0.5)'
}
}
}
)
categoriesChart.setOption(categoriesOption);
window.addEventListener('resize', () => {
categoriesChart.resize();
});
categoriesChart.on('click', 'series', (event) => {
if(event.data.path) window.location.href = '/' + event.data.path;
});
</script>`
}

添加echarts引用

在next/layout/_layout.swig文件中添加echarts引用(在<title>标签下一行添加)

1
<script src="https://npm.elemecdn.com/echarts@4.9.0/dist/echarts.min.js"></script>

标签页增加标签统计图

在next/layout/page.swig文件中修改。在{%- if page.type === 'tags' %}下一行添加<div id="tags-chart" data-length="10" style="border-radius: 8px; height: 300px; padding: 10px;"></div>

1
2
3
{%- if page.type === 'tags' %}
// 增加内容
<div id="tags-chart" data-length="10" style="border-radius: 8px; height: 300px; padding: 10px;"></div>

分类页增加分类统计图

在next/layout/page.swig文件中修改。在{% elif page.type === 'categories' %}下一行添加<div id="categories-chart" data-parent="true" style="height: 300px; padding: 10px;"></div>

1
2
3
{% elif page.type === 'categories' %}
// 增加内容
<div id="categories-chart" data-parent="true" style="height: 300px; padding: 10px;"></div>

归档页增加归档统计图

在next/layout/archive.swig文件中修改。在<span class="collection-header">{{ __('cheers.' + cheers) }}! {{ _p('counter.archive_posts', site.posts.length) }} {{ __('keep_on') }}</span>下一行添加<div id="posts-chart" data-start="2021-01" style="border-radius: 8px; height: 300px; padding: 10px;"></div>

1
2
3
<span class="collection-header">{{ __('cheers.' + cheers) }}! {{ _p('counter.archive_posts', site.posts.length) }} {{ __('keep_on') }}</span>
// 增加内容
<div id="posts-chart" data-start="2021-01" style="border-radius: 8px; height: 300px; padding: 10px;"></div>

软件过程

能力成熟度模型(CMM)

CMM 是针对软件过程改进的经典模型,核心是通过分级评估推动软件组织的过程从无序到有序、从经验型到量化管理的演进。

  1. 初始级
    • 特点:过程无规范,依赖个人经验,项目成功与否高度取决于团队成员的能力,缺乏可重复性。
    • 典型问题:进度、成本难以控制,质量不稳定,类似 “救火式” 开发。
  2. 可重复级
    • 核心:建立基础的项目管理流程,如计划制定、进度跟踪、成本控制、配置管理等,使成功的项目经验可重复。
    • 关键实践:制定基线(如需求基线、设计基线),进行项目评审,记录并复用过往项目的经验。
  3. 已定义级
    • 核心:将管理和工程过程标准化、文档化,形成组织级的标准过程(如标准开发流程、模板、指南),所有项目均基于此执行。
    • 关键实践:建立组织过程资产库(如过程手册、案例库),对过程进行裁剪以适应不同项目需求。
  4. 已管理级
    • 核心:引入定量管理,对过程和产品质量设定量化指标(如缺陷率、生产率),通过数据监控过程性能。
    • 关键实践:收集过程数据(如开发周期、工作量),使用统计方法分析偏差,确保过程稳定在可接受的量化范围内。
  5. 优化级
    • 核心:通过定量反馈创新改进持续优化过程,主动识别过程中的薄弱环节,引入新技术、新方法提升效率和质量。
    • 关键实践:建立过程改进小组,基于数据分析推动增量式改进,鼓励全员参与创新。

能力成熟度模型集成(CMMI)

CMMI 是在 CMM 基础上发展而来的综合性模型,不仅适用于软件,还覆盖硬件、系统工程等领域,通过 “阶段式” 或 “连续式” 评估框架,更灵活地支持组织的过程改进。

  1. CL0(未完成的)
    • 过程未执行或未达到预期目标,如未定义输入输出、关键活动缺失。
  2. CL1(已执行的)
    • 过程能产生可识别的输出(如代码、文档),但仅满足基本的 “做了”,缺乏系统性管理。
  3. CL2(已管理的)
    • 过程被计划和监控,明确输入、输出和资源,通过管理确保过程按计划执行(如跟踪进度、控制质量)。
    • 与 CMM “可重复级” 类似,但更强调对过程本身的管理而非仅项目管理。
  4. CL3(已定义的)
    • 过程基于组织级的标准和规程,被文档化并制度化,且与组织的业务目标对齐。
    • 相比 CMM 的 “已定义级”,CMMI 更强调过程与组织战略的关联,以及跨领域(如软件、硬件)的协同。
  5. CL4(定量管理的)
    • 对过程和产品的关键指标进行量化控制(如用统计过程控制 SPC 监控缺陷率),确保过程性能在量化目标范围内波动。
    • 核心:通过数据预测过程趋势,提前预防问题。
  6. CL5(优化的)
    • 利用量化数据和创新方法持续改进过程,主动识别并消除过程瓶颈,同时快速响应内外部变化(如技术革新、市场需求变化)。
    • 关键:建立组织级的改进文化,鼓励通过实验和学习优化过程。

CMM 与 CMMI 的核心区别

维度 CMM CMMI
适用范围 仅针对软件过程 覆盖软件、硬件、系统工程等多领域
评估框架 阶段式(固定级别递进) 支持阶段式和连续式(可按需选择过程域改进)
过程域划分 聚焦软件生命周期的核心过程 更细化,包含工程、管理、支持等多类过程域
灵活性 较固定,需按级别逐步改进 更灵活,允许组织根据优先级选择改进领域

两者的本质目标一致:通过规范化过程提升产品质量、降低成本、提高效率,只是 CMMI 在 CMM 的基础上更具通用性和灵活性,是当前行业更广泛采用的过程改进模型。

计算机病毒的详细分类

计算机病毒是一种能够自我复制、具有传染性和破坏性的计算机程序,根据其感染对象、传播方式和破坏机制等不同特征,可分为以下主要类型:

引导型病毒

  • 感染对象:主要感染计算机的引导扇区(如硬盘的主引导记录 MBR、软盘的引导扇区等)。
  • 传播方式:当计算机从被感染的存储介质(如软盘、U 盘、移动硬盘)启动时,病毒会先于操作系统加载到内存中,从而获得对系统的控制权,并感染其他存储介质的引导扇区。
  • 危害:可能导致计算机无法正常启动,出现蓝屏、死机等现象,甚至破坏硬盘分区表,造成数据丢失。

文件型病毒

  • 感染对象:主要感染可执行文件(如.exe、.com 等),部分也会感染脚本文件(如.bat、.vbs 等)。
  • 传播方式:当用户运行被感染的可执行文件时,病毒会驻留内存,并趁机感染其他未被感染的可执行文件。它通常会修改原文件的结构,将自身代码嵌入其中,或者替换原文件。
  • 危害:可能导致程序运行异常、崩溃,窃取用户信息,占用系统资源,影响计算机运行速度。

宏病毒

阅读全文 »

Shell 预定义变量:系统内置的实用变量详解

在 Shell 脚本中,除了用户自定义变量和位置参数变量,还有一些由系统预先定义的变量(预定义变量),它们用于获取进程状态、命令执行结果等关键信息,是编写健壮脚本的重要工具。以下是最常用的三个预定义变量的详细解析。

$$$$:当前进程的 PID(进程号)

作用

返回当前 Shell 脚本或进程的进程 ID(PID),唯一标识当前运行的进程。

适用场景

  • 记录脚本运行日志时标记进程;
  • 创建临时文件时使用 PID 作为文件名(避免重名);
  • 脚本中需要引用自身进程 ID 的场景。

示例

1
2
3
4
5
6
7
8
9
#!/bin/bash
# 文件名:show_pid.sh

echo "当前脚本的 PID 是:$$"

# 使用 PID 创建临时文件(避免与其他进程冲突)
temp_file="/tmp/my_script_$$.txt"
echo "临时文件路径:$temp_file"
touch "$temp_file"

执行脚本:

1
sh show_pid.sh

输出:

阅读全文 »

常见网络安全协议详解

在网络通信中,数据的安全性、完整性和隐私性至关重要。为实现这些目标,各类网络安全协议应运而生,它们在不同的网络层次或应用场景中发挥作用,构建起网络安全的防护体系。以下是几种常见的网络安全协议及其详细说明:

1. PGP(Pretty Good Privacy,优良保密协议)

核心定位

PGP 是一种用于邮件和文件加密的混合加密系统,结合了对称加密、非对称加密和哈希算法的优势,旨在保护数据的机密性、完整性和发送者身份的真实性。

工作原理

  • 混合加密机制:先用对称加密算法(如 IDEA、AES)加密文件或邮件内容(速度快),再用接收方的公钥加密对称加密所使用的密钥(安全性高)。
  • 数字签名:发送者用自己的私钥对数据的哈希值进行加密,接收方用发送者的公钥解密并验证,确保数据未被篡改且来源可靠。
  • 密钥管理:通过 “密钥环” 管理用户的公钥,支持密钥的生成、分发和吊销。

应用场景

  • 个人或企业的邮件加密(如保护敏感商务邮件、隐私通信)。
  • 本地文件加密(如加密存储重要文档、备份数据)。

优势

  • 加密强度高,算法组合灵活,适合个人和小型场景使用。
  • 开源版本(如 GnuPG)普及,兼容性强。

2. SSL(Secure Socket Layer,安全套接字协议)

核心定位

SSL 是工作在传输层与应用层之间的安全协议,为应用层协议(如 HTTP、FTP、SMTP)提供加密通信支持,曾是互联网安全通信的主流标准。

阅读全文 »