!63 feat: 增加图层隐藏锁定功能,图层列表模式控制

Merge pull request !63 from dodu/dev-layers
This commit is contained in:
奔跑的面条 2022-09-28 13:30:35 +00:00 committed by Gitee
commit 752cb744c0
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
19 changed files with 451 additions and 114 deletions

View File

@ -22,6 +22,7 @@ module.exports = {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-unused-vars': 'off', 'no-unused-vars': 'off',
'vue/no-unused-vars': 'off',
'vue/multi-word-component-names': 'off', 'vue/multi-word-component-names': 'off',
'vue/valid-template-root': 'off', 'vue/valid-template-root': 'off',
'vue/no-mutating-props': 'off' 'vue/no-mutating-props': 'off'

View File

@ -1,7 +1,7 @@
// 鼠标点击左右键 // 鼠标点击左右键
export enum MouseEventButton { export enum MouseEventButton {
LEFT = 1, LEFT = 1,
RIGHT = 2, RIGHT = 2
} }
// 页面拖拽键名 // 页面拖拽键名
@ -41,7 +41,15 @@ export enum MenuEnum {
// 后退 // 后退
BACK = 'back', BACK = 'back',
// 前进 // 前进
FORWORD = 'forward' FORWORD = 'forward',
// 锁定
LOCK = 'lock',
// 解除锁定
UNLOCK = 'unLock',
// 隐藏
HIDE = 'hide',
// 显示
SHOW = 'show'
} }
// Win 键盘枚举 // Win 键盘枚举
@ -49,9 +57,9 @@ export enum WinKeyboard {
CTRL = 'ctrl', CTRL = 'ctrl',
SHIFT = 'shift', SHIFT = 'shift',
ALT = ' alt', ALT = ' alt',
CTRL_SOURCE_KEY = "control", CTRL_SOURCE_KEY = 'control',
SHIFT_SOURCE_KEY = "shift", SHIFT_SOURCE_KEY = 'shift',
ALT_SOURCE_KEY = "alt" ALT_SOURCE_KEY = 'alt'
} }
// Mac 键盘枚举 // Mac 键盘枚举
@ -60,7 +68,7 @@ export enum MacKeyboard {
CTRL = '⌘', CTRL = '⌘',
SHIFT = '⇧', SHIFT = '⇧',
ALT = '⌥', ALT = '⌥',
CTRL_SOURCE_KEY = "⌘", CTRL_SOURCE_KEY = '⌘',
SHIFT_SOURCE_KEY = "⇧", SHIFT_SOURCE_KEY = '⇧',
ALT_SOURCE_KEY = "⌥" ALT_SOURCE_KEY = '⌥'
} }

View File

