yubo
2026-03-24 d68f647b3e74f073d65b21de5e3f1259f70cf1ac
feat(upload): 支持上传方式选择弹窗功能

- 在多个用户相关视图中新增上传方式选择弹窗,允许用户选择拍照上传或文件上传
- 修改头像区域点击事件,调用上传方式选择弹窗而非直接打开摄像头
- 实现文件上传验证逻辑,限定文件类型为图片,大小不超过10MB
- 支持文件上传预览及Base64编码读取,上传成功后提示用户
- 添加上传方式选择弹窗及相关样式,优化用户体验
- 调整部分表格列宽设置,移除多余宽度限制
6个文件已修改
465 ■■■■■ 已修改文件
.env.development 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/user/Informationinput.vue 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/user/archivesChange.vue 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/user/archivesEdit.vue 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/user/inemployees.vue 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/user/outemployess.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
.env.development
@@ -2,7 +2,7 @@
ENV = 'development'
# base api
VUE_APP_BASE_API = 'http://127.0.0.1:8301/'
VUE_APP_BASE_API = 'http://192.168.5.12:8301/'
# vue-cli uses the VUE_CLI_BABEL_TRANSPILE_MODULES environment variable,
# to control whether the babel-plugin-dynamic-import-node plugin is enabled.
src/views/user/Informationinput.vue
@@ -1086,11 +1086,11 @@
              v-if="empBaseInfoImageUrl"
              :src="empBaseInfoImageUrl"
              class="avatar"
              @click="openCamera"
              @click="openUploadChoice"
            >
            <div v-else class="avatar-uploader-placeholder" @click="openCamera">
            <div v-else class="avatar-uploader-placeholder" @click="openUploadChoice">
              <i class="el-icon-plus avatar-uploader-icon" />
              <div class="upload-tip">点击拍照上传</div>
              <div class="upload-tip">点击上传照片</div>
            </div>
          </div>
        </el-aside>
@@ -2594,6 +2594,25 @@
      <Selectuser @selectedUser="selectedUser" @cancleChooseUser="cancleChooseUser" />
    </el-dialog>
    <!-- 上传方式选择弹窗 -->
    <el-dialog
      title="选择上传方式"
      :visible.sync="uploadChoiceDialogVisible"
      width="400px"
      :close-on-click-modal="false"
    >
      <div class="upload-choice-container">
        <div class="upload-choice-item" @click="choiceCamera">
          <i class="el-icon-camera" />
          <span>拍照上传</span>
        </div>
        <div class="upload-choice-item" @click="choiceFile">
          <i class="el-icon-folder-opened" />
          <span>文件上传</span>
        </div>
      </div>
    </el-dialog>
    <!-- 摄像头拍照弹窗 -->
    <el-dialog
      title="拍照上传"
@@ -2640,6 +2659,8 @@
      baseicInformationForm: {},
      dialogShowDryg: false,
      empBaseInfoImageUrl: '',
      // 上传方式选择弹窗
      uploadChoiceDialogVisible: false,
      // 摄像头相关
      cameraDialogVisible: false,
      capturedImage: '',
