This commit is contained in:
xingyu4j
2025-11-03 18:09:01 +08:00
3 changed files with 48 additions and 135 deletions

View File

@@ -93,15 +93,11 @@ const handleDeleteComponent = () => {
}; };
</script> </script>
<template> <template>
<div class="component" :class="[{ active }]"> <div class="component relative cursor-move" :class="[{ active }]">
<div <div :style="style">
:style="{
...style,
}"
>
<component :is="component.id" :property="component.property" /> <component :is="component.id" :property="component.property" />
</div> </div>
<div class="component-wrap"> <div class="component-wrap absolute left-[-2px] top-0 block h-full w-full">
<!-- 左侧组件名悬浮的小贴条 --> <!-- 左侧组件名悬浮的小贴条 -->
<div class="component-name" v-if="component.name"> <div class="component-name" v-if="component.name">
{{ component.name }} {{ component.name }}
@@ -150,19 +146,8 @@ $hover-border-width: 1px;
$name-position: -85px; $name-position: -85px;
$toolbar-position: -55px; $toolbar-position: -55px;
/* 组件 */
.component { .component {
position: relative;
cursor: move;
.component-wrap { .component-wrap {
position: absolute;
top: 0;
left: -$active-border-width;
display: block;
width: 100%;
height: 100%;
/* 鼠标放到组件上时 */ /* 鼠标放到组件上时 */
&:hover { &:hover {
border: $hover-border-width dashed var(--el-color-primary); border: $hover-border-width dashed var(--el-color-primary);
@@ -228,7 +213,7 @@ $toolbar-position: -55px;
} }
} }
/* 组件选中时 */ /* 选中状态 */
&.active { &.active {
margin-bottom: 4px; margin-bottom: 4px;

View File

@@ -61,7 +61,10 @@ const handleCloneComponent = (component: DiyComponent<any>) => {
</script> </script>
<template> <template>
<ElAside class="editor-left" width="261px"> <ElAside
class="editor-left z-[1] shrink-0 select-none shadow-[8px_0_8px_-8px_rgb(0_0_0/0.12)]"
width="261px"
>
<ElScrollbar> <ElScrollbar>
<ElCollapse v-model="extendGroups"> <ElCollapse v-model="extendGroups">
<ElCollapseItem <ElCollapseItem
@@ -71,7 +74,7 @@ const handleCloneComponent = (component: DiyComponent<any>) => {
:title="group.name" :title="group.name"
> >
<draggable <draggable
class="component-container" class="flex flex-wrap items-center"
ghost-class="draggable-ghost" ghost-class="draggable-ghost"
item-key="index" item-key="index"
:list="group.components" :list="group.components"
@@ -83,9 +86,18 @@ const handleCloneComponent = (component: DiyComponent<any>) => {
> >
<template #item="{ element }"> <template #item="{ element }">
<div> <div>
<div class="drag-placement">组件放置区域</div> <div class="hidden text-white">组件放置区域</div>
<div class="component"> <div
<IconifyIcon :icon="element.icon" :size="32" /> class="component flex h-[86px] w-[86px] cursor-move flex-col items-center justify-center border-b border-r [&:nth-of-type(3n)]:border-r-0"
:style="{
borderColor: 'var(--el-border-color-lighter)',
}"
>
<IconifyIcon
:icon="element.icon"
:size="32"
class="mb-1 text-gray-500"
/>
<span class="mt-1 text-xs">{{ element.name }}</span> <span class="mt-1 text-xs">{{ element.name }}</span>
</div> </div>
</div> </div>
@@ -99,11 +111,6 @@ const handleCloneComponent = (component: DiyComponent<any>) => {
<style scoped lang="scss"> <style scoped lang="scss">
.editor-left { .editor-left {
z-index: 1;
flex-shrink: 0;
user-select: none;
box-shadow: 8px 0 8px -8px rgb(0 0 0 / 12%);
:deep(.el-collapse) { :deep(.el-collapse) {
border-top: none; border-top: none;
} }
@@ -124,50 +131,19 @@ const handleCloneComponent = (component: DiyComponent<any>) => {
border-bottom: none; border-bottom: none;
} }
.component-container { /* 组件 hover 和 active 状态(需要 CSS 变量)*/
display: flex;
flex-wrap: wrap;
align-items: center;
}
.component {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 86px;
height: 86px;
cursor: move;
border-right: 1px solid var(--el-border-color-lighter);
border-bottom: 1px solid var(--el-border-color-lighter);
.el-icon {
margin-bottom: 4px;
color: gray;
}
}
.component.active, .component.active,
.component:hover { .component:hover {
color: var(--el-color-white); color: var(--el-color-white);
background: var(--el-color-primary); background: var(--el-color-primary);
.el-icon { :deep(.iconify) {
color: var(--el-color-white); color: var(--el-color-white);
} }
} }
.component:nth-of-type(3n) {
border-right: none;
}
}
/* 拖拽占位提示,默认不显示 */
.drag-placement {
display: none;
color: #fff;
} }
/* 拖拽区域全局样式 */
.drag-area { .drag-area {
/* 拖拽到手机区域时的样式 */ /* 拖拽到手机区域时的样式 */
.draggable-ghost { .draggable-ghost {
@@ -203,14 +179,12 @@ const handleCloneComponent = (component: DiyComponent<any>) => {
background: #5487df; background: #5487df;
} }
/* 拖拽时隐藏组件 */
.component { .component {
display: none; display: none; /* 拖拽时隐藏组件 */
} }
/* 拖拽时显示占位提示 */ .hidden {
.drag-placement { display: block !important; /* 拖拽时显示占位提示 */
display: block;
} }
} }
} }

View File

@@ -304,9 +304,15 @@ onMounted(() => {
</script> </script>
<template> <template>
<div> <div>
<ElContainer class="editor"> <ElContainer class="editor flex h-full flex-col">
<!-- 顶部工具栏 --> <!-- 顶部工具栏 -->
<ElHeader class="editor-header"> <ElHeader
class="editor-header flex !h-[42px] items-center justify-between !border-b !p-0"
:style="{
backgroundColor: 'var(--el-bg-color)',
borderBottomColor: 'var(--el-border-color)',
}"
>
<!-- 左侧操作区 --> <!-- 左侧操作区 -->
<slot name="toolBarLeft"></slot> <slot name="toolBarLeft"></slot>
<!-- 中心操作区 --> <!-- 中心操作区 -->
@@ -334,7 +340,7 @@ onMounted(() => {
</ElHeader> </ElHeader>
<!-- 中心区域 --> <!-- 中心区域 -->
<ElContainer class="editor-container"> <ElContainer class="editor-container h-[calc(100vh-135px)]">
<!-- 左侧组件库ComponentLibrary --> <!-- 左侧组件库ComponentLibrary -->
<ElAside width="261px" class="editor-left"> <ElAside width="261px" class="editor-left">
<ComponentLibrary <ComponentLibrary
@@ -344,11 +350,15 @@ onMounted(() => {
/> />
</ElAside> </ElAside>
<!-- 中心设计区域ComponentContainer --> <!-- 中心设计区域ComponentContainer -->
<div class="editor-center page-prop-area" @click="handlePageSelected"> <div
class="editor-center page-prop-area relative mt-4 flex w-full flex-1 flex-col justify-center overflow-hidden"
:style="{ backgroundColor: 'var(--app-content-bg-color)' }"
@click="handlePageSelected"
>
<!-- 手机顶部 --> <!-- 手机顶部 -->
<div class="editor-design-top"> <div class="editor-design-top mx-auto flex w-[375px] flex-col">
<!-- 手机顶部状态栏 --> <!-- 手机顶部状态栏 -->
<img alt="" class="status-bar" :src="statusBarImg" /> <img alt="" class="h-5 w-[375px] bg-white" :src="statusBarImg" />
<!-- 手机顶部导航栏 --> <!-- 手机顶部导航栏 -->
<ComponentContainer <ComponentContainer
v-if="showNavigationBar" v-if="showNavigationBar"
@@ -415,7 +425,7 @@ onMounted(() => {
<!-- 手机底部导航 --> <!-- 手机底部导航 -->
<div <div
v-if="showTabBar" v-if="showTabBar"
class="editor-design-bottom component cursor-pointer" class="editor-design-bottom component mx-auto w-[375px] cursor-pointer"
> >
<ComponentContainer <ComponentContainer
:active="selectedComponent?.id === tabBarComponent.id" :active="selectedComponent?.id === tabBarComponent.id"
@@ -425,7 +435,9 @@ onMounted(() => {
/> />
</div> </div>
<!-- 固定布局的组件 操作按钮区 --> <!-- 固定布局的组件 操作按钮区 -->
<div class="fixed-component-action-group"> <div
class="fixed-component-action-group absolute right-4 top-0 flex flex-col gap-2"
>
<ElTag <ElTag
v-if="showPageConfig" v-if="showPageConfig"
:effect=" :effect="
@@ -467,7 +479,7 @@ onMounted(() => {
<!-- 右侧属性面板ComponentContainerProperty --> <!-- 右侧属性面板ComponentContainerProperty -->
<ElAside <ElAside
v-if="selectedComponent?.property" v-if="selectedComponent?.property"
class="editor-right" class="editor-right shrink-0 overflow-hidden shadow-[-8px_0_8px_-8px_rgb(0_0_0/0.12)]"
width="350px" width="350px"
> >
<ElCard <ElCard
@@ -516,22 +528,8 @@ onMounted(() => {
/* 手机宽度 */ /* 手机宽度 */
$phone-width: 375px; $phone-width: 375px;
/* 根节点样式 */
.editor { .editor {
display: flex;
flex-direction: column;
height: 100%;
/* 顶部:工具栏 */
.editor-header { .editor-header {
display: flex;
align-items: center;
justify-content: space-between;
height: 42px;
padding: 0;
background-color: var(--el-bg-color);
border-bottom: solid 1px var(--el-border-color);
/* 隐藏工具栏按钮的边框 */ /* 隐藏工具栏按钮的边框 */
:deep(.el-radio-button__inner), :deep(.el-radio-button__inner),
:deep(.el-button) { :deep(.el-button) {
@@ -543,14 +541,8 @@ $phone-width: 375px;
/* 中心操作区 */ /* 中心操作区 */
.editor-container { .editor-container {
height: calc(100vh - 135px);
/* 右侧属性面板 */ /* 右侧属性面板 */
:deep(.editor-right) { :deep(.editor-right) {
flex-shrink: 0;
overflow: hidden;
box-shadow: -8px 0 8px -8px rgb(0 0 0 / 12%);
/* 属性面板顶部:减少内边距 */ /* 属性面板顶部:减少内边距 */
:deep(.el-card__header) { :deep(.el-card__header) {
padding: 8px 16px; padding: 8px 16px;
@@ -579,37 +571,6 @@ $phone-width: 375px;
/* 中心区域 */ /* 中心区域 */
.editor-center { .editor-center {
position: relative;
display: flex;
flex: 1 1 0;
flex-direction: column;
justify-content: center;
width: 100%;
margin: 16px 0 0;
overflow: hidden;
background-color: var(--app-content-bg-color);
/* 手机顶部 */
.editor-design-top {
display: flex;
flex-direction: column;
width: $phone-width;
margin: 0 auto;
/* 手机顶部状态栏 */
.status-bar {
width: $phone-width;
height: 20px;
background-color: #fff;
}
}
/* 手机底部导航 */
.editor-design-bottom {
width: $phone-width;
margin: 0 auto;
}
/* 手机页面编辑区域 */ /* 手机页面编辑区域 */
:deep(.editor-design-center) { :deep(.editor-design-center) {
width: 100%; width: 100%;
@@ -632,13 +593,6 @@ $phone-width: 375px;
/* 固定布局的组件 操作按钮区 */ /* 固定布局的组件 操作按钮区 */
.fixed-component-action-group { .fixed-component-action-group {
position: absolute;
top: 0;
right: 16px;
display: flex;
flex-direction: column;
gap: 8px;
:deep(.el-tag) { :deep(.el-tag) {
border: none; border: none;
box-shadow: 0 2px 8px 0 rgb(0 0 0 / 10%); box-shadow: 0 2px 8px 0 rgb(0 0 0 / 10%);