前端技巧总结---持续更新

易遗漏事件总结

关闭/保存后

  • 重置
    • 一些变量、表单
  • 考虑业务流程
  • 加载数据提示

移动端技巧总结

阿里图库

在原有的基础上加新图标

请添加图片描述

该代码替换掉原有的 iconfont.css

src 请求头要加https,要不然移动端会不显示阿里图标

onBackPress 返回上一级

onBackPress() {
  // ...自定义操作---提示(是否确认退出)
if (this.ruleForm.Id) {
    uni.navigateTo({
      // 跳转到平常页面
      url: '/pages/business/B_WayBill/B_WayBill'
    })
  } else {
    // 跳转到 tabbar 页面
    uni.switchTab({
      url: '/pages/work/work'
    })
  }
  return true
}
实现这种提示:

请添加图片描述

<!--关闭按钮显示与否-->
    <u-modal :closeOnClickOverlay="true" @close="show = false" @cancel="show = false" cancelText="继续编辑"
      confirmText="下次再编辑" :show="show" @confirm="close" :showCancelButton="true" title="还差一点就添加成功了">
    </u-modal>
// 点击左上角返回按钮触发
onBackPress() {
    this.show = true
    return true
}

close(){
    uni.navigateTo(...)
}

❗close事件不要用 navigateBack 会进入死循环

✨路径传参

uni.navigateTo({
  url: `/pages/business/B_WayBill_Add/components/cargoMessage?rowData=${JSON.stringify(this.rowData)}&cargoData=${JSON.stringify(row)}`
});
❗如果传递的参数是对象,需要用 JSON.stringify 转成字符串,后面接受数据再用 JSON.parse 转成 object

条件编辑

  • // #ifdef H5
    // #endif
    
    // #ifdef APP-PLUS
    
    <!-- #ifndef MP-WEIXIN --> ✨除了这种之外
    
    <!-- #ifdef MP-WEIXIN -->
    

页面下拉刷新事件

async onPullDownRefresh() {
  this.getData()
  // 停止刷新
  uni.stopPullDownRefresh()
}

// 同时要在 pages 里面设置

返回顶部

<!-- 返回顶部的按钮组件 -->
<u-back-top class="u-back-top" :iconStyle="{color:'#fff'}" icon="arrow-up" :scroll-top="scrollTop"></u-back-top>

// 监听返回顶部按钮的距离
onPageScroll(e) {
  this.scrollTop = e.scrollTop;
}

页面重置到顶部

uni.pageScrollTo({
    scrollTop: 0,
    duration: 0
});

搜索定位

uni.pageScrollTo({
    scrollTop:dom元素(vue里面的ref).offsetTop,
})

图片预览

uni.previewImage({
  current: "",
  urls: [e],
})

页面跳转

// 页面会刷新触发onLoad(有表单组件的话需要存起来
uni.navigateTo()  
❗url要带'/',eg:'/pages/...'

// 页面不会刷新(有表单组件
uni.navigateBack()

uniapp开发

获取this.$refs为空对象或者为undefined

解决方法

非H5端只能用于获取自定义组件,不能用于获取内置组件实例(如:view、text)

ref组件使用了条件渲染,即v-if、v-show,这些DOM节点在mounted无法被访问到

使用了 v-for 循环动态绑定ref(大部分时请求后端数据动态渲染DOM),this.$ref[name]获取的是数组

  • 用法应该为 dom[x].xx

非H5端只能用于获取自定义组件,不能用于获取内置组件实例(如:view、text) $el只支持H5端

const query = uni.createSelectorQuery().in(this)
  query.select(`#${res.Data.BillInfo.BillNo}`).boundingClientRect(data => {
    console.log(data) // 返回节点的相对坐标信息top、bottom等 
    //查询当前屏幕的滚动位置等信息
    uni.createSelectorQuery().selectViewport().scrollOffset(result => {
      // result.scrollTop
      uni.pageScrollTo({
        scrollTop: Math.abs(data.top) - 300,
        duration: 100
      });
    }).exec();
  }).exec();

