SanHQin преди 11 месеца
родител
ревизия
54b9a4af5f

+ 1 - 1
src/components/pages/classroomObservation/components/tape.vue

@@ -42,7 +42,7 @@
 			</div> -->
 		<!-- </div> -->
 		<div class="t-chartArea" ref="chartAreaRef">
-			<div class="t_ca_box" v-for="(item, index) in chatData" :key="item.uid">
+			<div class="t_ca_box" v-for="(item, index) in chatData" :key="item.uid+index">
 			
 				<div class="t_ca_b_roleUser" v-if="item.content">
 					<div class="t_ca_b_u_content">

+ 1 - 5
src/components/pages/classroomObservation/index.vue

@@ -122,11 +122,7 @@ export default {
 			tid: "",
 			fileId: "",
 			fileIdId: "",
-			optionData: [
-				{ label: "03课程", value: "03" },
-				{ label: "04课程", value: "04" },
-				{ label: "05课程", value: "05" },
-			],
+			optionData: [],
 		};
 	},
 	methods: {

+ 63 - 0
src/components/pages/components/addNewCourseDialog.vue

@@ -0,0 +1,63 @@
+<template>
+	<div class="addNewCourse">
+		<el-dialog
+			title="添加课堂"
+			:visible.sync="dialogVisible"
+			width="500px"		>
+		<div>
+			<el-form ref="form" :model="form" label-position="top">
+			  <el-form-item label="课堂编号">
+  			  <el-input v-model="form.no" placeholder="请输入课堂编号"></el-input>
+  			</el-form-item>
+				<el-form-item label="课堂名称">
+  			  <el-input v-model="form.name" placeholder="请输入课堂名称"></el-input>
+  			</el-form-item>
+			</el-form>
+		</div>
+			<span slot="footer" class="dialog-footer">
+				<el-button @click="dialogVisible = false">取 消</el-button>
+				<el-button type="primary" @click="submit()"
+					>确 定</el-button
+				>
+			</span>
+		</el-dialog>
+	</div>
+</template>
+
+<script>
+export default {
+	emits:["success"],
+	data(){
+		return{
+			dialogVisible:false,
+			form:{
+				no:"",
+				name:"",
+			},
+		}
+	},
+	methods:{
+		open(){
+			this.form = {
+				name:"",
+				no:"",
+			}
+			this.dialogVisible = true;
+		},
+		submit(){
+			if(this.form.no.trim().length==0 || this.form.name.trim().length==0){
+				this.$message.error("请输入课堂编号和课堂名称")
+				return;
+			}
+			this.$emit("success",this.form)
+			this.dialogVisible = false;
+		}
+	}
+};
+</script>
+
+<style  scoped>
+.addNewCourse{
+
+}
+</style>

+ 428 - 0
src/components/pages/components/analysis.vue

@@ -0,0 +1,428 @@
+<template>
+	<div class="analysis">
+		<div class="a-header">
+			<div class="a-h-left" @click.stop="changeShowItem(!showItem)">
+				<span :class="['a-h-l-icon', showItem ? 'a-h-l-showIcon' : '']"></span>
+				<span class="a-h-l-title">{{ title }}</span>
+			</div>
+			<div class="a-h-right">
+				<div class="a-h-r-btn" @click.stop="addTemplate">
+					<img src="@/assets/icon/classroomObservation/newcon.png" alt="" />
+					添加模块
+				</div>
+			</div>
+		</div>
+		<div class="a-main" v-show="showItem">
+			<analysisItem
+				ref="analysisItemRef"
+				v-if="analysisItemList.length > 0"
+				v-for="(item, index) in analysisItemList"
+				:dialogTagDataList="dialogTagDataList"
+				:key="item.id"
+				:data="item"
+				:tid="tid"
+				:fileId="fileId"
+				:index="index"
+				@delItem="delItem"
+				@editItem="editItem"
+			/>
+			<div class="a_m_empty" v-if="analysisItemList.length == 0">
+				暂无模块...
+			</div>
+		</div>
+	</div>
+</template>
+
+<script>
+import analysisItem from "./analysisItem";
+export default {
+	emits: ["delItem", "editItem"],
+	props: {
+		title: {
+			type: String,
+			default: "分析",
+		},
+		dialogTagDataList:{
+			type:Array,
+			default:()=>{
+				return [];
+			}
+		},
+		analysisItemList: {
+			type: Array,
+			default: () => {
+				return [];
+			},
+		},
+		type: {
+			type: Number,
+			default: 0,
+		},
+		tid: {
+			type: String,
+			require: true,
+		},
+		fileId: {
+			type: String,
+			require: true,
+		},
+	},
+	components: {
+		analysisItem,
+	},
+	data() {
+		return {
+			showDialog: false,
+			showItem: true,
+		};
+	},
+	methods: {
+		addTemplate() {
+			this.$emit("updateMessage", this.type);
+		},
+		changeShowItem(newValue) {
+			this.showItem = newValue;
+		},
+		delItem(id) {
+			this.$emit("delItem", id);
+		},
+		editItem(id, _data) {
+			this.$emit("editItem", id, _data);
+		},
+		getReport(id) {
+			if (!id) {
+				this.$refs.analysisItemRef.forEach((i) => {
+					i.editBtn();
+				});
+			} else {
+				this.$nextTick(()=>{
+					this.$refs.analysisItemRef[this.analysisItemList.findIndex((i) => i.id == id)].editBtn();
+				})
+				
+			}
+		},
+	},
+};
+</script>
+
+<style scoped>
+.itemTit {
+	width: 136px;
+	height: 32px;
+	padding: 5px 8px 5px 8px;
+	gap: 8px;
+	opacity: 0px;
+	margin: 20px 0;
+	margin-bottom: 10px;
+	border-bottom: 1px #ccc solid;
+}
+.analysis {
+	width: 100%;
+	height: auto;
+}
+
+.a-header {
+	width: 100%;
+	height: 50px;
+	display: flex;
+	align-items: center;
+	justify-content: space-between;
+	box-sizing: border-box;
+	padding-right: 10px;
+}
+
+.a-h-left {
+	display: flex;
+	align-items: center;
+	cursor: pointer;
+}
+
+.a-h-l-icon {
+	width: 16px;
+	height: 16px;
+	background: url("../../../../assets/icon/classroomObservation/right.png")
+		no-repeat;
+	background-size: 100% 100%;
+	margin-right: 5px;
+	transition: 0.3s;
+}
+
+.a-h-l-showIcon {
+	transform: rotate(90deg);
+}
+
+.a-h-l-title {
+	font-size: 18px;
+}
+
+.a-h-r-btn {
+	font-size: 16px;
+	height: 35px;
+	width: auto;
+	box-sizing: border-box;
+	/* padding: 0 20px; */
+	/* border: solid 1px #C5C5C5; */
+	/* border-radius: 18px; */
+	cursor: pointer;
+	/* background-color: white; */
+	display: flex;
+	color: rgba(54, 129, 252, 1);
+	justify-content: center;
+	align-items: center;
+}
+
+.a-h-r-btn > img {
+	width: 16px;
+	height: 16px;
+	margin-right: 5px;
+}
+
+.a-main {
+	width: calc(100%);
+	height: auto;
+}
+
+.a-dialog {
+	position: fixed;
+	width: 1200px;
+	height: 500px;
+	min-height: 600px;
+	box-shadow: 0px 0 8px 0px #555555;
+	border-radius: 8px;
+	z-index: 999;
+	background-color: #fff;
+	left: 0;
+	right: 0;
+	top: 100px;
+	margin: 0 auto;
+	/* margin: 0 auto; */
+	/* top: 50%; */
+	/* margin: -18% 0 0 -300px; */
+	overflow: hidden;
+}
+
+.a-d-top {
+	/* background: #adadad; */
+	display: flex;
+	flex-direction: row;
+	flex-wrap: nowrap;
+	align-items: center;
+	justify-content: space-between;
+	height: 54px;
+	border-radius: 8px 8px 0 0;
+	user-select: none;
+	border-bottom: 1px #ccc solid;
+}
+.a-d-top >>> .el-input__inner {
+	width: 320px;
+	height: 32px;
+}
+.a-d-top >>> .el-input__icon {
+	line-height: 32px;
+}
+.a-d-topTit {
+	width: 171px;
+	height: 32px;
+	display: flex;
+	align-items: center;
+	font-family: PingFang SC;
+	box-sizing: border-box;
+	padding: 5px;
+	line-height: 22px;
+	justify-content: center;
+	/* text-align: left; */
+}
+.a-d-t-left {
+	width: 200px;
+	height: 100%;
+	display: flex;
+	align-items: center;
+	flex-direction: column;
+	justify-content: flex-start;
+	box-sizing: border-box;
+	padding-left: 5px;
+}
+
+.a-d-t-l-item {
+	/* width: auto;
+	height: 90%;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	padding: 0 10px;
+	border-radius: 10px;
+	background-color: #d4d9da;
+	margin-right: 3px;
+	cursor: pointer; */
+	cursor: pointer;
+	width: 136px;
+	height: 32px;
+	display: flex;
+	align-items: center;
+	border-radius: 5px;
+	font-family: PingFang SC;
+	box-sizing: border-box;
+	padding: 5px;
+	font-size: 14px;
+	font-weight: 600;
+	line-height: 22px;
+	text-align: left;
+	margin-bottom: 20px;
+}
+
+.a-d-t-l-item:hover {
+	background-color: white;
+}
+
+.a-d-t-right {
+	width: 40px;
+	height: 40px;
+	margin-right: 10px;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	color: black !important;
+}
+
+.a-d-t-right > span {
+	width: 25px;
+	height: 25px;
+	border-radius: 25px;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	/* align-items: center; */
+	font-size: 22px;
+	color: #fff;
+	/* background-color: #adadad; */
+	cursor: pointer;
+	/* background-color: #e6e6e6; */
+	color: #adadad;
+}
+
+.a-d-box {
+	width: 100%;
+	height: 100%;
+	/* height: calc(100% - 40px); */
+	background-color: #f0f2f5;
+	overflow: auto;
+
+	box-sizing: border-box;
+	padding: 15px;
+	padding-bottom: 50px;
+}
+
+.a-d-b-item {
+	width: 22%;
+	height: 200px;
+	display: flex;
+	flex-direction: column;
+	background-color: #fff;
+	border-radius: 10px;
+	padding: 15px;
+	float: left;
+	box-sizing: border-box;
+	margin-bottom: 10px;
+	/* position: relative; */
+}
+.a-d-b-item:hover .a-d-b-i-bottomBtn {
+	display: block !important;
+}
+.a-d-b-item:hover .a-d-b-i-bottomPer {
+	display: none !important;
+}
+.a-d-b-i-top {
+	width: 100%;
+	/* height: 50%; */
+	height: 20px;
+	margin-bottom: 15px;
+	display: flex;
+	align-items: center;
+	/* justify-content: space-between; */
+}
+.a-d-b-i-top > img {
+	width: 35px;
+	height: 35px;
+}
+/* .a-d-b-i-top>div{ */
+/* width: auto;
+	height: 35px;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	margin-left: 10px; */
+/* } */
+
+.a-d-b-i-t-title {
+	width: 100%;
+	height: 35px;
+	display: block;
+	align-items: center;
+	box-sizing: border-box;
+	padding: 0 10px;
+	text-overflow: ellipsis;
+	overflow: hidden;
+	word-break: break-all;
+	white-space: nowrap;
+	line-height: 35px;
+	/* display: -webkit-box;
+	-webkit-box-orient: vertical;
+	-webkit-line-clamp: 1;
+	overflow: hidden; */
+}
+
+.a-d-b-i-bottom {
+	width: 100%;
+	flex: 1;
+	overflow: hidden;
+	/* max-height: 186px; */
+	font-size: 14px;
+	-webkit-line-clamp: 5;
+	line-height: 20px;
+	display: -webkit-box;
+	-webkit-box-orient: vertical;
+	overflow: hidden;
+	text-overflow: ellipsis;
+}
+
+.a-d-b-i-t-btn {
+	font-size: 14px;
+	/* height: 30px; */
+	/* position: relative; */
+	/* top: 5px; */
+	box-sizing: border-box;
+	padding: 8px 25px;
+	/* background-color: #f3f3f3; */
+	border: 1px solid rgba(54, 129, 252, 1);
+	border-radius: 5px;
+	color: rgba(54, 129, 252, 1);
+
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	cursor: pointer;
+	/* position: absolute; */
+	/* right: 10px; */
+	/* top: 10px; */
+}
+.a-d-b-i-t-btn1 {
+	font-size: 14px;
+	box-sizing: border-box;
+	padding: 8px 25px;
+	border: 1px solid rgba(54, 129, 252, 1);
+	border-radius: 5px;
+	background-color: rgba(54, 129, 252, 1);
+	display: flex;
+	color: #fff;
+	justify-content: center;
+	align-items: center;
+	cursor: pointer;
+}
+.a_m_empty {
+	display: flex;
+	width: 100%;
+	justify-content: center;
+	font-size: 14px;
+	color: #555555;
+}
+</style>

+ 364 - 0
src/components/pages/components/analysisItem.vue

@@ -0,0 +1,364 @@
+<template>
+	<div class="analysisItem">
+		<div class="ai-header">
+			<div class="ai-h-left" @click.stop="changeOpenItem(!openItem)">
+				<span
+					:class="['ai-h-l-icon', openItem ? 'ai-h-l-iconActive' : '']"
+				></span>
+				<span class="ai-h-l-text">{{ data.jsonData.name }}</span>
+			</div>
+			<div class="ai-h-right">
+				<span style="width: 100px; color: red" v-if="loadNum == 2"
+					>优化失败
+				</span>
+				<span
+					:class="loading ? 'text1' : 'text2'"
+					style="width: 100px"
+					v-if="loadNum == 1"
+					>{{ loading ? "优化中..." : "优化完成" }}
+				</span>
+				<span
+					v-if="loadNum == 0 && !openItem"
+					class="ai-h-r-icon4"
+					@click.stop="delBtn()"
+				></span>
+				<span
+					v-if="loadNum == 0 && openItem"
+					:class="['ai-h-r-icon1',showIndex<=0?'ai_h_r_iconOpacity':'']"
+					@click.stop="changeShowIndex(-1)"
+				></span>
+				<span
+					v-if="loadNum != 1 && openItem"
+					:class="['ai-h-r-icon2',showIndex>=historyResult.length-1?'ai_h_r_iconOpacity':'']"
+					@click.stop="changeShowIndex(1)"
+				></span>
+				<span
+					v-if="loadNum != 1 && openItem"
+					class="ai-h-r-icon3"
+					@click.stop="editBtn()"
+				></span>
+				<!-- <span class="ai-h-r-icon4" @click.stop="delBtn()"></span> -->
+			</div>
+		</div>
+		<div class="ai-main" v-if="openItem">
+
+
+			<div class="a-m-brief">
+				<mdView :text="data.jsonData.result" />
+				<!-- {{ data.jsonData.result }} -->
+			</div>
+			<div v-for="(item, index) in data.jsonData.dataFileList" :key="item.uid">
+				<div v-if="imgTypeList.includes(checkFileType(item.url))">
+					<img style="max-width: 100%" :src="item.url" alt="" />
+				</div>
+				<div v-else-if="checkFileType(item.url) == 'md'">
+					<mdView :url="item.url" />
+				</div>
+				<div v-else-if="checkFileType(item.url) == 'json'">
+					<pieChart :url="item.url" />
+				</div>
+			</div>
+
+			<div v-for="(item, index) in data.jsonData.fileList" :key="item.uid">
+				<div v-if="imgTypeList.includes(checkFileType(item.url))">
+					<img style="max-width: 100%" :src="item.url" alt="" />
+				</div>
+				<div v-else-if="checkFileType(item.url) == 'md'">
+					<mdView :url="item.url" />
+				</div>
+				<div v-else-if="checkFileType(item.url) == 'json'">
+					<pieChart :url="item.url" />
+				</div>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script>
+import mdView from "./mdView.vue";
+import pieChart from "./pieChart.vue";
+export default {
+	emits: ["delItem", "editItem"],
+	components: {
+		mdView,
+		pieChart,
+	},
+	props: {
+		data: {
+			type: Object,
+			default: () => {
+				return {};
+			},
+		},
+		dialogTagDataList:{
+			type:Array,
+			default:()=>{
+				return []
+			}
+		},
+		index: {
+			type: Number,
+			default: 0,
+		},
+		tid: {
+			type: String,
+			require: true,
+		},
+		fileId: {
+			type: String,
+			require: true,
+		},
+	},
+	data() {
+		return {
+			openItem: false,
+			loading: false,
+			loadNum: 0,
+			showIndex:0,
+			historyResult:[],
+			imgTypeList: [
+				"png",
+				"jpg",
+				"jpeg",
+				"bmp",
+				"gif",
+				"webp",
+				"psd",
+				"svg",
+				"tiff",
+			],
+		};
+	},
+	computed: {
+		checkFileType() {
+			return (url) => {
+				return url.split(".").pop();
+			};
+		},
+	},
+	watch:{
+		// showIndex(){
+		// 	let _copyData = JSON.parse(JSON.stringify(this.data));
+		// 	_copyData.jsonData = this.historyResult[this.showIndex];
+		// 	_copyData.json_data = JSON.stringify(_copyData.jsonData);
+		// 	this.$emit("editItem", this.data.id, _copyData);
+		// }
+	},
+	methods: {
+		changeOpenItem(newValue) {
+			if (this.loading == true && this.loadNum != 0)
+				return this.$message("请稍后...");
+			this.loadNum = 0;
+			this.openItem = newValue;
+		},
+		editBtn() {
+			this.$nextTick(() => {
+				this.loading = true;
+				this.openItem = false;
+				this.loadNum = 1;
+				let assistant = this.dialogTagDataList.find(i=>i.title==this.data.jsonData.name);
+				let parm = {
+					assistant_id: assistant?assistant.value:null,
+					message:
+						"请使用代码解析器获取文件,帮我根据要求完整的分析,输出请按照要求。",
+					session_name: new Date().getTime(),
+					userId: "1cf9dc4b-d95f-11ea-af4c-52540005ab01",
+					file_ids: this.fileId,
+				};
+
+				if (!parm.assistant_id) {
+					this.loading = false;
+					this.loadNum = 2;
+					return this.$message.error("未找到对应的AI助手");
+				}
+
+				this.ajax
+					.post("https://gpt4.cocorobo.cn/ai_agent_park_chat", parm)
+					.then((res) => {
+						let _data = res.data.FunctionResponse;
+						if (
+							!_data.message ||
+							_data.message.indexOf("由于我无法直接访问您上传的文件内容") > -1
+						) {
+							this.loading = false;
+							return this.$message.error("AI无法识别优化");
+						}
+						let _copyData = JSON.parse(JSON.stringify(this.data));
+						_copyData.jsonData.result = _data.message;
+						_copyData.jsonData.dataFileList = [];
+						_copyData.jsonData.fileList = [];
+						_copyData.json_data = JSON.stringify(_copyData.jsonData);
+						if(this.historyResult.length==0){
+							this.historyResult.push(_copyData.jsonData)
+						}else{
+							this.historyResult.splice(this.showIndex+1,0,_copyData.jsonData)
+						}
+						this.changeShowIndex(1);
+						this.loading = false;
+					})
+					.catch((err) => {
+						this.$message.error("AI无法识别优化");
+						this.loading = false;
+						this.loadNum = 2;
+					});
+			});
+		},
+		delBtn() {
+			this.$confirm("确定删除?", "提示", {
+				confirmButtonText: "确定",
+				cancelButtonText: "取消",
+				type: "error",
+			}).then(() => {
+				this.$emit("delItem", this.data.id);
+			});
+		},
+		changeShowIndex(value){
+			if(this.historyResult.length==0)return;
+			if(value==-1){
+				if(this.showIndex>0)this.showIndex--;
+			}else if(value==1){
+				if(this.showIndex<this.historyResult.length-1)this.showIndex++;
+			}
+			let _copyData = JSON.parse(JSON.stringify(this.data));
+			let _oldCopyDate = JSON.parse(JSON.stringify(this.data));
+			_copyData.jsonData = this.historyResult[this.showIndex];
+			_copyData.json_data = JSON.stringify(_copyData.jsonData);
+			_oldCopyDate.json_data = JSON.stringify(_oldCopyDate.jsonData);
+			// 一样就不用更新了
+			if(JSON.stringify(_copyData)==JSON.stringify(_oldCopyDate))return;
+			this.$emit("editItem", this.data.id, _copyData);
+		}
+	},
+	mounted(){
+		if(this.data.jsonData.result){
+			this.historyResult.push(this.data.jsonData)
+		}
+	},
+};
+</script>
+
+<style scoped>
+.analysisItem {
+	width: 100%;
+	height: auto;
+
+	margin: 11.7px 0px;
+
+	padding-right: 10px;
+	box-sizing: border-box;
+}
+.text1 {
+	color: rgba(54, 129, 252, 1);
+}
+.text2 {
+	color: rgba(23, 196, 105, 1);
+}
+.ai-header {
+	width: 100%;
+	height: 50px;
+	display: flex;
+	background-color: white;
+	border-radius: 5px;
+}
+.ai-h-left {
+	flex: 1;
+	height: 100%;
+	display: flex;
+	align-items: center;
+	box-sizing: border-box;
+	padding: 0 10px;
+	cursor: pointer;
+}
+
+.ai-h-l-icon {
+	width: 16px;
+	height: 16px;
+	background: url("../../../../assets/icon/classroomObservation/right.png")
+		no-repeat;
+	background-size: 100% 100%;
+	margin-right: 10px;
+	transition: 0.3s;
+}
+
+.ai-h-l-iconActive {
+	transform: rotate(90deg);
+}
+.ai-h-l-text {
+	font-size: 18px;
+}
+
+.ai-h-right {
+	width: auto;
+	height: 100%;
+	display: flex;
+	align-items: center;
+	justify-content: space-around;
+	position: relative;
+}
+
+.ai-h-right > span {
+	width: 18px;
+	height: 18px;
+	background-size: 100% 100%;
+	margin: 0 10px;
+	cursor: pointer;
+}
+
+.ai-h-r-icon1 {
+	background: url("../../../../assets/icon/classroomObservation/back.png");
+	/* 镜像 */
+	transform: scaleX(-1);
+}
+
+.ai-h-r-icon2 {
+	background: url("../../../../assets/icon/classroomObservation/back.png");
+}
+
+.ai_h_r_iconOpacity{
+	opacity: 0.5;
+	cursor: not-allowed !important;
+}
+
+.ai-h-r-icon3 {
+	background: url("../../../../assets/icon/classroomObservation/edit.png");
+	/* background: url("../../../../assets/icon/classroomObservation/del.png"); */
+	/* display: none; */
+}
+
+.ai-header:hover .ai-h-r-icon4 {
+	display: block;
+}
+
+.ai-h-r-icon4 {
+	background: url("../../../../assets/icon/classroomObservation/del.png");
+	/* position: absolute; */
+	/* right: -40px; */
+	display: none;
+}
+
+.analysisItem:hover .ai-h-r-icon4 {
+	display: block;
+}
+
+.ai-main {
+	width: 100%;
+	height: auto;
+	background-color: white;
+	border-radius: 0 0 5px 5px;
+	overflow: auto;
+	box-sizing: border-box;
+	padding: 10px 20px;
+}
+
+.a-m-brief {
+	font-size: 16px;
+	/* 斜体 */
+	/* font-style: italic; */
+	margin-bottom: 10px;
+}
+
+td,
+th {
+	padding: 10px;
+}
+</style>

+ 463 - 0
src/components/pages/components/baseMessage.vue

@@ -0,0 +1,463 @@
+<template>
+	<div class="message" >
+		<div class="m-header" @click.stop="changeShowMain(!showMain)">
+			<span :class="['m-h-icon', showMain ? 'm-h-icon-active' : '']"></span>
+			<span class="m-h-title">{{ title }}</span>
+		</div>
+		<div class="m-main" v-show="showMain">
+			<div class="baseInfoLeft">
+				<div class="m-m-formItem">
+					<div class="m-m-fi-label">课程名称</div>
+					<div class="m-m-fi-input">
+						<el-input
+							v-model="data.courseName"
+							placeholder="请输入课程名称"
+							@change="changeData()"
+						></el-input>
+					</div>
+				</div>
+
+				<div class="baseInfoLeftBot">
+					<div class="m-m-formItemBot">
+						<div class="m-m-fi-label">授课年级</div>
+						<div class="m-m-fi-input">
+							<el-select v-model="data.grade" @change="changeData()" placeholder="请选择年级">
+								<el-option
+									v-for="(item, index) in gradeList"
+									:key="index"
+									:value="item.value"
+									:label="item.label"
+								></el-option>
+							</el-select>
+						</div>
+					</div>
+
+					<div class="m-m-formItemBot">
+						<div class="m-m-fi-label">授课科目</div>
+						<div class="m-m-fi-input">
+							<el-select v-model="data.subject" @change="changeData()" placeholder="请选择年级">
+								<el-option
+									v-for="(item, index) in subjectList"
+									:key="index"
+									:value="item.value"
+									:label="item.label"
+								></el-option>
+							</el-select>
+						</div>
+					</div>
+
+					<div class="m-m-formItemBot">
+						<div class="m-m-fi-label">授课班级</div>
+						<div class="m-m-fi-input">
+							<el-input
+								v-model="data.class"
+								placeholder="请输入班级"
+								@change="changeData()"
+							></el-input>
+						</div>
+					</div>
+				</div>
+			</div>
+
+			<!-- 授课老师,学生人数 -->
+			<div class="baseInfoRight">
+				<div class="m-m-formItem">
+					<div class="m-m-fi-label">授课老师</div>
+					<div class="m-m-fi-input">
+						<el-input
+							v-model="data.teacherName"
+							placeholder="请输入授课老师"
+							@change="changeData()"
+						></el-input>
+					</div>
+				</div>
+
+				<div class="m-m-formItem" style="margin: 10px 0">
+					<div class="m-m-fi-label">学生人数</div>
+					<div class="m-m-fi-input">
+						<el-input
+							v-model.number="data.studentNum"
+							placeholder="请输入学生人数"
+							@change="changeData()"
+						></el-input>
+					</div>
+				</div>
+			</div>
+			<div class="imgTit">
+				<span>添加课堂图片</span>
+				<span>(建议图片比例16:9,最多上传3张图片)</span>
+			</div>
+			<div class="m-m-formImage" v-loading="uploadImageLoading">
+				<div class="m-m-fi-imageList">
+					<div class="m-m-fi-imageList">
+						<div
+							class="m-m-fi-imageItem"
+							v-for="(value,key,index) in imageList"
+							:key="index"
+							v-if="key.indexOf('fileList')!=-1&&index!=0 && value.length>0"
+							@click.stop="previewImg(value[0].url)"
+						>
+						<el-image
+						class="itemUrl"
+      			:src="value[0].url"
+      			fit="cover"></el-image>
+						<span @click.stop="delImage(key)">x</span>
+							<!-- <img class="itemUrl" :src="value[0].url" alt="" /> -->
+						</div>
+						<!-- 图片区域 -->
+					</div>
+
+					<div class="m-m-fi-imageItem"  @click.stop="addImage()" v-if="imageList.fileList1&&(imageList.fileList1.length+imageList.fileList2.length+imageList.fileList3.length)<3">
+						<img
+							src="../../../../assets/icon/classroomObservation/Union.png"
+							alt=""
+						/>
+						<div
+							style="
+								font-size: 12px;
+								font-weight: 400;
+								margin-top: 5px;
+								color: rgba(0, 0, 0, 0.4);
+							"
+						>
+							点击上传图片
+						</div>
+					</div>
+					<!-- 图片区域 -->
+				</div>
+				<!-- <div class="m-m-fi-btn" >添加课堂图片</div> -->
+			</div>
+		</div>
+	</div>
+</template>
+
+<script>
+export default {
+	emits:["saveData","saveImage","delImage"],
+	props:{
+		data:{
+			type:Object,
+			default:()=>{
+				return {}
+			}
+		},
+		imageList:{
+			type:Object,
+			default:()=>{
+				return {}
+			}
+		},
+	},
+	data() {
+		return {
+			title: "基本信息",
+			showMain: true,
+			uploadImageLoading:false,
+			from: {
+				courseName: "",
+				teacherName: "",
+				grade: "小学五年级",
+				subject: "科学",
+				class: "",
+				studentNum: 0,
+			},
+			gradeList: [
+				{ value: "小学一年级", label: "小学一年级" },
+				{ value: "小学二年级", label: "小学二年级" },
+				{ value: "小学三年级", label: "小学三年级" },
+				{ value: "小学四年级", label: "小学四年级" },
+				{ value: "小学五年级", label: "小学五年级" },
+				{ value: "小学六年级", label: "小学六年级" },
+			],
+			subjectList: [{ value: "科学", label: "科学" }],
+		};
+	},
+	methods: {
+		// 添加图片
+		addImage() {
+			// 上传录音
+			let input = document.createElement("input");
+			input.type = "file";
+			input.accept = "image/*";
+			input.click();
+			input.onchange = () => {
+				this.uploadImageLoading = 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.$message.error("上传失败");
+							} else {
+								_this.$emit("saveImage",{
+									name:data.key,
+									status:"success",
+									uid:"1",
+									url:data.Location
+								});
+								// _this.from.audioUrl = data.Location;
+								// _this.from.audioName = file.name;
+								// _this.from.audioSize = file.size;
+								// _this.from.audioTime = file.lastModified;
+
+								// console.log(data.Location)
+							}
+							_this.uploadImageLoading = false;
+						});
+				}
+			};
+			// this.mainBtnStatus = 1;
+			// this.barNum = 4;
+		},
+		// 删除图片
+		delImage(key){
+			this.$confirm('确定删除该图片吗?', '提示', {
+				confirmButtonText: '确定',
+				cancelButtonText: '取消',
+				type: 'warning'
+			}).then(() => {
+				this.$emit("delImage",key);
+			})
+		},
+		changeShowMain(newValue) {
+			this.showMain = newValue;
+		},
+		previewImg(url) {
+			this.$hevueImgPreview(url);
+		},
+		changeData(){
+			this.$emit("saveData");
+		},
+	},
+	mounted(){
+		
+	}
+};
+</script>
+
+<style scoped>
+.message {
+	width: 100%;
+	height: auto;
+}
+.imgTit{
+  height: 40px;
+  line-height: 40px;
+}
+.imgTit :first-child {
+	font-size: 16px;
+	font-weight: 400;
+	line-height: 22px;
+	text-align: right;
+}
+.imgTit :last-child {
+	font-family: PingFang SC;
+	font-size: 12px;
+	font-weight: 400;
+	line-height: 20px;
+	text-align: left;
+  color: rgba(0, 0, 0, 0.4);
+
+}
+.baseInfoLeft {
+	display: flex;
+	width: 70%;
+	flex-direction: column;
+	/* justify-content: space-between; */
+	margin-right: 10px;
+}
+.baseInfoLeft >>> .baseInfoLeftBot {
+	display: flex;
+	justify-content: space-between;
+	margin: 10px 0;
+}
+.m-m-formItemBot {
+	width: 30%;
+	/* height: auto; */
+	/* display: flex; */
+	/* flex-direction: column; */
+	/* align-items: left; */
+	/* margin-bottom: 20px; */
+}
+.baseInfoRight {
+	display: flex;
+	flex: 1;
+	flex-direction: column;
+	/* justify-content: space-between; */
+}
+
+.m-header {
+	width: 100%;
+	height: 50px;
+	display: flex;
+	align-items: center;
+	cursor: pointer;
+}
+
+.m-h-icon {
+	width: 16px;
+	height: 16px;
+	background: url("../../../../assets/icon/classroomObservation/right.png")
+		no-repeat;
+	background-size: 100% 100%;
+	margin-right: 5px;
+	transition: 0.3s;
+}
+
+.m-h-icon-active {
+	transform: rotate(90deg);
+}
+
+.m-h-title {
+	font-size: 18px;
+}
+
+.m-main {
+	width: calc(100% - 10px);
+	height: auto;
+	border-radius: 5px;
+	background-color: #ffffff;
+	padding: 20px 20px 0 20px;
+	box-sizing: border-box;
+	display: flex;
+	flex-wrap: wrap;
+}
+
+.m-m-formItem {
+	width: 100%;
+	height: auto;
+	display: flex;
+	flex-direction: column;
+	align-items: left;
+	/* margin-bottom: 10px; */
+}
+
+.m-m-fi-input {
+	width: 100%;
+}
+
+.m-m-fi-label {
+	font-size: 16px;
+	display: flex;
+	padding-bottom: 5px;
+	/* justify-content: center; */
+	/* align-items: center; */
+	box-sizing: border-box;
+	/* padding: 0 10px; */
+	text-wrap: nowrap;
+	min-width: fit-content;
+}
+
+.m-m-formImage {
+	width: 100%;
+	height: auto;
+	margin-bottom: 20px;
+	display: flex;
+	justify-content: flex-start;
+	flex-wrap: wrap;
+}
+
+.m-m-fi-imageList {
+	height: auto;
+	display: flex;
+	flex-wrap: wrap;
+}
+
+.m-m-fi-imageItem {
+	width: auto;
+	height: auto;
+	background-color: rgba(238, 238, 238, 1);
+	width: 140px;
+	height: 100px;
+	margin-right: 10px;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	flex-direction: column;
+	box-sizing: border-box;
+	font-size: 14px;
+	cursor: pointer;
+	position: relative;
+}
+.m-m-fi-imageItem > .itemUrl {
+	width: 100%;
+	height: 100%;
+}
+
+.m-m-fi-imageItem>span{
+	width:30px;
+	height: 30px;
+	position: absolute;
+	right: 0;
+	top: 0;
+	display: flex;
+	justify-content: flex-end;
+	align-items: flex-start;
+	box-sizing: border-box;
+	padding: 2px 10px;
+	color: white;
+	background-color: #e60012;
+	cursor: pointer;
+	border-radius: 0 0 0 100%;
+	display: none;
+}
+
+.m-m-fi-imageItem:hover>span{
+	display: flex;
+}
+
+.m-m-fi-i-icon {
+	width: 20px;
+	height: 20px;
+	background: url("../../../../assets/icon/classroomObservation/file.png")
+		no-repeat;
+	background-size: 100% 100%;
+	margin-right: 5px;
+}
+
+.m-m-fi-btn {
+	width: auto;
+	height: 35px;
+	box-sizing: border-box;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	border-radius: 18px;
+	font-size: 14px;
+	border: solid 1px #c5c5c5;
+	background-color: white;
+	cursor: pointer;
+	padding: 0 10px;
+}
+</style>

