Commit 65571f29 authored by 罗超's avatar 罗超

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

# Conflicts:
#	package.json
#	src/store/index.ts
parents f52d598a 99144a80
VUE_APP_API_URL = 'http://192.168.10.214/api/common/post'
VUE_APP_UPLOADURLAPI_URL = 'http://192.168.10.214:8120'
VUE_APP_SHARE_URL = 'http://127.0.0.1:8080'
\ No newline at end of file
VUE_APP_API_URL = 'http://reborn.oytour.com/api/common/post'
VUE_APP_UPLOADURLAPI_URL = 'http://upload.oytour.com'
VUE_APP_SHARE_URL = 'http://vitto.com'
\ No newline at end of file
......@@ -5,6 +5,7 @@
// Generated by unplugin-auto-import
export {}
declare global {
const ElLoading: typeof import('element-plus/es')['ElLoading']
const ElMessage: typeof import('element-plus/es')['ElMessage']
const ElMessageBox: typeof import('element-plus/es')['ElMessageBox']
}
......@@ -22,12 +22,16 @@ declare module 'vue' {
EditableInput: typeof import('./src/components/ColorPicker/EditableInput.vue')['default']
ElAside: typeof import('element-plus/es')['ElAside']
ElButton: typeof import('element-plus/es')['ElButton']
ElButtonGroup: typeof import('element-plus/es')['ElButtonGroup']
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
ElCheckTag: typeof import('element-plus/es')['ElCheckTag']
ElCol: typeof import('element-plus/es')['ElCol']
ElContainer: typeof import('element-plus/es')['ElContainer']
ElDialog: typeof import('element-plus/es')['ElDialog']
ElDivider: typeof import('element-plus/es')['ElDivider']
ElDropdown: typeof import('element-plus/es')['ElDropdown']
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
ElEmpty: typeof import('element-plus/es')['ElEmpty']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElImage: typeof import('element-plus/es')['ElImage']
......@@ -37,6 +41,7 @@ declare module 'vue' {
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
ElOption: typeof import('element-plus/es')['ElOption']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElResult: typeof import('element-plus/es')['ElResult']
ElRow: typeof import('element-plus/es')['ElRow']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElTable: typeof import('element-plus/es')['ElTable']
......@@ -65,6 +70,7 @@ declare module 'vue' {
NumberInput: typeof import('./src/components/NumberInput.vue')['default']
Popover: typeof import('./src/components/Popover.vue')['default']
PopoverMenuItem: typeof import('./src/components/PopoverMenuItem.vue')['default']
QRCode: typeof import('./src/components/QRCode/QRCode.vue')['default']
RadioButton: typeof import('./src/components/RadioButton.vue')['default']
RadioGroup: typeof import('./src/components/RadioGroup.vue')['default']
Saturation: typeof import('./src/components/ColorPicker/Saturation.vue')['default']
......
......@@ -374,8 +374,8 @@ const createFrameElement = (url: string) => {
width: 800,
height: 480,
rotate: 0,
left: (VIEWPORT_SIZE - 800) / 2,
top: (VIEWPORT_SIZE * viewportRatio.value - 480) / 2,
left: (VIEWPORT_SIZE.Value - 800) / 2,
top: (VIEWPORT_SIZE.Value * viewportRatio.value - 480) / 2,
url,
})
}
......
......@@ -3,7 +3,7 @@
"version": "0.0.1",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"serve": "vue-cli-service serve --mode development",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
"build:fonts": "node scripts/build-fonts"
......@@ -15,6 +15,7 @@
"@icon-park/vue-next": "^1.4.2",
"@types/ali-oss": "^6.16.11",
"@types/psd": "^3.4.3",
"@vueuse/integrations": "^10.7.2",
"ali-oss": "^6.18.1",
"animate.css": "^4.1.1",
"axios": "^1.6.2",
......@@ -48,6 +49,7 @@
"prosemirror-state": "^1.4.1",
"prosemirror-view": "^1.27.2",
"psd.js": "^3.9.0",
"qrcode": "^1.5.3",
"register-service-worker": "^1.7.2",
"svg-arc-to-cubic-bezier": "^3.2.0",
"svg-pathdata": "^6.0.3",
......@@ -55,6 +57,7 @@
"tippy.js": "^6.3.7",
"vue": "^3.3.7",
"vue-konva": "^3.0.2",
"vue-router": "^4.2.5",
"vue3-leaderline": "^1.2.11",
"vuedraggable": "^4.1.0"
},
......
<template>
<div v-if="isFinish" style="height: 100vh;background: rgb(243, 246, 251);">
<Screen v-if="screening" />
<Market v-else-if="(market&&model!=2)||(market&&SalesEditor>0)"></Market>
<!-- <Screen v-if="screening" /> -->
<Market v-if="(market&&model!=2)||(market&&SalesEditor>0)"></Market>
<SellTemplate v-else-if="model==2&&SalesEditor==0"/>
<Editor v-else-if="_isPC" />
<Mobile v-else />
</div>
<el-result v-if="loading" title="404" :sub-title="ShareTips">
<template #icon></template>
<template #extra></template>
</el-result>
</template>
<script lang="ts" setup>
import { onMounted,ref,provide } from 'vue'
import { storeToRefs } from 'pinia'
import { useScreenStore, useMainStore, useSnapshotStore, useFontStore,useSellTemplate } from '@/store'
import { useScreenStore, useMainStore, useSnapshotStore,
useFontStore, useSellTemplateStore, useSlidesStore } from '@/store'
import { LOCALSTORAGE_KEY_DISCARDED_DB } from '@/configs/storage'
import { deleteDiscardedDB } from '@/utils/database'
import { isPC, query } from './utils/common'
......@@ -19,6 +24,7 @@ import { userStore } from './store/user'
import { injectKeyTemplate } from '@/types/injectKey'
import ConfigService from '@/services/ConfigService'
import { domainManager } from '@/utils/domainManager'
import { enterFullscreen, exitFullscreen, isFullscreen } from '@/utils/fullscreen'
import Editor from './views/Editor/index.vue'
import Screen from './views/Screen/index.vue'
......@@ -32,6 +38,9 @@ provide(injectKeyTemplate,searchData)
const isFinish = ref(false)
const loading = ref(false)
const ShareTips = ref('')
const _isPC = isPC()
......@@ -43,31 +52,44 @@ const ConfigIdStore = useScreenStore()
const marketStore = useScreenStore()
const isModelStore = useScreenStore()
const TempIdStore = useScreenStore()
const ScreenStore = useScreenStore()
const SellTemplateStore = useSellTemplateStore()
const slidesStore = useSlidesStore()
const { databaseId } = storeToRefs(mainStore)
const { screening, market, model, ConfigId } = storeToRefs(useScreenStore())
const { userInfo } = storeToRefs(userStore())
const { SalesEditor } = storeToRefs(useSellTemplate())
const { SalesEditor, SalesTripId } = storeToRefs(useSellTemplateStore())
const { slides } = storeToRefs(useSlidesStore())
const { enterScreeningFromStart } = storeToRefs(useScreenStore())
if (process.env.NODE_ENV === 'production') {
window.onbeforeunload = () => false
}
const userLoginHandler = async ()=>{
let param = query()
let userId = 1
let userId = 0
let ConfigId = 0 // 9117
let model = 0
let SalesTripId = ''
await useFontStore().loadAllFonts()
if(param.uid) userId=parseInt(param.uid)
if(param.ConfigId) ConfigId=parseInt(param.ConfigId)
if(param.model) model=parseInt(param.model)
if(param.SalesTripId) {
SalesTripId = param.SalesTripId
SellTemplateStore.setSalesTripId(SalesTripId)
try {
await sellGetTripTemplate()
} catch (error) {}
}
ConfigIdStore.setConfigId(ConfigId)
modelStore.setModel(model)
try {
await userStore().setUserLoginAsync(userId,ConfigId)
await useFontStore().loadAllFonts()
if(param.uid) await userStore().setUserLoginAsync(userId)
if(ConfigId>0&&model!=2) await GetTripConfig(ConfigId)
} catch (error) {}
isFinish.value=true
if(!ConfigId&&!model) ElMessageBox.confirm(
if(userId>0)isFinish.value=true
if(!ConfigId&&!model&&(param.SalesTripId&&param.SalesTripId=='')) ElMessageBox.confirm(
'当前没有权限进行任何操作,请联系管理员!',
'提示',
{
......@@ -79,13 +101,56 @@ const userLoginHandler = async ()=>{
.catch(() => {})
}
onMounted(async () => {
await deleteDiscardedDB()
snapshotStore.initSnapshotDatabase()
mainStore.setAvailableFonts()
})
// 销售模版数据
const sellGetTripTemplate = async () =>{
const loadingObj = ElLoading.service({
text:'正在渲染数据',
lock:true
})
try {
let queryMsg = {
Code: SalesTripId.value
}
const slidesData = slides.value
let dataRes = await ConfigService.GetTripOtherByCode(queryMsg);
if (dataRes.data.resultCode == 1) {
loadingObj.close()
loading.value = false
isFinish.value=true
let dataObj = dataRes.data.data
let viewportRatio = 1.414
if(dataRes.data.data.TempType==1) viewportRatio = 0.7069
if(dataRes.data.data.Width&&dataRes.data.data.Height) viewportRatio = dataRes.data.data.Height/dataRes.data.data.Width
slidesStore.setViewportRatio(viewportRatio)
let SlidesData = JSON.parse(dataObj.TempData)
let newSlides = []
await SlidesData.forEach((x,i)=>{
newSlides.push(x)
})
slidesStore.setSlides(newSlides)
slidesStore.updateSlideIndex(0)
ScreenStore.setScreening(true)
enterScreeningFromStart
}else{
loading.value = true
ShareTips.value = dataRes.data.message
loadingObj.close()
}
} catch (error) {
loading.value = true
ShareTips.value = error
loadingObj.close()
}
}
/**
* 根据团期配置编号获取行程详情
*/
......
......@@ -128,8 +128,8 @@ textarea {
}
::-webkit-scrollbar {
width: 5px;
height: 5px;
width: 7px;
height: 7px;
background-color: transparent;
}
::-webkit-scrollbar-thumb {
......
......@@ -39,7 +39,7 @@
</div>
<div class="text-small text-grey q-mt-lg" v-if="fonts && fonts.length>0">请完善以下字体信息补充:</div>
<div class="row items-center q-mt-md" v-for="(x,i) in fonts" :key="x">
<div class="text-small col">字体名称:{{ x }}</div>
<div class="text-small col" style="user-select: text !important;">字体名称:{{ x }}</div>
<el-upload
v-loading="uploadingIndex==i"
:on-change="(uploadFile:any, uploadFiles:any)=> uploadFontHandler(uploadFile, uploadFiles,x)"
......@@ -72,16 +72,18 @@
</el-dialog>
</template>
<script setup lang="ts">
import {ref,defineProps,watch} from 'vue'
import {ref,defineProps,watch,inject} from 'vue'
import { UploadFilled,Delete } from '@element-plus/icons-vue'
import type { UploadProps, UploadUserFile,UploadInstance } from 'element-plus'
import PSD from 'psd.js'
import {getFonts, toSlider, toThumbnails} from '@/utils/psdParser/index'
import {getFonts, toSlider,getDrawingBoardSize, toThumbnails} from '@/utils/psdParser/index'
import { useSlidesStore } from '@/store'
import { ElMessage, ElMessageBox } from 'element-plus'
import AliyunUpload from '@/utils/upload/aliyun'
import { useFontStore,useScreenStore } from '@/store'
import { CustomerFonts } from '@/store/font'
import { injectKeyDataSource,injectKeyTemplate } from '@/types/injectKey'
import { VIEWPORT_SIZE, VIEWPORT_VER_SIZE } from '@/configs/canvas'
const props = defineProps({
visible:{
......@@ -105,6 +107,11 @@ const fonts = ref<any[]>()
const uploadingIndex = ref(-1)
const uploadFinishFont = ref<string[]>([])
const queryObj = ref({} as any)
const searchData = ref({} as any)
queryObj.value = inject(injectKeyDataSource).queryObj
searchData.value = inject(injectKeyTemplate)
const handleChange: UploadProps['onChange'] = (uploadFile, uploadFiles) => {
isResolving.value=true
......@@ -114,6 +121,22 @@ const handleChange: UploadProps['onChange'] = (uploadFile, uploadFiles) => {
PSD.fromURL(url).then(async (psd:any) => {
thumbnails.value= await toThumbnails(psd)
sliders.value = await toSlider(psd)
let DrawingBoardSize = await getDrawingBoardSize(psd)
queryObj.value.TempType = DrawingBoardSize.Width>DrawingBoardSize.Height?1:2
if(searchData.value.TemplateType==2) {
queryObj.value.Width = DrawingBoardSize.Width
queryObj.value.Height = DrawingBoardSize.Height
VIEWPORT_SIZE.Value = queryObj.value.Height
VIEWPORT_VER_SIZE.Value = queryObj.value.Width
let viewportRatio = queryObj.value.Height/queryObj.value.Width
useSlidesStore().setViewportRatio(viewportRatio)
}else {
VIEWPORT_SIZE.Value = 1754
VIEWPORT_VER_SIZE.Value = 1240
let viewportRatio = 1.414
if(queryObj.value.TempType==1) viewportRatio = 0.7069
useSlidesStore().setViewportRatio(viewportRatio)
}
fonts.value = getFonts(psd)
if(fonts.value){
fonts.value = fonts.value.filter(x=>useFontStore().getFonts.findIndex(y=>y.fontFamily==x)==-1)
......
<script setup lang="ts">
import { useQRCode } from '@vueuse/integrations/useQRCode'
/*
参考文档:https://vueuse.org/integrations/useQRCode/
https://www.npmjs.com/package/qrcode#qr-code-options
*/
interface Props {
value?: string // 扫描后的文本或地址
size?: number // 二维码大小,单位px
color?: string // 二维码颜色,Value must be in hex format (十六进制颜色值)
bgColor?: string // 二维码背景色,Value must be in hex format (十六进制颜色值)
bordered?: boolean // 是否有边框
borderColor?: string // 边框颜色
scale?: number // 每个black dots多少像素
/*
纠错等级也叫纠错率,就是指二维码可以被遮挡后还能正常扫描,而这个能被遮挡的最大面积就是纠错率。
通常情况下二维码分为 4 个纠错级别:L级 可纠正约 7% 错误、M级 可纠正约 15% 错误、Q级 可纠正约 25% 错误、H级 可纠正约30% 错误。
并不是所有位置都可以缺损,像最明显的三个角上的方框,直接影响初始定位。中间零散的部分是内容编码,可以容忍缺损。
当二维码的内容编码携带信息比较少的时候,也就是链接比较短的时候,设置不同的纠错等级,生成的图片不会发生变化。
*/
errorLevel?: 'L'|'M'|'Q'|'H' // 二维码纠错等级
}
const props = withDefaults(defineProps<Props>(), {
value: '',
size: 160,
color: '#000',
bgColor: '#FFF',
bordered: true,
borderColor: '#0505050f',
scale: 8,
errorLevel: 'H' // 可选 L M Q H
})
// `qrcode` will be a ref of data URL
const qrcode = useQRCode(props.value, {
errorCorrectionLevel: props.errorLevel,
type: 'image/png',
quality: 1,
margin: 3,
scale: props.scale, // 8px per modules(black dots)
color: {
dark: props.color, // 像素点颜色
light: props.bgColor // 背景色
}
})
</script>
<template>
<div class="m-qrcode" :class="{'bordered': bordered}" :style="`width: ${size}px; height: ${size}px; border-color: ${borderColor};`">
<img :src="qrcode" class="u-qrcode" alt="QRCode" />
</div>
</template>
<style scoped>
.m-qrcode {
display: inline-block;
border-radius: 8px;
overflow: hidden;
}
.u-qrcode {
width: 100%;
height: 100%;
}
.bordered {
border-width: 1px;
border-style: solid;
}
</style>
\ No newline at end of file
......@@ -3,7 +3,7 @@ import Axios, {
InternalAxiosRequestConfig,
AxiosError,
} from 'axios';
import { domainManager } from '@/utils/domainManager'
// import { domainManager } from '@/utils/domainManager'
let datas: AxiosResponse
export enum ApiResult{
......@@ -66,7 +66,9 @@ const getErrorCode2text = (response: AxiosResponse): string => {
* service.get<{data: string; code: number}>('/test').then(({data}) => { console.log(data.code) })
*/
const service = Axios.create({
baseURL: `${domainManager().domainUrl}`,//'http://192.168.10.214/api/common/post'
// process.env.VUE_APP_API_URL
//'http://reborn.oytour.com/api/common/post'
baseURL: process.env.VUE_APP_API_URL,
timeout: 20000,
headers: {
'User-Type': 'bus',
......
......@@ -3,7 +3,7 @@ import Axios, {
InternalAxiosRequestConfig,
AxiosError,
} from 'axios';
import { domainManager } from '../utils/domainManager'
// import { domainManager } from '../utils/domainManager'
let datas: AxiosResponse
export enum ApiResult{
......@@ -66,7 +66,7 @@ const getErrorCode2text = (response: AxiosResponse): string => {
* service.get<{data: string; code: number}>('/test').then(({data}) => { console.log(data.code) })
*/
const service = Axios.create({
baseURL: domainManager().UploadUrl,
baseURL: process.env.VUE_APP_UPLOADURLAPI_URL,
timeout: 20000,
headers: {
"Content-Type": "application/x-www-form-urlencoded;"
......
export const VIEWPORT_SIZE = 1754
export const VIEWPORT_VER_SIZE = 1240
\ No newline at end of file
// export const VIEWPORT_SIZE.Value = 1754 //竖版
// export const VIEWPORT_VER_SIZE.Value = 1240 //横版
class VIEWPORT_SIZE_Manager {
private _value = 0
constructor(val:number) {
this._value = val;
}
get Value(){
return this._value
}
set Value(val:number){
this._value = val
}
}
export const VIEWPORT_SIZE = new VIEWPORT_SIZE_Manager(1754)
export const VIEWPORT_VER_SIZE = new VIEWPORT_SIZE_Manager(1240)
\ No newline at end of file
......@@ -18,8 +18,8 @@ export default () => {
* @param command 对齐方向
*/
const alignElementToCanvas = (command: ElementAlignCommands) => {
const viewportWidth = VIEWPORT_SIZE
const viewportHeight = VIEWPORT_SIZE * viewportRatio.value
const viewportWidth = VIEWPORT_SIZE.Value
const viewportHeight = VIEWPORT_SIZE.Value * viewportRatio.value
const { minX, maxX, minY, maxY } = getElementListRange(activeElementList.value)
const newElementList: PPTElement[] = JSON.parse(JSON.stringify(currentSlide.value.elements))
......
......@@ -55,12 +55,12 @@ export default () => {
getImageSize(src).then(({ width, height }) => {
const scale = height / width
if (scale < viewportRatio.value && width > VIEWPORT_SIZE) {
width = VIEWPORT_SIZE
if (scale < viewportRatio.value && width > VIEWPORT_SIZE.Value) {
width = VIEWPORT_SIZE.Value
height = width * scale
}
else if (height > VIEWPORT_SIZE * viewportRatio.value) {
height = VIEWPORT_SIZE * viewportRatio.value
else if (height > VIEWPORT_SIZE.Value * viewportRatio.value) {
height = VIEWPORT_SIZE.Value * viewportRatio.value
width = height / scale
}
......@@ -70,8 +70,8 @@ export default () => {
src,
width,
height,
left: (VIEWPORT_SIZE - width) / 2,
top: (VIEWPORT_SIZE * viewportRatio.value - height) / 2,
left: (VIEWPORT_SIZE.Value - width) / 2,
top: (VIEWPORT_SIZE.Value * viewportRatio.value - height) / 2,
fixedRatio: true,
rotate: 0,
})
......@@ -154,8 +154,8 @@ export default () => {
colWidths,
rotate: 0,
data,
left: (VIEWPORT_SIZE - width) / 2,
top: (VIEWPORT_SIZE * viewportRatio.value - height) / 2,
left: (VIEWPORT_SIZE.Value - width) / 2,
top: (VIEWPORT_SIZE.Value * viewportRatio.value - height) / 2,
outline: {
width: 2,
style: 'solid',
......@@ -280,8 +280,8 @@ export default () => {
width: data.w,
height: data.h,
rotate: 0,
left: (VIEWPORT_SIZE - data.w) / 2,
top: (VIEWPORT_SIZE * viewportRatio.value - data.h) / 2,
left: (VIEWPORT_SIZE.Value - data.w) / 2,
top: (VIEWPORT_SIZE.Value * viewportRatio.value - data.h) / 2,
path: data.path,
latex: data.latex,
color: theme.value.fontColor,
......@@ -302,8 +302,8 @@ export default () => {
width: 500,
height: 300,
rotate: 0,
left: (VIEWPORT_SIZE - 500) / 2,
top: (VIEWPORT_SIZE * viewportRatio.value - 300) / 2,
left: (VIEWPORT_SIZE.Value - 500) / 2,
top: (VIEWPORT_SIZE.Value * viewportRatio.value - 300) / 2,
src,
autoplay: false,
})
......@@ -320,8 +320,8 @@ export default () => {
width: 50,
height: 50,
rotate: 0,
left: (VIEWPORT_SIZE - 50) / 2,
top: (VIEWPORT_SIZE * viewportRatio.value - 50) / 2,
left: (VIEWPORT_SIZE.Value - 50) / 2,
top: (VIEWPORT_SIZE.Value * viewportRatio.value - 50) / 2,
loop: false,
autoplay: false,
fixedRatio: true,
......
......@@ -40,7 +40,7 @@ export default () => {
setTimeout(() => {
const config: ExportImageConfig = {
quality,
width: (viewportRatio.value<1 ? VIEWPORT_SIZE : VIEWPORT_VER_SIZE ),
width: (viewportRatio.value<1 ? VIEWPORT_SIZE.Value : VIEWPORT_VER_SIZE.Value ),
}
if (ignoreWebfont) config.fontEmbedCSS = ''
......@@ -67,7 +67,7 @@ export default () => {
setTimeout(() => {
const config: ExportImageConfig = {
quality,
width: (viewportRatio.value<1 ? VIEWPORT_SIZE : VIEWPORT_VER_SIZE ),
width: (viewportRatio.value<1 ? VIEWPORT_SIZE.Value : VIEWPORT_VER_SIZE.Value ),
}
if (ignoreWebfont) config.fontEmbedCSS = ''
......
......@@ -215,7 +215,7 @@ export default () => {
e.preventDefault()
order(ElementOrderCommands.BOTTOM)
}
if (key === KEYS.DELETE || key === KEYS.BACKSPACE) {
if (key === KEYS.DELETE) {// || key === KEYS.BACKSPACE
if (disableHotkeys.value) return
e.preventDefault()
remove()
......
......@@ -88,7 +88,7 @@ export default () => {
const json = await parse(e.target!.result as ArrayBuffer)
const width = json.size.width
const scale = VIEWPORT_SIZE / width
const scale = VIEWPORT_SIZE.Value / width
const slides: Slide[] = []
for (const item of json.slides) {
......
......@@ -4,6 +4,14 @@ import Api,{ HttpResponse, Result } from './../utils/request';
* 配置相关方法
*/
class ConfigService{
/**
* 分享销售模版详情
*/
static async GetTripOtherByCode(params : any):Promise<HttpResponse>{
return Api.Post("triptemplate_GetTripOtherByCode",params)
}
/**
* 销售模版详情
*/
......
......@@ -4,8 +4,8 @@ import { useSnapshotStore } from './snapshot'
import { useKeyboardStore } from './keyboard'
import { useScreenStore } from './screen'
import { useFontStore } from './font'
import { useSellTemplate } from './sellTemplate'
import { useMapStore } from './map'
import { useSellTemplateStore } from './sellTemplate'
export {
useMainStore,
......@@ -14,6 +14,6 @@ export {
useKeyboardStore,
useScreenStore,
useFontStore,
useSellTemplate,
useMapStore
useMapStore,
useSellTemplateStore
}
\ No newline at end of file
......@@ -38,6 +38,7 @@ export const useScreenStore = defineStore('screen', {
actions: {
setScreening(screening: boolean) {
if(location.href.indexOf(process.env.VUE_APP_SHARE_URL)!=-1&&!screening) return
this.screening = screening
if (screening) {
this.market = false
......
......@@ -3,12 +3,14 @@ import { defineStore } from 'pinia'
export interface SalesState {
SalesEditor: number,
SalesBack: number,
SalesTripId: string,
}
export const useSellTemplate = defineStore('sales', {
export const useSellTemplateStore = defineStore('sales', {
state: (): SalesState => ({
SalesEditor: 0, // 1 新增 2编辑
SalesBack: 0,// 0
SalesEditor: 0, // 1 新增模版 2编辑模版 3新增广告 4编辑广告
SalesBack: 0,// 0 销售首页 1 模版首页
SalesTripId: '', // 销售行程id
}),
actions: {
......@@ -18,5 +20,8 @@ export const useSellTemplate = defineStore('sales', {
setSalesBack(SalesBack: number) {
this.SalesBack = SalesBack
},
setSalesTripId(SalesTripId: string) {
this.SalesTripId = SalesTripId
},
},
})
\ No newline at end of file
......@@ -39,6 +39,7 @@ export const useSnapshotStore = defineStore('snapshot', {
const newFirstSnapshot = {
index: slidesStore.slideIndex,
slides: slidesStore.slides,
thumbnails: slidesStore.thumbnails
}
await db.snapshots.add(newFirstSnapshot)
this.setSnapshotCursor(0)
......@@ -64,6 +65,7 @@ export const useSnapshotStore = defineStore('snapshot', {
const snapshot = {
index: slidesStore.slideIndex,
slides: slidesStore.slides,
thumbnails: slidesStore.thumbnails
}
await db.snapshots.add(snapshot)
......@@ -98,11 +100,12 @@ export const useSnapshotStore = defineStore('snapshot', {
const snapshotCursor = this.snapshotCursor - 1
const snapshots: Snapshot[] = await db.snapshots.orderBy('id').toArray()
const snapshot = snapshots[snapshotCursor]
const { index, slides } = snapshot
const { index, slides, thumbnails } = snapshot
const slideIndex = index > slides.length - 1 ? slides.length - 1 : index
slidesStore.setSlides(slides)
slidesStore.setThumbnails(thumbnails)
slidesStore.updateSlideIndex(slideIndex)
this.setSnapshotCursor(snapshotCursor)
mainStore.setActiveElementIdList([])
......@@ -117,12 +120,13 @@ export const useSnapshotStore = defineStore('snapshot', {
const snapshotCursor = this.snapshotCursor + 1
const snapshots: Snapshot[] = await db.snapshots.orderBy('id').toArray()
const snapshot = snapshots[snapshotCursor]
const { index, slides } = snapshot
const { index, slides, thumbnails } = snapshot
const slideIndex = index > slides.length - 1 ? slides.length - 1 : index
slidesStore.setSlides(slides)
slidesStore.updateSlideIndex(slideIndex)
slidesStore.setThumbnails(thumbnails)
this.setSnapshotCursor(snapshotCursor)
mainStore.setActiveElementIdList([])
},
......
......@@ -703,7 +703,9 @@ export interface Slide {
animations?: PPTAnimation[]
turningMode?: TurningMode
pageType: number,
isTripItems?:boolean
isTripItems?:boolean,
width?:number,
height?:number,
}
/**
......
......@@ -18,14 +18,23 @@ export const isPC = () => {
export const query = (url?:string)=>{
url = url??location.href
let str = url.substr(url.indexOf('?') + 1)
let json = {} as any
const arr = str.split('&')
for(let i = 0; i < arr.length; i++) {
let item = arr[i].split('=')
json[item[0]] = item[1]
if(url.indexOf(process.env.VUE_APP_SHARE_URL)!=-1){
let str = url.substr(url.indexOf('?') + 1)
let json = {
SalesTripId: str.slice(2)
} as any
return json
}else{
let str = url.substr(url.indexOf('?') + 1)
let json = {} as any
const arr = str.split('&')
for(let i = 0; i < arr.length; i++) {
let item = arr[i].split('=')
json[item[0]] = item[1]
}
return json
}
return json
}
/**
......
......@@ -10,7 +10,8 @@ export interface writingBoardImg {
export interface Snapshot {
index: number
slides: Slide[]
slides: Slide[],
thumbnails: string[]
}
const databaseNamePrefix = 'PPTist'
......
import { ResolvePsdToSliderHandler, ResolveThumbHandler,ResolveSliderFonts } from "./resolve"
import { ResolvePsdToSliderHandler,ResolvePsdDrawingBoardSize, ResolveThumbHandler,ResolveSliderFonts } from "./resolve"
export const toSlider = async (psd:any)=>{
const sliders = await ResolvePsdToSliderHandler(psd);
return sliders
}
export const getDrawingBoardSize= (psd:any) => {
return ResolvePsdDrawingBoardSize(psd)
}
export const getFonts = (psd:any) => {
return ResolveSliderFonts(psd)
}
......
......@@ -18,7 +18,7 @@ export const ResolveThumbHandler = async (psd:any)=>{
Z_INDEX = 2000
const item = _children[i];
if(item.layer.artboard) {
if(item.layer.artboard && item.layer.visible) {
let cropImagePath = await cropImage(imgBase64String,"image/jpeg",item.layer.artboard().export().coords)
let imagePath = await compressionThumbnail(cropImagePath,"image/jpeg",600)
imgs.push(imagePath)
......@@ -63,6 +63,28 @@ export const ResolvePsdToSliderHandler = async (psd:any) => {
}
}
export const ResolvePsdDrawingBoardSize= async (psd:any) => {
let items:Array<any> = psd.tree().children().filter((x:any)=>x.layer.visible)
let DrawingBoardSize = {
Width: 0,
Height: 0
}
if(items && items.length>0){
for (let i = 0; i < items.length; i++) {
const x = items[i];
const {_children} = x
if(_children && _children.length>0){
DrawingBoardSize = {
Width: x.layer.artboard().export().coords.right-x.layer.artboard().export().coords.left,
Height: x.layer.artboard().export().coords.bottom-x.layer.artboard().export().coords.top
}
}
}
}
return DrawingBoardSize
}
export const ResolveSliderFonts = (psd:any) => {
let items:Array<any> = psd.tree().children().filter((x:any)=>x.layer.visible)
......
......@@ -35,8 +35,8 @@ const gridColor = computed(() => {
// 网格路径
const path = computed(() => {
const maxX = VIEWPORT_SIZE
const maxY = VIEWPORT_SIZE * viewportRatio.value
const maxX = VIEWPORT_SIZE.Value
const maxY = VIEWPORT_SIZE.Value * viewportRatio.value
let p = ''
for (let i = 0; i <= Math.floor(maxY / gridLineSize.value); i++) {
......
......@@ -26,8 +26,8 @@ export default (
if (!activeElementIdList.value.includes(element.id)) return
let isMouseDown = true
const edgeWidth = VIEWPORT_SIZE
const edgeHeight = VIEWPORT_SIZE * viewportRatio.value
const edgeWidth = VIEWPORT_SIZE.Value
const edgeHeight = VIEWPORT_SIZE.Value * viewportRatio.value
const sorptionRange = 5
......
......@@ -155,8 +155,8 @@ export default (
// 包括页面内除目标元素外的其他元素在画布中的各个可吸附对齐位置:上下左右四边
// 其中线条和被旋转过的元素不参与吸附对齐
else {
const edgeWidth = VIEWPORT_SIZE
const edgeHeight = VIEWPORT_SIZE * viewportRatio.value
const edgeWidth = VIEWPORT_SIZE.Value
const edgeHeight = VIEWPORT_SIZE.Value * viewportRatio.value
const isActiveGroupElement = element.id === activeGroupElementId.value
for (const el of elementList.value) {
......
......@@ -16,7 +16,7 @@ export default (canvasRef: Ref<HTMLElement | undefined>) => {
if (!canvasRef.value) return
const canvasWidth = canvasRef.value.clientWidth
const canvasHeight = canvasRef.value.clientHeight
const USED_VIEWPORT_SIZE = viewportRatio.value<=1?VIEWPORT_SIZE:VIEWPORT_VER_SIZE
const USED_VIEWPORT_SIZE = viewportRatio.value<=1?VIEWPORT_SIZE.Value:VIEWPORT_VER_SIZE.Value
if (canvasHeight / canvasWidth > viewportRatio.value) {
const viewportActualWidth = canvasWidth * (canvasPercentage.value / 100)
mainStore.setCanvasScale(viewportActualWidth / USED_VIEWPORT_SIZE)
......@@ -33,7 +33,7 @@ export default (canvasRef: Ref<HTMLElement | undefined>) => {
// 更新画布可视区域的位置
const setViewportPosition = (newValue: number, oldValue: number) => {
if (!canvasRef.value) return
const USED_VIEWPORT_SIZE = viewportRatio.value<=1?VIEWPORT_SIZE:VIEWPORT_VER_SIZE
const USED_VIEWPORT_SIZE = viewportRatio.value<=1?VIEWPORT_SIZE.Value:VIEWPORT_VER_SIZE.Value
const canvasWidth = canvasRef.value.clientWidth
const canvasHeight = canvasRef.value.clientHeight
......@@ -69,8 +69,8 @@ export default (canvasRef: Ref<HTMLElement | undefined>) => {
// 画布可视区域位置和大小的样式
const viewportStyles = computed(() => ({
width: viewportRatio.value<=1?VIEWPORT_SIZE:VIEWPORT_VER_SIZE,
height: (viewportRatio.value<=1?VIEWPORT_SIZE:VIEWPORT_VER_SIZE) * viewportRatio.value,
width: viewportRatio.value<=1?VIEWPORT_SIZE.Value:VIEWPORT_VER_SIZE.Value,
height: (viewportRatio.value<=1?VIEWPORT_SIZE.Value:VIEWPORT_VER_SIZE.Value) * viewportRatio.value,
left: viewportLeft.value,
top: viewportTop.value,
}))
......
......@@ -23,8 +23,8 @@
:style="{
width: viewportStyles.width * canvasScale + 'px',
height: viewportStyles.height * canvasScale + 'px',
left: (viewportStyles.left) + 'px',
top: (viewportStyles.top) + 'px',
left: viewportStyles.left<0? '5px':(viewportStyles.left) + 'px',
top: viewportStyles.top<0? '5px':(viewportStyles.top) + 'px',
}"
>
<div class="operates">
......@@ -72,7 +72,7 @@
:height="mouseSelection.height"
:quadrant="mouseSelectionQuadrant"
/>
<EditableElement
<EditableElement
v-for="(element, index) in elementList"
:key="element.id"
:elementInfo="element"
......@@ -111,6 +111,7 @@ import { injectKeySlideScale } from '@/types/injectKey'
import { removeAllRanges } from '@/utils/selection'
import { KEYS } from '@/configs/hotkey'
import { injectKeyDataSource } from '@/types/injectKey'
import { VIEWPORT_SIZE, VIEWPORT_VER_SIZE } from '@/configs/canvas'
import useViewportSize from './hooks/useViewportSize'
import useMouseSelection from './hooks/useMouseSelection'
......@@ -157,7 +158,7 @@ const {
canvasScale,
textFormatPainter,
} = storeToRefs(mainStore)
const { currentSlide, slides } = storeToRefs(useSlidesStore())
const { currentSlide, slides, viewportRatio } = storeToRefs(useSlidesStore())
const { ctrlKeyState, spaceKeyState } = storeToRefs(useKeyboardStore())
const viewportRef = ref<HTMLElement>()
......@@ -220,7 +221,6 @@ onMounted(() => {
// 点击画布的空白区域:清空焦点元素、设置画布焦点、清除文字选区、清空格式刷状态
const handleClickBlankArea = (e: MouseEvent) => {
console.log(e)
if (activeElementIdList.value.length) mainStore.setActiveElementIdList([])
if (!spaceKeyState.value && e.button==0) updateMouseSelection(e)
else if(e.button==1) dragViewport(e)
......@@ -243,13 +243,15 @@ const handleDblClick = (e: MouseEvent) => {
const viewportRect = viewportRef.value.getBoundingClientRect()
const left = (e.pageX - viewportRect.x) / canvasScale.value
const top = (e.pageY - viewportRect.y) / canvasScale.value
createTextElement({
left,
top,
width: 200 / canvasScale.value, // 除以 canvasScale 是为了与点击选区创建的形式保持相同的宽度
height: 0,
})
const width = viewportRatio.value<1 ? VIEWPORT_SIZE.Value : VIEWPORT_VER_SIZE.Value
if(left<width && left>=0){
createTextElement({
left,
top,
width: 200 / canvasScale.value, // 除以 canvasScale 是为了与点击选区创建的形式保持相同的宽度
height: 0,
})
}
}
// 画布注销时清空格式刷状态
......@@ -268,15 +270,16 @@ const throttleScaleCanvas = throttle(scaleCanvas, 100, { leading: true, trailing
const throttleUpdateSlideIndex = throttle(updateSlideIndex, 300, { leading: true, trailing: false })
const handleMousewheelCanvas = (e: WheelEvent) => {
e.preventDefault()
// 按住Ctrl键时:缩放画布
if (ctrlKeyState.value) {
e.preventDefault()
if (e.deltaY > 0) throttleScaleCanvas('-')
else if (e.deltaY < 0) throttleScaleCanvas('+')
}
// 上下翻页
else {
return
if (e.deltaY > 0) throttleUpdateSlideIndex(KEYS.DOWN)
else if (e.deltaY < 0) throttleUpdateSlideIndex(KEYS.UP)
}
......@@ -367,7 +370,7 @@ provide(injectKeySlideScale, canvasScale)
.canvas {
height: 100%;
user-select: none;
overflow: hidden;
/* overflow: hidden; */
background-color: $lightGray;
position: relative;
}
......
......@@ -220,10 +220,10 @@ const setImgs = async () => {
if(imgType.value==2){
let width = propsDatas.value.width
let height = propsDatas.value.height
let maxWidth = VIEWPORT_SIZE,maxHeight = VIEWPORT_VER_SIZE, viewportRatio = slidesStore.viewportRatio
let maxWidth = VIEWPORT_SIZE,maxHeight = VIEWPORT_VER_SIZE.Value, viewportRatio = slidesStore.viewportRatio
if(viewportRatio<1){
maxWidth = VIEWPORT_VER_SIZE
maxHeight = VIEWPORT_SIZE
maxWidth = VIEWPORT_VER_SIZE.Value
maxHeight = VIEWPORT_SIZE.Value
}
if(activeName.value=='1'||activeName.value=='3' || activeName.value == '4'){
......@@ -249,10 +249,12 @@ const setImgs = async () => {
tempSize.width = Math.ceil(tempSize.width*ratio)
tempSize.height = Math.ceil(tempSize.height*ratio)
}
propsDatas.value.left -= (tempSize.width - width) / 2
propsDatas.value.top -= (tempSize.height - height) / 2
propsDatas.value.width = tempSize.width
propsDatas.value.height = tempSize.height
if(tempSize.width) {
propsDatas.value.left -= (tempSize.width - width) / 2
propsDatas.value.top -= (tempSize.height - height) / 2
propsDatas.value.width = tempSize.width
propsDatas.value.height = tempSize.height
}
} else{
var img = new Image()
img.src = propsDatas.value.src
......@@ -277,10 +279,12 @@ const setImgs = async () => {
img.width = Math.ceil(img.width*ratio)
img.height = Math.ceil(img.height*ratio)
}
propsDatas.value.left -= (img.width - width) / 2
propsDatas.value.top -= (img.height - height) / 2
propsDatas.value.width = img.width
propsDatas.value.height = img.height
if(img.width) {
propsDatas.value.left -= (img.width - width) / 2
propsDatas.value.top -= (img.height - height) / 2
propsDatas.value.width = img.width
propsDatas.value.height = img.height
}
}
}
propsDatas.value.fixedRatio = true
......
......@@ -4,7 +4,7 @@
<div v-if="SalesEditor==0" class="menu-item" v-tooltip="'返回到首页'" @click="goBack(1)">返回</div>
<Popover v-if="SalesEditor>0" trigger="click" placement="bottom-start" v-model:value="mainBackVisible">
<template #content>
<div class="menu-item" @click="goBack(1)">去选模版</div>
<div class="menu-item" @click="goBack(1)">去选 {{searchData.TemplateType==2?'广告':'模版'}}</div>
<div class="menu-item" @click="goBack(0)">返回到首页</div>
</template>
<div class="menu-item">
......@@ -78,43 +78,42 @@
size="small" :loading="datas.loading"
@click="setTemplate" style="color: #ffff;margin-left: 10px;">
<template v-if="userInfo.IsEditTripTemplate==1&&model==1">
保存模板
保存{{searchData.TemplateType!=2?'模板':'广告'}}
</template>
<template v-if="(ConfigId>0&&model!=2)">
<template v-if="ConfigId>0&&model!=2">
保存行程
</template>
</el-button>
<template v-if="model==2">
<Popover trigger="click" placement="bottom-start" v-model:value="sellSaveVisible">
<template #content>
<div>
<el-dropdown v-if="searchData.sellId>0" split-button
size="small" type="primary" @click="setTemplate(0)">
保存
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>
<el-button type="primary"
size="small" :loading="datas.loading"
style="color: #ffff;margin-left: 10px;"
@click="setTemplate(0)">
保存模板
style="color: #ffff;"
@click.stop="setTemplate(0)">
保存{{searchData.TemplateType!=2?'行程':'广告'}}
</el-button>
</div>
<div class="q-mt-md">
</el-dropdown-item>
<el-dropdown-item>
<el-button type="primary"
size="small" :loading="datas.loading"
style="color: #ffff;margin-left: 10px;"
@click="setTemplate(1)">
另存模板
style="color: #ffff;"
@click.stop="setTemplate(1)">
另存{{searchData.TemplateType!=2?'行程':'广告'}}
</el-button>
</div>
</template>
<el-button v-if="searchData.sellId>0" type="primary"
size="small" :loading="datas.loading"
style="color: #ffff;margin-left: 10px;">
保存
</el-button>
</Popover>
<el-button v-if="!searchData.sellId" type="primary"
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-button v-if="!searchData.sellId" type="primary"
size="small" :loading="datas.loading"
style="color: #ffff;margin-left: 10px;"
@click="setTemplate(0)">
保存模板
保存{{searchData.TemplateType!=2?'行程':'广告'}}
</el-button>
</template>
......@@ -135,11 +134,12 @@
</template>
<script lang="ts" setup>
import { ArrowDown } from '@element-plus/icons-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, useSellTemplate } from '@/store'
import { useMainStore, useSlidesStore, useSellTemplateStore } from '@/store'
import useScreening from '@/hooks/useScreening'
import useImport from '@/hooks/useImport'
import useSlideHandler from '@/hooks/useSlideHandler'
......@@ -173,9 +173,9 @@ const mainStore = useMainStore()
const slidesStore = useSlidesStore()
const layoutsStore = useSlidesStore()
const { title, slides, slideIndex, viewportRatio } = storeToRefs(slidesStore)
const { SalesEditor, SalesBack } = storeToRefs(useSellTemplate())
const SalesEditorStore = useSellTemplate()
const SalesBackStore = useSellTemplate()
const { SalesEditor, SalesBack } = storeToRefs(useSellTemplateStore())
const SalesEditorStore = useSellTemplateStore()
const SalesBackStore = useSellTemplateStore()
const { enterScreening, enterScreeningFromStart } = useScreening()
const { importSpecificFile, importPPTXFile, exporting } = useImport()
const { resetSlides } = useSlideHandler()
......@@ -187,7 +187,6 @@ const hotkeyDrawerVisible = ref(false)
const editingTitle = ref(false)
const titleInputRef = ref<InstanceType<typeof Input>>()
const titleValue = ref('')
const sellSaveVisible = ref(false)
const { setNewDatasList } = useEditor()
......@@ -270,22 +269,37 @@ const setNewDatas = (type,i) => {
CoverImgStore.setCoverImg(null)
}
}
// 导入PSD6
// 导入PSD
const UploadPsdHandler = () => {
psdVisibleStatus.value = true
}
// 销售新增修改模版
// 销售新增修改行程、广告
const SetSellTemplate = async (type) => {
try {
let TempId = 0
if(queryObj.value.TempId) TempId = queryObj.value.TempId
if(!TempId){
return ElMessage({
showClose: true,
message: `${queryObj.value.TemplateType==2?'广告':'模版'}不存在, 请重选`,
type: 'warning',
})
}
let Id = 0
if(searchData.value.sellId&&!type) Id = searchData.value.sellId
if(searchData.value.sellId&&type==0) Id = searchData.value.sellId
if(type==1) Id = 0
let queryMsg = {
Id: Id,
TempId: queryObj.value.TempId,
TempId: TempId,
TempData: queryObj.value.TempData,
Title: queryObj.value.Title
Title: queryObj.value.Title,
OWidth: 0,
OHeight: 0,
}
if(queryObj.value.TemplateType==2){
queryMsg.OWidth = queryObj.value.Width
queryMsg.OHeight = queryObj.value.Height
}
let TemplateRes = await ConfigService.sellSetTemplate(queryMsg);
if (TemplateRes.data.resultCode == 1) {
......@@ -294,10 +308,10 @@ const SetSellTemplate = async (type) => {
message: '操作成功',
type: 'success',
})
sellSaveVisible.value = false
setTimeout(()=>{
if(type==1) setTimeout(()=>{
SalesEditorStore.setSalesEditor(0)
},100)
}else{
ElMessage({
showClose: true,
......@@ -310,7 +324,7 @@ const SetSellTemplate = async (type) => {
}
// 新增修改模版
// 新增修改行程、广告模版
const SetTripTemplateSlide = async () => {
try {
let TemplateRes = await ConfigService.SetTripTemplateSlide(queryObj.value);
......@@ -326,6 +340,7 @@ const SetTripTemplateSlide = async () => {
})
datas.DataSource.DataSourceOverlay = false
dataLoadingStore.setDataLoading(1)
useSlidesStore().setThumbnails([])
}else{
ElMessage({
showClose: true,
......@@ -387,7 +402,7 @@ const uploadImageHandler = async ()=>{
queryObj.value.PageImage.push(url)
}
}
useSlidesStore().setThumbnails([])
}
loadingInstance.value.close()
......@@ -407,7 +422,7 @@ const dataURLtoFile = (dataurl:string, filename:string) => {
return new File([u8arr], filename, { type: mime });
}
// 用户新增修改数据
// 用户新增修改行程数据
const SetTripTemplateConfig = async () => {
try {
let queryMsg = {
......@@ -445,13 +460,17 @@ const setTemplate = async (type) =>{
if(SourceLoading.value) setNewDatasList(datas.DataSource)
await uploadImageHandler()
console.log(JSON.stringify(queryObj.value))
let arr = JSON.parse(JSON.stringify(slides.value))
if(dataLoading.value){
dataLoadingStore.setDataLoading(0)
}
if(model.value==1&&userInfo.value.IsEditTripTemplate==1){
arr.forEach(x=>{
if(searchData.value.TemplateType==2) {
x.pageType = 0
x.width = queryObj.value.Width
x.height = queryObj.value.Height
}
x.elements.forEach(y=>{
delete y.TemplateList
})
......@@ -464,23 +483,23 @@ const setTemplate = async (type) =>{
message: '请生成封面图',
type: 'warning',
})
}else if(queryObj.value.CoverImg=='' && CoverImg.value.startsWith("data:image")){
}else if(queryObj.value.CoverImg=='' && CoverImg.value && CoverImg.value.startsWith("data:image")){
let name = new Date().getTime()+".jpg"
const file = dataURLtoFile(CoverImg.value, name)
let url = await AliyunUpload.UploadAsync(file,`Feature/CoverImg_${name}`)
queryObj.value.CoverImg = url
}
if(queryObj.value.Title==''||!queryObj.value.LineId||queryObj.value.LineName==''
||queryObj.value.CountryName==''||queryObj.value.SeasonName==''
){
datas.loading = false
mainStore.setToolbarState(ToolbarStates.EL_TEMPLATEDATA)
return ElMessage({
showClose: true,
message: '请完善右侧模版数据',
type: 'warning',
})
}
if(searchData.value.TemplateType!=2&&(queryObj.value.Title==''
||!queryObj.value.LineId||queryObj.value.LineName==''
||queryObj.value.CountryName==''||queryObj.value.SeasonName=='')){
datas.loading = false
mainStore.setToolbarState(ToolbarStates.EL_TEMPLATEDATA)
return ElMessage({
showClose: true,
message: '请完善右侧模版数据',
type: 'warning',
})
}
} else {
arr.forEach(x=>{
x.elements.forEach(y=>{
......@@ -500,15 +519,13 @@ const setTemplate = async (type) =>{
})
}
}
// console.log(arr,'-------tttt')
queryObj.value.TempData = JSON.stringify(arr)
console.log(JSON.stringify(queryObj.value))
if(model.value==1&&userInfo.value.IsEditTripTemplate==1){
await SetTripTemplateSlide()
}else if(ConfigId.value&&model.value!=2){
await SetTripTemplateConfig()
}else if(model.value==2){
}else if(model.value==2&&SalesEditor.value>0){
await SetSellTemplate(type)
}
}
......
......@@ -7,7 +7,7 @@
v-for="slide in renderSlides"
:key="slide.id"
:slide="slide"
:size="viewportRatio<1 ? VIEWPORT_SIZE : VIEWPORT_VER_SIZE"
:size="viewportRatio<1 ? VIEWPORT_SIZE.Value : VIEWPORT_VER_SIZE.Value"
/>
</div>
</div>
......
......@@ -5,17 +5,17 @@
<ThumbnailSlide
class="thumbnail"
:slide="currentSlide"
:size="viewportRatio<1 ? VIEWPORT_SIZE : VIEWPORT_VER_SIZE"
:size="viewportRatio<1 ? VIEWPORT_SIZE.Value : VIEWPORT_VER_SIZE.Value"
v-if="rangeType === 'current'"
/>
<template v-else>
<ThumbnailSlide
class="thumbnail"
:class="{ 'break-page': (index + 1) % count === 0 }"
v-for="(slide, index) in slides"
v-for="(slide, index) in newSlides"
:key="slide.id"
:slide="slide"
:size="viewportRatio<1 ? VIEWPORT_SIZE : VIEWPORT_VER_SIZE"
:size="viewportRatio<1 ? VIEWPORT_SIZE.Value : VIEWPORT_VER_SIZE.Value"
/>
</template>
</div>
......@@ -80,6 +80,7 @@ const emit = defineEmits<{
}>()
const { slides, currentSlide, viewportRatio } = storeToRefs(useSlidesStore())
const newSlides = ref<Array<any>>([])
const pdfThumbnailsRef = ref<HTMLElement>()
const rangeType = ref<'all' | 'current'>('all')
......@@ -88,7 +89,7 @@ const padding = ref(false)
const expPDF = () => {
if (!pdfThumbnailsRef.value) return
const width = viewportRatio.value<1 ? VIEWPORT_SIZE : VIEWPORT_VER_SIZE
const width = viewportRatio.value<1 ? VIEWPORT_SIZE.Value : VIEWPORT_VER_SIZE.Value
const pageSize = {
width: width,
height: rangeType.value === 'all' ? width * viewportRatio.value * count.value : width * viewportRatio.value,
......@@ -96,6 +97,16 @@ const expPDF = () => {
}
print(pdfThumbnailsRef.value, pageSize)
}
const formatSliders =()=>{
const width = viewportRatio.value<1 ? VIEWPORT_SIZE.Value : VIEWPORT_VER_SIZE.Value
slides.value.forEach((x)=>{
let item = JSON.parse(JSON.stringify(x))
item.elements = item.elements.filter(y=>y.left<width && (y.left+y.width)>=0)
newSlides.value.push(item)
})
}
formatSliders()
</script>
<style lang="scss" scoped>
......
This diff is collapsed.
......@@ -6,7 +6,13 @@
></div>
<ElementFlip />
<div class="element-data-mapping">
<divider />
<div class="q-mb-md row items-center">
<div class="text-small col">锁定裁剪</div>
<el-checkbox v-model="ctrlKeyState" label="锁定裁剪比列" size="small" @change="getLockCutRatio"/>
</div>
</div>
<ButtonGroup class="row" passive>
<Button first style="width: calc(100% / 6 * 5);" @click="clipImage()"><IconTailoring class="btn-icon" /> 裁剪图片</Button>
<Popover trigger="click" v-model:value="clipPanelVisible" style="width: calc(100% / 6);">
......@@ -67,7 +73,7 @@
<script lang="ts" setup>
import { type Ref, ref, provide } from 'vue'
import { storeToRefs } from 'pinia'
import { useMainStore, useSlidesStore, useScreenStore } from '@/store'
import { useMainStore, useSlidesStore, useScreenStore, useKeyboardStore } from '@/store'
import type { PPTImageElement, SlideBackground } from '@/types/slides'
import { CLIPPATHS } from '@/configs/imageClip'
import { getImageDataURL } from '@/utils/image'
......@@ -123,8 +129,11 @@ const ratioClipOptions = [
const mainStore = useMainStore()
const slidesStore = useSlidesStore()
const keyboardStore = useKeyboardStore()
const { handleElement, handleElementId } = storeToRefs(mainStore)
const { currentSlide } = storeToRefs(slidesStore)
const { ctrlKeyState } = storeToRefs(useKeyboardStore())
const param = query()
const mode = parseInt(param.model??1)
......@@ -138,6 +147,10 @@ const { imgReplaceVisible } = storeToRefs(useScreenStore())
provide('imgType',2)
const getLockCutRatio = () =>{
keyboardStore.setCtrlKeyState(ctrlKeyState.value)
}
const getImgVis = () =>{
imgReplaceVisible.value = true
}
......@@ -175,6 +188,7 @@ const getImageElementDataBeforeClip = () => {
// 预设裁剪
const presetImageClip = (shape: string, ratio = 0) => {
if(shape=='ellipse') ratio = 1
const _handleElement = handleElement.value as PPTImageElement
const {
......
......@@ -14,8 +14,17 @@
<Divider />
<SelectGroup class="row">
<Select
<SelectGroup class="row formatFontsBox">
<el-select v-model="richTextAttrs.fontname" placeholder="" filterable
@change="emitRichTextCommand('fontname', richTextAttrs.fontname)">
<el-option
v-for="item in formatFonts"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<!-- <Select
class="font-select"
style="width: 60%;"
:value="richTextAttrs.fontname"
......@@ -27,7 +36,7 @@
<template #icon>
<IconFontSize />
</template>
</Select>
</Select> -->
<Select
style="width: 40%;"
:value="richTextAttrs.fontsize"
......@@ -658,4 +667,7 @@ watch(handleElement, () => {
.popover-btn {
padding: 0 3px;
}
.formatFontsBox .el-input__wrapper{
border: 0;
}
</style>
\ No newline at end of file
......@@ -16,6 +16,7 @@
</Button>
</div>
</div>
<template v-if="searchData.TemplateType!=2">
<div class="row q-mt-md">
<Button style="flex: 1;" @click="AllDataSource()">
<!-- <span class="Required">*</span> -->
......@@ -34,11 +35,12 @@
/>
</el-select>
</div>
</template>
<p class="q-mt-md"><span class="Required q-mr-md">*</span>模版名称</p>
<p class="q-mt-md"><span class="Required q-mr-md">*</span>{{searchData.TemplateType!=2?'模版名称':'广告名称'}}</p>
<div class="row wrap q-mt-md">
<el-input v-model="titleValue" @blur="handleUpdateTitle()"
placeholder="输入模板名称" class="input-with-select"></el-input>
:placeholder="searchData.TemplateType!=2?'输入模板名称':'输入广告名称'" class="input-with-select"></el-input>
</div>
<p class="q-mt-md"><span class="Required q-mr-md">*</span>国家:</p>
<div class="row wrap q-mt-md">
......@@ -114,7 +116,7 @@
<el-option
v-for="item in dispositionObj.ColorList"
:key="item.ColorValue"
:label="item.ColorName"
:label="`${item.ColorName} / ${item.ColorValue}`"
:value="item.ColorValue"
/>
</el-select>
......@@ -186,7 +188,7 @@
class="thumbnail"
:key="slides[slideIndex].id"
:slide="slides[slideIndex]"
:size="viewportRatio<1 ? VIEWPORT_SIZE : VIEWPORT_VER_SIZE"
:size="viewportRatio<1 ? VIEWPORT_SIZE.Value : VIEWPORT_VER_SIZE.Value"
/>
</div>
</div>
......@@ -202,7 +204,7 @@
import { useMainStore, useSlidesStore, useScreenStore } from '@/store'
import LineService from '@/services/LineService'
import ConfigService from '@/services/ConfigService'
import { injectKeyDataSource } from '@/types/injectKey'
import { injectKeyDataSource, injectKeyTemplate } from '@/types/injectKey'
import { getHtmlPlainText } from '@/utils/common'
import useExport from '@/hooks/useExport'
import ThumbnailSlide from '@/views/components/ThumbnailSlide/index.vue'
......@@ -222,8 +224,10 @@
})
const dispositionObj = ref({} as any)
const queryObj = ref({} as any)
const searchData = ref({} as any)
datas.DataSource = inject(injectKeyDataSource)
queryObj.value = inject(injectKeyDataSource).queryObj
searchData.value = inject(injectKeyTemplate)
const lines = ref([] as Array < any > ) //线路
const Series = ref([] as Array < any > ) //系列
......@@ -385,19 +389,21 @@
// mainStore.setDialogForExport(type)
let CoverImgPath = CoverImg.value.split('/')
let namePath = ''
let namePaths = ''
queryObj.value.CoverImg = ''
mainMenuVisible.value = false
isCoverImgStore.setIsCoverImg(true)
let datas = await exportFeatureImg(FeatureImgRef.value, 'jpeg', 1, false,0)
if(datas){
let t =new Date().getTime()
let name = new Date().getTime()+".jpg"
let name = new Date().getTime()+".jpeg"
const file = dataURLtoFile(datas, name)
let url = ''
if(CoverImgPath[CoverImgPath.length-1].indexOf('CoverImg_')==-1){
namePath = `CoverImg_${name}`
}else {
namePath = CoverImgPath[CoverImgPath.length-1]
namePaths = CoverImgPath[CoverImgPath.length-1]
namePath = namePaths.split('?')[0]
}
url = await AliyunUpload.UploadAsync(file,`Feature/${namePath}?t=${t}`)
coverImgStore.setCoverImg(url)
......
......@@ -99,32 +99,34 @@
<div class="row">
<Button style="flex: 1;" @click="applyBackgroundAllSlide()">应用背景到全部</Button>
</div>
<Divider />
<template v-if="queryObj.TemplateType!=2">
<Divider />
<div class="row">
<div style="width: 40%;">画布尺寸:</div>
<!-- <Select
style="width: 60%;"
:value="viewportRatio"
@update:value="value => updateViewportRatio(value as number)"
:options="[
{ label: '宽屏 16 : 9', value: 0.5625 },
{ label: '宽屏 16 : 10', value: 0.625 },
{ label: '标准 4 : 3', value: 0.75 },
{ label: '纸张 A3 / A4', value: 0.70710678 },
]"
/> -->
<Select
style="width: 60%;"
:value="viewportRatio"
@update:value="value => updateViewportRatio(value as number)"
:options="[
{ label: '横屏', value: 0.7069 },
{ label: '竖屏', value: 1.414 },
]"
/>
</div>
<div class="row">
<div style="width: 40%;">画布尺寸:</div>
<!-- <Select
style="width: 60%;"
:value="viewportRatio"
@update:value="value => updateViewportRatio(value as number)"
:options="[
{ label: '宽屏 16 : 9', value: 0.5625 },
{ label: '宽屏 16 : 10', value: 0.625 },
{ label: '标准 4 : 3', value: 0.75 },
{ label: '纸张 A3 / A4', value: 0.70710678 },
]"
/> -->
<Select
style="width: 60%;"
:value="viewportRatio"
@update:value="value => updateViewportRatio(value as number)"
:options="[
{ label: '横屏', value: 0.7069 },
{ label: '竖屏', value: 1.414 },
]"
/>
</div>
</template>
<Divider />
......@@ -136,16 +138,25 @@
<IconRight v-else />
</span>
</div>
<div class="row">
<div class="row formatFontsBox">
<div style="width: 40%;">字体:</div>
<Select
<el-select v-model="theme.fontName" placeholder="" filterable style="width: 137px;"
@change="updateTheme({ fontName: theme.fontName })">
<el-option
v-for="item in formatFonts"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<!-- <Select
style="width: 60%;"
:value="theme.fontName"
@update:value="value => updateTheme({ fontName: value as string })"
:options="[
...formatFonts
]"
/>
/> -->
<!-- ...availableFonts,
...WEB_FONTS -->
</div>
......@@ -309,7 +320,7 @@ import { WEB_FONTS } from '@/configs/font'
import useHistorySnapshot from '@/hooks/useHistorySnapshot'
import useSlideTheme from '@/hooks/useSlideTheme'
import { getImageDataURL } from '@/utils/image'
import { injectKeyDataSource } from '@/types/injectKey'
import { injectKeyDataSource, injectKeyTemplate } from '@/types/injectKey'
import ColorButton from './common/ColorButton.vue'
import FileInput from '@/components/FileInput.vue'
......@@ -329,6 +340,8 @@ const datas = reactive({
const queryObj = ref({} as any)
datas.DataSource = inject(injectKeyDataSource)
queryObj.value = inject(injectKeyDataSource).queryObj
const searchData = ref({} as any)
searchData.value = inject(injectKeyTemplate)
const slidesStore = useSlidesStore()
const { availableFonts } = storeToRefs(useMainStore())
const { slides, currentSlide, viewportRatio, theme, slideIndex } = storeToRefs(slidesStore)
......@@ -547,4 +560,7 @@ const updateViewportRatio = (value: number) => {
}
}
}
.formatFontsBox .el-input__wrapper{
border: 0;
}
</style>
\ No newline at end of file
......@@ -63,10 +63,10 @@ const updateUnionElement = ref<boolean>(true)
const loading = ref<boolean>(false)
const loadingObj = ref<any>('')
const query = ref<string>('')
let maxWidth = VIEWPORT_SIZE,maxHeight = VIEWPORT_VER_SIZE, viewportRatio = slidesStore.viewportRatio
let maxWidth = VIEWPORT_SIZE,maxHeight = VIEWPORT_VER_SIZE.Value, viewportRatio = slidesStore.viewportRatio
if(viewportRatio<1){
maxWidth = VIEWPORT_VER_SIZE
maxHeight = VIEWPORT_SIZE
maxWidth = VIEWPORT_VER_SIZE.Value
maxHeight = VIEWPORT_SIZE.Value
}
if(handleElement.value?.dataMapping){
......
<template>
<Screen v-if="screening" />
<div class="pptist-editor" v-show="model!=3">
<EditorHeader class="layout-header" />
<div class="layout-content">
<Thumbnails class="layout-content-left" />
<div class="layout-content-center" style="overflow: hidden;">
<CanvasTool class="center-top" />
<!-- :style="{ height: `calc(100% - ${remarkHeight + 40}px)` }" -->
<Canvas class="center-body" :style="{ height: `calc(100% - 40px)`}" />
<div v-if="!reloadLoading" style="height: calc(100% - 40px);overflow: scroll;background: #f9f9f9;">
<!-- <Canvas class="center-body" :style="{ height: `calc(100% - 40px)`}" /> -->
<Canvas class="center-body" :style="{ height: '100%'}" />
</div>
<!-- <Remark
class="center-bottom"
v-model:height="remarkHeight"
......@@ -37,7 +40,7 @@
class="thumbnail"
:key="slide.id"
:slide="slide"
:size="param.w?param.w:(viewportRatio<1 ? VIEWPORT_SIZE : VIEWPORT_VER_SIZE )"
:size="param.w?param.w:(viewportRatio<1 ? VIEWPORT_SIZE.Value : VIEWPORT_VER_SIZE.Value )"
/>
</div>
</div>
......@@ -71,20 +74,24 @@ import ConfigService from '@/services/ConfigService'
import { VIEWPORT_SIZE, VIEWPORT_VER_SIZE } from '@/configs/canvas'
import ThumbnailSlide from '@/views/components/ThumbnailSlide/index.vue'
import Screen from '@/views/Screen/index.vue'
const slidesStore = useSlidesStore()
const TemplateTypeStore = useScreenStore()
const TempDataSourceStore = useScreenStore()
const { slides, slideIndex, viewportRatio } = storeToRefs(slidesStore)
const { model, TemplateType, ConfigId, TempId} = storeToRefs(TemplateTypeStore)
const { screening,model, TemplateType, ConfigId, TempId} = storeToRefs(TemplateTypeStore)
const pageTypesList = ref([] as any)
const searchData = ref({} as any)
searchData.value = inject(injectKeyTemplate)
let TempIds = 0
let TemplatesType = 0
if(searchData.value.TempId) TempIds = searchData.value.TempId
else if(TempId.value) TempIds = TempId.value
if(searchData.value.TemplateType) TemplatesType = searchData.value.TemplateType
if(searchData.value.TemplateType!=2&&!searchData.value.TempId) useSlidesStore().setViewportRatio(1.414)
const refThumbnails = ref(null)
const param = ref("")
param.value = query()
......@@ -109,6 +116,9 @@ const datas = reactive({
ColorStr: null,//颜色值 是 [string]
TempType: 1,// 版面类型(1-横版,2-竖版)
TagJsonStr: '',// 标签
TemplateType: TemplatesType,// 1行程模版 2广告模版
Width: 0,
Height: 0,
}
},
......@@ -122,6 +132,15 @@ watch(() => refThumbnails.value, () => {
getDomeWH()
},1000)
})
const reloadLoading = ref(true)
const reload = () => {
setTimeout(()=>{
reloadLoading.value = false
},200)
}
// 监听viewportRatio 重新渲染画布
watch(viewportRatio, reload())
const getDomeWH = async () => {
if(refThumbnails.value){
let height = await refThumbnails.value.offsetHeight;
......@@ -155,7 +174,8 @@ const GetTripFiled = async () =>{
console.log("TemplateGetTripFiled", error);
}
}
GetTripFiled()
if(searchData.value.TemplateType!=2) GetTripFiled()
const mainStore = useMainStore()
const { dialogForExport, showSelectPanel, showSearchPanel } = storeToRefs(mainStore)
......
This diff is collapsed.
......@@ -55,8 +55,8 @@ const insertTextElement = () => {
const height = 56
createTextElement({
left: (VIEWPORT_SIZE - width) / 2,
top: (VIEWPORT_SIZE * viewportRatio.value - height) / 2,
left: (VIEWPORT_SIZE.Value - width) / 2,
top: (VIEWPORT_SIZE.Value * viewportRatio.value - height) / 2,
width,
height,
}, { content: '<p>新添加文本</p>' })
......@@ -81,8 +81,8 @@ const insertShapeElement = (type: 'square' | 'round') => {
const size = 200
createShapeElement({
left: (VIEWPORT_SIZE - size) / 2,
top: (VIEWPORT_SIZE * viewportRatio.value - size) / 2,
left: (VIEWPORT_SIZE.Value - size) / 2,
top: (VIEWPORT_SIZE.Value * viewportRatio.value - size) / 2,
width: size,
height: size,
}, shape[type])
......
......@@ -83,8 +83,8 @@ const canvasScale = computed(() => {
const contentheight = contentRef.value.clientHeight
const contentRatio = contentheight / contentWidth
if (contentRatio >= viewportRatio.value) return (contentWidth - 20) / VIEWPORT_SIZE
return (contentheight - 20) / viewportRatio.value / VIEWPORT_SIZE
if (contentRatio >= viewportRatio.value) return (contentWidth - 20) / VIEWPORT_SIZE.Value
return (contentheight - 20) / viewportRatio.value / VIEWPORT_SIZE.Value
})
onMounted(() => {
......@@ -93,8 +93,8 @@ onMounted(() => {
})
const viewportStyles = computed(() => ({
width: VIEWPORT_SIZE * canvasScale.value + 'px',
height: VIEWPORT_SIZE * viewportRatio.value * canvasScale.value + 'px',
width: VIEWPORT_SIZE.Value * canvasScale.value + 'px',
height: VIEWPORT_SIZE.Value * viewportRatio.value * canvasScale.value + 'px',
}))
const elementList = ref<PPTElement[]>([])
......
......@@ -3,8 +3,8 @@
class="screen-slide"
style="overflow: hidden;"
:style="{
width: (viewportRatio<1 ? VIEWPORT_SIZE:VIEWPORT_VER_SIZE )+ 'px',
height: (viewportRatio<1 ? VIEWPORT_SIZE:VIEWPORT_VER_SIZE ) * viewportRatio + 'px',
width: (viewportRatio<1 ? VIEWPORT_SIZE.Value:VIEWPORT_VER_SIZE.Value )+ 'px',
height: (viewportRatio<1 ? VIEWPORT_SIZE.Value:VIEWPORT_VER_SIZE.Value ) * viewportRatio + 'px',
transform: `scale(${scale})`,
}"
>
......
......@@ -68,7 +68,7 @@ const slidesWithTurningMode = computed(() => {
}
})
})
const scale = computed(() => props.slideWidth / (viewportRatio.value<1 ? VIEWPORT_SIZE:VIEWPORT_VER_SIZE ))
const scale = computed(() => props.slideWidth / (viewportRatio.value<1 ? VIEWPORT_SIZE.Value:VIEWPORT_VER_SIZE.Value ))
provide(injectKeySlideScale, scale)
</script>
......
This diff is collapsed.
<template>
<div class="common-layout" style="background: #f3f6fb;height:100vh;overflow: auto;">
<div style="padding: 30px; max-width:1440px; margin:0 auto;height: 100%;">
<el-container class="SellTemplate-form bg-white">
<el-aside width="200px">
<sellNavs />
</el-aside>
<el-container>
<el-main class="bg-white rounded-bottom rounded-top">
<SellTemplate />
</el-main>
</el-container>
</el-container>
<div class="rounded-bottom rounded-top">
<SellTemplate />
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, provide, watch, inject } from 'vue'
import sellNavs from '@/views/components/sellNavs/index.vue'
import SellTemplate from './SellTemplate.vue'
const datas = reactive({
SellDatas:{
activeId: 1,
......@@ -30,7 +23,6 @@
<style lang="scss" scoped>
.SellTemplate-form{
height: 100%;
background: none;
}
</style>
\ No newline at end of file
......@@ -9,8 +9,8 @@
class="elements"
style="overflow: hidden;"
:style="{
width: (viewportRatio<1 ? VIEWPORT_SIZE:VIEWPORT_VER_SIZE )+ 'px',
height: (viewportRatio<1 ? VIEWPORT_SIZE:VIEWPORT_VER_SIZE ) * viewportRatio + 'px',
width: (viewportRatio<1 ? VIEWPORT_SIZE.Value:VIEWPORT_VER_SIZE.Value )+ 'px',
height: (viewportRatio<1 ? VIEWPORT_SIZE.Value:VIEWPORT_VER_SIZE.Value ) * viewportRatio + 'px',
transform: `scale(${scale})`,
}"
v-if="visible"
......@@ -51,7 +51,7 @@ const { viewportRatio } = storeToRefs(useSlidesStore())
const background = computed(() => props.slide.background)
const { backgroundStyle } = useSlideBackgroundStyle(background)
const scale = computed(() => props.size / (viewportRatio.value<1 ? VIEWPORT_SIZE:VIEWPORT_VER_SIZE ))
const scale = computed(() => props.size / (viewportRatio.value<1 ? VIEWPORT_SIZE.Value:VIEWPORT_VER_SIZE.Value ))
provide(injectKeySlideScale, scale)
</script>
......
......@@ -60,8 +60,8 @@ const audioIconSize = computed(() => {
return Math.min(props.elementInfo.width, props.elementInfo.height) + 'px'
})
const audioPlayerPosition = computed(() => {
const canvasWidth = VIEWPORT_SIZE
const canvasHeight = VIEWPORT_SIZE * viewportRatio.value
const canvasWidth = VIEWPORT_SIZE.Value
const canvasHeight = VIEWPORT_SIZE.Value * viewportRatio.value
const audioWidth = 280 / scale.value
const audioHeight = 50 / scale.value
......
......@@ -62,8 +62,8 @@ const audioIconSize = computed(() => {
return Math.min(props.elementInfo.width, props.elementInfo.height) + 'px'
})
const audioPlayerPosition = computed(() => {
const canvasWidth = VIEWPORT_SIZE
const canvasHeight = VIEWPORT_SIZE * viewportRatio.value
const canvasWidth = VIEWPORT_SIZE.Value
const canvasHeight = VIEWPORT_SIZE.Value * viewportRatio.value
const audioWidth = 280 / canvasScale.value
const audioHeight = 50 / canvasScale.value
......
<template>
<div
class="base-element-text"
style="pointer-events:none;"
class="editable-element-text"
:class="{ 'lock': elementInfo.lock }"
:style="{
top: elementInfo.top + 'px',
left: elementInfo.left + 'px',
......@@ -14,10 +16,11 @@
>
<div
class="element-content"
ref="elementRef"
:style="{
width: elementInfo.vertical ? 'auto' : elementInfo.width + 'px',
height: elementInfo.vertical ? elementInfo.height + 'px' : 'auto',
backgroundColor: elementInfo.fill,
background: elementInfo.fill,
opacity: elementInfo.opacity,
textShadow: shadowStyle,
lineHeight: elementInfo.lineHeight,
......@@ -25,43 +28,49 @@
color: elementInfo.defaultColor,
fontFamily: elementInfo.defaultFontName,
writingMode: elementInfo.vertical ? 'vertical-rl' : 'horizontal-tb',
WebkitTextStroke: elementInfo.stroke ? elementInfo.stroke:''
}"
:class="{'clip-box':elementInfo.clip}"
>
<ElementOutline
:width="elementInfo.width"
:height="elementInfo.height"
:outline="elementInfo.outline"
/>
<div
class="text ProseMirror-static"
<ProsemirrorEditor
:class="{'text-static':elementInfo.clip,'text':!elementInfo.clip}"
:elementId="elementInfo.id"
:defaultColor="elementInfo.defaultColor"
:defaultFontName="elementInfo.defaultFontName"
:editable="!elementInfo.lock"
:value="elementInfo.content"
:style="{
'--paragraphSpace': `${elementInfo.paragraphSpace === undefined ? 5 : elementInfo.paragraphSpace}px`,
}"
v-html="elementInfo.content"
></div>
/>
<!-- 当字号过大且行高较小时,会出现文字高度溢出的情况,导致拖拽区域无法被选中,因此添加了以下节点避免该情况 -->
<div class="drag-handler top"></div>
<div class="drag-handler bottom"></div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { computed } from 'vue'
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
import type { PPTTextElement } from '@/types/slides'
import ElementOutline from '@/views/components/element/ElementOutline.vue'
import useElementShadow from '@/views/components/element/hooks/useElementShadow'
import ProsemirrorEditor from '@/views/components/element/ProsemirrorEditor.vue'
const props = defineProps<{
elementInfo: PPTTextElement
}>()
const shadow = computed(() => props.elementInfo.shadow)
const { shadowStyle } = useElementShadow(shadow)
</script>
<style lang="scss" scoped>
.base-element-text {
.editable-element-text {
position: absolute;
&.lock .element-content {
cursor: default;
}
}
.rotate-wrapper {
width: 100%;
......@@ -72,9 +81,32 @@ const { shadowStyle } = useElementShadow(shadow)
padding: 10px;
line-height: 1.5;
word-break: break-word;
cursor: move;
.text {
position: relative;
}
.text-static {
position: static;
}
::v-deep(a) {
cursor: text;
}
}
.drag-handler {
height: 20px;
position: absolute;
left: 0;
right: 0;
&.top {
top: 0;
}
&.bottom {
bottom: 0;
}
}
.clip-box{
-webkit-background-clip: text !important;
}
</style>
<template>
<div class="navs-form">
<el-button class="navs-Button" type="primary" @click="sellAdd(1)">
<div class="navs-form row items-center">
<el-dropdown split-button
size="small" type="primary"
@click="searchData.TemplateType=1,sellAdd(1)">
<span style="font-size: 23px;margin-right: 10px;"> + </span> <span>新建</span>
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>
<el-button type="primary"
size="small" :loading="datas.loading"
style="color: #ffff;"
@click.stop="searchData.TemplateType=1,sellAdd(1)">
新建行程
</el-button>
</el-dropdown-item>
<el-dropdown-item v-if="true">
<el-button type="primary"
size="small" :loading="datas.loading"
style="color: #ffff;"
@click.stop="searchData.TemplateType=2,sellAdd(3)">
新建广告
</el-button>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<!-- <el-button class="navs-Button" type="primary" @click="sellAdd(1)">
<span style="font-size: 23px;margin-right: 10px;"> + </span> <span>新建</span>
</el-button> -->
<div class="q-mt-lg">
<div v-for="(item,index) in datas.navs" class="q-mb-md">
<el-button class="navs-Button"
......@@ -17,15 +42,16 @@
</template>
<script setup lang="ts">
import { ArrowDown } from '@element-plus/icons-vue'
import { reactive, ref, inject, watch, onMounted} from "vue";
import { storeToRefs } from 'pinia'
import { useSellTemplate,useScreenStore } from '@/store'
import { useSellTemplateStore,useScreenStore } from '@/store'
import { injectKeyTemplate } from '@/types/injectKey'
const searchData = ref({} as any)
searchData.value = inject(injectKeyTemplate)
const SalesEditorStore = useSellTemplate()
const SalesEditorStore = useSellTemplateStore()
const marketStore = useScreenStore()
const { market } = storeToRefs(useScreenStore())
......@@ -37,17 +63,17 @@
]
})
datas.SellDatas = inject('SellDatas')
const sellAdd = () =>{
const sellAdd = (type:number) =>{
searchData.value.sellId = 0
searchData.value.sellTempId = 0
SalesEditorStore.setSalesEditor(1)
SalesEditorStore.setSalesEditor(type)
marketStore.setMarket(true)
}
</script>
<style lang="scss" scoped>
.navs-form{
padding: 15px;
/* padding: 15px; */
}
.navs-Button{
width: 100%;
......
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