熟悉业务需求

  • 完成状态下没有完成中的操作(状态
  • 完成业务后重置刷新数据
  • 初始赋值

可以使用JavaScript的reduce()方法来合并具有相同属性和值的对象

const mergedTaleData = data.tableData.reduce((acc, curr) => {
    const existingObj = acc.find(obj => obj.PickBill_Id == curr.PickBill_Id && obj.FeeName == curr.FeeName && obj.Currency == curr.Currency);
    console.log(existingObj, acc, curr);
    if (existingObj) {
      existingObj.AccTotal = xeUtils.add(existingObj.AccTotal, curr.AccTotal);
      existingObj.Acc = xeUtils.add(existingObj.Acc, curr.Acc);
    } else {
      acc.push({ ...curr, AccTotal: curr.AccTotal, Acc: curr.Acc });
    }
    return acc;
  }, []);

响应式数组置空

  • resultData.value.length = 0

有时候 disabled 的样式安卓端与ios端可能不一样

/deep/ .uni-input-input:disabled { // 安卓
    color: #666;
}

/deep/ .is-disabled { // ios
    color: #666;
}

异步操作放在actions中,actions通过commit调用mutations中方法操作state

地址转经纬度

uni.request({
  url: 'https://restapi.amap.com/v3/geocode/geo?parameters',
  method: 'GET',
  data: {
    key: '', //高德地图key
    address: '广东省湛江市' // 详细地址
  },
  success: (res) => {
    let lo = res.data.geocodes[0].location.split(',')[0]
    let la = res.data.geocodes[0].location.split(',')[1]
    console.log(lo,la);
    navigaToShop({
      latitude: lo,
      longitude: la,
      stationname: res.data.geocodes[0].formatted_address
    })
  },
  fail: (err) => {
    this.$toast(err)
  }
})

APP(IOS Android )转到高德地图、百度地图、腾讯地图

// getMap.js

  export function getCurLocation() {
    uni.getLocation({
      geocode: true,
      type: 'gcj02',
      success: (res) => {}
    })
  }

  //打开第三方地图
  export function toMapAPP(latitude, longitude, address) {
    let url = ''
    if (plus.os.name == "Android") { //判断是安卓端
      let url = "";
      plus.nativeUI.actionSheet({ //选择菜单
        title: "选择地图应用",
        cancel: "取消",
        buttons: [{
          title: "腾讯地图"
        }, {
          title: "百度地图"
        }, {
          title: "高德地图"
        }]
      }, function(e) {
        switch (e.index) {
          //下面是拼接url,不同系统以及不同地图都有不同的拼接字段
          case 1:
            //注意referer=xxx的xxx替换成你在腾讯地图开发平台申请的key
            url =
              `qqmap://map/routeplan?type=drive&from=我的位置&fromcoord=CurrentLocation&to=${address}&tocoord=${latitude},${longitude}&referer=`;
            break;
          case 2:
            url =
              "baidumap://map/direction?origin=我的位置" +
              "&destination=name:" + address +
              "|latlng:" + latitude + "," + longitude +
              "&coord_type=gcj02&mode=driving&src=andr.baidu.openAPIdemo"
            break;
          case 3:
            url =
              `amapuri://route/plan/?did=&dlat=${latitude}&dlon=${longitude}&dname=${address}&dev=0&t=0`
            break;
          default:
            break;
        }
        if (url != "") {
          url = encodeURI(url);
          //plus.runtime.openURL(url,function(e){})调起手机APP应用
          plus.runtime.openURL(url, function(e) {
            plus.nativeUI.alert("本机未安装指定的地图应用");
          });
        }
      })
    } else {
      // iOS上获取本机是否安装了百度高德地图,需要在manifest里配置
      // 在manifest.json文件app-plus->distribute->apple->urlschemewhitelist节点下添加
      //(如urlschemewhitelist:["iosamap","baidumap"])  
      plus.nativeUI.actionSheet({
        title: "选择地图应用",
        cancel: "取消",
        buttons: [{
          title: "高德地图"
        }, {
          title: "百度地图"
        }, {
          title: "腾讯地图"
        }]
      }, function(e) {
        switch (e.index) {
          case 1:
            url =
              `iosamap://path?sourceApplication=applicationName&did=&dlat=${latitude}&dlon=${longitude}&dname=${address}&dev=0&t=0`;
            break;
          case 2:
            url = "baidumap://map/direction?origin=我的位置" +
              "&destination=name:" + address +
              "|latlng:" + latitude + "," + longitude +
              "&coord_type=gcj02&mode=driving&src=ios.baidu.openAPIdemo";
            break;
          case 3:
            url =
              `qqmap://map/routeplan?type=drive&from=我的位置&fromcoord=CurrentLocation&to=${address}&tocoord=${latitude},${longitude}&referer=`;
            break;
          default:
            break;
        }
        if (url != "") {
          url = encodeURI(url);
          plus.runtime.openURL(url, function(e) {
            plus.nativeUI.alert("本机未安装指定的地图应用");
          });
        }
      })
    }
  }

uniapp某个容器滚动到底部事件

<scroll-view v-if="actionSheetCurrentItem.remote" :scroll-top="scrollTop" scroll-y="true"
    @scrolltolower="scrolltolower" style="height: 300px" @scroll="scroll">
    
</scroll-view>

scrollTop: 0,
old: {
  scrollTop: 0
}

scroll: function(e) {
    this.old.scrollTop = e.detail.scrollTop
}

// 置顶
// 解决view层不同步的问题
  this.scrollTop = this.old.scrollTop
  this.$nextTick(function() {
    this.scrollTop = 0
  });

置顶头部部分

position: fixed;
background-color: skyblue;
height: 50px;
line-height: 50px;
top: 44px;
z-index: 999;
width: 100%;
// app平台的代码
// #ifdef APP-PLUS 
position: fixed;
top: 0px;
z-index: 9999;

点击电话调起拨打功能

<a :href="'tel:' + item.VehiclenoCarManTel">{{item.VehiclenoCarManTel}}</a>

PDA(iData)

  • 注意开启以及结束的时机
  • 开启新一个要结束掉上一个
  • ✨使用插件如果要用到 this 要合理的使用箭头函数
// 一般在 onLoad 事件里面开启

onLoad(options) {
  this.getData()
  // 初始化扫描
  barcodeModel.initScan((ret) => {});
  // 获取扫描结果
  globalEvent.addEventListener('iDataBarcodeEvent', (e) => { 
    // this.searchValue = e.barcode
    if (e.barcode) this.PDAScanInoneflag(e.barcode)
    //连续扫描如果间隔时间比较短,会出现toast不提示的情况,数据可以正常接收到,建议查看控制台输出
  });
  TTSSpeech.init((status) => {
    if (status === 0) {
      this.ttsInitSuccess = true
    }
  }, 'com.iflytek.speechcloud')
}

// 结束 一般在onUnLoad 或者根据业务在 onBackPress 中
onBackPress() {
  // 结束扫描
  globalEvent.removeEventListener("iDataBarcodeEvent");
  barcodeModel.closeScan((ret) => {});
  uni.navigateTo({
    url: '/pages/business/PDA/PDA'
  })
  return true
},

语音播报

const TTSSpeech = uni.requireNativePlugin("MT-TTS-Speech");

TTSSpeech.speak({
    text
  })

关于封装组件的技巧

  • 分析需求
    • 把页面中可以复用的 ✨结构、样式以及功能✨ 单独抽离成一个
  • 具体步骤
    • Vue.component (全局)components: {}(局部) 注册组件
    • 子:props 接收数据 emits触发父的自定义事件 父::传输数据 @自定义事件
    • 组件传入模板 则定义为插槽 slot
    • 数据或者方法之类的如果需要被调用,记得暴露出来
  • 一个强大的公用组件包含的文件可分为:
    • hooks.js
      • 公共的方法、样式等
    • index.vue
      • 组件主要逻辑
    • porps.js
      • 接受传过来的数据
    • linkColumn.vue
      • 子组件.vue

记得使用数组方法

  • some
  • every
  • filter
  • find
  • reduce
  • map

APP上架到iosStore的注意事项

不用苹果电脑的Xcode↓↓↓
ipa上传工具
  • app Store 的图标不能是透明的,而且图标不能包含圆角
    请添加图片描述
APP隐私声明示例
欢迎您使用本软件(以下简称“本服务”),本软件是由xx有限公司(以下简称“本公司”)向员工提供的工作软件。为保证您的权益,便于更好地使用本软件及相应的配套服务,请您务必在注册前认真阅读本协议,若您阅读并接受本协议,使用本软件提供的产品和服务,即视为您受本协议的约束,若您不同意本协议,请勿使用本网站任何产品和服务。
    一、服务内容
    本软件运用自己的系统通过互联网络为用户提供软件交易、工程师服务等服务。用户使用本服务需要下载本软件客户端软件,同时,用户必须:
    1、自行配备上网所需设备,包括个人电脑、调制解调器或其他必备上网装置。
    2、自行负担个人上网所支付的与此服务有关的电话费用、网络费用。
    3、基于本软件所提供的服务的重要性,用户应同意:
    1)提供详尽、准确的个人资料。
    2)不断更新注册资料,符合及时、详尽、准确的要求,如果您提供的注册资料不合法、不真实的,需自行承担由此引起的责任及后果,本公司保留终止您使用本软件各项服务的权利。
    4、为防止他人冒用您的身份注册、使用本服务,企鹅外汇可能会给您的手机发送短信进行验证,由此产生的短信费用由本公司支付,您不需支付任何费用。
    二、服务的提供、修改及终止
    1、您保证在您同意接受本协议并注册成为本软件用户时,您已经年满18周岁,您是具备完全民事权利能力和完全民事行为能力的自然人。
    2、在接受本公司各项服务的同时,您同意接受本公司提供的各类信息服务,并在此授权本公司可以向您电子邮件、手机等发送商业信息。
    3、您有权选择不接受本公司提供的各类信息服务,并进入本软件相关页面进行更改。
    4、本公司可以提前3天通知,单方终止本协议。
    5、本软件有权在必要时修改服务条款,若您对本协议的修改有异议,您可以停止使用本软件的网络服务,在此情况下,本软件没有义务传送任何未处理的信息或未完成的服务给您或任何无直接关联的第三方。
    三、用户隐私制度
    1、本公司将会采取合法、合理的措施保护用户的个人信息,非经法定原因或用户事先许可,本公司不会向任何第三方透露您的密码、姓名、手机号码等非公开信息。以下情况除外:
    1)用户授权本软件透露这些信息。
    2)相关的法律法规或监管机构、司法机构要求本软件提供您的个人资料;国家司法机关符合法律规定并经法定程序的检查及其他操作。
    3)任何第三方盗用、冒用或未经许可擅自披露、使用或对外公开您的个人隐私资料。
    4)由您要求网站提供特定服务时,需要把您的姓名和地址提供给第三方的。
    5)您在使用本服务、参加网站活动或访问网站网页时,自动接收并记录的浏览器端或手机客户端数据,包括但不限于IP地址、网站Cookie中的资料及您要求取用的网页记录。
    6)本软件从商业伙伴处合法获取的个人信息。 7)我们的产品基于DCloud uni-app(5+
    App/Wap2App)开发,应用运行期间需要收集您的设备唯一识别码(IMEI/android
    ID/DEVICE_ID/IDFASIMIMSI
    信息、OAID)以提供统计分析服务,并通过应用启动数据及异常错误日志分析改进性能和用户体验,为用户提供更好的服务。
    2、在适合的情况下,并在您同意的前提下,我们会利用您的信息来联络您,为您发送信息。
    3、您应当妥善保管您的设备和密码及身份信息,对于因密码泄露、身份信息泄露、设备丢失所致的损失,由您自行承担。
    四、用户行为规范
    除非法律允许或本公司书面许可,您使用本软件过程中不得从事下列行为:
    1、删除本软件及其副本上关于著作权的信息。
    2、对本软件进行反向工程、反向汇编、反向编译,或者以其他方式尝试发现本软件的源代码。
    3、对本公司拥有知识产权的内容进行使用、出租、出借、复制、修改、链接、转载、汇编、发表、出版、建立镜像站点等。
    4、对本软件或者本软件运行过程中释放到任何终端内存中的数据、软件运行过程中客户端与服务器端的交互数据,以及本软件运行所必需的系统数据,进行复制、修改、增加、删除、挂接运行或创作任何衍生作品,形式包括但不限于使用插件、外挂或非本公司经授权的第三方工具/服务接入本软件和相关系统。
    5、通过修改或伪造软件运行中的指令、数据,增加、删减、变动软件的功能或运行效果,或者将用于上述用途的软件、方法进行运营或向公众传播,无论这些行为是否为商业目的。
    6、通过修改或伪造软件运行中的指令、数据,增加、删减、变动软件的功能或运行效果,或者将用于上述用途的软件、方法进行运营或向公众传播,无论这些行为是否为商业目的。
    7、自行或者授权他人、第三方软件对本软件及其组件、模块、数据进行干扰。
    8、其他未经本公司明示授权的行为。 五、适用法律和管辖权
    1、企鹅外汇注册所在地法律、法规应规范本协议以及本协议各方之间引起的争议,除非存在法律指定适用其他管辖法律。
    2、在合约一方寻求实现本协议下的权利,或寻求宣告本协议下的任何权利或义务的任何民事诉讼或其他诉讼过程中,各合约方应承担其代理律师的费用和支出。
    3、本协议的解释语言应为中文。
    4、本公司可能通过多种语言将本协议或任何其他文件、信息和消息提供给合作方。本协议规定,合作方应当承认及确认本公司工作语言为中文。如果任何文件、信息和消息的非中文表述和中文表述之间出现矛盾和不一致的情况下,双方将以中文文件、信息和消息为标准。