+ 1738 - 0
src/components/pages/components/chatArea.vue

@@ -0,0 +1,1738 @@
+<template>
+	<div class="chatArea" v-loading="loading">
+		<div class="m-operation">
+			<div>实时转录</div>
+			<div>2023-11-11 11:11:24</div>
+		</div>
+		<div class="titBar">
+			<div class="titBarLeft">
+				<div
+					@click="cutBar(index)"
+					:class="pageStatus == index ? 'titBarBorder' : ''"
+					v-for="(i, index) in titBarList"
+					:key="index + 'a'"
+				>
+					<img :src="pageStatus == index ? i.ico : i.ico1" alt="" />{{
+						i.title
+					}}
+				</div>
+			</div>
+			<div class="titBarRig">
+				<!-- <img src="@/assets/icon/classroomObservation/put.png" alt="" />
+				<div style="cursor: pointer">收起</div> -->
+			</div>
+		</div>
+		<div class="ca-top">
+			<!-- 开始页面 -->
+			<startPage
+				v-show="showIndexPage"
+				@startTape="recordedStart"
+				@uploadTape="uploadRecording"
+			/>
+			<!-- 原文速递 -->
+			<transcription
+				v-show="pageStatus == 1 && !showIndexPage"
+				:data="transcriptionData"
+			/>
+			<!-- ai对话 -->
+			<tape
+				ref="tapeRef"
+				:aiNameList="roleList"
+				:chatData="chatList"
+				v-show="pageStatus == 0 && !showIndexPage"
+			/>
+			<!-- <div class="t-t-m-Item" v-show="cardStatus==1"> -->
+			<iframe
+				ref="viframe"
+				v-if="pageStatus == 2 && !showIndexPage"
+				style="width: 100%; height: 100%; border: none"
+				:src="
+					'https://view.officeapps.live.com/op/view.aspx?src=' +
+					encodeURIComponent(fileUrl)
+				"
+			></iframe>
+			<!-- </div> -->
+		</div>
+		<div class="ca-bottom">
+			<div class="ca-b-operation">
+				<div class="ca-b-o-header">
+					<div class="ca-b-o-h-left">
+						<!-- <div class="ca-b-o-h-l-select" @click.stop="changeAnalysis()">
+							<span class="ca-b-o-h-l-s-icon el-icon-collection"></span>
+							<div class="ca-b-o-h-s-l-text">课堂观察</div>
+							<span class="ca-b-o-h-s-l-icon2 el-icon-caret-top"></span>
+						</div> -->
+
+						<div class="ca-b-o-h-l-select" style="color: #3681fc">
+							<div
+								style="cursor: pointer"
+								class="ca-b-o-h-s-l-text"
+								@click.stop="languageShow = !languageShow"
+							>
+								{{ languageList.find((i) => i.label == languageRadio).lang }}
+							</div>
+
+							<div
+								class="languageList"
+								v-click-outside="handleBlur"
+								v-if="languageShow"
+							>
+								<el-radio
+									v-for="(i, index) in languageList"
+									:key="index + 'lag'"
+									:class="i.label == languageRadio ? 'radioBg' : ''"
+									v-model.number="languageRadio"
+									:label.Num="i.label"
+									>{{ i.lang }}</el-radio
+								>
+							</div>
+
+							<!-- <span class="ca-b-o-h-s-l-icon2 el-icon-caret-top"></span> -->
+						</div>
+
+						<div class="ca-b-o-h-l-btn" @click.stop="uploadRecording()">
+							<div class="ca-b-o-h-b-l-text">上传录音</div>
+						</div>
+					</div>
+					<div class="ca-b-o-h-right">
+						<div class="ca-b-o-h-r-radio">
+							<div
+								:class="
+									(index == 0 && showIndexPage) ||
+									(index == 1 && !showIndexPage)
+										? 'TapeCss'
+										: ''
+								"
+								class="tapeSty"
+								@click="cutTape(index)"
+								v-for="(i, index) in tapeList"
+								:key="index + 'b'"
+							>
+								<img
+									:src="
+										(index == 0 && showIndexPage) ||
+										(index == 1 && !showIndexPage)
+											? i.ico
+											: i.ico1
+									"
+									alt=""
+								/>
+							</div>
+
+							<!-- <span @click.stop="changeContinuousDialogue(!continuousDialogue)"
+								>连续对话</span
+							>
+							<el-switch
+								v-model="continuousDialogue"
+								active-color="#3681FC"
+								inactive-color="#b2bfc3"
+							>
+							</el-switch> -->
+						</div>
+					</div>
+				</div>
+				<div class="ca-b-o-main">
+					<div
+						class="ca-b-o-m-tape"
+						v-show="controlsStatus == 0"
+						@click.stop="recordedStart()"
+					>
+						<span class="el-icon-microphone"></span>
+						<div class="ca-b-o-m-t-text">点击开始录音</div>
+					</div>
+					<div class="ca-b-o-m-tapeTwo" v-show="controlsStatus == 2">
+						<mini-audio
+							v-if="audioUrl"
+							:audio-source="audioUrl"
+							class="audio_class"
+						></mini-audio>
+						<div
+							style="
+								width: 32px;
+								height: 32px;
+								margin-left: 20px;
+								cursor: pointer;
+								background-color: #3681fc;
+								border-radius: 50%;
+								display: flex;
+								justify-content: center;
+								align-items: center;
+							"
+							@click="recordedStart()"
+						>
+							<img
+								style="width: 10px; height: 16px"
+								src="../../../../assets/icon/classroomObservation/blueMai.png"
+								alt=""
+							/>
+						</div>
+					</div>
+					<div
+						class="ca-b-o-m-inputAre"
+						v-show="controlsStatus == 3"
+						v-loading="textareaLoading"
+					>
+						<div class="ca-b-o-m-left">
+							<textarea
+								id="myTextarea"
+								ref="textareaRef"
+								min-rows="1"
+								max-rows="5"
+								v-model="textareaValue"
+								placeholder="在此输入您想了解的内容"
+								autosize="none"
+								@input="textareaChange"
+								@change="textareaChange"
+								@keydown="textareaKeydown"
+							></textarea>
+						</div>
+						<div class="ca-b-o-m-right">
+							<!-- <span @click.stop="tapeSubmit()"></span> -->
+							<!-- <div :class="sendBtnDsiable ? 'ca-b-o-m-r-dsiableBtn' : ''">
+								发送
+							</div> -->
+							<el-button
+								:disabled="textareaValue.trim().length == 0"
+								type="primary"
+								size="mini"
+								@click="send()"
+								>发送</el-button
+							>
+						</div>
+						<div
+							ref="roleListRef"
+							v-click-outside="noShowRoleList"
+							class="ca_b_o_m_roleList"
+							v-if="showRoleList && choseRoleList.length != 0"
+						>
+							<div
+								:class="[
+									'ca_b_o_m_rl_item',
+									roleListIndex == index ? 'ca_b_o_m_rl_itemActive' : '',
+								]"
+								v-for="(item, index) in choseRoleList"
+								:key="item.assistant_id"
+								@click="choseRole(item)"
+								@mouseover="() => (roleListIndex = index)"
+							>
+								<div class="ca_b-o_m_rl_i_top">
+									<el-avatar
+										size="medium"
+										:src="
+											item.headUrl
+												? item.headUrl
+												: require('@/assets/icon/classroomObservation/aiAvatar.png')
+										"
+									></el-avatar>
+									<div>
+										<div>{{ item.assistantName }}</div>
+										<span v-if="item.username">作者:{{ item.username }}</span>
+									</div>
+								</div>
+								<div class="ca_b-o_m_rl_i_bottom">
+									<span>{{ item.description }}</span>
+								</div>
+							</div>
+						</div>
+					</div>
+					<div class="ca-b-o-m-TapeArea" v-show="controlsStatus == 1">
+						<div class="ca-b-o-m-i-left">
+							<img
+								style="height: 120%"
+								src="@/assets/icon/classroomObservation/isTape.png"
+								alt=""
+							/>
+							<div>
+								<div v-if="recordedForm.status == 1" style="color: #ee3e3e">
+									录音中...
+								</div>
+								<div v-if="recordedForm.status == 2" style="color: #6b798e">
+									已暂停...
+								</div>
+								<span>{{ recordedForm.time }}</span>
+							</div>
+						</div>
+						<!-- <img
+							style="height: 120%"
+							src="@/assets/icon/classroomObservation/tapetime.png"
+							alt=""
+						/> -->
+						<div
+							style="
+								width: 100px;
+								display: flex;
+								justify-content: space-between;
+							"
+						>
+							<div class="lyStart" @click="stopRecorded()">
+								<img
+									style="width: 12px; height: 12px"
+									src="@/assets/icon/classroomObservation/lyStart.png"
+									alt=""
+									v-if="recordedForm.status == 1"
+								/>
+								<img
+									style="width: 12px; height: 12px"
+									src="@/assets/icon/classroomObservation/start.png"
+									alt=""
+									v-if="recordedForm.status == 2"
+								/>
+							</div>
+							<div class="lyStart" @click="finishRecorded()">
+								<img
+									style="width: 12px; height: 12px"
+									src="@/assets/icon/classroomObservation/lyStop.png"
+									alt=""
+								/>
+							</div>
+						</div>
+						<!-- <div class="ca-b-o-m-left">
+							<textarea :value="textareaValue" autosize="none"></textarea>
+						</div>
+						<div class="ca-b-o-m-right">
+							<span @click.stop="tapeSubmit()"></span>
+							<div :class="sendBtnDsiable ? 'ca-b-o-m-r-dsiableBtn' : ''">
+								发送
+							</div>
+						</div> -->
+					</div>
+				</div>
+			</div>
+		</div>
+		<!-- 录音转文字 -->
+		<iframe
+			allow="camera *; microphone *;display-capture;midi;encrypted-media;"
+			src="https://beta.cloud.cocorobo.cn/browser/public/index.html"
+			ref="iiframe"
+			v-show="false"
+		></iframe>
+	</div>
+</template>
+
+<script>
+import startPage from "./startPage.vue";
+import transcription from "./transcription.vue";
+import tape from "./tape.vue";
+import { v4 as uuidv4 } from "uuid";
+import Recorder from "js-audio-recorder";
+import MarkdownIt from "markdown-it";
+const lamejs = require("lamejs");
+
+const recorder = new Recorder({
+	sampleBits: 16, // 采样位数,支持 8 或 16,默认是16
+	sampleRate: 48000, // 采样率,支持 11025、16000、22050、24000、44100、48000,根据浏览器默认值,我的chrome是48000
+	numChannels: 1, // 声道,支持 1 或 2, 默认是1
+	// compiling: false,(0.x版本中生效,1.x增加中) // 是否边录边转换,默认是false
+});
+
+// 绑定事件-打印的是当前录音数据
+// recorder.onprogress = function (params) {
+// 	console.log('--------------START---------------')
+// 	console.log('录音时长(秒)', params.duration);
+// 	console.log('录音大小(字节)', params.fileSize);
+// 	console.log('录音音量百分比(%)', params.vol);
+// 	console.log('当前录音的总数据([DataView, DataView...])', params.data);
+// 	console.log('--------------END---------------')
+// };
+
+// 自定义指令,用于处理点击外部区域的事件
+const clickOutside = {
+	bind(el, binding) {
+		// 在元素上绑定一个点击事件监听器
+		el.clickOutsideEvent = function (event) {
+			// 检查点击事件是否发生在元素的内部
+			if (!(el === event.target || el.contains(event.target))) {
+				// 如果点击事件发生在元素的外部,则触发指令绑定的方法,将点击的event数据传过去
+				binding.value(event);
+			}
+		};
+		// 在文档上添加点击事件监听器
+		document.addEventListener("click", el.clickOutsideEvent);
+	},
+	unbind(el) {
+		// 在元素上解除点击事件监听器
+		document.removeEventListener("click", el.clickOutsideEvent);
+	},
+};
+export default {
+	emits: ["updateFileId", "changeAudioUrl"],
+	props: {
+		tid: {
+			type: String,
+			require: true,
+		},
+		fileId: {
+			type: String,
+			default: "",
+		},
+		fileIdId: {
+			type: String,
+			default: "",
+		},
+	},
+	components: {
+		startPage,
+		transcription,
+		tape,
+	},
+	directives: {
+		"click-outside": clickOutside, // 注册自定义指令
+	},
+	data() {
+		return {
+			// continuousDialogue: true,
+			controlsStatus: 3, //0--点击开始录音  1--录音中   2--录音完毕预览  3--文字输入
+			pageStatus: 0, //0--ai对话  1--原文文稿  2--转录文稿
+			showIndexPage: false, //是否显示初始页面
+			languageRadio: 1, //设置选择语言
+			languageShow: false, //控制显示
+			loading: false,
+			textareaValue: "",
+			textareaLoading: false,
+			showRoleList: false,
+			roleListIndex: 0,
+			recordedForm: {
+				time: "00:00:00", //时间
+				status: 0, //0--未录音  1--正在录音  2--暂停  3--录音结束
+			},
+			roleList: [],
+			publicRoleList: [],
+			audioUrl: "",
+			fileUrl:
+				"https://ccrb.s3.cn-northwest-1.amazonaws.com.cn/%E8%BD%AC%E5%BD%95%E6%96%87%E7%A8%BF1713172600896.xlsx",
+			// 设置list
+			languageList: [
+				{ label: 1, lang: "普通话" },
+				{ label: 2, lang: "广东话" },
+				{ label: 3, lang: "英语" },
+			],
+			// 语音文字list
+			tapeList: [
+				{
+					ico: require("@/assets/icon/classroomObservation/mai1.png"),
+					ico1: require("@/assets/icon/classroomObservation/mai2.png"),
+				},
+				{
+					ico: require("@/assets/icon/classroomObservation/wen1.png"),
+					ico1: require("@/assets/icon/classroomObservation/wen2.png"),
+				},
+			],
+			titBarList: [
+				{
+					title: "Al对话",
+					ico: require("@/assets/icon/classroomObservation/Group9101.png"),
+					ico1: require("@/assets/icon/classroomObservation/Group9102.png"),
+				},
+				{
+					title: "原文速览",
+					ico1: require("@/assets/icon/classroomObservation/Vector.png"),
+					ico: require("@/assets/icon/classroomObservation/Vector2.png"),
+				},
+				{
+					title: "转录文稿",
+					ico1: require("@/assets/icon/classroomObservation/zhuanlu.png"),
+					ico: require("@/assets/icon/classroomObservation/zhuanlu2.png"),
+				},
+			],
+			transcriptionData: {
+// 				time: "2024-01-13 15:58:00",
+// 				content: `对爱整洁,有精神、不拖拉,多思考。老师你好,同学们好。
+// 请坐。前几天白老师关注到一则新闻,我们大鹏新区政府提出要将2024年作为旅游高质量发展年,为了吸引更多的游客来到咱们大鹏,你认为最重要的是什么来?第一小组。
+// 我认为最重要的是要先把环境、绿水青山环境弄好,这样才能吸引更多的人来,大鹏是的。
+// 大家同意他的想法吗?保护大棚环境人人有责,上周咱们班的两位环境调查员就去到葵冲进行了实地调查,我们来看看他们发现了什么。
+// 大家好,我们是人大附中深圳学校五1班的张龙溪,何瑞一为了响应区政府的号召,今天我们作为环境调查员,准备实地调查一下葵冲的环境情况,走跟随我们的镜头一起去看一看我们调查到的情况有什么样,这样的。
+// 应该是这样的,就是这样的,像这样的。
+// 像这种情况。
+// 像这种情况。
+// 像这样的。
+// 我们调查到的情况不是很好,同学们你们。
+// 谁知谁知。
+// 刚刚视频有一些小小的不同步,但不影响我们去观察里面的内容。
+// 好,来,同学们,通过刚才的视频,你看到了哪些环境问题?第五小组。
+// 我看到了菜市场上到处是垃圾,到处是那些烂掉的瓜果皮,是的。
+// 其他小组呢?来第八小组我。
+// 看我看到了路上还有很多漂移的垃圾就路上了。
+// 你观察得很认真,是的,在咱们生活的这片区域确实存在很多的垃圾问题,这些垃圾主要是家庭里面产生的生活垃圾。
+// 根据调查,一个家庭一天产生的垃圾大约是三千克,咱们班一共有45位同学,所有同学的家庭一天产生的生活垃圾总质量是多少?老师还调查到一些数据。我校小学部一共有1556人,整个大鹏新区共有55,224户家庭,整个深圳市有6,424,556户家庭,他们一天分别产生多少垃圾呢?现在请小组长从材料框当中拿出实验记录单,我们用计算器来速算一下,将结果记录下来。
+// 好,可以开始计算。二十五六场这样做的之前工作。
+// 还差一些,这样更准确一点。
+// 用计算器这样更准确算完了没有?
+// 你们。
+// 算完了吗老师?
+// 拍一下照片。
+// 大家算完了吗?
+// 好,算完的小组面向老师坐,好好表扬第八小组。好,第五、第四、第三。
+// 好,这是第四小组的数据,我们来看跟大家的一样吗?一样,在刚刚大家记录数据的时候,你发现了什么?在你刚刚写下这些数据,有什么发现?第三小组。
+// 我发现每天生产的垃圾会非常多。
+// 垃圾很多多,到什么程度?我们来看。多到全班同学一天产生的垃圾,相当于一头成年公羊这么重,多到全校小学部家庭的垃圾,一天有一头非洲大象这么重。大鹏新区所有家庭一天产生的垃圾量相当于32头,非洲大象这么重,整个深圳市一天产生的垃圾高达3800头,非洲大象这么重。
+// 老师看到很多同学发出来很惊讶的表情,整个深圳市就产生这么多的垃圾,整个广东省全国23个省加起来,放眼全球,全世界一年产生的垃圾是多少?我们来看这个统计图,把各个地区的垃圾量加起来,全球一年产生垃圾量高达20.17亿吨。这么多的垃圾如果填埋高度是一米的话,它可以埋掉三个深圳或者一个广州的面积。
+// 面对这组数据,你有什么样的感受跟大家分享一下。来,第一小组,垃圾好多。
+// 垃圾很多。
+// 其他小组有什么样的感受?第八小组。
+// 如果我们一直都以填埋的方式去处理垃圾的话,可能全球的土地都用于填埋垃圾了。
+// 你还告诉了大家处理垃圾的一种方式是的,这么多的垃圾如果没有得到及时的处理,他们会侵占大量的土地包围我们的城市家园,我们就会陷入到一种垃圾围城的现状,面对这么庞大的垃圾量,我们该怎么办?
+// 第七小组。
+// 我觉得我们可以按照垃圾的种类来进行分类,比如说回收垃圾,它可以是回收,然后再利用的或者就是有害垃圾,比如说电池会污染,将会人们将会对它处以专业的处理。
+// 你想到了垃圾分类,其他小组。
+// 第五小组。
+// 并且像很多我们要应该少用一次性的垃圾,比如说你买菜的时候要少用一次性的塑料袋,尽量用布袋,这样子的话可以节省既节省资源,又节省了垃圾的。
+// 生产量,你说的是一个非常实用的方法,大家刚刚都说出来了,实际的行动是的。今天就让我们一起行动起来,一起共同想办法来解决垃圾问题。
+// 面对垃圾问题。
+// 正如第八小组刚刚说的,咱们国家早期的处理方式是直接填埋和焚烧发电,这两种方法在一定程度上减少了垃圾,但也存在着很严重的弊端。现在我们就一起来说一说这两种方法的好处和弊端。第一小组。
+// 直接填埋这种方法会占用大量土地,而且还会污染土地,焚烧发电这种方法会会污_
+// `,
+			},
+			chatList: [],
+		};
+	},
+	computed: {
+		// 选择可以@的角色
+		choseRoleList() {
+			let result = [...this.roleList, ...this.publicRoleList];
+			const _index = this.textareaValue.lastIndexOf("@");
+			if (_index !== -1) {
+				let roleName = this.textareaValue.substring(_index + 1);
+				result = result.filter((i) => i.assistantName.indexOf(roleName) != -1);
+			} else {
+				return [];
+			}
+			return result;
+		},
+	},
+	watch: {
+		choseRoleList() {
+			this.roleListIndex = 0;
+		},
+	},
+	methods: {
+		handleBlur(event) {
+			// console.log("点击其它区域啦", event);
+			this.languageShow = !this.languageShow;
+		},
+		// 上传录音
+		uploadRecording() {
+			let input = document.createElement("input");
+			input.type = "file";
+			input.accept = ".wav";
+			// input.accept = "audio/*, .txt, .pdf, .xlsx";
+			input.click();
+			input.onchange = () => {
+				this.loading = true;
+				let file = input.files[0];
+				this.uploadWavFileAndGetText(file)
+				return;
+				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.$message.error("上传失败");
+								_this.loading = false;
+							} else {
+								// 判断是不是音频文件
+								const audioRegex = /\.(mp3|wav|ogg|flac|m4a)$/i;
+								if (audioRegex.test(data.Location)) {
+									_this.controlsStatus = 2;
+									_this.showIndexPage = false;
+									_this.pageStatus = 1;
+									// _this.audioUrl = data.Location;
+									_this.$emit("changeAudioUrl", data);
+									_this.loading = false;
+								} else {
+									_this.ajax
+										.put("https://gpt4.cocorobo.cn/upload_file_knowledge", {
+											url: data.Location,
+										})
+										.then((res) => {
+											let _data = res.data.FunctionResponse;
+											if (_data.result && _data.result.id) {
+												_this.$emit("updateFileId", _data.result.id);
+												let pram2 = {
+													id: _this.fileIdId,
+													json_data: JSON.stringify({
+														file_ids: _data.result.id,
+													}),
+													// json_data: JSON.stringify({file_ids:'file-r5phg4I2oFqly4WpW7oOOTnA'}),
+												};
+												_this.ajax.post(
+													"https://gpt4.cocorobo.cn/update_classroom_observation",
+													pram2
+												);
+											} else {
+												_this.$message.error("上传失败");
+											}
+											_this.loading = false;
+											// this.$emit("updateFileId", data.Location)
+										})
+										.catch((e) => {
+											_this.loading = false;
+											_this.$message.error("上传失败");
+										});
+								}
+
+								// console.log(data.Location)
+							}
+						});
+				}
+			};
+			// this.mainBtnStatus = 1;
+			// this.barNum = 4;
+		},
+		cutBar(val) {
+			this.pageStatus = val;
+			if (this.pageStatus == 0) {
+				//ai对话
+				this.controlsStatus = 3;
+			} else if (this.pageStatus == 1 || this.pageStatus == 2) {
+				// 原文速览&&转录文稿
+				this.controlsStatus = 2;
+			}
+			this.showIndexPage = false;
+		},
+		cutTape(val) {
+			if (val == 0) {
+				this.showIndexPage = true;
+				this.controlsStatus = 0;
+			} else if (val == 1) {
+				if (this.pageStatus == 0) {
+					//ai对话
+					this.controlsStatus = 3;
+				} else if (this.pageStatus == 1 || this.pageStatus == 2) {
+					// 原文速览&&转录文稿
+					this.controlsStatus = 2;
+				}
+				this.showIndexPage = false;
+			}
+		},
+		recordedStart() {
+			// 开始录音
+			if (this.audioUrl) {
+				this.$confirm("再次录音会顶替掉原先的录音,您确定吗", "提醒", {
+					confirmButtonText: "确定",
+					cancelButtonText: "取消",
+					type: "warning",
+				})
+					.then(() => {
+						this.recordedForm.status = 0;
+						this.audioUrl = "";
+						recorder.initRecorder(); //初始化录音
+						recorder.destroy(); // 销毁录音
+						this.recordedStart();
+					})
+					.catch((_) => {
+						console.log("不顶替");
+					});
+			} else if (this.controlsStatus != 1 && this.recordedForm.status == 0) {
+				recorder.initRecorder(); //初始化录音
+				recorder.destroy(); // 销毁录音
+				// 开始录音
+				recorder.start().then(
+					() => {
+						this.controlsStatus = 1;
+						this.recordedForm.status = 1;
+						recorder.onprogress = this.updateRecordedTime;
+						this.$message.success("录音已开始");
+					},
+					(error) => {
+						this.controlsStatus = 0;
+						this.recordedForm.status = 0;
+						// _this.$message.error(`${error.name} : ${error.message}`);
+						this.$message.error(
+							`没有找到可使用的麦克风,或者您没有允许此网页使用麦克风`
+						);
+						// 出错了
+						console.log(`${error.name} : ${error.message}`);
+						// if (_this.calcTimer) {
+						//   clearInterval(_this.calcTimer)
+						//   _this.calcTimer = null;
+						// }
+					}
+				);
+			} else if ([1, 2].includes(this.recordedForm.status)) {
+				this.controlsStatus = 1;
+				this.$message.info("还在录音中");
+			}
+			// this.controlsStatus = 1;
+		},
+		updateRecordedTime({ duration }) {
+			// 更新currentTime,将秒数转换为时分秒格式
+			let hours = Math.floor(duration / 3600);
+			let minutes = Math.floor((duration % 3600) / 60);
+			let seconds = Math.floor(duration % 60);
+			this.recordedForm.time = `${hours.toString().padStart(2, "0")}:${minutes
+				.toString()
+				.padStart(2, "0")}:${seconds.toString().padStart(2, "0")}`;
+		},
+		//切换观察
+		// changeAnalysis() {
+		// 	this.$message.info("切换观察");
+		// },
+		// 切换连续对话
+		changeContinuousDialogue(newValue) {
+			this.continuousDialogue = newValue;
+		},
+		// 点击发送旁的录音
+		// tapeSubmit() {
+		// this.$message.info("发送旁的录音");
+		// this.mainBtnStatus = 0;
+		// this.pageStatus = 1;
+		// this.TapeNum = 0;
+		// },
+		async finishRecorded() {
+			this.loading = true;
+			recorder.stop();
+			this.$message.success("已结束录音");
+
+			
+			let file = this.convertToMp3(recorder.getWAV());
+			const mp3Blob = recorder.getWAVBlob();
+			let audioFile = this.dataURLtoAudio(mp3Blob, "wav");
+			this.uploadWavFileAndGetText(audioFile)
+			return;
+			file.lastModifiedDate = new Date();
+			file.name = "recordedFile.mp3";
+			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.$message.error("上传失败");
+							_this.loading = false;
+						} else {
+							// 判断是不是音频文件
+							// 更改录音文件
+							_this.$emit("changeAudioUrl", data);
+							_this.controlsStatus = 2;
+							_this.pageStatus = 1;
+							_this.recordedForm.status = 3;
+							_this.showIndexPage = false;
+							_this.loading = false;
+							// const audioRegex = /\.(mp3|wav|ogg|flac|m4a)$/i;
+							// if (audioRegex.test(data.Location)) {
+							// 	_this.controlsStatus = 2;
+							// 	_this.showIndexPage = false;
+							// 	_this.pageStatus = 1;
+							// 	_this.audioUrl = data.Location;
+							// 	_this.loading = false;
+							// } else {
+							// 	_this.ajax
+							// 		.put("https://gpt4.cocorobo.cn/upload_file_knowledge", {
+							// 			url: data.Location,
+							// 		})
+							// 		.then((res) => {
+							// 			let _data = res.data.FunctionResponse;
+							// 			if (_data.result && _data.result.id) {
+							// 				_this.$emit("updateFileId", _data.result.id);
+							// 				let pram2 = {
+							// 					id: _this.fileIdid,
+							// 					json_data: JSON.stringify({
+							// 						file_ids: _data.result.id,
+							// 					}),
+							// 					// json_data: JSON.stringify({file_ids:'file-r5phg4I2oFqly4WpW7oOOTnA'}),
+							// 				};
+							// 				_this.ajax.post(
+							// 					"https://gpt4.cocorobo.cn/update_classroom_observation",
+							// 					pram2
+							// 				);
+							// 			} else {
+							// 				_this.$message.error("上传失败");
+							// 			}
+							// 			_this.loading = false;
+							// 			// this.$emit("updateFileId", data.Location)
+							// 		})
+							// 		.catch((e) => {
+							// 			_this.loading = false;
+							// 			_this.$message.error("上传失败");
+							// 		});
+							// }
+
+							// console.log(data.Location)
+						}
+					});
+			}
+			// recorder.download(mp3Blob, "recorder", "mp3");
+			// let wavBlob = await recorder.getWAVBlob()
+			// const reader = new FileReader();
+			//将blob转未ArrayBuffer
+			// reader.onload = () => {
+			// 	const audioData = reader.result; // 获取 WAV blob 数据
+			// 	const samples = new Int16Array(audioData); // 转换为 Int16Array
+			// 	// 创建一个mp3编码器
+			// 	const mp3Encoder = new lamejs.Mp3Encoder(1, 44100, 128);
+			// 	// 将录音数据编码为MP3
+			// 	const mp3Data = mp3Encoder.encodeBuffer(samples);
+			// 	// 创建一个Blob包含MP3数据
+			// 	const mp3Blob = new Blob([mp3Data], { type: "audio/mp3" });
+			// 	console.log(`MP3文件:${mp3Blob}`);
+			// 	this.loading = false;
+			// 	recorder.initRecorder(); // 初始化录音
+			// 	recorder.destroy(); // 销毁录音
+			// };
+			// reader.readAsArrayBuffer(blob);
+		},
+		stopRecorded() {
+			if (!recorder.ispause) {
+				recorder.pause();
+				this.recordedForm.status = 2;
+				this.$message.warning("已暂停录音");
+			} else {
+				recorder.resume();
+				this.recordedForm.status = 1;
+				this.$message.success("已继续录音");
+			}
+		},
+		changeAudioUrl(newValue) {
+			if (!newValue) return;
+			this.audioUrl = newValue;
+			this.pageStatus = 1;
+			this.controlsStatus = 2;
+			this.showIndexPage = false;
+		},
+		// 发送消息
+		send(_text = this.textareaValue) {
+			this.textareaValue = "";
+			// 判断输入的文本是否为空
+			if (!_text.trim()) return;
+			// 这里处理@的角色
+			let _atRoleList = [];
+			let _roleList = [...this.roleList, ...this.publicRoleList];
+			_roleList.forEach((i) => {
+				if (_text.indexOf(`@${i.assistantName}`) != -1) {
+					_atRoleList.push(i);
+				}
+			});
+			if (_atRoleList.length > 0) {
+				//有@角色
+				let _replaceText = _text;
+				let _htmlText = _text;
+				_atRoleList.forEach((_i) => {
+					_replaceText = _replaceText.replaceAll(`@${_i.assistantName}`, ``);
+					_htmlText = _htmlText.replaceAll(
+						`@${_i.assistantName}`,
+						`<span class='aite-name'>@${_i.assistantName}</span>`
+					);
+				});
+				_atRoleList.forEach((_item, _index) => {
+					const _uid = uuidv4();
+					if (_index == 0) {
+						this.chatList.push({
+							loading: true,
+							role: "user",
+							content: _htmlText,
+							uid: _uid,
+							AI: "AI",
+							aiContent: "",
+							oldContent: "",
+							isShowSynchronization: false,
+							filename: _item.file_names,
+							index: this.chatList.length,
+							is_mind_map: false,
+							fileid: _item.assistantName,
+						});
+					} else {
+						this.chatList.push({
+							loading: true,
+							role: "user",
+							content: "",
+							uid: _uid,
+							AI: "AI",
+							aiContent: "",
+							oldContent: "",
+							isShowSynchronization: false,
+							filename: _item.file_names,
+							index: this.chatList.length,
+							is_mind_map: false,
+							fileid: _item.assistantName,
+						});
+					}
+					this.scrollBottom();
+					let params = {
+						assistant_id: _item.assistant_id,
+						userId: "602def61-005d-11ee-91d8-005056b86db5",
+						message: _replaceText,
+						session_name: "",
+						uid: _uid,
+						file_ids: this.fileId ? [this.fileId] : [],
+					};
+					this.ajax
+						.post("https://gpt4.cocorobo.cn/ai_agent_park_chat_new", params)
+						.then((res) => {
+							if (res.data.FunctionResponse.result == "发送成功") {
+							} else {
+								this.$message.warning(response.data.FunctionResponse.result);
+							}
+						})
+						.catch((err) => {
+							console.log(err);
+						});
+					this.getAtAuContent(
+						_uid,
+						_htmlText,
+						_item.headUrl,
+						_item.assistantName
+					);
+				});
+			} else {
+				//未@角色
+
+				let _uuid = uuidv4();
+				this.chatList.push({
+					role: "user",
+					content: `${_text}`,
+					uid: _uuid,
+					AI: "AI",
+					aiContent: "",
+					oldContent: "",
+					isShowSynchronization: false,
+					filename: "",
+					index: this.chatList.length,
+					is_mind_map: false,
+					loading: true,
+				});
+				this.scrollBottom();
+				// 连续对话设置
+				let _historyMessage = [];
+				// this.chatList.forEach(i=>{
+
+				// })
+				_historyMessage.push({ role: "user", content: _text });
+				let params = JSON.stringify({
+					model: "gpt-3.5-turbo",
+					temperature: 0,
+					max_tokens: 4096,
+					top_p: 1,
+					frequency_penalty: 0,
+					presence_penalty: 0,
+					messages: _historyMessage,
+					uid: _uuid,
+					mind_map_question: "",
+				});
+				this.ajax
+					.post("https://gpt4.cocorobo.cn/chat", params)
+					.then((res) => {
+						if (res.data.FunctionResponse.result == "发送成功") {
+						} else {
+							this.$message.warning(response.data.FunctionResponse.result);
+						}
+					})
+					.catch((e) => {
+						console.log(e);
+					});
+				this.getAiContent(_uuid);
+			}
+		},
+		getAiContent(_uid) {
+			let _source = new EventSource(`https://gpt4.cocorobo.cn/stream/${_uid}`); //http://gpt4.cocorobo.cn:8011/stream/     https://gpt4.cocorobo.cn/stream/
+			let _allText = "";
+			let _mdText = "";
+			const md = new MarkdownIt();
+			_source.onmessage = (_e) => {
+				if (_e.data.replace("'", "").replace("'", "") == "[DONE]") {
+					//对话已经完成
+					_mdText = _mdText.replace("_", "");
+					_source.close();
+					this.chatList.find((i) => i.uid == _uid).aiContent = _mdText;
+					this.chatList.find((i) => i.uid == _uid).isalltext = true;
+					this.chatList.find((i) => i.uid == _uid).isShowSynchronization = true;
+					this.chatList.find((i) => i.uid == _uid).loading = false;
+					// 保存对话👇 #TODO保存对话
+
+					return;
+				} else {
+					//对话还在继续
+					let _text = "";
+					_text = _e.data.replaceAll("'", "");
+					if (_allText == "") {
+						_allText = _text.replace(/^\n+/, ""); //去掉回复消息中偶尔开头就存在的连续换行符
+					} else {
+						_allText += _text;
+					}
+					_mdText = _allText + "_";
+					_mdText = _mdText.replace(/\\n/g, "\n");
+					_mdText = _mdText.replace(/\\/g, "");
+					if (_allText.split("```").length % 2 == 0) _mdText += "\n```\n";
+					//转化返回的回复流数据
+					_mdText = md.render(_mdText);
+					this.chatList.find((i) => i.uid == _uid).aiContent = _mdText;
+					this.chatList.find((i) => i.uid == _uid).loading = false;
+					this.scrollBottom();
+					// 处理流数据
+				}
+			};
+		},
+		getAtAuContent(_uid, _text, _headUrl, _assistantName) {
+			let _source = new EventSource(
+				`https://gpt4.cocorobo.cn/question/${_uid}`
+			); //http://gpt4.cocorobo.cn:8011/question/   https://gpt4.cocorobo.cn/question/
+			let _allText = "";
+			let _mdText = "";
+			const md = new MarkdownIt();
+			_source.onmessage = (_e) => {
+				let _eData = JSON.parse(_e.data);
+				if (_eData.content.replace("'", "").replace("'", "") == "[DONE]") {
+					let _result = [];
+					if ("result" in _eData) {
+						_result = _eData.result;
+						for (let i = 0; i < _result.length; i++) {
+							_mdText = _mdText.replace(_result[i].text, _result[i].fileName);
+						}
+					}
+					_mdText = _mdText.replace("_", "");
+					this.chatList.find((i) => i.uid == _uid).aiContent = _mdText;
+					this.chatList.find((i) => i.uid == _uid).isalltext = true;
+					this.chatList.find((i) => i.uid == _uid).isShowSynchronization = true;
+					this.chatList.find((i) => i.uid == _uid).loading = false;
+					this.scrollBottom();
+					//#TODO 这里保存对话
+				} else {
+					let _text = _eData.content.replace("'", "").replace("'", "");
+					if (_allText == "") {
+						_allText = _text.replace(/^\n+/, ""); //去掉回复消息中偶尔开头就存在的连续换行符
+					} else {
+						_allText += _text;
+					}
+					_mdText = _allText + "_";
+					_mdText = _mdText.replace(/\\n/g, "\n");
+					_mdText = _mdText.replace(/\\/g, "");
+					if (_allText.split("```").length % 2 == 0) _mdText += "\n```\n";
+					//转化返回的回复流数据
+					_mdText = md.render(_mdText);
+					this.chatList.find((i) => i.uid == _uid).aiContent = _mdText;
+					this.chatList.find((i) => i.uid == _uid).loading = false;
+					this.scrollBottom();
+					// 处理流数据
+				}
+			};
+		},
+		textareaChange() {
+			if (this.textareaValue.at(-1) == "@") {
+				this.showRoleList = true;
+			}
+			this.$refs.textareaRef.style.height = "50px";
+			this.$refs.textareaRef.style.height =
+				this.$refs.textareaRef.scrollHeight + "px";
+			if (this.$refs.roleListRef) {
+				let roleListHeight =
+					this.$refs.textareaRef.scrollHeight >= 500
+						? 520
+						: this.$refs.textareaRef.scrollHeight + 20;
+				this.$refs.roleListRef.style["margin-bottom"] = "70px";
+				this.$refs.roleListRef.style["margin-bottom"] = roleListHeight + "px";
+				this.$refs.roleListRef.style["max-height"] = `calc(100vh - ${
+					roleListHeight + 160
+				}px)`;
+			}
+		},
+		// 滚动条触底
+		scrollBottom() {
+			this.$nextTick(() => {
+				this.textareaChange();
+				this.$refs.tapeRef.$el.querySelector(".t-chartArea").scrollTop =
+					this.$refs.tapeRef.$el.querySelector(".t-chartArea").scrollHeight;
+			});
+		},
+		// 点击了其他地方然后关闭角色列表
+		noShowRoleList() {
+			this.showRoleList = false;
+		},
+		convertToMp3(wavDataView) {
+			// 获取wav头信息
+			const wav = lamejs.WavHeader.readHeader(wavDataView); // 此处其实可以不用去读wav头信息,毕竟有对应的config配置
+			const { channels, sampleRate } = wav;
+			const mp3enc = new lamejs.Mp3Encoder(channels, sampleRate, 128);
+			// 获取左右通道数据
+			const result = recorder.getChannelData();
+			const buffer = [];
+			const leftData =
+				result.left &&
+				new Int16Array(result.left.buffer, 0, result.left.byteLength / 2);
+			const rightData =
+				result.right &&
+				new Int16Array(result.right.buffer, 0, result.right.byteLength / 2);
+			const remaining = leftData.length + (rightData ? rightData.length : 0);
+			const maxSamples = 1152;
+			for (let i = 0; i < remaining; i += maxSamples) {
+				const left = leftData.subarray(i, i + maxSamples);
+				let right = null;
+				let mp3buf = null;
+				if (channels === 2) {
+					right = rightData.subarray(i, i + maxSamples);
+					mp3buf = mp3enc.encodeBuffer(left, right);
+				} else {
+					mp3buf = mp3enc.encodeBuffer(left);
+				}
+				if (mp3buf.length > 0) {
+					buffer.push(mp3buf);
+				}
+			}
+			const enc = mp3enc.flush();
+			if (enc.length > 0) {
+				buffer.push(enc);
+			}
+			return new Blob(buffer, { type: "audio/mp3" });
+		},
+		uploadWavFileAndGetText(audioFile) {
+			let iiframe = this.$refs["iiframe"];
+			let _this = this;
+			iiframe.contentWindow.onRecognizedResult = function (e) {
+				console.log("onRecognizedResult", e);
+				let privText = e.privText;
+				console.log(privText)
+				_this.transcriptionData.content = privText;
+			};
+			iiframe.contentWindow.doContinuousPronunciationAssessment("", {
+				files: [audioFile],
+			});
+		},
+		// 录音转wav
+		dataURLtoAudio(blob, filename) {
+      return new File([blob], filename, { type: "audio/wav" });
+    },
+		getRoleList() {
+			this.roleList = [];
+			let params = {
+				userId: "602def61-005d-11ee-91d8-005056b86db5",
+			};
+			this.ajax
+				.post("https://gpt4.cocorobo.cn/get_ai_agent_assistant_list", params)
+				.then((res) => {
+					let _data = res.data.FunctionResponse.result;
+					if (_data) {
+						this.roleList = JSON.parse(_data);
+					}
+				})
+				.catch((e) => {
+					this.$message.error("获取角色列表失败");
+					this.roleList = [];
+				});
+		},
+		getPublicRoleList() {
+			this.publicRoleList = [];
+			let params = {
+				userId: "602def61-005d-11ee-91d8-005056b86db5",
+				organizeid: "45facc0a-1211-11ec-80ad-005056b86db5",
+			};
+			this.ajax
+				.post(
+					"https://gpt4.cocorobo.cn/get_ai_agent_assistant_share_list",
+					params
+				)
+				.then((res) => {
+					let _data = res.data.FunctionResponse.result;
+					if (_data) {
+						this.publicRoleList = JSON.parse(_data);
+					}
+				})
+				.catch((e) => {
+					this.publicRoleList = [];
+					console.log("获取公共角色失败", e);
+				});
+		},
+		// 选择了@的角色
+		choseRole(_data) {
+			let _lastAtIndex = this.textareaValue.lastIndexOf("@");
+			this.textareaValue = `${this.textareaValue.slice(0, _lastAtIndex)}@${
+				_data.assistantName
+			} `;
+			this.$refs.textareaRef.focus();
+			this.showRoleList = false;
+		},
+		// 输入框键盘按下监听
+		textareaKeydown(_e) {
+			if (this.showRoleList && this.choseRoleList.length > 0) {
+				switch (_e.keyCode) {
+					case 38: //小键盘上
+						_e.preventDefault();
+						if (this.roleListIndex == 0) return;
+						this.roleListIndex--;
+						// 修改滚动条高度
+						this.$refs.roleListRef.scrollTop = this.roleListIndex * 105;
+						break;
+					case 40: //小键盘下
+						_e.preventDefault();
+						if (this.roleListIndex == this.choseRoleList.length - 1) return;
+						this.roleListIndex++;
+						this.$refs.roleListRef.scrollTop = this.roleListIndex * 105;
+						break;
+					case 13: //回车
+						_e.preventDefault();
+						this.choseRole(this.choseRoleList[this.roleListIndex]);
+						break;
+				}
+			}
+		},
+		getData() {
+			this.getRoleList();
+			this.getPublicRoleList();
+		},
+		//保存消息
+		insertChat(answer, problem, type, alltext, assistant_id, headUrl, file_id) {
+			let params = {};
+		},
+	},
+	mounted() {
+		this.getData();
+		this.scrollBottom();
+	},
+};
+</script>
+
+<style scoped>
+.chatArea {
+	width: 100%;
+	height: 100%;
+	display: flex;
+	flex-direction: column;
+	/* align-items: center; */
+	/* justify-content: center; */
+	box-sizing: border-box;
+	padding: 20px;
+}
+.audio_class {
+	/* width: 100% !important; */
+	/* height: 100% !important; */
+	background: #ccc !important;
+	margin: 0 !important;
+}
+
+.audio_class >>> .slider .process {
+	background: #000;
+}
+.tapeSty {
+	cursor: pointer;
+	width: 40px;
+	height: 25px;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	/* padding: 10px 15px; */
+	border-radius: 20px;
+}
+.TapeCss {
+	background-color: #1467ee;
+}
+.titBar {
+	width: 100%;
+	height: 30px;
+	margin-bottom: 5px;
+	display: flex;
+	/* align-items: center; */
+	border-bottom: 1px #ccc solid;
+	z-index: 99;
+}
+.titBar > .titBarLeft {
+	width: 40%;
+	min-width: 400px;
+	flex-shrink: 0;
+	display: flex;
+	cursor: pointer;
+	justify-content: flex-start;
+}
+.titBar > .titBarRig {
+	display: flex;
+	flex: 1;
+	justify-content: flex-end;
+	align-items: center;
+	font-family: PingFang SC;
+	font-size: 14px;
+	font-weight: 400;
+	line-height: 22px;
+	text-align: left;
+}
+.titBar > .titBarRig > img {
+	margin-right: 5px;
+	width: 16px;
+	height: 16px;
+	cursor: pointer;
+}
+.titBarBorder {
+	font-weight: 600;
+	box-sizing: border-box;
+	border-bottom: 2px #1467ee solid;
+}
+.titBar > .titBarLeft > div {
+	height: 100%;
+	font-family: PingFang SC;
+	font-size: 16px;
+	min-width: 75px;
+	margin-right: 30px;
+	text-align: left;
+	display: flex;
+	align-items: center;
+}
+.titBar > .titBarLeft > div > img {
+	margin-right: 5px;
+}
+.m-operation {
+	width: 100%;
+	height: 30px;
+	display: flex;
+	font-size: 14px;
+	box-sizing: border-box;
+	padding-right: 30px;
+	margin-bottom: 10px;
+	align-items: baseline;
+}
+.m-operation :first-child {
+	font-family: PingFang SC;
+	font-size: 18px;
+	font-weight: 600;
+	line-height: 26px;
+	text-align: left;
+	margin-right: 10px;
+}
+.m-operation :nth-child(2) {
+	font-family: PingFang SC;
+	font-size: 12px;
+	font-weight: 400;
+	text-align: left;
+}
+.ca-top {
+	width: 100%;
+	flex: 1;
+}
+.ca-bottom {
+	width: 100%;
+	height: 120px;
+}
+
+.ca-b-operation {
+	width: 100%;
+	height: 100%;
+	display: flex;
+	flex-direction: column;
+	justify-content: flex-end;
+	position: relative;
+}
+.ca-b-o-header {
+	width: 100%;
+	height: 40px;
+	display: flex;
+	align-items: center;
+}
+
+.ca-b-o-h-left {
+	flex: 1;
+	height: 100%;
+	display: flex;
+	align-items: center;
+}
+
+.ca-b-o-h-l-select {
+	width: auto;
+	display: flex;
+	justify-content: center;
+	position: relative;
+	align-items: center;
+	font-size: 14px;
+	box-sizing: border-box;
+	padding: 5px 10px;
+	margin-right: 20px;
+	border-radius: 15px;
+	cursor: pointer;
+	background-color: white;
+}
+
+/* .ca-b-o-h-l-select:hover .languageBlock{
+	display: block;
+}
+.languageBlock {
+	display: none;
+} */
+
+.languageList {
+	position: absolute;
+	background-color: #fff;
+	top: calc(-100% - 100px);
+	left: 0%;
+	width: 120px;
+	/* padding: 0 10px; */
+	/* box-sizing: border-box; */
+	padding: 10px 0;
+	border-radius: 5px;
+	display: flex;
+	justify-content: space-between;
+	flex-direction: column;
+	align-items: center;
+	height: 100px;
+}
+
+.languageList >>> .el-radio {
+	width: 70%;
+	/* margin-bottom: 10px; */
+	padding: 5px 10px;
+	margin: 0;
+	border-radius: 5px;
+}
+.radioBg {
+	background: rgba(243, 247, 253, 1);
+}
+
+.ca-b-o-h-l-s-icon {
+	margin-right: 2px;
+	font-size: 16px;
+}
+
+.ca-b-o-h-s-l-icon2 {
+	margin-left: 5px;
+	font-size: 18px;
+}
+
+.ca-b-o-h-l-btn {
+	width: auto;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	font-size: 14px;
+	box-sizing: border-box;
+	padding: 5px 10px;
+	margin-right: 20px;
+	border-radius: 15px;
+	background-color: white;
+	cursor: pointer;
+}
+
+.ca-b-o-h-right {
+	width: auto;
+	height: 100%;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+}
+
+.ca-b-o-h-r-radio {
+	display: flex;
+	align-items: center;
+	font-size: 14px;
+	background-color: #e7e7e7;
+	border-radius: 20px;
+	overflow: hidden;
+}
+.ca-b-o-h-r-radio > span {
+	margin-right: 5px;
+	cursor: pointer;
+}
+
+.ca-b-o-main {
+	width: 100%;
+	/* flex: 1; */
+	height: 64px;
+
+	margin-top: 5px;
+	border-radius: 16px;
+	transition: 0.3s;
+	position: relative;
+}
+
+.ca-b-o-main:hover {
+	box-shadow: 0 5px 10px 10px #e1e8eb;
+}
+
+.ca-b-o-m-tape {
+	width: 100%;
+	height: 100%;
+	cursor: pointer;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	font-size: 20px;
+	color: #3681fc;
+	border-radius: 16px;
+	background-color: white;
+	box-shadow: 0 5px 10px 10px #e6eaeb;
+	transition: 0.3s;
+}
+.ca-b-o-m-tapeTwo {
+	width: 100%;
+	height: 100%;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	font-size: 20px;
+	color: #3681fc;
+	box-sizing: border-box;
+	padding: 0 20px;
+	border-radius: 16px;
+	background-color: white;
+	box-shadow: 0 5px 10px 10px #e6eaeb;
+	transition: 0.3s;
+}
+.ca-b-o-m-tapeTwo >>> .vueAudioBetter {
+	width: 90%;
+}
+.ca-b-o-m-tape > span {
+	margin-right: 10px;
+	font-size: 22px;
+}
+
+.ca-b-o-m-tape:hover {
+	color: #1467ee;
+}
+
+.ca-b-o-m-inputAre {
+	width: 100%;
+	min-height: 100%;
+	height: auto;
+	display: flex;
+	align-items: flex-end;
+	border-radius: 16px;
+	background-color: white;
+	box-shadow: 0 5px 10px 10px #e6eaeb;
+	transition: 0.3s;
+	position: absolute;
+	bottom: 0;
+}
+
+.ca-b-o-m-TapeArea {
+	width: 100%;
+	height: 100%;
+	display: flex;
+	justify-content: space-between;
+	border-radius: 16px;
+	background-color: #f0f2f5;
+	box-shadow: 0 5px 10px 10px #e6eaeb;
+	transition: 0.3s;
+	align-items: center;
+	padding-right: 20px;
+	box-sizing: border-box;
+}
+
+.ca-b-o-m-i-left {
+	display: flex;
+	align-items: center;
+}
+
+.ca-b-o-m-i-left > div > div {
+	color: #ee3e3e;
+	font-weight: bold;
+}
+
+.ca-b-o-m-i-left > div > span {
+	font-size: 14px;
+	margin-top: 5px;
+}
+
+.ca-b-o-m-i-left > img {
+	margin-left: 10px;
+	border-radius: 50%;
+	margin-right: 20px;
+}
+
+.ca-b-o-m-left {
+	flex: 1;
+	height: auto;
+	min-height: 64px;
+	display: flex;
+	/* justify-content: center; */
+	align-items: center;
+	box-sizing: border-box;
+	padding-left: 20px;
+}
+
+.ca-b-o-m-left > textarea {
+	resize: none;
+	min-height: 50px;
+	margin: 7px 0;
+	max-height: 500px;
+	width: 100%;
+	font-size: 18px;
+	border: none;
+	outline: none;
+	resize: none;
+	overflow: auto;
+}
+.ca-b-o-m-right {
+	width: 100px;
+	min-width: 80px;
+	height: 64px;
+	max-height: 64px;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	margin-right: 10px;
+}
+
+#myTextarea::-webkit-input-placeholder {
+	/* Chrome, Opera, Safari */
+	font-size: 14px; /* 修改placeholder字体大小 */
+	color: grey; /* 修改placeholder文字颜色 */
+}
+
+#myTextarea:-moz-placeholder {
+	/* Firefox 18- */
+	font-size: 14px; /* 修改placeholder字体大小 */
+	color: grey; /* 修改placeholder文字颜色 */
+	opacity: 1; /* 修复Firefox的透明度问题 */
+}
+
+#myTextarea::-moz-placeholder {
+	/* Firefox 19+ */
+	font-size: 14px; /* 修改placeholder字体大小 */
+	color: grey; /* 修改placeholder文字颜色 */
+	opacity: 1; /* 修复Firefox的透明度问题 */
+}
+
+#myTextarea:-ms-input-placeholder {
+	/* Internet Explorer 10-11 */
+	font-size: 14px; /* 修改placeholder字体大小 */
+	color: grey; /* 修改placeholder文字颜色 */
+}
+.ca-b-o-m-right > span {
+	width: 24px;
+	height: 24px;
+	background: url("../../../../assets/icon/classroomObservation/tapeIng.png")
+		no-repeat;
+	background-size: 100% 100%;
+	cursor: pointer;
+	margin-right: 10px;
+}
+
+.ca-b-o-m-right > div {
+	width: 52px;
+	height: 30px;
+
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	color: white;
+	font-size: 14px;
+	border-radius: 5px;
+	background-color: #1467ee;
+	margin-right: 10px;
+	cursor: pointer;
+}
+
+.ca-b-o-m-r-dsiableBtn {
+	/* 禁止手势 */
+	cursor: not-allowed !important;
+	background-color: #aeccfe !important;
+}
+.lyStart {
+	width: 38px;
+	height: 32px;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	background-color: #fff;
+	border-radius: 5px;
+	cursor: pointer;
+}
+.ca_b_o_m_roleList {
+	position: absolute;
+	left: 0;
+	width: 100%;
+	height: auto;
+	max-height: calc(100vh - 230px);
+	margin-bottom: 70px;
+	overflow: auto;
+	background-color: white;
+	border-radius: 10px;
+	box-sizing: border-box;
+	padding: 15px;
+}
+
+.ca_b_o_m_rl_item {
+	width: 100%;
+	height: auto;
+	margin-bottom: 15px;
+	background-color: #f3f7fd;
+	border-radius: 10px;
+	cursor: pointer;
+	transition: 0.3s;
+	box-sizing: border-box;
+	padding: 10px;
+}
+
+.ca_b_o_m_rl_itemActive {
+	background-color: #c3ddfa;
+}
+
+.ca_b_o_m_rl_i_left {
+	width: 50px;
+	height: 50px;
+	border-radius: 50%;
+	margin-right: 15px;
+}
+
+.ca_b-o_m_rl_i_top {
+	display: flex;
+}
+.ca_b-o_m_rl_i_top > div {
+	margin-left: 10px;
+}
+
+.ca_b-o_m_rl_i_top > div > span {
+	font-size: 14px;
+	margin-top: 5px;
+	color: #6b798e;
+}
+
+.ca_b-o_m_rl_i_bottom {
+	margin-top: 10px;
+	width: 90%;
+	overflow: hidden;
+	display: block;
+	white-space: nowrap;
+	text-overflow: ellipsis;
+}
+</style>

