From 74612d64c6bd2d28dcf80450f2cb94ffbee9970d Mon Sep 17 00:00:00 2001
From: yubo <autumnal_wind@yeah.net>
Date: 星期四, 12 三月 2026 11:13:27 +0800
Subject: [PATCH] feat(user): 添加员工档案拍照上传功能

---
 src/views/user/inemployees.vue |  436 ++++++++++++++++++++++++++++++++++++++++++++++--------
 1 files changed, 369 insertions(+), 67 deletions(-)

diff --git a/src/views/user/inemployees.vue b/src/views/user/inemployees.vue
index 4fca2d6..49d7fe0 100644
--- a/src/views/user/inemployees.vue
+++ b/src/views/user/inemployees.vue
@@ -124,7 +124,7 @@
                   </td>
                 </tr>
                 <tr>
-                  <td class="td">保险类型:</td>
+                  <td class="td">社保档位:</td>
                   <td class="td-group">
                     <el-checkbox-group v-model="queryParams.insuranceType" class="fj-checkbox">
                       <el-checkbox label="" @change="selectAllInsuranceType">全部</el-checkbox>
@@ -189,7 +189,7 @@
                     <el-checkbox-group v-model="queryParams.entryType" class="fj-checkbox">
                       <el-checkbox label="" @change="selectAllEntryType">全部</el-checkbox>
                       <el-checkbox :label="20">新入职</el-checkbox>
-                      <el-checkbox :label="21">重新新入职</el-checkbox>
+                      <el-checkbox :label="21">重新入职</el-checkbox>
                       <el-checkbox :label="22">返聘入职</el-checkbox>
                       <!--                      <el-checkbox-->
                       <!--                        v-for="data in dimissionTypeOptions"-->
@@ -198,6 +198,21 @@
                       <!--                      >-->
                       <!--                        {{ data.dicItemName }}-->
                       <!--                      </el-checkbox>-->
+                    </el-checkbox-group>
+                  </td>
+                </tr>
+                <tr>
+                  <td class="td">相关证件:</td>
+                  <td class="td-group">
+                    <el-checkbox-group v-model="queryParams.certificateList" class="fj-checkbox">
+                      <el-checkbox label="" @change="selectAllCertificateList">全部</el-checkbox>
+                      <el-checkbox
+                        v-for="data in certificateListOptions"
+                        :key="data.dicItemName"
+                        :label="data.dicItemCode"
+                      >
+                        {{ data.dicItemName }}
+                      </el-checkbox>
                     </el-checkbox-group>
                   </td>
                 </tr>
@@ -230,14 +245,6 @@
           <el-table-column type="selection" width="55" />
           <el-table-column label="操作" width="130">
             <template slot-scope="scope">
-              <!--              <el-button-->
-              <!--                type="text"-->
-              <!--                size="small"-->
-              <!--                @click="editArchives(scope.row)"-->
-              <!--              >-->
-              <!--                档案</el-button>-->
-              <!--              <el-button type="text" size="small" @click="editEmpBase(scope.row)">编辑</el-button>-->
-
               <span
                 style="color: #a00515;display: inline-block;width: 40%;cursor: pointer"
                 @click="editArchives(scope.row)"
@@ -248,28 +255,84 @@
               >编辑</span>
             </template>
           </el-table-column>
