修改自定义历史记录逻辑处理

v1.2.0
于肖磊 2025-01-10 14:22:36 +08:00
parent ffbead8c02
commit 0e8077e1fe
18 changed files with 567 additions and 79 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -5,6 +5,34 @@
"css_prefix_text": "icon-",
"description": "",
"glyphs": [
{
"icon_id": "43087330",
"name": "箭头左",
"font_class": "left-arrow",
"unicode": "e7b4",
"unicode_decimal": 59316
},
{
"icon_id": "43087331",
"name": "历史记录",
"font_class": "historical-records",
"unicode": "e7b9",
"unicode_decimal": 59321
},
{
"icon_id": "43087329",
"name": "箭头右",
"font_class": "right-arrow",
"unicode": "e7ba",
"unicode_decimal": 59322
},
{
"icon_id": "42958997",
"name": "编组",
"font_class": "grouping",
"unicode": "e7d6",
"unicode_decimal": 59350
},
{
"icon_id": "42773571",
"name": "更多四块",

Binary file not shown.

View File

@ -15,11 +15,9 @@
<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>
<div class="bg-f5 divider-line" />
<card-container>
<div class="mb-20">自定义设置</div>
<el-button class="w" size="large" @click="custom_edit"><icon name="edit" size="12"></icon>自定义编辑</el-button>
<el-form-item label="滚动条">
<el-switch v-model="form.is_scroll_bar" active-value="1" inactive-value="0" @change="operation_end" />
</el-form-item>
</card-container>
</template>
<script setup lang="ts">
@ -47,10 +45,7 @@ const group_change = () => {
}
};
const emit = defineEmits(['custom_edit', 'operation_end']);
const custom_edit = () => {
emit('custom_edit', form.value.data_source_field);
};
const emit = defineEmits(['operation_end']);
//
const operation_end = () => {
emit('operation_end');

View File

@ -128,7 +128,7 @@ const get_nested_property = (obj: any, path: string) => {
}
// #endregion
//
const style_container = computed(() => common_styles_computer(new_style.value.common_style) + 'overflow: auto;');
const style_container = computed(() => common_styles_computer(new_style.value.common_style) + (form.value.is_scroll_bar == '1' ? 'overflow: auto;' : ''));
const style_img_container = computed(() => common_img_computer(new_style.value.common_style));
//
const style_content_container = computed(() => common_styles_computer(new_style.value.data_content_style));

View File

@ -17,12 +17,14 @@
<div class="bg-f5 divider-line" />
<el-tabs v-model="tabs_name" class="content-tabs">
<el-tab-pane label="内容设置" name="content">
<custom-tabs-content :value="form" :options="options" @custom_edit="custom_edit" @operation_end="operation_end"></custom-tabs-content>
<custom-tabs-content :value="form" :options="options" @operation_end="operation_end"></custom-tabs-content>
</el-tab-pane>
<el-tab-pane label="样式设置" name="styles">
<model-custom-styles :value="form.data_style" :content="form" :is-floating-up="false" @operation_end="operation_end"></model-custom-styles>
</el-tab-pane>
</el-tabs>
<div class="bg-f5 divider-line" />
<el-button class="w custom-button size-14" size="large" @click="custom_edit"><icon name="edit" size="14"></icon>自定义编辑</el-button>
</el-form>
</div>
</template>
@ -49,7 +51,8 @@ const { diy_data } = toRefs(state);
const form = ref(diy_data.value.com_data);
//#region
const emit = defineEmits(['custom_edit', 'operation_end']);
const custom_edit = (data_source_field: any[]) => {
const custom_edit = () => {
const data_source_field = form.value.data_source_field;
const { custom_list, com_width, custom_height } = form.value;
//
const width = form.value.data_source_direction != 'vertical-scroll' ? com_width / form.value.data_source_carousel_col : com_width; //
@ -99,4 +102,13 @@ watch(
width: 100%;
}
}
.custom-button {
position: -webkit-sticky;
position: sticky;
bottom: 0; /* 固定在底部 */
background-color: white; /* 设置背景色以避免按钮看不见 */
z-index: 1000; /* 确保按钮在其他内容之上 */
width: 100%; /* 确保按钮宽度占满父容器 */
padding: 1rem; /* 添加一些内边距 */
}
</style>

View File

@ -50,7 +50,7 @@
<el-radio value="italic">倾斜</el-radio>
</el-radio-group>
<el-form-item label="字号" label-width="50" class="mb-0 w">
<slider v-model="form.text_size" :max="100" @update:model-value="text_size_change"></slider>
<slider v-model="form.text_size" :max="100" @update:model-value="text_size_change" @operation_end="operation_end"></slider>
</el-form-item>
<el-form-item label="行间距" label-width="50" class="mb-0 w">
<slider v-model="form.line_text_size" :max="200" @operation_end="operation_end"></slider>
@ -187,7 +187,6 @@ const text_link_change = (key: string) => {
//
const text_size_change = (size: number) => {
form.value.line_text_size = size;
operation_end();
};
const mult_color_picker_event = (arry: color_list[], type: number) => {
form.value.color_list = arry;

View File

@ -2,7 +2,7 @@
<Dialog v-model:visible="dialogVisible" :title="configType == 'custom' ? '编辑自定义' : '编辑自定义组'" @accomplish="accomplish">
<div class="flex-row h w">
<!-- 左侧和中间区域 -->
<DragIndex ref="draglist" :key="dragkey" v-model:height="center_height" v-model:width="center_width" :config-type="configType" :source-list="sourceList" :custom-group-field-id="customGroupFieldId" :is-custom="configType == 'custom'? isCustom : false" :show-data="showData" :list="customList" @right-update="right_update"></DragIndex>
<DragIndex ref="draglist" :key="dragkey" v-model:height="center_height" v-model:width="center_width" :config-type="configType" :source-list="sourceList" :custom-group-field-id="customGroupFieldId" :is-custom="configType == 'custom'? isCustom : false" :show-data="showData" :list="new_list" @right-update="right_update" @operation_end="operation_end"></DragIndex>
<!-- 右侧配置区域 -->
<div class="settings">
<template v-if="diy_data.key === 'img'">
@ -35,6 +35,11 @@
<script lang="ts" setup>
import Dialog from '@/components/model-custom/components/dialog.vue';
import DragIndex from '@/components/model-custom/components/index.vue';
import { DataSourceStore } from '@/store';
import { formatDate } from '@/utils';
import { cloneDeep, isEqual } from "lodash";
const data_source_store = DataSourceStore();
const props = defineProps({
configType: {
type: String,
@ -73,6 +78,7 @@ const props = defineProps({
default: () => [],
},
});
const new_list = ref(cloneDeep(props.customList))
//
const custom_father_list = defineModel('fatherList', { type: Array, default: () => [] });
//
@ -109,8 +115,14 @@ const accomplish = () => {
} else {
//
if (props.configType == 'custom') {
//
data_source_store.set_custom_records([]);
data_source_store.set_custom_records_index(-1);
emits('accomplish', props.configType, draglist.value.diy_data);
} else {
//
data_source_store.set_custom_group_records([]);
data_source_store.set_custom_group_records_index(-1);
//
custom_father_list.value.forEach((item: any) => {
if (item.id == props.customId) {
@ -130,8 +142,86 @@ const custom_edit = (id: string, list: diy, width: number, height: number, data_
}
emits('custom_edit', 'custom-group', id, father_list, list, width, height, data_source_field);
};
const operation_end = () => {
console.log('操作结束');
//
onBeforeMount(() => {
const new_data = [{ name: formatDate('HH点mm分ss秒'), height: center_height.value, value: props.customList }];
if (props.configType == 'custom') {
// store
data_source_store.set_custom_records(new_data);
//
data_source_store.set_custom_records_index(0);
} else {
// store
data_source_store.set_custom_group_records(new_data);
//
data_source_store.set_custom_group_records_index(0);
}
})
//
const operation_end = (is_compare: boolean = true) => {
//
if (!draglist.value) {
return;
} else {
// is_compare = false
let old_index = 0;
let old_compare_data = {};
if (props.configType == 'custom') {
old_index = data_source_store.custom_records_index;
old_compare_data = cloneDeep(data_source_store.custom_records[old_index].value) || {};
} else {
old_index = data_source_store.custom_group_records_index;
old_compare_data = cloneDeep(data_source_store.custom_group_records[old_index].value) || {};
}
//
const new_compare_data = cloneDeep(draglist.value.diy_data);
if (!is_compare || !isEqual(old_compare_data, new_compare_data)) {
//
if (props.configType == 'custom') {
//
const new_data = old_data_handle(data_source_store?.custom_records || [], data_source_store?.custom_records_index || -1);
//
new_data.unshift({ name: formatDate('HH点mm分ss秒'), height: center_height.value, value: new_compare_data });
// store
data_source_store.set_custom_records(new_data);
//
data_source_store.set_custom_records_index(0);
} else {
//
const new_data = old_data_handle(data_source_store?.custom_group_records || [], data_source_store?.custom_group_records_index || -1);
//
new_data.unshift({ name: formatDate('HH点mm分ss秒'), height: center_height.value, value: new_compare_data });
//
data_source_store.set_custom_group_records(new_data);
//
data_source_store.set_custom_group_records_index(0);
}
}
}
}
/**
* 处理旧数据以生成新数据数组
* 此函数用于根据当前选中的历史记录索引对旧数据进行克隆和更新
* 主要目的是维护一个最多包含10条记录的历史数据列表
*
* @param old_data 任何类型的旧数据数组包含历史记录
* @param index 当前选中的历史记录的索引用于决定如何更新数据
* @returns 返回更新后的新数据数组
*/
const old_data_handle = (old_data: any, index: number) => {
//
const new_data = cloneDeep(old_data);
// ,
if (index !== 0 && index !== -1) {
// index10
new_data.splice(index, 1);
} else if (new_data.length == 10) {
// 10
new_data.splice(new_data.length - 1, 1);
}
//
return new_data;
}
</script>

View File

@ -224,6 +224,7 @@ const custom_group_com_data = {
custom_height: 100, // 自定义高度
data_source_direction: 'vertical', // 铺满方式
data_source_carousel_col: 1, // 铺满数量
is_scroll_bar: false, // 是否需要滚动条
data_style: {
...defaultCustom.style
}

View File

@ -17,7 +17,7 @@
</card-container>
<card-container class="mb-8">
<div class="mb-12">内容设置</div>
<slider v-model="center_height" :max="1000"></slider>
<slider v-model="center_height" :max="1000" @operation_end="operation_end(false)"></slider>
</card-container>
<card-container class="h selected">
<div class="flex-col gap-10 drawer-container">
@ -42,7 +42,7 @@
</template>
</div>
<div class="abs draggable-icon" :style="item.show_tabs == '1' ? 'opacity: 1;' : 'opacity: 0.5;'">
<el-icon class="iconfont icon-commodity-edit size-16 cr-primary do-not-trigger two-click" @click="on_edit(index)" />
<el-icon class="iconfont icon-edit size-16 cr-primary do-not-trigger two-click" @click="on_edit(index)" />
<el-icon class="iconfont icon-close-round-o size-16" @click.stop="del(index)" />
</div>
</li>
@ -62,12 +62,13 @@
<right-side-operation v-if="typeof select_index === 'number' && !isNaN(select_index) && diy_data.length > 0" v-model:index="select_index" v-model:data-length="diy_data.length" @del="del" @copy="copy" @previous_layer="previous_layer" @underlying_layer="underlying_layer" @top_up="top_up" @bottom_up="bottom_up"></right-side-operation>
<!-- 拖拽区 -->
<div class="model-drag">
<top-side-operation class="model-top-wall" :config-type="props.configType" @back="back" @forward="forward" @handle-history="handle_history"></top-side-operation>
<div class="model-wall">
<div ref="imgBoxRef" class="drag-area re dropzone" @dragover.prevent @dragenter.prevent @drop="drop">
<div class="w h" @mousedown.prevent="start_drag" @mousemove.prevent="move_drag" @mouseup.prevent="end_drag">
<DraggableContainer v-if="draggable_container" style="z-index:0" :reference-line-visible="true" :disabled="false" reference-line-color="#ddd" @selectstart.prevent @contextmenu.prevent @dragstart.prevent>
<!-- @mouseover="on_choose(index)" -->
<Vue3DraggableResizable v-for="(item, index) in diy_data" :key="item.id" v-model:w="item.com_data.com_width" v-model:h="item.com_data.com_height" :min-w="0" :min-h="0" :class="{'plug-in-show-component-line': is_show_component_line, 'plug-in-show-tabs': item.show_tabs == '1', 'vdr-handle-z-index': item.com_data.bottom_up == '1' }" :style="{ 'z-index': (diy_data.length - 1) - index }" :init-w="item.com_data.com_width" :init-h="item.com_data.com_height" :x="item.location.x" :y="item.location.y" :parent="true" :draggable="is_draggable" @mousedown.stop="on_choose(index, item.show_tabs)" @click.stop="on_choose(index, item.show_tabs)" @drag-end="dragEndHandle($event, index)" @resizing="resizingHandle($event, item.key, index)" @resize-end="resizingHandle($event, item.key, index)">
<Vue3DraggableResizable v-for="(item, index) in diy_data" :key="item.id" v-model:w="item.com_data.com_width" v-model:h="item.com_data.com_height" :min-w="0" :min-h="0" :class="{'plug-in-show-component-line': is_show_component_line, 'plug-in-show-tabs': item.show_tabs == '1', 'vdr-handle-z-index': item.com_data.bottom_up == '1' }" :style="{ 'z-index': (diy_data.length - 1) - index }" :init-w="item.com_data.com_width" :init-h="item.com_data.com_height" :x="item.location.x" :y="item.location.y" :parent="true" :draggable="is_draggable" @mousedown.stop="on_choose(index, item.show_tabs)" @click.stop="on_choose(index, item.show_tabs)" @drag-end="dragEndHandle($event, index)" @resizing="resizingHandle($event, item.key, index, 'resizing')" @resize-end="resizingHandle($event, item.key, index, 'resizeEnd')">
<div :class="['main-content flex-row', { 'plug-in-border': item.show_tabs == '1' }]">
<template v-if="item.key == 'text'">
<model-text :key="item.id" :value="item.com_data" :source-list="props.sourceList" :is-custom="isCustom" :custom-group-field-id="customGroupFieldId" :title-params="showData?.data_name || 'name'"></model-text>
@ -110,7 +111,7 @@
</div>
</template>
<script setup lang="ts">
import { cloneDeep, isEmpty, property } from 'lodash';
import { cloneDeep, isEmpty, property, isEqual } from 'lodash';
import { get_math, adjustPosition, getPlatform } from '@/utils';
import { defaultComData, isRectangleIntersecting } from "./index-default";
import { SortableEvent, VueDraggable } from 'vue-draggable-plus';
@ -119,7 +120,7 @@ const common_store = commonStore();
//
const app = getCurrentInstance();
//#region
const emits = defineEmits(['rightUpdate']);
const emits = defineEmits(['rightUpdate', 'operation_end']);
interface Props {
configType: string;
list: diy_content[];
@ -203,6 +204,7 @@ const on_sort = (item: SortableEvent) => {
let index = item?.newIndex || 0;
//
set_show_tabs(index);
operation_end();
};
//#endregion
//#region
@ -237,8 +239,12 @@ const on_edit = (index: number) => {
//
const outerClick = (e: any) => {
if ((!isEmpty(e.target.className) && !e.target.className.includes('do-not-trigger')) && (!isEmpty(e.target.parentNode.className) && !e.target.parentNode.className.includes('do-not-trigger'))) {
edit_close_processing(edit_index.value);
edit_index.value = -1;
// , -1
if (edit_index.value !== -1) {
edit_close_processing(edit_index.value);
edit_index.value = -1;
operation_end();
}
}
};
//
@ -275,6 +281,7 @@ const copy = (index: null | number) => {
//
diy_data.value.splice(index, 0, new_data);
set_show_tabs(index + 1);
operation_end();
}
};
@ -305,6 +312,7 @@ const del = (index: null | number) => {
diy_data.value.splice(index, 1);
}
select_index.value = diy_data.value.length > 0 ? select_index.value : null;
operation_end();
});
}
};
@ -345,6 +353,7 @@ const moveItem = (index: number, newIndex: number) => {
//
diy_data.value.splice(newIndex, 0, old_data);
set_show_tabs(newIndex);
operation_end();
} catch (error) {
console.error('Error moving item:', error);
}
@ -410,6 +419,8 @@ const center_width = defineModel('width', { type: Number, default: 390 });
const drag_area_height = computed(() => center_height.value + 'px');
const drag_area_width = computed(() => center_width.value + 'px');
//
const drag_area_top_width = computed(() => center_width.value > 170 ? center_width.value + 'px' : '170px');
const draggable_container = ref(true);
let data = reactive<diy_content[]>([]);
@ -490,7 +501,7 @@ const drop = (event: any) => {
//
const newItem = {
...draggedItem.value,
new_name: list.length > 0 ? draggedItem.value.name + list.length : draggedItem.value.name, //
new_name: list.length > 0 ? draggedItem.value.name + list.length : '', //
location: {
x: adjustedX,
y: adjustedY,
@ -503,15 +514,22 @@ const drop = (event: any) => {
diy_data.value.unshift(newItem);
// 0
set_show_tabs(0);
operation_end();
}
};
//#endregion
//#region
const dragEndHandle = (item: any, index: number) => {
diy_data.value[index].location = { x: item.x, y: item.y, record_x: item.x, record_y: item.y, staging_y: item.y };
const old_location = diy_data.value[index].location;
const new_location = { x: item.x, y: item.y, record_x: item.x, record_y: item.y, staging_y: item.y };
//
if (!isEqual(old_location, new_location)) {
operation_end();
}
diy_data.value[index].location = new_location;
};
// {x: number, y: number, w: number, h: number}
const resizingHandle = (new_location: any, key: string, index: number) => {
const resizingHandle = (new_location: any, key: string, index: number, type: string) => {
const { x, y, w, h } = new_location;
//
diy_data.value[index].location = { x, y, record_x: x, record_y: y, staging_y: y };
@ -531,6 +549,9 @@ const resizingHandle = (new_location: any, key: string, index: number) => {
com_data.line_width = line_width;
com_data.line_size = line_size;
}
if (type == 'resizeEnd') {
operation_end();
}
};
//
const handleImg = (com_data: any, w: number, h: number ) => {
@ -683,6 +704,7 @@ const start_drag_area_box = (index: number, event: MouseEvent) => {
item.location.record_y = y;
}
});
operation_end();
};
};
@ -804,7 +826,6 @@ const rect_style = computed(() => {
const platform = getPlatform();
// 退
const data_source_store = DataSourceStore();
const pressedKeys = new Set(); // 使 Set
const handle_keydown = (e: KeyboardEvent) => {
//
const default_list = ['textarea', 'input'];
@ -812,22 +833,90 @@ const handle_keydown = (e: KeyboardEvent) => {
if (e.target instanceof HTMLElement && default_list.includes(e.target?.localName)) {
return;
}
// 使 key code keyCode
const key = e.key.toLowerCase(); //
// Set
pressedKeys.add(key);
// A B
if ((pressedKeys.has('control') && pressedKeys.has('z') && platform == 'Windows') || (pressedKeys.has('meta') && pressedKeys.has('z') && platform == 'Mac')) {
if ((e.ctrlKey && e.key === 'z' && platform == 'Windows') || (e.metaKey && e.key === 'z' && platform == 'Mac')) {
// 退
if (!data_source_store.is_children_custom && props.configType == 'custom') {
console.log('同时按下了A和B键', props.configType); //
back(data_source_store.custom_records_index, props.configType);
} else if (data_source_store.is_children_custom && props.configType == 'custom-group') {
console.log('同时按下了A和B键', props.configType); //
back(data_source_store.custom_group_records_index, props.configType);
}
} else if ((e.ctrlKey && e.key === 'y' && platform == 'Windows') || (e.metaKey && e.key === 'y' && platform == 'Mac')) {
// 退
if (!data_source_store.is_children_custom && props.configType == 'custom') {
forward(data_source_store.custom_records_index, props.configType);
} else if (data_source_store.is_children_custom && props.configType == 'custom-group') {
forward(data_source_store.custom_group_records_index, props.configType);
}
}
//
e.preventDefault();
}
//
const getRecordsList = (type: string) => {
if (type === 'custom') {
return data_source_store.custom_records;
} else if (type === 'custom-group') {
return data_source_store.custom_group_records;
}
throw new Error('Invalid type');
};
//
const setIndex = (type: string, index: number) => {
if (type === 'custom') {
data_source_store.set_custom_records_index(index);
} else if (type === 'custom-group') {
data_source_store.set_custom_group_records_index(index);
}
};
// 退
const back = (index: number, type: string) => {
const list = getRecordsList(type);
if (!list || list.length === 0) return;
let new_index = index + 1;
if (new_index < list.length) {
setIndex(type, new_index);
const data = list[new_index];
if (!isEmpty(data)) {
diy_data.value = data.value;
center_height.value = data?.height || center_height.value;
cancel();
}
}
};
//
const forward = (index: number, type: string) => {
const list = getRecordsList(type);
if (!list || list.length === 0) return;
let new_index = Math.max(0, index - 1);
if (new_index >= 0 && new_index < list.length) {
setIndex(type, new_index);
const data = list[new_index];
if (!isEmpty(data)) {
diy_data.value = data.value;
center_height.value = data?.height || center_height.value;
cancel();
}
}
};
//
const handle_history = (index: number, type: string) => {
const list = getRecordsList(type);
if (!list || list.length === 0 || index === -1) return;
if (index >= 0 && index < list.length) {
setIndex(type, index);
const data = list[index];
if (!isEmpty(data)) {
diy_data.value = data.value;
center_height.value = data?.height || center_height.value;
cancel();
}
}
};
const handleKeyUp = (e: KeyboardEvent) => {
//
const default_list = ['textarea', 'input'];
@ -850,24 +939,20 @@ const handleKeyUp = (e: KeyboardEvent) => {
}
// 退
if (!data_source_store.is_children_custom && props.configType == 'custom') {
//
const key = e.key.toLowerCase();
pressedKeys.delete(key);
//
e.preventDefault();
//
if (key_code.includes(e.key)) {
data_handling(x, y);
operation_end();
}
} else if (data_source_store.is_children_custom && props.configType == 'custom-group') {
//
const key = e.key.toLowerCase();
pressedKeys.delete(key);
//
e.preventDefault();
//
if (key_code.includes(e.key)) {
data_handling(x, y);
operation_end();
}
}
};
@ -938,11 +1023,15 @@ onMounted(() => {
});
onUnmounted(() => {
document.removeEventListener('keyup', handleKeyUp);
document.removeEventListener('keydown', handle_keydown);
//
document.removeEventListener('keyup', handleKeyUp);
});
//#endregion
//#endregion
//
const operation_end = (is_compare: boolean = true) => {
emits('operation_end', is_compare);
}
//
defineExpose({
diy_data,
@ -953,6 +1042,11 @@ defineExpose({
@import 'index.scss';
.model-drag {
overflow-y: scroll;
.model-top-wall {
width: v-bind(drag_area_top_width);
margin: 0 auto;
z-index: 999;
}
.model-wall {
width: v-bind(drag_area_width);
background-image: linear-gradient(45deg, #e5e5e5 25%, transparent 25%), linear-gradient(135deg, #e5e5e5 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #e5e5e5 75%), linear-gradient(135deg, transparent 75%, #e5e5e5 75%);
@ -962,7 +1056,7 @@ defineExpose({
.drag-area {
height: v-bind(drag_area_height);
width: 100%;
margin: 0.5rem 0; //
margin: 1.4rem 0; //
user-select: none;
cursor: crosshair;
}

View File

@ -0,0 +1,192 @@
<template>
<div v-if="typeof records_index === 'number' && !isNaN(records_index)" class="sticky-top">
<div class="acticons">
<el-tooltip effect="dark" :show-after="200" :hide-after="200" content="回退" placement="top">
<el-icon :class="['iconfont icon-left-arrow tooltip-icon', { 'disabled': records_index == history_list.length - 1 }]" @click.stop="back(records_index, records_index < history_list.length - 1)" />
</el-tooltip>
<div class="icon_border"></div>
<el-tooltip effect="dark" :show-after="200" :hide-after="200" content="前进" placement="top">
<el-icon :class="['iconfont icon-right-arrow tooltip-icon', { 'disabled': records_index <= 0 }] " @click.stop="forward(records_index, records_index > 0)" />
</el-tooltip>
<div class="icon_border"></div>
<el-tooltip effect="dark" :show-after="200" :hide-after="200" content="历史记录" placement="top">
<el-icon :class="['iconfont icon-historical-records tooltip-icon', { 'disabled': history_list.length === 0 }]" @click.stop="open_history(history_list.length !== 0)" />
</el-tooltip>
</div>
<el-dialog v-model="dialogVisible" class="history-dialog" :style="{ top: '80px', left: dialog_left + 'px' }" title="历史记录" width="200" draggable show-close :modal="false" :close-on-click-modal="false" :close-on-press-escape="false">
<div ref="historyDialog" class="history-dialog-content flex-col gap-14">
<div v-for="(item, index1) in history_list" :key="index1" :class="[`history-dialog-item ${props.configType}`, {'active': records_index == index1, }]" @click="handleHistory(index1, records_index !== index1)">
<div class="history-dialog-item-title">{{ item.name }}</div>
<el-icon v-if="records_index == index1" class="iconfont icon-checked size-14" />
</div>
</div>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { DataSourceStore } from '@/store';
const dataSourceStore = DataSourceStore();
const props = defineProps({
configType: {
type: String,
default: 'custom',
}
});
const records_index = ref(-1);
const history_list = ref<any[]>([]);
const historyDialog = ref<HTMLElement | null>(null);
onMounted(() => {
handleResize();
window.addEventListener('resize', handleResize);
});
onUnmounted(() => {
window.removeEventListener('resize', handleResize);
});
const dialog_left = ref(0);
const handleResize = () => {
//
let width = (window.innerWidth - 200) / 2;
//
if (window.innerWidth <= 1560) {
width = width - 410;
} else {
width = width - 470;
}
// dialogleft
dialog_left.value = width;
}
watchEffect(() => {
if (props.configType == 'custom') {
history_list.value = dataSourceStore.custom_records;
records_index.value = dataSourceStore.custom_records_index;
} else {
history_list.value = dataSourceStore.custom_group_records;
records_index.value = dataSourceStore.custom_group_records_index;
}
// --
const activeCard: HTMLElement | null = document.querySelector(`.history-dialog-item.active.${props.configType}`);
if (activeCard) {
//
const scrollY = activeCard.offsetTop;
if (historyDialog.value) {
//
historyDialog.value.scrollTo({ top: scrollY - 100, behavior: 'smooth' });
}
}
});
const emits = defineEmits(['back', 'forward', 'handleHistory']);
const dialogVisible = ref(false);
//退
const back = (index: number, is_click: boolean) => {
if (is_click) {
emits('back', index, props.configType);
}
}
//
const forward = (index: number, is_click: boolean) => {
if (is_click) {
emits('forward', index, props.configType)
}
}
const handleHistory = (index: number, is_click: boolean) => {
//
if (is_click) {
emits('handleHistory', index, props.configType);
}
}
const open_history = (is_click: boolean) => {
//
if (is_click) {
dialogVisible.value = !dialogVisible.value;
}
}
</script>
<style scoped lang="scss">
.sticky-top{
position: sticky;
top: 1rem;
z-index: 1;
height: 4rem;
.acticons {
display: flex;
align-items: center;
max-width: 16rem;
background: #fff;
}
.icon_border {
border-right: 1px solid #eeeeee;
height: 1.6rem;
}
.tooltip-icon {
font-size: 2rem;
width: 5.7rem;
height: 100%;
padding: 0.8rem 1.6rem;
cursor: pointer;
}
.tooltip-icon.disabled {
cursor: default !important;
}
}
:deep(.el-dialog) {
// transform: translate(238px, 159px);
.el-dialog__header{
padding: 1.2rem 1.4rem !important;
}
.el-dialog__header .el-dialog__headerbtn {
padding: 1.3rem 1.4rem 0.7rem !important;
font-size: 1.6rem !important;
}
.el-dialog__title {
font-size: 1.2rem !important;
line-height: 1.2rem !important;
}
}
// dialog
:deep(:has(> .el-overlay-dialog .history-dialog)) {
pointer-events: none !important;
}
:deep(.el-overlay-dialog) {
pointer-events: none !important;
.history-dialog {
.el-dialog__header,
.el-dialog__body,
.el-dialog__footer {
pointer-events: auto !important;
}
}
}
.history-dialog-content {
overflow: auto;
height: 23rem;
padding: 1rem 0.8rem;
background: #fff;
.history-dialog-item {
cursor: pointer;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.6rem 1rem;
}
.history-dialog-item-title {
font-weight: 400;
font-size: 14px;
color: #666666;
line-height: 20px;
font-style: normal;
}
.history-dialog-item.active {
background: #F4f4f4;
border-radius: 4px;
}
.icon-checked{
color: #2A94FF;
}
}
</style>

View File

@ -1,5 +1,5 @@
<template>
<div class="auxiliary-line">
<div class="auxiliary-line custom-data">
<el-form :model="form" label-width="70">
<card-container>
<div class="mb-20">数据源</div>
@ -54,10 +54,7 @@
</template>
</template>
<div class="divider-line"></div>
<card-container>
<div class="mb-20">内容设置</div>
<el-button class="w" size="large" @click="custom_edit('custom')"><icon name="edit" size="12"></icon>自定义编辑</el-button>
</card-container>
<el-button class="w custom-button size-14" size="large" @click="custom_edit('custom')"><icon name="edit" size="14"></icon>自定义编辑</el-button>
</el-form>
<!-- 自定义内容处理 -->
<custom-config :key="dragkey + 'custom'" v-model:visible="dialogVisible" v-model:width="custom_width" v-model:height="center_height" :dragkey="dragkey + 'custom'" :options="model_data_source" :source-list="!isEmpty(data_source_content_list) ? data_source_content_list[0] : {}" :is-custom="form.is_custom_data == '1'" :show-data="form?.show_data || { data_key: 'id', data_name: 'name' }" :custom-list="custom_list" @accomplish="accomplish" @custom_edit="custom_edit"></custom-config>
@ -538,4 +535,16 @@ watch(() => data_source_content_value.value, (new_val, old_val) => {
padding: 2.4rem 3rem;
}
}
.custom-data {
background: transparent;
}
.custom-button {
position: -webkit-sticky;
position: sticky;
bottom: 0; /* 固定在底部 */
background-color: white; /* 设置背景色以避免按钮看不见 */
z-index: 1000; /* 确保按钮在其他内容之上 */
width: 100%; /* 确保按钮宽度占满父容器 */
padding: 1rem; /* 添加一些内边距 */
}
</style>

View File

@ -273,34 +273,34 @@ const default_list = {
** @return {Array}
*/
const commodity_list = (list: any[], num: number, data_content: any, data_style: any) => {
if (list.length > 0) {
//
const goods_list = cloneDeep(list).map((item: any) => ({
...item.data,
title: !isEmpty(item.new_title) ? item.new_title : item.data.title,
new_cover: item.new_cover,
}));
//
let nav_list: { split_list: any[] }[] = [];
// translation
if (data_style.rolling_fashion != 'translation') {
//
const split_num = Math.ceil(goods_list.length / num);
for (let i = 0; i < split_num; i++) {
nav_list.push({ split_list: goods_list.slice(i * num, (i + 1) * num) });
}
return nav_list;
} else {
return rotation_calculation(goods_list, num, data_content, data_style)
if (list.length > 0) {
//
const goods_list = cloneDeep(list).map((item: any) => ({
...item.data,
title: !isEmpty(item.new_title) ? item.new_title : item.data.title,
new_cover: item.new_cover,
}));
//
let nav_list: { split_list: any[] }[] = [];
// translation
if (data_style.rolling_fashion != 'translation') {
//
const split_num = Math.ceil(goods_list.length / num);
for (let i = 0; i < split_num; i++) {
nav_list.push({ split_list: goods_list.slice(i * num, (i + 1) * num) });
}
return nav_list;
} else {
const list = Array(num).fill(default_list);
if (data_style.rolling_fashion != 'translation') {
return [{ split_list: list }];
} else {
return rotation_calculation(list, num, data_content, data_style)
}
return rotation_calculation(goods_list, num, data_content, data_style)
}
} else {
const list = Array(num).fill(default_list);
if (data_style.rolling_fashion != 'translation') {
return [{ split_list: list }];
} else {
return rotation_calculation(list, num, data_content, data_style)
}
}
}
const rotation_calculation = (list: Array<any>, num: number, data_content: any, data_style: any) => {

View File

@ -7,6 +7,7 @@
<el-radio-group v-model="form.button_jump">
<el-radio value="link">页面链接</el-radio>
<el-radio value="customer_service">客服入口</el-radio>
<el-radio value="quick_nav">快捷导航</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="上传图片">

View File

@ -18,6 +18,12 @@ export const DataSourceStore = defineStore('dataSource', () => {
const is_data_source_api = ref(false);
// 数据源
const data_source_list = ref<data_source_content[]>([]);
// 自定义历史记录
const custom_records_index = ref<number>(-1);
const custom_records = ref<any[]>([]);
// 自定义组历史记录
const custom_group_records_index = ref<number>(-1);
const custom_group_records = ref<any[]>([]);
// 存储上传分类列表
const set_data_source = (data_source_content: data_source_content[]) => {
data_source_list.value = data_source_content;
@ -31,13 +37,34 @@ export const DataSourceStore = defineStore('dataSource', () => {
const set_is_children_custom = (bool: boolean) => {
is_children_custom.value = bool;
};
// 设置自定义的历史数据
const set_custom_records_index = (index: number) => {
custom_records_index.value = index;
};
const set_custom_records = (records: any[]) => {
custom_records.value = records;
};
// 设置自定义组的历史数据
const set_custom_group_records_index = (index: number) => {
custom_group_records_index.value = index;
};
const set_custom_group_records = (records: any[]) => {
custom_group_records.value = records;
};
return {
custom_records,
custom_group_records,
custom_records_index,
custom_group_records_index,
is_children_custom,
data_source_list,
is_data_source_api,
set_data_source,
set_is_data_source_api,
set_is_children_custom,
set_custom_records,
set_custom_records_index,
set_custom_group_records,
set_custom_group_records_index,
};
});

View File

@ -835,4 +835,29 @@ export const getPlatform = () => {
}
return 'Unknown';
}
/**
*
*
* @param date
* @param format 'YYYY-MM-DD HH:mm:ss'
* @returns
*/
export function formatDate(format: string = 'YYYY-MM-DD HH:mm:ss'): string {
const date = new Date();
const year = String(date.getFullYear());
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
const seconds = String(date.getSeconds()).padStart(2, '0');
return format
.replace('YYYY', year)
.replace('MM', month)
.replace('DD', day)
.replace('HH', hours)
.replace('mm', minutes)
.replace('ss', seconds);
}

View File

@ -8,7 +8,7 @@
<el-radio-button class="radio-item" value="2">样式</el-radio-button>
</el-radio-group>
</card-container>
<div class="setting-content">
<div class="setting-content" :style="value.key == 'custom' ? '' : 'background-color: #fff;'">
<!-- 基础组件 -->
<!-- 页面设置 -->
<template v-if="value.key == 'page-settings'">
@ -155,7 +155,6 @@ const radio = ref('1'); // 创建一个响应式的数字变量初始值为0
.setting-content {
height: calc(100vh - 14.8rem);
overflow: auto;
background-color: #fff;
}
:deep(.el-input-number) {
width: 100%;