|
@@ -0,0 +1,738 @@
|
|
|
+<template>
|
|
|
+ <div>
|
|
|
+ <div class="left">
|
|
|
+ <el-button type="primary" @click="start">开始</el-button>
|
|
|
+ </div>
|
|
|
+ <div class="right">
|
|
|
+
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import markdownIt from "markdown-it";
|
|
|
+
|
|
|
+import QRCode from "qrcodejs2";
|
|
|
+import * as echarts from "echarts";
|
|
|
+import "echarts-wordcloud";
|
|
|
+// word
|
|
|
+import htmlDocx from "html-docx-js/dist/html-docx";
|
|
|
+import JSZip from "jszip";
|
|
|
+export default {
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ tidList: [
|
|
|
+ "1f01ab3b-2249-453d-9b97-980fed675f94",
|
|
|
+ "3f854673-e3a3-4165-994d-d9bac0ea41cd",
|
|
|
+ "02a78b78-322a-4f43-9671-6ac533efd110",
|
|
|
+ "c2903392-1066-4512-8e8d-3256db2d2518",
|
|
|
+ "a4300c9c-4fc5-4f72-b337-f285fa9d033f",
|
|
|
+ "bb8d66ab-9876-4d17-b0e4-a095ecea58d1",
|
|
|
+ "2655a7b9-4add-472f-a62b-3c72bccc7a07",
|
|
|
+ "659af006-7415-4c73-aba6-2946d8b9ac6c",
|
|
|
+ "90926fc7-1c39-46e6-aa3e-34eeada89fa6",
|
|
|
+ "734bbdd4-a6eb-4e0e-acdb-bd5127cc8260",
|
|
|
+ "af8c74e5-cde8-4a33-ab3e-db077c37cec4",
|
|
|
+ "f665bb6f-54e5-4041-9b9b-489ea5814c7a",
|
|
|
+ "95453e35-9182-4e0c-a045-6757a84b7933",
|
|
|
+ "08958289-8d62-4468-b050-2e84a68edb26",
|
|
|
+ "2420d2d4-0627-43cc-9a8e-758ed074169c",
|
|
|
+ "4334e60d-f705-42bd-a89c-2bfc69b7615b",
|
|
|
+ "62e76f44-1f25-4a13-a699-93c4e58dc5ce",
|
|
|
+ "83891871-9eed-4aae-a318-9a1a1b52c047",
|
|
|
+ "7e32c688-051d-40b6-b2bd-a076fcf83ff5",
|
|
|
+ ],
|
|
|
+ message:[],
|
|
|
+ doTid: "",
|
|
|
+ dataList: [],
|
|
|
+ bmData: [],
|
|
|
+ dialogTagList: [
|
|
|
+ { value: 0, name: "通用课堂分析", loading: false },
|
|
|
+ { value: 1, name: "学科课堂分析", loading: false },
|
|
|
+ { value: 2, name: "扩展分析", loading: false }
|
|
|
+ ],
|
|
|
+ imageList: [],
|
|
|
+ tag: {
|
|
|
+ 0: "一",
|
|
|
+ 1: "二",
|
|
|
+ 2: "三",
|
|
|
+ 3: "四",
|
|
|
+ 4: "五",
|
|
|
+ 5: "六",
|
|
|
+ 6: "七",
|
|
|
+ 7: "八",
|
|
|
+ 8: "九",
|
|
|
+ 9: "十",
|
|
|
+ 10: "十一",
|
|
|
+ 11: "十二",
|
|
|
+ 12: "十三",
|
|
|
+ 13: "十四",
|
|
|
+ 14: "十五",
|
|
|
+ 15: "十六",
|
|
|
+ 16: "十七",
|
|
|
+ 17: "十八",
|
|
|
+ 18: "十九",
|
|
|
+ 19: "二十"
|
|
|
+ }
|
|
|
+ };
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ start() {
|
|
|
+ this.getData();
|
|
|
+ },
|
|
|
+ init() {
|
|
|
+ this.doTid = "";
|
|
|
+ this.dataList = [];
|
|
|
+ this.bmData = {};
|
|
|
+ this.dialogTagList = [
|
|
|
+ { value: 0, name: "通用课堂分析", loading: false },
|
|
|
+ { value: 1, name: "学科课堂分析", loading: false },
|
|
|
+ { value: 2, name: "扩展分析", loading: false }
|
|
|
+ ];
|
|
|
+ this.imageList = [];
|
|
|
+ },
|
|
|
+ getAnalysisData(type) {
|
|
|
+ if (!this.doTid) return;
|
|
|
+ return new Promise(resolve => {
|
|
|
+ let params = {
|
|
|
+ tid: this.doTid,
|
|
|
+ type: type
|
|
|
+ };
|
|
|
+
|
|
|
+ if (type == 0) {
|
|
|
+ this.bmData = {
|
|
|
+ id: "",
|
|
|
+ tId: this.doTid,
|
|
|
+ tIndex: 0,
|
|
|
+ jsonData: {
|
|
|
+ activity_methods: "",
|
|
|
+ activity_structure: "",
|
|
|
+ classroom_resources: "",
|
|
|
+ courseName: "",
|
|
|
+ name: "",
|
|
|
+ studentNum: 0,
|
|
|
+ subject: "",
|
|
|
+ textbook: ""
|
|
|
+ }
|
|
|
+ };
|
|
|
+ this.imageList = [];
|
|
|
+ this.dataList = [];
|
|
|
+ this.dialogTagList = [
|
|
|
+ { value: 0, name: "通用课堂分析", loading: true },
|
|
|
+ { value: 1, name: "学科课堂分析", loading: true },
|
|
|
+ { value: 2, name: "扩展分析", loading: true }
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ this.ajax
|
|
|
+ .post(
|
|
|
+ "https://gpt4.cocorobo.cn/get_classroom_observation_new",
|
|
|
+ params
|
|
|
+ )
|
|
|
+ .then(res => {
|
|
|
+ let _data = res.data.FunctionResponse.result.length
|
|
|
+ ? JSON.parse(res.data.FunctionResponse.result)
|
|
|
+ : [];
|
|
|
+
|
|
|
+ if (type == 0) {
|
|
|
+ //第一个分析
|
|
|
+ let _bmData = _data.find(i => i.tIndex == 0);
|
|
|
+ let _dialogTagList = [];
|
|
|
+ // 基础信息
|
|
|
+ _bmData.jsonData = JSON.parse(_bmData.jsonData);
|
|
|
+ _dialogTagList = _bmData.jsonData.dialogTagList || [
|
|
|
+ { value: 0, name: "通用课堂分析", loading: false },
|
|
|
+ { value: 1, name: "学科课堂分析", loading: false },
|
|
|
+ { value: 2, name: "扩展分析", loading: false }
|
|
|
+ ];
|
|
|
+ // 图片
|
|
|
+ let _imageList = _data.find(i => i.tIndex == 1);
|
|
|
+ _imageList.jsonData = JSON.parse(_imageList.jsonData);
|
|
|
+
|
|
|
+ if (!_imageList.jsonData.videoList) {
|
|
|
+ _imageList.jsonData.videoList = [];
|
|
|
+ }
|
|
|
+ if (!_imageList.jsonData.NephogramList) {
|
|
|
+ _imageList.jsonData.NephogramList = [];
|
|
|
+ }
|
|
|
+ //通用分析
|
|
|
+ let currency = [];
|
|
|
+ for (let i = 2; i < _data.length; i++) {
|
|
|
+ let _currency = _data[i];
|
|
|
+ _currency.jsonData = JSON.parse(_currency.jsonData);
|
|
|
+ currency.push(_currency);
|
|
|
+ }
|
|
|
+
|
|
|
+ //判断是否有保留装了文稿数据
|
|
|
+ this.dataList.push(...currency);
|
|
|
+ this.bmData = _bmData;
|
|
|
+ this.dialogTagList = _dialogTagList;
|
|
|
+ this.imageList = _imageList;
|
|
|
+ resolve();
|
|
|
+ } else {
|
|
|
+ let _analysisData = [];
|
|
|
+ for (let i = 0; i < _data.length; i++) {
|
|
|
+ let _analysis = _data[i];
|
|
|
+ _analysis.jsonData = JSON.parse(_analysis.jsonData);
|
|
|
+ _analysisData.push(_analysis);
|
|
|
+ }
|
|
|
+ this.dataList.push(..._analysisData);
|
|
|
+ resolve();
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .catch(e => {
|
|
|
+ console.log(type, "获取分析失败", e);
|
|
|
+ resolve();
|
|
|
+ });
|
|
|
+ });
|
|
|
+ },
|
|
|
+ async getData() {
|
|
|
+ let fileData = [];
|
|
|
+
|
|
|
+ for (let i = 0; i < this.tidList.length; i++) {
|
|
|
+ this.doTid = this.tidList[i];
|
|
|
+ let dataList2 = [];
|
|
|
+ let bmData2 = {};
|
|
|
+ let dialogTagList2 = [
|
|
|
+ { value: 0, name: "通用课堂分析", loading: false },
|
|
|
+ { value: 1, name: "学科课堂分析", loading: false },
|
|
|
+ { value: 2, name: "扩展分析", loading: false }
|
|
|
+ ];
|
|
|
+ let imageList2 = [];
|
|
|
+ await this.getAnalysisData(0);
|
|
|
+ let promise = [];
|
|
|
+ this.dialogTagList.forEach(i2 => {
|
|
|
+ if (i2.value == 0) return;
|
|
|
+ promise.push(this.getAnalysisData(i2.value));
|
|
|
+ });
|
|
|
+
|
|
|
+ await Promise.all(promise);
|
|
|
+
|
|
|
+ this.dataList.sort((a, b) => a.tIndex - b.tIndex);
|
|
|
+ this.dataList.sort((a, b) => a.Type - b.Type);
|
|
|
+
|
|
|
+ dataList2 = JSON.parse(JSON.stringify(this.dataList));
|
|
|
+ bmData2 = JSON.parse(JSON.stringify(this.bmData));
|
|
|
+ dialogTagList2 = JSON.parse(JSON.stringify(this.dialogTagList));
|
|
|
+ imageList2 = JSON.parse(JSON.stringify(this.imageList));
|
|
|
+
|
|
|
+ // return
|
|
|
+ let file = await this.getDocFnPromise({
|
|
|
+ bmData: bmData2.jsonData,
|
|
|
+ dataList: dataList2,
|
|
|
+ tagList: dialogTagList2,
|
|
|
+ tid: this.doTid
|
|
|
+ });
|
|
|
+ fileData.push(file);
|
|
|
+ // console.log("👉file",file,this.doTid)
|
|
|
+ console.log("=========================")
|
|
|
+ console.log(`进度:${(i+1)}/${this.tidList.length}`)
|
|
|
+ console.log("课程id:",this.doTid)
|
|
|
+ console.log("文件名:",file.name)
|
|
|
+ console.log("文件:",file)
|
|
|
+ console.log("=========================")
|
|
|
+ this.init();
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log("👉文件列表", fileData);
|
|
|
+
|
|
|
+ if (fileData.length == 1) {
|
|
|
+ saveAs(fileData[0], `${fileData[0].name}`);
|
|
|
+ this.$message.success("导出报告成功");
|
|
|
+ } else if (fileData.length > 1) {
|
|
|
+ const zip = new JSZip();
|
|
|
+ fileData.forEach((i, index) => {
|
|
|
+ let fileName = i.name;
|
|
|
+ if (fileData.map(i => i.name).filter((fi,findex)=>findex!==index).includes(fileName))
|
|
|
+ fileName =
|
|
|
+ fileName.replace(/\.[^/.]+$/, "") +
|
|
|
+ `(${index}).` +
|
|
|
+ fileName.replace(/^.*\./, "");
|
|
|
+ zip.file(fileName, i, { binary: true });
|
|
|
+ });
|
|
|
+ zip.generateAsync({ type: "blob" }).then(content => {
|
|
|
+ // 生成二进制流
|
|
|
+ saveAs(content, `课堂观察报告.zip`); // 利用file-saver保存文件 自定义文件名
|
|
|
+ });
|
|
|
+
|
|
|
+ this.$message.success("导出报告成功");
|
|
|
+ }
|
|
|
+ },
|
|
|
+ getQrCodeImageSrc(url) {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ let qrcode = new QRCode(document.createElement("div"), {
|
|
|
+ text: url, // 需要转换为二维码的内容
|
|
|
+ width: 150,
|
|
|
+ height: 150,
|
|
|
+ colorDark: "#000000",
|
|
|
+ colorLight: "#ffffff",
|
|
|
+ correctLevel: QRCode.CorrectLevel.H
|
|
|
+ });
|
|
|
+ let img = qrcode._el.getElementsByTagName("img")[0];
|
|
|
+ img.onload = () => {
|
|
|
+ resolve(img.src);
|
|
|
+ };
|
|
|
+ });
|
|
|
+ },
|
|
|
+ getEChartsImageSrc(option) {
|
|
|
+ return new Promise(resolve => {
|
|
|
+ try {
|
|
|
+ let hiddenDiv = document.createElement("div");
|
|
|
+ hiddenDiv.style.width = "400px";
|
|
|
+ hiddenDiv.style.height = "400px";
|
|
|
+ hiddenDiv.style.position = "absolute";
|
|
|
+ hiddenDiv.style.left = "-9999px"; // 隐藏div
|
|
|
+ document.body.appendChild(hiddenDiv);
|
|
|
+
|
|
|
+ // 初始化图表
|
|
|
+ let myChart = echarts.init(hiddenDiv);
|
|
|
+
|
|
|
+
|
|
|
+ let time;
|
|
|
+ myChart
|
|
|
+ .on("rendered", async () => {
|
|
|
+ // console.log("生成echarts成功")
|
|
|
+ // 获取图表的图片
|
|
|
+
|
|
|
+ clearTimeout(time);
|
|
|
+ time = setTimeout(() => {
|
|
|
+ let base64Image = myChart.getDataURL({
|
|
|
+ type: "png", // 图片格式
|
|
|
+ pixelRatio: 0.9, // 图像清晰度
|
|
|
+ backgroundColor: "#fff" // 背景颜色
|
|
|
+ });
|
|
|
+
|
|
|
+ resolve(base64Image);
|
|
|
+ // 清除隐藏的div和图表实例
|
|
|
+ document.body.removeChild(hiddenDiv);
|
|
|
+ myChart.dispose();
|
|
|
+ }, 200);
|
|
|
+ })
|
|
|
+ .on("error", error => {
|
|
|
+ console.log("生成echarts失败", error);
|
|
|
+ resolve("#");
|
|
|
+ });
|
|
|
+
|
|
|
+ // 设置图标配置
|
|
|
+ myChart.setOption(option);
|
|
|
+ // console.log("词云图???",option)
|
|
|
+ } catch (error) {
|
|
|
+ console.log(error, "error");
|
|
|
+ resolve("#");
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+ getImageSrcToBase64(src) {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ const image = new Image();
|
|
|
+ image.src = src;
|
|
|
+ image.onload = () => {
|
|
|
+ const canvas = document.createElement("canvas");
|
|
|
+ canvas.width = image.naturalWidth;
|
|
|
+ canvas.height = image.naturalHeight;
|
|
|
+ canvas.style.width = `${canvas.width / window.devicePixelRatio}px`;
|
|
|
+ canvas.style.height = `${canvas.height / window.devicePixelRatio}px`;
|
|
|
+
|
|
|
+ const context = canvas.getContext("2d");
|
|
|
+ context.drawImage(image, 0, 0);
|
|
|
+ const base64 = canvas.toDataURL("image/png");
|
|
|
+ resolve(base64);
|
|
|
+ };
|
|
|
+ });
|
|
|
+ },
|
|
|
+ getEChartsSpectrogramImage(data) {
|
|
|
+ return new Promise(resolve => {
|
|
|
+ try {
|
|
|
+ let canvas = document.createElement("canvas");
|
|
|
+ let ctx = canvas.getContext("2d");
|
|
|
+ canvas.width = 600 * window.devicePixelRatio;
|
|
|
+ canvas.height = 200 * window.devicePixelRatio;
|
|
|
+ // 缩放绘图上下文
|
|
|
+ ctx.scale(1, 1);
|
|
|
+
|
|
|
+ let canvasWidth = canvas.width;
|
|
|
+ let canvasWidth2 = canvasWidth - 20;
|
|
|
+ let canvasHeight = canvas.height;
|
|
|
+ ctx.imageSmoothingEnabled = false;
|
|
|
+ ctx.lineWidth = 1;
|
|
|
+
|
|
|
+ // 设置颜色和文字
|
|
|
+ const teacherColor = "#5470C6"; // 老师的颜色
|
|
|
+ const studentColor = "#91CC75"; // 学生的颜色
|
|
|
+ const fontSize = 14; //字体大小
|
|
|
+
|
|
|
+ ctx.fillStyle = teacherColor;
|
|
|
+ this.drawRoundedRect(ctx, 0, canvasHeight - 20, 20, 15, 4);
|
|
|
+ // ctx.fillRect(0, canvasHeight - 20, 25, 20);
|
|
|
+ ctx.fillStyle = "black";
|
|
|
+ ctx.font = `${fontSize}px serif`;
|
|
|
+ ctx.fillText("老师", 28, canvasHeight - 7);
|
|
|
+
|
|
|
+ ctx.fillStyle = studentColor;
|
|
|
+ // ctx.fillRect(100, canvasHeight - 20, 25, 20);
|
|
|
+ this.drawRoundedRect(ctx, 100, canvasHeight - 20, 20, 15, 4);
|
|
|
+ ctx.fillStyle = "black";
|
|
|
+ ctx.font = `${fontSize}px serif`;
|
|
|
+ ctx.fillText("学生", 128, canvasHeight - 7);
|
|
|
+ let sum = data.data.reduce((pre, cur) => (pre += cur.value), 0);
|
|
|
+ // 当前x位置的起始点
|
|
|
+ let currentX = 10;
|
|
|
+ // 计算并绘制每个区域
|
|
|
+ data.data.forEach(i => {
|
|
|
+ const segmentWidth = parseFloat(
|
|
|
+ (i.value / (sum / canvasWidth2)).toFixed(2)
|
|
|
+ );
|
|
|
+ ctx.fillStyle = i.type == 0 ? teacherColor : studentColor;
|
|
|
+ ctx.fillRect(currentX, 20, segmentWidth, canvasHeight - 100);
|
|
|
+
|
|
|
+ // 更新x位置
|
|
|
+ currentX += segmentWidth;
|
|
|
+ });
|
|
|
+
|
|
|
+ // 绘制红色垂直线(指定位置)
|
|
|
+ // ctx.strokeStyle = "red";
|
|
|
+ // ctx.lineWidth = 2;
|
|
|
+
|
|
|
+ // data.breakpoint.forEach(i => {
|
|
|
+ // const breakpointPo = parseFloat(
|
|
|
+ // (i / (sum / canvasWidth2)).toFixed(2)
|
|
|
+ // );
|
|
|
+ // ctx.beginPath();
|
|
|
+ // ctx.moveTo(breakpointPo, 10);
|
|
|
+ // ctx.lineTo(breakpointPo, canvasHeight - 70);
|
|
|
+ // ctx.stroke();
|
|
|
+ // });
|
|
|
+
|
|
|
+ let interval = parseFloat((300 / (sum / canvasWidth2)).toFixed(2));
|
|
|
+ //绘制竖线
|
|
|
+ let _lastI = 0;
|
|
|
+ //绘制竖线
|
|
|
+ for (let i = 0; i < canvasWidth2; i += interval) {
|
|
|
+ ctx.beginPath();
|
|
|
+ ctx.strokeStyle = "#BFBFBF";
|
|
|
+ ctx.moveTo(i == 0 ? 10 : i, canvasHeight - 70);
|
|
|
+ ctx.lineTo(i == 0 ? 10 : i, canvasHeight - 55);
|
|
|
+ ctx.stroke();
|
|
|
+ ctx.fillStyle = "#868686";
|
|
|
+ let timeLabel = (((i / canvasWidth2) * sum) / 60).toFixed(0); // 时间标识计算
|
|
|
+ ctx.font = `${fontSize}px serif`;
|
|
|
+ if (i == 0) {
|
|
|
+ ctx.fillText(`${timeLabel}min`, i + 10, canvasHeight - 40);
|
|
|
+ } else if (i + interval >= canvasWidth2) {
|
|
|
+ ctx.fillText(`${timeLabel}min`, i - 20, canvasHeight - 40);
|
|
|
+ } else {
|
|
|
+ ctx.fillText(`${timeLabel}min`, i - 15, canvasHeight - 40);
|
|
|
+ }
|
|
|
+ _lastI = i;
|
|
|
+ }
|
|
|
+ if (canvasWidth2 - _lastI > 60) {
|
|
|
+ ctx.beginPath();
|
|
|
+ ctx.strokeStyle = "#BFBFBF";
|
|
|
+ ctx.moveTo(canvasWidth2 + 10, canvasHeight - 70);
|
|
|
+ ctx.lineTo(canvasWidth2 + 10, canvasHeight - 55);
|
|
|
+ ctx.stroke();
|
|
|
+ ctx.fillStyle = "#868686";
|
|
|
+ let timeLabel = (sum / 60).toFixed(0); // 时间标识计算
|
|
|
+ ctx.font = `${fontSize}px serif`;
|
|
|
+ ctx.fillText(
|
|
|
+ `${timeLabel}min`,
|
|
|
+ canvasWidth2 - 20,
|
|
|
+ canvasHeight - 40
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ ctx.beginPath();
|
|
|
+ ctx.strokeStyle = "#BFBFBF";
|
|
|
+ ctx.moveTo(10, canvasHeight - 55);
|
|
|
+ ctx.lineTo(canvasWidth2 + 10, canvasHeight - 55);
|
|
|
+ ctx.stroke();
|
|
|
+
|
|
|
+ // 将 canvas 转换为 base64 格式的图片地址
|
|
|
+ const base64Image = canvas.toDataURL("image/png");
|
|
|
+ resolve(base64Image);
|
|
|
+ } catch (e) {
|
|
|
+ console.log(e);
|
|
|
+ resolve("#");
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+ getEChartsechartsRTCHImage(data) {
|
|
|
+ return new Promise(resolve => {
|
|
|
+ try {
|
|
|
+ let canvas = document.createElement("canvas");
|
|
|
+ let ctx = canvas.getContext("2d");
|
|
|
+ canvas.width = 300 * window.devicePixelRatio;
|
|
|
+ canvas.height = 350 * window.devicePixelRatio;
|
|
|
+ // 缩放绘图上下文
|
|
|
+ ctx.scale(1, 1);
|
|
|
+
|
|
|
+ let canvasWidth = canvas.width;
|
|
|
+ let canvasHeight = canvas.height;
|
|
|
+ let fontColor = "#1a7ad3";
|
|
|
+ ctx.imageSmoothingEnabled = false;
|
|
|
+ ctx.lineWidth = 1;
|
|
|
+ const img = new Image();
|
|
|
+ img.src = require("../../../assets/icon/classroomObservation/rt-ch_echarts2.svg"); //ch_echarts2
|
|
|
+ img.onload = () => {
|
|
|
+ ctx.drawImage(img, 0, 0, canvasWidth, canvasWidth);
|
|
|
+ ctx.beginPath();
|
|
|
+ let _showWidth = canvasWidth - (canvasWidth / 8.8) * 2;
|
|
|
+ ctx.fillStyle = fontColor;
|
|
|
+ ctx.arc(
|
|
|
+ canvasWidth / 8.8 + _showWidth * parseFloat(data.RT),
|
|
|
+ canvasWidth / 8.8 + _showWidth - _showWidth * parseFloat(data.CH),
|
|
|
+ 4,
|
|
|
+ 0,
|
|
|
+ 2 * Math.PI
|
|
|
+ );
|
|
|
+ ctx.lineWidth = 0.5; // 设置边框大小
|
|
|
+ // ctx.arc((canvasWidth*parseFloat(this.data.RT))+(canvasWidth/8.8),(canvasWidth-(canvasWidth*parseFloat(this.data.CH))+(canvasWidth/8.8)), 4, 0, 2 * Math.PI);
|
|
|
+ ctx.fill();
|
|
|
+ ctx.stroke();
|
|
|
+ ctx.fillStyle = fontColor;
|
|
|
+ ctx.font = "italic bold 24px Arial";
|
|
|
+ const text = `RT=${data.RT} CH=${data.CH}`;
|
|
|
+ const textWidth = ctx.measureText(text).width;
|
|
|
+ ctx.fillText(
|
|
|
+ text,
|
|
|
+ (canvasWidth - textWidth) / 2,
|
|
|
+ canvasHeight - canvasWidth / 12
|
|
|
+ );
|
|
|
+ const base64Image = canvas.toDataURL("image/png");
|
|
|
+ resolve(base64Image);
|
|
|
+ };
|
|
|
+ } catch (e) {
|
|
|
+ console.log(e);
|
|
|
+ resolve("#");
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+ drawRoundedRect(ctx, x, y, width, height, radius) {
|
|
|
+ // 限制 radius 的最大值,防止它超过矩形的宽度或高度的一半
|
|
|
+ const actualRadius = Math.min(radius, width / 2, height / 2);
|
|
|
+
|
|
|
+ ctx.beginPath();
|
|
|
+ ctx.moveTo(x + actualRadius, y); // 起点,矩形顶部的左侧
|
|
|
+
|
|
|
+ // 右上角的弧线
|
|
|
+ ctx.arcTo(x + width, y, x + width, y + height, actualRadius);
|
|
|
+
|
|
|
+ // 右下角的弧线
|
|
|
+ ctx.arcTo(x + width, y + height, x, y + height, actualRadius);
|
|
|
+
|
|
|
+ // 左下角的弧线
|
|
|
+ ctx.arcTo(x, y + height, x, y, actualRadius);
|
|
|
+
|
|
|
+ // 左上角的弧线
|
|
|
+ ctx.arcTo(x, y, x + width, y, actualRadius);
|
|
|
+
|
|
|
+ ctx.closePath();
|
|
|
+ ctx.fill(); // 填充颜色
|
|
|
+ },
|
|
|
+ async getDocFnPromise({ bmData, dataList, tagList, tid }) {
|
|
|
+ return new Promise(async resolve => {
|
|
|
+ try {
|
|
|
+ const md = new markdownIt();
|
|
|
+ let showBrief = true;
|
|
|
+ tagList.forEach(i => (i.dataList = []));
|
|
|
+ let url = `https://beta.cloud.cocorobo.cn/aigpt/#/classroom_observation_board?tid=${tid}`;
|
|
|
+ const qRCodeSrc = await this.getQrCodeImageSrc(url);
|
|
|
+
|
|
|
+ // dataList.sort((a, b) => a.tIndex - b.tIndex);
|
|
|
+
|
|
|
+ dataList.sort((a, b) => a.tIndex - b.tIndex);
|
|
|
+ dataList.sort((a, b) => a.Type - b.Type);
|
|
|
+ dataList.forEach(i1 => {
|
|
|
+ tagList.forEach(i2 => {
|
|
|
+ if (i2.value == i1.Type) {
|
|
|
+ i2.dataList.push(i1);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ let directoryHtml = `<div style="margin-bottom:1in"><div style="text-align:center;font-size:20pt;margin-bottom:0.5in">目录</div>`;
|
|
|
+
|
|
|
+ let analysisHtml = ``;
|
|
|
+
|
|
|
+ for (let c = 0; c < tagList.length; c++) {
|
|
|
+ let i = tagList[c];
|
|
|
+ let dire = `<div>`;
|
|
|
+ let tagHtml = `<div style="margin-bottom:0.5in">`;
|
|
|
+ if (i.value == 0) {
|
|
|
+ i.dataList = i.dataList.filter(i2 => i2.tIndex != 2);
|
|
|
+ }
|
|
|
+ i.dataList.sort((a, b) => a.tIndex - b.tIndex);
|
|
|
+ tagHtml += `<h1 style="font-size:16pt;margin-bottom:-1in">${
|
|
|
+ this.tag[i.value]
|
|
|
+ }、${i.name}</h1>`;
|
|
|
+ dire += `<p style="font-size:14pt;margin-bottom:-0.8in">${
|
|
|
+ this.tag[i.value]
|
|
|
+ }、${i.name}</p>`;
|
|
|
+
|
|
|
+ for (let d = 0; d < i.dataList.length; d++) {
|
|
|
+ let i2 = i.dataList[d];
|
|
|
+ let i2Index = d;
|
|
|
+ tagHtml += `<h2 style="font-size:14pt;margin-bottom:-1in">${i2Index +
|
|
|
+ 1}、${
|
|
|
+ i2.jsonData.anotherName
|
|
|
+ ? i2.jsonData.anotherName
|
|
|
+ : i2.jsonData.name
|
|
|
+ }</h2>`;
|
|
|
+ dire += `<p style="font-size:11pt;margin-bottom:-0.8in;margin-left:0.1in">${i2Index +
|
|
|
+ 1}、${
|
|
|
+ i2.jsonData.anotherName
|
|
|
+ ? i2.jsonData.anotherName
|
|
|
+ : i2.jsonData.name
|
|
|
+ }</p>`;
|
|
|
+ if (showBrief && i2.jsonData.result) {
|
|
|
+ tagHtml += `<p style="font-size:10.5pt;font-style:italic;margin-bottom:-0.7in;color:#6b798e">${i2.jsonData.result}</p>`;
|
|
|
+ }
|
|
|
+ if (i2.jsonData.eChartData) {
|
|
|
+ tagHtml += `<div style="width:100vw;padding:70%;box-sizing: border-box;text-align:center"><img style="margin:auto" src="${await this.getEChartsImageSrc(
|
|
|
+ i2.jsonData.eChartData
|
|
|
+ )}"/></div>`;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (i2.jsonData.spectrogramData) {
|
|
|
+ tagHtml += `<div style="width:100vw;padding:70%;box-sizing: border-box;text-align:center"><img style="margin:auto" src="${await this.getEChartsSpectrogramImage(
|
|
|
+ i2.jsonData.spectrogramData
|
|
|
+ )}"/></div>`;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (i2.jsonData.CH && i2.jsonData.RT) {
|
|
|
+ tagHtml += `<div style="width:100vw;text-align:center;padding:70%;box-sizing: border-box;"><img style="margin:auto" src="${await this.getEChartsechartsRTCHImage(
|
|
|
+ {
|
|
|
+ RT: i2.jsonData.RT,
|
|
|
+ CH: i2.jsonData.CH
|
|
|
+ }
|
|
|
+ )}"/></div>`;
|
|
|
+ }
|
|
|
+ let _content = md
|
|
|
+ .render(i2.jsonData.content ? i2.jsonData.content : "")
|
|
|
+ .replace(/<p>/g, "")
|
|
|
+ .replace(/<\/p>/g, "")
|
|
|
+ .replace(/<strong>/g, '<span style="font-weight: bold;">')
|
|
|
+ .replace(/<\/strong>/g, "</span>");
|
|
|
+ tagHtml += `<p style="font-size:10.5pt;margin-bottom:-0.5in">${_content}</p>`;
|
|
|
+ }
|
|
|
+ tagHtml += "</div>";
|
|
|
+ dire += "</div>";
|
|
|
+ analysisHtml += tagHtml;
|
|
|
+ directoryHtml += dire;
|
|
|
+ }
|
|
|
+
|
|
|
+ directoryHtml += "</div>";
|
|
|
+ let _html = `
|
|
|
+ <div>
|
|
|
+ <p style="width:100vw;margin-bottom:1.5in">*分析结果仅供参考</p>
|
|
|
+ <p style="font-size:28pt;width:100vw;text-align:center;">课堂观察报告</p>
|
|
|
+ <p style="font-size:10pt;width:100vw;text-align:center;margin-bottom:0.6in">报告生成时间:${new Date().toLocaleString()}</p>
|
|
|
+ <div style="font-size:16pt;width:100vw;text-align:center;margin-bottom:1in">
|
|
|
+ <p style="font-size:20pt;margin-bottom:0.7in">《${bmData.courseName}》</p>
|
|
|
+ <p style="margin-bottom:-1in">授课老师:${
|
|
|
+ bmData.teacherName ? bmData.teacherName : "未填写"
|
|
|
+ }</p>
|
|
|
+ <p style="margin-bottom:-1in">授课年级:${
|
|
|
+ bmData.grade ? bmData.grade : "未填写"
|
|
|
+ }</p>
|
|
|
+ <p style="margin-bottom:-1in">授课科目:${
|
|
|
+ bmData.subject ? bmData.subject : "未填写"
|
|
|
+ }</p>
|
|
|
+ <p style="margin-bottom:-1in">授课时间:${
|
|
|
+ bmData.time ? bmData.time : "未填写"
|
|
|
+ }</p>
|
|
|
+ </div>
|
|
|
+ <div style="font-size:16pt;width:100vw;text-align:center;margin-bottom:0.5in">
|
|
|
+ <img src="${qRCodeSrc}" style="width:150px;height:150px;margin:auto;"/>
|
|
|
+ <p>扫码查看网页版</p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ ${directoryHtml}
|
|
|
+
|
|
|
+ <div>
|
|
|
+ ${analysisHtml}
|
|
|
+ </div>
|
|
|
+ `;
|
|
|
+
|
|
|
+ const content = `<!DOCTYPE html>
|
|
|
+ <html xmlns:v='urn:schemas-microsoft-com
|
|
|
+ :vml'xmlns:o='urn:schemas-microsoft-com:office
|
|
|
+ :office'xmlns:w='urn:schemas-microsoft-com:office
|
|
|
+ :word'xmlns:m='http://schemas.microsoft.com/office/2004/12/omml'
|
|
|
+ xmlns='http://www.w3.org/TR/REC-html40'
|
|
|
+ xmlns='http://www.w3.org/1999/xhtml'>
|
|
|
+ <head>
|
|
|
+ <meta charset="UTF-8">
|
|
|
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
|
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
+ <title>《${bmData.courseName}》课堂观察报告</title>
|
|
|
+ <style>
|
|
|
+ *{
|
|
|
+ font-family: '宋体';
|
|
|
+ margin:0;
|
|
|
+ padding:0;
|
|
|
+ line-height:1;
|
|
|
+ }
|
|
|
+ table {
|
|
|
+ border-collapse: collapse; /* 折叠边框 */
|
|
|
+ width: 100%;
|
|
|
+ font-size:10.5pt;
|
|
|
+ }
|
|
|
+ th, td {
|
|
|
+ border: 1px solid black; /* 线条样式 */
|
|
|
+ padding: 8px;
|
|
|
+ text-align: left;
|
|
|
+ font-size:10.5pt;
|
|
|
+ }
|
|
|
+ ol,ul{
|
|
|
+ margin:0;
|
|
|
+ padding:0;
|
|
|
+ margin-right:-1in;
|
|
|
+ }
|
|
|
+ li{
|
|
|
+ position: relative;
|
|
|
+ padding-left: 1.5em; /* 控制项目符号与文本的距离 */
|
|
|
+ margin-bottom: 0.5em;
|
|
|
+ text-indent: -1.5em; /* 负缩进使文本与符号对齐 */
|
|
|
+ mso-special-format: bullet;
|
|
|
+ margin-left: 0;
|
|
|
+ padding-left: 10pt;
|
|
|
+ text-indent: -10pt;
|
|
|
+ mso-style-name: "Normal";
|
|
|
+ mso-style-priority: 99;
|
|
|
+ mso-style-unhide: no;
|
|
|
+ mso-style-qformat: yes;
|
|
|
+ mso-style-parent: "";
|
|
|
+ }
|
|
|
+ p{
|
|
|
+ margin:0;
|
|
|
+ padding:0
|
|
|
+ }
|
|
|
+ </style>
|
|
|
+ </head>
|
|
|
+ <body>
|
|
|
+ ${_html}
|
|
|
+ </body>
|
|
|
+ </html>`;
|
|
|
+ // debugger
|
|
|
+ let blob = htmlDocx.asBlob(content);
|
|
|
+ const file = new File(
|
|
|
+ [blob],
|
|
|
+ `${bmData.courseName}课堂观察报告.docx`,
|
|
|
+ {
|
|
|
+ type: ".docx",
|
|
|
+ lastModified: new Date().getTime()
|
|
|
+ }
|
|
|
+ );
|
|
|
+ resolve(file);
|
|
|
+ } catch (error) {
|
|
|
+ console.log("👉", error);
|
|
|
+ resolve(error);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped></style>
|