Commit e83e74eb authored by 罗超's avatar 罗超

Merge branch 'master' of http://gitlab.oytour.com/viitto/pptist

# Conflicts:
#	src/App.vue
parents 2f4e69e0 f23b1243
<template>
<div v-if="isFinish" style="height: 100vh;overflow: auto;background: rgb(243, 246, 251);">
<div v-if="isFinish" style="height: 100vh;background: rgb(243, 246, 251);">
<Screen v-if="screening" />
<Market v-else-if="market"></Market>
<Editor v-else-if="_isPC" />
......@@ -10,20 +10,20 @@
<script lang="ts" setup>
import { onMounted,ref,provide } from 'vue'
import { storeToRefs } from 'pinia'
import { useScreenStore, useMainStore, useSnapshotStore } from '@/store'
import { useScreenStore, useMainStore, useSnapshotStore, useFontStore } from '@/store'
import { LOCALSTORAGE_KEY_DISCARDED_DB } from '@/configs/storage'
import { deleteDiscardedDB } from '@/utils/database'
import { isPC, query } from './utils/common'
import { userStore } from './store/user'
import { injectKeyTemplate } from '@/types/injectKey'
import ConfigService from '@/services/ConfigService'
import Editor from './views/Editor/index.vue'
import Screen from './views/Screen/index.vue'
import Mobile from './views/Mobile/index.vue'
import Market from './views/Market/Index.vue'
import { useFontStore } from './store/font'
import { ElMessageBox } from 'element-plus'
import NewFile from './views/Market/newFile.vue'
import { ElMessage, ElMessageBox } from 'element-plus'
const searchData = ref({} as any)
provide(injectKeyTemplate,searchData)
......@@ -32,32 +32,39 @@ const isFinish = ref(false)
const _isPC = isPC()
const mainStore = useMainStore()
const snapshotStore = useSnapshotStore()
const modelStore = useScreenStore()
const ConfigIdStore = useScreenStore()
const marketStore = useScreenStore()
const isModelStore = useScreenStore()
const TempIdStore = useScreenStore()
const { databaseId } = storeToRefs(mainStore)
const { screening, market, model, ConfigId } = storeToRefs(useScreenStore())
const { userInfo } = storeToRefs(userStore())
if (process.env.NODE_ENV === 'production') {
window.onbeforeunload = () => false
}
const userLoginHandler = async ()=>{
let param = query()
let userId = 1
let ConfigId = 0
let model = 1
let ConfigId = 0 // 9117
let model = 0
if(param.uid) userId=parseInt(param.uid)
if(param.ConfigId) ConfigId=parseInt(param.ConfigId)
if(param.model) model=parseInt(param.model)
ConfigIdStore.setConfigId(ConfigId)
modelStore.setModel(model)
try {
await userStore().setUserLoginAsync(userId)
await useFontStore().loadAllFonts()
} catch (error) {
if(!userInfo.value.EmployeeId){
try {
await userStore().setUserLoginAsync(userId,ConfigId)
await useFontStore().loadAllFonts()
if(ConfigId) await GetTripConfig(ConfigId)
} catch (error) {}
}else{
if(ConfigId) await GetTripConfig(ConfigId)
}
isFinish.value=true
if(!ConfigId&&!model) ElMessageBox.confirm(
......@@ -72,7 +79,6 @@ const userLoginHandler = async ()=>{
.catch(() => {})
}
userLoginHandler()
onMounted(async () => {
await deleteDiscardedDB()
......@@ -80,6 +86,33 @@ onMounted(async () => {
mainStore.setAvailableFonts()
})
/**
* 根据团期配置编号获取行程详情
*/
const GetTripConfig = async (ConfigId) =>{
try {
let queryMsg = {
ConfigId: ConfigId
}
let datasRes = await ConfigService.triptemplateGetTripConfig(queryMsg);
if (datasRes.data.resultCode == 1) {
if(datasRes.data.data&&datasRes.data.data.length>0&&datasRes.data.data[0].TempId){
isModelStore.setIsModel(true)
marketStore.setMarket(!market)
TempIdStore.setTempId(datasRes.data.data[0].TempId)
}
}else{
return ElMessage({
showClose: true,
message: datasRes.data.message,
type: 'warning',
})
}
} catch (error) {
console.log("triptemplate_GetTripConfig", error);
}
}
userLoginHandler()
// 应用注销时向 localStorage 中记录下本次 indexedDB 的数据库ID,用于之后清除数据库
window.addEventListener('unload', () => {
const discardedDB = localStorage.getItem(LOCALSTORAGE_KEY_DISCARDED_DB)
......
import Axios, {
AxiosResponse,
InternalAxiosRequestConfig,
AxiosError,
} from 'axios';
import { domainManager } from '../utils/domainManager'
let datas: AxiosResponse
export enum ApiResult{
'SUCCESS' = 1,
'FAILED' = 0,
'TOKEN_INVALID' = 10000,
'TOKEN_ILLEGAL' = 10001,
}
/**
* get status code
* @param {AxiosResponse} response Axios response object
*/
const getErrorCode2text = (response: AxiosResponse): string => {
const code = response.status
let message = 'Request Error'
switch (code) {
case 400:
message = 'Request Error'
break
case 401:
message = 'Unauthorized, please login'
break
case 403:
message = '拒绝访问'
break
case 404:
message = '访问资源不存在'
break
case 408:
message = '请求超时'
break
case 500:
message = '位置错误'
break
case 501:
message = '承载服务未实现'
break
case 502:
message = '网关错误'
break
case 503:
message = '服务暂不可用'
break
case 504:
message = '网关超时'
break
case 505:
message = '暂不支持的 HTTP 版本'
break
default:
message = '位置错误'
}
return message
}
/**
* @returns {AxiosResponse} result
* @tutorial see more:https://github.com/onlyling/some-demo/tree/master/typescript-width-axios
* @example
* service.get<{data: string; code: number}>('/test').then(({data}) => { console.log(data.code) })
*/
const service = Axios.create({
baseURL: domainManager().UploadUrl,
timeout: 20000,
headers: {
"Content-Type": "application/x-www-form-urlencoded;"
}
})
/**
* @description 请求发起前的拦截器
* @returns {AxiosRequestConfig} config
*/
service.interceptors.request.use(
async (config: InternalAxiosRequestConfig) => {
return config;
},
error => {
//TODO: 新增网络请求异常处理业务
return Promise.reject(error)
}
)
/**
* @description 响应收到后的拦截器
* @returns {}
*/
service.interceptors.response.use(
/** 请求有响应 */
async (response: AxiosResponse) => {
if (response.status === 200) {
if(response.data.resultCode == ApiResult.TOKEN_ILLEGAL || response.data.resultCode == ApiResult.TOKEN_INVALID){
}
datas = response
return Promise.resolve(datas)
} else {
const __text = getErrorCode2text(response)
return Promise.reject(new Error(__text))
}
},
/** 请求无响应 */
(error: AxiosError) => {
if (error && error.response) {
const __text = getErrorCode2text(error.response);
return Promise.reject(new Error(__text));
} else {
return Promise.reject(new Error('unknow error'));
}
}
)
export default service
......@@ -27,10 +27,40 @@ export default () => {
const slidesStore = useSlidesStore()
const coverImgStore = useScreenStore()
const isCoverImgStore = useScreenStore()
const FeatureImgStore = useScreenStore()
const { slides, theme, viewportRatio, title } = storeToRefs(slidesStore)
const { isCoverImg } = storeToRefs(useScreenStore())
const { isCoverImg, FeatureImg } = storeToRefs(useScreenStore())
const exporting = ref(false)
// 生成行程图片
const exportFeatureImg = (domRef: HTMLElement, format: string, quality: number, ignoreWebfont = true, i: number) => {
const toImage = format === 'png' ? toPng : toJpeg
const foreignObjectSpans = domRef.querySelectorAll('foreignObject [xmlns]')
foreignObjectSpans.forEach(spanRef => spanRef.removeAttribute('xmlns'))
setTimeout(() => {
const config: ExportImageConfig = {
quality,
width: 1600,
}
if (ignoreWebfont) config.fontEmbedCSS = ''
toImage(domRef, config).then(dataUrl => {
const obj = {
index: i,
url: dataUrl
}
FeatureImg.value.push(obj)
FeatureImgStore.setFeatureImg(JSON.parse(JSON.stringify(FeatureImg.value)))
}).catch(() => {
message.error('导出图片失败')
})
}, 200)
}
// 导出图片
const exportImage = (domRef: HTMLElement, format: string, quality: number, ignoreWebfont = true) => {
exporting.value = true
......@@ -49,7 +79,7 @@ export default () => {
exporting.value = false
if (isCoverImg.value) {
coverImgStore.setCoverImg(dataUrl)
isCoverImgStore.setIsCoverImg(false)
isCoverImgStore.setIsCoverImg(false)
} else {
saveAs(dataUrl, `${title.value}.${format}`)
}
......@@ -783,6 +813,7 @@ export default () => {
return {
exporting,
exportFeatureImg,
exportImage,
exportJSON,
exportSpecificFile,
......
import Api,{ HttpResponse, Result } from './../utils/requestUpload';
import { domainManager } from './../utils/domainManager'
/**
* 配置相关方法
*/
class UploadService{
/**
* base64Str上传
*/
static async UploadBase64Two(path:any, params: any):Promise<HttpResponse>{
let apiurl = `${domainManager().UploadUrl}/Upload/UploadBase64${path}`
return Api.Post(apiurl,params)
}
}
export default UploadService;
\ No newline at end of file
......@@ -9,9 +9,12 @@ export interface ScreenState {
ConfigId:number,
TemplateType: [],
TemplateDataSource: [],
TempId: number,
CoverImg: any,
isCoverImg: boolean,
dataLoading: boolean,
FeatureImg: any
}
export const useScreenStore = defineStore('screen', {
......@@ -23,9 +26,11 @@ export const useScreenStore = defineStore('screen', {
ConfigId: 0,
TemplateType: [], // 数据源分类
TemplateDataSource: [], // 所有数据源
TempId: 0, // 模版Id
CoverImg: null, // 封面图
isCoverImg: false, // 封面
dataLoading: false, // 记录保存是否成功
FeatureImg: [], // 行程特色图
}),
actions: {
......@@ -59,8 +64,14 @@ export const useScreenStore = defineStore('screen', {
setTemplateDataSource(TemplateDataSource: []) {
this.TemplateDataSource = TemplateDataSource
},
setTempId(TempId: number) {
this.TempId = TempId
},
setDataLoading(dataLoading: boolean) {
this.dataLoading = dataLoading
},
setFeatureImg(FeatureImg: any) {
this.FeatureImg = FeatureImg
}
},
})
\ No newline at end of file
......@@ -36,7 +36,15 @@ export const useSlidesStore = defineStore('slides', {
state: (): SlidesState => ({
title: '未命名演示文稿', // 幻灯片标题
theme: theme, // 主题样式
slides: slides, // 幻灯片页面数据
slides: [{
id: 'test-slide-1',
pageType: 1,
elements: [],
background: {
type: 'solid',
color: '#ffffff',
},
}], // 幻灯片页面数据 slides
slideIndex: 0, // 当前页面索引
viewportRatio: 0.7069, // 可视区域比例,默认16:9 0.5625
layoutSlides: slides, // 所有模版数据
......
export const domainManager = () => {
const locationName = window.location.hostname
console.log(window.location.hostname, '=====hostname')
const obj = {
domainUrl: locationName.indexOf('oytour') !== -1 ? "http://reborn.oytour.com" : "http://192.168.10.214/api/common/post",
//上传站点
UploadUrl: locationName.indexOf('oytour') !== -1 ? "http://upload.oytour.com" : "http://192.168.10.214:8120",
//文件站点
ViittoFileUrl: locationName.indexOf('oytour') !== -1 ? "http://imgfile.oytour.com" : 'http://192.168.10.214:8130',
}
return obj
}
\ No newline at end of file
import service, { ApiResult } from "../configs/axiosUpload";
export interface HttpResponse {
status: number
statusText: string
data: Result
}
export interface Result {
resultCode: ApiResult
message: string
data: any
[key: string]: any
}
class Api{
constructor(){ }
static Post = (path:string,base64Str:any): Promise<HttpResponse>=>{
return service.post(path,base64Str)
}
}
export default Api;
......@@ -94,19 +94,35 @@
<LinkDialog @close="linkDialogVisible = false" />
</Modal>
</div>
<div style="position: relative;z-index: -1;">
<div class="export-img-dialog">
<div class="thumbnails-view">
<div class="thumbnails" v-for="slide in slides" ref="FeatureImgRef">
<ThumbnailSlide
class="thumbnail"
:key="slide.id"
:slide="slide"
:size="1600"
/>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { nextTick, onMounted, onUnmounted, provide, ref, watch, watchEffect } from 'vue'
import { nextTick, onMounted, onUnmounted, provide, ref, reactive, watch, watchEffect, computed, inject } from 'vue'
import { throttle } from 'lodash'
import { storeToRefs } from 'pinia'
import { useMainStore, useSlidesStore, useKeyboardStore } from '@/store'
import { useMainStore, useSlidesStore, useKeyboardStore, useScreenStore } from '@/store'
import type { ContextmenuItem } from '@/components/Contextmenu/types'
import type { PPTElement } from '@/types/slides'
import type { AlignmentLineProps, CreateCustomShapeData } from '@/types/edit'
import { injectKeySlideScale } from '@/types/injectKey'
import { removeAllRanges } from '@/utils/selection'
import { KEYS } from '@/configs/hotkey'
import useExport from '@/hooks/useExport'
import { injectKeyDataSource } from '@/types/injectKey'
import useViewportSize from './hooks/useViewportSize'
import useMouseSelection from './hooks/useMouseSelection'
......@@ -138,6 +154,7 @@ import MultiSelectOperate from './Operate/MultiSelectOperate.vue'
import Operate from './Operate/index.vue'
import LinkDialog from './LinkDialog.vue'
import Modal from '@/components/Modal.vue'
import ThumbnailSlide from '@/views/components/ThumbnailSlide/index.vue'
const mainStore = useMainStore()
const {
......@@ -153,12 +170,45 @@ const {
canvasScale,
textFormatPainter,
} = storeToRefs(mainStore)
const { currentSlide } = storeToRefs(useSlidesStore())
const { currentSlide, slides } = storeToRefs(useSlidesStore())
const { ctrlKeyState, spaceKeyState } = storeToRefs(useKeyboardStore())
const viewportRef = ref<HTMLElement>()
const alignmentLines = ref<AlignmentLineProps[]>([])
const { exportFeatureImg } = useExport()
const datas = reactive({
FeatureImgList: [],
loading: false
})
datas.FeatureImgList = inject(injectKeyDataSource).FeatureImgList
const FeatureImgStore = useScreenStore()
const { market, model, ConfigId, CoverImg, dataLoading, FeatureImg } = storeToRefs(useScreenStore())
const renderSlides = computed(() => {
return slides.value
})
const FeatureImgRef = ref(null)
// 将界面生成形成图
watch(() => slides.value, (n,o) =>{
FeatureImgStore.setFeatureImg([])
setTimeout(()=>{
for(let i=0;i<FeatureImgRef.value.length;i++){
exportFeatureImg(FeatureImgRef.value[i], 'jpeg', 1, true,i+1)
}
},500)
})
// 监听请求保存成功 重新请求数据
watch(() => FeatureImgRef.value, (n,o) =>{
})
watch(() => FeatureImg.value, (n,o) =>{
FeatureImg.value.sort((a,b)=>{
return a.index-b.index
})
})
const linkDialogVisible = ref(false)
const openLinkDialog = () => linkDialogVisible.value = true
......
......@@ -109,10 +109,10 @@
x.TemplateDataSource.Content = obj.Content
x.TemplateDataSource.Name = obj.Name
if(x.type=="text"){
x.FiledTypeStr = obj.Name
x.content = x.content.replace(getHtmlPlainText(x.content),obj.Name)
}
// if(x.type=="text"){
// x.FiledTypeStr = obj.Name
// x.content = x.content.replace(getHtmlPlainText(x.content),obj.Name)
// }
}
})
setNewDatas()
......
......@@ -18,8 +18,8 @@
</FileInput>
<PopoverMenuItem @click="setDialogForExport('pptx')">导出文件</PopoverMenuItem>
<PopoverMenuItem @click="resetSlides(); mainMenuVisible = false">重置幻灯片</PopoverMenuItem>
<PopoverMenuItem @click="goLink('https://github.com/pipipi-pikachu/PPTist/issues')">意见反馈</PopoverMenuItem>
<PopoverMenuItem @click="goLink('https://github.com/pipipi-pikachu/PPTist/blob/master/doc/Q&A.md')">常见问题</PopoverMenuItem>
<!-- <PopoverMenuItem @click="goLink('https://github.com/pipipi-pikachu/PPTist/issues')">意见反馈</PopoverMenuItem>
<PopoverMenuItem @click="goLink('https://github.com/pipipi-pikachu/PPTist/blob/master/doc/Q&A.md')">常见问题</PopoverMenuItem> -->
<PopoverMenuItem @click="mainMenuVisible = false; hotkeyDrawerVisible = true">快捷键</PopoverMenuItem>
</template>
<div class="menu-item"><IconHamburgerButton class="icon" /></div>
......@@ -77,6 +77,8 @@
保存行程
</template>
</el-button>
<div>
</div>
<!-- <a class="github-link" href="https://github.com/pipipi-pikachu/PPTist" target="_blank">
<div class="menu-item"><IconGithub class="icon" /></div>
</a> -->
......@@ -93,11 +95,15 @@
<FullscreenSpin :loading="exporting" tip="正在导入..." />
<Psd-Upload :visible="psdVisibleStatus" @closed="psdVisibleStatus=false"></Psd-Upload>
</div>
</template>
<script lang="ts" setup>
import { nextTick, ref, reactive, inject } from 'vue'
import { nextTick, ref, reactive, inject, computed, watch } from 'vue'
import { storeToRefs } from 'pinia'
import { toPng, toJpeg } from 'html-to-image'
import message from '@/utils/message'
import { useMainStore, useSlidesStore } from '@/store'
import useScreening from '@/hooks/useScreening'
import useImport from '@/hooks/useImport'
......@@ -109,6 +115,11 @@ import ConfigService from '@/services/ConfigService'
import { injectKeyDataSource, injectKeyTemplate } from '@/types/injectKey'
import PsdUpload from '@/components/PSD/Index.vue'
import { svg2Base64 } from '@/utils/svg2Base64'
import UploadService from '@/services/UploadService'
import { domainManager } from '@/utils/domainManager'
import { ToolbarStates } from '@/types/toolbar'
import HotkeyDoc from './HotkeyDoc.vue'
import FileInput from '@/components/FileInput.vue'
......@@ -118,6 +129,7 @@ import Input from '@/components/Input.vue'
import Popover from '@/components/Popover.vue'
import PopoverMenuItem from '@/components/PopoverMenuItem.vue'
const mainStore = useMainStore()
const slidesStore = useSlidesStore()
const layoutsStore = useSlidesStore()
......@@ -126,18 +138,22 @@ const { enterScreening, enterScreeningFromStart } = useScreening()
const { importSpecificFile, importPPTXFile, exporting } = useImport()
const { resetSlides } = useSlideHandler()
const imageThumbnailsRef = ref<HTMLElement>()
const mainMenuVisible = ref(false)
const hotkeyDrawerVisible = ref(false)
const editingTitle = ref(false)
const titleInputRef = ref<InstanceType<typeof Input>>()
const titleValue = ref('')
const { userInfo } = storeToRefs(userStore())
const datas = reactive({
FeatureImgList:[],
DataSource:{},
loading: false
})
datas.FeatureImgList = inject(injectKeyDataSource).FeatureImgList
const queryObj = ref({} as any)
const searchData = ref({} as any)
datas.DataSource = inject(injectKeyDataSource)
......@@ -146,8 +162,12 @@ searchData.value = inject(injectKeyTemplate)
const marketStore = useScreenStore()
const CoverImgStore = useScreenStore()
const dataLoadingStore = useScreenStore()
const TempIdStore = useScreenStore()
const FeatureImgStore = useScreenStore()
const ConfigIdStore = useScreenStore()
const psdVisibleStatus = ref(false)
const { market, model, ConfigId, CoverImg, dataLoading } = storeToRefs(useScreenStore())
const { market, model, ConfigId, CoverImg, dataLoading, TempId, FeatureImg } = storeToRefs(useScreenStore())
// 返回到首页
const goBack = () =>{
......@@ -163,7 +183,8 @@ const goBack = () =>{
}
]
if(model.value) {
searchData.value.TempId = 0
// searchData.value.TempId = 0
TempIdStore.setTempId(0)
marketStore.setMarket(true)
slidesStore.setSlides(list)
layoutsStore.setLayouts([])
......@@ -180,7 +201,8 @@ const goBack = () =>{
}
)
.then(() => {
searchData.value.TempId = 0
// searchData.value.TempId = 0
TempIdStore.setTempId(0)
marketStore.setMarket(true)
slidesStore.setSlides(list)
layoutsStore.setLayouts([])
......@@ -196,7 +218,6 @@ const UploadPsdHandler = () => {
psdVisibleStatus.value = true
}
// 新增修改模版
const SetTripTemplateSlide = async () => {
// console.log(JSON.parse(queryObj.value.TempData),'--------')
......@@ -204,93 +225,160 @@ const SetTripTemplateSlide = async () => {
console.log(queryObj.value,'新增修改模版---')
let TemplateRes = await ConfigService.SetTripTemplateSlide(queryObj.value);
if (TemplateRes.data.resultCode == 1) {
if(!queryObj.value.TempId){
queryObj.value.TempId = TemplateRes.data.data.TempId
searchData.value.TempId = TemplateRes.data.data.TempId
}
ElMessage({
showClose: true,
message: '操作成功',
type: 'success',
})
dataLoadingStore.setDataLoading(true)
}else{
ElMessage({
showClose: true,
message: '操作失败',
type: 'warning',
})
}
dataLoadingStore.setDataLoading(true)
datas.loading = false
} catch (error) {
datas.loading = false
console.log("TemplateGetTripFiled", error);
ElMessage({
showClose: true,
message: '操作失败',
type: 'warning',
})
}
}
// 用户新增修改数据
const SetTripTemplateConfig = async () => {
try {
try {
let queryMsg = {
ConfigId: ConfigId.value,
TempId: queryObj.value.TempId,
TempData: queryObj.value.TempData
TempData: queryObj.value.TempData,
FeatureImg: datas.FeatureImgList
}
console.log(queryMsg,'新增修改团数据---')
let TemplateRes = await ConfigService.SetSetTripConfig(queryMsg);
if (TemplateRes.data.resultCode == 1) {
}
dataLoadingStore.setDataLoading(true)
datas.loading = false
} catch (error) {
datas.loading = false
console.log("TemplateGetTripFiled", error);
}
let TemplateRes = await ConfigService.SetSetTripConfig(queryMsg);
if (TemplateRes.data.resultCode == 1) {
if(!ConfigId.value){
ConfigIdStore.value = TemplateRes.data.data.ConfigId
}
ElMessage({
showClose: true,
message: '操作成功',
type: 'success',
})
dataLoadingStore.setDataLoading(true)
}else{
ElMessage({
showClose: true,
message: '操作失败',
type: 'warning',
})
}
FeatureImgStore.setFeatureImg([])
datas.loading = false
} catch (error) {
FeatureImgStore.setFeatureImg([])
datas.loading = false
ElMessage({
showClose: true,
message: '操作失败',
type: 'warning',
})
}
}
// 保存
const setTemplate = async () =>{
let arr = JSON.parse(JSON.stringify(slides.value))
if(dataLoading.value){
dataLoadingStore.setDataLoading(false)
}
// console.log(JSON.stringify(slides.value),'----保存接口',queryObj.value)
if(model.value&&userInfo.value.IsEditTripTemplate==1){
arr.forEach(x=>{
x.elements.forEach(y=>{
delete y.TemplateList
})
})
if(CoverImg&&CoverImg.value) queryObj.value.CoverImg = CoverImg.value
else return ElMessage({
showClose: true,
message: '请生成封面图',
type: 'warning',
else {
mainStore.setToolbarState(ToolbarStates.EL_TEMPLATEDATA)
return ElMessage({
showClose: true,
message: '请生成封面图',
type: 'warning',
})
}
if(queryObj.value.Title==''||!queryObj.value.LineId||!queryObj.value.LtId
||queryObj.value.CoverImg==''
||queryObj.value.CountryName==''||queryObj.value.SeasonName==''
||queryObj.value.ColorName==''||queryObj.value.ColorStr==''||queryObj.value.LineName==''){
mainStore.setToolbarState(ToolbarStates.EL_TEMPLATEDATA)
return ElMessage({
showClose: true,
message: '请完善右侧模版数据',
type: 'warning',
})
}
} else {
arr.forEach(x=>{
x.elements.forEach(y=>{
delete y.TemplateList
delete y.TemplateDataSource
})
})
}
for(let i=0;i<slides.value.length;i++){
if(slides.value[i].elements.length==0) {
mainStore.setToolbarState(ToolbarStates.EL_TEMPLATEDATA)
return ElMessage({
showClose: true,
message: `请设计 第 ${i+1} 页 的数据`,
type: 'warning',
})
}
// for(let j=0;j<slides.value[i].elements.length;j++){
// if(model.value&&userInfo.value.IsEditTripTemplate==1){
// if(slides.value[i].elements[j].type=="text"||slides.value[i].elements[j].type=="image"){
// if(!slides.value[i].elements[j].TemplateDataSource
// ||!slides.value[i].elements[j].TemplateDataSource.Id) {
// return ElMessage({
// showClose: true,
// message: `请完善 第 ${i+1} 页 需要绑定的数据源`,
// type: 'warning',
// })
// }
// }
// }
// }
}
queryObj.value.TempData = JSON.stringify(slides.value)
// console.log(arr,'-------tttt')
queryObj.value.TempData = JSON.stringify(arr)
datas.loading = true
if(model.value&&userInfo.value.IsEditTripTemplate==1){
await SetTripTemplateSlide()
}else if(ConfigId.value){
await SetTripTemplateConfig()
}
if(ConfigId.value){
datas.FeatureImgList = []
FeatureImg.value.forEach(item=>{
setTimeout(()=>{
setFeatureImgList(item.url)
},300)
})
}
}
// 上传文件
const setFeatureImgList = async (url) => {
try {
let queryObj = {
MyFile: url
}
let path = `?fileType=1&fileLimit=5&&filePath=Feature/${ConfigId.value}_`
let Res = await UploadService.UploadBase64Two(path,queryObj);
if (Res.data&&Res.data.FilePath) {
datas.FeatureImgList.push(`${domainManager().ViittoFileUrl}/${Res.data.FilePath}`)
if(datas.FeatureImgList.length==slides.value.length){
await SetTripTemplateConfig()
}
}
} catch (error) {
}
}
const startEditTitle = () => {
titleValue.value = title.value
......@@ -315,6 +403,70 @@ const setDialogForExport = (type: DialogForExportTypes) => {
</script>
<style lang="scss" scoped>
.export-img-dialog {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
position: relative;
overflow: hidden;
}
.thumbnails-view {
@include absolute-0();
&::after {
content: '';
background-color: #fff;
@include absolute-0();
}
}
.configs {
width: 350px;
height: calc(100% - 100px);
display: flex;
flex-direction: column;
justify-content: center;
z-index: 1;
.row {
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 25px;
}
.title {
width: 100px;
position: relative;
&::after {
content: attr(data-range);
position: absolute;
top: 20px;
left: 0;
}
}
.config-item {
flex: 1;
}
}
.btns {
width: 300px;
height: 100px;
display: flex;
justify-content: center;
align-items: center;
z-index: 1;
.export {
flex: 1;
}
.close {
width: 100px;
margin-left: 10px;
}
}
.editor-header {
background-color: #fff;
user-select: none;
......
......@@ -64,19 +64,20 @@ import ThumbnailSlide from '@/views/components/ThumbnailSlide/index.vue'
import LayoutPool from './LayoutPool.vue'
import Popover from '@/components/Popover.vue'
import Draggable from 'vuedraggable'
const mainStore = useMainStore()
const slidesStore = useSlidesStore()
const layoutsStore = useSlidesStore()
const keyboardStore = useKeyboardStore()
const { selectedSlidesIndex: _selectedSlidesIndex, thumbnailsFocus } = storeToRefs(mainStore)
const { slides, slideIndex, layoutSlides } = storeToRefs(slidesStore)
const { slides, currentSlide, slideIndex, layoutSlides } = storeToRefs(slidesStore)
const { ctrlKeyState, shiftKeyState } = storeToRefs(keyboardStore)
const { slidesLoadLimit } = useLoadSlides()
const TemplateTypeStore = useScreenStore()
const CoverImgStore = useScreenStore()
const dataLoadingStore = useScreenStore()
const { ConfigId, TemplateDataSource, TemplateType, dataLoading } = storeToRefs(TemplateTypeStore)
const { ConfigId, TemplateDataSource, TemplateType, dataLoading, TempId } = storeToRefs(TemplateTypeStore)
const selectedSlidesIndex = computed(() => [..._selectedSlidesIndex.value, slideIndex.value])
const presetLayoutPopoverVisible = ref(false)
......@@ -235,6 +236,7 @@ const GetTripTemplate = async () =>{
queryObj.value.ColorName = dataRes.data.data.ColorName
queryObj.value.ColorStr = dataRes.data.data.ColorStr
queryObj.value.TempType = dataRes.data.data.TempType
slidesStore.updateSlideIndex(0)
if(ConfigId.value==0) return
await GetTripFiledData(1)
}
......
......@@ -54,7 +54,7 @@
<div class="row wrap q-mt-md">
<el-select v-model="queryObj.LineId" class="m-2"
filterable placeholder="请选择线路"
@change="onLineChangeHandler">
@change="onLineChangeHandler(1)">
<el-option
v-for="item in lines"
:key="item.LineID"
......@@ -267,6 +267,7 @@
let response = await LineService.GetLineListAsync()
if (response.data.resultCode == 1) {
lines.value = response.data.data;
onLineChangeHandler()
}
} catch (error) {
console.log("getLinesHandler", error);
......@@ -276,12 +277,14 @@
/**
* 获取系列列表
*/
const onLineChangeHandler = async () =>{
const onLineChangeHandler = async (type) =>{
let obj = lines.value.find(x=>{
return x.LineID == queryObj.value.LineId
})
queryObj.value.LtId = null
queryObj.value.LineName = obj.LineName
if(type){
queryObj.value.LtId = null
}
if(obj&&obj.LineName) queryObj.value.LineName = obj.LineName
try {
let queryMsg = {
lineID: queryObj.value.LineId,
......
......@@ -120,7 +120,7 @@
:value="viewportRatio"
@update:value="value => updateViewportRatio(value as number)"
:options="[
{ label: '横屏', value: 0.7069 },
{ label: '横屏', value: 0.7723 },
{ label: '竖屏', value: 1.414 },
]"
/>
......@@ -337,7 +337,7 @@ const { formatFonts } = storeToRefs(useFontStore())
const moreThemeConfigsVisible = ref(false)
const background = computed(() => {
if (!currentSlide.value.background) {
if (!currentSlide.value||!currentSlide.value.background) {
return {
type: 'solid',
value: '#fff',
......
......@@ -51,14 +51,27 @@ import Modal from '@/components/Modal.vue'
import DataaSource from './DataaSource/index.vue'
import ConfigService from '@/services/ConfigService'
const slidesStore = useSlidesStore()
const TemplateTypeStore = useScreenStore()
const TempDataSourceStore = useScreenStore()
const { slides, slideIndex } = storeToRefs(slidesStore)
const { TemplateType, ConfigId, TempId} = storeToRefs(TemplateTypeStore)
const pageTypesList = ref([] as any)
const searchData = ref({} as any)
searchData.value = inject(injectKeyTemplate)
let TempIds = 0
if(TempId.value&&!searchData.value.TempId) TempIds = TempId.value
if(searchData.value.TempId) TempIds = searchData.value.TempId
const datas = reactive({
DataSource:{
ConfigId: inject(injectKeyTemplate).ConfigId?inject(injectKeyTemplate).ConfigId:0,
FeatureImgList: [],
ConfigId: ConfigId.value?ConfigId.value:0,
pageType: 1,//1基础 2酒店 3景 4餐
DataSourceOverlay: false,
DataSourceList:[],
queryObj:{
TempId: inject(injectKeyTemplate).TempId?inject(injectKeyTemplate).TempId:0,//编号(新增传0) 是 [int]
TempId: TempIds,//编号(新增传0) 是 [int]
LineId: null,//线路Id 是 [int]
LineName: '',//线路名称 是 [string]
LtId: null,//系列Id 是 [int]
......@@ -76,14 +89,6 @@ const datas = reactive({
})
const slidesStore = useSlidesStore()
const TemplateTypeStore = useScreenStore()
const TempDataSourceStore = useScreenStore()
const { slides, slideIndex } = storeToRefs(slidesStore)
const { TemplateType } = storeToRefs(TemplateTypeStore)
const pageTypesList = ref([] as any)
watch(() => datas.DataSource, () => {
provide(injectKeyDataSource,datas.DataSource)
})
......
<template>
<div style="background: #f3f6fb;height:100vh;">
<div style="background: #f3f6fb;height:100vh;overflow: auto;">
<div style="padding: 30px; max-width:1440px; margin:0 auto;">
<el-row justify="space-between">
<el-col :span="6">
......@@ -20,7 +20,7 @@
</el-col>
</el-row>
<div class="q-mt-lg bg-white rounded text-nowrap" style="padding: 20px 20px 0 20px;">
<div class="row text-small items-center" style="flex-wrap: wrap;">
<div class="row text-small items-center wrap">
<span style="margin-right: 50px;">适用线路:</span>
<el-check-tag :checked="queryObj.LineId == 0" @change="onLineChangeHandler(0)"
class="text-small q-mr-md">全部</el-check-tag>
......@@ -30,7 +30,7 @@
<el-divider style="margin:12px 0;border-top-color:#f3f6fb;"></el-divider>
<div class="row wrap q-pb-md">
<div class="q-pb-md">
<div class="row text-small items-center">
<div class="row text-small items-center wrap">
<span style="margin-right: 50px;">适用国家:</span>
<el-check-tag :checked="queryObj.CountryName == ''" @change="onCountryNameChangeHandler('')"
class="text-small q-mr-md">通用</el-check-tag>
......@@ -43,7 +43,7 @@
<div class="MarketVerticalLine">&nbsp;</div>
</div>
<div class="q-pb-md">
<div class="row text-small items-center">
<div class="row text-small items-center wrap">
<span style="margin-right: 50px;">季节:</span>
<el-check-tag :checked="queryObj.SeasonName == ''" @change="onSeasonNameChangeHandler('')"
class="text-small q-mr-md">通用</el-check-tag>
......@@ -55,7 +55,7 @@
<div class="MarketVerticalLine">&nbsp;</div>
</div>
<div class="q-pb-md">
<div class="row text-small items-center">
<div class="row text-small items-center wrap">
<span style="margin-right: 50px;">颜色:</span>
<el-check-tag :checked="queryObj.ColorName == ''" @change="onColorNameChangeHandler('')"
class="text-small q-mr-md">通用</el-check-tag>
......@@ -68,6 +68,27 @@
<div style="margin-top: 20px;" v-loading="loading">
<div v-if="dataList.length>0" class="q-mt-lg row wrap bg-white rounded" style="padding: 30px 10px 0 10px;">
<template v-for="(item,index) in dataList">
<div class="MarketIndexListBox">
<div class="MarketIndexList bg-white rounded">
<div class="MarketIndexList-Hover">
<div class="q-mr-lg">
<el-button type="primary" v-tooltip="'详情'"
icon="Tickets" circle style="color: #ffff;"
@click="getTemplate(item)"></el-button>
</div>
<div class="q-ml-lg">
<el-button type="primary" v-tooltip="'选择该模版'"
icon="Pointer" circle style="color: #ffff;"
@click="goToTemplate(item)"></el-button>
</div>
</div>
<div class="MarketIndexList-img">
<el-image style="width: 100%;height: 100%" :src="item.CoverImg" fit="cover" />
</div>
<div class="MarketIndexList-text">{{item.Title}}</div>
</div>
</div>
<!--
<el-popover effect="light" trigger="hover" placement="bottom-start" width="auto" height="auto">
<template #default>
<LayoutPool v-loading="queryObj.loading" />
......@@ -78,13 +99,12 @@
@mouseover="getTemplate(item)">
<div class="MarketIndexList-img">
<el-image style="width: 100%;height: 100%" :src="item.CoverImg" fit="cover" />
<!-- <img :src="item.CoverImg" style="height:100%"/> -->
</div>
<div class="MarketIndexList-text">{{item.Title}}</div>
</div>
</div>
</template>
</el-popover>
</el-popover> -->
</template>
</div>
<div v-else class="q-mt-lg bg-white rounded" style="padding: 30px 10px 30px 10px;text-align: center;color: #909399;">暂无数据</div>
......@@ -104,11 +124,31 @@
</div>
</div>
</div>
<el-dialog v-model="datas.DetailsVisible" :show-close="true" :close-on-press-escape="false"
:close-on-click-modal="true" style="width: 650px;">
<template #header>
<div class="text-title">{{datas.TemplateRow.Title}} 模版详情</div>
</template>
<div class="MarketDetails-LayoutPool" v-loading="datas.loading">
<LayoutPool style="width: 100%;"/>
</div>
<div class="q-mt-lg row items-center">
<div class="col">
</div>
<div>
<el-button v-if="ConfigId||model" class="q-ml-lg"
@click="datas.DetailsVisible=false,datas.TemplateRow={}">关闭</el-button>
<el-button v-if="ConfigId||model" type="primary" class="q-ml-lg"
@click="goToTemplate(datas.TemplateRow)">选择该模版</el-button>
</div>
</div>
</el-dialog>
</template>
<script setup lang="ts">
import { reactive, ref, inject} from "vue";
import { reactive, ref, inject, watch} from "vue";
import LineService from '@/services/LineService'
import ConfigService from '@/services/ConfigService'
import { userStore } from "@/store/user";
......@@ -117,7 +157,7 @@
import { storeToRefs } from "pinia";
import { injectKeyTemplate } from '@/types/injectKey'
import LayoutPool from '../Editor/Thumbnails/LayoutPool.vue'
import LayoutPool from './LayoutPool.vue'
const {
userInfo
......@@ -129,6 +169,11 @@
const dataList = ref([] as Array < any > ); //模板数据列表
const currentPage = ref(1 as Number);
const datas = reactive({
TemplateRow: {},
DetailsVisible: false,
loading: false,
})
const queryObj = reactive({
pageIndex: 1,
pageSize: 20,
......@@ -139,7 +184,6 @@
ColorName: '', //颜色名称
totalCount: 0, //总调试
pageCount: 0, //总页数
loading: false,
})
const loading = ref(false as any)
......@@ -148,15 +192,20 @@
const marketStore = useScreenStore()
const isModelStore = useScreenStore()
const layoutsStore = useSlidesStore()
const { screening, market, model, isModel, ConfigId} = storeToRefs(useScreenStore())
const TempIdStore = useScreenStore()
const { screening, market, model, isModel, ConfigId, TempId} = storeToRefs(useScreenStore())
const addTemplate = () =>{
marketStore.setMarket(!market)
// TempIdStore.setTempId(0)
searchData.value.TempId = 0
}
// 查看所有子模版
const getTemplate = async (item) => {
queryObj.loading = true
datas.DetailsVisible = true
datas.TemplateRow = JSON.parse(JSON.stringify(item))
datas.loading = true
layoutsStore.setLayouts([])
try {
let queryMsg = {
......@@ -176,44 +225,23 @@
newSlides = SlidesData
}
layoutsStore.setLayouts(JSON.parse(JSON.stringify(newSlides)))
queryObj.loading = false
}
} catch (error) {
console.log("GetTripTemplateSlide", error);
queryObj.loading = false
}
}
/**
* 根据团期配置编号获取行程详情
*/
const GetTripConfig = async () =>{
try {
if(!ConfigId||isModel) return
let queryMsg = {
ConfigId: ConfigId
}
let datasRes = await ConfigService.triptemplateGetTripConfig(queryMsg);
if (datasRes.data.resultCode == 1) {
if(datasRes.data.data&&datasRes.data.data.length>0&&datasRes.data.data[0].TempId){
searchData.value.TempId = datasRes.data.data[0].TempId
isModelStore.setIsModel(true)
marketStore.setMarket(!market)
}
}
datas.loading = false
} catch (error) {
console.log("triptemplate_GetTripConfig", error);
console.log("GetTripTemplateSlide", error);
datas.loading = false
}
}
GetTripConfig()
}
/**
* 页面跳转
*/
const goToTemplate = (item: any) => {
if(!ConfigId.value&&!model.value) return
datas.DetailsVisible = false
searchData.value.TempId = item.TempId
marketStore.setMarket(!market)
datas.TemplateRow = {}
// console.log("item", item.TempId);
}
......@@ -310,7 +338,7 @@
queryTemplateBySearchHandler();
</script>
<style scoped>
<style lang="scss" scoped>
@import url('../../assets/styles/common.css');
@font-face {
......@@ -338,6 +366,36 @@
}
.MarketIndexList{
width: 100%;
position: relative;
overflow: hidden;
}
.MarketIndexList-Hover{
z-index: 1;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(23,23,23,0.7);
opacity: 0;
transition: opacity 2s ease;
-webkit-transition: opacity 2s ease;
-moz-transition: opacity 2s ease;
-ms-transition: opacity 2s ease;
-o-transition: opacity 2s ease;
display: flex;
justify-content: center;
align-items: center;
color: $themeHoverColor;
}
.MarketIndexList:hover .MarketIndexList-Hover{
opacity: 1;
}
.MarketIndexList-Hover div span{
display: inline-block;
background: #fff;
border-radius: 4px;
padding: 0 10px 3px 10px;
}
.MarketIndexListBox:hover{
position: relative;
......@@ -378,4 +436,7 @@
margin-right: 30px;
margin-top: 5px;
}
.layout-item{
width: 180px;
}
</style>
\ No newline at end of file
<template>
<div class="layout-pool">
<div
class="layout-item q-ma-md"
v-for="slide in layouts"
:key="slide.id"
@click="selectSlideTemplate(slide)"
>
<ThumbnailSlide class="thumbnail" :slide="slide" :size="180" />
</div>
</div>
</template>
<script lang="ts" setup>
import { storeToRefs } from 'pinia'
import { useSlidesStore } from '@/store'
import type { Slide } from '@/types/slides'
import ThumbnailSlide from '@/views/components/ThumbnailSlide/index.vue'
const emit = defineEmits<{
(event: 'select', payload: Slide): void
}>()
const { layouts } = storeToRefs(useSlidesStore())
const selectSlideTemplate = (slide: Slide) => {
emit('select', slide)
}
</script>
<style lang="scss" scoped>
.layout-pool {
width: 394px;
height: 500px;
padding: 2px;
/* margin-right: -12px;
padding-right: 12px; */
overflow: auto;
@include flex-grid-layout();
}
.layout-item {
/* @include flex-grid-layout-children(2, 48%); */
width: 180px;
&:nth-last-child(2), &:last-child {
margin-bottom: 0;
}
.thumbnail {
outline: 1px solid $borderColor;
cursor: pointer;
&:hover {
outline-color: $themeColor;
}
}
}
</style>
\ No newline at end of file
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