侧边工具栏
This commit is contained in:
parent
19901d802f
commit
7dfa51f5dc
|
@ -1,7 +1,8 @@
|
|||
/**
|
||||
* 共享数据状态-多窗口
|
||||
*/
|
||||
const { ipcRenderer } = require('electron') // app使用
|
||||
// const { ipcRenderer } = require('electron') // app使用
|
||||
const { ipcRenderer } = window.electron || {}
|
||||
export function shareStorePlugin({store}) {
|
||||
store.$subscribe(() => { // 自动同步
|
||||
// 在存储变化的时候执行
|
||||
|
@ -20,14 +21,14 @@ function stateSync(store) {
|
|||
const storeName = store.$id
|
||||
const jsonStr = JSON.stringify(store.$state)
|
||||
// 通知主线程更新
|
||||
ipcRenderer.invoke('pinia-state-change', storeName, jsonStr)
|
||||
ipcRenderer?.invoke('pinia-state-change', storeName, jsonStr)
|
||||
}
|
||||
// 同步数据-接收主线程消息
|
||||
function stateChange(store) {
|
||||
const storeName = store.$id
|
||||
ipcRenderer.on('pinia-state-set', (e, sName, jsonStr) => {
|
||||
ipcRenderer?.on('pinia-state-set', (e, sName, jsonStr) => {
|
||||
if (sName == storeName) { // 更新对应数据
|
||||
console.log('state-set', jsonStr, sName)
|
||||
// console.log('state-set', jsonStr, sName)
|
||||
const curJson = JSON.stringify(store.$state) // 当前数据
|
||||
const isUp = curJson != jsonStr // 不同的时候才写入,不然会导致触发数据变化监听,导致死循环
|
||||
if (!isUp) return
|
||||
|
|
|
@ -39,15 +39,19 @@ export const getStaticUrl = (url = '', type = 'app', exitPath = '', isFile = fal
|
|||
* url:路由地址,width:窗口宽度,height:窗口高度,option:自定义选项
|
||||
* @returns
|
||||
*/
|
||||
export function ipcMsgSend(key, data) {
|
||||
return new Promise((resolve) => {
|
||||
// 返回结果-监听
|
||||
ipcRenderer.once(`${key}-reply`, (e, res) => {
|
||||
resolve(res)
|
||||
export function ipcMsgSend(key, data, type) {
|
||||
if (type == 'old') {
|
||||
return new Promise((resolve) => {
|
||||
// 返回结果-监听
|
||||
ipcRenderer.once(`${key}-reply`, (e, res) => {
|
||||
resolve(res)
|
||||
})
|
||||
// 发送消息
|
||||
ipcRenderer.send(key, data)
|
||||
})
|
||||
// 发送消息
|
||||
ipcRenderer.send(key, data)
|
||||
})
|
||||
} else { // 新版本-默认
|
||||
return ipcRenderer.invoke(key, data)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 创建-窗口 调用该方法
|
||||
|
@ -173,13 +177,13 @@ const eventHandles = (type, win) => {
|
|||
case 'tool-sphere': { // 创建-悬浮球
|
||||
// 监听设置穿透
|
||||
const setIgnore = (_, ignore) => {win.setIgnoreMouseEvents(ignore, {forward: true})}
|
||||
Remote.ipcMain.on('tool-sphere:set:ignore', setIgnore)
|
||||
Remote.ipcMain.handle('tool-sphere:set:ignore', setIgnore) // 异步
|
||||
// 关闭窗口
|
||||
Remote.ipcMain.once('tool-sphere:close', () => { win&&win.destroy() })
|
||||
// 放大监听-测试
|
||||
Remote.ipcMain.once('maximize-window', () => {win&&win.destroy()})
|
||||
const on = {
|
||||
onClosed: () => {Remote.ipcMain.off('tool-sphere:set:ignore', setIgnore)}
|
||||
onClosed: () => {Remote.ipcMain.removeHandler('tool-sphere:set:ignore', setIgnore)}
|
||||
}
|
||||
publicMethods(on) // 加载公共方法
|
||||
break}
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
<template>
|
||||
<div class="warp" ref="btnRef">
|
||||
<slot name="start"></slot>
|
||||
<!-- 工具按钮 -->
|
||||
<el-space direction="vertical">
|
||||
<template v-for="(item,index) in list">
|
||||
<slot :name="item.prop" :item="item" :index="index">
|
||||
<div class="c-btn flex flex-col items-center gap-2 p-2" @click.stop="clickHandel(item,$event)">
|
||||
<i class="iconfont" :class="item.icon" :style="item.style" />
|
||||
<span>{{item.label||item.text||item.name}}</span>
|
||||
</div>
|
||||
</slot>
|
||||
</template>
|
||||
<slot name="append"></slot>
|
||||
</el-space>
|
||||
<slot name="end"></slot>
|
||||
<!-- 内容部分 -->
|
||||
<transition name="el-fade-in">
|
||||
<div class="c-popover" :style="`--top: ${topPos}px;--height:${hPost}px;`" v-show="isVisible">
|
||||
<div class="content">{{activeObj}}</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { computed, defineProps, ref, reactive, watchEffect, onMounted } from 'vue'
|
||||
// 功能说明:侧边-工具栏
|
||||
const colors = ['#00f389', '#ff7f00', '#ffff00', '#409EFF', '#00baff', '#13b189', '#F56C6C']
|
||||
const emit = defineEmits(['update:modelValue','change'])
|
||||
const props = defineProps({
|
||||
modelValue: { // 是否显示
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
data: { // 数据
|
||||
type: Array,
|
||||
default: () => [
|
||||
{ label: '资源', prop: 'resource', icon: 'icon-hudong' },
|
||||
{ label: '互动', prop: 'interact', icon: 'icon-hudong' },
|
||||
{ label: '窗口', prop: 'win', icon: 'icon-hudong' },
|
||||
{ label: '下课', prop: 'over', icon: 'icon-hudong' },
|
||||
]
|
||||
}
|
||||
})
|
||||
const isVisible = ref(false) // 是否显示内容
|
||||
const activeObj = ref(null) // 当前激活的按钮
|
||||
const btnRef = ref(null) // 按钮元素-ref
|
||||
const topPos = ref(30) // 顶部距离-内容的距离
|
||||
const hPost = ref(0) // 顶部距离-内容的距离
|
||||
let posBtnAll = {} // 存储位置
|
||||
// === 计算属性 ===
|
||||
const list = computed(() => props.data.map((o,i) => {
|
||||
o.style = getStyle(o.style, i)
|
||||
return o
|
||||
}))
|
||||
onMounted(() => {
|
||||
posBtnAll = btnRef.value.getBoundingClientRect()
|
||||
hPost.value = posBtnAll.height
|
||||
})
|
||||
// === 方法 ===
|
||||
// 获取颜色索引
|
||||
const getIndex = i => i % colors.length
|
||||
// 获取样式
|
||||
const getStyle = (style,index) => {
|
||||
const color = colors[getIndex(index)]
|
||||
if (!style) return {color}
|
||||
else {
|
||||
if (typeof style === 'object') return {...style, color}
|
||||
return `${style}${style.endsWith(';')?'':';'}color:${color};`
|
||||
}
|
||||
}
|
||||
// 事件
|
||||
const clickHandel = (o, e) => {
|
||||
const node = e.target.parentNode.getBoundingClientRect()
|
||||
const isColse = activeObj.value && activeObj.value.prop === o.prop && isVisible.value
|
||||
isVisible.value = !isColse // 相同的按钮且打开状态,点击关闭
|
||||
activeObj.value = o
|
||||
const nodeH = parseInt(node.height / 2) // 高度的一半
|
||||
topPos.value = parseInt(node.top) - posBtnAll.top + nodeH
|
||||
emit('change', o)
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.warp{
|
||||
border: 1px solid;
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
right: 10px;
|
||||
min-height: 40vh;
|
||||
min-width: 4em;
|
||||
border-radius: 4em;
|
||||
background-color: #121212;
|
||||
.el-space{margin: 20px 0;}
|
||||
.c-btn{
|
||||
color: #d9dce3;
|
||||
cursor: pointer;
|
||||
.iconfont{
|
||||
font-size: 20px;
|
||||
color: attr(color);
|
||||
}
|
||||
span{font-size: 12px;}
|
||||
&:hover{
|
||||
border-radius: 4px;
|
||||
background: #454545fa;
|
||||
}
|
||||
}
|
||||
}
|
||||
.c-popover{
|
||||
--top: 30px;
|
||||
--height: 40vh;
|
||||
position: fixed;
|
||||
inset: 50% 75px auto auto;
|
||||
transform: translateY(-50%);
|
||||
background-color: #121212;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
min-height: var(--height);
|
||||
width: 30em;
|
||||
text-align: left;
|
||||
|
||||
&:before{
|
||||
content: "";
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background-color: #121212;
|
||||
position: absolute;
|
||||
right: -5px;
|
||||
top: var(--top);
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
}
|
||||
.content{
|
||||
color: red;
|
||||
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,39 @@
|
|||
/**
|
||||
* 处理元素是否穿透-窗口
|
||||
*/
|
||||
import { ipcMsgSend } from '@/utils/tool'
|
||||
class Ignore {
|
||||
el // 绑定元素
|
||||
binding // 绑定对象(参数)
|
||||
value // 绑定值
|
||||
isAuto // 是否自动控制
|
||||
constructor(el, binding) {
|
||||
this.el = el
|
||||
this.binding = binding
|
||||
this.value = binding.value
|
||||
this.isAuto = !(this.value && this.value instanceof Boolean)
|
||||
}
|
||||
// 设置是否穿透
|
||||
send(bool) {
|
||||
ipcMsgSend('tool-sphere:set:ignore', bool)
|
||||
}
|
||||
// 监听元素移入移出,自动设置
|
||||
mounted() {
|
||||
this.el.addEventListener('mouseenter', () => { // 进入
|
||||
this.send(false) // 元素不穿透,鼠标有效
|
||||
})
|
||||
this.el.addEventListener('mouseleave', () => { // 离开
|
||||
this.send(true) // 元素穿透,鼠标无效
|
||||
})
|
||||
}
|
||||
}
|
||||
export default {
|
||||
mounted(el, binding) {
|
||||
const ignore = new Ignore(el, binding)
|
||||
if (ignore.isAuto) { // 自动控制
|
||||
ignore.mounted()
|
||||
} else { // 手动设置
|
||||
ignore.send(ignore.value)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
<template>
|
||||
<div class="warp-all">
|
||||
<!-- 画板 -->
|
||||
<board-vue v-model="tabActive" v-show="isShow"></board-vue>
|
||||
<!-- 侧边工具栏 -->
|
||||
<side-vue v-ignore></side-vue>
|
||||
<!-- 底部工具栏 :style="dataPos.style"-->
|
||||
<div class="tool-bottom-all"
|
||||
@mouseenter="mouseChange(0)" @mouseleave="mouseChange(1)">
|
||||
<div class="tool-bottom-all" @mouseenter="mouseChange(0)" @mouseleave="mouseChange(1)">
|
||||
<div v-drag="{handle:'.tool-bottom-all', dragtime}"
|
||||
@v-drag-start="dragtime = Date.now()">
|
||||
<div class="c-logo" @click="logoHandle" title="拖动 | 折叠 | 展开">
|
||||
|
@ -15,7 +17,7 @@
|
|||
@change="tabChange">
|
||||
<template #default="{item}">
|
||||
<div class="c-btn flex flex-col items-center gap-2 p-2">
|
||||
<i class="iconfont" :class="item.icon" :style="item.style"></i>
|
||||
<i class="iconfont" :class="item.icon" :style="item.style" />
|
||||
<span>{{item.label}}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -29,18 +31,21 @@
|
|||
// 功能说明:electron 悬浮球
|
||||
import { onMounted, ref, reactive, watchEffect } from 'vue'
|
||||
import logo from '@root/resources/icon.png' // logo
|
||||
import boardVue from './components/board.vue' // 画板
|
||||
import boardVue from './components/board.vue' // 画板-子组件
|
||||
import sideVue from './components/side.vue' // 画板-子组件
|
||||
import vDrag from './directive/drag' // 自定义指令-拖拽
|
||||
import vIgnore from './directive/ignore' // 自定义指令-穿透
|
||||
import { useToolState } from '@/store/modules/tool'
|
||||
const { ipcRenderer } = require('electron') // app使用
|
||||
// const ipcRenderer = { send: () => {} } // 网页测试使用
|
||||
import { ipcMsgSend } from '@/utils/tool' // 相关工具
|
||||
// const { ipcRenderer } = require('electron') // app使用
|
||||
const { ipcRenderer } = window.electron || {}
|
||||
|
||||
const tabActive = ref('select') // 工具栏当前选中项
|
||||
const isFold = ref(false) // 折叠工具栏
|
||||
const isDrag = ref(false) // 开始拖拽
|
||||
const dragtime = ref(0) // 拖拽时间-计算点击还是拖动
|
||||
const isShow = ref(false)
|
||||
const toolStore = useToolState()
|
||||
const isShow = ref(true) // 是否显示-画板
|
||||
const toolStore = useToolState() // 状态管理
|
||||
const btnList = [ // 工具栏按钮列表
|
||||
{ label: '选择', value: 'select', icon: 'icon-mouse' },
|
||||
{ label: '画笔', value: 'brush', icon: 'icon-huabi' },
|
||||
|
@ -51,16 +56,11 @@ const btnList = [ // 工具栏按钮列表
|
|||
// { label: '更多', value: 'more', icon: 'icon-xiazai9' },
|
||||
]
|
||||
onMounted(() => {
|
||||
// isShow.value = toolStore.showBoardAll // 是否显示-画板
|
||||
// console.log('xxx: ', toolStore.model)
|
||||
// setTimeout(() => {
|
||||
// toolStore.windowState.test = '测试成功'
|
||||
// }, 2000);
|
||||
|
||||
})
|
||||
// ==== 方法 ===
|
||||
const tabChange = (val) => { // 切换tab-change
|
||||
// console.log('xxxx:', val)
|
||||
toolStore.showBoardAll = true
|
||||
switch (val) {
|
||||
case 'brush': // 画笔
|
||||
break
|
||||
|
@ -73,7 +73,7 @@ const tabChange = (val) => { // 切换tab-change
|
|||
case 'more':
|
||||
break
|
||||
case 'close':
|
||||
ipcRenderer.send('tool-sphere:close')
|
||||
ipcRenderer?.send('tool-sphere:close')
|
||||
break
|
||||
default:
|
||||
break
|
||||
|
@ -85,11 +85,10 @@ const logoHandle = (e,t) => { // logo 点击-事件 折叠|展开
|
|||
isFold.value = !isFold.value
|
||||
}
|
||||
}
|
||||
|
||||
const mouseChange = (bool) => { // 鼠标移入工具栏 是否穿透
|
||||
let resBool = false
|
||||
if (tabActive.value == 'select') resBool = !!bool
|
||||
ipcRenderer.send('tool-sphere:set:ignore', resBool)
|
||||
ipcRenderer?.send('tool-sphere:set:ignore', resBool)
|
||||
}
|
||||
watchEffect(() => { // 监听
|
||||
isShow.value = toolStore.showBoardAll // 是否显示-画板
|
||||
|
|
Loading…
Reference in New Issue