Ver código fonte

药师线上签名提交

yjwang 1 semana atrás
pai
commit
63c51046f9

+ 7 - 1
src/api/doctor.js

@@ -88,4 +88,10 @@ export function editDoctorInfo(data) {
   })
 }
 
-
+// 生成药师签名上传二维码
+export function getSignQrCode() {
+  return request({
+    url: '/app/doctor/getSignQrCode',
+    method: 'get'
+  })
+}

+ 92 - 6
src/views/doctor/profile/doctorInfo.vue

@@ -81,26 +81,62 @@
         <el-input v-model="form.practiseCode" maxlength="50"/>
       </el-form-item>
       <el-form-item label="药师签名">
-        <div class="demo-image__preview">
+        <!-- 签名已上传:显示签名图片 -->
+        <div v-if="form.signUrl" class="demo-image__preview">
           <el-image
             style="width: 100px; height: 100px"
             :src="form.signUrl"
             :preview-src-list="srcList">
           </el-image>
         </div>
+        <!-- 签名未上传:显示扫码上传按钮 -->
+        <div v-else>
+          <el-button type="primary" icon="el-icon-mobile-phone" @click="handleScanUploadSign">扫码上传签名</el-button>
+          <div style="color: #909399; font-size: 12px; margin-top: 4px;">
+            点击按钮生成二维码,使用手机扫码后在手机端完成签名上传。
+          </div>
+        </div>
       </el-form-item>
       <el-form-item style="display: flex; justify-content: flex-end;">
         <el-button type="primary"  @click="submit">保 存</el-button>
         <el-button type="danger"  @click="close">关 闭</el-button>
       </el-form-item>
     </el-form>
+
+    <!-- 扫码上传签名二维码弹窗 -->
+    <el-dialog
+      title="扫码上传签名"
+      :visible.sync="qrCodeDialog.visible"
+      width="360px"
+      :close-on-click-modal="false"
+      append-to-body
+      center>
+      <div style="text-align: center;">
+        <div v-loading="qrCodeDialog.loading" style="min-height: 260px; display: flex; align-items: center; justify-content: center;">
+          <img
+            v-if="qrCodeDialog.qrCodeUrl"
+            :src="qrCodeDialog.qrCodeUrl"
+            alt="签名上传二维码"
+            style="width: 260px; height: 260px;"
+          />
+        </div>
+        <div style="color: #606266; font-size: 13px; margin-top: 12px; line-height: 1.6;">
+          请使用手机扫码,跳转至签名上传页面完成签名上传。<br/>
+          上传完成后,点击下方"已上传,刷新"按钮同步签名。
+        </div>
+      </div>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="qrCodeDialog.visible = false">关 闭</el-button>
+        <el-button type="primary" :loading="qrCodeDialog.refreshing" @click="refreshSignUrl">已上传,刷新</el-button>
+      </div>
+    </el-dialog>
   </div>
 </template>
 
 <script>
 import doctorDetails from '../../components/doctor/doctorDetails.vue';
 import {getCitys} from "@/api/store";
