【sgMobileUploadTypeSelect】自定义组件:从底部弹出选择上传图片文件的方式【1、上传本地文件,2、拍摄上传】

sgMobileUploadTypeSelect.vue

html 复制代码
<template>
  <!-- 选择是直接上传手机文件还是拍摄上传 -->
  <div :class="$options.name" :show="visible">
    <div class="bg" @click="visible = false"></div>
    <div class="container">
      <div class="close" @click="visible = false">
        <i class="el-icon-close" />
      </div>

      <div class="upload-types">
        <div class="item" @click="(uploadBtn || {}).click()">
          <i class="el-icon-upload" />
        </div>
        <div class="item" @click="scan">
          <i class="el-icon-camera" />
        </div>
      </div>
    </div>
    <sgUploadCameraImage ref="sgUploadCameraImage" @change="getUploadFiles_scan" />
    <el-upload
      ref="upload"
      :accept="``"
      :action="`#`"
      :auto-upload="false"
      :multiple="true"
      :on-change="getUploadFiles_upload"
      :show-file-list="false"
      :dragenter="isDragenter"
      :drag="false"
    />
    <!-- 如果drag=true,accept必须为具体的格式,否则无法监听on-change -->
  </div>
</template>
<script>
import sgUploadCameraImage from "@/vue/components/admin/sgUploadCameraImage";

export default {
  name: `sgMobileUploadTypeSelect`,
  components: { sgUploadCameraImage },
  data() {
    return {
      visible: false,
      form: {},
      change: null,

      uploadBtn: null, //上传触发按钮
      isDragenter: false, //是否拖拽进入
    };
  },

  props: [`value`, `data`],

  watch: {
    value: {
      handler(d) {
        this.visible = d;
      },
      deep: true,
      immediate: true,
    },
    visible(d) {
      this.$emit("input", d);
    },
    data: {
      handler(newValue, oldValue) {
        //console.log(`深度监听${this.$options.name}:`, newValue, oldValue);
        if (Object.keys(newValue || {}).length) {
          this.form = this.$g.deepCopy(newValue);
          this.$g.cF2CP(`disabled`, this);
          this.$g.cF2CP(`change`, this);
        }
      },
      deep: true, //深度监听
      immediate: true, //立即执行
    },
  },
  mounted() {
    this.uploadBtn = this.$refs.upload.$children[0].$refs.input;
  },

  methods: {
    // -------------------------------------
    // 唤起摄像头拍照扫描上传
    scan(d) {
      this.$refs.sgUploadCameraImage.scan();
    },
    // 获取拍照图片文件
    getUploadFiles_scan(file) {
      this.$emit(`change`, file);
      this.change && this.change(file);
      this.visible = false;
    },
    // -------------------------------------
    // 获取上传文件----------------------------------------
    getUploadFiles_upload(file) {
      file = file.raw;
      this.$emit(`change`, file);
      this.change && this.change(file);
      this.visible = false;
    },
  },
};
</script>
<style lang="scss" scoped>
.sgMobileUploadTypeSelect {
  position: fixed;
  width: 100vw;
  height: 100vh;
  left: 0;
  bottom: 0;
  pointer-events: none;
  transition: 0.2s;
  z-index: 1;
  .bg {
    width: 100%;
    height: 100%;
    background-color: #00000099;
    opacity: 0;
    transition: 0.2s;
  }
  .container {
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: column;
    transition: 0.2s;
    position: absolute;
    transform: translateY(100%);
    width: 100%;
    left: 0;
    bottom: 0;
    background-color: white;
    height: 200px;
    border-radius: 16px 16px 0 0;
    box-shadow: 0 11px 33px 0 #00000011;
    box-sizing: border-box;
    padding: 20px;
    //   padding-top: 60px;
    .close {
      position: absolute;
      right: 20px;
      top: 20px;
      z-index: 1;
      color: #666;
      i {
        font-size: 20px;
      }
    }

    .upload-types {
      width: 100%;
      display: flex;
      align-items: center;
      justify-content: space-evenly;
      .item {
        width: 100px;
        height: 100px;
        /*禁止选中文本*/
        user-select: none;
        background-color: #eff2f7;
        color: #8896b3;
        font-size: 40px;
        display: flex;
        justify-content: center;
        align-items: center;
        border-radius: 8px;
        box-sizing: border-box;
        &:active {
          color: black;
          background-color: #c6d1de;
        }
      }
    }
  }

  &[show] {
    pointer-events: auto;
    .bg {
      opacity: 1;
    }
    .container {
      transform: translateY(0%);
    }
  }
}
</style>

demo

html 复制代码
<!-- 选择上传方式 -->
<sgMobileUploadTypeSelect
  :data="data_sgMobileUploadTypeSelect"
  v-model="show_sgMobileUploadTypeSelect"
/>


import sgMobileUploadTypeSelect from "@/vue/components/admin/sgMobileUploadTypeSelect";


rw_sgMobileUploadTypeSelect({ w = true, d = {} } = {}) {
  this.data_sgMobileUploadTypeSelect = this.$g.deepCopy(d);
  this.data_sgMobileUploadTypeSelect.disabled = !w;
  this.show_sgMobileUploadTypeSelect = true;
},



<div
  class="upload-btn"
  @click="rw_sgMobileUploadTypeSelect({ d: { change: getUploadFiles } })"
>
  <i class="el-icon-camera" />
</div>
相关推荐
GIS之路1 小时前
ArcGIS Pro 中的 Notebooks 入门
前端
IT_陈寒2 小时前
React状态管理终极对决:Redux vs Context API谁更胜一筹?
前端·人工智能·后端
lemon_yyds3 小时前
《vue 2 升级vue3 父组件 子组件 传值: value 和 v-model
vue.js
Kagol3 小时前
TinyVue 支持 Skills 啦!现在你可以让 AI 使用 TinyVue 组件搭建项目
前端·agent·ai编程
柳杉3 小时前
从零打造 AI 全球趋势监测大屏
前端·javascript·aigc
simple_lau3 小时前
Cursor配置MasterGo MCP:一键读取设计稿生成高还原度前端代码
前端·javascript·vue.js
睡不着先生3 小时前
如何设计一个真正可扩展的表单生成器?
前端·javascript·vue.js
天蓝色的鱼鱼3 小时前
模块化与组件化:90%的前端开发者都没搞懂的本质区别
前端·架构·代码规范
明君879973 小时前
Flutter 如何给图片添加多行文字水印
前端·flutter
进击的尘埃4 小时前
AI 代码审查工具链搭建:用 AST 解析 + LLM 实现自动化 Code Review 的前端工程方案
javascript