QuestionGroupStruct.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  1. <template>
  2. <div class="question-group-struct">
  3. <div class="struct-set">
  4. <el-checkbox v-model="useClassify" @change="setChange"
  5. >指定文件夹抽题</el-checkbox
  6. >
  7. <el-checkbox v-model="useDifficult" @change="setChange"
  8. >按难度抽题</el-checkbox
  9. >
  10. <el-checkbox v-model="useProperty" @change="setChange"
  11. >按课程属性抽题</el-checkbox
  12. >
  13. <el-select
  14. v-if="useProperty"
  15. v-model="curCoursePropertyId"
  16. class="margin-left-10"
  17. size="small"
  18. @change="buildDataList"
  19. >
  20. <el-option
  21. v-for="item in propertyInfo"
  22. :key="item.id"
  23. :label="item.name"
  24. :value="item.id"
  25. ></el-option>
  26. </el-select>
  27. </div>
  28. <div v-if="!USE_NONE_SET" class="struct-box">
  29. <div v-if="useClassify" class="struct-folder">
  30. <el-tree
  31. ref="folderTree"
  32. class="folder-tree"
  33. :data="classifyTree"
  34. node-key="id"
  35. default-expand-all
  36. :expand-on-click-node="false"
  37. check-on-click-node
  38. show-checkbox
  39. check-strictly
  40. :props="treeProps"
  41. @check-change="checkChange"
  42. ></el-tree>
  43. </div>
  44. <div class="struct-prop">
  45. <el-table
  46. :data="tableData"
  47. :tree-props="tableTreeProps"
  48. row-key="id"
  49. border
  50. max-height="400"
  51. >
  52. <el-table-column label="分类" prop="name">
  53. <template slot-scope="scope">
  54. <i
  55. v-if="scope.row.isClassify"
  56. class="icon icon-files-act margin-right-5"
  57. ></i>
  58. <span
  59. v-if="scope.row.isClassify && scope.row.id === '0'"
  60. class="inline-middle"
  61. >根目录</span
  62. >
  63. <span v-else class="inline-middle">{{ scope.row.name }}</span>
  64. </template>
  65. </el-table-column>
  66. <el-table-column
  67. v-if="!useDifficult || USE_ONLY_DIFFICULT"
  68. label="数量"
  69. width="150"
  70. >
  71. <template slot-scope="scope">
  72. <el-input-number
  73. v-model="scope.row.selectCount"
  74. size="mini"
  75. :step="1"
  76. :min="0"
  77. :max="scope.row.questionCount"
  78. :controls="false"
  79. :precision="0"
  80. step-strictly
  81. @change="selectCountChange"
  82. ></el-input-number>
  83. <span class="inline-middle">/ {{ scope.row.questionCount }}</span>
  84. </template>
  85. </el-table-column>
  86. <el-table-column
  87. v-for="(dinfo, dindex) in difficultDistributeInfo"
  88. v-else
  89. :key="dindex"
  90. :label="dinfo"
  91. width="150"
  92. >
  93. <template
  94. v-if="
  95. scope.row.difficultDistributeInfo &&
  96. scope.row.difficultDistributeInfo.length &&
  97. !(scope.row.useClassify && useProperty)
  98. "
  99. slot-scope="scope"
  100. >
  101. <el-input-number
  102. v-model="scope.row.difficultDistributeInfo[dindex].selectCount"
  103. size="mini"
  104. :step="1"
  105. :min="0"
  106. :max="scope.row.difficultDistributeInfo[dindex].questionCount"
  107. :controls="false"
  108. :precision="0"
  109. step-strictly
  110. @change="selectCountChange"
  111. ></el-input-number>
  112. <span class="inline-middle"
  113. >/
  114. {{
  115. scope.row.difficultDistributeInfo[dindex].questionCount
  116. }}</span
  117. >
  118. </template>
  119. </el-table-column>
  120. </el-table>
  121. </div>
  122. </div>
  123. </div>
  124. </template>
  125. <script>
  126. import { questionGroupStructListApi } from "../api";
  127. import { classifyTreeApi } from "../../question/api";
  128. import { calcSum, deepCopy } from "@/plugins/utils";
  129. export default {
  130. name: "QuestionGroupStruct",
  131. props: {
  132. filterData: {
  133. type: Object,
  134. default() {
  135. return {
  136. courseId: "",
  137. sourceDetailId: "",
  138. };
  139. },
  140. },
  141. dataSource: {
  142. type: Object,
  143. default: null,
  144. },
  145. },
  146. data() {
  147. return {
  148. useClassify: false,
  149. useDifficult: false,
  150. useProperty: false,
  151. selectedFolderIds: [],
  152. classifyTree: [
  153. {
  154. id: 0,
  155. parent: null,
  156. name: "根目录",
  157. children: [],
  158. },
  159. ],
  160. treeProps: {
  161. label: "name",
  162. },
  163. originList: [],
  164. dataList: [],
  165. tableData: [],
  166. tableTreeProps: {
  167. children: "propertyDistributeInfo",
  168. },
  169. difficultDistributeInfo: [],
  170. curCoursePropertyId: "",
  171. propertyInfo: [],
  172. sourceDataMap: {},
  173. };
  174. },
  175. computed: {
  176. USE_ONLY_DIFFICULT() {
  177. return !this.useClassify && this.useDifficult && !this.useProperty;
  178. },
  179. USE_ONLY_PROPERTY() {
  180. return !this.useClassify && !this.useDifficult && this.useProperty;
  181. },
  182. USE_NONE_SET() {
  183. return !this.useClassify && !this.useDifficult && !this.useProperty;
  184. },
  185. SOURCE_REVIEW() {
  186. return !!this.dataSource;
  187. },
  188. },
  189. watch: {
  190. filterData: {
  191. deep: true,
  192. handler(val, oldval) {
  193. if (JSON.stringify(val) !== JSON.stringify(oldval)) {
  194. this.resetDataList();
  195. }
  196. },
  197. },
  198. },
  199. async mounted() {
  200. if (this.dataSource) {
  201. this.parseSourceDataMap();
  202. this.useProperty = !!this.dataSource.useProperty;
  203. this.useClassify = !!this.dataSource.useClassify;
  204. this.useDifficult = !!this.dataSource.useDifficult;
  205. if (this.useClassify)
  206. this.selectedFolderIds = this.dataSource.classifyIdList || [];
  207. }
  208. await this.resetDataList();
  209. await this.getClassifyTree();
  210. if (this.selectedFolderIds.length) {
  211. this.$nextTick(() => {
  212. this.$refs.folderTree.setCheckedKeys(this.selectedFolderIds);
  213. });
  214. }
  215. },
  216. methods: {
  217. setChange() {
  218. if (!this.useClassify) this.selectedFolderIds = [];
  219. this.resetDataList();
  220. },
  221. checkChange() {
  222. this.selectedFolderIds = this.$refs.folderTree.getCheckedKeys();
  223. this.resetDataList();
  224. },
  225. async getClassifyTree() {
  226. const res = await classifyTreeApi();
  227. this.classifyTree[0].children = res.data || [];
  228. },
  229. async resetDataList() {
  230. if (!this.useClassify && !this.useDifficult && !this.useProperty) {
  231. this.originList = [];
  232. this.dataList = [];
  233. this.tableData = [];
  234. return;
  235. }
  236. const res = await this.getOriginList().catch(() => {});
  237. if (!res) return;
  238. if (this.useProperty) {
  239. this.parsePropertyInfo();
  240. }
  241. this.buildDataList();
  242. },
  243. async getOriginList() {
  244. if (this.useClassify && !this.selectedFolderIds.length) {
  245. this.$message.error("请选择选择文件夹!");
  246. return Promise.reject();
  247. }
  248. let data = {
  249. ...this.filterData,
  250. useDifficult: this.useDifficult,
  251. useProperty: this.useProperty,
  252. };
  253. if (this.useClassify && this.selectedFolderIds.length)
  254. data.classifyIdList = this.selectedFolderIds.join();
  255. const res = await questionGroupStructListApi(data);
  256. this.originList = res.data || [];
  257. return true;
  258. },
  259. parsePropertyInfo() {
  260. if (!this.useProperty) {
  261. this.propertyInfo = [];
  262. return;
  263. }
  264. let coursePropertyIds = [],
  265. propertyInfo = [];
  266. this.originList.forEach((item) => {
  267. if (
  268. !item.coursePropertyDistributeInfo ||
  269. !item.coursePropertyDistributeInfo.length
  270. )
  271. return;
  272. item.coursePropertyDistributeInfo.forEach((prop) => {
  273. if (coursePropertyIds.includes(prop.coursePropertyId)) return;
  274. coursePropertyIds.push(prop.coursePropertyId);
  275. propertyInfo.push({
  276. id: prop.coursePropertyId,
  277. name: prop.coursePropertyName,
  278. });
  279. });
  280. });
  281. this.propertyInfo = propertyInfo;
  282. if (!propertyInfo.length) {
  283. this.curCourseProp = {};
  284. return;
  285. }
  286. const curCourseProp = propertyInfo.find(
  287. (item) => item.coursePropertyId === this.curCoursePropertyId
  288. );
  289. if (!curCourseProp) {
  290. this.curCoursePropertyId = propertyInfo[0].id;
  291. }
  292. },
  293. buildDataList() {
  294. let _this = this;
  295. function buildClassifyData(data) {
  296. let cpreInfo = data.classifyId + "";
  297. let classifyData = {
  298. id: cpreInfo,
  299. name: data.classifyName,
  300. classifyId: data.classifyId,
  301. classifyName: data.classifyName,
  302. isClassify: true,
  303. questionCount: data.questionCount,
  304. selectCount: _this.gerSourceData(cpreInfo),
  305. difficultDistributeInfo: [],
  306. };
  307. if (
  308. data.difficultDistributeInfo &&
  309. data.difficultDistributeInfo.length
  310. ) {
  311. classifyData.difficultDistributeInfo = parseQuestionDistributeInfo(
  312. data.difficultDistributeInfo,
  313. cpreInfo
  314. );
  315. }
  316. if (
  317. !data.coursePropertyDistributeInfo ||
  318. !data.coursePropertyDistributeInfo.length
  319. )
  320. return classifyData;
  321. const curCourseProp = data.coursePropertyDistributeInfo.find(
  322. (item) => item.coursePropertyId === _this.curCoursePropertyId
  323. );
  324. if (!curCourseProp) {
  325. return classifyData;
  326. }
  327. classifyData.propertyDistributeInfo =
  328. curCourseProp.propertyDistributeInfo.map((item) =>
  329. parsePropertyData(item, `${cpreInfo}_${_this.curCoursePropertyId}`)
  330. );
  331. classifyData.propertyDistributeInfo = filterPropertyData(
  332. classifyData.propertyDistributeInfo
  333. );
  334. return classifyData;
  335. }
  336. function parseQuestionDistributeInfo(data, preInfo) {
  337. if (!_this.difficultDistributeInfo.length) {
  338. _this.difficultDistributeInfo = data.map(
  339. (item) => item.difficultLevel
  340. );
  341. }
  342. return data.map((item) => {
  343. return {
  344. ...item,
  345. selectCount: _this.gerSourceData(
  346. `${preInfo}_${item.difficultLevel}`
  347. ),
  348. };
  349. });
  350. }
  351. function parsePropertyData(data, parentId) {
  352. const propId = `${parentId}_${data.propertyId}`;
  353. let propData = {
  354. id: propId,
  355. name: data.propertyName,
  356. propertyId: data.propertyId,
  357. propertyName: data.propertyName,
  358. questionCount: data.questionCount,
  359. selectCount: _this.gerSourceData(propId),
  360. difficultDistributeInfo: null,
  361. };
  362. if (
  363. data.difficultDistributeInfo &&
  364. data.difficultDistributeInfo.length
  365. ) {
  366. propData.difficultDistributeInfo = parseQuestionDistributeInfo(
  367. data.difficultDistributeInfo,
  368. propId
  369. );
  370. }
  371. if (data.propertyDistributeInfo && data.propertyDistributeInfo.length) {
  372. propData.propertyDistributeInfo = data.propertyDistributeInfo.map(
  373. (item) => parsePropertyData(item, propId)
  374. );
  375. }
  376. return propData;
  377. }
  378. function filterPropertyData(propertyDistributeInfo) {
  379. // 只处理两级属性
  380. let pDistributeInfo = propertyDistributeInfo.map((data) => {
  381. if (
  382. data.propertyDistributeInfo &&
  383. data.propertyDistributeInfo.length
  384. ) {
  385. data.propertyDistributeInfo = data.propertyDistributeInfo.filter(
  386. (item) => item.questionCount
  387. );
  388. }
  389. return data;
  390. });
  391. pDistributeInfo = pDistributeInfo.filter(
  392. (data) =>
  393. data.propertyDistributeInfo && data.propertyDistributeInfo.length
  394. );
  395. return pDistributeInfo;
  396. }
  397. this.dataList = this.originList.map((item) => buildClassifyData(item));
  398. this.tableData = this.dataList;
  399. if (this.USE_ONLY_DIFFICULT) {
  400. this.tableData = this.dataList[0].difficultDistributeInfo;
  401. this.tableData = this.tableData.map((item) => {
  402. let nitem = { ...item };
  403. nitem.id = item.difficultLevel;
  404. nitem.name = item.difficultLevel;
  405. return nitem;
  406. });
  407. this.selectCountChange();
  408. return;
  409. }
  410. if (this.useClassify && !this.useProperty) {
  411. this.tableData = this.dataList.map((item) => {
  412. let nitem = { ...item };
  413. nitem.propertyDistributeInfo = null;
  414. return nitem;
  415. });
  416. this.selectCountChange();
  417. return;
  418. }
  419. if (!this.useClassify && this.useProperty) {
  420. this.tableData = this.dataList[0].propertyDistributeInfo;
  421. this.selectCountChange();
  422. return;
  423. }
  424. this.selectCountChange();
  425. },
  426. getTotalQuestionCount() {
  427. let questionCount = 0;
  428. if (this.USE_ONLY_DIFFICULT) {
  429. questionCount = calcSum(
  430. this.tableData.map((item) => item.selectCount || 0)
  431. );
  432. return questionCount;
  433. }
  434. let _this = this;
  435. function getCount(data) {
  436. let count = 0;
  437. if (_this.useDifficult) {
  438. if (data.difficultDistributeInfo) {
  439. count = calcSum(
  440. data.difficultDistributeInfo.map((item) => item.selectCount || 0)
  441. );
  442. }
  443. } else {
  444. count = data.selectCount || 0;
  445. }
  446. return count;
  447. }
  448. this.tableData.forEach((item) => {
  449. questionCount += getCount(item);
  450. if (this.useProperty && item.propertyDistributeInfo) {
  451. item.propertyDistributeInfo.forEach((pItem) => {
  452. questionCount += getCount(pItem);
  453. if (pItem.propertyDistributeInfo) {
  454. pItem.propertyDistributeInfo.forEach((spItem) => {
  455. questionCount += getCount(spItem);
  456. });
  457. }
  458. });
  459. }
  460. });
  461. return questionCount;
  462. },
  463. selectCountChange() {
  464. this.$emit("count-change", this.getTotalQuestionCount());
  465. },
  466. getDataList() {
  467. let dataList = deepCopy(this.dataList);
  468. let structInfo = {
  469. classifyIdList: this.useClassify ? this.selectedFolderIds : [],
  470. questionDistributeInfo: null,
  471. questionCount: this.getTotalQuestionCount(),
  472. useClassify: this.useClassify,
  473. useDifficult: this.useDifficult,
  474. useProperty: this.useProperty,
  475. curCoursePropertyId: this.curCoursePropertyId,
  476. };
  477. if (this.USE_ONLY_DIFFICULT) {
  478. dataList[0].difficultDistributeInfo = this.tableData.map((item) => {
  479. return {
  480. difficultLevel: item.difficultLevel,
  481. questionCount: item.questionCount,
  482. selectCount: item.selectCount,
  483. };
  484. });
  485. structInfo.propertyDistributeInfo = dataList;
  486. return structInfo;
  487. } else if (this.useClassify && !this.useProperty) {
  488. dataList = deepCopy(this.tableData);
  489. structInfo.propertyDistributeInfo = dataList;
  490. return structInfo;
  491. } else if (!this.useClassify && this.useProperty) {
  492. dataList[0].propertyDistributeInfo = this.tableData;
  493. }
  494. const curCourseProp = this.propertyInfo.find(
  495. (item) => item.id === this.curCoursePropertyId
  496. );
  497. dataList = dataList.map((item) => {
  498. item.coursePropertyDistributeInfo = [
  499. {
  500. coursePropertyId: curCourseProp.id,
  501. coursePropertyName: curCourseProp.name,
  502. propertyDistributeInfo: item.propertyDistributeInfo,
  503. },
  504. ];
  505. return item;
  506. });
  507. structInfo.propertyDistributeInfo = dataList;
  508. return structInfo;
  509. },
  510. // source relate
  511. gerSourceData(k) {
  512. return this.sourceDataMap[k] || undefined;
  513. },
  514. parseSourceDataMap() {
  515. let sourceDataMap = {};
  516. function parseDifficultMap(difficultDistributeInfo, preInfo) {
  517. if (!difficultDistributeInfo || !difficultDistributeInfo.length) return;
  518. difficultDistributeInfo.forEach((d) => {
  519. if (d.selectCount)
  520. sourceDataMap[`${preInfo}_${d.difficultLevel}`] = d.selectCount;
  521. });
  522. }
  523. function parsePropertyMap(propertyDistributeInfo, preInfo) {
  524. if (!propertyDistributeInfo || !propertyDistributeInfo.length) return;
  525. propertyDistributeInfo.forEach((pInfo) => {
  526. let ppreInfo = `${preInfo}_${pInfo.propertyId}`;
  527. if (pInfo.selectCount) sourceDataMap[ppreInfo] = pInfo.selectCount;
  528. parseDifficultMap(pInfo.difficultDistributeInfo, ppreInfo);
  529. parsePropertyMap(pInfo.propertyDistributeInfo, ppreInfo);
  530. });
  531. }
  532. if (
  533. this.dataSource.questionDistributeInfo &&
  534. this.dataSource.questionDistributeInfo.length
  535. ) {
  536. this.dataSource.questionDistributeInfo.forEach((classifyData) => {
  537. let cpreInfo = classifyData.classifyId + "";
  538. if (classifyData.selectCount) {
  539. sourceDataMap[cpreInfo] = classifyData.selectCount;
  540. }
  541. parseDifficultMap(classifyData.difficultDistributeInfo, cpreInfo);
  542. if (
  543. classifyData.coursePropertyDistributeInfo &&
  544. classifyData.coursePropertyDistributeInfo.length
  545. ) {
  546. let preInfo = `${cpreInfo}_${classifyData.coursePropertyDistributeInfo[0].coursePropertyId}`;
  547. parsePropertyMap(
  548. classifyData.coursePropertyDistributeInfo[0]
  549. .propertyDistributeInfo,
  550. preInfo
  551. );
  552. }
  553. });
  554. }
  555. this.sourceDataMap = sourceDataMap;
  556. },
  557. },
  558. };
  559. </script>