自定义获取数据改版
parent
2d234639cd
commit
543099e796
|
|
@ -1,19 +1,22 @@
|
|||
<template>
|
||||
<drag :data="drag_list" :space-col="20" :is-show-edit="true" @remove="remove" @on-sort="on_sort" @edit="edit" @replace="replace">
|
||||
<template #default="{ row, index }">
|
||||
<upload v-model="row.new_cover" :limit="1" size="40" styles="2"></upload>
|
||||
<el-image :src="row.data[imgParams]" fit="contain" class="img radius-xs">
|
||||
<template #error>
|
||||
<div class="bg-f5 flex-row jc-c align-c radius-xs h w">
|
||||
<icon name="error-img" size="16" color="9"></icon>
|
||||
</div>
|
||||
</template>
|
||||
</el-image>
|
||||
<!-- 自定义模式,并且没有传递图片参数,就不显示图片,否则的话,显示图片 -->
|
||||
<template v-if="(type == 'custom' && !isEmpty(imgParams)) || type == 'other'">
|
||||
<upload v-model="row.new_cover" :limit="1" size="40" styles="2"></upload>
|
||||
<el-image :src="row.data[imgParams]" fit="contain" class="img radius-xs">
|
||||
<template #error>
|
||||
<div class="bg-f5 flex-row jc-c align-c radius-xs h w">
|
||||
<icon name="error-img" size="16" color="9"></icon>
|
||||
</div>
|
||||
</template>
|
||||
</el-image>
|
||||
</template>
|
||||
<template v-if="index === edit_index">
|
||||
<el-input v-model="row.new_title" placeholder="请输入链接" type="textarea" class="flex-1 do-not-trigger" clearable :rows="2"></el-input>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="flex-1 flex-width text-line-2 size-12 self-s do-not-trigger" @dblclick="double_click(index)">{{ !isEmpty(row.new_title) ? row.new_title : row.data.title }}</div>
|
||||
<div class="flex-1 flex-width text-line-2 size-12 self-s do-not-trigger" @dblclick="double_click(index)">{{ !isEmpty(row.new_title) ? row.new_title : row.data[props.titleParams] }}</div>
|
||||
</template>
|
||||
</template>
|
||||
</drag>
|
||||
|
|
@ -25,10 +28,14 @@ import { isEmpty } from 'lodash';
|
|||
interface Props {
|
||||
list: any[];
|
||||
imgParams: string;
|
||||
titleParams?: string;
|
||||
type?: string;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
imgParams: 'cover',
|
||||
titleParams: 'title',
|
||||
type: 'other'
|
||||
});
|
||||
|
||||
const drag_list = ref(props.list);
|
||||
|
|
@ -81,13 +88,13 @@ const double_click = (index: number) => {
|
|||
const edit_processing = (index: number) => {
|
||||
const list = drag_list.value[index];
|
||||
if (!isEmpty(list) && isEmpty(list.new_title)) {
|
||||
list.new_title = list.data.title;
|
||||
list.new_title = list.data[props.titleParams];
|
||||
}
|
||||
};
|
||||
//编辑关闭前的处理
|
||||
const edit_close_processing = (index: number) => {
|
||||
const list = drag_list.value[index];
|
||||
if (!isEmpty(list) && !isEmpty(list.new_title) && list.new_title === list.data.title) {
|
||||
if (!isEmpty(list) && !isEmpty(list.new_title) && list.new_title === list.data[props.titleParams]) {
|
||||
list.new_title = '';
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,142 @@
|
|||
<template>
|
||||
<div :class="props.direction == 'vertical' ? 'flex-col gap-x-18' : 'flex-row gap-y-20 jc-e'">
|
||||
<div v-for="(item, index) in props.filterData" :key="index" class="filter-style flex-row gap-12">
|
||||
<div v-if="!isEmpty(item.title) && Number(props.titleWidth) > 0" class="title text-line-1" :style="`width: ${ props.titleWidth }px;`">{{ item.title }}</div>
|
||||
<div class="w h filter-right">
|
||||
<template v-if="item.type == 'select'">
|
||||
<template v-if="item.config.data_level > 1">
|
||||
<div class="flex-row gap-10">
|
||||
<el-cascader v-model="new_data_Interface[item.form_name]" :placeholder="placeholder_config(item, 'select')" clearable class="w h" collapse-tags popper-class="filter-form-cascader" placement="left" :props="{ 'expandTrigger': 'hover', 'multiple': item.config.is_multiple == '1', 'checkStrictly': true, 'emitPath': false, 'value': item.data_key, 'label': item.data_name, 'children': item.config.children }" :options="selectData(item.data, item.const_key)" />
|
||||
<template v-if="item.config.is_multiple == '1'">
|
||||
<el-tooltip effect="dark" :show-after="200" :hide-after="200" content="父级选中包含所有子级" raw-content placement="top">
|
||||
<icon name="miaosha-hdgz" size="12" color="#999"></icon>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-select v-model="new_data_Interface[item.form_name]" :multiple="item.config.is_multiple == '1'" collapse-tags :placeholder="placeholder_config(item, 'select')" clearable>
|
||||
<el-option v-for="item1 in selectData(item.data, item.const_key)" :key="item1[item.data_key]" :label="item1[item.data_name]" :value="item1[item.data_key]" />
|
||||
</el-select>
|
||||
</template>
|
||||
</template>
|
||||
<template v-else-if="item.type == 'input'">
|
||||
<template v-if="item.config.type == 'number'">
|
||||
<el-input-number v-model="new_data_Interface[item.form_name]" :min="item.config?.min || 0" :max="item.config?.max || undefined" type="number" :placeholder="placeholder_config(item, 'input')" value-on-clear="min" class="w number-show" controls-position="right"></el-input-number>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-input v-model="new_data_Interface[item.form_name]" :placeholder="placeholder_config(item, 'input')" clearable />
|
||||
</template>
|
||||
</template>
|
||||
<template v-else-if="item.type == 'switch'">
|
||||
<el-switch v-model="new_data_Interface[item.form_name]" active-value="1" inactive-value="0" />
|
||||
</template>
|
||||
<template v-else-if="item.type =='radio'">
|
||||
<el-radio-group v-model="new_data_Interface[item.form_name]">
|
||||
<el-radio v-for="item1 in selectData(item.data, item.const_key)" :key="item1[item.data_key]" :value="item1[item.data_key]">{{ item1[item.data_name] }}</el-radio>
|
||||
</el-radio-group>
|
||||
</template>
|
||||
<template v-else-if="item.type =='checkbox'">
|
||||
<el-checkbox-group v-model="new_data_Interface[item.form_name]">
|
||||
<el-checkbox v-for="item1 in selectData(item.data, item.const_key)" :key="item1[item.data_key]" :value="item1[item.data_key]">{{ item1[item.data_name] }}</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { isEmpty } from 'lodash';
|
||||
import { commonStore } from '@/store';
|
||||
const common_store = commonStore();
|
||||
|
||||
interface filter {
|
||||
const_key: string; // 存放数据对应的key
|
||||
type: string; // 类型
|
||||
config: any;
|
||||
title: string;
|
||||
form_name: string;
|
||||
data: any[];
|
||||
data_key: string;
|
||||
data_name: string;
|
||||
}
|
||||
const props = defineProps({
|
||||
filterData: {
|
||||
type: Array as PropType<filter[]>,
|
||||
default: () => ([]),
|
||||
},
|
||||
direction: {
|
||||
type: String,
|
||||
default: 'vertical',
|
||||
},
|
||||
dataInterface: {
|
||||
type: Object as PropType<any>,
|
||||
default: () => {},
|
||||
},
|
||||
titleWidth: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
}
|
||||
});
|
||||
|
||||
const new_data_Interface = ref<any>({})
|
||||
// 监听初始数据变化,并赋值给新数据
|
||||
watch(() => props.dataInterface, (new_val, old_val) => {
|
||||
if (JSON.stringify(new_val)!= JSON.stringify(old_val)) {
|
||||
new_data_Interface.value = { ...new_val }
|
||||
}
|
||||
}, { deep: true, immediate: true });
|
||||
// 监听新数据的变化,将对应的数据传递给父组件,用于父组件调用接口
|
||||
const emit = defineEmits(['form-change']);
|
||||
watch(() => new_data_Interface.value, (val) => {
|
||||
emit('form-change', val);
|
||||
}, { deep: true });
|
||||
|
||||
const placeholder_config = computed(() => {
|
||||
return (data: any, type: string) => {
|
||||
if (!data.config.placeholder) {
|
||||
return type == 'select'? `请选择${ data?.title || '' }` : `请输入${ data?.title || '' }`;
|
||||
} else {
|
||||
return data.config.placeholder;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 通过类型判断从哪里取下拉框的值
|
||||
const selectData = computed(() => {
|
||||
return (data: any, type: string) => {
|
||||
if (!isEmpty(data)) {
|
||||
return data;
|
||||
} else {
|
||||
return !isEmpty((common_store.common as any)[type]) ? (common_store.common as any)[type] : [];
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.filter-style {
|
||||
min-height: 3.2rem;
|
||||
}
|
||||
.filter-right {
|
||||
min-width: 20rem;
|
||||
}
|
||||
.title {
|
||||
font-size: 1.2rem;
|
||||
line-height: 3.2rem;
|
||||
text-align: right;
|
||||
width: 100%;
|
||||
color: #606266;
|
||||
}
|
||||
.number-show {
|
||||
:deep(.el-input__wrapper .el-input__inner) {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
.custom-content {
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
<template>
|
||||
<el-table :data="tableData" class="custom-table" style="width: 100%" height="438" :header-cell-style="{ background: '#f7f7f7' }" @row-click="row_click" @select="handle_select" @select-all="handle_select" >
|
||||
<el-table-column v-if="multiple" type="selection" width="60" />
|
||||
<el-table-column v-else label="#" width="60" type="">
|
||||
<template #default="scope">
|
||||
<el-radio v-model="template_selection" :label="scope.$index + ''"> </el-radio>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template v-for="(item, index) in tableColumnList" :key="index">
|
||||
<el-table-column :prop="item.field" :label="item.name" :width="item?.width || ''">
|
||||
<template #default="scope">
|
||||
<template v-if="item.type == 'images'">
|
||||
<image-empty v-if="scope.row[item.field]" v-model="scope.row[item.field]" fit="contain" class="img"></image-empty>
|
||||
</template>
|
||||
<template v-else-if="item.type == 'text'">
|
||||
{{ scope.row[item.field] }}
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</template>
|
||||
<template #empty>
|
||||
<no-data></no-data>
|
||||
</template>
|
||||
</el-table>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
interface TableColumn {
|
||||
field: string;
|
||||
name: string;
|
||||
width?: string;
|
||||
type: string;
|
||||
}
|
||||
const props = defineProps({
|
||||
tableData: {
|
||||
type: Array,
|
||||
default: () => ([]),
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'id',
|
||||
},
|
||||
// 是否为tabs
|
||||
tableColumnList: {
|
||||
type: Array as PropType<TableColumn[]>,
|
||||
default: () => ([]),
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
}
|
||||
});
|
||||
|
||||
const template_selection = ref('');
|
||||
const emit = defineEmits(['select']);
|
||||
//#region 点击事件
|
||||
// 单选时的触发逻辑
|
||||
const row_click = (row: any) => {
|
||||
if (!props.multiple) {
|
||||
// 深拷贝数据,防止修改数据后影响到原来的数据
|
||||
const new_table_data = JSON.parse(JSON.stringify(props.tableData));
|
||||
// 通过唯一标识判断当前是属于哪一个的数据
|
||||
template_selection.value = new_table_data.findIndex((item: any) => item[props.type] == row[props.type]).toString();
|
||||
emit('select', row);
|
||||
}
|
||||
};
|
||||
// 多选时的触发逻辑
|
||||
const handle_select = (selection: any) => {
|
||||
emit('select', selection);
|
||||
};
|
||||
//#endregion
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.custom-table {
|
||||
.img {
|
||||
width: 3.2rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
<template>
|
||||
<el-dialog v-model="dialogVisible" class="radius-lg" width="1168" draggable append-to-body :close-on-click-modal="false" :top="dialogPositionTop ? dialogPositionTop + 'px' : ''" @close="close_event">
|
||||
<template #header>
|
||||
<div class="title center re">
|
||||
<div class="tc size-16 fw">{{ config?.popup_title || '' }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<div class="flex-col gap-20 w h pa-20">
|
||||
<filter-form :filter-data="config.filter_form_config" direction="horizontal" :data-interface="default_data" @form-change="filter_form_change"></filter-form>
|
||||
<table-config v-loading="loading" :table-data="tableData" :multiple="multiple" :table-column-list="config.header" @select="table_select"></table-config>
|
||||
<div class="flex-row jc-e">
|
||||
<el-pagination :current-page="pagination_data.page" background :page-size="pagination_data.page_size" :pager-count="5" layout="prev, pager, next" :total="pagination_data.data_total" @current-change="get_list" />
|
||||
</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>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import request from '@/utils/request';
|
||||
import { isEmpty } from 'lodash';
|
||||
|
||||
const props = defineProps({
|
||||
type: {
|
||||
type: Array as PropType<string[]>,
|
||||
default: () => [],
|
||||
},
|
||||
config: {
|
||||
type: Object as PropType<any>,
|
||||
default: () => {},
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
dialogPositionTop: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
});
|
||||
const dialogVisible = defineModel('dialogVisible', { type: Boolean, default: false });
|
||||
//#region 数据传递
|
||||
const select_data = ref([]);
|
||||
const table_select = (val: any) => {
|
||||
select_data.value = val;
|
||||
};
|
||||
const emit = defineEmits(['confirm_event']);
|
||||
const close_event = () => {
|
||||
dialogVisible.value = false;
|
||||
};
|
||||
const confirm_event = () => {
|
||||
dialogVisible.value = false;
|
||||
emit('confirm_event', select_data.value);
|
||||
};
|
||||
//#endregion
|
||||
//#region 初始化数据
|
||||
// 数据处理
|
||||
const default_data = ref({});
|
||||
const pagination_data = ref({
|
||||
page: 1,
|
||||
page_size: 10,
|
||||
data_total: 0,
|
||||
});
|
||||
// 初始化数据显示
|
||||
watchEffect(() => {
|
||||
if (!isEmpty(props.config) && dialogVisible.value) {
|
||||
// 处理数据
|
||||
const staging_data : any = {};
|
||||
pagination_data.value = {
|
||||
page: 1,
|
||||
page_size: props.config?.page_size || 10,
|
||||
data_total: 0,
|
||||
}
|
||||
// 将数据赋值给默认数据
|
||||
// 根据不同的类型,初始化不同的数据, 并将对象处理成对应的值
|
||||
props.config.filter_form_config.forEach((item: any) => {
|
||||
let value : number | string | Array<any> = '';
|
||||
if (item.type == 'checkbox' || item.type == 'select' && item.config.is_multiple == '1') { // 多选
|
||||
value = item.config.default ? item.config.default : [];
|
||||
} else if (item.type == 'input' && item.config.type == 'number') { // 数字
|
||||
value = item.config.default ? item.config.default : 0;
|
||||
} else if (item.type == 'switch') {
|
||||
value = item.config.default ? item.config.default : "0";
|
||||
}
|
||||
staging_data[item.form_name] = value;
|
||||
})
|
||||
// 循环完之后赋值,避免多次赋值,传递给子组件出现多次调用和回调问题
|
||||
default_data.value = staging_data;
|
||||
}
|
||||
});
|
||||
// 子组件数据变更传递过来的数据
|
||||
const filter_form_change = (val: any) => {
|
||||
default_data.value = val;
|
||||
// 更换数据之后,需要从第一页开始请求
|
||||
pagination_data.value.page = 1;
|
||||
};
|
||||
//#endregion
|
||||
//region 表格数据更新操作
|
||||
const get_list = (new_page: number) => {
|
||||
pagination_data.value.page = new_page;
|
||||
get_table_list(default_data.value);
|
||||
}
|
||||
const tableData = ref([]);
|
||||
const loading = ref(false);
|
||||
const get_table_list = (val: any) => {
|
||||
tableData.value = [];
|
||||
// 判断是否有数据和配置和请求地址
|
||||
if (!isEmpty(val) && !isEmpty(props.config) && !isEmpty(props.config.data_url)) {
|
||||
// 发送请求,获取数据
|
||||
const data = {
|
||||
...val,
|
||||
page: pagination_data.value.page,
|
||||
page_size: pagination_data.value.page_size,
|
||||
}
|
||||
loading.value = true;
|
||||
request({
|
||||
url: props.config.data_url, // 请求地址
|
||||
method: 'post', // 请求方式
|
||||
data // 请求数据
|
||||
}).then((res) => {
|
||||
if (res.data) {
|
||||
tableData.value = res.data.data_list;
|
||||
pagination_data.value.data_total = res.data.data_total;
|
||||
}
|
||||
}).finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
watch(() => default_data.value, (val) => {
|
||||
get_table_list(val);
|
||||
}, { deep: true })
|
||||
//#endregion
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
|
|
@ -1,15 +1,15 @@
|
|||
<template>
|
||||
<div :style="style_container">
|
||||
<div class="w h re" :style="style_img_container">
|
||||
<template v-if="data_source_content_list.length > 0 && form.data_source_direction == '0'">
|
||||
<template v-if="data_source_content_list.length > 0 && form.data_source_direction == 'vertical'">
|
||||
<div v-for="(item1, index1) in data_source_content_list" :key="index1" :style="style_chunk_container">
|
||||
<div class="w h" :style="style_chunk_img_container">
|
||||
<data-rendering :custom-list="form.custom_list" :source-list="item1" :source-type="form?.data_source || ''" :data-height="form.height" :scale="scale"></data-rendering>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div v-else-if="data_source_content_list.length > 0 && ['1', '2'].includes(form.data_source_direction)" class="oh" :style="form.data_source_direction == '2' ? `height:100%;` : `height: ${ swiper_height }px;`">
|
||||
<swiper :key="carouselKey" class="w flex" :direction="form.data_source_direction == '2' ? 'horizontal': 'vertical'" :height="swiper_height" :loop="true" :autoplay="autoplay" :slides-per-view="slides_per_view" :slides-per-group="slides_per_group" :allow-touch-move="false" :pause-on-mouse-enter="true" :modules="modules" @slide-change="slideChange">
|
||||
<div v-else-if="data_source_content_list.length > 0 && ['vertical-scroll', 'horizontal'].includes(form.data_source_direction)" class="oh" :style="form.data_source_direction == 'horizontal' ? `height:100%;` : `height: ${ swiper_height }px;`">
|
||||
<swiper :key="carouselKey" class="w flex" :direction="form.data_source_direction == 'horizontal' ? 'horizontal': 'vertical'" :height="swiper_height" :loop="true" :autoplay="autoplay" :slides-per-view="slides_per_view" :slides-per-group="slides_per_group" :allow-touch-move="false" :pause-on-mouse-enter="true" :modules="modules" @slide-change="slideChange">
|
||||
<swiper-slide v-for="(item1, index1) in data_source_content_list" :key="index1">
|
||||
<div :style="style_chunk_container">
|
||||
<div class="w h" :style="style_chunk_img_container">
|
||||
|
|
@ -106,8 +106,9 @@ const style_chunk_img_container = computed(() => common_img_computer(new_style.v
|
|||
|
||||
// 数据来源的内容
|
||||
let data_source_content_list = computed(() => {
|
||||
if (['goods', 'article', 'brand'].includes(form.value.data_source)) {
|
||||
if (form.value.data_source_content.data_type == '0') {
|
||||
// 是自定义数据类型的时候,显示自定义数据,否则显示数据来源的数据
|
||||
if (form.value.is_custom_data == '1') {
|
||||
if (form.value.data_source_content.data_type == 'appoint') {
|
||||
return form.value.data_source_content?.data_list || [];
|
||||
} else {
|
||||
return !isEmpty(form.value.data_source_content) ?
|
||||
|
|
@ -150,7 +151,7 @@ watchEffect(() => {
|
|||
}
|
||||
const { padding_top, padding_bottom, margin_bottom, margin_top } = new_style.value.data_style;
|
||||
// 轮播图高度控制
|
||||
if (form.value.data_source_direction == '2') {
|
||||
if (form.value.data_source_direction == 'horizontal') {
|
||||
swiper_height.value = form.value.height * scale.value + padding_top + padding_bottom + margin_bottom + margin_top;
|
||||
} else {
|
||||
swiper_height.value = (form.value.height * scale.value + padding_top + padding_bottom + margin_bottom + margin_top) * form.value.data_source_carousel_col;
|
||||
|
|
|
|||
|
|
@ -10,40 +10,43 @@
|
|||
</el-form-item>
|
||||
</card-container>
|
||||
<!-- 筛选数据 -->
|
||||
<template v-if="['goods', 'article', 'brand'].includes(form.data_source)">
|
||||
<template v-if="!isEmpty(default_type_data)">
|
||||
<div class="divider-line"></div>
|
||||
<card-container>
|
||||
<div class="mb-12">显示设置</div>
|
||||
<el-form-item label="铺满方式">
|
||||
<el-radio-group v-model="form.data_source_direction">
|
||||
<el-radio value="0">纵向展示</el-radio>
|
||||
<el-radio value="1">纵向滑动</el-radio>
|
||||
<el-radio value="2">横向滑动</el-radio>
|
||||
<el-radio v-for="(item, index) in default_type_data.show_type" :key="index" :value="item">{{ item == 'vertical' ? '纵向展示' : item == 'vertical-scroll' ? '纵向滑动' : '横向滑动' }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="['1', '2'].includes(form.data_source_direction)" label="每屏显示">
|
||||
<el-form-item v-if="['vertical-scroll', 'horizontal'].includes(form.data_source_direction)" label="每屏显示">
|
||||
<el-radio-group v-model="form.data_source_carousel_col">
|
||||
<template v-if="form.data_source_direction == '2'">
|
||||
<el-radio :value="1">单列展示</el-radio>
|
||||
<el-radio :value="2">两列展示</el-radio>
|
||||
<el-radio :value="3">三列展示</el-radio>
|
||||
<el-radio :value="4">四列展示</el-radio>
|
||||
<el-radio v-for="(item, index) in default_type_data.show_number" :key="index" :value="item">{{ item }}{{ form.data_source_direction == 'vertical-scroll' ? '行' : '列' }}展示</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</card-container>
|
||||
<template v-if="!isEmpty(default_type_data.data_type)">
|
||||
<div class="divider-line"></div>
|
||||
<card-container>
|
||||
<div class="mb-12">数据设置</div>
|
||||
<div class="flex-col">
|
||||
<el-form-item label="读取方式">
|
||||
<el-radio-group v-model="form.data_source_content.data_type">
|
||||
<el-radio v-for="(item, index) in default_type_data.data_type" :key="index" :value="item">{{ item == 'appoint' ? '指定数据' : '筛选数据' }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<template v-if="form.data_source_content.data_type == 'appoint'">
|
||||
<div class="nav-list">
|
||||
<drag-group :list="form.data_source_content.data_list" :img-params="default_type_data?.appoint_config?.show_data?.data_logo || ''" :title-params="default_type_data?.appoint_config?.show_data?.data_name || 'name'" type="custom" @onsort="data_list_sort" @remove="data_list_remove" @replace="data_list_replace"></drag-group>
|
||||
<el-button class="mt-20 w" @click="add">+添加</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-radio :value="1">单行展示</el-radio>
|
||||
<el-radio :value="2">两行展示</el-radio>
|
||||
<el-radio :value="3">三行展示</el-radio>
|
||||
<el-radio :value="4">四行展示</el-radio>
|
||||
<filter-form :filter-data="default_type_data.filter_config.filter_form_config" direction="vertical" :title-width="70" :data-interface="form.data_source_content" @form-change="filter_form_change"></filter-form>
|
||||
</template>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</card-container>
|
||||
<div class="divider-line"></div>
|
||||
<card-container>
|
||||
<div class="mb-12">{{ form.data_source == 'goods' ? '商品' : form.data_source == 'article' ? '文章' : '品牌' }}设置</div>
|
||||
<!-- 数据筛选组件, 根据数据源类型显示不同的筛选组件 -->
|
||||
<data-filter :type="form.data_source" :value="form.data_source_content" :list="form.data_source_content.data_list" :base-list="base_list" @add="add" @data_list_replace="data_list_replace" @data_list_remove="data_list_remove" @data_list_sort="data_list_sort"></data-filter>
|
||||
</card-container>
|
||||
</div>
|
||||
</card-container>
|
||||
</template>
|
||||
</template>
|
||||
<div class="divider-line"></div>
|
||||
<card-container>
|
||||
|
|
@ -80,21 +83,17 @@
|
|||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
<url-value-dialog v-model:dialog-visible="url_value_dialog_visible" :type="[form.data_source]" :multiple="url_value_multiple_bool" @update:model-value="url_value_dialog_call_back"></url-value-dialog>
|
||||
<custom-dialog v-model:dialog-visible="url_value_dialog_visible" :config="default_type_data.appoint_config" :multiple="url_value_multiple_bool" @confirm_event="url_value_dialog_call_back"></custom-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import Dialog from './components/dialog.vue';
|
||||
import DragIndex from './components/index.vue';
|
||||
import { isEmpty, cloneDeep } from 'lodash';
|
||||
import { isEmpty, cloneDeep, omit } from 'lodash';
|
||||
import request from '@/utils/request';
|
||||
import CustomAPI from '@/api/custom';
|
||||
import ShopAPI from '@/api/shop';
|
||||
import ArticleAPI from '@/api/article';
|
||||
import BrandAPI from '@/api/brand';
|
||||
import { commonStore, DataSourceStore } from '@/store';
|
||||
import { DataSourceStore } from '@/store';
|
||||
import { get_math } from '@/utils';
|
||||
import { source_list } from '@/config/const/custom';
|
||||
const common_store = commonStore();
|
||||
const data_source_store = DataSourceStore();
|
||||
|
||||
const props = defineProps({
|
||||
|
|
@ -114,8 +113,8 @@ const form = ref(props.value);
|
|||
const center_width = ref(props.magicWidth);
|
||||
|
||||
const custom_width = computed(() => {
|
||||
// 如果是横向展示,那么就需要根据每屏显示的数量来计算宽度 data_source_direction == 2 为横向滑动
|
||||
if (['goods', 'article', 'brand'].includes(form.value.data_source) && form.value.data_source_direction == '2') {
|
||||
// 如果是横向展示,那么就需要根据每屏显示的数量来计算宽度 data_source_direction == horizontal 为横向滑动
|
||||
if (form.value.is_custom_data == '1' && form.value.data_source_direction == 'horizontal') {
|
||||
return center_width.value / form.value.data_source_carousel_col;
|
||||
} else {
|
||||
return center_width.value;
|
||||
|
|
@ -124,12 +123,20 @@ const custom_width = computed(() => {
|
|||
// 弹出框里的内容
|
||||
let custom_list = reactive([]);
|
||||
const center_height = ref(0);
|
||||
interface custom_config {
|
||||
show_type: string[],
|
||||
show_number: number[],
|
||||
data_type: string[],
|
||||
filter_config: object,
|
||||
appoint_config: object,
|
||||
}
|
||||
interface data_list {
|
||||
name: string;
|
||||
field: string;
|
||||
type: string;
|
||||
}
|
||||
interface options_item {
|
||||
custom_config?: custom_config;
|
||||
appoint_data?: object;
|
||||
name: string;
|
||||
data: data_list[];
|
||||
|
|
@ -144,6 +151,7 @@ const getCustominit = () => {
|
|||
data_source_store.set_data_source(data_source);
|
||||
// 数据处理
|
||||
processing_data(form.value.data_source);
|
||||
data_processing();
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -155,8 +163,17 @@ onBeforeMount(() => {
|
|||
options.value = data_source_store.data_source_list;
|
||||
// 如果历史的值出现,根据历史选中的值处理一下传递的数据结构
|
||||
processing_data(form.value.data_source);
|
||||
data_processing();
|
||||
}
|
||||
});
|
||||
// 回显数据处理
|
||||
const data_processing = () => {
|
||||
// 如果有历史的值,那么就需要根据历史的值来处理一下数据
|
||||
const type_data = options.value.filter((item) => item.type == form.value.data_source);
|
||||
if (type_data.length > 0 && !isEmpty(type_data[0].custom_config)) {
|
||||
default_type_data.value = type_data[0].custom_config;
|
||||
}
|
||||
};
|
||||
// 处理显示的图片和传递到下去的数据结构
|
||||
const model_data_source = ref<data_list[]>([]);
|
||||
const processing_data = (key: string) => {
|
||||
|
|
@ -209,106 +226,82 @@ const accomplish = () => {
|
|||
//#region 数据源更新逻辑处理
|
||||
// 打开弹出框
|
||||
const url_value_dialog_visible = ref(false);
|
||||
const default_type_data = ref<any>({})
|
||||
const url_value_multiple_bool = ref(true);
|
||||
const changeDataSource = (key: string) => {
|
||||
form.value.is_custom_data = '0';
|
||||
const type_data = options.value.filter((item) => item.type == key);
|
||||
processing_data(key);
|
||||
// 默认数据类型
|
||||
form.value.data_source_content = {
|
||||
// 存放手动输入的id
|
||||
data_ids: [],
|
||||
data_list: [],
|
||||
// 自动
|
||||
data_auto_list: [],
|
||||
}
|
||||
// 默认数据, 避免不选报错
|
||||
default_type_data.value = {};
|
||||
form.value.data_source_direction = 'vertical';
|
||||
// 如果存在默认数据类型的时候,就直接赋值给data_list
|
||||
if (type_data.length > 0 && !isEmpty(type_data[0].appoint_data)) {
|
||||
form.value.data_source_direction = '0';
|
||||
form.value.data_source_content = {
|
||||
// 存放手动输入的id
|
||||
data_ids: [],
|
||||
data_list: [ type_data[0].appoint_data ],
|
||||
// 自动
|
||||
data_auto_list: [],
|
||||
}
|
||||
} else {
|
||||
form.value.data_source_content = {
|
||||
form.value.data_source_content.data_list = [ type_data[0].appoint_data ];
|
||||
} else if (type_data.length > 0 && !isEmpty(type_data[0].custom_config)) {
|
||||
// 是自定义数据类型
|
||||
form.value.is_custom_data = '1';
|
||||
// 自定义数据取值
|
||||
const custom_config = type_data[0].custom_config;
|
||||
// 默认赋值第一个
|
||||
form.value.data_source_direction = custom_config.show_type[0];
|
||||
// 处理数据
|
||||
const staging_data : any = {
|
||||
// 存放手动输入的id
|
||||
data_ids: [],
|
||||
// 手动输入
|
||||
data_list: [],
|
||||
// 自动
|
||||
data_auto_list: [],
|
||||
// 类型
|
||||
data_type: custom_config.data_type[0],
|
||||
};
|
||||
if (!isEmpty(key) && key in source_list) {
|
||||
form.value.data_source_content = cloneDeep(source_list[key as keyof typeof source_list]);
|
||||
}
|
||||
// 将数据赋值给默认数据
|
||||
default_type_data.value = custom_config;
|
||||
// 如果不存在的时候,默认取id
|
||||
form.value.data_list_key = default_type_data.value?.appoint_config?.show_data?.data_key || 'id';
|
||||
// 根据不同的类型,初始化不同的数据, 并将对象处理成对应的值
|
||||
default_type_data.value.filter_config.filter_form_config.forEach((item: any) => {
|
||||
let value : number | string | Array<any> = '';
|
||||
if (item.type == 'checkbox' || item.type == 'select' && item.config.is_multiple == '1') { // 多选
|
||||
value = item.config.default ? item.config.default : [];
|
||||
} else if (item.type == 'input' && item.config.type == 'number') { // 数字
|
||||
value = item.config.default ? item.config.default : 0;
|
||||
} else if (item.type == 'switch') {
|
||||
value = item.config.default ? item.config.default : "0";
|
||||
}
|
||||
staging_data[item.form_name] = value;
|
||||
})
|
||||
// 循环完之后赋值,避免多次赋值,传递给子组件出现多次调用和回调问题
|
||||
form.value.data_source_content = staging_data;
|
||||
}
|
||||
};
|
||||
const filter_form_change = (val: any) => {
|
||||
form.value.data_source_content = val;
|
||||
}
|
||||
//#endregion
|
||||
//#region 数据源内容多选处理
|
||||
const base_list = reactive({
|
||||
// 商品分类
|
||||
product_list: [
|
||||
{ name: '指定商品', value: '0' },
|
||||
{ name: '筛选商品', value: '1' },
|
||||
],
|
||||
product_category_list: [] as select_1[],
|
||||
product_brand_list: [] as select_1[],
|
||||
sort_list: [
|
||||
{ name: '综合', value: '0' },
|
||||
{ name: '销量', value: '1' },
|
||||
{ name: '热度', value: '2' },
|
||||
{ name: '价格', value: '3' },
|
||||
{ name: '最新', value: '4' },
|
||||
],
|
||||
// 公共分类
|
||||
order_by_rule_list: [
|
||||
{ name: '降序(desc)', value: '0' },
|
||||
{ name: '升序(asc)', value: '1' },
|
||||
],
|
||||
// 文章分类
|
||||
article_category_list: [] as select_1[],
|
||||
data_type_list: [
|
||||
{ name: '指定文章', value: '0' },
|
||||
{ name: '筛选文章', value: '1' },
|
||||
],
|
||||
new_sort_list: [
|
||||
{ name: '综合', value: '0' },
|
||||
{ name: '时间', value: '1' },
|
||||
{ name: '浏览量', value: '2' },
|
||||
],
|
||||
field_show_list: [
|
||||
{ name: '日期时间', value: '0' },
|
||||
{ name: '浏览量', value: '1' },
|
||||
{ name: '描述', value: '2' },
|
||||
],
|
||||
// 品牌数据
|
||||
brand_category_list: [] as select_1[],
|
||||
brand_data_type_list: [
|
||||
{ name: '指定品牌', value: '0' },
|
||||
{ name: '筛选品牌', value: '1' },
|
||||
],
|
||||
brand_sort_list: [
|
||||
{ name: '最新', value: '0' },
|
||||
{ name: '热度', value: '1' },
|
||||
],
|
||||
});
|
||||
onBeforeMount(() => {
|
||||
nextTick(() => {
|
||||
// 定时获取common_store.common.article_category的数据,直到拿到值或者关闭页面为止
|
||||
const interval = setInterval(() => {
|
||||
const { goods_category = [], brand_list = [], article_category = [], brand_category = [] } = common_store.common;
|
||||
if (goods_category.length > 0 || brand_list.length > 0 || article_category.length > 0) {
|
||||
base_list.product_category_list = goods_category;
|
||||
base_list.product_brand_list = brand_list;
|
||||
base_list.article_category_list = article_category;
|
||||
base_list.brand_category_list = brand_category;
|
||||
clearInterval(interval);
|
||||
}
|
||||
}, 1000);
|
||||
});
|
||||
});
|
||||
|
||||
const url_value_multiple_bool = ref(true);
|
||||
const data_list_replace_index = ref(0);
|
||||
const data_list_replace = (index: number) => {
|
||||
// 替换的时候,index为选择的索引
|
||||
data_list_replace_index.value = index;
|
||||
// 替换的时候,是单选
|
||||
url_value_multiple_bool.value = false;
|
||||
url_value_dialog_visible.value = true;
|
||||
};
|
||||
const add = () => {
|
||||
url_value_multiple_bool.value = true;
|
||||
// 添加的时候,index为-1
|
||||
data_list_replace_index.value = -1;
|
||||
// 添加是单选还是多选由后台配置决定
|
||||
url_value_multiple_bool.value = default_type_data.value.appoint_config.is_multiple.toString() == '1' ? true : false;
|
||||
url_value_dialog_visible.value = true;
|
||||
};
|
||||
// 拖拽更新之后,更新数据
|
||||
|
|
@ -325,7 +318,8 @@ const url_value_dialog_call_back = (item: any[]) => {
|
|||
item.title = item.name;
|
||||
});
|
||||
}
|
||||
if (url_value_multiple_bool.value) {
|
||||
// 如果是单选,当时data_list_replace_index 为-1,说明是添加,否则是替换
|
||||
if (url_value_multiple_bool.value || data_list_replace_index.value == -1) {
|
||||
item.forEach((item: any) => {
|
||||
form.value.data_source_content.data_list.push({
|
||||
id: get_math(),
|
||||
|
|
@ -345,8 +339,8 @@ const url_value_dialog_call_back = (item: any[]) => {
|
|||
};
|
||||
// 数据来源的内容
|
||||
let data_source_content_list = computed(() => {
|
||||
if (['goods', 'article', 'brand'].includes(form.value.data_source)) {
|
||||
if (form.value.data_source_content.data_type == '0') {
|
||||
if (form.value.is_custom_data == '1') {
|
||||
if (form.value.data_source_content.data_type == 'appoint') {
|
||||
return form.value.data_source_content.data_list;
|
||||
} else {
|
||||
return form.value.data_source_content.data_auto_list;
|
||||
|
|
@ -356,79 +350,39 @@ let data_source_content_list = computed(() => {
|
|||
}
|
||||
})
|
||||
// 获取商品自动数据
|
||||
const get_products = () => {
|
||||
const { category_ids, brand_ids, number, order_by_type, order_by_rule, keyword } = form.value.data_source_content;
|
||||
const params = {
|
||||
goods_keywords: keyword,
|
||||
goods_category_ids: category_ids,
|
||||
goods_brand_ids: brand_ids,
|
||||
goods_order_by_type: order_by_type,
|
||||
goods_order_by_rule: order_by_rule,
|
||||
goods_number: number,
|
||||
};
|
||||
// 获取商品列表
|
||||
ShopAPI.getShopLists(params).then((res: any) => {
|
||||
// 清空历史数据
|
||||
form.value.data_source_content.data_auto_list = [];
|
||||
if (!isEmpty(res.data)) {
|
||||
form.value.data_source_content.data_auto_list = res.data;
|
||||
}
|
||||
});
|
||||
};
|
||||
// 获取文章自动数据
|
||||
const get_article = async () => {
|
||||
const { category_ids, number, order_by_type, order_by_rule, is_cover, keyword } = form.value.data_source_content;
|
||||
const new_data = {
|
||||
article_keywords: keyword,
|
||||
article_category_ids: category_ids.join(','),
|
||||
article_order_by_type: order_by_type,
|
||||
article_order_by_rule: order_by_rule,
|
||||
article_number: number,
|
||||
article_is_cover: is_cover,
|
||||
};
|
||||
const res = await ArticleAPI.getAutoList(new_data);
|
||||
// 清空历史数据
|
||||
const get_auto_data = () => {
|
||||
// 清空数据, 避免接口报错等显示的还是老数据
|
||||
form.value.data_source_content.data_auto_list = [];
|
||||
if (!isEmpty(res.data)) {
|
||||
form.value.data_source_content.data_auto_list = res.data;
|
||||
if (!isEmpty(default_type_data.value) && !isEmpty(default_type_data.value.filter_config) && !isEmpty(default_type_data.value.filter_config.data_url)) {
|
||||
const data = omit(cloneDeep(form.value.data_source_content), ['data_ids', 'data_list', 'data_auto_list', 'data_type']);
|
||||
request({
|
||||
url: default_type_data.value.filter_config.data_url, // 请求地址
|
||||
method: 'post', // 请求方式
|
||||
data // 请求数据
|
||||
}).then((res) => {
|
||||
if (res.data) {
|
||||
form.value.data_source_content.data_auto_list = res.data;
|
||||
}
|
||||
})
|
||||
}
|
||||
};
|
||||
// 获取品牌自动数据
|
||||
const get_brand = () => {
|
||||
const { category_ids, number, order_by_type, order_by_rule, keyword } = form.value.data_source_content;
|
||||
const params = {
|
||||
brand_keywords: keyword,
|
||||
brand_category_ids: category_ids,
|
||||
brand_order_by_type: order_by_type,
|
||||
brand_order_by_rule: order_by_rule,
|
||||
brand_number: number,
|
||||
};
|
||||
// 获取商品列表
|
||||
BrandAPI.getBrandLists(params).then((res: any) => {
|
||||
// 清空历史数据
|
||||
form.value.data_source_content.data_auto_list = [];
|
||||
if (!isEmpty(res.data)) {
|
||||
form.value.data_source_content.data_auto_list = res.data;
|
||||
}
|
||||
});
|
||||
form.value.data_source_content.data_auto_list = [];
|
||||
}
|
||||
// 将不需要监听的数据移除,只监听需要监听的数据
|
||||
const data_source_content_value = computed(() => {
|
||||
const { category_ids, brand_ids, number, order_by_type, order_by_rule, data_type, is_cover, keyword } = form.value.data_source_content;
|
||||
return { category_ids: category_ids, brand_ids: brand_ids, number: number, order_by_type: order_by_type, order_by_rule: order_by_rule, data_type: data_type, is_cover: is_cover, keyword: keyword };
|
||||
})
|
||||
|
||||
watch(() => data_source_content_value.value, (new_val) => {
|
||||
// 数据发生变化时,如果是自动获取数据,则调用接口获取数据
|
||||
if (new_val.data_type != '0') {
|
||||
if (form.value.data_source == 'goods') {
|
||||
get_products();
|
||||
} else if (form.value.data_source == 'article') {
|
||||
get_article();
|
||||
} else if (form.value.data_source == 'brand') {
|
||||
get_brand();
|
||||
// 从对象中移除不需要监听的 'data_ids', 'data_list', 'data_auto_list'三个值
|
||||
const data: any = {};
|
||||
for (const key in form.value.data_source_content) {
|
||||
if (!['data_ids', 'data_list', 'data_auto_list'].includes(key)) {
|
||||
data[key] = form.value.data_source_content[key];
|
||||
}
|
||||
}
|
||||
return data;
|
||||
})
|
||||
// 数据发生变化时,调用接口获取数据
|
||||
watch(() => data_source_content_value.value, (new_val, old_val) => {
|
||||
// 数据发生变化时,如果是自动获取数据,则调用接口获取数据
|
||||
if (JSON.stringify(new_val) != JSON.stringify(old_val) && new_val.data_type != 'appoint') {
|
||||
get_auto_data();
|
||||
}
|
||||
},{ deep: true });
|
||||
//#endregion
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div class="w">
|
||||
<el-form :model="form" label-width="70">
|
||||
<template v-if="['1', '2'].includes(data.data_source_direction)">
|
||||
<template v-if="['vertical-scroll', 'horizontal'].includes(data.data_source_direction)">
|
||||
<card-container>
|
||||
<div class="mb-12">轮播设置</div>
|
||||
<el-form-item label="自动轮播">
|
||||
|
|
|
|||
|
|
@ -77,6 +77,8 @@ interface content {
|
|||
interface defaultSearch {
|
||||
content: {
|
||||
height: number;
|
||||
is_custom_data: string;
|
||||
data_list_key: string;
|
||||
data_source: string;
|
||||
data_source_content: content;
|
||||
data_source_direction: string;
|
||||
|
|
@ -114,6 +116,10 @@ const defaultSearch: defaultSearch = {
|
|||
// 自动
|
||||
data_auto_list: [],
|
||||
},
|
||||
// 手动模式下的唯一标识
|
||||
data_list_key: '',
|
||||
// 是否是自定义数据类型
|
||||
is_custom_data: '0',
|
||||
// 数据源类型 商品(goods) 文章(article) 品牌(brand) 用户信息(user-info)
|
||||
data_source:'',
|
||||
// 铺满方式 0 纵向展示 1 纵向滑动 2 横向滑动
|
||||
|
|
|
|||
|
|
@ -267,4 +267,26 @@ p {
|
|||
|
||||
.el-input__wrapper .el-input__inner {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.filter-form-cascader {
|
||||
.el-checkbox,
|
||||
.el-radio {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.el-cascader-node {
|
||||
padding-left: 34px;
|
||||
}
|
||||
|
||||
.el-checkbox__input,
|
||||
.el-radio__input {
|
||||
position: absolute;
|
||||
left: 20px;
|
||||
}
|
||||
}
|
||||
|
|
@ -351,11 +351,12 @@ const save_formmat_form_data = (data: diy_data_item, close: boolean = false, is_
|
|||
} else if (new_array_5.includes(item.key)) {
|
||||
// 从数据中剔除data_source_content_value字段
|
||||
item.com_data.content = omit(cloneDeep(item.com_data.content), ['data_source_content_value']);
|
||||
if (['goods', 'article', 'brand'].includes(item.com_data.content.data_source)) {
|
||||
// 是否是自定义数据
|
||||
if (item.com_data.content.is_custom_data == '1') {
|
||||
// 手动的数据内容
|
||||
const data_list = cloneDeep(item.com_data.content.data_source_content.data_list);
|
||||
// 数据改造,存放手动的id
|
||||
item.com_data.content.data_source_content.data_ids = data_list.map((item: any) => item.data.id || '').join(',') || '';
|
||||
item.com_data.content.data_source_content.data_ids = data_list.map((item1: any) => item1.data[item.com_data.content?.data_list_key || 'id'] || '').join(',') || '';
|
||||
// 数据改造,存放手动的清除里边的data
|
||||
item.com_data.content.data_source_content.data_list = data_list.map((item1: any) => {
|
||||
return {
|
||||
|
|
|
|||
Loading…
Reference in New Issue