Guideline 2.1 - Information Needed
Dear Review team: After receiving your reason for rejection back, we carefully verified the interior of the application and found nothing wrong with what you said. In order to avoid unnecessary misunderstanding, we specially provide the relevant qualification certificate, and provide the following instructions: 

1. Is your app limited to users who belong to the same company? 

   This may include users of company partners, employees, and contractors. No, our app can serve employees and customers of different logistics companies. 

2. Is your app designed for a limited or specific group of companies? 

   Yes, our app is specifically for international logistics companies. 

3. What features (if any) in the application are intended for public use? 

   No. 

4. Determine which countries or territories you plan to release your application in?

   The app plans to roll out China to China. 

5. How do users get an account? 

   We also have a back office system where the administrator gives the user account. 

6.  Is there paid content in the app? For example, does the user pay to open an account or use certain features in the app? 

   The app doesn't have a paid feature. Users don't have to pay for any features, and all features are free to try. 

7. Who pays for premium content and how do users access it? 

   The App has no payment function, and users can access all content by logging into the app after obtaining an account.

Guideline 5.1.1 - Legal - Privacy - Data Collection and Storage

详细介绍申请这些权限的用处

请添加图片描述

PC端(Vue)技巧总结

element

多试试插槽,实现个性化样式
<template #title>
  <el-tag effect="dark" size="large" style="margin-right: 10px;">主单号</el-tag>
