mirror of
https://github.com/purocean/yn.git
synced 2026-10-12 23:31:39 +08:00
Merge branch 'release/2.0.0-alpha3'
This commit is contained in:
@@ -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. 默认仓库数据和配置改为在 `<home>/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. 目录树和集成终端增加拖动调整尺寸功能
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<title>YN</title>
|
||||
<title>Yank-Note</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
@@ -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(`})\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(`})\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()
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<div class="wrapper" @click.stop>
|
||||
<h4>{{title}}</h4>
|
||||
<p v-if="content">{{content}}</p>
|
||||
<input v-if="type === 'input'" ref="input" @keydown.enter.stop="ok" :type="type" :placeholder="inputHint" v-model="inputValue">
|
||||
<input v-if="type === 'input'" ref="input" :type="inputType" :placeholder="inputHint" v-model="inputValue">
|
||||
<div class="action">
|
||||
<button @click="cancel">取消</button>
|
||||
<button class="primary" @click="ok">确定</button>
|
||||
@@ -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 => {
|
||||
|
||||
@@ -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))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="tree-node">
|
||||
<details v-if="item.type === 'dir'" class="name" :title="item.name + '\n\n' + dirTitle" ref="dir" :open="item.path === '/'">
|
||||
<details @keydown.enter.prevent v-if="item.type === 'dir'" class="name" :title="item.name + '\n\n' + dirTitle" ref="dir" :open="item.path === '/'">
|
||||
<summary
|
||||
class="dir-label"
|
||||
:style="{background: selected ? '#313131' : 'none'}"
|
||||
@@ -13,13 +13,12 @@
|
||||
</details>
|
||||
<div
|
||||
v-else
|
||||
class="name"
|
||||
:class="{name: true, 'file-name': true, selected}"
|
||||
:title="item.name + '\n\n' + fileTitle"
|
||||
@click="select(item)"
|
||||
@dblclick.ctrl.exact="revealInExplorer()"
|
||||
@contextmenu.ctrl.prevent="renameFile"
|
||||
@contextmenu.shift.prevent="deleteFile"
|
||||
:style="{background: selected ? '#313131' : 'none'}"> {{ item.name }} </div>
|
||||
@contextmenu.shift.prevent="deleteFile"> {{ item.name }} </div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
},
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user