feat: plugin flexible layout (#1219)
This commit is contained in:
parent
20c3b071bd
commit
8d31bd3ed4
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<component :is="CanvasLayout">
|
||||
<component :is="CanvasLayout" :class="{ 'not-selected': getMoveDragBarState() }">
|
||||
<template #header>
|
||||
<component v-if="!isBlock()" :is="CanvasRouteBar"></component>
|
||||
</template>
|
||||
|
|
@ -67,6 +67,8 @@ export default {
|
|||
const { canvasSrc = '' } = getOptions(meta.id) || {}
|
||||
const canvasSrcDoc = ref('')
|
||||
|
||||
const { getMoveDragBarState, getFixedPanelsStatus, closePlugin, closeSetting } = useLayout()
|
||||
|
||||
useMessage().subscribe({
|
||||
topic: 'init_canvas_deps',
|
||||
subscriber: 'canvas_design_canvas',
|
||||
|
|
@ -151,9 +153,17 @@ export default {
|
|||
)
|
||||
|
||||
const nodeSelected = (node, parent, type, id) => {
|
||||
const { leftPanelFixed, rightPanelFixed } = getFixedPanelsStatus()
|
||||
|
||||
const { toolbars } = useLayout().layoutState
|
||||
if (type !== 'clickTree') {
|
||||
useLayout().closePlugin()
|
||||
if (!leftPanelFixed) {
|
||||
closePlugin()
|
||||
}
|
||||
|
||||
if (!rightPanelFixed) {
|
||||
closeSetting(true)
|
||||
}
|
||||
}
|
||||
|
||||
const { getSchema, getNodePath } = useCanvas()
|
||||
|
|
@ -275,6 +285,7 @@ export default {
|
|||
useNotify
|
||||
},
|
||||
isBlock,
|
||||
getMoveDragBarState,
|
||||
CanvasLayout,
|
||||
canvasRef,
|
||||
CanvasContainer,
|
||||
|
|
@ -284,3 +295,10 @@ export default {
|
|||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.not-selected {
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ export default {
|
|||
|
||||
watch(() => useLayout().getDimension().width, setScale, { flush: 'post', immediate: true })
|
||||
|
||||
watch(() => useLayout().getPluginState().fixedPanels, setScale, { flush: 'post' })
|
||||
watch(() => useLayout().leftFixedPanelsStorage.value, setScale, { flush: 'post' })
|
||||
|
||||
watch(
|
||||
() => useLayout().getPluginState().render,
|
||||
|
|
@ -106,6 +106,20 @@ export default {
|
|||
{ flush: 'post' }
|
||||
)
|
||||
|
||||
watch(() => useLayout().rightFixedPanelsStorage.value, setScale, { flush: 'post' })
|
||||
|
||||
watch(
|
||||
() => useLayout().getSettingState().render,
|
||||
(value) => {
|
||||
const currentFixed = useLayout().rightFixedPanelsStorage.value.includes(value)
|
||||
|
||||
if (!value || currentFixed) {
|
||||
setScale()
|
||||
}
|
||||
},
|
||||
{ flush: 'post' }
|
||||
)
|
||||
|
||||
watch(
|
||||
() => sizeStyle.value,
|
||||
() => {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="plugin-panel">
|
||||
<div class="plugin-panel-header">
|
||||
<div class="plugin-panel" ref="panel" :style="{ width: panelWidth + 'px' }">
|
||||
<div :class="['plugin-panel-header', headerBottomLine]">
|
||||
<div class="plugin-panel-title">
|
||||
<span class="title"
|
||||
>{{ title }}<link-button class="link" v-if="isShowDocsIcon" :href="docsUrl"></link-button
|
||||
|
|
@ -9,22 +9,53 @@
|
|||
</div>
|
||||
<div class="plugin-panel-icon">
|
||||
<slot name="header"></slot>
|
||||
<tiny-tooltip
|
||||
v-if="isShowCollapseIcon"
|
||||
effect="light"
|
||||
:content="isCollapsed ? '展开' : '折叠'"
|
||||
placement="top"
|
||||
:visible-arrow="false"
|
||||
>
|
||||
<template #default>
|
||||
<svg-button :name="settingIcon" @click="clickCollapseIcon"></svg-button>
|
||||
</template>
|
||||
</tiny-tooltip>
|
||||
<svg-button
|
||||
class="item icon-sidebar"
|
||||
:name="fixedPanels?.includes(fixedName) ? 'fixed-solid' : 'fixed'"
|
||||
:tips="!fixedPanels?.includes(fixedName) ? '固定面板' : '解除固定面板'"
|
||||
@click="fixPanel"
|
||||
></svg-button>
|
||||
<close-icon v-if="!isCloseLeft" :name="name" @close="closePanel"></close-icon>
|
||||
</div>
|
||||
</div>
|
||||
<slot name="content"></slot>
|
||||
<div class="scroll-content">
|
||||
<slot name="content"></slot>
|
||||
</div>
|
||||
|
||||
<div v-if="isWidthResizable">
|
||||
<div class="resizer-right" v-if="isLeftResizer" @mousedown="onMouseDownRight"></div>
|
||||
<div class="resizer-left" v-if="isRightResizer" @mousedown="onMouseDownLeft"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { useThrottleFn } from '@vueuse/core'
|
||||
import { inject, ref, computed, onMounted, provide } from 'vue'
|
||||
import { useLayout } from '@opentiny/tiny-engine-meta-register'
|
||||
import { SvgButton } from '@opentiny/tiny-engine-common'
|
||||
import { constants } from '@opentiny/tiny-engine-utils'
|
||||
import LinkButton from './LinkButton.vue'
|
||||
import CloseIcon from './CloseIcon.vue'
|
||||
import { Tooltip } from '@opentiny/vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TinyTooltip: Tooltip,
|
||||
LinkButton,
|
||||
CloseIcon
|
||||
CloseIcon,
|
||||
SvgButton
|
||||
},
|
||||
props: {
|
||||
/**
|
||||
|
|
@ -52,18 +83,151 @@ export default {
|
|||
isShowDocsIcon: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
/**
|
||||
* 固定面板插件数组
|
||||
*/
|
||||
fixedPanels: {
|
||||
type: Array
|
||||
},
|
||||
/**
|
||||
* 固定面板标识
|
||||
*/
|
||||
fixedName: {
|
||||
type: String
|
||||
},
|
||||
/**
|
||||
* 是否展示标题下边线
|
||||
*/
|
||||
showBottomBorder: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
/**
|
||||
* 是否展示折叠按钮
|
||||
*/
|
||||
isShowCollapseIcon: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
emits: ['close'],
|
||||
emits: ['close', 'updateCollapseStatus'],
|
||||
setup(props, { emit }) {
|
||||
const closePanel = () => {
|
||||
useLayout().closePlugin()
|
||||
|
||||
emit('close')
|
||||
}
|
||||
|
||||
const { PLUGIN_DEFAULT_WIDTH } = constants
|
||||
|
||||
const MIN_WIDTH = PLUGIN_DEFAULT_WIDTH // 固定的最小宽度值
|
||||
const MAX_WIDTH = 1000 // 固定的最大宽度值
|
||||
const panel = ref(null)
|
||||
let startX = 0
|
||||
let startWidth = 0
|
||||
|
||||
const isCollapsed = ref(false)
|
||||
const settingIcon = computed(() => (isCollapsed.value ? 'collapse_all' : 'expand_all'))
|
||||
|
||||
provide('isCollapsed', isCollapsed)
|
||||
|
||||
const panelState = inject('panelState')
|
||||
const fixPanel = () => {
|
||||
panelState.emitEvent('fixPanel', props.fixedName)
|
||||
}
|
||||
|
||||
const headerBottomLine = computed(() => (props.showBottomBorder ? 'header-bottom-line' : ''))
|
||||
|
||||
const { getPluginWidth, changePluginWidth, getPluginByLayout, changeMoveDragBarState, isPanelWidthResizable } =
|
||||
useLayout()
|
||||
|
||||
const align = ref(getPluginByLayout(props.fixedName)) // 滚动条位置
|
||||
const panelWidth = ref(getPluginWidth(props.fixedName)) // 面板使用默认宽度
|
||||
const isLeftResizer = ref(align.value.includes('left'))
|
||||
const isRightResizer = ref(align.value.includes('right'))
|
||||
const isWidthResizable = computed(() => isPanelWidthResizable(props.fixedName))
|
||||
|
||||
const onMouseMoveRight = (event) => {
|
||||
const newWidth = startWidth + (event.clientX - startX)
|
||||
panelWidth.value = Math.max(MIN_WIDTH, Math.min(newWidth, MAX_WIDTH))
|
||||
changePluginWidth(props.fixedName, panelWidth.value)
|
||||
}
|
||||
|
||||
const onMouseMoveLeft = (event) => {
|
||||
const newWidth = startWidth - (event.clientX - startX)
|
||||
panelWidth.value = Math.max(MIN_WIDTH, Math.min(newWidth, MAX_WIDTH))
|
||||
changePluginWidth(props.fixedName, panelWidth.value)
|
||||
}
|
||||
|
||||
const throttledMouseMoveRight = useThrottleFn(onMouseMoveRight, 50)
|
||||
const throttledMouseMoveLeft = useThrottleFn(onMouseMoveLeft, 50)
|
||||
|
||||
const leftResizer = ref(null)
|
||||
const rightResizer = ref(null)
|
||||
|
||||
const onMouseUpRight = () => {
|
||||
changeMoveDragBarState(false)
|
||||
document.removeEventListener('mousemove', throttledMouseMoveRight)
|
||||
document.removeEventListener('mouseup', onMouseUpRight)
|
||||
rightResizer.value.style.cursor = ''
|
||||
rightResizer.value.classList.remove('dragging')
|
||||
}
|
||||
|
||||
const onMouseDownRight = (event) => {
|
||||
changeMoveDragBarState(true)
|
||||
startX = event.clientX
|
||||
startWidth = panel.value.offsetWidth
|
||||
document.addEventListener('mousemove', throttledMouseMoveRight)
|
||||
document.addEventListener('mouseup', onMouseUpRight)
|
||||
rightResizer.value.style.cursor = 'ew-resize'
|
||||
rightResizer.value.classList.add('dragging')
|
||||
}
|
||||
|
||||
const onMouseUpLeft = () => {
|
||||
changeMoveDragBarState(false)
|
||||
document.removeEventListener('mousemove', throttledMouseMoveLeft)
|
||||
document.removeEventListener('mouseup', onMouseUpLeft)
|
||||
leftResizer.value.style.cursor = ''
|
||||
leftResizer.value.classList.remove('dragging')
|
||||
}
|
||||
|
||||
const onMouseDownLeft = (event) => {
|
||||
changeMoveDragBarState(true)
|
||||
startX = event.clientX
|
||||
startWidth = panel.value.offsetWidth
|
||||
document.addEventListener('mousemove', throttledMouseMoveLeft)
|
||||
document.addEventListener('mouseup', onMouseUpLeft)
|
||||
leftResizer.value.style.cursor = 'ew-resize'
|
||||
leftResizer.value.classList.add('dragging')
|
||||
}
|
||||
|
||||
const initResizerDOM = () => {
|
||||
leftResizer.value = document.querySelector('.resizer-left')
|
||||
rightResizer.value = document.querySelector('.resizer-right')
|
||||
}
|
||||
|
||||
const clickCollapseIcon = () => {
|
||||
isCollapsed.value = !isCollapsed.value
|
||||
emit('updateCollapseStatus', isCollapsed.value)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initResizerDOM()
|
||||
})
|
||||
|
||||
return {
|
||||
closePanel
|
||||
isWidthResizable,
|
||||
headerBottomLine,
|
||||
clickCollapseIcon,
|
||||
isCollapsed,
|
||||
settingIcon,
|
||||
closePanel,
|
||||
fixPanel,
|
||||
panel,
|
||||
panelWidth,
|
||||
onMouseDownRight,
|
||||
onMouseDownLeft,
|
||||
isLeftResizer,
|
||||
isRightResizer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -88,12 +252,15 @@ export default {
|
|||
padding: 12px;
|
||||
color: var(--te-component-common-text-color-primary);
|
||||
font-weight: var(--te-base-font-weight-7);
|
||||
|
||||
.plugin-panel-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.title + .icon-wrap {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
@ -109,10 +276,65 @@ export default {
|
|||
:deep(.svg-button + .svg-button) {
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
:deep(.svg-button + .icon-wrap) {
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 右边拖拽线
|
||||
.resizer-right {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 1px;
|
||||
height: 100%;
|
||||
cursor: ew-resize;
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
.header-bottom-line {
|
||||
border-bottom: 1px solid var(--te-common-border-divider);
|
||||
}
|
||||
|
||||
.dragging {
|
||||
width: 2px !important;
|
||||
background-color: var(--te-component-common-resizer-border-color) !important;
|
||||
}
|
||||
|
||||
.resizer-right:hover {
|
||||
width: 2px;
|
||||
background-color: var(--te-component-common-resizer-border-color);
|
||||
}
|
||||
|
||||
// 左边拖拽线
|
||||
.resizer-left {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 1px;
|
||||
height: 100%;
|
||||
cursor: ew-resize;
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
.resizer-left:hover {
|
||||
width: 2px;
|
||||
background-color: var(--te-component-common-resizer-border-color);
|
||||
}
|
||||
|
||||
.scroll-content {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
|
||||
.scroll-content::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,179 @@
|
|||
<template>
|
||||
<ul v-if="false" class="plugin-context-menu" :style="{ left: contextMenu.x + 'px', top: contextMenu.y + 'px' }">
|
||||
<li v-if="contextMenu.type" @click="hidePlugin">隐藏 "{{ contextMenu.item.title }}"</li>
|
||||
<li v-if="contextMenu.type" class="bottom-li" @click="switchAlign">
|
||||
切换到{{ align.includes('right') ? '左侧' : '右侧' }}
|
||||
</li>
|
||||
<li
|
||||
v-for="(item, index) in list"
|
||||
:key="index"
|
||||
@click.stop="changeShowState(item.id)"
|
||||
class="menu-item-wrapper"
|
||||
:class="{
|
||||
'bottom-li': index === list.length - 1
|
||||
}"
|
||||
>
|
||||
<span class="check-mark">
|
||||
<span v-show="getPluginShown(item.id)">√</span>
|
||||
</span>
|
||||
<span>{{ item.title }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { reactive, onMounted, onBeforeUnmount, nextTick, ref } from 'vue'
|
||||
import { useLayout } from '@opentiny/tiny-engine-meta-register'
|
||||
export default {
|
||||
props: {
|
||||
list: {
|
||||
type: Array
|
||||
},
|
||||
align: {
|
||||
type: String,
|
||||
default: 'left'
|
||||
}
|
||||
},
|
||||
emits: ['close', 'switchAlign'],
|
||||
setup(props, { emit }) {
|
||||
const { getPluginShown, changePluginShown, changeMenuShown } = useLayout()
|
||||
const pluginGroupPosition = ref('')
|
||||
const contextMenu = reactive({
|
||||
type: true,
|
||||
visible: false,
|
||||
x: 0,
|
||||
y: 0,
|
||||
item: null,
|
||||
index: null,
|
||||
list: null
|
||||
})
|
||||
|
||||
// 显示菜单
|
||||
const contextMenuWidth = props.align.includes('right') ? 130 : 0
|
||||
const showContextMenu = (x, y, type, item, index, position) => {
|
||||
const windowHeight = window.innerHeight
|
||||
|
||||
contextMenu.type = type
|
||||
contextMenu.visible = true
|
||||
|
||||
nextTick(() => {
|
||||
const pluginMenuPanel = document.querySelector('.plugin-context-menu')
|
||||
const menuHeight = pluginMenuPanel?.offsetHeight
|
||||
|
||||
const spaceBelow = windowHeight - y - 20
|
||||
|
||||
if (menuHeight && spaceBelow < menuHeight) {
|
||||
contextMenu.y = y - menuHeight
|
||||
} else {
|
||||
contextMenu.y = y
|
||||
}
|
||||
|
||||
contextMenu.x = x - contextMenuWidth
|
||||
})
|
||||
|
||||
if (type) {
|
||||
contextMenu.item = item
|
||||
contextMenu.index = index
|
||||
pluginGroupPosition.value = position
|
||||
}
|
||||
}
|
||||
|
||||
// 隐藏菜单
|
||||
const hideContextMenu = () => {
|
||||
contextMenu.visible = false
|
||||
}
|
||||
|
||||
// 隐藏插件
|
||||
const hidePlugin = () => {
|
||||
emit('close')
|
||||
changePluginShown(contextMenu.item.id)
|
||||
hideContextMenu()
|
||||
}
|
||||
|
||||
// 切换组件位置
|
||||
const switchAlign = () => {
|
||||
emit('close')
|
||||
emit('switchAlign', contextMenu.index, contextMenu.item.id, pluginGroupPosition.value)
|
||||
hideContextMenu()
|
||||
}
|
||||
|
||||
// 隐藏活动栏
|
||||
const hideSidebar = () => {
|
||||
const align = props.align.includes('right') ? 'right' : 'left'
|
||||
changeMenuShown(align)
|
||||
hideContextMenu()
|
||||
}
|
||||
|
||||
// 改变组件显示状态
|
||||
const changeShowState = (pluginName) => {
|
||||
changePluginShown(pluginName)
|
||||
}
|
||||
|
||||
const handleClickOutside = (event) => {
|
||||
if (!event.target.closest('.plugin-context-menu')) {
|
||||
hideContextMenu()
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener('click', handleClickOutside)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
document.removeEventListener('click', handleClickOutside)
|
||||
})
|
||||
|
||||
return {
|
||||
contextMenu,
|
||||
showContextMenu,
|
||||
changeShowState,
|
||||
getPluginShown,
|
||||
hidePlugin,
|
||||
switchAlign,
|
||||
hideSidebar
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 引入B的CSS样式 */
|
||||
.plugin-context-menu {
|
||||
position: absolute;
|
||||
background: white;
|
||||
border: 1px solid #ccc;
|
||||
list-style: none;
|
||||
width: 135px;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.plugin-context-menu-header {
|
||||
padding: 8px 12px;
|
||||
font-weight: bold;
|
||||
cursor: default;
|
||||
background: #f5f5f5;
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
.bottom-li {
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.plugin-context-menu li {
|
||||
padding: 8px 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.plugin-context-menu li:hover {
|
||||
background: #f0f0f0;
|
||||
}
|
||||
.menu-item-wrapper {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.check-mark {
|
||||
width: 20px;
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,6 +1,14 @@
|
|||
<template>
|
||||
<div
|
||||
:class="['plugin-setting', { 'second-panel': isSecond }, { 'full-screen': state.isFullScreen }]"
|
||||
id="panel-setting"
|
||||
:class="[
|
||||
'plugin-setting',
|
||||
{ 'second-panel': isSecond },
|
||||
{ 'full-screen': state.isFullScreen },
|
||||
{ 'align-right': align.includes('right') },
|
||||
shadowClass
|
||||
]"
|
||||
:style="alignStyle"
|
||||
@click="$emit('click')"
|
||||
>
|
||||
<div class="plugin-setting-header">
|
||||
|
|
@ -31,11 +39,12 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { reactive, watchEffect } from 'vue'
|
||||
import { nextTick, reactive, watchEffect, computed } from 'vue'
|
||||
import { Button } from '@opentiny/vue'
|
||||
import { iconPlus } from '@opentiny/vue-icon'
|
||||
import ButtonGroup from './ButtonGroup.vue'
|
||||
import SvgButton from './SvgButton.vue'
|
||||
import { useLayout } from '@opentiny/tiny-engine-meta-register'
|
||||
|
||||
const EVENTS = {
|
||||
FULL_SCREEN_CHANGE: 'fullScreenChange',
|
||||
|
|
@ -91,6 +100,14 @@ export default {
|
|||
icon: {
|
||||
type: Object,
|
||||
default: iconPlus()
|
||||
},
|
||||
fixedName: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
align: {
|
||||
type: String,
|
||||
default: 'leftTop'
|
||||
}
|
||||
},
|
||||
emits: [EVENTS.FULL_SCREEN_CHANGE, EVENTS.SAVE, EVENTS.CANCEL, EVENTS.ADD, EVENTS.CLICK],
|
||||
|
|
@ -99,6 +116,28 @@ export default {
|
|||
isFullScreen: false
|
||||
})
|
||||
|
||||
const { getPluginWidth } = useLayout()
|
||||
|
||||
const firstPanelOffset = computed(() => {
|
||||
return getPluginWidth(props.fixedName) + 1
|
||||
})
|
||||
|
||||
const secondPanelAlign = computed(() => {
|
||||
return props.align.includes('left') ? 'left' : 'right'
|
||||
})
|
||||
|
||||
const alignStyle = computed(() => {
|
||||
return `${secondPanelAlign.value} : ${firstPanelOffset.value}px`
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
// 处理二级面板偏移量
|
||||
const secondPanelOffset = document.querySelector('.plugin-setting')?.clientWidth + firstPanelOffset.value
|
||||
nextTick(() => {
|
||||
document.querySelector('.second-panel')?.style.setProperty(secondPanelAlign.value, `${secondPanelOffset}px`)
|
||||
})
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
state.isFullScreen = props.isFullScreen
|
||||
})
|
||||
|
|
@ -112,7 +151,16 @@ export default {
|
|||
return isFullScreen ? '收起' : '全屏查看'
|
||||
}
|
||||
|
||||
// 计算属性用于确定阴影类
|
||||
const shadowClass = computed(() => {
|
||||
if (props.isSecond) return ''
|
||||
return props.align.includes('right') ? 'shadow-right' : 'shadow-left'
|
||||
})
|
||||
|
||||
return {
|
||||
alignStyle,
|
||||
shadowClass,
|
||||
firstPanelOffset,
|
||||
state,
|
||||
fullScreen,
|
||||
getFullScreenLabel
|
||||
|
|
@ -124,7 +172,6 @@ export default {
|
|||
<style lang="less" scoped>
|
||||
.plugin-setting {
|
||||
position: absolute;
|
||||
left: var(--base-left-panel-width);
|
||||
top: 0;
|
||||
width: var(--base-collection-panel-width);
|
||||
height: 100%;
|
||||
|
|
@ -132,17 +179,24 @@ export default {
|
|||
background: var(--te-component-common-bg-color);
|
||||
overflow: hidden;
|
||||
border-left: 1px solid var(--te-component-common-border-color-divider);
|
||||
|
||||
&:not(.second-panel) {
|
||||
box-shadow: 6px 0px 3px 0px var(--te-component-common-shadow-color);
|
||||
border-right: none;
|
||||
border-left: none;
|
||||
&.shadow-left {
|
||||
box-shadow: 6px 0px 3px 0px var(--te-component-common-shadow-color);
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
&.shadow-right {
|
||||
box-shadow: -6px 0px 3px 0px var(--te-component-common-shadow-color);
|
||||
border-left: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.full-screen {
|
||||
width: var(--base-collection-panel-full-screen-width);
|
||||
}
|
||||
|
||||
&.second-panel {
|
||||
left: calc(var(--base-left-panel-width) + var(--base-collection-panel-width));
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
|
|
@ -167,6 +221,7 @@ export default {
|
|||
color: var(--te-component-common-text-color-primary);
|
||||
padding: 0 12px;
|
||||
border-bottom: 1px solid var(--te-component-common-border-color-divider);
|
||||
|
||||
.plugin-setting-header-title {
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
|
|
@ -175,9 +230,11 @@ export default {
|
|||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
:deep(.svg-button + .svg-button) {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
:deep(.tiny-button.tiny-button) {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
|
@ -195,4 +252,8 @@ export default {
|
|||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.align-right {
|
||||
right: 0;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import ConfigGroup from './ConfigGroup.vue'
|
|||
import ConfigItem from './ConfigItem.vue'
|
||||
export { default as PluginSetting } from './PluginSetting.vue'
|
||||
export { default as PluginPanel } from './PluginPanel.vue'
|
||||
export { default as PluginRightMenu } from './PluginRightMenu.vue'
|
||||
export { default as SvgButton } from './SvgButton.vue'
|
||||
export { default as LinkButton } from './LinkButton.vue'
|
||||
export { default as ConfigCollapse } from './ConfigCollapse.vue'
|
||||
|
|
|
|||
|
|
@ -44,13 +44,14 @@
|
|||
"eslint-linter-browserify": "8.57.0",
|
||||
"monaco-editor": "0.51.0",
|
||||
"prettier": "2.7.1",
|
||||
"vue-draggable-next": "2.1.0"
|
||||
"vue-draggable-next": "2.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@opentiny/tiny-engine-vite-plugin-meta-comments": "workspace:*",
|
||||
"@vitejs/plugin-vue": "^5.1.2",
|
||||
"@vitejs/plugin-vue-jsx": "^4.0.1",
|
||||
"glob": "^10.3.4",
|
||||
"@vueuse/core": "^9.6.0",
|
||||
"vite": "^5.4.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@
|
|||
--te-component-common-border-color-transparent: var(--te-base-color-transparent); // 透明色直接使用base的变量
|
||||
--te-component-common-button-border-color: var(--te-common-border-secondary);
|
||||
|
||||
--te-component-common-resizer-border-color: var(--te-base-blue-70);
|
||||
|
||||
--te-component-common-shadow-color: var(--te-common-shadow-panel);
|
||||
|
||||
--te-component-config-item-bind-bg-color: var(--te-common-bg-info);
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@
|
|||
"@opentiny/tiny-engine-utils": "workspace:*",
|
||||
"@vue/shared": "^3.3.4",
|
||||
"monaco-editor": "0.51.0",
|
||||
"vue-draggable-next": "2.1.0"
|
||||
"vue-draggable-next": "2.2.1"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
|
|
|||
|
|
@ -1,18 +1,26 @@
|
|||
<svg
|
||||
data-icon="DisplayInline"
|
||||
aria-hidden="true"
|
||||
focusable="false"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
style="display: block"
|
||||
>
|
||||
<path opacity=".6" fill-rule="evenodd" clip-rule="evenodd" d="M1 2h1v12H1V2zm14 0h-1v12h1V2z"></path>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M7.25 3L3.5 13h2l1-3h3l1 3h2L8.75 3h-1.5zm1.917 6L8 5.5 6.833 9h2.334z"
|
||||
></path>
|
||||
</svg>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 26.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 132 132" style="enable-background:new 0 0 132 132;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:none;}
|
||||
.st1{fill-rule:evenodd;clip-rule:evenodd;fill:currentColor;}
|
||||
</style>
|
||||
<rect id="size" class="st0" width="132" height="132"/>
|
||||
<path id="矢量_28" class="st1" d="M8.2,66C8.2,34.1,34.1,8.2,66,8.2c31.9,0,57.8,25.8,57.8,57.8c0,15.9-35.8,0.9-39.9,19.2
|
||||
c-0.7,3,1.1,8.8,3,14.8c2.7,8.6,5.4,17.6,1.3,19.3c-6.8,2.8-14.3,4.4-22.1,4.4C34.1,123.8,8.2,97.9,8.2,66z M46.7,20.4
|
||||
c2.7-1.1,5.4-2,8.2-2.6c3.6-0.8,7.3-1.2,11.1-1.2c3.8,0,7.5,0.4,11.1,1.2c2.8,0.6,5.5,1.5,8.2,2.6c2.8,1.2,5.3,2.6,7.8,4.2
|
||||
c2.8,1.8,5.5,4,7.9,6.4s4.6,5.1,6.5,7.9v0c1.6,2.5,3,5.1,4.2,7.8c1.1,2.7,2,5.4,2.7,8.2l0,0c0.8,3.4,1.2,6.8,1.2,10.4
|
||||
c-0.8,0.3-2,0.5-3.6,0.7c-1.2,0.1-3.2,0.3-6.1,0.4c-3.4,0.2-6,0.4-7.8,0.6c-3.2,0.4-6.1,1-8.5,1.8c-3.4,1.1-6.2,2.8-8.4,5
|
||||
c-0.9,0.9-1.7,1.9-2.4,2.9c-1.4,2-2.3,4.3-2.9,6.8c-0.3,1.6-0.4,3.3-0.2,5.3c0.1,1.3,0.4,2.9,0.8,4.6c0.4,1.9,1.3,5,2.6,9.1
|
||||
c0.9,2.8,1.5,4.8,1.8,6.1c0.5,1.7,0.8,3.1,1,4.3c-1.6,0.5-3.2,1-4.8,1.4c-3.5,0.8-7.2,1.2-10.9,1.2c-3.8,0-7.5-0.4-11.1-1.2
|
||||
c-2.8-0.6-5.5-1.5-8.2-2.7c-2.8-1.2-5.3-2.6-7.8-4.2c-2.8-1.9-5.5-4-7.9-6.5s-4.6-5.1-6.5-7.9c-1.6-2.5-3-5.1-4.2-7.8
|
||||
c-1.1-2.7-2-5.4-2.6-8.2c-0.8-3.6-1.2-7.3-1.2-11.1c0-3.8,0.4-7.5,1.2-11.1c0.6-2.8,1.5-5.5,2.6-8.2c1.2-2.8,2.6-5.3,4.2-7.8
|
||||
c1.8-2.8,4-5.5,6.4-7.9s5.1-4.6,7.9-6.4l0,0l0,0l0,0C41.4,22.9,44,21.5,46.7,20.4z M33,45.4C33,38.5,38.5,33,45.4,33
|
||||
s12.4,5.5,12.4,12.4s-5.5,12.4-12.4,12.4S33,52.2,33,45.4z M86.6,33c-6.8,0-12.4,5.5-12.4,12.4s5.5,12.4,12.4,12.4
|
||||
c6.8,0,12.4-5.5,12.4-12.4S93.4,33,86.6,33z M41.2,45.4c0-2.3,1.8-4.1,4.1-4.1c2.3,0,4.1,1.8,4.1,4.1c0,2.3-1.8,4.1-4.1,4.1
|
||||
C43.1,49.5,41.2,47.7,41.2,45.4z M86.6,41.2c-2.3,0-4.1,1.8-4.1,4.1c0,2.3,1.8,4.1,4.1,4.1c2.3,0,4.1-1.8,4.1-4.1
|
||||
C90.8,43.1,88.9,41.2,86.6,41.2z M45.4,74.2c-6.8,0-12.4,5.5-12.4,12.4C33,93.4,38.5,99,45.4,99s12.4-5.6,12.4-12.4
|
||||
C57.8,79.8,52.2,74.2,45.4,74.2z M45.4,82.5c-2.3,0-4.1,1.8-4.1,4.1c0,2.3,1.8,4.1,4.1,4.1c2.3,0,4.1-1.8,4.1-4.1
|
||||
C49.5,84.3,47.7,82.5,45.4,82.5z"/>
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 459 B After Width: | Height: | Size: 2.3 KiB |
|
|
@ -1,17 +1,17 @@
|
|||
<svg version="1.1" id="图层_1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 64 64" style="enable-background:new 0 0 64 64;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:currentcolor;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M57.5,9.2h-51c-1.1,0-2,0.9-2,2v39.7c0,1.1,0.9,2,2,2h51.1c1.1,0,2-0.9,2-2V11.2C59.5,10.1,58.7,9.2,57.5,9.2z
|
||||
M55.5,48.9h-47V13.2h47.1L55.5,48.9L55.5,48.9z"/>
|
||||
<rect x="14.3" y="20.6" class="st0" width="5.3" height="3"/>
|
||||
<rect x="25.1" y="20.6" class="st0" width="24.5" height="3"/>
|
||||
<rect x="14.3" y="29.6" class="st0" width="5.3" height="3"/>
|
||||
<rect x="25.1" y="29.6" class="st0" width="24.5" height="3"/>
|
||||
<rect x="14.3" y="38.5" class="st0" width="5.3" height="3"/>
|
||||
<rect x="25.1" y="38.5" class="st0" width="24.5" height="3"/>
|
||||
</g>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 26.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 132 132" style="enable-background:new 0 0 132 132;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:none;}
|
||||
.st1{fill:currentColor;}
|
||||
</style>
|
||||
<rect id="size" class="st0" width="132" height="132"/>
|
||||
<path id="形状结合" class="st1" d="M119.6,33h-12.9c-2.1-9.6-10.3-16.5-20.1-16.5c-9.9,0-18.2,6.9-20.2,16.5h-54
|
||||
c-2.3,0-4.1,1.8-4.1,4.1c0,2.2,1.8,4.1,4.1,4.1h54c2.1,9.5,10.3,16.5,20.2,16.5c9.8,0,18.1-7,20.1-16.5h12.9c2.2,0,4.1-1.9,4.1-4.1
|
||||
C123.8,34.8,121.8,33,119.6,33z M86.6,49.5c-7,0-12.4-5.5-12.4-12.4c0-7,5.4-12.4,12.4-12.4c6.9,0,12.4,5.4,12.4,12.4
|
||||
C99,44,93.6,49.5,86.6,49.5z M12.4,99h12.8c2.1,9.5,10.3,16.5,20.2,16.5c9.8,0,18.1-7,20.1-16.5h54.1c2.2,0,4.1-1.9,4.1-4.1
|
||||
c0-2.3-1.9-4.1-4.1-4.1H65.5c-2.1-9.6-10.3-16.5-20.1-16.5c-9.9,0-18.1,6.9-20.2,16.5H12.4c-2.3,0-4.1,1.8-4.1,4.1
|
||||
C8.2,97.1,10.1,99,12.4,99z M45.4,82.5c6.9,0,12.4,5.4,12.4,12.4c0,6.9-5.5,12.4-12.4,12.4c-7,0-12.4-5.4-12.4-12.4
|
||||
C33,87.9,38.4,82.5,45.4,82.5z"/>
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 877 B After Width: | Height: | Size: 1.2 KiB |
|
|
@ -1,3 +1,25 @@
|
|||
<svg data-icon="Target" aria-hidden="true" focusable="false" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9 2H7v2H4v3H2v2h2v3h3v2h2v-2h3V9h2V7h-2V4H9V2zm1 5V6H6v4h4V7z" fill="currentColor"></path>
|
||||
</svg>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 26.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 132 132" style="enable-background:new 0 0 132 132;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:none;}
|
||||
.st1{fill-rule:evenodd;clip-rule:evenodd;fill:currentColor;}
|
||||
</style>
|
||||
<rect id="size" class="st0" width="132" height="132"/>
|
||||
<path id="矢量_29" class="st1" d="M79.1,13.5C76.7,9.4,72.6,7,68,7H53.3l-1.5,0.2l-1.3,0.2c-3.9,1.1-6.9,3.7-8.5,7.3l-3.5,8
|
||||
l-1.7,1l-8.7-1l-1.2-0.1c-4.7,0-8.9,2.3-11.2,6.3L8.2,41.7L7.6,43l-0.4,1.2c-1,3.9-0.2,7.8,2.1,11l5.3,7v1.9l-5.3,7l-0.8,1.2
|
||||
C6.2,76.4,6.2,81.1,8.4,85l7.4,12.8l0.8,1.1l0.9,1.1c2.8,2.8,6.7,4.1,10.5,3.6l8.7-1.1l1.7,1.1l3.5,8l0.6,1.1
|
||||
c2.4,4.1,6.5,6.5,11,6.5h7.3v-8.2h-7.3c-1.6,0-3.4-1.5-4.1-2.9l-3.9-9.1c-0.4-0.8-1-1.5-1.7-2l-4.1-2.3c-0.8-0.5-1.6-0.7-2.6-0.6
|
||||
L27,95.5c-1.5,0.2-3.4-1.1-4.3-2.2l-7.3-12.4c-0.8-1.3-0.2-3.6,0.5-5l5.9-7.9c0.5-0.7,0.8-1.6,0.8-2.5v-4.7c0-0.9-0.3-1.7-0.8-2.5
|
||||
l-6.1-8.1c-0.9-1.2-0.8-3.5-0.2-4.9L22.8,33c0.8-1.3,3-2.1,4.5-2.1l9.9,1.2c0.9,0.1,1.7-0.1,2.6-0.6l4.1-2.3c0.7-0.5,1.3-1.2,1.7-2
|
||||
l4-9.3c0.6-1.3,2.6-2.4,4.1-2.6H68c1.6,0,3.4,1.5,4.1,2.9l3.9,9.1c0.3,0.8,1,1.5,1.7,2l4.1,2.3c0.8,0.5,1.7,0.7,2.6,0.6l10.1-1.2
|
||||
c1.5-0.2,3.3,1.1,4.3,2.2l7.3,12.4c0.8,1.3,0.2,3.6-0.5,5l-5.9,7.9c-0.6,0.7-0.8,1.6-0.8,2.5v4.7c0,0.9,0.2,1.8,0.8,2.5l2.6,3.5
|
||||
h10.3c-0.1-0.1-0.2-0.2-0.2-0.3l-5.3-7v-1.9l5.3-7l0.8-1.2c2.3-4.1,2.3-8.8,0.1-12.7l-7.4-12.8l-0.8-1.2l-0.9-1.1
|
||||
c-2.8-2.8-6.7-4.1-10.5-3.6l-8.7,1l-1.8-1l-3.4-8L79.1,13.5z M82.7,63.2c0,2.9-0.6,5.7-1.6,8.2h-9.5c1.8-2.3,2.9-5.1,2.9-8.2
|
||||
c0-7.4-6-13.4-13.4-13.4s-13.3,5.9-13.3,13.4c0,7.3,5.8,13.2,13,13.3v8.2C49.1,84.6,39.6,75,39.6,63.2c0-12,9.7-21.6,21.5-21.6
|
||||
C73.1,41.6,82.7,51.3,82.7,63.2z M126.9,83.8c0-2.3-1.9-4.1-4.1-4.1H73.2c-2.3,0-4.1,1.8-4.1,4.1c0,2.2,1.8,4.1,4.1,4.1h49.5
|
||||
C125,88,126.9,86.1,126.9,83.8z M122.7,96.2c2.2,0,4.1,1.8,4.1,4.1c0,2.2-1.9,4.1-4.1,4.1H73.2c-2.3,0-4.1-1.9-4.1-4.1
|
||||
c0-2.3,1.8-4.1,4.1-4.1H122.7z M126.9,116.8c0-2.3-1.9-4.1-4.1-4.1H73.2c-2.3,0-4.1,1.8-4.1,4.1c0,2.2,1.8,4.1,4.1,4.1h49.5
|
||||
C125,121,126.9,119.1,126.9,116.8z"/>
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 280 B After Width: | Height: | Size: 2.2 KiB |
|
|
@ -26,7 +26,8 @@
|
|||
"@opentiny/tiny-engine-common": "workspace:*",
|
||||
"@opentiny/tiny-engine-meta-register": "workspace:*",
|
||||
"@opentiny/tiny-engine-utils": "workspace:*",
|
||||
"@vueuse/core": "^9.6.0"
|
||||
"@vueuse/core": "^9.6.0",
|
||||
"vue-draggable-next": "2.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@opentiny/tiny-engine-vite-plugin-meta-comments": "workspace:*",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,15 @@
|
|||
<!-- 左侧插件栏-->
|
||||
<template>
|
||||
<div id="tiny-engine-nav-panel" :style="{ 'pointer-events': pluginState.pluginEvent }">
|
||||
<ul class="nav-panel-lists top">
|
||||
<li
|
||||
<vue-draggable-next
|
||||
v-model="state.topNavLists"
|
||||
filter="EditorHelp"
|
||||
class="nav-panel-lists top"
|
||||
id="leftTop"
|
||||
group="plugins"
|
||||
@end="onEnd"
|
||||
>
|
||||
<div
|
||||
v-for="(item, index) in state.topNavLists"
|
||||
:key="index"
|
||||
:class="{
|
||||
|
|
@ -12,8 +20,9 @@
|
|||
}"
|
||||
:title="item.title"
|
||||
@click="clickMenu({ item, index })"
|
||||
@contextmenu.prevent="showContextMenu($event, true, item, index, PLUGIN_POSITION.leftTop)"
|
||||
>
|
||||
<div>
|
||||
<div v-if="getPluginShown(item.id)">
|
||||
<span class="item-icon">
|
||||
<svg-icon
|
||||
v-if="typeof iconComponents[item.id] === 'string'"
|
||||
|
|
@ -23,65 +32,80 @@
|
|||
<component v-else :is="iconComponents[item.id]" class="panel-icon"></component>
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="nav-panel-lists bottom">
|
||||
<li style="flex: 1" class="list-item"></li>
|
||||
<li
|
||||
v-for="(item, index) in state.bottomNavLists"
|
||||
:key="index"
|
||||
:class="[
|
||||
'list-item',
|
||||
{ active: renderPanel === item.id, prev: state.prevIdex - 1 === index, 'first-item': index === 0 }
|
||||
]"
|
||||
:title="item.title"
|
||||
@click="clickMenu({ item, index })"
|
||||
>
|
||||
<div :class="{ 'is-show': renderPanel }">
|
||||
<span class="item-icon">
|
||||
<public-icon
|
||||
v-if="typeof iconComponents[item.id] === 'string'"
|
||||
:name="iconComponents[item.id]"
|
||||
class="panel-icon"
|
||||
svgClass="panel-svg"
|
||||
></public-icon>
|
||||
<component v-else :is="iconComponents[item.id]" class="panel-icon"></component>
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</vue-draggable-next>
|
||||
|
||||
<div
|
||||
v-show="renderPanel && components[renderPanel]"
|
||||
id="tiny-engine-left-panel"
|
||||
:class="[renderPanel, { 'is-fixed': pluginState.fixedPanels.includes(renderPanel) }]"
|
||||
>
|
||||
<div class="left-panel-wrap">
|
||||
<keep-alive>
|
||||
<component
|
||||
:is="components[renderPanel]"
|
||||
ref="pluginRef"
|
||||
:fixed-panels="pluginState.fixedPanels"
|
||||
@close="close"
|
||||
@fixPanel="fixPanel"
|
||||
></component>
|
||||
</keep-alive>
|
||||
<!-- 图标菜单下侧区域(附加icon) -->
|
||||
<div class="nav-panel-lists bottom">
|
||||
<div style="flex: 1" class="list-item" @contextmenu.prevent="showContextMenu($event, false)" />
|
||||
<vue-draggable-next id="leftBottom" v-model="state.bottomNavLists" group="plugins" @end="onEnd">
|
||||
<div
|
||||
v-for="(item, index) in state.bottomNavLists"
|
||||
:key="index"
|
||||
:class="[
|
||||
'list-item',
|
||||
{ active: renderPanel === item.id, prev: state.prevIdex - 1 === index, 'first-item': index === 0 }
|
||||
]"
|
||||
:title="item.title"
|
||||
@click="clickMenu({ item, index })"
|
||||
@contextmenu.prevent="showContextMenu($event, true, item, index, PLUGIN_POSITION.leftBottom)"
|
||||
>
|
||||
<div :class="{ 'is-show': renderPanel }" v-if="getPluginShown(item.id)">
|
||||
<span class="item-icon">
|
||||
<public-icon
|
||||
v-if="typeof iconComponents[item.id] === 'string'"
|
||||
:name="iconComponents[item.id]"
|
||||
class="panel-icon"
|
||||
svgClass="panel-svg"
|
||||
></public-icon>
|
||||
<component v-else :is="iconComponents[item.id]" class="panel-icon"></component>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</vue-draggable-next>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div :class="{ 'not-selected': getMoveDragBarState() }">
|
||||
<!-- 插件面板 -->
|
||||
<div
|
||||
v-show="renderPanel && components[renderPanel]"
|
||||
id="tiny-engine-left-panel"
|
||||
:class="[renderPanel, { 'is-fixed': leftFixedPanelsStorage.includes(renderPanel) }]"
|
||||
>
|
||||
<div class="left-panel-wrap">
|
||||
<keep-alive>
|
||||
<component
|
||||
ref="pluginRef"
|
||||
:is="currentComponent"
|
||||
:fixed-panels="leftFixedPanelsStorage"
|
||||
@close="close"
|
||||
@fixPanel="fixPanel"
|
||||
></component>
|
||||
</keep-alive>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<plugin-right-menu
|
||||
ref="rightMenu"
|
||||
:list="[...state.topNavLists, ...state.bottomNavLists]"
|
||||
:align="left"
|
||||
@switchAlign="switchAlign"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { reactive, ref, watch } from 'vue'
|
||||
import { reactive, ref, watch, computed } from 'vue'
|
||||
import { Popover, Tooltip } from '@opentiny/vue'
|
||||
import { useLayout, usePage, useModal, META_APP } from '@opentiny/tiny-engine-meta-register'
|
||||
import { PublicIcon } from '@opentiny/tiny-engine-common'
|
||||
import { constants } from '@opentiny/tiny-engine-utils'
|
||||
|
||||
const { STORAGE_KEY_FIXED_PANELS } = constants
|
||||
import { VueDraggableNext } from 'vue-draggable-next'
|
||||
import { useLayout, usePage, META_APP } from '@opentiny/tiny-engine-meta-register'
|
||||
import { PublicIcon, PluginRightMenu } from '@opentiny/tiny-engine-common'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
PluginRightMenu,
|
||||
VueDraggableNext,
|
||||
TinyPopover: Popover,
|
||||
TinyTooltip: Tooltip,
|
||||
PublicIcon
|
||||
|
|
@ -93,6 +117,10 @@ export default {
|
|||
plugins: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
pluginList: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
emits: ['click', 'node-click'],
|
||||
|
|
@ -101,18 +129,56 @@ export default {
|
|||
const iconComponents = {}
|
||||
const pluginRef = ref(null)
|
||||
const { isTemporaryPage } = usePage()
|
||||
const { message } = useModal()
|
||||
const pluginState = useLayout().getPluginState()
|
||||
|
||||
props.plugins.forEach(({ id, entry, icon }) => {
|
||||
const {
|
||||
changeLeftFixedPanels,
|
||||
leftFixedPanelsStorage,
|
||||
getPluginById,
|
||||
getPluginShown,
|
||||
PLUGIN_POSITION,
|
||||
getMoveDragBarState,
|
||||
isSameSide,
|
||||
dragPluginLayout,
|
||||
getPluginsByPosition
|
||||
} = useLayout()
|
||||
|
||||
const rightMenu = ref(null)
|
||||
const showContextMenu = (event, type, item, index, align) => {
|
||||
if (!type) {
|
||||
rightMenu.value.showContextMenu(event.clientX, event.clientY, type)
|
||||
} else {
|
||||
rightMenu.value.showContextMenu(event.clientX, event.clientY, type, item, index, align)
|
||||
}
|
||||
}
|
||||
|
||||
const state = reactive({
|
||||
prevIdex: -2,
|
||||
topNavLists: getPluginsByPosition(PLUGIN_POSITION.leftTop, props.pluginList),
|
||||
bottomNavLists: getPluginsByPosition(PLUGIN_POSITION.leftBottom, props.pluginList)
|
||||
})
|
||||
|
||||
const changeAlign = (pluginId) => {
|
||||
const item = getPluginById(props.pluginList, pluginId)
|
||||
const existingItemIndex = state.topNavLists.findIndex((plugin) => plugin.id === item.id)
|
||||
|
||||
if (existingItemIndex !== -1) {
|
||||
state.topNavLists.splice(existingItemIndex, 1)
|
||||
}
|
||||
|
||||
state.topNavLists.unshift(item)
|
||||
}
|
||||
|
||||
props.pluginList.forEach(({ id, entry, icon }) => {
|
||||
components[id] = entry
|
||||
iconComponents[id] = icon
|
||||
})
|
||||
|
||||
const state = reactive({
|
||||
prevIdex: -2,
|
||||
topNavLists: props.plugins.filter((item) => item.align === 'top'),
|
||||
bottomNavLists: props.plugins.filter((item) => item.align === 'bottom')
|
||||
const currentComponent = computed(() => {
|
||||
const isExistedComponent = [...state.topNavLists, ...state.bottomNavLists].some(
|
||||
(item) => item.id === props.renderPanel
|
||||
)
|
||||
return isExistedComponent ? components[props.renderPanel] : null
|
||||
})
|
||||
|
||||
const clickMenu = ({ item, index }) => {
|
||||
|
|
@ -127,14 +193,14 @@ export default {
|
|||
result &&
|
||||
emit('click', {
|
||||
item,
|
||||
navLists: item.align === 'top' ? state.topNavLists[index] : state.bottomNavLists[index]
|
||||
navLists: item.align === 'leftTop' ? state.topNavLists[index] : state.bottomNavLists[index]
|
||||
})
|
||||
|
||||
pluginRef.value?.[lastPlugin.confirm](confirmCallback)
|
||||
} else {
|
||||
emit('click', {
|
||||
item,
|
||||
navLists: item.align === 'top' ? state.topNavLists[index] : state.bottomNavLists[index]
|
||||
navLists: item.align === 'leftTop' ? state.topNavLists[index] : state.bottomNavLists[index]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -154,35 +220,43 @@ export default {
|
|||
useLayout().closePlugin(true)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param index 组件索引
|
||||
* @param id 组件 ID, 类似于 'engine.plugins.*'
|
||||
* @param from 组件来源
|
||||
*/
|
||||
const switchAlign = (index, id, from) => {
|
||||
if (from === PLUGIN_POSITION.leftTop) {
|
||||
state.topNavLists.splice(index, 1)
|
||||
} else {
|
||||
state.bottomNavLists.splice(index, 1)
|
||||
}
|
||||
emit('changeLeftAlign', id)
|
||||
|
||||
if (!isSameSide(index, 0)) close()
|
||||
dragPluginLayout(from, PLUGIN_POSITION.rightTop, index, 0)
|
||||
}
|
||||
|
||||
const fixPanel = (pluginName) => {
|
||||
pluginState.fixedPanels = pluginState.fixedPanels?.includes(pluginName)
|
||||
? pluginState.fixedPanels?.filter((item) => item !== pluginName)
|
||||
: [...pluginState.fixedPanels, pluginName]
|
||||
|
||||
try {
|
||||
localStorage.setItem(STORAGE_KEY_FIXED_PANELS, JSON.stringify(pluginState.fixedPanels))
|
||||
} catch (error) {
|
||||
message({ message: `'存储固定面板数据失败:'${error}`, status: 'error' })
|
||||
}
|
||||
changeLeftFixedPanels(pluginName)
|
||||
}
|
||||
|
||||
const restoreFixedPanels = () => {
|
||||
try {
|
||||
const storedPanels = localStorage.getItem(STORAGE_KEY_FIXED_PANELS)
|
||||
pluginState.fixedPanels = storedPanels ? JSON.parse(storedPanels) : []
|
||||
|
||||
if (!Array.isArray(pluginState.fixedPanels)) {
|
||||
pluginState.fixedPanels = []
|
||||
}
|
||||
} catch (error) {
|
||||
message({ message: `'读取固定面板数据失败:'${error}`, status: 'error' })
|
||||
pluginState.fixedPanels = []
|
||||
}
|
||||
//监听拖拽结束事件
|
||||
const onEnd = (e) => {
|
||||
if (!isSameSide(e.from.id, e.to.id)) close()
|
||||
dragPluginLayout(e.from.id, e.to.id, e.oldIndex, e.newIndex)
|
||||
}
|
||||
|
||||
restoreFixedPanels()
|
||||
|
||||
return {
|
||||
leftFixedPanelsStorage,
|
||||
currentComponent,
|
||||
changeAlign,
|
||||
rightMenu,
|
||||
PLUGIN_POSITION,
|
||||
showContextMenu,
|
||||
switchAlign,
|
||||
getPluginShown,
|
||||
onEnd,
|
||||
state,
|
||||
clickMenu,
|
||||
pluginRef,
|
||||
|
|
@ -190,6 +264,7 @@ export default {
|
|||
fixPanel,
|
||||
pluginState,
|
||||
components,
|
||||
getMoveDragBarState,
|
||||
iconComponents
|
||||
}
|
||||
}
|
||||
|
|
@ -198,7 +273,7 @@ export default {
|
|||
|
||||
<style lang="less" scoped>
|
||||
#tiny-engine-left-panel {
|
||||
width: var(--base-left-panel-width);
|
||||
width: auto !important;
|
||||
height: calc(100vh - var(--base-top-panel-height));
|
||||
border-right: 1px solid var(--te-layout-common-border-color);
|
||||
background: var(--te-layout-common-bg-color);
|
||||
|
|
@ -209,10 +284,6 @@ export default {
|
|||
left: var(--base-nav-panel-width);
|
||||
z-index: 999;
|
||||
|
||||
&[class~='engine.plugins.i18n'] {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
&.is-fixed {
|
||||
position: relative;
|
||||
top: 0;
|
||||
|
|
@ -324,4 +395,9 @@ export default {
|
|||
:deep(.svg-icon.icon-plugin-icon-plugin-help) {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.not-selected {
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,130 +1,328 @@
|
|||
<!-- 右侧插件栏 -->
|
||||
<template>
|
||||
<div id="tiny-right-panel">
|
||||
<tiny-tabs v-model="layoutState.settings.render">
|
||||
<tiny-tab-item v-for="(setting, index) in settings" :key="index" :title="setting.title" :name="setting.name">
|
||||
<component :is="setting.entry"></component>
|
||||
<div v-show="activating" class="active"></div>
|
||||
</tiny-tab-item>
|
||||
</tiny-tabs>
|
||||
<div v-if="layoutState.settings.render === 'style'" class="tabs-setting">
|
||||
<tiny-tooltip effect="light" :content="isCollapsed ? '展开' : '折叠'" placement="top" :visible-arrow="false">
|
||||
<template #default> <svg-icon :name="settingIcon" @click="isCollapsed = !isCollapsed"></svg-icon> </template>
|
||||
</tiny-tooltip>
|
||||
<div :class="{ 'not-selected': getMoveDragBarState() }">
|
||||
<div
|
||||
v-show="renderPanel && components[renderPanel]"
|
||||
id="tiny-engine-right-panel"
|
||||
:class="[renderPanel, { 'is-fixed': rightFixedPanelsStorage.includes(renderPanel) }]"
|
||||
>
|
||||
<div class="right-panel-wrap">
|
||||
<component
|
||||
:is="currentComponent"
|
||||
:fixed-panels="rightFixedPanelsStorage"
|
||||
@close="close"
|
||||
@fixPanel="fixPanel"
|
||||
></component>
|
||||
<div v-show="activating" class="active2" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="tiny-engine-nav-panel">
|
||||
<vue-draggable-next id="rightTop" v-model="settingPlugins" class="nav-panel-lists" group="plugins" @end="onEnd">
|
||||
<div
|
||||
v-for="(item, index) in settingPlugins"
|
||||
:key="index"
|
||||
:class="['list-item', { 'first-item': item === settingPlugins[0], active: item.id === renderPanel }]"
|
||||
:title="item.title"
|
||||
@click="clickMenu({ item, index })"
|
||||
@contextmenu.prevent="showContextMenu($event, true, item, index, PLUGIN_POSITION.rightTop)"
|
||||
>
|
||||
<span class="item-icon" v-if="getPluginShown(item.id)">
|
||||
<svg-icon v-if="iconComponents[item.id]" :name="iconComponents[item.id]" class="panel-icon"></svg-icon>
|
||||
<component v-else :is="iconComponents[item.id]" class="panel-icon"></component>
|
||||
</span>
|
||||
</div>
|
||||
<div style="flex: 1" class="list-item" @contextmenu.prevent="showContextMenu($event, false)"></div>
|
||||
</vue-draggable-next>
|
||||
</div>
|
||||
|
||||
<plugin-right-menu
|
||||
ref="rightMenu"
|
||||
:list="settingPlugins"
|
||||
:align="PLUGIN_POSITION.rightTop"
|
||||
@switchAlign="switchAlign"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { computed, provide, ref } from 'vue'
|
||||
import { Tabs, TabItem, Tooltip } from '@opentiny/vue'
|
||||
import { computed, ref, watch, toRefs } from 'vue'
|
||||
import { Tabs, TabItem } from '@opentiny/vue'
|
||||
import { useLayout } from '@opentiny/tiny-engine-meta-register'
|
||||
import { VueDraggableNext } from 'vue-draggable-next'
|
||||
import { PluginRightMenu } from '@opentiny/tiny-engine-common'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
PluginRightMenu,
|
||||
TinyTabs: Tabs,
|
||||
TinyTabItem: TabItem,
|
||||
TinyTooltip: Tooltip
|
||||
VueDraggableNext
|
||||
},
|
||||
props: {
|
||||
settings: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
renderPanel: {
|
||||
type: String
|
||||
},
|
||||
pluginList: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
setup() {
|
||||
const { layoutState } = useLayout()
|
||||
const activating = computed(() => layoutState.settings.activating)
|
||||
const showMask = ref(true)
|
||||
const isCollapsed = ref(false)
|
||||
const settingIcon = computed(() => (isCollapsed.value ? 'collapse_all' : 'expand_all'))
|
||||
setup(props, { emit }) {
|
||||
const components = {}
|
||||
const iconComponents = {}
|
||||
|
||||
provide('isCollapsed', isCollapsed)
|
||||
const {
|
||||
getPluginsByPosition,
|
||||
getPluginById,
|
||||
PLUGIN_POSITION,
|
||||
rightFixedPanelsStorage,
|
||||
changeRightFixedPanels,
|
||||
dragPluginLayout,
|
||||
isSameSide,
|
||||
getPluginShown,
|
||||
getMoveDragBarState,
|
||||
layoutState: { settings: settingsState }
|
||||
} = useLayout()
|
||||
|
||||
const rightMenu = ref(null)
|
||||
const { renderPanel } = toRefs(props)
|
||||
const showContextMenu = (event, type, item, index, align) => {
|
||||
if (!type) {
|
||||
rightMenu.value.showContextMenu(event.clientX, event.clientY, type)
|
||||
} else {
|
||||
rightMenu.value.showContextMenu(event.clientX, event.clientY, type, item, index, align)
|
||||
}
|
||||
}
|
||||
|
||||
props.pluginList.forEach(({ id, entry, icon }) => {
|
||||
components[id] = entry
|
||||
iconComponents[id] = icon
|
||||
})
|
||||
|
||||
const settingPlugins = ref(getPluginsByPosition(PLUGIN_POSITION.rightTop, props.pluginList))
|
||||
|
||||
const currentComponent = computed(() => {
|
||||
const isExistedComponent = settingPlugins.value.some((item) => item.id === renderPanel.value)
|
||||
return isExistedComponent ? components[renderPanel.value] : null
|
||||
})
|
||||
|
||||
const close = () => {
|
||||
useLayout().closeSetting(true)
|
||||
}
|
||||
|
||||
const switchAlign = (index, id, from) => {
|
||||
settingPlugins.value.splice(index, 1)
|
||||
emit('changeRightAlign', id)
|
||||
if (!isSameSide(index, 0)) close()
|
||||
dragPluginLayout(from, PLUGIN_POSITION.leftTop, index, 0)
|
||||
}
|
||||
|
||||
const changeAlign = (pluginId) => {
|
||||
const item = getPluginById(props.pluginList, pluginId)
|
||||
const existingItemIndex = settingPlugins.value.findIndex((plugin) => plugin.id === item.id)
|
||||
|
||||
if (existingItemIndex !== -1) {
|
||||
settingPlugins.value.splice(existingItemIndex, 1)
|
||||
}
|
||||
settingPlugins.value.unshift(item)
|
||||
}
|
||||
|
||||
const setRender = (curId) => {
|
||||
settingsState.render = curId
|
||||
}
|
||||
|
||||
//点击右侧菜单icon按钮
|
||||
const clickMenu = ({ item }) => {
|
||||
if (settingsState.render == item.id) {
|
||||
useLayout().closeSetting(true)
|
||||
return
|
||||
}
|
||||
setRender(item.id)
|
||||
}
|
||||
|
||||
watch(renderPanel, (n) => {
|
||||
setRender(n)
|
||||
})
|
||||
|
||||
//切换面板状态
|
||||
const fixPanel = (pluginName) => {
|
||||
changeRightFixedPanels(pluginName)
|
||||
}
|
||||
|
||||
//监听拖拽结束事件
|
||||
const onEnd = (e) => {
|
||||
if (!isSameSide(e.from.id, e.to.id)) close()
|
||||
dragPluginLayout(e.from.id, e.to.id, e.oldIndex, e.newIndex)
|
||||
}
|
||||
|
||||
const activating = computed(() => settingsState.activating)
|
||||
const showMask = ref(true)
|
||||
|
||||
return {
|
||||
currentComponent,
|
||||
changeAlign,
|
||||
showMask,
|
||||
isCollapsed,
|
||||
activating,
|
||||
settingIcon,
|
||||
layoutState
|
||||
settingsState,
|
||||
settingPlugins,
|
||||
components,
|
||||
iconComponents,
|
||||
clickMenu,
|
||||
close,
|
||||
fixPanel,
|
||||
rightFixedPanelsStorage,
|
||||
onEnd,
|
||||
showContextMenu,
|
||||
PLUGIN_POSITION,
|
||||
getPluginShown,
|
||||
switchAlign,
|
||||
rightMenu,
|
||||
getMoveDragBarState
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
#tiny-right-panel {
|
||||
width: var(--base-right-panel-width);
|
||||
height: 100%;
|
||||
transition: 0.3s linear;
|
||||
position: relative;
|
||||
#tiny-engine-right-panel {
|
||||
height: calc(100vh - var(--base-top-panel-height));
|
||||
border-left: 1px solid var(--te-layout-common-border-color);
|
||||
padding-top: 12px;
|
||||
background-color: var(--te-layout-common-bg-color);
|
||||
background: var(--ti-lowcode-common-component-bg);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: absolute;
|
||||
top: var(--base-top-panel-height);
|
||||
right: var(--base-nav-panel-width);
|
||||
z-index: 999;
|
||||
|
||||
.tabs-setting {
|
||||
position: absolute;
|
||||
top: 9px;
|
||||
right: 18px;
|
||||
line-height: 26px;
|
||||
color: var(--te-layout-common-icon-color);
|
||||
cursor: pointer;
|
||||
&.I18n {
|
||||
width: auto;
|
||||
}
|
||||
.tiny-tabs {
|
||||
|
||||
&.is-fixed {
|
||||
position: relative;
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.right-panel-wrap {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
:deep(.tiny-tabs) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.tiny-tabs__header .tiny-tabs__nav {
|
||||
width: 60%;
|
||||
background-color: var(--te-layout-common-bg-color);
|
||||
position: relative;
|
||||
:deep(.tiny-tabs__nav.is-show-active-bar) .tiny-tabs__item {
|
||||
margin-right: 0;
|
||||
}
|
||||
.tiny-tabs__nav-scroll {
|
||||
margin-left: 12px;
|
||||
.tiny-tabs__active-bar {
|
||||
height: 3px;
|
||||
background-color: var(--te-layout-common-text-color-active);
|
||||
}
|
||||
}
|
||||
|
||||
.tiny-tabs__content {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.tiny-tabs__nav.is-show-active-bar .tiny-tabs__item {
|
||||
margin-right: 8px;
|
||||
}
|
||||
.tiny-tabs__item {
|
||||
flex: 1;
|
||||
background-color: var(--te-layout-common-bg-color);
|
||||
color: var(--te-layout-common-text-color-secondary);
|
||||
margin-right: 5px;
|
||||
&:hover {
|
||||
color: var(--te-layout-common-text-color-hover);
|
||||
}
|
||||
&.is-active {
|
||||
color: var(--te-layout-common-text-color-active);
|
||||
border: none;
|
||||
}
|
||||
|
||||
.tiny-tabs__item__title {
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.tiny-tabs__nav-wrap-not-separator::after {
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.tiny-collapse-item__content) {
|
||||
padding: 0 8px 12px 12px; // 这里的bottom为4px + 内部行元素与底部的距离为8px = 12px
|
||||
}
|
||||
}
|
||||
|
||||
.active {
|
||||
#tiny-engine-nav-panel {
|
||||
display: none;
|
||||
width: var(--base-nav-panel-width);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
background: var(--te-layout-common-bg-color);
|
||||
box-sizing: border-box;
|
||||
z-index: 1000;
|
||||
border-left: 1px solid var(--te-layout-common-border-color);
|
||||
|
||||
&.completed {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.nav-panel-lists {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
|
||||
&.bottom {
|
||||
flex: 1;
|
||||
padding-bottom: 28px;
|
||||
}
|
||||
|
||||
.list-item {
|
||||
width: 100%;
|
||||
padding: 3px 0;
|
||||
|
||||
&:first-child {
|
||||
padding-top: 12px;
|
||||
}
|
||||
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
&:hover,
|
||||
&.active {
|
||||
.item-icon {
|
||||
background: var(--te-layout-common-bg-color-hover);
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
position: relative;
|
||||
|
||||
.item-icon {
|
||||
color: var(--te-layout-common-text-color-secondary-checked);
|
||||
}
|
||||
}
|
||||
|
||||
&.prev {
|
||||
border-bottom-color: var(--te-layout-common-border-color);
|
||||
}
|
||||
}
|
||||
|
||||
.item-icon {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: var(--te-layout-common-text-color);
|
||||
font-size: 22px;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
|
||||
svg {
|
||||
font-size: 18px;
|
||||
}
|
||||
.public-icon {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.panel-svg) {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
:deep(.svg-icon.icon-plugin-icon-plugin-help) {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.not-selected {
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
:deep(.svg-icon.icon-plugin-icon-plugin-help) {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
//高亮显示动画
|
||||
.active2 {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
|
|
@ -135,10 +333,10 @@ export default {
|
|||
|
||||
@keyframes glow {
|
||||
0% {
|
||||
box-shadow: inset 0px 0px 4px var(--te-layout-setting-bg-color-hover);
|
||||
box-shadow: inset 0px 0px 4px var(--ti-lowcode-canvas-handle-hover-bg);
|
||||
}
|
||||
100% {
|
||||
box-shadow: inset 0px 0px 14px var(--te-layout-setting-bg-color-hover);
|
||||
box-shadow: inset 0px 0px 14px var(--ti-lowcode-canvas-handle-hover-bg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -6,8 +6,12 @@
|
|||
<div class="tiny-engine-left-wrap">
|
||||
<div class="tiny-engine-content-wrap">
|
||||
<design-plugins
|
||||
v-if="leftMenuShownStorage"
|
||||
ref="left"
|
||||
:plugins="registry.plugins"
|
||||
:plugin-list="pluginList"
|
||||
:render-panel="plugins.render"
|
||||
@changeLeftAlign="changeLeftAlign"
|
||||
@click="toggleNav"
|
||||
></design-plugins>
|
||||
<component :is="registry.canvas.entry"></component>
|
||||
|
|
@ -15,9 +19,13 @@
|
|||
</div>
|
||||
<div class="tiny-engine-right-wrap">
|
||||
<design-settings
|
||||
:settings="registry.settings"
|
||||
v-show="layoutState.settings.showDesignSettings"
|
||||
v-if="rightMenuShownStorage"
|
||||
ref="right"
|
||||
:settings="registry.settings"
|
||||
:render-panel="settings.render"
|
||||
:plugin-list="pluginList"
|
||||
v-show="layoutState.settings.showDesignSettings"
|
||||
@changeRightAlign="changeRightAlign"
|
||||
></design-settings>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -27,10 +35,12 @@
|
|||
|
||||
<script>
|
||||
import { useLayout, getMergeRegistry } from '@opentiny/tiny-engine-meta-register'
|
||||
import { constants } from '@opentiny/tiny-engine-utils'
|
||||
import DesignToolbars from './DesignToolbars.vue'
|
||||
import DesignPlugins from './DesignPlugins.vue'
|
||||
import DesignSettings from './DesignSettings.vue'
|
||||
import meta from '../meta'
|
||||
import { ref } from 'vue'
|
||||
|
||||
export default {
|
||||
name: 'TinyLowCode',
|
||||
|
|
@ -49,24 +59,82 @@ export default {
|
|||
type: Object
|
||||
}
|
||||
},
|
||||
setup() {
|
||||
setup(props) {
|
||||
const layoutRegistry = getMergeRegistry(meta.type)
|
||||
const configProvider = layoutRegistry.options.configProvider
|
||||
const configProviderDesign = layoutRegistry.options.configProviderDesign
|
||||
|
||||
const { layoutState } = useLayout()
|
||||
const { plugins } = layoutState
|
||||
const { layoutState, leftMenuShownStorage, rightMenuShownStorage, initPluginStorageReactive } = useLayout()
|
||||
const { plugins, settings } = layoutState
|
||||
|
||||
const toggleNav = ({ item }) => {
|
||||
if (!item.id) return
|
||||
plugins.render = plugins.render === item.id ? null : item.id
|
||||
}
|
||||
|
||||
const left = ref(null)
|
||||
const right = ref(null)
|
||||
|
||||
const changeLeftAlign = (pluginId) => {
|
||||
right.value?.changeAlign(pluginId)
|
||||
}
|
||||
const changeRightAlign = (pluginId) => {
|
||||
left.value?.changeAlign(pluginId)
|
||||
}
|
||||
|
||||
// 合并插件和设置列表
|
||||
const pluginList = [...props.registry.plugins, ...props.registry.settings]
|
||||
|
||||
// 收集插件的 align 信息
|
||||
const alignGroups = {}
|
||||
const plugin = {}
|
||||
|
||||
const { PLUGIN_DEFAULT_WIDTH } = constants
|
||||
|
||||
pluginList.forEach((item) => {
|
||||
if (item.id) {
|
||||
const align = item?.align || 'leftTop'
|
||||
|
||||
// 初始化 alignGroups[align]
|
||||
if (!alignGroups[align]) {
|
||||
alignGroups[align] = []
|
||||
}
|
||||
|
||||
// 将 item.id 推入对应的 alignGroups
|
||||
alignGroups[align].push(item.id)
|
||||
|
||||
// 为每个插件分配 index 和相关属性
|
||||
const index = alignGroups[align].indexOf(item.id)
|
||||
const widthResizable = item?.widthResizable ?? false
|
||||
|
||||
plugin[item.id] = {
|
||||
width: item?.width || PLUGIN_DEFAULT_WIDTH,
|
||||
align: align,
|
||||
index: index,
|
||||
isShow: true,
|
||||
entry: item.entry,
|
||||
id: item.id,
|
||||
icon: item.icon,
|
||||
widthResizable
|
||||
}
|
||||
}
|
||||
})
|
||||
localStorage.setItem('plugin', JSON.stringify(plugin))
|
||||
initPluginStorageReactive(plugin)
|
||||
|
||||
return {
|
||||
left,
|
||||
right,
|
||||
changeLeftAlign,
|
||||
changeRightAlign,
|
||||
leftMenuShownStorage,
|
||||
rightMenuShownStorage,
|
||||
pluginList,
|
||||
layoutRegistry,
|
||||
configProvider,
|
||||
configProviderDesign,
|
||||
plugins,
|
||||
settings,
|
||||
toggleNav,
|
||||
layoutState
|
||||
}
|
||||
|
|
@ -98,7 +166,8 @@ export default {
|
|||
}
|
||||
}
|
||||
.tiny-engine-right-wrap {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
z-index: 4;
|
||||
}
|
||||
:deep(.monaco-editor .suggest-widget) {
|
||||
|
|
|
|||
|
|
@ -11,12 +11,27 @@
|
|||
*/
|
||||
|
||||
import { reactive, nextTick } from 'vue'
|
||||
import { useStorage } from '@vueuse/core'
|
||||
import { constants } from '@opentiny/tiny-engine-utils'
|
||||
import { META_APP as PLUGIN_NAME, getMetaApi } from '@opentiny/tiny-engine-meta-register'
|
||||
|
||||
const { PAGE_STATUS } = constants
|
||||
const { PAGE_STATUS, STORAGE_KEY_LEFT_FIXED_PANELS, STORAGE_KEY_RIGHT_FIXED_PANELS, PLUGIN_DEFAULT_WIDTH } = constants
|
||||
|
||||
const PLUGIN_POSITION = {
|
||||
leftTop: 'leftTop',
|
||||
leftBottom: 'leftBottom',
|
||||
independence: 'independence',
|
||||
rightTop: 'rightTop',
|
||||
rightBottom: 'rightBottom',
|
||||
fixed: 'fixed'
|
||||
}
|
||||
|
||||
const pluginState = reactive({
|
||||
pluginEvent: 'all'
|
||||
})
|
||||
|
||||
const layoutState = reactive({
|
||||
isMoveDragBar: false,
|
||||
dimension: {
|
||||
deviceType: 'desktop',
|
||||
width: '',
|
||||
|
|
@ -26,12 +41,17 @@ const layoutState = reactive({
|
|||
height: '100%'
|
||||
},
|
||||
plugins: {
|
||||
isShow: true,
|
||||
fixedPanels: [PLUGIN_NAME.Materials],
|
||||
render: null,
|
||||
pluginEvent: 'all'
|
||||
render: PLUGIN_NAME.Materials,
|
||||
pluginEvent: 'all',
|
||||
activating: false, // 右侧面版激活提示状态
|
||||
showDesignSettings: true
|
||||
},
|
||||
settings: {
|
||||
render: 'props',
|
||||
isShow: true,
|
||||
fixedPanels: [PLUGIN_NAME.Props, PLUGIN_NAME.Styles, PLUGIN_NAME.Event],
|
||||
render: PLUGIN_NAME.Props,
|
||||
api: null,
|
||||
activating: false, // 右侧面版激活提示状态
|
||||
showDesignSettings: true
|
||||
|
|
@ -41,10 +61,44 @@ const layoutState = reactive({
|
|||
},
|
||||
pageStatus: ''
|
||||
})
|
||||
const getMoveDragBarState = () => {
|
||||
return layoutState.isMoveDragBar
|
||||
}
|
||||
const changeMoveDragBarState = (state) => {
|
||||
layoutState.isMoveDragBar = state
|
||||
}
|
||||
const leftMenuShownStorage = useStorage('leftMenuShown', layoutState.plugins.isShow)
|
||||
const rightMenuShownStorage = useStorage('rightMenuShown', layoutState.settings.isShow)
|
||||
const changeMenuShown = (menuName) => {
|
||||
switch (menuName) {
|
||||
case 'left': {
|
||||
leftMenuShownStorage.value = !leftMenuShownStorage.value
|
||||
break
|
||||
}
|
||||
case 'right': {
|
||||
rightMenuShownStorage.value = !rightMenuShownStorage.value
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
const leftFixedPanelsStorage = useStorage(STORAGE_KEY_LEFT_FIXED_PANELS, layoutState.plugins.fixedPanels)
|
||||
const rightFixedPanelsStorage = useStorage(STORAGE_KEY_RIGHT_FIXED_PANELS, layoutState.settings.fixedPanels)
|
||||
|
||||
const changeLeftFixedPanels = (pluginName) => {
|
||||
leftFixedPanelsStorage.value = leftFixedPanelsStorage.value?.includes(pluginName)
|
||||
? leftFixedPanelsStorage.value?.filter((item) => item !== pluginName)
|
||||
: [...leftFixedPanelsStorage.value, pluginName]
|
||||
}
|
||||
const changeRightFixedPanels = (pluginName) => {
|
||||
rightFixedPanelsStorage.value = rightFixedPanelsStorage.value?.includes(pluginName)
|
||||
? rightFixedPanelsStorage.value?.filter((item) => item !== pluginName)
|
||||
: [...rightFixedPanelsStorage.value, pluginName]
|
||||
}
|
||||
|
||||
const getScale = () => layoutState.dimension.scale
|
||||
|
||||
const getPluginState = () => layoutState.plugins
|
||||
const getSettingState = () => layoutState.settings
|
||||
|
||||
const getDimension = () => layoutState.dimension
|
||||
|
||||
|
|
@ -66,6 +120,22 @@ const activeSetting = (name) => {
|
|||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 两侧面板的固定状态
|
||||
*/
|
||||
const getFixedPanelsStatus = () => {
|
||||
const leftPanelFixed = leftFixedPanelsStorage.value.includes(layoutState.plugins.render)
|
||||
const rightPanelFixed = rightFixedPanelsStorage.value.includes(layoutState.settings.render)
|
||||
return { leftPanelFixed, rightPanelFixed }
|
||||
}
|
||||
|
||||
const closeSetting = (forceClose) => {
|
||||
const { settings } = layoutState
|
||||
if (!settings.fixedPanels.includes(settings.render) || forceClose) {
|
||||
settings.render = null
|
||||
}
|
||||
}
|
||||
|
||||
// 激活plugin面板并返回当前插件注册的Api
|
||||
const activePlugin = (name, noActiveRender) => {
|
||||
const { plugins } = layoutState
|
||||
|
|
@ -90,16 +160,191 @@ const closePlugin = (forceClose) => {
|
|||
const isEmptyPage = () => layoutState.pageStatus?.state === PAGE_STATUS.Empty
|
||||
|
||||
export default () => {
|
||||
let plugin = []
|
||||
|
||||
try {
|
||||
const storedPlugin = localStorage.getItem('plugin')
|
||||
if (storedPlugin) {
|
||||
plugin = JSON.parse(storedPlugin)
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(error)
|
||||
}
|
||||
|
||||
// 如果 plugin 不是一个数组,则将其重置为默认值
|
||||
if (!Array.isArray(plugin)) {
|
||||
plugin = []
|
||||
}
|
||||
|
||||
const pluginStorageReactive = useStorage('plugin', plugin)
|
||||
|
||||
// 获取插件宽度
|
||||
const getPluginWidth = (name) => pluginStorageReactive.value[name]?.width || PLUGIN_DEFAULT_WIDTH
|
||||
|
||||
// 修改插件宽度
|
||||
const changePluginWidth = (name, width, offset) => {
|
||||
if (Object.prototype.hasOwnProperty.call(pluginStorageReactive.value, name)) {
|
||||
pluginStorageReactive.value[name].width = width
|
||||
pluginStorageReactive.value[name].offset = offset
|
||||
} else {
|
||||
pluginStorageReactive.value[name] = {
|
||||
width
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取插件布局
|
||||
const getPluginByLayout = (name) => pluginStorageReactive.value[name]?.align || 'leftTop'
|
||||
|
||||
// 获取某个布局(左上/左下/右上/右下)的插件名称列表
|
||||
const getPluginsByLayout = (layout = 'all') => {
|
||||
// 筛选出符合布局条件的插件名称
|
||||
const pluginNames = Object.keys(pluginStorageReactive.value).filter(
|
||||
(key) => pluginStorageReactive.value[key].align === layout || layout === 'all'
|
||||
)
|
||||
|
||||
pluginNames.sort((a, b) => pluginStorageReactive.value[a].index - pluginStorageReactive.value[b].index)
|
||||
|
||||
return pluginNames
|
||||
}
|
||||
|
||||
const getPluginById = (pluginList, pluginId) => {
|
||||
return pluginList.find((item) => item.id === pluginId)
|
||||
}
|
||||
|
||||
const getPluginsByPosition = (position, pluginList) => {
|
||||
return getPluginsByLayout(position).map((pluginId) => getPluginById(pluginList, pluginId))
|
||||
}
|
||||
|
||||
// 修改某个插件的布局
|
||||
const changePluginLayout = (name, layout) => {
|
||||
if (pluginStorageReactive.value[name]) {
|
||||
pluginStorageReactive.value[name].align = layout
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 拖拽后改变插件位置
|
||||
* @param {*} from 插件的起始位置
|
||||
* @param {*} to 插件的结束位置
|
||||
* @param {*} oldIndex 插件的起始索引
|
||||
* @param {*} newIndex 插件的结束索引
|
||||
* @returns
|
||||
*/
|
||||
const dragPluginLayout = (from, to, oldIndex, newIndex) => {
|
||||
if (from === to && oldIndex === newIndex) return
|
||||
|
||||
const items = Object.values(pluginStorageReactive.value)
|
||||
// 记录拖拽项
|
||||
const movedItem = items.find((item) => item.align === from && item.index === oldIndex)
|
||||
|
||||
// 同一列表中的拖拽
|
||||
if (from === to) {
|
||||
if (oldIndex < newIndex) {
|
||||
//往后移动
|
||||
items.forEach((item) => {
|
||||
if (item !== movedItem && item.align === from && item.index > oldIndex && item.index <= newIndex) {
|
||||
item.index -= 1
|
||||
}
|
||||
})
|
||||
} else {
|
||||
//往前移动
|
||||
items.forEach((item) => {
|
||||
if (item !== movedItem && item.align === from && item.index >= newIndex && item.index < oldIndex) {
|
||||
item.index += 1
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// 跨列表拖拽
|
||||
items.forEach((item) => {
|
||||
if (item !== movedItem && item.align === from && item.index > oldIndex) {
|
||||
item.index -= 1
|
||||
}
|
||||
if (item !== movedItem && item.align === to && item.index >= newIndex) {
|
||||
item.index += 1
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 更新拖拽项的位置
|
||||
if (movedItem) {
|
||||
movedItem.align = to
|
||||
movedItem.index = newIndex
|
||||
}
|
||||
}
|
||||
|
||||
//判断是否在同一侧
|
||||
const isSameSide = (from, to) => {
|
||||
const leftSide = [PLUGIN_POSITION.leftTop, PLUGIN_POSITION.leftBottom]
|
||||
const rightSide = [PLUGIN_POSITION.rightTop, PLUGIN_POSITION.rightBottom]
|
||||
|
||||
const isLeft = leftSide.includes(from) && leftSide.includes(to)
|
||||
const isRight = rightSide.includes(from) && rightSide.includes(to)
|
||||
|
||||
return isLeft || isRight
|
||||
}
|
||||
|
||||
//获取插件显示状态
|
||||
const getPluginShown = (name) => pluginStorageReactive.value[name]?.isShow
|
||||
|
||||
//修改插件显示状态
|
||||
const changePluginShown = (name) => {
|
||||
if (!pluginStorageReactive.value[name]) {
|
||||
pluginStorageReactive.value[name] = { isShow: true }
|
||||
}
|
||||
pluginStorageReactive.value[name].isShow = !pluginStorageReactive.value[name].isShow
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回面板是否宽度可调
|
||||
* @param {string} name 插件名称
|
||||
* @returns
|
||||
*/
|
||||
const isPanelWidthResizable = (name) => pluginStorageReactive.value[name]?.widthResizable
|
||||
|
||||
const initPluginStorageReactive = (pluginList) => {
|
||||
if (Object.keys(pluginStorageReactive.value).length) return
|
||||
pluginStorageReactive.value = pluginList
|
||||
}
|
||||
|
||||
return {
|
||||
isPanelWidthResizable,
|
||||
getFixedPanelsStatus,
|
||||
initPluginStorageReactive,
|
||||
PLUGIN_NAME,
|
||||
PLUGIN_POSITION,
|
||||
activeSetting,
|
||||
closeSetting,
|
||||
activePlugin,
|
||||
closePlugin,
|
||||
layoutState,
|
||||
getScale,
|
||||
setDimension,
|
||||
getDimension,
|
||||
getPluginById,
|
||||
pluginState,
|
||||
getPluginState,
|
||||
isEmptyPage
|
||||
getSettingState,
|
||||
isEmptyPage,
|
||||
getPluginWidth,
|
||||
changePluginWidth,
|
||||
leftFixedPanelsStorage,
|
||||
rightFixedPanelsStorage,
|
||||
leftMenuShownStorage,
|
||||
rightMenuShownStorage,
|
||||
changeLeftFixedPanels,
|
||||
changeRightFixedPanels,
|
||||
getPluginsByLayout,
|
||||
changePluginLayout,
|
||||
getPluginByLayout,
|
||||
dragPluginLayout,
|
||||
isSameSide,
|
||||
getPluginShown,
|
||||
changePluginShown,
|
||||
changeMenuShown,
|
||||
getMoveDragBarState,
|
||||
changeMoveDragBarState,
|
||||
getPluginsByPosition
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,6 @@ export default {
|
|||
id: 'engine.plugins.blockmanage',
|
||||
title: '区块管理',
|
||||
type: 'plugins',
|
||||
align: 'top',
|
||||
align: 'leftTop',
|
||||
icon: 'plugin-icon-symbol'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
v-if="isOpen"
|
||||
class="plugin-block-setting"
|
||||
title="区块设置"
|
||||
:align="align"
|
||||
:fixed-name="PLUGIN_NAME.BlockManage"
|
||||
@mouseleave="onMouseLeave"
|
||||
@click="handleClick"
|
||||
>
|
||||
|
|
@ -86,7 +88,14 @@
|
|||
<script lang="jsx">
|
||||
import { reactive, ref, watch, watchEffect, computed } from 'vue'
|
||||
import { Button as TinyButton, Collapse as TinyCollapse, CollapseItem as TinyCollapseItem } from '@opentiny/vue'
|
||||
import { useModal, getMergeMeta, useBlock, getMetaApi, META_SERVICE } from '@opentiny/tiny-engine-meta-register'
|
||||
import {
|
||||
useLayout,
|
||||
useModal,
|
||||
getMergeMeta,
|
||||
useBlock,
|
||||
getMetaApi,
|
||||
META_SERVICE
|
||||
} from '@opentiny/tiny-engine-meta-register'
|
||||
import { BlockHistoryList, PluginSetting, CloseIcon, SvgButton, ButtonGroup } from '@opentiny/tiny-engine-common'
|
||||
import { previewBlock } from '@opentiny/tiny-engine-common/js/preview'
|
||||
import { LifeCycles } from '@opentiny/tiny-engine-common'
|
||||
|
|
@ -152,6 +161,9 @@ export default {
|
|||
})
|
||||
const blockConfigForm = ref(null)
|
||||
|
||||
const { PLUGIN_NAME, getPluginByLayout } = useLayout()
|
||||
const align = computed(() => getPluginByLayout(PLUGIN_NAME.BlockManage))
|
||||
|
||||
const state = reactive({
|
||||
activeName: ['base', 'attribute', 'event', 'lifeCycle', 'history'],
|
||||
backupList: [],
|
||||
|
|
@ -277,6 +289,8 @@ export default {
|
|||
}
|
||||
|
||||
return {
|
||||
align,
|
||||
PLUGIN_NAME,
|
||||
state,
|
||||
isOpen,
|
||||
showDeployBlockDialog,
|
||||
|
|
|
|||
|
|
@ -1,129 +1,128 @@
|
|||
<template>
|
||||
<div class="plugin-block">
|
||||
<plugin-panel
|
||||
class="block-manage"
|
||||
title="区块管理"
|
||||
:docsUrl="docsUrl"
|
||||
:isShowDocsIcon="true"
|
||||
:isCloseLeft="false"
|
||||
@close="closePanel"
|
||||
>
|
||||
<template #header>
|
||||
<svg-button name="add-page" placement="bottom" tips="新建区块" @click="openBlockAdd"></svg-button>
|
||||
</template>
|
||||
<template #content>
|
||||
<div class="app-manage-type">
|
||||
<tiny-select
|
||||
ref="groupSelect"
|
||||
v-model="state.categoryId"
|
||||
popper-class="block-popper"
|
||||
:placeholder="groupLabels.selectPlaceholder"
|
||||
filterable
|
||||
:filter-method="categoryFilter"
|
||||
clearable
|
||||
top-create
|
||||
@top-create-click="createCategory"
|
||||
@change="changeCategory"
|
||||
@clear="changeCategory"
|
||||
@visible-change="handleSelectVisibleChange"
|
||||
class="search-select"
|
||||
<plugin-panel
|
||||
title="区块管理"
|
||||
class="plugin-block"
|
||||
:fixed-name="PLUGIN_NAME.BlockManage"
|
||||
:fixedPanels="fixedPanels"
|
||||
:docsUrl="docsUrl"
|
||||
:isShowDocsIcon="true"
|
||||
@close="close"
|
||||
>
|
||||
<template #header>
|
||||
<svg-button name="add-page" placement="bottom" tips="新建区块" @click="openBlockAdd"></svg-button>
|
||||
</template>
|
||||
<template #content>
|
||||
<div class="app-manage-type">
|
||||
<tiny-select
|
||||
ref="groupSelect"
|
||||
v-model="state.categoryId"
|
||||
popper-class="block-popper"
|
||||
:placeholder="groupLabels.selectPlaceholder"
|
||||
filterable
|
||||
:filter-method="categoryFilter"
|
||||
clearable
|
||||
top-create
|
||||
@top-create-click="createCategory"
|
||||
@change="changeCategory"
|
||||
@clear="changeCategory"
|
||||
@visible-change="handleSelectVisibleChange"
|
||||
class="search-select"
|
||||
>
|
||||
<tiny-option
|
||||
v-for="item in categoryList"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
class="block-group-option-item"
|
||||
>
|
||||
<tiny-option
|
||||
v-for="item in categoryList"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
class="block-group-option-item"
|
||||
>
|
||||
<div class="block-item">
|
||||
<span>{{ item.name }}</span>
|
||||
<div class="item-btns">
|
||||
<svg-button
|
||||
class="item-icon"
|
||||
name="to-edit"
|
||||
:hoverBgColor="false"
|
||||
@click.stop="editCategory(item)"
|
||||
></svg-button>
|
||||
<tiny-popover
|
||||
:modelValue="state.currentDeleteGroupId === item.id"
|
||||
placement="right"
|
||||
trigger="manual"
|
||||
popper-class="block-category-option-popper-wrapper"
|
||||
@update:modelValue="handleChangeDeletePopoverVisible"
|
||||
>
|
||||
<div class="popper-confirm" @mousedown.stop="">
|
||||
<div class="popper-confirm-header">删除</div>
|
||||
<div class="popper-confirm-content">
|
||||
<span class="title">{{ groupLabels.deletePrompt }}</span>
|
||||
</div>
|
||||
<div class="popper-confirm-footer">
|
||||
<tiny-button class="cancel-btn" size="small" @click="handleShowDeleteModal(null)"
|
||||
>取消</tiny-button
|
||||
>
|
||||
<tiny-button class="confirm-btn" size="small" type="primary" @click="delCategory(item.id)"
|
||||
>确定</tiny-button
|
||||
>
|
||||
</div>
|
||||
<div class="block-item">
|
||||
<span>{{ item.name }}</span>
|
||||
<div class="item-btns">
|
||||
<svg-button
|
||||
class="item-icon"
|
||||
name="to-edit"
|
||||
:hoverBgColor="false"
|
||||
@click.stop="editCategory(item)"
|
||||
></svg-button>
|
||||
<tiny-popover
|
||||
:modelValue="state.currentDeleteGroupId === item.id"
|
||||
placement="right"
|
||||
trigger="manual"
|
||||
popper-class="block-category-option-popper-wrapper"
|
||||
@update:modelValue="handleChangeDeletePopoverVisible"
|
||||
>
|
||||
<div class="popper-confirm" @mousedown.stop="">
|
||||
<div class="popper-confirm-header">删除</div>
|
||||
<div class="popper-confirm-content">
|
||||
<span class="title">{{ groupLabels.deletePrompt }}</span>
|
||||
</div>
|
||||
<template #reference>
|
||||
<svg-button
|
||||
v-if="!item.blocks.length"
|
||||
class="item-icon"
|
||||
name="delete"
|
||||
:hoverBgColor="false"
|
||||
@click.stop="handleShowDeleteModal(item.id)"
|
||||
></svg-button>
|
||||
</template>
|
||||
</tiny-popover>
|
||||
</div>
|
||||
<div class="popper-confirm-footer">
|
||||
<tiny-button class="cancel-btn" size="small" @click="handleShowDeleteModal(null)"
|
||||
>取消</tiny-button
|
||||
>
|
||||
<tiny-button class="confirm-btn" size="small" type="primary" @click="delCategory(item.id)"
|
||||
>确定</tiny-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<template #reference>
|
||||
<svg-button
|
||||
v-if="!item.blocks.length"
|
||||
class="item-icon"
|
||||
name="delete"
|
||||
:hoverBgColor="false"
|
||||
@click.stop="handleShowDeleteModal(item.id)"
|
||||
></svg-button>
|
||||
</template>
|
||||
</tiny-popover>
|
||||
</div>
|
||||
</tiny-option>
|
||||
</tiny-select>
|
||||
</div>
|
||||
<div class="app-manage-search">
|
||||
<tiny-search v-model="state.searchKey" placeholder="搜索">
|
||||
<template #prefix>
|
||||
<tiny-icon-search />
|
||||
</template>
|
||||
</tiny-search>
|
||||
</div>
|
||||
<div class="plugin-block-list">
|
||||
<plugin-block-list
|
||||
:data="state.blockList"
|
||||
:isBlockManage="true"
|
||||
:showBlockShot="true"
|
||||
:blockStyle="state.layout"
|
||||
default-icon-tip="查看区块"
|
||||
:externalBlock="externalBlock"
|
||||
@editBlock="editBlock"
|
||||
@iconClick="openSettingPanel"
|
||||
></plugin-block-list>
|
||||
</div>
|
||||
<block-setting></block-setting>
|
||||
<div class="block-footer">
|
||||
<tiny-dropdown trigger="click" @item-click="changeType">
|
||||
<span>
|
||||
<span>{{ state.sortTypeLabel }}</span>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<tiny-dropdown-menu
|
||||
popper-class="my-class"
|
||||
placement="top"
|
||||
:options="state.sortOptions"
|
||||
></tiny-dropdown-menu>
|
||||
</template>
|
||||
</tiny-dropdown>
|
||||
<block-group-arrange v-model="state.layout" :arrangeList="state.arrangeList"></block-group-arrange>
|
||||
</div>
|
||||
</template>
|
||||
</plugin-panel>
|
||||
<category-edit v-model="state.editVisible" :initialValue="state.groupInitialValue"></category-edit>
|
||||
<save-new-block :boxVisibility="boxVisibility" @close="close"></save-new-block>
|
||||
</div>
|
||||
</div>
|
||||
</tiny-option>
|
||||
</tiny-select>
|
||||
</div>
|
||||
<div class="app-manage-search">
|
||||
<tiny-search v-model="state.searchKey" placeholder="搜索">
|
||||
<template #prefix>
|
||||
<tiny-icon-search />
|
||||
</template>
|
||||
</tiny-search>
|
||||
</div>
|
||||
<div class="plugin-block-list">
|
||||
<plugin-block-list
|
||||
:data="state.blockList"
|
||||
:isBlockManage="true"
|
||||
:showBlockShot="true"
|
||||
:blockStyle="state.layout"
|
||||
default-icon-tip="查看区块"
|
||||
:externalBlock="externalBlock"
|
||||
@editBlock="editBlock"
|
||||
@iconClick="openSettingPanel"
|
||||
></plugin-block-list>
|
||||
</div>
|
||||
<block-setting></block-setting>
|
||||
<div class="block-footer">
|
||||
<tiny-dropdown trigger="click" @item-click="changeType">
|
||||
<span>
|
||||
<span>{{ state.sortTypeLabel }}</span>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<tiny-dropdown-menu
|
||||
popper-class="my-class"
|
||||
placement="top"
|
||||
:options="state.sortOptions"
|
||||
></tiny-dropdown-menu>
|
||||
</template>
|
||||
</tiny-dropdown>
|
||||
<block-group-arrange v-model="state.layout" :arrangeList="state.arrangeList"></block-group-arrange>
|
||||
</div>
|
||||
</template>
|
||||
</plugin-panel>
|
||||
<category-edit v-model="state.editVisible" :initialValue="state.groupInitialValue"></category-edit>
|
||||
<save-new-block :boxVisibility="boxVisibility" @close="close"></save-new-block>
|
||||
</template>
|
||||
|
||||
<script lang="jsx">
|
||||
import { ref, reactive, computed, watch } from 'vue'
|
||||
import { ref, reactive, computed, watch, provide } from 'vue'
|
||||
import {
|
||||
Search as TinySearch,
|
||||
Select as TinySelect,
|
||||
|
|
@ -217,8 +216,13 @@ export default {
|
|||
TinyButton,
|
||||
TinyIconSearch: IconSearch()
|
||||
},
|
||||
|
||||
setup() {
|
||||
props: {
|
||||
fixedPanels: {
|
||||
type: Array
|
||||
}
|
||||
},
|
||||
emits: ['close'],
|
||||
setup(props, { emit }) {
|
||||
const docsUrl = useHelp().getDocsUrl('block')
|
||||
const { getBlockList, sort } = useBlock()
|
||||
const { isSaved } = useCanvas()
|
||||
|
|
@ -266,6 +270,14 @@ export default {
|
|||
|
||||
const groupSelect = ref(null)
|
||||
|
||||
const { PLUGIN_NAME } = useLayout()
|
||||
|
||||
const panelState = reactive({
|
||||
emitEvent: emit
|
||||
})
|
||||
|
||||
provide('panelState', panelState)
|
||||
|
||||
watch(
|
||||
() => [getBlockList(), state.searchKey, state.publishFilterType],
|
||||
() => {
|
||||
|
|
@ -304,15 +316,15 @@ export default {
|
|||
}
|
||||
const close = () => {
|
||||
boxVisibility.value = false
|
||||
emit('close')
|
||||
closePanel()
|
||||
}
|
||||
|
||||
const editBlock = async (block) => {
|
||||
const isEdit = true
|
||||
|
||||
if (isSaved()) {
|
||||
await refreshBlockData(block)
|
||||
useBlock().initBlock(block, {}, isEdit)
|
||||
useLayout().closePlugin()
|
||||
closePanel()
|
||||
getMetaApi(META_SERVICE.GlobalService).updateBlockId(block.id)
|
||||
} else {
|
||||
confirm({
|
||||
|
|
@ -320,8 +332,6 @@ export default {
|
|||
exec: async () => {
|
||||
await refreshBlockData(block)
|
||||
useBlock().initBlock(block, {}, isEdit)
|
||||
useLayout().closePlugin()
|
||||
closePanel()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -423,6 +433,7 @@ export default {
|
|||
}
|
||||
|
||||
return {
|
||||
PLUGIN_NAME,
|
||||
state,
|
||||
groupSelect,
|
||||
categoryList,
|
||||
|
|
@ -458,7 +469,7 @@ export default {
|
|||
}
|
||||
.app-manage-type {
|
||||
padding: 0 10px;
|
||||
margin: 12px 0;
|
||||
margin-bottom: 12px;
|
||||
display: flex;
|
||||
.search-select {
|
||||
flex: 1;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,6 @@ export default {
|
|||
id: 'engine.plugins.bridge',
|
||||
title: '资源管理',
|
||||
type: 'plugins',
|
||||
align: 'top',
|
||||
align: 'leftTop',
|
||||
icon: 'plugin-icon-sresources'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<plugin-setting v-if="isOpen">
|
||||
<plugin-setting v-if="isOpen" :align="align" :fixed-name="PLUGIN_NAME.Bridge">
|
||||
<template #title>
|
||||
<div class="title-wrap">
|
||||
<span>{{ state.title }}</span>
|
||||
|
|
@ -112,7 +112,7 @@ import {
|
|||
getResourceNamesByType
|
||||
} from './js/resource'
|
||||
import { VueMonaco as MonacoEditor, PluginSetting, SvgButton, ButtonGroup } from '@opentiny/tiny-engine-common'
|
||||
import { useModal, useNotify, getMetaApi, META_SERVICE } from '@opentiny/tiny-engine-meta-register'
|
||||
import { useLayout, useModal, useNotify, getMetaApi, META_SERVICE } from '@opentiny/tiny-engine-meta-register'
|
||||
import { getMergeMeta } from '@opentiny/tiny-engine-meta-register'
|
||||
|
||||
const isOpen = ref(false)
|
||||
|
|
@ -151,6 +151,9 @@ export default {
|
|||
}
|
||||
const { confirm } = useModal()
|
||||
|
||||
const { PLUGIN_NAME, getPluginByLayout } = useLayout()
|
||||
const align = computed(() => getPluginByLayout(PLUGIN_NAME.Bridge))
|
||||
|
||||
const state = reactive({
|
||||
resource: computed(() => getResource()),
|
||||
name: '',
|
||||
|
|
@ -273,6 +276,8 @@ export default {
|
|||
}
|
||||
|
||||
return {
|
||||
align,
|
||||
PLUGIN_NAME,
|
||||
rules,
|
||||
resourceForm,
|
||||
editor,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
<template>
|
||||
<plugin-panel title="资源管理" class="plugin-bridge" :isCloseLeft="false" @close="closePanel">
|
||||
<plugin-panel
|
||||
title="资源管理"
|
||||
class="plugin-bridge"
|
||||
:fixed-name="PLUGIN_NAME.Bridge"
|
||||
:fixedPanels="fixedPanels"
|
||||
@close="closePanel"
|
||||
>
|
||||
<template #header>
|
||||
<svg-button name="add-utils" placement="left" :tips="tips" @click="addResource('npm')"></svg-button>
|
||||
</template>
|
||||
|
|
@ -11,8 +17,9 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, computed } from 'vue'
|
||||
import { ref, reactive, computed, provide } from 'vue'
|
||||
import { PluginPanel, SvgButton } from '@opentiny/tiny-engine-common'
|
||||
import { useLayout } from '@opentiny/tiny-engine-meta-register'
|
||||
import { RESOURCE_TYPE } from './js/resource'
|
||||
import BridgeManage from './BridgeManage.vue'
|
||||
import BridgeSetting, { openPanel, closePanel } from './BridgeSetting.vue'
|
||||
|
|
@ -25,11 +32,23 @@ export default {
|
|||
BridgeManage,
|
||||
BridgeSetting
|
||||
},
|
||||
setup() {
|
||||
props: {
|
||||
fixedPanels: {
|
||||
type: Array
|
||||
}
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const activedName = ref(RESOURCE_TYPE.Util)
|
||||
const utilsRef = ref(null)
|
||||
const tips = computed(() => RESOURCE_TIP[activedName.value])
|
||||
|
||||
const { PLUGIN_NAME } = useLayout()
|
||||
|
||||
const panelState = reactive({
|
||||
emitEvent: emit
|
||||
})
|
||||
provide('panelState', panelState)
|
||||
|
||||
const openBridgePanel = () => {
|
||||
openPanel()
|
||||
}
|
||||
|
|
@ -43,6 +62,7 @@ export default {
|
|||
}
|
||||
|
||||
return {
|
||||
PLUGIN_NAME,
|
||||
addResource,
|
||||
RESOURCE_TYPE,
|
||||
activedName,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,6 @@ export default {
|
|||
id: 'engine.plugins.collections',
|
||||
title: '数据源',
|
||||
type: 'plugins',
|
||||
align: 'top',
|
||||
align: 'leftTop',
|
||||
icon: 'plugin-icon-data'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
<template>
|
||||
<plugin-setting v-if="isOpen" title="设置数据源" class="data-source-form">
|
||||
<plugin-setting
|
||||
v-if="isOpen"
|
||||
title="设置数据源"
|
||||
class="data-source-form plugin-datasource"
|
||||
:fixed-name="PLUGIN_NAME.Collections"
|
||||
:align="align"
|
||||
>
|
||||
<template #header>
|
||||
<button-group>
|
||||
<tiny-button class="field-save" type="primary" @click="save">保存</tiny-button>
|
||||
|
|
@ -27,7 +33,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="jsx">
|
||||
import { reactive, ref, watch } from 'vue'
|
||||
import { reactive, ref, watch, computed } from 'vue'
|
||||
import { Form, Button } from '@opentiny/vue'
|
||||
import { ButtonGroup, PluginSetting, SvgButton } from '@opentiny/tiny-engine-common'
|
||||
import DataSourceType from './DataSourceType.vue'
|
||||
|
|
@ -41,7 +47,14 @@ import {
|
|||
requestDeleteDataSource,
|
||||
requestGenerateDataSource
|
||||
} from './js/http'
|
||||
import { useModal, useDataSource, useNotify, getMetaApi, META_SERVICE } from '@opentiny/tiny-engine-meta-register'
|
||||
import {
|
||||
useLayout,
|
||||
useModal,
|
||||
useDataSource,
|
||||
useNotify,
|
||||
getMetaApi,
|
||||
META_SERVICE
|
||||
} from '@opentiny/tiny-engine-meta-register'
|
||||
import { extend } from '@opentiny/vue-renderless/common/object'
|
||||
|
||||
const isOpen = ref(false)
|
||||
|
|
@ -84,6 +97,9 @@ export default {
|
|||
dataSource: {}
|
||||
})
|
||||
|
||||
const { PLUGIN_NAME, getPluginByLayout } = useLayout()
|
||||
const align = computed(() => getPluginByLayout(PLUGIN_NAME.Collections))
|
||||
|
||||
watch(
|
||||
() => state.dataSource.name,
|
||||
(value) => {
|
||||
|
|
@ -131,7 +147,8 @@ export default {
|
|||
|
||||
dataSourceState.dataSourceColumn = { name, type: type || 'array', columns: filterColumns }
|
||||
dataSourceState.dataSourceColumnCopies = extend(true, {}, dataSourceState.dataSourceColumn)
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
const closeAllPanel = () => {
|
||||
|
|
@ -240,6 +257,8 @@ export default {
|
|||
}
|
||||
|
||||
return {
|
||||
align,
|
||||
PLUGIN_NAME,
|
||||
state,
|
||||
isOpen,
|
||||
save,
|
||||
|
|
|
|||
|
|
@ -1,28 +1,34 @@
|
|||
<template>
|
||||
<div v-if="isOpen" class="global-data-handler">
|
||||
<plugin-setting title="全局设置" @cancel="close" @save="saveGlobalDataHandle">
|
||||
<template #content>
|
||||
<tiny-collapse v-model="activeNames">
|
||||
<tiny-collapse-item title="请求参数处理函数(willFetch)" name="willFetch">
|
||||
<data-handler-editor v-model="state.willFetchValue"></data-handler-editor>
|
||||
</tiny-collapse-item>
|
||||
<tiny-collapse-item title="请求完成回调函数(dataHandler)" name="dataHandler">
|
||||
<data-handler-editor v-model="state.dataHandlerValue"></data-handler-editor>
|
||||
</tiny-collapse-item>
|
||||
<tiny-collapse-item title="请求失败后的回调函数(errorHandler)" name="errorHandler">
|
||||
<data-handler-editor v-model="state.errorHandlerValue"></data-handler-editor>
|
||||
</tiny-collapse-item>
|
||||
</tiny-collapse>
|
||||
</template>
|
||||
</plugin-setting>
|
||||
</div>
|
||||
<plugin-setting
|
||||
v-if="isOpen"
|
||||
title="全局设置"
|
||||
class="plugin-datasource global-data-handler"
|
||||
:align="align"
|
||||
:fixed-name="PLUGIN_NAME.Collections"
|
||||
@cancel="close"
|
||||
@save="saveGlobalDataHandle"
|
||||
>
|
||||
<template #content>
|
||||
<tiny-collapse v-model="activeNames">
|
||||
<tiny-collapse-item title="请求参数处理函数(willFetch)" name="willFetch">
|
||||
<data-handler-editor v-model="state.willFetchValue"></data-handler-editor>
|
||||
</tiny-collapse-item>
|
||||
<tiny-collapse-item title="请求完成回调函数(dataHandler)" name="dataHandler">
|
||||
<data-handler-editor v-model="state.dataHandlerValue"></data-handler-editor>
|
||||
</tiny-collapse-item>
|
||||
<tiny-collapse-item title="请求失败后的回调函数(errorHandler)" name="errorHandler">
|
||||
<data-handler-editor v-model="state.errorHandlerValue"></data-handler-editor>
|
||||
</tiny-collapse-item>
|
||||
</tiny-collapse>
|
||||
</template>
|
||||
</plugin-setting>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DataHandlerEditor from './RemoteDataAdapterForm.vue'
|
||||
import { watch, ref, nextTick, reactive } from 'vue'
|
||||
import { watch, ref, nextTick, reactive, computed } from 'vue'
|
||||
import { requestGlobalDataHandler } from './js/http'
|
||||
import { useModal, useResource, getMetaApi, META_SERVICE } from '@opentiny/tiny-engine-meta-register'
|
||||
import { useLayout, useModal, useResource, getMetaApi, META_SERVICE } from '@opentiny/tiny-engine-meta-register'
|
||||
import { PluginSetting } from '@opentiny/tiny-engine-common'
|
||||
import { Collapse, CollapseItem } from '@opentiny/vue'
|
||||
import { constants } from '@opentiny/tiny-engine-utils'
|
||||
|
|
@ -48,6 +54,9 @@ export default {
|
|||
setup() {
|
||||
const { confirm } = useModal()
|
||||
|
||||
const { PLUGIN_NAME, getPluginByLayout } = useLayout()
|
||||
const align = computed(() => getPluginByLayout(PLUGIN_NAME.Collections))
|
||||
|
||||
const state = reactive({
|
||||
dataHandlerValue: useResource().appSchemaState?.dataHandler?.value,
|
||||
willFetchValue: useResource().appSchemaState.willFetch?.value,
|
||||
|
|
@ -88,6 +97,8 @@ export default {
|
|||
)
|
||||
|
||||
return {
|
||||
align,
|
||||
PLUGIN_NAME,
|
||||
isOpen,
|
||||
close,
|
||||
saveGlobalDataHandle,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
v-if="isOpen"
|
||||
:is-icon-button="false"
|
||||
:showIfFullScreen="true"
|
||||
:fixed-name="PLUGIN_NAME.Collections"
|
||||
:align="align"
|
||||
title="静态数据管理"
|
||||
class="datasource-record-list"
|
||||
@cancel="closeRecordList"
|
||||
|
|
@ -122,7 +124,8 @@ export default {
|
|||
const grid = ref(null)
|
||||
const { confirm } = useModal()
|
||||
const { toClipboard } = useClipboard()
|
||||
const { layoutState } = useLayout()
|
||||
const { layoutState, PLUGIN_NAME, getPluginByLayout } = useLayout()
|
||||
const align = computed(() => getPluginByLayout(PLUGIN_NAME.Collections))
|
||||
|
||||
const state = reactive({
|
||||
totalData: [],
|
||||
|
|
@ -559,6 +562,8 @@ export default {
|
|||
}
|
||||
|
||||
return {
|
||||
align,
|
||||
PLUGIN_NAME,
|
||||
isOpen,
|
||||
state,
|
||||
grid,
|
||||
|
|
|
|||
|
|
@ -2,8 +2,9 @@
|
|||
<div class="remote">
|
||||
<plugin-setting
|
||||
title="获取远程字段"
|
||||
class="remote-setting"
|
||||
class="remote-setting plugin-datasource"
|
||||
:isSecond="true"
|
||||
:align="align"
|
||||
@cancel="closePanel"
|
||||
@save="saveRemote"
|
||||
>
|
||||
|
|
@ -52,7 +53,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { reactive, watch, ref } from 'vue'
|
||||
import { reactive, watch, ref, computed } from 'vue'
|
||||
import { Collapse, CollapseItem, Tabs, TabItem, Button } from '@opentiny/vue'
|
||||
import { PluginSetting } from '@opentiny/tiny-engine-common'
|
||||
import DataSourceRemoteForm, { getServiceForm } from './DataSourceRemoteForm.vue'
|
||||
|
|
@ -61,7 +62,7 @@ import DataSourceRemoteAutoload from './DataSourceRemoteAutoload.vue'
|
|||
import DataSourceRemoteAdapter from './DataSourceRemoteDataAdapter.vue'
|
||||
import DataSrouceRemoteDataResult, { getResponseData } from './DataSourceRemoteDataResult.vue'
|
||||
import { open as openRemoteMapping } from './DataSourceRemoteMapping.vue'
|
||||
import { useDataSource, useNotify } from '@opentiny/tiny-engine-meta-register'
|
||||
import { useLayout, useDataSource, useNotify } from '@opentiny/tiny-engine-meta-register'
|
||||
import { isEmptyObject } from '@opentiny/vue-renderless/common/type'
|
||||
import { utils } from '@opentiny/tiny-engine-utils'
|
||||
import { getRequest } from './js/datasource'
|
||||
|
|
@ -114,6 +115,9 @@ export default {
|
|||
const dataSourceRemoteAdapteRef = ref(null)
|
||||
const { dataSourceState } = useDataSource()
|
||||
|
||||
const { PLUGIN_NAME, getPluginByLayout } = useLayout()
|
||||
const align = computed(() => getPluginByLayout(PLUGIN_NAME.Collections))
|
||||
|
||||
const state = reactive({
|
||||
remoteData: { options: {} },
|
||||
activeName: ['excute', 'result'],
|
||||
|
|
@ -221,6 +225,7 @@ export default {
|
|||
}
|
||||
|
||||
return {
|
||||
align,
|
||||
state,
|
||||
dataSourceRemoteAdapteRef,
|
||||
closePanel: close,
|
||||
|
|
|
|||
|
|
@ -1,62 +1,67 @@
|
|||
<template>
|
||||
<div class="plugin-datasource">
|
||||
<plugin-panel title="数据源">
|
||||
<template #header>
|
||||
<link-button :href="docsUrl"></link-button>
|
||||
<svg-button
|
||||
class="set-data-source"
|
||||
tips="全局设置"
|
||||
name="global-setting"
|
||||
@click="openGlobalDataHanderPanel"
|
||||
></svg-button>
|
||||
<svg-button
|
||||
class="refresh-data-source"
|
||||
tips="刷新数据源"
|
||||
name="flow-refresh"
|
||||
@click="refreshDataSource"
|
||||
></svg-button>
|
||||
</template>
|
||||
<template #content>
|
||||
<tiny-button class="add-data-source" @click="openDataSourceFormPanel()">
|
||||
<svg-icon name="add"></svg-icon>添加数据源
|
||||
</tiny-button>
|
||||
<data-source-list @edit="openDataSourceFormPanel"></data-source-list>
|
||||
</template>
|
||||
</plugin-panel>
|
||||
<data-source-remote-panel
|
||||
v-if="isOpenRemotePanel"
|
||||
v-model="state.currentDataSource.data"
|
||||
:editable="state.editable"
|
||||
@confirm="getRomoteReponseData"
|
||||
></data-source-remote-panel>
|
||||
<data-source-form
|
||||
v-model="state.currentDataSource"
|
||||
:editable="state.editable"
|
||||
@save="refreshDataSource"
|
||||
></data-source-form>
|
||||
<data-source-remote-mapping
|
||||
v-if="isOpenSourceRemoteMapping"
|
||||
v-model="state.remoteFields"
|
||||
:data="state.remoteResponData"
|
||||
></data-source-remote-mapping>
|
||||
<data-source-global-data-handler></data-source-global-data-handler>
|
||||
</div>
|
||||
<plugin-panel
|
||||
title="数据源"
|
||||
class="plugin-datasource"
|
||||
:fixed-name="PLUGIN_NAME.Collections"
|
||||
:fixedPanels="fixedPanels"
|
||||
:docsUrl="docsUrl"
|
||||
:isShowDocsIcon="true"
|
||||
@close="$emit('close')"
|
||||
>
|
||||
<template #header>
|
||||
<svg-button
|
||||
class="set-data-source"
|
||||
tips="全局设置"
|
||||
name="global-setting"
|
||||
@click="openGlobalDataHanderPanel"
|
||||
></svg-button>
|
||||
<svg-button
|
||||
class="refresh-data-source"
|
||||
tips="刷新数据源"
|
||||
name="flow-refresh"
|
||||
@click="refreshDataSource"
|
||||
></svg-button>
|
||||
</template>
|
||||
<template #content>
|
||||
<tiny-button class="add-data-source" @click="openDataSourceFormPanel()">
|
||||
<svg-icon name="add"></svg-icon>添加数据源
|
||||
</tiny-button>
|
||||
<data-source-list @edit="openDataSourceFormPanel"></data-source-list>
|
||||
</template>
|
||||
</plugin-panel>
|
||||
<data-source-remote-panel
|
||||
v-if="isOpenRemotePanel"
|
||||
v-model="state.currentDataSource.data"
|
||||
:editable="state.editable"
|
||||
@confirm="getRomoteReponseData"
|
||||
></data-source-remote-panel>
|
||||
<data-source-form
|
||||
v-model="state.currentDataSource"
|
||||
:editable="state.editable"
|
||||
@save="refreshDataSource"
|
||||
></data-source-form>
|
||||
<data-source-remote-mapping
|
||||
v-if="isOpenSourceRemoteMapping"
|
||||
v-model="state.remoteFields"
|
||||
:data="state.remoteResponData"
|
||||
></data-source-remote-mapping>
|
||||
<data-source-global-data-handler></data-source-global-data-handler>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { reactive, watch } from 'vue'
|
||||
import { reactive, watch, provide } from 'vue'
|
||||
import { Button } from '@opentiny/vue'
|
||||
import DataSourceList, { refresh as refreshDataSourceList, clearActive } from './DataSourceList.vue'
|
||||
import DataSourceRemotePanel, {
|
||||
close as closeRemotePanel,
|
||||
isOpen as isOpenRemotePanel
|
||||
} from './DataSourceRemotePanel.vue'
|
||||
import { PluginPanel, SvgButton, LinkButton } from '@opentiny/tiny-engine-common'
|
||||
import { PluginPanel, SvgButton } from '@opentiny/tiny-engine-common'
|
||||
import DataSourceForm, { open as openDataSourceForm, close as closeDataSourceForm } from './DataSourceForm.vue'
|
||||
import { close as closeRecordList } from './DataSourceRecordList.vue'
|
||||
import { close as closeRecordForm } from './DataSourceRecordForm.vue'
|
||||
import DataSourceRemoteMapping, { isOpen as isOpenSourceRemoteMapping } from './DataSourceRemoteMapping.vue'
|
||||
import { useDataSource, useHelp } from '@opentiny/tiny-engine-meta-register'
|
||||
import { useDataSource, useHelp, useLayout } from '@opentiny/tiny-engine-meta-register'
|
||||
import { requestUpdateDataSource } from './js/http'
|
||||
import DataSourceGlobalDataHandler, {
|
||||
open as openGlobalDataHander,
|
||||
|
|
@ -72,10 +77,14 @@ export default {
|
|||
DataSourceGlobalDataHandler,
|
||||
PluginPanel,
|
||||
DataSourceForm,
|
||||
SvgButton,
|
||||
LinkButton
|
||||
SvgButton
|
||||
},
|
||||
setup() {
|
||||
props: {
|
||||
fixedPanels: {
|
||||
type: Array
|
||||
}
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const docsUrl = useHelp().getDocsUrl('datasource')
|
||||
const state = reactive({
|
||||
editable: true,
|
||||
|
|
@ -84,6 +93,14 @@ export default {
|
|||
remoteResponData: {}
|
||||
})
|
||||
|
||||
const { PLUGIN_NAME } = useLayout()
|
||||
|
||||
const panelState = reactive({
|
||||
emitEvent: emit
|
||||
})
|
||||
|
||||
provide('panelState', panelState)
|
||||
|
||||
const { dataSourceState, saveDataSource } = useDataSource()
|
||||
|
||||
watch(
|
||||
|
|
@ -134,6 +151,7 @@ export default {
|
|||
}
|
||||
|
||||
return {
|
||||
PLUGIN_NAME,
|
||||
state,
|
||||
open,
|
||||
isOpenRemotePanel,
|
||||
|
|
@ -153,6 +171,7 @@ export default {
|
|||
}
|
||||
.add-data-source {
|
||||
margin: 0 12px 12px 12px;
|
||||
width: calc(100% - 24px);
|
||||
}
|
||||
:deep(.help-box) {
|
||||
position: absolute;
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ import HelpIcon from './src/HelpIcon.vue'
|
|||
|
||||
export default {
|
||||
id: 'engine.plugins.editorhelp',
|
||||
title: '',
|
||||
title: '帮助',
|
||||
type: 'plugins',
|
||||
icon: HelpIcon,
|
||||
align: 'bottom'
|
||||
align: 'leftBottom'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ export default {
|
|||
{
|
||||
title: '组件设置',
|
||||
text: '拖拽至画布中的组件或区块,在这里进行属性、样式、事件绑定等众多高级配置;',
|
||||
domElement: '#tiny-right-panel',
|
||||
domElement: '#rightTop',
|
||||
classes: 'lwocode-guide-toolbar-right',
|
||||
popPosition: 'left',
|
||||
button: [
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ export default {
|
|||
id: 'engine.plugins.i18n',
|
||||
title: '国际化',
|
||||
type: 'plugins',
|
||||
align: 'top',
|
||||
align: 'leftTop',
|
||||
width: 600,
|
||||
icon: 'plugin-icon-i18n'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,12 @@
|
|||
<template>
|
||||
<plugin-panel title="国际化资源" :docsUrl="docsUrl" :isShowDocsIcon="true" :isCloseLeft="false" class="plugin-i18n">
|
||||
<plugin-panel
|
||||
title="国际化资源"
|
||||
class="plugin-i18n"
|
||||
:fixed-name="PLUGIN_NAME.I18n"
|
||||
:fixedPanels="fixedPanels"
|
||||
:docsUrl="docsUrl"
|
||||
:isShowDocsIcon="true"
|
||||
>
|
||||
<template #content>
|
||||
<div class="language-search-box">
|
||||
<tiny-select v-model="currentSearchType" :options="i18nSearchTypes"></tiny-select>
|
||||
|
|
@ -113,12 +120,19 @@
|
|||
</template>
|
||||
|
||||
<script lang="jsx">
|
||||
import { computed, ref, watchEffect, reactive, onMounted, nextTick, resolveComponent, watch } from 'vue'
|
||||
import { computed, ref, watchEffect, reactive, onMounted, nextTick, resolveComponent, watch, provide } from 'vue'
|
||||
import useClipboard from 'vue-clipboard3'
|
||||
import { Grid, GridColumn, Input, Popover, Button, FileUpload, Loading, Tooltip, Select } from '@opentiny/vue'
|
||||
import { iconLoadingShadow, iconUpload } from '@opentiny/vue-icon'
|
||||
import { PluginPanel, SearchEmpty } from '@opentiny/tiny-engine-common'
|
||||
import { useTranslate, useModal, useHelp, getMetaApi, META_SERVICE } from '@opentiny/tiny-engine-meta-register'
|
||||
import {
|
||||
useTranslate,
|
||||
useModal,
|
||||
useHelp,
|
||||
getMetaApi,
|
||||
META_SERVICE,
|
||||
useLayout
|
||||
} from '@opentiny/tiny-engine-meta-register'
|
||||
import { getMergeMeta } from '@opentiny/tiny-engine-meta-register'
|
||||
import { utils, constants } from '@opentiny/tiny-engine-utils'
|
||||
import { BASE_URL } from '@opentiny/tiny-engine-common/js/environments'
|
||||
|
|
@ -138,7 +152,12 @@ export default {
|
|||
SearchEmpty,
|
||||
IconUpload: iconUpload()
|
||||
},
|
||||
setup() {
|
||||
props: {
|
||||
fixedPanels: {
|
||||
type: Array
|
||||
}
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
// 组件库iconLoadingShadow图标不能切换颜色,因此不同主题用不同icon
|
||||
const SvgIcon = resolveComponent('SvgIcon')
|
||||
const lightSpinnerIcon = iconLoadingShadow()
|
||||
|
|
@ -146,6 +165,13 @@ export default {
|
|||
const isLightTheme = getMergeMeta('engine.config').theme === 'light'
|
||||
const { getLangs, i18nResource, currentLanguage, getI18nData } = useTranslate()
|
||||
const { toClipboard } = useClipboard()
|
||||
const { PLUGIN_NAME } = useLayout()
|
||||
|
||||
const panelState = reactive({
|
||||
emitEvent: emit
|
||||
})
|
||||
provide('panelState', panelState)
|
||||
|
||||
const fullLangList = computed(() => {
|
||||
const langs = getLangs()
|
||||
|
||||
|
|
@ -392,6 +418,7 @@ export default {
|
|||
}
|
||||
|
||||
return {
|
||||
PLUGIN_NAME,
|
||||
sortTypeChanges,
|
||||
currentSearchType,
|
||||
i18nSearchTypes,
|
||||
|
|
|
|||
|
|
@ -3,5 +3,5 @@ export default {
|
|||
title: '物料',
|
||||
type: 'plugins',
|
||||
icon: 'plugin-icon-materials',
|
||||
align: 'top'
|
||||
align: 'leftTop'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,12 @@
|
|||
<template>
|
||||
<plugin-setting v-if="panel.show" :title="validGroup.groupName" @cancel="closeGroupPanel" @save="addBlocks">
|
||||
<plugin-setting
|
||||
v-if="panel.show"
|
||||
:align="align"
|
||||
:title="validGroup.groupName"
|
||||
:fixed-name="PLUGIN_NAME.Materials"
|
||||
@cancel="closeGroupPanel"
|
||||
@save="addBlocks"
|
||||
>
|
||||
<template #content>
|
||||
<div class="block-add-content">
|
||||
<div class="block-add-content-title">区块列表</div>
|
||||
|
|
@ -27,12 +34,13 @@
|
|||
</plugin-setting>
|
||||
</template>
|
||||
<script>
|
||||
import { reactive, watch, provide, inject, ref } from 'vue'
|
||||
import { reactive, watch, provide, inject, ref, computed } from 'vue'
|
||||
import { Search } from '@opentiny/vue'
|
||||
import { iconSearch } from '@opentiny/vue-icon'
|
||||
import { PluginSetting } from '@opentiny/tiny-engine-common'
|
||||
import {
|
||||
useBlock,
|
||||
useLayout,
|
||||
useModal,
|
||||
useResource,
|
||||
useNotify,
|
||||
|
|
@ -117,6 +125,9 @@ export default {
|
|||
|
||||
const validGroup = ref({ ...selectedGroup.value })
|
||||
|
||||
const { PLUGIN_NAME, getPluginByLayout } = useLayout()
|
||||
const align = computed(() => getPluginByLayout(PLUGIN_NAME.Materials))
|
||||
|
||||
watch(
|
||||
() => selectedGroup.value.groupId,
|
||||
(groupId) => {
|
||||
|
|
@ -271,12 +282,14 @@ export default {
|
|||
})
|
||||
|
||||
return {
|
||||
align,
|
||||
validGroup,
|
||||
state,
|
||||
panel,
|
||||
closeGroupPanel,
|
||||
addBlocks,
|
||||
searchBlocks
|
||||
searchBlocks,
|
||||
PLUGIN_NAME
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,10 @@
|
|||
<template>
|
||||
<plugin-panel :title="shortcut ? '' : title" @close="$emit('close')">
|
||||
<template #header>
|
||||
<component
|
||||
v-if="!onlyShowDefault"
|
||||
:is="registryData?.components?.header"
|
||||
:fixedPanels="fixedPanels"
|
||||
@fix-panel="(id) => $emit('fix-panel', id)"
|
||||
></component>
|
||||
</template>
|
||||
<plugin-panel
|
||||
:title="shortcut ? '' : title"
|
||||
:fixed-name="PLUGIN_NAME.Materials"
|
||||
:fixedPanels="fixedPanels"
|
||||
@close="$emit('close')"
|
||||
>
|
||||
<template #content>
|
||||
<tiny-tabs v-model="activeName" tab-style="button-card" class="full-width-tabs" v-if="!onlyShowDefault">
|
||||
<tiny-tab-item :key="item.id" v-for="item in tabComponents" :title="item.title" :name="item.id">
|
||||
|
|
@ -23,7 +20,7 @@
|
|||
<script>
|
||||
import { reactive, provide, ref, computed } from 'vue'
|
||||
import { Tabs, TabItem } from '@opentiny/vue'
|
||||
import { getMergeMeta } from '@opentiny/tiny-engine-meta-register'
|
||||
import { META_APP as PLUGIN_NAME, getMergeMeta } from '@opentiny/tiny-engine-meta-register'
|
||||
import { PluginPanel } from '@opentiny/tiny-engine-common'
|
||||
|
||||
export default {
|
||||
|
|
@ -83,6 +80,7 @@ export default {
|
|||
const title = ref(props.registryData?.title)
|
||||
|
||||
return {
|
||||
PLUGIN_NAME,
|
||||
title,
|
||||
activeName,
|
||||
defaultComponent,
|
||||
|
|
|
|||
|
|
@ -3,5 +3,5 @@ export default {
|
|||
title: '页面',
|
||||
type: 'plugins',
|
||||
icon: 'plugin-icon-page',
|
||||
align: 'top'
|
||||
align: 'leftTop'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,13 @@
|
|||
<template>
|
||||
<div class="plugin-page">
|
||||
<plugin-panel :title="title" @close="pluginPanelClosed" :docsUrl="docsUrl" :isShowDocsIcon="true">
|
||||
<plugin-panel
|
||||
:title="title"
|
||||
:fixed-name="PLUGIN_NAME.AppManage"
|
||||
:fixedPanels="fixedPanels"
|
||||
@close="pluginPanelClosed"
|
||||
:docsUrl="docsUrl"
|
||||
:isShowDocsIcon="true"
|
||||
>
|
||||
<template #header>
|
||||
<svg-button
|
||||
class="add-folder-icon"
|
||||
|
|
@ -36,7 +43,7 @@
|
|||
|
||||
<script lang="jsx">
|
||||
import { reactive, ref, watchEffect, provide } from 'vue'
|
||||
import { useCanvas, usePage, useHelp, useModal, useNotify } from '@opentiny/tiny-engine-meta-register'
|
||||
import { useCanvas, usePage, useHelp, useModal, useNotify, useLayout } from '@opentiny/tiny-engine-meta-register'
|
||||
import { PluginPanel, SvgButton } from '@opentiny/tiny-engine-common'
|
||||
import { extend } from '@opentiny/vue-renderless/common/object'
|
||||
import PageSetting, { openPageSettingPanel, closePageSettingPanel } from './PageSetting.vue'
|
||||
|
|
@ -67,13 +74,25 @@ export default {
|
|||
title: {
|
||||
type: String,
|
||||
default: '页面'
|
||||
},
|
||||
fixedPanels: {
|
||||
type: Array
|
||||
}
|
||||
},
|
||||
setup() {
|
||||
emits: ['close'],
|
||||
setup(props, { emit }) {
|
||||
const { confirm } = useModal()
|
||||
const { pageState } = useCanvas()
|
||||
const { pageSettingState, getDefaultPage, isTemporaryPage, initCurrentPageData } = usePage()
|
||||
|
||||
const { PLUGIN_NAME } = useLayout()
|
||||
|
||||
const panelState = reactive({
|
||||
emitEvent: emit
|
||||
})
|
||||
|
||||
provide('panelState', panelState)
|
||||
|
||||
const pageTreeRef = ref(null)
|
||||
const ROOT_ID = pageSettingState.ROOT_ID
|
||||
const docsUrl = useHelp().getDocsUrl('page')
|
||||
|
|
@ -169,6 +188,7 @@ export default {
|
|||
provide('openSettingPanel', openSettingPanel)
|
||||
|
||||
const pluginPanelClosed = () => {
|
||||
emit('close')
|
||||
closePageSettingPanel()
|
||||
closeFolderSettingPanel()
|
||||
}
|
||||
|
|
@ -178,6 +198,7 @@ export default {
|
|||
}
|
||||
|
||||
return {
|
||||
PLUGIN_NAME,
|
||||
state,
|
||||
pageState,
|
||||
openNewPage,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
<template>
|
||||
<plugin-setting v-if="isShow" :title="state.title" class="pageFolder-plugin-setting">
|
||||
<plugin-setting
|
||||
v-if="isShow"
|
||||
:fixed-name="PLUGIN_NAME.AppManage"
|
||||
:align="align"
|
||||
:title="state.title"
|
||||
class="pageFolder-plugin-setting"
|
||||
>
|
||||
<template #header>
|
||||
<button-group>
|
||||
<tiny-button type="primary" @click="saveFolderSetting">保存</tiny-button>
|
||||
|
|
@ -27,11 +33,12 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { reactive, ref } from 'vue'
|
||||
import { reactive, ref, computed } from 'vue'
|
||||
import { Button, Collapse, CollapseItem } from '@opentiny/vue'
|
||||
import { PluginSetting, SvgButton, ButtonGroup } from '@opentiny/tiny-engine-common'
|
||||
import {
|
||||
usePage,
|
||||
useLayout,
|
||||
useModal,
|
||||
useNotify,
|
||||
getMergeRegistry,
|
||||
|
|
@ -83,6 +90,9 @@ export default {
|
|||
const pageGeneral = registry.components.PageGeneral
|
||||
const folderGeneralRef = ref(null)
|
||||
|
||||
const { PLUGIN_NAME, getPluginByLayout } = useLayout()
|
||||
const align = computed(() => getPluginByLayout(PLUGIN_NAME.AppManage))
|
||||
|
||||
const closeFolderSetting = () => {
|
||||
if (isEqual(pageSettingState.currentPageData, pageSettingState.currentPageDataCopy)) {
|
||||
closeFolderSettingPanel()
|
||||
|
|
@ -197,6 +207,8 @@ export default {
|
|||
}
|
||||
|
||||
return {
|
||||
align,
|
||||
PLUGIN_NAME,
|
||||
saveFolderSetting,
|
||||
deleteFolder: throttle(5000, true, deleteFolder),
|
||||
pageGeneral,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
<template>
|
||||
<plugin-setting v-if="isShow" :title="state.title" class="page-plugin-setting">
|
||||
<plugin-setting
|
||||
v-if="isShow"
|
||||
:fixed-name="PLUGIN_NAME.AppManage"
|
||||
:align="align"
|
||||
:title="state.title"
|
||||
class="page-plugin-setting"
|
||||
>
|
||||
<template #header>
|
||||
<button-group>
|
||||
<tiny-button type="primary" @click="savePageSetting">保存</tiny-button>
|
||||
|
|
@ -54,7 +60,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="jsx">
|
||||
import { reactive, ref } from 'vue'
|
||||
import { reactive, ref, computed } from 'vue'
|
||||
import { Button, Collapse, CollapseItem, Input } from '@opentiny/vue'
|
||||
import { PluginSetting, ButtonGroup, SvgButton, LifeCycles } from '@opentiny/tiny-engine-common'
|
||||
import {
|
||||
|
|
@ -136,6 +142,9 @@ export default {
|
|||
const beforeCreatePage = registry?.options?.beforeCreatePage
|
||||
const pageGeneralRef = ref(null)
|
||||
|
||||
const { PLUGIN_NAME, getPluginByLayout } = useLayout()
|
||||
const align = computed(() => getPluginByLayout(PLUGIN_NAME.AppManage))
|
||||
|
||||
const state = reactive({
|
||||
activeName: Object.values(PAGE_SETTING_SESSION),
|
||||
title: '页面设置',
|
||||
|
|
@ -398,6 +407,8 @@ export default {
|
|||
}
|
||||
|
||||
return {
|
||||
align,
|
||||
PLUGIN_NAME,
|
||||
state,
|
||||
isShow,
|
||||
savePageSetting,
|
||||
|
|
|
|||
|
|
@ -5,5 +5,5 @@ export default {
|
|||
title: 'AI对话框',
|
||||
type: 'plugins',
|
||||
icon: RobotIcon,
|
||||
align: 'bottom'
|
||||
align: 'leftBottom'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,5 +3,7 @@ export default {
|
|||
title: '页面 Schema',
|
||||
type: 'plugins',
|
||||
icon: 'plugin-icon-page-schema',
|
||||
align: 'bottom'
|
||||
align: 'leftBottom',
|
||||
widthResizable: true,
|
||||
width: 600
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,45 +1,49 @@
|
|||
<template>
|
||||
<div id="source-code" class="plugin-schema">
|
||||
<div class="source-code-header">
|
||||
<div class="title">页面Schema</div>
|
||||
<div class="header-title">
|
||||
<!-- 暂时放开schema录入功能,等画布功能完善后,再打开下面一行的注释 -->
|
||||
<!-- <tiny-popover v-if="isEdit" placement="bottom" trigger="hover" append-to-body content="保存"> -->
|
||||
<span class="icon-wrap" @click="saveSchema">
|
||||
<i v-show="!showRed" class="red"></i>
|
||||
<tiny-button type="primary">保存</tiny-button>
|
||||
</span>
|
||||
<tiny-popover v-show="false" placement="bottom" trigger="hover" append-to-body content="导入 Schema">
|
||||
<template #reference>
|
||||
<span class="icon-wrap">
|
||||
<icon-download-link></icon-download-link>
|
||||
</span>
|
||||
</template>
|
||||
</tiny-popover>
|
||||
<close-icon @close="close"></close-icon>
|
||||
<plugin-panel
|
||||
id="source-code"
|
||||
title="页面 Schema"
|
||||
class="plugin-schema"
|
||||
:fixed-name="PLUGIN_NAME.Schema"
|
||||
:fixedPanels="fixedPanels"
|
||||
@close="close"
|
||||
>
|
||||
<template #header>
|
||||
<span class="icon-wrap">
|
||||
<i v-show="!showRed" class="red"></i>
|
||||
<tiny-button type="primary" @click="saveSchema">保存</tiny-button>
|
||||
</span>
|
||||
<tiny-popover v-show="false" placement="bottom" trigger="hover" append-to-body content="导入 Schema">
|
||||
<template #reference>
|
||||
<span class="icon-wrap">
|
||||
<icon-download-link></icon-download-link>
|
||||
</span>
|
||||
</template>
|
||||
</tiny-popover>
|
||||
</template>
|
||||
|
||||
<template #content>
|
||||
<div class="source-code-content">
|
||||
<monaco-editor
|
||||
ref="container"
|
||||
class="code-edit-content"
|
||||
:value="state.pageData"
|
||||
:options="options"
|
||||
@change="editorChange"
|
||||
@shortcutSave="saveSchema"
|
||||
></monaco-editor>
|
||||
</div>
|
||||
</div>
|
||||
<div class="source-code-content">
|
||||
<monaco-editor
|
||||
ref="container"
|
||||
class="code-edit-content"
|
||||
:value="state.pageData"
|
||||
:options="options"
|
||||
@change="editorChange"
|
||||
@shortcutSave="saveSchema"
|
||||
></monaco-editor>
|
||||
</div>
|
||||
<div class="source-code-footer">
|
||||
<button>导入 Schema</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="source-code-footer">
|
||||
<button>导入 Schema</button>
|
||||
</div>
|
||||
</template>
|
||||
</plugin-panel>
|
||||
</template>
|
||||
|
||||
<script lang="jsx">
|
||||
import { nextTick, reactive, getCurrentInstance, onActivated, ref, onDeactivated } from 'vue'
|
||||
import { nextTick, reactive, getCurrentInstance, onActivated, ref, onDeactivated, provide } from 'vue'
|
||||
import { Popover, Button } from '@opentiny/vue'
|
||||
import { VueMonaco, CloseIcon } from '@opentiny/tiny-engine-common'
|
||||
import { useCanvas, useModal, useNotify, useMessage } from '@opentiny/tiny-engine-meta-register'
|
||||
import { VueMonaco, PluginPanel } from '@opentiny/tiny-engine-common'
|
||||
import { useCanvas, useModal, useNotify, useMessage, useLayout } from '@opentiny/tiny-engine-meta-register'
|
||||
import { utils } from '@opentiny/tiny-engine-utils'
|
||||
import { iconDownloadLink } from '@opentiny/vue-icon'
|
||||
import { useThrottleFn } from '@vueuse/core'
|
||||
|
|
@ -51,9 +55,14 @@ export default {
|
|||
MonacoEditor: VueMonaco,
|
||||
TinyPopover: Popover,
|
||||
TinyButton: Button,
|
||||
CloseIcon,
|
||||
PluginPanel,
|
||||
IconDownloadLink: iconDownloadLink()
|
||||
},
|
||||
props: {
|
||||
fixedPanels: {
|
||||
type: Array
|
||||
}
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const app = getCurrentInstance()
|
||||
const { pageState } = useCanvas()
|
||||
|
|
@ -63,6 +72,13 @@ export default {
|
|||
})
|
||||
const { subscribe, unsubscribe } = useMessage()
|
||||
|
||||
const { PLUGIN_NAME } = useLayout()
|
||||
|
||||
const panelState = reactive({
|
||||
emitEvent: emit
|
||||
})
|
||||
provide('panelState', panelState)
|
||||
|
||||
const isEdit = false
|
||||
const showRed = ref(true)
|
||||
|
||||
|
|
@ -148,6 +164,7 @@ export default {
|
|||
})
|
||||
|
||||
return {
|
||||
PLUGIN_NAME,
|
||||
state,
|
||||
isEdit,
|
||||
saveSchema,
|
||||
|
|
@ -169,57 +186,35 @@ export default {
|
|||
|
||||
<style lang="less" scoped>
|
||||
#source-code {
|
||||
width: 50vw;
|
||||
height: calc(100% - var(--base-top-panel-height));
|
||||
padding: 12px 0;
|
||||
position: fixed;
|
||||
top: var(--base-top-panel-height);
|
||||
left: 41px;
|
||||
background: var(--te-schema-panel-bg-color);
|
||||
box-shadow: 6px 0px 3px 0px var(--te-schema-panel-shadow-color);
|
||||
z-index: 1000;
|
||||
.source-code-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid var(--te-schema-common-border-color);
|
||||
margin-bottom: 12px;
|
||||
padding: 0 12px 12px;
|
||||
}
|
||||
.title {
|
||||
color: var(--te-schema-panel-title-text-color);
|
||||
font-weight: var(--te-base-font-weight-bold);
|
||||
}
|
||||
.header-title {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
.icon-wrap {
|
||||
position: relative;
|
||||
.tiny-button {
|
||||
min-width: 40px;
|
||||
margin-right: 2px;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
}
|
||||
.red {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background-color: var(--te-schema-dot-color);
|
||||
display: block;
|
||||
z-index: 100;
|
||||
position: absolute;
|
||||
top: -3px;
|
||||
right: -4px;
|
||||
}
|
||||
|
||||
.icon-wrap {
|
||||
position: relative;
|
||||
margin-right: 6px;
|
||||
|
||||
.tiny-button {
|
||||
min-width: 40px;
|
||||
margin-right: 2px;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
}
|
||||
& > span:not(:last-child) {
|
||||
margin-right: 8px;
|
||||
|
||||
.red {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background-color: var(--te-schema-dot-color);
|
||||
display: block;
|
||||
z-index: 100;
|
||||
position: absolute;
|
||||
top: -3px;
|
||||
right: -1px;
|
||||
}
|
||||
}
|
||||
|
||||
.source-code-content {
|
||||
height: calc(100% - 42px);
|
||||
height: calc(100% - 12px);
|
||||
border: 1px solid var(--te-schema-common-border-color);
|
||||
border-radius: 4px;
|
||||
margin: 0 12px;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ export default {
|
|||
title: '页面 JS',
|
||||
type: 'plugins',
|
||||
icon: 'plugin-icon-js',
|
||||
align: 'top',
|
||||
align: 'leftTop',
|
||||
width: 600,
|
||||
widthResizable: true,
|
||||
confirm: 'close' // 当点击插件栏切换或关闭前是否需要确认, 会调用插件中confirm值指定的方法,e.g. 此处指向 close方法,会调用插件的close方法执行确认逻辑
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,36 +1,39 @@
|
|||
<template>
|
||||
<div class="plugin-page-js-container plugin-script">
|
||||
<div class="code-edit-head">
|
||||
<div class="head-left">
|
||||
<span class="title">页面 JS</span>
|
||||
<link-button :href="docsUrl"></link-button>
|
||||
<plugin-panel
|
||||
title="页面 JS"
|
||||
:fixed-name="PLUGIN_NAME.Page"
|
||||
:fixedPanels="fixedPanels"
|
||||
:docsUrl="docsUrl"
|
||||
:isShowDocsIcon="true"
|
||||
@close="$emit('close')"
|
||||
class="plugin-page-js-container plugin-script"
|
||||
>
|
||||
<template #header>
|
||||
<span class="icon-wrap">
|
||||
<i v-show="state.isChanged" class="red"></i>
|
||||
<tiny-button type="primary" @click="saveMethods">保存</tiny-button>
|
||||
</span>
|
||||
</template>
|
||||
<template #content>
|
||||
<div class="code-edit-content">
|
||||
<monaco-editor
|
||||
ref="monaco"
|
||||
:value="state.script"
|
||||
:options="options"
|
||||
@change="change"
|
||||
@editorDidMount="editorDidMount"
|
||||
@shortcutSave="saveMethods"
|
||||
></monaco-editor>
|
||||
</div>
|
||||
<div class="head-right">
|
||||
<tiny-button type="primary" class="save-btn" @click="saveMethods">
|
||||
<span>保存</span>
|
||||
<span v-show="state.isChanged" class="dots"></span>
|
||||
</tiny-button>
|
||||
<close-icon @close="close"></close-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="code-edit-content">
|
||||
<monaco-editor
|
||||
ref="monaco"
|
||||
:value="state.script"
|
||||
:options="options"
|
||||
@change="change"
|
||||
@editorDidMount="editorDidMount"
|
||||
@shortcutSave="saveMethods"
|
||||
></monaco-editor>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</plugin-panel>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { onBeforeUnmount } from 'vue'
|
||||
import { onBeforeUnmount, reactive, provide } from 'vue'
|
||||
import { Button } from '@opentiny/vue'
|
||||
import { VueMonaco, CloseIcon, LinkButton } from '@opentiny/tiny-engine-common'
|
||||
import { useHelp } from '@opentiny/tiny-engine-meta-register'
|
||||
import { VueMonaco, PluginPanel } from '@opentiny/tiny-engine-common'
|
||||
import { useHelp, useLayout } from '@opentiny/tiny-engine-meta-register'
|
||||
import { initCompletion } from '@opentiny/tiny-engine-common/js/completion'
|
||||
import { initLinter } from '@opentiny/tiny-engine-common/js/linter'
|
||||
import useMethod, { saveMethod, highlightMethod, getMethodNameList, getMethods } from './js/method'
|
||||
|
|
@ -46,14 +49,25 @@ export default {
|
|||
components: {
|
||||
MonacoEditor: VueMonaco,
|
||||
TinyButton: Button,
|
||||
CloseIcon,
|
||||
LinkButton
|
||||
PluginPanel
|
||||
},
|
||||
props: {
|
||||
fixedPanels: {
|
||||
type: Array
|
||||
}
|
||||
},
|
||||
emits: ['close'],
|
||||
setup(props, { emit }) {
|
||||
const docsUrl = useHelp().getDocsUrl('script')
|
||||
const { state, monaco, change, close, saveMethods } = useMethod({ emit })
|
||||
|
||||
const { PLUGIN_NAME } = useLayout()
|
||||
|
||||
const panelState = reactive({
|
||||
emitEvent: emit
|
||||
})
|
||||
provide('panelState', panelState)
|
||||
|
||||
const options = {
|
||||
language: 'javascript',
|
||||
minimap: {
|
||||
|
|
@ -105,6 +119,7 @@ export default {
|
|||
})
|
||||
|
||||
return {
|
||||
PLUGIN_NAME,
|
||||
state,
|
||||
monaco,
|
||||
options,
|
||||
|
|
@ -120,59 +135,36 @@ export default {
|
|||
|
||||
<style lang="less" scoped>
|
||||
.plugin-page-js-container {
|
||||
width: 50vw;
|
||||
height: 100%;
|
||||
background: var(--te-plugin-js-panel-bg-color);
|
||||
box-shadow: 6px 0px 3px 0px var(--te-plugin-js-panel-shadow-color);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 999;
|
||||
box-sizing: border-box;
|
||||
|
||||
.code-edit-head {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid var(--te-plugin-js-common-border-color);
|
||||
padding: 12px 0;
|
||||
.icon-wrap {
|
||||
position: relative;
|
||||
margin-right: 6px;
|
||||
|
||||
.head-left {
|
||||
padding-left: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.title {
|
||||
color: var(--te-plugin-js-panel-title-text-color);
|
||||
font-weight: var(--te-base-font-weight-bold);
|
||||
}
|
||||
.tiny-button {
|
||||
min-width: 40px;
|
||||
margin-right: 2px;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.head-right {
|
||||
margin-right: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.save-btn {
|
||||
min-width: 40px;
|
||||
margin-right: 8px;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
.dots {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
background: var(--te-plugin-js-dot-color);
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
top: 9px;
|
||||
right: 34px;
|
||||
}
|
||||
}
|
||||
.red {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: var(--te-plugin-js-dot-color);
|
||||
display: block;
|
||||
z-index: 100;
|
||||
position: absolute;
|
||||
top: -3px;
|
||||
right: -1px;
|
||||
}
|
||||
}
|
||||
|
||||
.code-edit-content {
|
||||
padding: 12px;
|
||||
height: calc(100% - 54px);
|
||||
padding: 0 12px;
|
||||
height: calc(100% - 12px);
|
||||
|
||||
& > div {
|
||||
border: 1px solid var(--te-plugin-js-common-border-color);
|
||||
|
|
@ -181,6 +173,7 @@ export default {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.help-box) {
|
||||
height: auto;
|
||||
#help-icon {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,6 @@ export default {
|
|||
id: 'engine.plugins.state',
|
||||
title: '状态管理',
|
||||
type: 'plugins',
|
||||
align: 'top',
|
||||
align: 'leftTop',
|
||||
icon: 'plugin-icon-var'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,78 +1,84 @@
|
|||
<template>
|
||||
<div id="data-source" class="plugin-state">
|
||||
<div class="data-source-left-panel">
|
||||
<div class="title">
|
||||
<span>状态管理</span>
|
||||
<link-button :href="docsUrl"></link-button>
|
||||
<close-icon @close="closePanel"></close-icon>
|
||||
<plugin-panel
|
||||
id="data-source"
|
||||
title="状态管理"
|
||||
class="plugin-state"
|
||||
:fixed-name="PLUGIN_NAME.State"
|
||||
:fixedPanels="fixedPanels"
|
||||
:docsUrl="docsUrl"
|
||||
:isShowDocsIcon="true"
|
||||
@close="closePanel"
|
||||
>
|
||||
<template #content>
|
||||
<div class="data-source-left-panel">
|
||||
<tiny-tabs v-model="activeName" @click="tabClick" tab-style="button-card">
|
||||
<tiny-tab-item :name="STATE.CURRENT_STATE" :title="isBlock ? '区块状态' : '页面状态'"></tiny-tab-item>
|
||||
<tiny-tab-item :name="STATE.GLOBAL_STATE" title="应用状态"></tiny-tab-item>
|
||||
</tiny-tabs>
|
||||
<tiny-search
|
||||
:modelValue="query"
|
||||
class="left-filter"
|
||||
placeholder="请输入搜索条件"
|
||||
clearable
|
||||
@update:modelValue="search"
|
||||
>
|
||||
<template #prefix>
|
||||
<tiny-icon-search />
|
||||
</template>
|
||||
</tiny-search>
|
||||
<div class="add-btn">
|
||||
<tiny-button @click="openPanel(OPTION_TYPE.ADD)">
|
||||
<svg-icon name="add" class="add-btn-icon"></svg-icon>
|
||||
<span class="add-btn-text">{{ activeName === STATE.CURRENT_STATE ? '添加变量' : '添加全局变量' }}</span>
|
||||
</tiny-button>
|
||||
</div>
|
||||
<data-source-list
|
||||
:modelValue="Object.keys(state.dataSource)"
|
||||
:stateScope="activeName"
|
||||
:query="query"
|
||||
:selectedKey="selectedKey"
|
||||
@openPanel="openPanel"
|
||||
@remove="remove"
|
||||
@removeStore="removeStore"
|
||||
/>
|
||||
</div>
|
||||
<tiny-tabs v-model="activeName" @click="tabClick" tab-style="button-card">
|
||||
<tiny-tab-item :name="STATE.CURRENT_STATE" :title="isBlock ? '区块状态' : '页面状态'"></tiny-tab-item>
|
||||
<tiny-tab-item :name="STATE.GLOBAL_STATE" title="应用状态"></tiny-tab-item>
|
||||
</tiny-tabs>
|
||||
<tiny-search
|
||||
:modelValue="query"
|
||||
class="left-filter"
|
||||
placeholder="请输入搜索条件"
|
||||
clearable
|
||||
@update:modelValue="search"
|
||||
>
|
||||
<template #prefix>
|
||||
<tiny-icon-search />
|
||||
</template>
|
||||
</tiny-search>
|
||||
<div class="add-btn">
|
||||
<tiny-button @click="openPanel(OPTION_TYPE.ADD)">
|
||||
<svg-icon name="add" class="add-btn-icon"></svg-icon>
|
||||
<span class="add-btn-text">{{ activeName === STATE.CURRENT_STATE ? '添加变量' : '添加全局变量' }}</span>
|
||||
</tiny-button>
|
||||
<div class="data-source-right-panel" v-if="isPanelShow" :style="alignStyle">
|
||||
<div class="header">
|
||||
<span>{{ addDataSource }}</span>
|
||||
<span class="options-wrap">
|
||||
<tiny-button type="primary" @click="confirm">保存</tiny-button>
|
||||
<close-icon @close="cancel"></close-icon>
|
||||
</span>
|
||||
</div>
|
||||
<create-variable
|
||||
v-if="activeName === STATE.CURRENT_STATE"
|
||||
ref="variableRef"
|
||||
:dataSource="state.dataSource"
|
||||
:flag="flag"
|
||||
:updateKey="updateKey"
|
||||
:createData="state.createData"
|
||||
@nameInput="updateName"
|
||||
@close="cancel"
|
||||
@mouseleave="onMouseLeaveVariable"
|
||||
/>
|
||||
<create-store
|
||||
v-if="activeName === STATE.GLOBAL_STATE"
|
||||
ref="storeRef"
|
||||
:dataSource="state.dataSource"
|
||||
:flag="flag"
|
||||
:updateKey="updateKey"
|
||||
:storeData="state.createData"
|
||||
@nameInput="validName"
|
||||
@close="cancel"
|
||||
@mouseleave="onMouseLeaveStore"
|
||||
/>
|
||||
</div>
|
||||
<data-source-list
|
||||
:modelValue="Object.keys(state.dataSource)"
|
||||
:stateScope="activeName"
|
||||
:query="query"
|
||||
:selectedKey="selectedKey"
|
||||
@openPanel="openPanel"
|
||||
@remove="remove"
|
||||
@removeStore="removeStore"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="isPanelShow" class="data-source-right-panel">
|
||||
<div class="header">
|
||||
<span>{{ addDataSource }}</span>
|
||||
<span class="options-wrap">
|
||||
<tiny-button type="primary" @click="confirm">保存</tiny-button>
|
||||
<close-icon @close="cancel"></close-icon>
|
||||
</span>
|
||||
</div>
|
||||
<create-variable
|
||||
v-if="activeName === STATE.CURRENT_STATE"
|
||||
ref="variableRef"
|
||||
:dataSource="state.dataSource"
|
||||
:flag="flag"
|
||||
:updateKey="updateKey"
|
||||
:createData="state.createData"
|
||||
@nameInput="updateName"
|
||||
@close="cancel"
|
||||
@mouseleave="onMouseLeaveVariable"
|
||||
/>
|
||||
<create-store
|
||||
v-if="activeName === STATE.GLOBAL_STATE"
|
||||
ref="storeRef"
|
||||
:dataSource="state.dataSource"
|
||||
:flag="flag"
|
||||
:updateKey="updateKey"
|
||||
:storeData="state.createData"
|
||||
@nameInput="validName"
|
||||
@close="cancel"
|
||||
@mouseleave="onMouseLeaveStore"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</plugin-panel>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { reactive, ref, computed, onActivated, watch } from 'vue'
|
||||
import { reactive, ref, computed, onActivated, watch, provide } from 'vue'
|
||||
import { Button, Search, Tabs, TabItem } from '@opentiny/vue'
|
||||
import {
|
||||
useCanvas,
|
||||
|
|
@ -80,13 +86,14 @@ import {
|
|||
useResource,
|
||||
useNotify,
|
||||
useHelp,
|
||||
useLayout,
|
||||
getMetaApi,
|
||||
META_APP,
|
||||
META_SERVICE
|
||||
} from '@opentiny/tiny-engine-meta-register'
|
||||
import { getCommentByKey } from '@opentiny/tiny-engine-common/js/comment'
|
||||
import { iconSearch } from '@opentiny/vue-icon'
|
||||
import { CloseIcon, LinkButton } from '@opentiny/tiny-engine-common'
|
||||
import { CloseIcon, PluginPanel } from '@opentiny/tiny-engine-common'
|
||||
import DataSourceList from './DataSourceList.vue'
|
||||
import CreateVariable from './CreateVariable.vue'
|
||||
import CreateStore from './CreateStore.vue'
|
||||
|
|
@ -104,9 +111,14 @@ export default {
|
|||
TinyTabs: Tabs,
|
||||
TinyTabItem: TabItem,
|
||||
CreateStore,
|
||||
LinkButton,
|
||||
PluginPanel,
|
||||
TinyIconSearch: iconSearch()
|
||||
},
|
||||
props: {
|
||||
fixedPanels: {
|
||||
type: Array
|
||||
}
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const variableRef = ref(null)
|
||||
const storeRef = ref(null)
|
||||
|
|
@ -131,6 +143,24 @@ export default {
|
|||
})
|
||||
const selectedKey = ref(null)
|
||||
|
||||
const { PLUGIN_NAME, getPluginWidth, getPluginByLayout } = useLayout()
|
||||
|
||||
const firstPanelOffset = computed(() => {
|
||||
return getPluginWidth(PLUGIN_NAME.State)
|
||||
})
|
||||
|
||||
const alignStyle = computed(() => {
|
||||
const panelAlign = getPluginByLayout(PLUGIN_NAME.State)
|
||||
const align = panelAlign.includes('left') ? 'left' : 'right'
|
||||
return `${align}: ${firstPanelOffset.value}px`
|
||||
})
|
||||
|
||||
const panelState = reactive({
|
||||
emitEvent: emit
|
||||
})
|
||||
|
||||
provide('panelState', panelState)
|
||||
|
||||
watch(activeName, () => {
|
||||
selectedKey.value = null
|
||||
})
|
||||
|
|
@ -355,6 +385,9 @@ export default {
|
|||
})
|
||||
|
||||
return {
|
||||
alignStyle,
|
||||
firstPanelOffset,
|
||||
PLUGIN_NAME,
|
||||
isBlock,
|
||||
isPanelShow,
|
||||
errorMessage,
|
||||
|
|
@ -420,18 +453,6 @@ export default {
|
|||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
padding: 10px;
|
||||
font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans',
|
||||
'Helvetica Neue', sans-serif;
|
||||
color: var(--te-state-common-text-color);
|
||||
font-weight: var(--te-base-font-weight-bold);
|
||||
border-bottom: 1px solid var(--te-state-common-border-color-divider);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.left-filter {
|
||||
margin-top: 4px;
|
||||
padding: 0 8px;
|
||||
|
|
@ -459,7 +480,6 @@ export default {
|
|||
border-right: 1px solid var(--te-state-common-border-color-divider);
|
||||
background: var(--te-state-common-bg-color);
|
||||
position: absolute;
|
||||
left: var(--base-left-panel-width);
|
||||
top: 0;
|
||||
|
||||
.header {
|
||||
|
|
@ -490,7 +510,7 @@ export default {
|
|||
}
|
||||
|
||||
:deep(.tiny-tabs__header) {
|
||||
padding: 8px;
|
||||
padding: 0 8px 8px 8px;
|
||||
}
|
||||
|
||||
:deep(.tiny-tabs__header .tiny-tabs__active-bar) {
|
||||
|
|
|
|||
|
|
@ -3,5 +3,6 @@ export default {
|
|||
title: '大纲树',
|
||||
type: 'plugins',
|
||||
icon: 'plugin-icon-tree',
|
||||
align: 'top'
|
||||
widthResizable: true,
|
||||
align: 'leftTop'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
<template>
|
||||
<plugin-panel class="outlinebox plugin-tree" title="大纲树" @close="$emit('close')" ref="panelRef" tabindex="0">
|
||||
<template #header>
|
||||
<svg-button
|
||||
class="item icon-sidebar"
|
||||
:name="panelFixed ? 'fixed-solid' : 'fixed'"
|
||||
:tips="panelFixed ? '解除固定面板' : '固定面板'"
|
||||
@click="$emit('fix-panel', PLUGIN_NAME.OutlineTree)"
|
||||
></svg-button>
|
||||
</template>
|
||||
<plugin-panel
|
||||
tabindex="0"
|
||||
title="大纲树"
|
||||
ref="panelRef"
|
||||
class="outlinebox plugin-tree"
|
||||
:fixed-name="PLUGIN_NAME.OutlineTree"
|
||||
:fixedPanels="fixedPanels"
|
||||
@close="$emit('close')"
|
||||
>
|
||||
<template #content>
|
||||
<draggable-tree
|
||||
label-key="componentName"
|
||||
|
|
@ -38,8 +38,19 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { reactive, watch, computed, onActivated, onDeactivated, onMounted, onBeforeUnmount, nextTick, ref } from 'vue'
|
||||
import { PluginPanel, SvgButton } from '@opentiny/tiny-engine-common'
|
||||
import {
|
||||
reactive,
|
||||
watch,
|
||||
computed,
|
||||
onActivated,
|
||||
onDeactivated,
|
||||
provide,
|
||||
onMounted,
|
||||
onBeforeUnmount,
|
||||
nextTick,
|
||||
ref
|
||||
} from 'vue'
|
||||
import { PluginPanel } from '@opentiny/tiny-engine-common'
|
||||
import { constants } from '@opentiny/tiny-engine-utils'
|
||||
import {
|
||||
useCanvas,
|
||||
|
|
@ -56,7 +67,6 @@ const { PAGE_STATUS } = constants
|
|||
export default {
|
||||
components: {
|
||||
PluginPanel,
|
||||
SvgButton,
|
||||
DraggableTree
|
||||
},
|
||||
props: {
|
||||
|
|
@ -65,7 +75,7 @@ export default {
|
|||
}
|
||||
},
|
||||
emits: ['close', 'fix-panel'],
|
||||
setup(props) {
|
||||
setup(props, { emit }) {
|
||||
const { pageState } = useCanvas()
|
||||
const { getMaterial } = useMaterial()
|
||||
const { PLUGIN_NAME } = useLayout()
|
||||
|
|
@ -76,6 +86,11 @@ export default {
|
|||
|
||||
const selectedIds = computed(() => useMultiSelect().multiSelectedStates.value.map((state) => state.id))
|
||||
|
||||
const panelState = reactive({
|
||||
emitEvent: emit
|
||||
})
|
||||
provide('panelState', panelState)
|
||||
|
||||
const filterSchema = (data) => {
|
||||
const translateChild = (data) => {
|
||||
data.forEach((item) => {
|
||||
|
|
|
|||
|
|
@ -3,5 +3,5 @@ export default {
|
|||
title: 'TinyEngine 教程',
|
||||
type: 'plugins',
|
||||
icon: 'plugin-icon-tutorial',
|
||||
align: 'bottom'
|
||||
align: 'leftBottom'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
|
||||
*
|
||||
*/
|
||||
|
||||
import { isRef } from 'vue'
|
||||
import { initHook } from './hooks'
|
||||
|
||||
const vueLifeHook = [
|
||||
|
|
@ -161,7 +161,7 @@ export const preprocessRegistry = (registry) => {
|
|||
|
||||
export const generateRegistry = (registry) => {
|
||||
Object.entries(registry).forEach(([key, value]) => {
|
||||
if (typeof value === 'object' && value) {
|
||||
if (typeof value === 'object' && value && !isRef(value)) {
|
||||
const { id } = value
|
||||
// 如果匹配到了id,说明是元服务配置,对元服务配置做读取和写入
|
||||
if (id && key !== 'metaData') {
|
||||
|
|
|
|||
|
|
@ -20,9 +20,7 @@ import './src/styles/vars.less'
|
|||
export default {
|
||||
...metaData,
|
||||
entry,
|
||||
options: {
|
||||
commonEvents
|
||||
},
|
||||
options: { commonEvents },
|
||||
components: {
|
||||
BindEventsDialogSidebar,
|
||||
BindEventsDialogContent
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ export default {
|
|||
id: 'engine.setting.event',
|
||||
title: '高级',
|
||||
type: 'setting',
|
||||
align: 'left',
|
||||
align: 'rightTop',
|
||||
name: 'event',
|
||||
icon: ''
|
||||
icon: 'target'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +1,44 @@
|
|||
<template>
|
||||
<tiny-collapse v-model="activeNames">
|
||||
<tiny-collapse-item title="事件绑定" name="bindEvent">
|
||||
<bind-events></bind-events>
|
||||
</tiny-collapse-item>
|
||||
<tiny-collapse-item title="高级配置" name="advancedConfig">
|
||||
<advance-config></advance-config>
|
||||
</tiny-collapse-item>
|
||||
</tiny-collapse>
|
||||
<plugin-panel
|
||||
title="高级"
|
||||
:fixed-panels="fixedPanels"
|
||||
:fixed-name="PLUGIN_NAME.Event"
|
||||
:header-margin-bottom="0"
|
||||
@close="$emit('close')"
|
||||
>
|
||||
<template #content>
|
||||
<tiny-collapse v-model="activeNames">
|
||||
<tiny-collapse-item title="事件绑定" name="bindEvent">
|
||||
<bind-events></bind-events>
|
||||
</tiny-collapse-item>
|
||||
<tiny-collapse-item title="高级配置" name="advancedConfig">
|
||||
<advance-config></advance-config>
|
||||
</tiny-collapse-item>
|
||||
</tiny-collapse>
|
||||
</template>
|
||||
</plugin-panel>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { ref, reactive, provide, defineProps, defineEmits } from 'vue'
|
||||
import { Collapse as TinyCollapse, CollapseItem as TinyCollapseItem } from '@opentiny/vue'
|
||||
import BindEvents from './components/BindEvents.vue'
|
||||
import AdvanceConfig from './components/AdvanceConfig.vue'
|
||||
import { PluginPanel } from '@opentiny/tiny-engine-common'
|
||||
import { useLayout } from '@opentiny/tiny-engine-meta-register'
|
||||
|
||||
const activeNames = ref(['bindEvent', 'advancedConfig'])
|
||||
const { PLUGIN_NAME } = useLayout()
|
||||
|
||||
defineProps({
|
||||
fixedPanels: Array
|
||||
})
|
||||
|
||||
const emit = defineEmits([])
|
||||
|
||||
const panelState = reactive({
|
||||
emitEvent: emit
|
||||
})
|
||||
|
||||
provide('panelState', panelState)
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ export default {
|
|||
id: 'engine.setting.props',
|
||||
title: '属性',
|
||||
type: 'setting',
|
||||
align: 'left',
|
||||
align: 'rightTop',
|
||||
name: 'props',
|
||||
icon: ''
|
||||
icon: 'form'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,27 @@
|
|||
<template>
|
||||
<config-render :data="properties">
|
||||
<template #prefix="{ data }">
|
||||
<block-link-field v-if="isBlock" :data="data"></block-link-field>
|
||||
<plugin-panel
|
||||
title="属性"
|
||||
:fixed-panels="fixedPanels"
|
||||
:fixed-name="PLUGIN_NAME.Props"
|
||||
:show-bottom-border="showEmptyTips"
|
||||
@close="$emit('close')"
|
||||
>
|
||||
<template #content>
|
||||
<config-render :data="properties">
|
||||
<template #prefix="{ data }">
|
||||
<block-link-field v-if="isBlock" :data="data"></block-link-field>
|
||||
</template>
|
||||
</config-render>
|
||||
<block-description v-if="isBlock" class="block-description"> </block-description>
|
||||
<empty :showEmptyTips="showEmptyTips"></empty>
|
||||
</template>
|
||||
</config-render>
|
||||
<block-description v-if="isBlock" class="block-description"> </block-description>
|
||||
<empty :showEmptyTips="showEmptyTips"></empty>
|
||||
</plugin-panel>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { computed, watchEffect, ref } from 'vue'
|
||||
import { ConfigRender, BlockDescription, BlockLinkField } from '@opentiny/tiny-engine-common'
|
||||
import { useCanvas, useProperty } from '@opentiny/tiny-engine-meta-register'
|
||||
import { computed, watchEffect, ref, reactive, provide } from 'vue'
|
||||
import { ConfigRender, BlockDescription, BlockLinkField, PluginPanel } from '@opentiny/tiny-engine-common'
|
||||
import { useCanvas, useProperty, useLayout } from '@opentiny/tiny-engine-meta-register'
|
||||
import Empty from './components/Empty.vue'
|
||||
|
||||
export default {
|
||||
|
|
@ -19,13 +29,21 @@ export default {
|
|||
ConfigRender,
|
||||
BlockLinkField,
|
||||
BlockDescription,
|
||||
Empty
|
||||
Empty,
|
||||
PluginPanel
|
||||
},
|
||||
setup() {
|
||||
setup(props, { emit }) {
|
||||
const { pageState } = useCanvas()
|
||||
const { properties } = useProperty().getProperty({ pageState })
|
||||
const showEmptyTips = ref(false)
|
||||
|
||||
const { PLUGIN_NAME } = useLayout()
|
||||
|
||||
const panelState = reactive({
|
||||
emitEvent: emit
|
||||
})
|
||||
provide('panelState', panelState)
|
||||
|
||||
const isBlock = computed(() => pageState.isBlock)
|
||||
|
||||
watchEffect(() => {
|
||||
|
|
@ -33,6 +51,7 @@ export default {
|
|||
})
|
||||
|
||||
return {
|
||||
PLUGIN_NAME,
|
||||
isBlock,
|
||||
properties,
|
||||
showEmptyTips
|
||||
|
|
|
|||
|
|
@ -3,6 +3,6 @@ export default {
|
|||
title: '样式',
|
||||
name: 'style',
|
||||
type: 'setting',
|
||||
align: 'left',
|
||||
icon: ''
|
||||
align: 'rightTop',
|
||||
icon: 'display-inline'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,75 +1,87 @@
|
|||
<template>
|
||||
<div class="style-editor">
|
||||
<div class="line-style">
|
||||
<span class="line-text"> 行内样式 </span>
|
||||
<div class="inline-style">
|
||||
<component
|
||||
:is="CodeConfigurator"
|
||||
v-if="state.lineStyleDisable"
|
||||
:buttonShowContent="true"
|
||||
:modelValue="state.styleContent"
|
||||
title="编辑行内样式"
|
||||
:button-text="state.inlineBtnText"
|
||||
language="css"
|
||||
single
|
||||
@save="save"
|
||||
></component>
|
||||
<div v-if="!state.lineStyleDisable">
|
||||
<tiny-input v-model="state.propertiesList" class="inline-bind-style"> </tiny-input>
|
||||
<plugin-panel
|
||||
title="样式"
|
||||
:fixed-panels="fixedPanels"
|
||||
:fixed-name="PLUGIN_NAME.Styles"
|
||||
:is-show-collapse-icon="true"
|
||||
:show-bottom-border="true"
|
||||
@updateCollapseStatus="updateCollapseStatus"
|
||||
@close="$emit('close')"
|
||||
>
|
||||
<template #content>
|
||||
<div class="style-editor">
|
||||
<div class="line-style">
|
||||
<span class="line-text"> 行内样式 </span>
|
||||
<div class="inline-style">
|
||||
<component
|
||||
:is="CodeConfigurator"
|
||||
v-if="state.lineStyleDisable"
|
||||
:buttonShowContent="true"
|
||||
:modelValue="state.styleContent"
|
||||
title="编辑行内样式"
|
||||
:button-text="state.inlineBtnText"
|
||||
language="css"
|
||||
single
|
||||
@save="save"
|
||||
></component>
|
||||
<div v-if="!state.lineStyleDisable">
|
||||
<tiny-input v-model="state.propertiesList" class="inline-bind-style"> </tiny-input>
|
||||
</div>
|
||||
<component
|
||||
:is="VariableConfigurator"
|
||||
ref="bindVariable"
|
||||
:model-value="state.bindModelValue"
|
||||
name="advance"
|
||||
@update:modelValue="setConfig"
|
||||
>
|
||||
</component>
|
||||
</div>
|
||||
</div>
|
||||
<component
|
||||
:is="VariableConfigurator"
|
||||
ref="bindVariable"
|
||||
:model-value="state.bindModelValue"
|
||||
name="advance"
|
||||
@update:modelValue="setConfig"
|
||||
>
|
||||
</component>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<class-names-container></class-names-container>
|
||||
<tiny-collapse v-model="activeNames" @change="handoverGroup">
|
||||
<tiny-collapse-item title="布局" name="layout">
|
||||
<layout-group :display="state.style.display" @update="updateStyle" />
|
||||
<flex-box v-if="state.style.display === 'flex'" :style="state.style" @update="updateStyle"></flex-box>
|
||||
<grid-box v-if="state.style.display === 'grid'" :style="state.style" @update="updateStyle"></grid-box>
|
||||
</tiny-collapse-item>
|
||||
<class-names-container></class-names-container>
|
||||
<tiny-collapse v-model="activeNames" @change="handoverGroup">
|
||||
<tiny-collapse-item title="布局" name="layout">
|
||||
<layout-group :display="state.style.display" @update="updateStyle" />
|
||||
<flex-box v-if="state.style.display === 'flex'" :style="state.style" @update="updateStyle"></flex-box>
|
||||
<grid-box v-if="state.style.display === 'grid'" :style="state.style" @update="updateStyle"></grid-box>
|
||||
</tiny-collapse-item>
|
||||
|
||||
<tiny-collapse-item title="间距" name="spacing">
|
||||
<spacing-group :style="state.style" @update="updateStyle" />
|
||||
</tiny-collapse-item>
|
||||
<tiny-collapse-item title="间距" name="spacing">
|
||||
<spacing-group :style="state.style" @update="updateStyle" />
|
||||
</tiny-collapse-item>
|
||||
|
||||
<tiny-collapse-item title="尺寸" name="size">
|
||||
<size-group :style="state.style" @update="updateStyle" />
|
||||
</tiny-collapse-item>
|
||||
<tiny-collapse-item title="尺寸" name="size">
|
||||
<size-group :style="state.style" @update="updateStyle" />
|
||||
</tiny-collapse-item>
|
||||
|
||||
<tiny-collapse-item title="定位" name="position">
|
||||
<position-group :style="state.style" @update="updateStyle" />
|
||||
</tiny-collapse-item>
|
||||
<tiny-collapse-item title="定位" name="position">
|
||||
<position-group :style="state.style" @update="updateStyle" />
|
||||
</tiny-collapse-item>
|
||||
|
||||
<tiny-collapse-item title="文本" name="typography">
|
||||
<typography-group :style="state.style" @update="updateStyle" />
|
||||
</tiny-collapse-item>
|
||||
<tiny-collapse-item title="文本" name="typography">
|
||||
<typography-group :style="state.style" @update="updateStyle" />
|
||||
</tiny-collapse-item>
|
||||
|
||||
<tiny-collapse-item title="背景" name="backgrounds">
|
||||
<background-group :style="state.style" @update="updateStyle" />
|
||||
</tiny-collapse-item>
|
||||
<tiny-collapse-item title="背景" name="backgrounds">
|
||||
<background-group :style="state.style" @update="updateStyle" />
|
||||
</tiny-collapse-item>
|
||||
|
||||
<tiny-collapse-item title="边框" name="borders">
|
||||
<border-group :style="state.style" @update="updateStyle" />
|
||||
</tiny-collapse-item>
|
||||
<tiny-collapse-item title="边框" name="borders">
|
||||
<border-group :style="state.style" @update="updateStyle" />
|
||||
</tiny-collapse-item>
|
||||
|
||||
<tiny-collapse-item title="效果" name="effects" class="effects-style">
|
||||
<effect-group :style="state.style" @update="updateStyle" />
|
||||
</tiny-collapse-item>
|
||||
</tiny-collapse>
|
||||
<tiny-collapse-item title="效果" name="effects" class="effects-style">
|
||||
<effect-group :style="state.style" @update="updateStyle" />
|
||||
</tiny-collapse-item>
|
||||
</tiny-collapse>
|
||||
</template>
|
||||
</plugin-panel>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { watch, inject, ref } from 'vue'
|
||||
import { watch, ref, reactive, provide } from 'vue'
|
||||
import { Collapse, CollapseItem, Input } from '@opentiny/vue'
|
||||
import { useHistory, useCanvas, useProperties, getConfigurator } from '@opentiny/tiny-engine-meta-register'
|
||||
import { useLayout, useHistory, useCanvas, useProperties, getConfigurator } from '@opentiny/tiny-engine-meta-register'
|
||||
import {
|
||||
SizeGroup,
|
||||
LayoutGroup,
|
||||
|
|
@ -85,10 +97,12 @@ import {
|
|||
} from './components'
|
||||
import { CSS_TYPE } from './js/cssType'
|
||||
import useStyle from './js/useStyle'
|
||||
import { PluginPanel } from '@opentiny/tiny-engine-common'
|
||||
import { styleStrRemoveRoot } from './js/cssConvert'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
PluginPanel,
|
||||
SizeGroup,
|
||||
LayoutGroup,
|
||||
FlexBox,
|
||||
|
|
@ -104,7 +118,12 @@ export default {
|
|||
TinyCollapseItem: CollapseItem,
|
||||
TinyInput: Input
|
||||
},
|
||||
setup() {
|
||||
props: {
|
||||
fixedPanels: {
|
||||
type: Array
|
||||
}
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const CodeConfigurator = getConfigurator('CodeConfigurator')
|
||||
const VariableConfigurator = getConfigurator('VariableConfigurator')
|
||||
const styleCategoryGroup = [
|
||||
|
|
@ -117,13 +136,19 @@ export default {
|
|||
'borders',
|
||||
'effects'
|
||||
]
|
||||
const isCollapsed = inject('isCollapsed')
|
||||
const isCollapsed = ref(false)
|
||||
const activeNames = ref(styleCategoryGroup)
|
||||
const { getCurrentSchema } = useCanvas()
|
||||
// 获取当前节点 style 对象
|
||||
const { state, updateStyle } = useStyle() // updateStyle
|
||||
const { addHistory } = useHistory()
|
||||
const { getSchema, setProp } = useProperties()
|
||||
const { PLUGIN_NAME } = useLayout()
|
||||
|
||||
const panelState = reactive({
|
||||
emitEvent: emit
|
||||
})
|
||||
provide('panelState', panelState)
|
||||
|
||||
const handoverGroup = (actives) => {
|
||||
if (isCollapsed.value) {
|
||||
|
|
@ -196,6 +221,10 @@ export default {
|
|||
}
|
||||
)
|
||||
|
||||
const updateCollapseStatus = (val) => {
|
||||
isCollapsed.value = val
|
||||
}
|
||||
|
||||
watch(
|
||||
() => isCollapsed.value,
|
||||
() => {
|
||||
|
|
@ -208,6 +237,8 @@ export default {
|
|||
)
|
||||
|
||||
return {
|
||||
updateCollapseStatus,
|
||||
PLUGIN_NAME,
|
||||
CodeConfigurator,
|
||||
VariableConfigurator,
|
||||
state,
|
||||
|
|
@ -228,7 +259,7 @@ export default {
|
|||
<style lang="less" scoped>
|
||||
.style-editor {
|
||||
justify-content: space-around;
|
||||
padding: 12px 0 0;
|
||||
margin-top: 12px;
|
||||
column-gap: 8px;
|
||||
.line-style {
|
||||
padding: 0 8px 0 12px;
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ body {
|
|||
--base-bottom-panel-height: 30px;
|
||||
--base-nav-panel-width: 40px;
|
||||
--base-collection-panel-width: calc(
|
||||
(100vw - (var(--base-left-panel-width) + var(--base-right-panel-width) + var(--base-nav-panel-width) - 1px)) / 2
|
||||
(99vw - (var(--base-left-panel-width) + var(--base-right-panel-width) + var(--base-nav-panel-width) - 1px)) / 2
|
||||
);
|
||||
--base-collection-panel-full-screen-width: calc(
|
||||
(100vw - (var(--base-left-panel-width) + var(--base-right-panel-width) + var(--base-nav-panel-width) - 1px))
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ export default {
|
|||
const showpopover = ref(false)
|
||||
|
||||
const themeShowType = computed(() => {
|
||||
let filterList = THEME_DATA.value.filter((item) => ['light', 'dark'].includes(item.type)) || []
|
||||
const filterList = THEME_DATA.value.filter((item) => ['light', 'dark'].includes(item.type)) || []
|
||||
return THEME_DATA.value.length === filterList.length
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -97,7 +97,9 @@ export const BROADCAST_CHANNEL = {
|
|||
CanvasRouterViewSetting: `tiny-lowcode-canvas-router-view-setting-${CHANNEL_UID}`
|
||||
}
|
||||
|
||||
export const STORAGE_KEY_FIXED_PANELS = `tiny-engine-fixed-panels-${CHANNEL_UID}`
|
||||
export const STORAGE_KEY_LEFT_FIXED_PANELS = `tiny-engine-left-fixed-panels`
|
||||
export const STORAGE_KEY_RIGHT_FIXED_PANELS = `tiny-engine-right-fixed-panels`
|
||||
|
||||
export const CANVAS_ROUTER_VIEW_SETTING_VIEW_MODE_KEY = `tiny-engine-canvas-router-view-setting-view-mode`
|
||||
|
||||
export const AUTO_SAVED = 'tiny-engine-auto-saved'
|
||||
|
|
@ -161,3 +163,6 @@ export const SORT_TYPE = {
|
|||
// 字母倒序
|
||||
alphabetDesc: 'alphabetDesc'
|
||||
}
|
||||
|
||||
// 插件面板默认宽度
|
||||
export const PLUGIN_DEFAULT_WIDTH = 280
|
||||
|
|
|
|||
Loading…
Reference in New Issue