diff --git a/src/components/App.vue b/src/components/App.vue index 90b3102c..85799112 100644 --- a/src/components/App.vue +++ b/src/components/App.vue @@ -22,6 +22,8 @@ import networkSvc from '../services/networkSvc'; import tempFileSvc from '../services/tempFileSvc'; import store from '../store'; import './common/vueGlobals'; +import utils from '../services/utils'; +import providerRegistry from '../services/providers/common/providerRegistry'; const themeClasses = { light: ['app--light'], @@ -49,8 +51,40 @@ export default { close() { tempFileSvc.close(); }, + // 通过路径查看文件 支持相对路径 + viewFileByPath(path) { + // 如果是md结尾 + if (!path) { + return; + } + const currDirNode = store.getters['explorer/selectedNodeFolder']; + if (path.slice(-3) === '.md') { + const rootNode = store.getters['explorer/rootNode']; + const node = utils.findNodeByPath(rootNode, currDirNode, path); + if (!node) { + return; + } + store.commit('explorer/setSelectedId', node.item.id); + // Prevent from freezing the UI while loading the file + setTimeout(() => { + store.commit('file/setCurrentId', node.item.id); + }, 10); + } else { + const workspace = store.getters['workspace/currentWorkspace']; + const provider = providerRegistry.providersById[workspace.providerId]; + if (provider == null) { + return; + } + const absolutePath = utils.getAbsoluteFilePath(currDirNode, path); + const url = provider.getFilePathUrl(absolutePath); + if (url) { + window.open(url, '_blank'); + } + } + }, }, async created() { + window.viewFileByPath = this.viewFileByPath; try { await syncSvc.init(); await networkSvc.init(); diff --git a/src/services/editorSvc.js b/src/services/editorSvc.js index bed2e8d5..88829cd0 100644 --- a/src/services/editorSvc.js +++ b/src/services/editorSvc.js @@ -216,6 +216,15 @@ const editorSvc = Object.assign(new Vue(), editorSvcDiscussions, editorSvcUtils, ...imgs, ]; + Array.prototype.slice.call(sectionPreviewElt.getElementsByTagName('a')).forEach((aElt) => { + const url = aElt.attributes.href.nodeValue; + if (url.indexOf('http://') >= 0 || url.indexOf('https://') >= 0) { + return; + } + aElt.href = 'javascript:void(0);'; // eslint-disable-line no-script-url + aElt.setAttribute('onclick', `window.viewFileByPath('${utils.decodeUrlPath(url)}')`); + }); + // Create TOC section element sectionTocElt = document.createElement('div'); sectionTocElt.className = 'cl-toc-section'; diff --git a/src/services/providers/giteaWorkspaceProvider.js b/src/services/providers/giteaWorkspaceProvider.js index 97138707..e8590672 100644 --- a/src/services/providers/giteaWorkspaceProvider.js +++ b/src/services/providers/giteaWorkspaceProvider.js @@ -320,4 +320,12 @@ export default new Provider({ }); return Provider.parseContent(data, contentId); }, + getFilePathUrl(path) { + const token = this.getToken(); + if (!token) { + return null; + } + const workspace = store.getters['workspace/currentWorkspace']; + return `${token.serverUrl}/${workspace.owner}/${workspace.repo}/src/branch/${workspace.branch}${path}`; + }, }); diff --git a/src/services/providers/giteeAppDataProvider.js b/src/services/providers/giteeAppDataProvider.js index e0f87db9..0e9c8158 100644 --- a/src/services/providers/giteeAppDataProvider.js +++ b/src/services/providers/giteeAppDataProvider.js @@ -282,4 +282,11 @@ export default new Provider({ }); return Provider.parseContent(data, contentId); }, + getFilePathUrl(path) { + const token = this.getToken(); + if (!token) { + return null; + } + return `https://gitee.com/${token.name}/${appDataRepo}/blob/${appDataBranch}${path}`; + }, }); diff --git a/src/services/providers/giteeWorkspaceProvider.js b/src/services/providers/giteeWorkspaceProvider.js index b9797ae7..86dc6804 100644 --- a/src/services/providers/giteeWorkspaceProvider.js +++ b/src/services/providers/giteeWorkspaceProvider.js @@ -308,4 +308,8 @@ export default new Provider({ }); return Provider.parseContent(data, contentId); }, + getFilePathUrl(path) { + const workspace = store.getters['workspace/currentWorkspace']; + return `https://gitee.com/${workspace.owner}/${workspace.repo}/blob/${workspace.branch}${path}`; + }, }); diff --git a/src/services/providers/githubWorkspaceProvider.js b/src/services/providers/githubWorkspaceProvider.js index cc5e9387..84e465f8 100644 --- a/src/services/providers/githubWorkspaceProvider.js +++ b/src/services/providers/githubWorkspaceProvider.js @@ -306,4 +306,8 @@ export default new Provider({ }); return Provider.parseContent(data, contentId); }, + getFilePathUrl(path) { + const workspace = store.getters['workspace/currentWorkspace']; + return `https://github.com/${workspace.owner}/${workspace.repo}/blob/${workspace.branch}${path}`; + }, }); diff --git a/src/services/utils.js b/src/services/utils.js index a2c512c3..a3e81525 100644 --- a/src/services/utils.js +++ b/src/services/utils.js @@ -306,6 +306,9 @@ export default { encodeUrlPath(path) { return path ? path.split('/').map(encodeURIComponent).join('/') : ''; }, + decodeUrlPath(path) { + return path ? path.split('/').map(decodeURIComponent).join('/') : ''; + }, parseGithubRepoUrl(url) { const parsedRepo = url && url.match(/([^/:]+)\/([^/]+?)(?:\.git|\/)?$/); return parsedRepo && { @@ -414,4 +417,22 @@ export default { } return (path.indexOf('/') === 0 ? path : `/${path}`).replaceAll(' ', '%20'); }, + findNodeByPath(rootNode, currDirNode, filePath) { + // 先获取绝对路径 + const path = this.getAbsoluteFilePath(currDirNode, filePath).replaceAll('%20', ' '); + const pathArr = path.split('/'); + let node = rootNode; + for (let i = 0; i < pathArr.length; i += 1) { + if (i > 0) { + if (i === pathArr.length - 1) { + return node.files.find(it => `${it.item.name}.md` === pathArr[i]); + } + node = node.folders.find(it => it.item.name === pathArr[i]); + if (!node) { + return null; + } + } + } + return null; + }, };