-          <el-table-column show-overflow-tooltip="true" prop="empNumb" label="编号" width="80" sortable="custom" :sort-orders="['ascending', 'descending']" />
-          <el-table-column show-overflow-tooltip="true" prop="allDeptName" label="部门(护卫点)" width="300" sortable="custom" :sort-orders="['ascending', 'descending']" />
-          <el-table-column show-overflow-tooltip="true" prop="jobName" label="岗位" width="80" />
-          <el-table-column show-overflow-tooltip="true" prop="empName" label="姓名" width="100" sortable="custom" :sort-orders="['ascending', 'descending']" />
-          <el-table-column show-overflow-tooltip="true" prop="certificateNumb" label="身份证号码" width="160" />
-          <el-table-column show-overflow-tooltip="true" prop="sex" label="性别" width="60" :formatter="sexFormat" />
-          <el-table-column show-overflow-tooltip="true" prop="age" label="年龄" width="80" sortable="custom" :sort-orders="['ascending', 'descending']" />
-          <el-table-column show-overflow-tooltip="true" prop="education" label="学历" width="80" :formatter="educationFormat" sortable="custom" :sort-orders="['ascending', 'descending']" />
-          <el-table-column show-overflow-tooltip="true" prop="nativePlace" label="籍贯" width="70" :formatter="nativePlaceFormat" />
-          <el-table-column show-overflow-tooltip="true" prop="telePhone" label="联系电话" width="110" />
-          <el-table-column show-overflow-tooltip="true" prop="empTypeName" label="员工类型" width="80" />
-          <el-table-column show-overflow-tooltip="true" prop="entryDate" label="入职日期" width="110" sortable="custom" :sort-orders="['ascending', 'descending']" />
-          <el-table-column show-overflow-tooltip="true" prop="empStatus" label="员工状态" width="80">
+          <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="300"
+            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="100"
+            sortable="custom"
+            :sort-orders="['ascending', 'descending']"
+          />
+          <el-table-column show-overflow-tooltip prop="certificateNumb" label="身份证号码" width="160" />
+          <el-table-column show-overflow-tooltip prop="sex" label="性别" width="60" :formatter="sexFormat" />
+          <el-table-column
+            show-overflow-tooltip
+            prop="age"
+            label="年龄"
+            width="80"
+            sortable="custom"
+            :sort-orders="['ascending', 'descending']"
+          />
+          <el-table-column
+            show-overflow-tooltip
+            prop="education"
+            label="学历"
+            width="80"
+            :formatter="educationFormat"
+            sortable="custom"
+            :sort-orders="['ascending', 'descending']"
+          />
+          <el-table-column
+            show-overflow-tooltip
+            prop="nativePlace"
+            label="籍贯"
+            width="70"
+            :formatter="nativePlaceFormat"
+          />
+          <el-table-column show-overflow-tooltip prop="telePhone" label="联系电话" width="110" />
+          <el-table-column show-overflow-tooltip prop="empTypeName" label="员工类型" width="80" />
+          <el-table-column
+            show-overflow-tooltip
+            prop="entryDate"
+            label="入职日期"
+            width="110"
+            sortable="custom"
+            :sort-orders="['ascending', 'descending']"
+          />
+          <el-table-column show-overflow-tooltip prop="empStatus" label="员工状态" width="80">
             <template slot-scope="{row}">
               {{ transEmpStatus(row.empStatus) }}
             </template>
           </el-table-column>
-          <el-table-column prop="entryType" label="入职类型" width="120" sortable="custom" :sort-orders="['ascending', 'descending']">
+          <el-table-column
+            prop="entryType"
+            label="入职类型"
+            width="120"
+            sortable="custom"
+            :sort-orders="['ascending', 'descending']"
+          >
             <template slot-scope="{row}">
               {{ transEntryType(row.entryType) }}
             </template>
           </el-table-column>
+          <el-table-column show-overflow-tooltip prop="annualLeave" label="年假天数" width="110" />
         </el-table>
         <pagination
           v-show="total>0"
@@ -284,17 +347,19 @@
     <el-dialog title="基本信息" :visible.sync="dialogShowXzyg" width="50%" class="baseinfo">
       <el-container>
         <el-aside width="200px">
-          <el-upload
-            class="avatar-uploader"
-            action="#"
-            :show-file-list="false"
-            :on-change="handlePictureCardPreview"
-            :auto-upload="false"
-          >
-            <div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过4MB</div>
-            <img v-if="empBaseInfoImageUrl" :src="empBaseInfoImageUrl" class="avatar">
-            <i v-else class="el-icon-plus avatar-uploader-icon" />
-          </el-upload>
+          <!-- 头像显示区域 -->
+          <div class="avatar-wrapper">
+            <img
+              v-if="empBaseInfoImageUrl"
+              :src="empBaseInfoImageUrl"
+              class="avatar"
+              @click="openCamera"
+            >
+            <div v-else class="avatar-uploader-placeholder" @click="openCamera">
+              <i class="el-icon-plus avatar-uploader-icon" />
+              <div class="upload-tip">点击拍照上传</div>
+            </div>
+          </div>
         </el-aside>
         <el-main>
           <el-form
