aiCreateDialog.vue 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  1. <template>
  2. <el-dialog title="AI生成PPT" :visible.sync="dialogVisibleAiCreate" :append-to-body="true" width="700px"
  3. :before-close="handleClose" class="dialog_diy">
  4. <div style="height: 500px; padding: 15px" v-loading="loading">
  5. <!-- <div class="t_box">
  6. <span>选择:</span>
  7. <el-radio-group v-model="radio" @change="changeRadio">
  8. <el-radio :label="0">PPT</el-radio>
  9. <el-radio :label="1">教案</el-radio>
  10. <el-radio :label="2">视频</el-radio>
  11. </el-radio-group>
  12. </div> -->
  13. <div class="t_box" v-if="steps == 1" style="height: 100%">
  14. <textarea style="height: 100%" rows="10" class="binfo_input binfo_textarea" cols placeholder="请生成大纲"
  15. v-model="outline"></textarea>
  16. </div>
  17. <div style="height: 100%" v-else>
  18. <wOffice v-if="url" :url="url"></wOffice>
  19. </div>
  20. </div>
  21. <span slot="footer" class="dialog-footer">
  22. <el-button @click="aiGet(2)" type="primary" :disabled="loading">重新生成大纲</el-button>
  23. <el-button @click="aiGet(1)" type="primary" :disabled="loading">{{
  24. url ? "重新生成PPT" : "生成PPT"
  25. }}</el-button>
  26. <el-button @click="steps = 1" type="primary" v-if="steps == 2">上一步</el-button>
  27. <el-button @click="steps = 2" type="primary" v-else-if="steps == 1 && url">下一步</el-button>
  28. <el-button @click="confirm" type="primary">确 定</el-button>
  29. <el-button @click="close">关 闭</el-button>
  30. </span>
  31. </el-dialog>
  32. </template>
  33. <script>
  34. import Pptxgen from "pptxgenjs";
  35. import wOffice from "../components/wOffice.vue";
  36. import { v4 as uuidv4 } from "uuid";
  37. export default {
  38. components: {
  39. wOffice,
  40. },
  41. props: {
  42. dialogVisibleAiCreate: {
  43. type: Boolean,
  44. default: false,
  45. },
  46. courseName: {
  47. type: String,
  48. default: "",
  49. },
  50. infoData: {
  51. type: Array,
  52. default: "",
  53. },
  54. courseTypeId: {
  55. type: Array,
  56. default: "",
  57. },
  58. CourseTypeJson: {
  59. type: Object,
  60. default: ()=>({}),
  61. }
  62. },
  63. // 根据用户给你的参考资料
  64. data() {
  65. return {
  66. userid: this.$route.query.userid,
  67. radio: 0,
  68. aiJson: {
  69. ppt: `## 任务
  70. 请生成关于${this.courseName},为教师生成这节课的教学ppt,页数在20页左右。PPT的内容主要是讲解该课程中所有可能涉及到的知识点。
  71. ## 工作流
  72. 1. 从用户提供的参考资料中提取10个最重要的知识点(知识点水平限制在小学和初中),并输出。
  73. 2. 针对10个知识点中的每个,你使用1~3页ppt详细的对知识点进行讲解。你的讲解词应该在100token左右
  74. 3. 讲解完所有知识点后,再根据知识点出5道单选题(放在5页ppt中)
  75. ## 限制
  76. - 你不能输出错误的知识,如果你实在不清楚,输出“对不起,我不确定”
  77. - 你不能输出违反伦理的内容`,
  78. word: "",
  79. video: "",
  80. },
  81. aiUrl: {
  82. ppt: "",
  83. word: "",
  84. video: "",
  85. },
  86. detail: "",
  87. loading: false,
  88. url: "",
  89. uJson: {},
  90. outline: "",
  91. steps: 1,
  92. };
  93. },
  94. watch: {
  95. dialogVisibleAiCreate(newValue, oldValue) {
  96. if (newValue) {
  97. this.aiGet(2);
  98. }
  99. },
  100. },
  101. methods: {
  102. handleClose(done) {
  103. this.close();
  104. done();
  105. },
  106. close() {
  107. this.$emit("update:dialogVisibleAiCreate", false);
  108. },
  109. confirm() {
  110. if (this.url) {
  111. this.$emit("createAiPpt", this.uJson);
  112. } else {
  113. this.$message.error("请先生成ppt");
  114. }
  115. },
  116. changeRadio() {
  117. if (this.radio == 0) {
  118. this.detail = this.aiJson.ppt;
  119. }
  120. if (this.radio == 1) {
  121. this.detail = this.aiJson.word;
  122. }
  123. if (this.radio == 2) {
  124. this.detail = this.aiJson.video;
  125. }
  126. },
  127. createFileid(url) {
  128. let _this = this;
  129. return new Promise((resolve, reject) => {
  130. try {
  131. _this.ajax
  132. .put("https://gpt4.cocorobo.cn/upload_file_knowledge", {
  133. url: url,
  134. })
  135. .then((res) => {
  136. let _data = res.data.FunctionResponse;
  137. if (_data.result && _data.result.id) {
  138. resolve(_data.result.id);
  139. }
  140. })
  141. .catch(function (error) {
  142. resolve("");
  143. });
  144. } catch (e) {
  145. resolve();
  146. }
  147. });
  148. },
  149. async aiGet(type) {
  150. if (this.loading) {
  151. this.$message.error("正在生成中,请稍后");
  152. return;
  153. }
  154. this.url = "";
  155. this.uJson = {};
  156. let _this = this;
  157. let fileid = [];
  158. if (_this.infoData.length) {
  159. for (var i = 0; i < _this.infoData.length; i++) {
  160. if (_this.infoData[i].fileid) {
  161. fileid.push(_this.infoData[i].fileid);
  162. } else {
  163. let _fileid = await _this.createFileid(_this.infoData[i].url);
  164. if (_fileid) {
  165. _this.infoData[i].fileid = _fileid;
  166. _this.$forceUpdate();
  167. fileid.push(_fileid);
  168. }
  169. }
  170. }
  171. }
  172. console.log("fileid=========", fileid);
  173. let mclass = [];
  174. if (_this.courseTypeId.length) {
  175. for (var i = 0; i < _this.courseTypeId.length; i++) {
  176. let _sid = _this.courseTypeId[i];
  177. for (
  178. var j = 0;
  179. j <
  180. _this.CourseTypeJson["34628934-d02f-11ec-8c78-005056b86db5"].length;
  181. j++
  182. ) {
  183. if (
  184. _sid ==
  185. _this.CourseTypeJson["34628934-d02f-11ec-8c78-005056b86db5"][j].id
  186. ) {
  187. mclass.push(
  188. _this.CourseTypeJson["34628934-d02f-11ec-8c78-005056b86db5"][j]
  189. .name
  190. );
  191. }
  192. }
  193. }
  194. }
  195. let message = "";
  196. if (type == 1) {
  197. message = `NOTICE
  198. Role: 从用户提供的参考资料中提取5个最重要的学科概念${mclass.length ? "(水平限制在{面向年级}中)" : ""},作为ppt内容。
  199. Output: Provide your output in json format.
  200. Language: Please use the same language as the user requirement, if the user speaks Chinese, the specific text of your answer should also be in Chinese.
  201. ATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced "Format example".
  202. Instruction: Based on the context, follow "Format example", write content.
  203. # Context
  204. ## 任务
  205. 将参考#参考资料,为教师生成这节课的教学ppt。PPT的内容主要是讲解该课程中所有可能涉及到的知识点,每个知识点用1页讲解。
  206. ## 工作流
  207. 1. 针对大纲中的每个知识点,生成200字左右的详细讲解。你的语气应该让小学或初中的学生清晰易懂的讲解。请尽可能的详细,这对我很重要。
  208. 2. 针对大纲中的每个测试,详细设计不同测试题目,例如单选,多选,对错题等。
  209. # 每一页输出格式
  210. - 页数:序列数字
  211. - 标题:学科概念(请从给你的大纲中摘取)
  212. - 子标题:知识点(请从给你的大纲中摘取)
  213. - 知识点讲解:针对大纲中的每个知识点,生成200字左右的详细讲解。你的语气应该让小学或初中的学生清晰易懂的讲解。你的讲解词在200 token左右。请尽可能的详细,这对我很重要。
  214. ## 限制
  215. - 你不能输出错误的知识,如果你实在不清楚,修改大纲中的知识点。
  216. - 你不能输出违反伦理的内容
  217. ## 工作流
  218. 1. 从用户提供的参考资料中提取5个最重要的学科概念,并输出。
  219. 2. 分解每个学科概念为几个子知识点
  220. 3. 简要描述每个知识点
  221. 4.生成5个测试题以考察学生的掌握情况
  222. ## 参考资料
  223. ${_this.outline}
  224. # Format example
  225. [{"page": "页码(数字)","title": "学科概念(请从给你的大纲中摘取)(标题)","task": "知识点(请从给你的大纲中摘取)(子标题)","points": "知识点讲解:针对大纲中的每个知识点,生成200字左右的详细讲解。你的语气应该让小学或初中的学生清晰易懂的讲解。你的讲解词在100 token左右。请尽可能的详细,这对我很重要。"}]`;
  226. } else {
  227. message = `# 任务
  228. 请根据参考资料,生成关于${this.courseName},为教师生成这节课的教学ppt的大纲,大纲的主要内容课程知识点的讲解与相关练习和测试。你的输出应该符合#输出格式
  229. # 工作流
  230. 1. 从用户提供的参考资料中提取5个最重要的学科概念${mclass.length ? "(水平限制在{面向年级}中)" : ""},并输出。
  231. 2. 分解每个学科概念为几个子知识点
  232. 3. 简要描述每个知识点
  233. 4.生成5个测试题以考察学生的掌握情况
  234. ${mclass.length ? "#参考资料\n面向年级:" + mclass.join(",") : ""}
  235. # 输出格式
  236. - 标题:学科概念1
  237. 1.知识点:知识点1
  238. 2.知识点:知识点2
  239. 3.知识点:知识点3
  240. 4.知识点:知识点4
  241. 5.知识点:知识点5
  242. # 限制
  243. - 你不能输出错误的知识,如果你实在不清楚,输出“对不起,我不确定”
  244. - 你不能输出违反伦理的内容
  245. `;
  246. }
  247. // let params = JSON.stringify({
  248. // // "model": "Chat",
  249. // model: 'gpt-3.5-turbo',
  250. // temperature: 0,
  251. // max_tokens: 4096,
  252. // top_p: 1,
  253. // frequency_penalty: 0,
  254. // presence_penalty: 0,
  255. // messages: [{
  256. // content: message,
  257. // role: 'user'
  258. // }],
  259. // stream: false,
  260. // uid: this.userid,
  261. // mind_map_question: "",
  262. // })
  263. // _this.loading = true
  264. // _this.ajax.post('https://gpt4.cocorobo.cn/chat', params).then(function (response) {
  265. // console.log(response);
  266. // let data = response.data.FunctionResponse
  267. // if (data.choices && data.choices.length && data.choices[0].message) {
  268. // console.log(data.choices[0].message.content);
  269. // try {
  270. // let _data = JSON.parse(data.choices[0].message.content)
  271. // _this.createPpt(_data)
  272. // } catch (e) {
  273. // console.log('error_________________'+e);
  274. // _this.$message.error(data.choices[0].message.content)
  275. // _this.loading = false
  276. // }
  277. // }
  278. // }).catch(function (error) {
  279. // _this.loading = false
  280. // console.log(error);
  281. // });
  282. let parm = {
  283. assistant_id:
  284. type == 1
  285. ? "6063369f-289a-11ef-8bf4-12e77c4cb76b"
  286. : "f8e1ebb2-2e0d-11ef-8bf4-12e77c4cb76b",
  287. message: [{ type: "text", text: message }],
  288. session_name: uuidv4(),
  289. userId: this.userid,
  290. file_ids: fileid.length ? [...fileid] : "",
  291. };
  292. _this.loading = true;
  293. this.ajax
  294. .post("https://gpt4.cocorobo.cn/ai_agent_park_chat", parm)
  295. .then((response) => {
  296. console.log(response);
  297. let data = response.data.FunctionResponse;
  298. if (data.message) {
  299. console.log(data.message);
  300. if (type == 1) {
  301. try {
  302. let _data = JSON.parse(
  303. data.message.replaceAll("```json", "").replaceAll("```", "")
  304. );
  305. _this.createPpt(_data);
  306. _this.steps = 2;
  307. } catch (e) {
  308. console.log("error_________________" + e);
  309. // _this.$message.error(data.message)
  310. try {
  311. // let _data = JSON.parse(data.message.match(/(?<=```json).*?(?=```)/)[0])
  312. var message = data.message;
  313. var jsonStart = message.indexOf("```json") + 7; // `+ 7` 是为了跳过 ```json
  314. var jsonEnd = message.indexOf("```", jsonStart);
  315. if (jsonStart !== -1 && jsonEnd !== -1) {
  316. var jsonString = message
  317. .substring(jsonStart, jsonEnd)
  318. .trim();
  319. var _data2 = JSON.parse(jsonString);
  320. _this.createPpt(_data2);
  321. _this.steps = 2;
  322. } else {
  323. _this.$message.error("生成失败,正在重新生成");
  324. _this.aiGet(type);
  325. }
  326. } catch (error) {
  327. _this.$message.error("生成失败,正在重新生成");
  328. _this.aiGet(type);
  329. _this.loading = false;
  330. }
  331. }
  332. } else {
  333. _this.outline = data.message;
  334. _this.steps = 1;
  335. _this.loading = false;
  336. }
  337. }
  338. })
  339. .catch((error) => {
  340. _this.loading = false;
  341. console.log(error);
  342. });
  343. },
  344. createPpt(array) {
  345. // 1. 创建PPT
  346. const pres = new Pptxgen();
  347. for (var i = 0; i < array.length; i++) {
  348. // 2. 创建一个PPT页面,每调用一次 pres.addSlide() 都可以生成一张新的页面
  349. // 建议把每个页面的构造抽成一个个函数,然后通过函数调用生成新页面,代码不会很乱
  350. const _slide = pres.addSlide();
  351. // 3. 调用addTetx(),在PPT页面中插入文字“Hello World from PptxGenJS...”
  352. // 括号里面是对文字的配置,文字横坐标x为1.5,纵坐标y为1.5,字体颜色 363636……
  353. // 关于坐标长度与px的转换 x 1 = 127~128px 左右
  354. const page = i+1 > 10 ? i+1 : "0"+(i+1);
  355. _slide.addText(page, {
  356. x: 0.2, // 横坐标
  357. y: 0.5,
  358. color: "363636",
  359. fontSize: 20, // 字号
  360. align: "left",
  361. });
  362. const tempResult1 = array[i].title;
  363. _slide.addText(tempResult1, {
  364. x: 0.6, // 横坐标
  365. y: 0.5,
  366. color: "363636",
  367. fontSize: 28, // 字号
  368. bold: true,
  369. align: "left",
  370. });
  371. const tempResult2 = array[i].task;
  372. _slide.addText(tempResult2, {
  373. x: 0.6, // 横坐标
  374. y: 1.5,
  375. color: "363636",
  376. fontSize: 18, // 字号
  377. align: "left",
  378. });
  379. const tempResult3 = array[i].points;
  380. _slide.addText(tempResult3, {
  381. x: 0.6, // 横坐标
  382. y: 3,
  383. w: "60%",
  384. color: "363636",
  385. fontSize: 18, // 字号
  386. align: "left",
  387. });
  388. }
  389. // 获取PPTX文件的ArrayBuffer
  390. // 保存为 Blob 并处理
  391. pres.write("blob").then((blob) => {
  392. // 现在你有了一个 Blob 对象
  393. console.log(blob);
  394. const file = new File([blob], this.courseName+".pptx", {
  395. type: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
  396. });
  397. console.log(pres);
  398. this.beforeUpload(file);
  399. });
  400. },
  401. beforeUpload(event) {
  402. var file = event;
  403. var credentials = {
  404. accessKeyId: "AKIATLPEDU37QV5CHLMH",
  405. secretAccessKey: "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",
  406. }; //秘钥形式的登录上传
  407. window.AWS.config.update(credentials);
  408. window.AWS.config.region = "cn-northwest-1"; //设置区域
  409. var bucket = new window.AWS.S3({ params: { Bucket: "ccrb" } }); //选择桶
  410. var _this = this;
  411. if (file) {
  412. var params = {
  413. Key:
  414. file.name.split(".")[0] +
  415. new Date().getTime() +
  416. "." +
  417. file.name.split(".")[file.name.split(".").length - 1],
  418. ContentType: file.type,
  419. Body: file,
  420. "Access-Control-Allow-Credentials": "*",
  421. ACL: "public-read",
  422. }; //key可以设置为桶的相抵路径,Body为文件, ACL最好要设置
  423. var options = {
  424. partSize: 2048 * 1024 * 1024,
  425. queueSize: 2,
  426. leavePartsOnError: true,
  427. };
  428. bucket
  429. .upload(params, options)
  430. .on("httpUploadProgress", function (evt) {
  431. //这里可以写进度条
  432. // console.log("Uploaded : " + parseInt((evt.loaded * 80) / evt.total) + '%');
  433. })
  434. .send(function (err, data) {
  435. _this.loading = false;
  436. if (err) {
  437. _this.$message.error("上传失败");
  438. } else {
  439. _this.url = data.Location;
  440. _this.uJson = {
  441. name: file.name,
  442. url: data.Location,
  443. type: 3,
  444. };
  445. console.log(data.Location);
  446. }
  447. });
  448. }
  449. },
  450. },
  451. };
  452. </script>
  453. <style scoped>
  454. .dialog_diy>>>.el-dialog {
  455. height: auto;
  456. margin: 15vh auto 0 !important;
  457. }
  458. .dialog_diy>>>.el-dialog__header {
  459. background: #454545 !important;
  460. padding: 15px 20px;
  461. }
  462. .dialog_diy>>>.el-dialog__body {
  463. height: calc(100% - 124px);
  464. box-sizing: border-box;
  465. padding: 0px;
  466. }
  467. .dialog_diy>>>.el-dialog__title {
  468. color: #fff;
  469. }
  470. .dialog_diy>>>.el-dialog__headerbtn {
  471. top: 19px;
  472. }
  473. .dialog_diy>>>.el-dialog__headerbtn .el-dialog__close {
  474. color: #fff;
  475. }
  476. .dialog_diy>>>.el-dialog__headerbtn .el-dialog__close:hover {
  477. color: #fff;
  478. }
  479. .dialog_diy>>>.el-dialog__body,
  480. .dialog_diy>>>.el-dialog__footer {
  481. background: #fafafa;
  482. }
  483. .binfo_input {
  484. width: 100%;
  485. margin: 0;
  486. padding: 5px 7px;
  487. display: block;
  488. min-width: 0;
  489. outline: none;
  490. box-sizing: border-box;
  491. background: none;
  492. border: none;
  493. border-radius: 4px;
  494. background: #fff;
  495. font-size: 15px;
  496. resize: none;
  497. font-family: "Microsoft YaHei";
  498. min-height: 48px;
  499. /* border: 1px solid #3682fc00; */
  500. border: 1.5px solid #cad1dc;
  501. }
  502. .binfo_textarea {
  503. border: 1.5px solid #cad1dc;
  504. font-size: 15px;
  505. resize: none;
  506. /* background: #f6f6f6; */
  507. font-family: "Microsoft YaHei";
  508. }
  509. .binfo_input:focus-visible {
  510. border: 1.5px solid #3681fc !important;
  511. }
  512. .t_box {
  513. display: flex;
  514. margin-bottom: 15px;
  515. }
  516. .t_box>span:nth-child(1) {
  517. min-width: 80px;
  518. font-size: 16px;
  519. color: #000;
  520. }
  521. </style>