analysisItem.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  1. <template>
  2. <div class="outcomeCon">
  3. <div @click="showItem" style="height: 50px;display: flex; justify-content: space-between;align-items: center;">
  4. <div class="oc_title">
  5. <van-icon v-if="!show" name="arrow" />
  6. <van-icon v-else name="arrow-down" />
  7. <span class="tit">{{ data.jsonData ? data.jsonData.name : '' }}</span>
  8. </div>
  9. <van-popover placement="bottom-end" v-model="abuShow" v-if="[0].includes(status)">
  10. <div class="abu">
  11. <!-- <div class="abuBtn">
  12. <img src="../../assets/images/classObserve/addTel.png" alt="" />
  13. <span>重命名</span>
  14. </div> -->
  15. <div class="abuBtn" @click.stop="del()">
  16. <img src="../../../assets/images/classObserve/del.png" alt="" />
  17. <span>删除</span>
  18. </div>
  19. </div>
  20. <template #reference>
  21. <img @click.stop="abuShowItem" src="../../../assets/images/classObserve/colD.png" alt="" />
  22. </template>
  23. </van-popover>
  24. <div class="oc_status" v-if="[1,2,3].includes(status)">
  25. <div v-if="[1].includes(status)">
  26. <img src="../../../assets/images//classObserve/generate.svg">
  27. <span>优化中...</span>
  28. </div>
  29. <div v-if="[2].includes(status)">
  30. <span style="color:#e60012;">生成失败</span>
  31. </div>
  32. <div v-if="[3].includes(status)">
  33. <img src="../../../assets/images//classObserve/success.svg">
  34. <span style="color:#17C469">生成成功</span>
  35. </div>
  36. </div>
  37. </div>
  38. <div v-show="show">
  39. <div class="brief">
  40. {{ data.jsonData ? data.jsonData.result : '' }}
  41. </div>
  42. <div class="content">
  43. <!-- {{ data.jsonData }} -->
  44. <mdView :text="data.jsonData ? data.jsonData.content : ''" />
  45. </div>
  46. <div class="outcomeBtn" v-if="[0,2,3].includes(status)">
  47. <div @click.stop="down()" :class="[historyIndex==0?'ob_no':'']">
  48. <img src="../../../assets/images/classObserve/restore.png" alt="" style="transform:rotateY(180deg);"/><span>撤销</span>
  49. </div>
  50. <div @click.stop="up()" :class="[(historyIndex>=history.length-1)?'ob_no':'']">
  51. <img src="../../../assets/images/classObserve/restore.png" alt="" /><span>恢复</span>
  52. </div>
  53. <div @click.stop="optimize()">
  54. <img src="../../../assets/images/classObserve/optimize.png" alt="" /><span>优化</span>
  55. </div>
  56. </div>
  57. </div>
  58. </div>
  59. </template>
  60. <script>
  61. import mdView from './mdView.vue'
  62. import { v4 as uuidv4 } from "uuid";
  63. import {chatRequest,updateObsRequest} from '@/api/classObserve'
  64. export default {
  65. props: {
  66. data: {
  67. type: Object,
  68. default: () => {}
  69. },
  70. analysisList:{
  71. type:Array,
  72. default:()=>[]
  73. },
  74. bmData:{
  75. type:Object,
  76. default:()=>{}
  77. },
  78. fileId:{
  79. type:String,
  80. default:""
  81. }
  82. },
  83. components: {
  84. mdView
  85. },
  86. data() {
  87. return {
  88. show: false,
  89. abuShow: false,
  90. historyIndex:0,
  91. history:[],
  92. userId:this.$store.state.user.id,
  93. status:0,// 0:正常 1:优化中 2:优化成功 3:优化失败
  94. }
  95. },
  96. methods: {
  97. showItem() {
  98. if(![0,2,3].includes(this.status))return this.$toast("正在优化中...")
  99. this.show = !this.show
  100. this.status = 0;
  101. },
  102. abuShowItem() {
  103. this.abuShow = true
  104. },
  105. up() {
  106. if(this.historyIndex < this.history.length - 1){
  107. this.historyIndex++;
  108. this.data.jsonData = JSON.parse(JSON.stringify(this.history[this.historyIndex]))
  109. this.data.json_data = JSON.stringify(this.data.jsonData)
  110. this.saveData(this.data).then(_=>{
  111. console.log('保存成功')
  112. })
  113. }
  114. },
  115. down() {
  116. if(this.historyIndex>0){
  117. this.historyIndex--;
  118. this.data.jsonData = JSON.parse(JSON.stringify(this.history[this.historyIndex]))
  119. this.data.json_data = JSON.stringify(this.data.jsonData)
  120. this.saveData(this.data).then(_=>{
  121. console.log('保存成功')
  122. })
  123. }
  124. },
  125. optimize() {
  126. this.show = false;
  127. this.status = 1;
  128. let assistant = this.analysisList.find(
  129. (i) => i.title == this.data.jsonData.name
  130. );
  131. if (!assistant.value) {
  132. this.status = 2;
  133. return this.$toast.fail("未找到对应的AI助手");
  134. }
  135. let _msg = `使用文件检索的方式完整的去分析文件内容,并请完全按照要求输出。`
  136. if(['st1','st2','st3'].includes(assistant.value))return this.optimize2(assistant.value)
  137. if(assistant.value=='6b4a9650-48be-11ef-936b-12e77c4cb76b'){
  138. _msg = `使用文件检索的方式完整的去分析文件内容,并基于以下的课堂基本内容,使用cpote课程设计模型改编一堂同主题的课程。
  139. 课堂名称:${this.bmData.courseName} 搜课年级:${this.bmData.grade} 授课科目:${this.bmData.subject}`
  140. }
  141. let params = {
  142. assistant_id: assistant?assistant.value:null,
  143. message:_msg,
  144. session_name: uuidv4(),
  145. userId: this.userId,
  146. file_ids: this.fileId?[this.fileId]:'',
  147. model: "gpt-4o-2024-08-06",
  148. };
  149. chatRequest(params).then(res=>{
  150. let _data = res.FunctionResponse;
  151. let _copyData = JSON.parse(JSON.stringify(this.data));
  152. _copyData.jsonData.content = _data.message;
  153. _copyData.jsonData.dataFileList = [];
  154. _copyData.jsonData.fileList = [];
  155. _copyData.json_data = JSON.stringify(_copyData.jsonData);
  156. if (this.history.length == 0) {
  157. this.history.push(_copyData.jsonData);
  158. } else {
  159. this.history.splice(
  160. this.historyIndex + 1,
  161. 0,
  162. _copyData.jsonData
  163. );
  164. }
  165. this.up();
  166. this.status = 3;
  167. }).catch(e=>{
  168. console.log(e)
  169. this.status = 2;
  170. })
  171. },
  172. optimize2(value){
  173. this.status = 2;
  174. return console.log('特殊处理');
  175. if(this.bmData.jsonData.editorBarData.type != 0){
  176. this.status = 2;
  177. return this.$toast.fail("请上传表格形式的转录文稿");
  178. }
  179. try {
  180. let _result = [];
  181. let _data = this.bmData.jsonData.editorBarData.content;
  182. let _div = document.createElement("div");
  183. _div.innerHTML = _data;
  184. let _tableRows = _div.querySelectorAll(`table tbody tr`);
  185. _tableRows.forEach((i, index) => {
  186. if (index == 0) return;
  187. if(!i.cells[0].innerText)return;
  188. let obj = {
  189. index: i.cells[0].innerText,
  190. startTime: i.cells[1].innerText,
  191. endTime: i.cells[2].innerText,
  192. message: i.cells[3].innerText,
  193. time: i.cells[4].innerText,
  194. role: i.cells[5].innerText,
  195. behavior: i.cells[6].innerText,
  196. };
  197. _result.push(obj);
  198. });
  199. if (_result.length == 0){
  200. this.status = 2;
  201. return this.$toast.fail("未找到表格数据")
  202. };
  203. if (value=='st1') {
  204. this.getTimeAllocationData(_result);
  205. } else if (value=='st2') {
  206. this.getInteractionAnalysisData(_result);
  207. } else if (value=='st3') {
  208. this.getTeachingModeData(_result);
  209. } else {
  210. this.status = 2;
  211. return this.$toast.fail("未找到对应的分析");
  212. }
  213. } catch (e) {
  214. console.log(e)
  215. return this.status = 2;
  216. }
  217. },
  218. // 课堂时间分配
  219. getTimeAllocationData(_dataList) {
  220. this.status = 1;
  221. let _data = _dataList.reduce((pre,cur)=>{
  222. if(cur.role == '学生'){
  223. pre[1].value += this.convertToSeconds(cur.time)
  224. }else if(cur.role=='老师'){
  225. pre[0].value += this.convertToSeconds(cur.time)
  226. }
  227. return pre;
  228. },[
  229. {value:0,name:"老师"},
  230. {value:0,name:'学生'}
  231. ])
  232. const _option = {
  233. tooltip: {
  234. trigger: "item",
  235. formatter: "{a} <br/>{b}: {d}%" // {a}为系列名,{b}为数据名,{d}为百分比
  236. },
  237. legend: {
  238. top: "5%",
  239. left: "center",
  240. },
  241. series: [
  242. {
  243. name: "课堂时间分配",
  244. type: "pie",
  245. radius: ["40%", "70%"],
  246. avoidLabelOverlap: false,
  247. label: {
  248. show: false,
  249. position: "center",
  250. },
  251. emphasis: {
  252. label: {
  253. show: true,
  254. fontSize: 40,
  255. fontWeight: "bold",
  256. // formatter: "{b}: {d}%" // 显示百分比
  257. },
  258. },
  259. labelLine: {
  260. show: false,
  261. },
  262. data: _data
  263. },
  264. ],
  265. };
  266. let _copyData = JSON.parse(JSON.stringify(this.data));
  267. _copyData.jsonData.eChartData = _option;
  268. _copyData.json_data = JSON.stringify(_copyData.jsonData);
  269. if (this.history.length == 0) {
  270. this.history.push(_copyData.jsonData);
  271. } else {
  272. this.history.splice(this.historyIndex + 1, 0, _copyData.jsonData);
  273. }
  274. this.up();
  275. this.status = 3;
  276. },
  277. // 师生互动分析
  278. getInteractionAnalysisData(_dataList) {
  279. this.status = 1;
  280. let _pushData = [0,0]
  281. let _result = []
  282. _dataList.forEach(i=>{
  283. if(i.role=='老师'){
  284. _pushData[0] += this.convertToSeconds(i.time)
  285. }else if(i.role=='学生'){
  286. _pushData[1] += this.convertToSeconds(i.time)
  287. }
  288. return _result.push(JSON.parse(JSON.stringify(_pushData)))
  289. })
  290. let _flatArray = _result.flat();
  291. const _max = Math.max(..._flatArray)
  292. const _maxValue =Math.ceil(_max / 100) * 100;
  293. const _option = {
  294. xAxis: {
  295. name: "老师", // X轴标题
  296. nameLocation: "end", // 标题位置
  297. scale: true,
  298. min: 0,
  299. max:_maxValue
  300. },
  301. yAxis: {
  302. name: "学生", // Y轴标题
  303. nameLocation: "end", // 标题位置
  304. scale: true,
  305. min: 0,
  306. max:_maxValue
  307. },
  308. grid: {
  309. containLabel: true
  310. },
  311. series: [
  312. {
  313. name: "数据",
  314. step: "start",
  315. data: _result,
  316. type: "line",
  317. },
  318. {
  319. name: "对角线",
  320. type: "line",
  321. data: [[0, 0], [_maxValue, _maxValue]],
  322. lineStyle: {
  323. type: "dashed"
  324. },
  325. markLine: {
  326. "symbol": ["none", "none"]
  327. }
  328. }
  329. ],
  330. };
  331. let _copyData = JSON.parse(JSON.stringify(this.data));
  332. _copyData.jsonData.eChartData = _option;
  333. _copyData.json_data = JSON.stringify(_copyData.jsonData);
  334. if (this.history.length == 0) {
  335. this.history.push(_copyData.jsonData);
  336. } else {
  337. this.history.splice(this.historyIndex + 1, 0, _copyData.jsonData);
  338. }
  339. this.up();
  340. this.status = 3;
  341. },
  342. // 教学模式分析
  343. getTeachingModeData(_dataList) {
  344. this.status = 1;
  345. let _continuousTime = 0;
  346. let _totalTime = 0;
  347. let _continuousRole = "老师"
  348. let _teacherTime = 0;
  349. console.log(_dataList)
  350. _dataList.forEach((item,index)=>{
  351. if(index==0){//第一个
  352. _continuousRole = item.role
  353. }else if(_dataList.length-1==index){//最后一个
  354. if(_continuousRole==item.role){//连续对话了
  355. _continuousTime += this.convertToSeconds(_dataList[index-1].time)
  356. _continuousTime += this.convertToSeconds(item.time)
  357. }else{//没连续对话
  358. if(index>=2){
  359. if(_dataList[index-2].role==_dataList[index-1].role){
  360. _continuousTime += this.convertToSeconds(_dataList[index-1].time)
  361. }else{
  362. _continuousRole = item.role;
  363. }
  364. }else{
  365. _continuousRole = item.role;
  366. }
  367. }
  368. }else{
  369. if(_continuousRole==item.role){//连续对话了
  370. _continuousTime += this.convertToSeconds(_dataList[index-1].time)
  371. }else{//没连续对话
  372. if(index>=2){
  373. if(_dataList[index-2].role==_dataList[index-1].role){
  374. _continuousTime += this.convertToSeconds(_dataList[index-1].time)
  375. }else{
  376. _continuousRole = item.role;
  377. }
  378. }else{
  379. _continuousRole = item.role;
  380. }
  381. }
  382. }
  383. if(item.role == "老师"){
  384. _teacherTime+=this.convertToSeconds(item.time);
  385. }
  386. _totalTime += this.convertToSeconds(item.time);
  387. })
  388. let _RT = (_teacherTime/_totalTime).toFixed(2)
  389. let _CH = (_continuousTime/_totalTime).toFixed(2)
  390. let _copyData = JSON.parse(JSON.stringify(this.data));
  391. _copyData.jsonData.RT = _RT;
  392. _copyData.jsonData.CH = _CH;
  393. _copyData.json_data = JSON.stringify(_copyData.jsonData);
  394. if (this.history.length == 0) {
  395. this.history.push(_copyData.jsonData);
  396. } else {
  397. this.history.splice(this.historyIndex + 1, 0, _copyData.jsonData);
  398. }
  399. this.up();
  400. this.status = 3;
  401. },
  402. saveData(params){
  403. return new Promise((resolve, reject) => {
  404. updateObsRequest({
  405. id: params.id,
  406. json_data: JSON.stringify(params.jsonData)
  407. })
  408. .then(res => {
  409. resolve()
  410. })
  411. .catch(e => {
  412. console.log(e)
  413. console.log("保存失败")
  414. // this.$toast.fail('保存失败')
  415. resolve()
  416. })
  417. })
  418. },
  419. del() {
  420. this.$parent.delItem(this.data)
  421. },
  422. convertToSeconds(time) {
  423. let parts = time.split(':');
  424. let seconds = (+parts[0]) * 3600 + (+parts[1]) * 60 + (+parts[2]);
  425. return seconds;
  426. }
  427. },
  428. mounted(){
  429. this.historyIndex = 0;
  430. this.history.push(JSON.parse(JSON.stringify(this.data.jsonData)))
  431. },
  432. }
  433. </script>
  434. <style lang="scss" scoped>
  435. .outcomeCon {
  436. // display: flex;
  437. min-height: 50px;
  438. border: 0.5px #ccc solid;
  439. align-items: center;
  440. border-radius: 5px;
  441. min-height: 40px;
  442. padding: 0 10px;
  443. margin-bottom: 10px;
  444. }
  445. .abu {
  446. background-color: #fff;
  447. width: 100px;
  448. border-radius: 6px;
  449. // padding: 10px 10px;
  450. box-shadow: 0px 8px 10px -5px rgba(0, 0, 0, 0.08);
  451. box-shadow: 0px 16px 24px 2px rgba(0, 0, 0, 0.04);
  452. box-shadow: 0px 6px 30px 5px rgba(0, 0, 0, 0.05);
  453. .abuBtn {
  454. display: flex;
  455. align-items: center;
  456. padding: 10px 15px;
  457. font-size: 12px;
  458. font-weight: 400;
  459. img {
  460. margin-right: 5px;
  461. }
  462. }
  463. }
  464. .outcomeBtn {
  465. display: flex;
  466. justify-content: space-around;
  467. padding: 10px 0;
  468. .ob_no{
  469. opacity: .6;
  470. }
  471. div {
  472. display: flex;
  473. align-items: center;
  474. font-size: 12px;
  475. img {
  476. width: 18px;
  477. height: 18px;
  478. margin-right: 5px;
  479. }
  480. }
  481. }
  482. .brief {
  483. font-style: italic;
  484. color: #6b798e;
  485. font-size: 14px;
  486. }
  487. .oc_title {
  488. display: flex;
  489. align-items: center;
  490. .tit {
  491. font-size: 16px;
  492. font-weight: bold;
  493. margin-left: 10px;
  494. }
  495. }
  496. .oc_status{
  497. width: auto;
  498. height: 100%;
  499. display: flex;
  500. justify-content: flex-end;
  501. align-items: center;
  502. font-size: 14px;
  503. div{
  504. display: flex;
  505. justify-content: center;
  506. align-items: center;
  507. img{
  508. margin-right: 5px;
  509. width: 1.5em;
  510. height: 1.5em;
  511. }
  512. }
  513. }
  514. </style>