@@ -3707,6 +3728,58 @@
      } else {
        this.$message.error('图片大小超过4M,请重新上传')
      }
    },
    // 打开上传方式选择弹窗
    openUploadChoice() {
      this.uploadChoiceDialogVisible = true
    },
    // 选择拍照上传
    choiceCamera() {
      this.uploadChoiceDialogVisible = false
      this.cameraDialogVisible = true
      this.$nextTick(() => {
        this.initCamera()
      })
    },
    // 选择文件上传
    choiceFile() {
      this.uploadChoiceDialogVisible = false
      // 创建隐藏的文件输入框
      const input = document.createElement('input')
      input.type = 'file'
      input.accept = 'image/*'
      input.onchange = (e) => {
        const file = e.target.files[0]
        if (file) {
          this.handleFileUpload(file)
        }
      }
      input.click()
    },
    // 处理文件上传
    handleFileUpload(file) {
      // 验证文件类型
      const isImage = file.type.startsWith('image/')
      if (!isImage) {
        this.$message.error('请上传图片文件')
        return
      }
      // 验证文件大小(限制10MB)
      const isLt10M = file.size / 1024 / 1024 < 10
      if (!isLt10M) {
        this.$message.error('图片大小不能超过10MB')
        return
      }
      // 生成预览URL
      const imageUrl = URL.createObjectURL(file)
      this.empBaseInfoImageUrl = imageUrl
      // 读取文件为Base64
      const reader = new FileReader()
      reader.onload = (e) => {
        this.empBaseInfoForm.imagePath = e.target.result
        this.$message.success('照片上传成功')
      }
      reader.readAsDataURL(file)
    },
    // 打开摄像头
    openCamera() {
@@ -6676,6 +6749,42 @@
  color: #8c939d;
}
/* 上传方式选择容器 */
.upload-choice-container {
  display: flex;
  justify-content: space-around;
  padding: 20px 0;
  .upload-choice-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    width: 120px;
    height: 120px;
    border: 2px dashed #d9d9d9;
    border-radius: 8px;
    cursor: pointer;
    transition: all 0.3s;
    &:hover {
      border-color: #409eff;
      background-color: #f5f7fa;
    }
    i {
      font-size: 40px;
      color: #409eff;
      margin-bottom: 10px;
    }
    span {
      font-size: 14px;
      color: #606266;
    }
  }
}
/* 摄像头弹窗样式 */
.camera-container {
  display: flex;
src/views/user/archivesChange.vue
@@ -20,11 +20,11 @@
                v-if="empBaseInfoImageUrl"
                :src="empBaseInfoImageUrl"
                class="avatar"
                @click="openCamera"
                @click="openUploadChoice"
              >
              <div v-else class="avatar-uploader-placeholder" @click="openCamera">
              <div v-else class="avatar-uploader-placeholder" @click="openUploadChoice">
                <i class="el-icon-plus avatar-uploader-icon" />
                <div class="upload-tip">点击拍照上传</div>
                <div class="upload-tip">点击上传照片</div>
              </div>
            </div>
            <!-- <img src="https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=1333074204,3035391839&fm=26&gp=0.jpg" class="jbxxImg">-->
@@ -2597,6 +2597,25 @@
      </el-dialog>
    </el-dialog>
    <!-- 上传方式选择弹窗 -->
    <el-dialog
      title="选择上传方式"
      :visible.sync="uploadChoiceDialogVisible"
      width="400px"
      :close-on-click-modal="false"
    >
      <div class="upload-choice-container">
        <div class="upload-choice-item" @click="choiceCamera">
          <i class="el-icon-camera" />
          <span>拍照上传</span>
        </div>
        <div class="upload-choice-item" @click="choiceFile">
          <i class="el-icon-folder-opened" />
          <span>文件上传</span>
        </div>
      </div>
    </el-dialog>
    <!-- 摄像头拍照弹窗 -->
    <el-dialog
      title="拍照上传"
@@ -2757,6 +2776,8 @@
        }
      ],
      empBaseInfoImageUrl: '',
      // 上传方式选择弹窗
      uploadChoiceDialogVisible: false,
      // 摄像头相关
      cameraDialogVisible: false,
      capturedImage: '',
@@ -4761,6 +4782,58 @@
    getIndex($index) {
      return (this.pagination.num - 1) * this.pagination.size + $index + 1
    },
    // 打开上传方式选择弹窗
    openUploadChoice() {
      this.uploadChoiceDialogVisible = true
    },
    // 选择拍照上传
    choiceCamera() {
      this.uploadChoiceDialogVisible = false
      this.cameraDialogVisible = true
      this.$nextTick(() => {
        this.initCamera()
      })
    },
    // 选择文件上传
    choiceFile() {
      this.uploadChoiceDialogVisible = false
      // 创建隐藏的文件输入框
      const input = document.createElement('input')
      input.type = 'file'
      input.accept = 'image/*'
      input.onchange = (e) => {
        const file = e.target.files[0]
        if (file) {
          this.handleFileUpload(file)
        }
      }
      input.click()
    },
    // 处理文件上传
    handleFileUpload(file) {
      // 验证文件类型
      const isImage = file.type.startsWith('image/')
      if (!isImage) {
        this.$message.error('请上传图片文件')
        return
      }
      // 验证文件大小(限制10MB)
      const isLt10M = file.size / 1024 / 1024 < 10
      if (!isLt10M) {
        this.$message.error('图片大小不能超过10MB')
        return
      }
      // 生成预览URL
      const imageUrl = URL.createObjectURL(file)
      this.empBaseInfoImageUrl = imageUrl
      // 读取文件为Base64
      const reader = new FileReader()
      reader.onload = (e) => {
        this.empBaseInfoForm.imagePath = e.target.result
        this.$message.success('照片上传成功')
      }
      reader.readAsDataURL(file)
    },
    // 打开摄像头
    openCamera() {
      this.cameraDialogVisible = true
@@ -6132,6 +6205,42 @@
    }
  }
  // 上传方式选择容器
  .upload-choice-container {
    display: flex;
    justify-content: space-around;
    padding: 20px 0;
    .upload-choice-item {
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      width: 120px;
      height: 120px;
      border: 2px dashed #d9d9d9;
      border-radius: 8px;
      cursor: pointer;
      transition: all 0.3s;
      &:hover {
        border-color: #409eff;
        background-color: #f5f7fa;
      }
      i {
        font-size: 40px;
        color: #409eff;
        margin-bottom: 10px;
      }
      span {
        font-size: 14px;
        color: #606266;
      }
    }
  }
  // 摄像头容器
  .camera-container {
    text-align: center;
src/views/user/archivesEdit.vue
@@ -20,11 +20,11 @@
                v-if="empBaseInfoImageUrl"
                :src="empBaseInfoImageUrl"
                class="avatar"
                @click="openCamera"
                @click="openUploadChoice"
              >
              <div v-else class="avatar-uploader-placeholder" @click="openCamera">
              <div v-else class="avatar-uploader-placeholder" @click="openUploadChoice">
                <i class="el-icon-plus avatar-uploader-icon" />
                <div class="upload-tip">点击拍照上传</div>
                <div class="upload-tip">点击上传照片</div>
              </div>
            </div>
            <!-- <img src="https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=1333074204,3035391839&fm=26&gp=0.jpg" class="jbxxImg">-->
@@ -2893,6 +2893,25 @@
      </el-dialog>
    </el-dialog>
    <!-- 上传方式选择弹窗 -->
    <el-dialog
      title="选择上传方式"
      :visible.sync="uploadChoiceDialogVisible"
      width="400px"
      :close-on-click-modal="false"
    >
      <div class="upload-choice-container">
        <div class="upload-choice-item" @click="choiceCamera">
          <i class="el-icon-camera" />
          <span>拍照上传</span>
        </div>
        <div class="upload-choice-item" @click="choiceFile">
          <i class="el-icon-folder-opened" />
          <span>文件上传</span>
        </div>
      </div>
    </el-dialog>
    <!-- 摄像头拍照弹窗 -->
    <el-dialog
      title="拍照上传"
@@ -3078,6 +3097,8 @@
        }
      ],
      empBaseInfoImageUrl: '',
      // 上传方式选择弹窗
      uploadChoiceDialogVisible: false,
      // 摄像头相关
      cameraDialogVisible: false,
      capturedImage: '',