+ 121 - 0
src/components/pages/components/mdView.vue

@@ -0,0 +1,121 @@
+<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';
+import '../../../../common/aws-sdk-2.235.1.min.js'
+
+const getFile = (url) => {
+	return new Promise((resolve, reject) => {
+		var credentials = {
+			accessKeyId: "AKIATLPEDU37QV5CHLMH",
+			secretAccessKey: "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",
+		}; //秘钥形式的登录上传
+		window.AWS.config.update(credentials);
+		window.AWS.config.region = "cn-northwest-1"; //设置区域
+		let url2 = url;
+		let _url2 = "";
+		if (
+			url2.indexOf("https://view.officeapps.live.com/op/view.aspx?src=") != -1
+		) {
+			_url2 = url2.split(
+				"https://view.officeapps.live.com/op/view.aspx?src="
+			)[1];
+		} else {
+			_url2 = url2;
+		}
+		var s3 = new window.AWS.S3({ params: { Bucket: "ccrb" } });
+		let name = decodeURIComponent(
+			_url2.split("https://ccrb.s3.cn-northwest-1.amazonaws.com.cn/")[1]
+		);
+		var params = {
+			Bucket: "ccrb",
+			Key: name,
+		};
+		s3.getObject(params, function (err, data) {
+			if (err) {
+				console.log(err, err.stack);
+				resolve({ data: 1 });
+			} else {
+				const fileContent = data.Body.toString('utf-8');
+				resolve({ data: fileContent });
+			} // sxuccessful response
+		});
+		// axios({
+	});
+};
+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;
+			getFile(this.url).then(res=>{
+				this.markdownContent = res.data;
+			})
+		},
+	},
+	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;
+}
+</style>

