docs.vue 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666
  1. <template>
  2. <div class="w-main docs">
  3. <v-title>{{$L('知识库')}}-{{$L('轻量级的团队在线协作')}}</v-title>
  4. <div class="w-nav">
  5. <div class="nav-row">
  6. <div class="w-nav-left">
  7. <div class="page-nav-left">
  8. <span class="hover" @click="[addBookId=0,addBookShow=true]"><i class="ft icon">&#xE740;</i> {{$L('新建知识库')}}</span>
  9. <div v-if="loadIng > 0" class="page-nav-loading"><w-loading></w-loading></div>
  10. <div v-else class="page-nav-refresh"><em @click="getBookLists(true)">{{$L('刷新')}}</em></div>
  11. </div>
  12. </div>
  13. <div class="w-nav-flex"></div>
  14. </div>
  15. </div>
  16. <w-content>
  17. <div class="docs-main">
  18. <div class="docs-body">
  19. <div class="docs-menu">
  20. <h3>{{$L('我的知识库')}}</h3>
  21. <ul>
  22. <li v-for="book in bookLists" :class="{active:book.id==selectBookData.id}" @click="[selectBookData=book,getSectionLists(true)]">
  23. <div class="docs-title">{{book.title}}</div>
  24. <div class="docs-time">{{$A.formatDate("Y-m-d H:i:s", book.indate)}}</div>
  25. </li>
  26. <li v-if="bookHasMorePages" class="more" @click="getBookLists">{{$L('加载下一页...')}}</li>
  27. <li v-else-if="bookLoading" class="load"><WLoading/></li>
  28. <li v-else-if="bookLists.length == 0" class="none">{{bookNoDataText}}</li>
  29. </ul>
  30. </div>
  31. <div class="docs-container">
  32. <div v-if="selectBookData.id > 0" class="docs-box">
  33. <div class="docs-header">
  34. <div class="docs-h1">{{selectBookData.title}}</div>
  35. <div class="docs-setting">
  36. <Button @click="[addSectionId=0,addSectionShow=true]">{{$L('新增章节')}}</Button>
  37. <Button @click="[addBookId=selectBookData.id,addBookShow=true]">{{$L('修改标题')}}</Button>
  38. <Button @click="showShare">{{$L('分享')}}</Button>
  39. <Button @click="[settingDrawerShow=true,settingDrawerTab='setting']">{{$L('设置')}}</Button>
  40. <Button type="warning" ghost @click="onBookDelete(selectBookData.id)">{{$L('删除')}}</Button>
  41. </div>
  42. </div>
  43. <div class="docs-section">
  44. <nested-draggable :lists="sectionLists" :disabled="sortDisabled" @change="handleSection"></nested-draggable>
  45. <div v-if="sectionLists.length == 0" class="none">{{sectionNoDataText}}</div>
  46. </div>
  47. </div>
  48. </div>
  49. </div>
  50. </div>
  51. </w-content>
  52. <Modal
  53. v-model="addBookShow"
  54. :title="$L(addBookId > 0 ? '修改标题' : '新建知识库')"
  55. :closable="false"
  56. :mask-closable="false">
  57. <Form ref="bookAdd" :model="formBookAdd" :rules="ruleBookAdd" :label-width="110">
  58. <FormItem prop="title" :label="$L('知识库名称')" style="margin-right:28px">
  59. <Input type="text" v-model="formBookAdd.title" :maxlength="32"></Input>
  60. </FormItem>
  61. </Form>
  62. <div slot="footer">
  63. <Button type="default" @click="addBookShow=false">{{$L('取消')}}</Button>
  64. <Button type="primary" :loading="loadIng > 0" @click="onBookAdd">{{$L(addBookId > 0 ? '提交' : '添加')}}</Button>
  65. </div>
  66. </Modal>
  67. <Modal
  68. v-model="addSectionShow"
  69. :title="$L(addSectionId > 0 ? '修改文档标题' : '新建文档')"
  70. :closable="false"
  71. :mask-closable="false">
  72. <Form ref="sectionAdd" :model="formSectionAdd" :rules="ruleSectionAdd" :label-width="110">
  73. <FormItem prop="title" :label="$L('文档标题')" style="margin-right:28px">
  74. <Input type="text" v-model="formSectionAdd.title" :maxlength="32"></Input>
  75. </FormItem>
  76. <FormItem v-if="addSectionId <= 0" prop="type" :label="$L('文档类型')" style="margin-right:28px">
  77. <ButtonGroup>
  78. <Button v-for="(it, ik) in sectionTypeLists"
  79. :key="ik"
  80. :type="`${formSectionAdd.type==it.value?'primary':'default'}`"
  81. @click="formSectionAdd.type=it.value">{{it.text}}</Button>
  82. </ButtonGroup>
  83. </FormItem>
  84. </Form>
  85. <div slot="footer">
  86. <Button type="default" @click="addSectionShow=false">{{$L('取消')}}</Button>
  87. <Button type="primary" :loading="loadIng > 0" @click="onSectionAdd">{{$L(addSectionId > 0 ? '提交' : '添加')}}</Button>
  88. </div>
  89. </Modal>
  90. <WDrawer v-model="settingDrawerShow" maxWidth="750">
  91. <Tabs v-if="settingDrawerShow" v-model="settingDrawerTab">
  92. <TabPane :label="$L('文档设置')" name="setting">
  93. <book-setting :canload="settingDrawerShow && settingDrawerTab == 'setting'" :id="selectBookData.id"></book-setting>
  94. </TabPane>
  95. <TabPane :label="$L('文档成员')" name="member">
  96. <book-users :canload="settingDrawerShow && settingDrawerTab == 'member'" :id="selectBookData.id"></book-users>
  97. </TabPane>
  98. </Tabs>
  99. </WDrawer>
  100. </div>
  101. </template>
  102. <style lang="scss" scoped>
  103. .docs {
  104. .docs-main {
  105. display: flex;
  106. flex-direction: column;
  107. width: 100%;
  108. min-width: 1024px;
  109. height: 100%;
  110. padding: 15px;
  111. .docs-body {
  112. display: flex;
  113. flex-direction: row;
  114. width: 100%;
  115. height: 100%;
  116. min-height: 500px;
  117. .docs-menu {
  118. display: flex;
  119. flex-direction: column;
  120. width: 230px;
  121. border-radius: 3px 0 0 3px;
  122. background: rgba(255, 255, 255, 0.8);
  123. h3 {
  124. font-size: 18px;
  125. font-weight: normal;
  126. padding: 10px 12px;
  127. color: #333333;
  128. }
  129. ul {
  130. flex: 1;
  131. overflow: auto;
  132. li {
  133. padding: 12px;
  134. cursor: pointer;
  135. &.more {
  136. text-align: center;
  137. color: #555555;
  138. margin-bottom: 6px;
  139. &:hover {
  140. color: #333333;
  141. }
  142. }
  143. &.load {
  144. text-align: center;
  145. height: 42px;
  146. margin-bottom: 9px;
  147. }
  148. &.none {
  149. background-color: transparent;
  150. text-align: center;
  151. color: #666666;
  152. padding: 8px 18px;
  153. }
  154. &.active {
  155. background-color: #ffffff;
  156. }
  157. .docs-title {
  158. color: #242424;
  159. font-size: 13px;
  160. }
  161. .docs-time {
  162. display: block;
  163. color: #999;
  164. font-size: 12px;
  165. margin-top: 2px;
  166. position: relative;
  167. }
  168. }
  169. }
  170. }
  171. .docs-container {
  172. flex: 1;
  173. background-color: #ffffff;
  174. border-radius: 0 3px 3px 0;
  175. .docs-box {
  176. width: 100%;
  177. height: 100%;
  178. display: flex;
  179. flex-direction: column;
  180. }
  181. .docs-header {
  182. display: flex;
  183. align-items: center;
  184. margin: 6px 24px 0;
  185. padding: 12px 0;
  186. border-bottom: 1px solid #eeeeee;
  187. .docs-h1 {
  188. flex: 1;
  189. font-size: 16px;
  190. white-space: nowrap;
  191. }
  192. .docs-setting {
  193. display: flex;
  194. align-items: center;
  195. > button {
  196. margin: 0 6px;
  197. &:last-child {
  198. margin-right: 0;
  199. }
  200. }
  201. }
  202. }
  203. .docs-section {
  204. flex: 1;
  205. padding: 12px 26px;
  206. overflow: auto;
  207. transform: translateZ(0);
  208. .none {
  209. background-color: transparent;
  210. text-align: center;
  211. color: #666666;
  212. padding: 48px 24px;
  213. }
  214. }
  215. }
  216. }
  217. }
  218. }
  219. </style>
  220. <script>
  221. import WContent from "../components/WContent";
  222. import NestedDraggable from "../components/docs/NestedDraggable";
  223. import BookSetting from "../components/docs/setting";
  224. import BookUsers from "../components/docs/users";
  225. import WDrawer from "../components/iview/WDrawer";
  226. export default {
  227. components: {WDrawer, BookUsers, BookSetting, NestedDraggable, WContent},
  228. data () {
  229. return {
  230. loadIng: 0,
  231. userInfo: {},
  232. bookLists: [],
  233. bookListPage: 1,
  234. bookListTotal: 0,
  235. bookLoading: false,
  236. bookHasMorePages: false,
  237. bookNoDataText: "",
  238. addBookId: 0,
  239. addBookShow: false,
  240. formBookAdd: {
  241. title: '',
  242. },
  243. ruleBookAdd: {},
  244. selectBookData: {},
  245. sectionLists: [],
  246. sectionNoDataText: "",
  247. addSectionId: 0,
  248. addSectionShow: false,
  249. formSectionAdd: {
  250. title: '',
  251. type: 'document',
  252. },
  253. ruleSectionAdd: {},
  254. sectionTypeLists: [],
  255. sortDisabled: false,
  256. settingDrawerShow: false,
  257. settingDrawerTab: 'setting',
  258. }
  259. },
  260. created() {
  261. this.bookNoDataText = this.$L("数据加载中.....");
  262. this.sectionNoDataText = this.$L("数据加载中.....");
  263. this.sectionTypeLists = [
  264. {value: 'document', text: this.$L("文本")},
  265. {value: 'mind', text: this.$L("脑图")},
  266. {value: 'sheet', text: this.$L("表格")},
  267. {value: 'flow', text: this.$L("流程图")},
  268. {value: 'folder', text: this.$L("目录")},
  269. ];
  270. this.ruleBookAdd = {
  271. title: [
  272. { required: true, message: this.$L('请填写知识库名称!'), trigger: 'change' },
  273. { type: 'string', min: 2, message: this.$L('知识库名称长度至少2位!'), trigger: 'change' }
  274. ],
  275. };
  276. this.ruleSectionAdd = {
  277. title: [
  278. { required: true, message: this.$L('请填写文档标题!'), trigger: 'change' },
  279. { type: 'string', min: 2, message: this.$L('文档标题长度至少2位!'), trigger: 'change' }
  280. ],
  281. type: [
  282. { required: true },
  283. ],
  284. };
  285. },
  286. mounted() {
  287. this.getBookLists(true);
  288. this.userInfo = $A.getUserInfo((res, isLogin) => {
  289. if (this.userInfo.id != res.id) {
  290. this.userInfo = res;
  291. isLogin && this.getBookLists(true);
  292. } else {
  293. this.userInfo = res;
  294. }
  295. }, false);
  296. },
  297. deactivated() {
  298. this.addBookShow = false;
  299. this.addSectionShow = false;
  300. },
  301. computed: {
  302. },
  303. watch: {
  304. addBookShow(val) {
  305. if (val && this.addBookId > 0) {
  306. let tempLists = this.bookLists.filter((res) => { return res.id == this.addBookId });
  307. if (tempLists.length === 1) {
  308. this.$set(this.formBookAdd, 'title', tempLists[0].title);
  309. } else {
  310. this.$set(this.formBookAdd, 'title', '');
  311. }
  312. }
  313. },
  314. addSectionShow(val) {
  315. if (val && this.addSectionId > 0) {
  316. let tempLists = this.children2lists(this.sectionLists).filter((res) => { return res.id == this.addSectionId });
  317. if (tempLists.length === 1) {
  318. this.$set(this.formSectionAdd, 'title', tempLists[0].title);
  319. } else {
  320. this.$set(this.formSectionAdd, 'title', '');
  321. }
  322. }
  323. }
  324. },
  325. methods: {
  326. children2lists(lists) {
  327. let array = [];
  328. lists.forEach((item) => {
  329. array.push({
  330. id: item.id,
  331. title: item.title
  332. });
  333. array = array.concat(this.children2lists(item.children))
  334. });
  335. return array;
  336. },
  337. getBookLists(resetLoad) {
  338. if (resetLoad === true) {
  339. this.bookListPage = 1;
  340. } else {
  341. if (this.bookHasMorePages === false) {
  342. return;
  343. }
  344. this.bookListPage++;
  345. }
  346. this.bookLoading = true;
  347. this.bookNoDataText = this.$L("数据加载中.....");
  348. this.bookHasMorePages = false;
  349. $A.apiAjax({
  350. url: 'docs/book/lists',
  351. data: {
  352. page: Math.max(this.bookListPage, 1),
  353. pagesize: 20,
  354. },
  355. complete: () => {
  356. this.bookLoading = false;
  357. },
  358. error: () => {
  359. this.bookNoDataText = this.$L("数据加载失败!");
  360. },
  361. success: (res) => {
  362. if (res.ret === 1) {
  363. res.data.lists.forEach((item) => {
  364. let find = this.bookLists.find((t)=>{return t.id==item.id});
  365. if (!find) {
  366. this.bookLists.push(item);
  367. }
  368. });
  369. this.bookListTotal = res.data.total;
  370. this.bookNoDataText = this.$L("没有相关的数据");
  371. this.bookHasMorePages = res.data.hasMorePages;
  372. if (typeof this.selectBookData.id === "undefined") {
  373. this.selectBookData = this.bookLists[0];
  374. this.getSectionLists();
  375. }
  376. } else {
  377. this.bookLists = [];
  378. this.bookListTotal = 0;
  379. this.bookNoDataText = res.msg;
  380. }
  381. }
  382. });
  383. },
  384. onBookAdd() {
  385. this.$refs.bookAdd.validate((valid) => {
  386. if (valid) {
  387. this.loadIng++;
  388. $A.apiAjax({
  389. url: 'docs/book/add',
  390. data: Object.assign(this.formBookAdd, {id:this.addBookId}),
  391. complete: () => {
  392. this.loadIng--;
  393. },
  394. success: (res) => {
  395. if (res.ret === 1) {
  396. this.addBookShow = false;
  397. this.$Message.success(res.msg);
  398. this.$refs.bookAdd.resetFields();
  399. //
  400. if (this.addBookId > 0) {
  401. this.bookLists.some((item) => {
  402. if (item.id == this.addBookId) {
  403. this.$set(item, 'title', res.data.title);
  404. return true;
  405. }
  406. });
  407. } else {
  408. this.bookLists.unshift(res.data);
  409. this.selectBookData = this.bookLists[0];
  410. this.getSectionLists();
  411. }
  412. }else{
  413. this.$Modal.error({title: this.$L('温馨提示'), content: res.msg });
  414. }
  415. }
  416. });
  417. }
  418. });
  419. },
  420. onBookDelete(bookId) {
  421. this.$Modal.confirm({
  422. title: this.$L('删除知识库'),
  423. content: this.$L('你确定要删除此知识库吗?'),
  424. loading: true,
  425. onOk: () => {
  426. $A.apiAjax({
  427. url: 'docs/book/delete',
  428. data: {
  429. id: bookId
  430. },
  431. error: () => {
  432. this.$Modal.remove();
  433. alert(this.$L('网络繁忙,请稍后再试!'));
  434. },
  435. success: (res) => {
  436. this.$Modal.remove();
  437. this.bookLists.some((item, index) => {
  438. if (item.id == bookId) {
  439. this.bookLists.splice(index, 1);
  440. return true;
  441. }
  442. })
  443. this.selectBookData = this.bookLists[0];
  444. this.getSectionLists();
  445. //
  446. setTimeout(() => {
  447. if (res.ret === 1) {
  448. this.$Message.success(res.msg);
  449. } else {
  450. this.$Modal.error({title: this.$L('温馨提示'), content: res.msg});
  451. }
  452. }, 350);
  453. }
  454. });
  455. }
  456. });
  457. },
  458. getSectionLists(isClear) {
  459. if (isClear === true) {
  460. this.sectionLists = [];
  461. }
  462. let bookid = this.selectBookData.id;
  463. this.loadIng++;
  464. this.sectionNoDataText = this.$L("数据加载中.....");
  465. $A.apiAjax({
  466. url: 'docs/section/lists',
  467. data: {
  468. act: 'edit',
  469. bookid: bookid
  470. },
  471. complete: () => {
  472. this.loadIng--;
  473. },
  474. error: () => {
  475. if (bookid != this.selectBookData.id) {
  476. return;
  477. }
  478. this.sectionNoDataText = this.$L("数据加载失败!");
  479. },
  480. success: (res) => {
  481. if (bookid != this.selectBookData.id) {
  482. return;
  483. }
  484. if (res.ret === 1) {
  485. this.sectionLists = res.data.tree;
  486. this.sectionNoDataText = this.$L("没有相关的数据");
  487. }else{
  488. this.sectionLists = [];
  489. this.sectionNoDataText = res.msg;
  490. }
  491. }
  492. });
  493. },
  494. onSectionAdd() {
  495. this.$refs.sectionAdd.validate((valid) => {
  496. if (valid) {
  497. this.loadIng++;
  498. let bookid = this.selectBookData.id;
  499. $A.apiAjax({
  500. url: 'docs/section/add',
  501. data: Object.assign(this.formSectionAdd, {
  502. id: this.addSectionId,
  503. bookid: bookid,
  504. }),
  505. complete: () => {
  506. this.loadIng--;
  507. },
  508. success: (res) => {
  509. if (bookid != this.selectBookData.id) {
  510. return;
  511. }
  512. if (res.ret === 1) {
  513. this.addSectionShow = false;
  514. this.$Message.success(res.msg);
  515. this.$refs.sectionAdd.resetFields();
  516. //
  517. this.getSectionLists();
  518. }else{
  519. this.$Modal.error({title: this.$L('温馨提示'), content: res.msg });
  520. }
  521. }
  522. });
  523. }
  524. });
  525. },
  526. onSectionDelete(detail) {
  527. let sectionType = this.sectionTypeLists.find((item) => { return item.value == detail.type });
  528. this.$Modal.confirm({
  529. title: this.$L('删除文档'),
  530. content: this.$L('你确定要删除%【%】吗?', sectionType ? sectionType.text : '', detail.title),
  531. loading: true,
  532. onOk: () => {
  533. $A.apiAjax({
  534. url: 'docs/section/delete',
  535. data: {
  536. id: detail.id
  537. },
  538. error: () => {
  539. this.$Modal.remove();
  540. alert(this.$L('网络繁忙,请稍后再试!'));
  541. },
  542. success: (res) => {
  543. this.$Modal.remove();
  544. this.getSectionLists();
  545. //
  546. setTimeout(() => {
  547. if (res.ret === 1) {
  548. this.$Message.success(res.msg);
  549. } else {
  550. this.$Modal.error({title: this.$L('温馨提示'), content: res.msg});
  551. }
  552. }, 350);
  553. }
  554. });
  555. }
  556. });
  557. },
  558. handleSection(act, detail) {
  559. switch (act) {
  560. case 'open':
  561. this.goForward({name: 'docs-edit', params: {sid:detail.id, other:detail||{}}});
  562. break;
  563. case 'edit':
  564. this.addSectionId = detail.id;
  565. this.addSectionShow = true
  566. break;
  567. case 'add':
  568. this.addSectionId = detail.id * -1;
  569. this.addSectionShow = true
  570. break;
  571. case 'delete':
  572. if (this.sortDisabled) {
  573. this.$Modal.warning({
  574. title: this.$L('温馨提示'),
  575. content: this.$L('正在进行其他操作,请稍后重试...')
  576. });
  577. return;
  578. }
  579. this.onSectionDelete(detail);
  580. break;
  581. case 'sort':
  582. this.sortDisabled = true;
  583. this.loadIng++;
  584. $A.apiAjax({
  585. url: 'docs/section/sort',
  586. data: {
  587. bookid: this.selectBookData.id,
  588. newsort: detail,
  589. },
  590. complete: () => {
  591. this.sortDisabled = false;
  592. this.loadIng--;
  593. },
  594. error: () => {
  595. this.getSectionLists();
  596. alert(this.$L('网络繁忙,请稍后再试!'));
  597. },
  598. success: (res) => {
  599. if (res.ret === 1) {
  600. this.$Message.success(res.msg);
  601. } else {
  602. this.getSectionLists();
  603. this.$Modal.error({title: this.$L('温馨提示'), content: res.msg});
  604. }
  605. }
  606. });
  607. break;
  608. }
  609. },
  610. showShare() {
  611. this.$Modal.confirm({
  612. render: (h) => {
  613. return h('div', [
  614. h('div', {
  615. style: {
  616. fontSize: '16px',
  617. fontWeight: '500',
  618. marginBottom: '20px',
  619. }
  620. }, this.$L('文档链接')),
  621. h('Input', {
  622. props: {
  623. value: $A.webUrl('docs/view/b' + this.selectBookData.id),
  624. readonly: true,
  625. },
  626. })
  627. ])
  628. },
  629. });
  630. }
  631. },
  632. }
  633. </script>