3-19 关于避免使用边界值情况$refs

在销户项目中进行响应式操作时,监听对象本身无法触发回调函数。查阅官方文档得知需添加选项值 deep,指定 deep: true 可发现对象内部值变化,监听数组变动无需此操作。同时提醒不要被组件表象迷惑,操作的是组件内部,触发事件时做好区分。
销户项目
1.在项目里,用到使用$watch来进行响应式操作,但是发现无论如何监听一个对象本身,都触发不了回调函数,如下:
    // 监测账号选择索引列表,保证响应式赋值
    this.$watch('checkIndexListMap', val => {
      this.checkIndexListMap.cancelStockAccountList = this.$refs.stockList.checkAccountIndexList

    })

后来查阅官方文档才发现,需要加一个选项值deep才行

选项:deep

为了发现对象内部值的变化,可以在选项参数中指定 deep: true 。注意监听数组的变动不需要这么做。

vm.$watch('someObject', callback, {
  deep: true
})
vm.someObject.nestedValue = 123
// callback is fired
2.不要被组件所迷惑,实际上操作的都是组件内部。
<!-- 基金帐户 -->
    <FinanceList
      ref="foundation"
      type="fund"
      :useSelect="Account.businType == 600368"
      :accounts="Account.cancelOfstockAccountList.toJS()"
      :allChecked="allChecked"
      @foundationSelectListChanged="foundationSelectListChanged"
    />

    <!-- 证券理财 -->
    <FinanceList
      ref="secumList"
      type="secum"
      :useSelect="Account.businType == 600368"
      :accounts="Account.cancelSecumAccountList.toJS()"
      :allChecked="allChecked"
      @secumListSelectListChanged="secumListSelectListChanged"
    />

    <!-- 银行理财 -->
    <FinanceList
      ref="bankmList"
      type="bankm"
      :useSelect="Account.businType == 600368"
      :accounts="Account.cancelBankmAccountList.toJS()"
      :allChecked="allChecked"
      @bankmListSelectListChanged="bankmListSelectListChanged"
    />

上面看着像是三个相同组件的叠加,事实上是通过type进行区分的三个独立的组件’。而我们在操作组件内部触发事件时,区分即可。

     // 对外抛出选中的账号index列表
    this.$watch('checkAccountIndexList', val => {
      if(this.type=='fund'){
      this.$emit('foundationSelectListChanged', this.checkAccountIndexList)
      }else if(this.type=='secum'){
        this.$emit('secumListSelectListChanged', this.checkAccountIndexList)
      }
      else if(this.type=='bankm'){
        this.$emit('bankmListSelectListChanged', this.checkAccountIndexList)
      }
      })
