Quellcode durchsuchen

管理中心-素材列表-移动到文件夹功能

任一存 vor 2 Jahren
Ursprung
Commit
959a013236

+ 56 - 3
packages/qjkankan-editor/src/api/index.js

@@ -1,7 +1,7 @@
 import { http,getToken } from '../utils/request'
 import config from '../config'
 import { $waiting } from "@/components/shared/loading";
-
+import { postOrderTraversal } from "@/utils/other.js";
 
 const number = function() {
     return config.projectNum
@@ -519,9 +519,40 @@ export function uploadMaterial(data, ok, no, onProgress) {
     return http.postJson(`${URL_FILL}/manage/fodder/update`, data, ok, no)
 }
 
+/**
+ * 文件夹相关
+ */
+
+function folderTreeSortRoutine(folderList) {
+  if (folderList.children) {
+    folderList.children.sort((a, b) => {
+      const createTimeA = new Date(a.createTime)
+      const createTimeB = new Date(b.createTime)
+      if (createTimeA.getTime() > createTimeB.getTime()) {
+        return -1
+      } else if (createTimeA.getTime() === createTimeB.getTime()) {
+        return 0
+      } else {
+        return 1
+      }
+    })
+  }
+}
+
 // 获取目录结构
-export function getFolderTree(data, ok, no) {
-  return http.getJson(`${URL_FILL}/manage/dir/getTree/${data.type}`, {}, ok, no)
+export function getFolderTree(data) {
+  return http.getJson(`${URL_FILL}/manage/dir/getTree/${data.type}`).then((res) => {
+    if (res.code === 0 && Array.isArray(res.data)) {
+      const temp = {
+        name: '根目录',
+        id: 1,
+        children: res.data
+      }
+      res.data = temp
+      postOrderTraversal(res.data, folderTreeSortRoutine)
+    }
+    return res
+  })
 }
 
 // 素材库中新增文件夹
@@ -534,6 +565,28 @@ export function renameFolder(data, ok, no) {
   return http.postJson(`${URL_FILL}/manage/dir/save`, data, ok, no)
 }
 
+// 移动素材或文件夹到一个文件夹
+export function moveToFolder(list, targetId) {
+  const param = {
+    dirIds: list.filter((item) => {
+      return item.type === 'dir'
+    }).map((item) => {
+      return item.id
+    }).join(','),
+    fodderIds: list.filter((item) => {
+      return item.type !== 'dir'
+    }).map((item) => {
+      return item.id
+    }).join(','),
+    parentId: targetId,
+  }
+  return http.postJson(`${URL_FILL}/manage/dir/move`, param)
+}
+
+/**
+ * end of 文件夹相关
+ */
+
 /**
  * 添加我的作品
  * @param {*} data 

BIN
packages/qjkankan-editor/src/assets/images/icons/folder-blue-small-close.png


BIN
packages/qjkankan-editor/src/assets/images/icons/folder-blue-small-open.png


+ 91 - 13
packages/qjkankan-editor/src/components/nestedFolder.vue

@@ -4,28 +4,33 @@
   >
     <div
       class="top-bar"
+      :class="{
+        highlight: targetId === folderInfo.id,
+        disabled: isDisabled,
+      }"
       @click="onClickTopBar"
       :style="{
         paddingLeft: topBarPaddingLeft,
       }"
     >
       <i class="iconfont icon-edit_input_arrow icon-expand" :class="isExpanded ? '' : 'collapsed'"></i>
-      <i v-show="isExpanded" class="iconfont icon-editor_folder_on folder_expanded"></i>
-      <i v-show="!isExpanded" class="iconfont icon-editor_folder_off folder_collapsed"></i>
-      <span class="group-name" v-title="$i18n.t(`zh_key.${groupNode.name}`).indexOf('zh_key')>-1?groupNode.name:$i18n.t(`zh_key.${groupNode.name}`)">{{
-        $i18n.t(`zh_key.${groupNode.name}`).indexOf('zh_key')>-1?groupNode.name:$i18n.t(`zh_key.${groupNode.name}`)}}
-      </span>
+      <img v-show="!isExpanded" class="folder collapsed" src="@/assets/images/icons/folder-blue-small-close.png" alt="" draggable="false">
+      <img v-show="isExpanded" class="folder opened" src="@/assets/images/icons/folder-blue-small-open.png" alt="" draggable="false">
+      <span class="group-name">{{folderInfo.name}}</span>
     </div>
 
     <div class="group-content" v-if="isExpanded">
         <div
-          v-for="(item, index) of groupNode.children"
+          v-for="(item, index) of folderInfo.children"
           :key=item.id
         >
           <component
-            :is="'SceneGroup'"
-            :groupNode="item"
+            :is="'NestedFolder'"
+            :folderInfo="item"
             :level="level + 1"
+            :targetId="targetId"
+            :disabledFolderList=disabledFolderList
+            @targeted="onSonTargeted"
           />
         </div>
     </div>
@@ -36,22 +41,31 @@
 import { mapGetters } from "vuex";
 
 export default {
-  name: 'SceneGroup',
+  name: 'NestedFolder',
   components: {
   },
   props: {
-    groupNode: {
+    folderInfo: {
       type: Object,
       required: true,
     },
     level: {
       type: Number,
       default: 1,
+    },
+    disabledFolderList: {
+      type: Array,
+      default: () => {
+        return []
+      },
+    },
+    targetId: {
+      required: true,
     }
   },
   data() {
     return {
-      isExpanded: false,
+      isExpanded: this.level === 1 ? true : false,
     }
   },
   computed: {
@@ -61,11 +75,22 @@ export default {
     sceneItemPaddingLeft() {
       return 18 + this.level * 12 + 'px' 
     },
+    isDisabled() {
+      return this.disabledFolderList.some((item) => {
+        return item.id === this.folderInfo.id
+      })
+    },
   },
   methods: {
     onClickTopBar() {
-      this.isExpanded = !this.isExpanded
+      if (!this.isDisabled) {
+        this.isExpanded = !this.isExpanded
+        this.$emit('targeted', this.folderInfo.id)
+      }
     },
+    onSonTargeted(id) {
+      this.$emit('targeted', id)
+    }
   },
   mounted() {
   },
@@ -74,4 +99,57 @@ export default {
 }
 </script>
 
-<style lang="less" scoped></style>
+<style lang="less" scoped>
+.scene-group {
+  user-select: none;
+  .top-bar {
+    position: relative;
+    height: 40px;
+    border-radius: 4px;
+    display: flex;
+    align-items: center;
+    padding-right: 10px;
+    &:hover {
+    }
+    &.highlight {
+      background: #ebedf0;
+    }
+    &.disabled {
+      opacity: 0.5;
+    }
+    > .icon-expand {
+      display: inline-block;
+      font-size: 12px;
+      transform: scale(0.7);
+      color: rgba(130, 134, 142, 1);
+      flex: 0 0 auto;
+      &.collapsed {
+        display: inline-block;
+        transform: scale(0.7) rotate(-90deg);
+      }
+    }
+    > img.folder {
+      margin-left: 7px;
+      flex: 0 0 auto;
+      width: 16px;
+      &.collapsed {
+        height: 16px;
+        }
+      &.opened {
+        height: 14px;
+      }
+    }
+    > .group-name {
+      margin-left: 6px;
+      display: inline-block;
+      text-overflow: ellipsis;
+      overflow: hidden;
+      white-space: nowrap;
+      flex: 1 1 auto;
+      font-size: 14px;
+      color: #323233;
+      cursor: default;
+    }
+  }
+}
+</style>

+ 0 - 94
packages/qjkankan-editor/src/components/pulldownMenuInEditor copy.vue

@@ -1,94 +0,0 @@
-<template>
-  <div class="pull-down-menu-in-editor" v-clickoutside="onClickOutside">
-    <button class="menu-cover" @click="isExpand = !isExpand">
-      {{placeholder ? placeholder : $i18n.t(`edit_settings.${value}`)}}
-      <i class="iconfont icon-material_preview_upload_collect" :class="{flip: isExpand}"></i>
-    </button>
-    <div class="menu" v-show="isExpand">
-      <button v-for="(item, index) of valueList" :key="index"
-        @click="onSelect(item)"
-      >
-        {{$i18n.t(`edit_settings.${item}`)}}
-      </button>
-    </div>
-  </div>
-</template>
-
-<script>
-export default {
-  props: {
-    valueList: {
-      type: Array,
-      default: function() {
-        return [
-        '111',
-        '222',
-        ]
-      },
-    },
-    placeholder: {
-      type: String,
-      default: '',
-    },
-    value: {
-      type: String,
-      required: true,
-    }
-  },
-  data() {
-    return {
-      isExpand: false,
-    }
-  },
-  methods: {
-    onClickOutside() {
-      if (this.isExpand) {
-        this.isExpand = false
-      }
-    },
-    onSelect(item) {
-      console.log(item,'ads')
-      this.isExpand = false
-      this.$emit('input', this.$i18n.t(`edit_settings.${item}`))
-    }
-  }
-}
-</script>
-
-<style lang="less" scoped>
-  button {
-    background: #252526;
-    border: 1px solid #404040;
-    height: 36px;
-    color: #fff;
-    width: 100%;
-  }
-.pull-down-menu-in-editor {
-  width: 140px;
-  position: relative;
-  > button.menu-cover {
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-    padding: 16px;
-    cursor: pointer;
-    > .icon-material_preview_upload_collect {
-      font-size: 11px;
-      color: rgba(255, 255, 255, 0.6);
-      border-radius: 2px;
-      &.flip {
-        transform: translateY(-2px) rotate(180deg);
-      }
-    }
-  }
-  > .menu {
-    position: absolute;
-    width: 100%;
-    > button {
-      display: block;
-      border-top: none;
-      cursor: pointer;
-    }
-  }
-}
-</style>

+ 9 - 0
packages/qjkankan-editor/src/utils/other.js

@@ -200,4 +200,13 @@ export function isSameObject(object1, object2) {
 
 export function ossImagePreviewUrlSuffix(downScaleRate = 10) {
   return `?x-oss-process=image/resize,p_${downScaleRate}&${Math.random()}`
+}
+
+export function postOrderTraversal(root, routine) {
+  if (root.children && Array.isArray(root.children)) {
+    for (const child of root.children) {
+      postOrderTraversal(child, routine)
+    }
+  }
+  routine(root)
 }

+ 9 - 2
packages/qjkankan-editor/src/views/material/audio/index.vue

@@ -35,6 +35,7 @@
       </button>
       <button
         class="ui-button cancel"
+        :class="{disable: selectedList.length === 0}"
         @click="onClickMoveFolder"
       >
         {{$i18n.t(`gather.move_folder`)}}
@@ -55,7 +56,7 @@
       <tableList
         @selection-change="
           (data) => {
-            selectedArr = data;
+            selectedList = data;
           }
         "
         @request-more-data="getMoreMaterialItem"
@@ -149,6 +150,13 @@
       @close="isShowRenameFolder = false"
       @submit="onSubmitRenameFolder"
     />
+    <MoveFolder
+      v-if="isShowMoveFolder"
+      :folderTree="folderTree"
+      :selectedList="selectedList"
+      @close="isShowMoveFolder = false"
+      @submit="onSubmitMoveFolder"
+    />
     <rename
       v-if="showRename"
       :item="popupItem"
@@ -215,7 +223,6 @@ export default {
       showList: false,
       popupItem: null,
       tabHeader: data,
-      selectedArr: [],
       
       // 因为searchKey的变化经过debounce、异步请求的延时,才会反映到数据列表的变化上,所以是否显示、显示哪种无数据提示,也要等到数据列表变化后,根据数据列表是否为空,以及引发本次变化的那个searchKey瞬时值来决定。本变量就是用来保存那个瞬时值。
       lastestUsedSearchKey: '',

+ 16 - 3
packages/qjkankan-editor/src/views/material/folderMixinFactory.js

@@ -1,11 +1,13 @@
 import CreateFolder from "./popup/CreateFolder";
 import RenameFolder from "./popup/RenameFolder";
+import MoveFolder from "./popup/MoveFolder";
 
 import {
   getMaterialList,
   getFolderTree,
   createFolder as createFolderApi,
   renameFolder as renameFolderApi,
+  moveToFolder,
 } from "@/api";
 import {i18n} from "@/lang"
 
@@ -14,11 +16,14 @@ export default function(materialType) {
     components: {
       CreateFolder,
       RenameFolder,
+      MoveFolder,
     },
     data() {
       return {
+        selectedList: [],
         isShowNewFolder: false,
         isShowRenameFolder: false,
+        isShowMoveFolder: false,
         folderTree: null,
         folderPath: [
           {
@@ -145,29 +150,37 @@ export default function(materialType) {
         }
       },
       onClickMoveFolder() {
-        console.log('asdfsf');
         getFolderTree({
           type: materialType,
         }).then((res) => {
           this.folderTree = res.data
-          if (true) {
+          if (this.folderTree.length === 0) {
             this.$confirm({
               title: this.$i18n.t('gather.move_folder_to'),
               content: this.$i18n.t('gather.no_folder_need_create'),
             })
           } else {
-  
+            this.isShowMoveFolder = true
           }
         })
       },
+      onSubmitMoveFolder(targetFolderId) {
+        moveToFolder(this.selectedList, targetFolderId).then(() => {
+          this.$msg.success(this.$i18n.t('gather.success'))
+          this.isShowMoveFolder = false
+          this.refreshListDebounced()
+        })
+      },
       onClickPath(idx) {
         this.folderPath = this.folderPath.slice(0, idx + 1)
+        this.selectedList = []
       },
       onClickFolder(folder) {
         this.folderPath.push({
           name: folder.name,
           id: folder.id,
         })
+        this.selectedList = []
       },
     }
   }

+ 9 - 2
packages/qjkankan-editor/src/views/material/image/index.vue

@@ -35,6 +35,7 @@
       </button>
       <button
         class="ui-button cancel"
+        :class="{disable: selectedList.length === 0}"
         @click="onClickMoveFolder"
       >
         {{$i18n.t(`gather.move_folder`)}}
@@ -55,7 +56,7 @@
       <tableList
         @selection-change="
           (data) => {
-            selectedArr = data;
+            selectedList = data;
           }
         "
         @request-more-data="getMoreMaterialItem"
@@ -143,6 +144,13 @@
       @close="isShowRenameFolder = false"
       @submit="onSubmitRenameFolder"
     />
+    <MoveFolder
+      v-if="isShowMoveFolder"
+      :folderTree="folderTree"
+      :selectedList="selectedList"
+      @close="isShowMoveFolder = false"
+      @submit="onSubmitMoveFolder"
+    />
     <rename
       v-if="showRename"
       :item="popupItem"
@@ -216,7 +224,6 @@ export default {
       showList: false,
       popupItem: null,
       tabHeader: data,
-      selectedArr: [],
       // 因为searchKey的变化经过debounce、异步请求的延时,才会反映到数据列表的变化上,所以是否显示、显示哪种无数据提示,也要等到数据列表变化后,根据数据列表是否为空,以及引发本次变化的那个searchKey瞬时值来决定。本变量就是用来保存那个瞬时值。
       lastestUsedSearchKey: '',
       isFilterFocus: false,

+ 9 - 5
packages/qjkankan-editor/src/views/material/pano/index.vue

@@ -24,6 +24,7 @@
       </button>
       <button
         class="ui-button cancel"
+        :class="{disable: selectedList.length === 0}"
         @click="onClickMoveFolder"
       >
         {{$i18n.t(`gather.move_folder`)}}
@@ -40,7 +41,7 @@
       <tableList
         @selection-change="
           (data) => {
-            selectedArr = data;
+            selectedList = data;
           }
         "
         @request-more-data="getMoreMaterialItem"
@@ -157,6 +158,13 @@
       @close="isShowRenameFolder = false"
       @submit="onSubmitRenameFolder"
     />
+    <MoveFolder
+      v-if="isShowMoveFolder"
+      :folderTree="folderTree"
+      :selectedList="selectedList"
+      @close="isShowMoveFolder = false"
+      @submit="onSubmitMoveFolder"
+    />
     <rename
       v-if="showRename" 
       :item="popupItem"
@@ -242,7 +250,6 @@ export default {
       showList: false,
       popupItem: null,
       tabHeader: data,
-      selectedArr: [],
 
       // 因为searchKey的变化经过debounce、异步请求的延时,才会反映到数据列表的变化上,所以是否显示、显示哪种无数据提示,也要等到数据列表变化后,根据数据列表是否为空,以及引发本次变化的那个searchKey瞬时值来决定。本变量就是用来保存那个瞬时值。
       lastestUsedSearchKey: '',
@@ -267,17 +274,14 @@ export default {
     }
   },
   mounted() {
-    console.log('mounted!!!!!');
   },
   watch: {
     needLongPolling: {
       handler: function (newVal) {
         if (!newVal) {
-          console.log('dont need longpolling!!!!!!');
           this.clearinter();
         } else {
           this.clearinter();
-          console.log('need longpolling!!!!!');
           this.interval = setInterval(() => {
             this._checkMStatus();
           }, LONG_POLLING_INTERVAL * 1000);

+ 122 - 0
packages/qjkankan-editor/src/views/material/popup/MoveFolder.vue

@@ -0,0 +1,122 @@
+<template>
+  <popup>
+    <div class="ui-message ui-message-confirm">
+      <div class="ui-message-header">
+        <span>{{$i18n.t(`gather.move_folder_to`)}}</span>
+        <span @click="$emit('close')">
+          <i class="iconfont icon_close"></i>
+        </span>
+      </div>
+      
+      <div class="folder-tree">
+        <NestedFolder
+          :folderInfo="folderTree"
+          :level="1"
+          :targetId="targetFolderId"
+          :disabledFolderList="selectedList"
+          @targeted="onFolderTargetd"
+        ></NestedFolder>
+      </div>
+      
+      <div class="ui-message-footer">
+        <div class="btn">
+          <button @click="$emit('close')" class="ui-button ui-button-rect cancel">
+            {{$i18n.t(`gather.cancel`)}}
+          </button>
+          <button
+            @click="onClickConfirm"
+            class="ui-button ui-button-rect submit"
+            :class="{disable: !targetFolderId || targetFolderId === selectedList[0].dirId}"
+          >
+            {{$i18n.t(`gather.comfirm`)}}
+          </button>
+        </div>
+      </div>
+    </div>
+  </popup>
+</template>
+
+<script>
+import Popup from "@/components/shared/popup";
+import NestedFolder from "@/components/NestedFolder.vue";
+
+export default {
+  components: {
+    Popup,
+    NestedFolder
+  },
+  props: {
+    folderTree: {
+      type: Object,
+      required: true,
+    },
+    selectedList: {
+      type: Array,
+      default: () => {
+        return []
+      },
+    },
+  },
+  data() {
+    return {
+      targetFolderId: '',
+    }
+  },
+  watch: {
+  },
+  mounted() {
+  },
+  methods: {
+    onClickConfirm() {
+      this.$emit('submit', this.targetFolderId)
+    },
+    onFolderTargetd(id) {
+      this.targetFolderId = id
+    }
+  }
+}
+</script>
+
+<style lang="less" scoped>
+
+
+.ui-message-confirm {
+  width: 600px;
+  height: 642px;
+
+  .ui-message-header {
+    .icon_close {
+      color: #969799;
+    }
+  }
+
+  .folder-tree {
+    margin-top: 40px;
+    padding-top: 16px;
+    padding-bottom: 16px;
+    width: 548px;
+    height: 450px;
+    border: 1px solid #EBEDF0;
+    overflow: auto;
+    text-align: left;
+  }
+
+  .ui-message-footer {
+    margin-top: 40px;
+    width: 100%;
+    .btn {
+      display: flex;
+      justify-content: flex-end;
+
+      .ui-button {
+        max-width: 104px
+      }
+    }
+
+  }
+}
+</style>
+
+<style lang="less" scoped>
+@import '../style.less';
+</style>

+ 9 - 2
packages/qjkankan-editor/src/views/material/video/index.vue

@@ -35,6 +35,7 @@
       </button>
       <button
         class="ui-button cancel"
+        :class="{disable: selectedList.length === 0}"
         @click="onClickMoveFolder"
       >
         {{$i18n.t(`gather.move_folder`)}}
@@ -55,7 +56,7 @@
       <tableList
         @selection-change="
           (data) => {
-            selectedArr = data;
+            selectedList = data;
           }
         "
         @request-more-data="getMoreMaterialItem"
@@ -149,6 +150,13 @@
       @close="isShowRenameFolder = false"
       @submit="onSubmitRenameFolder"
     />
+    <MoveFolder
+      v-if="isShowMoveFolder"
+      :folderTree="folderTree"
+      :selectedList="selectedList"
+      @close="isShowMoveFolder = false"
+      @submit="onSubmitMoveFolder"
+    />
     <rename
       v-if="showRename"
       :item="popupItem"
@@ -219,7 +227,6 @@ export default {
       showList: false,
       popupItem: null,
       tabHeader: data,
-      selectedArr: [],
 
       // 因为searchKey的变化经过debounce、异步请求的延时,才会反映到数据列表的变化上,所以是否显示、显示哪种无数据提示,也要等到数据列表变化后,根据数据列表是否为空,以及引发本次变化的那个searchKey瞬时值来决定。本变量就是用来保存那个瞬时值。
       lastestUsedSearchKey: '',