@ -57,7 +57,12 @@ import {
ChevronDownOutline as ChevronDownOutlineIcon, ChevronDownOutline as ChevronDownOutlineIcon,
Pulse as PulseIcon, Pulse as PulseIcon,
Folder as FolderIcon, Folder as FolderIcon,
FolderOpen as FolderOpenIcon FolderOpen as FolderOpenIcon,
Image as ImageIcon,
Images as ImagesIcon,
List as ListIcon,
EyeOutline as EyeOutlineIcon,
EyeOffOutline as EyeOffOutlineIcon
} from '@vicons/ionicons5' } from '@vicons/ionicons5'
import { import {
@ -211,7 +216,16 @@ const ionicons5 = {
// 文件夹 // 文件夹
FolderIcon, FolderIcon,
// 文件夹打开 // 文件夹打开
FolderOpenIcon FolderOpenIcon,
// 图片
ImageIcon,
// 多个图片
ImagesIcon,
// 列表
ListIcon,
// 眼睛
EyeOutlineIcon,
EyeOffOutlineIcon
} }
const carbon = { const carbon = {

View File

@ -602,7 +602,8 @@ export const useChartEditStore = defineStore({
ids.push(item.id) ids.push(item.id)
}) })
} else { } else {
(historyData[0] as CreateComponentGroupType).groupList.forEach(item => { const group = historyData[0] as CreateComponentGroupType
group.groupList.forEach(item => {
ids.push(item.id) ids.push(item.id)
}) })
} }
@ -617,6 +618,32 @@ export const useChartEditStore = defineStore({
} }
return return
} }
switch (HistoryItem.actionType) {
// 锁定处理
case HistoryActionTypeEnum.LOCK:
case HistoryActionTypeEnum.UNLOCK:
if (!isForward) {
// 恢复原来状态
if (HistoryItem.actionType === HistoryActionTypeEnum.LOCK) historyData[0].status.lock = false
if (HistoryItem.actionType === HistoryActionTypeEnum.UNLOCK) historyData[0].status.lock = true
return
}
this.setLock(!historyData[0].status.lock, false)
break
// 隐藏处理
case HistoryActionTypeEnum.HIDE:
case HistoryActionTypeEnum.SHOW:
if (!isForward) {
// 恢复原来状态
if (HistoryItem.actionType === HistoryActionTypeEnum.HIDE) historyData[0].status.hide = false
if (HistoryItem.actionType === HistoryActionTypeEnum.SHOW) historyData[0].status.hide = true
return
}
this.setHide(!historyData[0].status.hide, false)
break
}
}, },
// * 撤回 // * 撤回
setBack() { setBack() {
@ -795,6 +822,76 @@ export const useChartEditStore = defineStore({
loadingFinish() loadingFinish()
} }
}, },
// * 锁定
setLock(status: boolean = true, isHistory: boolean = true) {
try {
// 暂不支持多选
if (this.getTargetChart.selectId.length > 1) return
loadingStart()
const index: number = this.fetchTargetIndex()
if (index !== -1) {
// 更新状态
const targetItem = this.getComponentList[index]
targetItem.status.lock = status
// 历史记录
if (isHistory) {
chartHistoryStore.createLockHistory(
[targetItem],
status ? HistoryActionTypeEnum.LOCK : HistoryActionTypeEnum.UNLOCK
)
}
this.updateComponentList(index, targetItem)
loadingFinish()
return
}
} catch (value) {
loadingError()
}
},
// * 解除锁定
setUnLock(isHistory: boolean = true) {
this.setLock(false, isHistory)
},
// * 隐藏
setHide(status: boolean = true, isHistory: boolean = true) {
try {
// 暂不支持多选
if (this.getTargetChart.selectId.length > 1) return
loadingStart()
const index: number = this.fetchTargetIndex()
if (index !== -1) {
// 更新状态
const targetItem = this.getComponentList[index]
targetItem.status.hide = status
// 历史记录
if (isHistory) {
chartHistoryStore.createHideHistory(
[targetItem],
status ? HistoryActionTypeEnum.HIDE : HistoryActionTypeEnum.SHOW
)
}
this.updateComponentList(index, targetItem)
loadingFinish()
// 取消选择隐藏
if (status) {
const chartEditStore = useChartEditStore()
chartEditStore.setTargetSelectChart(undefined)
}
return
}
} catch (value) {
loadingError()
}
},
// * 显示
setShow(isHistory: boolean = true) {
this.setHide(false, isHistory)
},
// ---------------- // ----------------
// * 设置页面大小 // * 设置页面大小
setPageSize(scale: number): void { setPageSize(scale: number): void {

View File

@ -1,7 +1,4 @@
import { import { HistoryTargetTypeEnum, HistoryActionTypeEnum } from './chartHistoryStore.d'
HistoryTargetTypeEnum,
HistoryActionTypeEnum
} from './chartHistoryStore.d'
export const historyActionTypeName = { export const historyActionTypeName = {
[HistoryActionTypeEnum.ADD]: '新增图表', [HistoryActionTypeEnum.ADD]: '新增图表',
@ -18,6 +15,10 @@ export const historyActionTypeName = {
[HistoryActionTypeEnum.GROUP]: '创建分组', [HistoryActionTypeEnum.GROUP]: '创建分组',
[HistoryActionTypeEnum.UN_GROUP]: '解除分组', [HistoryActionTypeEnum.UN_GROUP]: '解除分组',
[HistoryActionTypeEnum.SELECT_HISTORY]: '选择记录', [HistoryActionTypeEnum.SELECT_HISTORY]: '选择记录',
[HistoryActionTypeEnum.LOCK]: '锁定',
[HistoryActionTypeEnum.UNLOCK]: '解除锁定',
[HistoryActionTypeEnum.HIDE]: '隐藏',
[HistoryActionTypeEnum.SHOW]: '显示',
[HistoryTargetTypeEnum.CANVAS]: '画布初始化' [HistoryTargetTypeEnum.CANVAS]: '画布初始化'
} }

View File

@ -2,6 +2,7 @@ import { CreateComponentType, CreateComponentGroupType } from '@/packages/index.
import { EditCanvasType } from '@/store/modules/chartEditStore/chartEditStore.d' import { EditCanvasType } from '@/store/modules/chartEditStore/chartEditStore.d'
// 操作类型枚举 // 操作类型枚举
export enum HistoryActionTypeEnum { export enum HistoryActionTypeEnum {
// 新增 // 新增
ADD = 'add', ADD = 'add',
@ -30,7 +31,15 @@ export enum HistoryActionTypeEnum {
// 解组 // 解组
UN_GROUP = 'unGroup', UN_GROUP = 'unGroup',
// 选择历史记录 // 选择历史记录
SELECT_HISTORY = 'selectHistory' SELECT_HISTORY = 'selectHistory',
// 锁定
LOCK = 'lock',
// 解除锁定
UNLOCK = 'unLock',
// 隐藏
HIDE = 'hide',
// 显示
SHOW = 'show'
} }
// 对象类型 // 对象类型

View File

@ -167,6 +167,20 @@ export const useChartHistoryStore = defineStore({
// * 解除分组 // * 解除分组
createUnGroupHistory(item: Array<CreateComponentType | CreateComponentGroupType>) { createUnGroupHistory(item: Array<CreateComponentType | CreateComponentGroupType>) {
this.createStackItem(item, HistoryActionTypeEnum.UN_GROUP, HistoryTargetTypeEnum.CHART) this.createStackItem(item, HistoryActionTypeEnum.UN_GROUP, HistoryTargetTypeEnum.CHART)
},
// * 锁定记录
createLockHistory(
item: Array<CreateComponentType | CreateComponentGroupType>,
type: HistoryActionTypeEnum.LOCK | HistoryActionTypeEnum.UNLOCK
) {
this.createStackItem(item, type, HistoryTargetTypeEnum.CHART)
},
// * 隐藏记录
createHideHistory(
item: Array<CreateComponentType | CreateComponentGroupType>,
type: HistoryActionTypeEnum.HIDE | HistoryActionTypeEnum.SHOW
) {
this.createStackItem(item, type, HistoryTargetTypeEnum.CHART)
} }
} }
}) })

