图片支持相对本地空间的路径存储
This commit is contained in:
parent
e7450df251
commit
a4ab4b2da1
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "StackEdit中文版",
|
"name": "StackEdit中文版",
|
||||||
"description": "支持Gitee仓库/粘贴图片自动上传的浏览器内 Markdown 编辑器",
|
"description": "支持Gitee仓库/粘贴图片自动上传的浏览器内 Markdown 编辑器",
|
||||||
"version": "5.15.14",
|
"version": "5.15.15",
|
||||||
"manifest_version": 2,
|
"manifest_version": 2,
|
||||||
"container" : "GITEE",
|
"container" : "GITEE",
|
||||||
"api_console_project_id" : "241271498917",
|
"api_console_project_id" : "241271498917",
|
||||||
|
2
package-lock.json
generated
2
package-lock.json
generated
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "stackedit",
|
"name": "stackedit",
|
||||||
"version": "5.15.14",
|
"version": "5.15.15",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "stackedit",
|
"name": "stackedit",
|
||||||
"version": "5.15.14",
|
"version": "5.15.15",
|
||||||
"description": "免费, 开源, 功能齐全的 Markdown 编辑器",
|
"description": "免费, 开源, 功能齐全的 Markdown 编辑器",
|
||||||
"author": "Benoit Schweblin, 豆萁",
|
"author": "Benoit Schweblin, 豆萁",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
@ -34,7 +34,7 @@ export default {
|
|||||||
]),
|
]),
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async setImgAndDoClick(items) {
|
async processUpload(items) {
|
||||||
let file = null;
|
let file = null;
|
||||||
if (!items || items.length === 0) {
|
if (!items || items.length === 0) {
|
||||||
return;
|
return;
|
||||||
@ -73,6 +73,11 @@ export default {
|
|||||||
if (currImgStorageStr) {
|
if (currImgStorageStr) {
|
||||||
store.commit('img/changeCheckedStorage', JSON.parse(currImgStorageStr));
|
store.commit('img/changeCheckedStorage', JSON.parse(currImgStorageStr));
|
||||||
}
|
}
|
||||||
|
// 当前本地图片路径配置
|
||||||
|
const workspaceImgPath = localStorage.getItem('img/workspaceImgPath');
|
||||||
|
if (workspaceImgPath) {
|
||||||
|
store.commit('img/setWorkspaceImgPath', JSON.parse(workspaceImgPath));
|
||||||
|
}
|
||||||
const editorElt = this.$el.querySelector('.editor__inner');
|
const editorElt = this.$el.querySelector('.editor__inner');
|
||||||
const onDiscussionEvt = cb => (evt) => {
|
const onDiscussionEvt = cb => (evt) => {
|
||||||
let elt = evt.target;
|
let elt = evt.target;
|
||||||
@ -100,11 +105,11 @@ export default {
|
|||||||
|
|
||||||
editorElt.addEventListener('drop', (event) => {
|
editorElt.addEventListener('drop', (event) => {
|
||||||
const transItems = event.dataTransfer.items;
|
const transItems = event.dataTransfer.items;
|
||||||
this.setImgAndDoClick(transItems);
|
this.processUpload(transItems);
|
||||||
});
|
});
|
||||||
editorElt.addEventListener('paste', (event) => {
|
editorElt.addEventListener('paste', (event) => {
|
||||||
const pasteItems = (event.clipboardData || window.clipboardData).items;
|
const pasteItems = (event.clipboardData || window.clipboardData).items;
|
||||||
this.setImgAndDoClick(pasteItems);
|
this.processUpload(pasteItems);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$watch(
|
this.$watch(
|
||||||
|
@ -40,6 +40,7 @@ import AccountManagementModal from './modals/AccountManagementModal';
|
|||||||
import BadgeManagementModal from './modals/BadgeManagementModal';
|
import BadgeManagementModal from './modals/BadgeManagementModal';
|
||||||
import SponsorModal from './modals/SponsorModal';
|
import SponsorModal from './modals/SponsorModal';
|
||||||
import CommitMessageModal from './modals/CommitMessageModal';
|
import CommitMessageModal from './modals/CommitMessageModal';
|
||||||
|
import WorkspaceImgPathModal from './modals/WorkspaceImgPathModal';
|
||||||
|
|
||||||
// Providers
|
// Providers
|
||||||
import GooglePhotoModal from './modals/providers/GooglePhotoModal';
|
import GooglePhotoModal from './modals/providers/GooglePhotoModal';
|
||||||
@ -107,6 +108,7 @@ export default {
|
|||||||
BadgeManagementModal,
|
BadgeManagementModal,
|
||||||
SponsorModal,
|
SponsorModal,
|
||||||
CommitMessageModal,
|
CommitMessageModal,
|
||||||
|
WorkspaceImgPathModal,
|
||||||
// Providers
|
// Providers
|
||||||
GooglePhotoModal,
|
GooglePhotoModal,
|
||||||
GoogleDriveAccountModal,
|
GoogleDriveAccountModal,
|
||||||
|
@ -15,6 +15,21 @@
|
|||||||
<div>
|
<div>
|
||||||
<hr />
|
<hr />
|
||||||
<p>添加并选择图床后可在编辑区中粘贴/拖拽图片自动上传</p>
|
<p>添加并选择图床后可在编辑区中粘贴/拖拽图片自动上传</p>
|
||||||
|
|
||||||
|
<menu-entry @click.native="checkedImgDest(path)" v-for="path in workspaceImgPath" :key="path">
|
||||||
|
<icon-check-circle v-if="checkedStorage.sub === path" slot="icon"></icon-check-circle>
|
||||||
|
<icon-check-circle-un v-if="checkedStorage.sub !== path" slot="icon"></icon-check-circle-un>
|
||||||
|
<menu-item>
|
||||||
|
<icon-provider slot="icon" :provider-id="currentWorkspace.providerId"></icon-provider>
|
||||||
|
<div>
|
||||||
|
本文档空间图片路径
|
||||||
|
<button class="menu-item__button button" @click.stop="removeByPath(path)" v-title="'删除'">
|
||||||
|
<icon-delete></icon-delete>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<span>路径:{{path}}</span>
|
||||||
|
</menu-item>
|
||||||
|
</menu-entry>
|
||||||
<menu-entry @click.native="checkedImgDest(token.sub, token.providerId)" v-for="token in imageTokens" :key="token.sub">
|
<menu-entry @click.native="checkedImgDest(token.sub, token.providerId)" v-for="token in imageTokens" :key="token.sub">
|
||||||
<icon-check-circle v-if="checkedStorage.sub === token.sub" slot="icon"></icon-check-circle>
|
<icon-check-circle v-if="checkedStorage.sub === token.sub" slot="icon"></icon-check-circle>
|
||||||
<icon-check-circle-un v-if="checkedStorage.sub !== token.sub" slot="icon"></icon-check-circle-un>
|
<icon-check-circle-un v-if="checkedStorage.sub !== token.sub" slot="icon"></icon-check-circle-un>
|
||||||
@ -45,6 +60,10 @@
|
|||||||
<span> {{tokenStorage.uname}}, 仓库URL: {{tokenStorage.repoUrl}}, 路径: {{tokenStorage.path}}, 分支: {{tokenStorage.branch}}</span>
|
<span> {{tokenStorage.uname}}, 仓库URL: {{tokenStorage.repoUrl}}, 路径: {{tokenStorage.path}}, 分支: {{tokenStorage.branch}}</span>
|
||||||
</menu-item>
|
</menu-item>
|
||||||
</menu-entry>
|
</menu-entry>
|
||||||
|
<menu-entry @click.native="addWorkspaceImgPath">
|
||||||
|
<icon-provider slot="icon" :provider-id="currentWorkspace.providerId"></icon-provider>
|
||||||
|
<span>添加本文档空间图片路径</span>
|
||||||
|
</menu-entry>
|
||||||
<menu-entry @click.native="addSmmsAccount">
|
<menu-entry @click.native="addSmmsAccount">
|
||||||
<icon-provider slot="icon" provider-id="smms"></icon-provider>
|
<icon-provider slot="icon" provider-id="smms"></icon-provider>
|
||||||
<span>添加SM.MS图床账号</span>
|
<span>添加SM.MS图床账号</span>
|
||||||
@ -66,6 +85,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { mapGetters } from 'vuex';
|
||||||
import modalTemplate from './common/modalTemplate';
|
import modalTemplate from './common/modalTemplate';
|
||||||
import MenuEntry from '../menus/common/MenuEntry';
|
import MenuEntry from '../menus/common/MenuEntry';
|
||||||
import MenuItem from '../menus/common/MenuItem';
|
import MenuItem from '../menus/common/MenuItem';
|
||||||
@ -87,9 +107,16 @@ export default modalTemplate({
|
|||||||
url: '',
|
url: '',
|
||||||
}),
|
}),
|
||||||
computed: {
|
computed: {
|
||||||
|
...mapGetters('workspace', [
|
||||||
|
'currentWorkspace',
|
||||||
|
]),
|
||||||
checkedStorage() {
|
checkedStorage() {
|
||||||
return store.getters['img/getCheckedStorage'];
|
return store.getters['img/getCheckedStorage'];
|
||||||
},
|
},
|
||||||
|
workspaceImgPath() {
|
||||||
|
const workspaceImgPath = store.getters['img/getWorkspaceImgPath'];
|
||||||
|
return Object.keys(workspaceImgPath || {});
|
||||||
|
},
|
||||||
imageTokens() {
|
imageTokens() {
|
||||||
return [
|
return [
|
||||||
...Object.values(store.getters['data/smmsTokensBySub']).map(token => ({
|
...Object.values(store.getters['data/smmsTokensBySub']).map(token => ({
|
||||||
@ -195,6 +222,13 @@ export default modalTemplate({
|
|||||||
// Cancel
|
// Cancel
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
async removeByPath(path) {
|
||||||
|
store.dispatch('img/removeWorkspaceImgPath', path);
|
||||||
|
},
|
||||||
|
async addWorkspaceImgPath() {
|
||||||
|
const { path } = await store.dispatch('modal/open', { type: 'workspaceImgPath' });
|
||||||
|
store.dispatch('img/addWorkspaceImgPath', path);
|
||||||
|
},
|
||||||
async addSmmsAccount() {
|
async addSmmsAccount() {
|
||||||
const { proxyUrl, apiSecretToken } = await store.dispatch('modal/open', { type: 'smmsAccount' });
|
const { proxyUrl, apiSecretToken } = await store.dispatch('modal/open', { type: 'smmsAccount' });
|
||||||
await smmsHelper.addAccount(proxyUrl, apiSecretToken);
|
await smmsHelper.addAccount(proxyUrl, apiSecretToken);
|
||||||
@ -227,7 +261,10 @@ export default modalTemplate({
|
|||||||
},
|
},
|
||||||
async checkedImgDest(sub, provider, sid) {
|
async checkedImgDest(sub, provider, sid) {
|
||||||
let type = 'token';
|
let type = 'token';
|
||||||
if (provider === 'gitea' || provider === 'github') {
|
// 当前文档空间存储
|
||||||
|
if (!provider) {
|
||||||
|
type = 'workspace';
|
||||||
|
} else if (provider === 'gitea' || provider === 'github') {
|
||||||
type = 'tokenRepo';
|
type = 'tokenRepo';
|
||||||
}
|
}
|
||||||
store.dispatch('img/changeCheckedStorage', {
|
store.dispatch('img/changeCheckedStorage', {
|
||||||
|
45
src/components/modals/WorkspaceImgPathModal.vue
Normal file
45
src/components/modals/WorkspaceImgPathModal.vue
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<template>
|
||||||
|
<modal-inner aria-label="文档空间图片路径">
|
||||||
|
<div class="modal__content">
|
||||||
|
<div class="modal__image">
|
||||||
|
<icon-provider :provider-id="currentWorkspace.providerId"></icon-provider>
|
||||||
|
</div>
|
||||||
|
<p>在当前文档空间增加图片上传路径。</p>
|
||||||
|
<form-entry label="图片上传路径" error="path">
|
||||||
|
<input slot="field" class="textfield" type="text" placeholder="如:/imgs/{YYYY}-{MM}-{DD}" v-model.trim="path" @keydown.enter="resolve()">
|
||||||
|
<div class="form-entry__info">
|
||||||
|
如果不提供,默认为 /imgs/{YYYY}-{MM}-{DD} ,其中{YYYY}为年变量、{MM}为月变量、{DD}为日变量。<br/>
|
||||||
|
支持相对路径,如 ./imgs 或 imgs 都是相对当前编辑中文档的路径,不支持相对上级路径。
|
||||||
|
</div>
|
||||||
|
</form-entry>
|
||||||
|
</div>
|
||||||
|
<div class="modal__button-bar">
|
||||||
|
<button class="button" @click="config.reject()">取消</button>
|
||||||
|
<button class="button button--resolve" @click="resolve()">确认</button>
|
||||||
|
</div>
|
||||||
|
</modal-inner>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapGetters } from 'vuex';
|
||||||
|
import modalTemplate from './common/modalTemplate';
|
||||||
|
|
||||||
|
export default modalTemplate({
|
||||||
|
computedLocalSettings: {
|
||||||
|
path: '',
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters('workspace', [
|
||||||
|
'currentWorkspace',
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
resolve() {
|
||||||
|
const path = this.path && this.path.replace(/^\//, '');
|
||||||
|
this.config.resolve({
|
||||||
|
path: path || '/imgs/{YYYY}-{MM}-{DD}',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
@ -2,6 +2,7 @@ import Vue from 'vue';
|
|||||||
import DiffMatchPatch from 'diff-match-patch';
|
import DiffMatchPatch from 'diff-match-patch';
|
||||||
import Prism from 'prismjs';
|
import Prism from 'prismjs';
|
||||||
import markdownItPandocRenderer from 'markdown-it-pandoc-renderer';
|
import markdownItPandocRenderer from 'markdown-it-pandoc-renderer';
|
||||||
|
import md5 from 'js-md5';
|
||||||
import cledit from './editor/cledit';
|
import cledit from './editor/cledit';
|
||||||
import pagedown from '../libs/pagedown';
|
import pagedown from '../libs/pagedown';
|
||||||
import htmlSanitizer from '../libs/htmlSanitizer';
|
import htmlSanitizer from '../libs/htmlSanitizer';
|
||||||
@ -13,6 +14,9 @@ import editorSvcDiscussions from './editor/editorSvcDiscussions';
|
|||||||
import editorSvcUtils from './editor/editorSvcUtils';
|
import editorSvcUtils from './editor/editorSvcUtils';
|
||||||
import utils from './utils';
|
import utils from './utils';
|
||||||
import store from '../store';
|
import store from '../store';
|
||||||
|
import syncSvc from './syncSvc';
|
||||||
|
import constants from '../data/constants';
|
||||||
|
import localDbSvc from './localDbSvc';
|
||||||
|
|
||||||
const allowDebounce = (action, wait) => {
|
const allowDebounce = (action, wait) => {
|
||||||
let timeoutId;
|
let timeoutId;
|
||||||
@ -40,6 +44,39 @@ class SectionDesc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const pathUrlMap = Object.create(null);
|
||||||
|
|
||||||
|
const getCurrAbsolutePath = () => {
|
||||||
|
const fileId = store.getters['file/current'].id;
|
||||||
|
const fileSyncData = store.getters['data/syncDataByItemId'][fileId] || { id: '' };
|
||||||
|
const fileAbsolutePath = `${store.getters['workspace/currentWorkspace'].path || ''}${fileSyncData.id}`;
|
||||||
|
return fileAbsolutePath.substring(0, fileAbsolutePath.lastIndexOf('/'));
|
||||||
|
};
|
||||||
|
|
||||||
|
const getImgUrl = async (uri) => {
|
||||||
|
if (uri.indexOf('http://') !== 0 && uri.indexOf('https://') !== 0) {
|
||||||
|
const absoluteImgPath = utils.getAbsoluteFilePath(getCurrAbsolutePath(), uri);
|
||||||
|
if (pathUrlMap[absoluteImgPath]) {
|
||||||
|
return pathUrlMap[absoluteImgPath];
|
||||||
|
}
|
||||||
|
const md5Id = md5(absoluteImgPath);
|
||||||
|
let imgItem = await localDbSvc.getImgItem(md5Id);
|
||||||
|
if (!imgItem) {
|
||||||
|
await syncSvc.syncImg(absoluteImgPath);
|
||||||
|
imgItem = await localDbSvc.getImgItem(md5Id);
|
||||||
|
}
|
||||||
|
if (imgItem) {
|
||||||
|
// imgItem 如果不存在 则加载 TODO
|
||||||
|
const imgFile = utils.base64ToBlob(imgItem.content, uri);
|
||||||
|
const url = URL.createObjectURL(imgFile);
|
||||||
|
pathUrlMap[absoluteImgPath] = url;
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return uri;
|
||||||
|
};
|
||||||
|
|
||||||
// Use a vue instance as an event bus
|
// Use a vue instance as an event bus
|
||||||
const editorSvc = Object.assign(new Vue(), editorSvcDiscussions, editorSvcUtils, {
|
const editorSvc = Object.assign(new Vue(), editorSvcDiscussions, editorSvcUtils, {
|
||||||
// Elements
|
// Elements
|
||||||
@ -212,11 +249,18 @@ const editorSvc = Object.assign(new Vue(), editorSvcDiscussions, editorSvcUtils,
|
|||||||
this.makeTextToPreviewDiffs();
|
this.makeTextToPreviewDiffs();
|
||||||
|
|
||||||
// Wait for images to load
|
// Wait for images to load
|
||||||
const loadedPromises = loadingImages.map(imgElt => new Promise((resolve) => {
|
const loadedPromises = loadingImages.map(imgElt => new Promise((resolve, reject) => {
|
||||||
if (!imgElt.src) {
|
if (!imgElt.src) {
|
||||||
resolve();
|
resolve();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (imgElt.src.indexOf(constants.origin) >= 0) {
|
||||||
|
getImgUrl(imgElt.src.replace(constants.origin, '')).then((newUrl) => {
|
||||||
|
imgElt.src = newUrl;
|
||||||
|
resolve();
|
||||||
|
}, () => reject(new Error('加载本地空间图片出错')));
|
||||||
|
return;
|
||||||
|
}
|
||||||
const img = new window.Image();
|
const img = new window.Image();
|
||||||
img.onload = resolve;
|
img.onload = resolve;
|
||||||
img.onerror = resolve;
|
img.onerror = resolve;
|
||||||
@ -471,6 +515,7 @@ const editorSvc = Object.assign(new Vue(), editorSvcDiscussions, editorSvcUtils,
|
|||||||
let imgEltsToCache = [];
|
let imgEltsToCache = [];
|
||||||
if (store.getters['data/computedSettings'].editor.inlineImages) {
|
if (store.getters['data/computedSettings'].editor.inlineImages) {
|
||||||
this.clEditor.highlighter.on('sectionHighlighted', (section) => {
|
this.clEditor.highlighter.on('sectionHighlighted', (section) => {
|
||||||
|
const loadImgs = [];
|
||||||
section.elt.getElementsByClassName('token img').cl_each((imgTokenElt) => {
|
section.elt.getElementsByClassName('token img').cl_each((imgTokenElt) => {
|
||||||
const srcElt = imgTokenElt.querySelector('.token.cl-src');
|
const srcElt = imgTokenElt.querySelector('.token.cl-src');
|
||||||
if (srcElt) {
|
if (srcElt) {
|
||||||
@ -496,6 +541,9 @@ const editorSvc = Object.assign(new Vue(), editorSvcDiscussions, editorSvcUtils,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
imgEltsToCache.push(imgElt);
|
imgEltsToCache.push(imgElt);
|
||||||
|
if (imgElt.src.indexOf(origin) >= 0) {
|
||||||
|
loadImgs.push(imgElt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const imgTokenWrapper = document.createElement('span');
|
const imgTokenWrapper = document.createElement('span');
|
||||||
imgTokenWrapper.className = 'token img-wrapper';
|
imgTokenWrapper.className = 'token img-wrapper';
|
||||||
@ -504,9 +552,19 @@ const editorSvc = Object.assign(new Vue(), editorSvcDiscussions, editorSvcUtils,
|
|||||||
imgTokenWrapper.appendChild(imgTokenElt);
|
imgTokenWrapper.appendChild(imgTokenElt);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
if (loadImgs.length) {
|
||||||
|
// Wait for images to load
|
||||||
|
const loadWorkspaceImg = loadImgs.map(imgElt => new Promise((resolve, reject) => {
|
||||||
|
const uri = imgElt.src.replace(origin, '');
|
||||||
|
getImgUrl(uri).then((newUrl) => {
|
||||||
|
imgElt.src = newUrl;
|
||||||
|
resolve();
|
||||||
|
}, () => reject(new Error(`加载本地空间图片出错,uri:${uri}`)));
|
||||||
|
}));
|
||||||
|
Promise.all(loadWorkspaceImg).then();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.clEditor.highlighter.on('highlighted', () => {
|
this.clEditor.highlighter.on('highlighted', () => {
|
||||||
imgEltsToCache.forEach((imgElt) => {
|
imgEltsToCache.forEach((imgElt) => {
|
||||||
const cachedImgElt = getFromImgCache(imgElt);
|
const cachedImgElt = getFromImgCache(imgElt);
|
||||||
|
@ -1,17 +1,52 @@
|
|||||||
|
import md5 from 'js-md5';
|
||||||
import store from '../store';
|
import store from '../store';
|
||||||
import utils from './utils';
|
import utils from './utils';
|
||||||
|
import localDbSvc from './localDbSvc';
|
||||||
import smmsHelper from '../services/providers/helpers/smmsHelper';
|
import smmsHelper from '../services/providers/helpers/smmsHelper';
|
||||||
import giteaHelper from '../services/providers/helpers/giteaHelper';
|
import giteaHelper from '../services/providers/helpers/giteaHelper';
|
||||||
import githubHelper from '../services/providers/helpers/githubHelper';
|
import githubHelper from '../services/providers/helpers/githubHelper';
|
||||||
import customHelper from '../services/providers/helpers/customHelper';
|
import customHelper from '../services/providers/helpers/customHelper';
|
||||||
|
|
||||||
|
function getCurrAbsolutePath() {
|
||||||
|
const fileId = store.getters['file/current'].id;
|
||||||
|
const fileSyncData = store.getters['data/syncDataByItemId'][fileId] || { id: '' };
|
||||||
|
const fileAbsolutePath = `${store.getters['workspace/currentWorkspace'].path || ''}${fileSyncData.id}`;
|
||||||
|
return fileAbsolutePath.substring(0, fileAbsolutePath.lastIndexOf('/'));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getImagePath(confPath, imgType) {
|
||||||
|
const time = new Date();
|
||||||
|
const date = time.getDate();
|
||||||
|
const month = time.getMonth() + 1;
|
||||||
|
const year = time.getFullYear();
|
||||||
|
const path = confPath.replace('{YYYY}', year)
|
||||||
|
.replace('{MM}', `0${month}`.slice(-2)).replace('{DD}', `0${date}`.slice(-2));
|
||||||
|
return `${path}${path.endsWith('/') ? '' : '/'}${utils.uid()}.${imgType.split('/')[1]}`;
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
// 上传图片 返回图片链接
|
// 上传图片 返回图片链接
|
||||||
// { url: 'http://xxxx', error: 'xxxxxx'}
|
// { url: 'http://xxxx', error: 'xxxxxx'}
|
||||||
async updateImg(imgFile) {
|
async updateImg(imgFile) {
|
||||||
// 操作图片上传
|
// 操作图片上传
|
||||||
const currStorage = store.getters['img/getCheckedStorage'];
|
const currStorage = store.getters['img/getCheckedStorage'];
|
||||||
if (!currStorage || !currStorage.provider) {
|
if (!currStorage) {
|
||||||
|
return { error: '暂无已选择的图床!' };
|
||||||
|
}
|
||||||
|
// 判断是否文档空间路径
|
||||||
|
if (currStorage.type === 'workspace') {
|
||||||
|
const path = getImagePath(currStorage.sub, imgFile.type);
|
||||||
|
// 保存到indexeddb
|
||||||
|
const base64 = await utils.encodeFiletoBase64(imgFile);
|
||||||
|
const absolutePath = utils.getAbsoluteFilePath(getCurrAbsolutePath(), path);
|
||||||
|
await localDbSvc.saveImg({
|
||||||
|
id: md5(absolutePath),
|
||||||
|
path: absolutePath,
|
||||||
|
content: base64,
|
||||||
|
});
|
||||||
|
return { url: path };
|
||||||
|
}
|
||||||
|
if (!currStorage.provider) {
|
||||||
return { error: '暂无已选择的图床!' };
|
return { error: '暂无已选择的图床!' };
|
||||||
}
|
}
|
||||||
const token = store.getters[`data/${currStorage.provider}TokensBySub`][currStorage.sub];
|
const token = store.getters[`data/${currStorage.provider}TokensBySub`][currStorage.sub];
|
||||||
@ -32,13 +67,7 @@ export default {
|
|||||||
return { error: '暂无已选择的图床!' };
|
return { error: '暂无已选择的图床!' };
|
||||||
}
|
}
|
||||||
const checkStorage = checkStorages[0];
|
const checkStorage = checkStorages[0];
|
||||||
const time = new Date();
|
const path = getImagePath(checkStorage.path, imgFile.type);
|
||||||
const date = time.getDate();
|
|
||||||
const month = time.getMonth() + 1;
|
|
||||||
const year = time.getFullYear();
|
|
||||||
let path = checkStorage.path.replace('{YYYY}', year)
|
|
||||||
.replace('{MM}', `0${month}`.slice(-2)).replace('{DD}', `0${date}`.slice(-2));
|
|
||||||
path = `${path}${path.endsWith('/') ? '' : '/'}${utils.uid()}.${imgFile.type.split('/')[1]}`;
|
|
||||||
if (currStorage.provider === 'gitea') {
|
if (currStorage.provider === 'gitea') {
|
||||||
const result = await giteaHelper.uploadFile({
|
const result = await giteaHelper.uploadFile({
|
||||||
token,
|
token,
|
||||||
@ -46,7 +75,7 @@ export default {
|
|||||||
branch: checkStorage.branch,
|
branch: checkStorage.branch,
|
||||||
path,
|
path,
|
||||||
content: imgFile,
|
content: imgFile,
|
||||||
isFile: true,
|
isImg: true,
|
||||||
});
|
});
|
||||||
url = result.content.download_url;
|
url = result.content.download_url;
|
||||||
} else if (currStorage.provider === 'github') {
|
} else if (currStorage.provider === 'github') {
|
||||||
@ -57,7 +86,7 @@ export default {
|
|||||||
branch: checkStorage.branch,
|
branch: checkStorage.branch,
|
||||||
path,
|
path,
|
||||||
content: imgFile,
|
content: imgFile,
|
||||||
isFile: true,
|
isImg: true,
|
||||||
});
|
});
|
||||||
url = result.content.download_url;
|
url = result.content.download_url;
|
||||||
}
|
}
|
||||||
|
@ -5,8 +5,10 @@ import workspaceSvc from './workspaceSvc';
|
|||||||
import constants from '../data/constants';
|
import constants from '../data/constants';
|
||||||
|
|
||||||
const deleteMarkerMaxAge = 1000;
|
const deleteMarkerMaxAge = 1000;
|
||||||
const dbVersion = 1;
|
const dbVersion = 3;
|
||||||
const dbStoreName = 'objects';
|
const dbStoreName = 'objects';
|
||||||
|
const imgDbStoreName = 'imgs';
|
||||||
|
const imgWaitUploadIdsKey = 'waitUploadImgIds';
|
||||||
const { silent } = utils.queryParams;
|
const { silent } = utils.queryParams;
|
||||||
const resetApp = localStorage.getItem('resetStackEdit');
|
const resetApp = localStorage.getItem('resetStackEdit');
|
||||||
if (resetApp) {
|
if (resetApp) {
|
||||||
@ -24,7 +26,7 @@ class Connection {
|
|||||||
const request = indexedDB.open(this.dbName, dbVersion);
|
const request = indexedDB.open(this.dbName, dbVersion);
|
||||||
|
|
||||||
request.onerror = () => {
|
request.onerror = () => {
|
||||||
throw new Error("Can't connect to IndexedDB.");
|
throw new Error('无法连接到IndexedDB.');
|
||||||
};
|
};
|
||||||
|
|
||||||
request.onsuccess = (event) => {
|
request.onsuccess = (event) => {
|
||||||
@ -37,13 +39,8 @@ class Connection {
|
|||||||
|
|
||||||
request.onupgradeneeded = (event) => {
|
request.onupgradeneeded = (event) => {
|
||||||
const eventDb = event.target.result;
|
const eventDb = event.target.result;
|
||||||
const oldVersion = event.oldVersion || 0;
|
// const oldVersion = event.oldVersion || 0;
|
||||||
|
if (!eventDb.objectStoreNames.contains(dbStoreName)) {
|
||||||
// We don't use 'break' in this switch statement,
|
|
||||||
// the fall-through behavior is what we want.
|
|
||||||
/* eslint-disable no-fallthrough */
|
|
||||||
switch (oldVersion) {
|
|
||||||
case 0: {
|
|
||||||
// Create store
|
// Create store
|
||||||
const dbStore = eventDb.createObjectStore(dbStoreName, {
|
const dbStore = eventDb.createObjectStore(dbStoreName, {
|
||||||
keyPath: 'id',
|
keyPath: 'id',
|
||||||
@ -52,9 +49,11 @@ class Connection {
|
|||||||
unique: false,
|
unique: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
default:
|
if (!eventDb.objectStoreNames.contains(imgDbStoreName)) {
|
||||||
|
eventDb.createObjectStore(imgDbStoreName, {
|
||||||
|
keyPath: 'id',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
/* eslint-enable no-fallthrough */
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,6 +192,55 @@ const localDbSvc = {
|
|||||||
cb(storeItemMap);
|
cb(storeItemMap);
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
async saveImg(imgItem) {
|
||||||
|
await this.writeImgItem(imgItem);
|
||||||
|
const waitUploadIdsItem = (await this.getImgItem(imgWaitUploadIdsKey))
|
||||||
|
|| { id: imgWaitUploadIdsKey, ids: [] };
|
||||||
|
const waitUplodIds = waitUploadIdsItem.ids || [];
|
||||||
|
// 如果已上传
|
||||||
|
if (imgItem.uploaded) {
|
||||||
|
waitUplodIds.splice(waitUplodIds.indexOf(imgItem.id), 1);
|
||||||
|
} else {
|
||||||
|
waitUplodIds.push(imgItem.id);
|
||||||
|
}
|
||||||
|
waitUploadIdsItem.ids = waitUplodIds;
|
||||||
|
await this.writeImgItem(waitUploadIdsItem);
|
||||||
|
},
|
||||||
|
// 获取待上传的图片id
|
||||||
|
async getWaitUploadImgIds() {
|
||||||
|
const waitUploadIdsItem = (await this.getImgItem(imgWaitUploadIdsKey))
|
||||||
|
|| { id: imgWaitUploadIdsKey, ids: [] };
|
||||||
|
return waitUploadIdsItem.ids || [];
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 写入图片
|
||||||
|
*/
|
||||||
|
async writeImgItem(imgItem) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// Create the DB transaction
|
||||||
|
this.connection.createTx((tx) => {
|
||||||
|
const dbStore = tx.objectStore(imgDbStoreName);
|
||||||
|
dbStore.put(imgItem);
|
||||||
|
resolve();
|
||||||
|
}, () => reject(new Error('保存图片异常')));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 读取图片
|
||||||
|
*/
|
||||||
|
async getImgItem(id) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// Get the item from DB
|
||||||
|
this.connection.createTx((tx) => {
|
||||||
|
const dbStore = tx.objectStore(imgDbStoreName);
|
||||||
|
const request = dbStore.get(id);
|
||||||
|
request.onsuccess = () => {
|
||||||
|
const dbItem = request.result;
|
||||||
|
resolve(dbItem);
|
||||||
|
};
|
||||||
|
}, () => reject(new Error('indexeddb获取图片异常')));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write all changes from the store since previous transaction.
|
* Write all changes from the store since previous transaction.
|
||||||
|
@ -173,6 +173,18 @@ export default new Provider({
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
async downloadFile({ token, path }) {
|
||||||
|
const { sha, data } = await giteaHelper.downloadFile({
|
||||||
|
...store.getters['workspace/currentWorkspace'],
|
||||||
|
token,
|
||||||
|
path,
|
||||||
|
isImg: true,
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
content: data,
|
||||||
|
sha,
|
||||||
|
};
|
||||||
|
},
|
||||||
async downloadWorkspaceData({ token, syncData }) {
|
async downloadWorkspaceData({ token, syncData }) {
|
||||||
if (!syncData) {
|
if (!syncData) {
|
||||||
return {};
|
return {};
|
||||||
@ -200,25 +212,32 @@ export default new Provider({
|
|||||||
file,
|
file,
|
||||||
commitMessage,
|
commitMessage,
|
||||||
}) {
|
}) {
|
||||||
const path = store.getters.gitPathsByItemId[file.id];
|
const isImg = file.type === 'img';
|
||||||
const absolutePath = `${store.getters['workspace/currentWorkspace'].path || ''}${path}`;
|
const path = store.getters.gitPathsByItemId[file.id] || '';
|
||||||
const sha = gitWorkspaceSvc.shaByPath[path];
|
const absolutePath = !isImg ? `${store.getters['workspace/currentWorkspace'].path || ''}${store.getters.gitPathsByItemId[file.id]}` : file.path;
|
||||||
await giteaHelper.uploadFile({
|
const sha = gitWorkspaceSvc.shaByPath[!isImg ? path : file.path];
|
||||||
|
const res = await giteaHelper.uploadFile({
|
||||||
...store.getters['workspace/currentWorkspace'],
|
...store.getters['workspace/currentWorkspace'],
|
||||||
token,
|
token,
|
||||||
path: absolutePath,
|
path: absolutePath,
|
||||||
content: Provider.serializeContent(content),
|
content: !isImg ? Provider.serializeContent(content) : file.content,
|
||||||
sha,
|
sha,
|
||||||
|
isImg,
|
||||||
commitMessage,
|
commitMessage,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (isImg) {
|
||||||
|
return {
|
||||||
|
sha: res.content.sha,
|
||||||
|
};
|
||||||
|
}
|
||||||
// Return new sync data
|
// Return new sync data
|
||||||
return {
|
return {
|
||||||
contentSyncData: {
|
contentSyncData: {
|
||||||
id: store.getters.gitPathsByItemId[content.id],
|
id: store.getters.gitPathsByItemId[content.id],
|
||||||
type: content.type,
|
type: content.type,
|
||||||
hash: content.hash,
|
hash: content.hash,
|
||||||
sha,
|
sha: res.content.sha,
|
||||||
},
|
},
|
||||||
fileSyncData: {
|
fileSyncData: {
|
||||||
id: path,
|
id: path,
|
||||||
|
@ -110,6 +110,20 @@ export default new Provider({
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
async downloadFile({ token, path }) {
|
||||||
|
const { sha, data } = await giteeHelper.downloadFile({
|
||||||
|
owner: token.name,
|
||||||
|
repo: appDataRepo,
|
||||||
|
branch: appDataBranch,
|
||||||
|
token,
|
||||||
|
path,
|
||||||
|
isImg: true,
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
content: data,
|
||||||
|
sha,
|
||||||
|
};
|
||||||
|
},
|
||||||
async downloadWorkspaceData({ token, syncData }) {
|
async downloadWorkspaceData({ token, syncData }) {
|
||||||
if (!syncData) {
|
if (!syncData) {
|
||||||
return {};
|
return {};
|
||||||
@ -145,18 +159,25 @@ export default new Provider({
|
|||||||
file,
|
file,
|
||||||
commitMessage,
|
commitMessage,
|
||||||
}) {
|
}) {
|
||||||
const path = store.getters.gitPathsByItemId[file.id];
|
const isImg = file.type === 'img';
|
||||||
|
const path = !isImg ? store.getters.gitPathsByItemId[file.id] : file.path;
|
||||||
const res = await giteeHelper.uploadFile({
|
const res = await giteeHelper.uploadFile({
|
||||||
owner: token.name,
|
owner: token.name,
|
||||||
repo: appDataRepo,
|
repo: appDataRepo,
|
||||||
branch: appDataBranch,
|
branch: appDataBranch,
|
||||||
token,
|
token,
|
||||||
path,
|
path,
|
||||||
content: Provider.serializeContent(content),
|
content: !isImg ? Provider.serializeContent(content) : file.content,
|
||||||
sha: gitWorkspaceSvc.shaByPath[path],
|
sha: gitWorkspaceSvc.shaByPath[!isImg ? path : file.path],
|
||||||
|
isImg,
|
||||||
commitMessage,
|
commitMessage,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (isImg) {
|
||||||
|
return {
|
||||||
|
sha: res.content.sha,
|
||||||
|
};
|
||||||
|
}
|
||||||
// Return new sync data
|
// Return new sync data
|
||||||
return {
|
return {
|
||||||
contentSyncData: {
|
contentSyncData: {
|
||||||
|
@ -158,6 +158,18 @@ export default new Provider({
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
async downloadFile({ token, path }) {
|
||||||
|
const { sha, data } = await giteeHelper.downloadFile({
|
||||||
|
...store.getters['workspace/currentWorkspace'],
|
||||||
|
token,
|
||||||
|
path,
|
||||||
|
isImg: true,
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
content: data,
|
||||||
|
sha,
|
||||||
|
};
|
||||||
|
},
|
||||||
async downloadWorkspaceData({ token, syncData }) {
|
async downloadWorkspaceData({ token, syncData }) {
|
||||||
if (!syncData) {
|
if (!syncData) {
|
||||||
return {};
|
return {};
|
||||||
@ -185,17 +197,24 @@ export default new Provider({
|
|||||||
file,
|
file,
|
||||||
commitMessage,
|
commitMessage,
|
||||||
}) {
|
}) {
|
||||||
const path = store.getters.gitPathsByItemId[file.id];
|
const isImg = file.type === 'img';
|
||||||
const absolutePath = `${store.getters['workspace/currentWorkspace'].path || ''}${path}`;
|
const path = store.getters.gitPathsByItemId[file.id] || '';
|
||||||
|
const absolutePath = !isImg ? `${store.getters['workspace/currentWorkspace'].path || ''}${path}` : file.path;
|
||||||
const res = await giteeHelper.uploadFile({
|
const res = await giteeHelper.uploadFile({
|
||||||
...store.getters['workspace/currentWorkspace'],
|
...store.getters['workspace/currentWorkspace'],
|
||||||
token,
|
token,
|
||||||
path: absolutePath,
|
path: absolutePath,
|
||||||
content: Provider.serializeContent(content),
|
content: !isImg ? Provider.serializeContent(content) : file.content,
|
||||||
sha: gitWorkspaceSvc.shaByPath[path],
|
sha: gitWorkspaceSvc.shaByPath[!isImg ? path : file.path],
|
||||||
|
isImg,
|
||||||
commitMessage,
|
commitMessage,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (isImg) {
|
||||||
|
return {
|
||||||
|
sha: res.content.sha,
|
||||||
|
};
|
||||||
|
}
|
||||||
// Return new sync data
|
// Return new sync data
|
||||||
return {
|
return {
|
||||||
contentSyncData: {
|
contentSyncData: {
|
||||||
|
@ -158,6 +158,18 @@ export default new Provider({
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
async downloadFile({ token, path }) {
|
||||||
|
const { sha, data } = await githubHelper.downloadFile({
|
||||||
|
...store.getters['workspace/currentWorkspace'],
|
||||||
|
token,
|
||||||
|
path,
|
||||||
|
isImg: true,
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
content: data,
|
||||||
|
sha,
|
||||||
|
};
|
||||||
|
},
|
||||||
async downloadWorkspaceData({ token, syncData }) {
|
async downloadWorkspaceData({ token, syncData }) {
|
||||||
if (!syncData) {
|
if (!syncData) {
|
||||||
return {};
|
return {};
|
||||||
@ -185,17 +197,23 @@ export default new Provider({
|
|||||||
file,
|
file,
|
||||||
commitMessage,
|
commitMessage,
|
||||||
}) {
|
}) {
|
||||||
const path = store.getters.gitPathsByItemId[file.id];
|
const isImg = file.type === 'img';
|
||||||
const absolutePath = `${store.getters['workspace/currentWorkspace'].path || ''}${path}`;
|
const path = store.getters.gitPathsByItemId[file.id] || '';
|
||||||
|
const absolutePath = !isImg ? `${store.getters['workspace/currentWorkspace'].path || ''}${path}` : file.path;
|
||||||
const res = await githubHelper.uploadFile({
|
const res = await githubHelper.uploadFile({
|
||||||
...store.getters['workspace/currentWorkspace'],
|
...store.getters['workspace/currentWorkspace'],
|
||||||
token,
|
token,
|
||||||
path: absolutePath,
|
path: absolutePath,
|
||||||
content: Provider.serializeContent(content),
|
content: !isImg ? Provider.serializeContent(content) : file.content,
|
||||||
sha: gitWorkspaceSvc.shaByPath[path],
|
sha: gitWorkspaceSvc.shaByPath[!isImg ? path : file.path],
|
||||||
|
isImg,
|
||||||
commitMessage,
|
commitMessage,
|
||||||
});
|
});
|
||||||
|
if (isImg) {
|
||||||
|
return {
|
||||||
|
sha: res.content.sha,
|
||||||
|
};
|
||||||
|
}
|
||||||
// Return new sync data
|
// Return new sync data
|
||||||
return {
|
return {
|
||||||
contentSyncData: {
|
contentSyncData: {
|
||||||
|
@ -314,16 +314,20 @@ export default {
|
|||||||
path,
|
path,
|
||||||
content,
|
content,
|
||||||
sha,
|
sha,
|
||||||
isFile,
|
isImg,
|
||||||
commitMessage,
|
commitMessage,
|
||||||
}) {
|
}) {
|
||||||
|
let uploadContent = content;
|
||||||
|
if (isImg && typeof content !== 'string') {
|
||||||
|
uploadContent = await utils.encodeFiletoBase64(content);
|
||||||
|
}
|
||||||
const refreshedToken = await this.refreshToken(token);
|
const refreshedToken = await this.refreshToken(token);
|
||||||
return request(refreshedToken, {
|
return request(refreshedToken, {
|
||||||
method: sha ? 'PUT' : 'POST',
|
method: sha ? 'PUT' : 'POST',
|
||||||
url: `repos/${projectId}/contents/${encodeURIComponent(path)}`,
|
url: `repos/${projectId}/contents/${encodeURIComponent(path)}`,
|
||||||
body: {
|
body: {
|
||||||
message: commitMessage || getCommitMessage(sha ? 'updateFileMessage' : 'createFileMessage', path),
|
message: commitMessage || getCommitMessage(sha ? 'updateFileMessage' : 'createFileMessage', path),
|
||||||
content: isFile ? await utils.encodeFiletoBase64(content) : utils.encodeBase64(content),
|
content: isImg ? uploadContent : utils.encodeBase64(content),
|
||||||
sha,
|
sha,
|
||||||
branch,
|
branch,
|
||||||
},
|
},
|
||||||
@ -360,6 +364,7 @@ export default {
|
|||||||
projectId,
|
projectId,
|
||||||
branch,
|
branch,
|
||||||
path,
|
path,
|
||||||
|
isImg,
|
||||||
}) {
|
}) {
|
||||||
const refreshedToken = await this.refreshToken(token);
|
const refreshedToken = await this.refreshToken(token);
|
||||||
const { sha, content } = await request(refreshedToken, {
|
const { sha, content } = await request(refreshedToken, {
|
||||||
@ -368,7 +373,7 @@ export default {
|
|||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
sha,
|
sha,
|
||||||
data: utils.decodeBase64(content),
|
data: !isImg ? utils.decodeBase64(content) : content,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -276,15 +276,20 @@ export default {
|
|||||||
path,
|
path,
|
||||||
content,
|
content,
|
||||||
sha,
|
sha,
|
||||||
|
isImg,
|
||||||
commitMessage,
|
commitMessage,
|
||||||
}) {
|
}) {
|
||||||
|
let uploadContent = content;
|
||||||
|
if (isImg && typeof content !== 'string') {
|
||||||
|
uploadContent = await utils.encodeFiletoBase64(content);
|
||||||
|
}
|
||||||
const refreshedToken = await this.refreshToken(token);
|
const refreshedToken = await this.refreshToken(token);
|
||||||
return repoRequest(refreshedToken, owner, repo, {
|
return repoRequest(refreshedToken, owner, repo, {
|
||||||
method: sha ? 'PUT' : 'POST',
|
method: sha ? 'PUT' : 'POST',
|
||||||
url: `contents/${encodeURIComponent(path)}`,
|
url: `contents/${encodeURIComponent(path)}`,
|
||||||
body: {
|
body: {
|
||||||
message: commitMessage || getCommitMessage(sha ? 'updateFileMessage' : 'createFileMessage', path),
|
message: commitMessage || getCommitMessage(sha ? 'updateFileMessage' : 'createFileMessage', path),
|
||||||
content: utils.encodeBase64(content || ' '),
|
content: isImg ? uploadContent : utils.encodeBase64(content || ' '),
|
||||||
sha,
|
sha,
|
||||||
branch,
|
branch,
|
||||||
},
|
},
|
||||||
@ -323,6 +328,7 @@ export default {
|
|||||||
repo,
|
repo,
|
||||||
branch,
|
branch,
|
||||||
path,
|
path,
|
||||||
|
isImg,
|
||||||
}) {
|
}) {
|
||||||
const refreshedToken = await this.refreshToken(token);
|
const refreshedToken = await this.refreshToken(token);
|
||||||
const { sha, content } = await repoRequest(refreshedToken, owner, repo, {
|
const { sha, content } = await repoRequest(refreshedToken, owner, repo, {
|
||||||
@ -330,7 +336,7 @@ export default {
|
|||||||
params: { ref: branch },
|
params: { ref: branch },
|
||||||
});
|
});
|
||||||
if (sha) {
|
if (sha) {
|
||||||
const data = utils.decodeBase64(content);
|
const data = !isImg ? utils.decodeBase64(content) : content;
|
||||||
return {
|
return {
|
||||||
sha,
|
sha,
|
||||||
data: data === ' ' ? '' : data,
|
data: data === ' ' ? '' : data,
|
||||||
|
@ -176,15 +176,19 @@ export default {
|
|||||||
path,
|
path,
|
||||||
content,
|
content,
|
||||||
sha,
|
sha,
|
||||||
isFile,
|
isImg,
|
||||||
commitMessage,
|
commitMessage,
|
||||||
}) {
|
}) {
|
||||||
|
let uploadContent = content;
|
||||||
|
if (isImg && typeof content !== 'string') {
|
||||||
|
uploadContent = await utils.encodeFiletoBase64(content);
|
||||||
|
}
|
||||||
return repoRequest(token, owner, repo, {
|
return repoRequest(token, owner, repo, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
url: `contents/${encodeURIComponent(path)}`,
|
url: `contents/${encodeURIComponent(path)}`,
|
||||||
body: {
|
body: {
|
||||||
message: commitMessage || getCommitMessage(sha ? 'updateFileMessage' : 'createFileMessage', path),
|
message: commitMessage || getCommitMessage(sha ? 'updateFileMessage' : 'createFileMessage', path),
|
||||||
content: isFile ? await utils.encodeFiletoBase64(content) : utils.encodeBase64(content),
|
content: isImg ? uploadContent : utils.encodeBase64(content),
|
||||||
sha,
|
sha,
|
||||||
branch,
|
branch,
|
||||||
},
|
},
|
||||||
@ -222,6 +226,7 @@ export default {
|
|||||||
repo,
|
repo,
|
||||||
branch,
|
branch,
|
||||||
path,
|
path,
|
||||||
|
isImg,
|
||||||
}) {
|
}) {
|
||||||
const { sha, content } = await repoRequest(token, owner, repo, {
|
const { sha, content } = await repoRequest(token, owner, repo, {
|
||||||
url: `contents/${encodeURIComponent(path)}`,
|
url: `contents/${encodeURIComponent(path)}`,
|
||||||
@ -229,7 +234,7 @@ export default {
|
|||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
sha,
|
sha,
|
||||||
data: utils.decodeBase64(content),
|
data: !isImg ? utils.decodeBase64(content) : content,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import md5 from 'js-md5';
|
||||||
import localDbSvc from './localDbSvc';
|
import localDbSvc from './localDbSvc';
|
||||||
import store from '../store';
|
import store from '../store';
|
||||||
import utils from './utils';
|
import utils from './utils';
|
||||||
@ -841,6 +842,61 @@ const syncWorkspace = async (skipContents = false) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const syncImg = async (absolutePath) => {
|
||||||
|
const token = workspaceProvider.getToken();
|
||||||
|
const path = absolutePath.substring(1, absolutePath.length);
|
||||||
|
const { sha, content } = await workspaceProvider.downloadFile({
|
||||||
|
token,
|
||||||
|
path,
|
||||||
|
});
|
||||||
|
if (!sha || !content) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await localDbSvc.saveImg({
|
||||||
|
id: md5(absolutePath),
|
||||||
|
path: absolutePath,
|
||||||
|
content,
|
||||||
|
uploaded: 1,
|
||||||
|
sha,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const uploadImg = async (imgIds, index = 0) => {
|
||||||
|
if (imgIds.length - 1 < index) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const item = await localDbSvc.getImgItem(imgIds[index]);
|
||||||
|
// 不存在item 或已上传 则跳过
|
||||||
|
if (!item || item.uploaded) {
|
||||||
|
setTimeout(await uploadImg(imgIds, index + 1), 10);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const token = workspaceProvider.getToken();
|
||||||
|
const { sha } = await workspaceProvider.uploadWorkspaceContent({
|
||||||
|
token,
|
||||||
|
file: {
|
||||||
|
...utils.deepCopy(item),
|
||||||
|
type: 'img',
|
||||||
|
path: item.path.substring(1, item.path.length),
|
||||||
|
},
|
||||||
|
isImg: true,
|
||||||
|
});
|
||||||
|
await localDbSvc.saveImg({
|
||||||
|
...item,
|
||||||
|
uploaded: 1,
|
||||||
|
sha,
|
||||||
|
});
|
||||||
|
setTimeout(await uploadImg(imgIds, index + 1), 500);
|
||||||
|
};
|
||||||
|
|
||||||
|
const uploadImgs = async () => {
|
||||||
|
// 新增的图片
|
||||||
|
const imgIds = await localDbSvc.getWaitUploadImgIds();
|
||||||
|
if (imgIds.length > 0) {
|
||||||
|
await uploadImg(imgIds);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enqueue a sync task, if possible.
|
* Enqueue a sync task, if possible.
|
||||||
*/
|
*/
|
||||||
@ -888,6 +944,8 @@ const requestSync = (addTriggerSyncBadge = false) => {
|
|||||||
// all the syncedContent objects.
|
// all the syncedContent objects.
|
||||||
await syncFile(store.getters['file/current'].id);
|
await syncFile(store.getters['file/current'].id);
|
||||||
}
|
}
|
||||||
|
// 同步图片
|
||||||
|
await uploadImgs();
|
||||||
|
|
||||||
// Clean files
|
// Clean files
|
||||||
Object.entries(fileHashesToClean).forEach(([fileId, fileHash]) => {
|
Object.entries(fileHashesToClean).forEach(([fileId, fileHash]) => {
|
||||||
@ -983,6 +1041,7 @@ export default {
|
|||||||
}, 5000);
|
}, 5000);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
syncImg,
|
||||||
isSyncPossible,
|
isSyncPossible,
|
||||||
requestSync,
|
requestSync,
|
||||||
createSyncLocation,
|
createSyncLocation,
|
||||||
|
@ -190,6 +190,19 @@ export default {
|
|||||||
reader.onerror = error => reject(error);
|
reader.onerror = error => reject(error);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
base64ToBlob(dataurl, fileName) {
|
||||||
|
const potIdx = fileName.lastIndexOf('.');
|
||||||
|
const suffix = potIdx > -1 ? fileName.substring(potIdx + 1) : 'png';
|
||||||
|
const mime = `image/${suffix}`;
|
||||||
|
const bstr = atob(dataurl);
|
||||||
|
let n = bstr.length;
|
||||||
|
const u8arr = new Uint8Array(n);
|
||||||
|
while (n >= 0) {
|
||||||
|
n -= 1;
|
||||||
|
u8arr[n] = bstr.charCodeAt(n);
|
||||||
|
}
|
||||||
|
return new Blob([u8arr], { type: mime });
|
||||||
|
},
|
||||||
decodeBase64(str) {
|
decodeBase64(str) {
|
||||||
// In case of URL safe base64
|
// In case of URL safe base64
|
||||||
const sanitizedStr = str.replace(/_/g, '/').replace(/-/g, '+');
|
const sanitizedStr = str.replace(/_/g, '/').replace(/-/g, '+');
|
||||||
@ -370,4 +383,18 @@ export default {
|
|||||||
elt.parentNode.removeChild(elt);
|
elt.parentNode.removeChild(elt);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
// 根据当前绝对路径 与 文件路径计算出文件绝对路径
|
||||||
|
getAbsoluteFilePath(currAbsolutePath, filePath) {
|
||||||
|
// "/"开头说明已经是绝对路径
|
||||||
|
if (filePath.indexOf('/') === 0) {
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
let path = filePath;
|
||||||
|
if (filePath.indexOf('./') === 0) {
|
||||||
|
path = `${currAbsolutePath}/${path.replace('./', '')}`;
|
||||||
|
} else {
|
||||||
|
path = `${currAbsolutePath}/${path}`;
|
||||||
|
}
|
||||||
|
return path.indexOf('/') === 0 ? path : `/${path}`;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
@ -1,23 +1,26 @@
|
|||||||
const localKey = 'img/checkedStorage';
|
import utils from '../services/utils';
|
||||||
|
|
||||||
|
const checkStorageLocalKey = 'img/checkedStorage';
|
||||||
|
const workspacePathLocalKey = 'img/workspaceImgPath';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
namespaced: true,
|
namespaced: true,
|
||||||
state: {
|
state: {
|
||||||
// 来自粘贴板 或者 拖拽的图片的文件对象
|
// 当前图片上传中的临时ID
|
||||||
currImg: null,
|
|
||||||
// 当前图片ID
|
|
||||||
currImgId: null,
|
currImgId: null,
|
||||||
// 选择的存储图床信息
|
// 选择的存储图床信息
|
||||||
checkedStorage: {
|
checkedStorage: {
|
||||||
type: null, // 目前存储类型分两种 token 与 tokenRepo
|
type: 'workspace', // 目前存储类型分三种 token 与 tokenRepo 、workspace
|
||||||
provider: null, // 对应是何种账号
|
provider: null, // 对应是何种账号
|
||||||
sub: null, // 对应 token 中的sub
|
sub: '/imgs/{YYYY}-{MM}-{DD}', // 对应 token 中的sub
|
||||||
|
sid: null,
|
||||||
|
},
|
||||||
|
// 当前仓库图片存储位置 key 为path value 为true
|
||||||
|
workspaceImagePath: {
|
||||||
|
'/imgs/{YYYY}-{MM}-{DD}': true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
setNewImg: (state, value) => {
|
|
||||||
state.currImg = value;
|
|
||||||
},
|
|
||||||
setCurrImgId: (state, value) => {
|
setCurrImgId: (state, value) => {
|
||||||
state.currImgId = value;
|
state.currImgId = value;
|
||||||
},
|
},
|
||||||
@ -41,17 +44,28 @@ export default {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
setWorkspaceImgPath: (state, value) => {
|
||||||
|
state.workspaceImagePath = value;
|
||||||
|
localStorage.setItem(workspacePathLocalKey, JSON.stringify(state.workspaceImagePath));
|
||||||
|
},
|
||||||
|
addWorkspaceImgPath: (state, value) => {
|
||||||
|
state.workspaceImagePath[value] = true;
|
||||||
|
state.workspaceImagePath = utils.deepCopy(state.workspaceImagePath);
|
||||||
|
localStorage.setItem(workspacePathLocalKey, JSON.stringify(state.workspaceImagePath));
|
||||||
|
},
|
||||||
|
removeWorkspaceImgPath: (state, value) => {
|
||||||
|
delete state.workspaceImagePath[value];
|
||||||
|
state.workspaceImagePath = utils.deepCopy(state.workspaceImagePath);
|
||||||
|
localStorage.setItem(workspacePathLocalKey, JSON.stringify(state.workspaceImagePath));
|
||||||
|
},
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
getImg: state => state.currImg,
|
|
||||||
currImgId: state => state.currImgId,
|
currImgId: state => state.currImgId,
|
||||||
getCheckedStorage: state => state.checkedStorage,
|
getCheckedStorage: state => state.checkedStorage,
|
||||||
getCheckedStorageSub: state => state.checkedStorage.sub,
|
getCheckedStorageSub: state => state.checkedStorage.sub,
|
||||||
|
getWorkspaceImgPath: state => state.workspaceImagePath,
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
setImg({ commit }, img) {
|
|
||||||
commit('setNewImg', img);
|
|
||||||
},
|
|
||||||
setCurrImgId({ commit }, imgId) {
|
setCurrImgId({ commit }, imgId) {
|
||||||
commit('setCurrImgId', imgId);
|
commit('setCurrImgId', imgId);
|
||||||
},
|
},
|
||||||
@ -60,7 +74,16 @@ export default {
|
|||||||
},
|
},
|
||||||
changeCheckedStorage({ commit }, checkedStorage) {
|
changeCheckedStorage({ commit }, checkedStorage) {
|
||||||
commit('changeCheckedStorage', checkedStorage);
|
commit('changeCheckedStorage', checkedStorage);
|
||||||
localStorage.setItem(localKey, JSON.stringify(checkedStorage));
|
localStorage.setItem(checkStorageLocalKey, JSON.stringify(checkedStorage));
|
||||||
|
},
|
||||||
|
setWorkspaceImgPath({ commit }, workspaceImgPath) {
|
||||||
|
commit('setWorkspaceImgPath', workspaceImgPath);
|
||||||
|
},
|
||||||
|
addWorkspaceImgPath({ commit }, workspaceImgPathValue) {
|
||||||
|
commit('addWorkspaceImgPath', workspaceImgPathValue);
|
||||||
|
},
|
||||||
|
removeWorkspaceImgPath({ commit }, workspaceImgPathValue) {
|
||||||
|
commit('removeWorkspaceImgPath', workspaceImgPathValue);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user