SanHQin 1 рік тому
батько
коміт
41ab3e7cb3

+ 2 - 0
package.json

@@ -18,6 +18,7 @@
     "axios": "^0.19.2",
     "core-js": "^3.6.4",
     "echarts": "^5.5.0",
+    "echarts-wordcloud": "^2.1.0",
     "element-ui": "^2.15.13",
     "image-conversion": "^2.1.1",
     "js-audio-recorder": "^1.0.7",
@@ -31,6 +32,7 @@
     "vconsole": "^3.3.4",
     "vue": "^2.6.11",
     "vue-calendar-component": "^2.8.2",
+    "vue-markdown": "^2.2.4",
     "vue-mobile-calendar": "^3.3.0",
     "vue-router": "^3.1.5",
     "vue-video-player": "^5.0.2",

+ 60 - 0
src/api/classObserve.js

@@ -0,0 +1,60 @@
+// axios
+import request2 from '@/utils/request2'
+import request from '@/utils/request'
+
+export function getCourseListRequest(data){//获取课堂列表数据
+	return request2({
+    url: 'https://gpt4.cocorobo.cn/get_classroom_observation_all',
+    method: 'post',
+    data,
+    hideloading: true
+  })
+}
+
+export function updateObsRequest(data){//保存分析数据
+	return request2({
+    url: 'https://gpt4.cocorobo.cn/update_classroom_observation',
+    method: 'post',
+    data,
+    hideloading: true
+  })
+}
+
+export function getObsRequest(data){//获取分析数据
+	return request2({
+    url: 'https://gpt4.cocorobo.cn/get_classroom_observation_new',
+    method: 'post',
+    data,
+    hideloading: true
+  })
+}
+
+export function delClassRequest(data){//删除
+	return request2({
+    url: 'https://gpt4.cocorobo.cn/update_classroom_observation_isdel',
+    method: 'post',
+    data,
+    hideloading: true
+  })
+}
+
+export function delObsRequest(data){//删除分析数据
+	return request2({
+    url: 'https://gpt4.cocorobo.cn/delete_classroom_observation',
+    method: 'post',
+    data,
+    hideloading: true
+  })
+}
+
+
+export function chatNoStreamRequest(data){//不用流式传输的chat
+	return request2({
+    url: 'https://gpt4.cocorobo.cn/chat',
+    method: 'post',
+    data,
+    hideloading: true
+  })
+}
+
+

+ 3 - 0
src/assets/images/classObserve/Union.svg

@@ -0,0 +1,3 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M11.025 12.975V18.75H12.975V12.975H18.75V11.025H12.975V5.25H11.025V11.025H5.25V12.975H11.025Z" fill="black" fill-opacity="0.9"/>
+</svg>

BIN
src/assets/images/classObserve/bmRefresh.png


+ 3 - 0
src/assets/images/classObserve/delFile.svg

@@ -0,0 +1,3 @@
+<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M6.97031 6.43595L4.9125 4.30469C4.75 4.13751 4.48437 4.13438 4.31718 4.29688C4.23593 4.37344 4.18906 4.47969 4.1875 4.59219C4.18437 4.70469 4.22812 4.81251 4.30468 4.8922L6.3625 7.02345L4.23125 9.08126C4.06406 9.24376 4.06093 9.50938 4.22343 9.67657C4.29999 9.75782 4.40625 9.8047 4.51875 9.80626C4.63125 9.80938 4.73906 9.76563 4.81875 9.68907L6.95 7.63126L9.00781 9.76251C9.17031 9.9297 9.43593 9.93282 9.60312 9.77032C9.68437 9.69376 9.73125 9.58751 9.73281 9.47501C9.73593 9.36251 9.69218 9.25469 9.61562 9.17501L7.55781 7.04376L9.68906 4.98595C9.85625 4.82345 9.85937 4.55782 9.69687 4.39063C9.62031 4.30938 9.51406 4.26251 9.40156 4.26095C9.28906 4.25782 9.18124 4.30157 9.10156 4.37813L6.97031 6.43595ZM2.44375 11.3938C0.0109327 8.87345 0.0812452 4.85938 2.6 2.42501C5.12031 -0.00780511 9.13437 0.0625074 11.5687 2.58126C14.0016 5.10157 13.9328 9.11563 11.4125 11.55C8.89218 13.9828 4.87812 13.9141 2.44375 11.3938Z" fill="#FFBBBB"/>
+</svg>

BIN
src/assets/images/classObserve/isVideo.png


BIN
src/assets/images/classObserve/startIcon.png


BIN
src/assets/images/classObserve/stopIcon.png


Різницю між файлами не показано, бо вона завелика
+ 6 - 0
src/assets/images/classObserve/videoFile.svg


+ 4 - 1
src/plugins/vant.js

@@ -28,7 +28,9 @@ import {
   DropdownMenu,
   DropdownItem,
   ActionSheet,
-  Popover
+  Popover,
+	Picker,
+
 } from 'vant'
 Vue.use(Button)
   .use(Cell)
@@ -58,3 +60,4 @@ Vue.use(Button)
   .use(DropdownItem)
   .use(ActionSheet)
   .use(Popover)
+	.use(Picker)

+ 4 - 4
src/store/modules/user.js

@@ -76,10 +76,10 @@ const actions = {
           resolve(_user.userid)
         })
         .catch(error => {
-          var _user = { userid: '5943e08c-b7d4-11ed-8d51-005056b86db5' }
-          commit('SET_ID', _user.userid)
-          setToken(_user.userid)
-          resolve(_user.userid)
+          // var _user = { userid: '6c56ec0e-2c74-11ef-bee5-005056b86db5' }
+          // commit('SET_ID', _user.userid)
+          // setToken(_user.userid)
+          // resolve(_user.userid)
           reject(error)
         })
     })

+ 66 - 0
src/utils/request2.js

@@ -0,0 +1,66 @@
+import axios from 'axios'
+import store from '@/store'
+import qs from 'qs'
+import { Toast } from 'vant'
+// 根据环境不同引入不同api地址
+// create an axios instance
+const service = axios.create({
+  // baseURL: 'https://gpt4.cocorobo.cn', // url = base api url + request url
+  // withCredentials: true // send cookies when cross-domain requests
+})
+
+// request拦截器 request interceptor
+service.interceptors.request.use(
+  config => {
+    // 不传递默认开启loading
+    if (!config.hideloading) {
+      // loading
+      Toast.loading({
+        forbidClick: true
+      })
+    }
+    if (config.method === 'post' && config.url.indexOf('gpt4.cocorobo.cn')==-1) {
+      config.data = qs.stringify(config.data) // 序列化post 参数
+    }
+		
+		if(config.url.indexOf('gpt4.cocorobo.cn')!=-1){
+			config.headers={'Content-Type': 'application/json'}
+      config.data = config.data // 序列化post 参数
+		}
+
+    if (store.getters.token) {
+      config.headers['X-Token'] = ''
+    }
+    return config
+  },
+  error => {
+    // do something with request error
+    console.log(error) // for debug
+    return Promise.reject(error)
+  }
+)
+// respone拦截器
+service.interceptors.response.use(
+  response => {
+    Toast.clear()
+    const res = response.data
+    if (res.status && res.status !== 200) {
+      // 登录超时,重新登录
+      if (res.status === 401) {
+        store.dispatch('FedLogOut').then(() => {
+          location.reload()
+        })
+      }
+      return Promise.reject(res || 'error')
+    } else {
+      return Promise.resolve(res)
+    }
+  },
+  error => {
+    Toast.clear()
+    console.log('err' + error) // for debug
+    return Promise.reject(error)
+  }
+)
+
+export default service

+ 765 - 54
src/views/classObserve/classInfo.vue

@@ -6,93 +6,263 @@
       <div class="cellB">
         <div class="tit">课程名称</div>
         <div>
-          <input type="text" placeholder="请输入课程名称" />
+          <input type="text" v-model="bmData.jsonData.courseName" placeholder="请输入课程名称" @change="changeData()" />
         </div>
       </div>
       <div class="cellB">
         <div class="tit">学生人数</div>
         <div>
-          <input type="text" placeholder="请输入" />
+          <input
+            type="number"
+            v-model="bmData.jsonData.studentNum"
+            placeholder="请输入学生人数"
+            @change="changeData()"
+          />
         </div>
       </div>
       <div class="cellB">
         <div class="tit">授课老师</div>
         <div>
-          <input type="text" placeholder="请输入" />
+          <input
+            type="text"
+            v-model="bmData.jsonData.teacherName"
+            placeholder="请输入授课老师"
+            @change="changeData()"
+          />
         </div>
       </div>
       <div class="cellB">
         <div class="tit">授课时间</div>
-        <div @click="show = true">
-          <span v-if="!changeTime">请选择</span>
-          <span v-else>{{ changeTime }}</span>
+        <div @click="showTime = true">
+          <span v-if="!bmData.jsonData.time">请选择</span>
+          <span v-else>{{ bmData.jsonData.time }}</span>
           <van-icon name="arrow" />
         </div>
       </div>
       <div class="cellB">
         <div class="tit">授课年级</div>
-        <div @click="show = true">
-          <span v-if="!currentDate">请选择</span>
-          <span v-else></span>
+        <div @click="showGrade = true">
+          <span v-if="!bmData.jsonData.grade">请选择</span>
+          <span v-else>{{ bmData.jsonData.grade }}</span>
           <van-icon name="arrow" />
         </div>
       </div>
       <div class="cellB">
         <div class="tit">授课科目</div>
-        <div @click="show = true">
-          <span v-if="!currentDate">请选择</span>
-          <span v-else></span>
+        <div @click="showSubject = true">
+          <span v-if="!bmData.jsonData.subject">请选择</span>
+          <span v-else>{{ bmData.jsonData.subject }}</span>
           <van-icon name="arrow" />
         </div>
       </div>
       <div class="cellB">
         <div class="tit">教材版本</div>
-        <div @click="show = true">
+        <div>
+          <input type="text" v-model="bmData.jsonData.textbook" placeholder="请输入教程版本" @change="changeData()" />
+        </div>
+        <!-- <div @click="show = true">
           <span v-if="!currentDate">请选择</span>
           <span v-else></span>
           <van-icon name="arrow" />