@@ -392,8 +457,8 @@
                 <el-form-item label="银行名称">
                   <el-input v-model="empBaseInfoForm.bankName" />
                 </el-form-item>
-                <el-form-item label="保险类型" prop="insuranceType">
-                  <el-select v-model="empBaseInfoForm.insuranceType" placeholder="请选择保险类型">
+                <el-form-item label="社保档位" prop="insuranceType">
+                  <el-select v-model="empBaseInfoForm.insuranceType" placeholder="请选择社保档位">
                     <el-option
                       v-for="dict in insuranceTypeOptions"
                       :key="dict.dicItemCode"
@@ -402,10 +467,10 @@
                     />
                   </el-select>
                 </el-form-item>
-                <el-form-item label="家庭成员及关系" prop="family">
+                <el-form-item label="家庭成员及关系1" prop="family">
                   <el-input v-model="empBaseInfoForm.family" />
                 </el-form-item>
-                <el-form-item label="紧急联系电话" prop="urgencyPhone">
+                <el-form-item label="家庭成员及关系2" prop="urgencyPhone">
                   <el-input v-model="empBaseInfoForm.urgencyPhone" />
                 </el-form-item>
               </el-col>
@@ -611,7 +676,7 @@
               <el-upload
                 ref="upload"
                 class="upload-demo"
-                action="http://120.24.23.155:8301/hr/empBaseInfo/importEmp"
+                action="http://127.0.0.1:8301/hr/empBaseInfo/importEmp"
                 accept=".xls, .xlsx, .excel"
                 :headers="headers()"
                 :file-list="fileList"
@@ -628,7 +693,7 @@
         <el-button type="primary" @click="showDryg(2)">确 定</el-button>
       </div>
     </el-dialog>
-    <el-dialog title="关闭档案" :visible.sync="dialogShowGbda" width="50%">
+    <el-dialog title="关闭档案" :visible.sync="dialogShowGbda" width="70%">
       <el-form ref="gbdaForm" :model="gbdaForm" :rules="gbdaRules" label-position="right" label-width="120px">
         <el-row>
           <el-col span="24">
@@ -640,6 +705,7 @@
                 <el-radio :label="4">自动离职</el-radio>
                 <el-radio :label="5">试用期内</el-radio>
                 <el-radio :label="6">正常离职</el-radio>
+                <el-radio :label="7">到龄退休</el-radio>
               </el-radio-group>
             </el-form-item>
           </el-col>
@@ -682,7 +748,9 @@
       </div>
     </el-dialog>
     <el-dialog title="导出员工" :visible.sync="dialogShowDcyg" width="40%">
-      <div style="font-size: 16px;font-weight: 400;height: 35px;margin-top: -30px;color: #409EFF;">请勾选需要导出的字段</div>
+      <div style="font-size: 16px;font-weight: 400;height: 35px;margin-top: -30px;color: #409EFF;">
+        请勾选需要导出的字段
+      </div>
       <table id="dcygTable" width="100%">
         <tr>
           <td colspan="6" style="text-align: left;">
@@ -804,7 +872,7 @@
             </td>
           </tr>
           <tr>
-            <td>家庭成员及关系</td>
+            <td>家庭成员及关系1</td>
             <td>
               <el-checkbox label="family"><span /></el-checkbox>
             </td>
@@ -822,7 +890,7 @@
             <td>
               <el-checkbox label="entryDate"><span /></el-checkbox>
             </td>
-            <td>保险类型</td>
+            <td>社保档位</td>
             <td>
               <el-checkbox label="insuranceTypeName"><span /></el-checkbox>
             </td>
@@ -848,9 +916,9 @@
           <tr>
             <td>相关证件</td>
             <td>
-              <el-checkbox label="certificateList"><span /></el-checkbox>
+              <el-checkbox label="certificateListName"><span /></el-checkbox>
             </td>
-            <td>紧急联系电话</td>
+            <td>家庭成员及关系2</td>
             <td>
               <el-checkbox label="urgencyPhone"><span /></el-checkbox>
             </td>
@@ -873,16 +941,16 @@
               <el-checkbox label="empNumb"><span /></el-checkbox>
             </td>
           </tr>