@@ -5200,6 +5221,58 @@
    getIndex($index) {
      return (this.pagination.num - 1) * this.pagination.size + $index + 1
    },
    // 打开上传方式选择弹窗
    openUploadChoice() {
      this.uploadChoiceDialogVisible = true
    },
    // 选择拍照上传
    choiceCamera() {
      this.uploadChoiceDialogVisible = false
      this.cameraDialogVisible = true
      this.$nextTick(() => {
        this.initCamera()
      })
    },
    // 选择文件上传
    choiceFile() {
      this.uploadChoiceDialogVisible = false
      // 创建隐藏的文件输入框
      const input = document.createElement('input')
      input.type = 'file'
      input.accept = 'image/*'
      input.onchange = (e) => {
        const file = e.target.files[0]
        if (file) {
          this.handleFileUpload(file)
        }
      }
      input.click()
    },
    // 处理文件上传
    handleFileUpload(file) {
      // 验证文件类型
      const isImage = file.type.startsWith('image/')
      if (!isImage) {
        this.$message.error('请上传图片文件')
        return
      }
      // 验证文件大小(限制10MB)
      const isLt10M = file.size / 1024 / 1024 < 10
      if (!isLt10M) {
        this.$message.error('图片大小不能超过10MB')
        return
      }
      // 生成预览URL
      const imageUrl = URL.createObjectURL(file)
      this.empBaseInfoImageUrl = imageUrl
      // 读取文件为Base64
      const reader = new FileReader()
      reader.onload = (e) => {
        this.empBaseInfoForm.imagePath = e.target.result
        this.$message.success('照片上传成功')
      }
      reader.readAsDataURL(file)
    },
    // 打开摄像头
    openCamera() {
      this.cameraDialogVisible = true
@@ -6597,6 +6670,42 @@
  }
}
// 上传方式选择容器
.upload-choice-container {
  display: flex;
  justify-content: space-around;
  padding: 20px 0;
  .upload-choice-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    width: 120px;
    height: 120px;
    border: 2px dashed #d9d9d9;
    border-radius: 8px;
    cursor: pointer;
    transition: all 0.3s;
    &:hover {
      border-color: #409eff;
      background-color: #f5f7fa;
    }
    i {
      font-size: 40px;
      color: #409eff;
      margin-bottom: 10px;
    }
    span {
      font-size: 14px;
      color: #606266;
    }
  }
}
// 摄像头容器
.camera-container {
  text-align: center;
src/views/user/inemployees.vue
@@ -267,7 +267,6 @@
            show-overflow-tooltip
            prop="allDeptName"
            label="部门(护卫点)"
            width="300"
            sortable="custom"
            :sort-orders="['ascending', 'descending']"
          />
@@ -353,11 +352,11 @@
              v-if="empBaseInfoImageUrl"
              :src="empBaseInfoImageUrl"
              class="avatar"
              @click="openCamera"
              @click="openUploadChoice"
            >
            <div v-else class="avatar-uploader-placeholder" @click="openCamera">
            <div v-else class="avatar-uploader-placeholder" @click="openUploadChoice">
              <i class="el-icon-plus avatar-uploader-icon" />
              <div class="upload-tip">点击拍照上传</div>
              <div class="upload-tip">点击上传照片</div>
            </div>
          </div>
        </el-aside>
@@ -1003,6 +1002,25 @@
      </div>
    </el-dialog>
    <!-- 上传方式选择弹窗 -->
    <el-dialog
      title="选择上传方式"
      :visible.sync="uploadChoiceDialogVisible"
      width="400px"
      :close-on-click-modal="false"
    >
      <div class="upload-choice-container">
        <div class="upload-choice-item" @click="choiceCamera">
          <i class="el-icon-camera" />
          <span>拍照上传</span>
        </div>
        <div class="upload-choice-item" @click="choiceFile">
          <i class="el-icon-folder-opened" />
          <span>文件上传</span>
        </div>
      </div>
    </el-dialog>
    <!-- 摄像头拍照弹窗 -->
    <el-dialog
      title="拍照上传"
@@ -1104,6 +1122,8 @@
      },
      baseicInformationForm: {},
      empBaseInfoImageUrl: '',
      // 上传方式选择弹窗
      uploadChoiceDialogVisible: false,
      // 摄像头相关
      cameraDialogVisible: false,
      capturedImage: '',
