• vue学习(6)自定义指令详解及常见自定义指令


    1、钩子函数

    bind : 只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。(常用)

    inserted : 被绑定元素插入父节点时调用(保证父节点存在,但不一定已被插入文档中)。(常用)

    update : 所在组件的VNode更新时调用,但是可能发生在其子VNode更新之前。 指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新(详细的钩子函数参数下见)。(基本没咋用)

    componentUpdated : 指令所在组件的VNode 及其子 VNode 全部更新后调用。(基本没咋用)

    unbind : 只调用一次,指令与元素解绑时调用。(常用)

    2、钩子函数参数

    el : 指令所绑定的元素,可以用来直接操作DOM。(常用)

    binding : 一个对象,包含以下属性:(常用)

    1. name : 指令名,不包括 v- 前缀。
    2. value : 指令表达式的最终返回结果
    3. oldValue :指令表达式的最终返回结果前一个值,仅在 update 和 componentUpdated 钩子中可用。
    4. expression :表达式。
    5. arg :传给指令的参数,可选。例如 v-copy:foo 中,参数为 "foo"
    6. modifiers :一个包含修饰符的对象。例如:v-copy:foo.a 中的a

    vnode :Vue 编译生成的虚拟节点。(基本没咋用)

    oldVnode :上一个虚拟节点,仅在 update和 componentUpdated 钩子中可用。(基本没咋用)

     

    3、Vue插件机制 install

    1. // directive.js
    2. import directive0 from './directive0'
    3. import directive1 from './directive1'
    4. import directive2 from './directive2'
    5. const install = (Vue) => {
    6. Vue.directive('directive0', copy)
    7. Vue.directive('directive1', copy)
    8. Vue.directive('directive2', copy)
    9. }
    10. export default {
    11. install
    12. }
    13. // main.js
    14. import directive from './directive'
    15. Vue.use(directive)

    4、常见自定义指令

    1、复制输入框的值 v-copy(已实测可行

    调用:

    1. <script>
    2. export default {
    3. data () {
    4. return {
    5. url: 'xxx'
    6. }
    7. }
    8. }
    9. script>

    代码:

    1. const copy = {
    2. /**
    3. * 初始化
    4. * @param {DOM} el 指令所绑定的元素DOM节点
    5. * @param {*} value 指令的绑定值 即 v-copy="value" 的value值
    6. */
    7. bind(el, { value }) {
    8. // 给元素赋值一个$value值,即指令绑定的值
    9. el.$value = value
    10. el.handler = () => {
    11. // 如果可复制的值为空的时候,给出提示;
    12. if (!el.$value) {
    13. console.log('无复制内容')
    14. return
    15. }
    16. // 动态创建 textarea 标签
    17. const textarea = document.createElement('textarea');
    18. // 将该 textarea 设为 readonly 防止 IOS 下自动唤起键盘,同时将 textarea 移除可视区域
    19. textarea.readOnly = 'readonly';
    20. textarea.style.position = 'absolute';
    21. textarea.style.left = '-9999px';
    22. // 将要copy的值赋值给textarea 标签的value属性
    23. textarea.value = el.$value;
    24. // 将textarea 插入到body中
    25. document.body.appendChild(textarea);
    26. // 选中值并复制
    27. textarea.select()
    28. const result = document.execCommand('Copy');
    29. if (result) {
    30. console.log('复制成功');
    31. }
    32. document.body.removeChild(textarea);
    33. }
    34. // 绑定点击事件,点击的时候copy值
    35. el.addEventListener('click', el.handler);
    36. },
    37. // 当传递进来的值更新的时候触发
    38. componentUpdated(el, { value }) {
    39. el.$value = value;
    40. },
    41. // 指令与元素解绑的时候,移除事件绑定
    42. unbind(el) {
    43. el.removeEventListener('click', el.handler);
    44. }
    45. }
    46. export default copy;

    2、长按屏幕 v-longpress已实测可行

    调用:直接v-longpress则默认长按1秒执行函数,当然也可以自己传参,单位为毫秒

    1. <script>
    2. export default {
    3. methods: {
    4. longpress() {
    5. // xxx
    6. }
    7. }
    8. }
    9. script>

     代码:

    1. /**
    2. * 1. 创建一个计时器,n s 后执行函数
    3. * 2. 当用户按下按钮时触发 mousedown 事件,启动计时器;
    4. * 用户松开按钮时调用 mouseout 事件。
    5. * 3. 如果 mouseup 事件 n s内被触发,就清除计时器,当作一个普通的点击事件。
    6. * 4. 如果 计时器没有在n s内清除,则判定为移除长按,可以执行关联的函数。
    7. * 5. 在移动端要考虑 touchstart, touchend 事件。
    8. */
    9. const longpress = {
    10. bind (el, binding) {
    11. if (typeof binding.value !== 'function') {
    12. throw 'callback must be a function'
    13. }
    14. const time = binding.arg || 1000 // n秒后执行,默认1秒
    15. let pressTimer = null
    16. // 创建计时器(1s后执行函数)
    17. let start = e => {
    18. if (e.type === 'click' && e.button !== 0) return
    19. if (pressTimer === null) {
    20. pressTimer = setTimeout(() => {
    21. handler()
    22. }, time)
    23. }
    24. }
    25. // 取消计时器
    26. let cancel = e => {
    27. if (pressTimer !== null) {
    28. clearTimeout(pressTimer)
    29. pressTimer = null
    30. }
    31. }
    32. // 运行函数
    33. const handler = e => {
    34. binding.value()
    35. }
    36. // 添加事件监听器
    37. el.addEventListener('mousedown', start)
    38. el.addEventListener('touchstart', start)
    39. // 取消计时器
    40. el.addEventListener('click', cancel)
    41. el.addEventListener('mouseout', cancel)
    42. el.addEventListener('touchend', cancel)
    43. el.addEventListener('touchcancel', cancel)
    44. },
    45. // 当传递进来的值更新的时候触发
    46. componentUpdated(el, { value }) {
    47. el.$value = value
    48. },
    49. // 指令与元素解绑的时候,移除事件绑定
    50. unbind(el) {
    51. el.removeEventListener('click', el.handler)
    52. }
    53. }
    54. export default longpress

    3、 防抖 v-debounce 已实测可行

    调用:分为立即/非立即防抖,传参方式不同

    1. <script>export default {
    2. methods: {
    3. debounce() {
    4. // xxx
    5. }
    6. }
    7. }
    8. script>

    代码:

    1. // 在指定的时间段内,多次点击只会执行一次
    2. const debounce = {
    3. inserted(el ,binding) {
    4. let timer
    5. el.addEventListener('click', () => {
    6. if (timer) {
    7. clearTimeout(timer)
    8. }
    9. // 传值为 v-debounce="{ event: func, delay: 500, immediate: true}"
    10. if (typeof binding.value !== 'function') {
    11. const { event, delay = 500, immediate = false } = binding.value
    12. if (immediate) {
    13. let now = !timer
    14. timer = setTimeout(() => {
    15. timer = null
    16. }, delay)
    17. now && event()
    18. } else {
    19. timer = setTimeout(() => {
    20. timer = null
    21. event()
    22. }, delay)
    23. }
    24. } else {
    25. // 传值为 v-debounce="func"
    26. timer = setTimeout(() => {
    27. binding.value() // 延迟执行回调方法
    28. }, 1000)
    29. }
    30. })
    31. }
    32. }
    33. export default debounce

    4、 禁止输入框发表情及特殊字符 v-emoji目前暂不知道怎么用,无法测试

    调用:

    1. <script>
    2. export default {
    3. data () {
    4. return {
    5. name: ''
    6. }
    7. }
    8. }
    9. script>

    代码:

    1. let findEle = (parent, type) => {
    2. return parent.tagName.toLowerCase() === type ? parent : parent.querySelector(type)
    3. }
    4. const trigger = (el, type) => {
    5. // 创建一个指定类型的事件
    6. const e = document.createEvent('HTMLEvents')
    7. // 定义事件名为 type
    8. e.initEvent(type, true, true)
    9. // 触发对象可以是任何元素或其他事件目标
    10. el.dispatchEvent(e)
    11. }
    12. const emoji = {
    13. bind: function (el, binding) {
    14. // 正则规则可根据需求自定义
    15. var regRule = /[^\u4E00-\u9FA5|\d|\a-zA-Z|\r\n\s,.?!,。?!…—&$=()-+/*{}[\]]|\s/g
    16. let $inp = findEle(el, 'input')
    17. el.$inp = $inp
    18. $inp.handle = function () {
    19. let val = $inp.value
    20. $inp.value = val.replace(regRule, '')
    21. trigger($inp, 'input')
    22. }
    23. $inp.addEventListener('keyup', $inp.handle)
    24. },
    25. unbind: function (el) {
    26. el.$inp.removeEventListener('keyup', el.$inp.handle)
    27. },
    28. }
    29. export default emoji

    5、页面水印 v-waterMarker已实测可行

    调用:

     代码:

    1. /**
    2. * @param {*} name 文字
    3. * @param {*} width 文字宽度
    4. * @param {*} height 文字高度
    5. * @param {*} color 文字颜色
    6. */
    7. const waterMarker = {
    8. bind(el, binding) {
    9. const { name, width, height, color } = binding.value
    10. var can = document.createElement('canvas')
    11. el.appendChild(can)
    12. can.width = width
    13. can.height = height
    14. can.style.display = 'none'
    15. var cans = can.getContext('2d')
    16. cans.rotate((-20 * Math.PI) / 180)
    17. cans.fillStyle = color || 'rgba(180, 180, 180, 0.3)'
    18. cans.textAlign = 'left'
    19. cans.textBaseline = 'Middle'
    20. cans.fillText(name, can.width / 10, can.height / 2)
    21. el.style.backgroundImage = 'url(' + can.toDataURL('image/png') + ')'
    22. }
    23. }
    24. export default waterMarker

    6、拖拽 v-draggable已实测可行

    调用:针对于el-dialog的拖动

    代码:

    1. const draggable = {
    2. bind(el, binding, vnode) {
    3. const dialogHeaderEl = el.querySelector('.el-dialog__header') // 点击能拖动的地方
    4. const dragDom = el.querySelector('.el-dialog') // 被拖动的dom
    5. dialogHeaderEl.style.cssText += ';cursor:move;'
    6. dragDom.style.cssText += ';top:0px;'
    7. // 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
    8. const getStyle = (function() {
    9. if (window.document.currentStyle) {
    10. return (dom, attr) => dom.currentStyle[attr]
    11. } else {
    12. return (dom, attr) => getComputedStyle(dom, false)[attr]
    13. }
    14. })()
    15. dialogHeaderEl.onmousedown = (e) => {
    16. // 鼠标按下,计算当前元素距离可视区的距离
    17. const disX = e.clientX - dialogHeaderEl.offsetLeft
    18. const disY = e.clientY - dialogHeaderEl.offsetTop
    19. const dragDomWidth = dragDom.offsetWidth
    20. const dragDomHeight = dragDom.offsetHeight
    21. const screenWidth = document.body.clientWidth
    22. const screenHeight = document.body.clientHeight
    23. const minDragDomLeft = dragDom.offsetLeft
    24. const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth
    25. const minDragDomTop = dragDom.offsetTop
    26. const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomHeight
    27. // 获取到的值带px 正则匹配替换
    28. let styL = getStyle(dragDom, 'left')
    29. let styT = getStyle(dragDom, 'top')
    30. if (styL.includes('%')) {
    31. styL = +document.body.clientWidth * (+styL.replace(/\%/g, '') / 100)
    32. styT = +document.body.clientHeight * (+styT.replace(/\%/g, '') / 100)
    33. } else {
    34. styL = +styL.replace(/\px/g, '')
    35. styT = +styT.replace(/\px/g, '')
    36. }
    37. document.onmousemove = function(e) {
    38. // 通过事件委托,计算移动的距离
    39. let left = e.clientX - disX
    40. let top = e.clientY - disY
    41. // 边界处理
    42. if (-(left) > minDragDomLeft) {
    43. left = -minDragDomLeft
    44. } else if (left > maxDragDomLeft) {
    45. left = maxDragDomLeft
    46. }
    47. if (-(top) > minDragDomTop) {
    48. top = -minDragDomTop
    49. } else if (top > maxDragDomTop) {
    50. top = maxDragDomTop
    51. }
    52. // 移动当前元素
    53. dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;`
    54. }
    55. document.onmouseup = function(e) {
    56. document.onmousemove = null
    57. document.onmouseup = null
    58. }
    59. }
    60. }
    61. }
    62. export default draggable

     7、权限控制 v-permission已实测可行

    调用:其实传什么值 和 如何验证都是你可控制的,由你自己逻辑来定

    代码:

    1. /**
    2. * 实现你有无权限的地方,返回true则展示,返回false则隐藏,下面的1234则是举例子
    3. * @param {*} key
    4. * @returns
    5. */
    6. function checkPermission(key) {
    7. let arr = [1,2,3,4]
    8. return arr.indexOf(key) > -1
    9. }
    10. const permission = {
    11. inserted: function (el, binding) {
    12. let permission = binding.value // 获取到 v-permission的值
    13. if (permission) {
    14. let hasPermission = checkPermission(permission)
    15. if (!hasPermission) {
    16. // 没有权限 移除Dom元素
    17. el.parentNode && el.parentNode.removeChild(el)
    18. }
    19. }
    20. },
    21. }
    22. export default permission

    8、 超出宽度隐藏显示文本 v-overflow-tooltip(不推荐使用

    调用:(我个人觉得没啥必要用这个,你自己写个css样式就可以了)

    代码:

    1. /**
    2. * 超出设置宽度显示文字提示指令
    3. * 用法:v-overflow-tooltip / v-overflow-tooltip:width
    4. * width 可选
    5. * 只要当dom元素内容超出设置的宽度时,超出文字省略号显示,鼠标画上去有全部文字提示
    6. */
    7. export default {
    8. name: 'overflow-tooltip',
    9. bind (el, binding) {
    10. const width = binding.arg
    11. if (width) {
    12. el.style.width = `${width}px`
    13. }
    14. const style = {
    15. whiteSpace: 'nowrap',
    16. overflow: 'hidden',
    17. textOverflow: 'ellipsis'
    18. }
    19. setStyle(el, style)
    20. },
    21. inserted (el, binding) {
    22. addTooltip(el, binding)
    23. },
    24. unbind (el) {
    25. if (!el.tooltip) return
    26. el.removeEventListener('mouseenter', el.elMouseEnterHandler)
    27. el.removeEventListener('mouseleave', el.elMouseOutHandler)
    28. el.tooltip.destroy()
    29. }
    30. }
    31. function addTooltip (el, binding) {
    32. el.oldOffsetWidth = el.offsetWidth
    33. if (!el.textWidth) {
    34. // 计算文本宽度
    35. const range = document.createRange()
    36. range.setStart(el, 0)
    37. range.setEnd(el, el.childNodes.length)
    38. const rangeWidth = range.getBoundingClientRect().width
    39. const padding = (parseInt(getStyle(el, 'paddingLeft'), 10) || 0) +
    40. (parseInt(getStyle(el, 'paddingRight'), 10) || 0)
    41. const textWidth = rangeWidth + padding
    42. el.textWidth = textWidth
    43. }
    44. // 监听元素宽度变化
    45. const resizeObserver = new ResizeObserver(entry => {
    46. const target = entry[0].target
    47. el.oldOffsetWidth !== target.offsetWidth && addTooltip(el, binding)
    48. })
    49. resizeObserver.observe(el)
    50. // Math.max(el.offsetWidth, binding.arg) 处理offsetWidth不是设置宽度时的情况
    51. if (el.textWidth > Math.max(el.offsetWidth, binding.arg || 0)) {
    52. let tooltip = null
    53. const elMouseEnterHandler = el.elMouseEnterHandler = debounce((event) => {
    54. if (!tooltip) {
    55. const tooltipContent = el.innerText || el.textContent
    56. tooltip = new Tooltip()
    57. tooltip.create(tooltipContent)
    58. el.tooltip = tooltip
    59. }
    60. // 400为tootip最大宽度
    61. tooltip.show(event, Math.min(el.textWidth, 400))
    62. }, 300)
    63. const elMouseOutHandler = el.elMouseOutHandler = debounce(() => {
    64. tooltip && tooltip.hide()
    65. }, 300)
    66. el.addEventListener('mouseenter', elMouseEnterHandler)
    67. el.addEventListener('mouseleave', elMouseOutHandler)
    68. } else {
    69. el.tooltip && el.tooltip.destroy()
    70. el.elMouseEnterHandler && el.removeEventListener('mouseenter', el.elMouseEnterHandler)
    71. el.elMouseOutHandler && el.removeEventListener('mouseleave', el.elMouseOutHandler)
    72. }
    73. }
    74. function debounce(fn, delay = 500) {
    75. let timer
    76. return function() {
    77. const th = this
    78. const args = arguments
    79. if (timer) {
    80. clearTimeout(timer)
    81. }
    82. timer = setTimeout(function() {
    83. timer = null
    84. fn.apply(th, args)
    85. }, delay)
    86. }
    87. }
    88. const SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g
    89. const MOZ_HACK_REGEXP = /^moz([A-Z])/
    90. const ieVersion = Number(document.documentMode)
    91. const camelCase = function(name) {
    92. return name.replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
    93. return offset ? letter.toUpperCase() : letter
    94. }).replace(MOZ_HACK_REGEXP, 'Moz$1')
    95. }
    96. const getStyle = ieVersion < 9 ? function(element, styleName) {
    97. if (!element || !styleName) return null
    98. styleName = camelCase(styleName)
    99. if (styleName === 'float') {
    100. styleName = 'styleFloat'
    101. }
    102. try {
    103. switch (styleName) {
    104. case 'opacity':
    105. try {
    106. return element.filters.item('alpha').opacity / 100
    107. } catch (e) {
    108. return 1.0
    109. }
    110. default:
    111. return (element.style[styleName] || element.currentStyle ? element.currentStyle[styleName] : null)
    112. }
    113. } catch (e) {
    114. return element.style[styleName]
    115. }
    116. } : function(element, styleName) {
    117. if (!element || !styleName) return null
    118. styleName = camelCase(styleName)
    119. if (styleName === 'float') {
    120. styleName = 'cssFloat'
    121. }
    122. try {
    123. var computed = document.defaultView.getComputedStyle(element, '')
    124. return element.style[styleName] || computed ? computed[styleName] : null
    125. } catch (e) {
    126. return element.style[styleName]
    127. }
    128. }
    129. function setStyle(element, styleName, value) {
    130. if (!element || !styleName) return
    131. if (typeof styleName === 'object') {
    132. for (const prop in styleName) {
    133. if (styleName.hasOwnProperty(prop)) {
    134. setStyle(element, prop, styleName[prop])
    135. }
    136. }
    137. } else {
    138. styleName = camelCase(styleName)
    139. if (styleName === 'opacity' && ieVersion < 9) {
    140. element.style.filter = isNaN(value) ? '' : 'alpha(opacity=' + value * 100 + ')'
    141. } else {
    142. element.style[styleName] = value
    143. }
    144. }
    145. }
    146. class Tooltip {
    147. constructor () {
    148. this.id = 'autoToolTip'
    149. this.styleId = 'autoToolTipStyle'
    150. this.tooltipContent = ''
    151. this.styleElementText = `
    152. #autoToolTip {
    153. display: none;
    154. position: absolute;
    155. border-radius: 4px;
    156. padding: 10px;
    157. z-index: 99999;
    158. font-size: 12px;
    159. line-height: 1.2;
    160. min-width: 10px;
    161. max-width: 400px;
    162. word-break: break-word;
    163. color: #fff;
    164. background: #303133;
    165. transform-origin: center top;
    166. }
    167. #autoToolTip #arrow::after {
    168. content: " ";
    169. border-width: 5px;
    170. position: absolute;
    171. display: block;
    172. width: 0;
    173. height: 0;
    174. border-color: transparent;
    175. border-style: solid;
    176. bottom: -10px;
    177. left: calc(50% - 5px);
    178. border-top-color: #303133;
    179. }
    180. `
    181. this.tooltipElement = null
    182. this.styleElement = null
    183. this.showStatus = false
    184. }
    185. create (tooltipContent) {
    186. this.tooltipContent = tooltipContent
    187. const autoToolTip = document.querySelector('#' + this.id)
    188. // 同时只添加一个
    189. if (autoToolTip) {
    190. this.tooltipElement = autoToolTip
    191. return
    192. }
    193. const styleElement = document.createElement('style')
    194. styleElement.id = this.styleId
    195. styleElement.innerHTML = this.styleElementText
    196. document.head.append(styleElement)
    197. this.styleElement = styleElement
    198. const element = document.createElement('div')
    199. element.id = this.id
    200. const arrowElement = document.createElement('div')
    201. arrowElement.id = 'arrow'
    202. element.append(arrowElement)
    203. document.body.append(element)
    204. this.tooltipElement = element
    205. }
    206. show (event, textWidth) {
    207. if (this.showStatus) return
    208. const targetElement = event.target
    209. const targetElementRect = targetElement.getBoundingClientRect()
    210. const { left, top, width } = targetElementRect
    211. this.showStatus = true
    212. this.removeTextNode()
    213. this.tooltipElement.insertAdjacentText('afterbegin', this.tooltipContent)
    214. const style = {
    215. left: `${left - (textWidth + 20 - width) / 2}px`,
    216. top: `${top - 38}px`,
    217. display: 'block'
    218. }
    219. setStyle(this.tooltipElement, style)
    220. }
    221. hide () {
    222. const style = {
    223. left: '0px',
    224. top: '0px',
    225. display: 'none'
    226. }
    227. setStyle(this.tooltipElement, style)
    228. this.removeTextNode()
    229. this.showStatus = false
    230. }
    231. removeTextNode () {
    232. const { firstChild } = this.tooltipElement
    233. if (Object.prototype.toString.call(firstChild) === '[object Text]') {
    234. this.tooltipElement.removeChild(firstChild)
    235. }
    236. }
    237. destroy () {
    238. const { tooltipElement, styleElement } = this
    239. tooltipElement && tooltipElement.remove()
    240. styleElement && styleElement.remove()
    241. }
    242. }

     9、输入框输入限制 v-limit-input(可用但有bug导致栈内存溢出,暂时不推荐使用

    调用:

    代码:

    1. /**
    2. * 用法:v-limit-input:digit 只允许输入数字
    3. * v-limip-input:reg="your reg expression" 支持传正则表达式,处理一些特殊的场景
    4. */
    5. const copy = {
    6. bind (el, binding, vnode, oldvnode) {
    7. const typeMap = {
    8. // 只输入数字
    9. digit: /\D/g,
    10. // 只输入正整数
    11. positiveInteger: /^(0+)|\D+/g,
    12. // 只输入基本中文
    13. chinese: /[^\u4E00-\u9FA5]/g,
    14. // 只输入中文英文字母
    15. chineseAlphabet: /[^\u4E00-\u9FA5A-Za-z]/g,
    16. // 只输入大写字母及数字
    17. uppercaseLetterDigit: /[^A-Z0-9]/g,
    18. // 只输入字母及数字
    19. letterDigit: /[^0-9a-zA-Z]/,
    20. // 只输入合法的金额格式
    21. price: /(\d+)(\.\d{0,2})?/
    22. }
    23. const { arg, value } = binding
    24. console.log(binding);
    25. if (!arg) {
    26. throw Error('one arg is required')
    27. }
    28. if (arg && !typeMap.hasOwnProperty(arg)) {
    29. throw Error('arg is not in typeMap')
    30. }
    31. if (arg === 'reg' && !value) {
    32. throw Error('reg arg requires a reg expression value')
    33. }
    34. const tagName = el.tagName.toLowerCase()
    35. const input = tagName === 'input' ? el : el.querySelector('input')
    36. const regKey = arg || (arg === 'reg' && value)
    37. // 输入法气泡窗弹出,开始拼写
    38. el.compositionstartHandler = function () {
    39. el.inputLocking = true
    40. }
    41. // 输入法气泡窗关闭,输入结束
    42. el.compositionendHandler = function () {
    43. el.inputLocking = false
    44. input.dispatchEvent(new Event('input'))
    45. }
    46. el.inputHandler = function (e) {
    47. if (el.inputLocking) return
    48. const oldValue = e.target.value
    49. const newValue = oldValue.replace(typeMap[regKey], '')
    50. // price 正则在safar报错,导致页面无法打开,新增的判断
    51. if (regKey === 'price') {
    52. const rege = /(\d+)(\.\d{0,2})?/
    53. const target = e.target
    54. if (rege.test(target.value)) {
    55. const value = target.value.match(rege)[0]
    56. if (value.split('.').length === 1 && target.value === value) {
    57. input.value = Number(value)
    58. } else if (target.value !== value) {
    59. input.value = value
    60. input.dispatchEvent(new Event('input')) // 通知v-model更新
    61. }
    62. } else {
    63. input.value = ''
    64. input.dispatchEvent(new Event('input'))
    65. }
    66. } else {
    67. // 判断是否需要更新,避免进入死循环
    68. if (newValue !== oldValue) {
    69. input.value = newValue
    70. input.dispatchEvent(new Event('input')) // 通知v-model更新
    71. }
    72. }
    73. }
    74. input.addEventListener('compositionstart', el.compositionstartHandler)
    75. input.addEventListener('compositionend', el.compositionendHandler)
    76. input.addEventListener('input', el.inputHandler)
    77. },
    78. unbind (el) {
    79. const tagName = el.tagName.toLowerCase()
    80. const input = tagName === 'input' ? el : el.querySelector('input')
    81. input.removeEventListener('compositionstart', el.compositionstartHandler)
    82. input.removeEventListener('compositionend', el.compositionendHandler)
    83. input.removeEventListener('input', el.inputHandler)
    84. }
    85. }
    86. export default copy

    ---持续更新--- 

  • 相关阅读:
    SB30100LCT-ASEMI插件肖特基二极管SB30100LCT
    Revit插件的【快速视图样板】视图设置保存
    Django实战项目-学习任务系统-用户注册
    漏洞扫描程序
    [LeetCode]剑指 Offer 34. 二叉树中和为某一值的路径
    jpom ruoyi 发布后端
    华为路由器AR6300 取消密码重置提醒和密码长期有效
    Oracle数据库Bug:相关子查询多层嵌套报错:标识符无效
    消息队列 Kafka
    Vue的`provide`和`inject`特性:上下文传递与数据共享
  • 原文地址:https://blog.csdn.net/qq_39404437/article/details/126075132