297 lines
7.1 KiB
Vue
297 lines
7.1 KiB
Vue
|
|
<template>
|
|||
|
|
<view class="highlight-box">
|
|||
|
|
<template v-if="type === 'textarea'">
|
|||
|
|
<view v-if="value"
|
|||
|
|
class="textarea-outer"
|
|||
|
|
ref="textareaOuter"
|
|||
|
|
>
|
|||
|
|
<view ref="outerInner" class="outer-inner"
|
|||
|
|
v-html="highlightHtml(value)">
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
<textarea
|
|||
|
|
ref="textareaBox"
|
|||
|
|
maxlength="1000"
|
|||
|
|
auto-height
|
|||
|
|
:autoSize="true"
|
|||
|
|
@blur="blur"
|
|||
|
|
:placeholder="placeholder"
|
|||
|
|
v-model:value="value">
|
|||
|
|
</textarea>
|
|||
|
|
</template>
|
|||
|
|
<template v-if="type === 'input'">
|
|||
|
|
<view v-if="value"
|
|||
|
|
class="input-outer"
|
|||
|
|
v-html="highlightHtml(value)">
|
|||
|
|
</view>
|
|||
|
|
<a-input type="text" class="ant-input"
|
|||
|
|
:placeholder="placeholder"
|
|||
|
|
v-model:value.trim="value"/>
|
|||
|
|
</template>
|
|||
|
|
</view>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script >
|
|||
|
|
export default {
|
|||
|
|
name: 'HighlightTextarea',
|
|||
|
|
data() {
|
|||
|
|
return {
|
|||
|
|
value: ''
|
|||
|
|
};
|
|||
|
|
},
|
|||
|
|
props: {
|
|||
|
|
placeholder: {
|
|||
|
|
type: String,
|
|||
|
|
required: false,
|
|||
|
|
default: '请输入'
|
|||
|
|
},
|
|||
|
|
text: {
|
|||
|
|
type: String,
|
|||
|
|
required: false,
|
|||
|
|
default: ''
|
|||
|
|
},
|
|||
|
|
highlightKey: {
|
|||
|
|
type: Array,
|
|||
|
|
require: false,
|
|||
|
|
default: () => []
|
|||
|
|
},
|
|||
|
|
type: {
|
|||
|
|
type: String,
|
|||
|
|
required: true,
|
|||
|
|
default: 'textarea'
|
|||
|
|
},
|
|||
|
|
maxHeight: {
|
|||
|
|
type: Number,
|
|||
|
|
required: false,
|
|||
|
|
default: 220
|
|||
|
|
},
|
|||
|
|
modelValue: {
|
|||
|
|
type: String,
|
|||
|
|
default: ''
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
watch: {
|
|||
|
|
value (v) {
|
|||
|
|
// this.value = this.text.replace(/(^\s*)|(\s*$)/g, '').replace(/<br \/>|<br\/>|<br>/g, '\n');
|
|||
|
|
this.$emit("input", v);
|
|||
|
|
},
|
|||
|
|
modelValue: {
|
|||
|
|
deep: true,
|
|||
|
|
immediate: true,
|
|||
|
|
handler(v) {
|
|||
|
|
console.log(v, 'modelValue 3333')
|
|||
|
|
|
|||
|
|
this.value = v ? this.modelValue : ''
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
disabled: {
|
|||
|
|
deep: true,
|
|||
|
|
immediate: true,
|
|||
|
|
handler(v) {
|
|||
|
|
// this.editorConfig.readOnly = v
|
|||
|
|
// this.$nextTick(() => {
|
|||
|
|
// if (this.editor) v ? this.editor.disable() : this.editor.enable();
|
|||
|
|
// })
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
created() {
|
|||
|
|
// this.value = this.text.replace(/(^\s*)|(\s*$)/g, '').replace(/<br \/>|<br\/>|<br>/g, '\n');
|
|||
|
|
},
|
|||
|
|
mounted() {
|
|||
|
|
// this.scrollMousewheel();
|
|||
|
|
},
|
|||
|
|
methods: {
|
|||
|
|
setValue(newValue) {
|
|||
|
|
this.value = newValue;
|
|||
|
|
// 触发所有必要的事件
|
|||
|
|
this.$emit('input', newValue);
|
|||
|
|
this.$emit('update:modelValue', newValue);
|
|||
|
|
this.$emit('change', newValue);
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
highlightHtml(str) {
|
|||
|
|
if ((!str || !this.highlightKey || this.highlightKey.length === 0) && this.type !== 'textarea') {
|
|||
|
|
return str;
|
|||
|
|
}
|
|||
|
|
let rebuild = str;
|
|||
|
|
if (this.type === 'textarea') {
|
|||
|
|
rebuild = rebuild.replace(/\n/g, '<br/>').replace(/\s/g, ' ');
|
|||
|
|
// textarea有滚动条时,view底部不能和textarea重合,故加一个<br/>
|
|||
|
|
// const wrap = this.$refs.textareaBox;
|
|||
|
|
// if (wrap && wrap.scrollHeight > this.maxHeight) {
|
|||
|
|
// rebuild = rebuild + '<br/>';
|
|||
|
|
// }
|
|||
|
|
}
|
|||
|
|
if (this.highlightKey.filter(item => ~str.indexOf(item)).length) {
|
|||
|
|
let regStr = '';
|
|||
|
|
let regExp = null;
|
|||
|
|
this.highlightKey.forEach(list => {
|
|||
|
|
regStr = this.escapeString(list);
|
|||
|
|
regExp = new RegExp(regStr, 'g');
|
|||
|
|
rebuild = rebuild.replace(regExp,`<span style="color:red">${list}</span>`);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return rebuild;
|
|||
|
|
},
|
|||
|
|
blur(){
|
|||
|
|
this.$emit("blur")
|
|||
|
|
},
|
|||
|
|
syncScrollTop() {
|
|||
|
|
const wrap = this.$refs.textareaBox;
|
|||
|
|
const outerWrap = this.$refs.textareaOuter;
|
|||
|
|
const outerInner = this.$refs.outerInner;
|
|||
|
|
if (wrap.scrollHeight > this.maxHeight && outerInner.scrollHeight !== wrap.scrollHeight) {
|
|||
|
|
outerInner.style.height = `${wrap.scrollHeight}px`;
|
|||
|
|
}
|
|||
|
|
if (wrap.scrollTop !== outerWrap.scrollTop) {
|
|||
|
|
outerWrap.scrollTop = wrap.scrollTop;
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
// scrollMousewheel() {
|
|||
|
|
// if (this.type === 'input') {
|
|||
|
|
// return;
|
|||
|
|
// }
|
|||
|
|
// this.$nextTick(() => {
|
|||
|
|
// this.eventHandler('add');
|
|||
|
|
// });
|
|||
|
|
// },
|
|||
|
|
// 处理字符串中可能对正则有影响的字符
|
|||
|
|
escapeString(value) {
|
|||
|
|
const characterss = ['(', ')', '[', ']', '{', '}', '^', '$', '|', '?', '*', '+', '.'];
|
|||
|
|
let str = value.replace(new RegExp('\\\\', 'g'), '\\\\');
|
|||
|
|
characterss.forEach(function (characters) {
|
|||
|
|
let r = new RegExp('\\' + characters, 'g');
|
|||
|
|
str = str.replace(r, '\\' + characters);
|
|||
|
|
});
|
|||
|
|
return str;
|
|||
|
|
},
|
|||
|
|
eventHandler(type) {
|
|||
|
|
const wrap = this.$refs.textareaBox;
|
|||
|
|
if (wrap) {
|
|||
|
|
let mousewheelevt = (/Firefox/i.test(navigator.userAgent))
|
|||
|
|
? 'DOMMouseScroll' : 'mousewheel';
|
|||
|
|
wrap[`${type}EventListener`](mousewheelevt, this.syncScrollTop);
|
|||
|
|
wrap[`${type}EventListener`]('scroll', this.syncScrollTop);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
destroyed() {
|
|||
|
|
// this.eventHandler('remove');
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style lang="less">
|
|||
|
|
@width: 100%; // 500px
|
|||
|
|
.highlight-box {
|
|||
|
|
font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif;
|
|||
|
|
position: relative;
|
|||
|
|
display: flex;
|
|||
|
|
//font-size: 12px;
|
|||
|
|
width: @width;
|
|||
|
|
//position: relative;
|
|||
|
|
//color: #333333;
|
|||
|
|
background: #ffffff;
|
|||
|
|
border-radius: 2px;
|
|||
|
|
overflow: hidden;
|
|||
|
|
.textarea-outer,
|
|||
|
|
.input-outer {
|
|||
|
|
box-sizing: border-box;
|
|||
|
|
width: @width;
|
|||
|
|
position: absolute;
|
|||
|
|
top: 0;
|
|||
|
|
left: 0;
|
|||
|
|
right: 0;
|
|||
|
|
// border: 1px solid transparent;
|
|||
|
|
border-top: 0;
|
|||
|
|
// 鼠标事件失效 ie6-10不支持
|
|||
|
|
pointer-events: none;
|
|||
|
|
cursor: text;
|
|||
|
|
|
|||
|
|
color: #333333;
|
|||
|
|
font-weight: 400;
|
|||
|
|
font-size: 28rpx;
|
|||
|
|
line-height: 40rpx;
|
|||
|
|
span {
|
|||
|
|
color: red; // #F27C49
|
|||
|
|
}
|
|||
|
|
&:hover {
|
|||
|
|
border-color: #4C84FF;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
.textarea-outer {
|
|||
|
|
overflow-y: auto;
|
|||
|
|
//line-height: 20px;
|
|||
|
|
word-break: break-all;
|
|||
|
|
.outer-inner {
|
|||
|
|
// padding: 5px 8px;
|
|||
|
|
width: 100%;
|
|||
|
|
box-sizing: border-box;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
textarea {
|
|||
|
|
width: @width;
|
|||
|
|
//line-height: 20px;
|
|||
|
|
resize: none;
|
|||
|
|
}
|
|||
|
|
.input-outer,
|
|||
|
|
input {
|
|||
|
|
width: @width;
|
|||
|
|
height: 32px;
|
|||
|
|
line-height: 32px;
|
|||
|
|
}
|
|||
|
|
.input-outer {
|
|||
|
|
bottom: 0;
|
|||
|
|
padding: 0 8px;
|
|||
|
|
overflow: hidden;
|
|||
|
|
white-space: nowrap;
|
|||
|
|
text-overflow: ellipsis;
|
|||
|
|
}
|
|||
|
|
textarea,
|
|||
|
|
input {
|
|||
|
|
font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif;
|
|||
|
|
//font-size: 12px;
|
|||
|
|
// position: relative;
|
|||
|
|
// z-index: 2;
|
|||
|
|
// 光标的颜色
|
|||
|
|
//color: #333333;
|
|||
|
|
// 文本颜色
|
|||
|
|
text-shadow: 0 0 0 rgba(0, 0, 0, 0);
|
|||
|
|
-webkit-text-fill-color: transparent;
|
|||
|
|
// color: transparent;
|
|||
|
|
// color: #333333;
|
|||
|
|
font-weight: 400;
|
|||
|
|
font-size: 28rpx;
|
|||
|
|
line-height: 40rpx;
|
|||
|
|
min-height: 300rpx;
|
|||
|
|
background: transparent;
|
|||
|
|
border-radius: 2px;
|
|||
|
|
// border: 1px solid #d9d9d9;
|
|||
|
|
padding: 0px;
|
|||
|
|
box-sizing: border-box;
|
|||
|
|
//&::placeholder {
|
|||
|
|
// -webkit-text-fill-color: #d5d5d5;
|
|||
|
|
//}
|
|||
|
|
//&:hover {
|
|||
|
|
// border-color: #36cfc9;
|
|||
|
|
//}
|
|||
|
|
//&:focus {
|
|||
|
|
// border-color:#36cfc9;
|
|||
|
|
// box-shadow: 0 0 0 2px #DBE4FF;
|
|||
|
|
// outline: none;
|
|||
|
|
//}
|
|||
|
|
.textarea-placeholder{
|
|||
|
|
-webkit-text-fill-color: #B8B8B8;
|
|||
|
|
font-family: PingFangSC, PingFang SC;
|
|||
|
|
font-weight: 400;
|
|||
|
|
font-size: 28rpx;
|
|||
|
|
color: #B8B8B8;
|
|||
|
|
line-height: 40rpx;
|
|||
|
|
text-align: left;
|
|||
|
|
font-style: normal;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
</style>
|