</template>
loading的多种使用方式
  • 默认loading
    • v-loading 绑定布尔值
  • 自定义loading
    • v-loading="loading"
      element-loading-text="拼命加载中"
      element-loading-spinner="el-icon-loading"
      element-loading-background="rgba(0, 0, 0, 0.8)"
      
      
  • 整页加载
分割组件 splitpanes
import { Splitpanes, Pane } from 'splitpanes';
import 'splitpanes/dist/splitpanes.css';

<splitpanes style="height: 100%">
        <pane :size="40" v-if="!rowData"></pane>
</splitpanes>

vuex 的使用

const state = {
    lang:getLang(),
}

const mutations = {
    SET_LANG(state, lang){
        state.lang = lang
        setLang(lang)
    }
}

const actions = {
    changeLang({commit}, lang){
        commit('SET_LANG', lang)
    }
}



export default {
    namespaced: true,
    state,
    mutations,
    actions,
}

import { mapState } from 'vuex'
import store from '@/store/index.js'

computed: {
    ...mapState("addOrderCargo", ["sendMessage", "recieveMessage", "productOptions", "inventoryData", "ruleForm"])
}

store.getters.inventoryData

store.commit('addOrderCargo/setproductOptions', res.data)

自定义指令

指令钩子
const myDirective = {
  // 在绑定元素的 attribute 前
  // 或事件监听器应用前调用
  created(el, binding, vnode, prevVnode) {
    // 下面会介绍各个参数的细节
  },
  // 在元素被插入到 DOM 前调用
  beforeMount(el, binding, vnode, prevVnode) {},
  // 在绑定元素的父组件
  // 及他自己的所有子节点都挂载完成后调用
  mounted(el, binding, vnode, prevVnode) {},
  // 绑定元素的父组件更新前调用
  beforeUpdate(el, binding, vnode, prevVnode) {},
  // 在绑定元素的父组件
  // 及他自己的所有子节点都更新后调用
  updated(el, binding, vnode, prevVnode) {},
  // 绑定元素的父组件卸载前调用
  beforeUnmount(el, binding, vnode, prevVnode) {},
  // 绑定元素的父组件卸载后调用
  unmounted(el, binding, vnode, prevVnode) {}
}
钩子参数
el:指令绑定到的元素。这可以用于直接操作 DOM。

