folderDetail.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606
  1. <template>
  2. <div class="f_box">
  3. <div class="f_box_top" v-loading="isLoading">
  4. <div class="title">
  5. <textarea
  6. rows="1"
  7. style="width: 300px;"
  8. class="binfo_input binfo_textarea"
  9. ref="binfo_textarea"
  10. cols
  11. v-model="data.name"
  12. placeholder="输入文件夹名字"
  13. v-if="canEdit"
  14. ></textarea>
  15. <div class="t_title" v-else>{{ data.name }}</div>
  16. <div class="t_btn">
  17. <!-- <div class="btn">复制文件夹</div> -->
  18. <!-- <el-button type="primary" size="small"
  19. >复制文件夹</el-button> -->
  20. <el-button v-if="userid == data.userid && data.isMo == '2'" type="danger" size="small" @click="deleteFile(pid.split('/')[1])"
  21. >删除文件夹</el-button
  22. >
  23. <!-- <div
  24. class="btn"
  25. @click="deleteFile(pid.split('/')[1])"
  26. v-if="userid == data.userid && data.isMo == '2'"
  27. >
  28. 删除文件夹
  29. </div> -->
  30. </div>
  31. </div>
  32. <div class="detail">
  33. <textarea
  34. v-autoHeight="68"
  35. rows="2"
  36. class="binfo_input binfo_textarea"
  37. ref="binfo_textarea"
  38. cols
  39. v-model="data.detail"
  40. placeholder="输入文件夹描述"
  41. v-if="canEdit"
  42. ></textarea>
  43. <div
  44. class="detail_box"
  45. v-html="data.detail ? data.detail : '暂无描述'"
  46. v-else
  47. ></div>
  48. <div class="editbox" v-if="userid == data.userid && data.isMo == '2'">
  49. <div class="edit_btn" @click="editConfirm">
  50. {{ canEdit ? "确定" : "编辑" }}
  51. </div>
  52. </div>
  53. </div>
  54. <div class="tag_box" v-if="data.userid == userid && data.isMo == '2'">
  55. <div class="tag">
  56. <div class="tag_title">标签:</div>
  57. <div class="tag_check">
  58. <selectTag
  59. v-loading="tagLoading1"
  60. v-model="check1"
  61. :options="tagData1"
  62. :placeholder="'前选择学科标签'"
  63. style="width: calc(100% / 2 - 10px / 2); margin-right: 10px;"
  64. @change="updateFolder()"
  65. />
  66. <selectTag
  67. v-loading="tagLoading2"
  68. v-model="check2"
  69. :options="tagData2"
  70. :placeholder="'前选择年级标签'"
  71. style="width: calc(100% / 2 - 10px / 2); margin-right: 10px;"
  72. @change="updateFolder()"
  73. clearable
  74. />
  75. <selectTag2
  76. v-loading="tagLoading3"
  77. v-model="check3"
  78. :options="tagData3"
  79. :placeholder="'自定义标签'"
  80. style="width: 100%; margin-top: 10px;"
  81. @change="handleTagChange"
  82. allow-create
  83. filterable
  84. clearable
  85. @deleteTag="deleteTag"
  86. />
  87. <!-- <el-select
  88. v-loading="tagLoading3"
  89. v-model="check3"
  90. placeholder="自定义标签"
  91. clearable
  92. filterable
  93. multiple
  94. collapse-tags
  95. allow-create
  96. style="width: 100%; margin-top: 10px;"
  97. @change="handleTagChange"
  98. >
  99. <el-option
  100. v-for="(item, index) in tagData3"
  101. :key="index"
  102. :label="item.name"
  103. :value="item.name"
  104. >
  105. <div class="selectBox">
  106. <span>{{ item.name }}</span>
  107. <div class="controlsBox">
  108. <span
  109. class="delSelect"
  110. @click.stop="deleteTag(item.id, item.name)"
  111. ></span>
  112. </div>
  113. </div>
  114. </el-option>
  115. </el-select> -->
  116. </div>
  117. </div>
  118. <div class="tag">
  119. <div class="tag_title">权限管理:</div>
  120. <div class="tag_check">
  121. <el-radio-group v-model="data.juri" @change="updateFolder()">
  122. <el-radio label="1">私有</el-radio>
  123. <el-radio label="2">组织内</el-radio>
  124. <el-radio label="3">全部</el-radio>
  125. </el-radio-group>
  126. </div>
  127. </div>
  128. </div>
  129. <div class="tag_box" v-else-if="data.isMo == '2'">
  130. <div class="tag_check">
  131. <div class="tag_check_box" v-if="this.check1.length">
  132. <span>学科:</span>
  133. <span>{{ getTagName(1) }}</span>
  134. </div>
  135. <div class="tag_check_box" v-if="this.check2.length">
  136. <span>年级:</span>
  137. <span>{{ getTagName(2) }}</span>
  138. </div>
  139. <div class="tag_check_box" v-if="this.check3.length">
  140. <span>自定义:</span>
  141. <span>{{ getTagName(3) }}</span>
  142. </div>
  143. </div>
  144. </div>
  145. <folderFileBoxVue :pid="pid" :userid="userid" :typeArray="typeArray" :folderid="folderid" :moFolderid="moFolderid" :data="data"></folderFileBoxVue>
  146. </div>
  147. </div>
  148. </template>
  149. <script>
  150. import selectTag from './components/selectTag.vue';
  151. import selectTag2 from './components/selectTag2.vue';
  152. import folderFileBoxVue from './folderFileBox.vue';
  153. export default {
  154. props: {
  155. pid: {
  156. type: String
  157. },
  158. userid: {
  159. type: String
  160. },
  161. typeArray: {
  162. type: Array,
  163. },
  164. folderid: {
  165. String: String
  166. },
  167. moFolderid: {
  168. String: String
  169. }
  170. },
  171. components: {
  172. folderFileBoxVue,
  173. selectTag,
  174. selectTag2
  175. },
  176. data() {
  177. return {
  178. data: {},
  179. isLoading: false,
  180. canEdit: false,
  181. tagLoading1: false,
  182. tagLoading2: false,
  183. tagLoading3: false,
  184. tagData1: [],
  185. tagData2: [],
  186. tagData3: [],
  187. check1: [],
  188. check2: [],
  189. check3: []
  190. };
  191. },
  192. watch: {
  193. pid(newValue, oldValue) {
  194. this.data = {};
  195. this.getData();
  196. this.getTag(1); // 获取标签
  197. this.getTag(2); // 获取标签
  198. this.getTag(3); // 获取标签
  199. }
  200. },
  201. computed: {
  202. getTagName() {
  203. return function(type) {
  204. const tagDataMap = {
  205. 1: { data: this.tagData1, check: this.check1, key: "id" },
  206. 2: { data: this.tagData2, check: this.check2, key: "id" },
  207. 3: { data: this.tagData3, check: this.check3, key: "name" }
  208. };
  209. const { data, check, key } = tagDataMap[type] || {};
  210. return data
  211. ? data
  212. .filter(item => check.includes(item[key]))
  213. .map(item => item.name)
  214. .join(",")
  215. : "";
  216. };
  217. }
  218. },
  219. directives: {
  220. autoHeight: {
  221. update(el, binding) {
  222. const { value } = binding;
  223. if (value && typeof value === "number") {
  224. el.style.height = `${value}px`;
  225. } else {
  226. el.style.height = "auto";
  227. }
  228. },
  229. componentUpdated(el) {
  230. el.style.height = `${el.scrollHeight + 10}px`;
  231. }
  232. }
  233. },
  234. mounted() {
  235. this.getTag(1); // 获取标签
  236. this.getTag(2); // 获取标签
  237. this.getTag(3); // 获取标签
  238. this.getData();
  239. },
  240. methods: {
  241. getData() {
  242. this.isLoading = true;
  243. let type = this.pid.split("/");
  244. let params = {
  245. id: type[1]
  246. };
  247. this.ajax
  248. .post(this.$store.state.api + "getKnowledgeDetail", [params])
  249. .then(res => {
  250. this.isLoading = false;
  251. this.data = res.data[0][0];
  252. this.check1 = res.data[0][0].sub_tag
  253. ? res.data[0][0].sub_tag.split(",")
  254. : [];
  255. this.check2 = res.data[0][0].class_tag
  256. ? res.data[0][0].class_tag.split(",")
  257. : [];
  258. this.check3 = res.data[0][0].tag ? res.data[0][0].tag.split(",") : [];
  259. })
  260. .catch(err => {
  261. this.isLoading = false;
  262. console.error(err);
  263. });
  264. },
  265. getTag(type) {
  266. const tagLoadings = [
  267. this.tagLoading1,
  268. this.tagLoading2,
  269. this.tagLoading3
  270. ];
  271. tagLoadings[type - 1] = true;
  272. let params = {
  273. type: type,
  274. uid: this.userid
  275. };
  276. this.ajax
  277. .post(this.$store.state.api + "getKnowledgeTag", [params])
  278. .then(res => {
  279. tagLoadings[type - 1] = false;
  280. this[`tagData${type}`] = res.data[0];
  281. const checkKey = `check${type}`;
  282. if (type == 3) {
  283. this[checkKey] = this[checkKey].filter(tag =>
  284. this[`tagData${type}`].some(item => item.name === tag)
  285. );
  286. } else {
  287. this[checkKey] = this[checkKey].filter(tag =>
  288. this[`tagData${type}`].some(item => item.id === tag)
  289. );
  290. }
  291. })
  292. .catch(err => {
  293. tagLoadings[type - 1] = false;
  294. console.error(err);
  295. });
  296. },
  297. deleteFile(id) {
  298. this.$confirm("确定删除文件夹吗?", "提示", {
  299. confirmButtonText: "确定",
  300. cancelButtonText: "取消",
  301. type: "warning"
  302. })
  303. .then(() => {
  304. let params = [{ ids: id, folderids: this.folderid }];
  305. this.ajax
  306. .post(this.$store.state.fileApi + "deleteFolder", params)
  307. .then(res => {
  308. this.$message({
  309. message: "删除成功",
  310. type: "success"
  311. });
  312. this.$emit("checkType", this.pid.split("/")[0]);
  313. })
  314. .catch(err => {
  315. this.$message.error("删除失败");
  316. console.error(err);
  317. });
  318. })
  319. .catch(() => {});
  320. },
  321. editConfirm() {
  322. if (!this.canEdit) {
  323. this.canEdit = true;
  324. this.data.detail += "*1*/123/";
  325. this.$nextTick(() => {
  326. this.data.detail = this.data.detail.replace("*1*/123/", "");
  327. this.$refs.binfo_textarea.focus();
  328. });
  329. } else {
  330. this.canEdit = false;
  331. this.updateFolder();
  332. }
  333. },
  334. updateFolder() {
  335. if(!this.data.name.trim()){
  336. this.$message.error("文件夹名字不能为空");
  337. this.canEdit = true;
  338. return;
  339. }
  340. let type = this.pid.split("/");
  341. let params = {
  342. id: type[1],
  343. n: this.data.name,
  344. d: this.data.detail,
  345. st: this.check1.join(","),
  346. ct: this.check2.join(","),
  347. t: this.check3.join(","),
  348. j: this.data.juri,
  349. folderid: this.folderid
  350. };
  351. this.ajax
  352. .post(this.$store.state.fileApi + "updateFolder", [params])
  353. .then(res => {
  354. this.$message.success("修改成功");
  355. this.getData();
  356. })
  357. .catch(err => {
  358. console.error(err);
  359. });
  360. },
  361. handleTagChange(value) {
  362. const tagArray = this.tagData3;
  363. const selectedTags = value.flatMap(tag => tag.split(/[,,]/).map(t => t.trim()));
  364. // 去掉选中反而选中不带逗号的值
  365. const uniqueTags = [...new Set(selectedTags.map(tag => tag.replace(/[,,].*/, '').trim()))];
  366. const missingTags = uniqueTags.filter(
  367. selectedTag => !tagArray.some(tag => tag.name === selectedTag)
  368. );
  369. console.log("Selected tags:", uniqueTags);
  370. console.log("Missing tags:", missingTags);
  371. this.check3 = uniqueTags
  372. missingTags.forEach(tag => this.addTag(tag));
  373. this.updateFolder();
  374. },
  375. addTag(name) {
  376. let params = {
  377. n: name,
  378. uid: this.userid,
  379. type: 3,
  380. j: 3
  381. };
  382. this.ajax
  383. .post(this.$store.state.api + "addTag", [params])
  384. .then(res => {
  385. this.getTag(3);
  386. })
  387. .catch(err => {
  388. console.error(err);
  389. });
  390. },
  391. deleteTag(id, n) {
  392. this.$confirm("确定删除这个标签吗?", "提示", {
  393. confirmButtonText: "确定",
  394. cancelButtonText: "取消",
  395. type: "warning"
  396. })
  397. .then(() => {
  398. this.check3 = this.check3.filter(t => t != n);
  399. this.updateFolder();
  400. let params = {
  401. id: id
  402. };
  403. this.ajax
  404. .post(this.$store.state.api + "deleteTag", [params])
  405. .then(res => {
  406. this.$message.success("删除成功");
  407. this.getTag(3);
  408. })
  409. .catch(err => {
  410. console.error(err);
  411. });
  412. })
  413. .catch(() => {});
  414. }
  415. }
  416. };
  417. </script>
  418. <style scoped>
  419. .f_box {
  420. width: 100%;
  421. /* height: 100%; */
  422. position: relative;
  423. }
  424. .f_box_top {
  425. padding: 10px;
  426. width: 100%;
  427. box-sizing: border-box;
  428. }
  429. .f_box_top .btn {
  430. display: flex;
  431. cursor: pointer;
  432. align-items: center;
  433. height: 30px;
  434. padding: 0 8px;
  435. background: rgb(0, 97, 255);
  436. color: #fff;
  437. box-sizing: border-box;
  438. border-radius: 4px;
  439. font-size: 14px;
  440. align-items: center;
  441. }
  442. .f_box_top .btn + .btn {
  443. margin-left: 10px;
  444. }
  445. .f_box_top > .title {
  446. width: 100%;
  447. display: flex;
  448. align-items: center;
  449. }
  450. .f_box_top > .title > .t_title {
  451. font-size: 22px;
  452. font-weight: bold;
  453. }
  454. .f_box_top > .title > .t_btn {
  455. margin-left: auto;
  456. display: flex;
  457. align-items: center;
  458. }
  459. .binfo_input {
  460. width: 100%;
  461. margin: 0;
  462. padding: 12px 14px;
  463. display: block;
  464. min-width: 0;
  465. outline: none;
  466. box-sizing: border-box;
  467. background: none;
  468. border: none;
  469. border-radius: 4px;
  470. background: #fff;
  471. font-size: 16px;
  472. resize: none;
  473. font-family: "Microsoft YaHei";
  474. min-height: 48px;
  475. /* border: 1px solid #3682fc00; */
  476. border: 1.5px solid #cad1dc;
  477. }
  478. .binfo_textarea {
  479. border: 1.5px solid #cad1dc;
  480. font-size: 16px;
  481. resize: none;
  482. /* background: #f6f6f6; */
  483. font-family: "Microsoft YaHei";
  484. }
  485. .binfo_input:focus-visible {
  486. border: 1.5px solid #3681fc !important;
  487. }
  488. .f_box_top > .detail {
  489. width: 100%;
  490. margin-top: 10px;
  491. color: #6d6d6d;
  492. }
  493. .f_box_top > .detail > .detail_box {
  494. width: 100%;
  495. white-space: break-spaces;
  496. word-break: break-all;
  497. }
  498. .f_box_top > .detail > .editbox {
  499. display: flex;
  500. align-items: center;
  501. justify-content: flex-end;
  502. }
  503. .f_box_top > .detail > .editbox > .edit_btn {
  504. color: #3681fc;
  505. cursor: pointer;
  506. margin-top: 5px;
  507. }
  508. .tag_box {
  509. margin: 10px 0 0 0;
  510. display: flex;
  511. align-items: flex-start;
  512. width: 100%;
  513. }
  514. .tag_box > .tag {
  515. width: calc(100% / 2 - 20px / 2);
  516. display: flex;
  517. align-items: flex-start;
  518. line-height: 40px;
  519. }
  520. .tag_box > .tag + .tag {
  521. margin-left: 20px;
  522. }
  523. .tag_box > .tag > .tag_title {
  524. min-width: fit-content;
  525. }
  526. .tag_box > .tag > .tag_check {
  527. display: flex;
  528. flex-wrap: wrap;
  529. }
  530. .tag_box > .tag > .tag_check >>> .el-radio-button__inner,
  531. .el-radio-group,
  532. .tag_box > .tag > .tag_check >>> .el-radio__input,
  533. .tag_box > .tag > .tag_check >>> .el-radio__label,
  534. .tag_box > .tag > .tag_check >>> .el-radio {
  535. display: flex;
  536. align-items: center;
  537. height: 40px;
  538. }
  539. .selectBox {
  540. width: 100%;
  541. height: 100%;
  542. display: flex;
  543. align-items: center;
  544. justify-content: space-between;
  545. }
  546. .controlsBox {
  547. display: flex;
  548. align-items: center;
  549. width: auto;
  550. height: 100%;
  551. display: flex;
  552. }
  553. .delSelect {
  554. width: 16px;
  555. height: 16px;
  556. /* display: none; */
  557. align-items: center;
  558. justify-content: center;
  559. background: url("../../../assets/icon/classroomObservation/del.svg") no-repeat;
  560. background-size: 100% 100%;
  561. box-sizing: border-box;
  562. /* transform: translateY(7px); */
  563. }
  564. .tag_check_box {
  565. width: 100%;
  566. display: flex;
  567. margin: 0 0 10px;
  568. flex-wrap: nowrap;
  569. }
  570. .tag_check_box > span:nth-child(1) {
  571. min-width: 65px;
  572. text-align-last: justify;
  573. }
  574. .tag_check_box > span:nth-child(2) {
  575. word-break: break-all;
  576. }
  577. </style>