vue3邮箱输入框组件

1.0、场景

1.1、业务场景
  • 用户回复邮件时,需要实现用户对邮箱修改,新增,删除的操作;
    • 展示效果
      • 邮箱格式正确:用户名称<邮箱地址>
      • 邮箱格式错误:给出错误提示
    • 修改
      • 双击需要修改的邮箱,变成输入框回显点击项,进行修改
      • 修改完成,输入框失焦或用户回车,进行保存
    • 新增
      • 用户输入邮箱地址,失焦或用户回车时,处理数据展示成上面提到的邮箱格式
    • 删除
      • 用户按下delete,删除焦点前一个邮箱
  • 组件状态
    • 只展示
      • 对邮箱修改,新增,删除的操作不可用
    • 可编辑
      • 已有的邮箱只展示,修改,删除的操作不可用
      • 可以新增邮箱,并且可以对新增的邮箱进行修改
  • 存在格式错误的邮箱,禁止提交;

2.0、思路

  • 组件接收:展示邮箱(noChangeMailList <arr>);状态(isEdit <boolean>
  • 组件事件:获取邮箱(getMailList(mailList) <function>
  • 数据形式
    • 接收
      • ['zhangsan<zhangsan@zs.com>',...]
        
    • 展示和输出
      • [
          {
            name:'zhangsan',
            email:'zhangsan@zs.com',
            status:'normal', // normal-正常;err-错误;write-编辑
            content:'zhangsan<zhangsan@zs.com>'
          },
          ...
        ]
        
  • 组件设计
    • 展示:通过识别需要展示的邮箱的格式是否正确,展示不同状态
    • 新增:在展示的邮箱尾部,添加一个输入框
    • 修改:双击需要修改的邮箱,将展示框变成输入框
    • 删除:监听键盘事件做数组处理
  • 组件状态
    • 只展示:根据接受状态,判断是否阻止用户双击行为,键盘事件,是否隐藏新增输入框
    • 可编辑:展示邮箱接收的邮箱数组禁止双击和删除,新增的可以进行修改删除操作
  • 提交
    • 遍历邮箱数组,检查如果有格式错误的禁止提交

3.0、实现

3.1、相关代码
  • mailInput.vue

    <template>
        <div class="mail_input" :style="{borderWidth:isEdit?1:0}">
            <!-- 已有数据展示 -->
            <div class="mail_input_item" v-for="(i, idx) in showList" :key="i.email">
                <div class="item_input" v-if="i.status && i.status === 'write'">
                    <span class="item_input_hidden" :id="'item_input_hidden_' + idx">{{formatLabel(i.content)}}</span>
                    <el-input 
                        ref="item_input"
                        v-model="i.content" 
                        :id="'item_input_' + idx"
                        @keyup.enter="itemEnter(i,idx)"
                        @blur="itemBlur(i,idx)"
                        @input="itemChange(idx)"
                    />
                </div>
                <div class="item_err" v-else-if="i.status && i.status === 'err'" @dblclick="editMail(i,idx)">
                    <span v-html="formatLabel(i.content)"></span>
                    <el-tooltip
                        effect="light"
                        content="邮箱格式错误"
                        placement="top"
                    >
                        <!-- <fes-icon style="color: #EC4847;padding: 0 4px;" type="jinggao"/> -->
                        <el-icon color=" #EC4847"><Warning /></el-icon>
                    </el-tooltip>
                </div>
                <div class="item" v-else @dblclick="editMail(i,idx)">
                    <span>{{i.name}}</span>
                    <span><{{i.email}}></span>
                </div>
            </div>
            <!-- 新加输入框 -->
            <div class="mail_input_addBox" v-if="isEdit">
                <span class="item_input_hidden" id="item_input_hidden_add">{{addValue}}</span>
                <el-input 
                    ref="item_input_add"
                    id="item_input_add"
                    v-model="addValue"
                    @input="itemChange('add')"
                    @keyup.enter="addEnter"
                    @blur="addBlur"
                    @keydown="delItem($event)"
                ></el-input>
            </div>
        </div>
    </template>
    <script>
        import {Warning} from '@element-plus/icons-vue'
        import { ref, computed } from 'vue'
        import { formatLabel, getEmailData, getEmailUserList } from '@/common/mailInput.js'
        export default {
            props:{
                isEdit:{
                    type:Boolean,
                    default:()=>false
                },
                noChangeMailList:{
                    type:Array,
                    default:()=>[]
                },
                changeMailList:{
                    type:Array,
                    default:()=>[]
                }
            },
            components:{
                Warning
            },
            setup(props) {
                const list = ref([
                    ...getEmailUserList(props.changeMailList)
                ])
                const showList = computed(()=>{
                    return [].concat(getEmailUserList(props.noChangeMailList,false),list.value)
                })
                const addValue = ref('')
                return {
                    list,
                    showList,
                    formatLabel,
                    addValue
                }
            },
            data () {
                return {
    
                }
            },
            mounted(){
                this.$watch(()=>{
                    return this.showList
                },()=>{
                    this.$emit('getMailList',this.showList)
                },{
                    deep:true,
                    immediate:true
                })
            },
            methods:{
                // 邮箱双击切换输入框
                editMail(i,idx){
                    if (!i.isChange) return
                    i.status = 'write'
                    console.log(this)
                    this.$nextTick(() => {
                        const id = 'item_input_' + idx
                        const el = window.document.getElementById(id)
                        el.focus()
                        this.setInputStyle(idx)
                    })
                },
                // 双击切换至input的样式
                setInputStyle (idx) {
                    const id = 'item_input_hidden_' + idx
                    const inputId = 'item_input_' + idx
                    const el = window.document.getElementById(id)
                    const inputEl = window.document.getElementById(inputId)
                    const width = el.clientWidth
                    console.log(width)
                    let w = '100px'
                    if (width > 76) {
                        w = (width + 24) + 'px'
                    }
                    inputEl.style.width = w
                },
                // 双击切换至input的change事件
                itemChange(idx) {
    
                    this.$nextTick(() => {
                        this.setInputStyle(idx)
                    })
                },
                // 处理邮箱格式
                creatItem(i,idx) {
                    console.log('creatItem')
                    const item = getEmailData(i.content)
                    if (item) {
                        let filList = this.showList.filter((i,index)=> idx != index)
                        if (filList.some(it => it.email === item.email)) {
                            this.list.splice(idx - this.noChangeMailList.length, 1)
                            return
                        }
                        i.name = item.name
                        i.email = item.email
                        i.status = item.status
                        i.content = item.content
    
                    } else {
                        this.list.splice(idx - this.noChangeMailList.length, 1)
                        console.log('cjy',this.list)
                    }
                    // this.$emit('getMailList',this.showList)
                },
                // 双击切换至input的enter事件
                itemEnter(i) {
                    console.log('itemEnter',i.content,this.$refs.item_input)
                    // this.creatItem(i,idx)
    
                    this.$refs.item_input[0].blur()
                },
                // 双击切换至input的失焦事件
                itemBlur(i,idx) {
                    console.log('itemBlur',i)
                    this.creatItem(i,idx)
                },
                // 新增输入框enter事件
                addEnter(){
                    this.$refs.item_input_add.blur()
                },
                // 新增输入框blur事件
                addBlur(){
                    const data = getEmailData(this.addValue)
                    if (data && data.email) {
                        if (!this.showList.some(i => i.email === data.email)) this.list.push(data)
                        console.log(this.showList.filter(i=>i.status === 'normal'))
                        // this.$emit('getMailList',this.showList)
                    }
                    this.addValue = ''
                },
                // 删除邮箱
                delItem(e){
                    console.log(e,window.event)
                    var evt = window.event || e
                    if (evt.keyCode === 8) {
                        if (this.addValue === '') {
                        this.list.pop()
                        }
                    }
                }
            }
        }
    </script>
    <style lang="less" scoped>
        .mail_input {
            display: flex;
            flex-flow: row wrap;
            border: 1px solid #dcdfe6;
            padding: 6px 4px;
            :deep(.el-input__inner) {
                background: transparent;
                border: 0;
            }
            .mail_input_item {
                padding: 2px;
                .item_input {
                    .item_input_hidden {
                        white-space: nowrap;
                        position: absolute;
                        left: 0;
                        top: 0;
                        opacity: 0;
                        z-index: 1;
                        max-width: 200px;
                        overflow: hidden;
                    }
                }
                .item_err {
                    color: rgba(126, 128, 130, 100);
                    height: 20px;
                    border-radius: 12px;
                    background-color: rgb(255, 236, 235);
                    border: 1px solid rgb(253, 170, 155);
                    padding: 0 12px;
                    display: flex;
                    align-items: center;
                   & span:first-child {
                      margin-right: 4px; 
                      color: #202428;
                   }
                }
                .item {
                    color: rgba(126, 128, 130, 100);
                    height: 20px;
                    border-radius: 12px;
                    background-color: rgba(235, 244, 255, 1);
                    border: 1px solid rgba(178, 211, 253, 1);
                    padding: 0 12px;
                    display: flex;
                    align-items: center;
                   & span:first-child {
                      margin-right: 4px; 
                      color: #202428;
                   }
                }
            }
            .mail_input_addBox {
                .item_input_hidden {
                        white-space: nowrap;
                        position: absolute;
                        left: 0;
                        top: 0;
                        opacity: 0;
                        z-index: 1;
                        max-width: 200px;
                        overflow: hidden;
                    }
                /deep/ .el-input {
                    .el-input__wrapper {
                        box-shadow: none;
                        &.is-focus {
                            box-shadow: none;
                        }
                        &:hover {
                            box-shadow: none;
                        }
                    }
                }
    
            }
        }
    </style>
    
  • mailInput.js

    /** 处理 < 和 > 字符 */
    export const formatLabel = (str) => {
      return str.replace(/</g, '<').replace(/>/g, '>')
    }
    
    /** 根据输入信息获取邮箱信息 */
    export const getEmailData = (value,isChange=true) => {
      console.log('getEmailData', value)
      // debugger
      const reg = /^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.[a-zA-Z0-9]{2,6}$/
      let data = null
    
      // 如果数据为空则邮件无信息,做删除处理
      if (value && value !== '' && value.trim() !== '') {
        const inputData = value.trim()
        let name = null
        let email = null
    
        if (!reg.test(inputData)) {
          // 如果有 < 且有 > 则处理数据,根据结果再判断是否满足邮件格式
          const stNum = inputData.split('<').length - 1
          const etNum = inputData.split('>').length - 1
          const stIndex = inputData.indexOf('<')
          const etIndex = inputData.indexOf('>')
          // < 或者 > 只存在一个且 < 的位置小于 > 的位置
          if (stNum === 1 && etNum === 1 && stIndex < etIndex) {
            const inputName = inputData.substr(0, stIndex)
            const inputEmail = inputData.substr(stIndex + 1).replaceAll('>', '')
            if (reg.test(inputEmail)) {
              name = inputName
              email = inputEmail
            }
          } else {
            email = inputData
          }
        } else {
          email = inputData
          name = email.split('@')[0]
        }
        if (name === '' && email !== '') name = email.split('@')[0]
        data = {
          content: name && email ? `${name}<${email}>` : inputData,
          name: name ? name.trim() : null,
          email: email ? email.trim() : null,
          status: name && email ? 'normal' : 'err',
          isChange
        }
      }
      return data
    }
    /** 根据接口数据返回邮件用户列表 */
    export const getEmailUserList = (list,isChange=true) => {
      const result = []
          list.forEach(item => {
              result.push(getEmailData(item,isChange))
          })
      return result
    }
    
  • mailInputDemos.vue

    <script setup>
      import MailInput from '@/components/mailInput.vue'
      const noChangeMailList = ['zhangsan<zhangsan@zs.com>','lisi<lisi@zs.com>','wangwu<wangwu@zs.com..>']
      const getMailList = (list) => {
        console.log('email',list)
      }
    </script>
    <template>
      <h3>只展示</h3>
      <mail-input :noChangeMailList="noChangeMailList"></mail-input>
      <h3>可编辑</h3>
      <mail-input isEdit :changeMailList="noChangeMailList" @getMailList="getMailList"></mail-input>
    </template>
    

4.0、效果

4.1、展示
  • 格式正确
    在这里插入图片描述

  • 格式错误
    在这里插入图片描述

4.2、编辑

在这里插入图片描述

5.0、不足

  • 从具体项目中衍生出来的组件,业务场景很单一,通用性不足
  • 使用频率较少,存在未知缺陷的风险
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值