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()
......
This diff is collapsed.
......@@ -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)
})
......
This diff is collapsed.
<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