binding:一个对象,包含以下属性。

value:传递给指令的值。例如在 v-my-directive="1 + 1" 中,值是 2。
oldValue:之前的值,仅在 beforeUpdate 和 updated 中可用。无论值是否更改,它都可用。
arg:传递给指令的参数 (如果有的话)。例如在 v-my-directive:foo 中,参数是 "foo"modifiers:一个包含修饰符的对象 (如果有的话)。例如在 v-my-directive.foo.bar 中,修饰符对象是 { foo: true, bar: true }。
instance:使用该指令的组件实例。
dir:指令的定义对象。
vnode:代表绑定元素的底层 VNode。

prevNode:之前的渲染中代表指令所绑定元素的 VNode。仅在 beforeUpdate 和 updated 钩子中可用。
使用示例
// VTrim.js ----局部
/**
 *全局去掉输入框前后空格指令
 */
function getInput(el){
    let inputEle
    if(el.tagName !== 'INPUT'){
        inputEle = el.querySelector('input')
    }else{
        inputEle = el
    }
    return inputEle
}

function dispathEvent(el,type){
    const evt = document.creatEvent('HTMLEvents')
    evt.initEvent(type, true, true)
    el.dispatchEvent(evt)
}

const trimDirective = {
    mounted(el){
        const inputEle = getInput(el)
        const handler = function (event) {
            const newVal = event.target.value.trim()
            if (event.target.value !== newVal) {
                event.target.value = newVal
                dispatchEvent(inputEle, 'input')
            }
        }
        
        // 回车后过滤空格(因为输入框可以回车调接口查询,所以这时候单纯的失去焦点过滤不起作用,要对回车键单独做一下处理)
        const keydown = function (event) {
            if (event.keyCode === 13) {
                const newVal = event.target.value.trim()
                if (event.target.value !== newVal) {
                    event.target.value = newVal
                    dispatchEvent(inputEle, 'input')
                }
            }
        }
        el.inputEle = inputEle
        el._blurHandler = handler
        el._keyHandler = keydown
        inputEle.addEventListener('blur', handler)
        inputEle.addEventListener('keydown', keydown)
    },
    unmounted(el) {
        const {inputEle} = el
        inputEle.removeEventListener('blur', el._blurHandler)
        inputEle.removeEventListener('keydown', el._keyHandler)
    }
}

export default trimDirective;


// 使用
import Trim from "@/directives/VTrim";
// directives节点声明
directives:{
    Trim
  },
      
// tempalte中使用 
 <el-input v-trim></el-input>   


// 全局
const getInput = elm =>elm instanceof HTMLInputElement ? elm : elm.querySelector('input')
app.directive('focus',{
    mounted(el,{arg}){
        if(arg) el.focus?.()
        else getInput(el)?.focus()
    }
})

后台管理版本更新

import axios from 'axios'

export const isNewVersion = () => {
    // 开发环境不做判断
    if (process.env.NODE_ENV === 'development') return
    // 线上环境判断版本号
    let url = `//${window.location.host}/version.json?t=${new Date().getTime()}`
    axios.get(url).then(res => {
        if (res.status === 200) {
            let vueVersion = res.data.version || '1.0.0'
            let localVueVersion = localStorage.getItem('vueVersion')
            localStorage.setItem('vueVersion', vueVersion)
            if (localVueVersion && localVueVersion != vueVersion) {
                alert('检测到新版本,请点击确认刷新页面,加载资源需要一些时间,请稍等!')
                window.location.reload(true)
                return
            }
        }
    })
}

export default {
    isNewVersion
}

Vue2

Vue2 data or props 用到另外一个数值,记得函数不能用箭头函数,会把this丢失掉

vue2 引入组件

import yhCascader from '@/components/YhCascader'

components:{}

// 全局 main.js
import yhUpload from '@/components/yh-upload/yh-upload'
watch
"sendMessage.MailingType": {
    handler(oldVal, newVal) {
      this.getSendAddress()
    },
    deep: true, // 深度监听
    immediate: true // 第一次改变就执行
  }

computed

计算后属性必须渲染后,绑定的方法才会生效这里指就是定义后的变量名在上面html中显示

vue2一个对象中的一个值是一个计算属性

怎么搞?—只有计算属性依赖项变化才回触发

请添加图片描述

vue3

defineComponent

  • vue3中,新增了 defineComponent ,它并没有实现任何的逻辑,只是把接收的 Object 直接返回,它的存在是完全让传入的整个对象获得对应的类型,可以获得自动提示。

计算属性(一个页面可以使用多个computed和多个 watch)

  • data中的一个值如果依赖另一个值记得写成计算属性,不然不会同步

组合式API

// 1
export dedfault{
    setup(){
        ...
        // 要 return 数据、函数 出去
        return {
            ...
        }
    }
}
    
// 2 script setup 语法糖   不需要在setup函数里面写代码了,不需要 return,vue3 自动返回
<script setup>

</script>

引入组件

import yhCascader from '@/components/YhCascader

export default {
  components: {
    yhCascader
  }
}

总–总结

判断数据类型

const keyType = data => {
  let type = Object.prototype.toString.call(data);

  if (type === '[object String]') {
    type = 'String';
  } else if (type === '[object Number]') {
    type = 'Number';
  } else if (type === '[object Null]') {
    type = 'Null';
  } else if (type === '[object Boolean]') {
    type = 'Boolean';
  } else if (type === '[object Array]') {
    type = 'Array';
  } else if (type === '[object Object]') {
    type = 'Object';
  } else {
    type = '未进行判断的类型:' + type;
  }

  return type;
};

对象包裹对象转换为数组

for in   +   push

filter

赋值数据

const data = res.data || []

封装

将一些通用的参数和函数抽离出来,封装成一个通用的 hook

eg:定义列表页面的分页数据
export const pageHook = () => {
    return {
        sort: '',
        order: 'desc',
        Foots: '',
        total: 0,
    }
}

对象

深拷贝
JSON.parse(JSON.Stringfy())

有key字段,但还需要value字段
  • 直接使用 . 加一个名称属性给 Object
不同属性类型共享同一个 object
对象赋值
for (let key in ruleForm) {
      ruleForm[key] = value[key]
    }

callBack 合理使用

封装组件需要注意的点

  • 显示与否
  • 传递数据另给一个 js 文件存放
    • import propsData from './props'

    • // props.js
      export default {
          leftColumns: {
              type: Array,
              default: () => []
          },
      }
      
  • 个性化–合理利用插槽
    • <slot name="rightTop"></slot>
  • 组件间传递数据
    • props emits
  • 数据更新问题
    • $nextTick

    • 定时器

      • setTimeout(()=> {
              emits('changeAfter')
            },200)
        
        
  • vue3 在 data 中 使用 compute
    • list:computed(()=>{...})
      
      
  • 使用三段式
    • let activeData = isAll ? getLeftData() : leftTableRef.value.getSelected()
      
      :type="isShowLeftList ? 'info' : 'success'" 
      
      
  • 巧用 v-model
      • props:{
            value: {
                type: Array,
                default: () => [],
              }
        }
        
      • this.$emits('input',data)
        
    • 使用组件 用 v-model

    • v-model 的值就能关联 父里面的 data值了

❗v-if 会影响到 ref 的获取

请求接口参数

  • 拼接参数
    • ?a=${b}&
  • formData

async返回值都是 promise对象

解决:

  • 分开

请添加图片描述

  • 如果是 promise 的话,用 resolve返回

✨善于利用 uniapp 的 API + Promise

  • 代码式书写,解决先后执行问题
async removeBeforeAsync(id) {
        this.show = true
        let returnBool = false
        await new Promise((resolve, reject) => {
          uni.showModal({
            title: '删除文件', //提示标题
            content: '是否删除该文件', //提示内容
            showCancel: true, //是否显示取消按钮
            success: async (res) => {
              if (res.confirm) { //confirm为ture,代表用户点击确定
                let res2 = await this.http.post('/api/B_WayBill_Files/DelWayBillFile?Id=' + id, {}, true)
                if (res2.Status) {
                  this.$toast('删除成功!')
                } else {
                  this.$toast('删除失败!', 'error')
                }
                returnBool = res2.Status
              } else if (res.cancel) { //cancel为ture,代表用户点击取消
                returnBool = false
              }
              resolve()
            }
          })
        })
        return returnBool
      },

请求接口的是否加载可以加在config里面

  • 用 axios 的请求拦截器接收
  • 请求拦截器接收之后可以再响应拦截器中拿到并判断关闭

实现左右滑动

.insure {
  display: flex;
  height: 520px;
  .left {
    width: 40%;
    // overflow: auto;
    .header {
    }
  }
  .right {
    flex: 1;
    // overflow: auto;
  }
}

  • elementUI有个滚动条样式

循环一般用来读取数据,而不要改变响应式数据

根据这个思路,可以把methods或者computed计算完的数据放置于数组中,再在响应式内直接读取数组元素

APP(andriod,ios)热更新

代码:https://blog.csdn.net/itopit/article/details/124620871

借鉴:https://blog.csdn.net/m_xiaozhilei/article/details/126485684

打包的wgt版本要与后端记录的一样,线下打包的可以搞成第一点版本的去测试

请添加图片描述

// APP.vue
onLauch(){
    //检查更新
    // #ifdef APP-PLUS
    plus.runtime.getProperty(plus.runtime.appid, function(widgetInfo) {
        uni.setStorageSync("curversion", widgetInfo.version);
        let curversion = widgetInfo.version;
        console.log("当前版本", curversion);
        hotupdate.getAppByVersion(curversion, true);
      });
     // #endif
}

// hotupdate.js
import http from './http.js'

function update(fileName) {
  let downURL = fileName;
  console.log('下载地址', downURL);
  //后台显示进度条
  let dtask = plus.downloader.createDownload(
    downURL, {},
    function(d, status) {
      //下载完成
      install(d, status);
      // uni.showModal({
      //   title: "版本更新",
      //   content: "新版本已经准备好,是否更新应用?",
      //   success: (res) => {
      //     if (res.confirm) {
      //       install(d, status);
      //     }
      //   },
      // });
    }
  );
  start(dtask);
};