View File

@ -1,7 +1,7 @@
@import './var.scss'; @import "./var.scss";
@import './format.scss'; @import "./format.scss";
@import './animation.scss'; @import "./animation.scss";
@import './mixins/mixins.scss'; @import "./mixins/mixins.scss";
// 过度 // 过度
.go-transition { .go-transition {
@ -49,14 +49,14 @@
// 毛玻璃 // 毛玻璃
.go-background-filter { .go-background-filter {
backdrop-filter: $--filter-blur-base; backdrop-filter: $--filter-blur-base;
@include fetch-bg-color('filter-color'); @include fetch-bg-color("filter-color");
box-shadow: $--border-shadow; box-shadow: $--border-shadow;
} }
// 毛玻璃 // 毛玻璃
.go-background-filter-shallow { .go-background-filter-shallow {
backdrop-filter: $--filter-blur-base; backdrop-filter: $--filter-blur-base;
@include fetch-bg-color('filter-color-shallow'); @include fetch-bg-color("filter-color-shallow");
box-shadow: $--border-shadow; box-shadow: $--border-shadow;
} }
@ -68,7 +68,7 @@
// 背景斑点需配合 @mixin background-image 使用 // 背景斑点需配合 @mixin background-image 使用
.go-point-bg { .go-point-bg {
@include fetch-theme-custom('background-color', 'background-color1'); @include fetch-theme-custom("background-color", "background-color1");
background-size: 15px 15px, 15px 15px; background-size: 15px 15px, 15px 15px;
} }
@ -118,3 +118,10 @@
#{$type}: 0 !important; #{$type}: 0 !important;
} }
} }
.go-d-inline-block {
display: inline-block;
}
.go-d-block {
display: block;
}

View File

@ -10,14 +10,9 @@
<slot name="icon"></slot> <slot name="icon"></slot>
</div> </div>
</n-space> </n-space>
<n-space> <n-space align="center" style="gap: 4px">
<slot name="top-right"></slot> <slot name="top-right"></slot>
<n-icon <n-icon v-show="backIcon" size="20" class="go-cursor-pointer go-d-block" @click="backHandle">
v-show="backIcon"
size="20"
class="go-cursor-pointer"
@click="backHandle"
>
<chevron-back-outline-icon></chevron-back-outline-icon> <chevron-back-outline-icon></chevron-back-outline-icon>
</n-icon> </n-icon>
</n-space> </n-space>
@ -165,9 +160,7 @@ $topOrBottomHeight: 40px;
height: calc(100vh - #{$--header-height} - #{$topOrBottomHeight}); height: calc(100vh - #{$--header-height} - #{$topOrBottomHeight});
} }
.content-height-show-both { .content-height-show-both {
height: calc( height: calc(100vh - #{$--header-height} - #{$topOrBottomHeight} - #{$topOrBottomHeight});
100vh - #{$--header-height} - #{$topOrBottomHeight} - #{$topOrBottomHeight}
);
} }
} }
</style> </style>

View File

@ -85,26 +85,32 @@ const optionsHandle = (
allList: MenuOptionsItemType[], allList: MenuOptionsItemType[],
targetInstance: CreateComponentType targetInstance: CreateComponentType
) => { ) => {
//
const moreMenuEnums = [MenuEnum.GROUP, MenuEnum.DELETE]
//
const singleMenuEnums = [MenuEnum.UN_GROUP]
const filter = (menulist: MenuEnum[]) => { const filter = (menulist: MenuEnum[]) => {
const list: MenuOptionsItemType[] = [] return allList.filter(i => menulist.includes(i.key as MenuEnum))
allList.forEach(item => {
if (menulist.includes(item.key as MenuEnum)) {
list.push(item)
}
})
return list
} }
// //
if (chartEditStore.getTargetChart.selectId.length > 1) { if (chartEditStore.getTargetChart.selectId.length > 1) {
return filter(moreMenuEnums) return filter([MenuEnum.GROUP, MenuEnum.DELETE])
} else { } else {
return [...filter(singleMenuEnums), divider(), ...targetList] const statusMenuEnums: MenuEnum[] = []
if (targetInstance.status.lock) {
statusMenuEnums.push(MenuEnum.LOCK)
} else {
statusMenuEnums.push(MenuEnum.UNLOCK)
}
if (targetInstance.status.hide) {
statusMenuEnums.push(MenuEnum.HIDE)
} else {
statusMenuEnums.push(MenuEnum.SHOW)
}
//
const singleMenuEnums = [MenuEnum.UN_GROUP]
return [
...filter(singleMenuEnums),
divider(),
...targetList.filter(i => !statusMenuEnums.includes(i.key as MenuEnum))
]
} }
} }

View File

