Commit 4fb2d184 authored by 罗超's avatar 罗超

新增数据自动绑定后更换数据信息,TODO:空数据绑定

parent 6ba6b3ed
......@@ -19,6 +19,7 @@ declare module 'vue' {
Drawer: typeof import('./src/components/Drawer.vue')['default']
EditableInput: typeof import('./src/components/ColorPicker/EditableInput.vue')['default']
ElButton: typeof import('element-plus/es')['ElButton']
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
ElCheckTag: typeof import('element-plus/es')['ElCheckTag']
ElCol: typeof import('element-plus/es')['ElCol']
ElDialog: typeof import('element-plus/es')['ElDialog']
......
......@@ -6,7 +6,27 @@ export type RadioGroupValue = {
value: Ref<string>
updateValue: (value: string) => void
}
export type SlideDataSource = Ref<object>
export interface TravelAatas {
baseInfo:any,
dinnerInfo?:TravelDetail[],
hotelInfo?:TravelDetail[],
scenicInfo?:TravelDetail[]
[key: string]: any;
}
export interface TravelDetail {
Id:number,
Name:string,
Describe:string,
ImgList?:string[]
}
export type SlideDataSource = Ref<{
ConfigId:number,
DataSourceList?:Array<any>,
DataSourceOverlay?:boolean,
FeatureImgList?:any[],
TravelAatas?:TravelAatas,
[key: string]: any;
}>
export const injectKeySlideScale: InjectionKey<SlideScale> = Symbol()
export const injectKeySlideId: InjectionKey<SlideId> = Symbol()
export const injectKeyRadioGroupValue: InjectionKey<RadioGroupValue> = Symbol()
......
......@@ -114,6 +114,12 @@ interface PPTBaseElement {
rotate: number
link?: PPTElementLink
name?: string
dataMapping?:DataSourceMapping
}
interface DataSourceMapping{
id:number,
filed:string
}
/**
......@@ -264,6 +270,7 @@ export interface PPTImageElement extends PPTBaseElement {
shadow?: PPTElementShadow
colorMask?: string
layerName?: string
below?:number
}
......
......@@ -16,7 +16,7 @@ export const ResolveLayer = async (item: any, index: number,offsetLeft:number,of
id: "img_" + index,
type: 'image',
src,
fixedRatio: false,
fixedRatio: true,
filters: {
opacity:src==''?'0':opacity
},
......
......@@ -23,7 +23,7 @@
width: viewportStyles.width * canvasScale + 'px',
height: viewportStyles.height * canvasScale + 'px',
left: (viewportStyles.left) + 'px',
top: (viewportStyles.top/2) + 'px',
top: (viewportStyles.top) + 'px',
}"
>
<div class="operates">
......
......@@ -405,15 +405,15 @@ const setTemplate = async () =>{
})
}
for(let i=0;i<slides.value.length;i++){
if(!slides.value[i].typeId&&slides.value[i].pageType!=1){
datas.loading = false
mainStore.setToolbarState(ToolbarStates.EL_STYLE)
return ElMessage({
showClose: true,
message: `请选择 第 ${i+1} 页 的绑定数据`,
type: 'warning',
})
}
// if(!slides.value[i].typeId&&slides.value[i].pageType!=1){
// datas.loading = false
// mainStore.setToolbarState(ToolbarStates.EL_STYLE)
// return ElMessage({
// showClose: true,
// message: `请选择 第 ${i+1} 页 的绑定数据`,
// type: 'warning',
// })
// }
if(slides.value[i].elements.length==0) {
datas.loading = false
mainStore.setToolbarState(ToolbarStates.EL_TEMPLATEDATA)
......
......@@ -175,21 +175,7 @@ const GetTripFiledData = async () =>{
text:'正在渲染团队数据',
lock:true
})
let isHideOverflowText = false
try {
await ElMessageBox.confirm('行程数据可能会超出模板预设宽度或高度,是否自动裁剪','提示',{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
closeOnClickModal:false
})
isHideOverflowText = true
} catch (error) {}
let maxWidth = VIEWPORT_SIZE,maxHeight = VIEWPORT_VER_SIZE, viewportRatio = slidesStore.viewportRatio
if(viewportRatio<1){
maxWidth = VIEWPORT_VER_SIZE
maxHeight = VIEWPORT_SIZE
}
const slidesData = slides.value
try {
let queryMsg = {
......@@ -200,6 +186,21 @@ const GetTripFiledData = async () =>{
if(!dataRes.data.data) return
datas.DataSource.TravelAatas = dataRes.data.data
if(TempId.value&&!searchData.value.TempId) return
let isHideOverflowText = false
try {
await ElMessageBox.confirm('行程数据可能会超出模板预设宽度或高度,是否自动裁剪','提示',{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
closeOnClickModal:false
})
isHideOverflowText = true
} catch (error) {}
let maxWidth = VIEWPORT_SIZE,maxHeight = VIEWPORT_VER_SIZE, viewportRatio = slidesStore.viewportRatio
if(viewportRatio<1){
maxWidth = VIEWPORT_VER_SIZE
maxHeight = VIEWPORT_SIZE
}
const travel = dataRes.data.data
const cursors = [] as Array<any>
for (let index = 0; index < slidesData.length; index++) {
......@@ -209,6 +210,7 @@ const GetTripFiledData = async () =>{
if(y.TemplateDataSource && y.TemplateDataSource.Content){
let dataPath = y.TemplateDataSource.Content.split('.')
let value=JSON.parse(JSON.stringify(travel));
let parentData:any = null
for (let i = 0; i < dataPath.length; i++) {
const oo = dataPath[i];
if(value && value[oo]){
......@@ -229,6 +231,7 @@ const GetTripFiledData = async () =>{
}else{
value=value[oo]
}
if(i<dataPath.length-1) parentData = value
}else{
value = null
}
......@@ -238,7 +241,6 @@ const GetTripFiledData = async () =>{
if(isHideOverflowText){
const maxLength = getHtmlPlainText(y.content).length
const newValue = maxLength<value.length?value.substring(0,maxLength):value
console.log(newValue,value,maxLength)
y.content= y.content.replace(getHtmlPlainText(y.content),newValue)
}else{
y.content= y.content.replace(getHtmlPlainText(y.content),value)
......@@ -265,8 +267,16 @@ const GetTripFiledData = async () =>{
y.height = tempSize.height
} catch (error) { }
y.src = value[0]
y.fixedRatio = true
}
}
y.below = y.TemplateDataSource.index!=null && y.TemplateDataSource.index>=0 ? y.TemplateDataSource.index:-1
if(parentData && parentData.Id) {
y.dataMapping = {
id:parentData.Id,
filed:dataPath[dataPath.length-1]
}
}
}
}
}
......
......@@ -41,6 +41,8 @@
</Popover>
</ButtonGroup>
<ElementDataMapping v-if="handleElement.dataMapping"></ElementDataMapping>
<Divider />
<ElementColorMask />
<Divider />
......@@ -73,6 +75,7 @@ import ElementShadow from '../common/ElementShadow.vue'
import ElementFlip from '../common/ElementFlip.vue'
import ElementFilter from '../common/ElementFilter.vue'
import ElementColorMask from '../common/ElementColorMask.vue'
import ElementDataMapping from '../common/ElementDataMapping.vue'
import FileInput from '@/components/FileInput.vue'
import Divider from '@/components/Divider.vue'
import Button from '@/components/Button.vue'
......
......@@ -10,6 +10,8 @@
>{{item.label}}</div>
</div>
<ElementDataMapping v-if="handleElement.dataMapping"></ElementDataMapping>
<Divider />
<SelectGroup class="row">
......@@ -334,6 +336,7 @@ import message from '@/utils/message'
import ElementOpacity from '../common/ElementOpacity.vue'
import ElementOutline from '../common/ElementOutline.vue'
import ElementShadow from '../common/ElementShadow.vue'
import ElementDataMapping from '../common/ElementDataMapping'
import ColorButton from '../common/ColorButton.vue'
import TextColorButton from '../common/TextColorButton.vue'
import CheckboxButton from '@/components/CheckboxButton.vue'
......
<template>
<div class="element-data-mapping" v-if="currentId!=0">
<divider />
<div class="q-mb-md row items-center">
<div class="text-small col">数据绑定</div>
<el-checkbox v-model="updateUnionElement" label="更新关联数据" size="small"/>
</div>
<Select
class="col"
:value="currentId"
@update:value="value => changeDataSourceHandler(value)"
:options="selectDataSource"
/>
<div class="row items-center wrap" v-if="handleElement && handleElement.type=='image' && currentImages && currentImages.length>0">
<div class="item-image-box" v-for="(x,i) in currentImages" :key="i" @click="setImageHandler(x,handleElement,handleElement.dataMapping)">
<img :src="x" />
<div class="opera-box" v-if="x == handleElement.src">
<el-icon color="#67C23A" size="32"><SuccessFilled /></el-icon>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import Divider from '@/components/Divider.vue'
import { VIEWPORT_SIZE, VIEWPORT_VER_SIZE } from '@/configs/canvas';
import useHistorySnapshot from '@/hooks/useHistorySnapshot';
import FileService from '@/services/FileService';
import { useMainStore, useSlidesStore } from '@/store';
import { SlideDataSource, TravelDetail, injectKeyDataSource } from '@/types/injectKey';
import { PPTImageElement } from '@/types/slides';
import { getHtmlPlainText } from '@/utils/common';
import { storeToRefs } from 'pinia';
import { inject, onMounted, ref } from 'vue';
const trip = ref<SlideDataSource>()
const { addHistorySnapshot } = useHistorySnapshot()
trip.value = inject(injectKeyDataSource)
const mainStore = useMainStore()
const slidesStore = useSlidesStore()
const { hotelInfo, scenicInfo, dinnerInfo } = trip.value?.TravelAatas??{}
const resources:Array<TravelDetail[]> = [[],hotelInfo??[],scenicInfo??[],dinnerInfo??[]]
const { handleElement } = storeToRefs(mainStore)
const slide = slidesStore.currentSlide
const selectDataSource = ref<{label:string,value:string|number}[]>([])
const currentId = ref<number>(0)
const currentImages = ref<string[]>()
const updateUnionElement = ref<boolean>(true)
let maxWidth = VIEWPORT_SIZE,maxHeight = VIEWPORT_VER_SIZE, viewportRatio = slidesStore.viewportRatio
if(viewportRatio<1){
maxWidth = VIEWPORT_VER_SIZE
maxHeight = VIEWPORT_SIZE
}
if(handleElement.value?.dataMapping){
currentId.value = handleElement.value.dataMapping.id
resources[slide.pageType-1].forEach(x=>{
selectDataSource.value.push({
label:x.Name,
value:x.Id
})
})
}
const changeDataSourceHandler = (value:number) => {
currentId.value = value
const data = resources[slide.pageType-1].find(x=>x.Id==value)
if(data && handleElement.value && handleElement.value.dataMapping){
updateUnionElementsHanlder(value)
}
loadCurrentImages()
addHistorySnapshot()
}
const updateUnionElementsHanlder = (newValue:number) => {
let elements = slide.elements.filter(x=>x.dataMapping && x.below == handleElement.value?.below)
if(!updateUnionElement.value) elements = elements.filter(x=>x.id==handleElement.value?.id)
if(elements) {
const data = resources[slide.pageType-1].find(x=>x.Id==newValue)
elements.forEach(x => {
if(x.dataMapping){
const newDataMapping = {id:newValue, filed:x.dataMapping.filed}
if(x.type == 'text'){
const content = x.content.replace(getHtmlPlainText(x.content),data[x.dataMapping.filed])
const props = {content,dataMapping:newDataMapping}
slidesStore.updateElement({ id: x.id, props})
}else if(x.type == 'image'){
const urls = data[x.dataMapping.filed] as Array<string> ?? ['']
setImageHandler(urls[0],x as PPTImageElement,newDataMapping)
}
}
})
}
}
const setImageHandler = async (url:string, element:PPTImageElement, mapping:any) => {
let props = {
src: url,
width: element.width,
height: element.height,
left: element.left,
top: element.top,
dataMapping:mapping
}
if(url!='' && url!=handleElement.value?.src){
try {
let tempSize = await FileService.getImageSizeWithoutDownloading(url)
if(tempSize.width>maxWidth){
let ratio = maxWidth/tempSize.width
tempSize.width = maxWidth
tempSize.height = tempSize.height*ratio
}
if(tempSize.height>maxHeight){
let ratio = maxHeight/tempSize.height
tempSize.height = maxHeight
tempSize.width = tempSize.width*ratio
}
props = {
src: url,
width: tempSize.width,
height: tempSize.height,
left: element.left,
top: element.top,
dataMapping:mapping
}
if(props.left<0)props.left=0
if(props.top<0)props.top=0
} catch (error) {
}
}
slidesStore.updateElement({ id: element.id, props})
}
const loadCurrentImages = () => {
if(handleElement.value && handleElement.value.dataMapping){
currentImages.value = []
const data = resources[slide.pageType-1].find(x=>x.Id==currentId.value)
if(data){
currentImages.value = data[handleElement.value.dataMapping.filed] as Array<string> ?? []
}
}
}
loadCurrentImages()
</script>
<style scoped>
.item-image-box{
width:calc(33% - 5px);
height:50px;
position:relative;
border-radius: 5px;
background-color: #333;
margin-right: 5px;
margin-top: 5px;
overflow: hidden;
cursor: pointer;
}
.item-image-box img{
object-fit: cover;
width: 100%;
height: auto;
}
.item-image-box .opera-box{
background-color: rgba(0,0,0,.3);
position: absolute;
width: 100%;
height: 100%;
z-index: 3;
left: 0;
top: 0;
display: flex;
align-items: center;
justify-content: center;
}
</style>
\ No newline at end of file
......@@ -23,7 +23,7 @@ import ElementStylePanel from './ElementStylePanel/index.vue'
import ElementPositionPanel from './ElementPositionPanel.vue'
import ElementAnimationPanel from './ElementAnimationPanel.vue'
import ElementTemplateData from './ElementTemplateData.vue'
import EditDatas from './EditDatas.vue'
// import EditDatas from './EditDatas.vue'
import SlideDesignPanel from './SlideDesignPanel.vue'
import SlideAnimationPanel from './SlideAnimationPanel.vue'
import MultiPositionPanel from './MultiPositionPanel.vue'
......@@ -43,11 +43,10 @@ const elementTabs = computed<ElementTabs[]>(() => {
if (handleElement.value?.type === 'text') {
return [
{ label: '样式', key: ToolbarStates.EL_STYLE },
// { label: '数据', key: ToolbarStates.EDIT_DATAS },
{ label: '符号', key: ToolbarStates.SYMBOL },
{ label: '位置', key: ToolbarStates.EL_POSITION },
{ label: '动画', key: ToolbarStates.EL_ANIMATION },
{ label: '编辑', key: ToolbarStates.EDIT_DATAS },
{ label: '动画', key: ToolbarStates.EL_ANIMATION }
]
}
return [
......@@ -99,7 +98,7 @@ const currentPanelComponent = computed(() => {
[ToolbarStates.EL_STYLE]: ElementStylePanel,
[ToolbarStates.EL_POSITION]: ElementPositionPanel,
[ToolbarStates.EL_ANIMATION]: ElementAnimationPanel,
[ToolbarStates.EDIT_DATAS]: EditDatas,
// [ToolbarStates.EDIT_DATAS]: EditDatas,
[ToolbarStates.EL_TEMPLATEDATA]: ElementTemplateData,
[ToolbarStates.SLIDE_DESIGN]: SlideDesignPanel,
[ToolbarStates.SLIDE_ANIMATION]: SlideAnimationPanel,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment