<template> <div style="position:relative;" :class="difFontSize ? 'is-show-wordSize-limit' : 'is-show-word-limit'" > <el-input ref="rootDiv" v-model="model" :id="id" type="textarea" :style="{'border-bottom': isShowRed ? `1px solid ${isShowRed}` : ''}" class="show-word-limit show-scroll" :cols="cols" :rows="rows" v-bind="attrs" v-on="$listeners" v-bind:disabled="isDisable" :autosize="autosize" :placeholder="placeholderEnable || placeholder || $t('components.请输入')" :show-word-limit="!isLimitChar && !isIgnoreLine" :maxlength="rows * cols" @blur="onBlur" @change="handleChange" @focus="onFocus" > <!-- :class="isRequired? 'input-required' : null" --> <template v-slot:suffix> <slot name="suffix"></slot> </template> </el-input> <span v-if="isLimitChar || isIgnoreLine" class="cus-input-count">{{curLength}}/{{rows * cols}}</span> <div class="viewMulRow el-textarea el-input--small viewMulRowScroll" @click="viewClick" v-show="model && charmod && showTipX && !focused" v-html="colorText" :style="{'background-color': isDisable ? 'rgb(235, 235, 235)' : '#ffffff'}" ></div> <!-- <el-dialog ref="dialog" v-dialogDrag :visible.sync="modalVisible" center class="preview" :show-close="true" destroy-on-close title="文本清理提示" width="70%" :append-to-body="true" v-if="modalVisible" > <div> <el-col :offset="1" :span="10"> <label>清理后的内容({{`${rows} x ${cols}`}})</label> <div style="border:1px solid #555;height:30em;padding:1em;overflow:auto"> <pre>{{model}}</pre> </div> </el-col> <el-col :offset="2" :span="10"> <label>红色、划线字符已被自动清除</label> <div style="border:1px solid #555;height:30em;padding:1em;overflow:auto"> <p v-for="(line,index) in lineRows" :key="index" :style="{color:index>=rows?'red':'','text-decoration-line': index>=rows?'line-through':''}" v-html="line"> </p> </div> </el-col> </div> <div slot="footer" class="dialog-footer"> <el-button type="primary" @click="modalVisible=false">确定</el-button> </div> </el-dialog> --> </div> </template> <script> import commonDepend from '~/mixin/commonDepend.js' import markSetShowRed from '~/components/business/componentMixins/markSetShowRed.js' import lodash from "lodash"; const SP_STR = "✌"; const SP_CHAR = SP_STR.charCodeAt(0); const LINE_CHAR = "\n" const SWIFT_CHARS = " \r\n'()+,-./01234567890:?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; const SWIFTZ_CHARS = " \r\n!\"#%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz{"; const JNWB_CHARS = " .,-_()/=+?!&*;@#:%[]\n\r\t01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" //境内外币,行内划转中文模式不能输入改字符集 const JNWB_CNCHARS = "!!#$%^[]{}><'‘’“”\/??=+¥/、【】|" const CHARSET = ["", "", SWIFT_CHARS, SWIFTZ_CHARS,JNWB_CHARS,JNWB_CNCHARS]; /** * 使用antd input是因为jup-input加了debounce,延迟不利于控制字符 * 支持swift x、支持swiftx字符集 * 中文默认按两个字符算,默认为任意模式,支持中英混杂 */ export default { inject: { root: { default: () => null } }, mixins: [commonDepend, markSetShowRed], props: { value: { type: [String, Number], default: undefined }, disabled: { type: Boolean, default: false }, id: { type: String, default: undefined }, // 此对象为markset或者modifySet所在的对象,如果传了此对象可以标记当前对象下的字段为红色下划线 markSetData: { type: Object, default: () => null }, customModifykey: { type: String, default: null }, cols: { type: Number, default: 65 }, rows: { type: Number, default: 100 }, chsbit: { type: Number, default: 2 }, //0任意模式,1英文模式,2 swift模式 3 swiftz模式 charmod: { type: Number, default: 0 }, autosize: { type: Object, default: () => ({ minRows: 1, maxRows: 4 }) }, isCheckInCompontent: { type: Boolean, default: true }, // 是否必填 isRequired: { type: Boolean, default: false }, // 根据中英文判断必填的提示词 isEn: { type: Boolean, default: false }, //是否提示文字 showTip: { type: Boolean, default: true }, placeholder:{ type: String, default: null }, isShowFontNoSameWidth: { type: Boolean, default: false }, maxlength: { type: [Number, String], default: 1000 }, isCheckMaxRows: { type: Boolean, default: true }, difFontSize: { type: Boolean, default: false }, isLimitChar: { type: Boolean, default: false }, isIgnoreLine: { type: Boolean, default: true } }, data(){ return { lineRows:[], modalVisible:false, focused:false, colorText:'' } }, computed: { model: { get() { return this.value; }, set(newVal) { this.$emitNewValue = newVal; this.$emit("input", newVal) } }, mode () { return this.$store.state.Status.mode }, isDisable() { return this.mode === 'display' || this.disabled }, placeholderEnable(){ let label = this.$parent.label let curLabel = '' if (label) { curLabel = this.$i18n.locale === 'en' ? this.$t('components.请输入') + ' ' + this.$t('components.' + label) : this.$t('components.请输入') + this.$t('components.' + label) } return curLabel }, highlight () { return this.$store.state.Status.highlights.indexOf(this.id) !== -1 }, highlightChanges () { return this.$store.state.Status.highlightChanges.indexOf(this.id) !== -1 }, attrs(){ if(this.mode === 'display' || this.disabled) { let {placeholder,...rest} = this.$attrs return rest } return this.$attrs }, showTipX(){ if(!this.showTip){ return false } if(this.rows>1 && this.charmod){ return true } return false }, getCheckMaxRow() { let code = [ 'Gitadd','Hitupd','Netadd','Nitdla','Nitpdl','Gitame','Gitpam','Gitdla','Gitpdl','Nitcan', 'Nitcrq','Netcan','Gitplb','Gctadd','Nctcan','Gitppl','Netcrq','Netpcr','Gitpop','Nitpcj', 'Nitpcr','Hitcan','Hitdla','Hitpdl','Netset','Netopn','Gftcan','Gftadd','Nitset','Nitpse', 'Getcan','Gitopn','Nitopn','Hitopn','Hitpop','Nitame','Nitpam','Hitame','Hitpam','Nitplb', 'Hitpca','Nitppl','Gitset','Gitpse','Getset','Gitcrq','Gitpcr','Gitcan','Gitcrj','Gitpcj', 'Gctcan','Getopn','Netame','Getcrq','Getpcr','Nitcom','Nitadd','Nitmjm','Getadd','Niteng', 'Giteng','Nitenc','Nitfre','Nitpfr','Gitpfr','Gitfre','Conres','Gitatt','Nctfre','Nctadd', 'Nitcrj','Netfre','Nctpfr','Netpfr','Nftcan','Nftadd','Gctpfr','Getpfr','Gctfre','Getfre', 'Nitatt','Fctopn','Fctcan','Getame','Nitfee','Gctfee','Netfee','Gitzsq','Nitzsq','Nitpop', 'Getfee','Gitfee','Gitcom','Gitenc','Nctfee','Netfee' ] let formatCode = code.map((item) => { return item.toLowerCase() }) if (this.root && this.root.trnName && formatCode.includes(this.root.trnName.toLowerCase())) { return false } return this.isCheckMaxRows }, curLength() { let len = 0 if (this.isLimitChar && this.model && typeof(this.model) == 'string') { len = this.model.replace(/[^x00-xff]/g, 'xx').length } else { len = this.model != undefined ? this.model.length || 0 : 0 } if (this.isIgnoreLine && this.model && typeof(this.model) == 'string') { len = this.model.replace(/\r?\n/g, '').length } return len } }, mounted () { this.initValidCallback() this.colorText = this.filterChar(this.model) }, methods: { onBlur(e) { this.colorText = this.filterChar(this.model) this.focused = false }, onFocus(){ this.focused = true }, handleChange () { if (!this.customModifykey) { // 添加isModify属性 this.changeModify() } }, filterChar(valStr){ if(!valStr){ return "" } if(!this.charmod){ return valStr } if(!this.showTipX){ return valStr } let newVal = ""; let begPos = -2; for(let i = 0;i<valStr.length;i++){ let cStr = valStr.charAt(i) let c = valStr.charCodeAt(i); if(this.isCharFit(c,cStr)){ if(begPos >= 0){ newVal = newVal+"</span>"; begPos = -1 } newVal = newVal + cStr }else{ if(begPos < 0){ newVal = newVal + "<span class=\"mul_row_text_error_tip\">" begPos = i } newVal = newVal + cStr } } if(begPos >= 0){ newVal = newVal+"</span>"; } newVal = newVal.replace(/\r?\n/g,"<br/>") return newVal; }, isCharFit(c,cStr){ if ((c >= 0x0001 && c <= 0x007e) || (c >= 0xff60 && c <= 0xff9f)) { //判断字符集 if([2,3].includes(this.charmod) && CHARSET[this.charmod].indexOf(cStr)<0){ return false } }else{ if(this.charmod != 0){ return false } } return true }, // 校验多行输入框 isCheckMulRowInput (_this, charmod, totalLen, checkParams) { let prop = this.customModifykey || this.$parent.prop if (!prop || !this.root) { return false; } // 校验字符 let checkCode = [ {required: checkParams.isRequired, message: checkParams.isEn ? 'Required Field' : '必输项', source:"mulrow"}, { validator: (rule, value, callback) => { if (!_this.isLimitChar && value && _this.curLength > totalLen) { callback(new Error('长度不能超过' + totalLen)) } else { let res = { status: true, msg: '' } if (charmod === 0) { callback() _this.$emit('checkFalse', true) } else if (charmod === 1) { console.log('=========存在中文字符', prop) let valStr = value || ''; for(let i = 0; i < valStr.length; i++){ let cStr = valStr.charAt(i) let c = valStr.charCodeAt(i); if (!((c >= 0x0001 && c <= 0x007e) || (c >= 0xff60 && c <= 0xff9f))) { res = { status: false, msg: '存在中文字符,请核查' } break; } } if (res.status) { callback() _this.$emit('checkFalse', true) } else { callback(new Error(res.msg)) _this.$emit('checkFalse', false) } } else if (charmod > 1 && charmod < 5) { console.log('=========存在非法字符', prop) let valStr = value || ''; for(let i = 0; i < valStr.length; i++){ let cStr = valStr.charAt(i) let c = valStr.charCodeAt(i); if (CHARSET[charmod].indexOf(cStr) < 0) { res = { status: false, msg: '存在非法字符,请核查' } break; } } if (res.status) { _this.$emit('checkFalse', true) callback() } else { _this.$emit('checkFalse', false) callback(new Error(res.msg)) } } else if(charmod == 5) { let valStr = value || ''; for(let i = 0; i < valStr.length; i++){ let cStr = valStr.charAt(i) let c = valStr.charCodeAt(i); if (CHARSET[charmod].indexOf(cStr) >= 0) { res = { status: false, msg: '存在特殊字符'+ CHARSET[charmod] } break; } } if(valStr.indexOf('..') > -1){ res = { status: false, msg: '存在特殊字符..' } } if (res.status) { _this.$emit('checkFalse', true) callback() } else { _this.$emit('checkFalse', false) callback(new Error(res.msg)) } } } }, trigger: ['change', 'blur'], source:"mulrow" }, ] // 校验最大行 let checkMaxRow = _this.getCheckMaxRow ? [ { validator: (rule, value, callback) => { let scrollHeight = _this.$refs.rootDiv.$el.getElementsByClassName('el-textarea__inner')[0].scrollHeight; let lineHeight = _this.difFontSize ? 21*1.5 : 13*1.5; let curRows = Math.round((scrollHeight - 10) / lineHeight) console.log('最大行数校验:', curRows) if (curRows > _this.rows) { callback(new Error('输入内容不能超过' + _this.rows + '行')) } else { callback() } }, trigger: ['change', 'blur'], source:"mulrow" }, ] : [] // 校验字节 let checkLimitChar = _this.isLimitChar ? [ { validator: (rule, value, callback) => { if (_this.curLength > _this.rows * _this.cols) { callback(new Error('输入内容不能超过' + _this.rows * _this.cols + '个字节(一个中文字符占两个字节)')) } else { callback() } }, trigger: ['change', 'blur'], source:"mulrow" } ] : [] // 清除上次校验的问题 this.root.$refs['modelForm'].clearValidate([prop]); //合并交易下必填校验 let curProp = [...checkCode, ...checkMaxRow, ...checkLimitChar] if(this.root.rules[prop] && this.root.rules[prop].length){ let oldProp = this.root.rules[prop].filter(item=>!item.source && (item.hasOwnProperty("required") || item.hasOwnProperty("extends"))) curProp = [...oldProp,...curProp]; this.$set(this.root.rules, prop, curProp) }else{ this.$set(this.root.rules, prop, curProp) } console.log('校验规则rules:', this.root.rules, prop) }, initValidCallback () { this.$nextTick(() => { if (this.isCheckInCompontent) { let totalLen = this.cols * this.rows this.isCheckMulRowInput(this, this.charmod, totalLen, { isRequired: this.isRequired, isEn: this.isEn }) } this.setWangEditor() }) }, viewClick(){ this.onFocus() this.$refs.rootDiv.focus() }, getCharWidth() { // if (window['COU_FONT' + (this.cols)]) { // let textAreaWidth = window['COU_FONT' + (this.cols)] // return textAreaWidth // } let div = document.createElement('span') div.innerHTML = this.difFontSize ? `<div class="viewFitText viewPureText" style="padding: 0!important;font-size: 21px!important;">${'M'.repeat(this.cols)}</div>` : `<div class="viewFitText viewPureText" style="padding: 0!important;font-size: 13px!important;">${'M'.repeat(this.cols)}</div>` document.body.appendChild(div) let rect = div.children[0].getBoundingClientRect() let width = rect.width let textAreaWidth = width document.body.removeChild(div) window['COU_FONT' + (this.cols)] = textAreaWidth return textAreaWidth }, // 处理简式修改页面显示宋体 getCharWidthCn () { return 170 * 13 / 18.6 }, setWangEditor() { this.$nextTick(() => { let charWidth = this.isShowFontNoSameWidth ? this.getCharWidthCn() + 'mm' : this.getCharWidth() + 'px' let element = this.$refs.rootDiv.$el.getElementsByClassName('el-textarea__inner') let viewBox = this.$el.querySelector('.viewMulRow') if (element && element.length) { element = element[0] element.style.padding = `5px calc(100% - 18px - ${charWidth}) 5px 10px` viewBox.style.padding = `5px calc(100% - 18px - ${charWidth}) 5px 10px` } }) } }, beforeUpdate() { this.setWangEditor() }, watch: { isRequired () { console.log('监听isRequired') this.initValidCallback() }, charmod () { console.log('监听charmod') this.initValidCallback() }, isEn () { console.log('监听isEn') this.initValidCallback() }, model(newVal,oldVal){ if(!this.focused){ this.colorText = this.filterChar(newVal) } }, disabled(newVal) { if (newVal) { this.resetValidate() } } }, } </script> <style> .viewFitText { width: fit-content!important; position: absolute; visibility: hidden; } .viewPureText { padding: 5px 15px 5px 10px; line-height: 1.5em; font-family: 'Courier New'; overflow-wrap: break-word; overflow-y: scroll; height: 100%; font-size: 13px; } </style> <style lang="less" scoped> .viewMulRow{ position: absolute; top: 1px; left: 1px; width: calc(100% - 2px); height: calc(100% - 2px); padding: 5px 15px 5px 10px; line-height: 1.5em; font-family: '宋体'; overflow-wrap: break-word; overflow-y: auto; } span.mul_row_text_error_tip{ color:red !important; font-size: 13px; font-weight: bolder; font-family: 'Courier New'!important; text-decoration: underline; } .is-show-word-limit { .viewMulRowScroll { overflow-y: scroll; font-family: 'Courier New'!important; font-size: 13px!important; white-space: break-spaces; } /deep/ .el-textarea__inner { font-size: 13px!important; font-family: 'Courier New'; overflow-y: scroll; white-space: break-spaces; &::placeholder { font-size: 13px!important; } } /deep/ .el-input__count { position: absolute; right: 14px; bottom: 4px; z-index: 10; } } .is-show-wordSize-limit{ .viewMulRowScroll { overflow-y: scroll; font-family: 'Courier New'!important; font-size: 21px!important; white-space: break-spaces; } /deep/ .el-textarea__inner { font-size: 21px!important; font-family: 'Courier New'; overflow-y: scroll; white-space: break-spaces; &::placeholder { font-size: 21px!important; } } /deep/ .el-input__count { position: absolute; right: 14px; bottom: 4px; z-index: 10; } } .no-view-font-same-width { .viewMulRowScroll { font-family: '宋体'!important; } /deep/ .el-textarea__inner { font-family: '宋体'!important; } } .cus-input-count { position: absolute; right: 10px; bottom: 5px; z-index: 10; height: 16px; line-height: 16px; color: #939099; } </style>