-import {editDoctorInfo} from '@/api/doctor'
+import {editDoctorInfo, getSignQrCode, getInfo} from '@/api/doctor'
 export default {
   components: { doctorDetails},
   data() {
@@ -157,6 +193,15 @@ export default {
       },
       uploadUrl:process.env.VUE_APP_BASE_API+"/app/common/uploadOSS",
       baseUrl: process.env.VUE_APP_BASE_API,
+      // 扫码上传签名二维码弹窗
+      qrCodeDialog: {
+        visible: false,
+        loading: false,
+        qrCodeUrl: '',
+        content: '',
+        expireSeconds: 300,
+        refreshing: false
+      },
       // 表单校验规则(核心新增部分)
       rules: {
         avatar: [
@@ -305,10 +350,7 @@ export default {
         ? doctor.practiseImages.split(',').map(s => ({name: s.trim(), url: s.trim()})).filter(item => item.url)
         : [];
       this.form.signUrl= doctor.signUrl;
-
-      this.srcList.push( this.form.signUrl)
-        ? doctor.practiseImages.split(',').map(s => ({url: s.trim()})).filter(item => item.url)
-        : [];
+      this.srcList = this.form.signUrl ? [this.form.signUrl] : [];
     },
     handleRemove(file, fileList) {
       this.avatarList = [];
@@ -329,6 +371,50 @@ export default {
         .filter(url => url)
         .join(',');
     },
+    // 点击"扫码上传签名"按钮,调用后端接口生成二维码
+    handleScanUploadSign() {
+      this.qrCodeDialog.loading = true;
+      this.qrCodeDialog.qrCodeUrl = '';
+      this.qrCodeDialog.visible = true;
+      getSignQrCode().then(res => {
+        this.qrCodeDialog.loading = false;
+        // 后端 R.ok(map) 会把 map 的 key 提到顶层,所以 qrCodeUrl/content/expireSeconds 是顶层字段
+        if (res.code === 200 && res.qrCodeUrl) {
+          this.qrCodeDialog.qrCodeUrl = res.qrCodeUrl;
+          this.qrCodeDialog.content = res.content || '';
+          this.qrCodeDialog.expireSeconds = res.expireSeconds || 300;
+        } else {
+          this.$message.error(res.msg || '生成二维码失败');
+          this.qrCodeDialog.visible = false;
+        }
+      }).catch(() => {
+        this.qrCodeDialog.loading = false;
+        this.qrCodeDialog.visible = false;
+      });
+    },
+    // 手机端上传完成后,PC端点击"已上传,刷新"按钮同步签名
+    refreshSignUrl() {
+      this.qrCodeDialog.refreshing = true;
+      getInfo().then(res => {
+        this.qrCodeDialog.refreshing = false;
+        // 后端 R.ok(map) 会把 map 的 key 提到顶层,doctor 是顶层字段
+        if (res.code === 200 && res.doctor) {
+          const doctor = res.doctor;
+          if (doctor.signUrl) {
+            this.form.signUrl = doctor.signUrl;
+            this.srcList = [doctor.signUrl];
+            this.$message.success('签名同步成功');
+            this.qrCodeDialog.visible = false;
+          } else {
+            this.$message.warning('尚未检测到签名,请确认手机端已上传');
+          }
+        } else {
+          this.$message.error(res.msg || '刷新失败');
+        }
+      }).catch(() => {
+        this.qrCodeDialog.refreshing = false;
+      });
+    },
   },
 };
 </script>

+ 334 - 0
src/views/doctor/profile/doctorInfo.vue.bak

@@ -0,0 +1,334 @@
+<template>
+  <div>
+    <el-form ref="form" :model="form" :rules="rules" label-width="120px">
+      <!-- 原有表单结构不变 -->
+      <el-form-item label="药师照片" prop="avatar">
+        <el-upload
+          :action="uploadUrl"
+          list-type="picture-card"
+          :file-list="avatarList"
+          :on-success="(response, file) => handleFileSuccess(response, file, 'avatar')"
+          :before-upload="beforeAvatarUpload"
+          :limit="1"
+          :on-remove="handleRemove"
+        >
+          <i class="el-icon-plus"></i>
+        </el-upload>
+      </el-form-item>
+      <el-form-item :label="name" prop="doctorName">
+        <el-input v-model="form.doctorName" maxlength="30"/>
+      </el-form-item>
+      <el-form-item label="联系电话" prop="mobile">
+        <el-input v-model="form.mobile" maxlength="11"/>
+      </el-form-item>
+      <el-form-item label="身份证号" prop="idCard">
+        <el-input v-model="form.idCard" maxlength="18"/>
+      </el-form-item>
+      <el-form-item label="所属城市" prop="cityIds">
+        <el-cascader
+          ref="citySelect"
+          v-model="form.cityIds"
+          :options="citys"
+          @change="handleCityChange">
+        </el-cascader>
+      </el-form-item>
+      <el-form-item label="所属店铺" prop="hospitalName">
+        <el-input v-model="form.hospitalName" maxlength="50" disabled/>
+      </el-form-item>
+      <el-form-item label="职称" prop="position">
+        <el-input v-model="form.position" maxlength="50" disabled/>
+      </el-form-item>
+      <el-form-item label="身份证正面" prop="idCardFrontUrl">
+        <el-upload
+          :action="uploadUrl"
+          list-type="picture-card"
+          :file-list="idCardFrontUrlList"
+          :on-success="(response, file) => handleFileSuccess(response, file, 'idCardFrontUrl')"
+          :before-upload="beforeAvatarUpload"
+          :limit="1"
+          :on-remove="handleIdRemove"
+        >
+          <i class="el-icon-plus"></i>
+        </el-upload>
+      </el-form-item>
+      <el-form-item label="身份证反面" prop="idCardBackUrl">
+        <el-upload
+          :action="uploadUrl"
+          list-type="picture-card"
+          :file-list="idCardBackUrlList"
+          :on-success="(response, file) => handleFileSuccess(response, file, 'idCardBackUrl')"
+          :before-upload="beforeAvatarUpload"
+          :limit="1"
+          :on-remove="handleIdBackRemove"
+        >
+          <i class="el-icon-plus"></i>
+        </el-upload>
+      </el-form-item>
+      <el-form-item label="注册证书" prop="practiseImages">
+        <el-upload
+          :action="uploadUrl"
+          list-type="picture-card"
+          :file-list="practiseImagesList"
+          :on-success="(response, file) => handleFileImgsSuccess(response, file, 'practiseImages')"
+          :before-upload="beforeAvatarUpload"
+          :limit="3"
+          :on-remove="handlPractiseImagesRemove"
+        >
+          <i class="el-icon-plus"></i>
+        </el-upload>
+      </el-form-item>
+      <el-form-item label="注册证书编号" prop="practiseCode">
+        <el-input v-model="form.practiseCode" maxlength="50"/>
+      </el-form-item>
+      <el-form-item label="药师签名">
+        <div class="demo-image__preview">
+          <el-image
+            style="width: 100px; height: 100px"
+            :src="form.signUrl"
+            :preview-src-list="srcList">
+          </el-image>
+        </div>
+      </el-form-item>
+      <el-form-item style="display: flex; justify-content: flex-end;">
+        <el-button type="primary"  @click="submit">保 存</el-button>
+        <el-button type="danger"  @click="close">关 闭</el-button>
+      </el-form-item>
+    </el-form>
+  </div>
+</template>
+
+<script>
+import doctorDetails from '../../components/doctor/doctorDetails.vue';
+import {getCitys} from "@/api/store";
+import {editDoctorInfo} from '@/api/doctor'
+export default {
+  components: { doctorDetails},
+  data() {
+    // 手机号验证规则
+    const validateMobile = (rule, value, callback) => {
+      if (!value) {
+        return callback(new Error('请输入联系电话'));
+      }
+      const reg = /^1[3-9]\d{9}$/;
+      if (!reg.test(value)) {
+        return callback(new Error('请输入正确的手机号'));
+      }
+      callback();
+    };
+
+    // 身份证号验证规则
+    const validateIdCard = (rule, value, callback) => {
+      if (!value) {
+        return callback(new Error('请输入身份证号'));
+      }
+      const reg = /(^\d{18}$)|(^\d{17}(\d|X|x)$)/;
+      if (!reg.test(value)) {
+        return callback(new Error('请输入正确的身份证号'));
+      }
+      callback();
+    };
+
+    return {
+      idCardFrontUrlList:[],
+      idCardBackUrlList:[],
+      practiseImagesList:[],
+      avatarList:[],
+      srcList:[],
+      form:{
+        doctorId:null,
+        avatar:null,
+        cityIds:[],
+        doctorName:null,
+        storeName:null,
+        mobile:null,
+        idCard:null,
+        position:null,
+        practiseImages:null,
+        idCardFrontUrl:null,
+        idCardBackUrl:null,
+        practiseCode:null,
+        signUrl:null,
+      },
+      citys:[],
+      name:'医生姓名',
+      cardVisible: false,
+      doctorItem:{
+        open:false
+      },
+      uploadUrl:process.env.VUE_APP_BASE_API+"/app/common/uploadOSS",
+      baseUrl: process.env.VUE_APP_BASE_API,
+      // 表单校验规则(核心新增部分)
+      rules: {
+        avatar: [
+          { required: true, message: '请上传药师照片', trigger: 'change' }
+        ],
+        doctorName: [
+          { required: true, message: '请输入姓名', trigger: 'blur' },
+          { min: 1, max: 30, message: '姓名长度在1-30个字符之间', trigger: 'blur' }
+        ],
+        mobile: [
+          { required: true, validator: validateMobile, trigger: 'blur' }
+        ],
+        idCard: [
+          { required: true, validator: validateIdCard, trigger: 'blur' }
+        ],
+        cityIds: [
+          { required: true, message: '请选择所属城市', trigger: 'change' }
+        ],
+        hospitalName: [
+
+        ],
+        position: [
+          { required: true, message: '职称不能为空', trigger: 'blur' }
+        ],
+        idCardFrontUrl: [
+          { required: true, message: '请上传身份证正面', trigger: 'change' }
+        ],
+        idCardBackUrl: [
+          { required: true, message: '请上传身份证反面', trigger: 'change' }
+        ],
+        practiseImages: [
+          { required: true, message: '请上传注册证书(最多3张)', trigger: 'change' }
+        ],
+        practiseCode: [
+          { required: true, message: '请输入注册证书编号', trigger: 'blur' },
+          { min: 1, max: 50, message: '编号长度在1-50个字符之间', trigger: 'blur' }
+        ]
+      }
+    };
+  },
+  mounted(){
+    this.getType();
+    this.getCitys();
+    this.loadData();
+  },
+  methods: {
+    // 原有方法不变,仅修正save按钮绑定的事件为submit
+    handleCard() {
+      this.cardVisible = true;
+    },
+    handleDoctorDetails(){
+      this.doctorItem.open = true;
+    },
+    handleAvatarSuccess(res, file) {
+      if(res.code==200){
+        this.doctor.logoUrl=res.url;
+        this.$forceUpdate()
+      } else{
+        this.msgError(res.msg);
+      }
+    },
+    beforeAvatarUpload(file) {
+      const isLt1M = file.size / 1024 / 1024 < 1;
+      if (!isLt1M) {
+        this.$message.error('上传图片大小不能超过 1MB!');
+      }
+      return isLt1M;
+    },
+    submit() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          //处理拼接
+          this.form.cityIds = this.form.cityIds.join(",");
+          console.log(this.form);
+          editDoctorInfo(this.form).then(response => {
+            this.msgSuccess("修改成功");
+            this.close();
+          });
+        }
+      });
+    },
+    close() {
+      this.$store.dispatch("tagsView/delView", this.$route);
+      this.$router.push({ path: "/index" });
+    },
+    getType(){
+      const type = localStorage.getItem('loginType');
+      if(type == 2){
+        this.name = '药剂师姓名';
+      }
+    },
+    getCitys() {
+      getCitys().then(res => {
+        this.citys = res.data;
+      })
+    },
+    handleCityChange(value) {
+      const nodes = this.$refs.citySelect.getCheckedNodes();
+      if (nodes.length) {
+        this.form.address = nodes[0].pathLabels.join('');
+        this.form.cityIds = value;
+      }
+    },
+    handleFileSuccess(response, file, field) {
+      if (response.code === 200) {
+        this.$set(this.form, field, response.url);
+        this.$forceUpdate();
+      } else {
+        this.msgError(response.msg);
+      }
+    },
+    handleFileImgsSuccess(response, file, field) {
+      if (response.code === 200) {
+        const newUrl = (response.url || '').trim();
+        if (!newUrl) {
+          this.$message.warning('上传成功,但未获取到有效URL');
+          return;
+        }
+        const currentValue = (this.form[field] || '').trim();
+        const newValue = currentValue ? `${currentValue},${newUrl}` : newUrl;
+        this.$set(this.form, field, newValue);
+        console.log(`字段[${field}]的拼接结果:`, this.form[field]);
+      } else {
+        this.$message.error(response.msg || '上传失败,请重试');
+      }
+    },
+    loadData(data){
+      if (!data || !data.doctor) return;
+      const doctor = data.doctor;
+      this.form.doctorId = doctor.doctorId;
+      this.form.avatar = doctor.avatar;
+      this.avatarList = doctor.avatar ? [{url: doctor.avatar}] : [];
+      this.form.doctorName = doctor.doctorName;
+      this.form.mobile = doctor.mobile;
+      this.form.idCard = doctor.idCard;
+      this.form.cityIds = doctor.cityIds ? doctor.cityIds.split(",").map(Number) : [];
+      this.form.hospitalName = data.storeName || '';
+      this.form.position = doctor.position || '';
+      this.form.idCardFrontUrl = doctor.idCardFrontUrl;
+      this.idCardFrontUrlList = doctor.idCardFrontUrl ? [{url: doctor.idCardFrontUrl}] : [];
+      this.form.idCardBackUrl = doctor.idCardBackUrl;
+      this.idCardBackUrlList = doctor.idCardBackUrl ? [{url: doctor.idCardBackUrl}] : [];
+      this.form.practiseCode = doctor.practiseCode || '';
+      this.form.practiseImages = doctor.practiseImages || '';
+      this.practiseImagesList = doctor.practiseImages
+        ? doctor.practiseImages.split(',').map(s => ({name: s.trim(), url: s.trim()})).filter(item => item.url)
+        : [];
+      this.form.signUrl= doctor.signUrl;
+
+      this.srcList.push( this.form.signUrl)
+        ? doctor.practiseImages.split(',').map(s => ({url: s.trim()})).filter(item => item.url)
+        : [];
+    },
+    handleRemove(file, fileList) {
+      this.avatarList = [];
+      this.form.avatar = null;
+    },
+    handleIdRemove(file, fileList) {
+      this.idCardFrontUrlList = [];
+      this.form.idCardFrontUrl = null;
+    },
+    handleIdBackRemove(file, fileList) {
+      this.idCardBackUrlList = [];
+      this.form.idCardBackUrl = null;
+    },
+    handlPractiseImagesRemove(file, fileList) {
+      this.practiseImagesList = fileList;
+      this.form.practiseImages = fileList
+        .map(item => item.url?.trim())
+        .filter(url => url)
+        .join(',');
+    },
+  },
+};
+</script>