diff --git a/backend/file.js b/backend/file.js index e0c362fe..f5eb98df 100644 --- a/backend/file.js +++ b/backend/file.js @@ -1,6 +1,9 @@ const fs = require('fs') const xfs = require('fs-extra') const path = require('path') +const opn = require('opn') +const isWsl = require('is-wsl') +const wsl = require('./wsl') const mkdirPSync = location => { let normalizedPath = path.normalize(location) @@ -128,6 +131,16 @@ exports.tree = () => { }] } +exports.open = p => { + let path = resolvePath(p) + + if (isWsl) { + path = wsl.toWinPath(path) + } + + opn(path) +} + exports.search = str => { const files = [] const basePath = resolvePath('') diff --git a/backend/main.js b/backend/main.js index fb426341..e0a052f2 100644 --- a/backend/main.js +++ b/backend/main.js @@ -57,6 +57,17 @@ const attachment = async (ctx, next) => { } } +const open = async (ctx, next) => { + if (ctx.path.startsWith('/api/open')) { + if (ctx.method === 'GET') { + file.open(ctx.query.path) + ctx.body = result() + } + } else { + await next() + } +} + const plantumlGen = async (ctx, next) => { if (ctx.path.startsWith('/api/plantuml/svg')) { const gen = plantuml.generate(ctx.query.data, {format: 'svg'}); @@ -127,6 +138,7 @@ const wrapper = async (ctx, next, fun) => { app.use(async (ctx, next) => await wrapper(ctx, next, fileContent)) app.use(async (ctx, next) => await wrapper(ctx, next, attachment)) +app.use(async (ctx, next) => await wrapper(ctx, next, open)) app.use(async (ctx, next) => await wrapper(ctx, next, plantumlGen)) app.use(async (ctx, next) => await wrapper(ctx, next, runCode)) app.use(async (ctx, next) => await wrapper(ctx, next, convertFile)) diff --git a/backend/package.json b/backend/package.json index 12434c81..8fc2da8c 100644 --- a/backend/package.json +++ b/backend/package.json @@ -6,6 +6,7 @@ "koa-static": "^4.0.2", "mime": "^2.2.0", "node-plantuml": "^0.6.2", + "opn": "^5.3.0", "safe-buffer": "^5.1.2" } } diff --git a/backend/wsl.js b/backend/wsl.js new file mode 100644 index 00000000..4f1bbf18 --- /dev/null +++ b/backend/wsl.js @@ -0,0 +1,9 @@ +const cp = require('child_process'); + +exports.toWslPath = path => { + return cp.execFileSync('wslpath', ['-u', `${path.replace(/\\/g, '/')}`]).toString().trim() +} + +exports.toWinPath = path => { + return cp.execFileSync('wslpath', ['-w', path]).toString().trim() +} diff --git a/backend/yarn.lock b/backend/yarn.lock index eef75036..e31e35d6 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -145,6 +145,10 @@ is-generator-function@^1.0.3: version "1.0.7" resolved "http://registry.npm.taobao.org/is-generator-function/download/is-generator-function-1.0.7.tgz#d2132e529bb0000a7f80794d4bdf5cd5e5813522" +is-wsl@^1.1.0: + version "1.1.0" + resolved "http://registry.npm.taobao.org/is-wsl/download/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + jsonfile@^4.0.0: version "4.0.0" resolved "http://registry.npm.taobao.org/jsonfile/download/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -297,6 +301,12 @@ only@0.0.2: version "0.0.2" resolved "http://registry.npm.taobao.org/only/download/only-0.0.2.tgz#2afde84d03e50b9a8edc444e30610a70295edfb4" +opn@^5.3.0: + version "5.3.0" + resolved "http://registry.npm.taobao.org/opn/download/opn-5.3.0.tgz#64871565c863875f052cfdf53d3e3cb5adb53b1c" + dependencies: + is-wsl "^1.1.0" + pako@1.0.3: version "1.0.3" resolved "http://registry.npm.taobao.org/pako/download/pako-1.0.3.tgz#5f515b0c6722e1982920ae8005eacb0b7ca73ccf" diff --git a/frontend/src/components/View.vue b/frontend/src/components/View.vue index da4836ce..72272c29 100644 --- a/frontend/src/components/View.vue +++ b/frontend/src/components/View.vue @@ -86,7 +86,7 @@ export default { } const basePath = this.filePath.substr(0, this.filePath.lastIndexOf('/')) - return md.replace(/\[([^\]]*)\]\(\.\/([^)]*)\/([^)/]+)\)/g, `[$1](api/attachment/$3?path=${encodeURI(basePath)}%2F$2%2F$3){target="_blank"}`) + return md.replace(/\[([^\]]*)\]\(\.\/([^)]*)\/([^)/]+)\)/g, `[$1](api/attachment/$3?path=${encodeURI(basePath)}%2F$2%2F$3)`) }, updateOutline () { const tags = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] @@ -101,6 +101,12 @@ export default { }) }, handleClick (e) { + if (e.target.tagName === 'A' && e.target.classList.contains('open')) { + fetch(e.target.href.replace('api/attachment', 'api/open')) + e.preventDefault() + return false + } + if (e.target.tagName === 'IMG') { window.open(e.target.src) return diff --git a/frontend/src/pages/Home.vue b/frontend/src/pages/Home.vue index 5367b84c..aac735f9 100644 --- a/frontend/src/pages/Home.vue +++ b/frontend/src/pages/Home.vue @@ -115,7 +115,7 @@ export default { uploadFile (file) { File.upload(this.file.path, file, ({relativePath}) => { this.$refs.tree.change() - this.$refs.editor.insert(`附件:[${file.name} (${(file.size / 1024).toFixed(2)}KiB)](${encodeURI(relativePath)})\n`) + this.$refs.editor.insert(`附件:[${file.name} (${(file.size / 1024).toFixed(2)}KiB)](${encodeURI(relativePath).replace('(', '%28').replace(')', '%29')}){class=open target=_blank}\n`) }, `${dayjs().format('YYYYMMDDHHmmss')}.${file.name}`) }, switchTodoEditor (line, checked) {