Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
P
pptist
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
viitto
pptist
Commits
f8159880
Commit
f8159880
authored
Nov 27, 2023
by
罗超
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' of
http://gitlab.oytour.com/viitto/pptist
# Conflicts: # src/App.vue
parents
9bde0bff
1a7dd09f
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
33 changed files
with
1485 additions
and
147 deletions
+1485
-147
App.vue
src/App.vue
+28
-17
common.css
src/assets/styles/common.css
+9
-0
axios.ts
src/configs/axios.ts
+1
-1
useExport.ts
src/hooks/useExport.ts
+10
-5
useImport.ts
src/hooks/useImport.ts
+1
-0
useSlideHandler.ts
src/hooks/useSlideHandler.ts
+2
-0
main.ts
src/main.ts
+3
-0
layout.ts
src/mocks/layout.ts
+10
-0
slides.ts
src/mocks/slides.ts
+4
-0
icon.ts
src/plugins/icon.ts
+4
-0
ConfigService.ts
src/services/ConfigService.ts
+64
-0
LineService.ts
src/services/LineService.ts
+10
-0
main.ts
src/store/main.ts
+1
-1
screen.ts
src/store/screen.ts
+44
-3
slides.ts
src/store/slides.ts
+9
-4
injectKey.ts
src/types/injectKey.ts
+4
-2
slides.ts
src/types/slides.ts
+1
-0
toolbar.ts
src/types/toolbar.ts
+1
-0
common.ts
src/utils/common.ts
+13
-0
index.vue
src/views/Editor/Canvas/index.vue
+2
-2
index.vue
src/views/Editor/CanvasTool/index.vue
+2
-2
index.vue
src/views/Editor/DataaSource/index.vue
+162
-0
index.vue
src/views/Editor/EditorHeader/index.vue
+182
-8
ExportImage.vue
src/views/Editor/ExportDialog/ExportImage.vue
+10
-7
index.vue
src/views/Editor/ExportDialog/index.vue
+10
-4
LayoutPool.vue
src/views/Editor/Thumbnails/LayoutPool.vue
+0
-1
index.vue
src/views/Editor/Thumbnails/index.vue
+183
-10
ElementTemplateData.vue
src/views/Editor/Toolbar/ElementTemplateData.vue
+280
-0
SlideDesignPanel.vue
src/views/Editor/Toolbar/SlideDesignPanel.vue
+28
-4
index.vue
src/views/Editor/Toolbar/index.vue
+22
-7
index.vue
src/views/Editor/index.vue
+75
-5
Index.vue
src/views/Market/Index.vue
+309
-63
ThumbnailElement.vue
src/views/components/ThumbnailSlide/ThumbnailElement.vue
+1
-1
No files found.
src/App.vue
View file @
f8159880
<
template
>
<div
v-if=
"isFinish"
style=
"height: 100%;"
>
<Screen
v-if=
"screening"
/>
<Market
v-else-if=
"Market"
></Market>
<!--
<NewFile
v-else-if=
"market"
></NewFile>
-->
<Market
v-else-if=
"market"
></Market>
<Editor
v-else-if=
"_isPC"
/>
<Mobile
v-else
/>
</div>
</
template
>
<
script
lang=
"ts"
setup
>
import
{
onMounted
,
ref
}
from
'vue'
import
{
onMounted
,
ref
,
provide
}
from
'vue'
import
{
storeToRefs
}
from
'pinia'
import
{
useScreenStore
,
useMainStore
,
useSnapshotStore
}
from
'@/store'
import
{
LOCALSTORAGE_KEY_DISCARDED_DB
}
from
'@/configs/storage'
import
{
deleteDiscardedDB
}
from
'@/utils/database'
import
{
isPC
,
query
}
from
'./utils/common'
import
{
userStore
}
from
'./store/user'
import
{
injectKeyTemplate
}
from
'@/types/injectKey'
import
Editor
from
'./views/Editor/index.vue'
import
Screen
from
'./views/Screen/index.vue'
import
Mobile
from
'./views/Mobile/index.vue'
import
Market
from
'./views/Market/Index.vue'
import
NewFile
from
'./views/Market/newFile.vue'
const
searchData
=
ref
({}
as
any
)
provide
(
injectKeyTemplate
,
searchData
)
const
isFinish
=
ref
(
false
)
const
_isPC
=
isPC
()
const
mainStore
=
useMainStore
()
const
snapshotStore
=
useSnapshotStore
()
const
modelStore
=
useScreenStore
()
const
ConfigIdStore
=
useScreenStore
()
const
{
databaseId
}
=
storeToRefs
(
mainStore
)
const
{
screening
,
market
,
model
,
ConfigId
}
=
storeToRefs
(
useScreenStore
())
if
(
process
.
env
.
NODE_ENV
===
'production'
)
{
window
.
onbeforeunload
=
()
=>
false
}
const
userLoginHandler
=
async
()
=>
{
let
param
=
query
()
let
userId
=
1
let
ConfigId
=
0
let
model
=
1
if
(
param
.
uid
)
userId
=
parseInt
(
param
.
uid
)
if
(
ConfigId
)
ConfigIdStore
.
setConfigId
(
ConfigId
)
if
(
model
)
modelStore
.
setModel
(
model
)
try
{
await
userStore
().
setUserLoginAsync
(
userId
)
}
catch
(
error
)
{
...
...
@@ -37,17 +59,6 @@ const userLoginHandler = async ()=>{
}
userLoginHandler
()
const
_isPC
=
isPC
()
const
mainStore
=
useMainStore
()
const
snapshotStore
=
useSnapshotStore
()
const
{
databaseId
}
=
storeToRefs
(
mainStore
)
const
{
screening
,
market
}
=
storeToRefs
(
useScreenStore
())
if
(
process
.
env
.
NODE_ENV
===
'production'
)
{
window
.
onbeforeunload
=
()
=>
false
}
onMounted
(
async
()
=>
{
await
deleteDiscardedDB
()
snapshotStore
.
initSnapshotDatabase
()
...
...
src/assets/styles/common.css
View file @
f8159880
...
...
@@ -263,6 +263,9 @@ page {
.q-pt-md
{
padding-top
:
12px
;
}
.q-pb-md
{
padding-bottom
:
10px
;
}
.q-ma-lg
{
margin
:
20px
}
...
...
@@ -278,6 +281,9 @@ page {
.q-mt-lg
{
margin-top
:
20px
}
.q-mb-md
{
margin-bottom
:
10px
}
.q-mb-lg
{
margin-bottom
:
20px
}
...
...
@@ -308,4 +314,7 @@ page {
}
.text-light
{
font-weight
:
300
;
}
.text-nowrap
{
white-space
:
nowrap
;
}
\ No newline at end of file
src/configs/axios.ts
View file @
f8159880
...
...
@@ -65,7 +65,7 @@ const getErrorCode2text = (response: AxiosResponse): string => {
* service.get<{data: string; code: number}>('/test').then(({data}) => { console.log(data.code) })
*/
const
service
=
Axios
.
create
({
baseURL
:
'http://192.168.10.
160
/api/common/post'
,
baseURL
:
'http://192.168.10.
214
/api/common/post'
,
timeout
:
20000
,
headers
:
{
'User-Type'
:
'bus'
,
...
...
src/hooks/useExport.ts
View file @
f8159880
...
...
@@ -5,7 +5,7 @@ import { saveAs } from 'file-saver'
import
pptxgen
from
'pptxgenjs'
import
tinycolor
from
'tinycolor2'
import
{
toPng
,
toJpeg
}
from
'html-to-image'
import
{
useSlidesStore
}
from
'@/store'
import
{
useSlidesStore
,
useScreenStore
}
from
'@/store'
import
type
{
PPTElementOutline
,
PPTElementShadow
,
PPTElementLink
,
Slide
}
from
'@/types/slides'
import
{
getElementRange
,
getLineElementPath
,
getTableSubThemeColor
}
from
'@/utils/element'
import
{
type
AST
,
toAST
}
from
'@/utils/htmlParser'
...
...
@@ -25,18 +25,18 @@ interface ExportImageConfig {
export
default
()
=>
{
const
slidesStore
=
useSlidesStore
()
const
coverImgStore
=
useScreenStore
()
const
isCoverImgStore
=
useScreenStore
()
const
{
slides
,
theme
,
viewportRatio
,
title
}
=
storeToRefs
(
slidesStore
)
const
{
isCoverImg
}
=
storeToRefs
(
useScreenStore
())
const
exporting
=
ref
(
false
)
// 导出图片
const
exportImage
=
(
domRef
:
HTMLElement
,
format
:
string
,
quality
:
number
,
ignoreWebfont
=
true
)
=>
{
exporting
.
value
=
true
const
toImage
=
format
===
'png'
?
toPng
:
toJpeg
const
foreignObjectSpans
=
domRef
.
querySelectorAll
(
'foreignObject [xmlns]'
)
foreignObjectSpans
.
forEach
(
spanRef
=>
spanRef
.
removeAttribute
(
'xmlns'
))
setTimeout
(()
=>
{
const
config
:
ExportImageConfig
=
{
quality
,
...
...
@@ -47,7 +47,12 @@ export default () => {
toImage
(
domRef
,
config
).
then
(
dataUrl
=>
{
exporting
.
value
=
false
saveAs
(
dataUrl
,
`
${
title
.
value
}
.
${
format
}
`
)
if
(
isCoverImg
.
value
)
{
coverImgStore
.
setCoverImg
(
dataUrl
)
isCoverImgStore
.
setIsCoverImg
(
false
)
}
else
{
saveAs
(
dataUrl
,
`
${
title
.
value
}
.
${
format
}
`
)
}
}).
catch
(()
=>
{
exporting
.
value
=
false
message
.
error
(
'导出图片失败'
)
...
...
src/hooks/useImport.ts
View file @
f8159880
...
...
@@ -120,6 +120,7 @@ export default () => {
id
:
nanoid
(
10
),
elements
:
[],
background
,
pageType
:
1
}
const
parseElements
=
(
elements
:
Element
[])
=>
{
...
...
src/hooks/useSlideHandler.ts
View file @
f8159880
...
...
@@ -35,6 +35,7 @@ export default () => {
type
:
'solid'
,
color
:
theme
.
value
.
backgroundColor
,
},
pageType
:
1
}
slidesStore
.
updateSlideIndex
(
0
)
mainStore
.
setActiveElementIdList
([])
...
...
@@ -84,6 +85,7 @@ export default () => {
type
:
'solid'
,
color
:
theme
.
value
.
backgroundColor
,
},
pageType
:
1
,
}
mainStore
.
setActiveElementIdList
([])
slidesStore
.
addSlide
(
emptySlide
)
...
...
src/main.ts
View file @
f8159880
...
...
@@ -3,6 +3,9 @@ import { createPinia } from 'pinia'
import
App
from
'./App.vue'
import
'./registerServiceWorker'
import
{
ElMessage
,
ElMessageBox
}
from
'element-plus'
import
'element-plus/dist/index.css'
import
'@icon-park/vue-next/styles/index.css'
import
'prosemirror-view/style/prosemirror.css'
import
'animate.css'
...
...
src/mocks/layout.ts
View file @
f8159880
...
...
@@ -5,6 +5,7 @@ import type { Slide } from '@/types/slides'
export
const
layouts
:
Slide
[]
=
[
{
id
:
'template'
,
pageType
:
1
,
elements
:
[
{
type
:
'shape'
,
...
...
@@ -80,6 +81,7 @@ export const layouts: Slide[] = [
},
{
id
:
'template'
,
pageType
:
2
,
elements
:
[
{
type
:
'text'
,
...
...
@@ -139,6 +141,7 @@ export const layouts: Slide[] = [
},
{
id
:
'template'
,
pageType
:
3
,
elements
:
[
{
type
:
'shape'
,
...
...
@@ -188,6 +191,7 @@ export const layouts: Slide[] = [
},
{
id
:
'MZVO1kkj'
,
pageType
:
4
,
elements
:
[
{
type
:
'shape'
,
...
...
@@ -273,6 +277,7 @@ export const layouts: Slide[] = [
},
{
id
:
'template'
,
pageType
:
4
,
elements
:
[
{
type
:
'shape'
,
...
...
@@ -457,6 +462,7 @@ export const layouts: Slide[] = [
},
{
id
:
'template'
,
pageType
:
4
,
elements
:
[
{
type
:
'shape'
,
...
...
@@ -510,6 +516,7 @@ export const layouts: Slide[] = [
},
{
id
:
'template'
,
pageType
:
1
,
elements
:
[
{
type
:
'text'
,
...
...
@@ -573,6 +580,7 @@ export const layouts: Slide[] = [
},
{
id
:
'template'
,
pageType
:
1
,
elements
:
[
{
type
:
'text'
,
...
...
@@ -690,6 +698,7 @@ export const layouts: Slide[] = [
},
{
id
:
'template'
,
pageType
:
1
,
elements
:
[
{
type
:
'text'
,
...
...
@@ -766,6 +775,7 @@ export const layouts: Slide[] = [
},
{
id
:
'template'
,
pageType
:
1
,
elements
:
[
{
type
:
'shape'
,
...
...
src/mocks/slides.ts
View file @
f8159880
import
type
{
Slide
}
from
'@/types/slides'
// pageType 1基础 2酒店 3景 4餐
export
const
slides
:
Slide
[]
=
[
{
id
:
'test-slide-1'
,
pageType
:
1
,
elements
:
[
{
type
:
'shape'
,
...
...
@@ -77,6 +79,7 @@ export const slides: Slide[] = [
},
{
id
:
'test-slide-2'
,
pageType
:
2
,
elements
:
[
{
type
:
'text'
,
...
...
@@ -136,6 +139,7 @@ export const slides: Slide[] = [
},
{
id
:
'test-slide-3'
,
pageType
:
3
,
elements
:
[
{
type
:
'shape'
,
...
...
src/plugins/icon.ts
View file @
f8159880
...
...
@@ -96,7 +96,9 @@ import {
ListView
,
Magic
,
HighLight
,
Upload
,
Download
,
Check
,
IndentLeft
,
IndentRight
,
VerticalSpacingBetweenItems
,
...
...
@@ -221,7 +223,9 @@ export const icons: Icons = {
IconListView
:
ListView
,
IconMagic
:
Magic
,
IconHighLight
:
HighLight
,
IconUpload
:
Upload
,
IconDownload
:
Download
,
IconCheck
:
Check
,
IconIndentLeft
:
IndentLeft
,
IconIndentRight
:
IndentRight
,
IconVerticalSpacingBetweenItems
:
VerticalSpacingBetweenItems
,
...
...
src/services/ConfigService.ts
0 → 100644
View file @
f8159880
import
Api
,{
HttpResponse
,
Result
}
from
'./../utils/request'
;
/**
* 配置相关方法
*/
class
ConfigService
{
/**
* 根据ConfigId获取行程配置相关数据
*/
static
async
triptemplateGetTripFiledData
(
params
:
any
):
Promise
<
HttpResponse
>
{
return
Api
.
Post
(
"triptemplate_GetTripFiledData"
,
params
)
}
/**
* 根据团期配置编号获取行程详情
*/
static
async
triptemplateGetTripConfig
(
params
:
any
):
Promise
<
HttpResponse
>
{
return
Api
.
Post
(
"triptemplate_GetTripConfig"
,
params
)
}
/**
* 新增修改模版数据
*/
static
async
SetSetTripConfig
(
params
:
any
):
Promise
<
HttpResponse
>
{
return
Api
.
Post
(
"triptemplate_SetTripConfig"
,
params
)
}
/**
* 新增修改模版
*/
static
async
SetTripTemplateSlide
(
params
:
any
):
Promise
<
HttpResponse
>
{
return
Api
.
Post
(
"triptemplate_SetTripTemplate"
,
params
)
}
/**
* 根据TempId获取模版数据
*/
static
async
GetTripTemplateSlide
(
params
:
any
):
Promise
<
HttpResponse
>
{
return
Api
.
Post
(
"triptemplate_GetTripTemplate"
,
params
)
}
/**
* 获取绑定数据源列表
*/
static
async
TemplateGetTripFiled
(
params
:
any
):
Promise
<
HttpResponse
>
{
return
Api
.
Post
(
"triptemplate_GetTripFiled"
,
params
)
}
/**
*
* @returns 获取模板查询条件
*/
static
async
GetTemplateQueryAsync
():
Promise
<
HttpResponse
>
{
return
Api
.
Post
(
"triptemplate_GetTemplateConfigData"
,{})
}
/**
* 获取模板市场分页列表
*/
static
async
GetTemplagePageAsync
(
params
:
any
):
Promise
<
HttpResponse
>
{
return
Api
.
Post
(
"triptemplate_GetTripTemplatePage"
,
params
)
}
}
export
default
ConfigService
;
\ No newline at end of file
src/services/LineService.ts
View file @
f8159880
...
...
@@ -2,7 +2,17 @@ import Api,{ HttpResponse, Result } from './../utils/request';
class
LineService
{
// 获取配置项数据(颜色、国家、季节)
static
async
GetTemplateConfigData
(
params
:
any
):
Promise
<
HttpResponse
>
{
return
Api
.
Post
(
"triptemplate_GetTemplateConfigData"
,
params
)
}
// 系列
static
async
GetSeriesListAsync
(
params
:
any
):
Promise
<
HttpResponse
>
{
return
Api
.
Post
(
"team_post_GetList"
,
params
)
}
// 线路
static
async
GetLineListAsync
():
Promise
<
HttpResponse
>
{
return
Api
.
Post
(
"line_post_GetAllList"
,{})
}
...
...
src/store/main.ts
View file @
f8159880
...
...
@@ -59,7 +59,7 @@ export const useMainStore = defineStore('main', {
creatingElement
:
null
,
// 正在插入的元素信息,需要通过绘制插入的元素(文字、形状、线条)
creatingCustomShape
:
false
,
// 正在绘制任意多边形
availableFonts
:
SYS_FONTS
,
// 当前环境可用字体
toolbarState
:
ToolbarStates
.
SLIDE_DESIGN
,
// 右侧工具栏状态
toolbarState
:
ToolbarStates
.
SLIDE_DESIGN
,
// 右侧工具栏状态
设计 模版数据EL_TEMPLATEDATA
clipingImageElementId
:
''
,
// 当前正在裁剪的图片ID
richTextAttrs
:
defaultRichTextAttrs
,
// 富文本状态
selectedTableCells
:
[],
// 选中的表格单元格
...
...
src/store/screen.ts
View file @
f8159880
import
{
fa
}
from
'element-plus/es/locale'
import
{
defineStore
}
from
'pinia'
export
interface
ScreenState
{
screening
:
boolean
,
market
:
boolean
market
:
boolean
,
model
:
number
,
isModel
:
boolean
,
ConfigId
:
number
,
TemplateType
:
[],
TemplateDataSource
:
[],
CoverImg
:
any
,
isCoverImg
:
boolean
,
dataLoading
:
boolean
,
}
export
const
useScreenStore
=
defineStore
(
'screen'
,
{
state
:
():
ScreenState
=>
({
screening
:
false
,
// 是否进入放映状态
market
:
true
market
:
true
,
model
:
0
,
// 是否有新增修改模版权限
isModel
:
false
,
// 该团是否存在模版
ConfigId
:
0
,
TemplateType
:
[],
// 数据源分类
TemplateDataSource
:
[],
// 所有数据源
CoverImg
:
null
,
// 封面图
isCoverImg
:
false
,
// 封面
dataLoading
:
false
,
// 记录保存是否成功
}),
actions
:
{
...
...
@@ -18,8 +35,32 @@ export const useScreenStore = defineStore('screen', {
this
.
market
=
false
}
},
setMarket
(
market
:
boolean
){
setMarket
(
market
:
boolean
)
{
this
.
market
=
market
},
setModel
(
model
:
number
)
{
this
.
model
=
model
},
setIsModel
(
isModel
:
boolean
)
{
this
.
isModel
=
isModel
},
setConfigId
(
ConfigId
:
number
)
{
this
.
ConfigId
=
ConfigId
},
setTemplateType
(
TemplateType
:
[])
{
this
.
TemplateType
=
TemplateType
},
setCoverImg
(
CoverImg
:
any
)
{
this
.
CoverImg
=
CoverImg
},
setIsCoverImg
(
isCoverImg
:
boolean
)
{
this
.
isCoverImg
=
isCoverImg
},
setTemplateDataSource
(
TemplateDataSource
:
[])
{
this
.
TemplateDataSource
=
TemplateDataSource
},
setDataLoading
(
dataLoading
:
boolean
)
{
this
.
dataLoading
=
dataLoading
}
},
})
\ No newline at end of file
src/store/slides.ts
View file @
f8159880
...
...
@@ -28,6 +28,7 @@ export interface SlidesState {
slides
:
Slide
[]
slideIndex
:
number
viewportRatio
:
number
layoutSlides
:
Slide
[]
}
export
const
useSlidesStore
=
defineStore
(
'slides'
,
{
...
...
@@ -36,7 +37,8 @@ export const useSlidesStore = defineStore('slides', {
theme
:
theme
,
// 主题样式
slides
:
slides
,
// 幻灯片页面数据
slideIndex
:
0
,
// 当前页面索引
viewportRatio
:
0.5625
,
// 可视区域比例,默认16:9
viewportRatio
:
0.75
,
// 可视区域比例,默认16:9 0.5625
layoutSlides
:
slides
,
// 所有模版数据
}),
getters
:
{
...
...
@@ -92,10 +94,9 @@ export const useSlidesStore = defineStore('slides', {
fontName
,
backgroundColor
,
}
=
state
.
theme
const
subColor
=
tinycolor
(
fontColor
).
isDark
()
?
'rgba(230, 230, 230, 0.5)'
:
'rgba(180, 180, 180, 0.5)'
const
layoutsString
=
JSON
.
stringify
(
layout
s
)
console
.
log
(
'layouts,--------'
)
const
layoutsString
=
JSON
.
stringify
(
state
.
layoutSlide
s
)
.
replaceAll
(
'{{themeColor}}'
,
themeColor
)
.
replaceAll
(
'{{fontColor}}'
,
fontColor
)
.
replaceAll
(
'{{fontName}}'
,
fontName
)
...
...
@@ -123,6 +124,10 @@ export const useSlidesStore = defineStore('slides', {
setSlides
(
slides
:
Slide
[])
{
this
.
slides
=
slides
},
setLayouts
(
layoutSlides
:
Slide
[])
{
this
.
layoutSlides
=
layoutSlides
},
addSlide
(
slide
:
Slide
|
Slide
[])
{
const
slides
=
Array
.
isArray
(
slide
)
?
slide
:
[
slide
]
...
...
src/types/injectKey.ts
View file @
f8159880
...
...
@@ -6,7 +6,9 @@ export type RadioGroupValue = {
value
:
Ref
<
string
>
updateValue
:
(
value
:
string
)
=>
void
}
export
type
SlideDataSource
=
Ref
<
object
>
export
const
injectKeySlideScale
:
InjectionKey
<
SlideScale
>
=
Symbol
()
export
const
injectKeySlideId
:
InjectionKey
<
SlideId
>
=
Symbol
()
export
const
injectKeyRadioGroupValue
:
InjectionKey
<
RadioGroupValue
>
=
Symbol
()
\ No newline at end of file
export
const
injectKeyRadioGroupValue
:
InjectionKey
<
RadioGroupValue
>
=
Symbol
()
export
const
injectKeyDataSource
:
InjectionKey
<
SlideDataSource
>
=
Symbol
()
export
const
injectKeyTemplate
:
InjectionKey
<
SlideDataSource
>
=
Symbol
()
\ No newline at end of file
src/types/slides.ts
View file @
f8159880
...
...
@@ -676,6 +676,7 @@ export interface Slide {
background
?:
SlideBackground
animations
?:
PPTAnimation
[]
turningMode
?:
TurningMode
pageType
:
number
}
/**
...
...
src/types/toolbar.ts
View file @
f8159880
export
const
enum
ToolbarStates
{
SYMBOL
=
'symbol'
,
EL_ANIMATION
=
'elAnimation'
,
EL_TEMPLATEDATA
=
'elTemplateData'
,
EL_STYLE
=
'elStyle'
,
EL_POSITION
=
'elPosition'
,
SLIDE_DESIGN
=
'slideDesign'
,
...
...
src/utils/common.ts
View file @
f8159880
...
...
@@ -26,4 +26,17 @@ export const query = (url?:string)=>{
json
[
item
[
0
]]
=
item
[
1
]
}
return
json
}
/**
* 提取字符串中的文字
*/
export
const
getHtmlPlainText
=
(
html_str
:
string
)
=>
{
let
re
=
new
RegExp
(
'<[^<>]+>'
,
'g'
)
if
(
html_str
)
{
let
text
=
html_str
.
replace
(
re
,
''
)
return
text
}
else
{
return
''
}
}
\ No newline at end of file
src/views/Editor/Canvas/index.vue
View file @
f8159880
...
...
@@ -22,7 +22,7 @@
width: viewportStyles.width * canvasScale + 'px',
height: viewportStyles.height * canvasScale + 'px',
left: viewportStyles.left + 'px',
top: viewportStyles.top + 'px',
top: viewportStyles.top
-20
+ 'px',
}"
>
<div
class=
"operates"
>
...
...
@@ -168,7 +168,7 @@ watch(handleElementId, () => {
const
elementList
=
ref
<
PPTElement
[]
>
([])
const
setLocalElementList
=
()
=>
{
elementList
.
value
=
currentSlide
.
value
?
JSON
.
parse
(
JSON
.
stringify
(
currentSlide
.
value
.
elements
))
:
[]
elementList
.
value
=
currentSlide
.
value
&&
currentSlide
.
value
.
elements
?
JSON
.
parse
(
JSON
.
stringify
(
currentSlide
.
value
.
elements
))
:
[]
}
watchEffect
(
setLocalElementList
)
...
...
src/views/Editor/CanvasTool/index.vue
View file @
f8159880
...
...
@@ -51,7 +51,7 @@
<IconInsertTable
class=
"handler-item"
v-tooltip=
"'插入表格'"
/>
</Popover>
<IconFormula
class=
"handler-item"
v-tooltip=
"'插入公式'"
@
click=
"latexEditorVisible = true"
/>
<Popover
trigger=
"click"
v-model:value=
"mediaInputVisible"
>
<
!-- <
Popover trigger="click" v-model:value="mediaInputVisible">
<template #content>
<MediaInput
@close="mediaInputVisible = false"
...
...
@@ -60,7 +60,7 @@
/>
</template>
<IconVideoTwo class="handler-item" v-tooltip="'插入音视频'" />
</Popover>
</Popover>
-->
</div>
<div
class=
"right-handler"
>
...
...
src/views/Editor/DataaSource/index.vue
0 → 100644
View file @
f8159880
<
template
>
<div
v-if=
"datas.DataSource.DataSourceOverlay"
>
<div
class=
"DataaSourceOverlay"
@
click=
"OffDataSource"
></div>
<div
class=
"DataaSource"
>
<div
class=
"DataaSourceList"
>
<el-table
:data=
"datas.DataSource.DataSourceList"
style=
"width: 100%"
border
>
<el-table-column
prop=
""
label=
"基础数据"
>
<template
#
default=
"scope"
>
<div
class=
"DataaSourceL"
>
<div
v-if=
"scope.row.type=='text'"
>
{{
scope
.
row
.
FiledTypeStr
}}
</div>
<div
v-if=
"scope.row.type=='image'"
>
<img
style=
"width: 20px; height: 20px"
:src=
"scope.row.FiledTypeStr"
/>
</div>
</div>
</
template
>
</el-table-column>
<el-table-column
prop=
""
label=
"绑定数据源"
width=
"170"
>
<
template
#
default=
"scope"
>
<div
class=
"DataaSourceR"
v-if=
"scope.row.TemplateList"
>
<el-select
v-model=
"scope.row.TemplateDataSource.Id"
class=
"m-2"
placeholder=
"请绑定数据源"
@
change=
"setTemplateDataSource(scope.row.TemplateDataSource.Id,scope.$index)"
>
<!-- :disabled="scope.row.type=='image'&&item.Name.indexOf('图')==-1" -->
<el-option
v-for=
"item in setType(scope.row)"
:key=
"item.Id"
:label=
"item.Name"
:value=
"item.Id"
/>
</el-select>
</div>
</
template
>
</el-table-column>
</el-table>
</div>
</div>
</div>
</template>
<
script
lang=
"ts"
setup
>
import
{
computed
,
nextTick
,
ref
,
watch
,
reactive
,
inject
}
from
'vue'
import
{
storeToRefs
}
from
'pinia'
import
{
useSlidesStore
}
from
'@/store'
import
{
injectKeyDataSource
}
from
'@/types/injectKey'
import
{
getHtmlPlainText
}
from
'@/utils/common'
const
datas
=
reactive
({
DataSource
:{},
})
datas
.
DataSource
=
inject
(
injectKeyDataSource
)
const
slidesStore
=
useSlidesStore
()
const
{
slides
,
slideIndex
}
=
storeToRefs
(
slidesStore
)
const
setType
=
(
x
)
=>
{
if
(
x
.
type
==
"image"
){
return
x
.
TemplateList
.
filter
(
item
=>
{
return
item
.
Name
.
indexOf
(
'图'
)
!=-
1
})
}
if
(
x
.
type
==
"text"
){
return
x
.
TemplateList
.
filter
(
item
=>
{
return
item
.
Name
.
indexOf
(
'图'
)
==-
1
})
}
}
const
setNewDatas
=
()
=>
{
const
slidesData
=
slides
.
value
// 更新Slides数据
const
savelides
=
[]
const
newSlides
=
[]
let
obj
=
slidesData
.
find
((
x
,
index
)
=>
{
return
slideIndex
.
value
==
index
})
if
(
obj
){
obj
.
elements
.
forEach
(
x
=>
{
let
dataObj
=
datas
.
DataSource
.
DataSourceList
.
find
(
y
=>
{
return
y
.
id
==
x
.
id
})
if
(
dataObj
){
x
=
dataObj
}
savelides
.
push
(
x
)
})
obj
.
elements
=
JSON
.
parse
(
JSON
.
stringify
(
savelides
))
slidesData
.
forEach
((
x
,
index
)
=>
{
if
(
slideIndex
.
value
==
index
){
x
.
pgaeType
=
datas
.
DataSource
.
pgaeType
x
.
elements
=
obj
.
elements
}
newSlides
.
push
(
x
)
})
slidesStore
.
setSlides
(
JSON
.
parse
(
JSON
.
stringify
(
newSlides
)))
}
}
// 数据源关键数据赋值
const
setTemplateDataSource
=
(
Id
,
index
)
=>
{
datas
.
DataSource
.
DataSourceList
.
forEach
((
x
,
indexs
)
=>
{
if
(
index
==
indexs
){
let
obj
=
x
.
TemplateList
.
find
(
y
=>
{
return
y
.
Id
==
x
.
TemplateDataSource
.
Id
})
x
.
TemplateDataSource
.
Content
=
obj
.
Content
x
.
TemplateDataSource
.
Name
=
obj
.
Name
if
(
x
.
type
==
"text"
){
x
.
FiledTypeStr
=
obj
.
Name
x
.
content
=
x
.
content
.
replace
(
getHtmlPlainText
(
x
.
content
),
obj
.
Name
)
}
}
})
setNewDatas
()
}
const
OffDataSource
=
()
=>
{
datas
.
DataSource
.
DataSourceOverlay
=
!
datas
.
DataSource
.
DataSourceOverlay
}
</
script
>
<
style
lang=
"scss"
scoped
>
.DataaSourceOverlay
{
position
:
fixed
;
left
:
160px
;
top
:
80px
;
right
:
260px
;
bottom
:
0
;
z-index
:
1
;
background
:
rgba
(
23
,
23
,
23
,
0
.5
);
cursor
:
pointer
;
}
.DataaSource
{
position
:
fixed
;
left
:
200px
;
top
:
120px
;
right
:
300px
;
bottom
:
30px
;
z-index
:
2
;
padding
:
40px
;
border-radius
:
10px
;
background
:
#fff
;
}
.DataaSourceList
{
height
:
100%
;
border
:
1px
solid
#ebeef5
;
padding
:
20px
;
border-radius
:
5px
;
overflow
:
auto
;
}
.DataaSourceL
{
font-weight
:
bold
;
color
:
black
;
font-size
:
14px
;
}
</
style
>
\ No newline at end of file
src/views/Editor/EditorHeader/index.vue
View file @
f8159880
<
template
>
<div
class=
"editor-header"
>
<div
class=
"left"
>
<div
class=
"menu-item"
v-tooltip=
"'去首页'"
@
click=
"goBack()"
>
首页
</div>
<Popover
trigger=
"click"
placement=
"bottom-start"
v-model:value=
"mainMenuVisible"
>
<template
#
content
>
<FileInput
accept=
".pptist"
@
change=
"files =>
{
...
...
@@ -28,21 +29,21 @@
<Input
class=
"title-input"
ref=
"titleInputRef"
v-model:value=
"
titleValu
e"
v-model:value=
"
queryObj.Titl
e"
@
blur=
"handleUpdateTitle()"
v-if=
"editingTitle"
></Input>
<div
class=
"title-text"
@
click=
"startEditTitle()"
:title=
"
t
itle"
:title=
"
queryObj.T
itle"
v-else
>
{{
t
itle }}
</div>
>
{{
queryObj.T
itle }}
</div>
</div>
</div>
<div
class=
"right"
>
<div
class=
"group-menu-item"
>
<
!-- <
div class="group-menu-item">
<div class="menu-item" v-tooltip="'幻灯片放映'" @click="enterScreening()">
<IconPpt class="icon" />
</div>
...
...
@@ -53,13 +54,21 @@
</template>
<div class="arrow-btn"><IconDown class="arrow" /></div>
</Popover>
</div> -->
<div
class=
"group-menu-item"
v-if=
"userInfo.IsEditTripTemplate==1&&model"
>
<div
class=
"menu-item"
v-tooltip=
"'导入PSD'"
@
click=
"UploadPsd()"
>
<IconUpload
class=
"icon"
/>
</div>
</div>
<div
class=
"menu-item"
v-tooltip=
"'导出'"
@
click=
"setDialogForExport('pptx')"
>
<IconDownload
class=
"icon"
/>
</div>
<a
class=
"github-link"
href=
"https://github.com/pipipi-pikachu/PPTist"
target=
"_blank"
>
<el-button
v-tooltip=
"'保存'"
type=
"danger"
size=
"small"
icon=
"Check"
circle
:loading=
"datas.loading"
@
click=
"setTemplate()"
style=
"color: #ffff;"
></el-button>
<!-- <a class="github-link" href="https://github.com/pipipi-pikachu/PPTist" target="_blank">
<div class="menu-item"><IconGithub class="icon" /></div>
</a>
</a>
-->
</div>
<Drawer
...
...
@@ -75,13 +84,18 @@
</template>
<
script
lang=
"ts"
setup
>
import
{
nextTick
,
ref
}
from
'vue'
import
{
nextTick
,
ref
,
reactive
,
inject
}
from
'vue'
import
{
storeToRefs
}
from
'pinia'
import
{
useMainStore
,
useSlidesStore
}
from
'@/store'
import
useScreening
from
'@/hooks/useScreening'
import
useImport
from
'@/hooks/useImport'
import
useSlideHandler
from
'@/hooks/useSlideHandler'
import
type
{
DialogForExportTypes
}
from
'@/types/export'
import
{
userStore
}
from
"@/store/user"
;
import
{
useScreenStore
}
from
"@/store/screen"
;
import
ConfigService
from
'@/services/ConfigService'
import
{
injectKeyDataSource
,
injectKeyTemplate
}
from
'@/types/injectKey'
import
{
svg2Base64
}
from
'@/utils/svg2Base64'
import
HotkeyDoc
from
'./HotkeyDoc.vue'
import
FileInput
from
'@/components/FileInput.vue'
...
...
@@ -93,7 +107,8 @@ import PopoverMenuItem from '@/components/PopoverMenuItem.vue'
const
mainStore
=
useMainStore
()
const
slidesStore
=
useSlidesStore
()
const
{
title
}
=
storeToRefs
(
slidesStore
)
const
layoutsStore
=
useSlidesStore
()
const
{
title
,
slides
}
=
storeToRefs
(
slidesStore
)
const
{
enterScreening
,
enterScreeningFromStart
}
=
useScreening
()
const
{
importSpecificFile
,
importPPTXFile
,
exporting
}
=
useImport
()
const
{
resetSlides
}
=
useSlideHandler
()
...
...
@@ -104,6 +119,165 @@ const editingTitle = ref(false)
const
titleInputRef
=
ref
<
InstanceType
<
typeof
Input
>>
()
const
titleValue
=
ref
(
''
)
const
{
userInfo
}
=
storeToRefs
(
userStore
())
const
datas
=
reactive
({
DataSource
:{},
loading
:
false
})
const
queryObj
=
ref
({}
as
any
)
const
searchData
=
ref
({}
as
any
)
datas
.
DataSource
=
inject
(
injectKeyDataSource
)
queryObj
.
value
=
inject
(
injectKeyDataSource
).
queryObj
searchData
.
value
=
inject
(
injectKeyTemplate
)
const
marketStore
=
useScreenStore
()
const
CoverImgStore
=
useScreenStore
()
const
dataLoadingStore
=
useScreenStore
()
const
{
market
,
model
,
ConfigId
,
CoverImg
,
dataLoading
}
=
storeToRefs
(
useScreenStore
())
// 返回到首页
const
goBack
=
()
=>
{
let
list
=
[
{
id
:
'test-slide-1'
,
pageType
:
1
,
elements
:
[],
background
:
{
type
:
'solid'
,
color
:
'#ffffff'
,
},
}
]
if
(
model
.
value
)
{
searchData
.
value
.
TempId
=
0
marketStore
.
setMarket
(
true
)
slidesStore
.
setSlides
(
list
)
layoutsStore
.
setLayouts
([])
CoverImgStore
.
setCoverImg
(
null
)
return
}
ElMessageBox
.
confirm
(
'退出此页面将清空当前数据,请谨慎操作?'
,
'提示'
,
{
confirmButtonText
:
'确定'
,
cancelButtonText
:
'取消'
,
type
:
'warning'
,
}
)
.
then
(()
=>
{
searchData
.
value
.
TempId
=
0
marketStore
.
setMarket
(
true
)
slidesStore
.
setSlides
(
list
)
layoutsStore
.
setLayouts
([])
CoverImgStore
.
setCoverImg
(
null
)
})
.
catch
(()
=>
{
})
}
// 导入PSD
const
UploadPsd
=
()
=>
{
}
// 新增修改模版
const
SetTripTemplateSlide
=
async
()
=>
{
// console.log(JSON.parse(queryObj.value.TempData),'--------')
try
{
console
.
log
(
queryObj
.
value
,
'新增修改模版---'
)
let
TemplateRes
=
await
ConfigService
.
SetTripTemplateSlide
(
queryObj
.
value
);
if
(
TemplateRes
.
data
.
resultCode
==
1
)
{
}
dataLoadingStore
.
setDataLoading
(
true
)
datas
.
loading
=
false
}
catch
(
error
)
{
datas
.
loading
=
false
console
.
log
(
"TemplateGetTripFiled"
,
error
);
}
}
// 用户新增修改数据
const
SetTripTemplateConfig
=
async
()
=>
{
try
{
let
queryMsg
=
{
ConfigId
:
ConfigId
.
value
,
TempId
:
queryObj
.
value
.
TempId
,
TempData
:
queryObj
.
value
.
TempData
}
console
.
log
(
queryMsg
,
'新增修改团数据---'
)
let
TemplateRes
=
await
ConfigService
.
SetSetTripConfig
(
queryMsg
);
if
(
TemplateRes
.
data
.
resultCode
==
1
)
{
}
dataLoadingStore
.
setDataLoading
(
true
)
datas
.
loading
=
false
}
catch
(
error
)
{
datas
.
loading
=
false
console
.
log
(
"TemplateGetTripFiled"
,
error
);
}
}
// 保存
const
setTemplate
=
async
()
=>
{
if
(
dataLoading
.
value
){
dataLoadingStore
.
setDataLoading
(
false
)
}
// console.log(JSON.stringify(slides.value),'----保存接口',queryObj.value)
if
(
model
.
value
&&
userInfo
.
value
.
IsEditTripTemplate
==
1
){
if
(
CoverImg
&&
CoverImg
.
value
)
queryObj
.
value
.
CoverImg
=
CoverImg
.
value
else
return
ElMessage
({
showClose
:
true
,
message
:
'请生成封面图'
,
type
:
'warning'
,
})
if
(
queryObj
.
value
.
Title
==
''
||!
queryObj
.
value
.
LineId
||!
queryObj
.
value
.
LtId
||
queryObj
.
value
.
CoverImg
==
''
||
queryObj
.
value
.
CountryName
==
''
||
queryObj
.
value
.
SeasonName
==
''
||
queryObj
.
value
.
ColorName
==
''
||
queryObj
.
value
.
ColorStr
==
''
||
queryObj
.
value
.
LineName
==
''
){
return
ElMessage
({
showClose
:
true
,
message
:
'请完善右侧模版数据'
,
type
:
'warning'
,
})
}
}
for
(
let
i
=
0
;
i
<
slides
.
value
.
length
;
i
++
){
if
(
slides
.
value
[
i
].
elements
.
length
==
0
)
{
return
ElMessage
({
showClose
:
true
,
message
:
`请设计 第
${
i
+
1
}
页 的数据`
,
type
:
'warning'
,
})
}
// for(let j=0;j
<
slides
.
value
[
i
].
elements
.
length
;
j
++
){
// if(model.value&&userInfo.value.IsEditTripTemplate==1){
// if(slides.value[i].elements[j].type=="text"||slides.value[i].elements[j].type=="image"){
// if(!slides.value[i].elements[j].TemplateDataSource
// ||!slides.value[i].elements[j].TemplateDataSource.Id) {
// return ElMessage({
// showClose: true,
// message: `请完善 第 ${i+1} 页 需要绑定的数据源`,
// type: 'warning',
// })
// }
// }
// }
// }
}
queryObj
.
value
.
TempData
=
JSON
.
stringify
(
slides
.
value
)
datas
.
loading
=
true
if
(
model
.
value
&&
userInfo
.
value
.
IsEditTripTemplate
==
1
){
await
SetTripTemplateSlide
()
}
else
if
(
ConfigId
.
value
){
await
SetTripTemplateConfig
()
}
}
const
startEditTitle
=
()
=>
{
titleValue
.
value
=
title
.
value
editingTitle
.
value
=
true
...
...
src/views/Editor/ExportDialog/ExportImage.vue
View file @
f8159880
...
...
@@ -13,7 +13,7 @@
</div>
<div
class=
"configs"
>
<div
class=
"row"
>
<div
class=
"title"
>
导出格式:
</div>
<div
class=
"title"
>
{{
isCoverImg
?
'生成格式:'
:
'导出格式:'
}}
</div>
<RadioGroup
class=
"config-item"
v-model:value=
"format"
...
...
@@ -23,7 +23,7 @@
</RadioGroup>
</div>
<div
class=
"row"
>
<div
class=
"title"
>
导出范围:
</div>
<div
class=
"title"
>
{{
isCoverImg
?
'生成范围:'
:
'导出范围:'
}}
</div>
<RadioGroup
class=
"config-item"
v-model:value=
"rangeType"
...
...
@@ -65,18 +65,19 @@
</div>
<div
class=
"btns"
>
<Button
class=
"btn export"
type=
"primary"
@
click=
"expImage()"
>
导出图片
</Button>
<Button
class=
"btn export"
type=
"primary"
@
click=
"expImage()"
>
{{
isCoverImg
?
'生成封面'
:
'导出图片'
}}
</Button>
<Button
class=
"btn close"
@
click=
"emit('close')"
>
关闭
</Button>
</div>
<FullscreenSpin
:loading=
"exporting"
tip=
"正在导出...
"
/>
<FullscreenSpin
:loading=
"exporting"
:tip=
"isCoverImg?'正在生成...':'正在导出...'
"
/>
</div>
</
template
>
<
script
lang=
"ts"
setup
>
import
{
computed
,
ref
}
from
'vue'
import
{
storeToRefs
}
from
'pinia'
import
{
useSlidesStore
}
from
'@/store'
import
{
useSlidesStore
,
useScreenStore
}
from
'@/store'
import
useExport
from
'@/hooks/useExport'
import
ThumbnailSlide
from
'@/views/components/ThumbnailSlide/index.vue'
...
...
@@ -92,13 +93,15 @@ const emit = defineEmits<{
}
>
()
const
{
slides
,
currentSlide
}
=
storeToRefs
(
useSlidesStore
())
const
{
isCoverImg
}
=
storeToRefs
(
useScreenStore
())
const
imageThumbnailsRef
=
ref
<
HTMLElement
>
()
const
rangeType
=
ref
<
'all'
|
'current'
|
'custom'
>
(
'
all
'
)
const
rangeType
=
ref
<
'all'
|
'current'
|
'custom'
>
(
'
current
'
)
const
range
=
ref
<
[
number
,
number
]
>
([
1
,
slides
.
value
.
length
])
const
format
=
ref
<
'jpeg'
|
'png'
>
(
'jpeg'
)
const
quality
=
ref
(
1
)
const
ignoreWebfont
=
ref
(
true
)
const
ignoreWebfont
=
ref
(
false
)
//开启在线字体
const
renderSlides
=
computed
(()
=>
{
if
(
rangeType
.
value
===
'all'
)
return
slides
.
value
...
...
src/views/Editor/ExportDialog/index.vue
View file @
f8159880
...
...
@@ -7,7 +7,7 @@
@
update:value=
"key => setDialogForExport(key as DialogForExportTypes)"
/>
<div
class=
"content"
>
<component
:is=
"currentDialogComponent"
@
close=
"setDialogForExport('')"
></component>
<component
:is=
"currentDialogComponent"
@
close=
"setDialogForExport('')
,setDialogIsCoverImg()
"
></component>
</div>
</div>
</
template
>
...
...
@@ -15,7 +15,7 @@
<
script
lang=
"ts"
setup
>
import
{
computed
}
from
'vue'
import
{
storeToRefs
}
from
'pinia'
import
{
useMainStore
}
from
'@/store'
import
{
useMainStore
,
useScreenStore
}
from
'@/store'
import
type
{
DialogForExportTypes
}
from
'@/types/export'
import
ExportImage
from
'./ExportImage.vue'
...
...
@@ -30,11 +30,17 @@ interface TabItem {
label
:
string
}
const
isCoverImgStore
=
useScreenStore
()
const
mainStore
=
useMainStore
()
const
{
dialogForExport
}
=
storeToRefs
(
mainStore
)
const
{
isCoverImg
}
=
storeToRefs
(
isCoverImgStore
)
const
setDialogForExport
=
mainStore
.
setDialogForExport
const
setDialogIsCoverImg
=
()
=>
{
isCoverImgStore
.
setIsCoverImg
(
false
)
}
const
tabs
:
TabItem
[]
=
[
{
key
:
'pptist'
,
label
:
'导出 pptist 文件'
},
{
key
:
'pptx'
,
label
:
'导出 PPTX'
},
...
...
@@ -51,8 +57,8 @@ const currentDialogComponent = computed<unknown>(() => {
'pptx'
:
ExportPPTX
,
'pptist'
:
ExportSpecificFile
,
}
if
(
dialogForExport
.
value
)
return
dialogMap
[
dialogForExport
.
value
]
||
null
return
null
if
(
dialogForExport
.
value
)
return
dialogMap
[
dialogForExport
.
value
]
||
isCoverImgStore
.
setIsCoverImg
(
false
)
return
isCoverImgStore
.
setIsCoverImg
(
false
)
})
</
script
>
...
...
src/views/Editor/Thumbnails/LayoutPool.vue
View file @
f8159880
...
...
@@ -23,7 +23,6 @@ const emit = defineEmits<{
}
>
()
const
{
layouts
}
=
storeToRefs
(
useSlidesStore
())
const
selectSlideTemplate
=
(
slide
:
Slide
)
=>
{
emit
(
'select'
,
slide
)
}
...
...
src/views/Editor/Thumbnails/index.vue
View file @
f8159880
...
...
@@ -11,7 +11,7 @@
<template
#
content
>
<LayoutPool
@
select=
"slide =>
{ createSlideByTemplate(slide); presetLayoutPopoverVisible = false }" />
</
template
>
<div
class=
"select-btn"
><IconDown
/></div>
<div
class=
"select-btn"
v-if=
"layoutSlides.length>0"
><IconDown
/></div>
</Popover>
</div>
...
...
@@ -47,30 +47,36 @@
</template>
<
script
lang=
"ts"
setup
>
import
{
computed
,
nextTick
,
ref
,
watch
}
from
'vue'
import
{
computed
,
nextTick
,
ref
,
reactive
,
watch
,
inject
}
from
'vue'
import
{
storeToRefs
}
from
'pinia'
import
{
useMainStore
,
useSlidesStore
,
useKeyboardStore
}
from
'@/store'
import
{
useMainStore
,
useSlidesStore
,
useKeyboardStore
,
useScreenStore
}
from
'@/store'
import
{
fillDigit
}
from
'@/utils/common'
import
{
isElementInViewport
}
from
'@/utils/element'
import
type
{
ContextmenuItem
}
from
'@/components/Contextmenu/types'
import
useSlideHandler
from
'@/hooks/useSlideHandler'
import
useScreening
from
'@/hooks/useScreening'
import
useLoadSlides
from
'@/hooks/useLoadSlides'
import
{
injectKeyDataSource
,
injectKeyTemplate
}
from
'@/types/injectKey'
import
ConfigService
from
'@/services/ConfigService'
import
{
getHtmlPlainText
}
from
'@/utils/common'
import
ThumbnailSlide
from
'@/views/components/ThumbnailSlide/index.vue'
import
LayoutPool
from
'./LayoutPool.vue'
import
Popover
from
'@/components/Popover.vue'
import
Draggable
from
'vuedraggable'
const
mainStore
=
useMainStore
()
const
slidesStore
=
useSlidesStore
()
const
layoutsStore
=
useSlidesStore
()
const
keyboardStore
=
useKeyboardStore
()
const
{
selectedSlidesIndex
:
_selectedSlidesIndex
,
thumbnailsFocus
}
=
storeToRefs
(
mainStore
)
const
{
slides
,
slideIndex
}
=
storeToRefs
(
slidesStore
)
const
{
slides
,
slideIndex
,
layoutSlides
}
=
storeToRefs
(
slidesStore
)
const
{
ctrlKeyState
,
shiftKeyState
}
=
storeToRefs
(
keyboardStore
)
const
{
slidesLoadLimit
}
=
useLoadSlides
()
const
TemplateTypeStore
=
useScreenStore
()
const
CoverImgStore
=
useScreenStore
()
const
dataLoadingStore
=
useScreenStore
()
const
{
ConfigId
,
TemplateDataSource
,
TemplateType
,
dataLoading
}
=
storeToRefs
(
TemplateTypeStore
)
const
selectedSlidesIndex
=
computed
(()
=>
[...
_selectedSlidesIndex
.
value
,
slideIndex
.
value
])
const
presetLayoutPopoverVisible
=
ref
(
false
)
...
...
@@ -87,10 +93,15 @@ const {
sortSlides
,
}
=
useSlideHandler
()
// 页面被切换时
const
thumbnailsRef
=
ref
<
InstanceType
<
typeof
Draggable
>>
()
watch
(()
=>
slideIndex
.
value
,
()
=>
{
const
datas
=
reactive
({
DataSource
:{}
})
const
queryObj
=
ref
({}
as
any
)
const
searchData
=
ref
({}
as
any
)
datas
.
DataSource
=
inject
(
injectKeyDataSource
)
searchData
.
value
=
inject
(
injectKeyTemplate
)
watch
(()
=>
slideIndex
.
value
,
()
=>
{
// 清除多选状态的幻灯片
if
(
selectedSlidesIndex
.
value
.
length
)
{
mainStore
.
updateSelectedSlidesIndex
([])
...
...
@@ -107,10 +118,169 @@ watch(() => slideIndex.value, () => {
})
})
// 监听请求保存成功 重新请求数据
watch
(()
=>
dataLoading
.
value
,
(
n
,
o
)
=>
{
if
(
n
!=
o
&&
n
){
GetTripTemplate
()
}
})
queryObj
.
value
=
inject
(
injectKeyDataSource
).
queryObj
// 获取行程团数据
const
GetTripFiledData
=
async
(
status
)
=>
{
if
(
queryObj
.
value
.
TempId
&&!
status
)
return
const
slidesData
=
slides
.
value
try
{
let
queryMsg
=
{
ConfigId
:
ConfigId
.
value
}
let
dataRes
=
await
ConfigService
.
triptemplateGetTripFiledData
(
queryMsg
);
if
(
dataRes
.
data
.
resultCode
==
1
)
{
if
(
!
dataRes
.
data
.
data
)
return
const
travel
=
dataRes
.
data
.
data
const
cursors
=
[]
as
Array
<
any
>
slidesData
.
forEach
((
x
,
index
)
=>
{
x
.
elements
.
forEach
(
y
=>
{
if
(
y
.
TemplateDataSource
&&
y
.
TemplateDataSource
.
Content
){
let
dataPath
=
y
.
TemplateDataSource
.
Content
.
split
(
'.'
)
let
value
=
JSON
.
parse
(
JSON
.
stringify
(
travel
));
dataPath
.
forEach
((
oo
,
i
)
=>
{
if
(
value
&&
value
[
oo
]){
if
(
i
==
0
&&
Array
.
isArray
(
value
[
oo
])){
let
temp
=
cursors
.
find
(
item
=>
item
.
key
==
oo
)
if
(
temp
){
temp
.
index
++
}
else
{
temp
=
{
key
:
oo
,
index
:
0
}
cursors
.
push
(
temp
)
}
if
(
value
[
oo
].
length
>
temp
.
index
)
value
=
value
[
oo
][
temp
.
index
]
else
value
=
value
[
oo
]
}
else
{
value
=
value
[
oo
]
}
}
else
{
value
=
null
}
})
if
(
value
&&
typeof
(
value
)
==
'string'
){
//替换
y
.
content
=
y
.
content
.
replace
(
getHtmlPlainText
(
y
.
content
),
value
)
}
else
if
(
value
&&
Array
.
isArray
(
value
)){
//替换
y
.
content
=
value
[
0
]
}
}
})
})
slidesStore
.
setSlides
(
slidesData
)
}
}
catch
(
error
)
{
console
.
log
(
"triptemplateGetTripFiledData"
,
error
);
}
}
// 获取行程模版数据
const
GetTripTemplate
=
async
()
=>
{
if
(
!
queryObj
.
value
.
TempId
)
{
let
list
=
[
{
id
:
'test-slide-1'
,
pageType
:
1
,
elements
:
[],
background
:
{
type
:
'solid'
,
color
:
'#ffffff'
,
},
}
]
return
slidesStore
.
setSlides
(
list
)
}
try
{
let
queryMsg
=
{
TempId
:
queryObj
.
value
.
TempId
}
let
dataRes
=
await
ConfigService
.
GetTripTemplateSlide
(
queryMsg
);
if
(
dataRes
.
data
.
resultCode
==
1
)
{
let
viewportRatio
=
1.414
if
(
dataRes
.
data
.
data
.
TempType
==
1
)
viewportRatio
=
0.75
slidesStore
.
setViewportRatio
(
viewportRatio
)
let
SlidesData
=
JSON
.
parse
(
dataRes
.
data
.
data
.
TempData
)
let
newSlides
=
[]
if
(
typeof
SlidesData
==
'object'
&&!
SlidesData
.
length
){
let
obj
=
{
pageType
:
1
,
...
SlidesData
}
newSlides
.
push
(
obj
)
}
else
if
(
SlidesData
.
length
>
0
){
newSlides
=
SlidesData
}
slidesStore
.
setSlides
(
newSlides
)
layoutsStore
.
setLayouts
(
JSON
.
parse
(
JSON
.
stringify
(
newSlides
)))
CoverImgStore
.
setCoverImg
(
dataRes
.
data
.
data
.
CoverImg
)
queryObj
.
value
.
TempId
=
dataRes
.
data
.
data
.
TempId
queryObj
.
value
.
LineId
=
dataRes
.
data
.
data
.
LineId
queryObj
.
value
.
LineName
=
dataRes
.
data
.
data
.
LineName
queryObj
.
value
.
LtId
=
dataRes
.
data
.
data
.
LtId
queryObj
.
value
.
Title
=
dataRes
.
data
.
data
.
Title
queryObj
.
value
.
TempData
=
dataRes
.
data
.
data
.
TempData
queryObj
.
value
.
CoverImg
=
dataRes
.
data
.
data
.
CoverImg
queryObj
.
value
.
CountryName
=
dataRes
.
data
.
data
.
CountryName
queryObj
.
value
.
SeasonName
=
dataRes
.
data
.
data
.
SeasonName
queryObj
.
value
.
ColorName
=
dataRes
.
data
.
data
.
ColorName
queryObj
.
value
.
ColorStr
=
dataRes
.
data
.
data
.
ColorStr
queryObj
.
value
.
TempType
=
dataRes
.
data
.
data
.
TempType
if
(
ConfigId
.
value
==
0
)
return
await
GetTripFiledData
(
1
)
}
}
catch
(
error
)
{
console
.
log
(
"GetTripTemplateSlide"
,
error
);
}
}
// 页面被切换时
const
thumbnailsRef
=
ref
<
InstanceType
<
typeof
Draggable
>>
()
// 切换页面
const
changeSlideIndex
=
(
index
:
number
)
=>
{
mainStore
.
setActiveElementIdList
([])
// 绑定数据源
const
newElements
=
slides
.
value
.
find
((
slide
,
indexs
)
=>
{
return
index
==
indexs
})
datas
.
DataSource
.
pageType
=
newElements
.
pageType
let
TemplateList
=
TemplateDataSource
.
value
.
filter
(
x
=>
{
return
x
.
FiledType
==
newElements
.
pageType
})
datas
.
DataSource
.
DataSourceList
=
[]
newElements
&&
newElements
.
elements
&&
newElements
.
elements
.
forEach
(
slide
=>
{
if
(
slide
.
type
==
"text"
||
slide
.
type
==
"image"
)
{
let
FiledTypeStr
if
(
slide
.
type
==
"text"
)
FiledTypeStr
=
getHtmlPlainText
(
slide
.
content
)
if
(
slide
.
type
==
"image"
)
FiledTypeStr
=
slide
.
src
let
Obj
=
{}
Obj
=
{
...
slide
,
FiledTypeStr
:
FiledTypeStr
,
TemplateList
:
TemplateList
,
TemplateDataSource
:
{
Content
:
slide
.
TemplateDataSource
&&
slide
.
TemplateDataSource
.
Content
?
slide
.
TemplateDataSource
.
Content
:
''
,
Name
:
slide
.
TemplateDataSource
&&
slide
.
TemplateDataSource
.
Name
?
slide
.
TemplateDataSource
.
Name
:
''
,
Id
:
slide
.
TemplateDataSource
&&
slide
.
TemplateDataSource
.
Id
?
slide
.
TemplateDataSource
.
Id
:
null
as
Number
}
}
if
(
Obj
){
datas
.
DataSource
.
DataSourceList
.
push
(
Obj
)
}
}
})
if
(
slideIndex
.
value
===
index
)
return
slidesStore
.
updateSlideIndex
(
index
)
}
...
...
@@ -125,6 +295,7 @@ const handleClickSlideThumbnail = (e: MouseEvent, index: number) => {
// 如果被取消选中的页面刚好是当前激活页面,则需要从其他被选中的页面中选择第一个作为当前激活页面
if
(
ctrlKeyState
.
value
)
{
if
(
slideIndex
.
value
===
index
)
{
if
(
!
isMultiSelected
)
return
const
newSelectedSlidesIndex
=
selectedSlidesIndex
.
value
.
filter
(
item
=>
item
!==
index
)
...
...
@@ -253,6 +424,8 @@ const contextmenusThumbnailItem = (): ContextmenuItem[] => {
},
]
}
GetTripTemplate
()
</
script
>
<
style
lang=
"scss"
scoped
>
...
...
src/views/Editor/Toolbar/ElementTemplateData.vue
0 → 100644
View file @
f8159880
This diff is collapsed.
Click to expand it.
src/views/Editor/Toolbar/SlideDesignPanel.vue
View file @
f8159880
...
...
@@ -99,12 +99,12 @@
<div
class=
"row"
>
<Button
style=
"flex: 1;"
@
click=
"applyBackgroundAllSlide()"
>
应用背景到全部
</Button>
</div>
<Divider
/>
<div
class=
"row"
>
<div
style=
"width: 40%;"
>
画布尺寸:
</div>
<Select
<
!-- <
Select
style="width: 60%;"
:value="viewportRatio"
@update:value="value => updateViewportRatio(value as number)"
...
...
@@ -114,6 +114,15 @@
{ 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.75 },
{ label: '竖屏', value: 1.414 },
]"
/>
</div>
...
...
@@ -290,7 +299,7 @@
</template>
<
script
lang=
"ts"
setup
>
import
{
computed
,
ref
}
from
'vue'
import
{
computed
,
ref
,
reactive
,
inject
}
from
'vue'
import
{
storeToRefs
}
from
'pinia'
import
{
useMainStore
,
useSlidesStore
}
from
'@/store'
import
type
{
SlideBackground
,
SlideTheme
}
from
'@/types/slides'
...
...
@@ -299,6 +308,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
ColorButton
from
'./common/ColorButton.vue'
import
FileInput
from
'@/components/FileInput.vue'
...
...
@@ -310,9 +320,17 @@ import Select from '@/components/Select.vue'
import
Popover
from
'@/components/Popover.vue'
import
NumberInput
from
'@/components/NumberInput.vue'
const
datas
=
reactive
({
DataSource
:{
DataSourceOverlay
:
false
}
})
const
queryObj
=
ref
({}
as
any
)
datas
.
DataSource
=
inject
(
injectKeyDataSource
)
queryObj
.
value
=
inject
(
injectKeyDataSource
).
queryObj
const
slidesStore
=
useSlidesStore
()
const
{
availableFonts
}
=
storeToRefs
(
useMainStore
())
const
{
slides
,
currentSlide
,
viewportRatio
,
theme
}
=
storeToRefs
(
slidesStore
)
const
{
slides
,
currentSlide
,
viewportRatio
,
theme
,
slideIndex
}
=
storeToRefs
(
slidesStore
)
const
moreThemeConfigsVisible
=
ref
(
false
)
...
...
@@ -390,6 +408,7 @@ const applyBackgroundAllSlide = () => {
addHistorySnapshot
()
}
// 设置主题
const
updateTheme
=
(
themeProps
:
Partial
<
SlideTheme
>
)
=>
{
slidesStore
.
setTheme
(
themeProps
)
...
...
@@ -397,6 +416,11 @@ const updateTheme = (themeProps: Partial<SlideTheme>) => {
// 设置画布尺寸(宽高比例)
const
updateViewportRatio
=
(
value
:
number
)
=>
{
if
(
value
==
0.75
){
queryObj
.
value
.
TempType
=
1
}
else
{
queryObj
.
value
.
TempType
=
2
}
slidesStore
.
setViewportRatio
(
value
)
}
</
script
>
...
...
src/views/Editor/Toolbar/index.vue
View file @
f8159880
...
...
@@ -13,14 +13,16 @@
</
template
>
<
script
lang=
"ts"
setup
>
import
{
computed
,
watch
}
from
'vue'
import
{
ref
,
computed
,
watch
}
from
'vue'
import
{
storeToRefs
}
from
'pinia'
import
{
useMainStore
}
from
'@/store'
import
{
useScreenStore
}
from
'@/store'
import
{
ToolbarStates
}
from
'@/types/toolbar'
import
ElementStylePanel
from
'./ElementStylePanel/index.vue'
import
ElementPositionPanel
from
'./ElementPositionPanel.vue'
import
ElementAnimationPanel
from
'./ElementAnimationPanel.vue'
import
ElementTemplateData
from
'./ElementTemplateData.vue'
import
SlideDesignPanel
from
'./SlideDesignPanel.vue'
import
SlideAnimationPanel
from
'./SlideAnimationPanel.vue'
import
MultiPositionPanel
from
'./MultiPositionPanel.vue'
...
...
@@ -34,6 +36,7 @@ interface ElementTabs {
const
mainStore
=
useMainStore
()
const
{
activeElementIdList
,
handleElement
,
toolbarState
}
=
storeToRefs
(
mainStore
)
const
{
model
}
=
storeToRefs
(
useScreenStore
())
const
elementTabs
=
computed
<
ElementTabs
[]
>
(()
=>
{
if
(
handleElement
.
value
?.
type
===
'text'
)
{
...
...
@@ -50,11 +53,22 @@ const elementTabs = computed<ElementTabs[]>(() => {
{
label
:
'动画'
,
key
:
ToolbarStates
.
EL_ANIMATION
},
]
})
const
slideTabs
=
[
{
label
:
'设计'
,
key
:
ToolbarStates
.
SLIDE_DESIGN
},
{
label
:
'切换'
,
key
:
ToolbarStates
.
SLIDE_ANIMATION
},
{
label
:
'动画'
,
key
:
ToolbarStates
.
EL_ANIMATION
},
]
const
slideTabs
=
ref
([]
as
any
)
if
(
model
.
value
){
slideTabs
.
value
=
[
{
label
:
'设计'
,
key
:
ToolbarStates
.
SLIDE_DESIGN
},
{
label
:
'切换'
,
key
:
ToolbarStates
.
SLIDE_ANIMATION
},
{
label
:
'动画'
,
key
:
ToolbarStates
.
EL_ANIMATION
},
{
label
:
'模版数据'
,
key
:
ToolbarStates
.
EL_TEMPLATEDATA
},
]
}
else
{
slideTabs
.
value
=
[
{
label
:
'设计'
,
key
:
ToolbarStates
.
SLIDE_DESIGN
},
{
label
:
'切换'
,
key
:
ToolbarStates
.
SLIDE_ANIMATION
},
{
label
:
'动画'
,
key
:
ToolbarStates
.
EL_ANIMATION
},
]
}
const
multiSelectTabs
=
[
{
label
:
'样式'
,
key
:
ToolbarStates
.
EL_STYLE
},
{
label
:
'位置'
,
key
:
ToolbarStates
.
MULTI_POSITION
},
...
...
@@ -65,7 +79,7 @@ const setToolbarState = (value: ToolbarStates) => {
}
const
currentTabs
=
computed
(()
=>
{
if
(
!
activeElementIdList
.
value
.
length
)
return
slideTabs
if
(
!
activeElementIdList
.
value
.
length
)
return
slideTabs
.
value
else
if
(
activeElementIdList
.
value
.
length
>
1
)
return
multiSelectTabs
return
elementTabs
.
value
})
...
...
@@ -82,6 +96,7 @@ const currentPanelComponent = computed(() => {
[
ToolbarStates
.
EL_STYLE
]:
ElementStylePanel
,
[
ToolbarStates
.
EL_POSITION
]:
ElementPositionPanel
,
[
ToolbarStates
.
EL_ANIMATION
]:
ElementAnimationPanel
,
[
ToolbarStates
.
EL_TEMPLATEDATA
]:
ElementTemplateData
,
[
ToolbarStates
.
SLIDE_DESIGN
]:
SlideDesignPanel
,
[
ToolbarStates
.
SLIDE_ANIMATION
]:
SlideAnimationPanel
,
[
ToolbarStates
.
MULTI_POSITION
]:
MultiPositionPanel
,
...
...
src/views/Editor/index.vue
View file @
f8159880
...
...
@@ -5,12 +5,14 @@
<Thumbnails
class=
"layout-content-left"
/>
<div
class=
"layout-content-center"
>
<CanvasTool
class=
"center-top"
/>
<Canvas
class=
"center-body"
:style=
"
{ height: `calc(100% - ${remarkHeight + 40}px)` }" />
<Remark
<!-- :style="
{ height: `calc(100% - ${remarkHeight + 40}px)` }" -->
<Canvas
class=
"center-body"
:style=
"
{ height: `100%`}" />
<!--
<Remark
class=
"center-bottom"
v-model:height=
"remarkHeight"
:style=
"
{ height: `${remarkHeight}px` }"
/>
/> -->
<DataaSource/>
</div>
<Toolbar
class=
"layout-content-right"
/>
</div>
...
...
@@ -29,11 +31,12 @@
</
template
>
<
script
lang=
"ts"
setup
>
import
{
ref
}
from
'vue'
import
{
ref
,
reactive
,
provide
,
watch
,
inject
}
from
'vue'
import
{
storeToRefs
}
from
'pinia'
import
{
useMainStore
}
from
'@/store'
import
{
useMainStore
,
useSlidesStore
,
useScreenStore
}
from
'@/store'
import
useGlobalHotkey
from
'@/hooks/useGlobalHotkey'
import
usePasteEvent
from
'@/hooks/usePasteEvent'
import
{
injectKeyDataSource
,
injectKeyTemplate
}
from
'@/types/injectKey'
import
EditorHeader
from
'./EditorHeader/index.vue'
import
Canvas
from
'./Canvas/index.vue'
...
...
@@ -45,6 +48,69 @@ import ExportDialog from './ExportDialog/index.vue'
import
SelectPanel
from
'./SelectPanel.vue'
import
SearchPanel
from
'./SearchPanel.vue'
import
Modal
from
'@/components/Modal.vue'
import
DataaSource
from
'./DataaSource/index.vue'
import
ConfigService
from
'@/services/ConfigService'
const
datas
=
reactive
({
DataSource
:{
ConfigId
:
inject
(
injectKeyTemplate
).
ConfigId
?
inject
(
injectKeyTemplate
).
ConfigId
:
0
,
pageType
:
1
,
//1基础 2酒店 3景 4餐
DataSourceOverlay
:
false
,
DataSourceList
:[],
queryObj
:{
TempId
:
inject
(
injectKeyTemplate
).
TempId
?
inject
(
injectKeyTemplate
).
TempId
:
0
,
//编号(新增传0) 是 [int]
LineId
:
null
,
//线路Id 是 [int]
LineName
:
''
,
//线路名称 是 [string]
LtId
:
null
,
//系列Id 是 [int]
Title
:
''
,
//名称 是 [string]
TempData
:
null
,
//模板数据 是 [json]
CoverImg
:
''
,
//封面图 是 [string]
CountryName
:
null
,
//国家 是 [string]
SeasonName
:
''
,
//季节 是 [string]
ColorName
:
''
,
//颜色名称 是 [string]
ColorStr
:
null
,
//颜色值 是 [string]
TempType
:
1
,
// 版面类型(1-横版,2-竖版)
}
},
})
const
slidesStore
=
useSlidesStore
()
const
TemplateTypeStore
=
useScreenStore
()
const
TempDataSourceStore
=
useScreenStore
()
const
{
slides
,
slideIndex
}
=
storeToRefs
(
slidesStore
)
const
{
TemplateType
}
=
storeToRefs
(
TemplateTypeStore
)
const
pageTypesList
=
ref
([]
as
any
)
watch
(()
=>
datas
.
DataSource
,
()
=>
{
provide
(
injectKeyDataSource
,
datas
.
DataSource
)
})
// 数据源
const
GetTripFiled
=
async
()
=>
{
try
{
let
TemplateRes
=
await
ConfigService
.
TemplateGetTripFiled
();
if
(
TemplateRes
.
data
.
resultCode
==
1
)
{
TemplateRes
.
data
.
data
.
forEach
(
x
=>
{
let
obj
=
pageTypesList
.
value
.
findIndex
(
z
=>
{
return
z
.
FiledType
==
x
.
FiledType
})
if
(
obj
==-
1
){
let
object
=
{
FiledType
:
x
.
FiledType
,
Name
:
x
.
FiledTypeStr
}
pageTypesList
.
value
.
push
(
object
)
}
})
TemplateTypeStore
.
setTemplateType
(
pageTypesList
.
value
)
TempDataSourceStore
.
setTemplateDataSource
(
TemplateRes
.
data
.
data
)
}
}
catch
(
error
)
{
console
.
log
(
"TemplateGetTripFiled"
,
error
);
}
}
GetTripFiled
()
const
mainStore
=
useMainStore
()
const
{
dialogForExport
,
showSelectPanel
,
showSearchPanel
}
=
storeToRefs
(
mainStore
)
...
...
@@ -52,8 +118,12 @@ const closeExportDialog = () => mainStore.setDialogForExport('')
const
remarkHeight
=
ref
(
40
)
useGlobalHotkey
()
usePasteEvent
()
provide
(
injectKeyDataSource
,
datas
.
DataSource
)
</
script
>
<
style
lang=
"scss"
scoped
>
...
...
src/views/Market/Index.vue
View file @
f8159880
This diff is collapsed.
Click to expand it.
src/views/components/ThumbnailSlide/ThumbnailElement.vue
View file @
f8159880
...
...
@@ -29,7 +29,7 @@ import BaseVideoElement from '@/views/components/element/VideoElement/BaseVideoE
import
BaseAudioElement
from
'@/views/components/element/AudioElement/BaseAudioElement.vue'
const
props
=
defineProps
<
{
elementInfo
:
PPTElement
elementInfo
:
PPTElement
,
elementIndex
:
number
}
>
()
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment