1.优惠券开发

sws 2024-08-28
v1.0.0
sws 2024-08-28 16:24:23 +08:00
parent d4c8cb1b9e
commit 9661fab9ef
19 changed files with 464 additions and 6 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -29,7 +29,6 @@ interface Props {
const props = withDefaults(defineProps<Props>(), {
imgParams: 'cover',
isouterClick: false,
});
const drag_list = ref(props.list);

View File

@ -0,0 +1,145 @@
<template>
<el-dialog v-model="dialog_visible" class="radius-lg" width="1168" draggable append-to-body @close="close_event">
<template #header>
<div class="title center re">
<div class="tc size-16 fw">主题选择</div>
</div>
</template>
<div class="content pa-20 flex-row">
<div class="img-content">
<el-scrollbar height="480px" class="w">
<el-row v-if="data.length > 0">
<el-col v-for="(item, index) in data" :key="index" :span="8">
<div class="pa-10">
<div class="item plr-10 ptb-20 br-c radius-md tc flex-col jc-c gap-10" :class="{ active: item.name === temp_model_value }" @click="handle_select_theme(item)">
<image-empty v-model="item.url" class="img-height-auto"></image-empty>
</div>
</div>
</el-col>
</el-row>
<div v-else>
<no-data height="500"></no-data>
</div>
</el-scrollbar>
</div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button class="plr-28 ptb-10" @click="close_event"></el-button>
<el-button class="plr-28 ptb-10" type="primary" @click="confirm_event"></el-button>
</span>
</template>
</el-dialog>
<div class="flex-row align-c gap-10 br-d radius-sm plr-11 theme-input" @click="dialog_visible = true">
<div class="flex-1 flex-width size-12 text-line-1">
<text v-if="model_value">{{ model_value }}</text>
<text v-else class="cr-9">{{ placeholder }}</text>
</div>
<div class="theme-icon">
<template v-if="!model_value">
<icon name="arrow-right" size="12" color="9"></icon>
</template>
<template v-else>
<div @click.stop="clear_model_value">
<icon name="close-o" size="12" color="c"></icon>
</div>
</template>
</div>
</div>
</template>
<script setup lang="ts">
import { cloneDeep } from 'lodash';
interface data {
name: string;
url: string;
}
const props = defineProps({
placeholder: {
type: String,
default: '请选择主题',
},
data: {
type: Array as PropType<data[]>,
default: () => [],
},
});
const model_value = defineModel({ type: String, default: '' });
const { data } = toRefs(props);
const dialog_visible = ref(false);
watch(
() => dialog_visible.value,
(val) => {
if (val) {
temp_model_value.value = cloneDeep(model_value.value);
}
}
);
//#region ---------------------------------------------------start
const temp_model_value = ref('');
const handle_select_theme = (data: data) => {
temp_model_value.value = data.name;
};
//#endregion ---------------------------------------------------end
//
const close_event = () => {
dialog_visible.value = false;
};
//
const confirm_event = () => {
if (temp_model_value.value !== null) {
model_value.value = temp_model_value.value;
close_event();
} else {
ElMessage.error('请先选择主题');
}
};
//
const clear_model_value = () => {
temp_model_value.value = '';
model_value.value = '';
};
</script>
<style lang="scss" scoped>
.content {
.img-content {
margin: 0 -1rem;
width: calc(100% + 2rem);
.item {
height: 22rem;
transition: all 0.3s ease-in-out;
background-color: #f4f4f4;
&:hover {
color: $cr-primary;
border-color: $cr-primary;
position: relative;
scale: 1.02;
}
&.active {
color: $cr-primary;
border-color: $cr-primary;
position: relative;
scale: 1.02;
}
.img-height-auto {
height: auto;
}
}
}
}
.theme-input {
width: 100%;
height: 3.2rem;
line-height: 3.2rem;
cursor: pointer;
position: relative;
.theme-icon {
position: absolute;
right: 0;
width: 3.4rem;
z-index: 1;
text-align: center;
}
}
</style>

View File

@ -361,8 +361,8 @@ const all_tree = {
};
const type_data_list = ref<Tree[]>([]);
//
const get_tree = () => {
if (!upload_store.is_upload_api && upload_store.category.length === 0) {
const get_tree = (bool: boolean = false) => {
if ((!upload_store.is_upload_api && upload_store.category.length === 0) || bool) {
upload_store.set_is_upload_api(true);
UploadAPI.getTree()
.then((res) => {
@ -403,7 +403,7 @@ const add_type = () => {
};
//
const upload_category_confirm = () => {
get_tree();
get_tree(true);
};
const category_id = ref('');
//

View File

@ -0,0 +1,32 @@
<template>
<div :style="style_container">
<div class="content re" :style="style_content"></div>
</div>
</template>
<script setup lang="ts">
import { common_styles_computer } from '@/utils';
const props = defineProps({
value: {
type: Object,
default: () => ({}),
},
});
const style_content = ref('');
const style_container = ref('');
const content = ref('');
watch(
props.value,
(newVal, oldValue) => {
const new_content = newVal?.content || {};
const new_style = newVal?.style || {};
style_container.value = common_styles_computer(new_style.common_style);
},
{ immediate: true, deep: true }
);
</script>
<style lang="scss" scoped>
.content {
height: 22rem;
}
</style>

View File

@ -0,0 +1,90 @@
<template>
<div class="content">
<el-form :model="form" label-width="70" class="m-h">
<card-container class="mb-8">
<div class="mb-12">展示设置</div>
<el-form-item label="选择风格">
<theme-select v-model="form.theme" :data="base_list.themeList"></theme-select>
</el-form-item>
</card-container>
<card-container class="common-content-height">
<div class="mb-12">优惠券数据</div>
<el-form-item label="数据来源">
<el-radio-group v-model="form.data_type">
<el-radio v-for="item in base_list.data_type_list" :key="item.value" :value="item.value">{{ item.name }}</el-radio>
</el-radio-group>
</el-form-item>
<template v-if="form.data_type === '0'">
<el-form-item label="手动选择">
<div class="flex-col gap-20 w">
<drag v-if="form.data_list.length > 0" :data="form.data_list" :space-col="20" @remove="remove" @on-sort="on_sort">
<template #default="{ row }">
<div class="flex-1 cr-6 size-12">{{ row.title }}</div>
</template>
</drag>
<el-button class="w" @click="add">+</el-button>
</div>
</el-form-item>
</template>
<template v-else>
<slider></slider>
</template>
<el-form-item label="内容标题">
<el-input v-model="form.title" placeholder="请输入内容"></el-input>
</el-form-item>
<el-form-item label="内容描述">
<el-input v-model="form.desc" type="textarea" :rows="4" placeholder="请输入内容"></el-input>
</el-form-item>
</card-container>
</el-form>
</div>
<url-value-dialog v-model:dialog-visible="url_value_dialog_visible" :type="['article']" multiple @update:model-value="url_value_dialog_call_back"></url-value-dialog>
</template>
<script setup lang="ts">
const props = defineProps({
value: {
type: Object,
default: () => {},
},
});
const form = reactive(props.value);
const url_value_dialog_visible = ref(false);
const base_list = reactive({
data_type_list: [
{ name: '默认', value: '0' },
{ name: '手动', value: '1' },
],
themeList: [
{ name: '风格1', url: new URL(`../../assets/images/components/model-coupon/theme-1.png`, import.meta.url).href },
{ name: '风格2', url: new URL(`../../assets/images/components/model-coupon/theme-2.png`, import.meta.url).href },
{ name: '风格3', url: new URL(`../../assets/images/components/model-coupon/theme-3.png`, import.meta.url).href },
{ name: '风格3', url: new URL(`../../assets/images/components/model-coupon/theme-3.png`, import.meta.url).href },
{ name: '风格4', url: new URL(`../../assets/images/components/model-coupon/theme-4.png`, import.meta.url).href },
{ name: '风格5', url: new URL(`../../assets/images/components/model-coupon/theme-5.png`, import.meta.url).href },
{ name: '风格6', url: new URL(`../../assets/images/components/model-coupon/theme-6.png`, import.meta.url).href },
{ name: '风格7', url: new URL(`../../assets/images/components/model-coupon/theme-7.png`, import.meta.url).href },
],
});
const remove = (index: number) => {
form.data_list.splice(index, 1);
};
const on_sort = (item: any) => {
form.data_list = item;
};
const add = () => {
url_value_dialog_visible.value = true;
};
const url_value_dialog_call_back = (item: any[]) => {
item.forEach((child: any) => {
form.data_list.push(child);
});
};
</script>
<style lang="scss" scoped>
.content {
width: 100%;
.content-height {
min-height: calc(100vh - 31.8rem);
}
}
</style>

View File

@ -0,0 +1,118 @@
<template>
<div class="setting-content">
<template v-if="type == '1'">
<model-coupon-content :value="value.content" :styles="value.style" :default-config="default_config"></model-coupon-content>
</template>
<template v-else-if="type == '2'">
<model-coupon-styles :value="value.style" :content="value.content" :default-config="default_config"></model-coupon-styles>
</template>
</div>
</template>
<script setup lang="ts">
const props = defineProps({
type: {
type: String,
default: '1',
},
value: {
type: Object,
default: () => {},
},
});
const default_config = {
style: {
theme_1: {
price_color: '#FF3830',
name_color: '#FF3830',
// 1
desc_color: '#FF3830',
// 1
limit_send_count: '#999',
btn_background: [{ color: '#FF3830', color_percentage: '' }],
btn_color: '#FF3830',
background: [{ color: '#FFF1E1', color_percentage: '' }],
spacing: 20,
},
theme_2: {
price_color: '#FF3830',
name_color: '#FF3830',
desc_color: '#999',
// 2
limit_send_count: '#999',
btn_background: [{ color: '#FFF1E1', color_percentage: '' }],
btn_color: '#fff',
// 2
background: [{ color: '#fff', color_percentage: '' }],
spacing: 15,
common_style: {
background_img_url: [{ url: 'http://shopxo.com/static/upload/images/common/2024/08/28/1724815957918121.png' }],
},
},
theme_3: {
price_color: '#FF3830',
name_color: '#FF3830',
desc_color: '#FF3830',
limit_send_count: '#999',
btn_background: [{ color: '#FF3830', color_percentage: '' }],
btn_color: '#FF3830',
background: [{ color: '#FFF1E1', color_percentage: '' }],
// 34
spacing: 0,
},
theme_4: {
price_color: '#FF3830',
name_color: '#FF3830',
desc_color: '#333',
// 4
limit_send_count: '#999',
btn_background: [{ color: '#fff', color_percentage: '' }],
btn_color: '#FF3830',
background: [{ color: '#FFF1E1', color_percentage: '' }],
// 34
spacing: 0,
},
theme5: {
price_color: '#FF3830',
name_color: '#FF3830',
// 5
desc_color: '#333',
// 5
limit_send_count: '#999',
// 5
btn_background: [{ color: '#fff', color_percentage: '' }],
btn_color: '#fff',
background: [{ color: '#FFF1E1', color_percentage: '' }],
spacing: 10,
},
theme6: {
price_color: '#FF3830',
name_color: '#666',
// 6
desc_color: '#333',
// 6
limit_send_count: '#999',
// 6
btn_background: [{ color: '#fff', color_percentage: '' }],
btn_color: '#fff',
background: [{ color: '#FF3830', color_percentage: '' }],
spacing: 10,
},
theme7: {
price_color: '#fff',
name_color: '#fff',
// 7
desc_color: '#fff',
// 7
limit_send_count: '#999',
// 7
btn_background: [{ color: '#E1B47A', color_percentage: '' }],
btn_color: '#fff',
background: [
{ color: '#FF3830', color_percentage: '' },
{ color: '#E1B47A', color_percentage: '' },
],
spacing: 10,
},
},
};
</script>

View File

@ -0,0 +1,27 @@
<template>
<div class="styles">
<common-styles class="styles-height" :value="form.common_style" @update:value="common_styles_update" />
</div>
</template>
<script setup lang="ts">
const props = defineProps({
value: {
type: Object,
default: () => {},
},
});
const emit = defineEmits(['update:value']);
//
let form = reactive(props.value);
const common_styles_update = (val: Object) => {
form.common_style = val;
};
</script>
<style lang="scss" scoped>
.styles {
width: 100%;
.styles-height {
min-height: calc(100vh - 16.8rem);
}
}
</style>

View File

@ -1,16 +1,44 @@
import defaultCommon from './index';
interface DefaultCoupon {
content: {
theme: string;
data_type: string;
number: number;
title: string;
desc: string;
data_list: object[];
};
style: {
price_color: string;
name_color: string;
desc_color: string;
limit_send_count: string;
btn_background: color_list[];
btn_color: string;
background: color_list[];
spacing: number;
common_style: object;
};
}
const defaultCoupoin: DefaultCoupon = {
content: {
theme: '风格1',
data_type: '0',
number: 4,
title: '先领券 再购物',
desc: '领券下单·享购物优惠',
data_list: [],
},
style: {
common_style: { ...defaultCommon, padding: 10, padding_top: 10, padding_bottom: 10, padding_left: 10, padding_right: 10 },
price_color: '#FF3830',
name_color: '#FF3830',
desc_color: '#FF3830',
limit_send_count: '#999',
btn_background: [{ color: '#FFF1E1', color_percentage: '' }],
btn_color: '#FF3830',
background: [{ color: '#FFF1E1', color_percentage: '' }],
spacing: 10,
common_style: defaultCommon,
},
};

View File

@ -20,6 +20,7 @@ import defaultImgMagic from './default/img-magic';
import defaultHotZone from './default/hot-zone';
import defaultCustom from './default/custom';
import defaultDataMagic from './default/data-magic';
import defaultCoupon from './default/coupon';
// 系统设置
interface DefaultSettings {
@ -45,6 +46,7 @@ interface DefaultSettings {
data_magic: object;
hot_zone: object;
custom: object;
coupon: object;
}
const defaultSettings: DefaultSettings = {
@ -70,6 +72,7 @@ const defaultSettings: DefaultSettings = {
data_magic: defaultDataMagic,
hot_zone: defaultHotZone,
custom: defaultCustom,
coupon: defaultCoupon,
};
export default defaultSettings;

View File

@ -104,6 +104,11 @@
<template v-else-if="item.key == 'custom'">
<model-custom :key="item.com_data" :value="item.com_data" :show-tabs="item.show_tabs"></model-custom>
</template>
<!-- 营销组件 -->
<!-- 优惠券 -->
<template v-else-if="item.key == 'coupon'">
<model-coupon :key="item.com_data" :value="item.com_data"></model-coupon>
</template>
<!-- 工具组件 -->
<!-- 辅助线 -->
<template v-else-if="item.key == 'row-line'">
@ -222,8 +227,13 @@ const components = reactive([
],
},
{
title: '工具组件',
title: '营销组件',
key: '2',
item: [{ key: 'coupon', name: '优惠券' }],
},
{
title: '工具组件',
key: '3',
item: [
{ key: 'float-window', name: '悬浮按钮' },
{ key: 'text-title', name: '文本标题' },

View File

@ -11,6 +11,7 @@
</template>
</card-container>
<div class="setting-content">
<!-- 基础组件 -->
<!-- 页面设置 -->
<template v-if="value.key == 'page-settings'">
<page-content :value="value.com_data.content"></page-content>
@ -79,6 +80,11 @@
<template v-else-if="value.key == 'footer-nav'">
<footer-nav-setting :type="radio" :value="value.com_data"></footer-nav-setting>
</template>
<!-- 营销组件 -->
<!-- 优惠券 -->
<template v-else-if="value.key == 'coupon'">
<model-coupon-setting :type="radio" :value="value.com_data"></model-coupon-setting>
</template>
<!-- 工具组件 -->
<!-- 辅助空白 -->
<template v-else-if="value.key == 'auxiliary-blank'">