diff --git a/public/mocks/AIPPT_Outline.md b/public/mocks/AIPPT_Outline.md new file mode 100644 index 00000000..cca2bd1a --- /dev/null +++ b/public/mocks/AIPPT_Outline.md @@ -0,0 +1,69 @@ +# 5G技术如何改变我们的生活 +## 5G技术概述 +### 5G的定义 +- 第五代移动通信技术 +- 基于4G技术的重大升级 +### 5G的关键特性 +- 高速率 +- 低时延 +- 大容量 +### 5G的发展历程 +- 早期研究阶段 +- 标准制定阶段 +- 商用推广阶段 + +## 5G对通信领域的变革 +### 个人通信体验提升 +- 高清视频通话无卡顿 +- 快速下载大文件 +- 多人在线游戏低延迟 +### 通信网络架构优化 +- 网络切片技术实现差异化服务 +- 边缘计算减少数据传输距离 +### 通信安全保障增强 +- 新的加密算法保障数据安全 +- 实时监测防范网络攻击 + +## 5G与智能家居的融合 +### 智能家电控制 +- 远程控制家电开关和运行模式 +- 家电之间智能联动 +### 家庭安防升级 +- 高清实时监控家庭情况 +- 异常情况及时报警 +### 家居环境智能调节 +- 自动调节室内温度、湿度 +- 智能灯光控制营造氛围 + +## 5G推动智能交通发展 +### 自动驾驶汽车 +- 车辆间实时通信避免碰撞 +- 高精度地图实时更新 +### 智能交通管理 +- 实时监控交通流量并优化信号灯 +- 快速处理交通事故 +### 公共交通智能化 +- 实时公交信息查询 +- 车内高速网络服务 + +## 5G在医疗领域的应用 +### 远程医疗服务 +- 专家远程诊断病情 +- 远程手术指导 +### 医疗设备互联 +- 可穿戴设备实时传输健康数据 +- 医院内部设备信息共享 +### 智能医疗管理 +- 电子病历快速调取 +- 医疗资源智能分配 + +## 5G助力工业互联网升级 +### 智能制造 +- 生产设备实时监控和远程维护 +- 柔性生产线智能调度 +### 工业物流优化 +- 货物实时定位和跟踪 +- 智能仓储管理 +### 工业安全保障 +- 危险区域实时监测 +- 事故预警和应急处理 \ No newline at end of file diff --git a/src/components/OutlineEditor.vue b/src/components/OutlineEditor.vue index de7e9931..b2cc3062 100644 --- a/src/components/OutlineEditor.vue +++ b/src/components/OutlineEditor.vue @@ -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; diff --git a/src/hooks/useAIPPT.ts b/src/hooks/useAIPPT.ts index b499604c..51472cfd 100644 --- a/src/hooks/useAIPPT.ts +++ b/src/hooks/useAIPPT.ts @@ -19,6 +19,8 @@ export default () => { const { isEmptySlide } = useSlideHandler() const imgPool = ref([]) + const transitionIndex = ref(0) + const transitionTemplate = ref(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 diff --git a/src/services/index.ts b/src/services/index.ts index 02637997..815941dc 100644 --- a/src/services/index.ts +++ b/src/services/index.ts @@ -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 { + return fetch(`${SERVER_URL}/tools/aippt`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + content, + language, + model, + stream: true, + }), }) }, } \ No newline at end of file diff --git a/src/views/Editor/AIPPTDialog.vue b/src/views/Editor/AIPPTDialog.vue index dc333aec..2f28ff39 100644 --- a/src/views/Editor/AIPPTDialog.vue +++ b/src/views/Editor/AIPPTDialog.vue @@ -3,7 +3,7 @@
AIPPT 从下方挑选合适的模板,开始生成PPT - 确认下方内容大纲(点击编辑内容,右键添加/删除大纲项),开始挑选模板 + 确认下方内容大纲(点击编辑内容,右键添加/删除大纲项),开始选择模板 在下方输入您的PPT主题,并适当补充信息,如行业、岗位、学科、用途等
@@ -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' }, ]" /> @@ -42,7 +43,7 @@
- +
@@ -58,7 +59,7 @@
- +
@@ -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() const inputRef = ref>() 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() }