feat:【antd】【mall】diy-editor 代码风格统一 & 逐个测试 60%
This commit is contained in:
@@ -1,4 +1,87 @@
|
|||||||
// 导出所有优惠券相关组件
|
import type { MallCouponTemplateApi } from '#/api/mall/promotion/coupon/couponTemplate';
|
||||||
export { CouponDiscount } from './coupon-discount';
|
|
||||||
export { CouponDiscountDesc } from './coupon-discount-desc';
|
import { defineComponent } from 'vue';
|
||||||
export { CouponValidTerm } from './coupon-validTerm';
|
|
||||||
|
import {
|
||||||
|
CouponTemplateValidityTypeEnum,
|
||||||
|
PromotionDiscountTypeEnum,
|
||||||
|
} from '@vben/constants';
|
||||||
|
import { floatToFixed2, formatDate } from '@vben/utils';
|
||||||
|
|
||||||
|
/** 有效期 */
|
||||||
|
export const CouponValidTerm = defineComponent({
|
||||||
|
name: 'CouponValidTerm',
|
||||||
|
props: {
|
||||||
|
coupon: {
|
||||||
|
type: Object as () => MallCouponTemplateApi.CouponTemplate,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setup(props) {
|
||||||
|
const coupon = props.coupon as MallCouponTemplateApi.CouponTemplate;
|
||||||
|
const text =
|
||||||
|
coupon.validityType === CouponTemplateValidityTypeEnum.DATE.type
|
||||||
|
? `有效期:${formatDate(coupon.validStartTime, 'YYYY-MM-DD')} 至 ${formatDate(
|
||||||
|
coupon.validEndTime,
|
||||||
|
'YYYY-MM-DD',
|
||||||
|
)}`
|
||||||
|
: `领取后第 ${coupon.fixedStartTerm} - ${coupon.fixedEndTerm} 天内可用`;
|
||||||
|
return () => <div>{text}</div>;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/** 优惠值 */
|
||||||
|
export const CouponDiscount = defineComponent({
|
||||||
|
name: 'CouponDiscount',
|
||||||
|
props: {
|
||||||
|
coupon: {
|
||||||
|
type: Object as () => MallCouponTemplateApi.CouponTemplate,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setup(props) {
|
||||||
|
const coupon = props.coupon as MallCouponTemplateApi.CouponTemplate;
|
||||||
|
// 折扣
|
||||||
|
let value = `${(coupon.discountPercent ?? 0) / 10}`;
|
||||||
|
let suffix = ' 折';
|
||||||
|
// 满减
|
||||||
|
if (coupon.discountType === PromotionDiscountTypeEnum.PRICE.type) {
|
||||||
|
value = floatToFixed2(coupon.discountPrice);
|
||||||
|
suffix = ' 元';
|
||||||
|
}
|
||||||
|
return () => (
|
||||||
|
<div>
|
||||||
|
<span class={'text-20px font-bold'}>{value}</span>
|
||||||
|
<span>{suffix}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/** 优惠描述 */
|
||||||
|
export const CouponDiscountDesc = defineComponent({
|
||||||
|
name: 'CouponDiscountDesc',
|
||||||
|
props: {
|
||||||
|
coupon: {
|
||||||
|
type: Object as () => MallCouponTemplateApi.CouponTemplate,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setup(props) {
|
||||||
|
const coupon = props.coupon as MallCouponTemplateApi.CouponTemplate;
|
||||||
|
// 使用条件
|
||||||
|
const useCondition =
|
||||||
|
coupon.usePrice > 0 ? `满${floatToFixed2(coupon.usePrice)}元,` : '';
|
||||||
|
// 优惠描述
|
||||||
|
const discountDesc =
|
||||||
|
coupon.discountType === PromotionDiscountTypeEnum.PRICE.type
|
||||||
|
? `减${floatToFixed2(coupon.discountPrice)}元`
|
||||||
|
: `打${(coupon.discountPercent ?? 0) / 10}折`;
|
||||||
|
return () => (
|
||||||
|
<div>
|
||||||
|
<span>{useCondition}</span>
|
||||||
|
<span>{discountDesc}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
import type { MallCouponTemplateApi } from '#/api/mall/promotion/coupon/couponTemplate';
|
|
||||||
|
|
||||||
import { defineComponent } from 'vue';
|
|
||||||
|
|
||||||
import { PromotionDiscountTypeEnum } from '@vben/constants';
|
|
||||||
import { floatToFixed2 } from '@vben/utils';
|
|
||||||
|
|
||||||
// 优惠描述
|
|
||||||
export const CouponDiscountDesc = defineComponent({
|
|
||||||
name: 'CouponDiscountDesc',
|
|
||||||
props: {
|
|
||||||
coupon: {
|
|
||||||
type: Object as () => MallCouponTemplateApi.CouponTemplate,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setup(props) {
|
|
||||||
const coupon = props.coupon as MallCouponTemplateApi.CouponTemplate;
|
|
||||||
// 使用条件
|
|
||||||
const useCondition =
|
|
||||||
coupon.usePrice > 0 ? `满${floatToFixed2(coupon.usePrice)}元,` : '';
|
|
||||||
// 优惠描述
|
|
||||||
const discountDesc =
|
|
||||||
coupon.discountType === PromotionDiscountTypeEnum.PRICE.type
|
|
||||||
? `减${floatToFixed2(coupon.discountPrice)}元`
|
|
||||||
: `打${(coupon.discountPercent ?? 0) / 10}折`;
|
|
||||||
return () => (
|
|
||||||
<div>
|
|
||||||
<span>{useCondition}</span>
|
|
||||||
<span>{discountDesc}</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
import type { MallCouponTemplateApi } from '#/api/mall/promotion/coupon/couponTemplate';
|
|
||||||
|
|
||||||
import { defineComponent } from 'vue';
|
|
||||||
|
|
||||||
import { PromotionDiscountTypeEnum } from '@vben/constants';
|
|
||||||
import { floatToFixed2 } from '@vben/utils';
|
|
||||||
|
|
||||||
// 优惠值
|
|
||||||
export const CouponDiscount = defineComponent({
|
|
||||||
name: 'CouponDiscount',
|
|
||||||
props: {
|
|
||||||
coupon: {
|
|
||||||
type: Object as () => MallCouponTemplateApi.CouponTemplate,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setup(props) {
|
|
||||||
const coupon = props.coupon as MallCouponTemplateApi.CouponTemplate;
|
|
||||||
// 折扣
|
|
||||||
let value = `${(coupon.discountPercent ?? 0) / 10}`;
|
|
||||||
let suffix = ' 折';
|
|
||||||
// 满减
|
|
||||||
if (coupon.discountType === PromotionDiscountTypeEnum.PRICE.type) {
|
|
||||||
value = floatToFixed2(coupon.discountPrice);
|
|
||||||
suffix = ' 元';
|
|
||||||
}
|
|
||||||
return () => (
|
|
||||||
<div>
|
|
||||||
<span class={'text-20px font-bold'}>{value}</span>
|
|
||||||
<span>{suffix}</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
import type { MallCouponTemplateApi } from '#/api/mall/promotion/coupon/couponTemplate';
|
|
||||||
|
|
||||||
import { defineComponent } from 'vue';
|
|
||||||
|
|
||||||
import { CouponTemplateValidityTypeEnum } from '@vben/constants';
|
|
||||||
import { formatDate } from '@vben/utils';
|
|
||||||
|
|
||||||
// 有效期
|
|
||||||
export const CouponValidTerm = defineComponent({
|
|
||||||
name: 'CouponValidTerm',
|
|
||||||
props: {
|
|
||||||
coupon: {
|
|
||||||
type: Object as () => MallCouponTemplateApi.CouponTemplate,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setup(props) {
|
|
||||||
const coupon = props.coupon as MallCouponTemplateApi.CouponTemplate;
|
|
||||||
const text =
|
|
||||||
coupon.validityType === CouponTemplateValidityTypeEnum.DATE.type
|
|
||||||
? `有效期:${formatDate(coupon.validStartTime, 'YYYY-MM-DD')} 至 ${formatDate(
|
|
||||||
coupon.validEndTime,
|
|
||||||
'YYYY-MM-DD',
|
|
||||||
)}`
|
|
||||||
: `领取后第 ${coupon.fixedStartTerm} - ${coupon.fixedEndTerm} 天内可用`;
|
|
||||||
return () => <div>{text}</div>;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -13,12 +13,19 @@ import {
|
|||||||
CouponValidTerm,
|
CouponValidTerm,
|
||||||
} from './component';
|
} from './component';
|
||||||
|
|
||||||
/** 商品卡片 */
|
/** 优惠劵卡片 */
|
||||||
defineOptions({ name: 'CouponCard' });
|
defineOptions({ name: 'CouponCard' });
|
||||||
// 定义属性
|
|
||||||
|
/** 定义属性 */
|
||||||
const props = defineProps<{ property: CouponCardProperty }>();
|
const props = defineProps<{ property: CouponCardProperty }>();
|
||||||
// 商品列表
|
|
||||||
const couponList = ref<MallCouponTemplateApi.CouponTemplate[]>([]);
|
const couponList = ref<MallCouponTemplateApi.CouponTemplate[]>([]); // 优惠劵列表
|
||||||
|
const phoneWidth = ref(375); // 手机宽度
|
||||||
|
const containerRef = ref(); // 容器引用
|
||||||
|
const scrollbarWidth = ref('100%'); // 滚动条宽度
|
||||||
|
const couponWidth = ref(375); // 优惠券宽度
|
||||||
|
|
||||||
|
/** 监听优惠券 ID 变化,加载优惠券列表 */
|
||||||
watch(
|
watch(
|
||||||
() => props.property.couponIds,
|
() => props.property.couponIds,
|
||||||
async () => {
|
async () => {
|
||||||
@@ -32,15 +39,7 @@ watch(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// 手机宽度
|
/** 计算布局参数 */
|
||||||
const phoneWidth = ref(384);
|
|
||||||
// 容器
|
|
||||||
const containerRef = ref();
|
|
||||||
// 滚动条宽度
|
|
||||||
const scrollbarWidth = ref('100%');
|
|
||||||
// 优惠券的宽度
|
|
||||||
const couponWidth = ref(384);
|
|
||||||
// 计算布局参数
|
|
||||||
watch(
|
watch(
|
||||||
() => [props.property, phoneWidth, couponList.value.length],
|
() => [props.property, phoneWidth, couponList.value.length],
|
||||||
() => {
|
() => {
|
||||||
@@ -56,9 +55,10 @@ watch(
|
|||||||
},
|
},
|
||||||
{ immediate: true, deep: true },
|
{ immediate: true, deep: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/** 初始化 */
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 提取手机宽度
|
phoneWidth.value = containerRef.value?.wrapRef?.offsetWidth || 375;
|
||||||
phoneWidth.value = containerRef.value?.wrapRef?.offsetWidth || 384;
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
@@ -82,17 +82,14 @@ onMounted(() => {
|
|||||||
v-for="(coupon, index) in couponList"
|
v-for="(coupon, index) in couponList"
|
||||||
:key="index"
|
:key="index"
|
||||||
>
|
>
|
||||||
<!-- 布局1:1列-->
|
<!-- 布局 1:1 列-->
|
||||||
<div
|
<div
|
||||||
v-if="property.columns === 1"
|
v-if="property.columns === 1"
|
||||||
class="ml-4 flex flex-row justify-between p-2"
|
class="ml-4 flex flex-row justify-between p-2"
|
||||||
>
|
>
|
||||||
<div class="flex flex-col justify-evenly gap-1">
|
<div class="flex flex-col justify-evenly gap-1">
|
||||||
<!-- 优惠值 -->
|
|
||||||
<CouponDiscount :coupon="coupon" />
|
<CouponDiscount :coupon="coupon" />
|
||||||
<!-- 优惠描述 -->
|
|
||||||
<CouponDiscountDesc :coupon="coupon" />
|
<CouponDiscountDesc :coupon="coupon" />
|
||||||
<!-- 有效期 -->
|
|
||||||
<CouponValidTerm :coupon="coupon" />
|
<CouponValidTerm :coupon="coupon" />
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col justify-evenly">
|
<div class="flex flex-col justify-evenly">
|
||||||
@@ -107,17 +104,14 @@ onMounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 布局2:2列-->
|
<!-- 布局 2:2 列-->
|
||||||
<div
|
<div
|
||||||
v-else-if="property.columns === 2"
|
v-else-if="property.columns === 2"
|
||||||
class="ml-4 flex flex-row justify-between p-2"
|
class="ml-4 flex flex-row justify-between p-2"
|
||||||
>
|
>
|
||||||
<div class="flex flex-col justify-evenly gap-1">
|
<div class="flex flex-col justify-evenly gap-1">
|
||||||
<!-- 优惠值 -->
|
|
||||||
<CouponDiscount :coupon="coupon" />
|
<CouponDiscount :coupon="coupon" />
|
||||||
<!-- 优惠描述 -->
|
|
||||||
<CouponDiscountDesc :coupon="coupon" />
|
<CouponDiscountDesc :coupon="coupon" />
|
||||||
<!-- 领取说明 -->
|
|
||||||
<div v-if="coupon.totalCount >= 0">
|
<div v-if="coupon.totalCount >= 0">
|
||||||
仅剩:{{ coupon.totalCount - coupon.takeCount }}张
|
仅剩:{{ coupon.totalCount - coupon.takeCount }}张
|
||||||
</div>
|
</div>
|
||||||
@@ -135,11 +129,9 @@ onMounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 布局3:3列-->
|
<!-- 布局 3:3 列-->
|
||||||
<div v-else class="flex flex-col items-center justify-around gap-1 p-1">
|
<div v-else class="flex flex-col items-center justify-around gap-1 p-1">
|
||||||
<!-- 优惠值 -->
|
|
||||||
<CouponDiscount :coupon="coupon" />
|
<CouponDiscount :coupon="coupon" />
|
||||||
<!-- 优惠描述 -->
|
|
||||||
<CouponDiscountDesc :coupon="coupon" />
|
<CouponDiscountDesc :coupon="coupon" />
|
||||||
<div
|
<div
|
||||||
class="rounded-full px-2 py-0.5"
|
class="rounded-full px-2 py-0.5"
|
||||||
|
|||||||
@@ -5,23 +5,20 @@ import { ref } from 'vue';
|
|||||||
|
|
||||||
import { IconifyIcon } from '@vben/icons';
|
import { IconifyIcon } from '@vben/icons';
|
||||||
|
|
||||||
import { Button, Image, message } from 'ant-design-vue';
|
import { Button, Image } from 'ant-design-vue';
|
||||||
|
|
||||||
/** 悬浮按钮 */
|
/** 悬浮按钮 */
|
||||||
defineOptions({ name: 'FloatingActionButton' });
|
defineOptions({ name: 'FloatingActionButton' });
|
||||||
// 定义属性
|
|
||||||
|
/** 定义属性 */
|
||||||
defineProps<{ property: FloatingActionButtonProperty }>();
|
defineProps<{ property: FloatingActionButtonProperty }>();
|
||||||
|
|
||||||
// 是否展开
|
const expanded = ref(false); // 是否展开
|
||||||
const expanded = ref(false);
|
|
||||||
// 处理展开/折叠
|
|
||||||
const handleToggleFab = () => {
|
|
||||||
expanded.value = !expanded.value;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleActive = (index: number) => {
|
/** 处理展开/折叠 */
|
||||||
message.success(`点击了${index}`);
|
function handleToggleFab() {
|
||||||
};
|
expanded.value = !expanded.value;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
@@ -38,9 +35,8 @@ const handleActive = (index: number) => {
|
|||||||
v-for="(item, index) in property.list"
|
v-for="(item, index) in property.list"
|
||||||
:key="index"
|
:key="index"
|
||||||
class="flex flex-col items-center"
|
class="flex flex-col items-center"
|
||||||
@click="handleActive(index)"
|
|
||||||
>
|
>
|
||||||
<Image :src="item.imgUrl" fit="contain" class="h-7 w-7">
|
<Image :src="item.imgUrl" fit="contain" class="!h-7 !w-7">
|
||||||
<template #error>
|
<template #error>
|
||||||
<div class="flex h-full w-full items-center justify-center">
|
<div class="flex h-full w-full items-center justify-center">
|
||||||
<IconifyIcon
|
<IconifyIcon
|
||||||
@@ -64,33 +60,15 @@ const handleActive = (index: number) => {
|
|||||||
<Button type="primary" size="large" circle @click="handleToggleFab">
|
<Button type="primary" size="large" circle @click="handleToggleFab">
|
||||||
<IconifyIcon
|
<IconifyIcon
|
||||||
icon="lucide:plus"
|
icon="lucide:plus"
|
||||||
class="fab-icon"
|
class="transition-transform duration-300"
|
||||||
:class="[{ active: expanded }]"
|
:class="expanded ? 'rotate-[135deg]' : 'rotate-0'"
|
||||||
/>
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<!-- 模态背景:展开时显示,点击后折叠 -->
|
<!-- 模态背景:展开时显示,点击后折叠 -->
|
||||||
<div v-if="expanded" class="modal-bg" @click="handleToggleFab"></div>
|
<div
|
||||||
|
v-if="expanded"
|
||||||
|
class="absolute left-[calc(50%-384px/2)] top-0 z-[11] h-full w-[384px] bg-black/40"
|
||||||
|
@click="handleToggleFab"
|
||||||
|
></div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
/* 模态背景 */
|
|
||||||
.modal-bg {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: calc(50% - 384px / 2);
|
|
||||||
z-index: 11;
|
|
||||||
width: 384px;
|
|
||||||
height: 100%;
|
|
||||||
background-color: rgb(0 0 0 / 40%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.fab-icon {
|
|
||||||
transform: rotate(0deg);
|
|
||||||
transition: transform 0.3s;
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
transform: rotate(135deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { IconifyIcon } from '@vben/icons';
|
|||||||
|
|
||||||
import { Button, Image } from 'ant-design-vue';
|
import { Button, Image } from 'ant-design-vue';
|
||||||
|
|
||||||
import AppLinkSelectDialog from '#/views/mall/promotion/components/app-link-input/app-link-select-dialog.vue';
|
import { AppLinkSelectDialog } from '#/views/mall/promotion/components';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CONTROL_DOT_LIST,
|
CONTROL_DOT_LIST,
|
||||||
|
|||||||
@@ -9,9 +9,11 @@ import { Image } from 'ant-design-vue';
|
|||||||
|
|
||||||
/** 广告魔方 */
|
/** 广告魔方 */
|
||||||
defineOptions({ name: 'MagicCube' });
|
defineOptions({ name: 'MagicCube' });
|
||||||
|
|
||||||
const props = defineProps<{ property: MagicCubeProperty }>();
|
const props = defineProps<{ property: MagicCubeProperty }>();
|
||||||
// 一个方块的大小
|
|
||||||
const CUBE_SIZE = 93.75;
|
const CUBE_SIZE = 93.75; // 一个方块的大小
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 计算方块的行数
|
* 计算方块的行数
|
||||||
* 行数用于计算魔方的总体高度,存在以下情况:
|
* 行数用于计算魔方的总体高度,存在以下情况:
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ const handleHotAreaSelected = (_: any, index: number) => {
|
|||||||
</FormItem>
|
</FormItem>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
<!-- TODO @芋艿:距离不一致,需要看看怎么统一; -->
|
||||||
<FormItem label="上圆角" name="borderRadiusTop">
|
<FormItem label="上圆角" name="borderRadiusTop">
|
||||||
<Slider v-model:value="formData.borderRadiusTop" :max="100" :min="0" />
|
<Slider v-model:value="formData.borderRadiusTop" :max="100" :min="0" />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { Image } from 'ant-design-vue';
|
|||||||
|
|
||||||
/** 宫格导航 */
|
/** 宫格导航 */
|
||||||
defineOptions({ name: 'MenuGrid' });
|
defineOptions({ name: 'MenuGrid' });
|
||||||
|
|
||||||
defineProps<{ property: MenuGridProperty }>();
|
defineProps<{ property: MenuGridProperty }>();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
AppLinkInput,
|
AppLinkInput,
|
||||||
ColorInput,
|
ColorInput,
|
||||||
Draggable,
|
Draggable,
|
||||||
|
InputWithColor,
|
||||||
} from '#/views/mall/promotion/components';
|
} from '#/views/mall/promotion/components';
|
||||||
|
|
||||||
import ComponentContainerProperty from '../../component-container-property.vue';
|
import ComponentContainerProperty from '../../component-container-property.vue';
|
||||||
@@ -18,7 +19,9 @@ import { EMPTY_MENU_GRID_ITEM_PROPERTY } from './config';
|
|||||||
defineOptions({ name: 'MenuGridProperty' });
|
defineOptions({ name: 'MenuGridProperty' });
|
||||||
|
|
||||||
const props = defineProps<{ modelValue: MenuGridProperty }>();
|
const props = defineProps<{ modelValue: MenuGridProperty }>();
|
||||||
|
|
||||||
const emit = defineEmits(['update:modelValue']);
|
const emit = defineEmits(['update:modelValue']);
|
||||||
|
|
||||||
const formData = useVModel(props, 'modelValue', emit);
|
const formData = useVModel(props, 'modelValue', emit);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -32,7 +35,6 @@ const formData = useVModel(props, 'modelValue', emit);
|
|||||||
<Radio :value="4">4个</Radio>
|
<Radio :value="4">4个</Radio>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
||||||
<p class="text-base font-bold">菜单设置</p>
|
<p class="text-base font-bold">菜单设置</p>
|
||||||
<div class="flex flex-col gap-2 rounded-md p-4 shadow-lg">
|
<div class="flex flex-col gap-2 rounded-md p-4 shadow-lg">
|
||||||
<Draggable
|
<Draggable
|
||||||
@@ -47,17 +49,17 @@ const formData = useVModel(props, 'modelValue', emit);
|
|||||||
width="80px"
|
width="80px"
|
||||||
:show-description="false"
|
:show-description="false"
|
||||||
>
|
>
|
||||||
<template #tip> 建议尺寸:44 * 44 </template>
|
<template #tip> 建议尺寸:44 * 44</template>
|
||||||
</UploadImg>
|
</UploadImg>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="标题" prop="title">
|
<FormItem label="标题" prop="title">
|
||||||
<ColorInput
|
<InputWithColor
|
||||||
v-model="element.title"
|
v-model="element.title"
|
||||||
v-model:color="element.titleColor"
|
v-model:color="element.titleColor"
|
||||||
/>
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="副标题" prop="subtitle">
|
<FormItem label="副标题" prop="subtitle">
|
||||||
<ColorInput
|
<InputWithColor
|
||||||
v-model="element.subtitle"
|
v-model="element.subtitle"
|
||||||
v-model:color="element.subtitleColor"
|
v-model:color="element.subtitleColor"
|
||||||
/>
|
/>
|
||||||
@@ -70,7 +72,7 @@ const formData = useVModel(props, 'modelValue', emit);
|
|||||||
</FormItem>
|
</FormItem>
|
||||||
<template v-if="element.badge.show">
|
<template v-if="element.badge.show">
|
||||||
<FormItem label="角标内容" prop="badge.text">
|
<FormItem label="角标内容" prop="badge.text">
|
||||||
<ColorInput
|
<InputWithColor
|
||||||
v-model="element.badge.text"
|
v-model="element.badge.text"
|
||||||
v-model:color="element.badge.textColor"
|
v-model:color="element.badge.textColor"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { Image } from 'ant-design-vue';
|
|||||||
|
|
||||||
/** 列表导航 */
|
/** 列表导航 */
|
||||||
defineOptions({ name: 'MenuList' });
|
defineOptions({ name: 'MenuList' });
|
||||||
|
|
||||||
defineProps<{ property: MenuListProperty }>();
|
defineProps<{ property: MenuListProperty }>();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import {
|
|||||||
MagicCubeEditor,
|
MagicCubeEditor,
|
||||||
} from '#/views/mall/promotion/components';
|
} from '#/views/mall/promotion/components';
|
||||||
|
|
||||||
// 导航栏属性面板
|
/** 导航栏单元格属性面板 */
|
||||||
defineOptions({ name: 'NavigationBarCellProperty' });
|
defineOptions({ name: 'NavigationBarCellProperty' });
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -36,26 +36,21 @@ const props = defineProps({
|
|||||||
default: () => [],
|
default: () => [],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['update:modelValue']);
|
const emit = defineEmits(['update:modelValue']);
|
||||||
|
|
||||||
const cellList = useVModel(props, 'modelValue', emit);
|
const cellList = useVModel(props, 'modelValue', emit);
|
||||||
|
|
||||||
// 单元格数量:小程序6个(右侧胶囊按钮占了2个),其它平台8个
|
/**
|
||||||
|
* 计算单元格数量
|
||||||
|
* 1. 小程序:6 个(因为右侧有胶囊按钮占据 2 个格子的空间)
|
||||||
|
* 2. 其它平台:8 个(全部空间可用)
|
||||||
|
*/
|
||||||
const cellCount = computed(() => (props.isMp ? 6 : 8));
|
const cellCount = computed(() => (props.isMp ? 6 : 8));
|
||||||
|
|
||||||
// 转换为Rect格式的数据
|
const selectedHotAreaIndex = ref(0); // 选中的热区
|
||||||
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 = (
|
const handleHotAreaSelected = (
|
||||||
cellValue: NavigationBarCellProperty,
|
cellValue: NavigationBarCellProperty,
|
||||||
index: number,
|
index: number,
|
||||||
@@ -71,7 +66,7 @@ const handleHotAreaSelected = (
|
|||||||
<template>
|
<template>
|
||||||
<div class="h-40px flex items-center justify-center">
|
<div class="h-40px flex items-center justify-center">
|
||||||
<MagicCubeEditor
|
<MagicCubeEditor
|
||||||
v-model="rectList"
|
v-model="cellList as any"
|
||||||
:cols="cellCount"
|
:cols="cellCount"
|
||||||
:cube-size="38"
|
:cube-size="38"
|
||||||
:rows="1"
|
:rows="1"
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ defineOptions({ name: 'NavigationBar' });
|
|||||||
|
|
||||||
const props = defineProps<{ property: NavigationBarProperty }>();
|
const props = defineProps<{ property: NavigationBarProperty }>();
|
||||||
|
|
||||||
// 背景
|
/** 计算背景样式 */
|
||||||
const bgStyle = computed(() => {
|
const bgStyle = computed(() => {
|
||||||
const background =
|
const background =
|
||||||
props.property.bgType === 'img' && props.property.bgImg
|
props.property.bgType === 'img' && props.property.bgImg
|
||||||
@@ -26,19 +26,22 @@ const bgStyle = computed(() => {
|
|||||||
: props.property.bgColor;
|
: props.property.bgColor;
|
||||||
return { background };
|
return { background };
|
||||||
});
|
});
|
||||||
// 单元格列表
|
|
||||||
|
/** 获取当前预览的单元格列表 */
|
||||||
const cellList = computed(() =>
|
const cellList = computed(() =>
|
||||||
props.property._local?.previewMp
|
props.property._local?.previewMp
|
||||||
? props.property.mpCells
|
? props.property.mpCells
|
||||||
: props.property.otherCells,
|
: props.property.otherCells,
|
||||||
);
|
);
|
||||||
// 单元格宽度
|
|
||||||
|
/** 计算单元格宽度 */
|
||||||
const cellWidth = computed(() => {
|
const cellWidth = computed(() => {
|
||||||
return props.property._local?.previewMp
|
return props.property._local?.previewMp
|
||||||
? (384 - 80 - 86) / 6
|
? (375 - 80 - 86) / 6
|
||||||
: (384 - 90) / 8;
|
: (375 - 90) / 8;
|
||||||
});
|
});
|
||||||
// 获得单元格样式
|
|
||||||
|
/** 获取单元格样式 */
|
||||||
const getCellStyle = (cell: NavigationBarCellProperty) => {
|
const getCellStyle = (cell: NavigationBarCellProperty) => {
|
||||||
return {
|
return {
|
||||||
width: `${cell.width * cellWidth.value + (cell.width - 1) * 10}px`,
|
width: `${cell.width * cellWidth.value + (cell.width - 1) * 10}px`,
|
||||||
@@ -46,7 +49,8 @@ const getCellStyle = (cell: NavigationBarCellProperty) => {
|
|||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
} as StyleValue;
|
} as StyleValue;
|
||||||
};
|
};
|
||||||
// 获得搜索框属性
|
|
||||||
|
/** 获取搜索框属性配置 */
|
||||||
const getSearchProp = computed(() => (cell: NavigationBarCellProperty) => {
|
const getSearchProp = computed(() => (cell: NavigationBarCellProperty) => {
|
||||||
return {
|
return {
|
||||||
height: 30,
|
height: 30,
|
||||||
|
|||||||
@@ -17,16 +17,16 @@ import { ColorInput } from '#/views/mall/promotion/components';
|
|||||||
|
|
||||||
import NavigationBarCellProperty from './components/cell-property.vue';
|
import NavigationBarCellProperty from './components/cell-property.vue';
|
||||||
|
|
||||||
// 导航栏属性面板
|
/** 导航栏属性面板 */
|
||||||
defineOptions({ name: 'NavigationBarProperty' });
|
defineOptions({ name: 'NavigationBarProperty' });
|
||||||
|
|
||||||
const props = defineProps<{ modelValue: NavigationBarProperty }>();
|
const props = defineProps<{ modelValue: NavigationBarProperty }>();
|
||||||
|
|
||||||
const emit = defineEmits(['update:modelValue']);
|
const emit = defineEmits(['update:modelValue']);
|
||||||
|
|
||||||
// 表单校验
|
|
||||||
const rules: Record<string, any> = {
|
const rules: Record<string, any> = {
|
||||||
name: [{ required: true, message: '请输入页面名称', trigger: 'blur' }],
|
name: [{ required: true, message: '请输入页面名称', trigger: 'blur' }],
|
||||||
};
|
}; // 表单校验
|
||||||
|
|
||||||
const formData = useVModel(props, 'modelValue', emit);
|
const formData = useVModel(props, 'modelValue', emit);
|
||||||
if (!formData.value._local) {
|
if (!formData.value._local) {
|
||||||
|
|||||||
@@ -9,18 +9,19 @@ import { Image } from 'ant-design-vue';
|
|||||||
|
|
||||||
/** 弹窗广告 */
|
/** 弹窗广告 */
|
||||||
defineOptions({ name: 'Popover' });
|
defineOptions({ name: 'Popover' });
|
||||||
// 定义属性
|
|
||||||
defineProps<{ property: PopoverProperty }>();
|
|
||||||
|
|
||||||
// 处理选中
|
const props = defineProps<{ property: PopoverProperty }>();
|
||||||
const activeIndex = ref(0);
|
|
||||||
const handleActive = (index: number) => {
|
const activeIndex = ref(0); // 选中 index
|
||||||
|
|
||||||
|
/** 处理选中 */
|
||||||
|
function handleActive(index: number) {
|
||||||
activeIndex.value = index;
|
activeIndex.value = index;
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
v-for="(item, index) in property.list"
|
v-for="(item, index) in props.property.list"
|
||||||
:key="index"
|
:key="index"
|
||||||
class="absolute bottom-1/2 right-1/2 h-[454px] w-[292px] rounded border border-gray-300 bg-white p-0.5"
|
class="absolute bottom-1/2 right-1/2 h-[454px] w-[292px] rounded border border-gray-300 bg-white p-0.5"
|
||||||
:style="{
|
:style="{
|
||||||
@@ -40,5 +41,3 @@ const handleActive = (index: number) => {
|
|||||||
<div class="absolute right-1 top-1 text-xs">{{ index + 1 }}</div>
|
<div class="absolute right-1 top-1 text-xs">{{ index + 1 }}</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { onMounted, ref, watch } from 'vue';
|
|||||||
|
|
||||||
import { ElScrollbar } from 'element-plus';
|
import { ElScrollbar } from 'element-plus';
|
||||||
|
|
||||||
import * as CouponTemplateApi from '#/api/mall/promotion/coupon/couponTemplate';
|
import { getCouponTemplateList } from '#/api/mall/promotion/coupon/couponTemplate';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CouponDiscount,
|
CouponDiscount,
|
||||||
@@ -32,9 +32,7 @@ watch(
|
|||||||
() => props.property.couponIds,
|
() => props.property.couponIds,
|
||||||
async () => {
|
async () => {
|
||||||
if (props.property.couponIds?.length > 0) {
|
if (props.property.couponIds?.length > 0) {
|
||||||
couponList.value = await CouponTemplateApi.getCouponTemplateList(
|
couponList.value = await getCouponTemplateList(props.property.couponIds);
|
||||||
props.property.couponIds,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ function handleToggleFab() {
|
|||||||
<ElImage :src="item.imgUrl" fit="contain" class="h-7 w-7">
|
<ElImage :src="item.imgUrl" fit="contain" class="h-7 w-7">
|
||||||
<template #error>
|
<template #error>
|
||||||
<div class="flex h-full w-full items-center justify-center">
|
<div class="flex h-full w-full items-center justify-center">
|
||||||
<IconifyIcon icon="ep:picture" :color="item.textColor" />
|
<IconifyIcon icon="lucide:image" :color="item.textColor" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</ElImage>
|
</ElImage>
|
||||||
@@ -55,7 +55,7 @@ function handleToggleFab() {
|
|||||||
<!-- todo: @owen 使用APP主题色 -->
|
<!-- todo: @owen 使用APP主题色 -->
|
||||||
<ElButton type="primary" size="large" circle @click="handleToggleFab">
|
<ElButton type="primary" size="large" circle @click="handleToggleFab">
|
||||||
<IconifyIcon
|
<IconifyIcon
|
||||||
icon="ep:plus"
|
icon="lucide:plus"
|
||||||
class="transition-transform duration-300"
|
class="transition-transform duration-300"
|
||||||
:class="expanded ? 'rotate-[135deg]' : 'rotate-0'"
|
:class="expanded ? 'rotate-[135deg]' : 'rotate-0'"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ function handleActive(index: number) {
|
|||||||
activeIndex.value = index;
|
activeIndex.value = index;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
v-for="(item, index) in props.property.list"
|
v-for="(item, index) in props.property.list"
|
||||||
@@ -35,7 +34,7 @@ function handleActive(index: number) {
|
|||||||
<ElImage :src="item.imgUrl" fit="contain" class="h-full w-full">
|
<ElImage :src="item.imgUrl" fit="contain" class="h-full w-full">
|
||||||
<template #error>
|
<template #error>
|
||||||
<div class="flex h-full w-full items-center justify-center">
|
<div class="flex h-full w-full items-center justify-center">
|
||||||
<IconifyIcon icon="ep:picture" />
|
<IconifyIcon icon="lucide:image" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</ElImage>
|
</ElImage>
|
||||||
|
|||||||
Reference in New Issue
Block a user