特性:
1、支持任意深度的chm文件解析
2、解析后内容结构转换为tree数据呈现
3、点击树节点可以在html实时查看数据
4、不依赖任何浏览器端插件,兼容性较好
nodejs端核心代码
const $g = global.SG.$g, fs = global.SG.fs, router = global.SG.router, xlsx = global.SG.xlsx;
module.exports = global.SG.router;let webRootPath = 'http://127.0.0.1:9999/chm/';//测试环境chm文件根目录//上传单个文件(all方法支持POST、GET、PUT、PATCH、DELETE传参方式)
let uploadFileName = '';//获取上传后的文件名
router.all("/chm/upload",//接口路径$g.dir.upload("./upload",//存储临时上传文件的路径({ fileName, } = {}) => { uploadFileName = fileName; }).single("file"),//上传单个文件(req, res) => {// 开始解压上传的upload文件----------------------------------------let cp = require('child_process');cp.exec("reg query HKEY_CLASSES_ROOT\\360zip\\shell\\open\\command /ve", function (e, stdout, stderr) {let rootPath = `${__dirname.split('\\').slice(0, -3).join('\\')}`;let uploadFolderPath = `${rootPath}\\upload\\${uploadFileName}`;let targetFolderPath = `${rootPath}\\chm\\${uploadFileName}`;let str = stdout.match(/\"([^\"]+)\"/)[0];if (str) {// console.log('已经找到360zip程序,详细地址为:'+str);cp.exec(`${str} -x ${uploadFolderPath} ${targetFolderPath}`, { encoding: 'binary' }, function (e, stdout, stderr) {// 遍历读取目录里面的文件----------------------------------------let files = [];let walker = require('walk').walk(targetFolderPath, { followLinks: false });walker.on('file', function (roots, stat, next) {if (stat.name.includes(`.hhc`)) {let hhcFilePath = `${roots}/${stat.name}`;files.push(hhcFilePath);fs.readFile(hhcFilePath, 'utf-8', (err, data) => $g.json.res(req, res, "chm文件解析成功", {htmPath: `${webRootPath}${uploadFileName}/`,hhcFilePath: `${webRootPath}${uploadFileName}/${stat.name}`,hhcData: data,}, true));} else next();});walker.on('end', function () {files.length === 0 && $g.json.res(req, res, "没有找到hhc文件,请仔细检查chm文件是否正确!", { targetFolderPath }, false);});});} else {console.log('没有找到360zip程序,无法完成解压缩功能,请在服务器端安装360zip软件!');}});}
);
vue前端核心代码
<template><div :class="$options.name"><div class="sg-left " v-loading="loading"><!-- 树节点 --><div class="tree-header"><!-- 树节点 --><div class="tree-header"><div class="sg-left "><el-tooltip popper-class="sg-el-tooltip" :enterable="false" effect="dark" :content="`支持拖拽到树上传文件`"placement="top-start"><el-button type="text" icon="el-icon-upload" size="mini"@click="d => $refs.sgUpload.triggerUploadFile()">上传chm文件</el-button></el-tooltip></div><div class="sg-right "></div></div></div><div class="tree-body" @click="treeData.length === 0 ? $refs.sgUpload.triggerUploadFile() : ''"><el-tree ref="tree" @current-change="current_change" :data="treeData":props="{ label: 'Name', children: 'children' }" :icon-class="'folder-tree-node'" :indent="25"@node-click="nodeClick" node-key="id" :filter-node-method="filterNode" default-expand-allhighlight-current :default-expanded-keys="default_expanded_keys"><div slot="reference" class="node-label" slot-scope="{ node, data }"><label class="left" :title="node.label">{{ node.label }}</label></div></el-tree><sgUpload drag ref="sgUpload" :data="{accept: `.${['chm'].join(',.')}`,// actionUrl: `http://127.0.0.1:9999/api/chm/upload`,actionUrl: `http://xxx.xxxxxx.cn:33/api/chm/upload`,headers: {},}" @beforeUpload="beforeUpload" @uploadSuccess="uploadSuccess" @error="uploadError" hideUploadTray /></div></div><div class="sg-right "><iframe id="iframe" ref="iframe" :src="src" frameborder="no" style="width:100%;height:100%;"></iframe></div><div class="hhcHTML" ref="hhcHTML" style="display: none;"> </div></div>
</template><script>
import sgUpload from "@/vue/components/admin/sgUpload";
export default {name: 'chmDecode',components: {sgUpload,},data() {return {loading: false,htmPath: '',src: '',current_node: null,default_expanded_keys: [],treeData: [],}},created() {},methods: {// 解析hhc文件decodeHhcData(doms) {let r = [];let _recursion = (doms, d) => {[].slice.call(doms).forEach(v => {let OBJECT = v.querySelector(`OBJECT`);let p0 = OBJECT.querySelectorAll(`param`)[0];let p1 = OBJECT.querySelectorAll(`param`)[1];let obj = {id: this.$g.UUID(),[p0.getAttribute('name')]: p0.getAttribute('value'),//文件别名[p1.getAttribute('name')]: p1.getAttribute('value'),filePath: `${this.htmPath}${p1.getAttribute('value')}`,//文件路径}this.current_node || (this.current_node = obj);d.push(obj)if (OBJECT.nextElementSibling) {obj.children = []_recursion(OBJECT.nextElementSibling.children, obj.children)}});}_recursion(doms, r);return r;},// 开始上传beforeUpload(d) {this.loading = true;},// 上传成功uploadSuccess(d, f) {this.htmPath = d.data.htmPath;this.$refs.hhcHTML.innerHTML = d.data.hhcData;this.$nextTick(() => {let treeData = this.decodeHhcData(this.$refs.hhcHTML.querySelectorAll(`.hhcHTML>ul>li`))this.treeData = treeData;this.loading = false;this.$nextTick(() => {this.$refs.tree.setCurrentKey(this.current_node.id)this.src = this.current_node.filePath;});});},// 上传失败uploadError(d, f) { this.loading = false; },//点击节点nodeClick(data) { },//过滤节点filterNode(value, data) { },// 树节点修改current_change(d) { this.src = d.filePath; },}
};
</script>
<style lang="scss" scoped>
.chmDecode {width: 100%;display: flex;flex-wrap: nowrap;$treeWidth: 610px;$treeControlWidth: 100px;&>.sg-left {width: $treeWidth;flex-wrap: nowrap;white-space: nowrap;flex-shrink: 0;.tree-header {display: flex;justify-content: space-between;align-items: center;&>.sg-left {}&>.sg-right {}}.tree-body {height: calc(100vh - 200px);}}&>.sg-right {margin-left: 20px;flex-grow: 1;height: calc(100vh - 170px);.baseinfo {width: 100%;height: 100%;overflow-x: hidden;overflow-y: auto;position: relative;.form-body {height: calc(100% - 60px);overflow-y: auto;overflow-x: hidden;}.form-footer {position: absolute;height: 70px;box-sizing: border-box;padding-top: 20px;width: 100%;display: flex;justify-content: space-between;bottom: 0;&>* {width: 100%;flex-grow: 1;}}}}
}
</style>