feat: (web-ele)新增颜色输入框组件并优化图片上传组件

- 新增 ColorInput 组件用于颜色选择
- 重构 ImageUpload 组件,增加编辑和删除功能
- 更新 DIY 编辑器相关组件,优化用户体验
- 添加商城 H5 预览地址配置
- 优化导航栏单元格属性配置
This commit is contained in:
lrl
2025-08-05 15:32:12 +08:00
parent 1f155fa7c5
commit e7fc44715b
64 changed files with 2248 additions and 1165 deletions

View File

@@ -6,11 +6,16 @@ import type {
import { computed } from 'vue';
import { ElButton, ElTooltip } from 'element-plus';
import { components } from '#/components/diy-editor/components/mobile';
import VerticalButtonGroup from '#/components/vertical-button-group/index.vue';
/**
* 组件容器:目前在中间部分
* 用于包裹组件,为组件提供 背景、外边距、内边距、边框等样式
*/
defineOptions({ name: 'ComponentContainer' });
defineOptions({ name: 'ComponentContainer', components });
const props = defineProps({
component: {
@@ -29,6 +34,10 @@ const props = defineProps({
type: Boolean,
default: false,
},
showToolbar: {
type: Boolean,
default: false,
},
});
const emits = defineEmits<{
@@ -106,34 +115,37 @@ const handleDeleteComponent = () => {
{{ component.name }}
</div>
<!-- 右侧组件操作工具栏 -->
<div class="component-toolbar" v-if="component.name && active">
<div
class="component-toolbar"
v-if="showToolbar && component.name && active"
>
<VerticalButtonGroup type="primary">
<el-tooltip content="上移" placement="right">
<el-button
<ElTooltip content="上移" placement="right">
<ElButton
:disabled="!canMoveUp"
@click.stop="handleMoveComponent(-1)"
>
<Icon icon="ep:arrow-up" />
</el-button>
</el-tooltip>
<el-tooltip content="下移" placement="right">
<el-button
</ElButton>
</ElTooltip>
<ElTooltip content="下移" placement="right">
<ElButton
:disabled="!canMoveDown"
@click.stop="handleMoveComponent(1)"
>
<Icon icon="ep:arrow-down" />
</el-button>
</el-tooltip>
<el-tooltip content="复制" placement="right">
<el-button @click.stop="handleCopyComponent()">
</ElButton>
</ElTooltip>
<ElTooltip content="复制" placement="right">
<ElButton @click.stop="handleCopyComponent()">
<Icon icon="ep:copy-document" />
</el-button>
</el-tooltip>
<el-tooltip content="删除" placement="right">
<el-button @click.stop="handleDeleteComponent()">
</ElButton>
</ElTooltip>
<ElTooltip content="删除" placement="right">
<ElButton @click.stop="handleDeleteComponent()">
<Icon icon="ep:delete" />
</el-button>
</el-tooltip>
</ElButton>
</ElTooltip>
</VerticalButtonGroup>
</div>
</div>

View File

@@ -2,6 +2,20 @@
import type { ComponentStyle } from '#/components/diy-editor/util';
import { useVModel } from '@vueuse/core';
import {
ElCard,
ElForm,
ElFormItem,
ElRadio,
ElRadioGroup,
ElSlider,
ElTabPane,
ElTabs,
ElTree,
} from 'element-plus';
import ColorInput from '#/components/color-input/index.vue';
import UploadImg from '#/components/upload/image-upload.vue';
/**
* 组件容器属性:目前右边部分
@@ -110,48 +124,54 @@ const handleSliderChange = (prop: string) => {
</script>
<template>
<el-tabs stretch>
<ElTabs stretch>
<!-- 每个组件的自定义内容 -->
<el-tab-pane label="内容" v-if="$slots.default">
<ElTabPane label="内容" v-if="$slots.default">
<slot></slot>
</el-tab-pane>
</ElTabPane>
<!-- 每个组件的通用内容 -->
<el-tab-pane label="样式" lazy>
<el-card header="组件样式" class="property-group">
<el-form :model="formData" label-width="80px">
<el-form-item label="组件背景" prop="bgType">
<el-radio-group v-model="formData.bgType">
<el-radio value="color">纯色</el-radio>
<el-radio value="img">图片</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
<ElTabPane label="样式" lazy>
<ElCard header="组件样式" class="property-group">
<ElForm :model="formData" label-width="80px">
<ElFormItem label="组件背景" prop="bgType">
<ElRadioGroup v-model="formData.bgType">
<ElRadio value="color">纯色</ElRadio>
<ElRadio value="img">图片</ElRadio>
</ElRadioGroup>
</ElFormItem>
<ElFormItem
label="选择颜色"
prop="bgColor"
v-if="formData.bgType === 'color'"
>
<ColorInput v-model="formData.bgColor" />
</el-form-item>
<el-form-item label="上传图片" prop="bgImg" v-else>
<UploadImg v-model="formData.bgImg" :limit="1">
</ElFormItem>
<ElFormItem label="上传图片" prop="bgImg" v-else>
<UploadImg
v-model="formData.bgImg"
:limit="1"
:show-description="false"
>
<template #tip>建议宽度 750px</template>
</UploadImg>
</el-form-item>
<el-tree
</ElFormItem>
<ElTree
:data="treeData"
:expand-on-click-node="false"
default-expand-all
>
<template #default="{ node, data }">
<el-form-item
<ElFormItem
:label="data.label"
:prop="data.prop"
:label-width="node.level === 1 ? '80px' : '62px'"
class="m-b-0! w-full"
>
<el-slider
v-model="formData[data.prop as keyof ComponentStyle]"
<ElSlider
v-model="
formData[data.prop as keyof ComponentStyle] as number
"
:max="100"
:min="0"
show-input
@@ -159,14 +179,14 @@ const handleSliderChange = (prop: string) => {
:show-input-controls="false"
@input="handleSliderChange(data.prop)"
/>
</el-form-item>
</ElFormItem>
</template>
</el-tree>
</ElTree>
<slot name="style" :style="formData"></slot>
</el-form>
</el-card>
</el-tab-pane>
</el-tabs>
</ElForm>
</ElCard>
</ElTabPane>
</ElTabs>
</template>
<style scoped lang="scss">

View File

@@ -6,8 +6,10 @@ import type {
import { reactive, watch } from 'vue';
import { IconifyIcon } from '@vben/icons';
import { cloneDeep } from '@vben/utils';
import { ElAside, ElCollapse, ElCollapseItem, ElScrollbar } from 'element-plus';
import draggable from 'vuedraggable';
import { componentConfigs } from '../components/mobile/index';
@@ -63,10 +65,10 @@ const handleCloneComponent = (component: DiyComponent<any>) => {
</script>
<template>
<el-aside class="editor-left" width="261px">
<el-scrollbar>
<el-collapse v-model="extendGroups">
<el-collapse-item
<ElAside class="editor-left" width="261px">
<ElScrollbar>
<ElCollapse v-model="extendGroups">
<ElCollapseItem
v-for="group in groups"
:key="group.name"
:name="group.name"
@@ -87,16 +89,16 @@ const handleCloneComponent = (component: DiyComponent<any>) => {
<div>
<div class="drag-placement">组件放置区域</div>
<div class="component">
<Icon :icon="element.icon" :size="32" />
<IconifyIcon :icon="element.icon" :size="32" />
<span class="mt-4px text-12px">{{ element.name }}</span>
</div>
</div>
</template>
</draggable>
</el-collapse-item>
</el-collapse>
</el-scrollbar>
</el-aside>
</ElCollapseItem>
</ElCollapse>
</ElScrollbar>
</ElAside>
</template>
<style scoped lang="scss">

View File

@@ -1,5 +0,0 @@
import { components } from '../components/mobile/index';
export default {
components: { ...components },
};

View File

@@ -3,6 +3,10 @@ import type { CarouselProperty } from './config';
import { ref } from 'vue';
import { IconifyIcon } from '@vben/icons';
import { ElCarousel, ElCarouselItem, ElImage } from 'element-plus';
/** 轮播图 */
defineOptions({ name: 'Carousel' });
@@ -16,13 +20,13 @@ const handleIndexChange = (index: number) => {
<template>
<!-- 无图片 -->
<div
class="h-250px bg-gray-3 flex items-center justify-center"
class="flex h-[250px] items-center justify-center bg-gray-300"
v-if="property.items.length === 0"
>
<Icon icon="tdesign:image" class="text-gray-8 text-120px!" />
<IconifyIcon icon="tdesign:image" class="text-[120px] text-gray-800" />
</div>
<div v-else class="relative">
<el-carousel
<ElCarousel
height="174px"
:type="property.type === 'card' ? 'card' : ''"
:autoplay="property.autoplay"
@@ -30,13 +34,13 @@ const handleIndexChange = (index: number) => {
:indicator-position="property.indicator === 'number' ? 'none' : undefined"
@change="handleIndexChange"
>
<el-carousel-item v-for="(item, index) in property.items" :key="index">
<el-image class="h-full w-full" :src="item.imgUrl" />
</el-carousel-item>
</el-carousel>
<ElCarouselItem v-for="(item, index) in property.items" :key="index">
<ElImage class="h-full w-full" :src="item.imgUrl" />
</ElCarouselItem>
</ElCarousel>
<div
v-if="property.indicator === 'number'"
class="bottom-10px right-10px p-x-8px p-y-2px text-10px absolute rounded-xl bg-black text-white opacity-40"
class="absolute bottom-[10px] right-[10px] rounded-xl bg-black px-[8px] py-[2px] text-[10px] text-white opacity-40"
>
{{ currentIndex }} / {{ property.items.length }}
</div>

View File

@@ -1,7 +1,26 @@
<script setup lang="ts">
import type { CarouselProperty } from './config';
import { IconifyIcon } from '@vben/icons';
import { useVModel } from '@vueuse/core';
import {
ElCard,
ElForm,
ElFormItem,
ElRadioButton,
ElRadioGroup,
ElSlider,
ElSwitch,
ElText,
ElTooltip,
} from 'element-plus';
import AppLinkInput from '#/components/app-link-input/index.vue';
import ComponentContainerProperty from '#/components/diy-editor/components/ComponentContainerProperty.vue';
import Draggable from '#/components/draggable/index.vue';
import UploadFile from '#/components/upload/file-upload.vue';
import UploadImg from '#/components/upload/image-upload.vue';
// 轮播图属性面板
defineOptions({ name: 'CarouselProperty' });
@@ -13,33 +32,33 @@ const formData = useVModel(props, 'modelValue', emit);
<template>
<ComponentContainerProperty v-model="formData.style">
<el-form label-width="80px" :model="formData">
<el-card header="样式设置" class="property-group" shadow="never">
<el-form-item label="样式" prop="type">
<el-radio-group v-model="formData.type">
<el-tooltip class="item" content="默认" placement="bottom">
<el-radio-button value="default">
<Icon icon="system-uicons:carousel" />
</el-radio-button>
</el-tooltip>
<el-tooltip class="item" content="卡片" placement="bottom">
<el-radio-button value="card">
<Icon icon="ic:round-view-carousel" />
</el-radio-button>
</el-tooltip>
</el-radio-group>
</el-form-item>
<el-form-item label="指示器" prop="indicator">
<el-radio-group v-model="formData.indicator">
<el-radio value="dot">小圆点</el-radio>
<el-radio value="number">数字</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="是否轮播" prop="autoplay">
<el-switch v-model="formData.autoplay" />
</el-form-item>
<el-form-item label="播放间隔" prop="interval" v-if="formData.autoplay">
<el-slider
<ElForm label-width="80px" :model="formData">
<ElCard header="样式设置" class="property-group" shadow="never">
<ElFormItem label="样式" prop="type">
<ElRadioGroup v-model="formData.type">
<ElTooltip class="item" content="默认" placement="bottom">
<ElRadioButton value="default">
<IconifyIcon icon="system-uicons:carousel" />
</ElRadioButton>
</ElTooltip>
<ElTooltip class="item" content="卡片" placement="bottom">
<ElRadioButton value="card">
<IconifyIcon icon="ic:round-view-carousel" />
</ElRadioButton>
</ElTooltip>
</ElRadioGroup>
</ElFormItem>
<ElFormItem label="指示器" prop="indicator">
<ElRadioGroup v-model="formData.indicator">
<ElRadio value="dot">小圆点</ElRadio>
<ElRadio value="number">数字</ElRadio>
</ElRadioGroup>
</ElFormItem>
<ElFormItem label="是否轮播" prop="autoplay">
<ElSwitch v-model="formData.autoplay" />
</ElFormItem>
<ElFormItem label="播放间隔" prop="interval" v-if="formData.autoplay">
<ElSlider
v-model="formData.interval"
:max="10"
:min="0.5"
@@ -48,24 +67,24 @@ const formData = useVModel(props, 'modelValue', emit);
input-size="small"
:show-input-controls="false"
/>
<el-text type="info">单位</el-text>
</el-form-item>
</el-card>
<el-card header="内容设置" class="property-group" shadow="never">
<ElText type="info">单位</ElText>
</ElFormItem>
</ElCard>
<ElCard header="内容设置" class="property-group" shadow="never">
<Draggable v-model="formData.items" :empty-item="{ type: 'img' }">
<template #default="{ element }">
<el-form-item
<ElFormItem
label="类型"
prop="type"
class="m-b-8px!"
label-width="40px"
>
<el-radio-group v-model="element.type">
<el-radio value="img">图片</el-radio>
<el-radio value="video">视频</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
<ElRadioGroup v-model="element.type">
<ElRadio value="img">图片</ElRadio>
<ElRadio value="video">视频</ElRadio>
</ElRadioGroup>
</ElFormItem>
<ElFormItem
label="图片"
class="m-b-8px!"
label-width="40px"
@@ -77,19 +96,21 @@ const formData = useVModel(props, 'modelValue', emit);
height="80px"
width="100%"
class="min-w-80px"
:show-description="false"
/>
</el-form-item>
</ElFormItem>
<template v-else>
<el-form-item label="封面" class="m-b-8px!" label-width="40px">
<ElFormItem label="封面" class="m-b-8px!" label-width="40px">
<UploadImg
v-model="element.imgUrl"
draggable="false"
:show-description="false"
height="80px"
width="100%"
class="min-w-80px"
/>
</el-form-item>
<el-form-item label="视频" class="m-b-8px!" label-width="40px">
</ElFormItem>
<ElFormItem label="视频" class="m-b-8px!" label-width="40px">
<UploadFile
v-model="element.videoUrl"
:file-type="['mp4']"
@@ -97,15 +118,15 @@ const formData = useVModel(props, 'modelValue', emit);
:file-size="100"
class="min-w-80px"
/>
</el-form-item>
</ElFormItem>
</template>
<el-form-item label="链接" class="m-b-8px!" label-width="40px">
<ElFormItem label="链接" class="m-b-8px!" label-width="40px">
<AppLinkInput v-model="element.url" />
</el-form-item>
</ElFormItem>
</template>
</Draggable>
</el-card>
</el-form>
</ElCard>
</ElForm>
</ComponentContainerProperty>
</template>

View File

@@ -5,6 +5,8 @@ import type { MallCouponTemplateApi } from '#/api/mall/promotion/coupon/couponTe
import { onMounted, ref, watch } from 'vue';
import { ElScrollbar } from 'element-plus';
import * as CouponTemplateApi from '#/api/mall/promotion/coupon/couponTemplate';
import {
@@ -64,9 +66,9 @@ onMounted(() => {
});
</script>
<template>
<el-scrollbar class="z-1 min-h-30px" wrap-class="w-full" ref="containerRef">
<ElScrollbar class="z-10 min-h-[30px]" wrap-class="w-full" ref="containerRef">
<div
class="text-12px flex flex-row"
class="flex flex-row text-xs"
:style="{
gap: `${property.space}px`,
width: scrollbarWidth,
@@ -87,9 +89,9 @@ onMounted(() => {
<!-- 布局11-->
<div
v-if="property.columns === 1"
class="m-l-16px p-8px flex flex-row justify-between"
class="ml-4 flex flex-row justify-between p-2"
>
<div class="gap-4px flex flex-col justify-evenly">
<div class="flex flex-col justify-evenly gap-1">
<!-- 优惠值 -->
<CouponDiscount :coupon="coupon" />
<!-- 优惠描述 -->
@@ -99,7 +101,7 @@ onMounted(() => {
</div>
<div class="flex flex-col justify-evenly">
<div
class="rounded-20px p-x-8px p-y-2px"
class="rounded-full px-2 py-0.5"
:style="{
color: property.button.color,
background: property.button.bgColor,
@@ -112,9 +114,9 @@ onMounted(() => {
<!-- 布局22-->
<div
v-else-if="property.columns === 2"
class="m-l-16px p-8px flex flex-row justify-between"
class="ml-4 flex flex-row justify-between p-2"
>
<div class="gap-4px flex flex-col justify-evenly">
<div class="flex flex-col justify-evenly gap-1">
<!-- 优惠值 -->
<CouponDiscount :coupon="coupon" />
<!-- 优惠描述 -->
@@ -127,7 +129,7 @@ onMounted(() => {
</div>
<div class="flex flex-col">
<div
class="w-20px rounded-20px p-x-2px p-y-8px h-full text-center"
class="h-full w-5 rounded-full px-0.5 py-2 text-center"
:style="{
color: property.button.color,
background: property.button.bgColor,
@@ -138,16 +140,13 @@ onMounted(() => {
</div>
</div>
<!-- 布局33-->
<div
v-else
class="gap-4px p-4px flex flex-col items-center justify-around"
>
<div v-else class="flex flex-col items-center justify-around gap-1 p-1">
<!-- 优惠值 -->
<CouponDiscount :coupon="coupon" />
<!-- 优惠描述 -->
<CouponDiscountDesc :coupon="coupon" />
<div
class="rounded-20px p-x-8px p-y-2px"
class="rounded-full px-2 py-0.5"
:style="{
color: property.button.color,
background: property.button.bgColor,
@@ -158,6 +157,6 @@ onMounted(() => {
</div>
</div>
</div>
</el-scrollbar>
</ElScrollbar>
</template>
<style scoped lang="scss"></style>

View File

@@ -5,11 +5,23 @@ import type { MallCouponTemplateApi } from '#/api/mall/promotion/coupon/couponTe
import { ref, watch } from 'vue';
import { IconifyIcon } from '@vben/icons';
import { floatToFixed2 } from '@vben/utils';
import { useVModel } from '@vueuse/core';
import {
ElCard,
ElForm,
ElFormItem,
ElRadioButton,
ElRadioGroup,
ElSlider,
ElTooltip,
} from 'element-plus';
import * as CouponTemplateApi from '#/api/mall/promotion/coupon/couponTemplate';
import ColorInput from '#/components/color-input/index.vue';
import UploadImg from '#/components/upload/image-upload.vue';
import {
CouponTemplateTakeTypeEnum,
PromotionDiscountTypeEnum,
@@ -52,15 +64,15 @@ watch(
<template>
<ComponentContainerProperty v-model="formData.style">
<el-form label-width="80px" :model="formData">
<el-card header="优惠券列表" class="property-group" shadow="never">
<ElForm label-width="80px" :model="formData">
<ElCard header="优惠券列表" class="property-group" shadow="never">
<div
v-for="(coupon, index) in couponList"
:key="index"
class="flex items-center justify-between"
>
<el-text size="large" truncated>{{ coupon.name }}</el-text>
<el-text type="info" truncated>
<ElText size="large" truncated>{{ coupon.name }}</ElText>
<ElText type="info" truncated>
<span v-if="coupon.usePrice > 0">
{{ floatToFixed2(coupon.usePrice) }}
</span>
@@ -72,58 +84,59 @@ watch(
减{{ floatToFixed2(coupon.discountPrice) }}元
</span>
<span v-else> 打{{ coupon.discountPercent }}折 </span>
</el-text>
</ElText>
</div>
<el-form-item label-width="0">
<el-button
<ElFormItem label-width="0">
<ElButton
@click="handleAddCoupon"
type="primary"
plain
class="m-t-8px w-full"
>
<Icon icon="ep:plus" class="mr-5px" /> 添加
</el-button>
</el-form-item>
</el-card>
<el-card header="优惠券样式" class="property-group" shadow="never">
<el-form-item label="列数" prop="type">
<el-radio-group v-model="formData.columns">
<el-tooltip class="item" content="一列" placement="bottom">
<el-radio-button :value="1">
<Icon icon="fluent:text-column-one-24-filled" />
</el-radio-button>
</el-tooltip>
<el-tooltip class="item" content="二列" placement="bottom">
<el-radio-button :value="2">
<Icon icon="fluent:text-column-two-24-filled" />
</el-radio-button>
</el-tooltip>
<el-tooltip class="item" content="三列" placement="bottom">
<el-radio-button :value="3">
<Icon icon="fluent:text-column-three-24-filled" />
</el-radio-button>
</el-tooltip>
</el-radio-group>
</el-form-item>
<el-form-item label="背景图片" prop="bgImg">
<IconifyIcon icon="ep:plus" class="mr-5px" /> 添加
</ElButton>
</ElFormItem>
</ElCard>
<ElCard header="优惠券样式" class="property-group" shadow="never">
<ElFormItem label="列数" prop="type">
<ElRadioGroup v-model="formData.columns">
<ElTooltip class="item" content="一列" placement="bottom">
<ElRadioButton :value="1">
<IconifyIcon icon="fluent:text-column-one-24-filled" />
</ElRadioButton>
</ElTooltip>
<ElTooltip class="item" content="二列" placement="bottom">
<ElRadioButton :value="2">
<IconifyIcon icon="fluent:text-column-two-24-filled" />
</ElRadioButton>
</ElTooltip>
<ElTooltip class="item" content="三列" placement="bottom">
<ElRadioButton :value="3">
<IconifyIcon icon="fluent:text-column-three-24-filled" />
</ElRadioButton>
</ElTooltip>
</ElRadioGroup>
</ElFormItem>
<ElFormItem label="背景图片" prop="bgImg">
<UploadImg
v-model="formData.bgImg"
height="80px"
width="100%"
class="min-w-160px"
:show-description="false"
/>
</el-form-item>
<el-form-item label="文字颜色" prop="textColor">
</ElFormItem>
<ElFormItem label="文字颜色" prop="textColor">
<ColorInput v-model="formData.textColor" />
</el-form-item>
<el-form-item label="按钮背景" prop="button.bgColor">
</ElFormItem>
<ElFormItem label="按钮背景" prop="button.bgColor">
<ColorInput v-model="formData.button.bgColor" />
</el-form-item>
<el-form-item label="按钮文字" prop="button.color">
</ElFormItem>
<ElFormItem label="按钮文字" prop="button.color">
<ColorInput v-model="formData.button.color" />
</el-form-item>
<el-form-item label="间隔" prop="space">
<el-slider
</ElFormItem>
<ElFormItem label="间隔" prop="space">
<ElSlider
v-model="formData.space"
:max="100"
:min="0"
@@ -131,9 +144,9 @@ watch(
input-size="small"
:show-input-controls="false"
/>
</el-form-item>
</el-card>
</el-form>
</ElFormItem>
</ElCard>
</ElForm>
</ComponentContainerProperty>
<!-- 优惠券选择 -->
<CouponSelect

View File

@@ -3,7 +3,9 @@ import type { FloatingActionButtonProperty } from './config';
import { ref } from 'vue';
import { ElMessage } from 'element-plus';
import { IconifyIcon } from '@vben/icons';
import { ElImage, ElMessage } from 'element-plus';
/** 悬浮按钮 */
defineOptions({ name: 'FloatingActionButton' });
@@ -23,7 +25,7 @@ const handleActive = (index: number) => {
</script>
<template>
<div
class="bottom-32px z-12 gap-12px absolute right-[calc(50%-375px/2+32px)] flex items-center"
class="absolute bottom-8 right-[calc(50%-375px/2+32px)] z-20 flex items-center gap-3"
:class="[
{
'flex-row': property.direction === 'horizontal',
@@ -38,16 +40,16 @@ const handleActive = (index: number) => {
class="flex flex-col items-center"
@click="handleActive(index)"
>
<el-image :src="item.imgUrl" fit="contain" class="h-27px w-27px">
<ElImage :src="item.imgUrl" fit="contain" class="h-7 w-7">
<template #error>
<div class="flex h-full w-full items-center justify-center">
<Icon icon="ep:picture" :color="item.textColor" />
<IconifyIcon icon="ep:picture" :color="item.textColor" />
</div>
</template>
</el-image>
</ElImage>
<span
v-if="property.showText"
class="mt-4px text-12px"
class="mt-1 text-xs"
:style="{ color: item.textColor }"
>
{{ item.text }}
@@ -56,7 +58,11 @@ const handleActive = (index: number) => {
</template>
<!-- todo: @owen 使用APP主题色 -->
<el-button type="primary" size="large" circle @click="handleToggleFab">
<Icon icon="ep:plus" class="fab-icon" :class="[{ active: expanded }]" />
<IconifyIcon
icon="ep:plus"
class="fab-icon"
:class="[{ active: expanded }]"
/>
</el-button>
</div>
<!-- 模态背景展开时显示点击后折叠 -->

View File

@@ -2,6 +2,19 @@
import type { FloatingActionButtonProperty } from './config';
import { useVModel } from '@vueuse/core';
import {
ElCard,
ElForm,
ElFormItem,
ElRadio,
ElRadioGroup,
ElSwitch,
} from 'element-plus';
import AppLinkInput from '#/components/app-link-input/index.vue';
import Draggable from '#/components/draggable/index.vue';
import InputWithColor from '#/components/input-with-color/index.vue';
import UploadImg from '#/components/upload/image-upload.vue';
// 悬浮按钮属性面板
defineOptions({ name: 'FloatingActionButtonProperty' });
@@ -12,37 +25,42 @@ const formData = useVModel(props, 'modelValue', emit);
</script>
<template>
<el-form label-width="80px" :model="formData">
<el-card header="按钮配置" class="property-group" shadow="never">
<el-form-item label="展开方向" prop="direction">
<el-radio-group v-model="formData.direction">
<el-radio value="vertical">垂直</el-radio>
<el-radio value="horizontal">水平</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="显示文字" prop="showText">
<el-switch v-model="formData.showText" />
</el-form-item>
</el-card>
<el-card header="按钮列表" class="property-group" shadow="never">
<ElForm label-width="80px" :model="formData">
<ElCard header="按钮配置" class="property-group" shadow="never">
<ElFormItem label="展开方向" prop="direction">
<ElRadioGroup v-model="formData.direction">
<ElRadio value="vertical">垂直</ElRadio>
<ElRadio value="horizontal">水平</ElRadio>
</ElRadioGroup>
</ElFormItem>
<ElFormItem label="显示文字" prop="showText">
<ElSwitch v-model="formData.showText" />
</ElFormItem>
</ElCard>
<ElCard header="按钮列表" class="property-group" shadow="never">
<Draggable v-model="formData.list" :empty-item="{ textColor: '#fff' }">
<template #default="{ element, index }">
<el-form-item label="图标" :prop="`list[${index}].imgUrl`">
<UploadImg v-model="element.imgUrl" height="56px" width="56px" />
</el-form-item>
<el-form-item label="文字" :prop="`list[${index}].text`">
<ElFormItem label="图标" :prop="`list[${index}].imgUrl`">
<UploadImg
v-model="element.imgUrl"
height="56px"
width="56px"
:show-description="false"
/>
</ElFormItem>
<ElFormItem label="文字" :prop="`list[${index}].text`">
<InputWithColor
v-model="element.text"
v-model:color="element.textColor"
/>
</el-form-item>
<el-form-item label="跳转链接" :prop="`list[${index}].url`">
</ElFormItem>
<ElFormItem label="跳转链接" :prop="`list[${index}].url`">
<AppLinkInput v-model="element.url" />
</el-form-item>
</ElFormItem>
</template>
</Draggable>
</el-card>
</el-form>
</ElCard>
</ElForm>
</template>
<style scoped lang="scss"></style>

View File

@@ -6,6 +6,10 @@ import type { HotZoneItemProperty } from '#/components/diy-editor/components/mob
import { ref } from 'vue';
import { IconifyIcon } from '@vben/icons';
import { ElButton, ElDialog, ElImage } from 'element-plus';
import {
CONTROL_DOT_LIST,
CONTROL_TYPE_ENUM,
@@ -172,14 +176,14 @@ const handleAppLinkChange = (appLink: AppLink) => {
</script>
<template>
<Dialog
<ElDialog
v-model="dialogVisible"
title="设置热区"
width="780"
@close="handleClose"
>
<div ref="container" class="w-750px relative h-full">
<el-image
<ElImage
:src="imgUrl"
class="w-750px pointer-events-none h-full select-none"
/>
@@ -199,7 +203,7 @@ const handleAppLinkChange = (appLink: AppLink) => {
<span class="pointer-events-none select-none">{{
item.name || '双击选择链接'
}}</span>
<Icon
<IconifyIcon
icon="ep:close"
class="delete"
:size="14"
@@ -217,16 +221,16 @@ const handleAppLinkChange = (appLink: AppLink) => {
</div>
</div>
<template #footer>
<el-button @click="handleAdd" type="primary" plain>
<Icon icon="ep:plus" class="mr-5px" />
<ElButton @click="handleAdd" type="primary" plain>
<IconifyIcon icon="ep:plus" class="mr-5px" />
添加热区
</el-button>
<el-button @click="handleSubmit" type="primary" plain>
<Icon icon="ep:check" class="mr-5px" />
</ElButton>
<ElButton @click="handleSubmit" type="primary" plain>
<IconifyIcon icon="ep:check" class="mr-5px" />
确定
</el-button>
</ElButton>
</template>
</Dialog>
</ElDialog>
<AppLinkSelectDialog
ref="appLinkDialogRef"
@app-link-change="handleAppLinkChange"

View File

@@ -32,6 +32,7 @@ const handleOpenEditDialog = () => {
height="50px"
width="auto"
class="min-w-80px"
:show-description="false"
>
<template #tip>
<el-text type="info" size="small"> 推荐宽度 750</el-text>

View File

@@ -21,6 +21,7 @@ const formData = useVModel(props, 'modelValue', emit);
height="80px"
width="100%"
class="min-w-80px"
:show-description="false"
>
<template #tip> 建议宽度750 </template>
</UploadImg>

View File

@@ -35,7 +35,12 @@ const handleHotAreaSelected = (_: any, index: number) => {
<template v-for="(hotArea, index) in formData.list" :key="index">
<template v-if="selectedHotAreaIndex === index">
<el-form-item label="上传图片" :prop="`list[${index}].imgUrl`">
<UploadImg v-model="hotArea.imgUrl" height="80px" width="80px" />
<UploadImg
v-model="hotArea.imgUrl"
height="80px"
width="80px"
:show-description="false"
/>
</el-form-item>
<el-form-item label="链接" :prop="`list[${index}].url`">
<AppLinkInput v-model="hotArea.url" />

View File

@@ -1,6 +1,8 @@
<script setup lang="ts">
import type { MenuGridProperty } from './config';
import { ElImage } from 'element-plus';
/** 宫格导航 */
defineOptions({ name: 'MenuGrid' });
defineProps<{ property: MenuGridProperty }>();
@@ -11,13 +13,13 @@ defineProps<{ property: MenuGridProperty }>();
<div
v-for="(item, index) in property.list"
:key="index"
class="p-b-14px p-t-20px relative flex flex-col items-center"
class="relative flex flex-col items-center pb-3.5 pt-5"
:style="{ width: `${100 * (1 / property.column)}%` }"
>
<!-- 右上角角标 -->
<span
v-if="item.badge?.show"
class="left-50% top-10px z-1 h-20px rounded-50% p-x-6px text-12px leading-20px absolute text-center"
class="absolute left-1/2 top-2.5 z-10 h-5 rounded-full px-1.5 text-center text-xs leading-5"
:style="{
color: item.badge.textColor,
backgroundColor: item.badge.bgColor,
@@ -25,15 +27,15 @@ defineProps<{ property: MenuGridProperty }>();
>
{{ item.badge.text }}
</span>
<el-image v-if="item.iconUrl" class="h-28px w-28px" :src="item.iconUrl" />
<ElImage v-if="item.iconUrl" class="h-7 w-7" :src="item.iconUrl" />
<span
class="m-t-8px h-16px text-12px leading-16px"
class="mt-2 h-4 text-xs leading-4"
:style="{ color: item.titleColor }"
>
{{ item.title }}
</span>
<span
class="m-t-6px h-12px text-10px leading-12px"
class="mt-1.5 h-3 text-xs leading-3"
:style="{ color: item.subtitleColor }"
>
{{ item.subtitle }}

View File

@@ -2,6 +2,19 @@
import type { MenuGridProperty } from './config';
import { useVModel } from '@vueuse/core';
import {
ElCard,
ElForm,
ElFormItem,
ElRadio,
ElRadioGroup,
ElSwitch,
} from 'element-plus';
import AppLinkInput from '#/components/app-link-input/index.vue';
import ComponentContainerProperty from '#/components/diy-editor/components/ComponentContainerProperty.vue';
import Draggable from '#/components/draggable/index.vue';
import UploadImg from '#/components/upload/image-upload.vue';
import { EMPTY_MENU_GRID_ITEM_PROPERTY } from './config';
@@ -16,58 +29,63 @@ const formData = useVModel(props, 'modelValue', emit);
<template>
<ComponentContainerProperty v-model="formData.style">
<!-- 表单 -->
<el-form label-width="80px" :model="formData" class="m-t-8px">
<el-form-item label="每行数量" prop="column">
<el-radio-group v-model="formData.column">
<el-radio :value="3">3</el-radio>
<el-radio :value="4">4</el-radio>
</el-radio-group>
</el-form-item>
<ElForm label-width="80px" :model="formData" class="m-t-8px">
<ElFormItem label="每行数量" prop="column">
<ElRadioGroup v-model="formData.column">
<ElRadio :value="3">3</ElRadio>
<ElRadio :value="4">4</ElRadio>
</ElRadioGroup>
</ElFormItem>
<el-card header="菜单设置" class="property-group" shadow="never">
<ElCard header="菜单设置" class="property-group" shadow="never">
<Draggable
v-model="formData.list"
:empty-item="EMPTY_MENU_GRID_ITEM_PROPERTY"
>
<template #default="{ element }">
<el-form-item label="图标" prop="iconUrl">
<UploadImg v-model="element.iconUrl" height="80px" width="80px">
<ElFormItem label="图标" prop="iconUrl">
<UploadImg
v-model="element.iconUrl"
height="80px"
width="80px"
:show-description="false"
>
<template #tip> 建议尺寸44 * 44 </template>
</UploadImg>
</el-form-item>
<el-form-item label="标题" prop="title">
</ElFormItem>
<ElFormItem label="标题" prop="title">
<InputWithColor
v-model="element.title"
v-model:color="element.titleColor"
/>
</el-form-item>
<el-form-item label="副标题" prop="subtitle">
</ElFormItem>
<ElFormItem label="副标题" prop="subtitle">
<InputWithColor
v-model="element.subtitle"
v-model:color="element.subtitleColor"
/>
</el-form-item>
<el-form-item label="链接" prop="url">
</ElFormItem>
<ElFormItem label="链接" prop="url">
<AppLinkInput v-model="element.url" />
</el-form-item>
<el-form-item label="显示角标" prop="badge.show">
<el-switch v-model="element.badge.show" />
</el-form-item>
</ElFormItem>
<ElFormItem label="显示角标" prop="badge.show">
<ElSwitch v-model="element.badge.show" />
</ElFormItem>
<template v-if="element.badge.show">
<el-form-item label="角标内容" prop="badge.text">
<ElFormItem label="角标内容" prop="badge.text">
<InputWithColor
v-model="element.badge.text"
v-model:color="element.badge.textColor"
/>
</el-form-item>
<el-form-item label="背景颜色" prop="badge.bgColor">
</ElFormItem>
<ElFormItem label="背景颜色" prop="badge.bgColor">
<ColorInput v-model="element.badge.bgColor" />
</el-form-item>
</ElFormItem>
</template>
</template>
</Draggable>
</el-card>
</el-form>
</ElCard>
</ElForm>
</ComponentContainerProperty>
</template>

View File

@@ -26,7 +26,12 @@ const formData = useVModel(props, 'modelValue', emit);
>
<template #default="{ element }">
<el-form-item label="图标" prop="iconUrl">
<UploadImg v-model="element.iconUrl" height="80px" width="80px">
<UploadImg
v-model="element.iconUrl"
height="80px"
width="80px"
:show-description="false"
>
<template #tip> 建议尺寸44 * 44 </template>
</UploadImg>
</el-form-item>

View File

@@ -46,7 +46,12 @@ const formData = useVModel(props, 'modelValue', emit);
>
<template #default="{ element }">
<el-form-item label="图标" prop="iconUrl">
<UploadImg v-model="element.iconUrl" height="80px" width="80px">
<UploadImg
v-model="element.iconUrl"
height="80px"
width="80px"
:show-description="false"
>
<template #tip> 建议尺寸98 * 98 </template>
</UploadImg>
</el-form-item>

View File

@@ -1,9 +1,24 @@
<script lang="ts" setup>
import type { NavigationBarCellProperty } from '../config';
import type { Rect } from '#/components/magic-cube-editor/util';
import { computed, ref } from 'vue';
import { useVModel } from '@vueuse/core';
import {
ElFormItem,
ElInput,
ElRadio,
ElRadioGroup,
ElSlider,
} from 'element-plus';
import appNavBarMp from '#/assets/imgs/diy/app-nav-bar-mp.png';
import AppLinkInput from '#/components/app-link-input/index.vue';
import ColorInput from '#/components/color-input/index.vue';
import MagicCubeEditor from '#/components/magic-cube-editor/index.vue';
import UploadImg from '#/components/upload/image-upload.vue';
// 导航栏属性面板
defineOptions({ name: 'NavigationBarCellProperty' });
@@ -24,6 +39,18 @@ const cellList = useVModel(props, 'modelValue', emit);
// 单元格数量小程序6个右侧胶囊按钮占了2个其它平台8个
const cellCount = computed(() => (props.isMp ? 6 : 8));
// 转换为Rect格式的数据
const rectList = computed<Rect[]>(() => {
return cellList.value.map((cell) => ({
left: cell.left,
top: cell.top,
width: cell.width,
height: cell.height,
right: cell.left + cell.width,
bottom: cell.top + cell.height,
}));
});
// 选中的热区
const selectedHotAreaIndex = ref(0);
const handleHotAreaSelected = (
@@ -41,7 +68,7 @@ const handleHotAreaSelected = (
<template>
<div class="h-40px flex items-center justify-center">
<MagicCubeEditor
v-model="cellList"
v-model="rectList"
:cols="cellCount"
:cube-size="38"
:rows="1"
@@ -51,54 +78,55 @@ const handleHotAreaSelected = (
<img
v-if="isMp"
alt=""
class="h-30px w-76px"
src="@/assets/imgs/diy/app-nav-bar-mp.png"
style="width: 76px; height: 30px"
:src="appNavBarMp"
/>
</div>
<template v-for="(cell, cellIndex) in cellList" :key="cellIndex">
<template v-if="selectedHotAreaIndex === Number(cellIndex)">
<el-form-item :prop="`cell[${cellIndex}].type`" label="类型">
<el-radio-group v-model="cell.type">
<el-radio value="text">文字</el-radio>
<el-radio value="image">图片</el-radio>
<el-radio value="search">搜索框</el-radio>
</el-radio-group>
</el-form-item>
<ElFormItem :prop="`cell[${cellIndex}].type`" label="类型">
<ElRadioGroup v-model="cell.type">
<ElRadio value="text">文字</ElRadio>
<ElRadio value="image">图片</ElRadio>
<ElRadio value="search">搜索框</ElRadio>
</ElRadioGroup>
</ElFormItem>
<!-- 1. 文字 -->
<template v-if="cell.type === 'text'">
<el-form-item :prop="`cell[${cellIndex}].text`" label="内容">
<el-input v-model="cell!.text" maxlength="10" show-word-limit />
</el-form-item>
<el-form-item :prop="`cell[${cellIndex}].text`" label="颜色">
<ElFormItem :prop="`cell[${cellIndex}].text`" label="内容">
<ElInput v-model="cell!.text" maxlength="10" show-word-limit />
</ElFormItem>
<ElFormItem :prop="`cell[${cellIndex}].text`" label="颜色">
<ColorInput v-model="cell!.textColor" />
</el-form-item>
<el-form-item :prop="`cell[${cellIndex}].url`" label="链接">
</ElFormItem>
<ElFormItem :prop="`cell[${cellIndex}].url`" label="链接">
<AppLinkInput v-model="cell.url" />
</el-form-item>
</ElFormItem>
</template>
<!-- 2. 图片 -->
<template v-else-if="cell.type === 'image'">
<el-form-item :prop="`cell[${cellIndex}].imgUrl`" label="图片">
<ElFormItem :prop="`cell[${cellIndex}].imgUrl`" label="图片">
<UploadImg
v-model="cell.imgUrl"
:limit="1"
height="56px"
width="56px"
:show-description="false"
>
<template #tip>建议尺寸 56*56</template>
</UploadImg>
</el-form-item>
<el-form-item :prop="`cell[${cellIndex}].url`" label="链接">
</ElFormItem>
<ElFormItem :prop="`cell[${cellIndex}].url`" label="链接">
<AppLinkInput v-model="cell.url" />
</el-form-item>
</ElFormItem>
</template>
<!-- 3. 搜索框 -->
<template v-else>
<el-form-item :prop="`cell[${cellIndex}].placeholder`" label="提示文字">
<el-input v-model="cell.placeholder" maxlength="10" show-word-limit />
</el-form-item>
<el-form-item :prop="`cell[${cellIndex}].borderRadius`" label="圆角">
<el-slider
<ElFormItem :prop="`cell[${cellIndex}].placeholder`" label="提示文字">
<ElInput v-model="cell.placeholder" maxlength="10" show-word-limit />
</ElFormItem>
<ElFormItem :prop="`cell[${cellIndex}].borderRadius`" label="圆角">
<ElSlider
v-model="cell.borderRadius"
:max="100"
:min="0"
@@ -106,7 +134,7 @@ const handleHotAreaSelected = (
input-size="small"
show-input
/>
</el-form-item>
</ElFormItem>
</template>
</template>
</template>

View File

@@ -10,6 +10,7 @@ import type { SearchProperty } from '#/components/diy-editor/components/mobile/S
import { computed } from 'vue';
import appNavbarMp from '#/assets/imgs/diy/app-nav-bar-mp.png';
import SearchBar from '#/components/diy-editor/components/mobile/SearchBar/index.vue';
/** 页面顶部导航栏 */
@@ -75,9 +76,9 @@ const getSearchProp = computed(() => (cell: NavigationBarCellProperty) => {
</div>
<img
v-if="property._local?.previewMp"
src="@/assets/imgs/diy/app-nav-bar-mp.png"
:src="appNavbarMp"
alt=""
class="h-30px w-86px"
style="width: 86px; height: 30px"
/>
</div>
</template>

View File

@@ -70,6 +70,7 @@ if (!formData.value._local) {
:limit="1"
width="56px"
height="56px"
:show-description="false"
/>
<span class="mb-2 ml-2 text-xs text-gray-400">建议宽度750</span>
</div>

View File

@@ -21,7 +21,11 @@ const formData = useVModel(props, 'modelValue', emit);
<ComponentContainerProperty v-model="formData.style">
<el-form label-width="80px" :model="formData" :rules="rules">
<el-form-item label="公告图标" prop="iconUrl">
<UploadImg v-model="formData.iconUrl" height="48px">
<UploadImg
v-model="formData.iconUrl"
height="48px"
:show-description="false"
>
<template #tip>建议尺寸24 * 24</template>
</UploadImg>
</el-form-item>

View File

@@ -28,7 +28,11 @@ const formData = useVModel(props, 'modelValue', emit);
<ColorInput v-model="formData!.backgroundColor" />
</el-form-item>
<el-form-item label="背景图片" prop="backgroundImage">
<UploadImg v-model="formData!.backgroundImage" :limit="1">
<UploadImg
v-model="formData!.backgroundImage"
:limit="1"
:show-description="false"
>
<template #tip>建议宽度 750px</template>
</UploadImg>
</el-form-item>

View File

@@ -16,7 +16,12 @@ const formData = useVModel(props, 'modelValue', emit);
<Draggable v-model="formData.list" :empty-item="{ showType: 'once' }">
<template #default="{ element, index }">
<el-form-item label="图片" :prop="`list[${index}].imgUrl`">
<UploadImg v-model="element.imgUrl" height="56px" width="56px" />
<UploadImg
v-model="element.imgUrl"
height="56px"
width="56px"
:show-description="false"
/>
</el-form-item>
<el-form-item label="跳转链接" :prop="`list[${index}].url`">
<AppLinkInput v-model="element.url" />

View File

@@ -7,6 +7,8 @@ import { ref, watch } from 'vue';
import { fenToYuan } from '@vben/utils';
import { ElImage } from 'element-plus';
import * as ProductSpuApi from '#/api/mall/product/spu';
/** 商品卡片 */
@@ -55,7 +57,7 @@ const calculateWidth = () => {
</script>
<template>
<div
class="min-h-30px box-content flex w-full flex-row flex-wrap"
class="box-content flex min-h-[30px] w-full flex-row flex-wrap"
ref="containerRef"
>
<div
@@ -74,28 +76,28 @@ const calculateWidth = () => {
<!-- 角标 -->
<div
v-if="property.badge.show && property.badge.imgUrl"
class="z-1 absolute left-0 top-0 items-center justify-center"
class="absolute left-0 top-0 z-[1] items-center justify-center"
>
<el-image
<ElImage
fit="cover"
:src="property.badge.imgUrl"
class="h-26px w-38px"
class="h-[26px] w-[38px]"
/>
</div>
<!-- 商品封面图 -->
<div
class="h-140px"
class="h-[140px]"
:class="[
{
'w-full': property.layoutType !== 'oneColSmallImg',
'w-140px': property.layoutType === 'oneColSmallImg',
'w-[140px]': property.layoutType === 'oneColSmallImg',
},
]"
>
<el-image fit="cover" class="h-full w-full" :src="spu.picUrl" />
<ElImage fit="cover" class="h-full w-full" :src="spu.picUrl" />
</div>
<div
class="gap-8px p-8px box-border flex flex-col"
class="box-border flex flex-col gap-[8px] p-[8px]"
:class="[
{
'w-full': property.layoutType !== 'oneColSmallImg',
@@ -107,7 +109,7 @@ const calculateWidth = () => {
<!-- 商品名称 -->
<div
v-if="property.fields.name.show"
class="text-14px"
class="text-[14px]"
:class="[
{
truncate: property.layoutType !== 'oneColSmallImg',
@@ -122,7 +124,7 @@ const calculateWidth = () => {
<!-- 商品简介 -->
<div
v-if="property.fields.introduction.show"
class="text-12px truncate"
class="truncate text-[12px]"
:style="{ color: property.fields.introduction.color }"
>
{{ spu.introduction }}
@@ -131,7 +133,7 @@ const calculateWidth = () => {
<!-- 价格 -->
<span
v-if="property.fields.price.show"
class="text-16px"
class="text-[16px]"
:style="{ color: property.fields.price.color }"
>
{{ fenToYuan(spu.price as any) }}
@@ -139,12 +141,12 @@ const calculateWidth = () => {
<!-- 市场价 -->
<span
v-if="property.fields.marketPrice.show && spu.marketPrice"
class="ml-4px text-10px line-through"
class="ml-[4px] text-[10px] line-through"
:style="{ color: property.fields.marketPrice.color }"
>{{ fenToYuan(spu.marketPrice) }}
</span>
</div>
<div class="text-12px">
<div class="text-[12px]">
<!-- 销量 -->
<span
v-if="property.fields.salesCount.show"
@@ -162,11 +164,11 @@ const calculateWidth = () => {
</div>
</div>
<!-- 购买按钮 -->
<div class="bottom-8px right-8px absolute">
<div class="absolute bottom-[8px] right-[8px]">
<!-- 文字按钮 -->
<span
v-if="property.btnBuy.type === 'text'"
class="p-x-12px p-y-4px text-12px rounded-full text-white"
class="rounded-full px-[12px] py-[4px] text-[12px] text-white"
:style="{
background: `linear-gradient(to right, ${property.btnBuy.bgBeginColor}, ${property.btnBuy.bgEndColor}`,
}"
@@ -174,9 +176,9 @@ const calculateWidth = () => {
{{ property.btnBuy.text }}
</span>
<!-- 图片按钮 -->
<el-image
<ElImage
v-else
class="h-28px w-28px rounded-full"
class="h-[28px] w-[28px] rounded-full"
fit="cover"
:src="property.btnBuy.imgUrl"
/>

View File

@@ -1,8 +1,24 @@
<script setup lang="ts">
import type { ProductCardProperty } from './config';
import { useVModel } from '@vueuse/core';
import { IconifyIcon } from '@vben/icons';
import { useVModel } from '@vueuse/core';
import {
ElCard,
ElCheckbox,
ElForm,
ElFormItem,
ElInput,
ElRadioButton,
ElRadioGroup,
ElSlider,
ElSwitch,
ElTooltip,
} from 'element-plus';
import ColorInput from '#/components/color-input/index.vue';
import UploadImg from '#/components/upload/image-upload.vue';
import SpuShowcase from '#/views/mall/product/spu/components/spu-showcase.vue';
// 商品卡片属性面板
@@ -15,114 +31,116 @@ const formData = useVModel(props, 'modelValue', emit);
<template>
<ComponentContainerProperty v-model="formData.style">
<el-form label-width="80px" :model="formData">
<el-card header="商品列表" class="property-group" shadow="never">
<ElForm label-width="80px" :model="formData">
<ElCard header="商品列表" class="property-group" shadow="never">
<SpuShowcase v-model="formData.spuIds" />
</el-card>
<el-card header="商品样式" class="property-group" shadow="never">
<el-form-item label="布局" prop="type">
<el-radio-group v-model="formData.layoutType">
<el-tooltip class="item" content="单列大图" placement="bottom">
<el-radio-button value="oneColBigImg">
<Icon icon="fluent:text-column-one-24-filled" />
</el-radio-button>
</el-tooltip>
<el-tooltip class="item" content="单列小图" placement="bottom">
<el-radio-button value="oneColSmallImg">
<Icon icon="fluent:text-column-two-left-24-filled" />
</el-radio-button>
</el-tooltip>
<el-tooltip class="item" content="双列" placement="bottom">
<el-radio-button value="twoCol">
<Icon icon="fluent:text-column-two-24-filled" />
</el-radio-button>
</el-tooltip>
</el-radio-group>
</el-form-item>
<el-form-item label="商品名称" prop="fields.name.show">
</ElCard>
<ElCard header="商品样式" class="property-group" shadow="never">
<ElFormItem label="布局" prop="type">
<ElRadioGroup v-model="formData.layoutType">
<ElTooltip class="item" content="单列大图" placement="bottom">
<ElRadioButton value="oneColBigImg">
<IconifyIcon icon="fluent:text-column-one-24-filled" />
</ElRadioButton>
</ElTooltip>
<ElTooltip class="item" content="单列小图" placement="bottom">
<ElRadioButton value="oneColSmallImg">
<IconifyIcon icon="fluent:text-column-two-left-24-filled" />
</ElRadioButton>
</ElTooltip>
<ElTooltip class="item" content="双列" placement="bottom">
<ElRadioButton value="twoCol">
<IconifyIcon icon="fluent:text-column-two-24-filled" />
</ElRadioButton>
</ElTooltip>
</ElRadioGroup>
</ElFormItem>
<ElFormItem label="商品名称" prop="fields.name.show">
<div class="gap-8px flex">
<ColorInput v-model="formData.fields.name.color" />
<el-checkbox v-model="formData.fields.name.show" />
<ElCheckbox v-model="formData.fields.name.show" />
</div>
</el-form-item>
<el-form-item label="商品简介" prop="fields.introduction.show">
</ElFormItem>
<ElFormItem label="商品简介" prop="fields.introduction.show">
<div class="gap-8px flex">
<ColorInput v-model="formData.fields.introduction.color" />
<el-checkbox v-model="formData.fields.introduction.show" />
<ElCheckbox v-model="formData.fields.introduction.show" />
</div>
</el-form-item>
<el-form-item label="商品价格" prop="fields.price.show">
</ElFormItem>
<ElFormItem label="商品价格" prop="fields.price.show">
<div class="gap-8px flex">
<ColorInput v-model="formData.fields.price.color" />
<el-checkbox v-model="formData.fields.price.show" />
<ElCheckbox v-model="formData.fields.price.show" />
</div>
</el-form-item>
<el-form-item label="市场价" prop="fields.marketPrice.show">
</ElFormItem>
<ElFormItem label="市场价" prop="fields.marketPrice.show">
<div class="gap-8px flex">
<ColorInput v-model="formData.fields.marketPrice.color" />
<el-checkbox v-model="formData.fields.marketPrice.show" />
<ElCheckbox v-model="formData.fields.marketPrice.show" />
</div>
</el-form-item>
<el-form-item label="商品销量" prop="fields.salesCount.show">
</ElFormItem>
<ElFormItem label="商品销量" prop="fields.salesCount.show">
<div class="gap-8px flex">
<ColorInput v-model="formData.fields.salesCount.color" />
<el-checkbox v-model="formData.fields.salesCount.show" />
<ElCheckbox v-model="formData.fields.salesCount.show" />
</div>
</el-form-item>
<el-form-item label="商品库存" prop="fields.stock.show">
</ElFormItem>
<ElFormItem label="商品库存" prop="fields.stock.show">
<div class="gap-8px flex">
<ColorInput v-model="formData.fields.stock.color" />
<el-checkbox v-model="formData.fields.stock.show" />
<ElCheckbox v-model="formData.fields.stock.show" />
</div>
</el-form-item>
</el-card>
<el-card header="角标" class="property-group" shadow="never">
<el-form-item label="角标" prop="badge.show">
<el-switch v-model="formData.badge.show" />
</el-form-item>
<el-form-item
label="角标"
prop="badge.imgUrl"
v-if="formData.badge.show"
>
<UploadImg v-model="formData.badge.imgUrl" height="44px" width="72px">
</ElFormItem>
</ElCard>
<ElCard header="角标" class="property-group" shadow="never">
<ElFormItem label="角标" prop="badge.show">
<ElSwitch v-model="formData.badge.show" />
</ElFormItem>
<ElFormItem label="角标" prop="badge.imgUrl" v-if="formData.badge.show">
<UploadImg
v-model="formData.badge.imgUrl"
height="44px"
width="72px"
:show-description="false"
>
<template #tip> 建议尺寸36 * 22 </template>
</UploadImg>
</el-form-item>
</el-card>
<el-card header="按钮" class="property-group" shadow="never">
<el-form-item label="按钮类型" prop="btnBuy.type">
<el-radio-group v-model="formData.btnBuy.type">
<el-radio-button value="text">文字</el-radio-button>
<el-radio-button value="img">图片</el-radio-button>
</el-radio-group>
</el-form-item>
</ElFormItem>
</ElCard>
<ElCard header="按钮" class="property-group" shadow="never">
<ElFormItem label="按钮类型" prop="btnBuy.type">
<ElRadioGroup v-model="formData.btnBuy.type">
<ElRadioButton value="text">文字</ElRadioButton>
<ElRadioButton value="img">图片</ElRadioButton>
</ElRadioGroup>
</ElFormItem>
<template v-if="formData.btnBuy.type === 'text'">
<el-form-item label="按钮文字" prop="btnBuy.text">
<el-input v-model="formData.btnBuy.text" />
</el-form-item>
<el-form-item label="左侧背景" prop="btnBuy.bgBeginColor">
<ElFormItem label="按钮文字" prop="btnBuy.text">
<ElInput v-model="formData.btnBuy.text" />
</ElFormItem>
<ElFormItem label="左侧背景" prop="btnBuy.bgBeginColor">
<ColorInput v-model="formData.btnBuy.bgBeginColor" />
</el-form-item>
<el-form-item label="右侧背景" prop="btnBuy.bgEndColor">
</ElFormItem>
<ElFormItem label="右侧背景" prop="btnBuy.bgEndColor">
<ColorInput v-model="formData.btnBuy.bgEndColor" />
</el-form-item>
</ElFormItem>
</template>
<template v-else>
<el-form-item label="图片" prop="btnBuy.imgUrl">
<ElFormItem label="图片" prop="btnBuy.imgUrl">
<UploadImg
v-model="formData.btnBuy.imgUrl"
height="56px"
width="56px"
:show-description="false"
>
<template #tip> 建议尺寸56 * 56 </template>
</UploadImg>
</el-form-item>
</ElFormItem>
</template>
</el-card>
<el-card header="商品样式" class="property-group" shadow="never">
<el-form-item label="上圆角" prop="borderRadiusTop">
<el-slider
</ElCard>
<ElCard header="商品样式" class="property-group" shadow="never">
<ElFormItem label="上圆角" prop="borderRadiusTop">
<ElSlider
v-model="formData.borderRadiusTop"
:max="100"
:min="0"
@@ -130,9 +148,9 @@ const formData = useVModel(props, 'modelValue', emit);
input-size="small"
:show-input-controls="false"
/>
</el-form-item>
<el-form-item label="下圆角" prop="borderRadiusBottom">
<el-slider
</ElFormItem>
<ElFormItem label="下圆角" prop="borderRadiusBottom">
<ElSlider
v-model="formData.borderRadiusBottom"
:max="100"
:min="0"
@@ -140,9 +158,9 @@ const formData = useVModel(props, 'modelValue', emit);
input-size="small"
:show-input-controls="false"
/>
</el-form-item>
<el-form-item label="间隔" prop="space">
<el-slider
</ElFormItem>
<ElFormItem label="间隔" prop="space">
<ElSlider
v-model="formData.space"
:max="100"
:min="0"
@@ -150,9 +168,9 @@ const formData = useVModel(props, 'modelValue', emit);
input-size="small"
:show-input-controls="false"
/>
</el-form-item>
</el-card>
</el-form>
</ElFormItem>
</ElCard>
</ElForm>
</ComponentContainerProperty>
</template>

View File

@@ -61,7 +61,12 @@ const formData = useVModel(props, 'modelValue', emit);
prop="badge.imgUrl"
v-if="formData.badge.show"
>
<UploadImg v-model="formData.badge.imgUrl" height="44px" width="72px">
<UploadImg
v-model="formData.badge.imgUrl"
height="44px"
width="72px"
:show-description="false"
>
<template #tip> 建议尺寸36 * 22 </template>
</UploadImg>
</el-form-item>

View File

@@ -8,6 +8,8 @@ import { ref, watch } from 'vue';
import { fenToYuan } from '@vben/utils';
import { ElImage } from 'element-plus';
import * as ProductSpuApi from '#/api/mall/product/spu';
import * as CombinationActivityApi from '#/api/mall/promotion/combination/combinationActivity';
@@ -97,7 +99,7 @@ const calculateWidth = () => {
</script>
<template>
<div
class="min-h-30px box-content flex w-full flex-row flex-wrap"
class="box-content flex min-h-[30px] w-full flex-row flex-wrap"
ref="containerRef"
>
<div
@@ -116,28 +118,28 @@ const calculateWidth = () => {
<!-- 角标 -->
<div
v-if="property.badge.show"
class="z-1 absolute left-0 top-0 items-center justify-center"
class="absolute left-0 top-0 z-[1] items-center justify-center"
>
<el-image
<ElImage
fit="cover"
:src="property.badge.imgUrl"
class="h-26px w-38px"
class="h-[26px] w-[38px]"
/>
</div>
<!-- 商品封面图 -->
<div
class="h-140px"
class="h-[140px]"
:class="[
{
'w-full': property.layoutType !== 'oneColSmallImg',
'w-140px': property.layoutType === 'oneColSmallImg',
'w-[140px]': property.layoutType === 'oneColSmallImg',
},
]"
>
<el-image fit="cover" class="h-full w-full" :src="spu.picUrl" />
<ElImage fit="cover" class="h-full w-full" :src="spu.picUrl" />
</div>
<div
class="gap-8px p-8px box-border flex flex-col"
class="box-border flex flex-col gap-[8px] p-[8px]"
:class="[
{
'w-full': property.layoutType !== 'oneColSmallImg',
@@ -149,7 +151,7 @@ const calculateWidth = () => {
<!-- 商品名称 -->
<div
v-if="property.fields.name.show"
class="text-14px"
class="text-[14px]"
:class="[
{
truncate: property.layoutType !== 'oneColSmallImg',
@@ -164,7 +166,7 @@ const calculateWidth = () => {
<!-- 商品简介 -->
<div
v-if="property.fields.introduction.show"
class="text-12px truncate"
class="truncate text-[12px]"
:style="{ color: property.fields.introduction.color }"
>
{{ spu.introduction }}
@@ -173,7 +175,7 @@ const calculateWidth = () => {
<!-- 价格 -->
<span
v-if="property.fields.price.show"
class="text-16px"
class="text-[16px]"
:style="{ color: property.fields.price.color }"
>
{{ fenToYuan(spu.price || Infinity) }}
@@ -181,13 +183,13 @@ const calculateWidth = () => {
<!-- 市场价 -->
<span
v-if="property.fields.marketPrice.show && spu.marketPrice"
class="ml-4px text-10px line-through"
class="ml-[4px] text-[10px] line-through"
:style="{ color: property.fields.marketPrice.color }"
>
{{ fenToYuan(spu.marketPrice) }}
</span>
</div>
<div class="text-12px">
<div class="text-[12px]">
<!-- 销量 -->
<span
v-if="property.fields.salesCount.show"
@@ -205,11 +207,11 @@ const calculateWidth = () => {
</div>
</div>
<!-- 购买按钮 -->
<div class="bottom-8px right-8px absolute">
<div class="absolute bottom-[8px] right-[8px]">
<!-- 文字按钮 -->
<span
v-if="property.btnBuy.type === 'text'"
class="p-x-12px p-y-4px text-12px rounded-full text-white"
class="rounded-full px-[12px] py-[4px] text-[12px] text-white"
:style="{
background: `linear-gradient(to right, ${property.btnBuy.bgBeginColor}, ${property.btnBuy.bgEndColor}`,
}"
@@ -217,9 +219,9 @@ const calculateWidth = () => {
{{ property.btnBuy.text }}
</span>
<!-- 图片按钮 -->
<el-image
<ElImage
v-else
class="h-28px w-28px rounded-full"
class="h-[28px] w-[28px] rounded-full"
fit="cover"
:src="property.btnBuy.imgUrl"
/>

View File

@@ -5,9 +5,22 @@ import type { MallCombinationActivityApi } from '#/api/mall/promotion/combinatio
import { onMounted, ref } from 'vue';
import { IconifyIcon } from '@vben/icons';
import { useVModel } from '@vueuse/core';
import {
ElForm,
ElFormItem,
ElRadioButton,
ElRadioGroup,
ElSlider,
ElSwitch,
ElTooltip,
} from 'element-plus';
import * as CombinationActivityApi from '#/api/mall/promotion/combination/combinationActivity';
import ColorInput from '#/components/color-input/index.vue';
import UploadImg from '#/components/upload/image-upload.vue';
import { CommonStatusEnum } from '#/utils';
import CombinationShowcase from '#/views/mall/promotion/combination/components/combination-showcase.vue';
@@ -31,119 +44,116 @@ onMounted(async () => {
<template>
<ComponentContainerProperty v-model="formData.style">
<el-form label-width="80px" :model="formData">
<el-card header="拼团活动" class="property-group" shadow="never">
<ElForm label-width="80px" :model="formData">
<ElCard header="拼团活动" class="property-group" shadow="never">
<CombinationShowcase v-model="formData.activityIds" />
</el-card>
<el-card header="商品样式" class="property-group" shadow="never">
<el-form-item label="布局" prop="type">
<el-radio-group v-model="formData.layoutType">
<el-tooltip class="item" content="单列大图" placement="bottom">
<el-radio-button value="oneColBigImg">
<Icon icon="fluent:text-column-one-24-filled" />
</el-radio-button>
</el-tooltip>
<el-tooltip class="item" content="单列小图" placement="bottom">
<el-radio-button value="oneColSmallImg">
<Icon icon="fluent:text-column-two-left-24-filled" />
</el-radio-button>
</el-tooltip>
<el-tooltip class="item" content="双列" placement="bottom">
<el-radio-button value="twoCol">
<Icon icon="fluent:text-column-two-24-filled" />
</el-radio-button>
</el-tooltip>
</ElCard>
<ElCard header="商品样式" class="property-group" shadow="never">
<ElFormItem label="布局" prop="type">
<ElRadioGroup v-model="formData.layoutType">
<ElTooltip class="item" content="单列大图" placement="bottom">
<ElRadioButton value="oneColBigImg">
<IconifyIcon icon="fluent:text-column-one-24-filled" />
</ElRadioButton>
</ElTooltip>
<ElTooltip class="item" content="单列小图" placement="bottom">
<ElRadioButton value="oneColSmallImg">
<IconifyIcon icon="fluent:text-column-two-left-24-filled" />
</ElRadioButton>
</ElTooltip>
<ElTooltip class="item" content="双列" placement="bottom">
<ElRadioButton value="twoCol">
<IconifyIcon icon="fluent:text-column-two-24-filled" />
</ElRadioButton>
</ElTooltip>
<!--<el-tooltip class="item" content="三列" placement="bottom">
<el-radio-button value="threeCol">
<Icon icon="fluent:text-column-three-24-filled" />
</el-radio-button>
</el-tooltip>-->
</el-radio-group>
</el-form-item>
<el-form-item label="商品名称" prop="fields.name.show">
</ElRadioButton>
</ElTooltip>-->
</ElRadioGroup>
</ElFormItem>
<ElFormItem label="商品名称" prop="fields.name.show">
<div class="gap-8px flex">
<ColorInput v-model="formData.fields.name.color" />
<el-checkbox v-model="formData.fields.name.show" />
<ElCheckbox v-model="formData.fields.name.show" />
</div>
</el-form-item>
<el-form-item label="商品简介" prop="fields.introduction.show">
</ElFormItem>
<ElFormItem label="商品简介" prop="fields.introduction.show">
<div class="gap-8px flex">
<ColorInput v-model="formData.fields.introduction.color" />
<el-checkbox v-model="formData.fields.introduction.show" />
<ElCheckbox v-model="formData.fields.introduction.show" />
</div>
</el-form-item>
<el-form-item label="商品价格" prop="fields.price.show">
</ElFormItem>
<ElFormItem label="商品价格" prop="fields.price.show">
<div class="gap-8px flex">
<ColorInput v-model="formData.fields.price.color" />
<el-checkbox v-model="formData.fields.price.show" />
<ElCheckbox v-model="formData.fields.price.show" />
</div>
</el-form-item>
<el-form-item label="市场价" prop="fields.marketPrice.show">
</ElFormItem>
<ElFormItem label="市场价" prop="fields.marketPrice.show">
<div class="gap-8px flex">
<ColorInput v-model="formData.fields.marketPrice.color" />
<el-checkbox v-model="formData.fields.marketPrice.show" />
<ElCheckbox v-model="formData.fields.marketPrice.show" />
</div>
</el-form-item>
<el-form-item label="商品销量" prop="fields.salesCount.show">
</ElFormItem>
<ElFormItem label="商品销量" prop="fields.salesCount.show">
<div class="gap-8px flex">
<ColorInput v-model="formData.fields.salesCount.color" />
<el-checkbox v-model="formData.fields.salesCount.show" />
<ElCheckbox v-model="formData.fields.salesCount.show" />
</div>
</el-form-item>
<el-form-item label="商品库存" prop="fields.stock.show">
</ElFormItem>
<ElFormItem label="商品库存" prop="fields.stock.show">
<div class="gap-8px flex">
<ColorInput v-model="formData.fields.stock.color" />
<el-checkbox v-model="formData.fields.stock.show" />
<ElCheckbox v-model="formData.fields.stock.show" />
</div>
</el-form-item>
</el-card>
<el-card header="角标" class="property-group" shadow="never">
<el-form-item label="角标" prop="badge.show">
<el-switch v-model="formData.badge.show" />
</el-form-item>
<el-form-item
label="角标"
prop="badge.imgUrl"
v-if="formData.badge.show"
>
</ElFormItem>
</ElCard>
<ElCard header="角标" class="property-group" shadow="never">
<ElFormItem label="角标" prop="badge.show">
<ElSwitch v-model="formData.badge.show" />
</ElFormItem>
<ElFormItem label="角标" prop="badge.imgUrl" v-if="formData.badge.show">
<UploadImg v-model="formData.badge.imgUrl" height="44px" width="72px">
<template #tip> 建议尺寸36 * 22</template>
</UploadImg>
</el-form-item>
</el-card>
<el-card header="按钮" class="property-group" shadow="never">
<el-form-item label="按钮类型" prop="btnBuy.type">
<el-radio-group v-model="formData.btnBuy.type">
<el-radio-button value="text">文字</el-radio-button>
<el-radio-button value="img">图片</el-radio-button>
</el-radio-group>
</el-form-item>
</ElFormItem>
</ElCard>
<ElCard header="按钮" class="property-group" shadow="never">
<ElFormItem label="按钮类型" prop="btnBuy.type">
<ElRadioGroup v-model="formData.btnBuy.type">
<ElRadioButton value="text">文字</ElRadioButton>
<ElRadioButton value="img">图片</ElRadioButton>
</ElRadioGroup>
</ElFormItem>
<template v-if="formData.btnBuy.type === 'text'">
<el-form-item label="按钮文字" prop="btnBuy.text">
<el-input v-model="formData.btnBuy.text" />
</el-form-item>
<el-form-item label="左侧背景" prop="btnBuy.bgBeginColor">
<ElFormItem label="按钮文字" prop="btnBuy.text">
<ElInput v-model="formData.btnBuy.text" />
</ElFormItem>
<ElFormItem label="左侧背景" prop="btnBuy.bgBeginColor">
<ColorInput v-model="formData.btnBuy.bgBeginColor" />
</el-form-item>
<el-form-item label="右侧背景" prop="btnBuy.bgEndColor">
</ElFormItem>
<ElFormItem label="右侧背景" prop="btnBuy.bgEndColor">
<ColorInput v-model="formData.btnBuy.bgEndColor" />
</el-form-item>
</ElFormItem>
</template>
<template v-else>
<el-form-item label="图片" prop="btnBuy.imgUrl">
<ElFormItem label="图片" prop="btnBuy.imgUrl">
<UploadImg
v-model="formData.btnBuy.imgUrl"
height="56px"
width="56px"
:show-description="false"
>
<template #tip> 建议尺寸56 * 56</template>
</UploadImg>
</el-form-item>
</ElFormItem>
</template>
</el-card>
<el-card header="商品样式" class="property-group" shadow="never">
<el-form-item label="上圆角" prop="borderRadiusTop">
<el-slider
</ElCard>
<ElCard header="商品样式" class="property-group" shadow="never">
<ElFormItem label="上圆角" prop="borderRadiusTop">
<ElSlider
v-model="formData.borderRadiusTop"
:max="100"
:min="0"
@@ -151,9 +161,9 @@ onMounted(async () => {
input-size="small"
:show-input-controls="false"
/>
</el-form-item>
<el-form-item label="下圆角" prop="borderRadiusBottom">
<el-slider
</ElFormItem>
<ElFormItem label="下圆角" prop="borderRadiusBottom">
<ElSlider
v-model="formData.borderRadiusBottom"
:max="100"
:min="0"
@@ -161,9 +171,9 @@ onMounted(async () => {
input-size="small"
:show-input-controls="false"
/>
</el-form-item>
<el-form-item label="间隔" prop="space">
<el-slider
</ElFormItem>
<ElFormItem label="间隔" prop="space">
<ElSlider
v-model="formData.space"
:max="100"
:min="0"
@@ -171,9 +181,9 @@ onMounted(async () => {
input-size="small"
:show-input-controls="false"
/>
</el-form-item>
</el-card>
</el-form>
</ElFormItem>
</ElCard>
</ElForm>
</ComponentContainerProperty>
</template>

View File

@@ -7,6 +7,8 @@ import { ref, watch } from 'vue';
import { fenToYuan } from '@vben/utils';
import { ElImage } from 'element-plus';
import * as ProductSpuApi from '#/api/mall/product/spu';
import * as PointActivityApi from '#/api/mall/promotion/point';
@@ -94,7 +96,7 @@ const calculateWidth = () => {
<template>
<div
ref="containerRef"
class="min-h-30px box-content flex w-full flex-row flex-wrap"
class="box-content flex min-h-[30px] w-full flex-row flex-wrap"
>
<div
v-for="(spu, index) in spuList"
@@ -112,28 +114,28 @@ const calculateWidth = () => {
<!-- 角标 -->
<div
v-if="property.badge.show"
class="z-1 absolute left-0 top-0 items-center justify-center"
class="absolute left-0 top-0 z-[1] items-center justify-center"
>
<el-image
<ElImage
:src="property.badge.imgUrl"
class="h-26px w-38px"
class="h-[26px] w-[38px]"
fit="cover"
/>
</div>
<!-- 商品封面图 -->
<div
class="h-140px"
class="h-[140px]"
:class="[
{
'w-full': property.layoutType !== 'oneColSmallImg',
'w-140px': property.layoutType === 'oneColSmallImg',
'w-[140px]': property.layoutType === 'oneColSmallImg',
},
]"
>
<el-image :src="spu.picUrl" class="h-full w-full" fit="cover" />
<ElImage :src="spu.picUrl" class="h-full w-full" fit="cover" />
</div>
<div
class="gap-8px p-8px box-border flex flex-col"
class="box-border flex flex-col gap-[8px] p-[8px]"
:class="[
{
'w-full': property.layoutType !== 'oneColSmallImg',
@@ -145,7 +147,7 @@ const calculateWidth = () => {
<!-- 商品名称 -->
<div
v-if="property.fields.name.show"
class="text-14px"
class="text-[14px]"
:class="[
{
truncate: property.layoutType !== 'oneColSmallImg',
@@ -161,7 +163,7 @@ const calculateWidth = () => {
<div
v-if="property.fields.introduction.show"
:style="{ color: property.fields.introduction.color }"
class="text-12px truncate"
class="truncate text-[12px]"
>
{{ spu.introduction }}
</div>
@@ -170,7 +172,7 @@ const calculateWidth = () => {
<span
v-if="property.fields.price.show"
:style="{ color: property.fields.price.color }"
class="text-16px"
class="text-[16px]"
>
{{ spu.point }}积分
{{
@@ -183,12 +185,12 @@ const calculateWidth = () => {
<span
v-if="property.fields.marketPrice.show && spu.marketPrice"
:style="{ color: property.fields.marketPrice.color }"
class="ml-4px text-10px line-through"
class="ml-[4px] text-[10px] line-through"
>
{{ fenToYuan(spu.marketPrice) }}
</span>
</div>
<div class="text-12px">
<div class="text-[12px]">
<!-- 销量 -->
<span
v-if="property.fields.salesCount.show"
@@ -206,22 +208,22 @@ const calculateWidth = () => {
</div>
</div>
<!-- 购买按钮 -->
<div class="bottom-8px right-8px absolute">
<div class="absolute bottom-[8px] right-[8px]">
<!-- 文字按钮 -->
<span
v-if="property.btnBuy.type === 'text'"
:style="{
background: `linear-gradient(to right, ${property.btnBuy.bgBeginColor}, ${property.btnBuy.bgEndColor}`,
}"
class="p-x-12px p-y-4px text-12px rounded-full text-white"
class="rounded-full px-[12px] py-[4px] text-[12px] text-white"
>
{{ property.btnBuy.text }}
</span>
<!-- 图片按钮 -->
<el-image
<ElImage
v-else
:src="property.btnBuy.imgUrl"
class="h-28px w-28px rounded-full"
class="h-[28px] w-[28px] rounded-full"
fit="cover"
/>
</div>

View File

@@ -1,8 +1,22 @@
<script lang="ts" setup>
import type { PromotionPointProperty } from './config';
import { useVModel } from '@vueuse/core';
import { IconifyIcon } from '@vben/icons';
import { useVModel } from '@vueuse/core';
import {
ElCard,
ElForm,
ElFormItem,
ElInput,
ElRadioButton,
ElRadioGroup,
ElSlider,
ElSwitch,
ElTooltip,
} from 'element-plus';
import ColorInput from '#/components/color-input/index.vue';
import PointShowcase from '#/views/mall/promotion/point/components/point-showcase.vue';
// 秒杀属性面板
@@ -15,119 +29,121 @@ const formData = useVModel(props, 'modelValue', emit);
<template>
<ComponentContainerProperty v-model="formData.style">
<el-form :model="formData" label-width="80px">
<el-card class="property-group" header="积分商城活动" shadow="never">
<ElForm :model="formData" label-width="80px">
<ElCard class="property-group" header="积分商城活动" shadow="never">
<PointShowcase v-model="formData.activityIds" />
</el-card>
<el-card class="property-group" header="商品样式" shadow="never">
<el-form-item label="布局" prop="type">
<el-radio-group v-model="formData.layoutType">
<el-tooltip class="item" content="单列大图" placement="bottom">
<el-radio-button value="oneColBigImg">
<Icon icon="fluent:text-column-one-24-filled" />
</el-radio-button>
</el-tooltip>
<el-tooltip class="item" content="单列小图" placement="bottom">
<el-radio-button value="oneColSmallImg">
<Icon icon="fluent:text-column-two-left-24-filled" />
</el-radio-button>
</el-tooltip>
<el-tooltip class="item" content="双列" placement="bottom">
<el-radio-button value="twoCol">
<Icon icon="fluent:text-column-two-24-filled" />
</el-radio-button>
</el-tooltip>
</ElCard>
<ElCard class="property-group" header="商品样式" shadow="never">
<ElFormItem label="布局" prop="type">
<ElRadioGroup v-model="formData.layoutType">
<ElTooltip class="item" content="单列大图" placement="bottom">
<ElRadioButton value="oneColBigImg">
<IconifyIcon icon="fluent:text-column-one-24-filled" />
</ElRadioButton>
</ElTooltip>
<ElTooltip class="item" content="单列小图" placement="bottom">
<ElRadioButton value="oneColSmallImg">
<IconifyIcon icon="fluent:text-column-two-left-24-filled" />
</ElRadioButton>
</ElTooltip>
<ElTooltip class="item" content="双列" placement="bottom">
<ElRadioButton value="twoCol">
<IconifyIcon icon="fluent:text-column-two-24-filled" />
</ElRadioButton>
</ElTooltip>
<!--<el-tooltip class="item" content="三列" placement="bottom">
<el-radio-button value="threeCol">
<Icon icon="fluent:text-column-three-24-filled" />
</el-radio-button>
</el-tooltip>-->
</el-radio-group>
</el-form-item>
<el-form-item label="商品名称" prop="fields.name.show">
</ElRadioGroup>
</ElFormItem>
<ElFormItem label="商品名称" prop="fields.name.show">
<div class="gap-8px flex">
<ColorInput v-model="formData.fields.name.color" />
<el-checkbox v-model="formData.fields.name.show" />
<ElCheckbox v-model="formData.fields.name.show" />
</div>
</el-form-item>
<el-form-item label="商品简介" prop="fields.introduction.show">
</ElFormItem>
<ElFormItem label="商品简介" prop="fields.introduction.show">
<div class="gap-8px flex">
<ColorInput v-model="formData.fields.introduction.color" />
<el-checkbox v-model="formData.fields.introduction.show" />
<ElCheckbox v-model="formData.fields.introduction.show" />
</div>
</el-form-item>
<el-form-item label="商品价格" prop="fields.price.show">
</ElFormItem>
<ElFormItem label="商品价格" prop="fields.price.show">
<div class="gap-8px flex">
<ColorInput v-model="formData.fields.price.color" />
<el-checkbox v-model="formData.fields.price.show" />
<ElCheckbox v-model="formData.fields.price.show" />
</div>
</el-form-item>
<el-form-item label="市场价" prop="fields.marketPrice.show">
</ElFormItem>
<ElFormItem label="市场价" prop="fields.marketPrice.show">
<div class="gap-8px flex">
<ColorInput v-model="formData.fields.marketPrice.color" />
<el-checkbox v-model="formData.fields.marketPrice.show" />
<ElCheckbox v-model="formData.fields.marketPrice.show" />
</div>
</el-form-item>
<el-form-item label="商品销量" prop="fields.salesCount.show">
</ElFormItem>
<ElFormItem label="商品销量" prop="fields.salesCount.show">
<div class="gap-8px flex">
<ColorInput v-model="formData.fields.salesCount.color" />
<el-checkbox v-model="formData.fields.salesCount.show" />
<ElCheckbox v-model="formData.fields.salesCount.show" />
</div>
</el-form-item>
<el-form-item label="商品库存" prop="fields.stock.show">
</ElFormItem>
<ElFormItem label="商品库存" prop="fields.stock.show">
<div class="gap-8px flex">
<ColorInput v-model="formData.fields.stock.color" />
<el-checkbox v-model="formData.fields.stock.show" />
<ElCheckbox v-model="formData.fields.stock.show" />
</div>
</el-form-item>
</el-card>
<el-card class="property-group" header="角标" shadow="never">
<el-form-item label="角标" prop="badge.show">
<el-switch v-model="formData.badge.show" />
</el-form-item>
<el-form-item
v-if="formData.badge.show"
label="角标"
prop="badge.imgUrl"
>
<UploadImg v-model="formData.badge.imgUrl" height="44px" width="72px">
</ElFormItem>
</ElCard>
<ElCard class="property-group" header="角标" shadow="never">
<ElFormItem label="角标" prop="badge.show">
<ElSwitch v-model="formData.badge.show" />
</ElFormItem>
<ElFormItem v-if="formData.badge.show" label="角标" prop="badge.imgUrl">
<UploadImg
v-model="formData.badge.imgUrl"
height="44px"
width="72px"
:show-description="false"
>
<template #tip> 建议尺寸36 * 22</template>
</UploadImg>
</el-form-item>
</el-card>
<el-card class="property-group" header="按钮" shadow="never">
<el-form-item label="按钮类型" prop="btnBuy.type">
<el-radio-group v-model="formData.btnBuy.type">
<el-radio-button value="text">文字</el-radio-button>
<el-radio-button value="img">图片</el-radio-button>
</el-radio-group>
</el-form-item>
</ElFormItem>
</ElCard>
<ElCard class="property-group" header="按钮" shadow="never">
<ElFormItem label="按钮类型" prop="btnBuy.type">
<ElRadioGroup v-model="formData.btnBuy.type">
<ElRadioButton value="text">文字</ElRadioButton>
<ElRadioButton value="img">图片</ElRadioButton>
</ElRadioGroup>
</ElFormItem>
<template v-if="formData.btnBuy.type === 'text'">
<el-form-item label="按钮文字" prop="btnBuy.text">
<el-input v-model="formData.btnBuy.text" />
</el-form-item>
<el-form-item label="左侧背景" prop="btnBuy.bgBeginColor">
<ElFormItem label="按钮文字" prop="btnBuy.text">
<ElInput v-model="formData.btnBuy.text" />
</ElFormItem>
<ElFormItem label="左侧背景" prop="btnBuy.bgBeginColor">
<ColorInput v-model="formData.btnBuy.bgBeginColor" />
</el-form-item>
<el-form-item label="右侧背景" prop="btnBuy.bgEndColor">
</ElFormItem>
<ElFormItem label="右侧背景" prop="btnBuy.bgEndColor">
<ColorInput v-model="formData.btnBuy.bgEndColor" />
</el-form-item>
</ElFormItem>
</template>
<template v-else>
<el-form-item label="图片" prop="btnBuy.imgUrl">
<ElFormItem label="图片" prop="btnBuy.imgUrl">
<UploadImg
v-model="formData.btnBuy.imgUrl"
height="56px"
width="56px"
:show-description="false"
>
<template #tip> 建议尺寸56 * 56</template>
</UploadImg>
</el-form-item>
</ElFormItem>
</template>
</el-card>
<el-card class="property-group" header="商品样式" shadow="never">
<el-form-item label="上圆角" prop="borderRadiusTop">
<el-slider
</ElCard>
<ElCard class="property-group" header="商品样式" shadow="never">
<ElFormItem label="上圆角" prop="borderRadiusTop">
<ElSlider
v-model="formData.borderRadiusTop"
:max="100"
:min="0"
@@ -135,9 +151,9 @@ const formData = useVModel(props, 'modelValue', emit);
input-size="small"
show-input
/>
</el-form-item>
<el-form-item label="下圆角" prop="borderRadiusBottom">
<el-slider
</ElFormItem>
<ElFormItem label="下圆角" prop="borderRadiusBottom">
<ElSlider
v-model="formData.borderRadiusBottom"
:max="100"
:min="0"
@@ -145,9 +161,9 @@ const formData = useVModel(props, 'modelValue', emit);
input-size="small"
show-input
/>
</el-form-item>
<el-form-item label="间隔" prop="space">
<el-slider
</ElFormItem>
<ElFormItem label="间隔" prop="space">
<ElSlider
v-model="formData.space"
:max="100"
:min="0"
@@ -155,9 +171,9 @@ const formData = useVModel(props, 'modelValue', emit);
input-size="small"
show-input
/>
</el-form-item>
</el-card>
</el-form>
</ElFormItem>
</ElCard>
</ElForm>
</ComponentContainerProperty>
</template>

View File

@@ -8,6 +8,8 @@ import { ref, watch } from 'vue';
import { fenToYuan } from '@vben/utils';
import { ElImage } from 'element-plus';
import * as ProductSpuApi from '#/api/mall/product/spu';
import * as SeckillActivityApi from '#/api/mall/promotion/seckill/seckillActivity';
@@ -93,7 +95,7 @@ const calculateWidth = () => {
</script>
<template>
<div
class="min-h-30px box-content flex w-full flex-row flex-wrap"
class="box-content flex min-h-[30px] w-full flex-row flex-wrap"
ref="containerRef"
>
<div
@@ -112,28 +114,28 @@ const calculateWidth = () => {
<!-- 角标 -->
<div
v-if="property.badge.show"
class="z-1 absolute left-0 top-0 items-center justify-center"
class="absolute left-0 top-0 z-[1] items-center justify-center"
>
<el-image
<ElImage
fit="cover"
:src="property.badge.imgUrl"
class="h-26px w-38px"
class="h-[26px] w-[38px]"
/>
</div>
<!-- 商品封面图 -->
<div
class="h-140px"
class="h-[140px]"
:class="[
{
'w-full': property.layoutType !== 'oneColSmallImg',
'w-140px': property.layoutType === 'oneColSmallImg',
'w-[140px]': property.layoutType === 'oneColSmallImg',
},
]"
>
<el-image fit="cover" class="h-full w-full" :src="spu.picUrl" />
<ElImage fit="cover" class="h-full w-full" :src="spu.picUrl" />
</div>
<div
class="gap-8px p-8px box-border flex flex-col"
class="box-border flex flex-col gap-2 p-2"
:class="[
{
'w-full': property.layoutType !== 'oneColSmallImg',
@@ -145,7 +147,7 @@ const calculateWidth = () => {
<!-- 商品名称 -->
<div
v-if="property.fields.name.show"
class="text-14px"
class="text-sm"
:class="[
{
truncate: property.layoutType !== 'oneColSmallImg',
@@ -160,7 +162,7 @@ const calculateWidth = () => {
<!-- 商品简介 -->
<div
v-if="property.fields.introduction.show"
class="text-12px truncate"
class="truncate text-xs"
:style="{ color: property.fields.introduction.color }"
>
{{ spu.introduction }}
@@ -169,7 +171,7 @@ const calculateWidth = () => {
<!-- 价格 -->
<span
v-if="property.fields.price.show"
class="text-16px"
class="text-base"
:style="{ color: property.fields.price.color }"
>
{{ fenToYuan(spu.price || Infinity) }}
@@ -177,13 +179,13 @@ const calculateWidth = () => {
<!-- 市场价 -->
<span
v-if="property.fields.marketPrice.show && spu.marketPrice"
class="ml-4px text-10px line-through"
class="ml-1 text-[10px] line-through"
:style="{ color: property.fields.marketPrice.color }"
>
{{ fenToYuan(spu.marketPrice) }}
</span>
</div>
<div class="text-12px">
<div class="text-xs">
<!-- 销量 -->
<span
v-if="property.fields.salesCount.show"
@@ -201,11 +203,11 @@ const calculateWidth = () => {
</div>
</div>
<!-- 购买按钮 -->
<div class="bottom-8px right-8px absolute">
<div class="absolute bottom-2 right-2">
<!-- 文字按钮 -->
<span
v-if="property.btnBuy.type === 'text'"
class="p-x-12px p-y-4px text-12px rounded-full text-white"
class="rounded-full px-3 py-1 text-xs text-white"
:style="{
background: `linear-gradient(to right, ${property.btnBuy.bgBeginColor}, ${property.btnBuy.bgEndColor}`,
}"
@@ -213,9 +215,9 @@ const calculateWidth = () => {
{{ property.btnBuy.text }}
</span>
<!-- 图片按钮 -->
<el-image
<ElImage
v-else
class="h-28px w-28px rounded-full"
class="h-7 w-7 rounded-full"
fit="cover"
:src="property.btnBuy.imgUrl"
/>

View File

@@ -5,9 +5,24 @@ import type { MallSeckillActivityApi } from '#/api/mall/promotion/seckill/seckil
import { onMounted, ref } from 'vue';
import { IconifyIcon } from '@vben/icons';
import { useVModel } from '@vueuse/core';
import {
ElCard,
ElCheckbox,
ElForm,
ElFormItem,
ElInput,
ElRadioButton,
ElRadioGroup,
ElSlider,
ElSwitch,
} from 'element-plus';
import * as SeckillActivityApi from '#/api/mall/promotion/seckill/seckillActivity';
import ColorInput from '#/components/color-input/index.vue';
import UploadImg from '#/components/upload/image-upload.vue';
import { CommonStatusEnum } from '#/utils/constants';
import SeckillShowcase from '#/views/mall/promotion/seckill/components/seckill-showcase.vue';
@@ -31,119 +46,121 @@ onMounted(async () => {
<template>
<ComponentContainerProperty v-model="formData.style">
<el-form label-width="80px" :model="formData">
<el-card header="秒杀活动" class="property-group" shadow="never">
<ElForm label-width="80px" :model="formData">
<ElCard header="秒杀活动" class="property-group" shadow="never">
<SeckillShowcase v-model="formData.activityIds" />
</el-card>
<el-card header="商品样式" class="property-group" shadow="never">
<el-form-item label="布局" prop="type">
<el-radio-group v-model="formData.layoutType">
<el-tooltip class="item" content="单列大图" placement="bottom">
<el-radio-button value="oneColBigImg">
<Icon icon="fluent:text-column-one-24-filled" />
</el-radio-button>
</el-tooltip>
<el-tooltip class="item" content="单列小图" placement="bottom">
<el-radio-button value="oneColSmallImg">
<Icon icon="fluent:text-column-two-left-24-filled" />
</el-radio-button>
</el-tooltip>
<el-tooltip class="item" content="双列" placement="bottom">
<el-radio-button value="twoCol">
<Icon icon="fluent:text-column-two-24-filled" />
</el-radio-button>
</el-tooltip>
</ElCard>
<ElCard header="商品样式" class="property-group" shadow="never">
<ElFormItem label="布局" prop="type">
<ElRadioGroup v-model="formData.layoutType">
<ElTooltip class="item" content="单列大图" placement="bottom">
<ElRadioButton value="oneColBigImg">
<IconifyIcon icon="fluent:text-column-one-24-filled" />
</ElRadioButton>
</ElTooltip>
<ElTooltip class="item" content="单列小图" placement="bottom">
<ElRadioButton value="oneColSmallImg">
<IconifyIcon icon="fluent:text-column-two-left-24-filled" />
</ElRadioButton>
</ElTooltip>
<ElTooltip class="item" content="双列" placement="bottom">
<ElRadioButton value="twoCol">
<IconifyIcon icon="fluent:text-column-two-24-filled" />
</ElRadioButton>
</ElTooltip>
<!--<el-tooltip class="item" content="三列" placement="bottom">
<el-radio-button value="threeCol">
<Icon icon="fluent:text-column-three-24-filled" />
</el-radio-button>
</el-tooltip>-->
</el-radio-group>
</el-form-item>
<el-form-item label="商品名称" prop="fields.name.show">
</ElTooltip>-->
</ElRadioGroup>
</ElFormItem>
<ElFormItem label="商品名称" prop="fields.name.show">
<div class="gap-8px flex">
<ColorInput v-model="formData.fields.name.color" />
<el-checkbox v-model="formData.fields.name.show" />
<ElCheckbox v-model="formData.fields.name.show" />
</div>
</el-form-item>
<el-form-item label="商品简介" prop="fields.introduction.show">
</ElFormItem>
<ElFormItem label="商品简介" prop="fields.introduction.show">
<div class="gap-8px flex">
<ColorInput v-model="formData.fields.introduction.color" />
<el-checkbox v-model="formData.fields.introduction.show" />
<ElCheckbox v-model="formData.fields.introduction.show" />
</div>
</el-form-item>
<el-form-item label="商品价格" prop="fields.price.show">
</ElFormItem>
<ElFormItem label="商品价格" prop="fields.price.show">
<div class="gap-8px flex">
<ColorInput v-model="formData.fields.price.color" />
<el-checkbox v-model="formData.fields.price.show" />
<ElCheckbox v-model="formData.fields.price.show" />
</div>
</el-form-item>
<el-form-item label="市场价" prop="fields.marketPrice.show">
</ElFormItem>
<ElFormItem label="市场价" prop="fields.marketPrice.show">
<div class="gap-8px flex">
<ColorInput v-model="formData.fields.marketPrice.color" />
<el-checkbox v-model="formData.fields.marketPrice.show" />
<ElCheckbox v-model="formData.fields.marketPrice.show" />
</div>
</el-form-item>
<el-form-item label="商品销量" prop="fields.salesCount.show">
</ElFormItem>
<ElFormItem label="商品销量" prop="fields.salesCount.show">
<div class="gap-8px flex">
<ColorInput v-model="formData.fields.salesCount.color" />
<el-checkbox v-model="formData.fields.salesCount.show" />
<ElCheckbox v-model="formData.fields.salesCount.show" />
</div>
</el-form-item>
<el-form-item label="商品库存" prop="fields.stock.show">
</ElFormItem>
<ElFormItem label="商品库存" prop="fields.stock.show">
<div class="gap-8px flex">
<ColorInput v-model="formData.fields.stock.color" />
<el-checkbox v-model="formData.fields.stock.show" />
<ElCheckbox v-model="formData.fields.stock.show" />
</div>
</el-form-item>
</el-card>
<el-card header="角标" class="property-group" shadow="never">
<el-form-item label="角标" prop="badge.show">
<el-switch v-model="formData.badge.show" />
</el-form-item>
<el-form-item
label="角标"
prop="badge.imgUrl"
v-if="formData.badge.show"
>
<UploadImg v-model="formData.badge.imgUrl" height="44px" width="72px">
</ElFormItem>
</ElCard>
<ElCard header="角标" class="property-group" shadow="never">
<ElFormItem label="角标" prop="badge.show">
<ElSwitch v-model="formData.badge.show" />
</ElFormItem>
<ElFormItem v-if="formData.badge.show" label="角标" prop="badge.imgUrl">
<UploadImg
v-model="formData.badge.imgUrl"
height="44px"
width="72px"
:show-description="false"
>
<template #tip> 建议尺寸36 * 22</template>
</UploadImg>
</el-form-item>
</el-card>
<el-card header="按钮" class="property-group" shadow="never">
<el-form-item label="按钮类型" prop="btnBuy.type">
<el-radio-group v-model="formData.btnBuy.type">
<el-radio-button value="text">文字</el-radio-button>
<el-radio-button value="img">图片</el-radio-button>
</el-radio-group>
</el-form-item>
</ElFormItem>
</ElCard>
<ElCard header="按钮" class="property-group" shadow="never">
<ElFormItem label="按钮类型" prop="btnBuy.type">
<ElRadioGroup v-model="formData.btnBuy.type">
<ElRadioButton value="text">文字</ElRadioButton>
<ElRadioButton value="img">图片</ElRadioButton>
</ElRadioGroup>
</ElFormItem>
<template v-if="formData.btnBuy.type === 'text'">
<el-form-item label="按钮文字" prop="btnBuy.text">
<el-input v-model="formData.btnBuy.text" />
</el-form-item>
<el-form-item label="左侧背景" prop="btnBuy.bgBeginColor">
<ElFormItem label="按钮文字" prop="btnBuy.text">
<ElInput v-model="formData.btnBuy.text" />
</ElFormItem>
<ElFormItem label="左侧背景" prop="btnBuy.bgBeginColor">
<ColorInput v-model="formData.btnBuy.bgBeginColor" />
</el-form-item>
<el-form-item label="右侧背景" prop="btnBuy.bgEndColor">
</ElFormItem>
<ElFormItem label="右侧背景" prop="btnBuy.bgEndColor">
<ColorInput v-model="formData.btnBuy.bgEndColor" />
</el-form-item>
</ElFormItem>
</template>
<template v-else>
<el-form-item label="图片" prop="btnBuy.imgUrl">
<ElFormItem label="图片" prop="btnBuy.imgUrl">
<UploadImg
v-model="formData.btnBuy.imgUrl"
height="56px"
width="56px"
:show-description="false"
>
<template #tip> 建议尺寸56 * 56</template>
</UploadImg>
</el-form-item>
</ElFormItem>
</template>
</el-card>
<el-card header="商品样式" class="property-group" shadow="never">
<el-form-item label="上圆角" prop="borderRadiusTop">
<el-slider
</ElCard>
<ElCard header="商品样式" class="property-group" shadow="never">
<ElFormItem label="上圆角" prop="borderRadiusTop">
<ElSlider
v-model="formData.borderRadiusTop"
:max="100"
:min="0"
@@ -151,9 +168,9 @@ onMounted(async () => {
input-size="small"
:show-input-controls="false"
/>
</el-form-item>
<el-form-item label="下圆角" prop="borderRadiusBottom">
<el-slider
</ElFormItem>
<ElFormItem label="下圆角" prop="borderRadiusBottom">
<ElSlider
v-model="formData.borderRadiusBottom"
:max="100"
:min="0"
@@ -161,9 +178,9 @@ onMounted(async () => {
input-size="small"
:show-input-controls="false"
/>
</el-form-item>
<el-form-item label="间隔" prop="space">
<el-slider
</ElFormItem>
<ElFormItem label="间隔" prop="space">
<ElSlider
v-model="formData.space"
:max="100"
:min="0"
@@ -171,9 +188,9 @@ onMounted(async () => {
input-size="small"
:show-input-controls="false"
/>
</el-form-item>
</el-card>
</el-form>
</ElFormItem>
</ElCard>
</ElForm>
</ComponentContainerProperty>
</template>

View File

@@ -1,6 +1,8 @@
<script setup lang="ts">
import type { SearchProperty } from './config';
import { IconifyIcon } from '@vben/icons';
/** 搜索框 */
defineOptions({ name: 'SearchBar' });
defineProps<{ property: SearchProperty }>();
@@ -28,7 +30,7 @@ defineProps<{ property: SearchProperty }>();
justifyContent: property.placeholderPosition,
}"
>
<Icon icon="ep:search" />
<IconifyIcon icon="ep:search" />
<span>{{ property.placeholder || '搜索商品' }}</span>
</div>
<div class="right">
@@ -37,7 +39,10 @@ defineProps<{ property: SearchProperty }>();
keyword
}}</span>
<!-- 扫一扫 -->
<Icon icon="ant-design:scan-outlined" v-show="property.showScan" />
<IconifyIcon
icon="ant-design:scan-outlined"
v-show="property.showScan"
/>
</div>
</div>
</div>

View File

@@ -3,9 +3,23 @@ import type { SearchProperty } from './config';
import { watch } from 'vue';
import { IconifyIcon } from '@vben/icons';
import { isString } from '@vben/utils';
import { useVModel } from '@vueuse/core';
import {
ElCard,
ElForm,
ElFormItem,
ElRadioButton,
ElRadioGroup,
ElSlider,
ElSwitch,
ElTooltip,
} from 'element-plus';
import ComponentContainerProperty from '#/components/diy-editor/components/ComponentContainerProperty.vue';
import Draggable from '#/components/draggable/index.vue';
/** 搜索框属性面板 */
defineOptions({ name: 'SearchProperty' });
@@ -31,9 +45,15 @@ watch(
<template>
<ComponentContainerProperty v-model="formData.style">
<!-- 表单 -->
<el-form label-width="80px" :model="formData" class="m-t-8px">
<el-card header="搜索热词" class="property-group" shadow="never">
<Draggable v-model="formData.hotKeywords" empty-item="" :min="0">
<ElForm label-width="80px" :model="formData" class="m-t-8px">
<ElCard header="搜索热词" class="property-group" shadow="never">
<Draggable
v-model="formData.hotKeywords"
:empty-item="{
type: 'input',
placeholder: '请输入热词',
}"
>
<template #default="{ index }">
<el-input
v-model="formData.hotKeywords[index]"
@@ -41,59 +61,59 @@ watch(
/>
</template>
</Draggable>
</el-card>
<el-card header="搜索样式" class="property-group" shadow="never">
<el-form-item label="框体样式">
<el-radio-group v-model="formData!.borderRadius">
<el-tooltip content="方形" placement="top">
<el-radio-button :value="0">
<Icon icon="tabler:input-search" />
</el-radio-button>
</el-tooltip>
<el-tooltip content="圆形" placement="top">
<el-radio-button :value="10">
<Icon icon="iconoir:input-search" />
</el-radio-button>
</el-tooltip>
</el-radio-group>
</el-form-item>
<el-form-item label="提示文字" prop="placeholder">
</ElCard>
<ElCard header="搜索样式" class="property-group" shadow="never">
<ElFormItem label="框体样式">
<ElRadioGroup v-model="formData!.borderRadius">
<ElTooltip content="方形" placement="top">
<ElRadioButton :value="0">
<IconifyIcon icon="tabler:input-search" />
</ElRadioButton>
</ElTooltip>
<ElTooltip content="圆形" placement="top">
<ElRadioButton :value="10">
<IconifyIcon icon="iconoir:input-search" />
</ElRadioButton>
</ElTooltip>
</ElRadioGroup>
</ElFormItem>
<ElFormItem label="提示文字" prop="placeholder">
<el-input v-model="formData.placeholder" />
</el-form-item>
<el-form-item label="文本位置" prop="placeholderPosition">
<el-radio-group v-model="formData!.placeholderPosition">
<el-tooltip content="居左" placement="top">
<el-radio-button value="left">
<Icon icon="ant-design:align-left-outlined" />
</el-radio-button>
</el-tooltip>
<el-tooltip content="居中" placement="top">
<el-radio-button value="center">
<Icon icon="ant-design:align-center-outlined" />
</el-radio-button>
</el-tooltip>
</el-radio-group>
</el-form-item>
<el-form-item label="扫一扫" prop="showScan">
<el-switch v-model="formData!.showScan" />
</el-form-item>
<el-form-item label="框体高度" prop="height">
<el-slider
</ElFormItem>
<ElFormItem label="文本位置" prop="placeholderPosition">
<ElRadioGroup v-model="formData!.placeholderPosition">
<ElTooltip content="居左" placement="top">
<ElRadioButton value="left">
<IconifyIcon icon="ant-design:align-left-outlined" />
</ElRadioButton>
</ElTooltip>
<ElTooltip content="居中" placement="top">
<ElRadioButton value="center">
<IconifyIcon icon="ant-design:align-center-outlined" />
</ElRadioButton>
</ElTooltip>
</ElRadioGroup>
</ElFormItem>
<ElFormItem label="扫一扫" prop="showScan">
<ElSwitch v-model="formData!.showScan" />
</ElFormItem>
<ElFormItem label="框体高度" prop="height">
<ElSlider
v-model="formData!.height"
:max="50"
:min="28"
show-input
input-size="small"
/>
</el-form-item>
<el-form-item label="框体颜色" prop="backgroundColor">
</ElFormItem>
<ElFormItem label="框体颜色" prop="backgroundColor">
<ColorInput v-model="formData.backgroundColor" />
</el-form-item>
<el-form-item class="lef" label="文本颜色" prop="textColor">
</ElFormItem>
<ElFormItem class="lef" label="文本颜色" prop="textColor">
<ColorInput v-model="formData.textColor" />
</el-form-item>
</el-card>
</el-form>
</ElFormItem>
</ElCard>
</ElForm>
</ComponentContainerProperty>
</template>

View File

@@ -1,6 +1,10 @@
<script setup lang="ts">
import type { TabBarProperty } from './config';
import { IconifyIcon } from '@vben/icons';
import { ElImage } from 'element-plus';
/** 页面底部导航栏 */
defineOptions({ name: 'TabBar' });
@@ -24,13 +28,13 @@ defineProps<{ property: TabBarProperty }>();
:key="index"
class="tab-bar-item"
>
<el-image :src="index === 0 ? item.activeIconUrl : item.iconUrl">
<ElImage :src="index === 0 ? item.activeIconUrl : item.iconUrl">
<template #error>
<div class="flex h-full w-full items-center justify-center">
<Icon icon="ep:picture" />
<IconifyIcon icon="ep:picture" />
</div>
</template>
</el-image>
</ElImage>
<span
:style="{
color:

View File

@@ -1,7 +1,24 @@
<script setup lang="ts">
import type { TabBarProperty } from './config';
import { IconifyIcon } from '@vben/icons';
import { useVModel } from '@vueuse/core';
import {
ElForm,
ElFormItem,
ElInput,
ElOption,
ElRadioButton,
ElRadioGroup,
ElSelect,
ElText,
} from 'element-plus';
import AppLinkInput from '#/components/app-link-input/index.vue';
import ColorInput from '#/components/color-input/index.vue';
import Draggable from '#/components/draggable/index.vue';
import UploadImg from '#/components/upload/image-upload.vue';
import { component, THEME_LIST } from './config';
// 底部导航栏
@@ -26,10 +43,10 @@ const handleThemeChange = () => {
<template>
<div class="tab-bar">
<!-- 表单 -->
<el-form :model="formData" label-width="80px">
<el-form-item label="主题" prop="theme">
<el-select v-model="formData!.theme" @change="handleThemeChange">
<el-option
<ElForm :model="formData" label-width="80px">
<ElFormItem label="主题" prop="theme">
<ElSelect v-model="formData!.theme" @change="handleThemeChange">
<ElOption
v-for="(theme, index) in THEME_LIST"
:key="index"
:label="theme.name"
@@ -37,55 +54,56 @@ const handleThemeChange = () => {
>
<template #default>
<div class="flex items-center justify-between">
<Icon :icon="theme.icon" :color="theme.color" />
<IconifyIcon :icon="theme.icon" :color="theme.color" />
<span>{{ theme.name }}</span>
</div>
</template>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="默认颜色">
</ElOption>
</ElSelect>
</ElFormItem>
<ElFormItem label="默认颜色">
<ColorInput v-model="formData!.style.color" />
</el-form-item>
<el-form-item label="选中颜色">
</ElFormItem>
<ElFormItem label="选中颜色">
<ColorInput v-model="formData!.style.activeColor" />
</el-form-item>
<el-form-item label="导航背景">
<el-radio-group v-model="formData!.style.bgType">
<el-radio-button value="color">纯色</el-radio-button>
<el-radio-button value="img">图片</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item label="选择颜色" v-if="formData!.style.bgType === 'color'">
</ElFormItem>
<ElFormItem label="导航背景">
<ElRadioGroup v-model="formData!.style.bgType">
<ElRadioButton value="color">纯色</ElRadioButton>
<ElRadioButton value="img">图片</ElRadioButton>
</ElRadioGroup>
</ElFormItem>
<ElFormItem label="选择颜色" v-if="formData!.style.bgType === 'color'">
<ColorInput v-model="formData!.style.bgColor" />
</el-form-item>
<el-form-item label="选择图片" v-if="formData!.style.bgType === 'img'">
</ElFormItem>
<ElFormItem label="选择图片" v-if="formData!.style.bgType === 'img'">
<UploadImg
v-model="formData!.style.bgImg"
width="100%"
height="50px"
class="min-w-200px"
class="min-w-[200px]"
:show-description="false"
>
<template #tip> 建议尺寸 375 * 50 </template>
</UploadImg>
</el-form-item>
</ElFormItem>
<el-text tag="p">图标设置</el-text>
<el-text type="info" size="small">
<ElText tag="p">图标设置</ElText>
<ElText type="info" size="small">
拖动左上角的小圆点可对其排序, 图标建议尺寸 44*44
</el-text>
</ElText>
<Draggable v-model="formData.items" :limit="5">
<template #default="{ element }">
<div class="m-b-8px flex items-center justify-around">
<div class="mb-2 flex items-center justify-around">
<div class="flex flex-col items-center justify-between">
<UploadImg
v-model="element.iconUrl"
width="40px"
height="40px"
:show-delete="false"
:show-btn-text="false"
:show-description="false"
/>
<el-text size="small">未选中</el-text>
<ElText size="small">未选中</ElText>
</div>
<div>
<UploadImg
@@ -93,30 +111,20 @@ const handleThemeChange = () => {
width="40px"
height="40px"
:show-delete="false"
:show-btn-text="false"
:show-description="false"
/>
<el-text>已选中</el-text>
<ElText>已选中</ElText>
</div>
</div>
<el-form-item
prop="text"
label="文字"
label-width="48px"
class="m-b-8px!"
>
<el-input v-model="element.text" placeholder="请输入文字" />
</el-form-item>
<el-form-item
prop="url"
label="链接"
label-width="48px"
class="m-b-0!"
>
<ElFormItem prop="text" label="文字" label-width="48px" class="mb-2">
<ElInput v-model="element.text" placeholder="请输入文字" />
</ElFormItem>
<ElFormItem prop="url" label="链接" label-width="48px" class="mb-0">
<AppLinkInput v-model="element.url" />
</el-form-item>
</ElFormItem>
</template>
</Draggable>
</el-form>
</ElForm>
</div>
</template>

View File

@@ -18,7 +18,12 @@ const rules = {};
<el-form label-width="85px" :model="formData" :rules="rules">
<el-card header="风格" class="property-group" shadow="never">
<el-form-item label="背景图片" prop="bgImgUrl">
<UploadImg v-model="formData.bgImgUrl" width="100%" height="40px">
<UploadImg
v-model="formData.bgImgUrl"
width="100%"
height="40px"
:show-description="false"
>
<template #tip>建议尺寸 750*80</template>
</UploadImg>
</el-form-item>

View File

@@ -42,6 +42,7 @@ const formData = useVModel(props, 'modelValue', emit);
height="80px"
width="100%"
class="min-w-80px"
:show-description="false"
>
<template #tip> 建议宽度750 </template>
</UploadImg>

View File

@@ -1,5 +0,0 @@
import { components } from './components/mobile/index';
export default {
components: { ...components },
};

View File

@@ -3,20 +3,40 @@ import type { DiyComponent, DiyComponentLibrary, PageConfig } from './util';
import { inject, onMounted, ref, unref, watch } from 'vue';
import { IconifyIcon } from '@vben/icons';
import { cloneDeep, isEmpty, isString } from '@vben/utils';
import {
ElAside,
ElButtonGroup,
ElCard,
ElContainer,
ElDialog,
ElHeader,
ElScrollbar,
ElTag,
ElText,
ElTooltip,
} from 'element-plus';
import draggable from 'vuedraggable';
import { componentConfigs } from '#/components/diy-editor/components/mobile';
import statusBarImg from '#/assets/imgs/diy/statusBar.png';
import {
componentConfigs,
components,
} from '#/components/diy-editor/components/mobile';
import { component as PAGE_CONFIG_COMPONENT } from '#/components/diy-editor/components/mobile/PageConfig/config';
import ComponentContainer from './components/ComponentContainer.vue';
import ComponentLibrary from './components/ComponentLibrary.vue';
import { component as NAVIGATION_BAR_COMPONENT } from './components/mobile/NavigationBar/config';
import { component as TAB_BAR_COMPONENT } from './components/mobile/TabBar/config';
/** 页面装修详情页 */
defineOptions({ name: 'DiyPageDetail' });
defineOptions({
name: 'DiyPageDetail',
components,
});
// 定义属性
const props = defineProps({
// 页面配置支持Json字符串
@@ -34,7 +54,6 @@ const props = defineProps({
// 预览地址:提供了预览地址,才会显示预览按钮
previewUrl: { type: String, default: '' },
});
// 工具栏操作
const emits = defineEmits(['reset', 'preview', 'save', 'update:modelValue']);
@@ -271,218 +290,217 @@ watch(
() => [props.showPageConfig, props.showNavigationBar, props.showTabBar],
() => setDefaultSelectedComponent(),
);
onMounted(() => setDefaultSelectedComponent());
onMounted(() => {
setDefaultSelectedComponent();
});
</script>
<template>
<el-container class="editor">
<!-- 顶部工具栏 -->
<el-header class="editor-header">
<!-- 左侧操作区 -->
<slot name="toolBarLeft"></slot>
<!-- 中心操作区 -->
<div class="header-center flex flex-1 items-center justify-center">
<span>{{ title }}</span>
</div>
<!-- 右侧操作区 -->
<el-button-group class="header-right">
<el-tooltip content="重置">
<el-button @click="handleReset">
<Icon :size="24" icon="system-uicons:reset-alt" />
</el-button>
</el-tooltip>
<el-tooltip v-if="previewUrl" content="预览">
<el-button @click="handlePreview">
<Icon :size="24" icon="ep:view" />
</el-button>
</el-tooltip>
<el-tooltip content="保存">
<el-button @click="handleSave">
<Icon :size="24" icon="ep:check" />
</el-button>
</el-tooltip>
</el-button-group>
</el-header>
<div>
<ElContainer class="editor">
<!-- 顶部工具栏 -->
<ElHeader class="editor-header">
<!-- 左侧操作区 -->
<slot name="toolBarLeft"></slot>
<!-- 中心操作区 -->
<div class="header-center flex flex-1 items-center justify-center">
<span>{{ title }}</span>
</div>
<!-- 右侧操作区 -->
<ElButtonGroup class="header-right">
<ElTooltip content="重置">
<ElButton @click="handleReset">
<IconifyIcon :size="24" icon="system-uicons:reset-alt" />
</ElButton>
</ElTooltip>
<ElTooltip v-if="previewUrl" content="预览">
<ElButton @click="handlePreview">
<IconifyIcon :size="24" icon="ep:view" />
</ElButton>
</ElTooltip>
<ElTooltip content="保存">
<ElButton @click="handleSave">
<IconifyIcon :size="24" icon="ep:check" />
</ElButton>
</ElTooltip>
</ElButtonGroup>
</ElHeader>
<!-- 中心区域 -->
<el-container class="editor-container">
<!-- 左侧组件库ComponentLibrary -->
<ComponentLibrary
v-if="libs && libs.length > 0"
ref="componentLibrary"
:list="libs"
/>
<!-- 中心设计区域ComponentContainer -->
<div class="editor-center page-prop-area" @click="handlePageSelected">
<!-- 手机顶部 -->
<div class="editor-design-top">
<!-- 手机顶部状态栏 -->
<img
alt=""
class="status-bar"
src="@/assets/imgs/diy/statusBar.png"
/>
<!-- 手机顶部导航栏 -->
<ComponentContainer
v-if="showNavigationBar"
:active="selectedComponent?.id === navigationBarComponent.id"
:component="navigationBarComponent"
:show-toolbar="false"
class="cursor-pointer!"
@click="handleNavigationBarSelected"
/>
</div>
<!-- 绝对定位的组件例如 弹窗浮动按钮等 -->
<div
v-for="(component, index) in pageComponents"
:key="index"
@click="handleComponentSelected(component, index)"
>
<component
:is="component.id"
v-if="
component.position === 'fixed' &&
selectedComponent?.uid === component.uid
"
:property="component.property"
/>
</div>
<!-- 手机页面编辑区域 -->
<el-scrollbar
:view-style="{
backgroundColor: pageConfigComponent.property.backgroundColor,
backgroundImage: `url(${pageConfigComponent.property.backgroundImage})`,
}"
height="100%"
view-class="phone-container"
wrap-class="editor-design-center page-prop-area"
>
<draggable
v-model="pageComponents"
:animation="200"
:force-fallback="true"
class="page-prop-area drag-area"
filter=".component-toolbar"
ghost-class="draggable-ghost"
group="component"
item-key="index"
@change="handleComponentChange"
>
<template #item="{ element, index }">
<ComponentContainer
v-if="!element.position || element.position === 'center'"
:active="selectedComponentIndex === index"
:can-move-down="index < pageComponents.length - 1"
:can-move-up="index > 0"
:component="element"
@click="handleComponentSelected(element, index)"
@copy="handleCopyComponent(index)"
@delete="handleDeleteComponent(index)"
@move="
(direction: number) => handleMoveComponent(index, direction)
"
/>
</template>
</draggable>
</el-scrollbar>
<!-- 手机底部导航 -->
<div
v-if="showTabBar"
class="editor-design-bottom component cursor-pointer!"
>
<ComponentContainer
:active="selectedComponent?.id === tabBarComponent.id"
:component="tabBarComponent"
:show-toolbar="false"
@click="handleTabBarSelected"
/>
</div>
<!-- 固定布局的组件 操作按钮区 -->
<div class="fixed-component-action-group">
<el-tag
v-if="showPageConfig"
:effect="
selectedComponent?.uid === pageConfigComponent.uid
? 'dark'
: 'plain'
"
:type="
selectedComponent?.uid === pageConfigComponent.uid
? 'primary'
: 'info'
"
size="large"
@click="handleComponentSelected(pageConfigComponent)"
>
<Icon :icon="pageConfigComponent.icon" :size="12" />
<span>{{ pageConfigComponent.name }}</span>
</el-tag>
<template v-for="(component, index) in pageComponents" :key="index">
<el-tag
v-if="component.position === 'fixed'"
:effect="
selectedComponent?.uid === component.uid ? 'dark' : 'plain'
"
:type="
selectedComponent?.uid === component.uid ? 'primary' : 'info'
"
closable
size="large"
@click="handleComponentSelected(component)"
@close="handleDeleteComponent(index)"
>
<Icon :icon="component.icon" :size="12" />
<span>{{ component.name }}</span>
</el-tag>
</template>
</div>
</div>
<!-- 右侧属性面板ComponentContainerProperty -->
<el-aside
v-if="selectedComponent?.property"
class="editor-right"
width="350px"
>
<el-card
body-class="h-[calc(100%-var(--el-card-padding)-var(--el-card-padding))]"
class="h-full"
shadow="never"
>
<!-- 组件名称 -->
<template #header>
<div class="gap-8px flex items-center">
<Icon :icon="selectedComponent?.icon" color="gray" />
<span>{{ selectedComponent?.name }}</span>
</div>
</template>
<el-scrollbar
class="m-[calc(0px-var(--el-card-padding))]"
view-class="p-[var(--el-card-padding)] p-b-[calc(var(--el-card-padding)+var(--el-card-padding))] property"
<!-- 中心区域 -->
<ElContainer class="editor-container">
<!-- 左侧组件库ComponentLibrary -->
<ComponentLibrary
v-if="libs && libs.length > 0"
ref="componentLibrary"
:list="libs"
/>
<!-- 中心设计区域ComponentContainer -->
<div class="editor-center page-prop-area" @click="handlePageSelected">
<!-- 手机顶部 -->
<div class="editor-design-top">
<!-- 手机顶部状态栏 -->
<img alt="" class="status-bar" :src="statusBarImg" />
<!-- 手机顶部导航栏 -->
<ComponentContainer
v-if="showNavigationBar"
:active="selectedComponent?.id === navigationBarComponent.id"
:component="navigationBarComponent"
:show-toolbar="false"
class="cursor-pointer"
@click="handleNavigationBarSelected"
/>
</div>
<!-- 绝对定位的组件例如 弹窗浮动按钮等 -->
<div
v-for="(component, index) in pageComponents"
:key="index"
@click="handleComponentSelected(component, index)"
>
<component
:is="`${selectedComponent?.id}Property`"
:key="selectedComponent?.uid || selectedComponent?.id"
v-model="selectedComponent.property"
:is="component.id"
v-if="
component.position === 'fixed' &&
selectedComponent?.uid === component.uid
"
:property="component.property"
/>
</el-scrollbar>
</el-card>
</el-aside>
</el-container>
</el-container>
</div>
<!-- 手机页面编辑区域 -->
<ElScrollbar
:view-style="{
backgroundColor: pageConfigComponent.property.backgroundColor,
backgroundImage: `url(${pageConfigComponent.property.backgroundImage})`,
}"
view-class="phone-container"
wrap-class="editor-design-center page-prop-area"
style="height: calc(100vh - 135px - 120px)"
>
<draggable
v-model="pageComponents"
:animation="200"
:force-fallback="true"
class="page-prop-area drag-area"
filter=".component-toolbar"
ghost-class="draggable-ghost"
group="component"
item-key="index"
@change="handleComponentChange"
>
<template #item="{ element, index }">
<ComponentContainer
v-if="!element.position || element.position === 'center'"
:active="selectedComponentIndex === index"
:can-move-down="index < pageComponents.length - 1"
:can-move-up="index > 0"
:component="element"
@click="handleComponentSelected(element, index)"
@copy="handleCopyComponent(index)"
@delete="handleDeleteComponent(index)"
@move="
(direction: number) => handleMoveComponent(index, direction)
"
/>
</template>
</draggable>
</ElScrollbar>
<!-- 手机底部导航 -->
<div
v-if="showTabBar"
class="editor-design-bottom component cursor-pointer"
>
<ComponentContainer
:active="selectedComponent?.id === tabBarComponent.id"
:component="tabBarComponent"
:show-toolbar="false"
@click="handleTabBarSelected"
/>
</div>
<!-- 固定布局的组件 操作按钮区 -->
<div class="fixed-component-action-group gap-2">
<ElTag
v-if="showPageConfig"
:effect="
selectedComponent?.uid === pageConfigComponent.uid
? 'dark'
: 'plain'
"
:type="
selectedComponent?.uid === pageConfigComponent.uid
? 'primary'
: 'info'
"
size="large"
@click="handleComponentSelected(pageConfigComponent)"
>
<IconifyIcon :icon="pageConfigComponent.icon" :size="12" />
<span>{{ pageConfigComponent.name }}</span>
</ElTag>
<template v-for="(component, index) in pageComponents" :key="index">
<ElTag
v-if="component.position === 'fixed'"
:effect="
selectedComponent?.uid === component.uid ? 'dark' : 'plain'
"
:type="
selectedComponent?.uid === component.uid ? 'primary' : 'info'
"
closable
size="large"
@click="handleComponentSelected(component)"
@close="handleDeleteComponent(index)"
>
<IconifyIcon :icon="component.icon" :size="12" />
<span>{{ component.name }}</span>
</ElTag>
</template>
</div>
</div>
<!-- 右侧属性面板ComponentContainerProperty -->
<ElAside
v-if="selectedComponent?.property"
class="editor-right"
width="350px"
>
<ElCard
body-class="h-[calc(100%-var(--el-card-padding)-var(--el-card-padding))]"
class="h-full"
shadow="never"
>
<!-- 组件名称 -->
<template #header>
<div class="flex items-center gap-2">
<IconifyIcon :icon="selectedComponent?.icon" color="gray" />
<span>{{ selectedComponent?.name }}</span>
</div>
</template>
<ElScrollbar
class="m-[calc(0px-var(--el-card-padding))]"
view-class="p-[var(--el-card-padding)] pb-[calc(var(--el-card-padding)+var(--el-card-padding))] property"
>
<component
:is="`${selectedComponent?.id}Property`"
:key="selectedComponent?.uid || selectedComponent?.id"
v-model="selectedComponent.property"
/>
</ElScrollbar>
</ElCard>
</ElAside>
</ElContainer>
</ElContainer>
<!-- 预览弹框 -->
<Dialog v-model="previewDialogVisible" title="预览" width="700">
<div class="flex justify-around">
<IFrame
:src="previewUrl"
class="w-375px border-4px border-rounded-8px p-2px h-667px! border-solid"
/>
<div class="flex flex-col">
<el-text>手机扫码预览</el-text>
<Qrcode :text="previewUrl" logo="/logo.gif" />
<!-- 预览弹框 -->
<ElDialog v-model="previewDialogVisible" title="预览" width="700">
<div class="flex justify-around">
<IFrame
:src="previewUrl"
class="h-[667px] w-[375px] rounded-lg border-4 border-solid p-0.5"
/>
<div class="flex flex-col">
<ElText>手机扫码预览</ElText>
<Qrcode :text="previewUrl" logo="/logo.gif" />
</div>
</div>
</div>
</Dialog>
</ElDialog>
</div>
</template>
<style lang="scss" scoped>
/* 手机宽度 */
@@ -493,7 +511,6 @@ $phone-width: 375px;
display: flex;
flex-direction: column;
height: 100%;
margin: calc(0px - var(--app-content-padding));
/* 顶部:工具栏 */
.editor-header {
@@ -525,15 +542,10 @@ $phone-width: 375px;
/* 中心操作区 */
.editor-container {
height: calc(
100vh - var(--top-tool-height) - var(--tags-view-height) - var(
--app-footer-height
) -
42px
);
height: calc(100vh - 135px);
/* 右侧属性面板 */
.editor-right {
:deep(.editor-right) {
flex-shrink: 0;
overflow: hidden;
box-shadow: -8px 0 8px -8px rgb(0 0 0 / 12%);
@@ -624,7 +636,6 @@ $phone-width: 375px;
right: 16px;
display: flex;
flex-direction: column;
gap: 8px;
:deep(.el-tag) {
border: none;