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> |