@ -48,8 +48,19 @@ import {
HistoryActionTypeEnum HistoryActionTypeEnum
} from '@/store/modules/chartHistoryStore/chartHistoryStore.d' } from '@/store/modules/chartHistoryStore/chartHistoryStore.d'
const { DesktopOutlineIcon, PencilIcon, TrashIcon, CopyIcon, LayersIcon, DuplicateIcon, HelpOutlineIcon } = const {
icon.ionicons5 DesktopOutlineIcon,
PencilIcon,
TrashIcon,
CopyIcon,
LayersIcon,
DuplicateIcon,
HelpOutlineIcon,
LockClosedOutlineIcon,
LockOpenOutlineIcon,
EyeOffOutlineIcon,
EyeOutlineIcon
} = icon.ionicons5
const { StackedMoveIcon, Carbon3DCursorIcon, Carbon3DSoftwareIcon } = icon.carbon const { StackedMoveIcon, Carbon3DCursorIcon, Carbon3DSoftwareIcon } = icon.carbon
const chartHistoryStoreStore = useChartHistoryStore() const chartHistoryStoreStore = useChartHistoryStore()
@ -83,6 +94,14 @@ const iconHandle = (e: HistoryItemType) => {
return Carbon3DCursorIcon return Carbon3DCursorIcon
case HistoryActionTypeEnum.UN_GROUP: case HistoryActionTypeEnum.UN_GROUP:
return Carbon3DSoftwareIcon return Carbon3DSoftwareIcon
case HistoryActionTypeEnum.LOCK:
return LockClosedOutlineIcon
case HistoryActionTypeEnum.UNLOCK:
return LockOpenOutlineIcon
case HistoryActionTypeEnum.HIDE:
return EyeOffOutlineIcon
case HistoryActionTypeEnum.SHOW:
return EyeOutlineIcon
default: default:
return PencilIcon return PencilIcon
} }
@ -109,9 +128,7 @@ const options = computed(() => {
} }
}) })
return reverse(options.filter(item => { return reverse(options.filter(item => item.label))
return item.label
}))
}) })
</script> </script>

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="go-shape-box"> <div class="go-shape-box" :class="{ lock: item.status.lock, hide: item.status.hide }">
<slot></slot> <slot></slot>
<!-- 锚点 --> <!-- 锚点 -->
<template v-if="!hiddenPoint"> <template v-if="!hiddenPoint">
@ -55,12 +55,14 @@ const themeColor = computed(() => {
// //
const hover = computed(() => { const hover = computed(() => {
if (props.item.status.lock) return false
return props.item.id === chartEditStore.getTargetChart.hoverId return props.item.id === chartEditStore.getTargetChart.hoverId
}) })
// //
const select = computed(() => { const select = computed(() => {
const id = props.item.id const id = props.item.id
if (props.item.status.lock) return false
return chartEditStore.getTargetChart.selectId.find((e: string) => e === id) return chartEditStore.getTargetChart.selectId.find((e: string) => e === id)
}) })
</script> </script>
@ -70,6 +72,14 @@ const select = computed(() => {
position: absolute; position: absolute;
cursor: move; cursor: move;
&.lock {
cursor: default !important;
}
&.hide {
display: none;
}
/* 锚点 */ /* 锚点 */
.shape-point { .shape-point {
z-index: 1; z-index: 1;

View File

@ -140,7 +140,9 @@ export const mousedownBoxSelect = (e: MouseEvent, item?: CreateComponentType | C
targetAttr.x1 - selectAttr.x1 >= 0 && targetAttr.x1 - selectAttr.x1 >= 0 &&
targetAttr.y1 - selectAttr.y1 >= 0 && targetAttr.y1 - selectAttr.y1 >= 0 &&
targetAttr.x2 - selectAttr.x2 <= 0 && targetAttr.x2 - selectAttr.x2 <= 0 &&
targetAttr.y2 - selectAttr.y2 <= 0 targetAttr.y2 - selectAttr.y2 <= 0 &&
!item.status.lock &&
!item.status.hide
) { ) {
isSelect = true isSelect = true
chartEditStore.setTargetSelectChart(item.id, true) chartEditStore.setTargetSelectChart(item.id, true)
@ -166,6 +168,7 @@ export const useMouseHandle = () => {
const mouseClickHandle = (e: MouseEvent, item: CreateComponentType | CreateComponentGroupType) => { const mouseClickHandle = (e: MouseEvent, item: CreateComponentType | CreateComponentGroupType) => {
e.preventDefault() e.preventDefault()
e.stopPropagation() e.stopPropagation()
if (item.status.lock) return
// 若此时按下了 CTRL, 表示多选 // 若此时按下了 CTRL, 表示多选
if ( if (
window.$KeyboardActive?.has(WinKeyboard.CTRL_SOURCE_KEY) || window.$KeyboardActive?.has(WinKeyboard.CTRL_SOURCE_KEY) ||
@ -185,6 +188,7 @@ export const useMouseHandle = () => {
const mousedownHandle = (e: MouseEvent, item: CreateComponentType | CreateComponentGroupType) => { const mousedownHandle = (e: MouseEvent, item: CreateComponentType | CreateComponentGroupType) => {
e.preventDefault() e.preventDefault()
e.stopPropagation() e.stopPropagation()
if (item.status.lock) return
onClickOutSide() onClickOutSide()
// 按下左键 + CTRL // 按下左键 + CTRL
if ( if (

View File

@ -114,24 +114,22 @@ const optionsHandle = (
allList: MenuOptionsItemType[], allList: MenuOptionsItemType[],
targetInstance: CreateComponentType targetInstance: CreateComponentType
) => { ) => {
//
const moreMenuEnums = [MenuEnum.GROUP, MenuEnum.DELETE]
//
const singleMenuEnums = targetList
// //
if (chartEditStore.getTargetChart.selectId.length > 1) { if (chartEditStore.getTargetChart.selectId.length > 1) {
const list: MenuOptionsItemType[] = [] return allList.filter(i => [MenuEnum.GROUP, MenuEnum.DELETE].includes(i.key as MenuEnum))
allList.forEach(item => {
//
if (moreMenuEnums.includes(item.key as MenuEnum)) {
list.push(item)
} }
}) const statusMenuEnums: MenuEnum[] = []
return list if (targetInstance.status.lock) {
statusMenuEnums.push(MenuEnum.LOCK)
} else {
statusMenuEnums.push(MenuEnum.UNLOCK)
} }
return singleMenuEnums if (targetInstance.status.hide) {
statusMenuEnums.push(MenuEnum.HIDE)
} else {
statusMenuEnums.push(MenuEnum.SHOW)
}
return targetList.filter(i => !statusMenuEnums.includes(i.key as MenuEnum))
} }
// //

View File

@ -2,7 +2,7 @@
<div class="go-content-layers-group-list-item"> <div class="go-content-layers-group-list-item">
<div <div
class="root-item-content" class="root-item-content"
:class="{ hover: hover, select: select }" :class="{ hover: hover, select: select, 'list-mini': layerMode === 'text' }"
@click="clickHandle($event)" @click="clickHandle($event)"
@mousedown="groupMousedownHandle($event)" @mousedown="groupMousedownHandle($event)"
@mouseenter="mouseenterHandle(componentGroupData)" @mouseenter="mouseenterHandle(componentGroupData)"
@ -18,11 +18,13 @@
<folder-icon></folder-icon> <folder-icon></folder-icon>
</template> </template>
</n-icon> </n-icon>
<n-ellipsis> <n-ellipsis style="margin-right: auto">
<n-text class="go-ml-2 list-text" :depth="2"> <n-text class="go-ml-2 list-text" :depth="2">
{{ componentGroupData.chartConfig.title }} {{ componentGroupData.chartConfig.title }}
</n-text> </n-text>
</n-ellipsis> </n-ellipsis>
<n-icon size="12" class="list-status-icon" :component="LockClosedOutlineIcon" v-if="status.lock" />
<n-icon size="12" class="list-status-icon" :component="EyeOffOutlineIcon" v-if="status.hide" />
</div> </div>
<div :class="{ 'select-modal': select }"></div> <div :class="{ 'select-modal': select }"></div>
</div> </div>
@ -31,6 +33,7 @@
v-for="element in componentGroupData.groupList" v-for="element in componentGroupData.groupList"
:key="element.id" :key="element.id"
:componentData="element" :componentData="element"
:layer-mode="layerMode"
@mousedown="mousedownHandle($event, element, componentGroupData.id)" @mousedown="mousedownHandle($event, element, componentGroupData.id)"
@mouseenter="mouseenterHandle(element)" @mouseenter="mouseenterHandle(element)"
@mouseleave="mouseleaveHandle(element)" @mouseleave="mouseleaveHandle(element)"
@ -50,13 +53,21 @@ import { useContextMenu, divider } from '@/views/chart/hooks/useContextMenu.hook
import { MenuOptionsItemType } from '@/views/chart/hooks/useContextMenu.hook.d' import { MenuOptionsItemType } from '@/views/chart/hooks/useContextMenu.hook.d'
import { CreateComponentType, CreateComponentGroupType } from '@/packages/index.d' import { CreateComponentType, CreateComponentGroupType } from '@/packages/index.d'
import { LayersListItem } from '../LayersListItem' import { LayersListItem } from '../LayersListItem'
import throttle from 'lodash/throttle'
import { icon } from '@/plugins' import { icon } from '@/plugins'
import { LayerModeEnum } from '../../enums'
const { LockClosedOutlineIcon, EyeOffOutlineIcon } = icon.ionicons5
const props = defineProps({ const props = defineProps({
componentGroupData: { componentGroupData: {
type: Object as PropType<CreateComponentGroupType>, type: Object as PropType<CreateComponentGroupType>,
required: true required: true
},
layerMode: {
type: Object as PropType<LayerModeEnum>,
default(): LayerModeEnum {
return 'thumbnail'
}
} }
}) })
@ -84,20 +95,29 @@ const optionsHandle = (
targetInstance: CreateComponentType targetInstance: CreateComponentType
) => { ) => {
const filter = (menulist: MenuEnum[]) => { const filter = (menulist: MenuEnum[]) => {
const list: MenuOptionsItemType[] = [] return allList.filter(i => menulist.includes(i.key as MenuEnum))
allList.forEach(item => {
if (menulist.includes(item.key as MenuEnum)) {
list.push(item)
}
})
return list
} }
// //
if (chartEditStore.getTargetChart.selectId.length > 1) { if (chartEditStore.getTargetChart.selectId.length > 1) {
return filter([MenuEnum.GROUP]) return filter([MenuEnum.GROUP])
} else { } else {
return [...filter([MenuEnum.UN_GROUP]), divider(), ...targetList] const statusMenuEnums: MenuEnum[] = []
if (targetInstance.status.lock) {
statusMenuEnums.push(MenuEnum.LOCK)
} else {
statusMenuEnums.push(MenuEnum.UNLOCK)
}
if (targetInstance.status.hide) {
statusMenuEnums.push(MenuEnum.HIDE)
} else {
statusMenuEnums.push(MenuEnum.SHOW)
}
return [
...filter([MenuEnum.UN_GROUP]),
divider(),
...targetList.filter(i => !statusMenuEnums.includes(i.key as MenuEnum))
]
} }
} }
@ -125,6 +145,10 @@ const hover = computed(() => {
return props.componentGroupData.id === chartEditStore.getTargetChart.hoverId return props.componentGroupData.id === chartEditStore.getTargetChart.hoverId
}) })
const status = computed(() => {
return props.componentGroupData.status
})
// //
const groupMousedownHandle = (e: MouseEvent) => { const groupMousedownHandle = (e: MouseEvent) => {
onClickOutSide() onClickOutSide()
@ -148,7 +172,11 @@ const groupMousedownHandle = (e: MouseEvent) => {
} }
// //
const mousedownHandle = (e: MouseEvent, componentInstance: CreateComponentType | CreateComponentGroupType, id?: string) => { const mousedownHandle = (
e: MouseEvent,
componentInstance: CreateComponentType | CreateComponentGroupType,
id?: string
) => {
e.preventDefault() e.preventDefault()
e.stopPropagation() e.stopPropagation()
@ -169,6 +197,7 @@ const mouseleaveHandle = (componentInstance: CreateComponentType | CreateCompone
<style lang="scss" scoped> <style lang="scss" scoped>
$centerHeight: 52px; $centerHeight: 52px;
$centerMiniHeight: 28px;
$textSize: 10px; $textSize: 10px;
@include go(content-layers-group-list-item) { @include go(content-layers-group-list-item) {
@ -178,6 +207,11 @@ $textSize: 10px;
margin-bottom: 5px; margin-bottom: 5px;
@extend .go-transition-quick; @extend .go-transition-quick;
:deep(.go-content-layers-list-item) {
margin-right: 0 !important;
width: 95% !important;
}
.root-item-content { .root-item-content {
height: $centerHeight; height: $centerHeight;
cursor: pointer; cursor: pointer;
@ -196,6 +230,17 @@ $textSize: 10px;
border: 1px solid v-bind('themeColor') !important; border: 1px solid v-bind('themeColor') !important;
} }
} }
// mini
&.list-mini {
height: $centerMiniHeight;
.item-content {
height: calc(#{$centerMiniHeight} - 10px) !important;
}
.select-modal {
height: calc(#{$centerMiniHeight} + 2px) !important;
}
}
} }
.select-modal, .select-modal,
.item-content { .item-content {
@ -220,5 +265,9 @@ $textSize: 10px;
padding-left: 6px; padding-left: 6px;
font-size: $textSize; font-size: $textSize;
} }
.list-status-icon {
margin-left: 3px;
}
} }
</style> </style>

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="go-content-layers-list-item" :class="{ hover: hover, select: select }"> <div class="go-content-layers-list-item" :class="{ hover: hover, select: select, 'list-mini': layerMode === 'text' }">
<div class="go-flex-center item-content"> <div class="go-flex-center item-content">
<n-image <n-image
class="list-img" class="list-img"
@ -8,21 +8,27 @@
:src="image" :src="image"
:fallback-src="requireErrorImg()" :fallback-src="requireErrorImg()"
></n-image> ></n-image>
<n-ellipsis> <n-ellipsis style="margin-right: auto">
<n-text class="list-text" :depth="2"> <n-text class="list-text" :depth="2">
{{ props.componentData.chartConfig.title }} {{ props.componentData.chartConfig.title }}
</n-text> </n-text>
</n-ellipsis> </n-ellipsis>
<n-icon size="12" class="list-status-icon" :component="LockClosedOutlineIcon" v-if="status.lock" />
<n-icon size="12" class="list-status-icon" :component="EyeOffOutlineIcon" v-if="status.hide" />
</div> </div>
<div :class="{ 'select-modal': select }"></div> <div :class="{ 'select-modal': select }"></div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { toRefs, computed } from 'vue' import { toRefs, computed, PropType } from 'vue'
import { requireErrorImg } from '@/utils' import { requireErrorImg } from '@/utils'
import { useDesignStore } from '@/store/modules/designStore/designStore' import { useDesignStore } from '@/store/modules/designStore/designStore'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { LayerModeEnum } from '../../enums'
import { icon } from '@/plugins'
const { LockClosedOutlineIcon, EyeOffOutlineIcon } = icon.ionicons5
// //
const designStore = useDesignStore() const designStore = useDesignStore()
@ -37,6 +43,12 @@ const props = defineProps({
componentData: { componentData: {
type: Object, type: Object,
required: true required: true
},
layerMode: {
type: Object as PropType<LayerModeEnum>,
default(): LayerModeEnum {
return 'thumbnail'
}
} }
}) })
@ -52,10 +64,15 @@ const select = computed(() => {
const hover = computed(() => { const hover = computed(() => {
return props.componentData.id === chartEditStore.getTargetChart.hoverId return props.componentData.id === chartEditStore.getTargetChart.hoverId
}) })
const status = computed(() => {
return props.componentData.status
})
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
$centerHeight: 52px; $centerHeight: 52px;
$centerMiniHeight: 28px;
$textSize: 10px; $textSize: 10px;
@include go(content-layers-list-item) { @include go(content-layers-list-item) {
@ -72,15 +89,7 @@ $textSize: 10px;
&:hover { &:hover {
@include fetch-bg-color('background-color4'); @include fetch-bg-color('background-color4');
} }
/* 选中 */
&.select {
border: 1px solid v-bind('themeColor');
/* 需要设置最高级,覆盖 hover 的颜色 */
background-color: rgba(0, 0, 0, 0);
.list-img {
border: 1px solid v-bind('themeColor') !important;
}
}
.select-modal, .select-modal,
.item-content { .item-content {
position: absolute; position: absolute;
@ -94,24 +103,46 @@ $textSize: 10px;
width: calc(100% - 10px); width: calc(100% - 10px);
height: calc(100% - 10px); height: calc(100% - 10px);
} }
.select-modal { .select-modal {
width: 100%; width: 100%;
height: 100%; height: 100%;
opacity: 0.3; opacity: 0.3;
background-color: v-bind('themeColor'); background-color: v-bind('themeColor');
} }
.list-img { .list-img {
flex-shrink: 0; flex-shrink: 0;
height: $centerHeight; height: $centerHeight;
border-radius: 5px; border-radius: 5px;
overflow: hidden; overflow: hidden;
border: 1px solid; border: none !important;
padding: 2px; padding: 2px;
@include hover-border-color('hover-border-color'); @include hover-border-color('hover-border-color');
} }
.list-text { .list-text {
padding-left: 6px; padding-left: 6px;
font-size: $textSize; font-size: $textSize;
} }
.list-status-icon {
margin-left: 3px;
}
/* 选中样式 */
&.select {
border: 1px solid v-bind('themeColor');
/* 需要设置最高级,覆盖 hover 的颜色 */
background-color: rgba(0, 0, 0, 0);
// .list-img {
// border: 1px solid v-bind('themeColor') !important;
// }
}
// mini
&.list-mini {
height: $centerMiniHeight;
}
} }
</style> </style>

View File

@ -0,0 +1 @@
export type LayerModeEnum = 'thumbnail' | 'text'

View File

@ -8,24 +8,49 @@
@mousedown="boxMousedownHandle($event)" @mousedown="boxMousedownHandle($event)"
> >
<template #icon> <template #icon>
<n-icon size="16" :depth="2"> <n-icon size="16" :depth="2" :component="LayersIcon" />
<component :is="LayersIcon"></component>
</n-icon>
</template> </template>
<template #top-right>
<n-button-group style="display: flex">
<n-button
v-for="(item, index) in layerModeEnumList"
:key="index"
ghost
size="tiny"
:type="layerMode === item.value ? 'primary' : 'tertiary'"
@click="layerMode = item.value as LayerModeEnum"
>
<n-tooltip :show-arrow="false" trigger="hover">
<template #trigger>
<n-icon size="14" :component="item.icon" />
</template>
{{ item.label }}
</n-tooltip>
</n-button>
</n-button-group>
</template>
<!-- 图层内容 --> <!-- 图层内容 -->
<n-space v-if="reverseList.length === 0" justify="center"> <n-space v-if="reverseList.length === 0" justify="center">
<n-text class="not-layer-text">暂无图层~</n-text> <n-text class="not-layer-text">暂无图层~</n-text>
</n-space> </n-space>
<!-- https://github.com/SortableJS/vue.draggable.next --> <!-- https://github.com/SortableJS/vue.draggable.next -->
<draggable item-key="id" v-model="layerList" ghostClass="ghost" @change="onMoveCallback"> <draggable item-key="id" v-model="layerList" ghostClass="ghost" @change="onMoveCallback">
<template #item="{ element }"> <template #item="{ element }">
<div class="go-content-layer-box"> <div class="go-content-layer-box">
<!-- 组合 --> <!-- 组合 -->
<layers-group-list-item v-if="element.isGroup" :componentGroupData="element"></layers-group-list-item> <layers-group-list-item
v-if="element.isGroup"
:componentGroupData="element"
:layer-mode="layerMode"
></layers-group-list-item>
<!-- 单组件 --> <!-- 单组件 -->
<layers-list-item <layers-list-item
v-else v-else
:componentData="element" :componentData="element"
:layer-mode="layerMode"
@mousedown="mousedownHandle($event, element)" @mousedown="mousedownHandle($event, element)"
@mouseenter="mouseenterHandle(element)" @mouseenter="mouseenterHandle(element)"
@mouseleave="mouseleaveHandle(element)" @mouseleave="mouseleaveHandle(element)"
@ -52,15 +77,21 @@ import { MenuEnum, MouseEventButton, WinKeyboard, MacKeyboard } from '@/enums/ed
import { LayersListItem } from './components/LayersListItem/index' import { LayersListItem } from './components/LayersListItem/index'
import { LayersGroupListItem } from './components/LayersGroupListItem/index' import { LayersGroupListItem } from './components/LayersGroupListItem/index'
import { LayerModeEnum } from './enums'
import { icon } from '@/plugins' import { icon } from '@/plugins'
const { LayersIcon } = icon.ionicons5 const { LayersIcon, ImageIcon, ImagesIcon, ListIcon } = icon.ionicons5
const chartLayoutStore = useChartLayoutStore() const chartLayoutStore = useChartLayoutStore()
const chartEditStore = useChartEditStore() const chartEditStore = useChartEditStore()
const { handleContextMenu, onClickOutSide } = useContextMenu() const { handleContextMenu, onClickOutSide } = useContextMenu()
const layerList = ref<any>([]) const layerList = ref<any>([])
const layerModeEnumList = [
{ label: '缩略图', icon: ImagesIcon, value: 'thumbnail' },
{ label: '文本列表', icon: ListIcon, value: 'text' }
]
const layerMode = ref<LayerModeEnum>('thumbnail')
// //
const reverseList = computed(() => { const reverseList = computed(() => {
@ -83,16 +114,20 @@ const optionsHandle = (
) => { ) => {
// //
if (chartEditStore.getTargetChart.selectId.length > 1) { if (chartEditStore.getTargetChart.selectId.length > 1) {
const list: MenuOptionsItemType[] = [] return targetList.filter(i => i.key === MenuEnum.GROUP)
targetList.forEach(item => {
//
if (item.key === MenuEnum.GROUP) {
list.push(item)
} }
}) const statusMenuEnums: MenuEnum[] = []
return list if (targetInstance.status.lock) {
statusMenuEnums.push(MenuEnum.LOCK)
} else {
statusMenuEnums.push(MenuEnum.UNLOCK)
} }
return targetList if (targetInstance.status.hide) {
statusMenuEnums.push(MenuEnum.HIDE)
} else {
statusMenuEnums.push(MenuEnum.SHOW)
}
return targetList.filter(item => !statusMenuEnums.includes(item.key as MenuEnum))
} }
// //
@ -159,7 +194,7 @@ const mouseleaveHandle = (item: CreateComponentType) => {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
$wight: 170px; $wight: 200px;
@include go(content-layers) { @include go(content-layers) {
width: $wight; width: $wight;
flex-shrink: 0; flex-shrink: 0;
@ -177,5 +212,8 @@ $wight: 170px;
.ghost { .ghost {
opacity: 0; opacity: 0;
} }
.go-layer-mode-active {
color: #51d6a9;
}
} }
</style> </style>

View File

@ -7,7 +7,18 @@ import { MenuOptionsItemType } from './useContextMenu.hook.d'
import { MenuEnum } from '@/enums/editPageEnum' import { MenuEnum } from '@/enums/editPageEnum'
import cloneDeep from 'lodash/cloneDeep' import cloneDeep from 'lodash/cloneDeep'
const { CopyIcon, CutIcon, ClipboardOutlineIcon, TrashIcon, ChevronDownIcon, ChevronUpIcon } = icon.ionicons5 const {
CopyIcon,
CutIcon,
ClipboardOutlineIcon,
TrashIcon,
ChevronDownIcon,
ChevronUpIcon,
LockOpenOutlineIcon,
LockClosedOutlineIcon,
EyeOutlineIcon,
EyeOffOutlineIcon
} = icon.ionicons5
const { UpToTopIcon, DownToBottomIcon, PaintBrushIcon, Carbon3DSoftwareIcon, Carbon3DCursorIcon } = icon.carbon const { UpToTopIcon, DownToBottomIcon, PaintBrushIcon, Carbon3DSoftwareIcon, Carbon3DCursorIcon } = icon.carbon
const chartEditStore = useChartEditStore() const chartEditStore = useChartEditStore()
@ -17,7 +28,7 @@ const chartEditStore = useChartEditStore()
* @param {number} n > 2 * @param {number} n > 2
* @returns * @returns
*/ */
export const divider = (n:number = 3) => { export const divider = (n: number = 3) => {
return { return {
type: 'divider', type: 'divider',
key: `d${n}` key: `d${n}`
@ -26,6 +37,34 @@ export const divider = (n:number = 3) => {
// * 默认单组件选项 // * 默认单组件选项
export const defaultOptions: MenuOptionsItemType[] = [ export const defaultOptions: MenuOptionsItemType[] = [
{
label: '锁定',
key: MenuEnum.LOCK,
icon: renderIcon(LockClosedOutlineIcon),
fnHandle: chartEditStore.setLock
},
{
label: '解除锁定',
key: MenuEnum.UNLOCK,
icon: renderIcon(LockOpenOutlineIcon),
fnHandle: chartEditStore.setUnLock
},
{
label: '隐藏',
key: MenuEnum.HIDE,
icon: renderIcon(EyeOffOutlineIcon),
fnHandle: chartEditStore.setHide
},
{
label: '显示',
key: MenuEnum.SHOW,
icon: renderIcon(EyeOutlineIcon),
fnHandle: chartEditStore.setShow
},
{
type: 'divider',
key: 'd0'
},
{ {
label: '复制', label: '复制',
key: MenuEnum.COPY, key: MenuEnum.COPY,