手册比较大,word大概有一百多m的样子,从头开发网页的话太浪费时间,能否自动生成呢?
自动生成的思路是:
1、开发一个上、左、右的导航网页,头部和左侧目录导航自己定制开发,而右侧使用word直接转成的html。
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>导航页</title>
</head>
<body>
<div style="float:left;width:30%;height:500px;background:red;">
<a onclick="toPage('b.html#A1')">1、《热爱生命》</a><br/>
<a onclick="toPage('b.html#A2')" >2、《雪狼》</a><br/>
<a onclick="toPage('b.html#A3')" >3、《荒野的呼唤》</a><br/>
<a onclick="toPage('b.html#A4')" >4、《海狼》</a><br/>
</div>
<div style="float:right;width:60%;height:500px;background:green;">
<iframe src="b.html#A2" id="frm" frameborder="0" style="width:100%;height:100%;"></iframe>
</div>
</body>
<script>
function toPage(url){
var page = document.getElementById('frm');
page.setAttribute('src',url);
}
</script>
我们将word右键另存为,保存为网页的形式。
注意,word默认保存的是gb2312的编码,而我们常用的是utf8编码,需要这样转一下。
保存完之后,会有一个htm文件和一个文件夹。打开网页,我们发现自动生成的目录是这样的: b.png
一级二级三级菜单都是扁平结构的,而我们一般开发的目录菜单,都是带层级的嵌套ul li结构。
所以难点就在于:
如何将word生成的目录,与我们开发的左侧导航联系起来?
以下是我的解决方案。主要是利用node的cheerio包,将扁平结构的目录,转成带层级的数据结构,然后再生成树结构。
目录处理完后,将word生成的目录部分去掉即可。
const fs = require('fs');
const cheerio = require('cheerio');
//=================================生成内容================================================//
async function handleContent(path,destPath) { //需清除word的目录
let newHtml = await fsRead(path);
let $ = cheerio.load(newHtml);
let bodyPart = $('body').html();
let $d = cheerio.load(bodyPart);
$d('.MsoToc1').parent().replaceWith('');
$d('.MsoTocHeading').replaceWith('');
$d('.MsoTitle').replaceWith('');
$d('body').append(`<script src="../layout/jquery.min.js"></script>
<script>
$('img').parents('p').removeAttr('style').css('text-align','left');
</script>`);
let res = await fsWrite(destPath, $d.html());//生成处理过的html
if (res === 'ok') {
console.log('内容处理完毕');
} else {
console.log('内容处理出错了');
}
}
//=================================生成目录================================================//
async function handlecatalogue(path, destPath) {
let content = await fsRead(path);
let listHtml = getHtml();//读取目录文件
let arr: any[] = [];
let count = 0;
let count1 = 0;
let count2 = 0;
let $ = cheerio.load(content);
let $list = cheerio.load(listHtml);
$('.MsoTocHeading').eq(0).siblings('p').each((key, element) => {
let className = $(element).attr('class');
if (className === 'MsoToc1') { // 一级目录
$(element).find('a').each((k, v) => {
count++;
let url = $(v).attr('href');
let title = $(v).text().replace(/\.{1,}\s+\d+/ig, '');
let obj = { id: count, title: title, url: url, parent: 0 };
arr.push(obj);
count1 = count;
});
}
if (className === 'MsoToc2') { // 一级目录
$(element).find('a').each((k, v) => {
count++;
let url = $(v).attr('href');
let title = $(v).text().trim().replace(/\.{1,}\s+\d+/ig, '');
let obj = { id: count, title: title, url: url, parent: count1 };
arr.push(obj);
count2 = count;
});
}
if (className === 'MsoToc3') { // 一级目录
$(element).find('a').each((k, v) => {
count++;
let url = $(v).attr('href');
let title = $(v).text().trim().replace(/\.{1,}\s+\d+/ig, '');
let obj = { id: count, title: title, url: url, parent: count2 };
arr.push(obj);
});
}
});
let newarr = convertData(arr);
let nodes = createTree(newarr, true);
$list('.left-part').html(nodes);
let res = await fsWrite(destPath, $list.html());//生成处理过的html
if (res === 'ok') {
console.log('目录处理完毕');
} else {
console.log('目录处理出错了');
}
}
//==================================生成html=======================================================//
function createTree(list, flag = false) {
if (Array.isArray(list)) {
let str = '';
if (flag) {
str = `<ul class="lp-cont">`;//最外层ul
} else {
str = `<ul class="lp-cont-child">`;
}
list.forEach(item => {
str += `<li onclick="toPage('./pages/book.html${item.url}')">${item.title}`;
if (item.children && item.children.length > 0) {
str += createTree(item.children);
}
str += `</li>`;
});
str += `</ul>`;
return str;
}
}
//======================================生成tree data============================================//
function convertData(jsonData) {
var result: any[] = [], temp = {}, i = 0, j = 0, len = jsonData.length;
for (; i < len; i++) {
temp[jsonData[i]['id']] = jsonData[i] // 以id作为索引存储元素,可以无需遍历直接定位元素
}
for (; j < len; j++) {
var currentElement = jsonData[j]
var tempCurrentElementParent = temp[currentElement['parent']] // 临时变量里面的当前元素的父元素 parentId 父级ID
if (tempCurrentElementParent) { // 如果存在父元素
if (!tempCurrentElementParent['children']) { // 如果父元素没有chindren键
tempCurrentElementParent['children'] = [] // 设上父元素的children键
}
tempCurrentElementParent['children'].push(currentElement) // 给父元素加上当前元素作为子元素
} else { // 不存在父元素,意味着当前元素是一级元素
result.push(currentElement);
}
}
return result;
}
//======================================读文件====================================================//
function fsRead(filename) {
return new Promise((resolve, reject) => {
fs.readFile(filename, { flag: 'r', encoding: 'utf-8' }, function (err, data) {
if (err) {
reject(err);
} else {
resolve(data);
}
})
})
}
//===================================写文件===================================================//
function fsWrite(path, content) {
return new Promise((resolve, reject) => {
fs.writeFile(path, content, { flag: 'w', encoding: 'utf-8' }, (err) => {
if (err) {
reject(err);
} else {
resolve('ok');
}
})
})
}
//====================================目录文件=====================================================//
function getHtml() {
return `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>用户手册</title>
<style>
body{
margin:0;
padding:0;
}
ul{
margin:0;
padding:0;
list-style:none;
/* margin-bottom:15px; */
}
ul li{
position: relative;
line-height:40px;
}
.container{
width:100%;
height:100%;
display:flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.header{
width:inherit;
height:50px;
display:flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
color:#fff;
background:#000;
}
.h-back{
width:20px;
height:20px;
margin-left:15px;
margin-right:20px;
}
.content{
width:inherit;
height:calc(100vh - 50px);
display:flex;
flex-direction: row;
justify-content: center;
}
.left-part{
width:30%;
display:flex;
flex-direction: column;
overflow-x:hidden;
overflow-y: auto;
}
.lp-title{
font-weight: 400;
color: #666666;
margin-left:180px;
margin-top:30px;
}
.lp-cont{
margin-left:180px;
margin-top:15px;
}
.lp-cont li{
font-size:16px;
font-weight: 400;
/* color: #097EFF; */
color:#333333;
}
.lp-cont li:hover{
cursor: pointer;
}
.right-part{
width:70%;
margin-left:16px;
}
.clear-bar{
width: 183px;
height: 1px;
background: #000000;
opacity: 0.1;
margin-top:10px;
}
.lp-cont-child{
display:none;
position: relative;
left:16px;
}
.lp-cont-child li{
font-size:14px;
}
.lp-cont-cate::before{
content:"";
position: absolute;
top:17px;
left: -15px;
border-top:6px solid #999999;
border-left:5px solid transparent;
border-right:5px solid transparent;
-webkit-transform: rotate(-90deg);
-moz-transform: rotate(-90deg);
-ms-transform: rotate(-90deg);
-o-transform: rotate(-90deg);
transform: rotate(-90deg);
}
.li-active::before{
-webkit-transform: rotate(0deg);
-moz-transform: rotate(0deg);
-ms-transform: rotate(0deg);
-o-transform: rotate(0deg);
transform: rotate(0deg);
}
.active{
color: #097EFF !important;
}
</style>
<script src="./layout/jquery.min.js"></script>
</head>
<body>
<div class="container">
<div class="header">
<img class="h-back" src="./layout/arrow-left.png" onclick="goBack()" />
<div class="h-title">用户手册</div>
</div>
<div class="content">
<div class="left-part">
</div>
<div class="right-part">
<iframe id="frm" src="./pages/book.html" frameborder="0" style="width:100%;height:100%;"></iframe>
</div>
</div>
</div>
</body>
</html>
<script>
let lp = document.querySelector('.lp-cont');
$('.lp-cont-child').parent().addClass('lp-cont-cate');//增加类
lp.addEventListener('click', function (e) {
if (e.target.tagName.toLowerCase() === "li") {
e.stopPropagation();
let element = $(e.target);//目标节点
if (element.children('.lp-cont-child').length > 0) {
element.removeAttr('onclick');
if (element.hasClass('li-active')) {
element.removeClass('li-active');
element.children('.lp-cont-child').slideUp();
} else {
element.addClass('li-active');
element.children('.lp-cont-child').slideDown();
}
} else {
$('li').removeClass('active');
element.addClass('active');
}
}
});
function toPage(url) {
var page = document.getElementById('frm');
page.setAttribute('src', url);
}
function goBack() {
location.href = '/';
}
</script>
`;
}
export { handlecatalogue, handleContent, fsWrite };
网友评论