@@ -1646,6 +1666,58 @@
      } else {
        this.$message.error('图片大小超过4M,请重新上传')
      }
    },
    // 打开上传方式选择弹窗
    openUploadChoice() {
      this.uploadChoiceDialogVisible = true
    },
    // 选择拍照上传
    choiceCamera() {
      this.uploadChoiceDialogVisible = false
      this.cameraDialogVisible = true
      this.$nextTick(() => {
        this.initCamera()
      })
    },
    // 选择文件上传
    choiceFile() {
      this.uploadChoiceDialogVisible = false
      // 创建隐藏的文件输入框
      const input = document.createElement('input')
      input.type = 'file'
      input.accept = 'image/*'
      input.onchange = (e) => {
        const file = e.target.files[0]
        if (file) {
          this.handleFileUpload(file)
        }
      }
      input.click()
    },
    // 处理文件上传
    handleFileUpload(file) {
      // 验证文件类型
      const isImage = file.type.startsWith('image/')
      if (!isImage) {
        this.$message.error('请上传图片文件')
        return
      }
      // 验证文件大小(限制10MB)
      const isLt10M = file.size / 1024 / 1024 < 10
      if (!isLt10M) {
        this.$message.error('图片大小不能超过10MB')
        return
      }
      // 生成预览URL
      const imageUrl = URL.createObjectURL(file)
      this.empBaseInfoImageUrl = imageUrl
      // 读取文件为Base64
      const reader = new FileReader()
      reader.onload = (e) => {
        this.empBaseInfoForm.imagePath = e.target.result
        this.$message.success('照片上传成功')
      }
      reader.readAsDataURL(file)
    },
    // 打开摄像头
    openCamera() {
@@ -2646,6 +2718,42 @@
  }
}
// 上传方式选择容器
.upload-choice-container {
  display: flex;
  justify-content: space-around;
  padding: 20px 0;
  .upload-choice-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    width: 120px;
    height: 120px;
    border: 2px dashed #d9d9d9;
    border-radius: 8px;
    cursor: pointer;
    transition: all 0.3s;
    &:hover {
      border-color: #409eff;
      background-color: #f5f7fa;
    }
    i {
      font-size: 40px;
      color: #409eff;
      margin-bottom: 10px;
    }
    span {
      font-size: 14px;
      color: #606266;
    }
  }
}
// 摄像头容器
.camera-container {
  text-align: center;
src/views/user/outemployess.vue
@@ -263,7 +263,7 @@
            </template>
          </el-table-column>
          <el-table-column show-overflow-tooltip prop="empNumb" label="编号" width="80" sortable="custom" :sort-orders="['ascending', 'descending']" />
          <el-table-column show-overflow-tooltip prop="allDeptName" label="部门(护卫点)" width="280" sortable="custom" :sort-orders="['ascending', 'descending']" />
          <el-table-column show-overflow-tooltip prop="allDeptName" label="部门(护卫点)" sortable="custom" :sort-orders="['ascending', 'descending']" />
          <el-table-column show-overflow-tooltip prop="jobName" label="岗位" width="80" />
          <el-table-column show-overflow-tooltip prop="empName" label="姓名" width="80" sortable="custom" :sort-orders="['ascending', 'descending']" />
          <el-table-column show-overflow-tooltip prop="certificateNumb" label="身份证号码" width="160" sortable="custom" :sort-orders="['ascending', 'descending']" />