function start(dtask) {
  try {
    dtask.start(); //开启下载任务
    let prg = 0;
    let showLoading = plus.nativeUI.showWaiting("正在下载");
    dtask.addEventListener("statechanged", function(task, status) {
      switch (task.state) {
        case 1:
          showLoading.setTitle("正在下载");
          break;
        case 2:
          showLoading.setTitle("已连接到服务器");
          break;
        case 3:
          prg = parseInt(
            (parseFloat(task.downloadedSize) /
              parseFloat(task.totalSize)) *
            100
          );
          showLoading.setTitle("  正在下载" + prg + "%  ");
          break;
        case 4:
          plus.nativeUI.closeWaiting();
          break;
      }
    });

  } catch (err) {
    plus.nativeUI.closeWaiting();
    uni.showToast({
      title: "更新失败-03",
      mask: false,
      duration: 1500,
    });
  }
};

function install(d, status) {
  //下载完成
  if (status === 200) {
    plus.runtime.install(d.filename, {
      force: false
    }, function() {
      plus.nativeUI.closeWaiting();
      plus.runtime.restart();
      // plus.nativeUI.alert("应用资源更新完成!是否重启应用!", function() {
      //   plus.runtime.restart();
      // });
    }, function(e) {
      plus.nativeUI.closeWaiting();
      console.log("安装wgt文件失败[" + e.code + "]:" + e.message);
      plus.nativeUI.alert("安装wgt文件失败[" + e.code + "]:" + e.message);
    });
  } else {
    uni.showToast({
      title: "下载失败",
      mask: false,
      duration: 1500,
    });
  }
};

function close(version) {
  try {
    console.log('保存要跳过的版本', version)
    uni.setStorageSync('skip_version', version);
  } catch (e) {
    // error
  }
};

function signOut() {
  // #ifdef APP-PLUS
  plus.runtime.quit();
  // #endif
}

export default {
  getAppByVersion(curversion, back) {
    console.log("came in");
    let params = {};
    if (uni.getSystemInfoSync().platform == "ios") {
      params = {
        app: "kdapp",
        type: "ios",
        appversion: curversion,
      };
    } else {
      params = {
        app: "kdapp",
        type: "android",
        appversion: curversion,
      };
    }
    // console.log(curversion);
    http.post(`/api/B_App_Ver_Record/GetNewAppVersion?VersionName=${curversion}`).then((res) => {
      console.log('version res');
      console.log(res);
      if (res.status && res.data[0]) {
        let result = res.data[0];
        if (back) {
          try {
            const value = uni.getStorageSync('skip_version');
            console.log('是否跳过当前版本', value, result.versionName)
            if (value && value === result.versionName) {
              return;
            }
          } catch (e) {}
        }
        if (result.versionName != curversion) {
          // console.log('有新版本要更新', result);
          // console.log('新版本', result.versionName);
          // console.log('目前版本', curversion);
          result.back = back;
          // if (uni.getSystemInfoSync().platform != "ios") {
            plus.nativeUI.confirm(result.verDescription || "系统提示", function(e) {
              if (e.index == 1) {
                console.log('update', result.filePath);
                update(http.fileAddress + result.filePath);
              } else if (result.forceUpdate == 1) {
                signOut();
              } else if (result.forceUpdate == 0) {
                close(result.versionName);
              }
            }, {
              "title": "版本更新",
              "buttons": [result.forceUpdate == 1 ? "退出应用" : "暂不更新", "立即更新"]
            });
          // } else {
          //   uni.showModal({
          //     title: "版本更新",
          //     content: "请点击按钮复制链接,粘贴到Safari浏览器打开,下载最新版本",
          //     confirmText: "点击复制",
          //     showCancel: false,
          //     success: () => {
          //       uni.setClipboardData({
          //         data: http.fileAddress + result.filePath,
          //         success: () => {
          //           uni.showToast({
          //             title: "复制链接成功,请粘贴到Safari浏览器打开"
          //           })
          //         }
          //       })
          //     }
          //   });
          // }
        } else if (!back) {
          //plus.nativeUI.alert("当前已是最新版本!");
        }
      } else if (!res.status) 
        console.log('error');
        uni.showToast({
          title: res.msg,
          icon: 'none', //如果要纯文本,不要icon,将值设为'none'
          duration: 3000 //持续时间为 2秒
        });
      } else if (!back) {
        //plus.nativeUI.alert("当前已是最新版本!");
      }
    })
  }
}

高亮与不高亮基本大小要一样才不会变来边去

请添加图片描述

请求参数如果没有就不添加:

let params = {}
    if (this.keyword2) {
      params["billNo"]=this.keyword2
    }
    if (this.startTime2 && this.endTime2) {
      params["beginTime"]=this.startTime2
      params["endTime"]=this.endTime2
    }

uniapp select被table遮挡

.uni-table-scroll{
    overflow-x: unset;
  }

this.$set解决新增的对象属性没有响应式(computed无效)

this.$set(this.rowData,'coupon',newVal * num || 0)// 优惠金额

图片

请添加图片描述

uniapp展示本地图片使用image>标签都可以展示图片。但是<img>标签在网页端和真机模拟都不可以展示图片。

路径直接为 /static/… 不需要相对,不需要@符号。直接从/static目录往下写就行了

数组

新增元素
  • arr.splice(1,0,xx)
  • arr.push(xx)
  • arr.unshfit(xx)

