SanHQin 11 місяців тому
батько
коміт
be8dd622bf

+ 2 - 1
package.json

@@ -27,6 +27,7 @@
     "lib-flexible": "^0.3.2",
     "lodash": "^4.17.15",
     "nprogress": "^0.2.0",
+    "opencc-js": "^1.0.5",
     "regenerator-runtime": "^0.13.5",
     "vant": "^2.12.7",
     "vconsole": "^3.3.4",
@@ -52,7 +53,7 @@
     "eslint": "^6.7.2",
     "eslint-plugin-prettier": "^3.1.1",
     "eslint-plugin-vue": "^6.2.2",
-    "node-sass": "^4.13.1",
+    "node-sass": "^4.14.1",
     "postcss-pxtorem": "^4.0.1",
     "prettier": "^1.19.1",
     "sass-loader": "^8.0.2",

+ 81 - 0
src/api/classObserve.js

@@ -47,6 +47,24 @@ export function delObsRequest(data){//删除分析数据
   })
 }
 
+export function insertNewClassRoomRequest(data){//通过模板创建新的课堂
+	return request2({
+    url: 'https://gpt4.cocorobo.cn/insert_classroom_observation_template',
+    method: 'post',
+    data,
+    hideloading: true
+  })
+}
+
+export function insertNewObsRequest(data){//创建新的分析
+	return request2({
+    url: 'https://gpt4.cocorobo.cn/insert_classroom_observation',
+    method: 'post',
+    data,
+    hideloading: true
+  })
+}
+
 
 export function chatNoStreamRequest(data){//不用流式传输的chat
 	return request2({
@@ -58,3 +76,66 @@ export function chatNoStreamRequest(data){//不用流式传输的chat
 }
 
 
+export function getBindFormDataRequest(params){//获取绑定表达的数据
+	return request({
+		url: '/selectTesttCourse3',
+    method: 'get',
+    params,
+    hideloading: true
+	})
+
+}
+
+export function getClassroomTemplateRequest(params){//获取模板数据
+	return request({
+		url: '/selectClassroomTemplate',
+    method: 'get',
+    params,
+    hideloading: true
+	})
+}
+
+export function getClassroomDefaultRequest(params){//获取默认模板数据
+	return request({
+		url: '/selectClassroomDefault',
+    method: 'get',
+    params,
+    hideloading: true
+	})
+}
+
+export function getClassroomTemplateDetailRequest(params){//查询模板数据
+  return request({
+		url: '/selectClassroomTemplateDetail',
+    method: 'get',
+    params,
+    hideloading: true
+	})
+}
+
+export function updateClassroomTemplateCollectRequest(data){//修改收藏模板状态
+	return request({
+		url: '/updateClassroomTemplateCollect',
+    method: 'post',
+    data,
+    hideloading: true
+	})
+}
+
+export function updateClassroomTemplateDataRequest(data){//修改模板数据
+	return request({
+		url: '/updateClassroomTemplateData',
+    method: 'post',
+    data,
+    hideloading: true
+	})
+}
+
+export function updateClassroomDefaultRequest(data){//修改模板数据
+	return request({
+		url: '/updateClassroomDefault',
+    method: 'post',
+    data,
+    hideloading: true
+	})
+}

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

@@ -0,0 +1,3 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M8.00001 1.5C8.1894 1.5 8.36253 1.61128 8.44723 1.78745L10.3276 5.69853L14.062 6.18401C14.2515 6.20865 14.411 6.34338 14.4729 6.53124C14.5349 6.71909 14.4886 6.92726 14.3536 7.06769L11.5537 9.9795L12.4851 13.8539C12.5302 14.0413 12.4715 14.2393 12.3329 14.368C12.1942 14.4967 11.9984 14.5347 11.8245 14.4669L8.00001 12.9754L4.17558 14.4669C4.0016 14.5347 3.80578 14.4967 3.66714 14.368C3.52849 14.2393 3.46988 14.0413 3.51494 13.8539L4.44628 9.9795L1.64646 7.06769C1.51143 6.92726 1.4651 6.71909 1.52708 6.53124C1.58905 6.34338 1.7485 6.20865 1.938 6.18401L5.67247 5.69853L7.5528 1.78745C7.6375 1.61128 7.81063 1.5 8.00001 1.5ZM8.00001 3.18275L6.44723 6.41255C6.37246 6.56806 6.22792 6.67442 6.06203 6.69598L3.07646 7.08411L5.35357 9.4523C5.47776 9.58146 5.52768 9.76891 5.48509 9.94611L4.71603 13.1454L7.82445 11.9331C7.93764 11.889 8.06238 11.889 8.17558 11.9331L11.284 13.1454L10.5149 9.94611C10.4723 9.76891 10.5223 9.58146 10.6465 9.4523L12.9236 7.08411L9.938 6.69598C9.77211 6.67442 9.62756 6.56806 9.5528 6.41255L8.00001 3.18275Z" fill="black" fill-opacity="0.4"/>
+</svg>

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

@@ -0,0 +1,3 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M8 2L10.3041 5.82865L14.6574 6.83688L11.7281 10.2113L12.1145 14.6631L8 12.92L3.8855 14.6631L4.27186 10.2113L1.3426 6.83688L5.69588 5.82865L8 2Z" fill="#F6C826" stroke="#F6C826" stroke-linejoin="round"/>
+</svg>

+ 6 - 0
src/assets/images/classObserve/del.svg

@@ -0,0 +1,6 @@
+<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M6.25 3.125C6.25 2.77982 6.52982 2.5 6.875 2.5H13.125C13.4702 2.5 13.75 2.77982 13.75 3.125C13.75 3.47018 13.4702 3.75 13.125 3.75H6.875C6.52982 3.75 6.25 3.47018 6.25 3.125Z" fill="black" fill-opacity="0.6"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M2.5 5.625C2.5 5.26481 2.77982 5 3.125 5H16.875C17.2202 5 17.5 5.26481 17.5 5.625C17.5 6.03952 17.2202 6.25 16.875 6.25L15.625 6.30435V17.5H4.375V6.25H3.125C2.77982 6.25 2.5 6.03952 2.5 5.625ZM5.625 6.25V16.1957L14.375 16.25V6.30435L5.625 6.25Z" fill="black" fill-opacity="0.6"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M8.125 8.06838C8.47018 8.06838 8.75 8.32276 8.75 8.63656V13.7502C8.75 14.064 8.47018 14.3184 8.125 14.3184C7.77982 14.3184 7.5 14.064 7.5 13.7502V8.63656C7.5 8.32276 7.77982 8.06838 8.125 8.06838Z" fill="black" fill-opacity="0.6"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M11.875 8.06838C12.2202 8.06838 12.5 8.32276 12.5 8.63656V13.7502C12.5 14.064 12.2202 14.3184 11.875 14.3184C11.5298 14.3184 11.25 14.064 11.25 13.7502V8.63656C11.25 8.32276 11.5298 8.06838 11.875 8.06838Z" fill="black" fill-opacity="0.6"/>
+</svg>

+ 7 - 6
src/router/router.config.js

@@ -46,14 +46,15 @@ export const constantRouterMap = [
         component: () => import('@/views/eva/index'),
         meta: { title: '学生评价', keepAlive: false }
       },
-      {
-        path: 'classObserve',
-        name: 'classObserve',
-        component: () => import('@/views/classObserve/index'),
-        meta: { title: '课堂观察', keepAlive: false }
-      }
+      
     ]
   },
+	{
+		path: '/classObserve',
+		name: 'classObserve',
+		component: () => import('@/views/classObserve/index'),
+		meta: { title: '课堂观察', keepAlive: false }
+	},
   {
     path: '/aiChat',
     component: () => import('@/views/classObserve/aiChat'),

+ 88 - 3
src/views/classObserve/addTel.vue

@@ -48,19 +48,104 @@ export default {
   props: {
     page: {
       type: Number,
-      default: 1
+      default: 1,
+			tagIndex:0,
     }
   },
   data() {
     return {
-      selectValue: ''
+      selectValue: '',
+			dataList:[],
+			tagSubject:"",
     }
   },
   methods: {
     cutPage() {
       this.$emit('cutPage', 6)
     },
-    
+		getMyselfData() {
+			this.loading = true;
+			this.templateData = [];
+			let params = {
+				uid: this.userId,
+				txt: this.text,
+				sub: this.tagSubject,
+				type1: 1,
+			};
+			this.ajax
+				.get(this.$store.state.api + "selectClassroomTemplate", params)
+				.then((res) => {
+					let _data = res.data[0];
+					this.templateData = _data;
+				})
+				.catch((e) => {
+					console.log(e);
+					this.$message.error("获取数据失败");
+				})
+				.finally(() => {
+					this.loading = false;
+				});
+		},
+		getCollectData(flag = true) {
+			this.loading = true;
+			if (flag) {
+				this.templateData = [];
+			}
+			let params = {
+				uid: this.userId,
+				txt: this.selectValue,
+				sub: this.tagSubject,
+				type1: 2,
+			};
+			this.ajax
+				.get(this.$store.state.api + "selectClassroomTemplate", params)
+				.then((res) => {
+					let _data = res.data[0];
+					if (flag) {
+						this.templateData = _data;
+					}
+					this.collectData = _data;
+				})
+				.catch((e) => {
+					console.log(e);
+					this.$message.error("获取数据失败");
+				})
+				.finally(() => {
+					this.loading = false;
+				});
+		},
+		getPublicData() {
+			this.loading = true;
+			this.templateData = [];
+			let params = {
+				uid: this.userId,
+				txt: this.text,
+				sub: this.tagSubject,
+				type1: 0,
+			};
+			this.ajax
+				.get(this.$store.state.api + "selectClassroomTemplate", params)
+				.then((res) => {
+					let _data = res.data[0];
+					this.templateData = _data;
+				})
+				.catch((e) => {
+					console.log(e);
+					this.$message.error("获取数据失败");
+				})
+				.finally(() => {
+					this.loading = false;
+				});
+		},
+		getData() {
+			if (this.tagIndex == 0) {
+				this.getPublicData();
+			} else if (this.tagIndex == 1) {
+				this.getMyselfData();
+			} else if (this.tagIndex == 2) {
+				this.getCollectData();
+			}
+		},
   }
 }
 </script>

+ 118 - 10
src/views/classObserve/bindFrom.vue

@@ -1,18 +1,18 @@
 <template>
-  <div class="bindFrom">
+  <div class="bindFrom" v-loading="loading">
     <bar @cutPage="cutPage" :tit="'绑定表单'" :backPage="0"></bar>
     <div style="height: 55px;width: 100%;"></div>
     <div class="selectInp">
       <van-search shape="round" v-model="selectValue" placeholder="搜索表单" />
-      <span>搜索</span>
+      <span @click.stop="search()">搜索</span>
     </div>
-    <div class="con">
-      <div class="conBlock" v-for="item in 30" :key="item">
-        <fromList></fromList>
+    <div class="con"  ref="listRef" @scroll="handlerScroll($event)">
+      <div class="conBlock" v-for="(item, index) in dataList" :key="index" @click.stop="choice(item)">
+        <fromList :data="item" :index="index + 1" :isChoice="choiceCourseId ? choiceCourseId : testData.testId" />
       </div>
     </div>
-    <div class="bot">
-      <div class="botBtn">确认绑定</div>
+    <div class="bot" v-loading="loading">
+      <div :class="['botBtn', choiceCourseId ? '' : 'botBtnDisabled']" @click.stop="bindFrom()">确认绑定</div>
     </div>
     <!-- <div style="height: 50px;width: 100%;"></div> -->
   </div>
@@ -21,6 +21,7 @@
 <script>
 import bar from './components/bar.vue'
 import fromList from './components/fromList'
+import { getBindFormDataRequest } from '../../api/classObserve'
 
 export default {
   components: {
@@ -28,20 +29,121 @@ export default {
     fromList
   },
   props: {
+    tid: {
+      type: String,
+      default: ''
+    },
     page: {
       type: Number,
       default: 1
+    },
+    testData: {
+      type: Object,
+      default: () => {}
     }
   },
   data() {
     return {
-      selectValue: ''
+      selectValue: '',
+      dataList: [],
+      loading: false,
+      choiceCourseId: '',
+      oid: this.$store.state.user.userinfo.organizeid,
+      org: this.$store.state.user.userinfo.org,
+      userId: this.$store.state.user.userinfo.userid,
+      PageData: {
+        page: 1,
+        pageSize: 10
+      }
+    }
+  },
+  watch: {
+    selectValue(newValue) {
+      this.flag = false
     }
   },
   methods: {
+    choice(item) {
+      this.choiceCourseId = item.courseId
+    },
     cutPage() {
       this.$emit('cutPage', 1)
+    },
+    search() {
+      this.PageData.page = 1
+      this.flag = true
+      this.dataList = []
+      this.getData()
+    },
+    bindFrom() {
+      if (!this.choiceCourseId || this.loading) return
+      let data = this.dataList.find(i => i.courseId == this.choiceCourseId)
+      if (!data) return
+      if (data.courseId == this.testData.testId) {
+        return this.$toast.fail('该课堂已绑定该表单')
+      }
+      let _result = {
+        testId: data.courseId,
+        checkUrl: `https://beta.pbl.cocorobo.cn/pbl-teacher-table/dist/#/checkToTest?cid=${data.courseId}&userid=${this.userId}&oid=${this.oid}&org=${this.org}&tcid=${this.tid}&isN=1&role=0&type=2,`,
+        doUrl: `https://beta.pbl.cocorobo.cn/pbl-teacher-table/dist/#/doTest?cid=${data.courseId}&userid=${this.userId}&oid=${this.oid}&org=${this.org}&tcid=${this.tid}&isN=1&role=0&type=2`
+      }
+      this.loading = true
+      this.$parent.bmData.jsonData.testData = _result
+      this.$parent.saveData(this.$parent.bmData).then(_ => {
+        this.$toast.success('绑定表单成功')
+        this.loading = false
+      })
+    },
+    getData(type = 0) {
+      if (this.loading) return
+      if (type == 1) {
+        this.PageData.page += 1
+      }
+      let params = {
+        ype: 4,
+        uid: this.userId,
+        oid: this.oid,
+        org: this.org,
+        typea: '',
+        typeb: '',
+        typec: '',
+        typed: '',
+        typef: '',
+        typeE: '',
+        cu: '',
+        cn: this.flag ? this.selectValue : '',
+        page: this.PageData.page,
+        pageSize: this.PageData.pageSize
+      }
+      this.loading = true
+      getBindFormDataRequest(params)
+        .then(res => {
+          let _data = res[0]
+          if (_data.length > 0) {
+            this.dataList.push(..._data)
+          } else {
+            this.PageData.page -= 1
+          }
+          this.loading = false
+        })
+        .catch(e => {
+          this.$toast.fail('获取表单失败')
+          this.loading = false
+        })
+    },
+    handlerScroll(e) {
+      const dom = e.target
+      var scrollTop = dom.scrollTop //滑入屏幕上方的高度
+      var windowHeitht = dom.clientHeight //能看到的页面的高度
+      var scrollHeight = dom.scrollHeight //监控的整个div的高度(包括现在看到的和上下隐藏起来看不到的)
+      let total = scrollTop + windowHeitht + 1
+      if (total >= scrollHeight) {
+        this.getData(1)
+      }
     }
+  },
+  mounted() {
+    this.getData()
   }
 }
 </script>
@@ -51,7 +153,7 @@ export default {
   display: flex;
   flex-direction: column;
   justify-content: space-between;
-  height: calc(100% - 50px);
+  height: calc(100%);
 }
 .selectInp {
   width: 100%;
@@ -63,7 +165,7 @@ export default {
   align-items: center;
   .van-search {
     width: 100%;
-    .van-search__content{
+    .van-search__content {
       height: 45px;
       align-items: center;
     }
@@ -103,6 +205,12 @@ export default {
     font-size: 16px;
     font-weight: 600;
     color: rgba(255, 255, 255, 0.9);
+    transition: 0.3s;
+  }
+
+  .botBtnDisabled {
+    background-color: rgb(172, 172, 172);
+    color: #fff;
   }
 }
 </style>

+ 1 - 1
src/views/classObserve/classInfo.vue

@@ -802,7 +802,7 @@ ${this.bmData.jsonData.editorBarData ? this.bmData.jsonData.editorBarData.conten
 <style lang="scss" scoped>
 .classInfo {
   padding: 0 20px;
-  height: calc(100% - 50px);
+  height: calc(100%);
   box-sizing: border-box;
   // margin-top: 15px;
   display: flex;

+ 27 - 4
src/views/classObserve/components/fromList.vue

@@ -1,13 +1,27 @@
 <template>
-  <div class="fromList">
-    <div style="text-align: center;width: 15%;" class="fromCol">01</div>
-    <div  class="fromCol" style="flex: 1;font-size: 16px;font-weight: 600;">智能音箱</div>
-    <div  class="perName">袁一鸣</div>
+  <div class="fromList" :class="{'choice':isChoice == data.courseId}">
+    <div style="text-align: center;width: 15%;" class="fromCol">{{ index }}</div>
+    <div  class="fromCol" style="flex: 1;font-size: 16px;font-weight: 600;">{{data.title}}</div>
+    <div  class="perName">{{data.uname}}</div>
   </div>
 </template>
 
 <script>
 export default {
+	props:{
+		data:{
+			type: Object,
+			default: () => {}
+		},
+		index:{
+			type: Number,
+			default:0
+		},
+		isChoice:{
+			type:String,
+			default:""
+		}
+	},
   data() {
     return {}
   }
@@ -25,6 +39,10 @@ export default {
   .fromCol {
     color: rgba(54, 129, 252, 1);
     font-size: 24px;
+		width: 80%;
+		overflow: hidden;
+		text-overflow: ellipsis;
+		white-space: nowrap;
   }
   .perName{
     width: 20%;
@@ -32,4 +50,9 @@ export default {
     color: rgba(0, 0, 0, 0.6);
   }
 }
+
+.choice{
+	// border-color: rgba(54, 129, 252, 1);
+	border: 2px rgba(54, 129, 252, 1) solid;
+}
 </style>

+ 41 - 10
src/views/classObserve/components/stencilledCon.vue

@@ -1,31 +1,57 @@
 <template>
-  <div class="stencilledCon">
+  <div :class="['stencilledCon',isChoice?'stencilledConActive':'']">
     <div class="top">
-      <div class="tit"><img src="../../../assets/images/classObserve/science.png" alt="" /> 科学通用模版</div>
+      <div class="tit"><img src="../../../assets/images/classObserve/science.png" alt="" />{{ data.name }}</div>
       <div class="btn">
-        <img src="../../../assets/images/classObserve/collSatrY.png" alt="" />
-        <div class="btnTxt" @click="PerTel">预览</div>
+        <img src="../../../assets/images/classObserve/collect1.svg" alt="" v-if="collectData.findIndex(i=>i.id==data.id)==-1" @click.stop="collect(0)"/>
+				<img src="../../../assets/images/classObserve/collect2.svg" alt="" v-else @click.stop="collect(1)"/>
+        <div class="btnTxt" @click.stop="PerTel">预览</div>
       </div>
     </div>
     <div class="con">
-      针对性分析科学课堂,包含通用分析模块以及科学学科分析模块,并采用5E和5EX课程设计模型对课堂进行改编。
+      {{ data.detail }}
     </div>
     <div class="bom">
-      <div>14人已使用</div>
-      <div>科学</div>
+      <div>{{ data.use }}人已使用</div>
+      <div>{{ tagSubjectList.find(i=>i.value==data.type)?tagSubjectList.find(i=>i.value==data.type).label:'' }}</div>
     </div>
   </div>
 </template>
 
 <script>
 export default {
+	props:{
+		data:{
+			type:Object,
+			default:{},
+		},
+		tagSubjectList:{
+			type:Array,
+			default:()=>[]
+		},
+		collectData:{
+			type:Array,
+			default:[]
+		},
+		isChoice:{
+			type:Boolean,
+			default:false
+		}
+	},
   data() {
-    return {}
+    return {
+
+		}
   },
   methods: {
     PerTel() {
-      this.$emit('previewTel')
-    }
+			this.$parent.previewTel(this.data)
+      // this.$emit('previewTel')
+    },
+		collect(type){
+			this.$parent.collect(type,this.data)
+			// this.$toast("收藏或不收藏")
+		},
   }
 }
 </script>
@@ -37,6 +63,11 @@ export default {
   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);
+	transition: .3s;
+	border: solid 2px #fff;
+}
+.stencilledConActive{
+	border-color: #3681FC;
 }
 .top {
   display: flex;

+ 5 - 2
src/views/classObserve/editTel.vue

@@ -87,14 +87,17 @@ export default {
     },
     saveTel() {
       this.showPopup = true
-    }
+    },
+		getData(){
+
+		},
   }
 }
 </script>
 
 <style lang="scss" scoped>
 .editTel {
-  height: calc(100% - 50px);
+  height: calc(100%);
   display: flex;
   flex-direction: column;
   justify-content: space-between;

+ 39 - 3
src/views/classObserve/homePage.vue

@@ -2,7 +2,8 @@
   <div class="observe">
     <div class="top">
       <div class="Title">
-        课堂观察
+        <div>课堂观察</div>
+				<span @click.stop="exitLogin">退出</span>
       </div>
       <div>
         <img src="../../assets/images/classObserve/CocoClass.png" alt="" />
@@ -101,6 +102,7 @@
 
 <script>
 import { Dialog } from 'vant';
+import { loginOut } from '@/api/user'
 export default {
   props: {
     page: {
@@ -186,7 +188,34 @@ export default {
         return
       }
       this.$parent.editClassName(item)
-    }
+    },
+		exitLogin(){
+			this.$dialog({
+        message: '是否退出' + this.$store.state.user.userinfo.username + '账号',
+        showCancelButton: true,
+        beforeClose: (action, done) => {
+          if (action === 'confirm') {
+            loginOut()
+              .then(res => {
+                this.$toast({
+                  message: '退出成功',
+                  type: 'success'
+                })
+                this.$store.dispatch('user/logout')
+                window.location.reload()
+                done()
+              })
+              .catch(err => {
+                console.error(err)
+                done()
+              })
+          } else {
+            // 拦截取消操作
+            done()
+          }
+        }
+      })
+		}
   }
 }
 </script>
@@ -194,7 +223,7 @@ export default {
 <style lang="scss" scoped>
 .observe {
   background-color: #e0eafb;
-  height: calc(100% - 50px);
+  height: calc(100%);
   display: flex;
   flex-direction: column;
   justify-content: space-between;
@@ -226,6 +255,13 @@ export default {
     justify-content: center;
     align-items: center;
     height: 30px;
+		position: relative;
+		width: 100%;
+		span{
+			position: absolute;
+			right: 20px;
+			font-size: 16px;
+		}
   }
 }
 .brief {

+ 109 - 6
src/views/classObserve/index.vue

@@ -10,7 +10,7 @@
       :tid="tid"
       :bmData="bmData"
     ></homePage>
-    <bindFrom ref="bindFromRef" @cutPage="cutPage" :page="page" v-if="page == 2"></bindFrom>
+    <bindFrom ref="bindFromRef" @cutPage="cutPage" :page="page" v-if="page == 2" :testData="bmData.jsonData.testData?bmData.jsonData.testData:{}"></bindFrom>
     <classInfo
       ref="classInfoRef"
       @cutPage="cutPage"
@@ -21,8 +21,8 @@
       :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>
+    <stencilled ref="stencilledRef" @cutPage="cutPage" :page="page" v-if="page == 5 && userId" ></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"
@@ -43,7 +43,8 @@ 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'
+import { v4 as uuidv4 } from "uuid";
+import { updateObsRequest, getCourseListRequest, getObsRequest, delClassRequest,delObsRequest,getClassroomDefaultRequest,insertNewClassRoomRequest,insertNewObsRequest } from '@/api/classObserve'
 
 export default {
   components: { homePage, bindFrom, classInfo, stencilled, editTel, addTel, outcome },
@@ -116,7 +117,7 @@ export default {
             _optionData = _optionData.filter(i => i.label != '' && i.tId != '')
             this.classList = _optionData
             this.loading = false
-            if (this.classList.length > 0) {
+            if (this.classList.length > 0 && !this.tid) {
               this.changeTid(this.classList[0].value)
             }
             resolve()
@@ -360,7 +361,109 @@ export default {
 				tid:this.tid
 			}).then(res=>{
 			})
-		}
+		},
+		//获取默认模板并创建课堂
+		addNewCourseByDefault() {
+			// 通过用户ID获取默认的模板
+			var OpenCC = require("opencc-js");
+			let converter = OpenCC.Converter({
+				from: "hk",
+				to: "cn",
+			});
+			return new Promise((resolve,reject)=>{
+				let params = {
+				uid: this.userId,
+			};
+			this.loading = true;
+			getClassroomDefaultRequest(params).then((res) => {
+				let _data = res[0][0];
+				if (_data) {
+					this.loading = true;
+					_data.tips = JSON.parse(_data.tips);
+					let _result = [];
+					_data.tips.forEach((i) => {
+						if(converter("词频词汇分析")==converter(i.jsonData.name))return;
+						let _obj = {
+							jsonData: i.jsonData,
+							type: i.Type,
+							index: i.tIndex,
+						};
+						_result.push(_obj);
+					});
+					this.loading = false;
+					// console.log('👇要生成的模板')
+					// console.log(_result)
+					this.addNewCourseByTemplate(_result).then(_=>{
+						resolve();
+					})
+				}else{
+					this.loading = false;
+					this.$toast.fail("获取模板详细为空");
+				}
+			});
+			})
+			
+		},
+		//通过模板创建新的课堂
+		addNewCourseByTemplate(json = []) {
+			if (json.length <= 0) return;
+			var OpenCC = require("opencc-js");
+			let converter = OpenCC.Converter({
+				from: "hk",
+				to: "cn",
+			});
+			// json = json.filter(i=>converter(i.jsonData.name)!=converter("词频词汇分析"))
+			return new Promise((resolve, reject) => {
+				this.loading = true;
+				const _newTid = uuidv4();
+				let params = {
+					tid: _newTid,
+					userid: this.userId,
+					template: json,
+				};
+				insertNewClassRoomRequest(params)
+					.then((res) => {
+						let _data = res.FunctionResponse;
+						if (converter(_data.message) == converter("创建成功")) {
+							// 设置新课堂的tid
+							// this.tid = params.tid;
+							this.changeTid(_newTid)
+							insertNewObsRequest({
+									tid: params.tid,
+									type: 10,
+									index: 0,
+									json_data: JSON.stringify({ file_ids: "" }),
+									userid: this.userId,
+								})
+								.then((res2) => {
+									let _data2 = res2.FunctionResponse;
+									if (converter(_data2.message) == converter("创建成功")) {
+										this.loading = false;
+										this.$nextTick(() => {
+											this.getCourseList().then(_ => {
+												
+											})
+										// 	this.getCourseList().then((_) => {
+										// 		this.getFileIdId();
+										// 		this.$refs.messageAreaRef.getData();
+										// 		this.$refs.chatAreaRef.getData();
+										// 		resolve();
+										// 	});
+										});
+									} else {
+										this.$toast.fail("创建fileIds失败");
+									}
+								});
+						} else if (converter(_data.message) == converter("tid重复")) {
+							this.$toast.fail("该课堂已存在");
+							this.loading = false;
+						} else {
+							this.$toast.fail("创建失败");
+							this.loading = false;
+						}
+					});
+			});
+		},
   },
   mounted() {
     this.getCourseList().then(_ => {

+ 1 - 1
src/views/classObserve/outcome.vue

@@ -89,7 +89,7 @@ export default {
 
 <style lang="scss" scoped>
 .outcome {
-	height: calc(100% - 50px);
+	height: calc(100%);
 	display: flex;
   flex-direction: column;
   justify-content: space-between;

+ 496 - 35
src/views/classObserve/stencilled.vue

@@ -4,22 +4,32 @@
     <div style="height: 55px;width: 100%;"></div>
     <div class="selectInp">
       <van-search shape="round" v-model="selectValue" placeholder="搜索表单" />
-      <span>搜索</span>
+      <span @click.stop="getData()">搜索</span>
     </div>
     <div class="sortList">
-      <div>社区</div>
-      <div>我的</div>
-      <div>收藏</div>
+      <div :class="[tagIndex == 0 ? 'sortActive' : '']" @click.stop="changeTagIndex(0)">社区</div>
+      <div :class="[tagIndex == 1 ? 'sortActive' : '']" @click.stop="changeTagIndex(1)">我的</div>
+      <div :class="[tagIndex == 2 ? 'sortActive' : '']" @click.stop="changeTagIndex(2)">收藏</div>
       <div @click="reveal" class="sortImg"><img src="../../assets/images/classObserve/filter.png" alt="" /> 筛选</div>
     </div>
-    <div class="stencilList">
-      <div style="margin-bottom: 15px;" v-for="item in 10" :key="item">
-        <stencilledCon @previewTel="previewTel"></stencilledCon>
+    <div class="stencilList" v-loading="loading">
+      <div
+        style="margin-bottom: 15px;"
+        v-for="(item, index) in templateData"
+        :key="item.id"
+        @click="choiceTemplate(item)"
+      >
+        <stencilledCon
+          :data="item"
+          :isChoice="choiceTemplateId == item.id"
+          :tagSubjectList="tagSubjectList"
+          :collectData="collectData"
+        ></stencilledCon>
       </div>
     </div>
     <div class="botBtn">
-      <div class="btn" @click="analysis">
-        使用该模版,一键分析
+      <div :class="['btn', choiceTemplateId ? '' : 'btnDisabled']" @click="getAnalysisAndUse()">
+        使用该模版创建课堂
       </div>
     </div>
 
@@ -27,41 +37,114 @@
       <div class="actionCon">
         <div class="sheetTit">学科</div>
         <div class="claList">
-          <div class="ListBlc" v-for="item in 100" :key="item">语文</div>
+          <div
+            :class="['ListBlc', tagSubject2 == item.value ? 'ListBlcActive' : '']"
+            v-for="(item, index) in tagSubjectList"
+            @click.stop="tagSubject2 = item.value"
+            :key="index"
+          >
+            {{ item.label }}
+          </div>
         </div>
         <div class="actionBtn">
-          <div class="btn">重置</div>
-          <div class="btn btn1">确认</div>
+          <div class="btn" @click.stop="revealReset">重置</div>
+          <div class="btn btn1" @click.stop="revealSubmit">确认</div>
         </div>
       </div>
     </van-action-sheet>
 
-    <van-action-sheet v-model="templateShow" title="预览模板">
-      <div class="actionTel">
+    <van-action-sheet
+      v-loading="saveTemplateLoading"
+      v-model="templateShow"
+      @closed="closeSheet"
+      :title="analysisDetail ? analysisDetail.name : '预览模板'"
+    >
+      <div class="actionTel" v-if="templateShow && analysisDetail">
         <div class="telCon">
-          <div class="telConCell" v-for="item in 5" :key="item">
-            <div class="tit">通用课堂分析</div>
+          <div class="telConCell">
+            <div class="tit"><div>通用课堂分析</div></div>
+            <div class="list">
+              <div
+                class="cell"
+                v-for="(item, index) in analysisDetail.tips.filter(i => i.Type == 0)"
+                :key="index + '-' + 0"
+                v-show="item.tIndex !== 2"
+              >
+                <div>{{ item.jsonData.name }}</div>
+                <span v-if="isEditTel" @click.stop="delTips(item, index, 0)">
+                  <img src="../../assets/images/classObserve/del.svg" />
+                </span>
+              </div>
+            </div>
+          </div>
+
+          <div class="telConCell">
+            <div class="tit">
+              <div>学科课堂分析</div>
+            </div>
+            <div class="list">
+              <div
+                class="cell"
+                v-for="(item, index) in analysisDetail.tips.filter(i => i.Type == 1)"
+                :key="index + '-' + 1"
+              >
+                <div>
+                  {{ item.jsonData.name }}
+                </div>
+                <span v-if="isEditTel" @click.stop="delTips(item, index, 1)">
+                  <img src="../../assets/images/classObserve/del.svg" />
+                </span>
+              </div>
+            </div>
+          </div>
+
+          <div class="telConCell">
+            <div class="tit"><div>扩展课堂分析</div></div>
             <div class="list">
-              <div class="cell">omo智慧课堂分析</div>
-              <div class="cell">omo智慧课堂分析</div>
-              <div class="cell">omo智慧课堂分析</div>
+              <div
+                class="cell"
+                v-for="(item, index) in analysisDetail.tips.filter(i => i.Type == 2)"
+                :key="index + '-' + 2"
+              >
+                <div>{{ item.jsonData.name }}</div>
+                <span v-if="isEditTel" @click.stop="delTips(item, index, 2)">
+                  <img src="../../assets/images/classObserve/del.svg" />
+                </span>
+              </div>
             </div>
           </div>
         </div>
         <div class="actionBtn">
-          <div class="btn" @click="editTel">编辑模板</div>
-          <div class="btn btn1">一键分析</div>
+          <div class="btn" @click="editTel(true)" v-if="userId === analysisDetail.userid && !isEditTel">编辑模板</div>
+
+          <div
+            class="btn"
+            v-if="!isEditTel && (defaultData ? defaultData.id !== analysisDetail.id : true)"
+            @click.stop="setDefaultTemplate(analysisDetail)"
+          >
+            设为默认
+          </div>
+          <div class="btn btn1" v-if="!isEditTel" @click.stop="useAnalysis(analysisDetail)">使用模板</div>
+          <div class="btn" @click="editTel(false)" v-if="userId === analysisDetail.userid && isEditTel">取消编辑</div>
+          <div class="btn btn1" v-if="isEditTel" @click.stop="saveTemplate">保存模板</div>
         </div>
       </div>
     </van-action-sheet>
-
-    <div style="height: 50px;width: 100%;"></div>
   </div>
 </template>
 
 <script>
 import bar from './components/bar.vue'
 import stencilledCon from './components/stencilledCon'
+import { Dialog } from 'vant'
+import {
+  getClassroomTemplateRequest,
+  getClassroomDefaultRequest,
+  updateClassroomTemplateCollectRequest,
+  getClassroomTemplateDetailRequest,
+  updateClassroomTemplateDataRequest,
+  updateClassroomDefaultRequest
+} from '../../api/classObserve'
 
 export default {
   components: {
@@ -79,27 +162,372 @@ export default {
       selectValue: '',
       sortShow: false,
       abuShow: false,
-      templateShow: false
+      loading: false,
+      saveTemplateLoading: false,
+      templateShow: false,
+      defaultData: null,
+      tagIndex: 0,
+      templateData: [],
+      collectData: [],
+      tagSubject: '',
+      tagSubject2: '',
+      userId: this.$store.state.user.userinfo.userid,
+      analysisDetail: null,
+      choiceTemplateId: '',
+      tagSubjectList: [
+        { value: '1', label: '语文' },
+        { value: '2', label: '数学' },
+        { value: '3', label: '英语' },
+        { value: '4', label: '科学' },
+        { value: '5', label: '物理' },
+        { value: '6', label: '化学' },
+        { value: '7', label: '生物' },
+        { value: '8', label: '历史' },
+        { value: '9', label: '地理' },
+        { value: '10', label: '政治' }
+      ],
+      previewData: null,
+      isEditTel: false
     }
   },
   methods: {
+    changeTagIndex(newIndex) {
+      this.tagIndex = newIndex
+      this.choiceTemplateId = ''
+      this.getData()
+    },
     cutPage() {
       this.$emit('cutPage', 1)
     },
-    editTel(){
-      // console.log('1111');
-      this.$emit('cutPage', 6)
+    editTel(newValue) {
+      this.isEditTel = newValue
+      if (!newValue) {
+        Dialog.confirm({
+          title: '放弃修改',
+          message: '确定放弃修改内容?'
+        })
+          .then(() => {
+            this.previewTel(this.analysisDetail)
+          })
+          .catch(() => {
+            this.isEditTel = true
+          })
+      }
     },
     reveal() {
+      this.tagSubject2 = this.tagSubject
       this.sortShow = true
     },
-    analysis() {
-      console.log(22)
-      // this.$router.push({ path: '/outcome', query: {} })
+    revealSubmit() {
+      this.tagSubject = this.tagSubject2
+      this.sortShow = false
+      this.getData()
+    },
+    revealReset() {
+      this.tagSubject = ''
+      this.tagSubject2 = ''
+    },
+    previewTel(data) {
+      this.loading = true
+      getClassroomTemplateDetailRequest({
+        uid: this.userId,
+        cid: data.id,
+        st: 0
+      })
+        .then(res => {
+          let _data = res[0][0]
+          if (_data) {
+            _data.tips = JSON.parse(_data.tips)
+            this.analysisDetail = _data
+            this.templateShow = true
+          } else {
+            this.$toast.fail('获取模板详细为空')
+          }
+        })
+        .catch(e => {
+          console.log(e)
+          this.$toast.fail('获取模板详细失败')
+        })
+        .finally(_ => {
+          this.loading = false
+        })
+      // this.templateShow = true;
+      // console.log(data)
+    },
+    getMyselfData() {
+      this.loading = true
+      this.templateData = []
+      let params = {
+        uid: this.userId,
+        txt: this.selectValue,
+        sub: this.tagSubject,
+        type1: 1
+      }
+      getClassroomTemplateRequest(params)
+        .then(res => {
+          let _data = res[0]
+          this.templateData = _data
+        })
+        .catch(e => {
+          console.log(e)
+          this.$toast.fail('获取数据失败')
+        })
+        .finally(() => {
+          this.loading = false
+        })
+    },
+    getCollectData(flag = true) {
+      this.loading = true
+      if (flag) {
+        this.templateData = []
+      }
+      let params = {
+        uid: this.userId,
+        txt: this.selectValue,
+        sub: this.tagSubject,
+        type1: 2
+      }
+      getClassroomTemplateRequest(params)
+        .then(res => {
+          let _data = res[0]
+          if (flag) {
+            this.templateData = _data
+          }
+          this.collectData = _data
+        })
+        .catch(e => {
+          console.log(e)
+          this.$toast.fail('获取数据失败')
+        })
+        .finally(() => {
+          this.loading = false
+        })
+    },
+    getPublicData() {
+      this.loading = true
+      this.templateData = []
+      let params = {
+        uid: this.userId,
+        txt: this.selectValue,
+        sub: this.tagSubject,
+        type1: 0
+      }
+      getClassroomTemplateRequest(params)
+        .then(res => {
+          let _data = res[0]
+          this.templateData = _data
+        })
+        .catch(e => {
+          console.log(e)
+          this.$toast.fail('获取数据失败')
+        })
+        .finally(() => {
+          this.loading = false
+        })
+    },
+    getDefaultTemplate() {
+      let params = {
+        uid: this.userId
+      }
+      getClassroomDefaultRequest(params)
+        .then(res => {
+          let _data = res[0][0]
+          this.defaultData = _data
+        })
+        .catch(e => {
+          this.$toast.fail('获取默认模板失败')
+          // this.$message.error("获取默认模板失败");
+          console.log(e)
+        })
     },
-    previewTel() {
-      this.templateShow = true
+    getData() {
+      if (this.tagIndex == 0) {
+        this.getPublicData()
+      } else if (this.tagIndex == 1) {
+        this.getMyselfData()
+      } else if (this.tagIndex == 2) {
+        this.getCollectData()
+      }
+    },
+    closeSheet() {
+      this.analysisDetail = null
+      this.isEditTel = false
+    },
+    collect(type = 0, data) {
+      this.loading = true
+      //1 取消收藏 0收藏
+      let cid = type == 0 ? data.id : this.collectData.filter(item => item.id == data.id)[0].id2
+      updateClassroomTemplateCollectRequest([
+        {
+          uid: this.userId,
+          cid: cid,
+          type: type
+        }
+      ])
+        .then(res => {
+          // this.$toast.success(type == 1 ? '取消收藏成功' : '收藏成功')
+        })
+        .catch(e => {
+          this.$toast.fail('操作失败')
+        })
+        .finally(_ => {
+          this.loading = false
+          if (this.tagIndex == 2) {
+            this.getCollectData()
+          } else {
+            this.getCollectData(false)
+          }
+        })
+    },
+    delTips(item, index, type) {
+      Dialog.confirm({
+        title: '删除分析',
+        message: `确定删除 "${item.jsonData.name}" 这个分析?`
+      })
+        .then(() => {
+          let _index = this.analysisDetail.tips.findIndex(_i => JSON.stringify(_i) === JSON.stringify(item))
+          if (_index != -1) {
+            let _data = JSON.parse(JSON.stringify(this.analysisDetail.tips))
+
+            _data.splice(_index, 1)
+            this.analysisDetail.tips = _data
+          }
+        })
+        .catch(() => {
+          console.log('不删除')
+          // on cancel
+        })
+
+      // this.analysisDetail.tips.splice(index,1);
+    },
+    saveTemplate() {
+      this.saveTemplateLoading = true
+      if (this.analysisDetail.userid !== this.userId) {
+        this.isEditTel = false
+        return this.$toast.fail('您没有权限修改此模板')
+      }
+      let params = [
+        {
+          uid: this.userId,
+          cid: this.analysisDetail.id,
+          title: this.analysisDetail.name,
+          jsonData: JSON.stringify(this.analysisDetail.tips)
+        }
+      ]
+      updateClassroomTemplateDataRequest(params)
+        .then(res => {
+          this.$toast.success('保存成功')
+					this.isEditTel = false;
+          // this.getData();
+        })
+        .catch(e => {
+          console.log(e)
+          this.$toast.fail('保存失败')
+        })
+        .finally(_ => {
+          this.saveTemplateLoading = false
+        })
+    },
+    choiceTemplate(item) {
+      if (this.choiceTemplateId == item.id) {
+        return (this.choiceTemplateId = '')
+      }
+      this.choiceTemplateId = item.id
+    },
+    useAnalysis(item) {
+      Dialog.confirm({
+        title: '使用模板',
+        message: `确定使用 "${item.name}" 这个模板来创建课堂?`
+      })
+        .then(() => {
+          getClassroomTemplateDetailRequest({
+            uid: this.userId,
+            cid: item.id,
+            st: 1
+          })
+            .then(res => {
+              var OpenCC = require('opencc-js')
+              let converter = OpenCC.Converter({
+                from: 'hk',
+                to: 'cn'
+              })
+              let _data = res[0][0]
+							
+              _data.tips = JSON.parse(_data.tips)
+							
+              let _result = []
+              _data.tips.forEach(i => {
+                if (converter('词频词汇分析') == converter(i.jsonData.name)) return
+                let _obj = {
+                  jsonData: i.jsonData,
+                  type: i.Type,
+                  index: i.tIndex
+                }
+                _result.push(_obj)
+              })
+              this.$parent.addNewCourseByTemplate(_result)
+              this.cutPage(1)
+            })
+            .catch(e => {
+              this.$toast.fail('获取默认模板失败')
+              // this.$message.error("获取默认模板失败");
+              console.log(e)
+            })
+        })
+        .catch(() => {
+          console.log('不使用')
+          // on cancel
+        })
+    },
+    getAnalysisAndUse() {
+      if (!this.choiceTemplateId) return this.$toast('请先选择模板')
+      let _data = this.templateData.find(i => i.id == this.choiceTemplateId)
+
+      if (_data) {
+        this.useAnalysis(_data)
+      }
+    },
+    setDefaultTemplate(item) {
+      Dialog.confirm({
+        title: '设为默认模板',
+        message: `确定将 "${item.name}" 这个模板设为默认模板?`
+      })
+        .then(() => {
+          let params = [
+            {
+              uid: this.userId,
+              tid: item.id
+            }
+          ]
+          this.saveTemplateLoading = true
+          updateClassroomDefaultRequest(params)
+            .then(res => {
+              if (res == 1) {
+                this.$toast.success('设置默认模板成功')
+              } else {
+                this.$toast.fail('设置默认模板失败')
+              }
+              this.getDefaultTemplate()
+            })
+            .catch(e => {
+              this.$toast.fail('设置默认模板失败')
+              console.log(e)
+              this.getDefaultTemplate()
+            })
+            .finally(_ => {
+              this.saveTemplateLoading = false
+            })
+        })
+        .catch(() => {
+          console.log('不设置')
+          // on cancel
+        })
     }
+  },
+  mounted() {
+    this.getData()
+    this.getCollectData(false)
+    this.getDefaultTemplate()
   }
 }
 </script>
@@ -136,6 +564,18 @@ export default {
       font-size: 16px;
       font-weight: 400;
       color: rgba(0, 0, 0, 0.9);
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      span {
+        width: 20px;
+        height: 20px;
+        margin-right: 10px;
+        img {
+          width: 100%;
+          height: 100%;
+        }
+      }
     }
   }
   .actionBtn {
@@ -143,16 +583,23 @@ export default {
     display: flex;
     justify-content: space-between;
     align-items: center;
-    padding: 0 15px;
+    // padding: 0 15px;
     .btn {
-      width: 48%;
+      flex: 1;
       border-radius: 6px;
       padding: 12px 0;
+      margin: 0 5px;
       display: flex;
       justify-content: center;
       color: rgba(54, 129, 252, 1);
       background-color: rgba(242, 243, 255, 1);
       font-size: 16px;
+      &:nth-of-type(1) {
+        margin-left: 10px;
+      }
+      &:nth-last-of-type(1) {
+        margin-right: 10px;
+      }
     }
     .btn1 {
       color: #fff;
@@ -200,6 +647,10 @@ export default {
   justify-content: space-between;
   padding-bottom: 15px;
   border-bottom: 0.5px rgba(231, 231, 231, 1) solid;
+  .sortActive {
+    color: rgba(54, 129, 252, 1);
+    font-weight: bold;
+  }
   div {
     width: 25%;
     text-align: center;
@@ -237,6 +688,11 @@ export default {
     // font-weight: 600;
     justify-content: center;
     align-items: center;
+    transition: 0.3s;
+  }
+  .btnDisabled {
+    background-color: #e7e7e7;
+    color: rgb(104, 104, 104);
   }
 }
 .actionCon {
@@ -268,6 +724,11 @@ export default {
       text-align: center;
       border: 1px rgba(242, 243, 255, 1) solid;
     }
+    .ListBlcActive {
+      background-color: #3681fc;
+      border-color: #3681fc;
+      color: #fff;
+    }
   }
   .claList > div {
     margin-right: calc(10% / 2);
@@ -282,7 +743,7 @@ export default {
     justify-content: space-between;
     align-items: center;
     .btn {
-      width: 48%;
+      flex: 1;
       border-radius: 6px;
       padding: 12px 0;
       display: flex;