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、不足
- 从具体项目中衍生出来的组件,业务场景很单一,通用性不足
- 使用频率较少,存在未知缺陷的风险

556

被折叠的 条评论
为什么被折叠?



