vue3 文件上传进度条组件

发布时间 2023-08-17 17:18:23作者: 奚奚奚

index.vue文件

<template>
  <div class="confirm-modal">
    <transition name="fade">
      <div class="modal-dialog" @click="clickMaskToClose ? handleCancel() : null" v-if="visible" @touchmove.prevent>
        <div class="modal">
          <div class="modal-title" v-if="title">
            {{ title }}
          </div>
          <div class="modal-content">
            <div class="lineBox">
              <div class="line" :style="{'width': parseInt(percentage)+'%'}">
              </div>
            </div>
            <div class="num">
              {{parseInt(percentage)}}%
            </div>
          </div>
          <div class="hint" v-if="hint">
            {{parseInt(percentage) === 100 ? '数据整在处理请耐心等待...' : hint}}
          </div>
        </div>
      </div>
    </transition>
  </div>
</template>

<script>
  import {ref, defineComponent, reactive, toRefs} from "vue";

  export default defineComponent({
    props: {
      visible: {
        type: Boolean,
        default: false,
      },
      title: {
        type: String,
        default: "提示",
      },
      percentage: {
        type: Number,
        default: 0,
      },
      hint: {
        type: String,
        default: '',
      },
      clickMaskToClose: {
        type: Boolean,
        default: false,
      }, // 点击遮罩是否隐藏
    },

    emits: {
      onConfirm: null,
      onCancel: null,
    },
    setup(props, context) {
      let percentage = ref(0)
      let tempData = Object.assign({}, props);
      const propsData = reactive(tempData);
      const handleConfirm = () => {
        propsData.visible = false;
        context.emit("onConfirm");
      };
      const handleCancel = () => {
        propsData.visible = false;
        context.emit("onCancel");
      };
      return {
        ...toRefs(propsData),
        handleCancel,
        handleConfirm
      };
    },
  });
</script>

<style lang='scss' scoped>
  .modal-dialog {
    width: 100%;
    height: 100%;
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 9999;
    transform: translateZ(9999px);
    letter-spacing: 0;
    background: rgba(0, 0, 0, 0.3);
  }

  .modal {
    position: absolute;
    top: 40%;
    left: 50%;
    z-index: 9000;
    width: 350px;
    transform: translate(-50%, -50%);
    box-sizing: border-box;
    background: #fff;
    border-radius: 4px;
    padding: 15px;
  }

  .modal-title {
    font-size: 16px;
    line-height: 25px;
    color: #030303;
    overflow: hidden;
    text-overflow:ellipsis;
    white-space: nowrap;
  }
  .hint {
    font-size: 13px;
    color: #e6a23c;
  }
  .modal-content {
    display: flex;
    justify-content: space-between;
    align-items: center;
    font-size: 16px;
    line-height: 21px;
    color: #5e5f64;
    padding: 10px 0;
    .lineBox {
      position: relative;
      width: 100%;
      height: 15px;
      background-color: #ebeef5;
      border-radius: 2px;
      .line {
        position: absolute;
        top: 0;
        left: 0;
        height: 15px;
        border-radius: 2px;
        background-color: #409eff;
        transition: width 0.5s;
      }
    }
    .num {
      text-align: right;
      width: 50px;
    }
  }

  .no-title-content {
    font-size: 16px;
    padding: 28px;
    color: #333333;
  }

  .modal-right {
    padding-right: 10px;
    width: 36px;
    background: #f2f2f2;
    color: rgba(0, 16, 38, 0.3);
    font-size: 12px;
    border-radius: 0 4px 4px 0;
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
  }

  .split-line-top {
    height: 1px;
    transform: scale(1, 0.5);
    background: #e8eaef;
  }

  .modal-footer {
    width: 100%;
    display: flex;
    align-items: center;
    height: 52px;
    font-size: 16px;
    line-height: 52px;
    text-align: center;
  }

  .split-line-center {
    width: 1px;
    height: 100%;
    transform: scale(0.5, 1);
    background: #e8eaef;
  }

  .btn-cancel {
    flex: 1;
    color: #696d76;
  }

  .btn-confirm {
    position: relative;
    flex: 1;
    color: #409eff;
  }
</style>

 

index.js文件

import { createApp } from "vue";
import uploadProgressIndex from "./index.vue";

const UploadProgressPlugin = {};
let $vm;

const defaultsOptions = {
  title: "提示",
  content: "内容",
  hint: "",
  clickMaskToClose: false,
};

const initInstance = () => {
  const app = createApp(uploadProgressIndex);
  const container = document.createElement("div");
  $vm = app.mount(container);
  document.body.appendChild(container);
};

UploadProgressPlugin.install = (app) => {
  const uploadProgress = {
    show(options) {
      if (!$vm) initInstance();
      options = Object.assign({}, defaultsOptions, options);
      for (const i in options) {
        $vm[i] = options[i];
      }
      $vm.visible = true;
      return $vm;
    },
    close() {
      $vm.percentage = 0
      if ($vm) $vm.visible = false;
    },
  };
  app.config.globalProperties.$uploadProgress = uploadProgress;
};

export default UploadProgressPlugin;