lvkun996 10 сар өмнө
parent
commit
1507dbba3d

+ 13 - 2
package-lock.json

@@ -10,6 +10,7 @@
       "dependencies": {
         "-": "0.0.1",
         "axios": "^1.5.0",
+        "dayjs": "^1.11.13",
         "docx-preview": "^0.3.2",
         "docxtemplater": "^3.47.2",
         "docxtemplater-image-module-free": "^1.1.1",
@@ -3459,6 +3460,11 @@
       "resolved": "https://registry.npmjs.org/dash-ast/-/dash-ast-2.0.1.tgz",
       "integrity": "sha512-5TXltWJGc+RdnabUGzhRae1TRq6m4gr+3K2wQX0is5/F2yS6MJXJvLyI3ErAnsAXuJoGqvfVD5icRgim07DrxQ=="
     },
+    "node_modules/dayjs": {
+      "version": "1.11.13",
+      "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.13.tgz",
+      "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg=="
+    },
     "node_modules/de-indent": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
@@ -4945,7 +4951,7 @@
     },
     "node_modules/html2canvas": {
       "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz",
+      "resolved": "https://registry.npmmirror.com/html2canvas/-/html2canvas-1.4.1.tgz",
       "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
       "dependencies": {
         "css-line-break": "^2.1.0",
@@ -10329,6 +10335,11 @@
       "resolved": "https://registry.npmjs.org/dash-ast/-/dash-ast-2.0.1.tgz",
       "integrity": "sha512-5TXltWJGc+RdnabUGzhRae1TRq6m4gr+3K2wQX0is5/F2yS6MJXJvLyI3ErAnsAXuJoGqvfVD5icRgim07DrxQ=="
     },
+    "dayjs": {
+      "version": "1.11.13",
+      "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.13.tgz",
+      "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg=="
+    },
     "de-indent": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
@@ -11432,7 +11443,7 @@
     },
     "html2canvas": {
       "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz",
+      "resolved": "https://registry.npmmirror.com/html2canvas/-/html2canvas-1.4.1.tgz",
       "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
       "requires": {
         "css-line-break": "^2.1.0",

+ 1 - 0
package.json

@@ -13,6 +13,7 @@
   "dependencies": {
     "-": "0.0.1",
     "axios": "^1.5.0",
+    "dayjs": "^1.11.13",
     "docx-preview": "^0.3.2",
     "docxtemplater": "^3.47.2",
     "docxtemplater-image-module-free": "^1.1.1",

+ 105 - 0
src/api/claszz.ts

@@ -0,0 +1,105 @@
+import request from "@/utils/request";
+
+
+
+/**
+ * 导出行为记录基础数据查询  开始一段话的数据
+ * https://open.test.luojigou.vip/teach/doc.html#/%E6%80%9D%E7%BB%B4%E8%8A%AF/%E6%B5%8B%E8%AF%84%E6%95%B0%E6%8D%AE%E7%BB%9F%E8%AE%A1/exportBaseCountDataUsingPOST_1
+ * "classIds": 班级ID集合,
+	"gradeSemesterIds": 年级学期ID集合,
+	"schoolId": 学校ID,
+	"teacherIds": 教师ID集合
+ * 
+ * 
+ *  
+ */
+export function getClazzBase(data: {
+  "classIds": string[],
+	"gradeSemesterIds": string[],
+	"schoolId": string,
+	"teacherIds": string[]
+}) {
+  return request<any>({
+    url: `/teach/evaluation/statistics/export/base/count`,
+    method: "POST",
+    data,
+  });
+}
+
+/**
+ * 记录场景分布查询  页面上第第一段折线图
+ * https://open.test.luojigou.vip/teach/doc.html#/%E6%80%9D%E7%BB%B4%E8%8A%AF/%E6%B5%8B%E8%AF%84%E6%95%B0%E6%8D%AE%E7%BB%9F%E8%AE%A1/exportBaseCountDataUsingPOST_1
+ * "classIds": 班级ID集合,
+	"gradeSemesterIds": 年级学期ID集合,
+	"schoolId": 学校ID,
+	"teacherIds": 教师ID集合
+ * 
+ * 
+ *  
+ */
+export function getClazzScene(data: {
+  "classIds": string[],
+	"gradeSemesterIds": string[],
+	"schoolId": string,
+	"teacherIds": string[]
+}) {
+  return request<any>({
+    url: `/teach/evaluation/statistics/class/scene`,
+    method: "POST",
+    data,
+  });
+}
+
+
+/**
+ * 观察领域分布 页面上第二个折线图
+ * https://open.test.luojigou.vip/teach/doc.html#/%E6%80%9D%E7%BB%B4%E8%8A%AF/%E6%B5%8B%E8%AF%84%E6%95%B0%E6%8D%AE%E7%BB%9F%E8%AE%A1/classSceneDetailUsingPOST_1
+ * "classIds": 班级ID集合,
+	"gradeSemesterIds": 年级学期ID集合,
+	"schoolId": 学校ID,
+	"teacherIds": 教师ID集合
+ * 
+ * 
+ *  
+ */
+  export function getClazzDomain(data: {
+    "classIds": string[],
+    "gradeSemesterIds": string[],
+    "schoolId": string,
+    "teacherIds": string[]
+  }) {
+    return request<any>({
+      url: `/teach/evaluation/statistics/class/domain`,
+      method: "POST",
+      data,
+    });
+  }
+
+
+
+/**
+ * 学生各领域表现阶段汇总 表格
+ * https://open.test.luojigou.vip/teach/doc.html#/%E6%80%9D%E7%BB%B4%E8%8A%AF/%E6%B5%8B%E8%AF%84%E6%95%B0%E6%8D%AE%E7%BB%9F%E8%AE%A1/classSceneDetailUsingPOST_1
+ * "classIds": 班级ID集合,
+	"gradeSemesterIds": 年级学期ID集合,
+	"schoolId": 学校ID,
+	"teacherIds": 教师ID集合
+ * 
+ * 
+ *  
+ */
+  export function getStudentDomain(data: {
+    "classIds": string[],
+    "gradeSemesterIds": string[],
+    "schoolId": string,
+    "teacherIds": string[]
+  }) {
+    return request<any>({
+      url: `/teach/evaluation/statistics/student/domain`,
+      method: "POST",
+      data,
+    });
+  }
+
+
+

BIN
src/assets/images/empty-cover.png


+ 59 - 0
src/components/exportButton.vue

@@ -0,0 +1,59 @@
+<template>
+  <div  class="export-button" v-if="!scrollState" @click="exportHandel" >
+    {{progress === '' ? '导出' : progress + '%'}}
+  </div>
+</template>
+<script lang='ts'  setup >
+// @ts-nocheck
+ import { withDefaults, ref } from 'vue'
+ export interface IProps {
+  progress: string
+ }
+
+ const props = withDefaults(defineProps<IProps>(), {
+  progress: ''
+ }) 
+
+ const emits = defineEmits(['export'])
+  
+
+ const scrollState = ref(false)
+
+ const exportHandel = () => {
+  emits('export')
+ }
+
+ let scrollTimeout: any;
+
+  function onScrollStart() {
+    scrollState.value = true
+  }
+
+  function onScrollStop() {
+    scrollState.value = false
+  }
+
+  window.addEventListener('scroll', function() {
+      clearTimeout(scrollTimeout);
+      onScrollStart(); // Detect when scrolling starts
+
+      scrollTimeout = setTimeout(onScrollStop, 200); // Adjust timeout as needed to detect when scrolling stops
+  }, true);
+
+</script>
+<style lang='scss' scoped >
+ .export-button {
+    width: 48px;
+    height: 48px;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    position: fixed;
+    right: 30px;
+    bottom: 100px;
+    border-radius: 50%;
+    background-color: #2A69FD;
+    color: #fff;
+    z-index: 10;
+ }
+</style>

+ 2 - 1
src/store/index.ts

@@ -2,6 +2,7 @@ import { createPinia } from "pinia";
 import useReportStore from "./modules/report";
 import useCustomizeStore from "./modules/customize";
 import useSummanyStore from "./modules/summany";
+import useClazzStore from "./modules/clazz";
 
-export { useReportStore, useCustomizeStore, useSummanyStore };
+export { useReportStore, useCustomizeStore, useSummanyStore, useClazzStore };
 export default createPinia();

+ 45 - 0
src/store/modules/clazz.ts

@@ -0,0 +1,45 @@
+import { defineStore } from "pinia";
+import { getClazzBase, getClazzDomain, getClazzScene, getStudentDomain   } from '@/api/claszz'
+import { ref } from "vue";
+import dayjs from 'dayjs'
+
+export default defineStore("clazz", () => {
+
+  const baseData = ref()
+  const clazzDomainData = ref()
+  const clazzSceneData = ref()
+  const studentDomainData = ref()
+
+  const _getClazzBase = async (params: any) => {
+    const { data } = await getClazzBase(params)
+    baseData.value = {
+      ...data,
+      startTimeFormat: dayjs(data.startTime).format('YYYY年MM月DD日'),
+      endTimeFormat: dayjs(data.endTime).format('YYYY年MM月DD')
+    }
+  }
+  const _getClazzDomain = async (params: any) => {
+    const { data } = await getClazzDomain(params)
+    clazzDomainData.value = data
+  }
+  const _getClazzScene = async (params: any) => {
+    const { data } = await getClazzScene(params)
+    clazzSceneData.value = data
+  }
+  const _getStudentDomain = async (params: any) => {
+    const { data } = await getStudentDomain(params)
+    studentDomainData.value = data
+  }
+
+  return {
+    baseData,
+    clazzDomainData,
+    clazzSceneData,
+    studentDomainData,
+    getClazzBase: _getClazzBase,
+    getClazzDomain: _getClazzDomain,
+    getClazzScene: _getClazzScene,
+    getStudentDomain: _getStudentDomain
+  }
+})
+

+ 0 - 12
src/store/modules/clazzReport.ts

@@ -1,12 +0,0 @@
-import { defineStore } from "pinia";
-
-
-
-
-
-
-export default defineStore("summany", () => {
-
-  
-
-})

+ 107 - 107
src/types/customize.d.ts

@@ -390,113 +390,113 @@ export function defaultSemesterReport(): ISemesterReport {
         }
     ],
     "questionList": [
-        {
-            "teacherName": "雷lEi\uD83C\uDF88",
-            "topic": "[思维魔法幼儿园]恰“桥”遇见你——走进小一班课程故事",
-            "background": "在开展中华文化主题《我家房子有特色》“神奇的土楼”的活动中,老师带领孩子们一起游览了龙岩有特色的建筑物,当老师问到:在我们家乡有哪些有特色的建筑物时,大部分孩子们说的是家旁边的龙岩大桥。于是,老师重点介绍了龙岩大桥的相关知识:主桥为“宝石形”独塔双索面斜拉桥,在建筑主体时,龙岩大桥百米高的万吨主塔成功二次转体,实现世界桥梁建设领域首例二次转体施工。对此孩子们对桥产生了浓厚的兴趣,一场“桥”的探秘之旅正式开始。",
-            "questions": [
-                {
-                    "id": "1803312604970586114",
-                    "question": "为什么要建这么多的桥,这些桥有什么作用呢?为什么桥要做成这么多种样式。",
-                    "questionCategory": "创造",
-                    "activityCategories": [
-                        "小组",
-                        "区角"
-                    ],
-                    "plan": {
-                        "content": "1.认识各式各样的桥\n通过集体分享,孩子们知道了桥的不同类型如:古代的桥、现代的桥、中国的桥、外国的桥等等,并能说出桥的构造:一座完整的桥梁基本组成部分包括桥面、桥墩、引桥、支座、护栏。\n2.桥的作用及演变。\n3.建构区里,孩子们开始搭建桥。\n",
-                        "images": [
-                            "https://app-resources-luojigou.luojigou.vip/f11f0dc989434b4aaa4716f25031e2fe"
-                        ],
-                        "videos": [],
-                        "children": []
-                    },
-                    "practice": {
-                        "content": "从“桥的演变\"故事,我们了解到:很久以前河上没有桥,被风吹倒的大树正好横搭在河两岸,人们从上面走过去很方便,这就成了最早的桥。接着人们试着用藤条、木板来做桥,于是有了悬在河上的吊桥。人们还试着用石头垒成桥洞,石头被挤成桥洞很牢固,这就成了石拱桥。后来人们又试着在河面上立桥墩铺石板搭桥,于是在很宽的河面上也能搭桥了。现在人们已经会造各式各样的桥啦。\n随着活动的不断深入,孩子们已经不仅仅满足于认识桥,他们更想成为一名优秀的桥梁建筑师。瞧!小朋友们化身小小设计师,设计出了一幅幅美丽而又壮观的桥。",
-                        "images": [],
-                        "videos": [],
-                        "children": [
-                            {
-                                "child": "1790976028074045441",
-                                "babyName": "苏珊娜",
-                                "headImg": "https://img.luojigou.vip/Fhe5ZRYB5MSxnDgC6W0ogpoPs3uX.png",
-                                "content": "以前没有钢筋和水泥、也没有玻璃、只有木头和石头。弯弯的桥洞要怎么做?",
-                                "images": [
-                                    "https://app-resources-luojigou.luojigou.vip/fd4ef2528c16499b9ab205e5143ba9c1"
-                                ],
-                                "videos": []
-                            },
-                            {
-                                "child": "1791275589778796546",
-                                "babyName": "奥利维",
-                                "headImg": "https://img.luojigou.vip/FjYlziq_45SihPiMNhWvVUC-9d6t.jpeg",
-                                "content": "为什么以前的桥和现在的桥有些不太一样长长的大桥,桥面怎么样才能连到一起呢?",
-                                "images": [
-                                    "https://app-resources-luojigou.luojigou.vip/e03e529a542f46a1896ce4bca4ece25c",
-                                    "https://app-resources-luojigou.luojigou.vip/b7617644a90d4d10b0b3b930a6d39c2d",
-                                    "https://app-resources-luojigou.luojigou.vip/c6085ab7b7e246828986b515d0e3913b"
-                                ],
-                                "videos": []
-                            }
-                        ]
-                    },
-                    "summary": {
-                        "content": "把握“进”与“退”\n\n      在课程实施的过程中,我们发现了孩子们的兴趣点并跟随孩子们的兴趣进行探索与发现,当出现超越他们解决能力的问题时,如:桥为什么会倒?我们是与孩子们一起讨论思考一起想办法解决问题,在孩子们探索的过程当中我们又作为旁观者,通过提问的方式介入,并带有导向的提问引发幼儿对各自游戏行为的进一步思考,从而产生新的想法。",
-                        "images": [
-                            "https://app-resources-luojigou.luojigou.vip/e6810addf974426aa0d13275832af5e5",
-                            "https://app-resources-luojigou.luojigou.vip/a7119d83bd97488a953ba0b41d96e672",
-                            "https://app-resources-luojigou.luojigou.vip/d61eb60efd8b46c7a475f88f25a5a30e",
-                            "https://app-resources-luojigou.luojigou.vip/f088e73e20c54dd993f413711e907bcb"
-                        ],
-                        "videos": [],
-                        "children": []
-                    },
-                    "tweaks": {
-                        "content": "顺应“前”与“后”\n\n      顺应幼儿的转变并不意味着任由课程随意发展,赞同他们的想法本身就是一种无声的支持。在活动中,我们从孩子们的兴趣点出发引导幼儿自由探索、主动学习,充分探索世界桥的秘密,用多元的方法如:亲子调查、实地参观等方式帮助幼儿大胆表现自己对桥的感受和认知,同时让孩子们从桥延伸出爱家乡的情感,这才是我们课程实施的真正意义。",
-                        "images": [],
-                        "videos": [],
-                        "children": []
-                    },
-                    "topic": {
-                        "content": "听每个城市有关桥建造的故事,你想设计什么样的桥?\n\n孩子们是天生的探索家,从对桥好奇,到走近桥、走进桥、搭建桥、创想桥,孩子们对桥有了更多的认识和了解,同时也提高了他们观察生活、探索发现、合作交流的能力。",
-                        "images": [],
-                        "videos": [],
-                        "children": [
-                            {
-                                "child": "1790976028074045441",
-                                "babyName": "苏珊娜",
-                                "headImg": "https://img.luojigou.vip/Fhe5ZRYB5MSxnDgC6W0ogpoPs3uX.png",
-                                "content": "我要设计一个城堡桥,我可以在城堡里过桥。",
-                                "images": [],
-                                "videos": []
-                            },
-                            {
-                                "child": "1791275589778796546",
-                                "babyName": "奥利维",
-                                "headImg": "https://img.luojigou.vip/FjYlziq_45SihPiMNhWvVUC-9d6t.jpeg",
-                                "content": "我觉得你的想法很好,可以让小矮人 白雪公主 住进来。\n我要设计一座高架桥,可以开很多车。我的高架桥可以有两层的。我设计的桥要很多交叉的路,而且还不会堵车。",
-                                "images": [],
-                                "videos": []
-                            }
-                        ]
-                    },
-                    "recordDate": "2024-06-12",
-                    "babyResults": [
-                        {
-                            "babyId": "1791275589778796546",
-                            "babyName": "奥利维",
-                            "list": [
-                                {
-                                    "levelCode": "3",
-                                    "learnDepth": "创造性思维",
-                                    "levelText": "1.(想象力):能够进行创造性地想象,比如,能够创编或续编故事。\n2.(在创造性活动中运用多种材料):自主选择特定的材料来解决问题,表达情感、建构、创造等。\n2.(在创造性活动中加入自己的想法):在探究或创作的过程中,能够增加新的材料或尝试,实现不同的效果。\n3.(在游戏时发现新玩法):自主提出游戏的新玩法或新规则。\n4.(在游戏时提出自己的想法):自主运用象征性的材料,创造道具来进行角色扮演。\n5.(运用创造性思维解决简单的问题):在游戏中自主寻找普通物体的新用法。"
-                                }
-                            ]
-                        }
-                    ]
-                }
-            ]
-        }
+        // {
+        //     "teacherName": "雷lEi\uD83C\uDF88",
+        //     "topic": "[思维魔法幼儿园]恰“桥”遇见你——走进小一班课程故事",
+        //     "background": "在开展中华文化主题《我家房子有特色》“神奇的土楼”的活动中,老师带领孩子们一起游览了龙岩有特色的建筑物,当老师问到:在我们家乡有哪些有特色的建筑物时,大部分孩子们说的是家旁边的龙岩大桥。于是,老师重点介绍了龙岩大桥的相关知识:主桥为“宝石形”独塔双索面斜拉桥,在建筑主体时,龙岩大桥百米高的万吨主塔成功二次转体,实现世界桥梁建设领域首例二次转体施工。对此孩子们对桥产生了浓厚的兴趣,一场“桥”的探秘之旅正式开始。",
+        //     "questions": [
+        //         {
+        //             "id": "1803312604970586114",
+        //             "question": "为什么要建这么多的桥,这些桥有什么作用呢?为什么桥要做成这么多种样式。",
+        //             "questionCategory": "创造",
+        //             "activityCategories": [
+        //                 "小组",
+        //                 "区角"
+        //             ],
+        //             "plan": {
+        //                 "content": "1.认识各式各样的桥\n通过集体分享,孩子们知道了桥的不同类型如:古代的桥、现代的桥、中国的桥、外国的桥等等,并能说出桥的构造:一座完整的桥梁基本组成部分包括桥面、桥墩、引桥、支座、护栏。\n2.桥的作用及演变。\n3.建构区里,孩子们开始搭建桥。\n",
+        //                 "images": [
+        //                     "https://app-resources-luojigou.luojigou.vip/f11f0dc989434b4aaa4716f25031e2fe"
+        //                 ],
+        //                 "videos": [],
+        //                 "children": []
+        //             },
+        //             "practice": {
+        //                 "content": "从“桥的演变\"故事,我们了解到:很久以前河上没有桥,被风吹倒的大树正好横搭在河两岸,人们从上面走过去很方便,这就成了最早的桥。接着人们试着用藤条、木板来做桥,于是有了悬在河上的吊桥。人们还试着用石头垒成桥洞,石头被挤成桥洞很牢固,这就成了石拱桥。后来人们又试着在河面上立桥墩铺石板搭桥,于是在很宽的河面上也能搭桥了。现在人们已经会造各式各样的桥啦。\n随着活动的不断深入,孩子们已经不仅仅满足于认识桥,他们更想成为一名优秀的桥梁建筑师。瞧!小朋友们化身小小设计师,设计出了一幅幅美丽而又壮观的桥。",
+        //                 "images": [],
+        //                 "videos": [],
+        //                 "children": [
+        //                     {
+        //                         "child": "1790976028074045441",
+        //                         "babyName": "苏珊娜",
+        //                         "headImg": "https://img.luojigou.vip/Fhe5ZRYB5MSxnDgC6W0ogpoPs3uX.png",
+        //                         "content": "以前没有钢筋和水泥、也没有玻璃、只有木头和石头。弯弯的桥洞要怎么做?",
+        //                         "images": [
+        //                             "https://app-resources-luojigou.luojigou.vip/fd4ef2528c16499b9ab205e5143ba9c1"
+        //                         ],
+        //                         "videos": []
+        //                     },
+        //                     {
+        //                         "child": "1791275589778796546",
+        //                         "babyName": "奥利维",
+        //                         "headImg": "https://img.luojigou.vip/FjYlziq_45SihPiMNhWvVUC-9d6t.jpeg",
+        //                         "content": "为什么以前的桥和现在的桥有些不太一样长长的大桥,桥面怎么样才能连到一起呢?",
+        //                         "images": [
+        //                             "https://app-resources-luojigou.luojigou.vip/e03e529a542f46a1896ce4bca4ece25c",
+        //                             "https://app-resources-luojigou.luojigou.vip/b7617644a90d4d10b0b3b930a6d39c2d",
+        //                             "https://app-resources-luojigou.luojigou.vip/c6085ab7b7e246828986b515d0e3913b"
+        //                         ],
+        //                         "videos": []
+        //                     }
+        //                 ]
+        //             },
+        //             "summary": {
+        //                 "content": "把握“进”与“退”\n\n      在课程实施的过程中,我们发现了孩子们的兴趣点并跟随孩子们的兴趣进行探索与发现,当出现超越他们解决能力的问题时,如:桥为什么会倒?我们是与孩子们一起讨论思考一起想办法解决问题,在孩子们探索的过程当中我们又作为旁观者,通过提问的方式介入,并带有导向的提问引发幼儿对各自游戏行为的进一步思考,从而产生新的想法。",
+        //                 "images": [
+        //                     "https://app-resources-luojigou.luojigou.vip/e6810addf974426aa0d13275832af5e5",
+        //                     "https://app-resources-luojigou.luojigou.vip/a7119d83bd97488a953ba0b41d96e672",
+        //                     "https://app-resources-luojigou.luojigou.vip/d61eb60efd8b46c7a475f88f25a5a30e",
+        //                     "https://app-resources-luojigou.luojigou.vip/f088e73e20c54dd993f413711e907bcb"
+        //                 ],
+        //                 "videos": [],
+        //                 "children": []
+        //             },
+        //             "tweaks": {
+        //                 "content": "顺应“前”与“后”\n\n      顺应幼儿的转变并不意味着任由课程随意发展,赞同他们的想法本身就是一种无声的支持。在活动中,我们从孩子们的兴趣点出发引导幼儿自由探索、主动学习,充分探索世界桥的秘密,用多元的方法如:亲子调查、实地参观等方式帮助幼儿大胆表现自己对桥的感受和认知,同时让孩子们从桥延伸出爱家乡的情感,这才是我们课程实施的真正意义。",
+        //                 "images": [],
+        //                 "videos": [],
+        //                 "children": []
+        //             },
+        //             "topic": {
+        //                 "content": "听每个城市有关桥建造的故事,你想设计什么样的桥?\n\n孩子们是天生的探索家,从对桥好奇,到走近桥、走进桥、搭建桥、创想桥,孩子们对桥有了更多的认识和了解,同时也提高了他们观察生活、探索发现、合作交流的能力。",
+        //                 "images": [],
+        //                 "videos": [],
+        //                 "children": [
+        //                     {
+        //                         "child": "1790976028074045441",
+        //                         "babyName": "苏珊娜",
+        //                         "headImg": "https://img.luojigou.vip/Fhe5ZRYB5MSxnDgC6W0ogpoPs3uX.png",
+        //                         "content": "我要设计一个城堡桥,我可以在城堡里过桥。",
+        //                         "images": [],
+        //                         "videos": []
+        //                     },
+        //                     {
+        //                         "child": "1791275589778796546",
+        //                         "babyName": "奥利维",
+        //                         "headImg": "https://img.luojigou.vip/FjYlziq_45SihPiMNhWvVUC-9d6t.jpeg",
+        //                         "content": "我觉得你的想法很好,可以让小矮人 白雪公主 住进来。\n我要设计一座高架桥,可以开很多车。我的高架桥可以有两层的。我设计的桥要很多交叉的路,而且还不会堵车。",
+        //                         "images": [],
+        //                         "videos": []
+        //                     }
+        //                 ]
+        //             },
+        //             "recordDate": "2024-06-12",
+        //             "babyResults": [
+        //                 {
+        //                     "babyId": "1791275589778796546",
+        //                     "babyName": "奥利维",
+        //                     "list": [
+        //                         {
+        //                             "levelCode": "3",
+        //                             "learnDepth": "创造性思维",
+        //                             "levelText": "1.(想象力):能够进行创造性地想象,比如,能够创编或续编故事。\n2.(在创造性活动中运用多种材料):自主选择特定的材料来解决问题,表达情感、建构、创造等。\n2.(在创造性活动中加入自己的想法):在探究或创作的过程中,能够增加新的材料或尝试,实现不同的效果。\n3.(在游戏时发现新玩法):自主提出游戏的新玩法或新规则。\n4.(在游戏时提出自己的想法):自主运用象征性的材料,创造道具来进行角色扮演。\n5.(运用创造性思维解决简单的问题):在游戏中自主寻找普通物体的新用法。"
+        //                         }
+        //                     ]
+        //                 }
+        //             ]
+        //         }
+        //     ]
+        // }
     ],
     "domainDataList": [
         {

+ 294 - 39
src/utils/exportPdf.js

@@ -1,14 +1,13 @@
 import pdfMake from "pdfmake/build/pdfmake";
 import pdfFonts from "pdfmake/build/vfs_fonts";
-
-
 import Regular from '@/assets/pdf/font/SourceHanSansCN-Regular.otf'
 import Bold from '@/assets/pdf/font/SourceHanSansCN-Bold.otf'
 import STXINGKA from '@/assets/pdf/font/STXINGKA.TTF'
 
 import { callAppFc, getRunPlatform } from '@/utils/index'
+import { dataDesc } from '@/utils/text'
 
-export const exportPDF = async (_semesterReport) => {
+export const exportPDF = async (_semesterReport, progressCb) => {
 
   const semesterReport = JSON.parse(JSON.stringify(_semesterReport))
 
@@ -34,7 +33,7 @@ export const exportPDF = async (_semesterReport) => {
     const H1Template = (text) => (
         { text: `${text}`, 
         fontSize: H1, 
-        margin: [0, 40, 0, 15], 
+        margin: [0, 40, 0, 4], 
         color: "blue",  
         bold: true, 
         font: 'STXINGKA.TTF', 
@@ -45,7 +44,7 @@ export const exportPDF = async (_semesterReport) => {
         { text: `${text}`, 
         color: '#1a2c5e',
         fontSize: H2, 
-        margin: [0, 15, 0, 15],
+        margin: [0, 0, 0, 0],
         bold: true
     })
 
@@ -53,7 +52,7 @@ export const exportPDF = async (_semesterReport) => {
         { text: `${text}`, 
         fontSize: H3, 
         color: '#1a2c5e',
-        margin: [0, 10, 0, 10],
+        margin: [0, 16, 0, 0],
         bold: true
     })
 
@@ -91,8 +90,8 @@ export const exportPDF = async (_semesterReport) => {
     const H5Template = (text) => (
         { text: `${text}`, 
         fontSize: H5,
-        margin: [0, 15, 0, 15],
-        lineHeight: 2
+        margin: [0, 15, 0, 4],
+        lineHeight: 1
     }) 
 
     const H5ColorTemplate =  (title, text) => (
@@ -100,28 +99,30 @@ export const exportPDF = async (_semesterReport) => {
             columns: [
                 {
                     image: 'deg',
-                    width: 6
+                    width: 6,
+                    margin: [0, 6, 0, 0],
                 },
                 { text: `${title}`, 
                     fontSize: H5,
                     color: '#2a69fd',
                     width: 55,
-                    margin: [5, -2, 0, 0],
+                    margin: [5, 4, 0, 0],
                 },
                 { text: `${text}`, 
                     fontSize: H5,
-                    margin: [5, -2, 0, 0],
+                    margin: [5, 4, 0, 0],
                 }
             ]
         }
-    ) 
-
+    )
 
     const qrTemplate = (url) => ({ qr: url, fit: '120', alignment: 'center'})
 
     const textTemplate = (text) => ({ text: `${text}`, fontSize: H4, margin: [0, 15, 0, 15] })
 
-    const imgTemplate = (url) => ({image: url, width: 160, alignment: 'center', margin: [0, 30, 0, 30] } )
+    const imgTemplate = (url) => ({image: url, width: 500, alignment: 'center', margin: [0, 0, 0, 0] } )
+
+    const imgTemplate2 = (url) => ({image: url, width: 160, alignment: 'left', margin: [0, 0, 0, 20] } )
 
     const radarImgTemplate = (url) => ({image: url, width: 320, alignment: 'center', margin: [0, 30, 0, 30] } )
 
@@ -153,19 +154,22 @@ export const exportPDF = async (_semesterReport) => {
                     columns: [
                       {
                         text: "",
-                        width: 82
+                        width: 0
                       },
-                      imgTemplate(imgs[0]),
+                      imgTemplate2(imgs[0]),
                        {
                         text: "",
                         width: 30
                       },
-                      imgTemplate(imgs[1])
+                      imgTemplate2(imgs[1])
                     ]
                 }
                 } 
             )
-            if (lastValue != null) r.push(imgTemplate(lastValue[0]))
+            if (lastValue != null) {
+                r.push(imgTemplate2(lastValue[0]))
+                r.push('')
+            }
             return r
     }
 
@@ -189,10 +193,10 @@ export const exportPDF = async (_semesterReport) => {
     }
 
     const coverInfo = {
-        text: `姓名:${_data.babyName}\n\n班级:${_data.className}\n\n成长阶段:${_data.startDate}-${_data.endDate}`,
+        text: `姓名:${_data.babyName}\n班级:${_data.className}\n成长阶段:${_data.startDate}-${_data.endDate}`,
         alignment: 'center',
         margin: [0, 50, 0, 200],
-        font: 'STXINGKA.TTF',
+        // font: 'STXINGKA.TTF',
         fontSize: 26
     }
 
@@ -200,34 +204,34 @@ export const exportPDF = async (_semesterReport) => {
         {
             text: `姓名:${_data.babyName}`,
             alignment: 'center',
-            font: 'STXINGKA.TTF',
+            // font: 'STXINGKA.TTF',
             margin: [0, 0, 0, 0],
             bold: true,
-            fontSize: 24
+            fontSize: 20
         },
         {
             text: `年龄:${_data.age}`,
             margin: [0, 20, 0, 20],
             alignment: 'center',
             bold: true,
-            font: 'STXINGKA.TTF',
-            fontSize: 24
+            // font: 'STXINGKA.TTF',
+            fontSize: 20
         },
         {
             text: `我的幼儿园:\n${_data.schoolName}`,
             margin: [0, 20, 0, 20],
             alignment: 'center',
             bold: true,
-            font: 'STXINGKA.TTF',
-            fontSize: 24
+            // font: 'STXINGKA.TTF',
+            fontSize: 20
         },
         {
             text: `我的爱好:\n${_data.hobby}`,
             margin: [0, 20, 0, 20],
             alignment: 'center',
             bold: true,
-            font: 'STXINGKA.TTF',
-            fontSize: 24
+            // font: 'STXINGKA.TTF',
+            fontSize: 20
         },
         
         {
@@ -235,9 +239,9 @@ export const exportPDF = async (_semesterReport) => {
             // text: `我的老师:\n${_data.teachers.slice(0, 2)}`,
             margin: [0, 20, 0, 600],
             alignment: 'center',
-            font: 'STXINGKA.TTF',
+            // font: 'STXINGKA.TTF',
             bold: true,
-            fontSize: 24
+            fontSize: 20
         },
     ]
         
@@ -261,7 +265,7 @@ export const exportPDF = async (_semesterReport) => {
 
     domainAbilityNameList.push(brakePage)
         
-    const questionList = [
+    const questionList = Array.isArray(_data.questionList) && _data.questionList.length > 0 ? [
         H1Template("问题探究"),
         ..._data.questionList.map( item => {
 
@@ -339,7 +343,7 @@ export const exportPDF = async (_semesterReport) => {
                 ...r
             ]
         })
-    ]
+    ].flat(2) : []
    
     const domainDataList = [
         H1Template('行为记录'),
@@ -350,16 +354,15 @@ export const exportPDF = async (_semesterReport) => {
             const recordList = []
 
             item.recordList.map( record => {
-
                 recordList.push(H3Template('能力名称:' + record.abilityName))
                 recordList.push(H4Template('记录日期:' + record.recordDate))
                 // recordList.push(recordList.push(caclImgSplit(record.story.images)))
                 recordList.push(H4Template('观察实录:'))
-                recordList.push(record.story.videos.length ? qrTemplate(record.story.videos[0]) : record.story.images.length === 0 ?  H4Template(record.abilityName) : caclImgSplit(record.story.images))
+                recordList.push(record.story.videos.length ? qrTemplate(record.story.videos[0]) : record.story.images.length === 0 ?  H5Template(record.abilityName) : caclImgSplit(record.story.images))
                 recordList.push(H4Template('行为描述:'))
-                recordList.push(H4Template(record.behave))
+                recordList.push(H5Template(record.behave))
                 recordList.push(H4Template('进阶能力描述:'))
-                recordList.push(H4Template(record.growth))
+                recordList.push(H5Template(record.growth))
                 recordList.push(H4Template('家园共育策略'))
                 recordList.push(H5ColorTemplate('一日活动', ''))
                 recordList.push(H5Template(record.tactics))
@@ -374,11 +377,10 @@ export const exportPDF = async (_semesterReport) => {
         }).flat(2)
     ]
 
-        return [cover, ava, coverInfo, { text: ' ', margin: [0, 0, 0, 120] },  ...userInfo, ...learnDepthList, brakePage, ...questionList.flat(2 ), brakePage, ...domainDataList]
+    return [cover, ava, coverInfo, { text: ' ', margin: [0, 0, 0, 120] },  ...userInfo, ...learnDepthList, brakePage, ...questionList, brakePage, ...domainDataList]
 
     }
 
-
     const findImgs = () => {
                   
       const question = semesterReport.questionList
@@ -432,10 +434,14 @@ export const exportPDF = async (_semesterReport) => {
               ]
           }
       };
-
     
         pdfContent.content = createPdfContent()
 
+        // 下载进度回调函数
+        const progressCallback = (progress) =>  progressCb(progress)
+
+        pdfMake.setProgressCallback(progressCallback);
+
         if (getRunPlatform() === 'app') {
             pdfMake.createPdf(pdfContent).getBase64().then(data => {
                 callAppFc('saveFile', {
@@ -510,3 +516,252 @@ export const exportTable = (_semesterReport) => {
       
 
 }
+
+
+export const exportClazzPDF = async (clazzData) => {
+    const _clazzData = JSON.parse(JSON.stringify(clazzData))
+    console.log(_clazzData);
+
+    const createPdfContent = () => {
+
+        const H1 = 30
+
+        const H2 = 20
+    
+        const H3 = 14
+    
+        const H4 = 12
+        
+        const H5 = 10
+
+        const H1Template = (text) => (
+            { text: `${text}`, 
+            fontSize: H1, 
+            margin: [0, 40, 0, 4], 
+            color: "blue",  
+            bold: true, 
+            font: 'STXINGKA.TTF', 
+            alignment: 'center'
+        })
+    
+        const H2Template = (text) => (
+            { text: `${text}`, 
+            color: '#1a2c5e',
+            fontSize: H2, 
+            margin: [0, 0, 0, 0],
+            bold: true
+        })
+    
+        const H3Template = (text) => (
+            { text: `${text}`, 
+            fontSize: H3, 
+            color: '#1a2c5e',
+            margin: [0, 16, 0, 0],
+            bold: true
+        })
+
+        const H4Template = (text) => (
+            { text: `${text}`, 
+            fontSize: H4,
+            color: '#1a2c5e',
+            margin: [0, 15, 0, 15],
+        }) 
+
+        const H5Template = (text) => (
+            { text: `${text}`, 
+            fontSize: H5,
+            margin: [0, 15, 0, 4],
+            lineHeight: 1
+        }) 
+
+        const emptyTemplate = () => (
+            { text: ``, 
+            fontSize: H5,
+            margin: [0, 10, 0, 20],
+            lineHeight: 1
+        }) 
+        
+        const imgTemplate = (url) => ({image: url, width: 500, alignment: 'center', margin: [0, 20, 0, 0] } )
+    
+        const cover =  {
+            image: 'cover',
+            width: 450,
+            alignment: 'center'
+        }
+          
+        const brakePage = {
+            text: " ",
+            margin: [0, 0, 0, 700]
+        }
+
+        const ava = {
+            image: _clazzData.baseData.schoolImage ,
+            // image: 'https://app-resources-luojigou.luojigou.vip/39602485-6404-430f-91d0-841653363d07_828x622.png',
+            // image: 'https://img.luojigou.vip/FjYlziq_45SihPiMNhWvVUC-9d6t.jpeg',
+            width: 200,
+            alignment: 'center',
+            margin: [0, 50, 0, 0]
+        }
+
+        const coverInfo = {
+            text: `${_clazzData.baseData.schoolName}\n班级:${_clazzData.baseData.className}\n成长阶段:${_clazzData.baseData.startTimeFormat}-${_clazzData.baseData.endTimeFormat}`,
+            alignment: 'center',
+            margin: [0, 50, 0, 200],
+            // font: 'STXINGKA.TTF',
+            fontSize: 26
+        }
+
+
+        var docDefinition = {
+            content: [
+              {
+                layout: 'lightHorizontalLines', // optional
+                table: {
+                  // headers are automatically repeated if the table spans over multiple pages
+                  // you can declare how many rows should be treated as headers
+                  headerRows: 1,
+                  widths: [ '*', 'auto', 100, '*' ],
+          
+                  body: [
+                    [ 'First', 'Second', 'Third', 'The last one' ],
+                    [ 'Value 1', 'Value 2', 'Value 3', 'Value 4' ],
+                    [ { text: 'Bold value', bold: true }, 'Val 2', 'Val 3', 'Val 4' ]
+                  ]
+                }
+              }
+            ]
+          };
+          
+        const coverTitle = (text) => (
+            { text: `${text}`, 
+            fontSize: 38, 
+            margin: [30, -120, 0, 4], 
+            color: "blue",  
+            bold: true,
+            alignment: 'left'
+        })
+
+
+        const coverTitle2 = (text) => (
+            { text: `${text}`, 
+            fontSize: 30, 
+            margin: [30, 0, 0, 4], 
+            color: "orange",  
+            bold: true,
+            alignment: 'left'
+        })
+
+        return [
+            cover, 
+            coverTitle('智慧观察评价报告'),
+            coverTitle2('班级版'),
+            ava,
+            coverInfo,
+            // 第二页
+            H1Template("综合发展情况"),
+            H2Template("评估结果分析"),
+            H3Template('班级基本概况'),
+            H4Template(`思维芯小一班共有学生${_clazzData?.baseData.totalStudent}名,在${_clazzData?.baseData.startTimeFormat}—${_clazzData?.baseData.endTimeFormat}时间内,
+          教师观察记录${_clazzData?.baseData.records}篇,观察到学生${_clazzData?.baseData.done}名,未观察到学生${_clazzData?.baseData.notDone}名,分别统计为:`),
+            imgTemplate(_clazzData.basePng),
+            emptyTemplate(),
+            H2Template("记录场景分布"),
+            
+            imgTemplate(_clazzData.clazzDomainChart),
+            emptyTemplate(),
+            H2Template("观察领域分布"),
+            emptyTemplate(),
+            imgTemplate(_clazzData.clazzSceneChart),
+            emptyTemplate(),
+            H2Template("学生各领域表现阶段汇总"),
+            emptyTemplate(),
+            imgTemplate(_clazzData.studentPng),
+            emptyTemplate(),
+            H2Template("数据说明"),
+            emptyTemplate(),
+            H4Template(dataDesc)
+        ]   
+    }
+ 
+
+    async function generate() {
+
+        const pdfContent = {
+            pageBreakBefore: false,
+            content: null,
+            images: {
+                [ _clazzData.baseData.schoolImage]:  _clazzData.baseData.schoolImage,
+                // ...findImgs(),
+                cover:  "https://img.luojigou.vip/empty-cover.png",
+                deg: "https://img.luojigou.vip/FvpoQUenV0XVm1U_DxONnfBIq0nR.png",
+                dot: "https://img.luojigou.vip/FjMe6awMO18PnKxhs5mHnmmvUKrt.png",
+            },
+            defaultStyle: {
+                font: 'SourceHanSans.ttf'
+            },
+            pageSize: 'A4',
+            pageMargins: [40, 30, 40, 30],
+            footer: function (currentPage, pageCount) {
+                return [
+                    { text: currentPage.toString() + ' / ' + pageCount, alignment: 'center' }
+                ]
+            }
+        };
+      
+          pdfContent.content = createPdfContent()
+
+  
+          // 下载进度回调函数
+        //   const progressCallback = (progress) => progressCb(progress)
+  
+        //   pdfMake.setProgressCallback(progressCallback);
+  
+          if (getRunPlatform() === 'app') {
+              pdfMake.createPdf(pdfContent).getBase64().then(data => {
+                  callAppFc('saveFile', {
+                      fileName: _clazzData.baseData.schoolName + new Date().getTime() + '.pdf', 
+                      data: `data:application/pdf;base64,` + data, 
+                      callback: () => { }}
+                  )
+              })
+          } else {
+              pdfMake.createPdf(pdfContent).download();
+          }
+       
+      }
+
+      const isDev = import.meta.env.MODE === 'development'
+
+       const prefix = isDev ? location.origin + '/src/static' : `https://luojigou.vip/report/static`
+
+      pdfMake.fonts = isDev?  {
+        "SourceHanSans.ttf": {
+            normal: prefix + '/SourceHanSansCN-Regular.otf',
+            bold: prefix + '/SourceHanSansCN-Bold.otf',
+            italics: prefix+ '/SourceHanSansCN-Regular.otf',
+            bolditalics:prefix + '/SourceHanSansCN-Bold.otf'
+        },
+        "STXINGKA.TTF": {
+            normal:  prefix + '/STXINGKA.TTF',
+            bold:  prefix + '/STXINGKA.TTF',
+            italics: prefix + '/STXINGKA.TTF',
+            bolditalics:  prefix + '/STXINGKA.TTF',
+        }
+     } : {
+            "SourceHanSans.ttf": {
+                normal: Regular,
+                bold: Bold,
+                italics: Regular,
+                bolditalics: Bold
+            },
+            "STXINGKA.TTF": {
+                normal: STXINGKA,
+                bold:  STXINGKA,
+                italics: STXINGKA,
+                bolditalics: STXINGKA,
+            }
+        }
+
+      generate()
+    
+}

+ 37 - 0
src/utils/text.js

@@ -0,0 +1,37 @@
+export const dataDesc = `通过系统性观察数据收集,在不同情境和活动中记录幼儿的表现。每次观察记录个领域能力按所处进阶等级(S1-S8),表示幼儿在特定领域的表现水平。
+
+          评估和跟踪幼儿发展
+
+          1.通过详细的进阶能力数据,可以评估和跟踪幼儿在各个领域的进步和发展。
+
+          2.帮助识别每个幼儿的发展阶段,了解其在某一领域的具体表现和能力水平。
+
+          3.提供科学依据,为制定个性化的教育计划和干预策略提供支持。
+
+          4.帮助教师和家长了解幼儿的成长轨迹,及时调整教育方法。
+
+          制定个性化教育计划
+
+          1.通过观察数据,可以准确识别幼儿的优势和需要改进的领域。
+
+          2.为每个幼儿设计个性化的教育方案,针对不同的进阶能力提供适当的支持和挑战。
+
+          提高教育质量
+
+          1.进阶能力数据可以用来评估教育项目和教学方法的有效性。
+
+          2.通过数据分析,可以发现教育过程中的成功经验和不足之处,持续改进教学方法。
+
+          3.提升教育质量,确保幼儿园提供高水平的教育服务。
+
+          4.推动教育实践的创新和发展,为幼儿提供更优质的学习体验。
+
+          促进家园共育
+
+          1.通过分享进阶能力数据,家长可以更好地了解孩子的表现和进步。
+
+          2.增强家长对幼儿园教育工作的理解和支持,促进家园共育的有效实施。
+
+          3.形成家园教育合力,共同支持幼儿的成长和发展。
+
+          4.提高家长参与度,增强家长和教师之间的沟通与合作。`

+ 432 - 23
src/views/customize/ClazzReport.vue

@@ -4,17 +4,20 @@
 
     <!-- 封面 -->
     <div class="rd-info" :style="{ backgroundImage: `url(${getImageUrl('semester-bg')})`, backgroundColor: '#fff' }" >
-      <div class="rd-info-cover" :style="{ backgroundImage: `url(${getImageUrl('cover')})`}"></div>
+      <div class="rd-info-cover" :style="{ backgroundImage: `url(${getImageUrl('empty-cover')})`}">
+        <div style="font-weight:600;font-size: 28px;color: rgb(35, 60, 213)" >智慧观察评价报告</div>
+        <div style="font-weight:600;font-size: 22px;color: rgb(240, 137, 49)">班级版</div>
+      </div>
       <div class="rd-info-bg" :style="{ backgroundImage: `url(${getImageUrl('baby-cover')})`}">
           <!-- 头像 -->
-          <!-- <img class="rd-info-bg-headImg"   /> -->
+          <img class="rd-info-bg-headImg"  :src="baseData?.schoolImage" />
           <div class="rd-info-bg-info">
-            <div class="rd-info-bg-info-name">姓名:困困</div>
-            <div class="rd-info-bg-info-class">班级:思维芯小一班</div>
-            <div class="rd-info-bg-info-stage">成长阶段:2024.03.15-2024.07.10</div>
+            <div class="rd-info-bg-info-name">{{baseData?.schoolName}}</div>
+            <div class="rd-info-bg-info-class">班级:{{baseData?.className}}</div>
+            <div class="rd-info-bg-info-stage">成长阶段:{{dayjs(baseData?.startTime).format('YYYY.MM.DD')}}-{{dayjs(baseData?.endTime).format('YYYY.MM.DD')}}</div>
           </div>
       </div>
-    </div>  
+    </div>
 
     <!-- 评估结果分析 -->
     <div class="report-result" >
@@ -26,12 +29,21 @@
           班级基本概况
         </div>
         <div class="base-content" >
-          思维芯小一班共有学生28名,在2024年02月12日— —2024年7月31日时间内,教师观察记录20篇,观察到学生28名,未观察到学生0名,分别统计为:
+          思维芯小一班共有学生{{baseData?.totalStudent}}名,在{{baseData?.startTimeFormat}}—{{baseData?.endTimeFormat}}时间内,
+          教师观察记录{{baseData?.records}}篇,观察到学生{{baseData?.done}}名,未观察到学生{{baseData?.notDone}}名,分别统计为:
         </div>
-        <div class="base-table" >
-          <div class="base-table-item" v-for="item in clazzBase" :key="item.title">
+        <div class="base-table" id="capture" >
+          <div class="base-table-item">
             <div class="title" >记录总数</div>
-            <div class="value" >20</div>
+            <div class="value" >{{baseData?.records}}篇</div>
+          </div>
+          <div class="base-table-item">
+            <div class="title" >行为记录</div>
+            <div class="value" >{{baseData?.behavior}}次</div>
+          </div>
+          <div class="base-table-item">
+            <div class="title" >问题探究</div>
+            <div class="value" >{{baseData?.question}}次</div>
           </div>
         </div>
       </div>
@@ -45,14 +57,124 @@
           </div>
         </div>
       </div>
+      <div class="report-clazz-base" >
+        <div class="base-title" >
+          观察领域分布
+        </div>
+        <div class="observe-scene" >
+          <div class="line">
+            <div ref="observeLine" style="width: 100%;height: 100%;" > </div>
+          </div>
+        </div>
+      </div>
+      <div class="report-clazz-base" >
+        <div class="base-title" >
+          学生各领域表现阶段汇总
+        </div>
+        <div class="total-scene" >
+          <!-- <div class="total-table" >
+            <div  class="total-table-item"  >
+              <div
+                :class="['total-table-item-cell', index === 0 ? 'total-table-item-title border-white' : '']"
+                v-for="(item, index) in tableData.name.data" 
+                :key="index" 
+              >
+                {{item}}
+              </div>
+            </div>
+            <div  class="total-table-item"  >
+              <div
+                :class="['total-table-item-cell', index === 0 ? 'total-table-item-title border-white' : '']"
+                v-for="(item, index) in tableData.record.data" 
+                :key="index"
+              >
+                {{item}}
+              </div>
+            </div>
+            <div  class="total-table-item"  >
+              <div class="performances-title border-white">
+                观察进阶
+              </div>
+              <div class="performances-item-content" >
+                <div class="performances-item-conten-card" v-for="(item, index) in  tableData.performances.data" :key="index">
+                  <div  :class="['total-table-item-cell', index === 0 ? 'border-white performances-subtitle': '']"  v-for="(_, index) in item" :key="index"  >
+                    {{_}}
+                  </div>
+                </div>
+              </div>
+            </div>
+          </div> -->
+          <div class="total-table-small" id="htmlToPng" >
+            <div  class="total-table-item"  >
+              <div
+                :class="['total-table-item-cell', index === 0 ? 'total-table-item-title border-white' : '']"
+                v-for="(item, index) in tableData.name.data" 
+                :key="index" 
+              >
+                {{item}}
+              </div>
+            </div>
+            <div  class="total-table-item"  >
+              <div
+                :class="['total-table-item-cell', index === 0 ? 'total-table-item-title border-white' : '']"
+                v-for="(item, index) in tableData.record.data" 
+                :key="index"
+                style="padding: 8px;"
+              >
+                {{item}}
+              </div>
+            </div>
+            <div  class="total-table-item"  >
+              <div class="performances-title border-white">
+                观察进阶
+              </div>
+              <div class="performances-item-content" >
+                <div class="performances-item-conten-card" v-for="(item, index) in  tableData.performances.data" :key="index">
+                  <div :style="{height: index === 0 ? '20px' : '40px'}" :class="['total-table-item-cell-small', index === 0 ? 'border-white performances-subtitle': '']"  v-for="(_, index) in item" :key="index"  >
+                    {{_}}
+                  </div>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+      <div class="report-clazz-base data-desc">
+        <div class="base-title" >
+          数据说明:
+        </div>
+        <div class="base-content" >
+          {{dataDesc}}
+        </div>
+      </div>
     </div>
+
+    <!-- 导出 -->
+    <export-button  @export="exportReport" />
   </div>
 </template>
 <script lang='ts'  setup >
+// @ts-nocheck
 import HeaderPart from './components/HeaderPart.vue'
-import { getImageUrl } from "@/utils";
+import { getImageUrl, getTestTeachToken } from "@/utils";
 import { ref, onMounted, nextTick } from 'vue'
 import * as echarts from 'echarts'
+import exportButton from '@/components/exportButton.vue'
+import { useClazzStore  } from '@/store/index'
+import { useRoute } from "vue-router";
+import { storeToRefs } from 'pinia'
+import dayjs from 'dayjs'
+import { exportClazzPDF } from '@/utils/exportPdf'
+import html2canvas from 'html2canvas'
+import { dataDesc } from '@/utils/text'
+
+const { p } = useRoute().query;
+
+ const clazzStore = useClazzStore()
+
+ const { baseData, clazzDomainData, clazzSceneData, studentDomainData } = storeToRefs(clazzStore)
+
+ const { getClazzBase, getClazzDomain, getClazzScene, getStudentDomain} = clazzStore
 
  const clazzBase = [
   {title: '记录总数', value: '20'},
@@ -60,7 +182,40 @@ import * as echarts from 'echarts'
   {title: '问题探究', value: '20'},
  ]
 
- const recordLine = ref('')
+ const tableData = ref({
+    name: {
+      key: 'name',
+      title: '姓名',
+      data: ['姓名']
+    },
+    record: {
+      key: 'record',
+      title: '行为记录',
+      data: ['行为记录']
+    },
+    performances: {
+      key: 'performances',
+      title: '观察进阶',
+      data: {
+        's1': ['s1'],
+        's2': ['s2'],
+        's3': ['s3'],
+        's4': ['s4'],
+        's5': ['s5'],
+        's6': ['s6'],
+        's7': ['s7'],
+        's8': ['s8']
+      }
+    }
+ })
+
+ const recordLine = ref()
+
+ const observeLine = ref()
+
+ const recordChart = ref()
+
+ const observeChart = ref()
 
  const echartsOption = {
   tooltip: {
@@ -70,9 +225,10 @@ import * as echarts from 'echarts'
     }
   },
   grid: {
-    left: '3%',
-    right: '4%',
-    bottom: '3%',
+    top: '',
+    left: '1%',
+    right: '3%',
+    bottom: '1%',
     containLabel: true
   },
   xAxis: {
@@ -81,7 +237,7 @@ import * as echarts from 'echarts'
   },
   yAxis: {
     type: 'category',
-    data: ['教师', '操场', '公共区', '走廊', '饲养区', '沙水池']
+    data: ['教师', '操场', '公共区', '走廊', '饲养区', '沙水池'],
   },
   series: [
     {
@@ -93,18 +249,106 @@ import * as echarts from 'echarts'
   ]
  };
 
+ const initRcordLineCharts = (options: any) => {
+  recordChart.value = echarts.init(recordLine.value);
+  recordChart.value.setOption(options);
+ }
+
+ const initObserveChartLineCharts = (options: any) => {
+  observeChart.value = echarts.init(observeLine.value);
+  observeChart.value.setOption(options);
+ }
+
+ const htmlToPng = () => {
+  return new Promise((resolve) => {
+    html2canvas(document.querySelector("#capture")!).then(canvas => {
+      resolve(canvas.toDataURL(''))
+    });
+  })
+ }
 
- const initRcordLineCharts = () => {
+ const studentChartHtmlToPng = () => {
+  return new Promise((resolve) => {
+    const style = document.createElement('style');
+    document.head.appendChild(style);
+    style.sheet?.insertRule('body > div:last-child img { display: inline-block; }');
 
+    html2canvas(document.querySelector("#htmlToPng")!).then(canvas => {
+      style.remove();
+      resolve(canvas.toDataURL(''))
+    });
+  })
  }
 
+ 
+
+
+ const exportReport = () => {
+  nextTick(async () => {
+    console.log('recordChart:', recordChart);
+
 
- onMounted(() => {
-  nextTick(() => {
-    initRcordLineCharts()
+    const $par = {
+      baseData: baseData.value,
+      clazzDomainData: clazzDomainData.value, 
+      clazzSceneData: clazzSceneData.value, 
+      studentDomainData: studentDomainData.value,
+      clazzDomainChart: recordChart.value.getDataURL(),
+      clazzSceneChart: observeChart.value.getDataURL(),
+      basePng: await htmlToPng(),
+      studentPng: await studentChartHtmlToPng(),
+    }
+    exportClazzPDF($par)
   })
- })
 
+}
+
+
+ onMounted( async () => {
+  await getAppToken()
+  // await getTestTeachToken()
+  const $par = JSON.parse(decodeURIComponent(atob(p)))
+  // const $par = {"classIds":["1655822889577734146"],"gradeSemesterIds":["00","11","01","-10","-11","20","10","21"],"schoolId":"1304705665728421889","teacherIds":["1450638512771702786","1436187006844682241","1304705367924449281","1711187837184294914","1800414891962654721","1778693522847088641"]}
+  getClazzBase($par)
+  getClazzDomain($par).then(() => {
+
+   nextTick(() => {
+    console.log('clazzDomainData:', clazzDomainData?.value);
+    
+    const $options = Object.assign({}, echartsOption)
+     //@ts-ignore
+    $options.yAxis.data =  clazzDomainData.value[0]?.domains.map(item => item.name)
+     //@ts-ignore
+    $options.series[0].data =  clazzDomainData.value[0]?.domains.map(item => item.count)
+    initRcordLineCharts($options)
+   })
+  })
+  getClazzScene($par).then(() => {
+    nextTick(() => {
+      const $options = Object.assign({}, echartsOption)
+       //@ts-ignore
+      $options.yAxis.data =  clazzSceneData.value[0]?.scenes.map(item => item.name)
+       //@ts-ignore
+      $options.series[0].data =  clazzSceneData.value[0]?.scenes.map(item => item.count)
+      initObserveChartLineCharts($options)
+    })
+  })
+  getStudentDomain($par).then(() => {
+     //@ts-ignore
+    tableData.value.name.data.push(...studentDomainData.value.map(item => item.babyName))
+     //@ts-ignore
+    tableData.value.record.data.push(...studentDomainData.value.map(item => item.behavior))
+            //@ts-ignore
+    studentDomainData.value.forEach( item => {
+      const keys = Object.keys(item).filter(key => key.includes('s'))
+      keys.forEach( key => {
+        //@ts-ignore
+        tableData.value.performances.data[key].push(item[key])
+      })
+    })
+    
+  })
+ })
 
 </script>
 <style lang='scss' scoped >
@@ -125,6 +369,12 @@ import * as echarts from 'echarts'
       width: 307px;
       height: 192px;
       background-size: 100% 100%;
+      display: flex;
+      flex-direction: column;
+      align-items: flex-start;
+      justify-content: center;
+      padding-top: 40px;
+      padding-left: 20px;
     }
     .rd-info-bg {
       width: 228px;
@@ -194,7 +444,8 @@ import * as echarts from 'echarts'
           height: 40px;
           display: flex;
           background-color: rgb(113, 207, 224);
-          font-size: 14px;
+          font-size: 12px;
+          color: #fff;
           .title {
             width: 50%;
             display: flex;
@@ -212,9 +463,167 @@ import * as echarts from 'echarts'
           }
         }
       }
-
       .record-scene {
+        .line {
+          width: 100%;
+          height: 200px;
+        }
+      }
+      .observe-scene {
+        .line {
+          width: 100%;
+          height: 200px;
+        }
+      }
+      .total-scene {
+        display: flex;
+        .total-table {
+          width: 100%;
+          overflow: hidden;
+          overflow-x: scroll;
+           display: flex;
+           .total-table-item {
+            .performances-title {
+              width: 100%;
+              height: 20px;
+              background-color: rgb(51, 179, 92);
+              color: #fff;
+              display: flex;
+              justify-content: center;
+              align-items: center;
+              border-bottom: 1px solid #fff !important;
+            }
+            .performances-item-content {
+              display: flex;
+              border-top: 1px solid #fff !important;
+              .performances-item-conten-card:last-child {
+                .total-table-item-cell {
+                  border-right: 1px solid #a19d9d;
+                }
+              }
+            }
+            .performances-subtitle {
+              width: 100%;
+              background-color: rgb(51, 179, 92);
+              height: 20px !important;
+              color: #fff;
+              box-sizing: border-box;
+            }
+            .total-table-item-cell {
+              width: 100px;
+              height: 40px;
+              display: flex;
+              flex-direction: column;
+              justify-content: center;
+              align-items: center;
+              border-bottom: 1px solid #a19d9d;
+              border-left: 1px solid #a19d9d;
+              box-sizing: border-box;
+              margin-top: -1px;
+            }
+            .total-table-item-title {
+              width: 100px;
+              height: 40px;
+              background-color: rgb(51, 179, 92);
+              color: #fff;
+              display: flex;
+              justify-content: center;
+              align-items: center;
+              box-sizing: border-box;
+            }
+            .border-white {
+              border-left: 1px solid #fff;
+              border-bottom: 1px solid #fff;
+              border-top: none;
+              box-sizing: border-box;
+              margin-top: 0px;
+            }
+          }
+          
+        }
+        .total-table-small {
+          width: 100%;
+          overflow: hidden;
+          overflow-x: scroll;
+          display: flex;
+          font-size: 10px;
+           .total-table-item {
+            .performances-title {
+              width: 100%;
+              height: 20px;
+              background-color: rgb(51, 179, 92);
+              color: #fff;
+              display: flex;
+              justify-content: center;
+              align-items: center;
+            }
+            .performances-item-content {
+              display: flex;
+              .performances-item-conten-card {
+                margin-top: 1px;
+                .total-table-item-cell-small {
+                  width: 31px;
+                  // height: 40px;
+                  display: flex;
+                  flex-direction: column;
+                  justify-content: center;
+                  align-items: center;
+                  border-bottom: 1px solid #a19d9d;
+                  border-left: 1px solid #a19d9d;
+                  box-sizing: border-box;
+                  margin-top: -1px;
+                  text-align: center;
+                }
+
+              }
+              .performances-item-conten-card:last-child {
+                .total-table-item-cell-small {
+                  border-right: 1px solid #a19d9d;
+                }
+              }
+            }
+            .performances-subtitle {
+              width: 100%;
+              background-color: rgb(51, 179, 92);
+              color: #fff;
+              box-sizing: border-box;
+            }
+            .total-table-item-cell {
+              width: 38px;
+              height: 40px;
+              display: flex;
+              flex-direction: column;
+              justify-content: center;
+              align-items: center;
+              border-bottom: 1px solid #a19d9d;
+              border-left: 1px solid #a19d9d;
+              box-sizing: border-box;
+              margin-top: -1px;
+              text-align: center;
+            }
 
+            .total-table-item-title {
+              width: 38px;
+              height: 40px;
+              background-color: rgb(51, 179, 92);
+              color: #fff;
+              display: flex;
+              justify-content: center;
+              align-items: center;
+              box-sizing: border-box;
+              white-space: normal;
+              text-align: center;
+            }
+            .border-white {
+              border-left: 1px solid #fff !important;
+              border-bottom: 1px solid #fff !important;
+              border-top: none;
+              box-sizing: border-box;
+              margin-top: 0px;
+            }
+          }
+          
+        }
       }
     }
   }

+ 18 - 3
src/views/customize/SemesterReport.vue

@@ -303,11 +303,15 @@
       <img :src="getImageUrl('small_triangle')" alt="" class="rd-modal-logo" />
       <div class="rd-modal-item flex-center" @click="share">分享</div>
       <div v-if="!semesterReport.sendReport && !isParent" class="rd-modal-item flex-center" @click="send">发送家长</div>
-      <div  class="rd-modal-item flex-center" @click="exportReport">导出报告</div>
+      <!-- <div  class="rd-modal-item flex-center" @click="exportReport">导出报告</div> -->
     </div>
   </div>
 
   <ShareModal :show="shareShow" @close="shareShow = false" />
+
+  <!-- 导出 -->
+  <export-button @export="exportReport" :progress="progressRef" :key="progressRef" />
+
 </template>
 
 <script setup lang="ts">
@@ -323,6 +327,7 @@ import { exportPDF } from '@/utils/exportPdf'
 
 import radarCharts from  '@/components/radarCharts.vue'
 import {nextTick } from 'vue'
+import exportButton from '@/components/exportButton.vue'
 
 const {
   b: babyId,
@@ -467,9 +472,10 @@ function getTargetStyle(index: number) {
   return style;
 }
 
+const progressRef = ref('')
 
 const exportReport = () => {
- 
+  progressRef.value = '0'
   nextTick(() => {
     // @ts-ignore
     semesterReport.value.radarChartBase64 = radarListDom.value[-1].getDataURL()
@@ -477,7 +483,16 @@ const exportReport = () => {
       // @ts-ignore
       item.radarChartBase64 = radarListDom.value[index].getDataURL()
     })
-    exportPDF(semesterReport.value)
+
+    exportPDF(semesterReport.value, async (progress: number) => {
+
+      const r = (progress * 100).toFixed(0) 
+      if (r === '100') {
+        progressRef.value = ''
+        return
+      }
+      progressRef.value  = r
+    })
   })
 
 }

+ 173 - 112
src/views/customize/SingleReport.vue

@@ -113,6 +113,12 @@
   </div>
 
   <ConfirmJoin :show="showConfirm" @close="showConfirm = false" @confirm="confirm" />
+
+  <exprotWord ref="exprotWordDom" />
+
+   <!-- 导出 -->
+  <export-button v-if="(getIsApp() || getMobileOperatingSystem() === 'iOS')" @export="exportWord" :progress="progressRef"  />
+
 </template>
 
 <script setup lang="ts">
@@ -123,6 +129,10 @@ import HeaderPart from "@/views/customize/components/HeaderPart.vue";
 import { ISingleRecord } from "@/types/customize.d";
 import { computed, onMounted, ref } from "vue";
 import ConfirmJoin from "@/views/customize/components/ConfirmJoin.vue";
+import exportButton from '@/components/exportButton.vue'
+import exprotWord from '@/views/test.vue'
+import { getIsApp } from "@/utils/index.js";
+
 
 const { b: babyId, i: id, p: isParent, o: isMyRoute } = useRoute().query;
 
@@ -133,118 +143,94 @@ const { getSingleRecord, sendSingleRecord, joinSemesterReport } = customizeStore
 const showConfirm = ref(false);
 
 const singleRecord = computed<ISingleRecord>(() => {
-  return customizeStore.singleRecord;
-  // return  {
-  //       "recordId": "1782591626363899905",
-  //       "babyId": "1382262152722042882",
-  //       "sendReport": 0,
-  //       "recordDate": "2024-04-23",
-  //       "teacherName": "雷lEi\uD83C\uDF88",
-  //       "babyNames": [
-  //           "王凯昊"
-  //       ],
-  //       "joinSemester": 1,
-  //       "sceneName": "区域活动",
-  //       "story": {
-  //           "images": [
-  //               "https://app-resources-luojigou.luojigou.vip/3649ac71b59e4fb1b8c025928c70e37e"
-  //           ],
-  //           "videos": ['https://app-resources-luojigou.luojigou.vip/3649ac71b59e4fb1b8c025928c70e37e'],
-  //           "content": "我也不知道在干嘛"
-  //       },
-  //       "domainList": [
-  //           {
-  //               "domainName": "逻辑思维通用领域",
-  //               "abilityList": [
-  //                   {
-  //                       "abilityName": "批判思维能力",
-  //                       "description": {
-  //                           "growth": "S1.在成人的帮助下,幼儿能够模仿其他学生的作品(例:运用自己喜欢的相似的材料)。",
-  //                           "behave": "看到其他小朋友在画上贴贴纸,他也会找喜欢的贴纸。",
-  //                           "behaviours": [],
-  //                           "example": "无",
-  //                           "tactics": "【环创】:\n1.创作机会和条件,支持幼儿自发的艺术表现和创造。\n2.为幼儿提供丰富的便于幼儿取放的材料、工具或物品。\n【师幼】:\n1.和幼儿一起发现并感受周边事物的特征。\n2.倾听幼儿的所见所想,鼓励幼儿大胆表达。",
-  //                           "education": "1.家长可以和幼儿一起玩串珠的游戏,家长示范,幼儿模仿。\n2.家长可以和幼儿一起进行阅读绘本,一起模仿绘本里动物或者人物的动作、声音和表情等。"
-  //                       },
-  //                       levelLabel: 'S10',
-  //                       "descriptionExtra": {
-  //                           "growth": "",
-  //                           "behave": "2024年04月23日,在来来来,王凯昊、朱婷、陈树炎、李佩、雷晴、刘雯雯、孙艺洲、钱小豪、宝宝、田甜小朋友正在来来来活动。\n看到其他小朋友在画上贴贴纸,他也会找喜欢的贴纸。",
-  //                           "behaviours": [],
-  //                           "example": "",
-  //                           "tactics": "来来来12345",
-  //                           "education": ""
-  //                       }
-  //                   },
-  //                   {
-  //                       "abilityName": "很强大的学习能力",
-  //                       "description": {
-  //                           "growth": "S1.在成人的帮助下,幼儿能够模仿其他学生的作品(例:运用自己喜欢的相似的材料)。",
-  //                           "behave": "看到其他小朋友在画上贴贴纸,他也会找喜欢的贴纸。",
-  //                           "behaviours": [],
-  //                           "example": "无",
-  //                           "tactics": "【环创】:\n1.创作机会和条件,支持幼儿自发的艺术表现和创造。\n2.为幼儿提供丰富的便于幼儿取放的材料、工具或物品。\n【师幼】:\n1.和幼儿一起发现并感受周边事物的特征。\n2.倾听幼儿的所见所想,鼓励幼儿大胆表达。",
-  //                           "education": "1.家长可以和幼儿一起玩串珠的游戏,家长示范,幼儿模仿。\n2.家长可以和幼儿一起进行阅读绘本,一起模仿绘本里动物或者人物的动作、声音和表情等。"
-  //                       },
-  //                       levelLabel: 'S10',
-  //                       "descriptionExtra": {
-  //                           "growth": "",
-  //                           "behave": "2024年04月23日,在来来来,王凯昊、朱婷、陈树炎、李佩、雷晴、刘雯雯、孙艺洲、钱小豪、宝宝、田甜小朋友正在来来来活动。\n看到其他小朋友在画上贴贴纸,他也会找喜欢的贴纸。",
-  //                           "behaviours": [],
-  //                           "example": "",
-  //                           "tactics": "来来来12345",
-  //                           "education": ""
-  //                       }
-  //                   },
-  //               ]
-  //           },
-  //           {
-  //               "domainName": "科学探索通用领域",
-  //               "abilityList": [
-  //                   {
-  //                       "abilityName": "科学探索能力",
-  //                       "description": {
-  //                           "growth": "S1.在成人的帮助下,幼儿能够模仿其他学生的作品(例:运用自己喜欢的相似的材料)。",
-  //                           "behave": "看到其他小朋友在画上贴贴纸,他也会找喜欢的贴纸。",
-  //                           "behaviours": [],
-  //                           "example": "无",
-  //                           "tactics": "【环创】:\n1.创作机会和条件,支持幼儿自发的艺术表现和创造。\n2.为幼儿提供丰富的便于幼儿取放的材料、工具或物品。\n【师幼】:\n1.和幼儿一起发现并感受周边事物的特征。\n2.倾听幼儿的所见所想,鼓励幼儿大胆表达。",
-  //                           "education": "1.家长可以和幼儿一起玩串珠的游戏,家长示范,幼儿模仿。\n2.家长可以和幼儿一起进行阅读绘本,一起模仿绘本里动物或者人物的动作、声音和表情等。"
-  //                       },
-  //                       levelLabel: 'S10',
-  //                       "descriptionExtra": {
-  //                           "growth": "",
-  //                           "behave": "2024年04月23日,在来来来,王凯昊、朱婷、陈树炎、李佩、雷晴、刘雯雯、孙艺洲、钱小豪、宝宝、田甜小朋友正在来来来活动。\n看到其他小朋友在画上贴贴纸,他也会找喜欢的贴纸。",
-  //                           "behaviours": [],
-  //                           "example": "",
-  //                           "tactics": "来来来12345",
-  //                           "education": ""
-  //                       }
-  //                   },
-  //                   {
-  //                       "abilityName": "超级无敌的学习能力",
-  //                       "description": {
-  //                           "growth": "S1.在成人的帮助下,幼儿能够模仿其他学生的作品(例:运用自己喜欢的相似的材料)。",
-  //                           "behave": "看到其他小朋友在画上贴贴纸,他也会找喜欢的贴纸。",
-  //                           "behaviours": [],
-  //                           "example": "无",
-  //                           "tactics": "【环创】:\n1.创作机会和条件,支持幼儿自发的艺术表现和创造。\n2.为幼儿提供丰富的便于幼儿取放的材料、工具或物品。\n【师幼】:\n1.和幼儿一起发现并感受周边事物的特征。\n2.倾听幼儿的所见所想,鼓励幼儿大胆表达。",
-  //                           "education": "1.家长可以和幼儿一起玩串珠的游戏,家长示范,幼儿模仿。\n2.家长可以和幼儿一起进行阅读绘本,一起模仿绘本里动物或者人物的动作、声音和表情等。"
-  //                       },
-  //                       levelLabel: 'S10',
-  //                       "descriptionExtra": {
-  //                           "growth": "",
-  //                           "behave": "2024年04月23日,在来来来,王凯昊、朱婷、陈树炎、李佩、雷晴、刘雯雯、孙艺洲、钱小豪、宝宝、田甜小朋友正在来来来活动。\n看到其他小朋友在画上贴贴纸,他也会找喜欢的贴纸。",
-  //                           "behaviours": [],
-  //                           "example": "",
-  //                           "tactics": "来来来12345",
-  //                           "education": ""
-  //                       }
-  //                   },
-  //               ]
-  //           },
-  //       ]
-  // }
+  // return customizeStore.singleRecord;
+  return  {
+        "recordId": "1825715384938938369",
+        "babyId": "1778357748956053505",
+        "sendReport": 0,
+        "recordDate": "2024-08-12",
+        "className": "小二班",
+        "teacherName": "董明辉",
+        "babyNames": [
+            "李悠然"
+        ],
+        "joinSemester": 1,
+        "sceneName": "集体活动",
+        "story": {
+            "images": [
+                "https://app-resources-luojigou.luojigou.vip/274dfcb208504e5298b81943868c7ff3"
+            ],
+            "videos": [],
+            "content": ""
+        },
+        "domainList": [
+            {
+                "domainName": "艺术领域",
+                "abilityList": [
+                    {
+                        "abilityName": "艺术欣赏能力",
+                        "levelLabel": "S3",
+                        "description": {
+                            "growth": "S3.  可以用自己的语言表达对于艺术品的欣赏之情。\n",
+                            "behave": "在活动过程中,幼儿尝试用简单的形容词描绘木偶娃娃,如:”卷卷的头发“等。",
+                            "behaviours": [],
+                            "example": "在活动过程中,幼儿尝试用简单的形容词描绘木偶娃娃,如:”卷卷的头发“等。",
+                            "tactics": "1.鼓励孩子在艺术活动中积极表达自己的感受,提供多种艺术形式的接触,如绘画、音乐和舞蹈。\n2.在阅读或观看图画时,与孩子一起讨论艺术作品的内容,引导他们用自己的话来表达感受。\n3.在家中设置一个艺术角落,经常更换展示的艺术品,鼓励孩子观察并描述他们的感受。",
+                            "education": "1.家长可以和孩子一起参与艺术制作活动,之后讨论每个人的作品,鼓励孩子描述并分享自己的创作过程和喜好。\n2.利用日常生活中的艺术元素,如衣服图案、家具设计,鼓励孩子表达他们的观点和感受。\n3.带孩子去参加各种艺术活动,如儿童剧场、音乐会或美术展览,之后让孩子讲述他们的经历和感受。"
+                        },
+                        "descriptionExtra": {
+                            "growth": "",
+                            "behave": "在观看画作或雕塑后,使用简单的词语表达喜欢或不喜欢,例如说“漂亮”、“好看”或“不喜欢”。\n\n\n\n在参加艺术创作活动后,能够讲述自己的作品是什么,为什么喜欢自己的作品。对于图画书中的插图,能用简单的语言描述自己的感受,如“这很好笑”或“这很可怕”。",
+                            "behaviours": [],
+                            "example": "",
+                            "tactics": "",
+                            "education": ""
+                        }
+                    },
+                    {
+                        "abilityName": "艺术创造能力",
+                        "levelLabel": "S1",
+                        "description": {
+                            "growth": "S1.喜欢大胆冒险,对于材料和工具的使用持开放态度,愿意用新型的材料和方法进行尝试。\n",
+                            "behave": "幼儿除了在纸上画出糖葫芦的模样,还愿意和老师一起利用糖浆、不同种类水果制作自己喜欢的糖葫芦。",
+                            "behaviours": [],
+                            "example": "无",
+                            "tactics": "1.提供各种材料和工具,鼓励孩子自由使用,如水彩、指画颜料、粘土等。\n2.组织简单的艺术活动,允许孩子自主选择材料和工具,以培养他们的探索性和创造性。\n3.鼓励孩子在创作过程中尝试新方法,如用手或脚印画画,以提升他们对艺术的兴趣和参与度。",
+                            "education": "1.家长可以在家中设立一个艺术角,常备各种易于使用的艺术材料,让孩子随时可以自由创作。\n2.与孩子一起参与创作活动,示范如何使用不同的材料和工具,同时给予孩子足够的空间来自由探索。\n3.鼓励孩子将日常物品用于创作,如用厨房用具或自然物品(如树叶、石头)来制作艺术作品。"
+                        },
+                        "descriptionExtra": {
+                            "growth": "",
+                            "behave": "2024年08月17日,在教室,李悠然小朋友正在画画活动。\n对新介绍的艺术工具表现出好奇,喜欢探索它们的不同用途。在创作过程中,经常进行实验,比如混合颜色、使用不同的涂抹技巧。在成人的引导下,尝试使用不同的材料来创建简单的艺术作品。",
+                            "behaviours": [],
+                            "example": "",
+                            "tactics": "",
+                            "education": ""
+                        }
+                    },
+                    {
+                        "abilityName": "艺术表现能力",
+                        "levelLabel": "S1",
+                        "description": {
+                            "growth": "S1.  表现出对色彩的喜爱,对鲜艳、明亮的颜色比较感兴趣。",
+                            "behave": "幼儿喜欢色彩鲜艳的服装,愿意参与找相同游戏活动。",
+                            "behaviours": [],
+                            "example": "幼儿喜欢色彩鲜艳的服装,愿意参与找相同游戏活动。",
+                            "tactics": "1.提供多种颜色的画材,如彩色粉笔、水彩笔,让孩子自由选择和使用。\n2.组织彩色的玩具排序游戏,帮助孩子识别和命名不同的颜色。\n3.在阅读或画画时间,与孩子讨论书中的图片或他们的画作中的颜色。",
+                            "education": "1.家长可以在家中创造一个色彩丰富的环境,如挂上彩色的画作或布置彩色的装饰。\n2.与孩子一起做色彩相关的活动,如彩色泡泡水或食物染色实验。\n3.购买或制作彩色的教具和玩具,如彩色的积木和拼图,鼓励孩子通过游戏学习颜色。"
+                        },
+                        "descriptionExtra": {
+                            "growth": "",
+                            "behave": "当看到鲜艳或明亮的颜色时,孩子会表现出明显的兴趣和喜悦。\n\n\n\n对于不同的颜色有明显的偏好,例如喜欢红色的球而不是灰色的。在涂色活动中,表现出兴奋感,喜欢使用多种不同的颜色。",
+                            "behaviours": [],
+                            "example": "",
+                            "tactics": "",
+                            "education": ""
+                        }
+                    }
+                ]
+            }
+        ]
+    }
 });
 
 const isImages = computed(() => {
@@ -306,12 +292,87 @@ function getStyle(index: number, length: number) {
   };
 }
 
+
+function getMobileOperatingSystem() {
+  // @ts-ignore
+    var userAgent = navigator.userAgent || navigator.vendor || window.opera;
+
+    // @ts-ignore
+    if (/iPad|iPhone|iPod/.test(userAgent) && !window.MSStream) {
+        return 'iOS';
+    }
+    // 检查Android设备
+    else if (/android/i.test(userAgent)) {
+        return 'Android';
+    }
+    return 'unknown';
+}
+
+
+
+const exprotWordDom = ref()
+
+const progressRef = ref('')
+
+const exportWord = () => {
+    // progressRef.value = '0'
+  exprotWordDom.value.exportWordDocx({
+    ...singleRecord.value,
+    //@ts-ignore
+    babyNames: singleRecord.value.babyNames.map(item => ({name: item})),
+    //@ts-ignore
+    behaves: singleRecord.value.domainList.map(item => item.abilityList.map(_ => ({behave: _.descriptionExtra.behave}))).flat(Infinity),
+    //@ts-ignore
+    storyImages: singleRecord.value.story.images.map(item => ({url: item})),
+        //@ts-ignore
+    storyContent: singleRecord.value.story.content,
+    //@ts-ignore
+    storyVideo: singleRecord.value.story.videos.length ? singleRecord.value!.story.videos[0] : '',
+    //@ts-ignore
+    domainList: singleRecord.value!.domainList.map(item => ({
+      domainName: item.domainName,
+      //@ts-ignore
+      abilityList: item.abilityList.map( (ability, index) => {
+        return {
+              //@ts-ignore
+          abilityName: '·' + ability.abilityName + (ability.levelLabel ? `(${ability.levelLabel})` : ' '),
+          growth: ability.description.growth,
+        }
+      })
+    })),
+    //@ts-ignore 
+    tactics:  singleRecord.value!.domainList.map(item => item.abilityList.map(_ => ({tactic: _.description.tactics.split('\n').map(_ => ({key: _}))}))).flat(Infinity),
+    //@ts-ignore
+    educations: singleRecord.value.domainList.map(item => item.abilityList.map(_ => ({education: _.description.education.split('\n').map(_ => ({key: _}))}))).flat(Infinity)
+  }, singleRecord.value!.babyNames[0] + '宝贝的观察评估')
+}
+
+
 onMounted(async () => {
   // await getTestTeachToken();
 
   if (typeof babyId === "string" && typeof id === "string") await getSingleRecord(babyId, id);
 });
 
+
+let scrollTimeout: any;
+
+function onScrollStart() {
+  console.log('Scroll started');
+}
+
+function onScrollStop() {
+    console.log('Scroll stopped');
+}
+
+window.addEventListener('scroll', function() {
+    clearTimeout(scrollTimeout);
+    onScrollStart(); // Detect when scrolling starts
+
+    scrollTimeout = setTimeout(onScrollStop, 100); // Adjust timeout as needed to detect when scrolling stops
+}, );
+
+
 /**
  * 行为记录单次报告
  *

+ 45 - 46
src/views/customize/components/HeaderPart.vue

@@ -28,9 +28,9 @@
         {{ singleRecord.joinSemester === 1 ? "已加入报告" : "加入报告" }}
       </div>
       <!-- 导出table 只有单次的行为记录才有 -->
-      <div v-if="props.exportWord && (getIsApp() || getMobileOperatingSystem() === 'iOS')"  class="header-modal-item flex-center" @click="exportWord">
+      <!-- <div v-if="props.exportWord && (getIsApp() || getMobileOperatingSystem() === 'iOS')"  class="header-modal-item flex-center" @click="exportWord">
         导出文件
-      </div>
+      </div> -->
     </div>
   </div>
   <ShareModal :show="shareShow" @close="shareShow = false" />
@@ -46,7 +46,6 @@ import { computed, onUnmounted, ref, watch } from "vue";
 import { ISingleRecord } from "@/types/customize";
 import ShareModal from "@/views/customize/components/ShareModal.vue";
 import exprotWord from '@/views/test.vue'
-import { getIsApp } from "@/utils/index.js";
 
 const customizeStore = useCustomizeStore();
 
@@ -114,20 +113,20 @@ const reportImg = computed(() => {
   return singleRecord.value.joinSemester === 1 ? getImageUrl("joined_report") : getImageUrl("add_report");
 });
 
-function getMobileOperatingSystem() {
-  // @ts-ignore
-    var userAgent = navigator.userAgent || navigator.vendor || window.opera;
-
-    // @ts-ignore
-    if (/iPad|iPhone|iPod/.test(userAgent) && !window.MSStream) {
-        return 'iOS';
-    }
-    // 检查Android设备
-    else if (/android/i.test(userAgent)) {
-        return 'Android';
-    }
-    return 'unknown';
-}
+// function getMobileOperatingSystem() {
+//   // @ts-ignore
+//     var userAgent = navigator.userAgent || navigator.vendor || window.opera;
+
+//     // @ts-ignore
+//     if (/iPad|iPhone|iPod/.test(userAgent) && !window.MSStream) {
+//         return 'iOS';
+//     }
+//     // 检查Android设备
+//     else if (/android/i.test(userAgent)) {
+//         return 'Android';
+//     }
+//     return 'unknown';
+// }
 
 watch(isShow, (val) => {
   if (val) {
@@ -139,35 +138,35 @@ watch(isShow, (val) => {
   }
 });
 
-const exportWord = () => {
-  exprotWordDom.value.exportWordDocx({
-    ...props.record,
-    //@ts-ignore
-    babyNames: props.record.babyNames.map(item => ({name: item})),
-    //@ts-ignore
-    behaves: props.record.domainList.map(item => item.abilityList.map(_ => ({behave: _.descriptionExtra.behave}))).flat(Infinity),
-    //@ts-ignore
-    storyImages: props.record.story.images.map(item => ({url: item})),
-    storyContent: props.record.story.content,
-    //@ts-ignore
-    storyVideo: props.record.story.videos.length ? props.record.story.videos[0] : '',
-    //@ts-ignore
-    domainList: props.record.domainList.map(item => ({
-      domainName: item.domainName,
-      //@ts-ignore
-      abilityList: item.abilityList.map( (ability, index) => {
-        return {
-          abilityName: '·' + ability.abilityName + (ability.levelLabel ? `(${ability.levelLabel})` : ' '),
-          growth: ability.description.growth,
-        }
-      })
-    })),
-    //@ts-ignore 
-    tactics:  props.record.domainList.map(item => item.abilityList.map(_ => ({tactic: _.description.tactics.split('\n').map(_ => ({key: _}))}))).flat(Infinity),
-    //@ts-ignore
-    educations:  props.record.domainList.map(item => item.abilityList.map(_ => ({education: _.description.education.split('\n').map(_ => ({key: _}))}))).flat(Infinity)
-  }, props.record.babyNames[0] + '宝贝的观察评估')
-}
+// const exportWord = () => {
+//   exprotWordDom.value.exportWordDocx({
+//     ...props.record,
+//     //@ts-ignore
+//     babyNames: props.record.babyNames.map(item => ({name: item})),
+//     //@ts-ignore
+//     behaves: props.record.domainList.map(item => item.abilityList.map(_ => ({behave: _.descriptionExtra.behave}))).flat(Infinity),
+//     //@ts-ignore
+//     storyImages: props.record.story.images.map(item => ({url: item})),
+//     storyContent: props.record.story.content,
+//     //@ts-ignore
+//     storyVideo: props.record.story.videos.length ? props.record.story.videos[0] : '',
+//     //@ts-ignore
+//     domainList: props.record.domainList.map(item => ({
+//       domainName: item.domainName,
+//       //@ts-ignore
+//       abilityList: item.abilityList.map( (ability, index) => {
+//         return {
+//           abilityName: '·' + ability.abilityName + (ability.levelLabel ? `(${ability.levelLabel})` : ' '),
+//           growth: ability.description.growth,
+//         }
+//       })
+//     })),
+//     //@ts-ignore 
+//     tactics:  props.record.domainList.map(item => item.abilityList.map(_ => ({tactic: _.description.tactics.split('\n').map(_ => ({key: _}))}))).flat(Infinity),
+//     //@ts-ignore
+//     educations:  props.record.domainList.map(item => item.abilityList.map(_ => ({education: _.description.education.split('\n').map(_ => ({key: _}))}))).flat(Infinity)
+//   }, props.record.babyNames[0] + '宝贝的观察评估')
+// }
 
 function back() {
   props.isMyRoute === "1" ? router.go(-1) : returnAppPage();