admin-doc.vue 11.7 KB
Newer Older
1 2 3 4 5
<template>
  <a-layout>
    <a-layout-content
        :style="{ background: '#fff', padding: '24px', margin: 0, minHeight: '280px' }"
    >
6
      <a-row :gutter="24">
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
        <a-col :span="8">
          <p>
            <a-form layout="inline" :model="docQueryForm">
              <a-form-item>
                <a-button type="primary" @click="handleQueryFormSubmit">
                  查询
                </a-button>
              </a-form-item>
              <a-form-item>
                <a-button type="primary" @click="add()">
                  新增
                </a-button>
              </a-form-item>
            </a-form>
          </p>
          <a-table
23
              v-if="level1.length > 0"
24 25 26 27 28
              :columns="columns"
              :row-key="record => record.id"
              :data-source="level1"
              :loading="loading"
              :pagination="false"
29
              size="small"
30
              :defaultExpandAllRows="true"
31
          >
32 33
            <template #name="{ text, record }">
              {{ record.sort }} {{ text }}
34 35 36
            </template>
            <template v-slot:action="{ text, record }">
              <a-space size="small">
37
                <a-button type="primary" @click="edit(record)" size="small">
38 39 40 41 42 43 44 45
                  编辑
                </a-button>
                <a-popconfirm
                    title="删除后不可恢复,确认删除?"
                    ok-text="是"
                    cancel-text="否"
                    @confirm="handleDeleteDoc(record.id)"
                >
46
                  <a-button type="danger" size="small">
47 48 49 50 51 52 53 54
                    删除
                  </a-button>
                </a-popconfirm>
              </a-space>
            </template>
          </a-table>
        </a-col>
        <a-col :span="16">
55 56 57 58 59 60 61 62 63 64 65 66
          <p>
            <a-form layout="inline" :model="param">
              <a-form-item>
                <a-button type="primary" @click="handleSaveDoc()">
                  保存
                </a-button>
              </a-form-item>
            </a-form>
          </p>
          <a-form :model="doc" layout="vertical">
            <a-form-item>
              <a-input v-model:value="doc.name" placeholder="名称"/>
67
            </a-form-item>
68
            <a-form-item>
69 70 71 72 73 74 75 76 77 78 79
              <a-tree-select
                  v-model:value="doc.parent"
                  style="width: 100%"
                  :dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
                  :tree-data="treeSelectData"
                  placeholder="请选择父文档"
                  tree-default-expand-all
                  :replaceFields="{title: 'name', key: 'id', value: 'id'}"
              >
              </a-tree-select>
            </a-form-item>
80 81
            <a-form-item>
              <a-input v-model:value="doc.sort" placeholder="顺序"/>
82
            </a-form-item>
83 84 85 86 87
            <a-form-item>
              <a-button type="primary" @click="handlePreviewContent()">
                <EyeOutlined /> 内容预览
              </a-button>
            </a-form-item>
88
            <a-form-item>
89 90 91 92 93 94
              <div id="content"></div>
            </a-form-item>
          </a-form>
        </a-col>
      </a-row>

95 96 97 98
      <a-drawer width="900" placement="right" :closable="false" :visible="drawerVisible" @close="onDrawerClose">
        <div class="wangeditor" :innerHTML="previewHtml"></div>
      </a-drawer>

99 100 101
    </a-layout-content>
  </a-layout>

102 103 104 105 106 107 108 109
  <!--<a-modal-->
  <!--  title="文档表单"-->
  <!--  v-model:visible="modalVisible"-->
  <!--  :confirm-loading="modalLoading"-->
  <!--  @ok="handleModalOk"-->
  <!--&gt;-->
  <!--  -->
  <!--</a-modal>-->
110 111 112
</template>

<script lang="ts">
113
import {createVNode, defineComponent, onMounted, reactive, ref, UnwrapRef} from 'vue';
114
import axios from 'axios';
115
import {message, Modal} from 'ant-design-vue';
116
import ExclamationCircleOutlined from "@ant-design/icons-vue/ExclamationCircleOutlined";
117 118
import {Tool} from "@/util/tool";
import {Doc, DocQueryForm} from "@/models";
119
import {useRoute} from "vue-router";
120
import E from 'wangeditor';
121 122 123 124 125


