mainPage.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. <template>
  2. <!-- 分析报告详情总页面 -->
  3. <div class="analysis_main">
  4. <div :class="['main_header', { 'full_screen': isLianXiao }]" ref="mainHeader">
  5. <div class="header_left">
  6. <span class="back_button" @click="GoBack">
  7. <i class="iconfont icon_return"></i>返回
  8. </span>
  9. <span class="header_title">{{ pageName }}</span>
  10. </div>
  11. <div class="header_right">
  12. <div class="select_list" v-if="isShowFilter">
  13. <el-select style="width: 120px" v-model="filterData[index].value" :placeholder="'请选择' + item.name"
  14. class="select_item" @change="ChangeFilters({ index: index, value: $event })"
  15. v-for="(item, index) in filteredFilterData" :key="index" v-show="item.list.length > 1">
  16. <el-option v-for="option in item.list" :key="option.value" :label="option.label"
  17. :value="option.value"></el-option>
  18. </el-select>
  19. </div>
  20. <template v-if="isShowBtn">
  21. <el-button size="medium" :disabled="!canBtnClick"
  22. @click="downloadWrongQuestions(0)">下载错题本</el-button>
  23. <el-button :disabled="!canBtnClick" type="primary" size="medium"
  24. @click="downloadWrongQuestions(1)">下载个性化提升手册</el-button>
  25. </template>
  26. <template v-if="isShowExportBtns">
  27. <el-button size="medium" @click="ExportQuestions" type="primary" class="export_btn">
  28. <i class="iconfont icon_export"></i> 导出精准提升试题
  29. </el-button>
  30. </template>
  31. <el-button v-if="showOneStudOneCase && activeBtn === pathFourth" size="medium" type="primary"
  32. @click="EditOneStudOneCase" style="width: 96px;">{{
  33. !isEditOneStudOneCase ? `${stuCaseIsEdited ? '编辑' : '开始编辑'}` : `${stuCaseIsEdited ? '重新提交':'提交'}`
  34. }}</el-button>
  35. <el-button v-if="isShowPadfBtn" style="margin-left: 10px;" size="medium" type="primary" :loading="stuPdfLoading" @click="StuDownloadPDF">下载PDF</el-button>
  36. </div>
  37. </div>
  38. <div class="main_content">
  39. <div class="content_right" ref="rightContent" @scroll="ScrollChange">
  40. <div :class="['content_right_scroll', { 'full_screen': isLianXiao }]" ref="contentRightScroll">
  41. <template v-if="isLianXiao">
  42. <div class="mm_body">
  43. <div class="left">
  44. <button class="mm_btn mb_10" :class="{ active: activeBtn === pathOne }"
  45. @click="toPage(pathOne)">成绩分析</button>
  46. <!-- v-if="isShowKnowledgeButtons" -->
  47. <button class="mm_btn mb_10" :class="{ active: activeBtn === pathTwo }"
  48. @click="toPage(pathTwo)">举一反三</button>
  49. <button v-if="!isTotalScore" class="mm_btn "
  50. :class="{ active: activeBtn === pathThree }"
  51. @click="toPage(pathThree)">个人画像</button>
  52. <button class="mm_btn" v-if="showOneStudOneCase"
  53. :class="{ active: activeBtn === pathFourth }"
  54. @click="toPage(pathFourth)">一生一案</button>
  55. </div>
  56. <!-- 520929546006958081 -->
  57. <div class="right">
  58. <div class="page_filter" ref="filterContent" v-if="activeBtn != pathFourth">
  59. <FiltersItem :filtersData="filteredFilterData" @selectItem="ChangeFilters">
  60. </FiltersItem>
  61. </div>
  62. <router-view ref="child" @isPdfDataLoadEnd="isPdfDataLoadEnd"
  63. @closePdfLoading="closePdfLoading"
  64. @StudentCaseIsEdited="StudentCaseIsEdited"></router-view>
  65. </div>
  66. </div>
  67. </template>
  68. <template v-else>
  69. <div class="page_filter" ref="filterContent">
  70. <FiltersItem :filtersData="filteredFilterData" @selectItem="ChangeFilters"></FiltersItem>
  71. </div>
  72. <router-view ref="child" @isPdfDataLoadEnd="isPdfDataLoadEnd"
  73. @closePdfLoading="closePdfLoading"></router-view>
  74. </template>
  75. </div>
  76. </div>
  77. </div>
  78. </div>
  79. </template>
  80. <script>
  81. import FiltersItem from "@/components/FiltersItem_ruoyan.vue";
  82. export default {
  83. components: { FiltersItem },
  84. computed: {
  85. pageName() {
  86. // let examName = this.$store.state.report.examSelectItem.examName;
  87. // if (examName) {
  88. // return examName.split("_")[0];
  89. // }
  90. return this.$store.state.report.examSelectItem.examName
  91. },
  92. updateScrollTop() {
  93. return this.$store.state.report.updateScrollTop;//监听改变滚动条参数
  94. },
  95. isShowKnowledgeButtons() {
  96. // 从环境变量中获取基础URL,判断是否显示按钮
  97. return process.env.VUE_APP_BASE !== 'https://www.k12100.com';
  98. },
  99. // 判断是否为总分科目,用于控制个人画像按钮显示
  100. isTotalScore() {
  101. return this.$store.state.report.isTotalScore;
  102. },
  103. // 动态过滤筛选数据,在个人画像页面隐藏总分选项
  104. filteredFilterData() {
  105. // 检查当前是否在个人画像页面
  106. const isPersonalProfilePage = this.$route.path === '/studentAnalysisReport/reportDetails/personalProfile';
  107. const isPersonalWrongQuestions = this.$route.path === this.pathTwo;
  108. if (!isPersonalProfilePage && !isPersonalWrongQuestions) {
  109. // 非个人画像页面,返回原始数据
  110. return this.filterData;
  111. }
  112. // 深拷贝原始数据,避免直接修改
  113. const updatedFilterData = JSON.parse(JSON.stringify(this.filterData));
  114. // 遍历筛选数据,找到科目名称筛选项
  115. updatedFilterData.forEach(filterItem => {
  116. if (filterItem.type === 'subjectName') {
  117. // 过滤掉总分选项(isTotal为1或subjectGroupType为1的选项)
  118. filterItem.list = filterItem.list.filter(item => item.isTotal !== 1 && item.subjectGroupType !== 1);
  119. }
  120. });
  121. return updatedFilterData;
  122. },
  123. canBtnClick() {
  124. return this.$store.state.question.canDownloanBtnClick;
  125. },
  126. showOneStudOneCase() {//是否开启了一生一案显示
  127. return this.$store.state.report.examSelectItem.oneStudOneCase
  128. }
  129. },
  130. data() {
  131. return {
  132. isShowFilter: false, //是否显示筛选条件
  133. isShowBtn: false, //是否显示下载按钮
  134. isShowExportBtns: false,//是否显示导出精准提升试题按钮
  135. isShowPadfBtn: false,//是否显示下载pdf按钮
  136. isPdfLoadEnd: false,//报告册数据是否加载完成
  137. filterData: [
  138. {
  139. name: "科目名称",
  140. value: '0',
  141. type: "subjectName",
  142. list: [], //选项
  143. }
  144. ],
  145. resizeTimer: null,
  146. activeBtn: this.$route.path,
  147. pathOne: '/studentAnalysisReport/reportDetails/scrolReport',
  148. pathTwo: '/studentAnalysisReport/reportDetails/personalWrongQuestions',
  149. pathThree: '/studentAnalysisReport/reportDetails/personalProfile',//个人画像
  150. pathFourth: '/studentAnalysisReport/reportDetails/studentCase',//一生一案
  151. isLianXiao: false,//是否联校
  152. stuPdfLoading: false,
  153. schoolId: this.$store.state.user.userInfo.id,
  154. isEditOneStudOneCase: false,//是否编辑
  155. stuCaseIsEdited: true,//判断一生一案是否是已经状态
  156. };
  157. },
  158. created() {
  159. this.isShowPadfBtn = this.$route.path == '/studentAnalysisReport/reportDetails/scrolReport';
  160. const schoolType = sessionStorage.getItem('schoolType') || '1'; //1:单校 2:联校
  161. this.isLianXiao = schoolType === '2';
  162. if (this.$store.state.report.filterData.length > 0) {
  163. //有数据
  164. this.filterData = this.$store.state.report.filterData;
  165. }
  166. // 绑定 resize 事件
  167. window.addEventListener('resize', this.HandleWidthChange);
  168. this.$nextTick(() => {
  169. this.SetHeadWidth();
  170. })
  171. },
  172. methods: {
  173. toPage(type) {
  174. this.activeBtn = type;
  175. this.$router.push(type);
  176. },
  177. // 处理宽度变化的逻辑
  178. HandleWidthChange() {
  179. // 防抖:避免窗口 resize 时频繁触发(100ms 内只执行一次)
  180. clearTimeout(this.resizeTimer);
  181. this.resizeTimer = setTimeout(() => {
  182. this.SetHeadWidth();
  183. }, 100);
  184. },
  185. SetHeadWidth() {
  186. if (this.$refs.contentRightScroll) {
  187. let scrollDivWidth = 0;
  188. if (this.isLianXiao) {
  189. scrollDivWidth = this.$refs.contentRightScroll.offsetWidth - 20;//联校
  190. } else {
  191. scrollDivWidth = this.$refs.contentRightScroll.offsetWidth;
  192. }
  193. const headDiv = this.$refs.mainHeader;
  194. headDiv.style.width = `${scrollDivWidth}px`;
  195. }
  196. },
  197. // 筛选事件
  198. ChangeFilters(e) {
  199. this.filterData[e.index].value = e.value;
  200. // 选中科目数据
  201. let courseObj = this.filterData[0].list.find(item => item.value == this.filterData[0].value);
  202. let filterObject = {
  203. examLevel: courseObj.examLevel,//1-联考 2-单校
  204. contrastExamIds: courseObj.contrastExamIds,//多次考试任务对比ID,不包含当前任务ID
  205. examId: courseObj.examId,//考试id
  206. subjectCode: courseObj.subjectCode, //科目code
  207. subjectGroupType: courseObj.subjectGroupType, //是否为组合科目 1为组合科目 0为非组合科目
  208. subjectName: courseObj.subjectName,//科目名称
  209. isTotal: courseObj.isTotal //是否为总分科目 1为总分 0为非总分
  210. };
  211. //设置是否是总分
  212. const isTotal = courseObj.isTotal == 1 || courseObj.subjectGroupType == 1;//1为总分 0为非总分 1为组合科目 0为非组合科目
  213. this.$store.commit("report/set_state", {
  214. key: "isTotalScore",
  215. value: isTotal,
  216. });
  217. localStorage.setItem('reportExamCourseId', courseObj.subjectId);//单科的考试科目id
  218. localStorage.setItem('reportIsTotalScore', isTotal)
  219. this.$store.dispatch("report/UpdateFilterObject", filterObject);
  220. // 如果选择了总分且当前在个人画像页面,跳转到成绩分析页面
  221. if (isTotal && this.$route.path === '/studentAnalysisReport/reportDetails/personalProfile') {
  222. this.toPage(this.pathOne);
  223. }
  224. },
  225. // 滚动条事件
  226. ScrollChange() {
  227. const path = this.$route.path;
  228. let scrollTop = this.$refs.rightContent.scrollTop;
  229. if (path === '/studentAnalysisReport/reportDetails/scrolReport') {
  230. const filterContent = this.$refs.filterContent;
  231. let scrollHeight = 242;
  232. if (filterContent) {
  233. scrollHeight = filterContent.clientHeight;
  234. }
  235. this.isShowFilter = (scrollTop >= scrollHeight);
  236. this.$store.commit("report/set_state", {
  237. key: "isShowFilter",
  238. value: this.isShowFilter,
  239. });
  240. if (this.$refs.child && typeof this.$refs.child.GetScrollTop === 'function') {
  241. this.$refs.child.GetScrollTop(scrollTop);
  242. }
  243. }
  244. if (path === '/studentAnalysisReport/reportDetails/personalWrongQuestions') {
  245. this.isShowBtn = (scrollTop >= 250);
  246. if (this.$refs.child && typeof this.$refs.child.GetScrollTop === 'function') {
  247. this.$refs.child.GetScrollTop(scrollTop);
  248. }
  249. }
  250. if (path === '/studentAnalysisReport/reportDetails/personalProfile') {
  251. this.isShowExportBtns = (scrollTop >= 150);
  252. if (this.$refs.child && typeof this.$refs.child.GetScrollTop === 'function') {
  253. this.$refs.child.GetScrollTop(scrollTop);
  254. }
  255. }
  256. },
  257. //重置滚动条
  258. ResetScroll() {
  259. this.$nextTick(() => {
  260. if (this.$refs.rightContent) {
  261. this.$refs.rightContent.scrollTop = 0;
  262. }
  263. })
  264. },
  265. downloadWrongQuestions(type) {
  266. this.$store.commit("question/SET_VARIANTION", type);
  267. },
  268. //导出精准提升试题
  269. ExportQuestions() {
  270. this.$refs.child.ExportQuestions();
  271. },
  272. //返回按钮点击
  273. GoBack() {
  274. const schoolType = sessionStorage.getItem('schoolType');
  275. if (schoolType == 1) {//单校
  276. this.$router.push("/studentAnalysisReport/list");
  277. } else {//联校
  278. this.$router.push("/jointStudentAnalysisReport/list");
  279. }
  280. },
  281. StuDownloadPDF() {
  282. if (!this.isPdfLoadEnd) {
  283. return this.$message.warning('请稍等报告数据生成中!');
  284. }
  285. this.stuPdfLoading = true;
  286. this.$refs.child.DownloadPdf();
  287. },
  288. isPdfDataLoadEnd() {
  289. this.isPdfLoadEnd = true;
  290. },
  291. closePdfLoading() {
  292. this.stuPdfLoading = false;
  293. },
  294. // 处理筛选数据更新,在个人画像页面隐藏总分选项
  295. handleFilterDataUpdate() {
  296. // 检查当前是否在个人画像页面
  297. const isPersonalProfilePage = this.$route.path === '/studentAnalysisReport/reportDetails/personalProfile';
  298. if (isPersonalProfilePage) {
  299. // 深拷贝原始数据,避免直接修改props
  300. const updatedFilterData = JSON.parse(JSON.stringify(this.filterData));
  301. // 遍历筛选数据,找到科目名称筛选项
  302. updatedFilterData.forEach((filterItem, index) => {
  303. if (filterItem.type === 'subjectName') {
  304. // 过滤掉总分选项(isTotal为1的选项)
  305. const nonTotalItems = filterItem.list.filter(item => item.isTotal !== 1 && item.subjectGroupType !== 1);
  306. // 检查当前选中的是否是总分选项
  307. const currentSelected = filterItem.list.find(item => item.value === filterItem.value);
  308. const isCurrentTotal = currentSelected && (currentSelected.isTotal === 1 || currentSelected.subjectGroupType === 1);
  309. // 如果当前选中的是总分选项且有其他选项,切换到第一个非总分选项
  310. if (isCurrentTotal && nonTotalItems.length > 0) {
  311. this.ChangeFilters({ index, value: nonTotalItems[0].value });
  312. }
  313. }
  314. });
  315. }
  316. },
  317. //编辑一生一案
  318. EditOneStudOneCase() {
  319. this.isEditOneStudOneCase = !this.isEditOneStudOneCase;
  320. this.$refs.child.EditOneStudOneCase(this.isEditOneStudOneCase);
  321. },
  322. StudentCaseIsEdited(id) {
  323. this.stuCaseIsEdited = id ? true : false;
  324. }
  325. },
  326. watch: {
  327. '$route'(to, from) {
  328. this.ResetScroll();//重置滚动条
  329. this.handleFilterDataUpdate();
  330. },//路由变化 重置页面滚动条位置
  331. updateScrollTop: {
  332. handler(newVal) {
  333. this.ResetScroll();//重置滚动条
  334. },
  335. },
  336. // 监听筛选数据变化,动态过滤总分选项
  337. filterData: {
  338. deep: true,
  339. handler() {
  340. this.handleFilterDataUpdate();
  341. }
  342. }
  343. },
  344. beforeUnmount() {
  345. // 解绑事件,避免内存泄漏
  346. window.removeEventListener('resize', this.HandleWidthChange);
  347. // 清除计时器
  348. clearTimeout(this.resizeTimer);
  349. }
  350. };
  351. </script>
  352. <style scoped lang="scss">
  353. .mm_body {
  354. display: flex;
  355. width: 100%;
  356. justify-content: space-between;
  357. .left {
  358. width: 170px;
  359. padding: 20px;
  360. border-radius: 10px;
  361. background-color: #ffffff;
  362. height: fit-content;
  363. box-sizing: border-box;
  364. position: sticky;
  365. top: 0px;
  366. .mb_10 {
  367. margin-bottom: 10px;
  368. }
  369. }
  370. .right {
  371. width: calc(100% - 180px);
  372. }
  373. }
  374. .export_btn {
  375. display: flex;
  376. align-items: center;
  377. gap: 5px;
  378. width: 152px;
  379. height: 36px;
  380. line-height: 36px;
  381. background: #2E64FA;
  382. color: white;
  383. border: none;
  384. border-radius: 4px;
  385. font-size: 14px;
  386. cursor: pointer;
  387. padding: 0;
  388. transition: all 0.3s ease;
  389. &:hover {
  390. background: #5883fb;
  391. }
  392. &:active {
  393. background: #2E64FA;
  394. }
  395. .iconfont {
  396. width: 14px;
  397. margin-left: 10px;
  398. }
  399. }
  400. </style>