feat: 增加自定义图片上传目录配置

This commit is contained in:
purocean
2021-10-27 15:53:38 +08:00
parent 29d56221b4
commit 4febcadfcc
11 changed files with 92 additions and 58 deletions
+2 -2
View File
@@ -169,7 +169,7 @@ export default defineComponent({
}
::v-deep(.je-form-input-label) {
width: 90px;
width: 100px;
display: inline-flex;
align-items: center;
flex: none;
@@ -177,7 +177,7 @@ export default defineComponent({
}
::v-deep(.je-form-input-label + input) {
max-width: calc(100% - 110px);
max-width: calc(100% - 120px);
}
::v-deep(.je-form-input-label ~ .je-form-input-label) {
+2
View File
@@ -8,6 +8,7 @@ import { useToast } from '@fe/support/ui/toast'
import { useModal } from '@fe/support/ui/modal'
import * as env from '@fe/support/env'
import store from '@fe/support/store'
import * as base from '@fe/services/base'
import * as api from '@fe/support/api'
import * as embed from '@fe/support/embed'
import * as args from '@fe/support/args'
@@ -23,6 +24,7 @@ import * as setting from '@fe/services/setting'
import * as lib from './lib'
const ctx = {
base,
api,
args,
store,
+4 -5
View File
@@ -1,27 +1,26 @@
import dayjs from 'dayjs'
import { insert, whenEditorReady } from '@fe/services/editor'
import type { Plugin } from '@fe/context'
import * as api from '@fe/support/api'
import store from '@fe/support/store'
import { encodeMarkdownLink } from '@fe/utils'
import type { Doc } from '@fe/types'
import { dirname, isBelongTo, join, relative } from '@fe/utils/path'
import { getActionHandler } from '@fe/core/action'
import { refreshTree } from '@fe/services/tree'
import { upload } from '@fe/services/base'
async function uploadFile (file: any, asImage: boolean) {
if (!store.state.currentFile) {
throw new Error('当前未打开文件')
}
const { repo, path } = store.state.currentFile
const filename = file.name
const { relativePath } = await api.upload(repo, path, file, filename)
const assetPath = await upload(file, store.state.currentFile, filename)
if (asImage) {
insert(`![图片](${encodeMarkdownLink(relativePath)})\n`)
insert(`![图片](${encodeMarkdownLink(assetPath)})\n`)
} else {
insert(`附件 [${dayjs().format('YYYY-MM-DD HH:mm')}] [${file.name} (${(file.size / 1024).toFixed(2)}KiB)](${encodeMarkdownLink(relativePath)}){class=open target=_blank}\n`)
insert(`附件 [${dayjs().format('YYYY-MM-DD HH:mm')}] [${file.name} (${(file.size / 1024).toFixed(2)}KiB)](${encodeMarkdownLink(assetPath)}){class=open target=_blank}\n`)
}
refreshTree()
+3 -4
View File
@@ -3,7 +3,7 @@ import { getEditor, insert } from '@fe/services/editor'
import { Plugin } from '@fe/context'
import { triggerHook } from '@fe/core/plugin'
import { refreshTree } from '@fe/services/tree'
import * as api from '@fe/support/api'
import { upload } from '@fe/services/base'
import store from '@fe/support/store'
import { encodeMarkdownLink, fileToBase64URL } from '@fe/utils'
@@ -35,9 +35,8 @@ async function pasteImage (file: File, asBase64: boolean) {
}
if (!(await triggerHook('ON_EDITOR_PASTE_IMAGE', file))) {
const { repo, path } = store.state.currentFile
const { relativePath } = await api.upload(repo, path, file)
insert(`![图片](${encodeMarkdownLink(relativePath)})\n`)
const assetPath = await upload(file, store.state.currentFile)
insert(`![图片](${encodeMarkdownLink(assetPath)})\n`)
}
refreshTree()
+2 -2
View File
@@ -4,7 +4,7 @@ import { Plugin } from '@fe/context'
import store from '@fe/support/store'
import { sleep } from '@fe/utils'
import { isElectron, nodeRequire } from '@fe/support/env'
import { basename, dirname, join } from '@fe/utils/path'
import { basename, dirname, join, resolve } from '@fe/utils/path'
import { switchDoc } from '@fe/services/document'
const handleLink = (link: HTMLAnchorElement) => {
@@ -121,7 +121,7 @@ function convertLink (state: StateCore) {
return
}
const filePath = join(basePath, val)
const filePath = resolve(basePath, val)
token.attrSet(attrName, `api/attachment/${encodeURIComponent(fileName)}?repo=${repo}&path=${encodeURIComponent(filePath)}`)
}
@@ -5,8 +5,9 @@ import { useToast } from '@fe/support/ui/toast'
import store from '@fe/support/store'
import { CtrlCmd, getKeysLabel, isCommand, LeftClick, Shift } from '@fe/core/shortcut'
import { replaceValue } from '@fe/services/editor'
import { Plugin } from '@fe/context'
import { refreshTree } from '@fe/services/tree'
import { upload } from '@fe/services/base'
import type { Plugin } from '@fe/context'
import type { BuildInActionName } from '@fe/types'
async function transformImgOutLink (img: HTMLImageElement) {
@@ -15,8 +16,6 @@ async function transformImgOutLink (img: HTMLImageElement) {
return
}
const { repo, path } = currentFile
const transform = (ximg: HTMLImageElement): Promise<string> => {
const canvas = document.createElement('canvas')
canvas.width = ximg.naturalWidth
@@ -26,8 +25,8 @@ async function transformImgOutLink (img: HTMLImageElement) {
canvas.toBlob(async blob => {
try {
const imgFile = new File([blob!], 'file.png')
const { relativePath } = await api.upload(repo, path, imgFile)
resolve(relativePath)
const assetPath = await upload(imgFile, currentFile)
resolve(assetPath)
} catch (error) {
reject(error)
}
@@ -44,8 +43,8 @@ async function transformImgOutLink (img: HTMLImageElement) {
const res = await api.proxyRequest(img.src, { method: 'get' }, headers)
const blob = await res.blob()
const imgFile = new File([blob!], 'file.' + mime.extension(res.headers.get('content-type')!))
const { relativePath } = await api.upload(repo, path, imgFile)
replacedLink = relativePath
const assetPath = await upload(imgFile, currentFile)
replacedLink = assetPath
}
if (replacedLink) {
+37
View File
@@ -0,0 +1,37 @@
import { slugify } from 'transliteration'
import type { Doc } from '@fe/types'
import * as api from '@fe/support/api'
import { getSettings } from './setting'
import { FLAG_DEMO } from '@fe/support/args'
import { binMd5, fileToBase64URL, getLogger } from '@fe/utils'
import { basename, resolve, extname, dirname, relative } from '@fe/utils/path'
import { dayjs } from '@fe/context/lib'
const logger = getLogger('service-base')
export async function upload (file: File, belongDoc: Pick<Doc, 'repo' | 'path'>, name?: string) {
if (FLAG_DEMO) {
return Promise.resolve(URL.createObjectURL(file))
}
const fileBase64Url = await fileToBase64URL(file)
const filename = name || binMd5(fileBase64Url).substr(0, 8) + extname(file.name)
const dirName = slugify(basename(belongDoc.path))
const parentPath = dirname(belongDoc.path)
const assetsDir = getSettings()['assets-dir']
.replace('{docSlug}', dirName.startsWith('.') ? 'upload' : dirName)
.replace('{date}', dayjs().format('YYYY-MM-DD'))
const path: string = resolve(parentPath, assetsDir, filename)
logger.debug('upload', belongDoc, file, path)
await api.upload(belongDoc.repo, fileBase64Url, path)
if (!assetsDir.startsWith('/')) {
return './' + relative(parentPath, path)
}
return path
}
+23 -6
View File
@@ -1,9 +1,20 @@
import * as api from '@fe/support/api'
import { FLAG_DISABLE_XTERM } from '@fe/support/args'
import store from '@fe/support/store'
import { BuildInSettings } from '@fe/types'
import { getThemeName } from './theme'
const schema = {
export type Schema = {
type: string,
title: string,
properties: {[K in keyof BuildInSettings]: {
defaultValue: BuildInSettings[K] extends any ? BuildInSettings[K] : any
[key: string]: any,
}},
required: (keyof BuildInSettings)[],
}
const schema: Schema = {
type: 'object',
title: '配置项',
properties: {
@@ -41,27 +52,33 @@ const schema = {
type: 'string',
enum: ['system', 'dark', 'light']
},
'assets-dir': {
defaultValue: './FILES/{docSlug}',
title: '图片存放目录',
type: 'string',
minLength: 1,
description: '支持相对路径和绝对路径(限于仓库内部),可用变量:docSlug, date'
} as any,
shell: {
defaultValue: '',
title: '终端 Shell',
type: 'string',
} as any,
},
} as any,
required: ['theme'],
dependentSchemas: {},
}
const settings = getDefaultSetting()
if (FLAG_DISABLE_XTERM) {
delete schema.properties.shell
delete (schema.properties as any).shell
}
export function getSchema () {
return schema
}
export function tapSchema (fun: (schema: any) => void) {
export function tapSchema (fun: (schema: Schema) => void) {
fun(schema)
}
@@ -81,7 +98,7 @@ function transformSettings (data: any) {
export function getDefaultSetting () {
return Object.fromEntries(
Object.entries(schema.properties).map(([key, val]) => [key, val.defaultValue])
)
) as BuildInSettings
}
export async function fetchSettings () {
+3 -32
View File
@@ -1,9 +1,5 @@
import { slugify } from 'transliteration'
import { basename, relative, extname, join, dirname } from '@fe/utils/path'
import { binMd5, fileToBase64URL } from '@fe/utils'
import type { Doc } from '@fe/types'
import { isElectron } from '@fe/support/env'
import { Doc } from '@fe/types'
import { FLAG_DEMO } from './args'
async function fetchHttp (input: RequestInfo, init?: RequestInit) {
const response = await fetch(input, init)
@@ -118,38 +114,13 @@ export async function search (repo: string, text: string) {
return result.data
}
export async function upload (repo: string, belongPath: string, uploadFile: any, name: string | null = null) {
if (FLAG_DEMO) {
return Promise.resolve({
repo,
path: belongPath,
relativePath: URL.createObjectURL(uploadFile)
})
}
const fileBase64Url = await fileToBase64URL(uploadFile)
const filename = name || binMd5(fileBase64Url).substr(0, 8) + extname(uploadFile.name)
export async function upload (repo: string, fileBase64Url: string, filePath: string) {
const formData = new FormData()
const dirName = slugify(basename(belongPath))
const parentPath = dirname(belongPath)
const path: string = join(
parentPath,
'FILES',
dirName.startsWith('.') ? 'upload' : dirName, filename
)
formData.append('repo', repo)
formData.append('path', path)
formData.append('path', filePath)
formData.append('attachment', fileBase64Url)
await fetchHttp('/api/attachment', { method: 'POST', body: formData })
let relativePath: string = relative(parentPath, path)
if (!relativePath.startsWith('/') || !relativePath.startsWith('./')) {
relativePath = './' + relativePath
}
return { repo, path, relativePath }
}
export async function openInOS ({ repo, path }: Doc) {
+9
View File
@@ -91,6 +91,15 @@ export type BuildInEvents = {
'doc.changed': Doc,
}
export type BuildInSettings = {
'repos': { name: string, path: string }[],
'theme': ThemeName,
'assets-dir': string,
'shell': string,
'plugin.image-hosting-picgo.server-url': string,
'plugin.image-hosting-picgo.enable-paste-image': boolean
}
export type HookType = 'ON_STARTUP'
| 'ON_EDITOR_PASTE_IMAGE'
| 'ON_VIEW_ELEMENT_CLICK'
+1
View File
@@ -5,6 +5,7 @@ export const {
extname,
dirname,
join,
resolve,
} = Path
export function relative (from: string, to: string) {