+ 1105 - 0
src/components/pages/components/messageArea.vue

@@ -0,0 +1,1105 @@
+<template>
+	<div class="messageArea" v-loading="loading">
+		<div class="m-operation">
+			<div class="m-o-switch">
+				<span class="m-o-s-text" @click.stop="changeShowBrief(!showBrief)"
+					>显示模块简介</span
+				>
+				<el-switch
+					v-model="showBrief"
+					active-color="#3681FC"
+					inactive-color="#b2bfc3"
+				>
+				</el-switch>
+			</div>
+			<div class="m-o-btn" @click.stop="useAnalysisTemplate()">
+				使用分析模板
+			</div>
+			<div class="m-o-btn" @click.stop="saveAsTemplate()">另存为模板</div>
+		</div>
+		<div class="ma-main">
+			<baseMessage
+				:data="bmData.jsonData"
+				:imageList="imageList.jsonData"
+				@saveData="saveBaseData"
+				@saveImage="saveBaseImage"
+				@delImage="delBaseImageList"
+				v-loading="baseMessageLoading"
+			/>
+
+			<analysis
+				@updateMessage="updateMessage"
+				ref="analysis0"
+				@delItem="delAnalysisItem"
+				@editItem="editAnalysisItem"
+				title="通用课堂分析"
+				:dialogTagDataList="dialogTagDataList"
+				:analysisItemList="dataList.filter((i) => i.Type === 0)"
+				:type="0"
+				:tid="tid"
+				:fileId="fileId"
+				v-loading="currencyLoading"
+			/>
+			<analysis
+				@updateMessage="updateMessage"
+				ref="analysis1"
+				@delItem="delAnalysisItem"
+				@editItem="editAnalysisItem"
+				title="科学课堂分析"
+				:dialogTagDataList="dialogTagDataList"
+				:analysisItemList="dataList.filter((i) => i.Type === 1)"
+				:type="1"
+				:tid="tid"
+				:fileId="fileId"
+				v-loading="scienceLoading"
+			/>
+			<analysis
+				@updateMessage="updateMessage"
+				ref="analysis2"
+				@delItem="delAnalysisItem"
+				@editItem="editAnalysisItem"
+				title="扩展分析"
+				:dialogTagDataList="dialogTagDataList"
+				:analysisItemList="dataList.filter((i) => i.Type === 2)"
+				:type="2"
+				:tid="tid"
+				:fileId="fileId"
+				v-loading="extendLoading"
+			/>
+			<analysis
+				@updateMessage="updateMessage"
+				ref="analysis3"
+				@delItem="delAnalysisItem"
+				@editItem="editAnalysisItem"
+				title="增值性分析"
+				:dialogTagDataList="dialogTagDataList"
+				:analysisItemList="dataList.filter((i) => i.Type === 3)"
+				:type="3"
+				:tid="tid"
+				:fileId="fileId"
+				v-loading="valueAddedLoading"
+			/>
+			<!-- <currencyAnalysis
+				@updateMessage="updateMessage"
+				@delItem="delAnalysisItem"
+				@editItem="editAnalysisItem"
+				:analysisItemList="dataList.filter((i) => i.Type === 0)"
+				:tid="tid"
+				v-loading="currencyLoading"
+				ref="currencyAnalysisRef"
+			/>
+
+			<scienceAnalysis
+				@updateMessage="updateMessage"
+				@delItem="delAnalysisItem"
+				@editItem="editAnalysisItem"
+				:analysisItemList="dataList.filter((i) => i.Type === 1)"
+				:tid="tid"
+				v-loading="scienceLoading"
+				ref="scienceAnalysisRef"
+			/>
+
+			<extendAnalysis
+				@updateMessage="updateMessage"
+				@delItem="delAnalysisItem"
+				@editItem="editAnalysisItem"
+				:analysisItemList="dataList.filter((i) => i.Type === 2)"
+				:tid="tid"
+				v-loading="extendLoading"
+				ref="extendAnalysisRef"
+			/> -->
+			<!-- <div style="height: 10000px;"></div> -->
+		</div>
+		<el-dialog :center="true" :visible.sync="dialogVisible" width="1200px">
+			<!-- <div v-if="showDialog == true" class="a-dialog" v-el-drag-dialog> -->
+			<div class="a-d-top">
+				<div class="a-d-topTit"><div style="width: 136px">添加模块</div></div>
+				<div>
+					<el-input
+						placeholder="请输入内容"
+						prefix-icon="el-icon-search"
+						v-model="input2"
+						clearable
+					>
+					</el-input>
+				</div>
+				<div class="a-d-t-right">
+					<span @click.stop="dialogVisible = false">×</span>
+				</div>
+			</div>
+			<div style="display: flex; height: 100%">
+				<div class="a-d-t-left">
+					<div class="itemTit">我的模块</div>
+					<div
+						:style="
+							tagIndex == index
+								? 'background: rgba(226, 238, 255, 1);color: rgba(54, 129, 252, 1)'
+								: ''
+						"
+						class="a-d-t-l-item"
+						v-for="(item, index) in dialogTagList"
+						:key="item.id"
+						@click.stop="tagIndex = index"
+					>
+						{{ item.name }}
+					</div>
+				</div>
+				<div class="a-d-box">
+					<div
+						style="
+							font-family: PingFang SC;
+							font-size: 16px;
+							font-weight: 600;
+							line-height: 22px;
+							text-align: left;
+							margin: 20px 0;
+							margin-bottom: 10px;
+						"
+					>
+						推荐
+					</div>
+					<div style="display: flex; flex-wrap: wrap">
+						<div
+							class="a-d-b-item"
+							v-for="(item, index) in searchDataList"
+							:key="index"
+						>
+							<div class="a-d-b-i-top">
+								<img
+									style="height: 22px; width: 22px"
+									:src="
+										require('../../../../assets/icon/classroomObservation/digImg.png')
+									"
+								/>
+								<div class="a-d-b-i-t-title">{{ item.title }}</div>
+							</div>
+							<div class="a-d-b-i-bottom">{{ item.brief }}</div>
+							<div class="a-d-b-i-bottomPer" style="display: block">
+								3982人已使用
+							</div>
+							<div class="a-d-b-i-bottomBtn" style="display: none">
+								<div
+									style="
+										display: flex;
+										width: 100%;
+										justify-content: space-around;
+									"
+								>
+									<!-- <div class="a-d-b-i-t-btn">详情</div> -->
+									<div
+										class="a-d-b-i-t-btn1"
+										@click="addAnalysisItem(item.title)"
+									>
+										添加
+									</div>
+								</div>
+							</div>
+						</div>
+					</div>
+				</div>
+			</div>
+			<!-- </div> -->
+		</el-dialog>
+	</div>
+</template>
+
+<script>
+import baseMessage from "./baseMessage.vue"; //基本信息
+// import currencyAnalysis from "./currencyAnalysis.vue"; //通用课堂分析
+// import scienceAnalysis from "./scienceAnalysis.vue"; //科学课堂分析
+// import extendAnalysis from "./extendAnalysis.vue"; //扩展分析
+import analysis from "./analysis.vue";
+
+export default {
+	emits:["changeChatAreaAudioUrl"],
+	components: {
+		baseMessage,
+		// currencyAnalysis,
+		// scienceAnalysis,
+		// extendAnalysis,
+		analysis,
+	},
+	props: {
+		tid: {
+			type: String,
+			default: "",
+		},
+		fileId: {
+			type: String,
+			default: "",
+		},
+	},
+	data() {
+		return {
+			showBrief: true, //是否显示模块简介
+			dialogVisible: false,
+			tagIndex: 0,
+			loading: false,
+			baseMessageLoading: false,
+			currencyLoading: false,
+			scienceLoading: false,
+			extendLoading: false,
+			valueAddedLoading: false,
+			input2: "",
+			dialogTagList: [
+				{ id: 0, name: "通用课堂分析" },
+				{ id: 1, name: "科学课堂分析" },
+				{ id: 2, name: "扩展分析" },
+				{ id: 3, name: "增值性分析" },
+			],
+			dialogTagDataList: [
+				{
+					title: "OMO智慧课堂分析",
+					brief: "多维度分析课堂整体情况",
+					value: "4cc367c1-0076-11ef-aaca-12e77c4cb76b",
+					type: 0,
+				},
+				{
+					title: "教学阶段九事件分析",
+					brief: "使用加涅九事件分析教学环节",
+					value: "5e0466b3-0075-11ef-aaca-12e77c4cb76b",
+					type: 0,
+				},
+				{
+					title: "布鲁姆问题分类",
+					brief: "多维度分析课堂整体情况",
+					value: "eac63117-00a7-11ef-aaca-12e77c4cb76b",
+					type: 0,
+				},
+				{
+					title: "麦卡锡问题分类",
+					brief: "多维度分析课堂整体情况",
+					value: "18545cf7-0125-11ef-aaca-12e77c4cb76b",
+					type: 0,
+				},
+				{
+					title: "学生回答情况",
+					brief: "多维度分析课堂整体情况",
+					value: "d3f75199-eb4e-11ee-aaca-12e77c4cb76b",
+					type: 0,
+				},
+				// {
+				// 	title: "课堂时间分配",
+				// 	brief: "多维度分析课堂整体情况",
+				// 	type: 0
+				// },
+				// {
+				// 	title: "师生互动分析",
+				// 	brief: "多维度分析课堂整体情况",
+				// 	type: 0
+				// },
+				// {
+				// 	title: "教学模式分析",
+				// 	brief: "多维度分析课堂整体情况",
+				// 	type: 0
+				// },
+				{
+					title: "课堂活动类型",
+					brief: "多维度分析课堂整体情况",
+					value: "41d2d2d4-0125-11ef-aaca-12e77c4cb76b",
+					type: 1,
+				},
+				{
+					title: "学科核心素养发展",
+					brief: "学科核心素养发展",
+					value: "b13a98de-0125-11ef-aaca-12e77c4cb76b",
+					type: 1,
+				},
+				{
+					title: "科学教育目标分析",
+					brief: "科学教育目标分析",
+					value: "b13a98de-0125-11ef-aaca-12e77c4cb76b",
+					type: 1,
+				},
+				{
+					title: "PORTAAL课堂观察",
+					brief: "PORTAAL课堂观察",
+					value: "8ab07d41-e143-11ee-aaca-12e77c4cb76b",
+					type: 1,
+				},
+				{
+					title: "5E课程改编",
+					brief: "5E课程改编",
+					value: "f757826e-0125-11ef-aaca-12e77c4cb76b",
+					type: 2,
+				},
+				{
+					title: "5EX课程改编",
+					brief: "5EX课程改编",
+					value: "0b6b08b7-0126-11ef-aaca-12e77c4cb76b",
+					type: 2,
+				},
+				{
+					title: "UTOP课堂观察",
+					brief: "UTOP课堂观察",
+					value: "8e3a389b-014f-11ef-aaca-12e77c4cb76b",
+					type: 3,
+				},
+				{
+					title: "RST模型",
+					brief: "多维度分析课堂整体情况",
+					value: "e649112e-0150-11ef-aaca-12e77c4cb76b",
+					type: 3,
+				},
+				{
+					title: "RTOP模型",
+					brief: "多维度分析课堂整体情况",
+					value: "68265b18-0151-11ef-aaca-12e77c4cb76b",
+					type: 3,
+				},
+				{
+					title: "课程质量评价",
+					brief: "多维度分析课堂整体情况", 
+				 	value:"25e53379-0152-11ef-aaca-12e77c4cb76b",
+					type: 3,
+				},
+				{
+					title: "SCOP课堂观察",
+					brief: "多维度分析课堂整体情况",
+					value: "d0c76d35-0152-11ef-aaca-12e77c4cb76b",
+					type: 3,
+				},
+				{
+					title: "RTOP课堂观察",
+					brief: "多维度分析课堂整体情况",
+					value: "a7107221-f7f5-11ee-aaca-12e77c4cb76b",
+					type: 3,
+				},
+			],
+			bmData: {},
+			dataList: [],
+			imageList: {},
+		};
+	},
+	computed: {
+		searchDataList() {
+			let _data = this.dialogTagDataList.filter((i) => i.type == this.tagIndex);
+			if (this.input2) {
+				return _data.filter((i) => i.title.indexOf(this.input2) > -1);
+			} else {
+				return _data;
+			}
+		},
+	},
+	methods: {
+		delAnalysisItem(id) {
+			let _index = this.dataList.findIndex((i) => i.id == id);
+			if (_index > -1) {
+				let _data = this.dataList[_index];
+				let params = {
+					id: _data.id,
+					type: _data.Type,
+					tid: this.tid,
+				};
+				this.ajax
+					.post("https://gpt4.cocorobo.cn/delete_classroom_observation", params)
+					.then((res) => {
+						this.dataList.splice(_index, 1);
+						this.$message.success("删除成功");
+					})
+					.catch((e) => {
+						this.$message.error("删除失败");
+					});
+			} else {
+				this.$message.error("删除失败");
+			}
+		},
+		editAnalysisItem(id, _data) {
+			let _index = this.dataList.findIndex((i) => i.id == id);
+			if (_index != -1) {
+				this.dataList[_index] = _data;
+				this.saveData(this.dataList[_index])
+				this.$forceUpdate();
+			} else {
+				this.$message.info("执行错误");
+			}
+		},
+		updateMessage(val) {
+			this.dialogVisible = true;
+			this.tagIndex = val;
+		},
+		handleClose(done) {
+			this.$confirm("确认关闭?")
+				.then((_) => {
+					done();
+				})
+				.catch((_) => {});
+		},
+		// 切换显示模板简介
+		changeShowBrief(newValue) {
+			this.showBrief = newValue;
+		},
+		// 使用分析模板
+		useAnalysisTemplate() {
+			this.$message.info("使用分析模板");
+		},
+		//另存为模板
+		saveAsTemplate() {
+			this.$message.info("另存为模板");
+		},
+		addAnalysisItem(title) {
+			return new Promise((resolve, reject) => {
+				this.dialogVisible = false;
+				let assistant = this.dialogTagDataList.find((i) => i.title == title);
+				
+				let parm = {
+					assistant_id: assistant ? assistant.value : null,
+					message:
+						"请使用代码解析器获取文件,帮我根据要求完整的分析,输出请按照要求。",
+					session_name: new Date().getTime(),
+					userId: "1cf9dc4b-d95f-11ea-af4c-52540005ab01",
+					file_ids: this.fileId,
+				};
+
+				if (!parm.assistant_id) {
+					resolve();
+					return this.$message.error("未找到对应的AI助手");
+				}
+				let newIndexData = this.dataList.filter((i) => i.Type == assistant.type).sort((a,b)=>new Date(a.createtime)-new Date(b.createtime))
+				newIndexData = newIndexData.length?newIndexData[newIndexData.length-1]:null;
+				let newIndex = newIndexData?newIndexData.tIndex+1:assistant.type==0?2:0;
+				let params = {
+					index: newIndex,
+					json_data: JSON.stringify({
+						name: assistant.title,
+						result: "",
+						fileList: [],
+						dataFileList: [],
+						content: "",
+					}),
+					tid: this.tid,
+					type: assistant.type.toString(),
+				};
+
+				// 这里调用添加
+				this.ajax
+					.post("https://gpt4.cocorobo.cn/insert_classroom_observation", params)
+					.then((res) => {
+						let _data = res.data.FunctionResponse;
+						if (_data.message && _data.message == "创建成功") {
+							let _result = JSON.parse(_data.result)[0];
+							_result.jsonData = JSON.parse(_result.jsonData);
+							this.dataList.push(_result);
+							if (_result.Type == 0) {
+								this.$refs.analysis0.getReport(_result.id);
+							} else if (_result.Type == 1) {
+								this.$refs.analysis1.getReport(_result.id);
+							} else if (_result.Type == 2) {
+								this.$refs.analysis2.getReport(_result.id);
+							} else if (_result.Type == 3) {
+								this.$refs.analysis3.getReport(_result.id);
+							}
+							this.$message.success("添加成功");
+							resolve();
+						} else {
+							this.$message.error("创建失败");
+						}
+					});
+			});
+		},
+		getCurrencyAndBaseMessageData() {
+			if(!this.tid)return;
+			let pram = {
+				tid: this.tid,
+				// tid:'02',
+				type: "0",
+			};
+			this.currencyLoading = true;
+			this.baseMessageLoading = true;
+			this.bmData = {
+				id: "",
+				tId: this.tid,
+				tIndex: 0,
+				jsonData: {
+					activity_methods: "",
+					activity_structure: "",
+					classroom_resources: "",
+					courseName: "",
+					name: "",
+					studentNum: 0,
+					subject: "",
+					textbook: "",
+				},
+			};
+			this.imageList = [];
+			this.dataList = [];
+			this.ajax
+				.post("https://gpt4.cocorobo.cn/get_classroom_observation_new", pram)
+				.then((res) => {
+					let _data = res.data.FunctionResponse.result.length
+						? JSON.parse(res.data.FunctionResponse.result)
+						: [];
+					if (_data.length == 0) {
+						return this.insertBaseMessage().then(_=>{
+							this.getCurrencyAndBaseMessageData();
+						})
+					}
+					let _bmData = _data.find(i=>i.tIndex==0);
+					// 基础信息
+					_bmData.jsonData = JSON.parse(_bmData.jsonData);
+					// 图片
+					let _imageList = _data.find(i=>i.tIndex==1);
+					_imageList.jsonData = JSON.parse(_imageList.jsonData);
+					//通用分析
+					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;
+					if(this.imageList.jsonData.fileList.length>0){
+						this.$emit("changeChatAreaAudioUrl",this.imageList.jsonData.fileList[0]);
+					}else{
+						this.$emit("changeChatAreaAudioUrl",{name:"",url:""});
+					}
+					this.baseMessageLoading = false;
+					this.currencyLoading = false;
+				});
+		},
+		getScienceData() {
+			if(!this.tid)return;
+			let pram = {
+				tid: this.tid,
+				// tid:'02',
+				type: "1",
+			};
+			this.scienceLoading = true;
+			this.ajax
+				.post("https://gpt4.cocorobo.cn/get_classroom_observation_new", pram)
+				.then((res) => {
+					let _data = res.data.FunctionResponse.result.length
+						? JSON.parse(res.data.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;
+				});
+		},
+		getExtendData() {
+			if(!this.tid)return;
+			let pram = {
+				tid: this.tid,
+				// tid:'02',
+				type: "2",
+			};
+			this.extendLoading = true;
+			this.ajax
+				.post("https://gpt4.cocorobo.cn/get_classroom_observation_new", pram)
+				.then((res) => {
+					let _data = res.data.FunctionResponse.result.length
+						? JSON.parse(res.data.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;
+				});
+		},
+		getValueAddedData() {
+			if(!this.tid)return;
+			let pram = {
+				tid: this.tid,
+				// tid:'02',
+				type: "3",
+			};
+			this.valueAddedLoading = true;
+			this.ajax
+				.post("https://gpt4.cocorobo.cn/get_classroom_observation_new", pram)
+				.then((res) => {
+					let _data = res.data.FunctionResponse.result.length
+						? JSON.parse(res.data.FunctionResponse.result)
+						: [];
+					if (_data.length == 0) {
+						return (this.valueAddedLoading = false);
+					}
+					let valueAdded = [];
+					for (let i = 0; i < _data.length; i++) {
+						let _valueAdded = _data[i];
+						_valueAdded.jsonData = JSON.parse(_valueAdded.jsonData);
+						valueAdded.push(_valueAdded);
+					}
+					this.dataList.push(...valueAdded);
+					this.valueAddedLoading = false;
+				});
+		},
+		getData() {
+			this.dataList = [];
+			this.getCurrencyAndBaseMessageData();
+			this.getScienceData();
+			this.getExtendData();
+			this.getValueAddedData();
+			// this.getFileId();
+		},
+		saveData(data) {
+			return new Promise((resolve,reject)=>{
+				this.ajax.post(
+						"https://gpt4.cocorobo.cn/update_classroom_observation",
+						{
+							id: data.id,
+							json_data: JSON.stringify(data.jsonData),
+						}
+					).then(res=>{
+						resolve();
+					})
+			})
+			// return;
+			// let saveDataList = [];
+
+			// // 保存基础信息
+			// saveDataList.push({
+			// 	id: this.bmData.id,
+			// 	json_data: JSON.stringify(this.bmData.jsonData),
+			// });
+
+			// // 所有分析
+			// this.dataList.forEach((i1) => {
+			// 	i1.forEach((i2) => {
+			// 		saveDataList.push({
+			// 			id: i2.id,
+			// 			json_data: JSON.stringify(i2.jsonData),
+			// 		});
+			// 	});
+			// });
+			// let promises = [];
+			// saveDataList.forEach((i) => {
+			// 	promises.push(
+			// 		new Promise((resolve) => {
+			// 			let pram = {
+			// 				id: i.id,
+			// 				json_data: i.json_data,
+			// 			};
+			// 			this.ajax
+			// 				.post(
+			// 					"https://gpt4.cocorobo.cn/update_classroom_observation",
+			// 					pram
+			// 				)
+			// 				.then((res) => {
+			// 					console.log(res);
+			// 					resolve();
+			// 				});
+			// 		})
+			// 	);
+			// });
+
+			// Promise.all(promises).then((res) => {
+			// 	this.$message.success("保存成功");
+			// 	fn ? fn() : "";
+			// });
+		},
+		saveBaseData(){
+			this.bmData.json_data = JSON.stringify(this.bmData.jsonData);
+			this.saveData(this.bmData)
+		},
+		saveBaseImage(newImage){
+			if(this.imageList.jsonData.fileList1.length==0){
+				this.imageList.jsonData.fileList1.push(newImage);
+			}else if(this.imageList.jsonData.fileList2.length==0){
+				this.imageList.jsonData.fileList2.push(newImage)
+			}else if(this.imageList.jsonData.fileList3.length==0){
+				this.imageList.jsonData.fileList3.push(newImage)
+			}else{
+				this.$message.error("最多只能上传3张图片");
+				return;
+			}
+			this.imageList.json_data = JSON.stringify(this.imageList.jsonData);
+			this.saveData(this.imageList);
+		},
+		delBaseImageList(key){
+			this.imageList.jsonData[key] = [];
+			this.imageList.json_data = JSON.stringify(this.imageList.jsonData);
+			this.saveData(this.imageList).then(res=>{
+				this.$message.success("删除图片成功")
+			});
+		},
+		// 切换录音文件
+		changeAudioUrl(data){
+			this.imageList.jsonData.fileList = [];
+			let audio = {
+				name:data.key,
+				status:"success",
+				uid:"1",
+				url:data.Location
+			}
+			this.imageList.jsonData.fileList.push(audio)
+			this.saveData(this.imageList).then(res=>{
+				this.$message.success("更换录文件成功");
+				this.$emit("changeChatAreaAudioUrl",audio);
+			})
+		},
+		getReport() {
+			this.$refs.analysis0.getReport();
+			this.$refs.analysis1.getReport();
+			this.$refs.analysis2.getReport();
+			this.$refs.analysis3.getReport();
+		},
+		insertBaseMessage() {
+			return new Promise((resolve, reject) => {
+				// 添加基础信息
+				let params = {
+					index: "0",
+					json_data: JSON.stringify({
+						courseName: "",
+						studentNum: "0",
+						subject: "",
+						textbook: "",
+						classroom_resources: "",
+						activity_structure: "",
+						activity_methods: "",
+						name: "",
+					}),
+					tid: this.tid,
+					type: "0",
+				};
+				// 添加课堂图片
+				let params2 = {
+					index: "1",
+					tid: this.tid,
+					type: "0",
+					json_data: JSON.stringify({
+						fileList: [],
+						name: "课堂记录",
+						fileList1: [],
+						fileList2: [],
+						fileList3: [],
+					}),
+				};
+				this.ajax
+					.post("https://gpt4.cocorobo.cn/insert_classroom_observation", params)
+					.then((res) => {
+						let _data = res.data.FunctionResponse;
+						if (_data.message && _data.message == "创建成功") {
+							this.ajax
+								.post(
+									"https://gpt4.cocorobo.cn/insert_classroom_observation",
+									params2
+								)
+								.then((res2) => {
+									let _data2 = res2.data.FunctionResponse;
+									if (_data2.message && _data2.message == "创建成功") {
+									} else {
+										this.$message.error("创建课堂图片失败");
+									}
+									resolve();
+								});
+						} else {
+							this.$message.error("创建基础信息失败");
+						}
+					})
+			});
+		},
+	},
+	mounted() {
+		this.getData();
+	},
+};
+</script>
+
+<style scoped>
+.messageArea {
+	width: 100%;
+	height: 100%;
+	box-sizing: border-box;
+	padding: 20px 10px;
+}
+.m-operation {
+	width: 100%;
+	height: 30px;
+	display: flex;
+	justify-content: flex-end;
+	align-items: center;
+	font-size: 14px;
+	box-sizing: border-box;
+	padding-right: 30px;
+	margin-bottom: 10px;
+}
+.m-o-btn {
+	width: auto;
+	height: 33px;
+	padding: 0 12px;
+	background-color: #ffffff;
+	box-sizing: border-box;
+	border: solid 1px #bdbdbd;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	margin-right: 10px;
+	border-radius: 17px;
+	cursor: pointer;
+}
+.m-o-switch {
+	display: flex;
+	align-items: center;
+	margin-right: 10px;
+}
+.m-o-s-text {
+	margin-right: 4px;
+	cursor: pointer;
+}
+.ma-main {
+	width: 100%;
+	height: calc(100vh - 123px);
+	overflow: auto;
+}
+
+.a-d-top {
+	/* background: #adadad; */
+	display: flex;
+	flex-direction: row;
+	flex-wrap: nowrap;
+	align-items: center;
+	justify-content: space-between;
+	height: 54px;
+	border-radius: 8px 8px 0 0;
+	user-select: none;
+	border-bottom: 1px #ccc solid;
+}
+.a-d-top >>> .el-input__inner {
+	width: 320px;
+	height: 32px;
+}
+.a-d-top >>> .el-input__icon {
+	line-height: 32px;
+}
+.a-d-topTit {
+	width: 171px;
+	height: 32px;
+	display: flex;
+	align-items: center;
+	font-family: PingFang SC;
+	box-sizing: border-box;
+	padding: 5px;
+	line-height: 22px;
+	justify-content: center;
+	/* text-align: left; */
+}
+.a-d-t-left {
+	width: 200px;
+	height: 100%;
+	display: flex;
+	align-items: center;
+	flex-direction: column;
+	justify-content: flex-start;
+	box-sizing: border-box;
+	padding-left: 5px;
+}
+
+.a-d-t-l-item {
+	/* width: auto;
+	height: 90%;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	padding: 0 10px;
+	border-radius: 10px;
+	background-color: #d4d9da;
+	margin-right: 3px;
+	cursor: pointer; */
+	cursor: pointer;
+	width: 136px;
+	height: 32px;
+	display: flex;
+	align-items: center;
+	border-radius: 5px;
+	font-family: PingFang SC;
+	box-sizing: border-box;
+	padding: 5px;
+	font-size: 14px;
+	font-weight: 600;
+	line-height: 22px;
+	text-align: left;
+	margin-bottom: 20px;
+}
+
+.a-d-t-l-item:hover {
+	background-color: white;
+}
+
+.a-d-t-right {
+	width: 40px;
+	height: 40px;
+	margin-right: 10px;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	color: black !important;
+}
+
+.a-d-t-right > span {
+	width: 25px;
+	height: 25px;
+	border-radius: 25px;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	/* align-items: center; */
+	font-size: 22px;
+	color: #fff;
+	/* background-color: #adadad; */
+	cursor: pointer;
+	/* background-color: #e6e6e6; */
+	color: #adadad;
+}
+
+.a-d-box {
+	width: 100%;
+	height: 100%;
+	background-color: #f0f2f5;
+	overflow: scroll;
+	overflow-x: hidden;
+	box-sizing: border-box;
+	padding: 15px;
+	padding-bottom: 50px;
+}
+
+.a-d-b-item {
+	width: 22%;
+	height: 200px;
+	display: flex;
+	flex-direction: column;
+	background-color: #fff;
+	border-radius: 10px;
+	padding: 15px;
+	float: left;
+	box-sizing: border-box;
+	margin-bottom: 10px;
+	margin-right: 20px;
+	/* position: relative; */
+}
+.a-d-b-item:hover .a-d-b-i-bottomBtn {
+	display: block !important;
+}
+.a-d-b-item:hover .a-d-b-i-bottomPer {
+	display: none !important;
+}
+.a-d-b-i-top {
+	width: 100%;
+	/* height: 50%; */
+	height: 20px;
+	margin-bottom: 15px;
+	display: flex;
+	align-items: center;
+	/* justify-content: space-between; */
+}
+.a-d-b-i-top > img {
+	width: 35px;
+	height: 35px;
+}
+/* .a-d-b-i-top>div{ */
+/* width: auto;
+	height: 35px;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	margin-left: 10px; */
+/* } */
+
+.a-d-b-i-t-title {
+	width: 100%;
+	height: 35px;
+	display: block;
+	align-items: center;
+	box-sizing: border-box;
+	padding: 0 10px;
+	text-overflow: ellipsis;
+	overflow: hidden;
+	word-break: break-all;
+	white-space: nowrap;
+	line-height: 35px;
+	/* display: -webkit-box;
+	-webkit-box-orient: vertical;
+	-webkit-line-clamp: 1;
+	overflow: hidden; */
+}
+
+.a-d-b-i-bottom {
+	width: 100%;
+	flex: 1;
+	overflow: hidden;
+	/* max-height: 186px; */
+	font-size: 14px;
+	-webkit-line-clamp: 5;
+	line-height: 20px;
+	display: -webkit-box;
+	-webkit-box-orient: vertical;
+	overflow: hidden;
+	text-overflow: ellipsis;
+}
+
+.a-d-b-i-t-btn {
+	font-size: 14px;
+	box-sizing: border-box;
+	padding: 8px 25px;
+	border: 1px solid rgba(54, 129, 252, 1);
+	border-radius: 5px;
+	color: rgba(54, 129, 252, 1);
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	cursor: pointer;
+}
+.a-d-b-i-t-btn1 {
+	font-size: 14px;
+	width: 98%;
+	box-sizing: border-box;
+	padding: 8px 25px;
+	border: 1px solid rgba(54, 129, 252, 1);
+	border-radius: 5px;
+	background-color: rgba(54, 129, 252, 1);
+	display: flex;
+	color: #fff;
+	justify-content: center;
+	align-items: center;
+	cursor: pointer;
+}
+.messageArea >>> .el-dialog {
+	min-width: 1200px;
+
+	height: 700px;
+	box-shadow: 0px 0 8px 0px #555555;
+	border-radius: 8px;
+	background-color: #fff;
+	/* top: 0px; */
+	/* margin: 0 auto; */
+	overflow: hidden;
+}
+.messageArea >>> .el-dialog__body {
+	height: 100%;
+	min-width: 1200px;
+	flex-shrink: 0;
+	box-sizing: border-box;
+	padding-bottom: 50px;
+	padding-top: 10px;
+}
+.messageArea >>> .el-dialog__header {
+	display: none;
+}
+.itemTit {
+	width: 136px;
+	height: 32px;
+	padding: 5px 8px 5px 8px;
+	gap: 8px;
+	opacity: 0px;
+	margin: 20px 0;
+	margin-bottom: 10px;
+	border-bottom: 1px #ccc solid;
+}
+</style>

+ 94 - 0
src/components/pages/components/pieChart.vue

@@ -0,0 +1,94 @@
+<template>
+	<div class="chart" id="charts_canvas" ref="chartRef">
+		
+	</div>
+</template>
+
+<script>
+const getFile = (url) => {
+	return new Promise((resolve, reject) => {
+		var credentials = {
+			accessKeyId: "AKIATLPEDU37QV5CHLMH",
+			secretAccessKey: "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",
+		}; //秘钥形式的登录上传
+		window.AWS.config.update(credentials);
+		window.AWS.config.region = "cn-northwest-1"; //设置区域
+		let url2 = url;
+		let _url2 = "";
+		if (
+			url2.indexOf("https://view.officeapps.live.com/op/view.aspx?src=") != -1
+		) {
+			_url2 = url2.split(
+				"https://view.officeapps.live.com/op/view.aspx?src="
+			)[1];
+		} else {
+			_url2 = url2;
+		}
+		var s3 = new window.AWS.S3({ params: { Bucket: "ccrb" } });
+		let name = decodeURIComponent(
+			_url2.split("https://ccrb.s3.cn-northwest-1.amazonaws.com.cn/")[1]
+		);
+		var params = {
+			Bucket: "ccrb",
+			Key: name,
+		};
+		s3.getObject(params, function (err, data) {
+			if (err) {
+				console.log(err, err.stack);
+				resolve({ data: 1 });
+			} else {
+				const fileContent = data.Body.toString("utf-8");
+				resolve({ data: fileContent });
+			} // sxuccessful response
+		});
+		// axios({
+	});
+};
+export default {
+	props: {
+		url: {
+			type: String,
+			default: "",
+		},
+	},
+	data() {
+		return {
+			chartObj:null,
+			chartData:null,
+		};
+	},
+	methods: {
+		getChartData() {
+			if (!this.url) return;
+			
+			getFile(this.url).then(res=>{
+				let _chartData = res.data;
+
+				// 去除option
+				_chartData = _chartData.replace('option = ','');
+				// 去除注释
+				_chartData = _chartData.replace(/\/\/.*$/gm, '');
+
+				_chartData = _chartData.replace(";", '');
+				this.chartData = JSON.parse(_chartData);
+				const _chartObj = this.$echarts.init(
+          this.$refs.chartRef
+        );
+				this.chartObj = _chartObj;
+				this.chartObj.setOption(this.chartData);
+			})
+		},
+	},
+	mounted() {
+		this.getChartData();
+	},
+};
+</script>
+
+<style scoped>
+.chart {
+	max-width: 100%;
+	width: 100%;
+	height: 550px;
+}
+</style>

+ 325 - 0
src/components/pages/components/sharePdf.vue

@@ -0,0 +1,325 @@
+<template>
+  <el-dialog
+    :visible.sync="localDialogVisibleShare"
+    :append-to-body="true"
+    width="560px"
+    :before-close="handleClose"
+    class="dialog_diy"
+  >
+    <div class="share_box">
+     
+   
+      <div class="url_box">
+        <div class="title">
+          分享链接
+        </div>
+        <div class="url">
+          <el-tooltip :content="origin" placement="top" effect="dark">
+            <!-- content to trigger tooltip here -->
+            <span>{{ origin }}</span>
+          </el-tooltip>
+          <span @click="copy" :data-clipboard-text="copyText" class="tag-read"
+            >复制链接</span
+          >
+        </div>
+      </div>
+      <div class="qrcode_box">
+        <div class="title">
+          分享二维码
+        </div>
+        <div class="qrcode">
+          <span class="qrcodeUrl" ref="qrCodeUrl"></span>
+          <span @click="downQr">下载二维码</span>
+        </div>
+      </div>
+      <div class="btn_box">
+        <button
+          class="c_pub_button_confirm tag-read2"
+          @click="copy2"
+          :data-clipboard-text="copyText2"
+        >
+          复制信息
+        </button>
+      </div>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+import Clipboard from "clipboard";
+import QRCode from "qrcodejs2";
+export default {
+  props: {
+    dialogVisibleShare: {
+      type: Boolean,
+      default: false
+    }
+  },
+  data() {
+    return {
+      origin: "",
+      copyText: "",
+      copyText2: "",
+      noCount: 0,
+      pname: [],
+      localDialogVisibleShare: this.dialogVisibleShare
+    };
+  },
+  methods: {
+    handleClose(done) {
+      this.close();
+      done();
+    },
+    close() {
+    //   this.$emit("update:dialogVisibleShare", false);
+      this.$emit('shareBtn');
+
+    },
+    setQr() {
+      setTimeout(() => {
+        let url = 'https://beta.cloud.cocorobo.cn/#/classroomObservation';
+        this.origin = url;
+        this.$refs.qrCodeUrl.innerHTML = "";
+        var qrcode = new QRCode(this.$refs.qrCodeUrl, {
+          text: url, // 需要转换为二维码的内容
+          width: 100,
+          height: 100,
+          colorDark: "#000000",
+          colorLight: "#ffffff",
+          correctLevel: QRCode.CorrectLevel.H
+        });
+      }, 500);
+    },
+    downQr() {
+      // 创建一个虚拟链接,并将 canvas 转换为图片文件
+      const link = document.createElement("a");
+      link.href = this.$refs.qrCodeUrl.querySelector("img").src;
+      link.download = "qrcode.png";
+
+      // 模拟点击链接进行下载
+      link.click();
+    },
+    copy() {
+      this.copyText = 'https://beta.cloud.cocorobo.cn/#/classroomObservation';
+      var clipboard = new Clipboard(".tag-read");
+      clipboard.on("success", e => {
+        this.$message.success("复制成功");
+        console.log("复制成功");
+        clipboard.destroy(); // 释放内存
+      });
+      clipboard.on("error", e => {
+        console.log("不支持复制,该浏览器不支持自动复制");
+        clipboard.destroy(); // 释放内存
+      });
+    },
+    copy2() {
+      let url = 'https://beta.cloud.cocorobo.cn/#/classroomObservation';
+      this.copyText2 = `链接提醒:${url}`;
+      var clipboard = new Clipboard(".tag-read2");
+      clipboard.on("success", e => {
+        this.$message.success("复制成功");
+        console.log("复制成功");
+        clipboard.destroy(); // 释放内存
+      });
+      clipboard.on("error", e => {
+        console.log("不支持复制,该浏览器不支持自动复制");
+        clipboard.destroy(); // 释放内存
+      });
+    }
+    // getData() {
+    //   // if(!this.testJson.juriP){
+    //   //     return;
+    //   // }
+    //   let params = {
+    //     id: this.testJson.courseId
+    //   };
+    //   this.ajax
+    //     .get(this.$store.state.api + "getTestWorkShare", params)
+    //     .then(res => {
+    //       let parray = res.data[0];
+    //       let is = res.data[1];
+    //       this.noCount = parray.length - is.length;
+    //       let isA = [];
+    //       for (var i = 0; i < is.length; i++) {
+    //         isA.push(is[i].userid);
+    //       }
+    //       let pname = [];
+    //       for (var i = 0; i < parray.length; i++) {
+    //         if (isA.indexOf(parray[i].userid) == -1) {
+    //           pname.push(parray[i].username);
+    //         }
+    //       }
+    //       this.pname = pname;
+    //     })
+    //     .catch(err => {
+    //       console.error(err);
+    //     });
+    // }
+  },
+  watch: {
+    dialogVisibleShare(newValue, oldValue) {
+      console.log("newValue", newValue);
+      if (newValue) {
+        this.localDialogVisibleShare=newValue
+        this.setQr();
+        // this.getData();
+        // console.log(top.origin);
+        // this.origin = top.origin
+      }
+    }
+  },
+  mounted() {
+    // console.log("this.info", this.info);
+
+    // console.log(top.origin);
+    // this.origin = top.origin
+  }
+};
+</script>
+
+<style scoped>
+.dialog_diy >>> .el-dialog {
+  /* height: 100%; */
+  /* margin: 0 auto !important; */
+}
+
+.dialog_diy >>> .el-dialog__header {
+  background: #fff !important;
+  padding: 15px 20px;
+}
+
+.dialog_diy >>> .el-dialog__body {
+  height: calc(100% - 124px);
+  box-sizing: border-box;
+  padding: 0px;
+}
+
+.dialog_diy >>> .el-dialog__title {
+  color: #000;
+}
+
+.dialog_diy >>> .el-dialog__headerbtn {
+  top: 19px;
+}
+
+.dialog_diy >>> .el-dialog__headerbtn .el-dialog__close {
+  color: #000;
+}
+
+.dialog_diy >>> .el-dialog__headerbtn .el-dialog__close:hover {
+  color: #000;
+}
+
+.dialog_diy >>> .el-dialog__body,
+.dialog_diy >>> .el-dialog__footer {
+  background: #fff;
+}
+
+.share_box {
+  width: 100%;
+  height: 100%;
+  padding: 0 20px 15px;
+  box-sizing: border-box;
+}
+
+.info_box {
+  margin: 0 0 20px;
+}
+
+.info_box > .info {
+  line-height: 18px;
+}
+
+.info_box > .info > span:nth-child(1) {
+  color: #00000099;
+}
+
+.info_box > .info > span:nth-child(2) {
+  color: #000000;
+}
+
+.info_box > .info + .info {
+  margin-top: 10px;
+}
+
+.url_box {
+  margin-bottom: 20px;
+}
+
+.qrcode_box {
+  margin-bottom: 20px;
+}
+
+.url_box > .url {
+  width: 100%;
+  display: flex;
+  align-items: center;
+  border: 1px solid #e7e7e7;
+  border-radius: 4px;
+  height: 35px;
+  padding: 0 7px;
+  box-sizing: border-box;
+}
+
+.url_box > .url > span {
+  font-size: 16px;
+  color: #000000;
+}
+
+.url_box > .url > span:nth-child(1) {
+  width: 100%;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+.url_box > .url > span:nth-child(2) {
+  margin-left: auto;
+  border-left: 1px solid #e7e7e7;
+  padding-left: 7px;
+  cursor: pointer;
+  min-width: fit-content;
+}
+
+.url_box > .title,
+.qrcode_box > .title {
+  font-size: 16px;
+  font-weight: 700;
+  margin-bottom: 10px;
+}
+
+.qrcode_box > .qrcode {
+  padding: 7px;
+  border: 1px solid #e7e7e7;
+  width: fit-content;
+  border-radius: 4px;
+  display: flex;
+  align-items: flex-end;
+}
+
+.qrcode_box > .qrcode > span:nth-child(1) {
+  width: 100px;
+  display: block;
+  height: 100px;
+}
+
+.qrcode_box > .qrcode > span:nth-child(2) {
+  background: #f0f2f5;
+  padding: 7px 15px;
+  font-size: 16px;
+  border-radius: 4px;
+  cursor: pointer;
+  color: #000;
+  margin-left: 7px;
+}
+
+.btn_box {
+  display: flex;
+  justify-content: flex-end;
+}
+
+.more {
+  cursor: pointer;
+  color: #0061ff;
+}
+</style>

+ 230 - 0
src/components/pages/components/startPage.vue

@@ -0,0 +1,230 @@
+<template>
+	<div class="startPage">
+		<div class="sp-introduce" v-if="showIntroduce">
+			<span class="sp-i-personIcon"></span>
+			<div class="sp-i-p-p">
+				<div class="sp-i-title">您好,我是 CocoClass 课堂观察</div>
+				<div class="sp-i-brief">
+					基于课堂录音和实录文稿进行分析,为您提供不同启发。
+				</div>
+			</div>
+
+			<!-- <span class="sp-i-icon1"></span>
+			<span class="sp-i-icon2"></span> -->
+			<div class="sp-i-btn" @click="showIntroduce = false">我已了解并关闭</div>
+		</div>
+		<div class="sp-main">
+			<div class="sp-m-item" @click.stop="$emit('startTape')">
+				<!-- <span class="sp-m-i-icon1"></span> -->
+				<div class="sp-m-item1">开始录音</div>
+				<div class="sp-m-item2">
+					<p>实时语音转录文字</p>
+					<p>智能课堂观察</p>
+				</div>
+				<div class="sp-m-item3">
+					<div style="position: relative;margin-top: 20px;">
+						<img
+							class="img1"
+							src="../../../../assets/icon/classroomObservation/yuan.png"
+							alt=""
+						/>
+						<img
+							class="img2"
+							src="../../../../assets/icon/classroomObservation/yuanYin.png"
+							alt=""
+						/>
+					</div>
+				</div>
+			</div>
+			<div class="sp-m-item" @click.stop="$emit('uploadTape')">
+				<!-- <span class="sp-m-i-icon2"></span> -->
+				<div class="sp-m-item1">上传录音</div>
+				<div class="sp-m-item2">
+					<p>录音复盘</p>
+					<p>一键分析课堂情况</p>
+				</div>
+				<div  class="sp-m-item3">
+					<img
+							class="img1"
+							src="../../../../assets/icon/classroomObservation/startPagewen.png"
+							alt=""
+						/>
+				</div>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script>
+export default {
+	emits: ["startTape", "uploadTape"],
+	data() {
+		return {
+			showIntroduce: true,
+		};
+	},
+	methods: {},
+};
+</script>
+
+<style scoped>
+.startPage {
+	width: 100%;
+	height: calc(100vh - 290px);
+	position: relative;
+	display: flex;
+	flex-direction: column;
+	overflow: auto;
+}
+
+.sp-introduce {
+	width: 100%;
+	min-height: 116px;
+	height: 116px;
+	background-color: #e2eeff;
+	display: flex;
+	flex-direction: column;
+	box-sizing: border-box;
+	padding-left: 220px;
+	padding-top: 20px;
+	position: relative;
+	margin-top: 7%;
+}
+
+.sp-i-title {
+	font-size: 24px;
+	font-weight: 600;
+	color: #3681fc;
+}
+
+.sp-i-p-p {
+	position: absolute;
+	bottom: 30%;
+}
+
+.sp-i-title > span {
+	margin: 0 10px;
+	white-space: nowrap;
+}
+
+.sp-i-brief {
+	font-size: 14px;
+	margin-top: 10px;
+}
+
+.sp-i-personIcon {
+	width: 200px;
+	height: 200px;
+	position: absolute;
+	left: 0px;
+	bottom: 0;
+	background: url("../../../../assets/icon/classroomObservation/aiImage.png")
+		no-repeat;
+	background-size: 100% 100%;
+	background-position: center 20px;
+}
+
+.sp-i-btn {
+	width: auto;
+	height: auto;
+	padding: 5px 10px;
+	position: absolute;
+	right: 10px;
+	bottom: 10px;
+	background-color: #3a76fc;
+	font-size: 12px;
+	border-radius: 20px;
+	cursor: pointer;
+	color: white;
+}
+
+.sp-main {
+	width: 80%;
+	margin: 10px auto;
+	flex: 1;
+	display: flex;
+	/* align-items: flex-end; */
+	align-items: center;
+	/* align-content: center; */
+	justify-content: space-around;
+	box-sizing: border-box;
+	/* padding-bottom: 15%; */
+}
+
+.sp-m-item {
+	width: 220px;
+	height: 330px;
+	gap: 0px;
+	display: flex;
+	flex-direction: column;
+	border-radius: 8px 0px 0px 0px;
+	/* opacity: 0px; */
+	background: rgba(224, 234, 251, 1);
+	box-sizing: border-box;
+	padding: 20px;
+	border-radius: 10px;
+	cursor: pointer;
+}
+.sp-m-item1 {
+	font-family: PingFang SC;
+	font-size: 28px;
+	font-weight: 600;
+	line-height: 36px;
+	text-align: left;
+}
+.sp-m-item2 {
+	margin: 20px 0;
+	margin: 0;
+}
+.sp-m-item2 {
+	font-size: 14px;
+	font-weight: normal;
+	margin: 20px 0; 
+}
+.sp-m-item2 > p{
+	margin: 0;
+	margin-bottom: 5px;
+}
+.sp-m-item3 {
+	width: 100%;
+	flex: 1;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+}
+.img1 {
+	width: 128px;
+	height: 128px;
+}
+.img2 {
+	width: 90px;
+	height: 90px;
+	position: absolute;
+	top: 50%;
+	left: 50%;
+	margin: 0;
+	background-color: #5e9afc;
+	/* overflow: hidden; */
+	border-radius: 50%;
+	transform: translate(-50%, -50%);
+}
+
+.sp-m-item > span {
+	width: 40%;
+	height: 40%;
+	background-size: 100% 100%;
+	margin-bottom: 15px;
+}
+
+.sp-m-i-icon1 {
+	background: url("../../../../assets/icon/classroomObservation/tape.png")
+		no-repeat;
+}
+
+.sp-m-i-icon2 {
+	background: url("../../../../assets/icon/classroomObservation/file.png")
+		no-repeat;
+	background-size: 85% 85% !important;
+	background-position: center 10px;
+}
+</style>

+ 534 - 0
src/components/pages/components/tape.vue

@@ -0,0 +1,534 @@
+<template>
+	<div class="tape">
+		<!-- <div class="t-header">
+			<div class="t-h-title">课堂录音</div>
+			<div class="t-h-time">2024-04-13 17:30:00</div>
+		</div> -->
+		<!-- <div class="t-tapeAudio">
+			<mini-audio :audio-source="audioUrl" class="audio_class"></mini-audio>
+		</div> -->
+		<!-- <div class="t-tapeFile"> -->
+			<!-- <div class="t-t-header">
+				<div class="t-t-h-left">
+					<div class="t-t-h-l-Item" :style="cardStatus==0&&foldStatus?'background:white':''" @click.stop="changeCardStatus(0)">
+						<span class="t-t-h-l-i-icon"></span>
+						<div class="t-t-h-l-i-text">录音原文</div>
+					</div>
+					<div class="t-t-h-l-Item" :style="cardStatus==1&&foldStatus?'background:white':''" @click.stop="changeCardStatus(1)">
+						<span class="t-t-h-l-i-icon"></span>
+						<div class="t-t-h-l-i-text">转录文稿.xlsx</div>
+					</div>
+				</div>
+				<div class="t-t-h-right" @click.stop="changeFoldStatus(!foldStatus)">
+					<span class="t-t-h-r-icon" :style="`transform: scaleY(${foldStatus==0?1:-1});`"></span>
+					<div class="t-t-h-r-text">{{ foldStatus==0 ? '展开' : '收起'}}</div>
+				</div>
+			</div> -->
+			<!-- <div class="t-t-main" v-show="foldStatus">
+				<div class="t-t-m-Item" v-show="cardStatus == 0">
+					<p v-for="(item, index) in pList" :key="index" v-text="item"></p>
+				</div>
+
+				<div class="t-t-m-Item" v-show="cardStatus == 1">
+					<iframe
+						ref="viframe"
+						style="width: 100%; height: 99%; border: none"
+						:src="
+							'https://view.officeapps.live.com/op/view.aspx?src=' +
+							encodeURIComponent(fileUrl)
+						"
+					></iframe>
+				</div>
+			</div> -->
+		<!-- </div> -->
+		<div class="t-chartArea" ref="chartAreaRef">
+			<div class="t_ca_box" v-for="(item, index) in chatData" :key="item.uid+index">
+			
+				<div class="t_ca_b_roleUser" v-if="item.content">
+					<div class="t_ca_b_u_content">
+						<div class="t_ca_b_u_c_main">
+							<div class="t_ca_b_u_c_m_left">
+								<!-- <img
+									:src="
+										require('../../../../assets/icon/classroomObservation/addWork.png')
+									"
+									@click="addWork()"
+								/> -->
+								<!-- <img
+									:src="
+										require('../../../../assets/icon/classroomObservation/copy.png')
+									"
+									@click="copyContent(item.content)"
+								/> -->
+							</div>
+							<div class="t_ca_b_u_c_m_right" v-html="item.content"></div>
+						</div>
+						<div class="t_ca_b_u_c_time">{{ item.createtime }}</div>
+					</div>
+					<div class="t_ca_b_u_avatar">
+						<el-avatar
+							size="medium"
+							:src="
+								require('../../../../assets/icon/classroomObservation/userAvatar.png')
+							"
+						></el-avatar>
+					</div>
+				</div>
+				<div class="t_ca_b_roleAi">
+					<div class="t_ca_b_ai_avatar">
+						<el-avatar
+							size="medium"
+							:src="item.filename?item.filename:require('../../../../assets/icon/classroomObservation/ai.png')"
+
+						></el-avatar>
+					</div>
+					<div class="t_ca_b_ai_content">
+						<div class="t_ca_b_ai_c_name">{{ item.fileid }}</div>
+						<div class="t_ca_b_ai_c_main">
+							<div class="t_ca_b_ai_c_m_left" v-loading="item.loading" v-html="item.aiContent"></div>
+							<div class="t_ca_b_ai_c_m_right">
+								<img
+									:src="
+										require('../../../../assets/icon/classroomObservation/addWork.png')
+									"
+									@click="addWork()"
+								/>
+								<img
+									:src="
+										require('../../../../assets/icon/classroomObservation/copy.png')
+									"
+									@click="copyContent(item.aiContent)"
+								/>
+							</div>
+						</div>
+						<div class="t_ca_b_ai_c_time">{{ item.createtime }}</div>
+					</div>
+				</div>
+			</div>
+			<!-- <div class="t-ca-item" v-for="(item,index) in chatData" :key="index">
+				<div class="t-ca-i-left">
+					<div class="t-ca-i-l-avatar">
+						<el-avatar size="medium" :src="require('../../../../assets/icon/classroomObservation/ai.png')"></el-avatar>
+					</div>
+				</div>
+				<div class="t-ca-i-right">
+					<div class="t-ca-i-r-name">{{ item.name }}</div>
+					<div class="t-ca-i-r-time">{{ item.create_at }}</div>
+					<div class="t-ca-i-r-content">{{ item.content }}</div>
+				</div>
+			</div> -->
+		</div>
+	</div>
+</template>
+
+<script>
+import pdf from "./pdf.vue";
+export default {
+	components: {
+		pdf,
+	},
+	props: {
+		chatData: {
+			type: Array,
+			default: () => {
+				return [];
+			},
+		},
+		aiNameList:{
+			type:Array,
+			default:()=>{
+				return [];
+			}
+		}
+	},
+	data() {
+		return {
+			// foldStatus: false, //0--收起状态   1--展开状态
+
+			fileUrl:
+				"https://ccrb.s3.cn-northwest-1.amazonaws.com.cn/%E8%BD%AC%E5%BD%95%E6%96%87%E7%A8%BF1713172600896.xlsx",
+			audioUrl:
+				"https://ccrb.s3.cn-northwest-1.amazonaws.com.cn/%E8%A7%A3%E5%86%B3%E5%9E%83%E5%9C%BE%E9%97%AE%E9%A2%981713172672090.m4a",
+		};
+	},
+	computed:{
+		contentValue(){
+			// return (text)=>{
+			// 	let newText = text;
+			// 	this.aiNameList.forEach(i=>{
+			// 		const regex = new RegExp(`@(${i.name})`,"g");
+			// 		newText = newText.replace(regex,`<span class='atName'>@$1</span>`);
+			// 	})
+			// 	return newText;
+			// }
+		},
+	},
+	methods: {
+		// 切换折叠状态
+		// changeFoldStatus(newValue) {
+		// 	this.foldStatus = newValue;
+		// },
+		//切换卡片
+		// changeCardStatus(newValue) {
+		// 	this.foldStatus = 1;
+		// 	this.cardStatus = newValue;
+		// },
+		addWork() {
+			this.$message.info("添加到工作区");
+		},
+		copyContent(value) {
+			const input = document.createElement("input");
+			// 设置 display为none会导致无法复制
+			// input.style.display = "none";
+			// 所以只能用其他方法隐藏
+			input.style.opacity = 0;
+			// 为了不影响布局
+			input.style.position = "fixed";
+			input.style.left = "-100%";
+			input.style.top = "-100%";
+			input.value = value;
+			document.body.appendChild(input);
+			input.select();
+			const success = document.execCommand("copy");
+			document.body.removeChild(input);
+			if (!success) {
+				return this.$message.error("复制失败");
+			}else{
+				return this.$message.success("复制成功")
+			}
+		},
+	},
+};
+</script>
+
+<style scoped>
+.tape {
+	width: 100%;
+	height: 100%;
+	overflow: auto;
+}
+.t-header {
+	width: 100%;
+	height: 30px;
+	display: flex;
+	align-items: center;
+}
+
+.t-h-title {
+	font-size: 20px;
+	margin-right: 10px;
+}
+.t-h-time {
+	font-size: 14px;
+	color: #a8a9ab;
+}
+
+.t-tapeAudio {
+	width: 100%;
+	height: 50px;
+	margin-top: 10px;
+	box-sizing: border-box;
+	padding-right: 30px;
+}
+
+.audio_class {
+	/* width: 100% !important; */
+	/* height: 100% !important; */
+	background: #3680fb !important;
+	margin: 0 !important;
+}
+
+.t-tapeAudio >>> .vueAudioBetter span:before {
+	color: #fff;
+}
+
+/* .t-tapeAudio>>>.slider{
+	width: 80% !important;
+} */
+
+.audio_class >>> .slider .process {
+	background: #000;
+}
+
+.t-tapeAudio >>> .vueAudioBetter .iconfont:active {
+	position: unset;
+}
+
+.t-tapeFile {
+	width: 100%;
+	max-height: calc(100vh - 294px);
+	overflow: auto;
+	/* background-color: red; */
+}
+
+.t-t-header {
+	width: 100%;
+	height: 35px;
+	display: flex;
+	justify-content: space-between;
+	align-items: center;
+	background-color: #e5e5e5;
+	border-radius: 5px 5px 0 0;
+	box-sizing: border-box;
+	padding-right: 10px;
+}
+
+.t-t-h-left {
+	width: calc(100% - 80px);
+	height: 100%;
+	overflow: auto;
+	box-sizing: border-box;
+	display: flex;
+	align-items: center;
+	box-sizing: border-box;
+	padding-left: 5px;
+}
+
+.t-t-h-l-Item {
+	width: auto;
+	height: 90%;
+	box-sizing: border-box;
+	padding: 0 20px;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	cursor: pointer;
+	font-size: 14px;
+	margin-right: 10px;
+	border-radius: 5px;
+}
+
+.t-t-h-l-i-icon {
+	width: 20px;
+	height: 20px;
+	background: url("../../../../assets/icon/classroomObservation/file.png")
+		no-repeat;
+	background-size: 100% 100%;
+	margin-right: 5px;
+	background-position: center;
+}
+
+.t-t-h-l-Item:hover {
+	background-color: white;
+}
+
+.t-t-h-right {
+	width: 80px;
+	height: 100%;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	cursor: pointer;
+}
+
+.t-t-h-r-icon {
+	width: 25px;
+	height: 25px;
+	background: url("../../../../assets/icon/classroomObservation/foldIcon.png")
+		no-repeat;
+	background-size: 100% 100%;
+	margin-right: 10px;
+	background-position: center;
+	/* 水平镜像 */
+	transform: scaleY(-1);
+}
+
+.t-t-h-r-text {
+	font-size: 16px;
+}
+
+.t-t-main {
+	width: 100%;
+	height: calc(100vh - 354px);
+	/* height: calc(100% - 35px); */
+	overflow: auto;
+	background-color: white;
+}
+
+.t-t-m-Item {
+	width: 100%;
+	height: 100%;
+}
+
+.t-t-m-Item > p {
+	margin: 10px;
+}
+
+.t-chartArea {
+	width: 100%;
+	height: calc(100vh - 280px);
+	overflow: auto;
+}
+
+.t-ca-item {
+	width: 100%;
+	height: auto;
+	margin-top: 20px;
+	display: flex;
+	justify-content: flex-start;
+}
+
+.t-ca-i-left {
+	width: 50px;
+	height: 50px;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+}
+
+.t-ca-i-right {
+	flex: 1;
+	height: auto;
+	padding: 10px;
+	box-sizing: border-box;
+}
+
+.t-ca-i-r-name {
+	width: 100%;
+	font-size: 16px;
+	font-weight: 600;
+}
+
+.t-ca-i-r-time {
+	width: 100%;
+	font-size: 14px;
+	color: #a8a9ab;
+	margin-top: 10px;
+}
+
+.t-ca-i-r-content {
+	margin-top: 10px;
+	width: 100%;
+	height: auto;
+	white-space: pre-line;
+	word-break: break-all;
+}
+
+.t_ca_b_roleAi {
+	width: 100%;
+	height: auto;
+	box-sizing: border-box;
+	padding: 10px;
+	display: flex;
+}
+
+.t_ca_b_ai_c_name {
+	font-weight: 600;
+}
+.t_ca_b_ai_c_main {
+	width: auto;
+	display: flex;
+}
+
+.t_ca_b_ai_c_m_left {
+	min-width: 50px;
+	height: auto;
+	padding: 12px;
+	display: block;
+	border-radius: 2px 8px 8px 8px;
+	box-shadow: 0 4px 10px 0 #1d388354;
+	margin-top: 10px;
+	/* white-space: pre-line; */
+	word-break: break-all;
+	background-color: white;
+	overflow: hidden;
+}
+
+.t_ca_b_ai_c_m_right {
+	min-width: 30px;
+	min-height: 50px;
+	display: flex;
+	flex-direction: column;
+	margin-top: 10px;
+	margin-left: 5px;
+}
+
+.t_ca_b_ai_c_m_right > img {
+	width: 20px;
+	height: 20px;
+	margin-bottom: 15px;
+	cursor: pointer;
+}
+
+.t_ca_b_u_c_m_left {
+	min-width: 30px;
+	min-height: 50px;
+	display: flex;
+	flex-direction: column;
+	margin-top: 10px;
+	margin-right: 5px;
+}
+
+.t_ca_b_u_c_m_left > img {
+	width: 20px;
+	height: 20px;
+	margin-bottom: 15px;
+	cursor: pointer;
+}
+
+.t_ca_b_ai_c_time {
+	font-size: 14px;
+	color: #a8a9ab;
+	margin-top: 10px;
+}
+
+.t_ca_b_ai_avatar {
+	width: 30px;
+	height: 30px;
+	margin-right: 10px;
+}
+
+.t_ca_b_roleUser {
+	width: 100%;
+	height: auto;
+	box-sizing: border-box;
+	padding: 10px;
+	display: flex;
+	justify-content: flex-end;
+}
+
+.t_ca_b_u_avatar {
+	width: 30px;
+	height: 30px;
+	margin-left: 10px;
+}
+
+.t_ca_b_u_c_main {
+	display: flex;
+	width: auto;
+	justify-content: flex-end;
+}
+.t_ca_b_u_c_m_right {
+	height: auto;
+	padding: 12px;
+	display: block;
+	border-radius: 8px 2px 8px 8px;
+	box-shadow: 0 4px 10px 0 #1d388346;
+	margin-top: 10px;
+	white-space: pre-line;
+	word-break: break-all;
+	background-color: #3681fc;
+	color: white;
+}
+
+.t_ca_b_u_c_time {
+	white-space: nowrap;
+	font-size: 14px;
+	color: #a8a9ab;
+	margin-top: 10px;
+	text-align: right;
+}
+.t_ca_b_u_c_m_right>>>.aite-name{
+	display: inline;
+	background-color: #063a708c;
+	border-radius: 5px;
+	box-sizing: border-box;
+	padding: 2px 5px;
+	color: white;
+	margin-right: 5px;
+}
+
+.t_ca_b_ai_c_m_left>>>ol{
+	margin-left: 15px;
+}
+</style>

+ 120 - 0
src/components/pages/components/transcription.vue

@@ -0,0 +1,120 @@
+<template>
+	<div class="transcription">
+		<!-- <div class="t-header">
+			<div class="t-h-title">实时转录</div>
+			<div class="t-h-time">{{ data.time }}</div>
+		</div> -->
+		<div class="t-content">
+			{{ data.content }}
+			<!-- <div class="contentCon" v-for="(i, index) in chatList" :key="index">
+				<div class="conTim">{{ i.timer }}</div>
+				<div class="conTxt">
+					{{ i.con }}
+				</div>
+			</div> -->
+		</div>
+	</div>
+</template>
+
+<script>
+export default {
+	props: {
+		data: {
+			type: Object,
+			default: () => {
+				return {};
+			},
+		},
+	},
+	data() {
+		return {
+			chatList: [
+				{
+					timer: "[00:00]",
+					con: "请用日常生活为主题,跟我进行英语对话,这里是语音同步转文字,这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字请用日常生活为主题,跟我进行英语对话,这里是语音同步转文字,这里是语音同步转文",
+				},
+				{
+					timer: "[05:00]",
+					con: "请用日常生活为主题,跟我进行英语对话,这里是语音同步转文字,这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字请用日常生活为主题,跟我进行英语对话,这里是语音同步转文字,这里是语音同步转文",
+				},
+				{
+					timer: "[10:00]",
+					con: "请用日常生活为主题,跟我进行英语对话,这里是语音同步转文字,这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字请用日常生活为主题,跟我进行英语对话,这里是语音同步转文字,这里是语音同步转文",
+				},
+				{
+					timer: "[15:00]",
+					con: "请用日常生活为主题,跟我进行英语对话,这里是语音同步转文字,这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字请用日常生活为主题,跟我进行英语对话,这里是语音同步转文字,这里是语音同步转文",
+				},
+				{
+					timer: "[20:00]",
+					con: "请用日常生活为主题,跟我进行英语对话,这里是语音同步转文字,这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字请用日常生活为主题,跟我进行英语对话,这里是语音同步转文字,这里是语音同步转文",
+				},
+				{
+					timer: "[25:00]",
+					con: "请用日常生活为主题,跟我进行英语对话,这里是语音同步转文字,这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字请用日常生活为主题,跟我进行英语对话,这里是语音同步转文字,这里是语音同步转文",
+				},
+				{
+					timer: "[30:00]",
+					con: "请用日常生活为主题,跟我进行英语对话,这里是语音同步转文字,这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字这里是语音同步转文字请用日常生活为主题,跟我进行英语对话,这里是语音同步转文字,这里是语音同步转文",
+				},
+			],
+		};
+	},
+};
+</script>
+
+<style scoped>
+.transcription {
+	width: 100%;
+	height: 100%;
+	max-height: calc(100% - 280px);
+	/* display: flex; */
+	/* flex-direction: column; */
+	/* overflow: auto; */
+	/* padding-top:30px; */
+}
+.contentCon {
+	display: flex;
+	justify-content: flex-start;
+}
+.conTim {
+	flex-shrink: 0;
+	margin-right: 10px;
+	line-height: 32px;
+
+	color: rgba(54, 129, 252, 1);
+}
+.conTxt {
+	font-family: PingFang SC;
+	font-size: 16px;
+	font-weight: 400;
+	line-height: 32px;
+	text-align: left;
+}
+.t-content {
+	width: 100%;
+	flex: 1;
+	/* height: 50%; */
+	height: calc(100vh - 290px);
+	padding: 20px 30px;
+	box-sizing: border-box;
+	overflow: auto;
+	word-break: break-all;
+}
+
+.t-header {
+	width: 100%;
+	height: 40px;
+	display: flex;
+	align-items: center;
+}
+.t-h-title {
+	font-size: 20px;
+	font-weight: bold;
+	margin-right: 10px;
+}
+.t-h-time {
+	font-size: 14px;
+	color: #aeafb1;
+}
+</style>