Commit 17377cec authored by zhengke's avatar zhengke

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

parents e7ccbe7b e83e74eb
......@@ -24,13 +24,16 @@ declare module 'vue' {
ElDialog: typeof import('element-plus/es')['ElDialog']
ElDivider: typeof import('element-plus/es')['ElDivider']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElImage: typeof import('element-plus/es')['ElImage']
ElInput: typeof import('element-plus/es')['ElInput']
ElOption: typeof import('element-plus/es')['ElOption']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElPopover: typeof import('element-plus/es')['ElPopover']
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']
ElTag: typeof import('element-plus/es')['ElTag']
ElUpload: typeof import('element-plus/es')['ElUpload']
FileInput: typeof import('./src/components/FileInput.vue')['default']
FormulaContent: typeof import('./src/components/LaTeXEditor/FormulaContent.vue')['default']
......
......@@ -11,6 +11,7 @@
"dependencies": {
"@element-plus/icons-vue": "^2.1.0",
"@icon-park/vue-next": "^1.4.2",
"@types/ali-oss": "^6.16.11",
"@types/psd": "^3.4.3",
"animate.css": "^4.1.1",
"axios": "^1.6.2",
......
......@@ -10,7 +10,7 @@
<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'
......@@ -23,6 +23,7 @@ import Screen from './views/Screen/index.vue'
import Mobile from './views/Mobile/index.vue'
import Market from './views/Market/Index.vue'
import NewFile from './views/Market/newFile.vue'
import { ElMessage, ElMessageBox } from 'element-plus'
const searchData = ref({} as any)
provide(injectKeyTemplate,searchData)
......@@ -59,7 +60,8 @@ const userLoginHandler = async ()=>{
if(!userInfo.value.EmployeeId){
try {
await userStore().setUserLoginAsync(userId,ConfigId)
if(ConfigId) await GetTripConfig(ConfigId)
await useFontStore().loadAllFonts()
if(ConfigId) await GetTripConfig(ConfigId)
} catch (error) {}
}else{
if(ConfigId) await GetTripConfig(ConfigId)
......
......@@ -12,7 +12,18 @@ page {
url("//at.alicdn.com/wf/webfont/MQHUV6e56ce5/cW4aPGiVFgIJ.woff") format("woff");
font-display: swap;
}
@font-face {
font-family: FZCSJW--GB1-0;
src: url("http://uploadfile.oytour.com/assets/font/FZCSJW.ttf") format("TrueType");
}
@font-face {
font-family: FZKTJW--GB1-0;
src: local('方正楷体简体');
}
@font-face {
font-family: DengXian;
src: local('等线');
}
.alifont{
font-family: alifont,chineseAlifont;
}
......
$fontList: '仓耳小丸子', '优设标题黑', '字制区喜脉体', '峰广明锐体', '得意黑', '摄图摩登小方体', '站酷快乐体', '素材集市康康体', '素材集市酷方体', '途牛类圆体', '锐字真言体';
@each $font in $fontList {
@font-face {
font-display: swap;
font-family: $font;
src: url('../fonts/#{$font}.woff2') format('woff2');
}
}
\ No newline at end of file
// $fontList: '优设标题黑', '字制区喜脉体', '峰广明锐体', '得意黑', '摄图摩登小方体', '站酷快乐体', '素材集市康康体', '素材集市酷方体', '途牛类圆体', '锐字真言体';
// // '仓耳小丸子'
// @each $font in $fontList {
// @font-face {
// font-display: swap;
// font-family: $font;
// src: url('../fonts/#{$font}.woff2') format('woff2');
// }
// }
\ 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;">
<el-dialog v-loading="sliderLoading" 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>
......@@ -33,28 +33,55 @@
</div>
<div v-else>
<div class="f12 text-grey">已成功解析PSD文件,请核对下方图片是否正确:</div>
<div class="text-small 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="text-small text-grey q-mt-lg">请完善以下字体信息补充:</div>
<div class="row items-center q-mt-md" v-for="(x,i) in fonts" :key="x">
<div class="text-small col">字体名称:{{ x }}</div>
<el-upload
v-loading="uploadingIndex==i"
:on-change="(uploadFile:any, uploadFiles:any)=> uploadFontHandler(uploadFile, uploadFiles,x)"
action=""
:limit="1"
v-if='uploadFinishFont?.indexOf(x)==-1'
:auto-upload="false"
accept=".ttf, .woff, .woff2"
:show-file-list="false"
>
<template #trigger>
<el-button link>选择字体</el-button>
</template>
</el-upload>
<el-tag v-else type="success" effect="light"> 已上传 </el-tag>
<el-button v-if='uploadFinishFont?.indexOf(x)==-1' type="danger" class="q-ml-md" @click="removeFontHandler(x)" :icon="Delete" link></el-button>
</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>
<el-button type="primary" class="q-ml-lg" @click="resolveToSliderHandler">确认导入</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 { UploadFilled,Delete } from '@element-plus/icons-vue'
import type { UploadProps, UploadUserFile,UploadInstance } from 'element-plus'
import PSD from 'psd.js'
import {toSlider} from '@/utils/psdParser/index'
import {getFonts, toSlider, toThumbnails} from '@/utils/psdParser/index'
import { useSlidesStore } from '@/store'
import { ElMessage, ElMessageBox } from 'element-plus'
import AliyunUpload from '@/utils/upload/aliyun'
import { useFontStore } from '@/store'
import { CustomerFonts } from '@/store/font'
const props = defineProps({
visible:{
......@@ -71,7 +98,13 @@ const emit = defineEmits<{
const visibleStatus = ref(props.visible)
const upload = ref<UploadInstance>()
const isResolving = ref(false)
const sliderLoading = ref(false)
const thumbnails = ref<Array<any>>([])
const sliders = ref<Array<any>>([])
const fonts = ref<any[]>()
const uploadingIndex = ref(-1)
const uploadFinishFont = ref<string[]>([])
const handleChange: UploadProps['onChange'] = (uploadFile, uploadFiles) => {
isResolving.value=true
......@@ -79,7 +112,13 @@ const handleChange: UploadProps['onChange'] = (uploadFile, uploadFiles) => {
var url = URL.createObjectURL(uploadFile?.raw)
try {
PSD.fromURL(url).then(async (psd:any) => {
thumbnails.value= await toSlider(psd)
thumbnails.value= await toThumbnails(psd)
sliders.value = toSlider(psd)
fonts.value = getFonts(psd)
if(fonts.value){
fonts.value = fonts.value.filter(x=>useFontStore().getFonts.findIndex(y=>y.fontFamily==x)==-1)
}
//currentPSD.value = psd
isResolving.value=false
})
} catch (error) {
......@@ -89,12 +128,65 @@ const handleChange: UploadProps['onChange'] = (uploadFile, uploadFiles) => {
isResolving.value=false
}
}
const uploadFontHandler = async (uploadFile:any, uploadFiles:any, fontName:string) => {
uploadingIndex.value = fonts.value?.indexOf(fontName)??-1
try {
const url = await AliyunUpload.UploadAsync(uploadFile?.raw,"tripfont/"+uploadFile.name)
if(url && url!=''){
//添加字体
uploadFinishFont.value.push(fontName)
let data:CustomerFonts = {
fontFamily:fontName,
label:uploadFile.name.split('.')[0],
fontUrl:url
}
const result = await useFontStore().uploadFontAsync(data)
if(result){
uploadFinishFont.value.push(fontName)
}else{
ElMessage({
message:'字体上传失败,请重试',
type:'error'
})
}
}
} catch (error) {
console.log("上传异常",error)
}
uploadingIndex.value=-1
}
const removeFontHandler = (fontName:any)=>{
ElMessageBox.confirm('删除后无法恢复,是否确定执行','提示',{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
console.log('then',!fonts.value)
if(fonts.value){
console.log(fonts.value,fontName)
fonts.value?.splice(fonts.value.indexOf(fontName),1)
}
})
.catch((error) => { console.log("catch",error)})
}
const handleExceed: UploadProps['onExceed'] = (files) => {
console.log('执行clear')
upload.value!.clearFiles()
const file = files[0] as UploadRawFile
upload.value!.handleStart(file)
}
const resolveToSliderHandler = async()=>{
if(uploadFinishFont.value?.length < (fonts.value?.length??0)){
ElMessage({
message:'请完善字体信息',
type:'error'
})
}else{
useSlidesStore().setSlides(sliders.value)
useSlidesStore().updateSlideIndex(0)
close()
}
}
const resetPsdHandler = ()=>{
thumbnails.value=[]
}
......@@ -108,10 +200,4 @@ watch(() => props.visible, () => {
visibleStatus.value = props.visible
})
</script>
<style>
.upload-drop-box{
}
</style>
\ No newline at end of file
</script>
\ No newline at end of file
import Api,{ HttpResponse, Result } from './../utils/request';
class FontService{
//获取字体文件信息
static async GetAllFontsAsync():Promise<HttpResponse>{
return Api.Post("triptemplate_GetFontList",{})
}
//新增一个字体
static async SetFontAsync(params : any):Promise<HttpResponse>{
params.Id=0
params.lable = params.label
return Api.Post("triptemplate_SetTripFile",params)
}
}
export default FontService;
\ No newline at end of file
import { ApiResult } from './../configs/axios';
import { defineStore } from 'pinia'
import FontService from '@/services/FontService'
import { ElLoading } from 'element-plus'
export interface CustomerFonts {
fontUrl: string
fontFamily: string
label: string
}
export interface FormatFonts {
label: string,
value: string
}
export interface FontState{
fonts:CustomerFonts[],
loaded:string[],
formatFonts:FormatFonts[]
}
export const useFontStore = defineStore('fonts',{
state: (): FontState => ({
fonts:[],
loaded:[],
formatFonts:[]
}),
getters:{
getFonts(state){
return state.fonts
}
},
actions:{
async loadAllFonts () {
try {
let response = await FontService.GetAllFontsAsync()
if(response.data.resultCode == ApiResult.SUCCESS && response.data.data){
this.fonts =response.data.data as CustomerFonts[]
this.fonts.forEach(x=>{
this.formatFonts.push({ label: x.label,value:x.fontFamily })
})
}
} catch (error) {
console.log('初始化字体信息失败')
}
},
async loadFontToDocument(items:string[]){
const loadingInstance = ElLoading.service({
lock:true,
text:'正在加载字体文件'
})
let fonts = Array.from(new Set(items))
console.log(fonts)
fonts = fonts.filter(x=>this.loaded.indexOf(x)==-1)
const loadFonts = this.fonts.filter(x=>fonts.indexOf(x.fontFamily)!=-1)
if(loadFonts && loadFonts.length>0){
for (let i = 0; i < loadFonts.length; i++) {
const item = loadFonts[i];
try {
const url = item.fontUrl.indexOf('local')==-1?`url('${item.fontUrl}')`:item.fontUrl
const fontFace = new FontFace(item.fontFamily,url);
fontFace.display = "swap"
await fontFace.load();
document.fonts.add(fontFace)
this.loaded.push(item.fontFamily)
} catch (error) {
console.log('加载字体报错',error)
}
}
}
loadingInstance.close()
//console.log(loadingInstance)
},
async uploadFontAsync(item:CustomerFonts) {
let data:any = JSON.parse(JSON.stringify(item))
try {
const response = await FontService.SetFontAsync(data)
if(response.data.resultCode == ApiResult.SUCCESS){
this.fonts.push(item)
return true
}
} catch (error) {
}
return false
}
}
})
\ No newline at end of file
......@@ -3,6 +3,7 @@ import { useSlidesStore } from './slides'
import { useSnapshotStore } from './snapshot'
import { useKeyboardStore } from './keyboard'
import { useScreenStore } from './screen'
import { useFontStore } from './font'
export {
useMainStore,
......@@ -10,4 +11,5 @@ export {
useSnapshotStore,
useKeyboardStore,
useScreenStore,
useFontStore
}
\ No newline at end of file
import { useFontStore } from './font';
import { defineStore } from 'pinia'
import tinycolor from 'tinycolor2'
import { omit } from 'lodash'
import type { Slide, SlideTheme, PPTElement, PPTAnimation } from '@/types/slides'
import type { Slide, SlideTheme, PPTElement, PPTAnimation, PPTTextElement } from '@/types/slides'
import { slides } from '@/mocks/slides'
import { theme } from '@/mocks/theme'
import { layouts } from '@/mocks/layout'
......@@ -103,7 +104,7 @@ export const useSlidesStore = defineStore('slides', {
backgroundColor,
} = state.theme
const subColor = tinycolor(fontColor).isDark() ? 'rgba(230, 230, 230, 0.5)' : 'rgba(180, 180, 180, 0.5)'
console.log('layouts,--------')
const layoutsString = JSON.stringify(state.layoutSlides)
.replaceAll('{{themeColor}}', themeColor)
.replaceAll('{{fontColor}}', fontColor)
......@@ -123,6 +124,9 @@ export const useSlidesStore = defineStore('slides', {
setTheme(themeProps: Partial<SlideTheme>) {
this.theme = { ...this.theme, ...themeProps }
if(themeProps.fontName){
useFontStore().loadFontToDocument([themeProps.fontName]);
}
},
setViewportRatio(viewportRatio: number) {
......@@ -131,6 +135,28 @@ export const useSlidesStore = defineStore('slides', {
setSlides(slides: Slide[]) {
this.slides = slides
this.setFonts(slides)
},
setFonts(slides: Slide[]){
let iFonts:string[] = []
var regex = /(?<=font-family:\s*).*?(?=;)/g
slides.forEach(x=>{
x.elements.forEach(y=>{
let temp = JSON.parse(JSON.stringify(y))
if(temp.defaultFontName) iFonts.push(temp.defaultFontName)
if(temp.content){
let tempFonts = temp.content.match(regex)
if(tempFonts){
tempFonts.forEach((item:string) => {
let f = item.replaceAll("'",'').replaceAll('"',"").trim().split(',')
iFonts.push(...f)
});
}
}
})
})
if(iFonts.length>0) useFontStore().loadFontToDocument(iFonts)
},
setLayouts(layoutSlides: Slide[]) {
......
......@@ -143,6 +143,8 @@ interface PPTBaseElement {
* paragraphSpace?: 段间距,默认 5px
*
* vertical?: 竖向文本
*
* FiledTypeStr?: 纯文本内容
*/
export interface PPTTextElement extends PPTBaseElement {
type: 'text'
......@@ -156,7 +158,8 @@ export interface PPTTextElement extends PPTBaseElement {
opacity?: number
shadow?: PPTElementShadow
paragraphSpace?: number
vertical?: boolean
vertical?: boolean,
FiledTypeStr?: string
}
......
......@@ -13,6 +13,7 @@ export const createDocument = (content: string) => {
const htmlString = `<div>${content}</div>`
const parser = new window.DOMParser()
const element = parser.parseFromString(htmlString, 'text/html').body.firstElementChild
console.log(element)
return DOMParser.fromSchema(schema).parse(element as Element)
}
......
export const ResolveFonts = (item: any): any[] => {
let f = item.layer.typeTool().export()
const { font } = f
const { names } = font
return names
}
\ No newline at end of file
import { ResolveLayer } from "./layerService"
import { ResolvePsdToSliderHandler, ResolveThumbHandler,ResolveSliderFonts } from "./resolve"
export const toSlider = async (psd:any)=>{
const thumbs = await ResolveLayer(psd);
export const toSlider = (psd:any)=>{
const sliders = ResolvePsdToSliderHandler(psd);
return sliders
}
export const getFonts = (psd:any) => {
return ResolveSliderFonts(psd)
}
export const toThumbnails = async (psd:any)=>{
const thumbs = await ResolveThumbHandler(psd);
return thumbs
}
\ No newline at end of file
import { PPTImageElement } from '@/types/slides';
import { compressionThumbnail } from './compressor';
export const ResolveLayer = (item: any, index: number,offsetLeft:number,offsetTop:number): PPTImageElement|null => {
//await compressionThumbnail(item.layer.image.toBase64(), "image/png", 0, 0.8)
try {
const src = item.layer.image.toBase64()
const left = item.coords.left-offsetLeft
const width = item.coords.right - item.coords.left
const top = item.coords.top-offsetTop
const height = item.coords.bottom - item.coords.top
const opacity = (parseFloat(item.layer.opacity) / 255.0).toFixed(2)
let element: PPTImageElement = {
id: "img_" + index,
type: 'image',
src,
fixedRatio: false,
filters: {
opacity:src==''?'0':opacity
},
left,
width,
top,
height,
rotate: 0,
lock: item.name.indexOf('editor')==-1 || (item.name.indexOf('editor')!=-1 && item.name.indexOf('before')!=-1)
}
return element
} catch (error) {
console.log("执行出错",item)
return null
}
}
\ 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 { ResolveFonts } from './font';
import type { PPTElement, Slide } from '@/types/slides'
import { compressionThumbnail, cropImage } from "./compressor"
import { ResolveLayer } from './layer'
import { ResolveText } from './text'
let Z_INDEX = 0
export const ResolveThumbHandler = async (psd:any)=>{
const { _children } = psd.tree()
let imgs:Array<any> = []
let imgBase64String = psd.image.toBase64()
for (let i = 0; i < _children.length; i++) {
Z_INDEX = 2000
const item = _children[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
}
export const ResolvePsdToSliderHandler = (psd:any) => {
const ID_PREV = "import-slide-"
let items:Array<any> = psd.tree().children().filter((x:any)=>x.layer.visible)
let sliders:Array<Slide> = []
if(items && items.length>0){
for (let i = 0; i < items.length; i++) {
const x = items[i];
let t =CreateDefaultSlider(ID_PREV+i)
const {_children} = x
if(_children && _children.length>0){
const {top,left} = x.layer.artboard().export().coords
t.elements = GetSlidersHandler(_children,left,top).reverse()
}
sliders.push(t)
}
}
return sliders.filter(x=>x.elements && x.elements.length>0)
}
export const ResolveSliderFonts = (psd:any) => {
let items:Array<any> = psd.tree().children().filter((x:any)=>x.layer.visible)
let fonts:Array<any> = []
if(items && items.length>0){
fonts = GetFontHandler(items)
}
return Array.from(new Set(fonts))
}
const CreateDefaultSlider = (id:string):Slide =>{
return {
id,
pageType:1,
elements:[],
background: {
type: 'solid',
color: '#ffffff',
},
}
}
const GetSlidersHandler = (child:any[],offsetLeft:number,offsetTop:number) : PPTElement[] =>{
let elements = [] as Array<PPTElement>
child.forEach(x=>{
Z_INDEX--
if(x.layer.typeTool){
elements.push(ResolveText(x, Z_INDEX, offsetLeft, offsetTop))
}
else if(x.width && x.width>0 && x.layer.image){
let ele = ResolveLayer(x, Z_INDEX, offsetLeft, offsetTop)
if(ele) elements.push(ele)
}
if(x._children && x._children.length>0){
let childers = GetSlidersHandler(x._children,offsetLeft,offsetTop) ?? []
elements = elements.concat(childers)
}
})
return elements
}
const GetFontHandler = (child:any[]) : any[] => {
let fonts = []
if(child && child.length>0){
for (let i = 0; i < child.length; i++) {
const x = child[i];
if(x.layer.typeTool){
let temp = ResolveFonts(x)
if(temp && temp.length>0) fonts.push(...temp)
}
if(x._children && x._children.length>0){
let childers = GetFontHandler(x._children) ?? []
fonts = fonts.concat(childers)
}
}
}
return fonts
}
import { PPTElementShadow, PPTTextElement } from '@/types/slides';
export const ResolveText = (item: any, index: number,offsetLeft:number,offsetTop:number): PPTTextElement => {
let domLeft = item.layer.left-offsetLeft
let domwidth = item.layer.width
let domTop = item.layer.top-offsetTop
let domheight = item.layer.height
const opacity = (parseFloat(item.layer.opacity) / 255.0)
let f = item.layer.typeTool().export()
const { left, top, width, height, value, font, transform } = f
const { colors, styles, alignment, sizes, names, weights } = font
let fontSize = 24.0
if (sizes && sizes[0]) {
if (transform.yy !== 1) {
fontSize = (Math.round((sizes[0] * transform.yy) * 100) * 0.01)
} else {
fontSize = sizes[0]
}
}
const isVertical = item.layer.adjustments.typeTool.obj.textData.Ornt.value != 'Hrzn'
if(isVertical) {
let checkWord = value[0]+value[value.length-1]
let words = checkWord.match(/[\u3002|\uff1f|\uff01|\uff0c|\u3001|\uff1b|\uff1a|\u201c|\u201d|\u2018|\u2019|\uff08|\uff09|\u300a|\u300b|\u3008|\u3009|\u3010|\u3011|\u300e|\u300f|\u300c|\u300d|\ufe43|\ufe44|\u3014|\u3015|\u2026|\u2014|\uff5e|\ufe4f|\uffe5]/g)
domheight+= words&&words.length>0?fontSize*words.length*1.3:fontSize*1.3
domTop-= words&&words.length>0?fontSize:0
domLeft += words&&words.length>0?fontSize*-0.25:(fontSize-35)/2
}
else {
domwidth +=fontSize+(value[value.length-1]=='】'?fontSize:0)
domLeft-=(value[value.length-1]=='】'?fontSize:0)
}
const StyleSheet = item.layer.adjustments.typeTool.obj.engineData.EngineDict.StyleRun.RunArray[0].StyleSheet || {}
const { StyleSheetData } = StyleSheet
const tracking = fontSize * (StyleSheetData.Tracking / 1000)
const lineHeight = StyleSheetData.Leading
let leading = (Math.round((lineHeight * transform.yy) * 100) * 0.01) / fontSize
let objectEFFFects = item.layer.objectEffects ? item.layer.objectEffects() : null
let color = `rgba(${colors[0][0]},${colors[0][1]},${colors[0][2]},${(parseFloat(colors[0][3]) / 255.0).toFixed(2)})`
let style = `text-align:${alignment[0]}; font-size:${fontSize-2}px; font-weight:${weights[0]};`
if (font.textDecoration || StyleSheetData.Strikethrough) {
style += `text-decoration:${font.textDecoration ? font.textDecoration[0] : ''} ${StyleSheetData.Strikethrough ? ' line-through' : ''}`
}
let content = `<p style="${style}">${value}</p>`
let textShadow: any = null
if (objectEFFFects && objectEFFFects.data?.DrSh.enab) {
textShadow = getShadows(objectEFFFects)
}
let background = ''
if (objectEFFFects && objectEFFFects.data?.GrFl.enab) {
background += `rgba(${colors[0][0]},${colors[0][1]},${colors[0][2]},${(parseFloat(colors[0][3]) / 255.0).toFixed(2)})`
background += " "+getGradient(objectEFFFects)
background += " no-repeat"
background += " fixed"
background += " center center"
// div.style.backgroundPositionX = objectEFFFects.data.GrFl.Ofst.Hrzn.value+"px"
// div.style.backgroundPositionY = objectEFFFects.data.GrFl.Ofst.Vrtc.value+"px"
color = 'transparent',
textShadow = null
}
let element: PPTTextElement = {
id: "text_" + index,
type: 'text',
left:domLeft,
width:domwidth,
top:domTop,
height:domheight,
rotate: 0,
opacity,
content,
defaultFontName: names.join(','),
defaultColor: color,
fill: background,
lineHeight: leading,
wordSpace: tracking,
vertical: isVertical,
FiledTypeStr: content
}
if (textShadow) element.shadow = textShadow
return element
}
const getGradient = (obj: any) => {
const { GrFl } = obj.data
const angle = GrFl.Angl.value - 90
let linear = `linear-gradient(${angle}deg `
let intr = GrFl.Grad.Intr
GrFl.Grad.Clrs.forEach((x: any, i: number) => {
const clrStr = JSON.stringify(x['Clr ']).split(',')
let r: string = '0'
let g: string = '0'
let b: string = '0'
clrStr.forEach(item => {
if (item.indexOf('Rd') !== -1) {
r = item.replace('"Rd ":', '')
} else if (item.indexOf('Bl') !== -1) {
b = item.replace('"Bl ":', '').replace('}', '')
} else if (item.indexOf('Grn') !== -1) {
g = item.replace('"Grn ":', '')
}
})
const color =
linear += `, rgba(${parseInt(r)},${parseInt(g)},${parseInt(b)},${GrFl.Opct.value / 100})`
//linear-gradient(90deg, #FFF 0%, 18% ,#e5b8a4 36%, 68%, #FFF)
let cent = "", posi = ""
if (i < GrFl.Grad.Clrs.length - 1) {
posi = Math.ceil(x.Lctn * 100 / intr) + "%"
cent = Math.ceil((x.Lctn + ((GrFl.Grad.Clrs[i + 1].Lctn - x.Lctn) * x.Mdpn / 100)) * 100 / intr) + "%"
linear += ` ${posi}, ${cent}`
}
})
linear += ")"
return linear;
}
const getShadows = (drsh: any) => {
const { DrSh } = drsh.data
const clrStr = JSON.stringify(DrSh['Clr ']).split(',')
let r: string = '0'
let g: string = '0'
let b: string = '0'
clrStr.forEach(item => {
if (item.indexOf('Rd') !== -1) {
r = item.replace('"Rd ":', '')
} else if (item.indexOf('Bl') !== -1) {
b = item.replace('"Bl ":', '').replace('}', '')
} else if (item.indexOf('Grn') !== -1) {
g = item.replace('"Grn ":', '')
}
})
const angle = DrSh.lagl.value
const distance = DrSh.Dstn.value
var angleInRadians = angle * Math.PI / 180;
const shadowColor = `rgba(${parseInt(r)},${parseInt(g)},${parseInt(b)},${DrSh.Opct.value / 100})`
const x = Math.round(distance * Math.cos(angleInRadians));
const y = Math.round(distance * Math.sin(angleInRadians));
let result: PPTElementShadow = {
v: x,
h: y,
blur: DrSh.blur.value,
color: shadowColor
}
return result;
}
\ No newline at end of file
class AliyunUpload {
static readonly REGION = 'oss-cn-chengdu'
static readonly ACCESS_KEY_ID = 'LTAIwE7l9dImZSa3'
static readonly ACCESS_KEY_SECRET = 'j47Ajn0d0WzUCIX8Biyj3P2r8QDltI'
static readonly BUCKET = 'vt-im-bucket'
static GetOSS = () => {
const OSS = require('ali-oss');
//return new OSS
return new OSS({
region:this.REGION,
accessKeyId:this.ACCESS_KEY_ID,
accessKeySecret:this.ACCESS_KEY_SECRET,
bucket:this.BUCKET
})
}
static UploadAsync = async (file:FormData,name:string)=>{
try {
const oss = this.GetOSS()
let result = await oss.put(name,file)
console.log(result)
return 'https://im.oytour.com/'+name//result.url
} catch (error) {
console.log('上传发生异常:',error)
return ''
}
}
}
export default AliyunUpload
\ No newline at end of file
......@@ -253,7 +253,7 @@ onMounted(() => {
// 点击画布的空白区域:清空焦点元素、设置画布焦点、清除文字选区、清空格式刷状态
const handleClickBlankArea = (e: MouseEvent) => {
console.log(e.button)
if (activeElementIdList.value.length) mainStore.setActiveElementIdList([])
if (!spaceKeyState.value && e.button==0) updateMouseSelection(e)
else if(e.button==1) dragViewport(e)
......@@ -406,6 +406,7 @@ provide(injectKeySlideScale, canvasScale)
.viewport-wrapper {
position: absolute;
box-shadow: 0 0 15px 0 rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.viewport {
position: absolute;
......@@ -413,4 +414,5 @@ provide(injectKeySlideScale, canvasScale)
left: 0;
transform-origin: 0 0;
}
</style>
\ No newline at end of file
......@@ -19,8 +19,7 @@
:value="richTextAttrs.fontname"
@update:value="value => emitRichTextCommand('fontname', value as string)"
:options="[
...availableFonts,
...WEB_FONTS
...formatFonts
]"
>
<template #icon>
......@@ -324,7 +323,7 @@
<script lang="ts" setup>
import { ref, watch } from 'vue'
import { storeToRefs } from 'pinia'
import { useMainStore, useSlidesStore } from '@/store'
import { useMainStore, useSlidesStore,useFontStore } from '@/store'
import type { PPTTextElement } from '@/types/slides'
import emitter, { EmitterEvents, type RichTextAction } from '@/utils/emitter'
import { WEB_FONTS } from '@/configs/font'
......@@ -427,6 +426,7 @@ const presetStyles = [
const mainStore = useMainStore()
const slidesStore = useSlidesStore()
const { formatFonts } = storeToRefs(useFontStore())
const { handleElement, handleElementId, richTextAttrs, availableFonts, textFormatPainter } = storeToRefs(mainStore)
const { addHistorySnapshot } = useHistorySnapshot()
......@@ -490,6 +490,9 @@ const updateFill = (value: string) => {
// 发射富文本设置命令
const emitRichTextCommand = (command: string, value?: string) => {
if(command=='fontname' && value){
useFontStore().loadFontToDocument([value])
}
emitter.emit(EmitterEvents.RICH_TEXT_COMMAND, { action: { command, value } })
}
......
......@@ -143,10 +143,11 @@
:value="theme.fontName"
@update:value="value => updateTheme({ fontName: value as string })"
:options="[
...availableFonts,
...WEB_FONTS
...formatFonts
]"
/>
<!-- ...availableFonts,
...WEB_FONTS -->
</div>
<div class="row">
<div style="width: 40%;">字体颜色:</div>
......@@ -301,7 +302,7 @@
<script lang="ts" setup>
import { computed, ref, reactive,inject } from 'vue'
import { storeToRefs } from 'pinia'
import { useMainStore, useSlidesStore } from '@/store'
import { useMainStore, useSlidesStore, useFontStore } from '@/store'
import type { SlideBackground, SlideTheme } from '@/types/slides'
import { PRESET_THEMES } from '@/configs/theme'
import { WEB_FONTS } from '@/configs/font'
......@@ -331,6 +332,7 @@ queryObj.value = inject(injectKeyDataSource).queryObj
const slidesStore = useSlidesStore()
const { availableFonts } = storeToRefs(useMainStore())
const { slides, currentSlide, viewportRatio, theme, slideIndex } = storeToRefs(slidesStore)
const { formatFonts } = storeToRefs(useFontStore())
const moreThemeConfigsVisible = ref(false)
......@@ -411,6 +413,7 @@ const applyBackgroundAllSlide = () => {
// 设置主题
const updateTheme = (themeProps: Partial<SlideTheme>) => {
console.log(themeProps)
slidesStore.setTheme(themeProps)
}
......
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