diff --git a/README.md b/README.md index 6fa6f315..64ca3d7f 100644 --- a/README.md +++ b/README.md @@ -136,13 +136,17 @@ ## 更新日志 [最新发布](https://github.com/purocean/yn/releases) +### [v2.0.0-alpha3](https://github.com/purocean/yn/releases/tag/v2.0.0-alpha3) 2019-08-02 +1. 优化界面样式,提升交互体验 +1. 修复若干 v2.0.0-alpha2 的问题 + ### [v2.0.0-alpha2](https://github.com/purocean/yn/releases/tag/v2.0.0-alpha2) 2019-08-02 1. 默认仓库数据和配置改为在 `/yank-note` 下保存 -2. 调整在 Windows 端的仓库配置,不论是否在 WSL 环境下都使用 windows 风格的路径 +1. 调整在 Windows 端的仓库配置,不论是否在 WSL 环境下都使用 windows 风格的路径 1. 前端重构文件接口 -2. 尝试引入 Electron -3. 后端重构改用 TypeScript -4. 修复若干 v2.0.0-alpha1 的问题 +1. 尝试引入 Electron +1. 后端重构改用 TypeScript +1. 修复若干 v2.0.0-alpha1 的问题 ### [v2.0.0-alpha1](https://github.com/purocean/yn/releases/tag/v2.0.0-alpha1) 2019-08-01 1. 目录树和集成终端增加拖动调整尺寸功能 diff --git a/frontend/public/index.html b/frontend/public/index.html index cc99afc4..d572a108 100644 --- a/frontend/public/index.html +++ b/frontend/public/index.html @@ -3,7 +3,7 @@ - YN + Yank-Note
diff --git a/frontend/src/components/Editor.vue b/frontend/src/components/Editor.vue index 8a7a1d3b..ec2ef8e3 100644 --- a/frontend/src/components/Editor.vue +++ b/frontend/src/components/Editor.vue @@ -157,17 +157,16 @@ export default { console.error(error) } }, - pasteImg (file) { - File.upload(this.currentFile.repo, this.currentFile.path, file, ({ relativePath }) => { - this.$bus.emit('file-uploaded', relativePath) - this.$refs.editor.insert(`![图片](${encodeURI(relativePath)})\n`) - }) + async pasteImg (file) { + const { relativePath } = await File.upload(this.currentFile.repo, this.currentFile.path, file) + this.$bus.emit('file-uploaded', relativePath) + this.$refs.editor.insert(`![图片](${encodeURI(relativePath)})\n`) }, - uploadFile (file) { - File.upload(this.currentFile.repo, this.currentFile.path, file, ({ relativePath }) => { - this.$bus.emit('file-uploaded', relativePath) - this.$refs.editor.insert(`附件 [${dayjs().format('YYYY-MM-DD HH:mm')}]:[${file.name} (${(file.size / 1024).toFixed(2)}KiB)](${encodeURI(relativePath).replace('(', '%28').replace(')', '%29')}){class=open target=_blank}\n`) - }, `${dayjs().format('YYYYMMDDHHmmss')}.${file.name}`) + async uploadFile (file) { + const filename = `${dayjs().format('YYYYMMDDHHmmss')}.${file.name}` + const { relativePath } = await File.upload(this.currentFile.repo, this.currentFile.path, file, filename) + this.$bus.emit('file-uploaded', relativePath) + this.$refs.editor.insert(`附件 [${dayjs().format('YYYY-MM-DD HH:mm')}]:[${file.name} (${(file.size / 1024).toFixed(2)}KiB)](${encodeURI(relativePath).replace('(', '%28').replace(')', '%29')}){class=open target=_blank}\n`) }, async changeFile (current, previous) { this.clearTimer() diff --git a/frontend/src/components/ModalUi.vue b/frontend/src/components/ModalUi.vue index 2109f9b3..d7300d2c 100644 --- a/frontend/src/components/ModalUi.vue +++ b/frontend/src/components/ModalUi.vue @@ -3,7 +3,7 @@

{{title}}

{{content}}

- +
@@ -30,38 +30,51 @@ export default { } }, methods: { - cancel () { - this.resolve && this.resolve(this.type === 'input' ? null : false) + handle (val) { this.show = false this.inputValue = '' + try { + this.resolve && this.resolve(val) + } catch (error) { + throw error + } finally { + this.resolve = null + } + }, + cancel () { + this.handle(this.type === 'input' ? null : false) }, ok () { - this.resolve && this.resolve(this.type === 'input' ? this.inputValue : true) - this.show = false - this.inputValue = '' + this.handle(this.type === 'input' ? this.inputValue : true) }, confirm ({ title, content }) { this.type = 'confirm' - this.show = true this.title = title || '提示' this.content = content || '' + this.show = true return new Promise(resolve => { this.resolve = resolve }) }, - input ({ type, title, hint, value, content }) { + input ({ type, title, hint, value, content, select = true }) { this.type = 'input' - this.show = true this.title = title || '请输入' this.content = content || '' this.inputType = type || 'text' this.inputValue = value || '' this.inputHint = hint || '' + this.show = true this.$nextTick(() => { this.$refs.input.focus() - this.$refs.input.select() + + if (select) { + this.$refs.input.select() + if (Array.isArray(select)) { + this.$refs.input.setSelectionRange(...select) + } + } }) return new Promise(resolve => { diff --git a/frontend/src/components/Preview.vue b/frontend/src/components/Preview.vue index d32d9ede..3d844901 100644 --- a/frontend/src/components/Preview.vue +++ b/frontend/src/components/Preview.vue @@ -205,12 +205,11 @@ export default { canvas.width = ximg.naturalWidth canvas.height = ximg.naturalHeight canvas.getContext('2d').drawImage(ximg, 0, 0) - canvas.toBlob(blob => { + canvas.toBlob(async blob => { const imgFile = new File([blob], 'file.png') - file.upload(this.fileRepo, this.filePath, imgFile, result => { - this.$bus.emit('tree-refresh') - this.$bus.emit('editor-replace-value', img.src, result.relativePath) - }) + const { relativePath } = await file.upload(this.fileRepo, this.filePath, imgFile) + this.$bus.emit('tree-refresh') + this.$bus.emit('editor-replace-value', img.src, relativePath) }) } @@ -221,12 +220,11 @@ export default { img.src.startsWith('https://') ) { window.fetch(`api/proxy?url=${encodeURI(img.src)}`).then(r => { - r.blob().then(blob => { + r.blob().then(async blob => { const imgFile = new File([blob], 'file.' + mime.extension(r.headers.get('content-type'))) - file.upload(this.fileRepo, this.filePath, imgFile, result => { - this.$bus.emit('tree-refresh') - this.$bus.emit('editor-replace-value', img.src, encodeURI(result.relativePath)) - }) + const { relativePath } = await file.upload(this.fileRepo, this.filePath, imgFile) + this.$bus.emit('tree-refresh') + this.$bus.emit('editor-replace-value', img.src, encodeURI(relativePath)) }) }) } diff --git a/frontend/src/components/TreeNode.vue b/frontend/src/components/TreeNode.vue index 86ebb1be..f0a44550 100644 --- a/frontend/src/components/TreeNode.vue +++ b/frontend/src/components/TreeNode.vue @@ -1,6 +1,6 @@ @@ -99,7 +98,8 @@ export default { title: '移动文件', hint: '新的路径', content: '当前路径:' + this.item.path, - value: this.item.path + value: this.item.path, + select: [this.item.path.lastIndexOf('/') + 1, this.item.path.length, 'forward'] }) if (!newPath) { @@ -153,10 +153,18 @@ export default { computed: { ...mapState('app', ['currentFile', 'currentRepo']), selected () { - return this.currentFile && this.currentFile.path === this.item.path && this.currentFile.repo === this.item.repo + if (!this.currentFile) { + return false + } + + if (this.item.type === 'dir') { + return this.currentFile.repo === this.item.repo && this.currentFile.path.startsWith(this.item.path + '/') + } + + return this.currentFile.repo === this.item.repo && this.currentFile.path === this.item.path }, shouldOpen () { - return this.currentFile && this.item.type === 'dir' && this.currentFile.path.startsWith(this.item.path + '/') + return this.item.type === 'dir' && this.currentFile && this.currentFile.path.startsWith(this.item.path + '/') && this.currentFile.repo === this.item.repo } }, watch: { @@ -178,7 +186,7 @@ export default { .tree-node { border-left: 1px rgb(87, 87, 87) solid; font-size: 16px; - line-height: 1.3em; + line-height: 1.4em; padding-left: 1em; } @@ -192,6 +200,23 @@ export default { overflow: hidden; } +.file-name { + padding-left: 0.2em; + transition: 50ms ease; +} + +.file-name.selected { + background: #5d5d5d; +} + +.file-name:hover { + background: #5d5d5d; +} + +.file-name:active { + padding-left: 0.3em; +} + .dir-label .count { color: #989898; font-size: 12px; diff --git a/frontend/src/lib/file.js b/frontend/src/lib/file.js index f7c276bf..e0e04a54 100644 --- a/frontend/src/lib/file.js +++ b/frontend/src/lib/file.js @@ -98,7 +98,7 @@ const search = async (repo, text) => { } const upload = async (repo, belongPath, uploadFile, name = null) => { - return Promise((resolve, reject) => { + return new Promise((resolve, reject) => { belongPath = belongPath.replace(/\\/g, '/') const fr = new FileReader() diff --git a/frontend/src/store/app.js b/frontend/src/store/app.js index 7ec5b350..df51cc45 100644 --- a/frontend/src/store/app.js +++ b/frontend/src/store/app.js @@ -1,6 +1,34 @@ import Storage from '@/lib/Storage' import file from '@/lib/file' +const getLastOpenFile = repo => { + const currentFile = Storage.get('currentFile') + const recentOpenTime = Storage.get('recentOpenTime', {}) + + if (!repo) { + return null + } + + if (currentFile && currentFile.repo === repo.name) { + return currentFile + } + + const item = Object.entries(recentOpenTime) + .filter(x => x[0].startsWith(repo.name + '|')) + .sort((a, b) => b[1] - a[1])[0] + + if (!item) { + return null + } + + const path = item[0].split('|', 2)[1] + if (!path) { + return null + } + + return { repo: repo.name, name: file.basename(path), path } +} + export default { namespaced: true, state: { @@ -13,7 +41,7 @@ export default { previousContent: '', previousHash: '', currentRepo: Storage.get('currentRepo'), - currentFile: Storage.get('currentFile'), + currentFile: getLastOpenFile(Storage.get('currentRepo')), recentOpenTime: Storage.get('recentOpenTime', {}), passwordHash: {}, documentInfo: { @@ -59,7 +87,7 @@ export default { }, setCurrentRepo (state, data) { state.currentRepo = data - state.currentFile = null + state.currentFile = getLastOpenFile(data) state.tree = null Storage.set('currentRepo', data) }, diff --git a/frontend/vue.config.js b/frontend/vue.config.js index 6adc4d0e..07a0a94f 100644 --- a/frontend/vue.config.js +++ b/frontend/vue.config.js @@ -3,13 +3,13 @@ module.exports = { port: 8066, proxy: { '/static': { - target: 'http://localhost:3044' + target: 'http://localhost:3000' }, '/api': { - target: 'http://localhost:3044' + target: 'http://localhost:3000' }, '/ws': { - target: 'http://localhost:3044', + target: 'http://localhost:3000', ws: true } }