mirror of
https://gitee.com/dromara/go-view.git
synced 2026-04-23 00:00:12 +08:00
feat: 新增复制粘贴功能
This commit is contained in:
@@ -1,19 +1,17 @@
|
||||
<template>
|
||||
<div class="go-edit-bottom">
|
||||
<n-space>
|
||||
<n-text>
|
||||
滤镜设置
|
||||
</n-text>
|
||||
<History />
|
||||
|
||||
<n-space class="bottom-ri">
|
||||
<!-- 快捷键提示 -->
|
||||
<n-popselect :options="shortcutKeyOptions" size="medium">
|
||||
<n-button class="scale-btn" secondary size="mini">
|
||||
<n-icon class="lock-icon" size="18" :depth="3">
|
||||
<n-button class="scale-btn" quaternary size="mini">
|
||||
<n-icon class="lock-icon" size="18" :depth="2">
|
||||
<DicomOverlayIcon />
|
||||
</n-icon>
|
||||
</n-button>
|
||||
</n-popselect>
|
||||
</n-space>
|
||||
<n-space class="bottom-ri">
|
||||
|
||||
<!-- 缩放比例 -->
|
||||
<n-select
|
||||
:disabled="lockScale"
|
||||
@@ -62,13 +60,14 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref, toRefs, watchEffect } from 'vue'
|
||||
import { icon } from '@/plugins'
|
||||
import { History } from '../History/index'
|
||||
import { useDesignStore } from '@/store/modules/designStore/designStore'
|
||||
const { LockClosedOutlineIcon, LockOpenOutlineIcon } = icon.ionicons5
|
||||
const { DicomOverlayIcon } = icon.carbon
|
||||
import {
|
||||
getChartEditStore,
|
||||
getChartEditStoreEnum
|
||||
} from '../../hooks/useStore.hook'
|
||||
import { useDesignStore } from '@/store/modules/designStore/designStore'
|
||||
|
||||
// 全局颜色
|
||||
const designStore = useDesignStore()
|
||||
@@ -141,9 +140,25 @@ const shortcutKeyOptions = [
|
||||
value: '1'
|
||||
},
|
||||
{
|
||||
label: 'Ctrl + C 复制',
|
||||
label: 'Delete 删除',
|
||||
value: '2'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Ctrl + C 复制',
|
||||
value: '3'
|
||||
},
|
||||
{
|
||||
label: 'Ctrl + X 剪切',
|
||||
value: '4'
|
||||
},
|
||||
{
|
||||
label: 'Ctrl + Z 后退',
|
||||
value: '5'
|
||||
},
|
||||
{
|
||||
label: 'Ctrl + Shift + Z 前进',
|
||||
value: '6'
|
||||
},
|
||||
]
|
||||
|
||||
// 监听 scale 变化
|
||||
@@ -157,7 +172,7 @@ watchEffect(() => {
|
||||
<style lang="scss" scoped>
|
||||
@include go(edit-bottom) {
|
||||
width: 100%;
|
||||
padding: 0 20px;
|
||||
padding: 0 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
import History from './index.vue'
|
||||
|
||||
export { History }
|
||||
@@ -0,0 +1,129 @@
|
||||
<template>
|
||||
<div class="go-edit-history go-flex-items-center">
|
||||
<n-dropdown
|
||||
@select="handleSelect"
|
||||
:show="showDropdownRef"
|
||||
:options="options"
|
||||
scrollable
|
||||
size="small"
|
||||
placement="top-start"
|
||||
style="max-height: 100vh; overflow-y: auto;"
|
||||
>
|
||||
<n-button
|
||||
class="mr-10"
|
||||
secondary
|
||||
size="small"
|
||||
:disabled="options.length === 0"
|
||||
@click="handleClick"
|
||||
>
|
||||
<span class="btn-text">历史记录</span>
|
||||
<!-- <n-icon class="lock-icon" size="18" :depth="2">
|
||||
<TimeOutlineIcon />
|
||||
</n-icon> -->
|
||||
</n-button>
|
||||
</n-dropdown>
|
||||
|
||||
<n-tooltip trigger="hover">
|
||||
<template #trigger>
|
||||
<n-icon size="21" :depth="3">
|
||||
<HelpOutlineIcon />
|
||||
</n-icon>
|
||||
</template>
|
||||
<span>最多只保留 20 条记录</span>
|
||||
</n-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { icon } from '@/plugins'
|
||||
import { renderIcon } from '@/utils'
|
||||
import { useChartHistoryStoreStore } from '@/store/modules/chartHistoryStore/chartHistoryStore'
|
||||
import { historyActionTypeName } from '@/store/modules/chartHistoryStore/chartHistoryDefine'
|
||||
import { CreateComponentType } from '@/packages/index.d'
|
||||
import {
|
||||
HistoryItemType,
|
||||
HistoryTargetTypeEnum,
|
||||
HistoryActionTypeEnum
|
||||
} from '@/store/modules/chartHistoryStore/chartHistoryStore.d'
|
||||
|
||||
const {
|
||||
TimeOutlineIcon,
|
||||
DesktopOutlineIcon,
|
||||
PencilIcon,
|
||||
TrashIcon,
|
||||
CopyIcon,
|
||||
LayersIcon,
|
||||
DuplicateIcon,
|
||||
HelpOutlineIcon
|
||||
} = icon.ionicons5
|
||||
const { StackedMoveIcon } = icon.carbon
|
||||
const showDropdownRef = ref(false)
|
||||
|
||||
const chartHistoryStoreStore = useChartHistoryStoreStore()
|
||||
|
||||
// 设置类型对应图标
|
||||
const iconHandle = (e: HistoryItemType) => {
|
||||
// 画布编辑
|
||||
if (e.targetType === HistoryTargetTypeEnum.CANVAS) {
|
||||
return renderIcon(DesktopOutlineIcon)
|
||||
}
|
||||
switch (e.actionType) {
|
||||
case HistoryActionTypeEnum.UPDATE:
|
||||
return renderIcon(PencilIcon)
|
||||
case HistoryActionTypeEnum.DELETE:
|
||||
return renderIcon(TrashIcon)
|
||||
case HistoryActionTypeEnum.PASTE:
|
||||
return renderIcon(CopyIcon)
|
||||
case HistoryActionTypeEnum.LARYER:
|
||||
return renderIcon(LayersIcon)
|
||||
case HistoryActionTypeEnum.MOVE:
|
||||
return renderIcon(StackedMoveIcon)
|
||||
case HistoryActionTypeEnum.ADD:
|
||||
return renderIcon(DuplicateIcon)
|
||||
default:
|
||||
return renderIcon(PencilIcon)
|
||||
}
|
||||
}
|
||||
|
||||
// 设置类型对应文本
|
||||
const labelHandle = (e: HistoryItemType) => {
|
||||
// 画布编辑
|
||||
if (e.targetType === HistoryTargetTypeEnum.CANVAS) {
|
||||
return historyActionTypeName[HistoryTargetTypeEnum.CANVAS]
|
||||
}
|
||||
return `${historyActionTypeName[e.actionType]} - ${
|
||||
(e.historyData as CreateComponentType).chartData.title
|
||||
}`
|
||||
}
|
||||
|
||||
const options = computed(() => {
|
||||
const backStack: HistoryItemType[] = chartHistoryStoreStore.getBackStack
|
||||
const options = backStack.map((e: HistoryItemType) => {
|
||||
return {
|
||||
label: labelHandle(e),
|
||||
key: e.id,
|
||||
icon: iconHandle(e)
|
||||
}
|
||||
})
|
||||
return options
|
||||
})
|
||||
|
||||
const handleClick = () => {
|
||||
showDropdownRef.value = !showDropdownRef.value
|
||||
}
|
||||
const handleSelect = (key: string) => {}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@include go(edit-history) {
|
||||
max-height: 50vh;
|
||||
.mr-10 {
|
||||
margin-right: 10px;
|
||||
}
|
||||
.btn-text {
|
||||
font-size: 12px;
|
||||
margin-right: 3px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -29,7 +29,7 @@ export const handleDrop = async (e: DragEvent) => {
|
||||
let newComponent:CreateComponentType = await createComponent(dropData)
|
||||
|
||||
newComponent.setPosition(e.offsetX - newComponent.attr.w / 2, e.offsetY - newComponent.attr.h / 2)
|
||||
chartEditStore.addComponentList(newComponent)
|
||||
chartEditStore.addComponentList(newComponent, false, true)
|
||||
chartEditStore.setTargetSelectChart(newComponent.id)
|
||||
loadingFinish()
|
||||
} catch (error) {
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import { isMac, addEventListener, removeEventListener } from '@/utils'
|
||||
import { getChartEditStore } from './useStore.hook'
|
||||
|
||||
const chartEditStore = getChartEditStore()
|
||||
|
||||
const KeyboardHandle = (e: KeyboardEvent) => {
|
||||
const ismacRes = isMac()
|
||||
|
||||
// 暂不支持mac,因为我没有😤👻
|
||||
if(ismacRes) return
|
||||
const key = e.key.toLowerCase()
|
||||
|
||||
if (key === 'delete') {
|
||||
chartEditStore.removeComponentList()
|
||||
return
|
||||
}
|
||||
if (e.ctrlKey) {
|
||||
switch (key) {
|
||||
// 复制
|
||||
case 'c': chartEditStore.setCopy()
|
||||
break;
|
||||
// 粘贴
|
||||
case 'v': chartEditStore.setParse()
|
||||
break;
|
||||
}
|
||||
e.preventDefault()
|
||||
}
|
||||
}
|
||||
|
||||
export const useAddKeyboard = () => {
|
||||
addEventListener(document, 'keyup', KeyboardHandle)
|
||||
}
|
||||
|
||||
export const useRemoveKeyboard = () => {
|
||||
removeEventListener(document, 'keyup', KeyboardHandle)
|
||||
}
|
||||
@@ -50,6 +50,7 @@ import { EditBottom } from './components/EditBottom'
|
||||
import { ShapeBox } from './components/ShapeBox/index'
|
||||
|
||||
import { useLayout } from './hooks/useLayout.hook'
|
||||
import { useAddKeyboard, useRemoveKeyboard } from './hooks/useKeyboard.hook'
|
||||
import { handleDrop, handleDragOver, useMouseHandle } from './hooks/useDrop.hook'
|
||||
import { useContextMenu } from '@/views/chart/hooks/useContextMenu.hook'
|
||||
import { getChartEditStore } from './hooks/useStore.hook'
|
||||
@@ -66,6 +67,12 @@ useLayout()
|
||||
// 点击事件
|
||||
const editRangeRef = ref<HTMLElement | null>(null)
|
||||
const { mouseenterHandle, mouseleaveHandle, mousedownHandle } = useMouseHandle()
|
||||
|
||||
// 键盘事件
|
||||
onMounted(() => {
|
||||
useAddKeyboard()
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -27,6 +27,7 @@ import { icon } from '@/plugins'
|
||||
const { LayersIcon, BarChartIcon, PrismIcon, HomeIcon } = icon.ionicons5
|
||||
import { useChartLayoutStore } from '@/store/modules/chartLayoutStore/chartLayoutStore'
|
||||
import { ChartLayoutStoreEnum } from '@/store/modules/chartLayoutStore/chartLayoutStore.d'
|
||||
import { useRemoveKeyboard } from '../ContentEdit/hooks/useKeyboard.hook'
|
||||
|
||||
const { setItem } = useChartLayoutStore()
|
||||
const { getLayers, getCharts, getDetails } = toRefs(useChartLayoutStore())
|
||||
@@ -76,7 +77,10 @@ const goHomeHandle = () => {
|
||||
goDialog({
|
||||
message: '返回将不会保存任何操作',
|
||||
isMaskClosable: true,
|
||||
onPositiveCallback: goHome
|
||||
onPositiveCallback: () => {
|
||||
goHome()
|
||||
useRemoveKeyboard()
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { CreateComponentType } from '@/packages/index.d'
|
||||
import { renderIcon, loadingError } from '@/utils'
|
||||
import { icon } from '@/plugins'
|
||||
|
||||
const { CopyIcon, TrashIcon, ChevronDownIcon, ChevronUpIcon } = icon.ionicons5
|
||||
const { CopyIcon, ClipboardOutlineIcon, TrashIcon, ChevronDownIcon, ChevronUpIcon } = icon.ionicons5
|
||||
const { UpToTopIcon, DownToBottomIcon } = icon.carbon
|
||||
|
||||
const chartEditStore = useChartEditStoreStore()
|
||||
@@ -12,6 +12,7 @@ const chartEditStore = useChartEditStoreStore()
|
||||
enum MenuEnum {
|
||||
DELETE = 'delete',
|
||||
COPY = 'copy',
|
||||
PARSE = 'parse',
|
||||
TOP = 'top',
|
||||
BOTTOM = 'bottom',
|
||||
UP = 'up',
|
||||
@@ -32,7 +33,13 @@ const defaultOptions: MenuOptionsItemType[] = [
|
||||
label: '复制',
|
||||
key: MenuEnum.COPY,
|
||||
icon: renderIcon(CopyIcon),
|
||||
fnHandle: () => {}
|
||||
fnHandle: chartEditStore.setCopy
|
||||
},
|
||||
{
|
||||
label: '粘贴',
|
||||
key: MenuEnum.PARSE,
|
||||
icon: renderIcon(ClipboardOutlineIcon),
|
||||
fnHandle: chartEditStore.setParse
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
|
||||
@@ -39,6 +39,14 @@ import { loadAsyncComponent } from '@/utils'
|
||||
import { HeaderPro } from '@/layout/components/HeaderPro'
|
||||
import { useContextMenu } from './hooks/useContextMenu.hook'
|
||||
import { useChartEditStoreStore } from '@/store/modules/chartEditStore/chartEditStore'
|
||||
|
||||
import { useChartLayoutStore } from '@/store/modules/chartLayoutStore/chartLayoutStore'
|
||||
import { useChartHistoryStoreStore } from '@/store/modules/chartHistoryStore/chartHistoryStore'
|
||||
const chartLayoutStore = useChartLayoutStore()
|
||||
const chartHistoryStoreStore = useChartHistoryStoreStore()
|
||||
// 记录初始化
|
||||
chartHistoryStoreStore.canvasInit(chartLayoutStore)
|
||||
|
||||
const chartEditStore = useChartEditStoreStore()
|
||||
|
||||
const HeaderLeftBtn = loadAsyncComponent(() =>
|
||||
@@ -47,9 +55,7 @@ const HeaderLeftBtn = loadAsyncComponent(() =>
|
||||
const HeaderRightBtn = loadAsyncComponent(() =>
|
||||
import('./HeaderRightBtn/index.vue')
|
||||
)
|
||||
const HeaderTitle = loadAsyncComponent(() =>
|
||||
import('./HeaderTitle/index.vue')
|
||||
)
|
||||
const HeaderTitle = loadAsyncComponent(() => import('./HeaderTitle/index.vue'))
|
||||
const ContentLayers = loadAsyncComponent(() =>
|
||||
import('./ContentLayers/index.vue')
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user