任一存 2 years ago
parent
commit
a3def0e9b9

+ 779 - 0
packages/qjkankan-editor/src/components/materialListInMaterialSelector.vue

@@ -0,0 +1,779 @@
+<template>
+  <div class="material-list">
+    <crumbs
+      class="crumbs"
+      v-if="!latestUsedSearchKey"
+      :list="folderPath"
+      :rootName="$i18n.t(`gather.${materialType}`)"
+      @click-path="onClickPath"
+    />
+    <div v-if="latestUsedSearchKey" class="crumbs">{{$i18n.t(`gather.${materialType}`)}}</div>
+
+    <div class="table table-pano">
+      <div class="table-head-row">
+        <span class="table-head">1</span>
+        <span class="table-head" v-for="(item, i) in tableHeaders" :key="i">
+          {{ item.name && $i18n.t(`zh_key.${item.name}`) }}
+        </span>
+      </div>
+      <div
+        v-show="listLocalLength !== 0 || hasMoreData"
+        class="table-body"
+        v-infinite-scroll="requestMoreData"
+        :infinite-scroll-disabled="!hasMoreData || isRequestingMoreData"
+      >
+        <!-- vuex中的上传中数据 -->
+        <div v-for="(item, i) in uploadStatusList" :key="item.uid">
+          <div
+            class="table-body-row"
+            v-if="item.parentFolderId === currentFolderId"
+            @click="onClickRow"
+          >
+            <!-- 如果已经上传成功 -->
+            <template v-if="item.status === 'SUCCESS'">
+              <span class="table-data">
+                <RadioOrCheckbox
+                  class="checkbox"
+                  :isLightTheme="true"
+                  :isMultiSelection="isMultiSelection"
+                  :isCheckedInitial="select.some(i => i.id === item.successInfo.id)"
+                  @change="v => selectItem(item.successInfo, v)"
+                />
+              </span>
+              <span class="table-data" v-for="(tableItemStructure, idx) in tableHeaders" :key="idx">
+                <div v-if="tableItemStructure.type == 'image'" class="list-img">
+                  <img
+                    :src="item.successInfo[tableItemStructure.key] + (Number(item.fileSize) > 512 ? $imgsuffix : ``)"
+                    alt=""
+                  />
+                </div>
+                <span
+                  v-else
+                  class="ellipsis"
+                  v-title="tableItemStructure.key === 'name' ? item.successInfo[tableItemStructure.key] : ''"
+                >
+                  {{item.successInfo[tableItemStructure.key]}}
+                </span>
+              </span>
+            </template>
+            <!-- 如果还在上传或切图处理中 -->
+            <template v-else-if="item.status === 'LOADING'">
+              <span class="table-data">
+                <div class="checkbox">
+                  <span class="for-outer-circle"></span>
+                  <span class="for-inner-circle disabled"></span>
+                </div>
+              </span>
+              <span class="table-data" v-for="(tableItemStructure, idx) in tableHeaders" :key="idx">
+                <div v-if="tableItemStructure.type == 'image'" class="list-img">
+                  <img src="@/assets/images/icons/upload-file-type-icon-image@2x.png" alt="">
+                </div>
+                <span
+                  v-if="tableItemStructure.key === 'name'"
+                  class="ellipsis"
+                  v-title="tableItemStructure.key === 'name' ? item.title : ''"
+                >
+                  {{ item.title }}
+                </span>
+                <span v-if="tableItemStructure.key === 'fileSize' && item.ifKnowProgress">
+                  {{$i18n.t(`gather.upload_material`)}} {{ Math.round(item.progress * 100)}}%
+                </span>
+                <span v-if="tableItemStructure.key === 'fileSize' && !item.ifKnowProgress">{{ item.statusText }}</span>
+                <span v-if="tableItemStructure.key !== 'name' && tableItemStructure.key !== 'fileSize'"></span>
+              </span>
+            </template>
+            <!-- 如果上传失败了 -->
+            <template v-else-if="item.status === 'FAIL'">
+              <span class="table-data">
+                <div class="checkbox">
+                  <span class="for-outer-circle"></span>
+                  <span class="for-inner-circle disabled"></span>
+                </div>
+              </span>
+              <span class="table-data" v-for="(tableItemStructure, idx) in tableHeaders" :key="idx">
+                <div v-if="tableItemStructure.type == 'image'" class="list-img">
+                  <img src="@/assets/images/icons/upload-file-type-icon-image@2x.png" alt="">
+                </div>
+                <span
+                  v-if="tableItemStructure.key === 'name'"
+                  class="ellipsis"
+                  v-title="tableItemStructure.key === 'name' ? item.title : ''"
+                >
+                  {{ item.title }}
+                </span>
+                <span v-if="tableItemStructure.key === 'fileSize'">{{ $i18n.t(`tips_code.FAILURE_3025`) }}</span>
+                <span v-if="tableItemStructure.key !== 'name' && tableItemStructure.key !== 'fileSize'"></span>
+              </span>
+            </template>
+          </div>
+        </div>
+        <!-- 本组件内的列表数据 -->
+        <div
+          class="table-body-row"
+          v-for="(item, i) in list"
+          :key="i"
+          @click="(e) => {
+            if (item.type === 'dir') {
+              onClickFolder(item)
+            } else {
+              onClickRow(e)
+            }
+          }"
+        >
+          <span class="table-data">
+            <RadioOrCheckbox
+              v-if="item.type !== 'dir'"
+              class="checkbox"
+              :isLightTheme="true"
+              :isMultiSelection="isMultiSelection"
+              :isCheckedInitial="select.some(i => i.id === item.id)"
+              @change="v => selectItem(item, v)"
+            />
+          </span>
+          <span
+            class="table-data"
+            v-for="(tableItemStructure, idx) in tableHeaders"
+            :key="idx"
+          >
+            <div
+              v-if="tableItemStructure.type == 'image'"
+              class="list-img"
+            >
+              <img
+                :class="{
+                  folderIcon: item.type === 'dir',
+                }"
+                :src="(item.type === 'dir') ? require('@/assets/images/icons/folder-blue.png') : (item[tableItemStructure.key] + (Number(item.fileSize) > 512 ? $imgsuffix : ``))"
+                alt=""
+              />
+            </div>
+            <span
+              class="ellipsis"
+              v-else v-title="tableItemStructure.key === 'name' ? item[tableItemStructure.key] : ''"
+            >
+              {{ item[tableItemStructure.key] }}
+            </span>
+          </span>
+        </div>
+      </div>
+      <!-- 无数据时的提示 -->
+      <div v-show="!(listLocalLength !== 0 || hasMoreData)" class="no-data">
+        <div v-if="latestUsedSearchKey">
+          <img :src="require('@/assets/images/default/empty_04_search.png')" alt="">
+          <span>{{ $i18n.t("gather.no_serch_result") }}</span>
+        </div>
+        <div v-if="!latestUsedSearchKey">
+          <img :src="require('@/assets/images/default/empty_04.png')" alt="">
+          <span>{{ $i18n.t("gather.no_material_result") }}</span>
+        </div>
+      </div>
+    </div>
+
+    <div class="btns">
+      <button v-if="canUpload" class="ui-button upload-btn" @click="onClickUpload">
+        <span>{{ $i18n.t("gather.upload_material") }}</span>
+        <i
+          class="iconfont icon-material_prompt tool-tip-for-editor"
+          v-tooltip="fileInputBtnTip"
+        />
+        <FileInput
+          ref="file-input"
+          :failString="fileInputFailString"
+          :limitFailStr="fileInputLimitFailStr"
+          :acceptType="fileInputAcceptType"
+          :mediaType="fileInputMediaType"
+          :limit="fileInputLimit"
+          @file-change="onFileInputChange"
+        />
+      </button>
+    </div>
+  </div>
+</template>
+
+<script>
+import {
+  getMaterialList,
+  uploadMaterial,
+  checkMStatus,
+  checkUserSize,
+} from "@/api";
+
+import { getImgWH, changeByteUnit } from "@/utils/file";
+import { debounce, capitalize } from "@/utils/other.js"
+
+import config from "@/config";
+
+import FileInput from "@/components/shared/uploads/UploadMultiple.vue";
+import RadioOrCheckbox from "@/components/shared/RadioOrCheckbox.vue";
+
+import folderMixin from "./materialSelectorFolderMixin.js";
+
+export default {
+  components: {
+    FileInput,
+    RadioOrCheckbox,
+  },
+  mixins: [
+    folderMixin,
+  ],
+  props: {
+    currentMaterialType: {
+      type: String,
+      required: true,
+    },
+    materialType: {
+      type: String,
+      required: true,
+    },
+    
+    tableHeaderKeyList: {
+      type: Array,
+      default: () => {
+        return ['icon', 'name', 'fileSize']
+      }
+    },
+    
+    isMultiSelection: {
+      type: Boolean,
+      default: false,
+    },
+    select: {
+      type: Array,
+      required: true,
+    },
+    
+    searchKey: {
+      type: String,
+      default: '',
+    },
+
+    canUpload: {
+      type: Boolean,
+      default: true,
+    },
+    uploadStatusList: {
+      type: Array,
+      default: () => {
+        return []
+      },
+    },
+    fileInputBtnTip: {
+      type: String,
+      required: true,
+    },
+    fileInputFailString: {
+      type: String,
+      required: true,
+    },
+    fileInputLimitFailStr: {
+      type: String,
+      required: true,
+    },
+    fileInputAcceptType: {
+      type: String,
+      required: true,
+    },
+    fileInputMediaType: {
+      type: String,
+      required: true,
+    },
+    fileInputLimit: {
+      type: Number,
+      required: true,
+    },
+  },
+  data() {
+    return {
+      list: [],
+
+      latestUsedSearchKey: '',
+
+      isRequestingMoreData: false,
+      hasMoreData: true,
+
+      longPollingIntervalId: null,
+    }
+  },
+  computed: {
+    tableHeaders() {
+      /*
+      [
+        {
+          en: "素材", // 暂时没用到
+          key: "icon",
+          name: "素材",
+          type: "image",
+          width: 150, // 暂时没用到
+        },
+        {
+          en: "名称",
+          key: "name",
+          name: "名称",
+          width: 240,
+        },
+        {
+          en: "大小",
+          key: "fileSize",
+          name: "大小",
+          width: 80,
+        }
+      ]
+       */
+      return this.$MAPTABLEHEADER[this.materialType].filter(item => {
+        return this.tableHeaderKeyList.includes(item.key)
+      })
+    },
+    listRealLength() {
+      return this.list.length + this.uploadStatusList.filter((item) => {
+        return item.status === 'SUCCESS'
+      }).length
+    },
+    listLocalLength() {
+      return this.list.length + this.uploadStatusList.length
+    },
+    needLongPolling() {
+      return this.uploadStatusList.some((item) => {
+        return item.status === 'LOADING' && item.ifKnowProgress === false
+      })
+    },
+  },
+  watch: {
+    searchKey: {
+      handler: function () {
+        if (this.currentMaterialType === this.materialType) {
+          this.refreshMaterialList()
+        }
+      },
+      immediate: false,
+    },
+    currentMaterialType: {
+      handler: function (newVal) {
+        if (newVal === this.materialType && this.list.length === 0) {
+          this.refreshMaterialList()
+        }
+      },
+      immediate: false,
+    },
+    needLongPolling: {
+      handler: function (newVal) {
+        if (!newVal) {
+          clearInterval(this.longPollingIntervalId)
+          this.longPollingIntervalId = null
+        } else {
+          clearInterval(this.longPollingIntervalId)
+          this.longPollingIntervalId = null
+          this.longPollingIntervalId = setInterval(() => {
+            this._checkMStatus();
+          }, 3000);
+        }
+      },
+      immediate: true,
+    },
+  },
+  methods: {
+    selectItem(item, v) {
+      item.materialType = this.materialType // 三维场景数据没有type字段来表明自己是三维场景。所以统一加一个字段。
+      if (this.isMultiSelection) {
+        if (v) {
+          this.select.push(item)
+        } else {
+          const toDeleteIdx = this.select.findIndex((eachSelect) => {
+            return eachSelect.id === item.id
+          })
+          if (toDeleteIdx >= 0) {
+            this.select.splice(toDeleteIdx, 1)
+          }
+        }
+      } else {
+        this.select.splice(0, this.select.length)
+        if (v) {
+          this.select.push(item)
+        }
+      }
+    },
+    requestMoreData() {
+      this.isRequestingMoreData = true
+      const latestUsedSearchKey = this.searchKey
+      getMaterialList(
+        {
+          dirId: this.currentFolderId,
+          pageNum: Math.floor(this.listRealLength / config.PAGE_SIZE) + 1,
+          pageSize: config.PAGE_SIZE,
+          searchKey: this.searchKey,
+          type: this.materialType,
+        },
+        (data) => {
+          const newData = data.data.list.map((i) => {
+            if (i.fileSize) {
+              i.fileSize = changeByteUnit(Number(i.fileSize));
+            } else {
+              i.fileSize = ''
+            }
+            i.createTime = i.createTime.substring(0, i.createTime.length - 3)
+            i.updateTime = i.updateTime.substring(0, i.updateTime.length - 3)
+            return i;
+          });
+          this.list = this.list.concat(newData)
+          if (this.listRealLength === data.data.total) {
+            this.hasMoreData = false
+          }
+          this.isRequestingMoreData = false
+          this.latestUsedSearchKey = latestUsedSearchKey
+        },
+        () => {
+          this.isRequestingMoreData = false
+          this.latestUsedSearchKey = latestUsedSearchKey
+        }
+      );
+    },
+    refreshMaterialList: debounce(function (type) {
+      this.isRequestingMoreData = false
+      this.hasMoreData = true
+      this.list = []
+      let filterResult = this.uploadStatusList.filter((item) => {
+        return item.status === 'LOADING'
+      })
+      const capitalizedMaterialType = capitalize(this.materialType)
+      this.$store.commit(`setUploadStatusList${capitalizedMaterialType}`, filterResult)
+      this.requestMoreData()
+    }, 500, false),
+    onFileInputChange(e) {
+      e.files.forEach(async (eachFile, i) => {
+        if (
+          eachFile.type.indexOf("jpeg") <= -1
+        ) {
+          console.log('格式不对!');
+          setTimeout(() => {
+            this.$msg({
+              message: `“${eachFile.name}”${this.fileInputFailString}`,
+              type: "warning",
+            });
+          }, i * 100);
+          return;
+        }
+
+        if (eachFile.name.substring(0, eachFile.name.lastIndexOf(".")).length > 50) {
+          setTimeout(() => {
+            this.$msg({
+              message: `“${eachFile.name}”${this.$i18n.t(`gather.too_long_word`)}`,
+              type: "warning",
+            });
+          }, i * 100);
+          return;
+        }
+
+        let WHRate = null
+        try {
+          const { width, height } = await getImgWH(eachFile)
+          WHRate = width / height
+        } catch (e) {
+          console.error('获取图像宽高失败:', e)
+          setTimeout(() => {
+            this.$msg({
+              message: `“${eachFile.name}”${this.fileInputFailString}`,
+              type: "warning",
+            });
+          }, i * 100);
+          return
+        }
+        if (WHRate !== 2) {
+          setTimeout(() => {
+            this.$msg({
+              message: `“${eachFile.name}”${this.fileInputFailString}`,
+              type: "warning",
+            });
+          }, i * 100);
+          return
+        }
+
+        let itemInUploadList = {
+          title: eachFile.name,
+          ifKnowProgress: true,
+          progress: 0,
+          status: 'LOADING',
+          statusText: this.$i18n.t(`gather.uploading_material`),
+          uid: `u_${this.$randomWord(true, 8, 8)}`,
+          abortHandler: null,
+          backendId: '',
+          parentFolderId: this.currentFolderId,
+        };
+
+        itemInUploadList.abortHandler = uploadMaterial(
+          {
+            dirId: this.currentFolderId,
+            file: eachFile,
+            tempId: itemInUploadList.uid,
+            type: this.materialType,
+          },
+          (response) => { // 上传成功
+            if (response.code !== 0) {
+              return
+            }
+            console.log('全景图上传成功');
+            itemInUploadList.statusText = this.$i18n.t(`gather.cutting`)
+            itemInUploadList.ifKnowProgress = false
+            itemInUploadList.backendId = response.data.id
+          },
+          (err) => {
+            if (err.statusText === 'abort') { // 用户取消了上传任务。
+              console.log('用户取消了任务!');
+              const index = this.uploadStatusList.findIndex((eachItem) => {
+                return eachItem.uid === itemInUploadList.uid
+              })
+              this.uploadStatusList.splice(index, 1)
+            } else {
+              console.log('全景图上传失败!');
+              itemInUploadList.status = 'FAIL'
+              itemInUploadList.statusText = this.$i18n.t(`gather.material_upload_fail`)
+            }
+          },
+          (progress) => {
+            console.log('进度:', progress);
+            itemInUploadList.progress = progress
+          }
+        )
+
+        this.uploadStatusList.unshift(itemInUploadList);
+      })
+    },
+    _checkMStatus() {
+      let needPollingTaskList = this.uploadStatusList.filter((item) => item.status === 'LOADING' && item.ifKnowProgress === false);
+      if (needPollingTaskList.length > 0) {
+        checkMStatus(
+          {
+            ids: needPollingTaskList.map((item) => item.backendId),
+            islongpolling: true,
+          },
+          (res) => {
+            // 1切图中,2失败,3成功
+            res.data.forEach(eachRes => {
+              if (eachRes.status === 2) {
+                const index = this.uploadStatusList.findIndex(eachTask => eachTask.backendId === eachRes.id)
+                if (index >= 0) {
+                  const targetItem = this.uploadStatusList[index]
+                  targetItem.status = 'FAIL'
+                  targetItem.statusText = this.$i18n.t(`gather.material_cutting_fail`)
+                  targetItem.ifKnowProgress = true
+                }
+              } else if (eachRes.status === 3) {
+                const index = this.uploadStatusList.findIndex(eachTask => eachTask.backendId === eachRes.id)
+                if (index >= 0) {
+                  const targetItem = this.uploadStatusList[index]
+                  targetItem.status = 'SUCCESS'
+                  if (eachRes.fileSize) {
+                    eachRes.fileSize = changeByteUnit(Number(eachRes.fileSize));
+                  } else {
+                    eachRes.fileSize = ''
+                  }
+                  targetItem.successInfo = eachRes
+                }
+              }
+            });
+          }
+        );
+      }
+    },
+    onClickRow(e) {
+      const checkboxNodeList = e.currentTarget.getElementsByClassName('selection-click-target')
+      if (checkboxNodeList && checkboxNodeList[0]) {
+        checkboxNodeList[0].click()
+      }
+    },
+    onClickUpload() {
+      checkUserSize({}, (data) => {
+        //判断已用是否大于3G
+        if ((data.data / 1024 / 1024) > 3) {
+          this.$alert({ content: this.$i18n.t('tips_code.FAILURE_3024')});
+        } else {
+          this.$refs['file-input'].click()
+        }
+      })
+    },
+  },
+  mounted() {
+  },
+  beforeDestroy() {
+    const capitalizedMaterialType = capitalize(this.materialType)
+    this.$store.commit(`setUploadStatusList${capitalizedMaterialType}`, this.uploadStatusList.filter((item) => {
+      return item.status === 'LOADING'
+    }))
+  }
+}
+</script>
+
+<style lang="less" scoped>
+.material-list {
+  .ellipsis {
+    text-overflow: ellipsis;
+    overflow: hidden;
+    white-space: nowrap;
+    width: 100%;
+    display: inline-block;
+  }
+
+  .table-select {
+    position: absolute;
+    z-index: 3;
+    left: 50%;
+    top: 50%;
+    transform: translateX(-50%) translateY(-50%);
+    width: 600px;
+    height: 730px;
+    border-radius: 4px;
+    border: 1px solid #EBEDF0;
+    padding: 26px;
+    background: #fff;
+  }
+
+  .crumbs {
+    margin-top: 10px;
+  }
+
+  @table-height: 420px;
+  @table-head-row-height: 40px;
+  @table-border-size: 1px;
+
+  .table {
+    margin-top: 10px;
+    border: @table-border-size solid #EBEDF0;
+    background: #ffffff;
+    width: 100%;
+    height: @table-height;
+
+    >.table-head-row {
+      width: 100%;
+      height: @table-head-row-height;
+      background: #F5F7FA;
+      color: #646566;
+
+      .table-head {
+        font-size: 16px;
+        line-height: @table-head-row-height;
+        height: 100%;
+        display: inline-block;
+      }
+    }
+
+    >.table-body {
+      height: calc(@table-height - @table-head-row-height - @table-border-size - @table-border-size);
+      overflow: auto;
+      display: inline-block;
+      width: 100%;
+
+      .table-body-row {
+        height: 50px;
+        border-bottom: 1px solid #EBEDF0;
+        display: flex;
+        align-items: center;
+        cursor: pointer;
+
+        >.table-data {
+          font-size: 14px;
+          line-height: 50px;
+          height: 100%;
+          color: #323233;
+
+          >.list-img {
+            position: relative;
+            height: 100%;
+            display: inline-block;
+            width: 100%;
+
+            >img,
+            .audio-player {
+              position: absolute;
+              top: 50%;
+              transform: translateY(-50%);
+              width: 40px;
+              height: 40px;
+              object-fit: cover;
+              &.folderIcon {
+                object-fit: contain;
+              }
+            }
+          }
+        }
+      }
+    }
+
+    >.no-data {
+      height: calc(@table-height - @table-head-row-height - @table-border-size - @table-border-size);
+      width: 100%;
+      position: relative;
+
+      >div {
+        position: absolute;
+        top: 50%;
+        left: 50%;
+        transform: translate(-50%, -50%);
+        text-align: center;
+
+        >img {
+          width: 116px;
+        }
+
+        >span {
+          margin-top: 20px;
+          display: block;
+          font-size: 14px;
+          color: rgba(255, 255, 255, 0.6);
+        }
+
+        >a {
+          >button {
+            margin-top: 20px;
+          }
+        }
+      }
+    }
+  }
+
+  .table-pano .table-head,
+  .table-pano .table-data {
+    &:nth-of-type(1) {
+      width: 50px;
+      color: transparent;
+    }
+
+    &:nth-of-type(2) {
+      width: calc(146px - 50px);
+    }
+
+    &:nth-of-type(3) {
+      width: calc(380px - 146px);
+      padding-right: 30px;
+    }
+
+    &:nth-of-type(4) {
+      width: calc(100% - 380px);
+    }
+  }
+
+  .checkbox {
+    width: 100%;
+    height: 100%;
+  }
+
+  .btns {
+    display: flex;
+    justify-content: space-between;
+    margin-top: 32px;
+
+    .upload-btn {
+      display: flex;
+      align-items: center;
+
+      >span {
+        display: inline-block;
+        margin-right: 4px;
+      }
+
+      i.tool-tip-for-editor {
+        font-size: 12px;
+        transform: scale(0.923) translateY(1px);
+        cursor: default;
+      }
+    }
+  }
+}
+</style>

