| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908 | <template>  <div class="page_box">    <van-form ref="resumption_form">      <div class="panel">        <NavBar :go="{ type: 'push', path: '/resumption' }" />        <van-panel :title="resumptionData.taskName">          <div class="tts">            <van-row>              <van-col span="12">                <div class="total_panel">                  <div class="title">履职项</div>                  <div class="content">                    <span class="con_num ok">已查 {{ resumptionData.yesPointNums }}</span>                    <span class="con_num no">未查 {{ resumptionData.noPointNums }}</span>                  </div>                </div>              </van-col>              <van-col span="12">                <div class="total_panel" @click="showDetail(1)">                  <div class="title">NFC</div>                  <div class="content">                    <span class="con_num ok">已扫 {{ yesList.length }}</span>                    <span class="con_num no">未扫 {{ noList.length}}</span>                  </div>                </div>              </van-col>            </van-row>          </div>        </van-panel>      </div>      <div class="progress">        <van-progress          :percentage="            (              ((resumptionData.yesPointNums + yesList.length) /                (resumptionData.yesPointNums +                  resumptionData.noPointNums +                  yesList.length +                  noList.length)) *              100            ).toFixed(2)          "        />      </div>      <!--   检查区域   -->      <div class="card">        <van-panel title="巡检区域">          <div style="padding: 5px">            <van-row>              <van-col span="8" v-for="(va, i) in areas" :key="va.areaId">                <div :class="{ active: i === 0 }" class="check-area" @click="clickArea(va, i)">                  <div class="ysj">                    <van-icon name="checked" :color="areaColor.complete" v-if="va.areaStatus === '1'" />                    <van-icon name="warning" :color="areaColor.loading" v-if="va.areaStatus === '2'" />                    <van-icon name="warning" :color="areaColor.noOpen" v-if="va.areaStatus === '0'" />                  </div>                  {{ va.areaName }}                </div>              </van-col>            </van-row>          </div>        </van-panel>      </div>      <van-action-sheet v-model="total_show">        <div class="content">          <van-tabs>            <van-tab title="未扫描" name="b">              <van-list finished-text="没有更多了" @load="onLoad">                <van-cell v-for="item in sheetNoList" :key="item.nfcCode" :label="areasMap[item.areaId] " :title="item.nfcName">                  <!-- <img :src="require('../../../assets/svg/NFC.svg')" class="nfc-icon"/>-->                  <span style="color: red">{{item.pointScan === 1?'必扫':'非必扫'}}</span>                </van-cell>              </van-list>            </van-tab>            <van-tab title="已扫描" name="a">              <van-list finished-text="没有更多了" @load="onLoad">                <van-cell v-for="item in sheetYesList" :key="item.nfcCode"  :label="areasMap[item.areaId]" :title="item.nfcName">                  <span style="color: green">{{item.pointScan === 1?'必扫':'非必扫'}}</span>                </van-cell>              </van-list>            </van-tab>          </van-tabs>        </div>      </van-action-sheet>      <div class="card">        <van-row>          <van-col span="16">            <van-cell @click="showDetail(0)">              <template #title>                <span>NFC</span>              </template>              <template #extra>                <div>                  <span style="display: inline-block; color: #26850c">{{ areaYesList.length }}</span>                  <span style="display: inline-block; color: #0e0e0e">/</span>                  <span style="display: inline-block; color: #0e0e0e">{{ areaNoList.length }}</span>                </div>                <img :src="require('../../../assets/svg/NFC.svg')" class="nfc-icon"  />              </template>            </van-cell>          </van-col>          <van-col span="8">            <van-cell>              <div class="okAll">                <van-radio-group v-if="enable" v-model="selectRadio" @change="changeSwitch">                  <van-radio name="1" icon-size="20px">一键填充</van-radio>                </van-radio-group>              </div>            </van-cell>          </van-col>        </van-row>        <van-row>          <van-col span="24">            <van-cell v-show="currentImgNFC.length > 0" :border="false">              <div                v-if="v.areaId === areaId && v.img"                class="nfc-img"                v-for="(v, i) in nfcs"                :key="v.img"                @click="preViewNFC(i)"              >                <img :src="imgUrl(v.img)" alt="" />                <span>{{ v.nfcName }}</span>                <div v-if="enable" class="cancel_icon" @click="cancelImg(v)">                  <van-icon name="clear" />                </div>              </div>            </van-cell>          </van-col>        </van-row>        <van-collapse v-model="activeNames">          <van-collapse-item            v-show="areaId === item.areaId"            v-for="item in checks"            :title="item.itemName + '(' + item.points.length + ')'"            :name="item.itemName"            :key="item.areaId"          >            <div v-for="(point, index) in item.points">              <van-cell>                <template #title>                  <pre>{{ point.pointName }}</pre>                </template>                <template #right-icon>                  <van-switch                    style="margin-left: 10px"                    v-model="point.resValue"                    v-show="point.dataStatus === 2"                    v-if="enable"                    :active-value="1"                    :inactive-value="0"                    inactive-color="#4fc08d"                    active-color="#ee0a24"                    size="18"                  />                  <span v-else>                    <van-tag v-if="point.resValue === 1" type="warning">异常</van-tag>                    <van-tag v-else type="success">正常</van-tag>                  </span>                  <van-switch                    style="margin-left: 10px"                    v-model="point.dataStatus"                    v-show="point.dataStatus === 1"                    :active-value="2"                    :inactive-value="1"                    inactive-color="#fcfcfc"                    active-color="#ee0a24"                    size="18"                    @change="changeCurrentSwitch(item.areaId)"                  />                </template>              </van-cell>              <van-cell-group v-show="point.resValue">                <select-cell                  :required="enable"                  :disabled="!enable"                  title="整改期限"                  name="rectificationDeadline"                  v-model="point.rectificationDeadline"                  :data-list="dayList"                />                <van-field                  :rules="[{ required: enable, message: '请输入情况描述' },{validator, message: '输入长度200字符以内'}]"                  :required="enable"                  v-model="point.resRemark"                  :readonly="!enable"                  rows="1"                  name="resRemark"                  autosize                  label="情况描述"                  type="textarea"                  :placeholder="enable ? '请输入情况描述' : ''"                />                <div class="upload-box">                  <uploader :maxCount="5" v-if="enable" v-model="point.imgs" />                  <van-cell v-else>                    <div                      class="nfc-img van-hairline--surround"                      v-for="(v, i) in point.imgs"                      :key="v.imgPath"                      @click="clickWarnImage(point.imgs, i)"                    >                      <img :src="imgUrl(v.imgPath)" alt="" />                      <span>{{ v.checkName }}</span>                    </div>                  </van-cell>                </div>              </van-cell-group>            </div>          </van-collapse-item>        </van-collapse>      </div>      <div class="bottomClass" v-if="enable">        <van-row>          <van-col span="8">            <van-button type="primary" @click="clickNFC">扫描NFC</van-button>          </van-col>          <van-col span="8">            <van-button type="default" @click="resumptionDataSave">保存</van-button>          </van-col>          <van-col span="8">            <van-button type="info" @click="submitResumptionData">提交</van-button>          </van-col>        </van-row>      </div>      <!--  nfc弹窗  -->      <nfc-popup v-if="enable" ref="NfcPopup" @checkNFC="checkNFC" @change="changeNfcImg"></nfc-popup>    </van-form>  </div></template><script>import { ImagePreview, Dialog } from 'vant'import NavBar from '@/components/NavBar/index.vue'import Uploader from '@/components/upload/gxuploader.vue'import SelectCell from '@/components/selectCell/index.vue'import { getDict } from '@/api/toConsult'import { saveTask, taskDetail } from '@/views/menu/resumption/api'import NfcPopup from '@/components/nfcPopup/gxmore'import { imgUrl } from '@/utils'import dayjs from 'dayjs'import { mapGetters } from 'vuex'import { ref } from 'vue'import {uploadBase64} from "@/api/public";export default {  components: {    SelectCell,    Uploader,    NavBar,    NfcPopup,    imgUrl,    [Dialog.Component.name]: Dialog.Component  },  data() {    return {      areaColor: {        // 已完成        complete: '#26850c',        // 未开始        noOpen: '#1989fa',        // 进行中        loading: '#ffa500'      },      NFCnums: 0,      yesNFCnums: 0,      currentImgNFC: [],      areaId: null,      resumptionData: {},      areas: [],      areasMap: {},      checks: [],      nfcs: [],      selectRadio: 0,      total_show: false,      enable: false,      activeNames: [],      dayList: [15, 30, 90, 180],      yesList: [],      noList: [],      areaYesList:[],      areaNoList:[],      sheetYesList: [],      sheetNoList: [],      loading: false,      finished: false,      selectArea: null,      preViewImages: {        images: [],        startPosition: 0      },      timer:null,    }  },  computed: {    ...mapGetters(['id'])  },  mounted() {    this.getResumptionData()    window.openNFCScanCallBack = this.openNFCScanCallBack;  },  created() {    getDict('rectification_deadline').then(res => {      let { data } = res      this.dayList = data    })  },  methods: {    //长度校验    validator(val) {      let len = val.length;      if( len > 200) {        this.$toast.fail('问题情况输入长度不能超过200');        return true      }else {        return false      }    },    checkNFC(){      this.useNFC();      this.$toast.loading({        duration: 0, // 持续展示 toast        position: 'top',        forbidClick: true,        message: '请靠近NFC标签,进行扫描!',      });      let second = 30;      this.timer = setInterval(() => {        second--;        if(!second){          this.$toast.clear();          clearInterval(this.timer);          this.$toast.fail({            message: '未扫描到任何信息!',          });        }      }, 1000);    },    openNFCScanCallBack(nfcStr){      clearInterval(this.timer);      let nfc = JSON.parse(nfcStr);      let nfcCode =  nfc.content;      this.checkNfcFilter(nfcCode);    },    checkNfcFilter(nfcCode){      let areaId = null;      let checkOk =  false;      this.nfcs.forEach(v => {        if(v.nfcCode === nfcCode){          areaId = v.areaId;          this.switchArea(areaId);          if(v.status === 1){            this.$toast.fail('NFC点位:' + v.nfcName + '已扫描,请勿重复扫描!');            throw new Error('NFC点位:' + v.nfcName + '已扫描,请勿重复扫描!');          }          v.status = 1          v.scanMethod = 2          v.submitTime = dayjs().format('YYYY-MM-DD HH:mm:ss')          v.submitBy = this.id          this.$toast.success('NFC点位:' + v.nfcName + '扫描成功!');          checkOk = true;        }      });      this.updateNFC(1,nfcCode);      if(!checkOk){        this.$toast.fail(nfcCode + ",不在本次履职范围内!");      }      this.validateArea(areaId)    },    switchArea(areaId){      this.areas.forEach((area, i) => {        if (areaId === area.areaId) {          this.activeArea(area, i)        }      })    },    useNFC(){      let system = this.isAndroidOrIos();      const parms = {        "iOS_SessionType":"0"      };      if(system === 1){        //android        // 判断当前环境是是否存在 js桥 'sap'        const hasSap = window.hasOwnProperty('sap');        if (hasSap) {          // 判断是否存在方法 ?preview          const fun = sap.hasOwnProperty('openNFCScan');          if (fun) {            sap.openNFCScan(JSON.stringify(parms));          }        }      }      if(system === 2){        //ios        // 判断 ios是否存在方法 preview        const fun = window.webkit.messageHandlers.hasOwnProperty('openNFCScan')        if (fun) {          window.webkit.messageHandlers.openNFCScan.postMessage(JSON.stringify(parms))        }      }    },    isAndroidOrIos(){      const urls = navigator.userAgent;      let isAndroid = urls.indexOf('Android') > -1 || urls.indexOf('Linux') > -1;      let isIos = !!urls.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);      if(isAndroid){        return 1;      }      if(isIos){        return 2;      }    },    preViewNFC(i) {      this.preViewImages.images = this.currentImgNFC.map(v => imgUrl(v.img))      this.preViewImages.startPosition = i      ImagePreview(this.preViewImages)    },    clickWarnImage(arr, i) {      this.preViewImages.images = arr.map(v => imgUrl(v.imgPath))      this.preViewImages.startPosition = i      ImagePreview(this.preViewImages)    },    getResumptionData() {      let data = {        taskId: this.$route.query.id,        taskDate: this.$route.query.taskDate      }      taskDetail(data).then(res => {        let { taskId, taskName, yesPointNums, noPointNums, yesNFCNums, noNFCNums, status } = res.data        this.resumptionData = {          taskId,          taskName,          yesPointNums,          noPointNums,          yesNFCNums,          noNFCNums,          status        }        this.enable = this.resumptionData.status === 1 || this.resumptionData.status === 2        this.checks = res.data.checks        this.nfcs = res.data.nfcs        this.areas = res.data.areas        let obj = {};        res.data.areas.forEach(function(v,i){          obj[v.areaId] = v.areaName;        });        this.areasMap = obj;        this.activeArea(this.areas[0], 0)        if (!this.enable) {          this.openCollapseItems()        }        this.updateNFC(0);      })    },    openCollapseItems() {      let len = this.checks.length      for (let i = 0; i < len; i++) {        let title = this.checks[i].itemName        this.activeNames.push(title)      }    },    changeSwitch() {      let num = 0      for (let i = 0; i < this.checks.length; i++) {        for (let j = 0; j < this.checks[i].points.length; j++) {          this.checks[i].points[j].dataStatus = 2          num++        }      }      this.resumptionData.yesPointNums = num      this.resumptionData.noPointNums = 0      this.areas.forEach((item, index) => {        this.validateArea(item.areaId)      })      this.openCollapseItems()    },    changeCurrentSwitch(areaId) {      this.resumptionData.yesPointNums = this.resumptionData.yesPointNums + 1      this.resumptionData.noPointNums = this.resumptionData.noPointNums - 1      this.validateArea(areaId)    },    validateArea(areaId) {      let total = 0      let yes = 0      this.checks.forEach(item => {        let pointList = item.points        pointList.forEach(point => {          if (item.areaId === areaId) {            total++            if (point.dataStatus === 2) {              yes++            }          }        })      })      let areaStatus = '0'      if (total === yes && this.areaNoList.length === 0) {        areaStatus = '1'      } else if (yes === 0 && this.areaNoList.length === this.NFCnums) {        areaStatus = '0'      } else {        areaStatus = '2'      }      this.areas.forEach((item, index) => {        if (item.areaId === areaId) {          this.areas[index].areaStatus = areaStatus        }      })    },    clickNFC() {      let arr = this.nfcs.filter(item => {        return item.areaId === this.areaId && item.status === 0      })      if (arr.length > 0) {        let nfcs = []        for (const nfc of arr) {          let pro = {}          pro.checkName = nfc.nfcName          pro.nfccdoe = nfc.nfcCode          nfcs.push(pro)        }        this.$refs.NfcPopup.show(nfcs)      }    },    cancelImg(imgItem) {      this.updateNFC(2,imgItem.nfcCode,imgItem);      this.validateArea(this.areaId)    },    changeNfcImg(imgItem) {      this.updateNFC(1,imgItem.nfcCode,imgItem);      this.validateArea(this.areaId);    },    updateNFC(type,nfcCode,imgItem){      //type 1 表示新增加扫描的数据,2 表示新增未扫描的数量,0 未做任何操作      //nfcCode 代表扫描和减少的nfc数据。      //更新全局nfc已扫描 和未扫描      this.yesList = [];      this.noList = [];      this.areaYesList = [];      this.areaNoList = [];      this.nfcs.forEach(item => {        if(type === 1){          //新增扫描到的标签          if(nfcCode === item.nfcCode){            item.status = 1            item.scanMethod = 1            item.submitTime = dayjs().format('YYYY-MM-DD HH:mm:ss')            item.submitBy = this.id            if(imgItem){              //拍照NFC特殊处理              item.img = imgItem.url              this.currentImgNFC.push(item)            }          }        }        if(type === 2){          //新增未扫描的标签          if(nfcCode === item.nfcCode){            item.status = 0            item.scanMethod = null            item.submitTime = null            item.submitBy = null            if(item.img){              //拍照NFC特殊处理              item.img = null;              this.currentImgNFC = this.currentImgNFC.filter(v => {                return v.nfcCode !== item.nfcCode              })            }          }        }        //扫描到nfc        if(item.status === 1){          this.yesList.push(item);          if(this.areaId === item.areaId){            this.areaYesList.push(item);          }        }else{          this.noList.push(item);          if(this.areaId === item.areaId){            this.areaNoList.push(item);          }        }      });    },    showDetail(data) {      if(data === 1){        this.sheetYesList = this.yesList;        this.sheetNoList = this.noList;      }else{        this.sheetNoList = this.areaNoList;        this.sheetYesList = this.areaYesList;      }      this.total_show = true    },    onLoad() {    },    //点击区域    clickArea(area, index) {      this.activeArea(area, index)    },    //选中区域时数据变更    activeArea(area, index) {      //获取当前选中区域      this.areaId = area.areaId      this.updateNFC(0);      //设置选中样式      this.$nextTick(() => {        let doms = document.getElementsByClassName('check-area')        Array.prototype.forEach.call(doms, item => {          item.classList.remove('active')        })        doms[index].classList.add('active')      })      let nfcs = 0      let current = []      let yesNum = 0;      for (let i = 0; i < this.nfcs.length; i++) {        let nfc = this.nfcs[i]        let areaId = nfc.areaId        if (areaId === area.areaId) {          nfcs++          if (nfc.status === 1) {            yesNum++;          }          if (nfc.img) {            current.push(nfc)          }        }      }      this.currentImgNFC = current      this.yesNFCnums = yesNum;      this.NFCnums = nfcs      this.selectArea = area    },    //保存数据    resumptionDataSave() {      //组装数据      let data = {}      data.taskId = this.resumptionData.taskId      data.checks = this.checks      data.nfcs = this.nfcs      data.subType = 1      saveTask(data).then(res => {        this.$toast('保存成功')        this.$router.go(-1)      })    },    submitResumptionData() {      //备份数据      let bakNfcs = this.nfcs;      let bakChecks = this.checks;      try{        //验证数据        let subNFCS = [];        let noNfc = this.nfcs.filter((item, index) => {          if(item.status === 1){            subNFCS.push(item);          }          if(item.status === 0 && item.pointScan === 1){            return true;          }        })        if (noNfc.length > 0) {          Dialog.alert({            message: 'NFC标签还未扫描完成,请完成后提交!'          })          throw new Error('NFC标签还未扫描完成,请完成后提交!')          return        }        for (let i = 0; i < this.checks.length; i++) {          for (let j = 0; j < this.checks[i].points.length; j++) {            let point =  this.checks[i].points[j];            if (point.dataStatus === 1) {              if( point.required === 0){                //如果不是必填内容                this.checks[i].points[j].dataStatus = 2                this.checks[i].points[j].resValue = 0              }else{                Dialog.alert({                  message: '存在未编辑完成履职项,无法提交!'                })                throw new Error('还有未完成的内容,请先完成再提交')              }            } else {              if (point.resValue === 1) {                if (!point.rectificationDeadline || !point.resRemark) {                  this.changeSwitch()                  this.areas.forEach((area, i) => {                    if (area.areaId === item.areaId) {                      this.activeArea(area, i)                      //切换后验证表单                      this.$refs.resumption_form.validate()                      this.$toast.fail({                        message: '请完成异常情况的信息填写!',                        position: 'top'                      })                    }                  })                  throw new Error('存在未编辑完成履职项,无法提交')                }                if(point.imgs.length < 1){                  this.$toast.fail({                    message: '请拍照上传异常图片!',                    position: 'top'                  })                  throw new Error('请上传异常图片!');                }              }            }          }        }        let data = {}        data.taskId = this.resumptionData.taskId        data.checks = this.checks        data.nfcs = subNFCS        data.subType = 2        saveTask(data).then(res => {          this.$toast('提交成功')          this.$router.go(-1)        })      }catch(e){        this.nfcs = bakNfcs;        this.checks = bakChecks;      }    }  }}</script><style lang="scss" scoped>.van-progress {  z-index: 999;  width: 98%;  left: 6px;  right: 6px;}.page_box {  height: calc(100vh - 60px);  overflow: scroll;}.content {  padding: 16px 16px 10px;}.panel {  background-color: white;  border-radius: 10px;  border: 1px solid #ffffff;  display: flex;  flex-direction: column;  box-shadow: 0 8px 12px #ebedf0;  margin-bottom: 10px;  .van-cell__title {    font-weight: bold;  }}.total_panel {  text-align: center;  .title {    padding-top: 10px;  }  .content {    padding-top: 10px;    padding-bottom: 10px;    .con_num {      padding: 15px;    }    .ok {      color: #26850c;    }    .no {      color: #98632d;    }  }}.card {  box-shadow: 0 10px 10px #eaeaea;  height: 50px;}.van-popup {  height: 50%;}.check-area {  //background-color: #f1f1f1;  text-align: center;  margin: 10px;  padding: 20px;  //color: #aaa;  border-radius: 6px;  justify-content: space-between;  align-items: center;  box-shadow: 0 2px 6px #ddd;  position: relative;  .ysj {    position: absolute;    right: 5px;    top: 5px;  }}.active {  background-color: #bdbdbd;  color: #333;}.complete {  color: #fff;  background-color: #1989fa;}.nfc-icon {  width: 50px;  height: 50px;  margin-left: 20px;}.bottomClass {  position: fixed;  width: 100%;  bottom: 0;  z-index: 999;  background: #ebedf0;  text-align: center;  .van-button {    width: 95%;    margin-top: 10px;  }}.okAll {  background-color: #fff;  display: flex;  justify-content: center;  align-items: center;  height: 50px;}.nfc-img {  display: inline-block;  width: 160px;  height: 160px;  margin: 0 10px;  position: relative;  > img {    width: 100%;    height: 100%;    border: none;  }  > span {    position: absolute;    padding: 0 10px;    bottom: 0;    left: 0;    display: block;    width: 100%;    background-color: rgba(0, 0, 0, 0.2);    color: #eaeaea;    font-size: 5px;    overflow: hidden;    text-overflow: ellipsis;    line-height: 30px;    white-space: break-spaces;    height: 30px;  }  .cancel_icon {    position: absolute;    font-size: 30px;    right: 5px;    top: 2px;  }}</style>
 |