hexo博客next主题使用echarts图表
hexo博客next主题使用echarts图表
创建charts.js
在next/scripts/helpers下创建charts.js
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>标签下一行添加)
<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>
{%- 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>
{% 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>
<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 是针对软件过程改进的经典模型,核心是通过分级评估推动软件组织的过程从无序到有序、从经验型到量化管理的演进。
- 初始级
- 特点:过程无规范,依赖个人经验,项目成功与否高度取决于团队成员的能力,缺乏可重复性。
- 典型问题:进度、成本难以控制,质量不稳定,类似 “救火式” 开发。
- 可重复级
- 核心:建立基础的项目管理流程,如计划制定、进度跟踪、成本控制、配置管理等,使成功的项目经验可重复。
- 关键实践:制定基线(如需求基线、设计基线),进行项目评审,记录并复用过往项目的经验。
- 已定义级
- 核心:将管理和工程过程标准化、文档化,形成组织级的标准过程(如标准开发流程、模板、指南),所有项目均基于此执行。
- 关键实践:建立组织过程资产库(如过程手册、案例库),对过程进行裁剪以适应不同项目需求。
- 已管理级
- 核心:引入定量管理,对过程和产品质量设定量化指标(如缺陷率、生产率),通过数据监控过程性能。
- 关键实践:收集过程数据(如开发周期、工作量),使用统计方法分析偏差,确保过程稳定在可接受的量化范围内。
- 优化级
- 核心:通过定量反馈和创新改进持续优化过程,主动识别过程中的薄弱环节,引入新技术、新方法提升效率和质量。
- 关键实践:建立过程改进小组,基于数据分析推动增量式改进,鼓励全员参与创新。
能力成熟度模型集成(CMMI)
CMMI 是在 CMM 基础上发展而来的综合性模型,不仅适用于软件,还覆盖硬件、系统工程等领域,通过 “阶段式” 或 “连续式” 评估框架,更灵活地支持组织的过程改进。
- CL0(未完成的)
- 过程未执行或未达到预期目标,如未定义输入输出、关键活动缺失。
- CL1(已执行的)
- 过程能产生可识别的输出(如代码、文档),但仅满足基本的 “做了”,缺乏系统性管理。
- CL2(已管理的)
- 过程被计划和监控,明确输入、输出和资源,通过管理确保过程按计划执行(如跟踪进度、控制质量)。
- 与 CMM “可重复级” 类似,但更强调对过程本身的管理而非仅项目管理。
- CL3(已定义的)
- 过程基于组织级的标准和规程,被文档化并制度化,且与组织的业务目标对齐。
- 相比 CMM 的 “已定义级”,CMMI 更强调过程与组织战略的关联,以及跨领域(如软件、硬件)的协同。
- CL4(定量管理的)
- 对过程和产品的关键指标进行量化控制(如用统计过程控制 SPC 监控缺陷率),确保过程性能在量化目标范围内波动。
- 核心:通过数据预测过程趋势,提前预防问题。
- CL5(优化的)
- 利用量化数据和创新方法持续改进过程,主动识别并消除过程瓶颈,同时快速响应内外部变化(如技术革新、市场需求变化)。
- 关键:建立组织级的改进文化,鼓励通过实验和学习优化过程。
CMM 与 CMMI 的核心区别
| 维度 | CMM | CMMI |
|---|---|---|
| 适用范围 | 仅针对软件过程 | 覆盖软件、硬件、系统工程等多领域 |
| 评估框架 | 阶段式(固定级别递进) | 支持阶段式和连续式(可按需选择过程域改进) |
| 过程域划分 | 聚焦软件生命周期的核心过程 | 更细化,包含工程、管理、支持等多类过程域 |
| 灵活性 | 较固定,需按级别逐步改进 | 更灵活,允许组织根据优先级选择改进领域 |
两者的本质目标一致:通过规范化过程提升产品质量、降低成本、提高效率,只是 CMMI 在 CMM 的基础上更具通用性和灵活性,是当前行业更广泛采用的过程改进模型。
计算机病毒
计算机病毒的详细分类
计算机病毒是一种能够自我复制、具有传染性和破坏性的计算机程序,根据其感染对象、传播方式和破坏机制等不同特征,可分为以下主要类型:
引导型病毒
- 感染对象:主要感染计算机的引导扇区(如硬盘的主引导记录 MBR、软盘的引导扇区等)。
- 传播方式:当计算机从被感染的存储介质(如软盘、U 盘、移动硬盘)启动时,病毒会先于操作系统加载到内存中,从而获得对系统的控制权,并感染其他存储介质的引导扇区。
- 危害:可能导致计算机无法正常启动,出现蓝屏、死机等现象,甚至破坏硬盘分区表,造成数据丢失。
文件型病毒
- 感染对象:主要感染可执行文件(如.exe、.com 等),部分也会感染脚本文件(如.bat、.vbs 等)。
- 传播方式:当用户运行被感染的可执行文件时,病毒会驻留内存,并趁机感染其他未被感染的可执行文件。它通常会修改原文件的结构,将自身代码嵌入其中,或者替换原文件。
- 危害:可能导致程序运行异常、崩溃,窃取用户信息,占用系统资源,影响计算机运行速度。