mirror of
https://github.com/wanglin2/mind-map.git
synced 2026-02-22 19:07:45 +08:00
新增electron相关页面
This commit is contained in:
parent
6bc309e3fa
commit
c8a3c569e1
26
web/main.js
Normal file
26
web/main.js
Normal file
@ -0,0 +1,26 @@
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
|
||||
const createWindow = () => {
|
||||
const win = new BrowserWindow({
|
||||
width: 1200,
|
||||
height: 800,
|
||||
frame: false,
|
||||
titleBarStyle: 'hiddenInset'
|
||||
})
|
||||
|
||||
win.loadURL('http://192.168.3.125:8080/#/workbenche')
|
||||
|
||||
app.on('activate', () => {
|
||||
if (BrowserWindow.getAllWindows().length === 0) createWindow()
|
||||
})
|
||||
|
||||
// win.webContents.openDevTools()
|
||||
}
|
||||
|
||||
app.whenReady().then(() => {
|
||||
createWindow()
|
||||
})
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') app.quit()
|
||||
})
|
||||
1046
web/package-lock.json
generated
1046
web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -2,6 +2,7 @@
|
||||
"name": "thoughts",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build && node ../copy.js",
|
||||
@ -9,13 +10,15 @@
|
||||
"buildLibrary": "vue-cli-service build --target lib --name simpleMindMap ../simple-mind-map/full.js --dest ../simple-mind-map/dist",
|
||||
"format": "prettier --write src/* src/*/* src/*/*/* src/*/*/*/*",
|
||||
"buildDoc": "node ./scripts/buildDoc.js",
|
||||
"autoBuildDoc": "node ./scripts/autoBuildDoc.js"
|
||||
"autoBuildDoc": "node ./scripts/autoBuildDoc.js",
|
||||
"start": "electron ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@toast-ui/editor": "^3.1.5",
|
||||
"core-js": "^3.6.5",
|
||||
"element-ui": "^2.15.1",
|
||||
"highlight.js": "^10.7.3",
|
||||
"uuid": "^3.4.0",
|
||||
"v-viewer": "^1.6.4",
|
||||
"vue": "^2.6.11",
|
||||
"vue-i18n": "^8.27.2",
|
||||
@ -29,6 +32,7 @@
|
||||
"@vue/cli-service": "^4.5.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"chokidar": "^3.5.3",
|
||||
"electron": "^23.1.1",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-plugin-vue": "^6.2.2",
|
||||
"less": "^3.12.2",
|
||||
|
||||
BIN
web/src/.DS_Store
vendored
BIN
web/src/.DS_Store
vendored
Binary file not shown.
3
web/src/css/global.css
Normal file
3
web/src/css/global.css
Normal file
@ -0,0 +1,3 @@
|
||||
.noDrag {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
@ -8,6 +8,7 @@ import '@/assets/icon-font/iconfont.css'
|
||||
import 'viewerjs/dist/viewer.css'
|
||||
import VueViewer from 'v-viewer'
|
||||
import i18n from './i18n'
|
||||
import './css/global.css'
|
||||
|
||||
Vue.config.productionTip = false
|
||||
Vue.prototype.$bus = new Vue()
|
||||
|
||||
35
web/src/pages/Workbenche/Index.vue
Normal file
35
web/src/pages/Workbenche/Index.vue
Normal file
@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<div class="workbencheContainer">
|
||||
<Header></Header>
|
||||
<div class="workbencheContent">
|
||||
<router-view></router-view>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Header from './components/Header.vue';
|
||||
|
||||
export default {
|
||||
name: 'Workbenche',
|
||||
components: {
|
||||
Header
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.workbencheContainer {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.workbencheContent {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
63
web/src/pages/Workbenche/components/Header.vue
Normal file
63
web/src/pages/Workbenche/components/Header.vue
Normal file
@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<div class="workbencheHeaderContainer">
|
||||
<div class="placeholder"></div>
|
||||
<div class="home noDrag" :class="{active: isInHome}">
|
||||
<span class="icon iconfont iconzhuye"></span>
|
||||
<span class="text">主页</span>
|
||||
</div>
|
||||
<ScrollTab></ScrollTab>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ScrollTab from './ScrollTab.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ScrollTab
|
||||
},
|
||||
computed: {
|
||||
isInHome() {
|
||||
return this.$route.path.includes('home')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.workbencheHeaderContainer {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
background-color: #ebeef1;
|
||||
-webkit-app-region: drag;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
|
||||
.placeholder {
|
||||
width: 100px;
|
||||
height: 100%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.home {
|
||||
padding: 0 10px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
background-color: #e3e6e9;
|
||||
flex-shrink: 0;
|
||||
font-size: 14px;
|
||||
|
||||
&.active {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.icon {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
227
web/src/pages/Workbenche/components/ScrollTab.vue
Normal file
227
web/src/pages/Workbenche/components/ScrollTab.vue
Normal file
@ -0,0 +1,227 @@
|
||||
<template>
|
||||
<div class="workbencheScrollTabContainer" ref="container">
|
||||
<div class="workbencheScrollTabBox noDrag">
|
||||
<div class="workbencheScrollTabList" ref="list" :style="{ transform: `translateX(${listTranslateX}px)` }">
|
||||
<div class="workbencheScrollTabItem" v-for="(item, index) in localEditList" :key="item.id"
|
||||
:style="{ width: tabWidth + 'px' }" :class="{ active: $route.params.id === item.id }"
|
||||
@click="openTab(item)">
|
||||
<span class="icon iconfont icondiannao"></span>
|
||||
<span class="text">{{ item.name }}</span>
|
||||
<span class="mask"></span>
|
||||
<span class="icon closeIcon el-icon-close" @click="deleteFile(index)"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="workbencheScrollTabControl noDrag" ref="control">
|
||||
<div class="workbencheScrollTabBtn el-icon-plus" @click="addFile"></div>
|
||||
<div class="workbencheScrollTabBtn el-icon-arrow-left" @click="scrollLeft"></div>
|
||||
<div class="workbencheScrollTabBtn el-icon-arrow-right" @click="scrollRight"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapMutations } from 'vuex';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
const MIN_TAB_WIDTH = 100
|
||||
const MAX_TAB_WIDTH = 220
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
tabWidth: 0,
|
||||
tabTotalWidth: 0,
|
||||
listTranslateX: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['localEditList']),
|
||||
},
|
||||
mounted() {
|
||||
this.computeTabWidth()
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(['setLocalEditList']),
|
||||
|
||||
addFile() {
|
||||
let item = {
|
||||
id: uuidv4(),
|
||||
name: '新建文件',
|
||||
path: ''
|
||||
}
|
||||
this.setLocalEditList([...this.localEditList, item])
|
||||
this.$nextTick(() => {
|
||||
this.openTab(item)
|
||||
this.computeTabWidth()
|
||||
this.scrollToEnd()
|
||||
})
|
||||
},
|
||||
|
||||
deleteFile(index) {
|
||||
let arr = [...this.localEditList]
|
||||
arr.splice(index, 1)
|
||||
this.setLocalEditList(arr)
|
||||
this.$nextTick(() => {
|
||||
this.computeTabWidth()
|
||||
})
|
||||
},
|
||||
|
||||
computeTabWidth() {
|
||||
let containerWidth = this.$refs.container.getBoundingClientRect().width
|
||||
let controlWidth = this.$refs.control.getBoundingClientRect().width
|
||||
this.tabTotalWidth = containerWidth - controlWidth
|
||||
let length = this.localEditList.length
|
||||
let tabWidth = Math.floor(this.tabTotalWidth / length)
|
||||
if (tabWidth >= MAX_TAB_WIDTH) {
|
||||
this.tabWidth = MAX_TAB_WIDTH
|
||||
} else if (tabWidth <= MIN_TAB_WIDTH) {
|
||||
this.tabWidth = MIN_TAB_WIDTH
|
||||
} else {
|
||||
this.tabWidth = tabWidth
|
||||
}
|
||||
},
|
||||
|
||||
scrollLeft() {
|
||||
if (this.listTranslateX + this.tabWidth < 0) {
|
||||
this.listTranslateX += this.tabWidth
|
||||
} else {
|
||||
this.listTranslateX = 0
|
||||
}
|
||||
},
|
||||
|
||||
scrollRight() {
|
||||
let maxScroll = this.tabTotalWidth - this.localEditList.length * this.tabWidth
|
||||
if (this.listTranslateX - this.tabWidth > maxScroll) {
|
||||
this.listTranslateX -= this.tabWidth
|
||||
} else {
|
||||
this.listTranslateX = maxScroll
|
||||
}
|
||||
},
|
||||
|
||||
scrollToEnd() {
|
||||
if (this.localEditList.length * this.tabWidth <= this.tabTotalWidth) {
|
||||
return
|
||||
}
|
||||
this.listTranslateX = this.tabTotalWidth - this.localEditList.length * this.tabWidth
|
||||
},
|
||||
|
||||
openTab(item) {
|
||||
this.$router.push({
|
||||
name: 'WorkbencheEdit',
|
||||
params: {
|
||||
id: item.id
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.workbencheScrollTabContainer {
|
||||
flex-grow: 1;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
|
||||
.workbencheScrollTabBox {
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
|
||||
.workbencheScrollTabList {
|
||||
display: flex;
|
||||
width: max-content;
|
||||
height: 100%;
|
||||
transition: all 0.3s;
|
||||
|
||||
.workbencheScrollTabItem {
|
||||
flex-shrink: 0;
|
||||
height: 100%;
|
||||
font-size: 12px;
|
||||
padding: 0 10px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
|
||||
.icon {
|
||||
|
||||
&.closeIcon {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.mask {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 20px;
|
||||
height: 100%;
|
||||
background-color: #fff;
|
||||
box-shadow: -10px 0 10px #fff;
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #fff;
|
||||
|
||||
.mask {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.closeIcon {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 10px;
|
||||
background-color: #999;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
right: 0;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: #fff;
|
||||
|
||||
&::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.text {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.workbencheScrollTabControl {
|
||||
flex-shrink: 0;
|
||||
padding: 0 10px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
|
||||
.workbencheScrollTabBtn {
|
||||
width: 20px;
|
||||
height: 100%;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
color: #000;
|
||||
margin: 0 5px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
75
web/src/pages/Workbenche/components/Sidebar.vue
Normal file
75
web/src/pages/Workbenche/components/Sidebar.vue
Normal file
@ -0,0 +1,75 @@
|
||||
|
||||
<template>
|
||||
<div class="workbencheHomeSidebarContainer">
|
||||
<div class="workbencheMenuList">
|
||||
<div class="workbencheMenuItem" v-for="item in menuList" :key="item.name"
|
||||
:class="{ active: $route.name === item.routerName }" @click="toMenu(item.rou
|
||||
)">
|
||||
<span class="icon iconfont" :class="[item.icon]"></span>
|
||||
<span class="text">{{ item.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
menuList: [
|
||||
{
|
||||
name: '本地',
|
||||
icon: 'iconbendi1x',
|
||||
routerName: 'WorkbencheHomeLocal'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toMenu(routerName) {
|
||||
this.$router.push(routerName)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.workbencheHomeSidebarContainer {
|
||||
width: 80px;
|
||||
background-color: #fff;
|
||||
height: 100%;
|
||||
flex-shrink: 0;
|
||||
padding: 20px 0;
|
||||
|
||||
.workbencheMenuList {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
.workbencheMenuItem {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #000;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
|
||||
&.active {
|
||||
background-color: #f3fbf4;
|
||||
color: #56b74b;
|
||||
}
|
||||
|
||||
.icon {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.text {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
21
web/src/pages/Workbenche/views/Edit.vue
Normal file
21
web/src/pages/Workbenche/views/Edit.vue
Normal file
@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<div class="workbencheHeaderContainer">
|
||||
<Edit :key="$route.params.id"></Edit>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Edit from '../../Edit/Index.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Edit
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.workbencheHeaderContainer {
|
||||
|
||||
}
|
||||
</style>
|
||||
32
web/src/pages/Workbenche/views/Home.vue
Normal file
32
web/src/pages/Workbenche/views/Home.vue
Normal file
@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<div class="workbencheHomeContainer">
|
||||
<Sidebar></Sidebar>
|
||||
<div class="workbencheHomeContent">
|
||||
<router-view></router-view>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Sidebar from '../components/Sidebar.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Sidebar
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.workbencheHomeContainer {
|
||||
background-color: #f6f8f9;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
|
||||
.workbencheHomeContent {
|
||||
flex-grow: 1;
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
20
web/src/pages/Workbenche/views/Local.vue
Normal file
20
web/src/pages/Workbenche/views/Local.vue
Normal file
@ -0,0 +1,20 @@
|
||||
<template>
|
||||
<div class="workbencheLocalContainer">
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.workbencheLocalContainer {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 5px;
|
||||
background-color: #fff;
|
||||
}
|
||||
</style>
|
||||
@ -3,25 +3,54 @@ import VueRouter from 'vue-router'
|
||||
import EditPage from '@/pages/Edit/Index'
|
||||
import DocPage from '@/pages/Doc/Index'
|
||||
import routerList from '@/pages/Doc/routerList'
|
||||
import WorkbenchePage from '@/pages/Workbenche/Index'
|
||||
import WorkbencheHomePage from '@/pages/Workbenche/views/Home'
|
||||
import WorkbencheEditPage from '@/pages/Workbenche/views/Edit'
|
||||
import WorkbencheHomeLocalPage from '@/pages/Workbenche/views/Local'
|
||||
|
||||
Vue.use(VueRouter)
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/',
|
||||
name: 'Edit',
|
||||
component: EditPage
|
||||
{
|
||||
path: '/',
|
||||
name: 'Edit',
|
||||
component: EditPage
|
||||
},
|
||||
{
|
||||
path: '/workbenche',
|
||||
name: 'Workbenche',
|
||||
component: WorkbenchePage,
|
||||
redirect: '/workbenche/home/local',
|
||||
children: [
|
||||
{
|
||||
path: 'home',
|
||||
name: 'WorkbencheHome',
|
||||
component: WorkbencheHomePage,
|
||||
children: [
|
||||
{
|
||||
path: 'local',
|
||||
name: 'WorkbencheHomeLocal',
|
||||
component: WorkbencheHomeLocalPage,
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'edit/:id',
|
||||
name: 'WorkbencheEdit',
|
||||
component: WorkbencheEditPage,
|
||||
}
|
||||
]
|
||||
},
|
||||
...routerList.map((item) => {
|
||||
return {
|
||||
path: `/doc/${item.lang}/`,
|
||||
return {
|
||||
path: `/doc/${item.lang}/`,
|
||||
redirect: `/doc/${item.lang}/introduction/`
|
||||
}
|
||||
}),
|
||||
...routerList.map((item) => {
|
||||
return {
|
||||
path: `/doc/${item.lang}/`,
|
||||
component: DocPage,
|
||||
return {
|
||||
path: `/doc/${item.lang}/`,
|
||||
component: DocPage,
|
||||
children: item.children.map((child) => {
|
||||
return {
|
||||
path: `${child.path}/:h?`,
|
||||
|
||||
@ -15,7 +15,8 @@ const store = new Vuex.Store({
|
||||
// 是否开启节点富文本
|
||||
openNodeRichText: true
|
||||
},
|
||||
activeSidebar: '' // 当前显示的侧边栏
|
||||
activeSidebar: '', // 当前显示的侧边栏
|
||||
localEditList: []// 客户端中正在编辑的思维导图列表
|
||||
},
|
||||
mutations: {
|
||||
/**
|
||||
@ -59,6 +60,11 @@ const store = new Vuex.Store({
|
||||
*/
|
||||
setActiveSidebar(state, data) {
|
||||
state.activeSidebar = data
|
||||
},
|
||||
|
||||
// 设置客户端中当前正在编辑的思维导图列表
|
||||
setLocalEditList(state, list) {
|
||||
state.localEditList = list
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user