-        </div>
+        </div> -->
       </div>
-      <div class="cellC">
+      <div class="cellC" v-loading="imageLoading">
         <div class="tit">课堂图片</div>
-        <div>
-          <img src="https://ccrb.s3.cn-northwest-1.amazonaws.com.cn/default%2FRectangle+15471721807777029.png" alt="" />
+        <div v-if="imageList.jsonData.fileList1.length">
+          <van-image
+            @click.stop="previewImage(imageList.jsonData.fileList1[0].url)"
+            width="2rem"
+            height="2rem"
+            fit="cover"
+            :src="imageList.jsonData.fileList1[0].url"
+          />
+          <span class="setting" @click.stop="delImage('fileList1')">
+            <img src="../../assets/images/classObserve/delFile.svg" />
+          </span>
+        </div>
+
+        <div v-if="imageList.jsonData.fileList2.length">
+          <van-image
+            @click.stop="previewImage(imageList.jsonData.fileList2[0].url)"
+            width="2rem"
+            height="2rem"
+            fit="cover"
+            :src="imageList.jsonData.fileList2[0].url"
+          />
+          <span class="setting" @click.stop="delImage('fileList2')">
+            <img src="../../assets/images/classObserve/delFile.svg" />
+          </span>
+        </div>
+
+        <div v-if="imageList.jsonData.fileList3.length">
+          <van-image
+            @click.stop="previewImage(imageList.jsonData.fileList3[0].url)"
+            width="2rem"
+            height="2rem"
+            fit="cover"
+            :src="imageList.jsonData.fileList3[0].url"
+          />
+          <span class="setting" @click.stop="delImage('fileList3')">
+            <img src="../../assets/images/classObserve/delFile.svg" />
+          </span>
+        </div>
+
+        <div
+          class="upload"
+          @click.stop="addImage()"
+          v-if="
+            !imageList.jsonData.fileList1.length ||
+              !imageList.jsonData.fileList2.length ||
+              !imageList.jsonData.fileList3.length
+          "
+        >
+          <van-image :src="require('../../assets/images/classObserve/Union.svg')" />
         </div>
       </div>
-      <div class="cellC">
+      <div class="cellC" v-loading="videoLoading">
         <div class="tit">课堂视频</div>
-        <div>
-          <img src="https://ccrb.s3.cn-northwest-1.amazonaws.com.cn/default%2FRectangle+15471721807777029.png" alt="" />
+        <div v-if="imageList.jsonData.videoList.length">
+          <van-image
+            width="3rem"
+            height="2rem"
+            fit="cover"
+            @click.stop="showVideoDialog()"
+            :src="require('../../assets/images/classObserve/isVideo.png')"
+          />
+          <span class="setting" @click.stop="delVideo()">
+            <img src="../../assets/images/classObserve/delFile.svg" />
+          </span>
+        </div>
+        <div
+          v-if="!imageList.jsonData.videoList.length && !progressData.uploadVideo"
+          class="upload"
+          @click.stop="addVideo()"
+          style="width: 3rem;height: 2rem;"
+        >
+          <van-image :src="require('../../assets/images/classObserve/Union.svg')" />
+        </div>
+
+        <div v-if="!imageList.jsonData.videoList.length && progressData.uploadVideo" style="width: 3rem;height: 2rem;">
+          <van-image width="3rem" height="2rem" :src="require('../../assets/images/classObserve/videoFile.svg')" />
+
+          <div class="progress">
+            <div>{{ progressData.value }}%</div>
+            <span v-if="!progressData.stop">上传中...</span>
+            <span v-else>已暂停</span>
+            <div class="bar">
+              <div :style="{ width: progressData.value + '%' }"></div>
+            </div>
+          </div>
+
+          <span class="setting">
+            <img @click.stop="delUploadVideo()" src="../../assets/images/classObserve/delFile.svg" />
+            <img
+              v-if="!progressData.stop"
+              @click.stop="stopUploadVideo()"
+              src="../../assets/images/classObserve/stopIcon.png"
+            />
+            <img
+              v-if="progressData.stop"
+              @click.stop="startUploadVideo()"
+              src="../../assets/images/classObserve/startIcon.png"
+            />
+          </span>
+        </div>
+      </div>
+
+      <div class="cellC" v-loading="NephogramLoading">
+        <div class="tit">词云图</div>
+        <div style="width:3rem;height:2rem" v-if="imageList.jsonData.NephogramList.length">
+          <eChartsView :data="imageList.jsonData.NephogramList[0]" />
+          <span class="setting" @click.stop="delNephogram()">
+            <img src="../../assets/images/classObserve/delFile.svg" />
+          </span>
+        </div>
+        <div v-else class="upload" @click.stop="addNephogram()" style="width:3rem;height:2rem">
+          <van-image
+            style="width:.5rem;height:.5rem"
+            :src="require('../../assets/images/classObserve/bmRefresh.png')"
+          />
         </div>
       </div>
     </div>
 
-    <van-action-sheet v-model="show" title="授课时间">
+    <van-action-sheet v-model="showTime" title="授课时间">
       <van-datetime-picker
-        v-model="currentDate"
+        v-model="showTimeValue"
         type="datetime"
         title=""
         :min-date="minDate"
         :max-date="maxDate"
         :formatter="formatter"
-        @confirm="onConfirm"
-        @cancel="onCancel"
+        @confirm="onConfirmTime"
+        @cancel="onCancelTime"
       />
     </van-action-sheet>
+
+    <van-action-sheet v-model="showGrade" title="授课年级">
+      <van-picker :show-toolbar="true" :columns="gradeList" @confirm="onConfirmGrade" @cancel="onCancelGrade" />
+    </van-action-sheet>
+
+    <van-action-sheet v-model="showSubject" title="授课科目">
+      <van-picker
+        :show-toolbar="showSubject"
+        :columns="subjectList"
+        @confirm="onConfirmSubject"
+        @cancel="onCancelSubject"
+      />
+    </van-action-sheet>
+    <uploadFile
+      v-if="progressData.uploadVideo"
+      ref="uploadFileRef"
+      @progressUpdate="videoProgressUpdate"
+      @delUpload="videoDelUpload"
+      @success="updateVideoSuccess"
+      @startUpload="videoStartUpload"
+    />
+    <van-dialog v-model="showVideo" title="视频查看" show-cancel-button>
+      <div class="showVideoDialogBox" v-if="option.sources && showVideo">
+        <video-player
+          style="width: 100%;height: 100%;display: flex;justify-content: center;align-items: center;"
+          :playsinline="true"
+          :options="option"
+        ></video-player>
+      </div>
+    </van-dialog>
   </div>
 </template>
 
 <script>
 import bar from './components/bar.vue'