在使用 <script setup> 的单文件组件中,导入的组件可以直接在模板中使用,无需注册

掉起拨打电话接口

<a :href="'tel:' + item.VehiclenoCarManTel">{{item.VehiclenoCarManTel}}</a>

devtools–给你的vue3项目的main.js加上这一句

app.config.devtools = true

vue 循环一定要弄key

  • 要不然顺序变化时会出现错误

js 计算精度问题

自己写公共方法:
/**

   ** 加

   **/

  add(arg1, arg2) {var r1, r2, m, c;try {

​      r1 = arg1.toString().split(".")[1].length;} catch (e) {

​      r1 = 0;}try {

​      r2 = arg2.toString().split(".")[1].length;} catch (e) {

​      r2 = 0;}

​    c = Math.abs(r1 - r2);

​    m = Math.pow(10, Math.max(r1, r2));if (c > 0) {var cm = Math.pow(10, c);if (r1 > r2) {

​        arg1 = Number(arg1.toString().replace(".", ""));

​        arg2 = Number(arg2.toString().replace(".", "")) * cm;} else {

​        arg1 = Number(arg1.toString().replace(".", "")) * cm;

​        arg2 = Number(arg2.toString().replace(".", ""));}} else {

​      arg1 = Number(arg1.toString().replace(".", ""));

​      arg2 = Number(arg2.toString().replace(".", ""));}return (arg1 + arg2) / m;

  },

  /**

   ** 减

   **/

  sub(arg1, arg2) {var r1, r2, m, n;try {

​      r1 = arg1.toString().split(".")[1].length;} catch (e) {

​      r1 = 0;}try {

​      r2 = arg2.toString().split(".")[1].length;} catch (e) {

​      r2 = 0;}

​    m = Math.pow(10, Math.max(r1, r2)); //last modify by deeka //动态控制精度长度

​    n = r1 >= r2 ? r1 : r2;return Number(((arg1 * m - arg2 * m) / m).toFixed(n));

  },

  /**

   ** 乘

   **/

  mul(arg1, arg2) {var m = 0,

​      s1 = arg1.toString(),

​      s2 = arg2.toString();try {

​      m += s1.split(".")[1].length;} catch (e) {}try {

​      m += s2.split(".")[1].length;} catch (e) {}return (Number(s1.replace(".", "")) * Number(s2.replace(".", ""))) / Math.pow(10, m);

  },

  /**

   ** 除

   **/

  div(arg1, arg2) {var t1 = 0,

​      t2 = 0,

​      r1,

​      r2;try {

​      t1 = arg1.toString().split(".")[1].length;} catch (e) {}try {

​      t2 = arg2.toString().split(".")[1].length;} catch (e) {}

​    r1 = Number(arg1.toString().replace(".", ""));

​    r2 = Number(arg2.toString().replace(".", ""));return (r1 / r2) * Math.pow(10, t2 - t1);

  },
或者xe-utils 解决 js 加减乘除精度问题

善于使用对象处理(存储)多数据

let obj = {}
obj[key] = xx

❗一定一定要记得复杂数据类型的特殊性,需求如果是不改变原数据一定要深拷贝

// 一般使用 JSON.stringify 进行拷贝---在获取的时候用JSON.parse来解构就行了

在用store存储、作为参数传递过去、赋值等等操作一定要注意

❗❗❗removeEventListener

addEventListener 之后一定要记得 removeEventListener
  • 取消对一个全局事件的监听。

插件的结束时期

数据传输 传过去=》再传回来(用 uni.navigateTo)

插件使用

❗❗❗回调函数里面如果需要用到 this 记得该回调函数一定得写成箭头函数

存储

要记得 JSON.stringfy 再存储

uni.setStorageSync(app_user_timestamp, JSON.stringify(Date.now())) //设置时间戳

uniapp FormData 形式的参数得更改请求头

if (isFormData) _header['Content-Type'] = 'application/x-www-form-urlencoded'

// 传递的参数是对象

对象

对象一整个赋值会改不到数据,要用 . 来更改属性值

搜索定位功能

uni.pageScrollTo({
    scrollTop: this.$refs[res.Data.BillInfo.BillNo][0].$el.offsetTop-310,
    duration: 0
  });

v-for涉及到增删改查时一定好设置一个唯一的id❗

公共的变量/函数/组件写在hooks.js里面

使多个地方可用到,复用性极强
请添加图片描述

一整个对象赋值改不到原始的对象,而是直接改了对象的数组

  • 要是想改掉原来对象的属性值,则需要:

  • for (let key in this.bjFormFields1) {
          this.bjFormFields1[key] = this.rowData[key]
        }
    

请求接口报错时,看看是不是多了个id的属性,这样有时会报错

❗computed要实现一定要在script里面用或者在视图里面用,不要然会不起作用!!!

vue2对象和数组变化一定要符合(数组:增删改查,对象:.xx = )规则才能有响应式,才可以正常进行计算以及watch

涉及到 ref 一定要注意有无 v-if(初始没有渲染dom)

样式

请添加图片描述

.out{
    background-image: linear-gradient(135deg, #b78666ff 16%, #ffe6d5ff 10%, #fff);
    background-size: 80% 100%;
    background-repeat: no-repeat;
    padding: 6px;

    .in {
      border-left: 4px solid #fff;
      padding-left: 8px;
      color: #fff;
    }
  }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wendyymei

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值