+ 25 - 137
packages/qjkankan-editor/src/components/materialSelectorForManageCenter.vue

@@ -44,7 +44,7 @@
       <i v-if="!searchKey" class="iconfont icon-editor_search search-icon" />
       <i v-if="searchKey" @click="searchKey = ''" class="iconfont icontoast_red clear-icon"></i>
     </div>
-    
+
     <crumbs
       class="crumbs"
       v-if="!latestUsedSearchKey && currentMaterialType === 'pano'"
@@ -54,142 +54,28 @@
     />
     <div v-if="latestUsedSearchKey" class="crumbs">{{$i18n.t(`gather.${currentMaterialType}`)}}</div>
 
-    <div class="table table-pano" v-show="currentMaterialType === 'pano'">
-      <div class="table-head-row">
-        <span class="table-head">1</span>
-        <span class="table-head" v-for="(item, i) in tableHeadersForPano" :key="i">
-          {{ item.name && $i18n.t(`zh_key.${item.name}`) }}</span>
-      </div>
-      <div
-        v-show="panoListLocalLength !== 0 || hasMorePanoData"
-        class="table-body"
-        v-infinite-scroll="requestMorePanoData"
-        :infinite-scroll-disabled="!hasMorePanoData || isRequestingMorePanoData"
-      >
-        <!-- vuex中的上传中数据 -->
-        <div v-for="(item, i) in uploadStatusListPano" :key="item.uid">
-          <div class="table-body-row" v-if="item.parentFolderId === currentFolderId"  @click="onClickRow">
-            <!-- 如果已经上传成功 -->
-            <template v-if="item.status === 'SUCCESS'">
-              <span class="table-data">
-                <RadioOrCheckbox class="checkbox" :isLightTheme="true" :isMultiSelection="isMultiSelection"
-                  :isCheckedInitial="select.some(i => i[primaryKey] === item.successInfo[primaryKey])"
-                  @change="v => selectItem(item.successInfo, v)" />
-              </span>
-              <span class="table-data" v-for="(tableItemStructure, idx) in tableHeadersForPano" :key="idx">
-                <div v-if="tableItemStructure.type == 'image'" class="list-img">
-                  <img
-                    :src="item.successInfo[tableItemStructure.key] + (Number(item.fileSize) > 512 ? $imgsuffix : ``)"
-                    alt="">
-                </div>
-                <span v-else class="ellipsis"
-                  v-title="tableItemStructure.key === 'name' ? item.successInfo[tableItemStructure.key] : ''">{{
-                      item.successInfo[tableItemStructure.key]
-                  }}</span>
-              </span>
-            </template>
-            <!-- 如果还在上传或切图处理中 -->
-            <template v-else-if="item.status === 'LOADING'">
-              <span class="table-data">
-                <div class="checkbox">
-                  <span class="for-outer-circle"></span>
-                  <span class="for-inner-circle disabled"></span>
-                </div>
-              </span>
-              <span class="table-data" v-for="(tableItemStructure, idx) in tableHeadersForPano" :key="idx">
-                <div v-if="tableItemStructure.type == 'image'" class="list-img">
-                  <img src="@/assets/images/icons/upload-file-type-icon-image@2x.png" alt="">
-                </div>
-                <span v-if="tableItemStructure.key !== 'name' && tableItemStructure.key !== 'fileSize'"></span>
-                <span v-if="tableItemStructure.key === 'fileSize' && item.ifKnowProgress">{{$i18n.t(`gather.upload_material`)}} {{ Math.round(item.progress
-                    * 100)
-                }}%</span>
-                <span v-if="tableItemStructure.key === 'fileSize' && !item.ifKnowProgress">{{ item.statusText }}</span>
-                <span v-if="tableItemStructure.key === 'name'" class="ellipsis"
-                  v-title="tableItemStructure.key === 'name' ? item.title : ''">{{ item.title }}</span>
-              </span>
-            </template>
-            <!-- 如果上传失败了 -->
-            <template v-else-if="item.status === 'FAIL'">
-              <span class="table-data">
-                <div class="checkbox">
-                  <span class="for-outer-circle"></span>
-                  <span class="for-inner-circle disabled"></span>
-                </div>
-              </span>
-              <span class="table-data" v-for="(tableItemStructure, idx) in tableHeadersForPano" :key="idx">
-                <div v-if="tableItemStructure.type == 'image'" class="list-img">
-                  <img src="@/assets/images/icons/upload-file-type-icon-image@2x.png" alt="">
-                </div>
-                <span v-if="tableItemStructure.key !== 'name' && tableItemStructure.key !== 'fileSize'"></span>
-                <span v-if="tableItemStructure.key === 'fileSize'">{{ $i18n.t(`tips_code.FAILURE_3025`) }}</span>
-                <span v-if="tableItemStructure.key === 'name'" class="ellipsis"
-                  v-title="tableItemStructure.key === 'name' ? item.title : ''">{{ item.title }}</span>
-              </span>
-            </template>
-          </div>
-        </div>
-        <!-- 本组件内的列表数据 -->
-        <div
-          class="table-body-row"
-          v-for="(item, i) in panoList"
-          :key="i"
-          @click="(e) => {
-            if (item.type === 'dir') {
-              onClickFolder(item)
-            } else {
-              onClickRow(e)
-            }
-          }"
-        >
-          <span class="table-data">
-            <RadioOrCheckbox
-              v-if="item.type !== 'dir'"
-              class="checkbox"
-              :isLightTheme="true"
-              :isMultiSelection="isMultiSelection"
-              :isCheckedInitial="select.some(i => i[primaryKey] === item[primaryKey])"
-              @change="v => selectItem(item, v)"
-            />
-          </span>
-          <span
-            class="table-data"
-            v-for="(sub, idx) in tableHeadersForPano"
-            :key="idx"
-          >
-            <div
-              v-if="sub.type == 'image'"
-              class="list-img"
-            >
-              <img
-                :class="{
-                  folderIcon: item.type === 'dir',
-                }"
-                :src="(item.type === 'dir') ? require('@/assets/images/icons/folder-blue.png') : (item[sub.key] + (Number(item.fileSize) > 512 ? $imgsuffix : ``))"
-                alt=""
-              />
-            </div>
-            <span
-              class="ellipsis"
-              v-else v-title="sub.key === 'name' ? item[sub.key] : ''"
-            >
-              {{ item[sub.key] }}
-            </span>
-          </span>
-        </div>
-      </div>
-      <!-- 无数据时的提示 -->
-      <div v-show="!(panoListLocalLength !== 0 || hasMorePanoData)" class="no-data">
-        <div v-if="latestUsedSearchKey">
-          <img :src="require('@/assets/images/default/empty_04_search.png')" alt="">
-          <span>{{ no_serch_result }}</span>
-        </div>
-        <div v-if="!latestUsedSearchKey">
-          <img :src="require('@/assets/images/default/empty_04.png')" alt="">
-          <span>{{ no_material_result }}</span>
-        </div>
-      </div>
-    </div>
+    <MaterialList
+      v-show="currentMaterialType === 'pano'"
+
+      :currentMaterialType="currentMaterialType"
+      :materialType="'pano'"
+      
+      :tableHeaderKeyList="['icon', 'name', 'fileSize']"
+      
+      :isMultiSelection="isMultiSelection"
+      :select="select"
+
+      :searchKey="searchKey"
+
+      :canUpload="true"
+      :uploadStatusList="uploadStatusListPano"
+      :fileInputBtnTip="$i18n.t(`gather.pano_size`)"
+      :fileInputFailString="$i18n.t(`gather.pano_fail`)"
+      :fileInputLimitFailStr="$i18n.t(`gather.pano_limit`)"
+      :fileInputAcceptType="'image/jpeg'"
+      :fileInputMediaType="'image'"
+      :fileInputLimit="120"
+    />
 
     <div class="table table-3D" v-show="currentMaterialType === '3D'">
       <div class="table-head-row">
@@ -266,6 +152,7 @@
 
 <script>
 import { mapMutations, mapState, } from "vuex";
+import MaterialList from "./materialListInMaterialSelector.vue";
 
 import {
   getMaterialList,
@@ -291,6 +178,7 @@ export default {
   components: {
     FileInput,
     RadioOrCheckbox,
+    MaterialList,
   },
   mixins: [
     folderMixin,

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

@@ -233,4 +233,14 @@ export function nodeIdList2nodeInfoListByNodeTree(nodeIdList, nodeTree) {
   }
 
   return ret
+}
+
+export function capitalize(str) {
+  if (!str) {
+    return
+  }
+  if (str.length === 1) {
+    return str[0].toUpperCase()
+  }
+  return str[0].toUpperCase() + str.slice(1, str.length)
 }