Merge pull request 'main-zjh' (#127) from main-zjh into master
Reviewed-on: #127
This commit is contained in:
commit
1bba180bbc
@ -4,8 +4,8 @@ NODE_ENV=development
|
||||
VITE_DEV=true
|
||||
|
||||
# 请求路径
|
||||
#VITE_BASE_URL='https://zysc.fjptzykj.com'
|
||||
VITE_BASE_URL='http://192.168.1.12:6127'
|
||||
VITE_BASE_URL='https://zysc.fjptzykj.com'
|
||||
# VITE_BASE_URL='http://192.168.1.12:6127'
|
||||
|
||||
# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持 S3 服务
|
||||
VITE_UPLOAD_TYPE=server
|
||||
|
@ -0,0 +1,51 @@
|
||||
import { ComponentStyle, DiyComponent } from '@/components/DiyEditor/util'
|
||||
|
||||
/** 轮播图属性 */
|
||||
export interface CarouselProperty {
|
||||
// 类型:默认 | 卡片
|
||||
type: 'default' | 'card'
|
||||
// 指示器样式:点 | 数字
|
||||
indicator: 'dot' | 'number'
|
||||
// 是否自动播放
|
||||
autoplay: boolean
|
||||
// 播放间隔
|
||||
interval: number
|
||||
// 轮播内容
|
||||
items: CarouselItemProperty[]
|
||||
// 组件样式
|
||||
style: ComponentStyle
|
||||
}
|
||||
// 轮播内容属性
|
||||
export interface CarouselItemProperty {
|
||||
// 类型:图片 | 视频
|
||||
type: 'img' | 'video'
|
||||
// 图片链接
|
||||
imgUrl: string
|
||||
// 视频链接
|
||||
videoUrl: string
|
||||
// 跳转链接
|
||||
url: string
|
||||
}
|
||||
import logo from '@/assets/imgs/DiyEditorImges/组件图标-09.png'
|
||||
// 定义组件
|
||||
export const component = {
|
||||
id: 'CarouselLong',
|
||||
name: '长轮播图',
|
||||
// icon: 'system-uicons:carousel',
|
||||
icon: logo,
|
||||
property: {
|
||||
type: 'default',
|
||||
indicator: 'dot',
|
||||
autoplay: false,
|
||||
interval: 3,
|
||||
items: [
|
||||
{ type: 'img', imgUrl: 'https://static.iocoder.cn/mall/banner-01.jpg', videoUrl: '' },
|
||||
{ type: 'img', imgUrl: 'https://static.iocoder.cn/mall/banner-02.jpg', videoUrl: '' }
|
||||
] as CarouselItemProperty[],
|
||||
style: {
|
||||
bgType: 'color',
|
||||
bgColor: '#fff',
|
||||
marginBottom: 8
|
||||
} as ComponentStyle
|
||||
}
|
||||
} as DiyComponent<CarouselProperty>
|
@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<!-- 无图片 -->
|
||||
<div class="h-250px flex items-center justify-center bg-gray-3" v-if="property.items.length === 0">
|
||||
<Icon icon="tdesign:image" class="text-gray-8 text-120px!" />
|
||||
</div>
|
||||
<div v-else class="relative">
|
||||
<el-carousel height="calc(100vh - 50px)" :type="property.type === 'card' ? 'card' : ''"
|
||||
:autoplay="property.autoplay" :interval="property.interval * 1000"
|
||||
: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>
|
||||
<div v-if="property.indicator === 'number'"
|
||||
class="absolute bottom-10px right-10px rounded-xl bg-black p-x-8px p-y-2px text-10px text-white opacity-40">{{
|
||||
currentIndex }} / {{ property.items.length }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { CarouselProperty } from './config'
|
||||
|
||||
/** 轮播图 */
|
||||
defineOptions({ name: 'Carousel' })
|
||||
|
||||
defineProps<{ property: CarouselProperty }>()
|
||||
|
||||
const currentIndex = ref(0)
|
||||
const handleIndexChange = (index: number) => {
|
||||
currentIndex.value = index + 1
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
@ -0,0 +1,106 @@
|
||||
<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 label="default">
|
||||
<Icon icon="system-uicons:carousel" />
|
||||
</el-radio-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip class="item" content="卡片" placement="bottom">
|
||||
<el-radio-button label="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 label="dot">小圆点</el-radio>
|
||||
<el-radio label="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
|
||||
v-model="formData.interval"
|
||||
:max="10"
|
||||
:min="0.5"
|
||||
:step="0.5"
|
||||
show-input
|
||||
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">
|
||||
<Draggable v-model="formData.items" :empty-item="{ type: 'img' }">
|
||||
<template #default="{ element }">
|
||||
<el-form-item label="类型" prop="type" class="m-b-8px!" label-width="40px">
|
||||
<el-radio-group v-model="element.type">
|
||||
<el-radio label="img">图片</el-radio>
|
||||
<el-radio label="video">视频</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="图片"
|
||||
class="m-b-8px!"
|
||||
label-width="40px"
|
||||
v-if="element.type === 'img'"
|
||||
>
|
||||
<UploadImg
|
||||
v-model="element.imgUrl"
|
||||
draggable="false"
|
||||
height="80px"
|
||||
width="100%"
|
||||
class="min-w-80px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<template v-else>
|
||||
<el-form-item label="封面" class="m-b-8px!" label-width="40px">
|
||||
<UploadImg
|
||||
v-model="element.imgUrl"
|
||||
draggable="false"
|
||||
height="80px"
|
||||
width="100%"
|
||||
class="min-w-80px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="视频" class="m-b-8px!" label-width="40px">
|
||||
<UploadFile
|
||||
v-model="element.videoUrl"
|
||||
:file-type="['mp4']"
|
||||
:limit="1"
|
||||
:file-size="100"
|
||||
class="min-w-80px"
|
||||
/>
|
||||
</el-form-item>
|
||||
</template>
|
||||
<el-form-item label="链接" class="m-b-8px!" label-width="40px">
|
||||
<AppLinkInput v-model="element.url" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
</Draggable>
|
||||
</el-card>
|
||||
</el-form>
|
||||
</ComponentContainerProperty>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { CarouselProperty } from './config'
|
||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
||||
|
||||
// 轮播图属性面板
|
||||
defineOptions({ name: 'CarouselProperty' })
|
||||
|
||||
const props = defineProps<{ modelValue: CarouselProperty }>()
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
@ -3,7 +3,7 @@ import {ComponentStyle, DiyComponent} from '@/components/DiyEditor/util'
|
||||
/** 积分商城属性 */
|
||||
export interface PromotionPointProperty {
|
||||
// 布局类型:单列 | 三列
|
||||
layoutType: 'oneColBigImg' | 'oneColSmallImg' | 'twoCol'
|
||||
layoutType: 'oneColBigImg' | 'oneColSmallImg' | 'twoCol' | 'oneColSwiper'
|
||||
// 商品字段
|
||||
fields: {
|
||||
// 商品名称
|
||||
|
@ -1,90 +1,66 @@
|
||||
<template>
|
||||
<div ref="containerRef" :class="`box-content min-h-30px w-full flex flex-row flex-wrap`">
|
||||
<div
|
||||
v-for="(spu, index) in spuList"
|
||||
:key="index"
|
||||
:style="{
|
||||
...calculateSpace(index),
|
||||
...calculateWidth(),
|
||||
borderTopLeftRadius: `${property.borderRadiusTop}px`,
|
||||
borderTopRightRadius: `${property.borderRadiusTop}px`,
|
||||
borderBottomLeftRadius: `${property.borderRadiusBottom}px`,
|
||||
borderBottomRightRadius: `${property.borderRadiusBottom}px`
|
||||
}"
|
||||
class="relative box-content flex flex-row flex-wrap overflow-hidden bg-white"
|
||||
>
|
||||
<div ref="containerRef" class="box-content min-h-30px w-full flex flex-row flex-wrap"
|
||||
:class="{ 'one-Col-swiper': property.layoutType === 'oneColSwiper' }">
|
||||
<div v-for="(spu, index) in spuList" :key="index" :style="{
|
||||
...calculateSpace(index),
|
||||
...calculateWidth(),
|
||||
borderTopLeftRadius: `${property.borderRadiusTop}px`,
|
||||
borderTopRightRadius: `${property.borderRadiusTop}px`,
|
||||
borderBottomLeftRadius: `${property.borderRadiusBottom}px`,
|
||||
borderBottomRightRadius: `${property.borderRadiusBottom}px`
|
||||
}" class="relative box-content flex flex-row flex-wrap overflow-hidden bg-white"
|
||||
:class="{ 'one-Col-swiper-item': property.layoutType === 'oneColSwiper' }">
|
||||
<!-- 角标 -->
|
||||
<div v-if="property.badge.show" class="absolute left-0 top-0 z-1 items-center justify-center">
|
||||
<el-image :src="property.badge.imgUrl" class="h-26px w-38px" fit="cover" />
|
||||
</div>
|
||||
<!-- 商品封面图 -->
|
||||
<div
|
||||
:class="[
|
||||
'h-140px',
|
||||
{
|
||||
'w-full': property.layoutType !== 'oneColSmallImg',
|
||||
'w-140px': property.layoutType === 'oneColSmallImg'
|
||||
}
|
||||
]"
|
||||
>
|
||||
<div :class="[
|
||||
'h-140px',
|
||||
{
|
||||
'w-full': property.layoutType !== 'oneColSmallImg',
|
||||
'w-140px': property.layoutType === 'oneColSmallImg'
|
||||
}
|
||||
]">
|
||||
<el-image :src="spu.picUrl" class="h-full w-full" fit="cover" />
|
||||
</div>
|
||||
<div
|
||||
:class="[
|
||||
' flex flex-col gap-8px p-8px box-border',
|
||||
{
|
||||
'w-full': property.layoutType !== 'oneColSmallImg',
|
||||
'w-[calc(100%-140px-16px)]': property.layoutType === 'oneColSmallImg'
|
||||
}
|
||||
]"
|
||||
>
|
||||
<div v-if="property.layoutType !== 'oneColSwiper'" :class="[
|
||||
' flex flex-col gap-8px p-8px box-border',
|
||||
{
|
||||
'w-full': property.layoutType !== 'oneColSmallImg',
|
||||
'w-[calc(100%-140px-16px)]': property.layoutType === 'oneColSmallImg'
|
||||
}
|
||||
]">
|
||||
<!-- 商品名称 -->
|
||||
<div
|
||||
v-if="property.fields.name.show"
|
||||
:class="[
|
||||
'text-14px ',
|
||||
{
|
||||
truncate: property.layoutType !== 'oneColSmallImg',
|
||||
'overflow-ellipsis line-clamp-2': property.layoutType === 'oneColSmallImg'
|
||||
}
|
||||
]"
|
||||
:style="{ color: property.fields.name.color }"
|
||||
>
|
||||
<div v-if="property.fields.name.show" :class="[
|
||||
'text-14px ',
|
||||
{
|
||||
truncate: property.layoutType !== 'oneColSmallImg',
|
||||
'overflow-ellipsis line-clamp-2': property.layoutType === 'oneColSmallImg'
|
||||
}
|
||||
]" :style="{ color: property.fields.name.color }">
|
||||
{{ spu.name }}
|
||||
</div>
|
||||
<!-- 商品简介 -->
|
||||
<div
|
||||
v-if="property.fields.introduction.show"
|
||||
:style="{ color: property.fields.introduction.color }"
|
||||
class="truncate text-12px"
|
||||
>
|
||||
<div v-if="property.fields.introduction.show" :style="{ color: property.fields.introduction.color }"
|
||||
class="truncate text-12px">
|
||||
{{ spu.introduction }}
|
||||
</div>
|
||||
<div>
|
||||
<!-- 积分 -->
|
||||
<span
|
||||
v-if="property.fields.price.show"
|
||||
:style="{ color: property.fields.price.color }"
|
||||
class="text-16px"
|
||||
>
|
||||
<span v-if="property.fields.price.show" :style="{ color: property.fields.price.color }" class="text-16px">
|
||||
{{ spu.point }}积分
|
||||
{{ !spu.pointPrice || spu.pointPrice === 0 ? '' : `+${fenToYuan(spu.pointPrice)}元` }}
|
||||
</span>
|
||||
<!-- 市场价 -->
|
||||
<span
|
||||
v-if="property.fields.marketPrice.show && spu.marketPrice"
|
||||
:style="{ color: property.fields.marketPrice.color }"
|
||||
class="ml-4px text-10px line-through"
|
||||
>
|
||||
<span v-if="property.fields.marketPrice.show && spu.marketPrice"
|
||||
:style="{ color: property.fields.marketPrice.color }" class="ml-4px text-10px line-through">
|
||||
¥{{ fenToYuan(spu.marketPrice) }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="text-12px">
|
||||
<!-- 销量 -->
|
||||
<span
|
||||
v-if="property.fields.salesCount.show"
|
||||
:style="{ color: property.fields.salesCount.color }"
|
||||
>
|
||||
<span v-if="property.fields.salesCount.show" :style="{ color: property.fields.salesCount.color }">
|
||||
已兑{{ (spu.pointTotalStock || 0) - (spu.pointStock || 0) }}件
|
||||
</span>
|
||||
<!-- 库存 -->
|
||||
@ -94,24 +70,15 @@
|
||||
</div>
|
||||
</div>
|
||||
<!-- 购买按钮 -->
|
||||
<div class="absolute bottom-8px right-8px">
|
||||
<div class="absolute bottom-8px right-8px" v-if="property.layoutType !== 'oneColSwiper'">
|
||||
<!-- 文字按钮 -->
|
||||
<span
|
||||
v-if="property.btnBuy.type === 'text'"
|
||||
:style="{
|
||||
background: `linear-gradient(to right, ${property.btnBuy.bgBeginColor}, ${property.btnBuy.bgEndColor}`
|
||||
}"
|
||||
class="rounded-full p-x-12px p-y-4px text-12px text-white"
|
||||
>
|
||||
<span v-if="property.btnBuy.type === 'text'" :style="{
|
||||
background: `linear-gradient(to right, ${property.btnBuy.bgBeginColor}, ${property.btnBuy.bgEndColor}`
|
||||
}" class="rounded-full p-x-12px p-y-4px text-12px text-white">
|
||||
{{ property.btnBuy.text }}
|
||||
</span>
|
||||
<!-- 图片按钮 -->
|
||||
<el-image
|
||||
v-else
|
||||
:src="property.btnBuy.imgUrl"
|
||||
class="h-28px w-28px rounded-full"
|
||||
fit="cover"
|
||||
/>
|
||||
<el-image v-else :src="property.btnBuy.imgUrl" class="h-28px w-28px rounded-full" fit="cover" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -199,4 +166,17 @@ const calculateWidth = () => {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
<style lang="scss" scoped>
|
||||
.one-Col-swiper {
|
||||
flex-wrap: nowrap;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.one-Col-swiper-item {
|
||||
width: 130px !important;
|
||||
min-width: 130px;
|
||||
height: 130px;
|
||||
margin-left: 8px !important;
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
</style>
|
||||
|
@ -22,6 +22,11 @@
|
||||
<Icon icon="fluent:text-column-two-24-filled" />
|
||||
</el-radio-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip class="item" content="单行滑动" placement="bottom">
|
||||
<el-radio-button value="oneColSwiper">
|
||||
<Icon icon="system-uicons:carousel" />
|
||||
</el-radio-button>
|
||||
</el-tooltip>
|
||||
<!--<el-tooltip class="item" content="三列" placement="bottom">
|
||||
<el-radio-button value="threeCol">
|
||||
<Icon icon="fluent:text-column-three-24-filled" />
|
||||
@ -104,34 +109,16 @@
|
||||
</el-card>
|
||||
<el-card class="property-group" header="商品样式" shadow="never">
|
||||
<el-form-item label="上圆角" prop="borderRadiusTop">
|
||||
<el-slider
|
||||
v-model="formData.borderRadiusTop"
|
||||
:max="100"
|
||||
:min="0"
|
||||
:show-input-controls="false"
|
||||
input-size="small"
|
||||
show-input
|
||||
/>
|
||||
<el-slider v-model="formData.borderRadiusTop" :max="100" :min="0" :show-input-controls="false"
|
||||
input-size="small" show-input />
|
||||
</el-form-item>
|
||||
<el-form-item label="下圆角" prop="borderRadiusBottom">
|
||||
<el-slider
|
||||
v-model="formData.borderRadiusBottom"
|
||||
:max="100"
|
||||
:min="0"
|
||||
:show-input-controls="false"
|
||||
input-size="small"
|
||||
show-input
|
||||
/>
|
||||
<el-slider v-model="formData.borderRadiusBottom" :max="100" :min="0" :show-input-controls="false"
|
||||
input-size="small" show-input />
|
||||
</el-form-item>
|
||||
<el-form-item label="间隔" prop="space">
|
||||
<el-slider
|
||||
v-model="formData.space"
|
||||
:max="100"
|
||||
:min="0"
|
||||
:show-input-controls="false"
|
||||
input-size="small"
|
||||
show-input
|
||||
/>
|
||||
<el-slider v-model="formData.space" :max="100" :min="0" :show-input-controls="false" input-size="small"
|
||||
show-input />
|
||||
</el-form-item>
|
||||
</el-card>
|
||||
</el-form>
|
||||
|
@ -0,0 +1,57 @@
|
||||
import { ComponentStyle, DiyComponent } from '@/components/DiyEditor/util'
|
||||
|
||||
/** 富文本属性 */
|
||||
export interface RichtextProperty {
|
||||
richText: string
|
||||
borderRadius: number
|
||||
// 上圆角
|
||||
borderRadiusTop: number
|
||||
// 下圆角
|
||||
borderRadiusBottom: number
|
||||
// 间隔
|
||||
space: number
|
||||
// 组件样式
|
||||
style: ComponentStyle
|
||||
// 宽
|
||||
width: number
|
||||
// 高
|
||||
height: number
|
||||
// 上
|
||||
top: number
|
||||
// 左
|
||||
left: number
|
||||
|
||||
|
||||
}
|
||||
|
||||
import logo from '@/assets/imgs/DiyEditorImges/组件图标-20.png'
|
||||
|
||||
export const plugins = [
|
||||
'advlist anchor autolink autosave code codesample directionality emoticons fullscreen hr image imagetools insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textpattern visualblocks visualchars wordcount',
|
||||
];
|
||||
export const toolbar = [
|
||||
'code searchreplace bold italic underline strikethrough alignleft aligncenter alignright outdent indent blockquote removeformat subscript superscript codesample hr bullist numlist link image charmap preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor fullscreen',
|
||||
];
|
||||
|
||||
// 定义组件
|
||||
export const component = {
|
||||
id: 'Richtext',
|
||||
name: '富文本',
|
||||
icon: logo,
|
||||
property: {
|
||||
borderRadius: 0,
|
||||
borderRadiusTop: 0,
|
||||
borderRadiusBottom: 0,
|
||||
space: 0,
|
||||
richText: '哈哈',
|
||||
width: 100,
|
||||
height: 100,
|
||||
top: 0,
|
||||
left: 0,
|
||||
style: {
|
||||
bgType: 'color',
|
||||
bgColor: '#fff',
|
||||
marginBottom: 8
|
||||
} as ComponentStyle
|
||||
}
|
||||
} as DiyComponent<RichtextProperty>
|
@ -0,0 +1,202 @@
|
||||
<template>
|
||||
<div class="mobile-page">
|
||||
<div class="box" :style="boxStyle" v-html="props.property.richText"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { RichtextProperty } from './config'
|
||||
|
||||
defineOptions({ name: 'Richtext' })
|
||||
const props = defineProps<{ property: RichtextProperty }>()
|
||||
const boxStyle = computed(() => {
|
||||
return [
|
||||
{ 'border-radius': props.property.borderRadius ? props.property.borderRadius + 'px' : '0' },
|
||||
{
|
||||
background: `linear-gradient(${props.property.style.bgColor}, ${props.property.style.bgColor})`,
|
||||
},
|
||||
{ margin: props.property.style.marginTop + 'px' + ' ' + props.property.style.marginRight + 'px' + ' ' + props.property.style.marginBottom + 'px' + ' ' + props.property.style.marginLeft + 'px' + ' ' },
|
||||
];
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.mobile-page {
|
||||
width: 100% !important;
|
||||
min-height: 100px;
|
||||
}
|
||||
|
||||
.box {
|
||||
width: 100%;
|
||||
min-height: 100px;
|
||||
padding: 10px;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
<!-- <template>
|
||||
<div class="mobile-page" v-if="configObj">
|
||||
<div class="box" :style="boxStyle" v-html="richText"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// +----------------------------------------------------------------------
|
||||
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: CRMEB Team <admin@crmeb.com>
|
||||
// +----------------------------------------------------------------------
|
||||
import { mapState, mapMutations } from 'vuex';
|
||||
export default {
|
||||
name: 'z_ueditor',
|
||||
cname: '富文本',
|
||||
configName: 'c_ueditor_box',
|
||||
icon: 't-icon-zujian-fuwenben',
|
||||
type: 2, // 0 基础组件 1 营销组件 2工具组件
|
||||
defaultName: 'richTextEditor', // 外面匹配名称
|
||||
props: {
|
||||
index: {
|
||||
type: null,
|
||||
default: -1,
|
||||
},
|
||||
num: {
|
||||
type: null,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapState('mobildConfig', ['defaultArray']),
|
||||
//外部盒子
|
||||
boxStyle() {
|
||||
return [
|
||||
{ 'border-radius': this.configObj.bgStyle.val ? this.configObj.bgStyle.val + 'px' : '0' },
|
||||
{
|
||||
background: `linear-gradient(${this.configObj.bgColor.color[0].item}, ${this.configObj.bgColor.color[1].item})`,
|
||||
},
|
||||
{ margin: this.configObj.mbConfig.val + 'px' + ' ' + this.configObj.lrConfig.val + 'px' + ' ' + 0 },
|
||||
];
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
pageData: {
|
||||
handler(nVal, oVal) {
|
||||
this.setConfig(nVal);
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
num: {
|
||||
handler(nVal, oVal) {
|
||||
let data = this.$store.state.mobildConfig.defaultArray[nVal];
|
||||
this.setConfig(data);
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
defaultArray: {
|
||||
handler(nVal, oVal) {
|
||||
let data = this.$store.state.mobildConfig.defaultArray[this.num];
|
||||
this.setConfig(data);
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 默认初始化数据禁止修改
|
||||
defaultConfig: {
|
||||
name: 'richTextEditor',
|
||||
timestamp: this.num,
|
||||
setUp: {
|
||||
tabVal: 0,
|
||||
cname: '富文本',
|
||||
},
|
||||
// 背景颜色
|
||||
bgColor: {
|
||||
title: '背景颜色',
|
||||
tabTitle: '颜色设置',
|
||||
color: [
|
||||
{
|
||||
item: '#FFFFFF',
|
||||
},
|
||||
{
|
||||
item: '#FFFFFF',
|
||||
},
|
||||
],
|
||||
default: [
|
||||
{
|
||||
item: '#FFFFFF',
|
||||
},
|
||||
{
|
||||
item: '#FFFFFF',
|
||||
},
|
||||
],
|
||||
},
|
||||
lrConfig: {
|
||||
title: '左右边距',
|
||||
tabTitle: '边距设置',
|
||||
val: 12,
|
||||
min: 0,
|
||||
},
|
||||
mbConfig: {
|
||||
title: '页面间距',
|
||||
val: 10,
|
||||
min: 0,
|
||||
},
|
||||
bgStyle: {
|
||||
tabTitle: '圆角设置',
|
||||
title: '背景圆角',
|
||||
name: 'bgStyle',
|
||||
val: 0,
|
||||
min: 0,
|
||||
max: 30,
|
||||
},
|
||||
richText: {
|
||||
tabTitle: '富文本内容',
|
||||
val: '',
|
||||
},
|
||||
},
|
||||
pageData: {},
|
||||
richText: '',
|
||||
configObj: null,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
if (this.num) {
|
||||
this.pageData = this.$store.state.mobildConfig.defaultArray[this.num];
|
||||
this.setConfig(this.pageData);
|
||||
}
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
setConfig(data) {
|
||||
if (!data) return;
|
||||
if (data) {
|
||||
this.configObj = data;
|
||||
this.richText = data.richText.val;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.mobile-page ::v-deep video {
|
||||
width: 100% !important;
|
||||
}
|
||||
.box {
|
||||
min-height: 100px;
|
||||
padding: 10px;
|
||||
background: #f5f5f5;
|
||||
::v-deep img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
-->
|
@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<ComponentContainerProperty v-model="formData.style">
|
||||
<!-- 表单 -->
|
||||
<el-form label-width="80px" :model="formData" class="m-t-8px">
|
||||
<!-- <el-text tag="p"> 富文本设置 </el-text> -->
|
||||
<el-form-item label-width="0" prop="richText">
|
||||
<Editor api-key="ooit62s6gekozi4cmblbsvdwhl34mxcgrkzu4wr8yqmsqxmw" v-model="formData.richText"
|
||||
:init="tinymceConfig" initial-value="Welcome to TinyMCE!" />
|
||||
</el-form-item>
|
||||
<!-- <el-text type="info" size="small"> 每格尺寸187 * 187 </el-text> -->
|
||||
<!-- <textarea :id="tinymceId" class="textarea" /> -->
|
||||
</el-form>
|
||||
</ComponentContainerProperty>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useVModel } from '@vueuse/core'
|
||||
import { RichtextProperty } from './config';
|
||||
// import { MagicCubeProperty } from '@/components/DiyEditor/components/mobile/MagicCube/config'
|
||||
// import { plugins, toolbar } from './config';
|
||||
// import loadTinymce from '@/utils/loadTinymce';
|
||||
// import { debounceNew } from '@/utils';
|
||||
|
||||
/** 富文本属性面板 */
|
||||
defineOptions({ name: 'RichtextProperty' });
|
||||
|
||||
const props = defineProps<{ modelValue: RichtextProperty }>();
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
const formData = useVModel(props, 'modelValue', emit);
|
||||
|
||||
|
||||
|
||||
const tinymceConfig = {
|
||||
toolbar_mode: 'sliding',
|
||||
plugins: [
|
||||
// Core editing features
|
||||
'anchor', 'autolink', 'charmap', 'codesample', 'emoticons', 'image', 'link', 'lists', 'media', 'searchreplace', 'table', 'visualblocks', 'wordcount',
|
||||
// Your account includes a free trial of TinyMCE premium features
|
||||
// Try the most popular premium features until Apr 24, 2025:
|
||||
'checklist', 'mediaembed', 'casechange', 'formatpainter', 'pageembed', 'a11ychecker', 'tinymcespellchecker', 'permanentpen', 'powerpaste', 'advtable', 'advcode', 'editimage', 'advtemplate', 'ai', 'mentions', 'tinycomments', 'tableofcontents', 'footnotes', 'mergetags', 'autocorrect', 'typography', 'inlinecss', 'markdown', 'importword', 'exportword', 'exportpdf'
|
||||
],
|
||||
toolbar: 'undo redo | blocks fontfamily fontsize | bold italic underline strikethrough | link image media table mergetags | addcomment showcomments | spellcheckdialog a11ycheck typography | align lineheight | checklist numlist bullist indent outdent | emoticons charmap | removeformat',
|
||||
tinycomments_mode: 'embedded',
|
||||
tinycomments_author: 'Author name',
|
||||
mergetags_list: [
|
||||
{ value: 'First.Name', title: 'First Name' },
|
||||
{ value: 'Email', title: 'Email' },
|
||||
],
|
||||
ai_request: (request, respondWith) => respondWith.string(() => Promise.reject('See docs to implement AI Assistant')),
|
||||
};
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
@ -76,7 +76,7 @@ export interface PageConfig {
|
||||
components: PageComponent[]
|
||||
}
|
||||
// 页面组件,只保留组件ID,组件属性
|
||||
export interface PageComponent extends Pick<DiyComponent<any>, 'id' | 'property'> {}
|
||||
export interface PageComponent extends Pick<DiyComponent<any>, 'id' | 'property'> { }
|
||||
|
||||
// 属性表单监听
|
||||
export function usePropertyForm<T>(modelValue: T, emit: Function): { formData: Ref<T> } {
|
||||
@ -119,7 +119,7 @@ export const PAGE_LIBS = [
|
||||
'MenuList',
|
||||
'Popover',
|
||||
'FloatingActionButton',
|
||||
"MenuGridTow"
|
||||
"MenuGridTow"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -128,11 +128,13 @@ export const PAGE_LIBS = [
|
||||
components: [
|
||||
'ImageBar',
|
||||
'Carousel',
|
||||
'CarouselLong',
|
||||
'TitleBar',
|
||||
'VideoPlayer',
|
||||
'Divider',
|
||||
'MagicCube',
|
||||
'HotZone'
|
||||
'HotZone',
|
||||
'Richtext'
|
||||
]
|
||||
},
|
||||
{ name: '商品组件', extended: true, components: ['ProductCard', 'ProductList'] },
|
||||
|
@ -87,7 +87,7 @@
|
||||
// (page: DiyPageApi.DiyPageVO) => page.name === templateItems[1].name
|
||||
// )
|
||||
currentFormData.value = formData.value!.pages[selectedTemplateItem.value]
|
||||
console.log(selectedTemplateItem.value, 'selectedTemplateItem.value11111111111111')
|
||||
// console.log(selectedTemplateItem.value, 'selectedTemplateItem.value11111111111111')
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
|
@ -0,0 +1,137 @@
|
||||
<template>
|
||||
<div class="main">
|
||||
<div class="mainTop">
|
||||
<div>商品分类</div>
|
||||
<div class="right">
|
||||
<div class="save" @click="save">保存</div>
|
||||
<div class="cz" @click="cz">重置</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mainBottom">
|
||||
<div class='item' :class="currItem == 1 ? 'on' : ''" @click="clickItem(1)">
|
||||
<img class="img"
|
||||
src="https://zysc.fjptzykj.com/admin-api/infra/file/25/get/9349d776503051646314323aced460314415a04c943dc220e9e5b86362f864dd.png" />
|
||||
<div class="text">样式1</div>
|
||||
</div>
|
||||
<!-- <div class='item' :class="currItem == 2 ? 'on' : ''" @click="clickItem(2)">
|
||||
<img class="img"
|
||||
src="https://zysc.fjptzykj.com/admin-api/infra/file/25/get/909ef630cff7cdc957962ae782ee9a882c86c35732140b1c9e013341fa559126.jpg" />
|
||||
<div class="text">样式2</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
// TODO @疯狂:要不要建个 decorate 目录,然后挪进去,改成 index.vue,这样可以更明确看到是个独立界面哈,更好找
|
||||
import * as DiyTemplateApi from '@/api/mall/promotion/diy/template'
|
||||
import { useTagsdivStore } from '@/store/modules/tagsdiv'
|
||||
import { DiyComponentLibrary, PAGE_LIBS } from '@/components/DiyEditor/util' // 商城的 DIY 组件,在 DiyEditor 目录下
|
||||
import { toNumber } from 'lodash-es'
|
||||
const message = useMessage() // 消息弹窗
|
||||
const currItem = ref();
|
||||
|
||||
function clickItem(val) {
|
||||
currItem.value = val;
|
||||
}
|
||||
|
||||
function cz(val) {
|
||||
currItem.value = 1;
|
||||
}
|
||||
|
||||
function save() {
|
||||
setProjuctClass(currItem.value);
|
||||
// console.log("请求接口还没有写啊!!!!快让后端提供啊")
|
||||
}
|
||||
|
||||
// 设置商品分类
|
||||
const setProjuctClass = async (id) => {
|
||||
// const res = await DiyTemplateApi.setDiyProjuctClass(id);
|
||||
// console.log(res, "sssss");
|
||||
// if (res) {
|
||||
message.success('保存成功')
|
||||
// }
|
||||
}
|
||||
|
||||
// 获取商品分类
|
||||
const getProjuctClass = async () => {
|
||||
const res = await DiyTemplateApi.getDiyProjuctClass();
|
||||
currItem.value = res
|
||||
}
|
||||
getProjuctClass()
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.main {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.mainTop {
|
||||
width: 100%;
|
||||
margin: 10px 10px;
|
||||
background: white;
|
||||
padding: 10px 20px;
|
||||
font-weight: 700;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.right {
|
||||
display: flex;
|
||||
|
||||
div {
|
||||
padding: 5px 15px;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.save {
|
||||
background: #0256FF;
|
||||
margin-right: 10px;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.cz {
|
||||
border: 1px solid #cccccc;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mainBottom {
|
||||
width: 100%;
|
||||
margin: 10px 10px;
|
||||
background: white;
|
||||
padding: 30px 30px;
|
||||
display: flex;
|
||||
|
||||
.item {
|
||||
margin-right: 20px;
|
||||
text-align: center;
|
||||
border-radius: 12px;
|
||||
cursor: pointer;
|
||||
|
||||
&.on {
|
||||
.text {
|
||||
color: #0256ff;
|
||||
}
|
||||
|
||||
.img {
|
||||
border: 2px solid #0256ff;
|
||||
}
|
||||
}
|
||||
|
||||
.img {
|
||||
width: 260px;
|
||||
border-radius: 12px;
|
||||
border: 2px solid white;
|
||||
}
|
||||
|
||||
.text {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
Loading…
x
Reference in New Issue
Block a user