mirror of
https://github.com/pipipi-pikachu/PPTist.git
synced 2025-04-15 02:20:00 +08:00
feat: 流式AIPPT页面生成
This commit is contained in:
parent
7603982de4
commit
bc6bec2835
69
public/mocks/AIPPT_Outline.md
Normal file
69
public/mocks/AIPPT_Outline.md
Normal file
@ -0,0 +1,69 @@
|
||||
# 5G技术如何改变我们的生活
|
||||
## 5G技术概述
|
||||
### 5G的定义
|
||||
- 第五代移动通信技术
|
||||
- 基于4G技术的重大升级
|
||||
### 5G的关键特性
|
||||
- 高速率
|
||||
- 低时延
|
||||
- 大容量
|
||||
### 5G的发展历程
|
||||
- 早期研究阶段
|
||||
- 标准制定阶段
|
||||
- 商用推广阶段
|
||||
|
||||
## 5G对通信领域的变革
|
||||
### 个人通信体验提升
|
||||
- 高清视频通话无卡顿
|
||||
- 快速下载大文件
|
||||
- 多人在线游戏低延迟
|
||||
### 通信网络架构优化
|
||||
- 网络切片技术实现差异化服务
|
||||
- 边缘计算减少数据传输距离
|
||||
### 通信安全保障增强
|
||||
- 新的加密算法保障数据安全
|
||||
- 实时监测防范网络攻击
|
||||
|
||||
## 5G与智能家居的融合
|
||||
### 智能家电控制
|
||||
- 远程控制家电开关和运行模式
|
||||
- 家电之间智能联动
|
||||
### 家庭安防升级
|
||||
- 高清实时监控家庭情况
|
||||
- 异常情况及时报警
|
||||
### 家居环境智能调节
|
||||
- 自动调节室内温度、湿度
|
||||
- 智能灯光控制营造氛围
|
||||
|
||||
## 5G推动智能交通发展
|
||||
### 自动驾驶汽车
|
||||
- 车辆间实时通信避免碰撞
|
||||
- 高精度地图实时更新
|
||||
### 智能交通管理
|
||||
- 实时监控交通流量并优化信号灯
|
||||
- 快速处理交通事故
|
||||
### 公共交通智能化
|
||||
- 实时公交信息查询
|
||||
- 车内高速网络服务
|
||||
|
||||
## 5G在医疗领域的应用
|
||||
### 远程医疗服务
|
||||
- 专家远程诊断病情
|
||||
- 远程手术指导
|
||||
### 医疗设备互联
|
||||
- 可穿戴设备实时传输健康数据
|
||||
- 医院内部设备信息共享
|
||||
### 智能医疗管理
|
||||
- 电子病历快速调取
|
||||
- 医疗资源智能分配
|
||||
|
||||
## 5G助力工业互联网升级
|
||||
### 智能制造
|
||||
- 生产设备实时监控和远程维护
|
||||
- 柔性生产线智能调度
|
||||
### 工业物流优化
|
||||
- 货物实时定位和跟踪
|
||||
- 智能仓储管理
|
||||
### 工业安全保障
|
||||
- 危险区域实时监测
|
||||
- 事故预警和应急处理
|
@ -256,6 +256,10 @@ const contextmenus = (el: HTMLElement): ContextmenuItem[] => {
|
||||
|
||||
&.contextmenu-active {
|
||||
color: $themeColor;
|
||||
|
||||
.text {
|
||||
background-color: rgba($color: $themeColor, $alpha: .08);
|
||||
}
|
||||
}
|
||||
|
||||
&.title {
|
||||
@ -279,7 +283,14 @@ const contextmenus = (el: HTMLElement): ContextmenuItem[] => {
|
||||
height: 100%;
|
||||
padding: 0 11px;
|
||||
line-height: 32px;
|
||||
border-radius: $borderRadius;
|
||||
transition: background-color .2s;
|
||||
cursor: pointer;
|
||||
@include ellipsis-oneline();
|
||||
|
||||
&:hover {
|
||||
background-color: rgba($color: $themeColor, $alpha: .08);
|
||||
}
|
||||
}
|
||||
.flag {
|
||||
width: 32px;
|
||||
|
@ -19,6 +19,8 @@ export default () => {
|
||||
const { isEmptySlide } = useSlideHandler()
|
||||
|
||||
const imgPool = ref<PexelsImage[]>([])
|
||||
const transitionIndex = ref(0)
|
||||
const transitionTemplate = ref<Slide | null>(null)
|
||||
|
||||
const checkTextType = (el: PPTElement, type: TextType) => {
|
||||
return (el.type === 'text' && el.textType === type) || (el.type === 'shape' && el.text && el.text.type === type)
|
||||
@ -226,6 +228,8 @@ export default () => {
|
||||
}
|
||||
|
||||
const AIPPT = (templateSlides: Slide[], _AISlides: AIPPTSlide[], imgs?: PexelsImage[]) => {
|
||||
slidesStore.updateSlideIndex(slidesStore.slides.length - 1)
|
||||
|
||||
if (imgs) imgPool.value = imgs
|
||||
|
||||
const AISlides: AIPPTSlide[] = []
|
||||
@ -307,16 +311,16 @@ export default () => {
|
||||
const contentTemplates = templateSlides.filter(slide => slide.type === 'content')
|
||||
const endTemplates = templateSlides.filter(slide => slide.type === 'end')
|
||||
|
||||
const coverTemplate = coverTemplates[Math.floor(Math.random() * coverTemplates.length)]
|
||||
const transitionTemplate = transitionTemplates[Math.floor(Math.random() * transitionTemplates.length)]
|
||||
const endTemplate = endTemplates[Math.floor(Math.random() * endTemplates.length)]
|
||||
if (!transitionTemplate.value) {
|
||||
const _transitionTemplate = transitionTemplates[Math.floor(Math.random() * transitionTemplates.length)]
|
||||
transitionTemplate.value = _transitionTemplate
|
||||
}
|
||||
|
||||
const slides = []
|
||||
|
||||
let transitionIndex = 0
|
||||
|
||||
for (const item of AISlides) {
|
||||
if (item.type === 'cover') {
|
||||
const coverTemplate = coverTemplates[Math.floor(Math.random() * coverTemplates.length)]
|
||||
const elements = coverTemplate.elements.map(el => {
|
||||
if (el.type === 'image' && el.imageType && imgPool.value.length) return getNewImgElement(el)
|
||||
if (el.type !== 'text' && el.type !== 'shape') return el
|
||||
@ -374,8 +378,8 @@ export default () => {
|
||||
})
|
||||
}
|
||||
else if (item.type === 'transition') {
|
||||
transitionIndex++
|
||||
const elements = transitionTemplate.elements.map(el => {
|
||||
transitionIndex.value = transitionIndex.value + 1
|
||||
const elements = transitionTemplate.value.elements.map(el => {
|
||||
if (el.type === 'image' && el.imageType && imgPool.value.length) return getNewImgElement(el)
|
||||
if (el.type !== 'text' && el.type !== 'shape') return el
|
||||
if (checkTextType(el, 'title') && item.data.title) {
|
||||
@ -385,7 +389,7 @@ export default () => {
|
||||
return getNewTextElement({ el, text: item.data.text, maxLine: 3 })
|
||||
}
|
||||
if (checkTextType(el, 'partNumber')) {
|
||||
return getNewTextElement({ el, text: transitionIndex + '', maxLine: 1, digitPadding: true })
|
||||
return getNewTextElement({ el, text: transitionIndex.value + '', maxLine: 1, digitPadding: true })
|
||||
}
|
||||
return el
|
||||
})
|
||||
@ -469,6 +473,7 @@ export default () => {
|
||||
})
|
||||
}
|
||||
else if (item.type === 'end') {
|
||||
const endTemplate = endTemplates[Math.floor(Math.random() * endTemplates.length)]
|
||||
const elements = endTemplate.elements.map(el => {
|
||||
if (el.type === 'image' && el.imageType && imgPool.value.length) return getNewImgElement(el)
|
||||
return el
|
||||
|
@ -1,5 +1,6 @@
|
||||
import axios from './config'
|
||||
|
||||
// export const SERVER_URL = 'http://localhost:5000'
|
||||
export const SERVER_URL = (import.meta.env.MODE === 'development') ? '/api' : 'https://server.pptist.cn'
|
||||
export const ASSET_URL = 'https://asset.pptist.cn'
|
||||
|
||||
@ -35,11 +36,18 @@ export default {
|
||||
content: string,
|
||||
language: string,
|
||||
model: string,
|
||||
) {
|
||||
return axios.post(`${SERVER_URL}/tools/aippt`, {
|
||||
content,
|
||||
language,
|
||||
model,
|
||||
): Promise<any> {
|
||||
return fetch(`${SERVER_URL}/tools/aippt`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
content,
|
||||
language,
|
||||
model,
|
||||
stream: true,
|
||||
}),
|
||||
})
|
||||
},
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
<div class="header">
|
||||
<span class="title">AIPPT</span>
|
||||
<span class="subtite" v-if="step === 'template'">从下方挑选合适的模板,开始生成PPT</span>
|
||||
<span class="subtite" v-else-if="step === 'outline'">确认下方内容大纲(点击编辑内容,右键添加/删除大纲项),开始挑选模板</span>
|
||||
<span class="subtite" v-else-if="step === 'outline'">确认下方内容大纲(点击编辑内容,右键添加/删除大纲项),开始选择模板</span>
|
||||
<span class="subtite" v-else>在下方输入您的PPT主题,并适当补充信息,如行业、岗位、学科、用途等</span>
|
||||
</div>
|
||||
|
||||
@ -30,8 +30,9 @@
|
||||
style="width: 160px;"
|
||||
v-model:value="model"
|
||||
:options="[
|
||||
{ label: 'DeepSeek-v3', value: 'ark-deepseek-v3' },
|
||||
{ label: 'Doubao-1.5-Pro', value: 'doubao-1.5-pro-32k' },
|
||||
{ label: 'DeepSeek-v3', value: 'ark-deepseek-v3' },
|
||||
{ label: 'GLM-4-Flash', value: 'GLM-4-flash' },
|
||||
]"
|
||||
/>
|
||||
</div>
|
||||
@ -42,7 +43,7 @@
|
||||
<OutlineEditor v-model:value="outline" />
|
||||
</div>
|
||||
<div class="btns" v-if="!outlineCreating">
|
||||
<Button class="btn" type="primary" @click="step = 'template'">挑选模板</Button>
|
||||
<Button class="btn" type="primary" @click="step = 'template'">选择模板</Button>
|
||||
<Button class="btn" @click="outline = ''; step = 'setup'">返回重新生成</Button>
|
||||
</div>
|
||||
</div>
|
||||
@ -58,7 +59,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="btns">
|
||||
<Button class="btn" type="primary" @click="createPPT()">继续</Button>
|
||||
<Button class="btn" type="primary" @click="createPPT()">生成</Button>
|
||||
<Button class="btn" @click="outline = ''; step = 'setup'">返回重新生成</Button>
|
||||
</div>
|
||||
</div>
|
||||
@ -84,7 +85,7 @@ import OutlineEditor from '@/components/OutlineEditor.vue'
|
||||
|
||||
const mainStore = useMainStore()
|
||||
const { templates } = storeToRefs(useSlidesStore())
|
||||
const { AIPPT, getJSONContent } = useAIPPT()
|
||||
const { AIPPT } = useAIPPT()
|
||||
|
||||
const language = ref<'zh' | 'en'>('zh')
|
||||
const keyword = ref('')
|
||||
@ -95,7 +96,7 @@ const outlineCreating = ref(false)
|
||||
const outlineRef = ref<HTMLElement>()
|
||||
const inputRef = ref<InstanceType<typeof Input>>()
|
||||
const step = ref<'setup' | 'outline' | 'template'>('setup')
|
||||
const model = ref('ark-deepseek-v3')
|
||||
const model = ref('doubao-1.5-pro-32k')
|
||||
|
||||
const recommends = ref([
|
||||
'大学生职业生涯规划',
|
||||
@ -152,19 +153,34 @@ const createOutline = async () => {
|
||||
const createPPT = async () => {
|
||||
loading.value = true
|
||||
|
||||
// const AISlides: AIPPTSlide[] = await api.getMockData('AIPPT')
|
||||
const AISlides: AIPPTSlide[] = await api.AIPPT(outline.value, language.value, 'doubao-1.5-pro-32k').then(ret => {
|
||||
const obj = JSON.parse(getJSONContent(ret.data[0].content))
|
||||
return obj.data
|
||||
})
|
||||
const stream = await api.AIPPT(outline.value, language.value, 'doubao-1.5-pro-32k')
|
||||
const templateSlides: Slide[] = await api.getFileData(selectedTemplate.value).then(ret => ret.slides)
|
||||
// const templateSlides: Slide[] = await api.getMockData(selectedTemplate.value).then(ret => ret.slides)
|
||||
|
||||
AIPPT(templateSlides, AISlides)
|
||||
const reader: ReadableStreamDefaultReader = stream.body.getReader()
|
||||
const decoder = new TextDecoder('utf-8')
|
||||
|
||||
const readStream = () => {
|
||||
reader.read().then(({ done, value }) => {
|
||||
if (done) {
|
||||
loading.value = false
|
||||
mainStore.setAIPPTDialogState(false)
|
||||
return
|
||||
}
|
||||
|
||||
const chunk = decoder.decode(value, { stream: true })
|
||||
try {
|
||||
const slide: AIPPTSlide = JSON.parse(chunk)
|
||||
AIPPT(templateSlides, [slide])
|
||||
}
|
||||
catch (err) {
|
||||
// eslint-disable-next-line
|
||||
console.error(err)
|
||||
}
|
||||
|
||||
loading.value = false
|
||||
|
||||
mainStore.setAIPPTDialogState(false)
|
||||
readStream()
|
||||
})
|
||||
}
|
||||
readStream()
|
||||
}
|
||||
</script>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user