获取特勤线路详细信息失败 TypeError: _this6.$refs.mineMapRef.updateRealtimeRoute is not a function at _callee3$ (index.vue?./node_modules/babel-loader/lib/index.js!./node_modules/vue-loader/lib/index.js??vue-loader-options:701:39) at tryCatch (regeneratorRuntime.js:72:16) at Generator.eval (regeneratorRuntime.js:161:17) at Generator.eval [as next] (regeneratorRuntime.js:102:21) at asyncGeneratorStep (asyncToGenerator.js:9:24) at _next (asyncToGenerator.js:28:9)deviceArray: [__ob__: Observer] vue.js:4857 [Vue warn]: Error in v-on handler: "TypeError: Cannot read properties of undefined (reading &#39;realtime&#39;)" found in ---> <SpeLineList> at src/views/components/SpeLineDetail/index.vue <Index> at src/views/largeScreen/SpecialLine/index.vue <App> at src/views/App.vue <Root> warn$2 @ vue.js:4857 logError @ vue.js:3547 globalHandleError @ vue.js:3543 handleError @ vue.js:3511 invokeWithErrorHandling @ vue.js:3527 invoker @ vue.js:1461 invokeWithErrorHandling @ vue.js:3519 Vue.$emit @ vue.js:2638 _callee2$ @ index.vue?./node_modules/babel-loader/lib/index.js!./node_modules/vue-loader/lib/index.js??vue-loader-options:360 tryCatch @ regeneratorRuntime.js:72 eval @ regeneratorRuntime.js:161 eval @ regeneratorRuntime.js:102 asyncGeneratorStep @ asyncToGenerator.js:9 _next @ asyncToGenerator.js:28 Promise.then asyncGeneratorStep @ asyncToGenerator.js:18 _next @ asyncToGenerator.js:28 eval @ asyncToGenerator.js:33 Wrapper @ export.js:20 eval @ asyncToGenerator.js:25 submitForm @ index.vue?./node_modules/babel-loader/lib/index.js!./node_modules/vue-loader/lib/index.js??vue-loader-options:373 invokeWithErrorHandling @ vue.js:3519 invoker @ vue.js:1461 original_1._wrapper @ vue.js:7516 vue.js:3551 TypeError: Cannot read properties of undefined (reading &#39;realtime&#39;) at VueComponent.updateVehiclePosition (index.vue?./node_modules/babel-loader/lib/index.js!./node_modules/vue-loader/lib/index.js??vue-loader-options:475:19) at VueComponent.handlesHeadTq (index.vue?./node_modules/babel-loader/lib/index.js!./node_modules/vue-loader/lib/index.js??vue-loader-options:677:29) at VueComponent.handlestartTq (index.vue?./node_modules/babel-loader/lib/index.js!./node_modules/vue-loader/lib/index.js??vue-loader-options:668:12) at invokeWithErrorHandling (vue.js:3519:28) at VueComponent.invoker (vue.js:1461:16) at invokeWithErrorHandling (vue.js:3519:28) at Vue.$emit (vue.js:2638:11) at _callee2$ (index.vue?./node_modules/babel-loader/lib/index.js!./node_modules/vue-loader/lib/index.js??vue-loader-options:360:24) at tryCatch (regeneratorRuntime.js:72:16) at Generator.eval (regeneratorRuntime.js:161:17) 地图组件,父组件 // 特勤路线演习 handlestartTq(type) { this.deviceArray = [] console.log(this.deviceArray, &#39;aaaaaaaaaaaa&#39;); console.log("type:", type, "deviceArray:", this.deviceArray); this.isStartTq=type this.handlesHeadTq() }, // 特勤路线演习-头车 handlesHeadTq() { console.log("deviceArray:", this.deviceArray); const dataPoints = this.deviceArray.map(item => ([ Number(item.longitude), Number(item.latitude) ])); this.$refs.mineMapRef.updateVehiclePosition(dataPoints[dataPoints.length - 1]) }, /** * @description:特勤线路详细信息 * @return {*} */ async handleTaskInfo(row) { try { const params = { id: row.id, }; const res = await getTqInfoApiBusiness(params); const dataPoints = res.data.xlData.map(item => ([ Number(item.longitude), Number(item.latitude) ])); this.$refs.mineMapRef.updateRealtimeRoute(dataPoints); this.$refs.mineMapRef.addRouteMarker(dataPoints[0], "end"); this.$refs.mineMapRef.addRouteMarker(dataPoints[dataPoints.length - 1], "start"); console.log(dataPoints, &#39;获取特勤线路详细信息成功&#39;); } catch (error) { console.log("获取特勤线路详细信息失败", error); } } //初始化MQTT initMQTT() { this.deviceArray = [] //开始链接MQTT const mqOptions = { clientId: new Date().getTime() + "", //客户端ID username: mqttUserName, //用户名 password: mqttUserPassword, //密码 keepalive: 30, //心跳时间 protocol: &#39;ws&#39;, //协议类型 reconnectPeriod: 30 * 1000, //两次重连间隔 connectTimeout: 40000, //重连超时时间 clean: true //设置为false,在离线时接收QoS 1和QoS 2消息 }; this.mqttClient = mqtt.connect(mqttAddress, mqOptions); // mqtt连接 this.mqttClient.on("connect", (e) => { this.mqttClient.subscribe(&#39;fusion-tqxl-gps&#39;, { qos: 0 }, (error) => { if (!error) { console.log(&#39;特勤路线【车载】订阅成功&#39;) } else { console.log(&#39;特勤路线【车载】订阅失败&#39;) } }) }); // 接收消息处理 this.mqttClient.on("message", (topic, message) => { const res = JSON.parse(message.toString()); // console.log("收到特勤路线【车载】00000", res) this.deviceArray.push(res) console.log("收到特勤路线【车载】", this.deviceArray) }); // 断开发起重连 this.mqttClient.on("reconnect", (error) => { console.log("特勤路线【车载】:", error); }); // 链接异常处理 this.mqttClient.on("error", (error) => { console.log("特勤路线【车载】:", error); }); },, watch: { deviceArray(newVal) { if (newVal.length > 0 && this.isStartTq) { this.handlesHeadTq(newVal) } }, },怎么修改报错,优化流程,在父组件获取到原路线先绘制 this.$refs.mineMapRef.updateRealtimeRoute(dataPoints); this.$refs.mineMapRef.addRouteMarker(dataPoints[0], "end"); this.$refs.mineMapRef.addRouteMarker(dataPoints[dataPoints.length - 1], "start");,在点击开始演习获取车辆实时位置handlestartTq,然后绘制新路线黄色,覆盖在原路线上,并且按照最新点位更新车辆图标
07-26
async createChartStep2(type, title, barData, barData2,lineData,months,yearToMonth) { const chart = echarts.init(document.getElementById("chartStep2-" + type), null, { renderer: &#39;svg&#39; });// 替换默认的 Canvas let stepTwoData = {} stepTwoData = await this.getProjectInitiationTrendChartStepTwo(title,yearToMonth) let barWidth = stepTwoData.deptNameList&&stepTwoData.deptNameList.length > 3 ? &#39;auto&#39; : &#39;15%&#39;; // 动态调整柱形图宽度 const option = { animation: true, tooltip: { trigger: &#39;axis&#39;, axisPointer: { type: &#39;none&#39; }, formatter: function (params) { // 默认圆点样式 const defaultDot = (color) => `<span style="display:inline-block;margin-right:5px;border-radius:50%;width:10px;height:10px;background-color:${color};"></span>`; // 构建 tooltip 内容 let tooltipContent = `<div style="font-size:14px;margin-bottom:10px;">${params[0].name}</div>`; params.forEach(param => { const value = param.seriesIndex === 2 ? `${param.value}%` : param.value; // 显示为百分比 tooltipContent += ` <div style="margin-bottom:5px;"> ${defaultDot(param.color)} <span style="font-size:14px;">${param.seriesName} : ${value}</span> </div> `; }); return tooltipContent; } }, legend: { data: this.legends[title], top: 0 }, grid: { top: &#39;20%&#39;, left: &#39;35px&#39;, right: &#39;4%&#39;, bottom: &#39;3%&#39;, containLabel: true }, xAxis: { type: &#39;category&#39;, data: stepTwoData.deptNameList?stepTwoData.deptNameList:[], axisLabel: { fontSize: 12, formatter: function (value) { // 将字符串拆分为单个字符,并用 \n 连接 return value.toString().split(&#39;&#39;).join(&#39;\n&#39;); } } }, yAxis: [ { type: &#39;value&#39;, name: title.includes(&#39;资金&#39;)?&#39;金额(万元)&#39;:&#39;数量&#39;, position: &#39;left&#39;, nameTextStyle: { padding: [0, 0, 5, 0] // 关键:通过内边距实现位移 }, splitLine: { show: true, // 显示背景线 lineStyle: { type: "dotted", // 设置为点线 color: "#d6d6d6", // 背景线的颜色 width: 1, // 背景线的宽度 }, }, }, { type: &#39;value&#39;, name: &#39;百分比(%)&#39;, min: 0, max: 100, position: &#39;right&#39;, nameTextStyle: { padding: [0, 0, 5, 0] // 关键:通过内边距实现位移 }, axisLabel: { formatter: &#39;{value}%&#39; }, splitLine: { show: false, // 显示背景线 lineStyle: { type: "dotted", // 设置为点线 color: "#d6d6d6", // 背景线的颜色 width: 1, // 背景线的宽度 }, }, } ], series: [ { name: this.legends[title][0], type: &#39;bar&#39;, barMaxWidth: barWidth, data: (type==&#39;zijin1&#39; || type==&#39;zijin2&#39; )?(stepTwoData.planPaymentAmountList.map(item=>{ return Math.round(item || 0) })):(stepTwoData.sumList || []), itemStyle: { color: &#39;#2563eb&#39; }, label: { normal: { show: true, position: &#39;top&#39;, distance: 15, // 上浮15像素 verticalAlign: &#39;bottom&#39;, // 文字底部对齐标签点 fontSize: 14, fontWeight: "bold", formatter: function (params, ticket, callback) { return params.value ? params.value : &#39;&#39;; } } }}, { name: this.legends[title][1], type: &#39;bar&#39;, barMaxWidth: barWidth, data: (type==&#39;zijin1&#39; || type==&#39;zijin2&#39; )?(stepTwoData.expectExpenditureAmountList.map(item=>{ return Math.round(item || 0) })):(stepTwoData.sumList || []), itemStyle: { color: &#39;#f97316&#39; }, label: { normal: { show: true, position: &#39;top&#39;, // 改为底部显示 distance: 2, // 上浮15像素 verticalAlign: &#39;bottom&#39;, // 文字底部对齐标签点 fontSize: 14, fontWeight: "bold", formatter: function (params, ticket, callback) { return params.value ? params.value : &#39;&#39;; } } }}, { name: this.legends[title][2], type: &#39;line&#39;, yAxisIndex: 1, data: stepTwoData.rateList, itemStyle: { color: &#39;#22c55e&#39; }, label: { show: true, formatter: &#39;{c}%&#39;, position: &#39;top&#39;, fontSize: 15, fontWeight: "bold", } } ] }; chart.setOption(option); let than = this; // 绑定事件前先移除旧的事件 chart.off(&#39;click&#39;); // 以 click 事件为例 chart.on("click", function (params) { let searchFieldsName = &#39;&#39; if(title == &#39;立项完成率趋势图(累加值)&#39; || title == &#39;立项完成率趋势图(当月值)&#39;){ than.showbar1Step2 = false than.showbar1Step3 = true if(title == &#39;立项完成率趋势图(累加值)&#39;) { than.showOneSetpEd = 1 searchFieldsName = &#39;search14Fields&#39; } if(title == &#39;立项完成率趋势图(当月值)&#39;) { than.showOneSetpEd = 2 searchFieldsName = &#39;search15Fields&#39; } than.stepbar1Breadcrumb = [ { title: title, step: 1, value: 1 + &#39;1&#39;, selected: false }, { title: title+&#39;部门情况&#39;, step: 2, value: 1 + &#39;2&#39;, selected: false }, { title: &#39;详情&#39;, step: 3, value: 1 + &#39;3&#39;, selected: true } ] } if(title == &#39;结项完成率趋势图(累加值)&#39; || title == &#39;结项完成率趋势图(当月值)&#39;){ than.showbar2Step2 = false than.showbar2Step3 = true if(title == &#39;结项完成率趋势图(累加值)&#39;) { than.showTwoSetpEd = 1 searchFieldsName = &#39;search16Fields&#39; } if(title == &#39;结项完成率趋势图(当月值)&#39;) { than.showTwoSetpEd = 2 searchFieldsName = &#39;search17Fields&#39; } than.stepbar2Breadcrumb = [ { title: title, step: 1, value: 1 + &#39;1&#39;, selected: false }, { title: title+&#39;部门情况&#39;, step: 2, value: 1 + &#39;2&#39;, selected: false }, { title: &#39;详情&#39;, step: 3, value: 1 + &#39;3&#39;, selected: true } ] } if(title == &#39;资金完成率趋势图(累加值)&#39;){ than.showbar3Step2 = false than.showbar3Step3 = true if(title == &#39;资金完成率趋势图(累加值)&#39;) { than.showTreeSetpEd = 1 searchFieldsName = &#39;search18Fields&#39; } if(title == &#39;资金完成率趋势图(当月值)&#39;) { than.showTreeSetpEd = 2 searchFieldsName = &#39;search23Fields&#39; } than.stepbar3Breadcrumb = [ { title: title, step: 1, value: 1 + &#39;1&#39;, selected: false }, { title: title+&#39;部门情况&#39;, step: 2, value: 1 + &#39;2&#39;, selected: false }, { title: &#39;详情&#39;, step: 3, value: 1 + &#39;3&#39;, selected: true } ] } if(title == &#39;计划外项目趋势图(累加值)&#39; || title == &#39;计划外项目趋势图(当月值)&#39;){ than.showbar4Step2 = false than.showbar4Step3 = true if(title == &#39;计划外项目趋势图(累加值)&#39;) { than.showFourSetpEd = 1 searchFieldsName = &#39;search19Fields&#39; } if(title == &#39;计划外项目趋势图(当月值)&#39;) { than.showFourSetpEd = 2 searchFieldsName = &#39;search20Fields&#39; } than.stepbar4Breadcrumb = [ { title: title, step: 1, value: 1 + &#39;1&#39;, selected: false }, { title: title+&#39;部门情况&#39;, step: 2, value: 1 + &#39;2&#39;, selected: false }, { title: &#39;详情&#39;, step: 3, value: 1 + &#39;3&#39;, selected: true } ] } if(title == &#39;项目变更趋势图(累加值)&#39; || title == &#39;项目变更趋势图(当月值)&#39;){ than.showbar5Step3 = false than.showbar5Step3 = true if(title == &#39;项目变更趋势图(累加值)&#39;) { than.showFiveSetpEd = 1 searchFieldsName = &#39;search21Fields&#39; } if(title == &#39;项目变更趋势图(当月值)&#39;) { than.showFiveSetpEd = 2 searchFieldsName = &#39;search22Fields&#39; } than.stepbar5Breadcrumb = [ { title: title, step: 1, value: 1 + &#39;1&#39;, selected: false }, { title: title+&#39;部门情况&#39;, step: 2, value: 1 + &#39;2&#39;, selected: false }, { title: &#39;详情&#39;, step: 3, value: 1 + &#39;3&#39;, selected: true } ] } setTimeout(() => { than.$nextTick(()=>{ if(than.$refs[searchFieldsName]){ than.$refs[searchFieldsName].handleQuery(&#39;init&#39;,stepTwoData.deptIdList[params.dataIndex]*1,than.legends[title][params.seriesIndex],&#39;&#39;,than.formatMonth(yearToMonth)) } }) }, 1000); }); // 监听窗口尺寸变化并自动调整图表 window.addEventListener("resize", function () { chart.resize(); }); // 使用 ResizeObserver 监听 div 大小变化 const resizeObserver = new ResizeObserver(() => { chart.resize(); // 当 div 尺寸发生变化时,重新调整 ECharts 图表大小 }); // 开始监听指定的 div 元素 resizeObserver.observe(document.getElementById("chartStep2-" + type)); }, stepTwoData.planPaymentAmountList = [ -1, 1390.6, 4, 0, 990 ] stepTwoData.expectExpenditureAmountList=[ 0, 19.7546, 0, 0, 0 ] stepTwoData.rateList= [ 0, 1.42, 0, 0, 0 ] 时 展示的百分百的折线 没有在左侧x轴的0的位置开始
08-19
<template> <!-- 日历视图 --> <div class="calendar-view"> <div class="date-content"> <a-month-picker v-model="currentMonth" :placeholder="$t(&#39;请选择月份&#39;)" valueFormat="YYYY-MM" @change="changeDate" :allowClear="false" /> </div> <div class="calendar-table"> <div class="calendar-title"> <span v-for="(day, index) in daysOfWeek" :key="index">{{ day }}</span> </div> <div class="calendar-content"> <div class="date-tr" v-for="(week, index) in calendar" :key="index"> <div :class="[&#39;date-td&#39;, {&#39;currentDay&#39;: date && currentDay === date.date},{&#39;errDay&#39;: date && errDates.includes(date.date)}]" @click="openModel(date)" v-for="date in week" :key="date.day"> <div class="top-title" v-if="date"> <span>{{ date.day }}</span> <span v-if="currentDay === date.date">{{ $t(&#39;今日&#39;) }}</span> </div> <div class="bottom-err" v-if="date && errDates.includes(date.date)"> {{ $t(&#39;异常&#39;) }}:{{ getAbnormalCount(date.date) }}</div> </div> </div> </div> </div> <!-- 异常列表 --> <error-list ref="errorList"></error-list> </div> </template> <script> import factory from &#39;../factory&#39;; import errorList from &#39;./errorList.vue&#39; export default { components: { errorList }, data () { return { daysOfWeek: [this.$t(&#39;一&#39;),this.$t(&#39;二&#39;), this.$t(&#39;三&#39;), this.$t(&#39;四&#39;), this.$t(&#39;五&#39;), this.$t(&#39;六&#39;), this.$t(&#39;日&#39;)], currentMonth: undefined, currentDay: moment().format("YYYY-MM-DD"), errDateArr: [], errDates: [], calendar: [], } }, props: { orgInfo: { type: Object, default: () => { return {} }, }, }, watch: { // 组织树code orgInfo: { handler (val) { this.getCalendar() }, deep: true, immediate: true } }, mounted () { document.querySelector(".standard-page .ant-calendar-picker-input").readOnly = true this.currentMonth = moment((new Date())).format("YYYY-MM") }, methods: { // 改变日期 changeDate (date) { let checkDate = date || undefined if (moment().format("YYYY-MM") === moment(checkDate).format("YYYY-MM")) { this.currentDay = moment().format("YYYY-MM-DD") } else { this.currentDay = "" } this.currentMonth = checkDate this.getCalendar() }, // 获取异常个数 getAbnormalCount (date) { let count = 0 if (this.errDateArr.length) { let obj = this.errDateArr.find(item => item.date === date) if (obj) count = obj.abnormalCount } return count }, // 渲染日历 async getCalendar () { let params = { orgCode: this.orgInfo.orgCode, date: this.currentMonth || moment().format("YYYY-MM"), } const today = moment(this.currentMonth) // 获取本月开始日期 const startMonth = moment(today).startOf(&#39;month&#39;); // 获取本月结束日期 const endMonth = moment(today).endOf(&#39;month&#39;); // 获取本月第一天是星期几 const startWeek = startMonth.isoWeekday(); // 获取本月总天数 const dayMonth = endMonth.date(); let calendar = []; let week = []; // 添加空白日期 for (let i = 1; i < startWeek; i++) { week.push(&#39;&#39;); } // 添加本月的日期 for (let days = 1; days <= dayMonth; days++) { // 日期 const day = moment(today).date(days).format("DD") const date = moment(today).date(days).format("YYYY-MM-DD") week.push({ day, date }); if (week.length === 7) { calendar.push(week); week = []; } } if (week.length > 0) { // 补充空字符串 for (let i = week.length; i < 7; i++) { week.push(&#39;&#39;); } calendar.push(week); } this.calendar = calendar; const res = await factory.getMeterAbnormalCalendarList(params) if (res.success) { this.errDateArr = res.data || [] this.errDates = res.data.map(item => item.date) || [] this.$forceUpdate() console.log(this.calendar, &#39; this.calendar=>>&#39;); } }, // 打开弹窗 openModel (date) { if (date && +this.getAbnormalCount(date.date) > 0) { this.$refs.errorList.showModal(date, this.orgInfo) } else { this.$message.destroy() this.$message.warning(this.$t("当前日期没有异常值")) } } }, } </script> <style lang="less" scoped> @import url(&#39;./calendarView.less&#39;); </style> 代码评审
07-17
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值