增加docx格式导出

This commit is contained in:
purocean
2018-06-12 11:25:16 +08:00
parent c5d0de8ae4
commit 87e8eb0085
6 changed files with 101 additions and 19 deletions
+3 -2
View File
@@ -33,8 +33,9 @@
+ 自动保存(加密文档不自动保存)
+ 列表自动补全
+ 粘贴剪切板里面的图片
+ 运行PHP,nodejs,Python 代码,代码块第一行需要包含以 `--run--` 字符串,示例见下面截图,当然需要安装相关环境
+ 开启了HTML解析,可以直接在文档里面使用 HTML 代码
+ 运行 PHP,nodejs,Python 代码,代码块第一行需要包含以 `--run--` 字符串,示例见下面截图,当然需要安装相关环境
+ 开启了 HTML 解析,可以直接在文档里面使用 HTML 代码
+ docx 格式导出,需要安装 [pandoc](https://pandoc.org/)
+ markdown 编辑器 [monaco-editor](https://github.com/Microsoft/monaco-editor)
+ markdown 解析 [markdown-it](https://github.com/markdown-it/markdown-it)
+ GitHub 风格样式和特性 [GitHub Flavored Markdown Spec](https://github.github.com/gfm/)
+30
View File
@@ -0,0 +1,30 @@
const os = require('os')
const fs = require('fs')
const spawn = require("child_process").spawn
const convert = async (html, type) => {
try {
const path = os.tmpdir() + `/yn_convert_${new Date().getTime()}.${type}`
return new Promise((resolve, reject) => {
const process = spawn('pandoc', ['-f', 'html', '-o', path])
process.on('close', (code) => {
try {
const data = fs.readFileSync(path)
fs.unlinkSync(path)
resolve(data)
} catch (error) {
reject(error)
}
})
process.stdin.write(html)
process.stdin.end()
})
} catch (e) {
return e.message
}
}
module.exports = convert
+32 -13
View File
@@ -7,12 +7,13 @@ const plantuml = require('node-plantuml')
const file = require('./file')
const run = require('./run')
const convert = require('./convert')
const result = (status = 'ok', message = '操作成功', data = null) => {
return { status, message, data }
}
const fileContent = (ctx, next) => {
const fileContent = async (ctx, next) => {
if (ctx.path === '/api/file') {
if (ctx.method === 'GET') {
ctx.body = result('ok', '获取成功', file.read(ctx.query.path).toString())
@@ -29,11 +30,11 @@ const fileContent = (ctx, next) => {
} else if (ctx.path === '/api/tree') {
ctx.body = result('ok', '获取成功', file.tree())
} else {
next()
await next()
}
}
const attachment = (ctx, next) => {
const attachment = async (ctx, next) => {
if (ctx.path.startsWith('/api/attachment')) {
if (ctx.method === 'POST') {
const path = ctx.request.body.fields.path
@@ -44,11 +45,11 @@ const attachment = (ctx, next) => {
ctx.body = file.read(ctx.query.path)
}
} else {
next()
await next()
}
}
const plantumlGen = (ctx, next) => {
const plantumlGen = async (ctx, next) => {
if (ctx.path.startsWith('/api/plantuml/svg')) {
const gen = plantuml.generate(ctx.query.data, {format: 'svg'});
@@ -60,16 +61,28 @@ const plantumlGen = (ctx, next) => {
ctx.type = 'image/png'
ctx.body = gen.out
} else {
next()
await next()
}
}
const runCode = (ctx, next) => {
const runCode = async (ctx, next) => {
if (ctx.path.startsWith('/api/run')) {
const rst = run.runCode(ctx.request.body.language, ctx.request.body.code)
ctx.body = result('ok', '运行成功', rst)
} else {
next()
await next()
}
}
const convertFile = async (ctx, next) => {
if (ctx.path.startsWith('/api/convert/')) {
const html = ctx.request.body.html
const type = ctx.request.body.type
ctx.type = mime.getType(`file.${type}`)
ctx.body = await convert(html, type)
} else {
await next()
}
}
@@ -79,11 +92,17 @@ app.use(static(
path.join(__dirname, '../static')
))
app.use(bodyParser({multipart: true}))
app.use(async (ctx, next) => fileContent(ctx, next))
app.use(async (ctx, next) => attachment(ctx, next))
app.use(async (ctx, next) => plantumlGen(ctx, next))
app.use(async (ctx, next) => runCode(ctx, next))
app.use(bodyParser({
multipart: true,
formLimit: '20mb',
jsonLimit: '20mb',
textLimit: '20mb'
}))
app.use(async (ctx, next) => await fileContent(ctx, next))
app.use(async (ctx, next) => await attachment(ctx, next))
app.use(async (ctx, next) => await plantumlGen(ctx, next))
app.use(async (ctx, next) => await runCode(ctx, next))
app.use(async (ctx, next) => await convertFile(ctx, next))
const port = 3000
app.listen(port)
+1 -1
View File
@@ -1,4 +1,4 @@
const execFileSync = require("child_process").execFileSync;
const execFileSync = require("child_process").execFileSync
const runCode = (language, code) => {
try {
+31 -2
View File
@@ -1,5 +1,13 @@
<template>
<div class="view">
<div class="convert">
<form ref="convertForm" :action="`/api/convert/${convert.fileName}`" method="post" target="_blank">
<input type="hidden" name="html" :value="convert.html">
<input type="hidden" name="type" :value="convert.type">
<!-- <button type="button" @click="convertFile('pdf')">pdf</button> -->
<button type="button" @click="convertFile('docx')">docx</button>
</form>
</div>
<div ref="outline" class="outline">
<div style="padding: .5em;"><b>目录</b></div>
<div class="catalog">
@@ -34,11 +42,13 @@ import 'katex/dist/katex.min.css'
export default {
name: 'xview',
props: {
value: String
value: String,
fileName: String
},
data () {
return {
heads: [],
convert: {},
markdown: Markdown({
linkify: true,
breaks: true,
@@ -54,7 +64,7 @@ export default {
}
}).use(TaskLists).use(MermaidPlugin).use(Plantuml, {
generateSource: umlCode => {
return '/api/plantuml/png?data=' + encodeURIComponent(umlCode)
return 'api/plantuml/png?data=' + encodeURIComponent(umlCode)
}
}).use(RunPlugin).use(katex).use(SourceLinePlugin).use(MarkdownItAttrs).use(MultimdTable, {enableMultilineRows: true})
}
@@ -98,6 +108,18 @@ export default {
break
}
}
},
convertFile (type) {
const baseUrl = location.origin + location.pathname.substring(0, location.pathname.lastIndexOf('/')) + '/'
this.convert = {
fileName: (this.fileName || '未命名') + `.${type}`,
html: this.$refs.view.outerHTML.replace(/src="api/g, `src="${baseUrl}api`),
type
}
setTimeout(() => {
this.$refs.convertForm.submit()
}, 300)
}
},
watch: {
@@ -150,6 +172,13 @@ export default {
background: #fff;
}
.convert {
position: fixed;
font-size: 14px;
right: 3em;
margin-top: -2.5em;
}
@media print {
.outline {
display: none;
+4 -1
View File
@@ -21,7 +21,7 @@
@scroll-line="syncScrollView"
@paste-img="pasteImg"
@save="saveFile"></Editor>
<XView ref="view" class="view" :value="value" @sync-scroll="syncScrollEditor"></XView>
<XView ref="view" class="view" :value="value" :file-name="fileName" @sync-scroll="syncScrollEditor"></XView>
</div>
</div>
</template>
@@ -133,6 +133,9 @@ export default {
computed: {
unsaved () {
return this.value !== this.lastSaveContent
},
fileName () {
return this.file ? this.file.name : null
}
}
}