index.vue 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987
  1. <template>
  2. <div class="ch_box" ref="ch_box">
  3. <div class="ch_content_box" v-show="type == 1">
  4. <searchArea
  5. :courseDetail="courseDetail"
  6. ref="searchAreaRef"
  7. :navList="navList"
  8. :tcid="tcid"
  9. :openMegaphone="openMegaphone"
  10. :fileId="fileId"
  11. :recordType="recordType"
  12. v-if="itemType == 1"
  13. :canShowTips="canShowTips"
  14. :showTipsLoading="showTipsLoading"
  15. :tipsList="tipsList"
  16. />
  17. <taskArea
  18. :courseDetail="courseDetail"
  19. :navList="navList"
  20. :courseType="courseType"
  21. :taskCount="taskCount"
  22. :worksStudent="worksStudent"
  23. :openMegaphone="openMegaphone"
  24. ref="taskAreaRef"
  25. :fileId="fileId"
  26. v-show="itemType == 2"
  27. />
  28. <countdown
  29. ref="countdownRef"
  30. :fileId="fileId"
  31. :courseDetail="courseDetail"
  32. v-show="itemType == 3"
  33. />
  34. <languageAssistant ref="languageAssistantRef" v-if="itemType == 4" />
  35. <reviewArea ref="reviewAreaRef" v-if="itemType==5" :courseType="courseType" :taskCount="taskCount"/>
  36. <!-- <dialogArea
  37. :courseDetail="courseDetail"
  38. :openMegaphone="openMegaphone"
  39. ref="dialogAreaRef"
  40. :fileId="fileId"
  41. v-if="itemType == 3"
  42. /> -->
  43. </div>
  44. <div class="ch_nav_box">
  45. <div class="ch_nav_box_top">
  46. <div @click.stop="changeFold(!fold)" ref="foldBtnRef">
  47. <el-tooltip
  48. class="item"
  49. effect="dark"
  50. :content="fold ? '折叠' : '展开'"
  51. placement="top"
  52. >
  53. <img
  54. :src="require('../../assets/icon/course/foldIcon.svg')"
  55. alt=""
  56. :class="[!fold?'':'foldActive']"
  57. />
  58. </el-tooltip>
  59. </div>
  60. <div @click="changeItemType(5)" v-if="tType == 1">
  61. <el-tooltip class="item" effect="dark" content="评论" placement="top">
  62. <img
  63. :src="require('../../assets/icon/course/comment2.svg')"
  64. alt=""
  65. style="width: 22px;height: 22px;"
  66. />
  67. </el-tooltip>
  68. </div>
  69. <div
  70. @click="startRecording()"
  71. v-if="!videoStart && (tType == 1 || tType == 4)"
  72. >
  73. <el-tooltip class="item" effect="dark" content="录制" placement="top">
  74. <img
  75. :src="require('../../assets/icon/course/record3.svg')"
  76. alt=""
  77. style="width: 22px;height: 22px;"
  78. />
  79. </el-tooltip>
  80. </div>
  81. <div
  82. @click="$emit('stopRecording')"
  83. v-else-if="tType == 1 || tType == 4"
  84. style="background:#f63564"
  85. >
  86. <el-tooltip class="item" effect="dark" content="下载" placement="top">
  87. <img
  88. :src="require('../../assets/icon/course/record4.svg')"
  89. alt=""
  90. style="width: 22px;height: 22px;"
  91. />
  92. </el-tooltip>
  93. </div>
  94. </div>
  95. <div class="ch_nav_box_middle">
  96. <div
  97. :class="[
  98. 'ch_nav_box_middle_item',
  99. itemType == 2 ? 'ch_nav_box_middle_item_active' : ''
  100. ]"
  101. @click.stop="changeItemType(2)"
  102. >
  103. <img
  104. v-if="itemType == 2"
  105. :src="require('../../assets/icon/course/task_active.png')"
  106. />
  107. <img
  108. v-if="itemType != 2"
  109. :src="require('../../assets/icon/course/task.png')"
  110. />
  111. <!-- <span :style="`background:url(${itemType==2?require('../../assets/icon/course/task_active.png'):require('../../assets/icon/course/task.png')});`"></span> -->
  112. <div>测验</div>
  113. </div>
  114. <div
  115. :class="[
  116. 'ch_nav_box_middle_item',
  117. itemType == 1 ? 'ch_nav_box_middle_item_active' : ''
  118. ]"
  119. @click.stop="changeItemType(1)"
  120. >
  121. <img
  122. v-if="itemType == 1"
  123. :src="require('../../assets/icon/course/up_active.png')"
  124. />
  125. <img
  126. v-if="itemType != 1"
  127. :src="require('../../assets/icon/course/up.png')"
  128. />
  129. <!-- <span :style="`background:url(${itemType==1?require('../../assets/icon/course/up_active.png'):require('../../assets/icon/course/up.png')});`"></span> -->
  130. <div>对话</div>
  131. </div>
  132. <div
  133. :class="[
  134. 'ch_nav_box_middle_item',
  135. itemType == 3 ? 'ch_nav_box_middle_item_active' : ''
  136. ]"
  137. @click.stop="changeItemType(3)"
  138. >
  139. <img
  140. v-if="itemType == 3"
  141. :src="require('../../assets/icon/course/Countdown2.svg')"
  142. />
  143. <img
  144. v-if="itemType != 3"
  145. :src="require('../../assets/icon/course/Countdown.svg')"
  146. />
  147. <div>倒计时</div>
  148. </div>
  149. </div>
  150. <div class="ch_nav_box_bottom">
  151. <div @click.stop="commentAndAnnotate()" style="z-index: 9999;">
  152. <el-tooltip class="item" effect="dark" content="批注" placement="top">
  153. <img
  154. :src="require('../../assets/icon/course/edit2.svg')"
  155. v-if="!AnnotationCanvasShow"
  156. />
  157. <img :src="require('../../assets/icon/course/edit3.svg')" v-else />
  158. </el-tooltip>
  159. </div>
  160. <!-- <div @click.stop="changeItemType(4)" :class="[itemType == 4?'ch_nav_box_middle_item_active':'']">
  161. <el-tooltip
  162. class="item"
  163. effect="dark"
  164. :content="itemType != 4?'开启语音助手':'关闭语音助手'"
  165. placement="top"
  166. >
  167. <img v-if="itemType != 4" :src="require('../../assets/icon/course/robot.svg')" />
  168. <img v-else :src="require('../../assets/icon/course/robot2.svg')" />
  169. </el-tooltip>
  170. </div> -->
  171. <div
  172. @click.stop="startAssistant()"
  173. :class="[recordType == 1 ? 'ch_nav_box_middle_item_active' : '']"
  174. >
  175. <el-tooltip
  176. class="item"
  177. effect="dark"
  178. :content="recordType == 0 ? '开启语音助手' : '关闭语音助手'"
  179. placement="top"
  180. >
  181. <img
  182. v-if="recordType != 1"
  183. :src="require('../../assets/icon/course/robot3.svg')"
  184. />
  185. <img v-else :src="require('../../assets/icon/course/robot3.svg')" />
  186. </el-tooltip>
  187. </div>
  188. <div @click.stop="$emit('goStep', 0)">
  189. <el-tooltip
  190. class="item"
  191. effect="dark"
  192. content="上一步"
  193. placement="top"
  194. >
  195. <img :src="require('../../assets/icon/course/last.png')" />
  196. </el-tooltip>
  197. </div>
  198. <div @click.stop="$emit('goStep', 1)">
  199. <el-tooltip
  200. class="item"
  201. effect="dark"
  202. content="下一步"
  203. placement="top"
  204. >
  205. <img :src="require('../../assets/icon/course/next.png')" />
  206. </el-tooltip>
  207. </div>
  208. <div @click="openSetting">
  209. <el-tooltip
  210. class="item"
  211. effect="dark"
  212. :content="type == 0 ? '展开' : '折叠'"
  213. placement="top"
  214. >
  215. <img :src="require('../../assets/icon/course/menu.png')" />
  216. </el-tooltip>
  217. </div>
  218. </div>
  219. </div>
  220. <div v-if="fold" class="itemFold" ref="itemFoldRef" v-click-outside="handleBlur">
  221. <div @click="$emit('backPage')">
  222. <img :src="require('../../assets/icon/course/return.png')" alt="" />
  223. <span>返回</span>
  224. </div>
  225. <div @click="$emit('refresh')">
  226. <img :src="require('../../assets/icon/course/refresh.png')" alt="" />
  227. <span>刷新</span>
  228. </div>
  229. <div @click="$emit('authority')" v-if="tType == 1 || tType == 4">
  230. <img :src="require('../../assets/icon/course/setting2.svg')" alt="" />
  231. <span>权限</span>
  232. </div>
  233. </div>
  234. <levitatedSphere ref="levitatedSphereRef" @startTime="startTime" />
  235. <timepiece ref="timepieceRef" />
  236. <AnnotationCanvas
  237. ref="AnnotationCanvasRef"
  238. @close="endCommentAndAnnotate"
  239. @changeStatus="changeAnnotationCanvasShow"
  240. />
  241. </div>
  242. </template>
  243. <script>
  244. import searchArea from "./component/searchArea.vue";
  245. import taskArea from "./component/taskArea.vue";
  246. // import dialogArea from "./component/dialogArea.vue";
  247. import levitatedSphere from "./component/levitatedSphere.vue";
  248. import timepiece from "./component/timepiece.vue";
  249. import countdown from "./component/countdown.vue";
  250. import AnnotationCanvas from "./component/AnnotationCanvas.vue";
  251. import languageAssistant from "./component/languageAssistant.vue";
  252. import reviewArea from "./component/reviewArea.vue";
  253. import { v4 as uuidv4 } from "uuid";
  254. // 自定义指令,用于处理点击外部区域的事件
  255. const clickOutside = {
  256. bind(el, binding) {
  257. // 在元素上绑定一个点击事件监听器
  258. el.clickOutsideEvent = function (event) {
  259. // 检查点击事件是否发生在元素的内部
  260. if (!(el === event.target || el.contains(event.target))) {
  261. // 如果点击事件发生在元素的外部,则触发指令绑定的方法,将点击的event数据传过去
  262. binding.value(event);
  263. }
  264. };
  265. // 在文档上添加点击事件监听器
  266. document.addEventListener("click", el.clickOutsideEvent);
  267. },
  268. unbind(el) {
  269. // 在元素上解除点击事件监听器
  270. document.removeEventListener("click", el.clickOutsideEvent);
  271. },
  272. };
  273. export default {
  274. emits: [
  275. "refresh",
  276. "goStep",
  277. "backPage",
  278. "authority",
  279. "review",
  280. "stopRecording",
  281. "startRecording"
  282. ],
  283. directives: {
  284. "click-outside": clickOutside, // 注册自定义指令
  285. },
  286. components: {
  287. searchArea,
  288. taskArea,
  289. // dialogArea,
  290. levitatedSphere,
  291. timepiece,
  292. countdown,
  293. AnnotationCanvas,
  294. languageAssistant,
  295. reviewArea
  296. },
  297. props: {
  298. courseDetail: {
  299. type: Object,
  300. default: () => {}
  301. },
  302. tType: {
  303. type:String,
  304. default: 0
  305. },
  306. navList: {
  307. type: Array,
  308. default: () => []
  309. },
  310. tcid: {
  311. type: String,
  312. default: ""
  313. },
  314. courseType: {
  315. type: Number,
  316. default: 0
  317. },
  318. taskCount: {
  319. type: Number,
  320. default: 0
  321. },
  322. worksStudent: {
  323. type: Array,
  324. default: () => []
  325. },
  326. fileList: {
  327. type: Array,
  328. default: () => []
  329. },
  330. videoStart: {
  331. type: Boolean,
  332. default: false
  333. }
  334. },
  335. data() {
  336. return {
  337. userid: this.$route.query.userid,
  338. courseId: this.$route.query.courseId,
  339. tcid2: this.$route.query.tcid,
  340. type: 0,
  341. itemType: 0, //0--无 1-搜索 2-任务 3-对话
  342. fileId: [],
  343. recordType: 0,
  344. recordLoading: false,
  345. fold: false,
  346. openMegaphone: false, //是否打开喇叭
  347. getFileIdLoading: false,
  348. AnnotationCanvasShow: false,
  349. canShowTips:false,
  350. showTipsLoading:false,
  351. tipsList:[],
  352. firstEnterTime:null,
  353. canGetTips:true,
  354. getTipsTimer:null,
  355. getWangLoading:false,
  356. canUseWangData:false,
  357. wangData:"",
  358. languageSetting:0,
  359. };
  360. },
  361. mounted() {
  362. let setting = this.courseDetail.setting;
  363. if(setting){
  364. setting = JSON.parse(setting);
  365. if(setting.languageSetting){
  366. this.languageSetting = setting.languageSetting;
  367. }
  368. }
  369. this.setWidth();
  370. this.getFileId();
  371. this.firstEnterTime = new Date().getTime();
  372. this.getWantSearch();
  373. setTimeout(()=>{
  374. this.canGetTips = true;
  375. this.getTipsList()
  376. },3000)
  377. },
  378. methods: {
  379. getTipsListTime(time = 5000){
  380. if(this.getTipsTimer)clearTimeout(this.getTipsListTime)
  381. this.getTipsTimer = setTimeout(()=>{
  382. this.canGetTips = true;
  383. this.getTipsList();
  384. this.getTipsTimer = null;
  385. },time)
  386. },
  387. getTipsList(){
  388. return new Promise((resolve)=>{
  389. if(!this.canGetTips)return;
  390. this.showTipsLoading = true;
  391. let nowTaskObj = this.navList[this.courseType].task[this.taskCount]
  392. let nowTask = `【任务${this.taskCount+1}:${nowTaskObj.taskName}】`
  393. let _textData = `课程名称:${this.courseDetail.title}\n分类:${this.courseDetail.name}\n\n`;
  394. let _chapters = JSON.parse(this.courseDetail.chapters);
  395. _chapters.forEach((i1, index1) => {
  396. if (i1.dyName) {
  397. _textData += `阶段${index1 + 1}:${i1.dyName}\n`;
  398. }
  399. i1.chapterInfo[0].taskJson.forEach((i2, index2) => {
  400. if (i2.task) {
  401. _textData += `任务${index2 + 1}:${i2.task}\n`;
  402. _textData += `${i2.taskDetail}\n`;
  403. }
  404. });
  405. _textData += "\n";
  406. });
  407. let _msg = `Language: ${this.getLang()}
  408. ATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced "Format example".
  409. Instruction: Based on the context, follow "Format example", write content
  410. #Context
  411. ## 任务
  412. 你是专业的教学评估员,擅长分析课堂动态和学生的认知状态,能够根据教师的当前思路提供有效的教学建议。
  413. 你需要根据我提供的信息,推测分析,之后给出3个具体的操作步骤的建议。
  414. ##工作流程
  415. 1. 仔细阅读并分析我提供的课堂信息或课堂实录内容。
  416. 2. 观察课堂上学生的行为和反应,推测他们的认知状态。
  417. 3. 通过教师的言行和教学安排,推测教师当前的思路和教学目标。
  418. 4. 根据教师的思路和学生的认知状态,提出一个教学建议,并给出3个具体的操作步骤。只需要描述对应的动作,不需要指出该动作的目标和作用。
  419. ##输出
  420. ###输出要求
  421. 1. 最终输出仅包含具体操作步骤。
  422. 2. 具体操作步骤以数组形式输出,包含3个步骤。
  423. 3. 每个步骤用一句话描述,详细一些
  424. ##课堂内容
  425. 当前进行到:${nowTask}
  426. ##Format example
  427. ["步骤的描述","步骤的描述","步骤的描述"]
  428. ${_textData}
  429. `
  430. let params = {
  431. // assistant_id: "6063369f-289a-11ef-8bf4-12e77c4cb76b",
  432. // userId: this.userid,
  433. // message: [{ type: "text", text: _msg }],
  434. // session_name: uuidv4(),
  435. // // uid: _uuid,
  436. // file_ids: this.fileId,
  437. // model: "gpt-4o-mini",
  438. model: "gpt-4o-mini",
  439. temperature: 0,
  440. max_tokens: 4096,
  441. top_p: 1,
  442. frequency_penalty: 0,
  443. presence_penalty: 0,
  444. messages: [{ role: "user", content: _msg }],
  445. uid: uuidv4(),
  446. mind_map_question: "",
  447. stream: false,
  448. };
  449. this.ajax
  450. // .post("https://gpt4.cocorobo.cn/chat", params)
  451. // .post("https://claude3.cocorobo.cn/chat", params)
  452. .post("https://gpt4.cocorobo.cn/chat", params)
  453. .then(res => {
  454. let _data = res.data.FunctionResponse.choices[0].message.content;
  455. _data = _data.replaceAll("```json", "").replaceAll("```", "");
  456. console.log(_data)
  457. const match = _data.match(/\[\s*[^]*\s*\]/);
  458. let _result = JSON.parse(match[0]) || [];
  459. this.tipsList.push(_result)
  460. this.showTipsLoading = false;
  461. this.canShowTips = true;
  462. this.canGetTips = false;
  463. resolve()
  464. })
  465. .catch(e => {
  466. this.showTipsLoading = false;
  467. this.$message.error("获取课堂小贴士失败");
  468. console.log(e);
  469. });
  470. })
  471. },
  472. handleBlur(){
  473. // console.log(this.fold)
  474. this.fold = !this.fold;
  475. },
  476. changeAnnotationCanvasShow(newValue) {
  477. this.AnnotationCanvasShow = newValue;
  478. },
  479. startRecording() {
  480. this.$emit("startRecording");
  481. this.insertMemorandum(`使用<span class="btn">录制</span>功能,录制课堂`);
  482. },
  483. insertMemorandum(_html) {
  484. //保存行为操作
  485. //variable
  486. //btn
  487. let params = [
  488. {
  489. uid: this.userid,
  490. courseId: this.courseId+(this.tcid2?this.tcid2:""),
  491. content: _html
  492. }
  493. ];
  494. this.ajax
  495. .post(
  496. this.$store.state.api + "insert_systemOperation_countdownBehavior",
  497. params
  498. )
  499. .then(res => {
  500. if (res.data == 1) {
  501. console.log("保存操作成功");
  502. } else {
  503. console.log("保存操作失败");
  504. }
  505. })
  506. .catch(e => {
  507. console.log("保存操作失败");
  508. console.log(e);
  509. });
  510. },
  511. setWidth() {
  512. let w = this.$refs.ch_box;
  513. let w2 = w.offsetWidth + 30 + "px";
  514. this.$emit("setWidth", w2);
  515. },
  516. openSetting() {
  517. this.type = this.type == 1 ? 0 : 1;
  518. this.$nextTick(() => {
  519. if(this.type==1){
  520. this.$parent.mlDialog = false
  521. }
  522. this.setWidth();
  523. });
  524. },
  525. changeItemType(type) {
  526. this.type = 0;
  527. if(this.itemType==type){
  528. this.itemType = 0;
  529. this.type = 1;
  530. this.openSetting();
  531. return;
  532. }
  533. this.openSetting();
  534. // this.$message.info("切换到"+type)
  535. this.$nextTick(() => {
  536. // if (this.itemType == 1 && type != 1) {
  537. // this.$refs.searchAreaRef.scrollBottom();
  538. // this.$refs.searchAreaRef.getWantSearch();
  539. // } else if (this.itemType == 2) {
  540. // this.$refs.taskAreaRef.scrollBottom();
  541. // } else if (this.itemType == 3) {
  542. // this.$refs.dialogAreaRef.scrollBottom();
  543. // }
  544. this.itemType = type;
  545. if (this.itemType == 4 && this.recordType == 1) {
  546. //关闭悬浮语音助手
  547. this.$refs.levitatedSphereRef.stopRecord();
  548. }
  549. if (this.itemType == 3) {
  550. this.insertMemorandum(`打开<span class="btn">倒计时</span>面板`);
  551. }
  552. });
  553. },
  554. //计时
  555. startTime(time) {
  556. this.$refs.timepieceRef.startTime(time);
  557. },
  558. getFileId() {
  559. if (this.getFileIdLoading) return;
  560. this.getFileIdLoading = true;
  561. this.fileId = [];
  562. let _this = this;
  563. let _successFileUrl = [];
  564. if (this.fileList.length <= 0) return;
  565. let addType = ["DOCX", "DOC", "PPT", "PPTX", "MD", "TXT", "PDF"];
  566. this.fileList.forEach(i => {
  567. if (
  568. addType.includes(
  569. i.url.split(".")[i.url.split(".").length - 1].toLocaleUpperCase()
  570. )
  571. ) {
  572. _successFileUrl.push(i.url);
  573. }
  574. });
  575. let promiseList = [];
  576. _successFileUrl.forEach(i => {
  577. promiseList.push(
  578. new Promise((resolve, reject) => {
  579. _this.ajax
  580. .put("https://gpt4.cocorobo.cn/upload_file_knowledge", {
  581. url: i
  582. })
  583. .then(res => {
  584. let _data = res.data.FunctionResponse;
  585. if (_data.result && _data.result.id) {
  586. this.fileId.push(_data.result.id);
  587. }
  588. resolve();
  589. });
  590. })
  591. );
  592. });
  593. Promise.all(promiseList).then(res => {
  594. this.getFileIdLoading = false;
  595. });
  596. },
  597. startAssistant() {
  598. if (this.recordLoading) return this.$message.info("请稍等...");
  599. this.recordLoading = true;
  600. if (this.recordType == 0) {
  601. if (this.itemType == 4) {
  602. this.itemType = 0;
  603. this.type = 0;
  604. }
  605. // this.$message.info("开启")
  606. // this.changeRecordType(1)
  607. this.$refs.levitatedSphereRef.recordStart();
  608. } else if (this.recordType == 1) {
  609. // this.$message.info("关闭")
  610. this.$refs.levitatedSphereRef.stopTwo();
  611. // this.changeRecordType(0)
  612. }
  613. },
  614. changeMegaphone() {
  615. this.openMegaphone = !this.openMegaphone;
  616. if (this.openMegaphone) {
  617. this.$message.success("已开启AI语音");
  618. } else {
  619. this.$message.success("已关闭AI语音");
  620. }
  621. },
  622. // 展开
  623. changeFold(newValue) {
  624. // this.$message.info("展开");
  625. this.fold = newValue;
  626. this.$nextTick(()=>{
  627. let e1 = this.$refs.foldBtnRef.getBoundingClientRect();
  628. let e2 = this.$refs.ch_box.getBoundingClientRect();
  629. console.log('👇👇')
  630. console.log(e1.top - e2.top)
  631. this.$refs.itemFoldRef.style.top = e1.top - e2.top + "px";
  632. })
  633. },
  634. // 收起
  635. changeUnfold(newValue) {
  636. // this.$message.info("收起");
  637. this.fold = newValue;
  638. },
  639. // 语音识别
  640. startRecord() {
  641. this.$refs.levitatedSphereRef.startRecord();
  642. },
  643. stopRecord() {
  644. this.$refs.levitatedSphereRef.stopTwo();
  645. },
  646. // 语音合成
  647. startSpeak() {
  648. },
  649. changeRecordType(type) {
  650. this.recordLoading = false;
  651. this.recordType = type;
  652. },
  653. commentAndAnnotate() {
  654. if(this.AnnotationCanvasShow){
  655. this.$refs.AnnotationCanvasRef.close()
  656. }else{
  657. this.$refs.AnnotationCanvasRef.open();
  658. this.insertMemorandum(`开始使用<span class="btn">批注</span>功能`);
  659. }
  660. },
  661. endCommentAndAnnotate() {
  662. this.insertMemorandum(`结束使用<span class="btn">批注</span>功能`);
  663. },
  664. getWantSearch() {
  665. console.log("获取猜你想搜")
  666. let _uuid = uuidv4();
  667. this.getWangLoading = true
  668. this.canUseWangData = false
  669. this.wangData=""
  670. let _msg = `
  671. Language: ${this.getLang()}
  672. ATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced "Format example".
  673. Instruction: Based on the context, follow "Format example", write content
  674. ## 任务
  675. 你的任务是根据“课程信息”,提供用户需要的搜索建议,将搜索建议的结果以有序列表的形式返回给用户。
  676. ## 课程信息
  677. #### 课程标题:${this.courseDetail.title ? this.courseDetail.title : ""}
  678. #### 分类:${this.courseDetail.name ? this.courseDetail.name : "无"}
  679. #### 学生年级:${
  680. this.courseDetail.classname ? this.courseDetail.classname : "无"
  681. }
  682. ## 规则
  683. 输出结果基于“课程信息”,避免提供无关的信息。
  684. 搜索建议的结果符合伦理规范。
  685. ## 输出
  686. 输出应包括6个相关的搜索建议,每个搜索建议需要以问号的方式结束。
  687. 请一步步思考如何根据现有信息推送搜索建议,但是不需要输出搜索建议以外的内
  688. ## 输出格式
  689. 搜索建议应以有序列表形式呈现,每个建议包括关键词和简短描述。输出JSON格式的
  690. ## Format example
  691. [{"index": 1,"title": "垃圾分类标准","label": "不同国家的垃圾分类标准和方法?"},{"index": 2,"title":"可回收垃圾处理","label": "可回收垃圾的处理流程和再利用方法?"},{ "index": 3, "title": "有害垃圾的影响", "label": "有害垃圾对环境和人体健康的潜在影响?"},{ "index": 4, "title": "垃圾分类标准", "label": "不同国家的垃圾分类标准和方法?"},{ "index": 5, "title": "可回收垃圾处理", "label": "可回收垃圾的处理流程和再利用方法?"},{ "index": 6, "title": "有害垃圾的影响", "label": "有害垃圾对环境和人体健康的潜在影响?"}]
  692. `;
  693. // let params = {
  694. // model: "gpt-3.5-turbo",
  695. // temperature: 0,
  696. // max_tokens: 4096,
  697. // top_p: 1,
  698. // frequency_penalty: 0,
  699. // presence_penalty: 0,
  700. // messages: [{ role: "user", content: _msg }],
  701. // uid: _uuid,
  702. // mind_map_question: ""
  703. // };
  704. let params = {
  705. assistant_id: "6063369f-289a-11ef-8bf4-12e77c4cb76b",
  706. userId: this.userid,
  707. message: [{ type: "text", text: _msg }],
  708. session_name: _uuid,
  709. // uid: _uuid,
  710. file_ids: this.fileId,
  711. model: "gpt-4o-mini"
  712. };
  713. // let params = {
  714. // message: {
  715. // anthropic_version: "bedrock-2023-05-31",
  716. // max_tokens: 4096,
  717. // temperature: 0,
  718. // top_p: 1,
  719. // messages: [{ role: "user", content: _msg }]
  720. // },
  721. // uid: _uuid,
  722. // model: "Claude 3 Sonnet" // Claude 3 Sonnet或者Claude 3 Haiku
  723. // };
  724. this.text = "";
  725. this.ajax
  726. // .post("https://gpt4.cocorobo.cn/chat", params)
  727. // .post("https://claude3.cocorobo.cn/chat", params)
  728. .post("https://gpt4.cocorobo.cn/ai_agent_park_chat", params)
  729. .then(res => {
  730. // console.log(res);
  731. let _data = res.data.FunctionResponse.message;
  732. _data = _data.replaceAll("```json", "").replaceAll("```", "");
  733. const match = _data.match(/\[\s*{[^]*}\s*\]/);
  734. this.wangData = match[0];
  735. this.canUseWangData = true;
  736. this.getWangLoading = false;
  737. // console.log(_data);
  738. // console.log(match);
  739. // this.chatList.find(i => i.uid == _uuid).aiContent = JSON.parse(
  740. // match[0]
  741. // );
  742. // this.chatList.find(i => i.uid == _uuid).isalltext = true;
  743. // this.chatList.find(i => i.uid == _uuid).isShowSynchronization = true;
  744. // this.chatList.find(i => i.uid == _uuid).loading = false;
  745. // this.scrollBottom();
  746. // this.chatLoading = false;
  747. })
  748. .catch(e => {
  749. this.chatLoading = false;
  750. this.canUseWangData = false;
  751. this.getWangLoading = false;
  752. console.log(e);
  753. });
  754. // this.getWAntSearchContent(_uuid);
  755. },
  756. getLang(){
  757. let lang = ''
  758. if(this.languageSetting == 0){
  759. lang = 'Chinese.'
  760. }else if(this.languageSetting == 1){
  761. lang = 'Traditional Chinese.'
  762. }else if(this.languageSetting == 2){
  763. lang = 'English.'
  764. }
  765. return lang
  766. },
  767. getWanData(){
  768. if(this.wangData && this.canUseWangData){
  769. const _result = this.wangData;
  770. this.wangData = "";
  771. this.canUseWangData = false;
  772. this.getWantSearch();
  773. return _result;
  774. }
  775. }
  776. }
  777. };
  778. </script>
  779. <style scoped>
  780. .ch_box {
  781. width: auto;
  782. background: rgb(255, 255, 255);
  783. position: fixed;
  784. height: calc(100% - 40px);
  785. border-radius: 10px;
  786. box-sizing: border-box;
  787. right: 20px;
  788. display: flex;
  789. top: 20px;
  790. z-index: 1000;
  791. }
  792. .ch_nav_box {
  793. height: 100%;
  794. width: 65px;
  795. display: flex;
  796. flex-direction: column;
  797. align-items: center;
  798. overflow-y: auto !important; /* 上下溢出显示滚动条 */
  799. overflow-x: hidden !important; /* 左右溢出正常溢出 */
  800. position: relative;
  801. }
  802. .ch_content_box {
  803. width: 400px;
  804. height: 100%;
  805. border-right: 2px solid #e7e7e7;
  806. }
  807. .ch_nav_box_bottom {
  808. width: 100%;
  809. box-sizing: border-box;
  810. border-top: solid 1px #eaeaea;
  811. display: flex;
  812. flex-direction: column;
  813. }
  814. .ch_nav_box_middle {
  815. width: 100%;
  816. box-sizing: border-box;
  817. border-top: solid 1px #eaeaea;
  818. flex-direction: column;
  819. justify-content: space-between;
  820. }
  821. .ch_nav_box_middle_item {
  822. width: 100%;
  823. height: 80px;
  824. display: flex;
  825. flex-direction: column;
  826. justify-content: center;
  827. align-items: center;
  828. cursor: pointer;
  829. transition: 0.3s;
  830. font-size: 14px;
  831. }
  832. .ch_nav_box_middle_item_active {
  833. background-color: #3681fc;
  834. color: white;
  835. }
  836. .ch_nav_box_middle_item > img {
  837. width: 24px;
  838. height: 24px;
  839. margin-bottom: 5px;
  840. }
  841. .ch_nav_box_bottom > div {
  842. width: 100%;
  843. height: 65px;
  844. display: flex;
  845. flex-direction: column;
  846. justify-content: center;
  847. align-items: center;
  848. cursor: pointer;
  849. }
  850. .ch_nav_box_bottom > div > img {
  851. width: 24px;
  852. height: 24px;
  853. }
  854. .ch_nav_box_top {
  855. width: 100%;
  856. height: auto;
  857. margin-top: auto;
  858. }
  859. .ch_nav_box_top > div {
  860. width: 100%;
  861. height: 65px;
  862. display: flex;
  863. justify-content: center;
  864. align-items: center;
  865. cursor: pointer;
  866. position: relative;
  867. }
  868. .ch_nav_box_top > div > img {
  869. width: 24px;
  870. height: 24px;
  871. transition: 0.3s;
  872. }
  873. .itemFold {
  874. position: absolute;
  875. width: 130px;
  876. right: 65px;
  877. top: 0;
  878. background: rgb(255, 255, 255);
  879. box-sizing: border-box;
  880. border: solid 1px #eaeaea;
  881. /* border-top: solid 1px #eaeaea; */
  882. z-index: 1000; /* 确保二级菜单在主菜单上层 */
  883. border-radius: 10px;
  884. box-sizing: border-box;
  885. padding:10px;
  886. display:flex;
  887. flex-direction: column;
  888. justify-content: center;
  889. align-items: center;
  890. }
  891. .itemFold > div {
  892. width: 100%;
  893. height: 45px;
  894. display: flex;
  895. justify-content: center;
  896. align-items: center;
  897. cursor: pointer;
  898. margin: 5px 0;
  899. border-radius: 10px;
  900. }
  901. .itemFold > div:hover{
  902. background:#F3F7FD;
  903. }
  904. .itemFold > div > img {
  905. width: 24px;
  906. height: 24px;
  907. }
  908. .itemFold > div > span{
  909. margin-left:10px;
  910. }
  911. @media screen and (max-height: 820px) {
  912. .ch_nav_box_bottom > div {
  913. height: auto;
  914. padding: 8px 0;
  915. }
  916. .ch_nav_box_middle_item{
  917. height: auto;
  918. padding: 8px 0;
  919. }
  920. .ch_nav_box_top > div{
  921. height: auto;
  922. padding: 8px 0;
  923. }
  924. }
  925. .foldActive{
  926. background-color: #F0F2F5;
  927. padding: 10px;
  928. border-radius: 10px;
  929. }
  930. </style>