-          <!--          <tr>-->
-          <!--            <td>家庭成员及关系</td>-->
-          <!--            <td>-->
-          <!--              <el-checkbox label="family"><span /></el-checkbox>-->
-          <!--            </td>-->
-          <!--            <td />-->
-          <!--            <td />-->
-          <!--            <td />-->
-          <!--            <td />-->
-          <!--          </tr>-->
+          <tr>
+            <td>年假天数</td>
+            <td>
+              <el-checkbox label="annualLeave"><span /></el-checkbox>
+            </td>
+            <td />
+            <td />
+            <td />
+            <td />
+          </tr>
         </el-checkbox-group>
       </table>
       <div slot="footer" class="dialog-footer">
@@ -910,8 +978,8 @@
           <el-col span="24">
             <el-form-item label="打开类型" prop="region">
               <el-radio-group v-model="openArchivesForm.openType">
-                <el-radio :label="20">重新入职</el-radio>
-                <el-radio :label="21">返聘</el-radio>
+                <el-radio :label="21">重新入职</el-radio>
+                <el-radio :label="22">返聘</el-radio>
               </el-radio-group>
               (主要对已退休员工返聘)
             </el-form-item>
@@ -932,6 +1000,59 @@
       <div slot="footer" class="dialog-footer">
         <el-button @click="showDkda(1, '')">取 消</el-button>
         <el-button type="primary" @click="showDkda(2, 'openArchivesForm')">确 定</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 摄像头拍照弹窗 -->
+    <el-dialog
+      title="拍照上传"
+      :visible.sync="cameraDialogVisible"
+      width="640px"
+      :close-on-click-modal="false"
+      @close="closeCamera"
+    >
+      <div class="camera-container">
+        <!-- 视频预览 -->
+        <video
+          v-show="!capturedImage"
+          ref="video"
+          class="camera-video"
+          autoplay
+          playsinline
+        />
+        <!-- 画布(用于拍照) -->
+        <canvas ref="canvas" style="display: none;" />
+
+        <!-- 拍照结果预览 -->
+        <img
+          v-if="capturedImage"
+          :src="capturedImage"
+          class="captured-image"
+        >
+      </div>
+
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="closeCamera">取消</el-button>
+        <el-button
+          v-if="!capturedImage"
+          type="primary"
+          @click="takePhoto"
+        >
+          拍照
+        </el-button>
+        <el-button
+          v-if="capturedImage"
+          @click="retakePhoto"
+        >
+          重拍
+        </el-button>
+        <el-button
+          v-if="capturedImage"
+          type="primary"
+          @click="confirmPhoto"
+        >
+          确认使用
+        </el-button>
       </div>
     </el-dialog>
   </div>
@@ -973,7 +1094,8 @@
         empCardStatus: [],
         handbookStatus: [],
         entryType: [],
-        empType: []
+        empType: [],
+        certificateList: []
       }, // 查询参数
       sort: {}, // 排序
       pagination: { // 分页参数
@@ -982,6 +1104,10 @@
       },
       baseicInformationForm: {},
       empBaseInfoImageUrl: '',
+      // 摄像头相关
+      cameraDialogVisible: false,
+      capturedImage: '',
+      stream: null,
       empBaseInfoForm: {
         imagePath: '',
         empId: '',
@@ -1079,7 +1205,7 @@
           message: '长度不超过36个字符',
           trigger: 'blur'
         }],
-        insuranceType: [{ required: true, message: '请选择保险类型', trigger: 'change' }],
+        insuranceType: [{ required: true, message: '请选择社保档位', trigger: 'change' }],
         entryDate: [{ required: true, message: '请选择入职日期', trigger: 'change' }],
         seniority: [{ required: true, message: '请输入入司工龄', trigger: 'blur' }],
         archivesStatus: [{ required: true, message: '请选择档案情况', trigger: 'change' }],
@@ -1187,7 +1313,7 @@
       exportUrl: '',
       checkAll: false,
       isIndeterminate: false,
