提交 | 用户 | 时间
|
820397
|
1 |
<template> |
H |
2 |
<el-container class="editor"> |
|
3 |
<!-- 顶部:工具栏 --> |
|
4 |
<el-header class="editor-header"> |
|
5 |
<!-- 左侧操作区 --> |
|
6 |
<slot name="toolBarLeft"></slot> |
|
7 |
<!-- 中心操作区 --> |
|
8 |
<div class="header-center flex flex-1 items-center justify-center"> |
|
9 |
<span>{{ title }}</span> |
|
10 |
</div> |
|
11 |
<!-- 右侧操作区 --> |
|
12 |
<el-button-group class="header-right"> |
|
13 |
<el-tooltip content="重置"> |
|
14 |
<el-button @click="handleReset"> |
|
15 |
<Icon icon="system-uicons:reset-alt" :size="24" /> |
|
16 |
</el-button> |
|
17 |
</el-tooltip> |
|
18 |
<el-tooltip content="预览" v-if="previewUrl"> |
|
19 |
<el-button @click="handlePreview"> |
|
20 |
<Icon icon="ep:view" :size="24" /> |
|
21 |
</el-button> |
|
22 |
</el-tooltip> |
|
23 |
<el-tooltip content="保存"> |
|
24 |
<el-button @click="handleSave"> |
|
25 |
<Icon icon="ep:check" :size="24" /> |
|
26 |
</el-button> |
|
27 |
</el-tooltip> |
|
28 |
</el-button-group> |
|
29 |
</el-header> |
|
30 |
|
|
31 |
<!-- 中心区域 --> |
|
32 |
<el-container class="editor-container"> |
|
33 |
<!-- 左侧:组件库(ComponentLibrary) --> |
|
34 |
<ComponentLibrary ref="componentLibrary" :list="libs" v-if="libs && libs.length > 0" /> |
|
35 |
<!-- 中心:设计区域(ComponentContainer) --> |
|
36 |
<div class="editor-center page-prop-area" @click="handlePageSelected"> |
|
37 |
<!-- 手机顶部 --> |
|
38 |
<div class="editor-design-top"> |
|
39 |
<!-- 手机顶部状态栏 --> |
|
40 |
<img src="@/assets/imgs/diy/statusBar.png" alt="" class="status-bar" /> |
|
41 |
<!-- 手机顶部导航栏 --> |
|
42 |
<ComponentContainer |
|
43 |
v-if="showNavigationBar" |
|
44 |
:component="navigationBarComponent" |
|
45 |
:show-toolbar="false" |
|
46 |
:active="selectedComponent?.id === navigationBarComponent.id" |
|
47 |
@click="handleNavigationBarSelected" |
|
48 |
class="cursor-pointer!" |
|
49 |
/> |
|
50 |
</div> |
|
51 |
<!-- 绝对定位的组件:例如 弹窗、浮动按钮等 --> |
|
52 |
<div |
|
53 |
v-for="(component, index) in pageComponents" |
|
54 |
:key="index" |
|
55 |
@click="handleComponentSelected(component, index)" |
|
56 |
> |
|
57 |
<component |
|
58 |
v-if="component.position === 'fixed' && selectedComponent?.uid === component.uid" |
|
59 |
:is="component.id" |
|
60 |
:property="component.property" |
|
61 |
/> |
|
62 |
</div> |
|
63 |
<!-- 手机页面编辑区域 --> |
|
64 |
<el-scrollbar |
|
65 |
height="100%" |
|
66 |
wrap-class="editor-design-center page-prop-area" |
|
67 |
view-class="phone-container" |
|
68 |
:view-style="{ |
|
69 |
backgroundColor: pageConfigComponent.property.backgroundColor, |
|
70 |
backgroundImage: `url(${pageConfigComponent.property.backgroundImage})` |
|
71 |
}" |
|
72 |
> |
|
73 |
<draggable |
|
74 |
class="page-prop-area drag-area" |
|
75 |
v-model="pageComponents" |
|
76 |
item-key="index" |
|
77 |
:animation="200" |
|
78 |
filter=".component-toolbar" |
|
79 |
ghost-class="draggable-ghost" |
|
80 |
:force-fallback="true" |
|
81 |
group="component" |
|
82 |
@change="handleComponentChange" |
|
83 |
> |
|
84 |
<template #item="{ element, index }"> |
|
85 |
<ComponentContainer |
|
86 |
v-if="!element.position || element.position === 'center'" |
|
87 |
:component="element" |
|
88 |
:active="selectedComponentIndex === index" |
|
89 |
:can-move-up="index > 0" |
|
90 |
:can-move-down="index < pageComponents.length - 1" |
|
91 |
@move="(direction) => handleMoveComponent(index, direction)" |
|
92 |
@copy="handleCopyComponent(index)" |
|
93 |
@delete="handleDeleteComponent(index)" |
|
94 |
@click="handleComponentSelected(element, index)" |
|
95 |
/> |
|
96 |
</template> |
|
97 |
</draggable> |
|
98 |
</el-scrollbar> |
|
99 |
<!-- 手机底部导航 --> |
|
100 |
<div v-if="showTabBar" :class="['editor-design-bottom', 'component', 'cursor-pointer!']"> |
|
101 |
<ComponentContainer |
|
102 |
:component="tabBarComponent" |
|
103 |
:show-toolbar="false" |
|
104 |
:active="selectedComponent?.id === tabBarComponent.id" |
|
105 |
@click="handleTabBarSelected" |
|
106 |
/> |
|
107 |
</div> |
|
108 |
<!-- 固定布局的组件 操作按钮区 --> |
|
109 |
<div class="fixed-component-action-group"> |
|
110 |
<el-tag |
|
111 |
v-if="showPageConfig" |
|
112 |
size="large" |
|
113 |
:effect="selectedComponent?.uid === pageConfigComponent.uid ? 'dark' : 'plain'" |
|
114 |
:type="selectedComponent?.uid === pageConfigComponent.uid ? '' : 'info'" |
|
115 |
@click="handleComponentSelected(pageConfigComponent)" |
|
116 |
> |
|
117 |
<Icon :icon="pageConfigComponent.icon" :size="12" /> |
|
118 |
<span>{{ pageConfigComponent.name }}</span> |
|
119 |
</el-tag> |
|
120 |
<template v-for="(component, index) in pageComponents" :key="index"> |
|
121 |
<el-tag |
|
122 |
v-if="component.position === 'fixed'" |
|
123 |
size="large" |
|
124 |
closable |
|
125 |
:effect="selectedComponent?.uid === component.uid ? 'dark' : 'plain'" |
|
126 |
:type="selectedComponent?.uid === component.uid ? '' : 'info'" |
|
127 |
@click="handleComponentSelected(component)" |
|
128 |
@close="handleDeleteComponent(index)" |
|
129 |
> |
|
130 |
<Icon :icon="component.icon" :size="12" /> |
|
131 |
<span>{{ component.name }}</span> |
|
132 |
</el-tag> |
|
133 |
</template> |
|
134 |
</div> |
|
135 |
</div> |
|
136 |
<!-- 右侧:属性面板(ComponentContainerProperty) --> |
|
137 |
<el-aside class="editor-right" width="350px" v-if="selectedComponent?.property"> |
|
138 |
<el-card |
|
139 |
shadow="never" |
|
140 |
body-class="h-[calc(100%-var(--el-card-padding)-var(--el-card-padding))]" |
|
141 |
class="h-full" |
|
142 |
> |
|
143 |
<!-- 组件名称 --> |
|
144 |
<template #header> |
|
145 |
<div class="flex items-center gap-8px"> |
|
146 |
<Icon :icon="selectedComponent?.icon" color="gray" /> |
|
147 |
<span>{{ selectedComponent?.name }}</span> |
|
148 |
</div> |
|
149 |
</template> |
|
150 |
<el-scrollbar |
|
151 |
class="m-[calc(0px-var(--el-card-padding))]" |
|
152 |
view-class="p-[var(--el-card-padding)] p-b-[calc(var(--el-card-padding)+var(--el-card-padding))] property" |
|
153 |
> |
|
154 |
<component |
|
155 |
:key="selectedComponent?.uid || selectedComponent?.id" |
|
156 |
:is="selectedComponent?.id + 'Property'" |
|
157 |
v-model="selectedComponent.property" |
|
158 |
/> |
|
159 |
</el-scrollbar> |
|
160 |
</el-card> |
|
161 |
</el-aside> |
|
162 |
</el-container> |
|
163 |
</el-container> |
|
164 |
|
|
165 |
<!-- 预览弹框 --> |
|
166 |
<Dialog v-model="previewDialogVisible" title="预览" width="700"> |
|
167 |
<div class="flex justify-around"> |
|
168 |
<IFrame |
|
169 |
class="w-375px border-4px border-rounded-8px border-solid p-2px h-667px!" |
|
170 |
:src="previewUrl" |
|
171 |
/> |
|
172 |
<div class="flex flex-col"> |
|
173 |
<el-text>手机扫码预览</el-text> |
|
174 |
<Qrcode :text="previewUrl" logo="/logo.gif" /> |
|
175 |
</div> |
|
176 |
</div> |
|
177 |
</Dialog> |
|
178 |
</template> |
|
179 |
<script lang="ts"> |
|
180 |
// 注册所有的组件 |
|
181 |
import { components } from './components/mobile/index' |
|
182 |
export default { |
|
183 |
components: { ...components } |
|
184 |
} |
|
185 |
</script> |
|
186 |
<script lang="ts" setup> |
|
187 |
import draggable from 'vuedraggable' |
|
188 |
import ComponentLibrary from './components/ComponentLibrary.vue' |
|
189 |
import { cloneDeep, includes } from 'lodash-es' |
|
190 |
import { component as PAGE_CONFIG_COMPONENT } from '@/components/DiyEditor/components/mobile/PageConfig/config' |
|
191 |
import { component as NAVIGATION_BAR_COMPONENT } from './components/mobile/NavigationBar/config' |
|
192 |
import { component as TAB_BAR_COMPONENT } from './components/mobile/TabBar/config' |
|
193 |
import { isString } from '@/utils/is' |
|
194 |
import { DiyComponent, DiyComponentLibrary, PageConfig } from '@/components/DiyEditor/util' |
|
195 |
import { componentConfigs } from '@/components/DiyEditor/components/mobile' |
|
196 |
import { array, oneOfType } from 'vue-types' |
|
197 |
import { propTypes } from '@/utils/propTypes' |
|
198 |
|
|
199 |
/** 页面装修详情页 */ |
|
200 |
defineOptions({ name: 'DiyPageDetail' }) |
|
201 |
|
|
202 |
// 左侧组件库 |
|
203 |
const componentLibrary = ref() |
|
204 |
// 页面设置组件 |
|
205 |
const pageConfigComponent = ref<DiyComponent<any>>(cloneDeep(PAGE_CONFIG_COMPONENT)) |
|
206 |
// 顶部导航栏 |
|
207 |
const navigationBarComponent = ref<DiyComponent<any>>(cloneDeep(NAVIGATION_BAR_COMPONENT)) |
|
208 |
// 底部导航菜单 |
|
209 |
const tabBarComponent = ref<DiyComponent<any>>(cloneDeep(TAB_BAR_COMPONENT)) |
|
210 |
|
|
211 |
// 选中的组件,默认选中顶部导航栏 |
|
212 |
const selectedComponent = ref<DiyComponent<any>>() |
|
213 |
// 选中的组件索引 |
|
214 |
const selectedComponentIndex = ref<number>(-1) |
|
215 |
// 组件列表 |
|
216 |
const pageComponents = ref<DiyComponent<any>[]>([]) |
|
217 |
// 定义属性 |
|
218 |
const props = defineProps({ |
|
219 |
// 页面配置,支持Json字符串 |
|
220 |
modelValue: oneOfType<string | PageConfig>([String, Object]).isRequired, |
|
221 |
// 标题 |
|
222 |
title: propTypes.string.def(''), |
|
223 |
// 组件库 |
|
224 |
libs: array<DiyComponentLibrary>(), |
|
225 |
// 是否显示顶部导航栏 |
|
226 |
showNavigationBar: propTypes.bool.def(true), |
|
227 |
// 是否显示底部导航菜单 |
|
228 |
showTabBar: propTypes.bool.def(false), |
|
229 |
// 是否显示页面配置 |
|
230 |
showPageConfig: propTypes.bool.def(true), |
|
231 |
// 预览地址:提供了预览地址,才会显示预览按钮 |
|
232 |
previewUrl: propTypes.string.def('') |
|
233 |
}) |
|
234 |
|
|
235 |
// 监听传入的页面配置 |
|
236 |
// 解析出 pageConfigComponent 页面整体的配置,navigationBarComponent、pageComponents、tabBarComponent 页面上、中、下的配置 |
|
237 |
watch( |
|
238 |
() => props.modelValue, |
|
239 |
() => { |
|
240 |
const modelValue = isString(props.modelValue) |
|
241 |
? (JSON.parse(props.modelValue) as PageConfig) |
|
242 |
: props.modelValue |
|
243 |
pageConfigComponent.value.property = modelValue?.page || PAGE_CONFIG_COMPONENT.property |
|
244 |
navigationBarComponent.value.property = |
|
245 |
modelValue?.navigationBar || NAVIGATION_BAR_COMPONENT.property |
|
246 |
tabBarComponent.value.property = modelValue?.tabBar || TAB_BAR_COMPONENT.property |
|
247 |
// 查找对应的页面组件 |
|
248 |
pageComponents.value = (modelValue?.components || []).map((item) => { |
|
249 |
const component = componentConfigs[item.id] |
|
250 |
return { ...component, property: item.property } |
|
251 |
}) |
|
252 |
}, |
|
253 |
{ |
|
254 |
immediate: true |
|
255 |
} |
|
256 |
) |
|
257 |
|
|
258 |
// 保存 |
|
259 |
const handleSave = () => { |
|
260 |
const pageConfig = { |
|
261 |
page: pageConfigComponent.value.property, |
|
262 |
navigationBar: navigationBarComponent.value.property, |
|
263 |
tabBar: tabBarComponent.value.property, |
|
264 |
components: pageComponents.value.map((component) => { |
|
265 |
// 只保留APP有用的字段 |
|
266 |
return { id: component.id, property: component.property } |
|
267 |
}) |
|
268 |
} as PageConfig |
|
269 |
if (!props.showTabBar) { |
|
270 |
delete pageConfig.tabBar |
|
271 |
} |
|
272 |
// 发送数据更新通知 |
|
273 |
const modelValue = isString(props.modelValue) ? JSON.stringify(pageConfig) : pageConfig |
|
274 |
emits('update:modelValue', modelValue) |
|
275 |
// 发送保存通知 |
|
276 |
emits('save', pageConfig) |
|
277 |
} |
|
278 |
|
|
279 |
// 处理页面选中:显示属性表单 |
|
280 |
const handlePageSelected = (event: any) => { |
|
281 |
if (!props.showPageConfig) return |
|
282 |
|
|
283 |
// 配置了样式 page-prop-area 的元素,才显示页面设置 |
|
284 |
if (includes(event?.target?.classList, 'page-prop-area')) { |
|
285 |
handleComponentSelected(unref(pageConfigComponent)) |
|
286 |
} |
|
287 |
} |
|
288 |
|
|
289 |
/** |
|
290 |
* 选中组件 |
|
291 |
* |
|
292 |
* @param component 组件 |
|
293 |
* @param index 组件的索引 |
|
294 |
*/ |
|
295 |
const handleComponentSelected = (component: DiyComponent<any>, index: number = -1) => { |
|
296 |
selectedComponent.value = component |
|
297 |
selectedComponentIndex.value = index |
|
298 |
} |
|
299 |
|
|
300 |
// 选中顶部导航栏 |
|
301 |
const handleNavigationBarSelected = () => { |
|
302 |
handleComponentSelected(unref(navigationBarComponent)) |
|
303 |
} |
|
304 |
|
|
305 |
// 选中底部导航菜单 |
|
306 |
const handleTabBarSelected = () => { |
|
307 |
handleComponentSelected(unref(tabBarComponent)) |
|
308 |
} |
|
309 |
|
|
310 |
// 组件变动(拖拽) |
|
311 |
const handleComponentChange = (dragEvent: any) => { |
|
312 |
// 新增,即从组件库拖拽添加组件 |
|
313 |
if (dragEvent.added) { |
|
314 |
const { element, newIndex } = dragEvent.added |
|
315 |
handleComponentSelected(element, newIndex) |
|
316 |
} else if (dragEvent.moved) { |
|
317 |
// 拖拽排序 |
|
318 |
const { newIndex } = dragEvent.moved |
|
319 |
// 保持选中 |
|
320 |
selectedComponentIndex.value = newIndex |
|
321 |
} |
|
322 |
} |
|
323 |
|
|
324 |
// 交换组件 |
|
325 |
const swapComponent = (oldIndex: number, newIndex: number) => { |
|
326 |
;[pageComponents.value[oldIndex], pageComponents.value[newIndex]] = [ |
|
327 |
pageComponents.value[newIndex], |
|
328 |
pageComponents.value[oldIndex] |
|
329 |
] |
|
330 |
// 保持选中 |
|
331 |
selectedComponentIndex.value = newIndex |
|
332 |
} |
|
333 |
|
|
334 |
/** 移动组件(上移、下移) */ |
|
335 |
const handleMoveComponent = (index: number, direction: number) => { |
|
336 |
const newIndex = index + direction |
|
337 |
if (newIndex < 0 || newIndex >= pageComponents.value.length) return |
|
338 |
|
|
339 |
swapComponent(index, newIndex) |
|
340 |
} |
|
341 |
|
|
342 |
/** 复制组件 */ |
|
343 |
const handleCopyComponent = (index: number) => { |
|
344 |
const component = cloneDeep(pageComponents.value[index]) |
|
345 |
component.uid = new Date().getTime() |
|
346 |
pageComponents.value.splice(index + 1, 0, component) |
|
347 |
} |
|
348 |
|
|
349 |
/** |
|
350 |
* 删除组件 |
|
351 |
* @param index 当前组件index |
|
352 |
*/ |
|
353 |
const handleDeleteComponent = (index: number) => { |
|
354 |
// 删除组件 |
|
355 |
pageComponents.value.splice(index, 1) |
|
356 |
if (index < pageComponents.value.length) { |
|
357 |
// 1. 不是最后一个组件时,删除后选中下面的组件 |
|
358 |
let bottomIndex = index |
|
359 |
handleComponentSelected(pageComponents.value[bottomIndex], bottomIndex) |
|
360 |
} else if (pageComponents.value.length > 0) { |
|
361 |
// 2. 不是第一个组件时,删除后选中上面的组件 |
|
362 |
let topIndex = index - 1 |
|
363 |
handleComponentSelected(pageComponents.value[topIndex], topIndex) |
|
364 |
} else { |
|
365 |
// 3. 组件全部删除之后,显示页面设置 |
|
366 |
handleComponentSelected(unref(pageConfigComponent)) |
|
367 |
} |
|
368 |
} |
|
369 |
|
|
370 |
// 工具栏操作 |
|
371 |
const emits = defineEmits(['reset', 'preview', 'save', 'update:modelValue']) |
|
372 |
|
|
373 |
// 注入无感刷新页面函数 |
|
374 |
const reload = inject<() => void>('reload') |
|
375 |
// 重置 |
|
376 |
const handleReset = () => { |
|
377 |
if (reload) reload() |
|
378 |
emits('reset') |
|
379 |
} |
|
380 |
|
|
381 |
// 预览 |
|
382 |
const previewDialogVisible = ref(false) |
|
383 |
const handlePreview = () => { |
|
384 |
previewDialogVisible.value = true |
|
385 |
emits('preview') |
|
386 |
} |
|
387 |
|
|
388 |
// 设置默认选中的组件 |
|
389 |
const setDefaultSelectedComponent = () => { |
|
390 |
if (props.showPageConfig) { |
|
391 |
selectedComponent.value = unref(pageConfigComponent) |
|
392 |
} else if (props.showNavigationBar) { |
|
393 |
selectedComponent.value = unref(navigationBarComponent) |
|
394 |
} else if (props.showTabBar) { |
|
395 |
selectedComponent.value = unref(tabBarComponent) |
|
396 |
} |
|
397 |
} |
|
398 |
|
|
399 |
watch( |
|
400 |
() => [props.showPageConfig, props.showNavigationBar, props.showTabBar], |
|
401 |
() => setDefaultSelectedComponent() |
|
402 |
) |
|
403 |
|
|
404 |
onMounted(() => setDefaultSelectedComponent()) |
|
405 |
</script> |
|
406 |
<style lang="scss" scoped> |
|
407 |
/* 手机宽度 */ |
|
408 |
$phone-width: 375px; |
|
409 |
$toolbar-height: 42px; |
|
410 |
|
|
411 |
/* 根节点样式 */ |
|
412 |
.editor { |
|
413 |
display: flex; |
|
414 |
height: 100%; |
|
415 |
margin: calc(0px - var(--app-content-padding)); |
|
416 |
flex-direction: column; |
|
417 |
|
|
418 |
/* 顶部:工具栏 */ |
|
419 |
.editor-header { |
|
420 |
display: flex; |
|
421 |
height: $toolbar-height; |
|
422 |
padding: 0; |
|
423 |
background-color: var(--el-bg-color); |
|
424 |
border-bottom: solid 1px var(--el-border-color); |
|
425 |
align-items: center; |
|
426 |
justify-content: space-between; |
|
427 |
|
|
428 |
/* 工具栏:右侧按钮 */ |
|
429 |
.header-right { |
|
430 |
height: 100%; |
|
431 |
|
|
432 |
.el-button { |
|
433 |
height: 100%; |
|
434 |
} |
|
435 |
} |
|
436 |
|
|
437 |
/* 隐藏工具栏按钮的边框 */ |
|
438 |
:deep(.el-radio-button__inner), |
|
439 |
:deep(.el-button) { |
|
440 |
border-top: none !important; |
|
441 |
border-bottom: none !important; |
|
442 |
border-radius: 0 !important; |
|
443 |
} |
|
444 |
} |
|
445 |
|
|
446 |
/* 中心操作区 */ |
|
447 |
.editor-container { |
|
448 |
height: calc( |
|
449 |
100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - |
|
450 |
$toolbar-height |
|
451 |
); |
|
452 |
|
|
453 |
/* 右侧属性面板 */ |
|
454 |
.editor-right { |
|
455 |
overflow: hidden; |
|
456 |
box-shadow: -8px 0 8px -8px rgb(0 0 0 / 12%); |
|
457 |
flex-shrink: 0; |
|
458 |
|
|
459 |
/* 属性面板顶部:减少内边距 */ |
|
460 |
:deep(.el-card__header) { |
|
461 |
padding: 8px 16px; |
|
462 |
} |
|
463 |
|
|
464 |
/* 属性面板分组 */ |
|
465 |
:deep(.property-group) { |
|
466 |
margin: 0 -20px; |
|
467 |
|
|
468 |
&.el-card { |
|
469 |
border: none; |
|
470 |
} |
|
471 |
|
|
472 |
/* 属性分组名称 */ |
|
473 |
.el-card__header { |
|
474 |
padding: 8px 32px; |
|
475 |
background: var(--el-bg-color-page); |
|
476 |
border: none; |
|
477 |
} |
|
478 |
|
|
479 |
.el-card__body { |
|
480 |
border: none; |
|
481 |
} |
|
482 |
} |
|
483 |
} |
|
484 |
|
|
485 |
/* 中心区域 */ |
|
486 |
.editor-center { |
|
487 |
position: relative; |
|
488 |
display: flex; |
|
489 |
width: 100%; |
|
490 |
margin: 16px 0 0; |
|
491 |
overflow: hidden; |
|
492 |
background-color: var(--app-content-bg-color); |
|
493 |
flex: 1 1 0; |
|
494 |
flex-direction: column; |
|
495 |
justify-content: center; |
|
496 |
|
|
497 |
/* 手机顶部 */ |
|
498 |
.editor-design-top { |
|
499 |
display: flex; |
|
500 |
width: $phone-width; |
|
501 |
margin: 0 auto; |
|
502 |
flex-direction: column; |
|
503 |
|
|
504 |
/* 手机顶部状态栏 */ |
|
505 |
.status-bar { |
|
506 |
width: $phone-width; |
|
507 |
height: 20px; |
|
508 |
background-color: #fff; |
|
509 |
} |
|
510 |
} |
|
511 |
|
|
512 |
/* 手机底部导航 */ |
|
513 |
.editor-design-bottom { |
|
514 |
width: $phone-width; |
|
515 |
margin: 0 auto; |
|
516 |
} |
|
517 |
|
|
518 |
/* 手机页面编辑区域 */ |
|
519 |
:deep(.editor-design-center) { |
|
520 |
width: 100%; |
|
521 |
|
|
522 |
/* 主体内容 */ |
|
523 |
.phone-container { |
|
524 |
position: relative; |
|
525 |
width: $phone-width; |
|
526 |
height: 100%; |
|
527 |
margin: 0 auto; |
|
528 |
background-repeat: no-repeat; |
|
529 |
background-size: 100% 100%; |
|
530 |
|
|
531 |
.drag-area { |
|
532 |
width: 100%; |
|
533 |
height: 100%; |
|
534 |
} |
|
535 |
} |
|
536 |
} |
|
537 |
|
|
538 |
/* 固定布局的组件 操作按钮区 */ |
|
539 |
.fixed-component-action-group { |
|
540 |
position: absolute; |
|
541 |
top: 0; |
|
542 |
right: 16px; |
|
543 |
display: flex; |
|
544 |
flex-direction: column; |
|
545 |
gap: 8px; |
|
546 |
|
|
547 |
:deep(.el-tag) { |
|
548 |
box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.1); |
|
549 |
border: none; |
|
550 |
.el-tag__content { |
|
551 |
width: 100%; |
|
552 |
display: flex; |
|
553 |
align-items: center; |
|
554 |
justify-content: flex-start; |
|
555 |
|
|
556 |
.el-icon { |
|
557 |
margin-right: 4px; |
|
558 |
} |
|
559 |
} |
|
560 |
} |
|
561 |
} |
|
562 |
} |
|
563 |
} |
|
564 |
} |
|
565 |
</style> |