export default defineComponent({
  name: 'AdminDoc',
  setup() {
126 127
    const route = useRoute();

128 129 130 131 132 133
    const docQueryForm: UnwrapRef<DocQueryForm> = reactive({
      name: ''
    });

    const docs = ref<Doc[]>([]);
    const loading = ref(false);
134 135
    const treeSelectData = ref();  // 因为树选择组件的属性状态,会随当前编辑的节点而变化,所以单独声明一个响应式变量
    treeSelectData.value = [];
136 137 138 139

    const columns = [
      {
        title: '名称',
140 141
        dataIndex: 'name',
        slots: { customRender: 'name' }
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
      },
      {
        title: 'Action',
        key: 'action',
        slots: { customRender: 'action' }
      }
    ];

    /**
     * 一级文档树,children属性就是二级文档
     * [{
     *   id: "",
     *   name: "",
     *   children: [{
     *     id: "",
     *     name: "",
     *   }]
     * }]
     */
    const level1 = ref(); // 一级文档树,children属性就是二级文档
162
    level1.value = [];
163 164 165 166 167


    /**
     * 数据查询
     **/
168
    const handleQueryDocs = () => {
169
      loading.value = true;
170
      level1.value = [];
171
      axios.get("/doc/query/" + route.query.ebookId).then((response) => {
172 173 174 175 176 177 178 179 180 181 182
        loading.value = false;
        const respData = response.data;

        if (respData.code == 0) {
          docs.value = respData.data;
          console.log("原始数组:", docs.value);

          level1.value = [];
          level1.value = Tool.array2Tree(docs.value, 0);
          console.log("树形结构:", level1);

183 184 185 186
          // 父文档下拉框初始化,相当于点击新增
          treeSelectData.value = Tool.copy(level1.value);
          // 为选择树添加一个"无"
          treeSelectData.value.unshift({id: 0, name: ''});
187 188 189 190 191 192 193 194 195 196
        } else {
          message.error(respData.msg);
        }
      });
    };

    /**
     * 根据表单提交的数据进行查询
     **/
    const handleQueryFormSubmit = () => {
197
      handleQueryDocs();
198 199 200 201
    };


    // -------- 表单 ---------
202
    const doc = ref();
203 204 205
    doc.value = {
      ebookId: route.query.ebookId  // 初始时便获得了该路由下的 ebookId
    };
206
    let textEditor: E;
207

208
    const handleSaveDoc = () => {
209 210
      doc.value.content = textEditor.txt.html();

211 212 213
      axios.post("/doc/save", doc.value).then((response) => {
        const respData = response.data;
        if (respData.code == 0) {
214
          message.success("保存成功");
215 216 217
        } else {
          message.error(respData.msg);
        }
218
        handleQueryDocs();
219 220 221
      })
    };

222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
    /**
     * 将某节点及其子孙节点全部置为disabled
     */
    const setDisable = (treeSelectData: any, id: any) => {
      // console.log(treeSelectData, id);
      // 遍历数组,即遍历某一层节点
      for (let i = 0; i < treeSelectData.length; i++) {
        const node = treeSelectData[i];
        if (node.id === id) {
          // 如果当前节点就是目标节点
          console.log("disabled", node);
          // 将目标节点设置为disabled
          node.disabled = true;

          // 遍历所有子节点,将所有子节点全部都加上disabled
          const children = node.children;
          if (Tool.isNotEmpty(children)) {
            for (let j = 0; j < children.length; j++) {
              setDisable(children, children[j].id)
            }
          }
        } else {
          // 如果当前节点不是目标节点,则到其子节点再找找看。
          const children = node.children;
          if (Tool.isNotEmpty(children)) {
            setDisable(children, id);
          }
        }
      }
    };

253 254 255 256 257 258 259 260 261 262 263 264 265 266
    /**
     * 内容查询
     **/
    const handleQueryContent = () => {
      axios.get("/doc/read-content/" + doc.value.id).then((response) => {
        const respData = response.data;
        if (respData.code === 0) {
          textEditor.txt.html(respData.data);
        } else {
          message.error(respData.msg);
        }
      });
    };

267

268 269 270 271
    /**
     * 编辑
     */
    const edit = (record: any) => {
272
      textEditor.txt.html("");  // 首先清空富文本框
273
      doc.value = Tool.copy(record);
274
      handleQueryContent();
275 276

      // 不能选择当前节点及其所有子孙节点,作为父节点,会使树断开
277
      treeSelectData.value = Tool.copy(level1.value) || [];
278 279 280 281
      setDisable(treeSelectData.value, record.id);

      // 为选择树添加一个"无"
      treeSelectData.value.unshift({id: 0, name: ''});
282 283 284 285 286 287
    };

    /**
     * 新增
     */
    const add = () => {
288
      textEditor.txt.html("");  // 首先清空富文本框
289
      let ebookId: string;
290 291 292
      doc.value = {
        ebookId: route.query.ebookId
      };
293
      treeSelectData.value = Tool.copy(level1.value) || [];
294 295 296

      // 为选择树添加一个"无"
      treeSelectData.value.unshift({id: 0, name: ''});
297 298
    }

299 300
    let docIdListToDelete: Array<string> = [];
    let docNameListToDelete: Array<string> = [];
301
    /**
302 303 304 305 306 307 308 309 310 311 312
     * 查找整根树枝
     */
    const getDeleteIds = (treeSelectData: any, id: any) => {
      // 遍历数组,即遍历某一层节点
      for (let i = 0; i < treeSelectData.length; i++) {
        const node = treeSelectData[i];
        if (node.id === id) {
          // 如果当前节点就是目标节点
          console.log("delete", node);
          // 将目标ID放入结果集ids
          // node.disabled = true;
313 314
          docIdListToDelete.push(id);
          docNameListToDelete.push(node.name);
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
          // 遍历所有子节点
          const children = node.children;
          if (Tool.isNotEmpty(children)) {
            for (let j = 0; j < children.length; j++) {
              getDeleteIds(children, children[j].id)
            }
          }
        } else {
          // 如果当前节点不是目标节点,则到其子节点再找找看。
          const children = node.children;
          if (Tool.isNotEmpty(children)) {
            getDeleteIds(children, id);
          }
        }
      }
    };

    /**
     * 删除用户选中的一个文档以及其所有子文档
     * @param docId 用户选中所要删除的文档的 id
335 336
     */
    const handleDeleteDoc = (docId: string) => {
337 338 339
      // 清空数组,否则多次删除时,数组会一直增加
      docIdListToDelete = [];
      docNameListToDelete = [];
340
      getDeleteIds(level1.value, docId);
341 342 343 344 345 346 347 348 349 350 351 352 353
      Modal.confirm({
        title: '重要提醒',
        icon: createVNode(ExclamationCircleOutlined),
        content: '将删除:【' + docNameListToDelete.join("") + "】删除后不可恢复,确认删除?",
        onOk() {
          console.log('deleteIds: ', docIdListToDelete);
          axios.delete("/doc/delete", {
            data: {
              ids: docIdListToDelete
            }
          }).then((response) => {
            const respData = response.data;
            if (respData.code == 0) {
354
              handleQueryDocs();
355 356 357
            }
          })
        },
358 359 360
      });
    }

361 362 363 364 365 366 367 368 369 370
    // ----------------富文本预览--------------
    const drawerVisible = ref(false);
    const previewHtml = ref();
    const handlePreviewContent = () => {
      previewHtml.value = textEditor.txt.html();
      drawerVisible.value = true;
    };
    const onDrawerClose = () => {
      drawerVisible.value = false;
    };
371 372

    onMounted(() => {
373
      handleQueryDocs();
374
      textEditor = new E('#content');
375 376
      textEditor.config.zIndex = 0;
      textEditor.create();
377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392
    });

    return {

      docQueryForm,
      level1,
      labelCol: { span: 4 },
      wrapperCol: { span: 14 },

      columns,
      loading,

      edit,
      add,
      handleDeleteDoc,
      handleQueryFormSubmit,
393
      handleSaveDoc,
394

395 396

      doc,
397
      treeSelectData,
398 399 400 401 402

      drawerVisible,
      previewHtml,
      handlePreviewContent,
      onDrawerClose,
403 404 405 406 407 408 409 410 411 412 413
    }
  }
});
</script>

<style scoped>
img {
  width: 50px;
  height: 50px;
}
</style>