-      cityOptions: ['archivesNumb', 'allDeptName', 'jobName', 'empName', 'certificateNumb', 'certificateValidity', 'sexName', 'nationName', 'age', 'marriageName', 'stature', 'birthdate', 'politicsName', 'empTypeName', 'educationName', 'nativePlaceName', 'censusAddress', 'currentAddress', 'guardNumb', 'returnReceipt', 'archivesStatusName', 'bankName', 'bankNumb', 'telePhone', 'entryDate', 'insuranceTypeName', 'socialNumb', 'introducer', 'seniority', 'empCardStatusName', 'certificateList', 'urgencyPhone', 'handbookStatusName', 'family', 'empStatusName', 'dimissionDate', 'entryTypeName', 'dimissionTypeName', 'empNumb'],
+      cityOptions: ['archivesNumb', 'allDeptName', 'jobName', 'empName', 'certificateNumb', 'certificateValidity', 'sexName', 'nationName', 'age', 'marriageName', 'stature', 'birthdate', 'politicsName', 'empTypeName', 'educationName', 'nativePlaceName', 'censusAddress', 'currentAddress', 'guardNumb', 'returnReceipt', 'archivesStatusName', 'bankName', 'bankNumb', 'telePhone', 'entryDate', 'insuranceTypeName', 'socialNumb', 'introducer', 'seniority', 'empCardStatusName', 'certificateListName', 'urgencyPhone', 'handbookStatusName', 'family', 'empStatusName', 'dimissionDate', 'entryTypeName', 'dimissionTypeName', 'empNumb', 'annualLeave'],
       checkedCities: [],
       tableData: [],
       selectDimissionType: 1,
@@ -1217,7 +1343,7 @@
       dimissionTypeOptions: [],
       ageStrOptions: [],
       sexOptions: [],
-      openArchivesForm: { }
+      openArchivesForm: {}
     }
   },
   mounted() {
@@ -1274,7 +1400,7 @@
     this.getDicts('LEAVETYPE').then(response => {
       this.leaveTypeOptions = response.data
     })
-    this.getDicts('insuranceGaers').then(response => {
+    this.getDicts('INSURANCETYPE').then(response => {
       this.insuranceGaersOptions = response.data
     })
     this.getDicts('applayStatus').then(response => {
@@ -1336,7 +1462,8 @@
         handbookStatus: [],
         baseKey: '',
         empType: [],
-        entryType: []
+        entryType: [],
+        certificateList: []
       }
       this.search()
     },
@@ -1520,6 +1647,97 @@
         this.$message.error('图片大小超过4M,请重新上传')
       }
     },
+    // 打开摄像头
+    openCamera() {
+      this.cameraDialogVisible = true
+      this.$nextTick(() => {
+        this.initCamera()
+      })
+    },
+    // 初始化摄像头
+    async initCamera() {
+      try {
+        // 请求摄像头权限
+        this.stream = await navigator.mediaDevices.getUserMedia({
+          video: {
+            width: { ideal: 640 },
+            height: { ideal: 480 },
+            facingMode: 'user' // 前置摄像头
+          },
+          audio: false
+        })
+
+        // 将视频流绑定到 video 元素
+        const video = this.$refs.video
+        if (video) {
+          video.srcObject = this.stream
+        }
+      } catch (error) {
+        this.$message.error('无法访问摄像头,请检查摄像头权限设置')
+        console.error('摄像头初始化失败:', error)
+      }
+    },
+    // 拍照
+    takePhoto() {
+      const video = this.$refs.video
+      const canvas = this.$refs.canvas
+
+      if (!video || !canvas) return
+
+      // 设置画布尺寸
+      canvas.width = video.videoWidth || 640
+      canvas.height = video.videoHeight || 480
+
+      // 绘制视频帧到画布
+      const ctx = canvas.getContext('2d')
+      ctx.drawImage(video, 0, 0, canvas.width, canvas.height)
+
+      // 转换为图片数据
+      this.capturedImage = canvas.toDataURL('image/jpeg', 0.9)
+
+      // 停止摄像头
+      this.stopCamera()
+    },
+    // 重拍
+    retakePhoto() {
+      this.capturedImage = ''
+      this.initCamera()
+    },
+    // 确认使用照片
+    confirmPhoto() {
+      if (this.capturedImage) {
+        // 设置图片预览
+        this.empBaseInfoImageUrl = this.capturedImage
+
+        // 设置表单数据(Base64格式)
+        this.empBaseInfoForm.imagePath = this.capturedImage
+
+        // 关闭弹窗
+        this.closeCamera()
+
+        this.$message.success('照片已保存')
+      }
+    },
+    // 关闭摄像头
+    closeCamera() {
+      this.stopCamera()
+      this.cameraDialogVisible = false
+      this.capturedImage = ''
+    },
+    // 停止摄像头流
+    stopCamera() {
+      if (this.stream) {
+        this.stream.getTracks().forEach(track => {
+          track.stop()
+        })
+        this.stream = null
+      }
+
+      const video = this.$refs.video
+      if (video) {
+        video.srcObject = null
+      }
+    },
     cleanEmpBase() {
       this.empBaseInfoForm.empId = ''
       this.empBaseInfoForm.customerId = ''
@@ -1604,6 +1822,7 @@
       this.empBaseInfoForm.stature = row.stature
       this.empBaseInfoForm.politics = row.politics
       this.empBaseInfoForm.entryDate = row.entryDate
+      this.empBaseInfoForm.entryType = row.entryType
       this.empBaseInfoForm.education = row.education
       this.empBaseInfoForm.seniority = row.seniority
       this.empBaseInfoForm.nativePlace = row.nativePlace
@@ -1902,12 +2121,14 @@
             var names = []
             var dates = []
             var numbers = []
+            var deptNames = []
             for (var i = 0; i < selection.length; i++) {
               var data = selection[i]
               ids.push(data.empId)
               names.push(data.empName)
               dates.push(data.entryDate)
               numbers.push(data.certificateNumb)
+              deptNames.push(data.allDeptName)
             }
             const nowDate = new Date()
             const year = nowDate.getFullYear()
@@ -1921,7 +2142,8 @@
               dimissionDate: year + '-' + month + '-' + day,
               remark: '',
               afterOperation: ['1'],
-              certificateNumb: numbers.join(',')
+              certificateNumb: numbers.join(','),
+              deptNames: deptNames.join(',')
             }
             this.dialogShowGbda = true
           }
