<!DOCTYPE html>
<html lang="{{ locale() }}">

<head>
  <meta charset="UTF-8">
  <base href="{{ $admin_base_url }}">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1">
  <meta name="csrf-token" content="{{ csrf_token() }}">
  <meta name="asset" content="{{ asset('/') }}">
  <script src="{{ asset('vendor/vue/2.7/vue.js') }}"></script>
  <script src="{{ asset('vendor/element-ui/index.js') }}"></script>
  <script src="{{ asset('vendor/cookie/js.cookie.min.js') }}"></script>
  <script src="{{ asset('vendor/jquery/jquery-3.6.0.min.js') }}"></script>
  <script src="{{ asset('vendor/layer/3.5.1/layer.js') }}"></script>
  <script src="{{ asset('vendor/vue/batch_select.js') }}"></script>
  <link href="{{ mix('/build/beike/admin/css/bootstrap.css') }}" rel="stylesheet">
  <link rel="stylesheet" href="{{ asset('vendor/element-ui/index-blue.css') }}">
  <link href="{{ mix('build/beike/admin/css/filemanager.css') }}" rel="stylesheet">
  <script src="{{ mix('build/beike/admin/js/app.js') }}"></script>
  @if (locale() != 'zh_cn')
    <script src="{{ asset('vendor/element-ui/language/' . locale() . '.js') }}"></script>
  @endif
  <title>beike filemanager</title>
  <script>
    @if (locale() != 'zh_cn')
      ELEMENT.locale(ELEMENT.lang['{{ locale() }}'])
    @endif
    const lang = {
      file_manager: '{{ __('admin/file_manager.file_manager') }}',
    }

    const config = {
      beike_version: '{{ config('beike.version') }}',
      api_url: '{{ config('beike.api_url') }}',
      app_url: '{{ config('app.url') }}',
    }
  </script>
</head>

