Vue中报错Error in v-on handler: “TypeError: dateObject.getTime is not a function“

文章描述了一个在Vue2项目中进行表单上传时遇到的错误——TypeError:dateObject.getTimeisnotafunction。问题根源在于rules中的日期验证规则type:date,由于日期字段被初始化为字符串而非日期对象,导致此问题。解决方案是移除rules中的type:date约束,确保日期字段在表单中重新选择后以正确的日期格式处理。
  1. 背景:在vue2项目中将表单进行上传,报错Error in v-on handler: "TypeError: dateObject.getTime is not a function"

  1. 分析:找到原因是rules中type:'date’造成的,因为初始化成了string类型。在表单中重新选择时间后此处的时间会变成字符串格式。

  1. 解决:在rules中的日期验证上删除 *type:‘date’,

<template> <view class="detail" :style="'height: '+ windowHeight +'px;'"> <swiper class="swipers" :current="current" :vertical="true" :circular="true" @change="swiperChange"> <swiper-item class="swipers-item" v-for="(item, index) in displaySwiperList" :key="index"> <com-video :key="tableTimeStamp" :ref="`videoRef-${item.id}`" :height="videoHeight" :vinfo="vodinfo" :item="item" :qingping="isqp" :current="current" :index="index" @ended="onEnded" @onPause="onPause" @onPlay="onPlay" v-if="current==index"></com-video> <view v-if="item.pays==1" class="playbtnbox" :style="{height:`${videoHeight}px`}"> <view class="unlockbtn" @click.stop="needpay()"> <text class="unlockbtn-txt">当前剧集需要解锁</text> </view> </view> <view class="swiper-selbar"> <view class="selvideo" @click.stop="sellxj()"> <view class="swipers-items-info-num">{{item.namets}}</view> <image src="@/static/up.png" class="selicon"></image> </view> <view class="swipers-items-qpimg" @click.stop="qingping()"> <uni-icons v-if="isqp" type="checkbox" size="24" color="#666"></uni-icons> <uni-icons v-else type="close" size="24" color="#666"></uni-icons> </view> </view> </swiper-item> </swiper> <uni-popup type="bottom" ref="select"> <view class="list"> <view class="list-top"> <image :src="vodinfo.img" class="list-top-img"></image> <view class="list-top-text"> <view> <text class="list-top-title">{{vodinfo.title}}</text> </view> <view> <text class="list-top-info">{{vodinfo.upstatus}} , 共{{total}}集</text> </view> </view> <uni-icons class="downicon" type="down" color="#ff007f" @click="down()"></uni-icons> </view> <view class="listtab"> <text class="listtab-txt" :style="tabindex == 0?'color:#333':''" @tap="changetab(0)">选集</text> <text class="listtab-txt" :style="tabindex == 1?'color:#333':''" @tap="changetab(1)">简介</text> </view> <scroll-view class="listwrap" v-if="tabindex == 0" :scroll-y="true" :scroll-top="scrollTop"> <view class="list-box"> <block v-for="(medias,index) in originList" :key="index"> <view @click="selectThisVideo(index)" class="list-box-item" :style="(index+1)%6 == 0?'margin-right:0':''"> <view class="list-box-item-info" :style="index == originIndex?'background-color:#fee9f8':''"> <view class="item_suo_box" v-if="medias.pays==1"> <uni-icons class="item_suo" type="locked" color="#ff007f"></uni-icons> </view> <text class="item_name" :style="index == originIndex?'color:#ff007f':''">{{index+1}}</text> </view> </view> </block> </view> </scroll-view> <scroll-view :scroll-y="true" v-if="tabindex == 1" class="voddesc"> <mp-html :content="vodinfo.info" container-style="color: #333;" /> </scroll-view> </view> </uni-popup> <!--弹窗广告--> <uni-popup ref="popupad" type="bottom"> <view class="custom_ad"> <view class="status_bar" :style="{'height':statusBarHeight+'px'}">状态栏</view> <view class="toptip"> <text class="adtip">AD | {{second}} {{second_tip}}</text> <text @click="closeAd()" class="adclose">关闭</text> </view> <view class="adcon" :style="{'height':(windowHeight-statusBarHeight-44)+'px'}"> <image v-if="adinfo.type =='image'" class="adimg" mode="aspectFit" :src="adinfo.src"></image> <video class="adimg" object-fit="cover" v-else :src="adinfo.src" autoplay="true" controls="false"></video> <cover-view class="adlink" @tap="clickAd()"></cover-view> </view> </view> </uni-popup> <uni-popup ref="popupbuy" type="bottom"> <charge-dialog :vid="vid" :serialNo="serialno" :mid="mid" :adunlock="adunlock" @Adunlock="freebyad" @watchClose="chargeClose" > </charge-dialog> </uni-popup> </view> </template> <script> import request from "@/common/request.js" var adtmjs,rewardedVideoAd import { mapActions } from 'vuex' export default { data() { return { vodinfo:[], total:0, originList: [], // 源数据 displaySwiperList: [], // swiper需要的数据 displayIndex: 0, // 用于显示swiper的真正的下标数值只有:0,1,2。 originIndex: 0, // 记录源数据的下标 windowHeight:0, videoHeight:0, statusBarHeight:0, current:0, isqp:false,//清屏 buyindex:0,//解锁集索引 scrollTop:0, vid: 0, serialno:0,//第几集 mid: 0,//单集ID uscore:0, priced:0,//单集价格 priceq:0,//全集价格 ad_isLoaded:false, adpid:'', mpadid:'', adunlock:0,//广告解锁开关 ad_unlock_vids:[],//广告解锁剧集 unlocknum:1,//解锁集数 second:60,//广告时间 second_tip:'秒后可获得奖励', adinfo:[],//平台广告 tabindex:0, timer: null } }, onLoad(e) { if(e.invitecode){ uni.setStorageSync('invitecode',e.invitecode) } if(e.vid>0){ this.vid = parseInt(e.vid) } if(e.mid>0){ this.mid = parseInt(e.mid) } if(e.serialno>0){ this.serialno = parseInt(e.serialno) } //缓存的解锁剧集 if(uni.getStorageSync("ad_unlock_vids")){ this.ad_unlock_vids = uni.getStorageSync("ad_unlock_vids") } this.config(); this.videoinfo(); }, onReady() { // 使用同步方法获取系统信息 const systemInfo = uni.getSystemInfoSync(); this.statusBarHeight = systemInfo.statusBarHeight; // 获取安全区域信息 const safeArea = systemInfo.safeArea; // 底部安全高度(注意:这里假设safeArea.bottom是相对于屏幕底部的安全距离) const safeHeight = safeArea.bottom ? safeArea.bottom : 0; this.windowHeight = systemInfo.windowHeight this.videoHeight = safeHeight-65 }, methods: { ...mapActions(['getConfig']), async config(){ let sitecfg = await this.getConfig() if(sitecfg && sitecfg.site.adunlock==1){ this.adunlock = parseInt(sitecfg.site.adunlock) this.unlocknum = sitecfg.site.adunlock_num this.adpid = sitecfg.site.appad.adpid;//app奖励广告adpid this.createAd(); } }, //加载剧集 async videoinfo(){ let that=this let data = {}; data.vid= parseInt(this.vid) let [err,res] =await request.get('/api/video/videoinfo',data,{token:true}); if(res.data.total){ this.vodinfo = res.data.info uni.setNavigationBarTitle({ title: this.vodinfo.title }); this.total = res.data.total//总集数 var msg = res.data.data var midlog = res.data.midlog //观看历史记录视频id //2.这里把视频添加到视频列表 for (let i = 0; i < msg.length; i++) { msg[i].vid = msg[i].pid if(that.serialno>0){ if(i == that.serialno-1){ that.originIndex=i } }else if(msg[i].mid==that.mid){ that.originIndex=i }else{ if(msg[i].mid == midlog){ that.originIndex=i } } //判断是否广告解锁 if(msg[i].pays==1 && that.ad_unlock_vids.indexOf(msg[i].pid+'_'+msg[i].mid) !=-1){ msg[i].pays = 2 } } that.originList = msg that.initSwiperData(that.originIndex); }else{ uni.showToast({ title: '加载出现错误',icon:"none" }); setTimeout(function(){ uni.navigateTo({ url:'/pages/index/index' }) },1000) } }, createAd(){ // #ifndef H5 let userId = this.memberInfo?this.memberInfo.id:0; if (this.adpid || this.mpadid){ rewardedVideoAd = uni.createRewardedVideoAd({ adpid: this.adpid, urlCallback: { // 服务器回调透传参数 userId: userId, extra: 'testdata' } }) rewardedVideoAd.onLoad(() => { this.ad_isLoaded = true console.log('onLoad event') // 当激励视频被关闭时,默认预载下一条数据,加载完成时仍然触发 `onLoad` 事件 }) rewardedVideoAd.onError((err) => { console.log('onError event', err) }) rewardedVideoAd.onClose(res => { if (res && res.isEnded) { // 正常播放结束 this.adEnded() } else { // 播放中途退出 } }) } // #endif }, //广告解锁 async freebyad(){ let that=this this.chargeClose(); if (this.ad_isLoaded) { rewardedVideoAd.show() }else{ //随机取一个广告 let data={} let [err,res] =await request.get('/api/index/rewardad',data); if(res.data.code==1){ this.adinfo=res.data.data } this.$refs.popupad.open('bottom'); clearInterval(adtmjs) this.second=30 //广告时间 adtmjs = setInterval(() => { this.second-- if (this.second == 0) { clearInterval(adtmjs) adtmjs = null this.second='' this.second_tip='已获得奖励' } }, 1000); } }, closeAd(){ this.$refs.popupad.close(); if(this.second==''){ this.adEnded() } }, clickAd(){ if(!this.adinfo.link){ return } if(this.adinfo.linktype==1){ uni.navigateTo({ url:'/pages/index/webva?url='+encodeURIComponent(this.adinfo.link) }) }else{ uni.navigateTo({ url:this.adinfo.link }) } }, //激励广告执行解锁 adEnded(){ var curindex = this.buyindex for (let i=0;i<this.unlocknum;i++) { if(!this.originList[curindex]){ break } let info=this.originList[curindex] let ad_vid=info.pid+'_'+info.id; if(this.ad_unlock_vids.indexOf(ad_vid)==-1){ this.ad_unlock_vids.push(ad_vid) } curindex++ } uni.setStorageSync("ad_unlock_vids",this.ad_unlock_vids) this.unlocked() uni.showToast({ title: '剧集解锁成功', icon:'none' }) }, //选集播放 selectThisVideo(index){ this.down(); this.duration = 20; this.originIndex=index this.initSwiperData(index); setTimeout(()=>{ this.duration = 500; },500) }, down(){ this.$refs.select.close(); }, chargeClose(){ this.$refs.popupbuy.close(); }, sellxj() { this.$refs.select.open('bottom'); }, needpay(){ if(this.originList[this.originIndex].pays==1){ this.buyindex=this.originIndex this.$refs.popupbuy.open('bottom'); return } }, //解锁成功 unlocked(){ console.log(this.buyindex,this.current) //刷新数据,判断是否广告解锁 for (let i=0;i<this.originList.length;i++) { let pid=this.originList[i].pid let mid=this.originList[i].mid if(this.originList[i].pays==1 && this.ad_unlock_vids.indexOf(pid+'_'+mid) !=-1){ this.originList[i].pays = 2 } } this.selectThisVideo(this.buyindex) }, qingping(){ this.isqp=!this.isqp }, onPlay(){ this.timer = setTimeout(()=>{ this.isqp=true },3000) }, onPause(){ this.isqp=false clearTimeout(this.timer); }, onEnded() { if(this.displayIndex<2){ this.current=this.displayIndex+1 }else{ this.current=0 } }, changetab(index){ this.tabindex=index }, /** * 初始一个显示的swiper数据 * originIndex 从源数据的哪个开始显示默认0,如从其他页面跳转进来,要显示第n个,这个参数就是他的下标 */ async initSwiperData(originIndex = this.originIndex) { const originListLength = this.originList.length; // 源数据长度 const displayList = []; displayList[this.displayIndex] = this.originList[originIndex]; displayList[this.displayIndex - 1 == -1 ? 2 : this.displayIndex - 1] = this.originList[originIndex - 1 == -1 ? originListLength - 1 : originIndex - 1]; displayList[this.displayIndex + 1 == 3 ? 0 : this.displayIndex + 1] = this.originList[originIndex + 1 == originListLength ? 0 : originIndex + 1]; this.displaySwiperList = displayList; this.tableTimeStamp = new Date().getTime(); //正在播放集数 this.mid = this.originList[originIndex].mid this.serialno = parseInt(originIndex+1)//集数 this.needpay() }, /** * swiper滑动时候 */ swiperChange(event) { this.circular=true const { current } = event.detail; this.current = current const originListLength = this.originList.length; // 源数据长度 // =============向后========== if (this.displayIndex - current == 2 || this.displayIndex - current == -1) { this.originIndex = this.originIndex + 1 == originListLength ? 0 : this.originIndex + 1; this.displayIndex = this.displayIndex + 1 == 3 ? 0 : this.displayIndex + 1; this.initSwiperData(this.originIndex); } // ======如果两者的差为-2或者1则是向前滑动============ else if (this.displayIndex - current == -2 || this.displayIndex - current == 1) { this.originIndex = this.originIndex - 1 == -1 ? originListLength - 1 : this.originIndex - 1; this.displayIndex = this.displayIndex - 1 == -1 ? 2 : this.displayIndex - 1; this.initSwiperData(this.originIndex); } } } } </script> <style> .detail { background-color: #000; position: relative; } .topbar{ width:100%; position: absolute; top:0; z-index:100; } .swipers { height:100%; } .swipers-items{ position: relative; } .swipers-items-video { width:100%; } .swipers-items-imgsbg { width: 100%; height:100%; border-radius: 0 0 25rpx 25rpx; } .playbtnbox{ width:100%; position: absolute; top:0; z-index:1; display: flex; justify-content: center; align-items: center; background-color: rgba(0, 0, 0,0.6); } .playState{ width: 160rpx; height: 160rpx; opacity: 0.8; } .unlockbtn{ padding:10upx 20upx; border-radius:50rpx; background-color: #ff007f; } .unlockbtn-txt{ color:white; font-size:28rpx; } .swipers-items-info { width: 600rpx; position: absolute; bottom: 200rpx; left: 20rpx; color: #ffffff; z-index: 1; } .swipers-items-info-title{ display: flex; flex-direction: row; align-items: center; } .swipers-items-info-img{ width:60rpx; height:80rpx; border-radius: 10rpx; } .swipers-items-info-name{ color: #ffffff; font-size: 28rpx; margin-left: 20rpx; } .swipers-items-info-content { font-size: 26rpx; color: #ffffff; overflow: hidden; margin-top: 10rpx; border-radius: 10rpx; } .swiper-selbar{ margin-top: 20rpx; display: flex; height:100rpx; align-items: center; } .selvideo{ width:100%; position: relative; } .selicon{ z-index: 1; width:60rpx; height:60rpx; position: absolute; top:35rpx; right: 50rpx; } .swipers-items-info-num { margin: 30rpx; color: #ffffff; font-size: 28rpx; background-color: #1a1a1a; border-radius: 10rpx; padding: 20rpx; opacity: 1; } .swipers-items-qpimg{ margin-right:15rpx; } .swipers-items-right { z-index: 1; width: 70rpx; position: absolute; right: 20rpx; bottom: 300rpx; z-index: 999; } .swipers-items-right-item { margin-bottom: 20rpx; text-align: center; } .swipers-items-right-item-img { width: 60rpx; height: 60rpx; margin-left:5rpx } .swipers-items-right-item-txt { text-align: center; font-size: 20rpx; color: #ffffff; } .share-btn{ z-index: 1; position: absolute; width: 60rpx; height: 60rpx; border: 0; background-color: transparent; } .list { background:#ffffff; padding:20upx; border-radius: 20upx 20upx 0 0; } .list-top { display: flex; flex-direction: row; } .list-top-img { width: 130upx; height: 160upx; border-radius: 10rpx; } .list-top-text { padding-left: 30rpx; padding-right: 30rpx; } .list-top-title { font-size:28upx; } .list-top-info { font-size:24upx; color:#999; margin-top: 20upx; } .listwrap{ width: 710upx; height: 500upx; } .list-item { padding: 0 30rpx; } .list-item-box { line-height: 90rpx; background-color: #fff; color: #000; font-weight: bold; text-align: center; margin-bottom: 40rpx; border-radius: 20rpx; } .list-box { width:710rpx; display:flex; flex-wrap: wrap; /* 允许换行 */ flex-direction: row; padding-top: 20upx; align-items: center; /* 子元素在交叉轴上的对齐方式 */ } .list-box-item { position: relative; text-align: center; margin: 0 20upx 20upx 0; height: 80upx; width: 100upx; overflow: hidden; background-color: #F2F5F7; border-radius: 5upx; } .item_name{ line-height: 80upx; font-size: 36rpx; text-align: center; } .item_suo_box{ position: absolute; z-index: 1; top:0; right:0; width: 36upx; height: 36upx; background-color: #e7e7e7; border-radius: 0 10rpx; } .flex_row{ display: flex; align-items: center; } .downicon { width: 30px; height: 30px; margin-top: 5px; position: absolute; z-index: 1; right: 10px; } .custom_ad{ width: 750rpx; position: relative; background-color: #000; } .toptip{ display: flex; flex-direction: row; justify-content: space-between; padding:20rpx; background-color: #000; } .adtip,.adclose{ color:#ccc; font-size: 12px; border: solid 1px #eee; border-radius: 10px; padding: 2px 5px; background-color: #333; } .adcon{ position: relative; display: flex; flex-direction: column; } .adimg{ /* #ifndef APP */ width: 100%; height: 100%; /* #endif */ /* #ifdef APP */ flex:1; width:750rpx; /* #endif */ } .adlink{ position: absolute; top:0; bottom: 0; left:0; right:0; z-index: 1000; } .listtab{ display: flex; flex-direction: row; padding:15rpx 0; } .listtab-txt{ font-size:32upx; padding:0 10upx; color: #999; } .voddesc{ font-size:28upx; padding: 0 10rpx; color: #333; height: 500upx; } </style> chunk-vendors.js:20712 [Vue warn]: Property or method "onError" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property. See: https://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties. found in ---> <OVideo> at components/common/video.vue at pages/video/detail.vue warn @ chunk-vendors.js:20712 warnNonPresent @ chunk-vendors.js:22116 get @ chunk-vendors.js:22171 error @ pages-video-detail~pages-video-video.js:2447 invokeWithErrorHandling @ chunk-vendors.js:21955 invoker @ chunk-vendors.js:22280 invokeWithErrorHandling @ chunk-vendors.js:21955 Vue.$emit @ chunk-vendors.js:24024 $trigger @ chunk-vendors.js:19677 onVideoError @ chunk-vendors.js:19677 invokeWithErrorHandling @ chunk-vendors.js:21955 invoker @ chunk-vendors.js:22280 original._wrapper @ chunk-vendors.js:27164 error (async) add$1 @ chunk-vendors.js:27168 updateListeners @ chunk-vendors.js:22312 updateDOMListeners @ chunk-vendors.js:27198 invokeCreateHooks @ chunk-vendors.js:26207 createElm @ chunk-vendors.js:26094 createChildren @ chunk-vendors.js:26191 createElm @ chunk-vendors.js:26092 createChildren @ chunk-vendors.js:26191 createElm @ chunk-vendors.js:26092 patch @ chunk-vendors.js:26618 Vue._update @ chunk-vendors.js:24081 updateComponent @ chunk-vendors.js:24202 get @ chunk-vendors.js:24621 Watcher @ chunk-vendors.js:24610 mountComponent @ chunk-vendors.js:24209 push.m44O.Vue.$mount @ chunk-vendors.js:28719 init @ chunk-vendors.js:23255 createComponent @ chunk-vendors.js:26116 createElm @ chunk-vendors.js:26063 createChildren @ chunk-vendors.js:26191 createElm @ chunk-vendors.js:26092 patch @ chunk-vendors.js:26618 Vue._update @ chunk-vendors.js:24081 updateComponent @ chunk-vendors.js:24202 get @ chunk-vendors.js:24621 Watcher @ chunk-vendors.js:24610 mountComponent @ chunk-vendors.js:24209 push.m44O.Vue.$mount @ chunk-vendors.js:28719 init @ chunk-vendors.js:23255 createComponent @ chunk-vendors.js:26116 createElm @ chunk-vendors.js:26063 patch @ chunk-vendors.js:26618 Vue._update @ chunk-vendors.js:24081 updateComponent @ chunk-vendors.js:24202 get @ chunk-vendors.js:24621 Watcher @ chunk-vendors.js:24610 mountComponent @ chunk-vendors.js:24209 push.m44O.Vue.$mount @ chunk-vendors.js:28719 init @ chunk-vendors.js:23255 createComponent @ chunk-vendors.js:26116 createElm @ chunk-vendors.js:26063 createChildren @ chunk-vendors.js:26191 createElm @ chunk-vendors.js:26092 patch @ chunk-vendors.js:26618 Vue._update @ chunk-vendors.js:24081 updateComponent @ chunk-vendors.js:24202 get @ chunk-vendors.js:24621 Watcher @ chunk-vendors.js:24610 mountComponent @ chunk-vendors.js:24209 push.m44O.Vue.$mount @ chunk-vendors.js:28719 init @ chunk-vendors.js:23255 createComponent @ chunk-vendors.js:26116 createElm @ chunk-vendors.js:26063 addVnodes @ chunk-vendors.js:26247 updateChildren @ chunk-vendors.js:26374 patchVnode @ chunk-vendors.js:26460 updateChildren @ chunk-vendors.js:26334 patchVnode @ chunk-vendors.js:26460 updateChildren @ chunk-vendors.js:26334 patchVnode @ chunk-vendors.js:26460 updateChildren @ chunk-vendors.js:26334 patchVnode @ chunk-vendors.js:26460 patch @ chunk-vendors.js:26623 Vue._update @ chunk-vendors.js:24084 updateComponent @ chunk-vendors.js:24202 get @ chunk-vendors.js:24621 run @ chunk-vendors.js:24696 flushSchedulerQueue @ chunk-vendors.js:24452 (anonymous) @ chunk-vendors.js:22081 flushCallbacks @ chunk-vendors.js:22007 Promise.then (async) timerFunc @ chunk-vendors.js:22034 nextTick @ chunk-vendors.js:22091 queueWatcher @ chunk-vendors.js:24544 update @ chunk-vendors.js:24686 notify @ chunk-vendors.js:20826 reactiveSetter @ chunk-vendors.js:21158 proxySetter @ chunk-vendors.js:24773 _callee2$ @ detail.vue:185 tryCatch @ chunk-vendors.js:5954 (anonymous) @ chunk-vendors.js:6035 (anonymous) @ chunk-vendors.js:5979 asyncGeneratorStep @ chunk-vendors.js:6408 _next @ chunk-vendors.js:6427 Promise.then (async) asyncGeneratorStep @ chunk-vendors.js:6417 _next @ chunk-vendors.js:6427 (anonymous) @ chunk-vendors.js:6432 (anonymous) @ chunk-vendors.js:6424 videoinfo @ detail.vue:221 onLoad @ detail.vue:153 invokeWithErrorHandling @ chunk-vendors.js:21955 push.m44O.Vue.__call_hook @ chunk-vendors.js:28703 r @ chunk-vendors.js:19677 a @ chunk-vendors.js:19677 created @ chunk-vendors.js:19677 invokeWithErrorHandling @ chunk-vendors.js:21955 callHook @ chunk-vendors.js:24361 Vue._init @ chunk-vendors.js:25146 VueComponent @ chunk-vendors.js:25292 createComponentInstanceForVnode @ chunk-vendors.js:23422 init @ chunk-vendors.js:23251 createComponent @ chunk-vendors.js:26116 createElm @ chunk-vendors.js:26063 addVnodes @ chunk-vendors.js:26247 updateChildren @ chunk-vendors.js:26374 patchVnode @ chunk-vendors.js:26460 updateChildren @ chunk-vendors.js:26334 patchVnode @ chunk-vendors.js:26460 patch @ chunk-vendors.js:26623 Vue._update @ chunk-vendors.js:24084 updateComponent @ chunk-vendors.js:24202 get @ chunk-vendors.js:24621 run @ chunk-vendors.js:24696 flushSchedulerQueue @ chunk-vendors.js:24452 (anonymous) @ chunk-vendors.js:22081 flushCallbacks @ chunk-vendors.js:22007 Promise.then (async) timerFunc @ chunk-vendors.js:22034 nextTick @ chunk-vendors.js:22091 queueWatcher @ chunk-vendors.js:24544 update @ chunk-vendors.js:24686 Vue.$forceUpdate @ chunk-vendors.js:24105 forceRender @ chunk-vendors.js:23783 (anonymous) @ chunk-vendors.js:23805 (anonymous) @ chunk-vendors.js:20429 (anonymous) @ index.js:1505 Promise.then (async) (anonymous) @ index.js:1504 resolveAsyncComponent @ chunk-vendors.js:23822 createComponent @ chunk-vendors.js:23339 _createElement @ chunk-vendors.js:23561 createElement @ chunk-vendors.js:23492 vm.$createElement @ chunk-vendors.js:23633 render @ index.js:2505 Vue._render @ chunk-vendors.js:23684 updateComponent @ chunk-vendors.js:24202 get @ chunk-vendors.js:24621 Watcher @ chunk-vendors.js:24610 mountComponent @ chunk-vendors.js:24209 push.m44O.Vue.$mount @ chunk-vendors.js:28719 init @ chunk-vendors.js:23255 createComponent @ chunk-vendors.js:26116 createElm @ chunk-vendors.js:26063 patch @ chunk-vendors.js:26618 Vue._update @ chunk-vendors.js:24081 updateComponent @ chunk-vendors.js:24202 get @ chunk-vendors.js:24621 Watcher @ chunk-vendors.js:24610 mountComponent @ chunk-vendors.js:24209 push.m44O.Vue.$mount @ chunk-vendors.js:28719 init @ chunk-vendors.js:23255 createComponent @ chunk-vendors.js:26116 createElm @ chunk-vendors.js:26063 patch @ chunk-vendors.js:26618 Vue._update @ chunk-vendors.js:24081 updateComponent @ chunk-vendors.js:24202 get @ chunk-vendors.js:24621 Watcher @ chunk-vendors.js:24610 mountComponent @ chunk-vendors.js:24209 push.m44O.Vue.$mount @ chunk-vendors.js:28719 init @ chunk-vendors.js:23255 createComponent @ chunk-vendors.js:26116 createElm @ chunk-vendors.js:26063 createChildren @ chunk-vendors.js:26191 createElm @ chunk-vendors.js:26092 patch @ chunk-vendors.js:26618 Vue._update @ chunk-vendors.js:24081 updateComponent @ chunk-vendors.js:24202 get @ chunk-vendors.js:24621 Watcher @ chunk-vendors.js:24610 mountComponent @ chunk-vendors.js:24209 push.m44O.Vue.$mount @ chunk-vendors.js:28719 init @ chunk-vendors.js:23255 createComponent @ chunk-vendors.js:26116 createElm @ chunk-vendors.js:26063 patch @ chunk-vendors.js:26657 Vue._update @ chunk-vendors.js:24081 updateComponent @ chunk-vendors.js:24202 get @ chunk-vendors.js:24621 Watcher @ chunk-vendors.js:24610 mountComponent @ chunk-vendors.js:24209 push.m44O.Vue.$mount @ chunk-vendors.js:28719 eval @ main.js:28 V+zZ @ index.js:1190 __webpack_require__ @ index.js:854 fn @ index.js:151 1 @ index.js:1037 __webpack_require__ @ index.js:854 checkDeferredModules @ index.js:46 (anonymous) @ index.js:994 (anonymous) @ index.js:997 Show 149 more frames chunk-vendors.js:20712 [Vue warn]: Error in v-on handler: "TypeError: Cannot read property 'apply' of undefined" found in ---> <OVideo> at components/common/video.vue at pages/video/detail.vue warn @ chunk-vendors.js:20712 e.config.errorHandler @ chunk-vendors.js:19677 globalHandleError @ chunk-vendors.js:21971 handleError @ chunk-vendors.js:21940 invokeWithErrorHandling @ chunk-vendors.js:21963 invoker @ chunk-vendors.js:22280 invokeWithErrorHandling @ chunk-vendors.js:21955 Vue.$emit @ chunk-vendors.js:24024 $trigger @ chunk-vendors.js:19677 onVideoError @ chunk-vendors.js:19677 invokeWithErrorHandling @ chunk-vendors.js:21955 invoker @ chunk-vendors.js:22280 original._wrapper @ chunk-vendors.js:27164 error (async) add$1 @ chunk-vendors.js:27168 updateListeners @ chunk-vendors.js:22312 updateDOMListeners @ chunk-vendors.js:27198 invokeCreateHooks @ chunk-vendors.js:26207 createElm @ chunk-vendors.js:26094 createChildren @ chunk-vendors.js:26191 createElm @ chunk-vendors.js:26092 createChildren @ chunk-vendors.js:26191 createElm @ chunk-vendors.js:26092 patch @ chunk-vendors.js:26618 Vue._update @ chunk-vendors.js:24081 updateComponent @ chunk-vendors.js:24202 get @ chunk-vendors.js:24621 Watcher @ chunk-vendors.js:24610 mountComponent @ chunk-vendors.js:24209 push.m44O.Vue.$mount @ chunk-vendors.js:28719 init @ chunk-vendors.js:23255 createComponent @ chunk-vendors.js:26116 createElm @ chunk-vendors.js:26063 createChildren @ chunk-vendors.js:26191 createElm @ chunk-vendors.js:26092 patch @ chunk-vendors.js:26618 Vue._update @ chunk-vendors.js:24081 updateComponent @ chunk-vendors.js:24202 get @ chunk-vendors.js:24621 Watcher @ chunk-vendors.js:24610 mountComponent @ chunk-vendors.js:24209 push.m44O.Vue.$mount @ chunk-vendors.js:28719 init @ chunk-vendors.js:23255 createComponent @ chunk-vendors.js:26116 createElm @ chunk-vendors.js:26063 patch @ chunk-vendors.js:26618 Vue._update @ chunk-vendors.js:24081 updateComponent @ chunk-vendors.js:24202 get @ chunk-vendors.js:24621 Watcher @ chunk-vendors.js:24610 mountComponent @ chunk-vendors.js:24209 push.m44O.Vue.$mount @ chunk-vendors.js:28719 init @ chunk-vendors.js:23255 createComponent @ chunk-vendors.js:26116 createElm @ chunk-vendors.js:26063 createChildren @ chunk-vendors.js:26191 createElm @ chunk-vendors.js:26092 patch @ chunk-vendors.js:26618 Vue._update @ chunk-vendors.js:24081 updateComponent @ chunk-vendors.js:24202 get @ chunk-vendors.js:24621 Watcher @ chunk-vendors.js:24610 mountComponent @ chunk-vendors.js:24209 push.m44O.Vue.$mount @ chunk-vendors.js:28719 init @ chunk-vendors.js:23255 createComponent @ chunk-vendors.js:26116 createElm @ chunk-vendors.js:26063 addVnodes @ chunk-vendors.js:26247 updateChildren @ chunk-vendors.js:26374 patchVnode @ chunk-vendors.js:26460 updateChildren @ chunk-vendors.js:26334 patchVnode @ chunk-vendors.js:26460 updateChildren @ chunk-vendors.js:26334 patchVnode @ chunk-vendors.js:26460 updateChildren @ chunk-vendors.js:26334 patchVnode @ chunk-vendors.js:26460 patch @ chunk-vendors.js:26623 Vue._update @ chunk-vendors.js:24084 updateComponent @ chunk-vendors.js:24202 get @ chunk-vendors.js:24621 run @ chunk-vendors.js:24696 flushSchedulerQueue @ chunk-vendors.js:24452 (anonymous) @ chunk-vendors.js:22081 flushCallbacks @ chunk-vendors.js:22007 Promise.then (async) timerFunc @ chunk-vendors.js:22034 nextTick @ chunk-vendors.js:22091 queueWatcher @ chunk-vendors.js:24544 update @ chunk-vendors.js:24686 notify @ chunk-vendors.js:20826 reactiveSetter @ chunk-vendors.js:21158 proxySetter @ chunk-vendors.js:24773 _callee2$ @ detail.vue:185 tryCatch @ chunk-vendors.js:5954 (anonymous) @ chunk-vendors.js:6035 (anonymous) @ chunk-vendors.js:5979 asyncGeneratorStep @ chunk-vendors.js:6408 _next @ chunk-vendors.js:6427 Promise.then (async) asyncGeneratorStep @ chunk-vendors.js:6417 _next @ chunk-vendors.js:6427 (anonymous) @ chunk-vendors.js:6432 (anonymous) @ chunk-vendors.js:6424 videoinfo @ detail.vue:221 onLoad @ detail.vue:153 invokeWithErrorHandling @ chunk-vendors.js:21955 push.m44O.Vue.__call_hook @ chunk-vendors.js:28703 r @ chunk-vendors.js:19677 a @ chunk-vendors.js:19677 created @ chunk-vendors.js:19677 invokeWithErrorHandling @ chunk-vendors.js:21955 callHook @ chunk-vendors.js:24361 Vue._init @ chunk-vendors.js:25146 VueComponent @ chunk-vendors.js:25292 createComponentInstanceForVnode @ chunk-vendors.js:23422 init @ chunk-vendors.js:23251 createComponent @ chunk-vendors.js:26116 createElm @ chunk-vendors.js:26063 addVnodes @ chunk-vendors.js:26247 updateChildren @ chunk-vendors.js:26374 patchVnode @ chunk-vendors.js:26460 updateChildren @ chunk-vendors.js:26334 patchVnode @ chunk-vendors.js:26460 patch @ chunk-vendors.js:26623 Vue._update @ chunk-vendors.js:24084 updateComponent @ chunk-vendors.js:24202 get @ chunk-vendors.js:24621 run @ chunk-vendors.js:24696 flushSchedulerQueue @ chunk-vendors.js:24452 (anonymous) @ chunk-vendors.js:22081 flushCallbacks @ chunk-vendors.js:22007 Promise.then (async) timerFunc @ chunk-vendors.js:22034 nextTick @ chunk-vendors.js:22091 queueWatcher @ chunk-vendors.js:24544 update @ chunk-vendors.js:24686 Vue.$forceUpdate @ chunk-vendors.js:24105 forceRender @ chunk-vendors.js:23783 (anonymous) @ chunk-vendors.js:23805 (anonymous) @ chunk-vendors.js:20429 (anonymous) @ index.js:1505 Promise.then (async) (anonymous) @ index.js:1504 resolveAsyncComponent @ chunk-vendors.js:23822 createComponent @ chunk-vendors.js:23339 _createElement @ chunk-vendors.js:23561 createElement @ chunk-vendors.js:23492 vm.$createElement @ chunk-vendors.js:23633 render @ index.js:2505 Vue._render @ chunk-vendors.js:23684 updateComponent @ chunk-vendors.js:24202 get @ chunk-vendors.js:24621 Watcher @ chunk-vendors.js:24610 mountComponent @ chunk-vendors.js:24209 push.m44O.Vue.$mount @ chunk-vendors.js:28719 init @ chunk-vendors.js:23255 createComponent @ chunk-vendors.js:26116 createElm @ chunk-vendors.js:26063 patch @ chunk-vendors.js:26618 Vue._update @ chunk-vendors.js:24081 updateComponent @ chunk-vendors.js:24202 get @ chunk-vendors.js:24621 Watcher @ chunk-vendors.js:24610 mountComponent @ chunk-vendors.js:24209 push.m44O.Vue.$mount @ chunk-vendors.js:28719 init @ chunk-vendors.js:23255 createComponent @ chunk-vendors.js:26116 createElm @ chunk-vendors.js:26063 patch @ chunk-vendors.js:26618 Vue._update @ chunk-vendors.js:24081 updateComponent @ chunk-vendors.js:24202 get @ chunk-vendors.js:24621 Watcher @ chunk-vendors.js:24610 mountComponent @ chunk-vendors.js:24209 push.m44O.Vue.$mount @ chunk-vendors.js:28719 init @ chunk-vendors.js:23255 createComponent @ chunk-vendors.js:26116 createElm @ chunk-vendors.js:26063 createChildren @ chunk-vendors.js:26191 createElm @ chunk-vendors.js:26092 patch @ chunk-vendors.js:26618 Vue._update @ chunk-vendors.js:24081 updateComponent @ chunk-vendors.js:24202 get @ chunk-vendors.js:24621 Watcher @ chunk-vendors.js:24610 mountComponent @ chunk-vendors.js:24209 push.m44O.Vue.$mount @ chunk-vendors.js:28719 init @ chunk-vendors.js:23255 createComponent @ chunk-vendors.js:26116 createElm @ chunk-vendors.js:26063 patch @ chunk-vendors.js:26657 Vue._update @ chunk-vendors.js:24081 updateComponent @ chunk-vendors.js:24202 get @ chunk-vendors.js:24621 Watcher @ chunk-vendors.js:24610 mountComponent @ chunk-vendors.js:24209 push.m44O.Vue.$mount @ chunk-vendors.js:28719 eval @ main.js:28 V+zZ @ index.js:1190 __webpack_require__ @ index.js:854 fn @ index.js:151 1 @ index.js:1037 __webpack_require__ @ index.js:854 checkDeferredModules @ index.js:46 (anonymous) @ index.js:994 (anonymous) @ index.js:997 Show 149 more frames chunk-vendors.js:19677 [system] TypeError: Cannot read property 'apply' of undefined at error (pages-video-detail~pages-video-video.js:2447) at invokeWithErrorHandling (chunk-vendors.js:21955) at VueComponent.invoker (chunk-vendors.js:22280) at invokeWithErrorHandling (chunk-vendors.js:21955) at VueComponent.Vue.$emit (chunk-vendors.js:24024) at VueComponent.$trigger (chunk-vendors.js:19677) at VueComponent.onVideoError (chunk-vendors.js:19677) at invokeWithErrorHandling (chunk-vendors.js:21955) at HTMLVideoElement.invoker (chunk-vendors.js:22280) at HTMLVideoElement.original._wrapper (chunk-vendors.js:27164)
10-21
/*-----------弹窗模块管理------------------- */ <template> <el-dialog :title="dialogMode === 'create' ? '新建' : dialogMode === 'edit' ? '修改' : '查看'" :visible.sync="dialogVisible" :modal-append-to-body="true" append-to-body :close-on-click-modal="false" custom-class="fixed-height-dialog" width="60%" top="5vh"> <el-form label-width="80px" ref="formRef" :model="currentForm" style="height: 100%; display: flex; flex-direction: column;" :rules="rules"> <!-- 项目信息区域 --> <div class="formBorder"> <el-row :gutter="10"> <el-col :span="6"> <el-form-item size="mini" label="项目名称" prop="projectName"> <el-input v-model="currentForm.projectName" clearable style="width:100%" size="mini" :disabled="dialogMode === 'view'"> </el-input> </el-form-item> </el-col> <el-col :span="6"> <el-form-item size="mini" label="项目编号" prop="projectCode"> <el-input v-model="currentForm.projectCode" clearable style="width:100%" size="mini" :disabled="dialogMode === 'view'"> </el-input> </el-form-item> </el-col> <el-col :span="12"> <el-form-item size="mini" label="项目周期" prop="projectDate"> <el-date-picker v-model="projectDate" range-separator="→" start-placeholder="请选择开始日期" end-placeholder="请选择结束日期" type="daterange" size="mini" style="width: 100%;" unlink-panels :disabled="dialogMode === 'view'"> </el-date-picker> </el-form-item> </el-col> </el-row> <el-row> <el-col :span="6"> <el-form-item label="负责人" size="mini" style="width: fit-content;"> <el-input v-model="currentForm.projectUser" clearable style="width:100%" size="mini" :disabled="dialogMode === 'view'"> </el-input> </el-form-item> </el-col> </el-row> <el-row> <el-col :span="24"> <el-form-item label="项目概述"> <el-input v-model="currentForm.remark" :rows="2" :disabled="dialogMode === 'view'"> </el-input> </el-form-item> </el-col> </el-row> </div> <div class="formBorder2"> <el-container style="height: 100%;"> <el-header style="height: auto; flex-shrink: 0; padding-bottom: 10px;"> <el-row :gutter="10" type="flex" class="searchDialog"> <el-col :span="5"> <el-select v-model="filterForm.maintenanceCompanyName" placeholder="请选择管养单位" size="mini" clearable filterable @clear="resetSearch" :disabled="dialogMode === 'view'"> <el-option v-for="item in MaintenanceUnitoptions" :key="item.value" :label="item.label" :value="item.value"> </el-option> </el-select> </el-col> <el-col :span="5"> <el-select v-model="filterForm.routeCode" placeholder="请选择路线编号" size="mini" clearable filterable @clear="resetSearch" :disabled="dialogMode === 'view'"> <el-option v-for="item in routeCodeOptions" :key="item.value" :label="item.label" :value="item.value"> </el-option> </el-select> </el-col> <el-col :span="5"> <el-input v-model="filterForm.searchKey" placeholder="请输入边坡编号或名称" size="mini" clearable @keyup.enter.native="searchForm" @clear="resetSearch" :disabled="dialogMode === 'view'"> <i slot="suffix" class="el-input__icon el-icon-search"></i> </el-input> </el-col> <el-col :span="5"> <el-select v-model="filterForm.evaluateLevel" placeholder="请选择技术状态等级" size="mini" clearable @clear="resetSearch" :disabled="dialogMode === 'view'"> <el-option v-for="item in evaluateLeveloptions" :key="item.value" :label="item.label" :value="item.value" /> </el-select> </el-col> <el-col :span="2" :offset="4"> <el-button type="primary" size="mini" style="width:100%" icon="el-icon-search" @click="searchForm" :loading="loading" :disabled="dialogMode === 'view'">搜索 </el-button> </el-col> </el-row> </el-header> <el-main style="overflow-y: hidden;"> <el-table ref="scrollTable" v-loading="loading" style="width: 100%;" border :data="formTabledata" :row-style="{ height: '36px' }" :cell-style="{ padding: '4px 0', textAlign: 'center' }" :header-cell-style="{ height: '36px', padding: '4px 0', lineHeight: '36px', textAlign: 'center' }" @selection-change="handleSelectionChange" :row-key="getRowkey"> <el-table-column type="selection" width="55" :selectable="isRowSelectable" :reserve-selection="true"> </el-table-column> <el-table-column label="管养单位" prop="maintenanceCompanyName" width="290" show-overflow-tooltip> </el-table-column> <el-table-column label="路线编号" prop="routeCode" width="100"> </el-table-column> <el-table-column label="边坡编号" prop="sideSlopeCode" width="240" show-overflow-tooltip> </el-table-column> <el-table-column label="边坡名称" prop="sideSlopeName" width="267" show-overflow-tooltip> </el-table-column> <el-table-column label="技术状态等级" width="137"> <template slot-scope="scope"> {{ mapEvaluateLevel(scope.row.evaluateLevel) }} </template> </el-table-column> </el-table> </el-main> <!-- 分页区域 --> <el-footer style="flex-shrink: 0; padding-top: 10px;"> <el-pagination background @current-change="handleCurrentChange" :current-page="pageParams.pageNo" :page-size="pageParams.pageSize" layout="total, prev, pager, next" :total="total"> </el-pagination> </el-footer> </el-container> </div> </el-form> <!-- 弹窗底部按钮 --> <div slot="footer" class="dialog-footer" v-if="dialogMode === 'create' || dialogMode === 'edit'"> <el-button @click="dialogVisible = false">取消 </el-button> <el-button type="primary" @click="submitForm">提交 </el-button> </div> </el-dialog> </template> <script> import { mapCfg } from "@/utils"; import { getPeriodicInspectionSideSlopePageList, addPeriodicInspection, modifyPeriodicInspection, getSelectedPeriodicInspectionSideSlopeList } from "../../api/testProject"; import { getMaintenanceCompanyList, getRouteList } from "../../api/basicInformation"; export default { name: "SideSlopeDialog", props: { visible: Boolean, // 控制弹窗显示 mode: String, // 模式:create/edit/view initialForm: Object, // 初始表单数据 }, data() { return { isInitializingSelection: false, // 初始化状态标志 dialogVisible: this.visible, // 弹窗显示状态 dialogMode: this.mode, // 当前模式 currentForm: { projectCode: '', // 项目编号 projectName: '', // 项目名称 projectStartDate: '', // 开始日期 projectEndDate: '', // 结束日期 projectUser: '', // 项目负责人 remark: '', // 备注 sideSlopeDetailList: [] // 边坡详情列表 }, projectDate: [], // 项目日期范围 total: 0, // 总数据量 loading: false, // 加载状态 pageParams: { // 分页参数 pageNo: 1, pageSize: 10, }, filterForm: { // 搜索条件 maintenanceCompanyName: "", routeCode: "", searchKey: "", evaluateLevel: "", }, mulitipleSelection: [], allSelection: new Map(), MaintenanceUnitoptions: [], // 管养单位选项 routeCodeOptions: [], // 路线编号选项 formTabledata: [], // 表格数据 evaluateLeveloptions: [], // 技术状态等级选项 rules: { // 表单验证规则 projectName: [ { required: true, message: "项目名称不能为空", trigger: "blur" }, ], projectCode: [ { required: true, message: "项目编码不能为空", trigger: "blur" }, ], }, }; }, watch: { // 修复1:监听visible变化时确保加载数据 async visible(val) { this.dialogVisible = val; if (val) { // 打开对话框时重置状态 this.resetAllData(); // 关键修复:确保在创建模式外加载数据 if (this.dialogMode !== 'create' && this.currentForm.id) { // 确保在DOM更新后加载数据 await this.$nextTick(); await this.LoadListData(); } } else { // 关闭对话框时重置数据 this.resetAllData(); } }, // 修复2:监听模式变化 mode(val) { this.dialogMode = val; // 当模式从创建变为编辑/查看时,确保加载数据 if (this.dialogVisible && val !== 'create' && this.currentForm.id) { this.LoadListData(); } }, // 修复3:监听初始表单数据变化 initialForm: { deep: true, immediate: true, // 添加立即执行 async handler(val) { this.currentForm = { ...val }; this.projectDate = [val.projectStartDate, val.projectEndDate]; // 关键修复:当初始表单有ID时立即加载数据 if (this.dialogVisible && val.id && this.dialogMode !== 'create') { await this.$nextTick(); await this.LoadListData(); } } }, // 同步弹窗显示状态到父组件 dialogVisible(val) { this.$emit("update:visible", val); }, projectDate: { deep: true, handler(value) { if (value && value.length === 2) { this.currentForm.projectStartDate = value[0]; this.currentForm.projectEndDate = value[1]; } }, }, }, async created() { // 初始化数据 this.getRouteList(); await this.getEvaluateLevel(); this.getMaintenanceCompanyList(); }, methods: { handleSelectionChange(selection) { // 跳过初始化阶段的选中状态变更 if (this.isInitializingSelection) return; // 获取当前页选中项的key集合 const currentPageKeys = new Set( selection.map(row => row.sideSlopeUniqueCode) ); // 处理当前页的取消选中操作 this.formTabledata.forEach(row => { const key = row.sideSlopeUniqueCode; // 仅当行在全局选中池中但不在当前页选中集合时删除 if (this.allSelection.has(key) && !currentPageKeys.has(key)) { this.allSelection.delete(key); } }); // 添加新选中的项到全局池 selection.forEach(row => { const key = row.sideSlopeUniqueCode; if (!this.allSelection.has(key)) { this.allSelection.set(key, row); } }); // 更新当前页选中引用 this.mulitipleSelection = selection; }, getRowkey(row) { return row.sideSlopeUniqueCode; }, // 判断行是否可选(查看模式禁用选择) isRowSelectable(row, index) { return this.dialogMode !== "view"; }, // 获取管养单位列表 async getMaintenanceCompanyList() { const res = await getMaintenanceCompanyList(); this.MaintenanceUnitoptions = res.map((item) => ({ value: item, label: item, })); }, // 获取路线列表 async getRouteList() { const res = await getRouteList(); this.routeCodeOptions = res.map((item) => ({ value: item.id, label: item.routeCode, })); }, // 搜索方法 searchForm() { // this.showSelectedOnly = false, this.pageParams.pageNo = 1; this.LoadListData(); }, // 重置搜索条件 resetSearch() { // this.showSelectedOnly = true, this.filterForm = { maintenanceCompanyName: "", routeCode: "", searchKey: "", evaluateLevel: "", }; this.pageParams.pageNo = 1; this.LoadListData(); }, // 重置组件状态 resetAllData() { this.resetSelection(); this.formTabledata = []; // 清空表格数据 this.total = 0; // 重置总条数 this.pageParams = { // 重置分页 pageNo: 1, pageSize: 10 }; // 重置搜索条件(可选) this.filterForm = { maintenanceCompanyName: "", routeCode: "", searchKey: "", evaluateLevel: "" }; }, // 修改原有方法 resetSelection() { this.allSelection.clear(); this.allSelection = new Map(); if (this.$refs.scrollTable) { this.$refs.scrollTable.clearSelection(); } }, // 映射技术状态等级 mapEvaluateLevel(level) { const option = this.evaluateLeveloptions.find( (item) => item.value === level ); return option.label; }, // 加载表格数据 async LoadListData() { this.loading = true; try { const params = { orgId: this.filterForm.maintenanceCompanyName, routeId: this.filterForm.routeCode, searchKey: this.filterForm.searchKey, evaluateLevel: this.filterForm.evaluateLevel, pageSize: this.pageParams.pageSize, pageNo: this.pageParams.pageNo, }; // 获取表格数据 const res = await getPeriodicInspectionSideSlopePageList(params); this.formTabledata = res.entities; this.total = res.entityCount; // 处理非创建模式的数据加载 if (this.dialogMode !== 'create' && this.currentForm.id) { // 首次加载时获取所有选中项 if (this.pageParams.pageNo === 1) { const selected = await getSelectedPeriodicInspectionSideSlopeList({ periodicId: this.currentForm.id, pageSize: 10000, // 获取所有选中项 pageNo: 1 }); // 重置全局选中池 this.allSelection.clear(); this.mulitipleSelection = selected.entities; // 存储全局选中状态 this.mulitipleSelection.forEach(item => { this.allSelection.set(item.sideSlopeUniqueCode, item); }); } // 设置当前页选中状态 this.isInitializingSelection = true; this.$nextTick(() => { this.formTabledata.forEach(row => { if (this.allSelection.has(row.sideSlopeUniqueCode)) { this.$refs.scrollTable.toggleRowSelection(row, true); } }); this.isInitializingSelection = false; }); } } catch (error) { console.error("加载数据失败:", error); this.$message.error("加载数据失败"); } finally { this.loading = false; } }, // 当前页码变化 handleCurrentChange(val) { this.pageParams.pageNo = val; this.LoadListData(); }, // 获取技术状态等级选项 async getEvaluateLevel() { const levelList = await mapCfg("Inspection.Regular.RegularEvaluateLevel")(); this.evaluateLeveloptions = levelList.map((item) => ({ value: item.key, label: item.value, })); }, // 提交表单 async submitForm() { this.$refs.formRef.validate(async (valid) => { if (valid) { // 验证是否选择了边坡 const selectedItems = Array.from(this.allSelection.values()); if (this.allSelection.size === 0) { this.$message.warning("请至少选择一个边坡"); return; } // 构造提交参数 const params = { ...this.currentForm, sideSlopeDetailList: selectedItems.map(item => ({ sideSlopeUniqueCode: item.sideSlopeUniqueCode, evaluateLevel: item.evaluateLevel, evaluateDate: item.evaluateDate || undefined })), }; // 根据模式选择操作 const action = this.dialogMode === "create" ? addPeriodicInspection : modifyPeriodicInspection; // 执行操作 try { const success = await action(params); if (success) { this.$message.success( this.dialogMode === "create" ? "新建成功" : "修改成功" ); this.$refs.scrollTable.clearSelection(); this.$emit("success"); this.dialogVisible = false; } else { this.$message.error("操作失败"); } } catch (error) { this.$message.error(error.message || "操作失败"); } } }); } }, }; </script> <style lang="scss" scoped> /* 修复1:弹性容器最小高度约束 */ :deep(.fixed-height-dialog), .formBorder2, .formBorder2 .el-container, .formBorder2 .el-main { min-height: 0 !important; } /* 表格行高优化 */ :deep(.el-table) { .el-table__row { height: 36px !important; td { padding: 4px 0 !important; } } .el-table__header { th { padding: 4px 0 !important; .cell { line-height: 28px !important; } } } /* 确保内部滚动 */ .el-table__body-wrapper { overflow-y: auto !important; max-height: calc(100vh - 400px) !important; } } /* 表单区域固定 */ .formBorder { position: relative; ///为伪元素提供定位上下文 border: thin dotted black !important; padding: 10px !important; margin-top: 15px !important; flex-shrink: 0 !important; height: auto !important; overflow: visible !important; margin-bottom: 15px !important; /* 边框的文字 */ &::before { content: "项目信息"; position: absolute; top: -8px; //调整到更合适的位置 left: 15px; //向右移动避免遮挡 background-color: #fff; //背景色需与页面背景一致 padding: 0 8px; font-size: 13px; color: #606266; z-index: 10; //提高层级确保显示 font-weight: 500; //加粗文字 pointer-events: none; //防止点击穿透 } } .formBorder2 { position: relative; border: thin dotted black; padding: 10px; flex: 1; min-height: 0; overflow: hidden; // 保留 display: flex; flex-direction: column; padding-top: 25px; // 新增 &::before { content: "待检边坡"; position: absolute; top: 5px; // 调整到新增的padding区域内部 left: 15px; background-color: #fff; padding: 0 8px; font-size: 13px; color: #606266; z-index: 1000; font-weight: 500; pointer-events: none; } .el-container { height: auto !important; // 覆盖行内样式 flex: 1; // 填满剩余空间 display: flex; flex-direction: column; min-height: 0; .el-header { flex-shrink: 0; height: auto !important; padding-bottom: 10px; } .el-main { flex: 1; overflow: hidden; position: relative; padding: 0; } .el-footer { flex-shrink: 0; padding-top: 10px; } } } // 弹窗底部按钮区域 .dialog-footer { padding: 10px 20px; border-top: 1px solid #ebeef5; text-align: center; } // 搜索区域样式 .searchDialog { margin-top: 5px; } // 空数据样式 :deep(.el-table__empty-block) { min-height: 200px; display: flex; justify-content: center; align-items: center; } // 分页样式 :deep(.el-pagination) { padding: 5px 0; } // 表格高度控制 :deep(.el-table) { height: 100% !important; .el-table__body-wrapper { overflow-y: auto !important; } } </style><template> <div class="project-management-block"> <!-- 弹窗组件 --> <side-slope-dialog :visible="dialogVisible" :mode="currentMode" :initial-form="currentForm" @update:visible="dialogVisible = $event" @success="handleDialogSuccess" /> <div class="project-management-block--header"> <el-form size="mini" :inline="true" class="fr ml10" align="left"> <el-input v-model="searchParams.projectName" placeholder="项目名称或编号" size="mini" clearable @clear="resetSearch" style="width: 140px;" class="mr10" /> <el-select v-model="searchParams.status" placeholder="项目状态" size="mini" clearable @clear="resetSearch" style="width: 140px;" class="mr10"> <el-option v-for="item in statusOptions" :key="item.value" :label="item.label" :value="item.value" filterable /> </el-select> <el-date-picker v-model="searchProjectDate" value-format="timestamp" size="mini" class="mr10" type="datetimerange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期"> </el-date-picker> <el-button type="primary" size="mini" style="width: 80px;" @click="handleSearch" class="mr10" >查询 </el-button> <el-button type="success" size="mini" style="width: 80px;" @click="openCreateDialog" class="mr10" >新建 </el-button> </el-form> </div> <div class="project-management-block--content" > <!-- 项目数据表格 --> <el-table :data="tableData" border style="width: 100%; flex: 1;" height="100%" > <el-table-column label="序号" type="index" width="120"> </el-table-column> <el-table-column label="项目名称" prop="projectName" width="250"> </el-table-column> <el-table-column label="项目编号" prop="projectCode" width="220"> </el-table-column> <el-table-column label="项目周期" width="250"> <template slot-scope="scope"> {{ scope.row.projectStartDate ? formatDateRange(scope.row.projectStartDate, scope.row.projectEndDate) : '' }} </template> </el-table-column> <el-table-column label="项目状态" width="150"> <template slot-scope="scope"> {{ scope.row.status ? getStatusText(scope.row.status) : '' }} </template> </el-table-column> <el-table-column label="边坡总数" prop="sideSlopeTotalCount" width="150"> </el-table-column> <el-table-column label="已完成边坡数" prop="sideSlopeCompleteCount" width="194"> </el-table-column> <el-table-column label="完成率" width="150"> <template slot-scope="scope"> {{ scope.row.sideSlopeTotalCount ? calculateCompletionRate( scope.row.sideSlopeCompleteCount, scope.row.sideSlopeTotalCount ) : '' }} </template> </el-table-column> <el-table-column label="操作" width="195"> <template slot-scope="scope"> <div v-if="scope.row.id" style="display: flex; justify-content: center;"> <el-button size="mini" type="primary" @click="openViewDialog(scope.row)" style="padding: 4px 8px; font-size: 12px" >查看 </el-button> <el-button size="mini" type="info" @click="openEditDialog(scope.row)" style="padding: 4px 8px; font-size: 12px" >编辑 </el-button> <el-button size="mini" type="danger" @click="deleteItem(scope.row)" style="padding: 4px 8px; font-size: 12px" >删除 </el-button> </div> </template> </el-table-column> </el-table> </div> <div class="project-management-block--footer"> <el-pagination background layout="total,prev, pager, next,jumper" :current-page.sync="pageParams.pageNo" :page-size="pageParams.pageSize" :total="pageParams.total"/> </div> </div> </template> <script> // 导入模块 import SideSlopeDialog from './SideSlopeDialog.vue' // 项目详情弹窗组件 import { mapCfg } from '@/utils' // 字典配置映射工具 //接口 import { getPeriodicInspectionPageList, deletePeriodicInspection } from '../../api/testProject' // 组件定义 export default { name: 'ProjectManagement', // 组件名称 components: { SideSlopeDialog // 注册弹窗组件 }, // 数据模型 data() { return { // 搜索参数对象 searchParams: { projectName: '', // 项目名称/编号搜索词 status: '', // 项目状态筛选值 projectStartDate: '', // 项目开始日期 projectEndDate: '' // 项目结束日期 }, // 日期范围选择器绑定值 searchProjectDate: [], // 项目状态下拉选项 statusOptions: [], // 表格数据源 tableData: [], // 分页参数 pageParams: { pageNo: 1, // 当前页码 pageSize: 10, // 每页条数 total: 0 // 总数据量 }, // 弹窗控制参数 dialogVisible: false, // 弹窗显示状态 currentMode: '', // 弹窗模式: 'create'/'edit'/'view' currentForm: null // 当前表单数据 } }, // 监听器 watch: { //监听日期范围选择器变化:将选择器值同步到搜索参数对象 searchProjectDate(newVal) { if (newVal && newVal.length === 2) { this.searchParams.projectStartDate = newVal[0] this.searchParams.projectEndDate = newVal[1] } else { this.searchParams.projectStartDate = '' this.searchParams.projectEndDate = '' } } }, // 生命周期钩子 async created() { // 初始化状态选项 this.getStatus() // 加载表格数据 this.loadTableData() }, // 方法/函数 methods: { // 工具方法 //根据状态值获取状态文本 getStatusText(statusValue) { const option = this.statusOptions.find((opt) => opt.value === statusValue) return option ? option.label : '' }, //格式化日期范围 formatDateRange(start, end) { if (!start || !end) return '' // 截取日期部分(去除时间) const format = (dateStr) => dateStr.substring(0, 10) return `${format(start)} 至 ${format(end)}` }, //计算完成率 calculateCompletionRate(completed, total) { if (!total || isNaN(total) || total <= 0) return '0%' return `${((completed / total) * 100).toFixed(1)}%` }, // 弹窗操作方法 //创建项目弹窗 openCreateDialog() { this.currentMode = 'create' // 重置表单数据 this.currentForm = { projectCode: '', projectName: '', projectStartDate: '', projectEndDate: '', projectUser: '', remark: '', sideSlopeDetailList: [] } this.dialogVisible = true }, //编辑项目弹窗 openEditDialog(row) { this.currentMode = 'edit' // 复制当前行数据到表单 this.currentForm = { ...row, projectStartDate: row.projectStartDate, projectEndDate: row.projectEndDate } this.dialogVisible = true }, //查看项目弹窗 openViewDialog(row) { this.currentMode = 'view' // 复制当前行数据到表单 this.currentForm = { ...row, projectStartDate: row.projectStartDate, projectEndDate: row.projectEndDate } this.dialogVisible = true }, // 弹窗操作成功回调,刷新表格数据(返回第一页) handleDialogSuccess() { this.pageParams.pageNo = 1 this.loadTableData() }, // 数据操作方法 //加载表格数据,根据搜索参数和分页设置获取项目数据 async loadTableData() { // 转换日期为时间戳格式 const startTime = this.searchParams.projectStartDate ? new Date(this.searchParams.projectStartDate).getTime() : null const endTime = this.searchParams.projectEndDate ? new Date(this.searchParams.projectEndDate).getTime() : null // 构造API参数 const params = { pageNo: this.pageParams.pageNo, pageSize: this.pageParams.pageSize, searchKey: this.searchParams.projectName, status: this.searchParams.status, startTime, endTime } try { // 调用API获取项目数据 const res = await getPeriodicInspectionPageList(params) this.tableData = res.entities || [] this.pageParams.total = res.entityCount || 0 } catch (error) { console.error('加载项目列表失败', error) this.tableData = [] this.pageParams.total = 0 } }, //搜索方法重置到第一页并重新加载数据 handleSearch() { this.pageParams.pageNo = 1 this.loadTableData() }, //重置搜索条件,清空所有搜索参数并重新加载数据 resetSearch() { this.searchParams = { projectName: '', status: '', projectStartDate: '', projectEndDate: '' } this.searchProjectDate = [] this.handleSearch() }, //删除项目 async deleteItem(row) { try { // 显示确认对话框 await this.$confirm('确定要删除该项目吗?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }) // 调用删除API await deletePeriodicInspection({ periodicId: row.id }) this.$message.success('删除成功') // 如果删除的是最后一页的最后一条,返回上一页 if (this.tableData.length === 1 && this.pageParams.pageNo > 1) { this.pageParams.pageNo -= 1 } // 重新加载数据 this.loadTableData() } catch (error) { // 用户取消删除时不显示错误 if (error !== 'cancel') { this.$message.error('删除失败') } } }, //从字典配置加载状态选项 getStatus() { try { // 获取字典配置 const dictList = mapCfg('Inspection.Periodic.PeriodicInspectionStatus')() // 转换为Element-UI需要的格式 this.statusOptions = dictList.map((item) => ({ value: item.key, label: item.value })) } catch (e) { console.error("获取状态失败", e); this.statusOptions = []; } } } } </script> <style lang="scss" scoped> .project-management-block { width: 100%; height: 100%; position: relative; background-color: #fff; &--header { height: 50px; box-sizing: border-box; padding: 10px; border-bottom: 1px solid #ebeef5; } &--content { display: block; padding: 10px; height: calc(100% - 90px); position: relative; z-index: 10; } &--footer { border-top: 1px solid #ebeef5; padding: 4px 10px 0 10px; position: relative; z-index: 11; text-align: center; } } /* 优化表格样式 */ ::v-deep .el-table { flex: 1; min-height: 0; // 防止flex布局溢出 /* 单元格样式 */ th, td { padding: 4px 0; } /* 表头样式 */ .el-table__header-wrapper { line-height: 1; th { .cell { line-height: 1.2; } } } } /* 分页组件样式优化,确保在不同缩放比例下可见 */ ::v-deep .el-pagination { padding: 4px 5px; .btn-prev, .btn-next, .el-pager li, .el-pagination__jump { min-width: 28px; height: 28px; line-height: 28px; margin: 0 2px; } .el-pagination__total, .el-pagination__jump { font-size: 12px; } } /* 响应式调整 */ @media screen and (max-width: 1200px) { .project-management-block--header .el-col { margin-bottom: 8px; } ::v-deep .el-pagination { display: flex; flex-wrap: wrap; justify-content: center; &>* { margin-bottom: 5px; } } } </style>vue.runtime.esm.js:4662 [Vue warn]: Error in callback for immediate watcher "initialForm" (Promise/async): "TypeError: Cannot read properties of null (reading 'projectStartDate')" found in ---> <SideSlopeDialog> at src/pages/conservation-management/SideSlopeDialog.vue <ProjectManagement> at src/pages/conservation-management/_ProjectManagement.vue <TabPage> at src/pages/base/components/TabPage.vue <ElTabPane> at packages/tabs/src/tab-pane.vue <ElTabs> at packages/tabs/src/tabs.vue <ElMain> at packages/main/src/main.vue <ElContainer> at packages/container/src/main.vue... (1 recursive calls) <AppMain> at src/pages/base/AppMain.vue <Backend> at src/pages/common/Backend.vue <App> at src/App.vue <Root>这是怎么回事
09-04
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值