@@ -2051,6 +2273,13 @@
         this.queryParams.entryType = []
       }
     },
+    selectAllCertificateList(val) {
+      if (val) {
+        this.queryParams.certificateList = this.dicListToArr(this.certificateListOptions)
+      } else {
+        this.queryParams.certificateList = []
+      }
+    },
     headers() {
       const token = getToken()
       if (token) {
@@ -2110,7 +2339,7 @@
                   empId: r.data.data.empId,
                   empName: r.data.data.empName,
                   certificateNumb: r.data.data.certificateNumb,
-                  openType: 20,
+                  openType: 21,
                   openDate: this.getDateString(),
                   remark: ''
                 }
@@ -2316,10 +2545,12 @@
 
 #dcygTable {
   border-collapse: collapse;
+
   tr {
     width: 100%;
     border-bottom: 1px dashed #ccc;
-    >td:nth-child(even) {
+
+    > td:nth-child(even) {
       width: 100px;
       text-align: left;
     }
@@ -2363,4 +2594,75 @@
   cursor: pointer;
   vertical-align: middle;
 }
+
+// 头像包装器
+.avatar-wrapper {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  margin-bottom: 10px;
+
+  .avatar {
+    width: 150px;
+    height: 150px;
+    border-radius: 4px;
+    cursor: pointer;
+    object-fit: cover;
+    border: 1px dashed #d9d9d9;
+
+    &:hover {
+      border-color: #409eff;
+    }
+  }
+
+  .avatar-uploader-placeholder {
+    width: 150px;
+    height: 150px;
+    border: 1px dashed #d9d9d9;
+    border-radius: 4px;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
+    cursor: pointer;
+    background-color: #fafafa;
+
+    &:hover {
+      border-color: #409eff;
+      background-color: #f0f9ff;
+    }
+
+    .avatar-uploader-icon {
+      font-size: 28px;
+      color: #8c939d;
+      line-height: 1;
+      margin-bottom: 8px;
+    }
+
+    .upload-tip {
+      font-size: 12px;
+      color: #8c939d;
+    }
+  }
+}
+
+// 摄像头容器
+.camera-container {
+  text-align: center;
+
+  .camera-video {
+    width: 100%;
+    max-width: 600px;
+    height: auto;
+    border-radius: 4px;
+    background: #000;
+  }
+
+  .captured-image {
+    width: 100%;
+    max-width: 600px;
+    height: auto;
+    border-radius: 4px;
+  }
+}
 </style>

--
Gitblit v1.8.0