<body class="page-filemanager">
  <div class="filemanager-wrap" id="filemanager-wrap-app" v-cloak ref="splitPane">
    @if (!is_mobile())
      <div class="filemanager-navbar" :style="'width:' + paneLengthValue">
        <el-tree :props="defaultProps" node-key="path" :data="treeData" :current-node-key="folderCurrent"
          :default-expanded-keys="defaultkeyarr" :expand-on-click-node="false" highlight-current ref="tree"
          @node-click="handleNodeClick" @node-expand="(node) => {updateDefaultExpandedKeys(node, 'expand')}"
          @node-collapse="(node) => {updateDefaultExpandedKeys(node, 'collapse')}" class="tree-wrap">
          <div class="custom-tree-node" slot-scope="{ node, data }">
            <div>@{{ node.label }}</div>
            {{-- v-if="node.isCurrent" --}}
            <div class="right">
              <el-tooltip class="item" effect="dark" content="{{ __('admin/file_manager.create_folder') }}" placement="top">
                <span @click.stop="() => {openInputBox('addFolder', node, data)}"><i
                    class="el-icon-circle-plus-outline"></i></span>
              </el-tooltip>

              <el-tooltip class="item" effect="dark" content="{{ __('admin/file_manager.rename') }}" placement="top">
                <span v-if="node.level != 1" @click.stop="() => {openInputBox('renameFolder', node, data)}"><i
                    class="el-icon-edit"></i></span>
              </el-tooltip>

              <el-tooltip class="item" effect="dark" content="{{ __('common.delete') }}" placement="top">
                <span v-if="node.level != 1" @click.stop="() => {deleteFolder(node, data)}"><i
                    class="el-icon-delete"></i></span>
              </el-tooltip>

            </div>
          </div>
        </el-tree>
      </div>
      <div class="filemanager-divider" @mousedown="handleMouseDown"></div>
      <div class="filemanager-content" v-loading="loading" element-loading-background="rgba(255, 255, 255, 0.5)">
        <div class="content-head">
          <div class="left d-lg-flex">
            <el-button class="me-5 mb-1 mb-lg-0" size="small" icon="el-icon-check" type="primary" @click="fileChecked" :disabled="!!!selectImageIndex.length">{{ __('admin/builder.modules_choose') }}</el-button>
            <el-link :underline="false" :disabled="!!!selectImageIndex.length" icon="el-icon-view"
              @click="viewImages">{{ __('common.view') }}</el-link>
            <el-link :underline="false" :disabled="!!!selectImageIndex.length" @click="deleteFile"
              icon="el-icon-delete">{{ __('common.delete') }}</el-link>
            <el-link :underline="false" :disabled="selectImageIndex.length == 1 ? false : true"
              @click="openInputBox('image')" icon="el-icon-edit">{{ __('admin/file_manager.rename') }}</el-link>
            <el-link :underline="false" :disabled="!!!images.length && !!!selectImageIndex.length"
              @click="selectAll()" icon="el-icon-finished">{{ __('common.select_all') }}</el-link>
             @hook('admin.file_manager.content.head.btns.after')
          </div>
          <div class="right">
            <el-popover
              placement="bottom"
              width="260"
              class="me-2"
              trigger="click">
              <div class="text-center mb-3 fw-bold">{{ __('admin/file_manager.file_sorting') }}</div>
              <div class="mb-3">
                <div class="mb-2">{{ __('admin/file_manager.text_type') }}</div>
                <el-radio-group v-model="filter.sort" size="small">
                  <el-radio-button label="created">{{ __('admin/file_manager.text_created') }}</el-radio-button>
                  <el-radio-button label="name">{{ __('admin/file_manager.file_name') }}</el-radio-button>
                </el-radio-group>
              </div>

              <div class="mb-3">
                <div class="mb-2">{{ __('admin/file_manager.to_sort') }}</div>
                <el-radio-group v-model="filter.order" size="small">
                  <el-radio-button label="desc">{{ __('admin/file_manager.text_desc') }}</el-radio-button>
                  <el-radio-button label="asc">{{ __('admin/file_manager.text_asc') }}</el-radio-button>
                </el-radio-group>
              </div>
              <el-button slot="reference" size="small" plain type="primary" icon="el-icon-s-operation"></el-button>
            </el-popover>
            <el-button size="small" plain type="primary" @click="openUploadFile" icon="el-icon-upload2">{{ __('admin/file_manager.upload_files') }}</el-button>
          </div>
        </div>
        <div v-if="images.length" class="content-center"
          v-batch-select="{ className: '.image-list', selectImageIndex, setSelectStatus: updateSelectStatus }">
          <div :class="['image-list', file.selected ? 'active' : '']" v-for="file, index in images"
            :key="index" @click="checkedImage(index)">
            <div class="img">
              <i class="el-icon-video-play" v-if="file.mime == 'video/mp4'"></i>
              <img v-else :src="file.url">
            </div>
            <div class="text">
              <span :title="file.name">@{{ file.name }}</span>
              <i v-if="file.selected" class="el-icon-check"></i>
            </div>
          </div>
        </div>
        <el-empty v-else description="{{ __('admin/file_manager.no_file') }}"></el-empty>
        <div class="content-footer">
          <div class="right"></div>
          <div class="pagination-wrap">
            <el-pagination
              @size-change="pageSizeChange"
              @current-change="pageCurrentChange"
              :current-page="image_page"
              :page-sizes="[20, 40, 60, 80, 100]"
              :page-size="20"
              layout="total, sizes, prev, pager, next, jumper"
              :total="image_total">
            </el-pagination>

          </div>
          <div class="right">

          </div>
        </div>
      </div>
    @else
    <div class="text-center mt-5 w-100 fs-4">{{ __('admin/file_manager.show_pc') }}</div>
    @endif

    <el-dialog title="{{ __('admin/file_manager.upload_files') }}" top="12vh" :visible.sync="uploadFileDialog.show" width="500px"
      @close="uploadFileDialogClose" custom-class="upload-wrap">
      <el-upload class="photos-upload" target="photos-upload" id="photos-upload" element-loading-text="{{ __('admin/file_manager.image_uploading') }}..."
        element-loading-background="rgba(0, 0, 0, 0.6)" drag action="" :show-file-list="false"
        accept=".jpg,.jpeg,.png,.JPG,.JPEG,.PNG,.mp4,.MP4,.gif,.webp" :before-upload="beforePhotoUpload"
        :on-success="handlePhotoSuccess" :on-change="handleUploadChange" :http-request="uploadFile"
        :multiple="true">
        <i class="el-icon-upload"></i>
        <div class="el-upload__text">{{ __('admin/file_manager.click_upload') }}</div>
      </el-upload>
      <div class="upload-image">
        <div v-for="image, index in uploadFileDialog.images" :key="index" class="list">
          <div class="info">
            <div class="name">@{{ index + 1 }}. @{{ image.name }}</div>
            <div class="status">
              <span v-if="image.status == 'complete'" class="text-success">{{ __('admin/file_manager.finish') }}</span>
              <span v-else-if="image.status == 'fail'" class="text-danger">{{ __('admin/file_manager.upload_fail') }}</span>
              <span v-else>{{ __('admin/file_manager.uploading') }}</span>
            </div>
          </div>
          <el-progress :percentage="image.progre" :status="image.status == 'fail' ? 'exception' : 'success'" :show-text="false" :stroke-width="4"></el-progress>
          <div v-if="image.fail_text" class="mt-1 text-danger" v-text="image.fail_text"></div>
        </div>
      </div>
    </el-dialog>
  </div>

  <script>
    @if (locale() != 'zh_cn')
      ELEMENT.locale(ELEMENT.lang['{{ locale() }}'])
    @endif
    var callback = null;

    var app = new Vue({
      el: '#filemanager-wrap-app',
      components: {},
      data: {
        min: 10,
        max: 40,
        paneLengthPercent: 26,
        triggerLength: 10,
        isShift: false,
        mime: @json(request('mime')),
        loading: false,
        isBatchSelect: false,
        selectImageIndex: [],
        filter: {
          sort: 'created',
          order: 'desc'
        },

        treeData: [{
          name: '{{ __('admin/file_manager.picture_space') }}',
          path: '/',
          children: @json($directories)
        }],

        defaultProps: {
          children: 'children',
          label: 'name',
          isLeaf: 'leaf'
        },
        selectIdxs: [],

        uploadFileDialog: {
          show: false,
          total: 0,
          images: []
        },

        folderCurrent: '/',
        defaultkeyarr: ['/'],

        triggerLeftOffset: 0,

        images: [],
        image_total: 0,
        image_page: 1,
        per_page: 20,
        @stack('admin.file_manager.vue.data')
      },
      // 计算属性
      computed: {
        paneLengthValue() {
          return `calc(${this.paneLengthPercent}% - ${this.triggerLength / 2 + 'px'})`
        },
      },
      // 侦听器
      watch: {
        images: {
          handler(val) {
            if (this.isBatchSelect) return;
            // 将选中的图片索引放入 selectImageIndex，未选中则清空
            this.selectImageIndex = val.filter(item => item.selected).map(e => this.images.indexOf(e));
          },
          deep: true
        },

        selectImageIndex(indexs) {
          this.images.forEach((item, index) => {
            item.selected = indexs.includes(index);
          });
        },

        filter: {
          handler(val) {
            this.image_page = 1;
            this.loadData()
          },
          deep: true
        }
      },
      // 组件方法
      methods: {
        handleNodeClick(e, node) {
          if (e.path == this.folderCurrent) {
            return;
          }

          this.folderCurrent = e.path
          this.image_page = 1;
          sessionStorage.setItem('folderCurrent', this.folderCurrent);
          this.loadData(e, node)
        },

        updateSelectStatus(status) {
          this.isBatchSelect = status
        },

        pageCurrentChange(e) {
          this.image_page = e
          this.loadData()
        },

        pageSizeChange(e) {
          this.per_page = e
          this.loadData()
        },

        uploadFileDialogClose() {
          if (this.uploadFileDialog.images.length) {
            this.loadData()
          }

          this.uploadFileDialog.images = [];
        },

        openUploadFile() {
          this.uploadFileDialog.show = true
        },

        beforePhotoUpload(file) {
          // this.editing.photoLoading = true;
        },

        handlePhotoSuccess(data) {
          // this.editing.photoLoading = false;

          // if (data.images) {
          //   this.images.push(data.images);
          // }
        },

        // 文件上传
        uploadFile(file) {
          const that = this;
          let newFile = {};

          var formData = new FormData();
          formData.append("file", file.file, file.file.name);
          formData.append("path", this.folderCurrent);

          newFile = {
            // index: this.images.length,
            name: file.file.name,
            progre: 0,
            status: 'padding'
          };

          this.uploadFileDialog.images.push(newFile);

          let index = this.uploadFileDialog.images.length - 1;

          $http.post('file_manager/upload', formData, {hmsg: true}).then((res) => {
            this.uploadFileDialog.images[index].status = 'complete';
            this.uploadFileDialog.images[index].progre = 100;
          }).catch((err) => {
            this.uploadFileDialog.images[index].status = 'fail';
            this.uploadFileDialog.images[index].progre = 80;
            this.uploadFileDialog.images[index].fail_text = err.response.data.message;
          }).finally(() => {
            index += 1
          });
        },

        handleUploadChange(e) {
          console.log(e);
          // console.log('handleUploadChange');
        },

        updateDefaultExpandedKeys(node, type) {
          const isExist = this.defaultkeyarr.some(item => item === node.path)

          if (type == 'expand') {
            if (!isExist) {
              this.defaultkeyarr.push(node.path)
            }
          } else {
            const index = this.defaultkeyarr.findIndex(e => e == node.path);
            if (index > -1) {
              this.defaultkeyarr.splice(index, 1);
            }
          }

          sessionStorage.setItem('defaultkeyarr', this.defaultkeyarr);
        },

        loadData(e, node) {
          this.loading = true;

          $http.get(`file_manager/files?base_folder=${this.folderCurrent}`, {
            page: this.image_page,
            per_page: this.per_page,
            sort: this.filter.sort,
            order: this.filter.order
          }, {
            hload: true
          }).then((res) => {
            this.images = res.images
            this.image_page = res.image_page
            this.image_total = res.image_total

            if (node) {
              node.expanded = true
              this.updateDefaultExpandedKeys(node.data, 'expand')
            }
          }).finally(() => this.loading = false);
        },

        // 按下滑动器
        handleMouseDown(e) {
          document.addEventListener('mousemove', this.handleMouseMove)
          document.addEventListener('mouseup', this.handleMouseUp)

          this.triggerLeftOffset = e.pageX - e.srcElement.getBoundingClientRect().left
        },

        // 按下滑动器后移动鼠标
        handleMouseMove(e) {
          const clientRect = this.$refs.splitPane.getBoundingClientRect()
          let paneLengthPercent = 0

          const offset = e.pageX - clientRect.left - this.triggerLeftOffset + this.triggerLength / 2
          paneLengthPercent = (offset / clientRect.width) * 100

          if (paneLengthPercent < this.min) {
            paneLengthPercent = this.min
          }
          if (paneLengthPercent > this.max) {
            paneLengthPercent = this.max
          }
          this.paneLengthPercent = paneLengthPercent;
        },

        // 松开滑动器
        handleMouseUp() {
          document.removeEventListener('mousemove', this.handleMouseMove)
        },

        checkedImage(index) {
          // 获取当前选中的 index
          const selectedIndex = this.images.findIndex(e => e.selected);

          if (this.isShift) {
            // 获取 selectedIndex 与 index 之间的所有图片
            let selectedImages = this.images.slice(Math.min(selectedIndex, index), Math.max(selectedIndex, index) +
            1);
            selectedImages.map(e => e.selected = true)
            return;
          }

          if (this.isCtrl) {
            this.images[index].selected = !this.images[index].selected;
            return;
          }

          if (this.selectImageIndex.length > 1) {
            this.images.map((e, i) => i != index ? e.selected = false : e.selected = true)
            return;
          }

          this.images.map((e, i) => i != index ? e.selected = false : '')
          this.images[index].selected = !this.images[index].selected
        },

        // 选取
        fileChecked() {
          let typedFiles = this.images.filter(e => e.selected)

          if (this.mime) {
            // 判断 typedFiles 数组内 mime 是否有不是 image 开头的
            if (this.mime == 'image' && typedFiles.some(e => !e.mime.startsWith('image'))) {
              layer.msg('{{ __('admin/file_manager.verify_select_image') }}', () => {});
              return;
            }

            // 判断 typedFiles 数组内 mime 是否有不是 video 开头的
            if (this.mime == 'video' && typedFiles.some(e => !e.mime.startsWith('video'))) {
              layer.msg('{{ __('admin/file_manager.verify_select_video') }}', () => {});
              return;
            }
          }

          if (callback !== null) {
            callback(typedFiles);
          }

          // 关闭弹窗
          var index = parent.layer.getFrameIndex(window.name);
          parent.layer.close(index);
        },

        deleteFile() {
          this.$confirm('{{ __('admin/file_manager.confirm_delete_file') }}', '{{ __('common.text_hint') }}', {
            type: 'warning'
          }).then(() => {
            const selectImageIndex = this.selectImageIndex;
            // 获取images中下标与selectImageIndex相同的图片
            const images = this.images.filter(e => selectImageIndex.includes(this.images.indexOf(e)));
            // images 取 path 组成数组 然后用 | 分割成字符串
            const files = images.map(e => e.name);

            this.loading = true;

            $http.delete('file_manager/files', {
              path: this.folderCurrent,
              files: files
            }, {
              hload: true
            }).then((res) => {
              layer.msg(res.message)
              this.loadData()
            })
          }).catch(_ => {});
        },

        deleteFolder(node, data) {
          if (data.path) {
            this.$confirm('{{ __('admin/file_manager.confirm_delete_folder') }}', '{{ __('common.text_hint') }}', {
              type: 'warning'
            }).then(() => {
              $http.delete(`file_manager/directories`, {
                name: data.path
              }).then((res) => {
                layer.msg(res.message)
                this.$refs.tree.setCurrentKey(node.parent.data.path)
                this.folderCurrent = node.parent.data.path;
                this.$refs.tree.remove(data.path)
              }).finally(() => this.loadData())
            }).catch(_ => {});
          }
        },

        selectAll() {
          // 获取 this.images 中的 selected 是否全部为 true
          const isAllSelected = this.images.every(e => e.selected);
          this.images.map(e => e.selected = !isAllSelected)
        },

        downloadImages() {
          // 获取选中的图片
          const selectedImages = this.images.filter(e => e.selected);
          // 创建 a 标签
          selectedImages.forEach(e => {
            const a = document.createElement('a');
            // 设置 a 标签的 href 属性
            a.href = e.origin_url;
            // 设置 a 标签的 download 属性
            a.download = e.name;
            // 触发 a 标签的 click 事件
            a.click();
          });
        },

        viewImages() {
          const selectedImages = this.images.filter(e => e.selected);
          selectedImages.forEach(e => {
            window.open(e.origin_url);
          });
        },

        openInputBox(type, node, data) {
          let fileSuffix, fileName = '';

          if (type == 'image') {
            const image = this.images[this.selectImageIndex].name;
            // 获取文件后缀
            fileSuffix = image.substring(image.lastIndexOf('.') + 1);
            // 获取文件名
            fileName = image.substring(0, image.lastIndexOf('.'));
          }

          this.$prompt('', type == 'addFolder' ? '{{ __('admin/file_manager.new_folder') }}' : '{{ __('admin/file_manager.rename') }}', {
            confirmButtonText: '{{ __('common.confirm') }}',
            cancelButtonText: '{{ __('common.cancel') }}',
            inputPattern: /^.+$/,
            closeOnClickModal: false,
            inputValue: type == 'image' ? fileName : (type == 'renameFolder' ? data.name : '{{ __('admin/file_manager.new_folder') }}'),
            inputErrorMessage: '{{ __('admin/file_manager.can_empty') }}'
          }).then(({
            value
          }) => {
            if (type == 'addFolder') {
              let fileAllPathName = this.folderCurrent + '/' + value;
              $http.post(`file_manager/directories`, {
                name: fileAllPathName
              }).then((res) => {
                layer.msg(res.message)
                node.expanded = true
                this.$refs.tree.append({
                  name: value,
                  path: fileAllPathName,
                  leaf: true
                }, node);
                this.$refs.tree.setCurrentKey(fileAllPathName)
                this.folderCurrent = fileAllPathName;
                this.images = [];
                this.image_page = 1
                this.image_total = 0
                this.updateDefaultExpandedKeys(node.data, 'expand')
              })
            }

            if (type == 'renameFolder') {
              this.folderCurrent = data.path;

              $http.post(`file_manager/rename`, {
                origin_name: data.path,
                new_name: value
              }).then((res) => {
                layer.msg(res.message)
                data.name = value;
                data.path = data.path.replace(/\/[^\/]*$/, '/' + value);
                this.folderCurrent = this.folderCurrent.replace(/\/[^\/]*$/, '/' + value);
                // 递归修改 data 内所有 children -> path 的对应 level = value
                this.changeChildren(data, node, value);
              })
            }

            if (type == 'image') {
              const name = this.images[this.selectImageIndex].name;
              const origin_name = this.folderCurrent == '/' ? '/' + name : this.folderCurrent + '/' + name;

              $http.post(`file_manager/rename`, {
                origin_name: origin_name,
                new_name: value + '.' + fileSuffix
              }).then((res) => {
                this.images[this.selectImageIndex].name = value + '.' + fileSuffix;
                layer.msg(res.message)
              })
            }
          }).catch(() => {});
        },

        changeChildren(data, node, value) {
          if (data.children) {
            data.children.map(e => {
              if (e.path) {
                // 将字符串转换为数组
                let path = e.path.split('/')
                path[node.level - 1] = value
                // 将数组转换为字符串
                e.path = path.join('/')
              }

              if (e.children) {
                this.changeChildren(e, node, value)
              }
            })
          }
        }
        @stack('admin.file_manager.vue.method')
      },

      created() {
        const defaultkeyarr = sessionStorage.getItem('defaultkeyarr');
        const folderCurrent = sessionStorage.getItem('folderCurrent');

        if (defaultkeyarr) {
          this.defaultkeyarr = defaultkeyarr.split(',');
        }

        if (folderCurrent) {
          this.folderCurrent = folderCurrent;
        }
      },
      // 实例被挂载后调用
      mounted() {
        this.loadData()
        // 获取键盘事件 是否按住 shift/ctrl 键 兼容 mac 和 windows
        document.addEventListener('keydown', (e) => {
          this.isShift = e.shiftKey;
          this.isCtrl = e.ctrlKey || e.metaKey;
        })

        // 获取键盘事件 是否松开 shift/ctrl 键
        document.addEventListener('keyup', (e) => {
          this.isShift = e.shiftKey;
          this.isCtrl = e.ctrlKey || e.metaKey;
        })

        // 判断鼠标是否点击 .image-list 元素
        document.addEventListener('click', (e) => {
          if (this.isBatchSelect) return;
          const targets = ['filemanager-navbar', 'content-center']
          if (targets.indexOf(e.target.className) > -1) {
            this.selectImageIndex = [];
            this.images.map(e => e.selected = false)
          }
        })
      },
    })
  </script>
</body>

</html>
