Commit f57f8dc9 authored by 罗超's avatar 罗超

新增psd解析弹窗,修复宽高比例的放大缩小,修复按住鼠标中建拖动画布,修改纵横比例

parent dde46752
......@@ -5,5 +5,6 @@
// Generated by unplugin-auto-import
export {}
declare global {
const ElMessage: typeof import('element-plus/es')['ElMessage']
const ElMessageBox: typeof import('element-plus/es')['ElMessageBox']
}
......@@ -21,13 +21,22 @@ declare module 'vue' {
ElButton: typeof import('element-plus/es')['ElButton']
ElCheckTag: typeof import('element-plus/es')['ElCheckTag']
ElCol: typeof import('element-plus/es')['ElCol']
ElDialog: typeof import('element-plus/es')['ElDialog']
ElDivider: typeof import('element-plus/es')['ElDivider']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElInput: typeof import('element-plus/es')['ElInput']
ElOption: typeof import('element-plus/es')['ElOption']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElRow: typeof import('element-plus/es')['ElRow']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElTable: typeof import('element-plus/es')['ElTable']
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
ElUpload: typeof import('element-plus/es')['ElUpload']
FileInput: typeof import('./src/components/FileInput.vue')['default']
FormulaContent: typeof import('./src/components/LaTeXEditor/FormulaContent.vue')['default']
FullscreenSpin: typeof import('./src/components/FullscreenSpin.vue')['default']
Hue: typeof import('./src/components/ColorPicker/Hue.vue')['default']
Index: typeof import('./src/components/PSD/Index.vue')['default']
Input: typeof import('./src/components/Input.vue')['default']
LaTeXEditor: typeof import('./src/components/LaTeXEditor/index.vue')['default']
MenuContent: typeof import('./src/components/Contextmenu/MenuContent.vue')['default']
......@@ -49,4 +58,7 @@ declare module 'vue' {
TextArea: typeof import('./src/components/TextArea.vue')['default']
WritingBoard: typeof import('./src/components/WritingBoard.vue')['default']
}
export interface ComponentCustomProperties {
vLoading: typeof import('element-plus/es')['ElLoadingDirective']
}
}
......@@ -6,22 +6,11 @@ page {
Segoe UI, Arial, Roboto, 'PingFang SC', 'miui', 'Hiragino Sans GB', 'Microsoft Yahei',
sans-serif;
}
/* .0123456789+-ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz 联络方式酒店地址月日周一二三四五六日 */
@font-face {
font-family: "alifont";src: url("//at.alicdn.com/wf/webfont/MQHUV6e56ce5/FZ2wHD6g3frR.woff2") format("woff2"),
url("//at.alicdn.com/wf/webfont/MQHUV6e56ce5/wXT52gRya21s.woff") format("woff");
font-display: swap;
}
@font-face {
font-family: "chineseAlifont";font-weight: 500;src: url("//at.alicdn.com/wf/webfont/MQHUV6e56ce5/VV2owoIrvvbI.woff2") format("woff2"),
url("//at.alicdn.com/wf/webfont/MQHUV6e56ce5/xV0pJqRVm5nb.woff") format("woff");
font-display: swap;
}
/* .0123456789+-ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz导入设计文件基础数据绑定 */
@font-face {
font-family: "adobeFont";font-weight: 500;src: url("//at.alicdn.com/wf/webfont/MQHUV6e56ce5/PbzuLcRVxcau.woff2") format("woff2"),
url("//at.alicdn.com/wf/webfont/MQHUV6e56ce5/fM6mSq6emEV5.woff") format("woff");
font-display: swap;
font-family: "alifont";src: url("//at.alicdn.com/wf/webfont/MQHUV6e56ce5/WSdQY8NXp7aI.woff2") format("woff2"),
url("//at.alicdn.com/wf/webfont/MQHUV6e56ce5/cW4aPGiVFgIJ.woff") format("woff");
font-display: swap;
}
.alifont{
......@@ -39,14 +28,14 @@ page {
.overflow-hide{
overflow: hidden;
}
::-webkit-scrollbar {
/* ::-webkit-scrollbar {
display: none;
width: 0 !important;
height: 0 !important;
-webkit-appearance: none;
background: transparent;
color: transparent;
}
} */
.light-shadow {
box-shadow: 0px 0px 20px 0px rgba(76,87,125,0.2)!important;
}
......@@ -312,9 +301,20 @@ page {
.text-small{
font-size: 12px;
}
.text-title{
font-size: 1.25rem;
font-family: alifont;
font-weight: 600;
}
.text-light{
font-weight: 300;
}
.text-nowrap{
white-space: nowrap;
}
.el-table__header .el-table .cell{
font-family: 'alifont';
}
.el-button{
font-size: 12px !important;
}
\ No newline at end of file
<template>
<el-dialog v-model="visibleStatus" :show-close="false" :close-on-press-escape="false" :close-on-click-modal="false" style="max-width:650px;">
<template #header>
<div class="text-title">导入PSD设计文件</div>
</template>
<div v-if="!thumbnails || thumbnails.length==0">
<el-upload
v-loading="isResolving"
ref="upload"
class="upload-drop-box"
:on-change="handleChange"
action=""
drag
:limit="1"
:on-exceed="handleExceed"
:auto-upload="false"
accept=".psd,image/vnd.adobe.photoshop"
:show-file-list="false"
>
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
<div class="el-upload__text">
拖动文件到这里释放或<em>点击上传</em>
</div>
<template #tip>
<div class="el-upload__tip">
当前仅支持PSD文件上传解析
</div>
</template>
</el-upload>
<div class="text-right q-mt-lg">
<el-button @click="close" :disabled="isResolving">取消导入</el-button>
</div>
</div>
<div v-else>
<div class="f12 text-grey">已成功解析PSD文件,请核对下方图片是否正确:</div>
<div class="row" style="flex-wrap:wrap">
<img v-for="(x,i) in thumbnails" :key="i" style="height:auto;width:calc(25% - 10px);" class="q-mr-md rounded q-mt-md" :src="x" />
</div>
<div class="q-mt-lg row items-center">
<div class="col">
<el-button @click="resetPsdHandler">重新选择</el-button>
</div>
<div>
<el-button @click="close">取消导入</el-button>
<el-button type="primary" class="q-ml-lg">确认导入</el-button>
</div>
</div>
</div>
</el-dialog>
</template>
<script setup lang="ts">
import {ref,defineProps,watch} from 'vue'
import { UploadFilled } from '@element-plus/icons-vue'
import type { UploadProps, UploadUserFile,UploadInstance } from 'element-plus'
import PSD from 'psd.js'
import {toSlider} from '@/utils/psdParser/index'
const props = defineProps({
visible:{
type:Boolean,
required:true,
default:true
}
})
const emit = defineEmits<{
(event: 'update:visible', payload: boolean): void
(event: 'closed'): void
}>()
const visibleStatus = ref(props.visible)
const upload = ref<UploadInstance>()
const isResolving = ref(false)
const thumbnails = ref<Array<any>>([])
const handleChange: UploadProps['onChange'] = (uploadFile, uploadFiles) => {
isResolving.value=true
if(uploadFile){
var url = URL.createObjectURL(uploadFile?.raw)
try {
PSD.fromURL(url).then(async (psd:any) => {
thumbnails.value= await toSlider(psd)
isResolving.value=false
})
} catch (error) {
isResolving.value=false
}
}else{
isResolving.value=false
}
}
const handleExceed: UploadProps['onExceed'] = (files) => {
console.log('执行clear')
upload.value!.clearFiles()
const file = files[0] as UploadRawFile
upload.value!.handleStart(file)
}
const resetPsdHandler = ()=>{
thumbnails.value=[]
}
const close = () => {
visibleStatus.value=false
resetPsdHandler()
emit('update:visible', false)
emit('closed')
}
watch(() => props.visible, () => {
visibleStatus.value = props.visible
})
</script>
<style>
.upload-drop-box{
}
</style>
\ No newline at end of file
export const VIEWPORT_SIZE = 1000
\ No newline at end of file
export const VIEWPORT_SIZE = 1754
export const VIEWPORT_VER_SIZE = 1240
\ No newline at end of file
......@@ -15,8 +15,9 @@ export default () => {
const scaleCanvas = (command: '+' | '-') => {
let percentage = canvasPercentage.value
const step = 5
const max = 200
const max = 400
const min = 30
if (command === '+' && percentage <= max) percentage += step
if (command === '-' && percentage >= min) percentage -= step
......
......@@ -37,7 +37,7 @@ export const useSlidesStore = defineStore('slides', {
theme: theme, // 主题样式
slides: slides, // 幻灯片页面数据
slideIndex: 0, // 当前页面索引
viewportRatio: 0.75, // 可视区域比例,默认16:9 0.5625
viewportRatio: 0.7069, // 可视区域比例,默认16:9 0.5625
layoutSlides: slides, // 所有模版数据
}),
......
const fileToDataURL = (file: Blob): Promise<any> => {
return new Promise((resolve) => {
const reader = new FileReader()
reader.onloadend = (e) => resolve((e.target as FileReader).result)
reader.readAsDataURL(file)
})
}
const dataURLToImage = (dataURL: string): Promise<HTMLImageElement> => {
return new Promise((resolve) => {
const img = new Image()
img.onload = () => {
resolve(img)
}
img.src = dataURL
})
}
const canvastoFile = (canvas: HTMLCanvasElement, type: string, quality: number): Promise<Blob | null> => {
return new Promise((resolve) => canvas.toBlob((blob) => resolve(blob), type, quality))
}
/**
* 图片紧缩办法
* @param {Object} file 图片文件
* @param {String} type 想紧缩成的文件类型
* @param {Nubmber} quality 紧缩质量参数
* @returns 紧缩后的新图片
*/
export const compressionFile = async(file:File, type = 'image/jpeg', quality = 0.5) => {
const fileName = file.name
const canvas = document.createElement('canvas')
const context = canvas.getContext('2d') as CanvasRenderingContext2D
const base64 = await fileToDataURL(file)
const img = await dataURLToImage(base64)
canvas.width = img.width
canvas.height = img.height
context.clearRect(0, 0, img.width, img.height)
context.drawImage(img, 0, 0, img.width, img.height)
const blob = (await canvastoFile(canvas, type, quality)) as Blob // quality:0.5可根据实际状况核算
const newFile = await new File([blob], fileName, {
type: type
})
return newFile
}
export const compressionThumbnail = async(datURL:string, type = 'image/jpeg', overrideWidth=0, quality = 0.5) => {
const canvas = document.createElement('canvas')
const context = canvas.getContext('2d') as CanvasRenderingContext2D
const img = await dataURLToImage(datURL)
let width=img.width, height =img.height
if(overrideWidth>0){
width=overrideWidth
height = Math.ceil(img.height*(overrideWidth/img.width))
}
canvas.width = width
canvas.height = height
context.clearRect(0, 0, width, height)
context.drawImage(img, 0, 0, width, height)
const fileString = canvas.toDataURL(type, quality)
return fileString
}
export const cropImage = async(datURL:string, type = 'image/jpeg', {top,right,bottom,left}:any) => {
const canvas = document.createElement('canvas')
const context = canvas.getContext('2d') as CanvasRenderingContext2D
const img = await dataURLToImage(datURL)
let width=right-left, height =bottom-top
canvas.width = width
canvas.height = height
context.clearRect(0, 0, width, height)
context.drawImage(img, left, top, width, height, 0, 0, width, height)
const fileString = canvas.toDataURL(type) //(await canvastoFile(canvas, type, 1)) as Blob
return fileString
}
\ No newline at end of file
import { ResolveLayer } from "./layerService"
export const toSlider = async (psd:any)=>{
const thumbs = await ResolveLayer(psd);
return thumbs
}
\ No newline at end of file
import { compressionThumbnail, cropImage } from "./compressor"
let PSDObject:any = null
export const ResolveLayer = async (psd:any) => {
PSDObject = psd
const { _children } = psd.tree()
let thumbs = await ResolveThumbHandler(_children)
return thumbs
}
const ResolveThumbHandler = async (items:any)=>{
let imgs:Array<any> = []
let imgBase64String = PSDObject.image.toBase64()
for (let i = 0; i < items.length; i++) {
const item = items[i];
if(item.layer.artboard) {
let cropImagePath = await cropImage(imgBase64String,"image/jpeg",item.layer.artboard().export().coords)
let imagePath = await compressionThumbnail(cropImagePath,"image/jpeg",300)
imgs.push(imagePath)
}
}
return imgs
}
const ResolveHandler = async (items:any) =>{
let imgs:Array<any> = []
let imgBase64String = PSDObject.image.toBase64()
items.forEach(async (x:any)=>{
if(x.layer.artboard) {
//console.log(x.layer.artboard().export())
console.log(x.layer.image)
let cropImagePath = await cropImage(imgBase64String,"image/jpeg",x.layer.artboard().export())
console.log('加载了1 ',cropImagePath)
let imagePath = await compressionThumbnail(cropImagePath,"image/jpeg",300)
console.log('加载了2 ')
imgs.push(imagePath)
console.log('加载了 ',imgs.length)
}
if(x._children && x._children.length>0) ResolveHandler(x._children)
})
console.log('我已经返回了',imgs.length)
return imgs
}
\ No newline at end of file
import { ref, computed, onMounted, onUnmounted, watch, type Ref } from 'vue'
import { storeToRefs } from 'pinia'
import { useMainStore, useSlidesStore } from '@/store'
import { VIEWPORT_SIZE } from '@/configs/canvas'
import { VIEWPORT_SIZE,VIEWPORT_VER_SIZE } from '@/configs/canvas'
export default (canvasRef: Ref<HTMLElement | undefined>) => {
const viewportLeft = ref(0)
......@@ -16,16 +16,15 @@ 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
if (canvasHeight / canvasWidth > viewportRatio.value) {
const viewportActualWidth = canvasWidth * (canvasPercentage.value / 100)
mainStore.setCanvasScale(viewportActualWidth / VIEWPORT_SIZE)
mainStore.setCanvasScale(viewportActualWidth / USED_VIEWPORT_SIZE)
viewportLeft.value = (canvasWidth - viewportActualWidth) / 2
viewportTop.value = (canvasHeight - viewportActualWidth * viewportRatio.value) / 2
}
else {
} else {
const viewportActualHeight = canvasHeight * (canvasPercentage.value / 100)
mainStore.setCanvasScale(viewportActualHeight / (VIEWPORT_SIZE * viewportRatio.value))
mainStore.setCanvasScale(viewportActualHeight / (USED_VIEWPORT_SIZE * viewportRatio.value))
viewportLeft.value = (canvasWidth - viewportActualHeight / viewportRatio.value) / 2
viewportTop.value = (canvasHeight - viewportActualHeight) / 2
}
......@@ -34,6 +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 canvasWidth = canvasRef.value.clientWidth
const canvasHeight = canvasRef.value.clientHeight
......@@ -42,14 +42,20 @@ export default (canvasRef: Ref<HTMLElement | undefined>) => {
const newViewportActualHeight = canvasHeight * (newValue / 100)
const oldViewportActualHeight = canvasHeight * (oldValue / 100)
let canvasScale = 0
if (canvasHeight / canvasWidth > viewportRatio.value) {
mainStore.setCanvasScale(newViewportActualWidth / VIEWPORT_SIZE)
//mainStore.setCanvasScale(newViewportActualWidth / USED_VIEWPORT_SIZE)
canvasScale = newViewportActualWidth / USED_VIEWPORT_SIZE
}
else {
mainStore.setCanvasScale(newViewportActualHeight / (VIEWPORT_SIZE * viewportRatio.value))
//mainStore.setCanvasScale(newViewportActualHeight / (USED_VIEWPORT_SIZE * viewportRatio.value))
canvasScale = newViewportActualHeight / (USED_VIEWPORT_SIZE * viewportRatio.value)
}
viewportLeft.value = viewportLeft.value - (newViewportActualWidth - oldViewportActualWidth) / 2
viewportTop.value = viewportTop.value - (newViewportActualHeight - oldViewportActualHeight) / 2
mainStore.setCanvasScale(canvasScale)
// viewportLeft.value = viewportLeft.value - (newViewportActualWidth - oldViewportActualWidth) / 2
// viewportTop.value = viewportTop.value - (newViewportActualHeight - oldViewportActualHeight) / 2
viewportLeft.value = (canvasWidth - USED_VIEWPORT_SIZE * canvasScale) / 2
viewportTop.value = (canvasHeight - (USED_VIEWPORT_SIZE*viewportRatio.value*canvasScale)) / 2
}
// 可视区域缩放或比例变化时,重置/更新可视区域的位置
......@@ -63,8 +69,8 @@ export default (canvasRef: Ref<HTMLElement | undefined>) => {
// 画布可视区域位置和大小的样式
const viewportStyles = computed(() => ({
width: VIEWPORT_SIZE,
height: VIEWPORT_SIZE * viewportRatio.value,
width: viewportRatio.value<=1?VIEWPORT_SIZE:VIEWPORT_VER_SIZE,
height: (viewportRatio.value<=1?VIEWPORT_SIZE:VIEWPORT_VER_SIZE) * viewportRatio.value,
left: viewportLeft.value,
top: viewportTop.value,
}))
......
......@@ -22,7 +22,7 @@
width: viewportStyles.width * canvasScale + 'px',
height: viewportStyles.height * canvasScale + 'px',
left: viewportStyles.left + 'px',
top: viewportStyles.top-20 + 'px',
top: viewportStyles.top + 'px',
}"
>
<div class="operates">
......@@ -203,10 +203,10 @@ onMounted(() => {
// 点击画布的空白区域:清空焦点元素、设置画布焦点、清除文字选区、清空格式刷状态
const handleClickBlankArea = (e: MouseEvent) => {
console.log(e.button)
if (activeElementIdList.value.length) mainStore.setActiveElementIdList([])
if (!spaceKeyState.value) updateMouseSelection(e)
else dragViewport(e)
if (!spaceKeyState.value && e.button==0) updateMouseSelection(e)
else if(e.button==1) dragViewport(e)
if (!editorAreaFocus.value) mainStore.setEditorareaFocus(true)
if (textFormatPainter.value) mainStore.setTextFormatPainter(null)
......
......@@ -56,7 +56,7 @@
</Popover>
</div> -->
<div class="group-menu-item" v-if="userInfo.IsEditTripTemplate==1&&model">
<div class="menu-item" v-tooltip="'导入PSD'" @click="UploadPsd()">
<div class="menu-item" v-tooltip="'导入PSD'" @click="UploadPsdHandler">
<IconUpload class="icon" />
</div>
</div>
......@@ -80,6 +80,7 @@
</Drawer>
<FullscreenSpin :loading="exporting" tip="正在导入..." />
<Psd-Upload :visible="psdVisibleStatus" @closed="psdVisibleStatus=false"></Psd-Upload>
</div>
</template>
......@@ -95,6 +96,7 @@ import { userStore } from "@/store/user";
import { useScreenStore } from "@/store/screen";
import ConfigService from '@/services/ConfigService'
import { injectKeyDataSource, injectKeyTemplate } from '@/types/injectKey'
import PsdUpload from '@/components/PSD/Index.vue'
import { svg2Base64 } from '@/utils/svg2Base64'
import HotkeyDoc from './HotkeyDoc.vue'
......@@ -133,6 +135,7 @@ searchData.value = inject(injectKeyTemplate)
const marketStore = useScreenStore()
const CoverImgStore = useScreenStore()
const dataLoadingStore = useScreenStore()
const psdVisibleStatus = ref(false)
const { market, model, ConfigId, CoverImg, dataLoading } = storeToRefs(useScreenStore())
// 返回到首页
......@@ -178,8 +181,8 @@ const goBack = () =>{
}
// 导入PSD
const UploadPsd = () => {
const UploadPsdHandler = () => {
psdVisibleStatus.value = true
}
......
......@@ -205,7 +205,7 @@ const GetTripTemplate = async () =>{
let dataRes = await ConfigService.GetTripTemplateSlide(queryMsg);
if (dataRes.data.resultCode == 1) {
let viewportRatio = 1.414
if(dataRes.data.data.TempType==1) viewportRatio = 0.75
if(dataRes.data.data.TempType==1) viewportRatio = 0.7723
slidesStore.setViewportRatio(viewportRatio)
let SlidesData = JSON.parse(dataRes.data.data.TempData)
......
......@@ -120,7 +120,7 @@
:value="viewportRatio"
@update:value="value => updateViewportRatio(value as number)"
:options="[
{ label: '横屏', value: 0.75 },
{ label: '横屏', value: 0.7069 },
{ label: '竖屏', value: 1.414 },
]"
/>
......@@ -416,7 +416,7 @@ const updateTheme = (themeProps: Partial<SlideTheme>) => {
// 设置画布尺寸(宽高比例)
const updateViewportRatio = (value: number) => {
if(value==0.75){
if(value==0.7069){
queryObj.value.TempType = 1
}else{
queryObj.value.TempType = 2
......
......@@ -6,7 +6,7 @@
<div class="layout-content-center">
<CanvasTool class="center-top" />
<!-- :style="{ height: `calc(100% - ${remarkHeight + 40}px)` }" -->
<Canvas class="center-body" :style="{ height: `100%`}" />
<Canvas class="center-body" :style="{ height: `calc(100% - 40px)`}" />
<!-- <Remark
class="center-bottom"
v-model:height="remarkHeight"
......
......@@ -3,7 +3,7 @@
<div style="padding: 30px; max-width:1440px; margin:0 auto;">
<el-row justify="space-between">
<el-col :span="6">
<h1 class="alifont" style="font-size:20px;">智慧设计平台</h1>
<h1 class="aliMarketfont" style="font-size:20px;">智慧设计平台</h1>
</el-col>
<el-col :span="6">
<el-input v-model="queryObj.Title" placeholder="输入模板关键字快速查找" class="input-with-select">
......@@ -269,15 +269,15 @@
@import url('../../assets/styles/common.css');
@font-face {
font-family: "alifont";
font-family: "aliMarketfont";
font-weight: 400;
src: url("//at.alicdn.com/wf/webfont/MQHUV6e56ce5/285OveHVCHM7.woff2") format("woff2"),
url("//at.alicdn.com/wf/webfont/MQHUV6e56ce5/pz3etdXOpfWP.woff") format("woff");
font-display: swap;
}
.alifont {
font-family: alifont;
.aliMarketfont {
font-family: aliMarketfont;
}
.el-check-tag {
......
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