-
+import { ImagePreview } from 'vant'
+import eChartsView from './components/eChartsView.vue'
+import { Dialog } from 'vant'
+import '../../utils/aws-sdk-2.235.1.min.js'
+import uploadFile from './components/uploadFile.vue'
+import { chatNoStreamRequest } from '../../api/classObserve'
+import { v4 as uuidv4 } from "uuid";
 export default {
   components: {
-    bar
+    bar,
+    eChartsView,
+    uploadFile
   },
   props: {
     page: {
       type: Number,
       default: 1
+    },
+    bmData: {
+      type: Object,
+      default: () => {}
+    },
+    tid: {
+      type: String,
+      default: ''
+    },
+    imageList: {
+      type: Object,
+      default: () => {}
     }
   },
   data() {
@@ -104,40 +274,99 @@ export default {
       selectValue: '',
       value: '',
       show: false,
+      showTime: false,
+      showGrade: false,
+      showSubject: false,
+      videoLoading: false,
+      imageLoading: false,
+      showVideo: false,
+      NephogramLoading: false,
+      showTimeValue: this.bmData.jsonData.time,
       dateTime: '',
-      columns: ['杭州', '宁波', '温州', '绍兴', '湖州', '嘉兴', '金华', '衢州']
+      gradeList: [
+        '小学一年级',
+        '小学二年级',
+        '小学三年级',
+        '小学四年级',
+        '小学五年级',
+        '小学六年级',
+        '初中一年级',
+        '初中二年级',
+        '初中三年级',
+        '高中一年级',
+        '高中二年级',
+        '高中三年级'
+      ],
+      subjectList: [
+        '语文',
+        '数学',
+        '英语',
+        '科学',
+        '信息技术',
+        '心理',
+        '物理',
+        '化学',
+        '生物',
+        '历史',
+        '地理',
+        '通用技术',
+        '政治',
+        'STEM',
+        '美术',
+        '音乐',
+        '其他'
+      ],
+      option: {
+        playbackRates: [0.7, 1.0, 1.5, 2.0], // 播放速度
+        autoplay: false, //如果true,浏览器准备好时开始回放。
+        muted: false, // 默认情况下将会消除任何音频。
+        loop: false, // 导致视频一结束就重新开始。
+        preload: 'auto', // 建议浏览器在<video>加载元素后是否应该开始下载视频数据。auto浏览器选择最佳行为,立即开始加载视频(如果浏览器支持)
+        language: 'zh-CN',
+        aspectRatio: '16:9', // 将播放器置于流畅模式,并在计算播放器的动态大小时使用该值。值应该代表一个比例 - 用冒号分隔的两个数字(例如"16:9"或"4:3")
+        fluid: true, // 当true时,Video.js player将拥有流体大小。换句话说,它将按比例缩放以适应其容器。
+        sources: '',
+        // poster: "https://p1.music.126.net/5zs7IvmLv7KahY3BFzUmrg==/109951163635241613.jpg?param=600y500", // 你的封面地址
+        notSupportedMessage: '此视频暂无法播放,请稍后再试', // 允许覆盖Video.js无法播放媒体源时显示的默认信息。
+        controlBar: {
+          timeDivider: true,
+          durationDisplay: true,
+          remainingTimeDisplay: false,
+          fullscreenToggle: true // 全屏按钮
+        }
+      },
+      progressData: {
+        stop: false,
+        uploadVideo: false,
+        value: 0,
+        status: '',
+        key: '',
+        uploadid: ''
+      }
     }
   },
-  // computed: {
-  //   chanDate() {
-  //     return this.timeFormat(this.currentDate)
-  //   }
-  // },
   methods: {
-    onConfirm(value) {
-      this.show = false
-      this.changeTime = this.timeFormat(value)
-      // this.currentDate = value
-    },
-
-    onCancel() {
-      this.currentDate = new Date()
-      this.show = false
-    },
-    timeFormat(time) {
-      // 时间格式化 2019-09-08
-      let year = time.getFullYear()
-      let month = time.getMonth() + 1
-      let day = time.getDate()
-      // 提取小时和分钟
-      let hours = String(time.getHours()).padStart(2, '0') // 确保小时为两位数
-      let minutes = String(time.getMinutes()).padStart(2, '0') // 确保分钟为两位数
-      return year + '年' + month + '月' + day + '日' + hours + ':' + minutes
+    changeData() {
+      this.$parent.saveData(this.bmData)
+    },
+    onConfirmTime(value) {
+      let date = new Date(value)
+      const year = date.getFullYear()
+      const month = String(date.getMonth() + 1).padStart(2, '0')
+      const day = String(date.getDate()).padStart(2, '0')
+      const hours = String(date.getHours()).padStart(2, '0')
+      const minutes = String(date.getMinutes()).padStart(2, '0')
+      const seconds = String(date.getSeconds()).padStart(2, '0')
+      this.bmData.jsonData.time = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
+      this.showTime = false
+      this.$parent.saveData(this.bmData)
+    },
+    onCancelTime() {
+      this.showTime = false
     },
     cutPage() {
       this.$emit('cutPage', 1)
     },
-
     formatter(type, val) {
       if (type === 'year') {
         return val + '年'
@@ -155,6 +384,415 @@ export default {
         return val + '分'
       }
       return val
+    },
+    onConfirmGrade(value) {
+      this.bmData.jsonData.grade = value
+      this.changeData()
+      this.showGrade = false
+    },
+    onCancelGrade() {
+      this.showGrade = false
+      console.log('取消')
+    },
+    onConfirmSubject(value) {
+      this.bmData.jsonData.subject = value
+      this.changeData()
+      this.showSubject = false
+    },
+    onCancelSubject() {
+      this.showSubject = false
+      console.log('取消')
+    },
+    previewImage(url) {
+      ImagePreview([url])
+    },
+    addImage() {
+      if (!this.tid) return this.$toast.fail('请先选择课堂')
+      // 上传图片
+      let input = document.createElement('input')
+      input.type = 'file'
+      input.accept = 'image/*'
+      input.click()
+      input.onchange = () => {
+        this.imageLoading = true
+        let file = input.files[0]
+        var credentials = {
+          accessKeyId: 'AKIATLPEDU37QV5CHLMH',
+          secretAccessKey: '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'
+        } //秘钥形式的登录上传
+        window.AWS.config.update(credentials)
+        window.AWS.config.region = 'cn-northwest-1' //设置区域
+
+        var bucket = new window.AWS.S3({ params: { Bucket: 'ccrb' } }) //选择桶
+        var _this = this
+
+        if (file) {
+          var params = {
+            Key:
+              file.name.split('.')[0] +
+              new Date().getTime() +
+              '.' +
+              file.name.split('.')[file.name.split('.').length - 1],
+            ContentType: file.type,
+            Body: file,
+            'Access-Control-Allow-Credentials': '*',
+            ACL: 'public-read'
+          } //key可以设置为桶的相抵路径,Body为文件, ACL最好要设置
+          var options = {
+            partSize: 2048 * 1024 * 1024,
+            queueSize: 2,
+            leavePartsOnError: true
+          }
+          bucket
+            .upload(params, options)
+            .on('httpUploadProgress', function(evt) {
+              //这里可以写进度条
+              // console.log("Uploaded : " + parseInt((evt.loaded * 80) / evt.total) + '%');
+            })
+            .send(function(err, data) {
+              if (err) {
+                _this.$toast.fail('上传图片失败')
+                _this.imageLoading = false
+              } else {
+                let _fileKey = ''
+                if (_this.imageList.jsonData.fileList1.length == 0) {
+                  _fileKey = 'fileList1'
+                } else if (_this.imageList.jsonData.fileList2.length == 0) {
+                  _fileKey = 'fileList2'
+                } else if (_this.imageList.jsonData.fileList3.length == 0) {
+                  _fileKey = 'fileList3'
+                }
+                if (_fileKey) {
+                  _this.imageList.jsonData[_fileKey] = [
+                    {
+                      name: data.key,
+                      status: 'success',
+                      uid: '1',
+                      url: data.Location
+                    }
+                  ]
+                  _this.$parent.saveData(_this.imageList).then(_ => {
+                    _this.imageLoading = false
+                    _this.$toast.success('上传图片成功')
+                  })
+                } else {
+                  _this.$toast.fail('上传图片失败')
+                }
+              }
+            })
+        }
+      }
+    },
+    delImage(key) {
+      Dialog.confirm({
+        title: '删除课堂图片',
+        message: `确定删除课堂图片?`
+      })
+        .then(() => {
+          this.imageLoading = true
+          this.imageList.jsonData[key] = []
+          this.$parent.saveData(this.imageList).then(_ => {
+            this.imageLoading = false
+            this.$toast.success('成功删除图片')
+          })
+        })
+        .catch(() => {
+          console.log('不删除')
+          // on cancel
+        })
+    },
+    showVideoDialog() {
+      // console.log("测试测试")
+      this.option.sources = this.imageList.jsonData.videoList[0].url
+      this.showVideo = true
+    },
+    addVideo() {
+      if (!this.tid) return this.$toast.fail('请选择课堂')
+      let input = document.createElement('input')
+      input.type = 'file'
+      input.accept = 'video/*'
+      input.click()
+      input.onchange = () => {
+        this.progressData.uploadVideo = true
+        // this.uploadVideoLoading = true;
+        this.progressData.stop = false
+        this.progressData.status = ''
+        this.progressData.value = 0
+        let file = input.files[0]
+        this.$nextTick(() => {
+          this.$refs.uploadFileRef.awsupload({
+            file: file,
+            folderName: this.tid
+          })
+        })
+      }
+    },
+    delVideo() {
+      Dialog.confirm({
+        title: '删除课堂视频',
+        message: `确定删除该视频?`
+      })
+        .then(() => {
+          this.videoLoading = true
+          this.imageList.jsonData.videoList = []
+          this.$parent.saveData(this.imageList).then(_ => {
+            this.videoLoading = false
+            this.$toast.success('成功删除视频')
+          })
+        })
+        .catch(() => {
+          console.log('不删除')
+          // on cancel
+        })
+    },
+    videoStartUpload({ key, uploadid }) {
+      this.progressData.uploadid = uploadid
+      this.progressData.key = key
+      this.progressData.status = 'start'
+    },
+    updateVideoSuccess(res) {
+      if (!res.data) return
+      this.progressData.uploadVideo = false
+      this.progressData.stop = false
+      this.progressData.uploadid = ''
+      this.progressData.status = ''
+      this.progressData.value = 0
+      let { data } = res
+      this.$refs.uploadFileRef.file = null
+      this.videoLoading = true
+      this.imageList.jsonData.videoList = [
+        {
+          name: data.Key,
+          status: 'success',
+          uid: 'qgt',
+          url: data.Location
+        }
+      ]
+      this.$parent.saveData(this.imageList).then(_ => {
+        this.videoLoading = false
+        this.$toast.success('上传视频成功')
+      })
+      // this.$emit('saveVideo', {
+      //   name: data.Key,
+      //   status: 'success',
+      //   uid: 'qgt',
+      //   url: data.Location
+      // })
+    },
+    videoProgressUpdate(data) {
+      if (data.status == 'processing') {
+        this.progressData.value = data.percent
+        this.progressData.status = data.status
+      } else if (data.status == 'fail') {
+        this.progressData.value = data.percent
+        this.progressData.status = data.status
+        this.$toast.fail('上传发生错误,请点击继续上传')
+        this.$refs.uploadFileRef.stopUpload()
+      } else if (data.status == 'success') {
+        this.progressData.value = data.percent
+        this.progressData.status = data.status
+        this.$refs.uploadFileRef.stopUpload()
+      } else if (data.status == 'error') {
+        this.progressData.stop = true
+        this.progressData.status = data.status
+        this.$refs.uploadFileRef.stopUpload()
+        this.$toast.fail('传发生错误,请重新上传')
+      }
+      console.log(this.progressData)
+    },
+    videoDelUpload(res) {
+      this.progressData.uploadVideo = false
+      this.progressData.uploadid = ''
+      this.progressData.key = ''
+      this.progressData.status = ''
+      this.progressData.stop = false
+      this.progressData.value = 0
+      this.$toast.success('已删除视频')
+    },
+    // 停止上传视频
+    stopUploadVideo() {
+      this.$refs.uploadFileRef.stopUpload()
+      this.progressData.stop = true
+      this.$toast.success('已停止上传')
+    },
+    //开始上传视频
+    startUploadVideo() {
+      // this.$message.info("开始上传")
+      if (this.$refs.uploadFileRef.file && this.progressData.key) {
+        this.$refs.uploadFileRef.awsupload({ keyName: this.progressData.key })
+      } else {
+        this.addVideo()
+      }
+      this.progressData.stop = false
+    },
+    delUploadVideo() {
+      this.$refs.uploadFileRef.stopUpload()
+      this.$refs.uploadFileRef.abortMultipartUpload(this.progressData.key, this.progressData.uploadid)
+    },
+    addNephogram() {
+      if (!this.tid) return this.$toast.fail('请选择课堂')
+      if (this.NephogramLoading) return this.$toast('请稍等...')
+      this.NephogramLoading = true
+      const _msg = `NOTICE
+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.
+ATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced "Format example".
+Instruction: Based on the context, follow "Format example", write content.
+
+## 任务
+
+请基于以下课堂实录文本(大约5000字),提炼出20-30个关键词,用于绘制词云图。请给出相应的关键词,关键词出现的频次,词云图上的大小。请确保输出的关键字准确反映课堂实录的主要内容和主题。
+
+## 要求
+
+1. **提取关键词**:从提供的课堂实录文本中提取出20-30个最具代表性的关键字。关键词应该涵盖课堂实录中的主要概念、重要术语和核心主题。尽量选择多样化的关键词,避免过于集中在某一个主题或概念上。
+2. **词频统计**:计算每个关键字在文本中出现的频率。
+3. **词汇大小**:根据词频数量,确定每个关键字在词云图中的大小。词频越高,词汇大小数值越大,数值范围1-100。
+4. **输出格式**:输出结果应包含每个关键字、对应的词频数量以及词汇大小数值。
+
+## 输出格式
+
+### 输出格式
+
+[
+	{"value":1,"name":"氯化钠","textStyle":{"color":"#ee7959"}},
+	{"value":2,"name":"溶液","textStyle":{"color":"#db9b34"}},
+	{"value":1,"name":"实验","textStyle":{"color":"#9d9d82"}},
+	{"value":3,"name":"质量分数","textStyle":{"color":"#ea5514"}},
+	{"value":1,"name":"溶质","textStyle":{"color":"#c8161d"}},
+	{"value":2,"name":"氢氧化钠","textStyle":{"color":"#e60012"}},
+	{"value":1,"name":"溶解度","textStyle":{"color":"#1e2732"}},
+	{"value":4,"name":"饱和溶液","textStyle":{"color":"#e3adb9"}}
+]
+
+请仅仅输出表头,输出关键词和相应的内容,无需其它任何说明文字。
+冒号、逗号等符号均使用英文字符。
+
+### 输出示例
+
+[
+	{"value":1,"name":"氯化钠","textStyle":{"color":"#ee7959"}},
+	{"value":2,"name":"溶液","textStyle":{"color":"#db9b34"}},
+	{"value":1,"name":"实验","textStyle":{"color":"#9d9d82"}},
+	{"value":3,"name":"质量分数","textStyle":{"color":"#ea5514"}},
+	{"value":1,"name":"溶质","textStyle":{"color":"#c8161d"}},
+	{"value":2,"name":"氢氧化钠","textStyle":{"color":"#e60012"}},
+	{"value":1,"name":"溶解度","textStyle":{"color":"#1e2732"}},
+	{"value":4,"name":"饱和溶液","textStyle":{"color":"#e3adb9"}}
+]
+
+## 课堂实录 
+${this.bmData.jsonData.editorBarData ? this.bmData.jsonData.editorBarData.content : ''}
+`
+      const _uuid = uuidv4()
+      let params = {
+        model: 'gpt-3.5-turbo',
+        temperature: 0,
+        max_tokens: 4096,
+        top_p: 1,
+        frequency_penalty: 0,
+        presence_penalty: 0,
+        messages: [{ role: 'user', content: _msg }],
+        uid: _uuid,
+        mind_map_question: '',
+        stream: false
+      }
+      chatNoStreamRequest(params)
+        .then(res => {
+          let _data = res.FunctionResponse.choices[0]
+          let _jsonData = _data.message.content
+          _jsonData = _jsonData.replaceAll('```json', '').replaceAll('```', '')
+          let _result = JSON.parse(_jsonData)
+
+          // this.$emit('saveNephogram', {
+          //   tooltip: {
+          //     show: false
+          //   },
+          //   series: [
+          //     {
+          //       type: 'wordCloud',
+          //       sizeRange: [14, 38],
+          //       rotationRange: [0, 0],
+          //       keepAspect: false,
+          //       shape: 'circle',
+          //       left: 'center',
+          //       top: 'center',
+          //       right: null,
+          //       bottom: null,
+          //       width: '90%',
+          //       height: '90%',
+          //       // maskImage: maskImage,
+          //       // sizeRange: [12, 60],
+          //       rotationRange: [-90, 90],
+          //       rotationStep: 45,
+          //       // gridSize: 12,
+          //       // autoSize: {
+          //       //   enable: true,
+          //       //   minSize: 12,
+          //       // },
+          //       data: _result
+          //     }
+          //   ]
+          // })
+          this.imageList.jsonData.NephogramList = [
+            {
+              tooltip: {
+                show: false
+              },
+              series: [
+                {
+                  type: 'wordCloud',
+                  sizeRange: [14, 38],
+                  rotationRange: [0, 0],
+                  keepAspect: false,
+                  shape: 'circle',
+                  left: 'center',
+                  top: 'center',
+                  right: null,
+                  bottom: null,
+                  width: '90%',
+                  height: '90%',
+                  // maskImage: maskImage,
+                  // sizeRange: [12, 60],
+                  rotationRange: [-90, 90],
+                  rotationStep: 45,
+                  // gridSize: 12,
+                  // autoSize: {
+                  //   enable: true,
+                  //   minSize: 12,
+                  // },
+                  data: _result
+                }
+              ]
+            }
+          ]
+          this.$parent.saveData(this.imageList).then(_ => {
+            this.NephogramLoading = false
+            this.$toast.success('生成词云图成功')
+          })
+        })
+        .catch(e => {
+          console.log(e)
+          this.$toast.fail('生成词云图失败')
+          this.NephogramLoading = false
+        })
+    },
+    delNephogram() {
+      Dialog.confirm({
+        title: '删除词云图',
+        message: `确定删除该词云图?`
+      })
+        .then(() => {
+          this.NephogramLoading = true
+          this.imageList.jsonData.NephogramList = []
+          this.$parent.saveData(this.imageList).then(_ => {
+            this.NephogramLoading = false
+            this.$toast.success('成功删除词云图')
+          })
+        })
+        .catch(() => {
+          console.log('不删除')
+          // on cancel
+        })
     }
   }
 }
@@ -179,7 +817,7 @@ export default {
     font-weight: 400;
   }
 }
-.cellList{
+.cellList {
   flex: 1;
   overflow: auto;
 }
@@ -217,11 +855,84 @@ export default {
   justify-content: space-between;
   align-items: center;
   border-bottom: 1px #ccc solid;
-
+  .upload {
+    width: 2rem;
+    height: 2rem;
+    background-color: #ccc;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+  }
   .tit {
     color: rgba(0, 0, 0, 0.8);
     font-size: 16px;
     font-weight: 500;
   }
+  div {
+    position: relative;
+    img {
+      width: 80%;
+    }
+    .progress {
+      width: 100%;
+      height: 100%;
+      position: absolute;
+      display: flex;
+      flex-direction: column;
+      justify-content: center;
+      align-items: center;
+      top: 0;
+      /* 加载 */
+      /* cursor:wait !important; */
+      background-color: #00000099;
+      div:nth-child(1) {
+        font-size: 20px;
+        color: white;
+      }
+      span:nth-child(2) {
+        font-size: 16px;
+        color: #ffffff8c;
+      }
+      .bar {
+        width: 100%;
+        height: 5px;
+        position: absolute;
+        bottom: 0;
+        background-color: #d8e3f7;
+        div {
+          background-color: #3975ce;
+          max-width: 100%;
+          height: 100%;
+        }
+      }
+    }
+    .setting {
+      position: absolute;
+      top: -0.25rem;
+      left: -0.25rem;
+      width: auto;
+      height: 0.5rem;
+      img {
+        width: 0.5rem;
+        height: 100%;
+        margin-right: 0.2rem;
+      }
+    }
+  }
+}
+
+.showVideoDialogBox {
+  width: 100%;
+  height: 5rem;
+  :deep(.video-js) {
+    width: 100%;
+    height: 100%;
+  }
+
+  :deep(.vjs-big-play-button) {
+    top: 50%;
+    left: 50%;
+    transform: translate(-50%, -50%);
+  }
 }
 </style>

+ 132 - 0
src/views/classObserve/components/analysisItem.vue

@@ -0,0 +1,132 @@
+<template>
+  <div class="outcomeCon">
+    <div @click="showItem" style="height: 50px;display: flex; justify-content: space-between;align-items: center;">
+      <div>
+        <van-icon v-if="!show" name="arrow" />
+        <van-icon v-else name="arrow-down" />
+        <span class="tit">{{ data.jsonData?data.jsonData.name:'' }}</span>
+      </div>
+      <van-popover placement="bottom-end" v-model="abuShow">
+        <div class="abu">
+          <!-- <div class="abuBtn">
+                <img src="../../assets/images/classObserve/addTel.png" alt="" />
+                <span>重命名</span>
+              </div> -->
+          <div class="abuBtn" @click.stop="del()">
+            <img src="../../../assets/images/classObserve/del.png" alt="" />
+            <span>删除</span>
+          </div>
+        </div>
+        <template #reference>
+          <img @click.stop="abuShowItem" src="../../../assets/images/classObserve/colD.png" alt="" />
+        </template>
+      </van-popover>
+    </div>
+    <div v-show="show">
+      <div class="brief">
+        {{ data.jsonData?data.jsonData.result:'' }}
+      </div>
+			<div class="content">
+				<!-- {{ data.jsonData }} -->
+				<mdView :text="data.jsonData?data.jsonData.content:''"/>
+			</div>
+      <div class="outcomeBtn">
+        <div @click.stop="down()"><img src="../../../assets/images/classObserve/revoke.png" alt="" /><span>撤销</span></div>
+        <div @click.stop="up()"><img src="../../../assets/images/classObserve/restore.png" alt="" /><span>恢复</span></div>
+        <div @click.stop="optimize()"><img src="../../../assets/images/classObserve/optimize.png" alt="" /><span>优化</span></div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import mdView from './mdView.vue'
+export default {
+  props: {
+    data: {
+      type: Object,
+      default: () => {}
+    }
+  },
+	components:{
+		mdView
+	},
+  data() {
+    return {
+      show: false,
+      abuShow: false
+    }
+  },
+
+  methods: {
+    showItem() {
+      this.show = !this.show
+    },
+    abuShowItem() {
+      this.abuShow = true
+    },
+		up(){
+			console.log("恢复")
+		},
+		down(){
+			console.log("撤销")
+		},
+		optimize(){
+			console.log("优化")
+		},
+		del(){
+			this.$parent.delItem(this.data)
+		}
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.outcomeCon {
+  // display: flex;
+  min-height: 50px;
+  border: 0.5px #ccc solid;
+  align-items: center;
+  border-radius: 5px;
+  min-height: 40px;
+  padding: 0 10px;
+	margin-bottom: 10px;
+}
+
+.abu {
+  background-color: #fff;
+  width: 100px;
+  border-radius: 6px;
+  //   padding: 10px 10px;
+  box-shadow: 0px 8px 10px -5px rgba(0, 0, 0, 0.08);
+  box-shadow: 0px 16px 24px 2px rgba(0, 0, 0, 0.04);
+  box-shadow: 0px 6px 30px 5px rgba(0, 0, 0, 0.05);
+  .abuBtn {
+    display: flex;
+    align-items: center;
+    padding: 10px 15px;
+    font-size: 12px;
+    font-weight: 400;
+    img {
+      margin-right: 5px;
+    }
+  }
+}
+.outcomeBtn {
+  display: flex;
+  justify-content: space-around;
+  padding: 10px 0;
+  div {
+    display: flex;
+    img {
+      width: 16px;
+      height: 16px;
+    }
+  }
+}
+
+.brief{
+	font-style: italic;
+	color: #6b798e;
+}
+</style>

+ 48 - 0
src/views/classObserve/components/eChartsView.vue

@@ -0,0 +1,48 @@
+<template>
+	<div class="chart" id="charts_canvas" ref="chartRef"></div>
+</template>
+
+<script>
+import * as echarts from 'echarts';
+import "echarts-wordcloud";
+export default {
+	props: {
+		data: {
+			type: Object,
+			default: () => {},
+		},
+	},
+	data() {
+		return {
+			chartObj: null,
+			chartData: null,
+		};
+	},
+	watch: {
+		data() {
+			this.getChartData();
+		},
+	},
+	methods: {
+		getChartData() {
+			this.chartObj = echarts.init(this.$refs.chartRef);
+			this.chartObj.setOption(this.data);
+			window.addEventListener("resize", () => {
+					this.chartObj.resize();
+      });
+		},
+	},
+	mounted() {
+		this.getChartData();
+	},
+};
+</script>
+
+<style scoped>
+.chart {
+	max-width: 100%;
+	width: 100%;
+	height: 100%;
+	background-color: #fff;
+}
+</style>

+ 84 - 0
src/views/classObserve/components/mdView.vue

@@ -0,0 +1,84 @@
+<template>
+	<div class="mdView">
+		<!-- <div v-html="renderedMarkdown"></div> -->
+		<vue-markdown :source="markdownContent" />
+	</div>
+</template>
+
+<script>
+// import MarkdownIt from "markdown-it";
+import VueMarkdown from 'vue-markdown';
+
+export default {
+	components: {
+		VueMarkdown,
+	},
+	props: {
+		url: {
+			type: String,
+			default: "",
+		},
+		text:{
+			type:String,
+			default:"",
+		},
+	},
+	data() {
+		return {
+			markdownContent: "",
+		};
+	},
+	watch:{
+		text(){
+			this.markdownContent = this.text;
+		}
+	},
+	// computed: {
+	// 	renderedMarkdown() {
+	// 		return this.md.render(this.markdownContent);
+	// 	},
+	// },
+	methods: {
+		getMarkdown() {
+			if(this.text)return this.markdownContent = this.text;
+			if(!this.url)return;
+		},
+	},
+	mounted(){
+		this.getMarkdown();
+	}
+};
+</script>
+<style scoped>
+.mdView {
+	max-width: 100%;
+	height: 100%;
+}
+
+.mdView >>>.table{
+	width: 100%;
+  border-collapse: collapse;
+	margin: 10px 0;
+}
+
+.mdView >>>th,td{
+    border: 1px solid #999;
+    text-align: center;
+    padding: 10px 10px;
+}
+.mdView >>>td{
+    border: 1px solid #999;
+    text-align: center;
+    padding: 10px 10px;
+}
+
+.mdView>>>ol{
+	margin-left: 20px;
+	list-style: auto;
+}
+
+.mdView>>>ul{
+	margin-left: 20px;
+	list-style: auto;
+}
+</style>

+ 319 - 0
src/views/classObserve/components/uploadFile.vue

@@ -0,0 +1,319 @@
+<template>
+	<div class="uploadBox"></div>
+</template>
+
+<script>
+import "../../../utils/aws-sdk-2.235.1.min.js";
+export default {
+	emits: ["progressUpdate"],
+	data() {
+		return {
+			bucket: "", //aws上传接口
+			bucketname: "ccrb", //桶
+			uploadid: "",
+			partsize: 10 * 1024 * 1024, //10MB  分片
+			filestate: {
+				status: "", //有 error(直接报错) 和 fail(上传的时候报错) 和 success(上传成功) 和 processing(上传中)
+				percent: "", //(0-100的进度)
+			},
+			file: null,
+			flag:true,
+		};
+	},
+	watch: {
+		filestate: {
+			handler(newValue) {
+				this.$emit("progressUpdate", newValue);
+			},
+			immediate: true,
+			deep: true,
+		},
+	},
+	methods: {
+		//--------------------------分断上传保证稳定性
+		//初始化上传
+		// async init(){
+		// 	const credentials = {
+		// 	    accessKeyId: "AKIATLPEDU37QV5CHLMH",
+		// 	    secretAccessKey: "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",
+		// 	}; //秘钥形式的登录上传
+		// 	window.AWS.config.update(credentials);
+		// 	window.AWS.config.region = "cn-northwest-1"; //设置区域
+		// 	//桶的设置
+		// 	bucket = new window.AWS.S3({
+		// 	    params: {
+		// 	        Bucket: this.bucketname
+		// 	    }
+		// 	});
+		// 	return bucket;
+		// },
+		// 初始化上传入口
+		async initMultipartUpload(key, file) {
+			const params = {
+				Bucket: this.bucketname,
+				Key: key,
+				ContentType: file.type,
+				ACL: "public-read",
+			};
+			//创建一个续传通道
+			const data = await this.bucket.createMultipartUpload(params).promise();
+			return data.UploadId;
+		},
+		// 上传文件的某一部分
+		async uploadPart(file, keyname, uploadid, pn, start, end) {
+			//key可以设置为桶的相对路径,Body为文件, ACL最好要设置
+			if(!this.flag)return;
+			var params = {
+				Bucket: this.bucketname,
+				Key: keyname,
+				// ContentType: file.type,
+				PartNumber: pn,
+				UploadId: uploadid,
+				Body: file.slice(start, end),
+				// "Access-Control-Allow-Credentials": "*",
+				// ACL: "public-read",
+			};
+			const result = await this.bucket.uploadPart(params).promise();
+			return { ETag: result.ETag, PartNumber: pn };
+		},
+		//完成分块上传
+		async completeMultipartUpload(parts, keyname, uploadid) {
+			if(!this.flag)return;
+			const params = {
+				Bucket: this.bucketname,
+				Key: keyname,
+				MultipartUpload: { Parts: parts },
+				UploadId: uploadid,
+			};
+			return await this.bucket.completeMultipartUpload(params).promise();
+		},
+		async abortMultipartUpload(key, uploadid) {
+			const params = {
+				Bucket: this.bucketname,
+				Key: key,
+				UploadId: uploadid,
+			};
+			let data = await this.bucket.abortMultipartUpload(params).promise();
+			this.$emit("delUpload")
+			
+		},
+		//--------------------------------下面支持断点续传
+
+		//初始化亚马逊参数
+		async init() {
+			//秘钥形式的登录上传
+			const credentials = {
+				accessKeyId: "AKIATLPEDU37QV5CHLMH",
+				secretAccessKey: "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",
+				region: "cn-northwest-1",
+			};
+			window.AWS.config.update(credentials);
+			// window.AWS.config.region = "cn-northwest-1"; //设置区域
+			//桶的设置
+			this.bucket = new window.AWS.S3({
+				params: {
+					Bucket: this.bucketname,
+				},
+			});
+			return this.bucket;
+		},
+
+		//获取当前文件是否有已上传断点信息
+		async getawscheckpoint(key) {
+			let partsinfo;
+			try {
+				const result = await this.bucket
+					.listMultipartUploads({ Bucket: this.bucketname, Prefix: key })
+					.promise();
+				//获取具体分片信息
+				if (result.Uploads.length) {
+					this.uploadid = result.Uploads[result.Uploads.length - 1].UploadId;
+					partsinfo = await this.bucket
+						.listParts({
+							Bucket: this.bucketname,
+							Key: key,
+							UploadId: this.uploadid,
+						})
+						.promise();
+				}
+			} catch (err) {
+				console.log(err);
+			}
+			return { uploadid: this.uploadid, partsinfo };
+		},
+
+		//分段上传
+		async awsuploadpart(filestate, file, uploadid, parts, key) {
+			var partarr = []; //已完成的数组
+			//已完成的分片,转化成提交格式
+			const completeparts = parts.map((_) => {
+				partarr.push(_.PartNumber);
+				return { PartNumber: _.PartNumber, ETag: _.ETag };
+			});
+			// 分块上传文件
+			// parts = [];
+			let uploadpart;
+			let start = 0;
+			let end = 0;
+			let len = Math.ceil(file.size / this.partsize); //循环的长度
+			if (partarr.length) {
+				this.filestate.status = "processing";
+				this.filestate.percent = parseInt((completeparts.length * 100) / len);
+			}
+			//循环上传
+			for (let i = 0; i < len; i++) {
+				if(!this.flag)break;
+				start = i * this.partsize;
+				end = (i + 1) * this.partsize;
+				if (!partarr.includes(i+1)) {
+
+					uploadpart = await this.uploadPart(
+						file,
+						key,
+						uploadid,
+						i + 1,
+						start,
+						end
+					);
+					if (uploadpart.ETag != null) {
+						completeparts.push(uploadpart);
+						partarr.push(uploadpart.PartNumber);
+						this.filestate.status = "processing";
+						this.filestate.percent = parseInt(
+							(completeparts.length * 100) / len
+						);
+					} else {
+						this.filestate.status = "fail";
+						this.stopUpload();
+						return;
+					}
+				}
+			}
+			//提交上传成功信息
+			if(this.flag){
+					let data = await this.completeMultipartUpload(
+					completeparts,
+					key,
+					uploadid
+				);
+				this.filestate.status = "success";
+				return data;
+			}
+		},
+
+		//上传的接口
+		async awsupload({ file = this.file, keyName, folderName }) {
+			if (!file) return this.$message.error("请上传文件");
+			this.init(); //初始化桶
+			// const key = (folderid || uuidv4()) + "/" + file.name; //需要上传的文件名
+			let key = "";
+			if (keyName) {
+				key = keyName;
+			} else {
+				if (folderName) {
+					key = `${folderName}/${file.name}`;
+				} else {
+					key = `${file.name.split(".")[0] +
+							new Date().getTime() +
+							"." +
+							file.name.split(".")[file.name.split(".").length - 1]}`;
+				}
+			}
+			this.filestate = {
+				percent: 0,
+				status: "start",
+			};
+			this.filestate.percent = 0;
+			this.filestate.status = "start";
+			this.file = file;
+			//上传的参数
+			var params = {
+				Bucket: this.bucketname,
+				Key: key,
+			};
+			this.flag = true;
+			//设置桶上传文件
+			try {
+				//检查文件是否已上传
+				this.bucket.headObject(params, async (err, data) => {
+					// 没有上传成功,head方法会返回失败
+					if (err) {
+						//检查是否部分上传
+						const { uploadid, partsinfo } = await this.getawscheckpoint(
+							key,
+							this.bucket
+						);
+						//如果已经部分存在,那么直接在节点续传
+						if (uploadid) {
+							//断点续传
+							this.$emit("startUpload", { key, uploadid });
+							this.flag = true;
+							let data = await this.awsuploadpart(
+								this.filestate,
+								file,
+								uploadid,
+								partsinfo.Parts,
+								key
+							);
+							
+							if(this.flag || this.filestate.percent==100)return this.$emit("success", { data, key, uploadid });
+							// return {data,key,uploadid}
+						}
+						//不存在,上传新的
+						else {
+							const uploadid = await this.initMultipartUpload(key, file); //初始化文件上传
+							this.$emit("startUpload", { key, uploadid });
+							this.flag = true;
+							let data = await this.awsuploadpart(
+								this.filestate,
+								file,
+								uploadid,
+								[],
+								key
+							);
+							if(this.flag || this.filestate.percent==100)return this.$emit("success", { data, key, uploadid });
+							// return {data,key,uploadid}
+						}
+					}
+					//如果已经上传成功了,那么直接返回状态百分百
+					else if (data) {
+						//data存在,上传成功
+						this.filestate.percent = 100;
+						this.filestate.status = "success";
+						let url = `https://ccrb.s3.cn-northwest-1.amazonaws.com.cn/${key}`
+						this.file = null;
+						if(this.flag)return this.$emit("success", { data:{
+							Key:key,
+							Location:url,
+							ETag:data.ETag,
+							size:data.ContentLength,
+							ServerSideEncryption:data.ServerSideEncryption,
+							Bucket:this.bucketname
+							} });
+						// return {data,key,uploadid}
+					}
+				});
+			} catch (err) {
+				this.filestate.status = "error";
+				this.stopUpload();
+				console.log(err);
+			}
+		},
+		// 停止上传
+		stopUpload(){
+			this.filestate.status = "stop";
+			this.flag = false;
+		}
+	},
+	mounted() {
+		this.bucket = "";
+		this.file = null;
+	},
+};
+</script>
+
+<style scoped>
+.uploadBox {
+	display: none;
+}
+</style>

+ 118 - 32
src/views/classObserve/homePage.vue

@@ -14,24 +14,21 @@
     </div>
     <div class="FunctionalArea">
       <div class="tit">
-        <div>课程名称</div>
-        <span @click="BindForm(2)">
+        <div>{{bmData.jsonData?bmData.jsonData.courseName:''}}</div>
+        <span @click="cutPage(2)">
           <img src="../../assets/images/classObserve/lianjie.png" alt="" />
         </span>
       </div>
-      <div class="teaInfo" @click="BindForm(3)">
-        <div class="teaName">王老师</div>
-        <div class="classInfo">9年级</div>
-        <div class="classInfo">科学</div>
+      <div class="teaInfo" @click="cutPage(3)">
+        <div class="teaName">{{ bmData.jsonData?bmData.jsonData.teacherName:'' }}</div>
+        <div class="classInfo" v-if="bmData.jsonData && bmData.jsonData.grade">{{ bmData.jsonData.grade }}</div>
+        <div class="classInfo" v-if="bmData.jsonData && bmData.jsonData.subject">{{ bmData.jsonData.subject }}</div>
         <img src="../../assets/images/classObserve/fillInfo.png" alt="" />
       </div>
       <div class="chat">
-        <div class="chatTxt">
-          请用日常生活为主题,跟我进行英语对话, 这里是语音同步转文字, 这里是语音同步转文字这里是语音同转文字这|...
-          请用日常生活为主题,跟我进行英语对话, 这里是语音同步转文字, 这里是语音同步转文字这里是语音同转文字这|...
-        </div>
+        <div class="chatTxt" v-text="bmData.jsonData?bmData.jsonData.transcriptionData:''"></div>
         <img src="../../assets/images/classObserve/waveanimation.png" alt="" />
-        <div class="time">00:08</div>
+        <div class="time">00:00</div>
       </div>
       <div class="controlArea">
         <img @click="goChat" src="../../assets/images/classObserve/rootper.png" alt="" />
@@ -42,34 +39,34 @@
         <div @click="historyBtn" style="position: relative;">
           <span
             style="position: absolute;top: -5px;right: -9px;color: rgba(54, 129, 252, 1);width: 15px;font-size: 10px;"
-            >99</span
+            >{{ classList.length }}</span
           >
           <img src="../../assets/images/classObserve/book.png" alt="" />
         </div>
       </div>
       <div class="selectBtn">
-        <button :disabled="isParse" class="btn1" @click="BindForm(5)">
+        <button :disabled="isParse" class="btn1" @click="cutPage(5)">
           选择模版
           <div v-if="isParse" class="one"></div>
         </button>
-        <button :disabled="isParse" class="btn2" @click="analysis">
-          一键分析
+        <button :disabled="isParse" class="btn2" @click="cutPage(8)">
+          查看分析
           <div v-if="isParse" class="one"></div>
         </button>
       </div>
     </div>
 
-    <van-action-sheet v-model="historyShow" title="历史记录">
+    <van-action-sheet v-model="historyShow" title="历史课堂" v-loading="historyListLoading">
       <div class="historyList">
-        <div class="con" v-for="item in 10" :key="item">
-          <div class="tit">课程信息</div>
-          <van-popover placement="bottom-end" v-model="abuShow" trigger="click">
+        <div class="con" v-for="(item, index) in classList" :key="index">
+          <div :class="['tit', tid == item.value ? 'titActive' : '']" @click="changeTidFn(item)">{{ item.label }}</div>
+          <van-popover placement="bottom-end" v-model="item.show" trigger="click">
             <div class="abu">
-              <div class="abuBtn">
+              <div class="abuBtn" @click.stop="editClass(item)">
                 <img src="../../assets/images/classObserve/edit.png" alt="" />
                 <span>重命名</span>
               </div>
-              <div class="abuBtn">
+              <div class="abuBtn" @click.stop="delClass(item)">
                 <img src="../../assets/images/classObserve/del.png" alt="" />
                 <span>删除</span>
               </div>
@@ -78,6 +75,24 @@
               <img src="../../assets/images/classObserve/colD.png" alt="" />
             </template>
           </van-popover>
+          <van-popover class="editPopover" placement="left-end" v-model="item.edit" trigger="click">
+            <van-cell-group>
+              <van-field
+                class="ecn_field"
+                v-model="item.newLabel"
+                :disabled="historyListLoading"
+                center
+                label="名称:"
+                placeholder="请输入新的课堂名称"
+              >
+                <template #button>
+                  <van-button :disabled="historyListLoading" size="small" type="primary" @click.stop="onEdit(item)"
+                    >确定修改</van-button
+                  >
+                </template>
+              </van-field>
+            </van-cell-group>
+          </van-popover>
         </div>
       </div>
     </van-action-sheet>
@@ -85,22 +100,35 @@
 </template>
 
 <script>
+import { Dialog } from 'vant';
 export default {
   props: {
     page: {
       type: Number,
       default: 1
+    },
+    classList: {
+      type: Array,
+      default: () => []
+    },
+		bmData:{
+			type:Object,
+			default:()=>{}
+		},
+    tid: {
+      type: String,
+      default: ''
     }
   },
   data() {
     return {
       isParse: false,
       historyShow: false,
-      abuShow: false
+      abuShowId: '',
+      historyListLoading: false
     }
   },
   methods: {
-  
     goChat() {
       this.$router.push({ path: '/aiChat', query: {} })
     },
@@ -108,10 +136,14 @@ export default {
       this.historyShow = true
     },
     // 绑定表单
-    BindForm(val) {
-      this.$emit('cutPage', val)
+    cutPage(val) {
+			if([2,3,8].includes(val) && !this.tid){
+				return this.$toast("请先选择或创建课堂");
+			}
+      this.$emit('cutPage', val);
+
     },
-    
+
     ctrlRecord() {
       this.isParse = !this.isParse
     },
@@ -119,6 +151,41 @@ export default {
     analysis() {
       console.log(22)
       this.$router.push({ path: '/outcome', query: {} })
+    },
+    changeTidFn(item) {
+      this.historyShow = false
+      this.$parent.changeTid(item.value)
+    },
+    editClass(item) {
+      let _index = this.$parent.classList.findIndex(i => i.id == item.id)
+      if (_index !== -1) {
+        this.$parent.classList[_index].edit = true
+        this.$parent.classList[_index].show = false
+      }
+    },
+    delClass(item) {
+      if (this.historyListLoading) return this.$toast('请稍等...')
+      Dialog.confirm({
+        title: '删除课堂',
+        message: `确定删除 "${item.label}" 这个课堂?`
+      })
+        .then(() => {
+					this.$parent.delClass(item)
+        })
+        .catch(() => {
+					console.log("不删除")
+          // on cancel
+        })
+      
+      // this.$parent.delClass(item)
+    },
+    onEdit(item) {
+      if (this.historyListLoading) return this.$toast('请稍等...')
+      if (item.newLabel.trim() == '') {
+        this.$toast('请输入新的课堂名称')
+        return
+      }
+      this.$parent.editClassName(item)
     }
   }
 }
@@ -187,6 +254,7 @@ export default {
   .tit {
     display: flex;
     justify-content: space-between;
+
     div {
       font-weight: 600;
       font-size: 22px;
@@ -223,12 +291,14 @@ export default {
     font-weight: 400;
     font-size: 14px;
     line-height: 22px;
-    -webkit-line-clamp: 4;
-    display: -webkit-box;
-    -webkit-box-orient: vertical;
-    overflow: hidden;
-    text-overflow: ellipsis;
-
+    // -webkit-line-clamp: 4;
+    // display: -webkit-box;
+    // -webkit-box-orient: vertical;
+    // overflow: hidden;
+    // text-overflow: ellipsis;
+		max-height: 150px;
+		min-height: 100px;
+		overflow: auto;
     color: rgba(0, 0, 0, 0.6);
   }
   img {
@@ -298,6 +368,16 @@ export default {
     .tit {
       font-weight: 400;
       font-size: 16px;
+      width: 100%;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+    }
+    .titActive {
+      color: rgba(54, 129, 252, 1);
+    }
+    .editClassName {
+      width: 100%;
     }
   }
 }
@@ -320,4 +400,10 @@ export default {
     }
   }
 }
+
+.ecn_field {
+  :deep(.van-cell__title) {
+    width: 3em;
+  }
+}
 </style>

+ 353 - 14
src/views/classObserve/index.vue

@@ -1,12 +1,37 @@
 <template>
-  <div class="allPage">
-    <homePage @cutPage="cutPage" :page="page" v-if="page == 1"></homePage>
-    <bindFrom @cutPage="cutPage" :page="page" v-if="page == 2"></bindFrom>
-    <classInfo @cutPage="cutPage" :page="page" v-if="page == 3"></classInfo>
-    <stencilled @cutPage="cutPage" :page="page" v-if="page == 5"></stencilled>
-    <editTel @cutPage="cutPage" :page="page" v-if="page == 6"></editTel>
-    <addTel @cutPage="cutPage" :page="page" v-if="page == 7"></addTel>
-
+  <div class="allPage" v-loading="loading">
+    <homePage
+      ref="homePageRef"
+      @cutPage="cutPage"
+      :page="page"
+      v-if="page == 1"
+      :baseMessageLoading="baseMessageLoading"
+      :classList="classList"
+      :tid="tid"
+      :bmData="bmData"
+    ></homePage>
+    <bindFrom ref="bindFromRef" @cutPage="cutPage" :page="page" v-if="page == 2"></bindFrom>
+    <classInfo
+      ref="classInfoRef"
+      @cutPage="cutPage"
+      :page="page"
+			:tid="tid"
+      v-if="page == 3"
+      v-loading="baseMessageLoading"
+      :bmData="bmData"
+      :imageList="imageList"
+    ></classInfo>
+    <stencilled ref="stencilledRef" @cutPage="cutPage" :page="page" v-if="page == 5"></stencilled>
+    <editTel ref="editTelRef" @cutPage="cutPage" :page="page" v-if="page == 6"></editTel>
+    <addTel ref="addTelRef" @cutPage="cutPage" :page="page" v-if="page == 7"></addTel>
+    <outcome
+      ref="outcomeRef"
+      @cutPage="cutPage"
+      :page="page"
+      v-show="page == 8"
+      :loading="[currencyLoading, scienceLoading, extendLoading]"
+      :dataList="dataList"
+    />
   </div>
 </template>
 
@@ -17,21 +42,335 @@ import classInfo from './classInfo'
 import stencilled from './stencilled'
 import editTel from './editTel'
 import addTel from './addTel'
-
-
-
+import outcome from './outcome.vue'
+import { updateObsRequest, getCourseListRequest, getObsRequest, delClassRequest,delObsRequest } from '@/api/classObserve'
 
 export default {
-  components: { homePage, bindFrom, classInfo, stencilled,editTel,addTel },
+  components: { homePage, bindFrom, classInfo, stencilled, editTel, addTel, outcome },
   data() {
     return {
-      page: 1
+      userId: this.$store.state.user.id,
+      page: 1,
+      loading: false,
+      currencyLoading: true,
+      baseMessageLoading: true,
+      scienceLoading: true,
+      extendLoading: true,
+      classList: [],
+      bmData: {
+        id: '',
+        tId: '',
+        tIndex: 0,
+        jsonData: {
+          activity_methods: '',
+          activity_structure: '',
+          classroom_resources: '',
+          courseName: '',
+          name: '',
+          studentNum: 0,
+          subject: '',
+          textbook: ''
+        }
+      },
+      imageList: {},
+      dataList: [],
+      tid: ''
     }
   },
   methods: {
     cutPage(val) {
       this.page = val
-    }
+    },
+    // 获取课堂列表
+    getCourseList() {
+      this.loading = true
+      return new Promise((resolve, reject) => {
+        getCourseListRequest({
+          userid: this.userId
+        })
+          .then(res => {
+            let _data = res.FunctionResponse.result
+            let _result = []
+            try {
+              _result = _data ? JSON.parse(_data) : []
+            } catch (error) {
+              _result = []
+            }
+
+            if (_result.length <= 0) {
+              this.loading = false
+              resolve()
+              return
+            }
+            let _optionData = _result.map(item => {
+              item.jsonData = item.jsonData ? JSON.parse(item.jsonData) : {}
+              return {
+                label: item.jsonData.courseName ? item.jsonData.courseName : `${item.tId}课堂`,
+                value: item.tId,
+                id: item.id,
+                newLabel: item.jsonData.courseName ? item.jsonData.courseName : `${item.tId}课堂`,
+                show: false,
+                edit: false
+              }
+            })
+            _optionData = _optionData.filter(i => i.label != '' && i.tId != '')
+            this.classList = _optionData
+            this.loading = false
+            if (this.classList.length > 0) {
+              this.changeTid(this.classList[0].value)
+            }
+            resolve()
+          })
+          .catch(e => {
+            console.log(e)
+            resolve()
+            this.$message.error('获取课堂列表失败')
+          })
+      })
+    },
+    // 切换课堂
+    changeTid(newTid) {
+      if (this.tid == newTid) return
+      let _flag = this.tid !== newTid
+      this.tid = newTid
+      if (_flag) {
+        this.getData()
+      }
+    },
+    //编辑课堂名称
+    editClassName(item) {
+      let _index = this.classList.findIndex(i => i.id == item.id)
+      if (_index != -1) {
+        if (this.tid == item.value) {
+          //是现在预览的课堂
+          this.$refs.homePageRef.historyListLoading = true
+          this.bmData.jsonData.courseName = item.newLabel
+          this.saveData(this.bmData).then(_ => {
+            this.$refs.homePageRef.historyListLoading = false
+            this.classList[_index].label = item.newLabel
+            this.classList[_index].edit = false
+            this.classList[_index].show = false
+            this.loading = false
+            this.$toast.success('修改成功')
+          })
+        } else {
+          this.$refs.homePageRef.historyListLoading = true
+          getObsRequest({ tid: item.value, type: 0 })
+            .then(res => {
+              let _data = res.FunctionResponse.result
+              _data = JSON.parse(_data)
+              let _bmData = _data.find(i => i.tIndex == 0)
+              _bmData.jsonData = JSON.parse(_bmData.jsonData)
+              _bmData.jsonData.courseName = item.newLabel
+              this.saveData(_bmData).then(_ => {
+                this.classList[_index].label = item.newLabel
+                this.classList[_index].edit = false
+                this.classList[_index].show = false
+                this.loading = false
+                this.$refs.homePageRef.historyListLoading = false
+                this.$toast.success('修改成功')
+              })
+            })
+            .catch(e => {
+              console.log(e)
+              this.$toast.fail('修改失败')
+            })
+        }
+      }
+    },
+    //删除课堂
+    delClass(item) {
+      this.$refs.homePageRef.historyListLoading = true
+      delClassRequest({ tid: item.id }).then(_ => {
+        this.$toast.success('删除成功')
+        this.getCourseList().then(_ => {
+          if (this.tid == item.value || (this.tid == '' && this.classList.length > 0)) {
+            this.changeTid(this.classList[0].value)
+          }
+          this.$refs.homePageRef.historyListLoading = false
+        })
+      })
+    },
+    //保存分析数据
+    saveData(params) {
+      return new Promise((resolve, reject) => {
+        updateObsRequest({
+          id: params.id,
+          json_data: JSON.stringify(params.jsonData)
+        })
+          .then(res => {
+            resolve()
+          })
+          .catch(e => {
+            this.$message.error('保存失败')
+            resolve()
+          })
+      })
+    },
+    //获取通用分析与基本信息
+    getCurrencyAndBaseMessageData() {
+      return new Promise(resolve => {
+        if (!this.tid) return
+        this.loading = true
+        this.currencyLoading = true
+        this.baseMessageLoading = true
+        getObsRequest({ tid: this.tid, type: '0' })
+          .then(res => {
+            let _data = res.FunctionResponse.result.length ? JSON.parse(res.FunctionResponse.result) : []
+            let _bmData = _data.find(i => i.tIndex == 0)
+            // 基础信息
+            _bmData.jsonData = JSON.parse(_bmData.jsonData)
+						_bmData.jsonData.time? "":_bmData.jsonData.time = "";
+            // 图片
+            let _imageList = _data.find(i => i.tIndex == 1)
+            _imageList.jsonData = JSON.parse(_imageList.jsonData)
+
+            if (!_imageList.jsonData.videoList) {
+              _imageList.jsonData.videoList = []
+            }
+            if (!_imageList.jsonData.NephogramList) {
+              _imageList.jsonData.NephogramList = []
+            }
+
+            let currency = []
+            for (let i = 2; i < _data.length; i++) {
+              let _currency = _data[i]
+              _currency.jsonData = JSON.parse(_currency.jsonData)
+              currency.push(_currency)
+            }
+            // 赋值
+            this.dataList.push(...currency)
+            this.bmData = _bmData
+            this.imageList = _imageList
+
+            this.baseMessageLoading = false
+            this.currencyLoading = false
+            this.loading = false
+            resolve()
+          })
+          .catch(e => {
+            console.log(e)
+            this.$toast.fail('获取通用分析失败')
+            resolve()
+          })
+      })
+    },
+    //获取学科分析
+    getScienceData() {
+      return new Promise(resolve => {
+        if (!this.tid) return
+        this.scienceLoading = true
+        getObsRequest({ tid: this.tid, type: '1' })
+          .then(res => {
+            let _data = res.FunctionResponse.result.length ? JSON.parse(res.FunctionResponse.result) : []
+            if (_data.length == 0) {
+              return (this.scienceLoading = false)
+            }
+            let science = []
+            for (let i = 0; i < _data.length; i++) {
+              let _science = _data[i]
+              _science.jsonData = JSON.parse(_science.jsonData)
+              science.push(_science)
+            }
+            this.dataList.push(...science)
+            this.scienceLoading = false
+            resolve()
+          })
+          .catch(e => {
+            console.log(e)
+            this.$toast.fail('获取科学分析失败')
+            resolve()
+          })
+      })
+    },
+    //获取扩展分析
+    getExtendData() {
+      return new Promise(resolve => {
+        if (!this.tid) return
+        this.extendLoading = true;
+				getObsRequest({tid:this.tid,type:"2"}).then(res=>{
+					let _data = res.FunctionResponse.result.length
+						? JSON.parse(res.FunctionResponse.result)
+						: [];
+					if (_data.length == 0) {
+						return (this.extendLoading = false);
+					}
+					let extent = [];
+					for (let i = 0; i < _data.length; i++) {
+						let _extent = _data[i];
+						_extent.jsonData = JSON.parse(_extent.jsonData);
+						extent.push(_extent);
+					}
+					this.dataList.push(...extent);
+					this.extendLoading = false;
+					resolve();
+				}).catch((e) => {
+					console.log(e);
+					this.$toast("获取扩展分析失败")
+					resolve();
+				});
+      })
+    },
+		//获取全部的数据
+    getData() {
+			this.dataList = [];
+			this.imageList = {};
+      return new Promise(resolve => {
+        Promise.all([this.getCurrencyAndBaseMessageData(),this.getScienceData(),this.getExtendData()]).then(_ => {
+          console.log('所有分析获取完成')
+					resolve();
+        })
+      })
+    },
+		//删除分析
+		delAnalysis(item){
+			if(!this.tid)return;
+			this.currencyLoading = true;
+			this.baseMessageLoading = true;
+      this.scienceLoading = true;
+      this.extendLoading = true;
+			delObsRequest({
+				id:item.id,
+				type:item.Type,
+				tid:this.tid
+			}).then(res=>{
+				this.dataList = this.dataList.filter(_item=>_item.id!=item.id);
+				this.$toast.success('删除成功');
+				this.currencyLoading = false;
+				this.baseMessageLoading= false;
+				this.scienceLoading= false;
+				this.extendLoading= false;
+			}).catch(e=>{
+				this.$toast.fail('删除失败');
+				this.currencyLoading = false;
+				this.baseMessageLoading= false;
+				this.scienceLoading= false;
+				this.extendLoading= false;
+			})
+		},
+		//修改分析
+		editAnalysis(item){
+			if(!this.tid)return;
+			this.baseMessageLoading= true;
+      this.scienceLoading= true;
+      this.extendLoading= true;
+			editObsRequest({
+				id:item.id,
+				type:item.Type,
+				tid:this.tid
+			}).then(res=>{
+			})
+		}
+  },
+  mounted() {
+    this.getCourseList().then(_ => {
+      // if(this.tid=="" && this.classList.length>0){
+      // 	this.changeTid(this.classList[0].value);
+
+      // }
+      // this.getData()
+    })
+    // console.log(getClassList)
   }
 }
 </script>

+ 56 - 52
src/views/classObserve/outcome.vue

@@ -1,56 +1,22 @@
 <template>
   <div class="outcome">
-    <bar @cutPage="cutPage" :tit="'分析结果'" :backPage="1"></bar>
-    <div style="height: 55px;width: 100%;"></div>
-
+    <bar @cutPage="cutPage" :tit="'分析结果'" :backPage="0"></bar>
     <div class="sortList">
-      <div>通用</div>
-      <div>学科</div>
-      <div>扩展</div>
+      <div :class="[showIndex==0?'sortActive':'']" @click.stop="changeShowIndex(0)">通用</div>
+      <div :class="[showIndex==1?'sortActive':'']" @click.stop="changeShowIndex(1)">学科</div>
+      <div :class="[showIndex==2?'sortActive':'']" @click.stop="changeShowIndex(2)">扩展</div>
     </div>
-    <div class="stencilList">
-      <div class="outcomeCon">
-        <div @click="ctrlShow" style="height: 50px;display: flex; justify-content: space-between;align-items: center;">
-          <div>
-            <van-icon v-if="!cellShow" name="arrow" />
-            <van-icon v-else name="arrow-down" />
-            <span class="tit">麦卡锡问题分析</span>
-          </div>
-          <van-popover placement="bottom-end" v-model="abuShow">
-            <div class="abu">
-              <div class="abuBtn">
-                <img src="../../assets/images/classObserve/addTel.png" alt="" />
-                <span>重命名</span>
-              </div>
-              <div class="abuBtn">
-                <img src="../../assets/images/classObserve/del.png" alt="" />
-                <span>删除</span>
-              </div>
-            </div>
-            <template #reference>
-              <img @click.stop="showAbu" src="../../assets/images/classObserve/colD.png" alt="" />
-            </template>
-          </van-popover>
-        </div>
-        <div v-if="cellShow">
-          <div>
-            6666
-          </div>
-          <div class="outcomeBtn">
-            <div><img src="../../assets/images/classObserve/revoke.png" alt="" /><span>撤销</span></div>
-            <div><img src="../../assets/images/classObserve/restore.png" alt="" /><span>恢复</span></div>
-            <div><img src="../../assets/images/classObserve/optimize.png" alt="" /><span>优化</span></div>
-          </div>
-        </div>
-      </div>
+		
+    <div class="stencilList" v-loading="loading[showIndex]">
+			<analysisItem ref="analysisItemRef" v-for="(item,index) in dataList" :key="item.id" :data="item" v-show="showIndex === item.Type" v-if="!(item.Type==0 && item.tIndex==2)"/>
     </div>
 
     <div class="botBtn">
-      <div class="shareBtn">
-        <img src="../../assets/images/classObserve/share.png" alt="" />
+      <div class="shareBtn" @click.stop="share()">
+        <img src="../../assets/images/classObserve/share.png" alt=""/>
         <span>分享</span>
       </div>
-      <div class="btn">
+      <div class="btn" @click.stop="viewTheReport()">
         查看报告
       </div>
     </div>
@@ -59,41 +25,72 @@
 
 <script>
 import bar from './components/bar.vue'
-
+import analysisItem from './components/analysisItem.vue'
+import { Dialog } from 'vant';
 export default {
   components: {
-    bar
+    bar,
+		analysisItem
   },
   props: {
     page: {
       type: Number,
       default: 1
-    }
+    },
+		dataList:{
+			type:Array,
+			default:()=>[]
+		},
+		loading:{
+			type:Array,
+			default:()=>[true,true,true]
+		}
   },
   data() {
     return {
       abuShow: false,
-      cellShow: false
+      cellShow: false,
+			showIndex:0,
     }
   },
   methods: {
     cutPage() {
-      this.$emit('cutPage', 6)
+      this.$emit('cutPage', 1)
     },
     ctrlShow() {
       this.cellShow = !this.cellShow
     },
     showAbu() {
       this.abuShow = true
-    }
+    },
+		changeShowIndex(newIndex){
+			this.showIndex = newIndex;
+		},
+		delItem(item){
+			Dialog.confirm({
+        title: '删除分析',
+        message: `确定删除 "${item.jsonData.name}" 这个分析?`
+      }).then(() => {
+				this.$parent.delAnalysis(item);
+      }).catch(() => {
+				console.log("不删除")
+        // on cancel
+      })
+		},
+		share(){
+			console.log("分享")
+		},
+		viewTheReport(){
+			console.log("查看报告");
+		}
   }
 }
 </script>
 
 <style lang="scss" scoped>
 .outcome {
-  height: 100vh;
-  display: flex;
+	height: calc(100% - 50px);
+	display: flex;
   flex-direction: column;
   justify-content: space-between;
   box-sizing: border-box;
@@ -103,6 +100,7 @@ export default {
   display: flex;
   box-sizing: border-box;
   justify-content: space-between;
+	margin-top: 55px;
   padding: 15px;
   border-bottom: 0.5px rgba(231, 231, 231, 1) solid;
   div {
@@ -110,6 +108,8 @@ export default {
     text-align: center;
     font-size: 16px;
     font-weight: 400;
+		transition: .3s;
+		position: relative;
   }
   .sortImg {
     display: flex;
@@ -117,6 +117,10 @@ export default {
     justify-content: center;
     border-left: 0.5px rgba(231, 231, 231, 1) solid;
   }
+	.sortActive{
+		color: #3681FC;
+		font-weight: bold;
+	}
 }
 .stencilList {
   flex: 1;

+ 8 - 4
src/views/layouts/index.vue

@@ -27,7 +27,8 @@ export default {
           },
           icon: 'home-o'
         }
-      ]
+      ],
+			orgArray:["45facc0a-1211-11ec-80ad-005056b86db5","414f2361-ad04-11ed-b13d-005056b86db5"]
     }
   },
   components: {
@@ -60,15 +61,18 @@ export default {
           activeIcon: require('../../assets/images/course/commmt-active1.png'),
           normalIcon: require('../../assets/images/course/comment1.png')
         },
-        {
+
+      )
+			if(this.orgArray.includes(this.$store.state.user.userinfo.organizeid)){
+				this.tabbars.push(        {
           title: '课堂观察',
           to: {
             path: '/classObserve'
           },
           activeIcon: require('../../assets/images/course/commmt-active1.png'),
           normalIcon: require('../../assets/images/course/comment1.png')
-        }
-      )
+        })
+			}
     }
   }
 }

Деякі файли не було показано, через те що забагато файлів було змінено