Kaynağa Gözat

模型匹配

newStyle
corala 2 yıl önce
ebeveyn
işleme
41327dedb5
2 değiştirilmiş dosya ile 1524 ekleme ve 0 silme
  1. +713
    -0
      src/views/Template/labelModel.vue
  2. +811
    -0
      src/views/Template/modelTodo.vue

+ 713
- 0
src/views/Template/labelModel.vue Dosyayı Görüntüle

@@ -0,0 +1,713 @@
<template>
<div class="box-center">
<div class="app-top">
<el-radio-group v-model="keyType" size="medium" @change="search">
<el-radio-button :label="0">需求挖掘</el-radio-button>
<el-radio-button :label="1">销讲词</el-radio-button>
<el-radio-button :label="2">客户标签</el-radio-button>
<el-radio-button :label="3">违禁词</el-radio-button>
</el-radio-group>
<el-input size="small" clearable style="margin: 0 10px;width: 220px" v-model="value" placeholder="标签名称"></el-input>
<el-button size="small" type="primary" @click="search()">筛选</el-button>
<el-button size="small" type="text" @click="Emptycondition()">清空筛选条件</el-button>
</div>
<div class="app-box">
<el-table
:data="tableData"
stripe
:load="loading"
:header-cell-style="{background:'#F7F8FA',borderColor:'#E0E0E0',color:'#606775'}"
style="width: 100%">

<el-table-column
prop="level1Name"
:label="keyType==3?'违禁词':'画像一级'"
align="center">
</el-table-column>
<el-table-column
v-if="keyType!=3"
prop="level2Name"
label="画像二级"
align="center">
</el-table-column>
<el-table-column
v-if="keyType!=3"
prop="level3Name"
:label="keyType==0?'挖掘话术':'画像三级'"
align="center">
</el-table-column>
<el-table-column
prop="showFormatExpression"
label="匹配模型"
width="330"
align="center">
<template slot-scope="scope">
<el-tooltip class="item" effect="dark" placement="top">
<div v-html="scope.row.showFormatExpression" slot="content" style="max-width: 530px;"></div>
<div class="hidden-tooltip">{{scope.row.showFormatExpression}}</div>
</el-tooltip>
</template>
</el-table-column>
<el-table-column
prop="distance"
label="匹配距离"
align="center">
</el-table-column>
<el-table-column
prop="updateUserName"
label="最后修改人"
align="center">
</el-table-column>
<el-table-column
prop="updateTime"
label="最后修改时间"
width="110"
align="center">
</el-table-column>
<el-table-column label="操作" width="140" fixed="right" align="center">
<template slot-scope="scope">
<el-button type="text" @click="editFun(scope.row)">编辑</el-button>
<el-button type="text" @click="deleteFun(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="block">
<div class="blockbox">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="pageNum"
:page-sizes="[10, 20, 30, 40,50,100]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination>
</div>
</div>
</div>
<el-dialog
title="编辑模型"
@open="openModel"
:close-on-click-modal="false"
:visible.sync="dialogVisible"
>
<el-dialog
title="匹配规则"
:visible.sync="innerVisible"
append-to-body>
<div style="max-height: 430px;overflow: auto">
<p><b>匹配距离</b>:关键字匹配字数距离,主要针对near/after/w- 生效,w- 的字数统计从挖掘话术后,客户的第一句话开始统计;</p>
<p style="padding-left: 15px">例如:设置为10,面积 after 140</br>
命中话术:"想买一个面积在140平的"</br>
超出10个字,有关键字不命中话术参考:“房屋面积啊,你们这边有多大的,有没有140平左右的”;</p>
<p><b>or(或)</b>:多个关键词,匹配上1个就行</p>
<p style="padding-left: 15px">例如标签:自住</br>
匹配模型:自己住 or 给自己</br>
命中话术:我们自己住/给自己买的</p>
<p><b>and not(非)</b>:排除反面意思</p>
<p style="padding-left: 15px">例如:北京户口,</br>
匹配模型:是北京户口 and not 不是北京户口</br>
命中话术:我是北京户口;</br>
反面话术:我不是北京户口</p>
<p><b>near ( 临近)</b>:1个关键词前后一定范围出现过另一个关键词就算命中,有距离限制</p>
<p style="padding-left: 15px">例如标签:购房预算 140w</br>
匹配模型:预算 near 140</br>
命中话术:我的预算是140w或大概140w的购房预算</p>
<p><b>after(后面)</b>:一段文本出现2个关键词,并且按照先后的顺序即算命中,有距离限制</p>
<p style="padding-left: 15px">例如标签:意向面积 140平</br>
匹配模型: 面积 after 140 ,140在面积后面才算生效;</br>
命中话术:想买一个面积在140平的;</p>
<p><b>answer(挖掘话术业务)</b>:顾问执行了挖掘话术,客户回答结果,有距离限制,从客户话术文本开始</p>
<p style="padding-left: 15px">例如标签:北京户口</br>
匹配模型:(w-是 or w-有) and not (w-不是 or w-没有)</br>
命中话术:销售:你有北京户口吗?</br>
客户:有的;</p>
<p><b>-n(命中距离)</b>:near/after-n ,单个near/after的匹配距离,n大于0,小于500</p>
<p style="padding-left: 15px">例如 :面积 after-10 140</br>
10个字内,命中话术参考:"想买一个面积在140平的"</br>
超出10个字,有关键字不命中话术参考:“房屋面积啊,你们这边有多大的,有没有140平左右的”;</p>
<p><b>注意</b>:-n 与 near/after/answer 之间<b>不能有空格</b>;</p></br>
<p><b>注意</b>:优先匹配关键词可以排在前面;</p>
</div>
</el-dialog>
<el-form ref="form" size="mini" :inline="true" :model="form" label-position="right">
<el-form-item :label="keyType==0?'挖掘话术:':keyType==3?'违禁词:':'标签名称:'">
<div style="max-width:400px;min-width: 200px;">{{form.keywordsName}}</div>
</el-form-item>
<el-form-item label="after,near,answer 匹配距离:">
<el-input-number v-model="form.distance" controls-position="right" :min="0" :max="500"></el-input-number></el-input><span style="color:red;margin-left:10px">*请输入0~500内的整数</span> <el-button type="text" style="margin-left:30px" @click="innerVisible=true">规则说明</el-button>
</el-form-item>
<div contentEditable="true"
@click="myeditorenter($event)"
@keypress.enter="myeditorenter($event)"
@blur="saveRange"
@paste="onPaste"
class="editDiv"
id="huashuModel">
</div>
<div v-if="keyType==0||dynamiclist.length">{{keyType==0?'匹配标签:':'需求挖掘匹配:'}}</div>
<!-- <%-- keyType=0 需求挖掘--%> -->
<div class="dynamicbox" v-if="keyType==0">
<div v-for="(item,index) in dynamiclist" :key="item" class="itemlist">
<el-select size="small" v-model="item.markid" @change="checkrepeat" placeholder="标签" clearable>
<el-option v-for="(mark,i) in wajueList" :disabled="mark.disabled" :key="i" :label="mark.name" :value="mark.id"></el-option>
</el-select>
<div contentEditable="true"
@click="myeditorenter($event)"
@keypress.enter="myeditorenter($event)"
@blur="saveRange"
class="item-input"
v-html="item.editValue"
:id="'huashuModel'+index"></div>
<el-button size="small" plain type="primary" @click="delItemFun(index)">删除</el-button>
</div>
</div>
<div class="dynamicbox" v-else>
<div v-for="item in dynamiclist" :key="item" class="itemlist">
<el-input size="small" disabled style="width: 220px" v-model="item.question"></el-input>
<div class="item-input" style="pointer-events: none;" v-html="item.editValue"></div>
</div>
</div>
<div>
<el-button v-if="keyType==0" :disabled="dynamiclist.length==wajueList.length" @click="addItemFun" icon="el-icon-plus" type="primary" size="mini" style="width: 120px;margin:20px 0;"></el-button>
</div>
<el-form-item label="插入节点:">
<el-button size="mini" type="primary" style="margin-left:8px;" v-for="(item,index) in taglist" :key="index" @click="insertTag(item,index)">{{item.label}}</el-button>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取 消</el-button>
<el-button type="primary" @click="saveFun">保 存</el-button>
</span>
</el-dialog>
</div>
</template>

<script>
export default {
data(){
return {
dynamiclist: [],
taglist:[
{
label: 'or (或)',
value: 'or'
},
{
label: 'near (临近)',
value: 'near'
},
{
label: 'after (后面)',
value: 'after'
},
{
label: 'and not (非)',
value: 'andnot'
},
// {
// label: 'answer (问题)',
// value: 'answer'
// },
],
form: {
keywordsName: '',
id: '',
keywordsId: '',
distance: 10,
originalExpression: ''
},
innerVisible:false,
innerVisible1:false,
dialogVisible: false,
value: '',
keyType: 0,
loading: false,
currentPage: 1,
tableData: [{}],
type: '0',
pageNum:1,
pageSize:10,
houseId:'',
total:0,
huashu: '',
wajueList: [],
level: ''
}
},
mounted() {
// this.houseId=localStorage.getItem("AitemId");
// this.getorgCode()
},
methods: {
// 校验是否已经选择过此标签
checkrepeat(){
this.wajueList.forEach(item=>{
item.disabled = false
})
this.wajueList.forEach(item=>{
this.dynamiclist.forEach(obj=>{
if(item.id==obj.markid){
item.disabled = true
}
})
})
},
addItemFun(){
this.dynamiclist.push({
editValue: '',//匹配模型正则表达式
markid:'',
level: '',
name:''
})
},
delItemFun(index){
this.wajueList.forEach(item=>{
if(item.id==this.dynamiclist[index].markid){
item.disabled = false
}
})
this.dynamiclist.splice(index,1)
document.getElementById('huashuModel').focus(); // 防止插入到外面,造成不可删除的操作
},

// 基于保存的光标插入内容 --用于失去焦点后再继续插入内容
insertContent(str) {
let selection, range = window._range;// 当前光标位置对象
if (!window.getSelection) {
range.pasteHTML(str);
range.collapse(false);
range.select();
} else {
selection = window.getSelection ? window.getSelection() : document.selection;
range.collapse(false);
let hasR = range.createContextualFragment(str);
let hasR_lastChild = hasR.lastChild;
while (hasR_lastChild && hasR_lastChild.nodeName.toLowerCase() == "br" && hasR_lastChild.previousSibling && hasR_lastChild.previousSibling.nodeName.toLowerCase() == "br") {
let e = hasR_lastChild;
hasR_lastChild = hasR_lastChild.previousSibling;
hasR.removeChild(e);
}
range.insertNode(hasR);
if (hasR_lastChild) {
range.setEndAfter(hasR_lastChild);
range.setStartAfter(hasR_lastChild);
}
selection.removeAllRanges();
selection.addRange(range);
}
},
// 失去焦点时保存光标位置,记录光标位置
saveRange: () => {
let selection = window.getSelection ? window.getSelection() : document.selection;
if (!selection.rangeCount) return;
let range = selection.createRange ? selection.createRange() : selection.getRangeAt(0);
window._range = range;
},
// 回显模型数据,Dialog 的内容是懒渲染的,即在第一次被打开之前,传入的默认 slot 不会被渲染到 DOM 上,so在 open 事件回调中进行
openModel(){
this.$nextTick(()=>{
let huashuModel = document.getElementById('huashuModel')
huashuModel.innerHTML = this.form.originalExpression
})
},
//格式化粘贴文本方法
onPaste(event) {
// var e = event || window.event
// // 阻止默认粘贴
// e.preventDefault();
// // 粘贴事件有一个clipboardData的属性,提供了对剪贴板的访问
// // clipboardData的getData(fomat) 从剪贴板获取指定格式的数据
// var text = (e.originalEvent || e).clipboardData.getData('text/plain') || prompt('在这里输入文本');
// //清除回车
// text = text.replace(/\[\d+\]|\n|\r/ig,"")
// // 插入
// document.execCommand("insertText", false, text);
let e = event || window.event
let types = event.clipboardData.types
// 粘贴事件有一个clipboardData的属性,提供了对剪贴板的访问
let flag = false
if (types && types.length > 0) {
types.forEach(ele => {
if (ele == 'Files') {
flag = true
}
})
}
if (flag) {
event.preventDefault()
}
},
myeditorenter(e) {
e.preventDefault();
},
// 插入节点
insertHtmlAtCaret(html) {
document.getElementById('huashuModel').focus();
var sel, range;
if (window.getSelection) {
// IE9 and non-IE
sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
range = sel.getRangeAt(0);
range.deleteContents();
// Range.createContextualFragment() would be useful here but is
// non-standard and not supported in all browsers (IE9, for one)
var el = document.createElement("div");
el.innerHTML = html;
var frag = document.createDocumentFragment(),
node, lastNode;
while ((node = el.firstChild)) {
lastNode = frag.appendChild(node);
}
range.insertNode(frag);
// Preserve the selection
if (lastNode) {
range = range.cloneRange();
range.setStartAfter(lastNode);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
}
}
} else if (document.selection && document.selection.type != "Control") {
// IE < 9
document.selection.createRange().pasteHTML(html);
}
},
// 拿问题获取所在的标签数据
findKeywordsById(keywordsId){
axios({
url: `${jypath}/zk/keywords/findKeywordsById`,
method: 'get',
params: {
houseId: this.houseId,
keywordsId: keywordsId,
level: 1
}
}).then(res => {
if (res.data.res == 1){
// console.log(res.data)
this.wajueList = res.data.obj
if(this.wajueList&&this.wajueList.length){
this.wajueList.forEach(item=>{
item.disabled = false
})
this.wajueList.forEach(item=>{
this.dynamiclist.forEach(obj=>{
if(item.id==obj.markid){
item.disabled = true
}
})
})
}
}
});
},
// 插入节点
insertTag(item,index){
if(index==4) {// answer
this.huashu = ''
this.innerVisible1 = true
}else{
if(window._range){
this.insertContent("<span contentEditable='false' style='color:red'>"+item.value+"</span><text>&nbsp;</text>");
}else{
this.insertHtmlAtCaret("<span contentEditable='false' style='color:red'>"+item.value+"</span><text>&nbsp;</text>");
}
}
},
// 处理标签,删除不需要的标签格式
delMark(str) {
const hasStr = (str) => {
let index = str.indexOf('<')
let index1 = str.indexOf('>')
if(index>0&&index1>0){
let replaceStr= str.substring(index, index1+1)
str= str.replace(replaceStr,'')
hasStr(str)
}
}
hasStr(str)
},
// 确认插入选择的话术
clickOK(){
this.innerVisible1 = false
if(!this.huashu) {this.$message.error('请选择话术'); return ;}
this.insertContent("<text>"+this.huashu+"</text><span contentEditable='false' style='color:red'>answer</span><text>&nbsp;</text>");
},
search(){
this.pageNum = 1
this.getorgCode()
},
//点击编辑按钮
editFun(item){
this.dialogVisible = true
this.form.id = item.id
if(this.keyType==0){// 挖掘话术类型
// 回显标签模型数据
if(item.answerList&&item.answerList.length){
this.dynamiclist = item.answerList.map(obj=>{
return {
id: obj.id,
mark: obj.level3Name?obj.level3Name:obj.level2Name,
level: obj.level3Id?3:2,
editValue: obj.originalExpression,
markid: obj.level3Id?obj.level3Id:obj.level2Id
}
})
// console.log(this.dynamiclist)
}else{
this.dynamiclist = []
}
this.form.keywordsName = item.level3Name
this.form.distance = item.distance||10
this.form.originalExpression = item.originalExpression||item.level3Name
this.level = item.level2Id?2:1
this.questionId = item.questionId
this.form.keywordsId = item.level2Id?item.level2Id:item.level1Id
this.findKeywordsById(item.level1Id)// 获取标签下拉数据
}else if(this.keyType==3){// 违禁词
this.form.keywordsName = item.level1Name
this.level = 1
this.form.distance = item.distance||10
this.form.originalExpression = item.originalExpression||item.level1Name
this.dynamiclist = []
}else{
this.form.keywordsId = item.level3Id?item.level3Id:item.level2Id
this.level = item.level3Id?3:2
this.form.keywordsName = item.level3Name||item.level2Name
this.form.distance = item.distance||10
this.form.originalExpression = item.originalExpression||item.level3Name||item.level2Name
if(item.answerList&&item.answerList.length){
this.dynamiclist = item.answerList.map(obj=>{
return {
question: obj.question,
editValue: obj.originalExpression
}
})
}else{
this.dynamiclist = []
}
}
},
// 删除列表
deleteFun(item){
this.$alert(item.keywordsName, '确定删除该条数据吗', {
confirmButtonText: '确定',
callback: action => {
axios({
url: `${jypath}/zk/keymodel/delKeywordsModel`,
method: 'get',
params: {
id: item.id,
houseId: this.houseId
}
}).then(data => {
if(data.data.res==1){
this.getorgCode()
}
}).catch((e)=>{
})
}
});
},
// 处理模型 关键词加#号
replaceFun(str){
let temp = str
temp = temp.replace(/<text>\&nbsp\;<\/text>/g,'');
temp = temp.replace(/\&nbsp\;/g,'');
temp = temp.replace(/<text>/g,'');
temp = temp.replace(/<\/text>/g,'');
temp = temp.replace(/<span contenteditable="false" style="color:red">/g,' #');// 后台返回是这样的,变了,需要也处理一下
temp = temp.replace(/<span style="color:red" contenteditable="false">/g,' #');
temp = temp.replace(/<\/br>/g,'')
temp = temp.replace(/<br>/g,'')
temp = temp.replace(/<\/span>/g,'#')
// console.log('处理前temp',temp);
this.delMark(temp);
// console.log(temp);
return temp
},
//修改保存标签模型
saveFun(){
if(this.cansave) return ; // 防止多次点击
this.cansave = true;
let text = document.getElementById('huashuModel');
let answerList = []
// console.log(text.innerHTML);
// console.log(text.innerText);
let temp = text.innerHTML
if(this.form.distance=='') {this.$message.error('请输入距离'); return;}
if(text.innerText=='') {this.$message.error('请输入标签模型'); return;}
if(this.keyType==0){
for (var i=0;i<this.dynamiclist.length;i++){
// 使用for循环判断可以跳出循环
if(!this.dynamiclist[i].markid) {this.$message.error('请完善要选择的标签'); return ;}
this.wajueList.map(item1=>{
if(item1.id==this.dynamiclist[i].markid){
this.dynamiclist[i].level = item1.level
this.dynamiclist[i].name = item1.name
}
})
let huashuModel = document.getElementById('huashuModel'+i);
if(huashuModel.innerText=='') {this.$message.error('请完善选择的标签模型'); return ;}
this.dynamiclist[i].editValue = huashuModel.innerHTML
this.dynamiclist[i].editText = huashuModel.innerText
}

answerList = this.dynamiclist.map(item=>{
return {
keyType: 2,
id: item.id||null,
level: item.level,
keywordsId: item.markid,
showFormatExpression: item.editText,//文本形式
formatExpression: this.replaceFun(item.editValue),// 问题表达式,关键词加#号
originalExpression: item.editValue // html==带span标签
}
})
}
axios({
url: `${jypath}/zk/keymodel/updateKeywordsModel`,
method: 'post',
data: {
id: this.form.id,
questionId: this.keyType==0?this.questionId:'',
houseId:this.houseId,
keyType: this.keyType,
level: this.level,
keywordsId: this.form.keywordsId,
keywordsName: this.form.keywordsName,
formatExpression: this.replaceFun(temp),// 问题表达式,关键词加#号
originalExpression: temp, // html==带span标签
answerList: answerList,
showFormatExpression: text.innerText,
distance: this.form.distance // 距离
}
}).then(data => {
this.cansave = false
this.dialogVisible = false
if(data.data.res==1){
this.$message.success(data.data.obj)
this.getorgCode()
}else{
this.$message.error(data.data.resMsg)
}
}).catch((e)=>{
this.cansave = false
this.dialogVisible = false
})
},
//清空筛选条件
Emptycondition(){
this.value='';
this.keyType = 0
this.pageNum =1;
this.getorgCode()
},
//初始化
getorgCode(){
this.loading= true
this.tableData=[];
axios({
url: `${jypath}/zk/keymodel/findKeywordsModel`,
method: 'get',
params: {
houseId:this.houseId,
pageNum:this.pageNum,
pageSize:this.pageSize,
keywordsName: this.value,
keyType: this.keyType
}
}).then(data => {
this.loading= false
if(data.data.res==1){
this.tableData=data.data.obj.results;
this.total=data.data.obj.totalRecord;
}
}).catch((e)=>{
this.loading= false
})
},
handleSizeChange(val) {
console.log("每页条"+ val);
this.pageSize=val;
this.getorgCode()
},
handleCurrentChange(val) {
console.log("当前页"+ val);
this.pageNum=val;
this.getorgCode()
}
}

}
</script>

<style lang="scss" scoped >
.box-center {
width: 100%;
padding: 5px 15px 40px;
min-width: 1000px;
}
.app-top {
margin-bottom: 20px;
width: 100%;
background: #ffffff;
box-shadow: 0px 0px 12px 0px rgba(0, 0, 0, 0.04);
border-radius: 4px;
padding: 15px;
}
.block{
width: 100%;
margin-top: 5px;
display: flex;
}
.blockbox{
margin-left: auto;
}
.editDiv{
width:100%;
min-height:60px;
max-height:120px;
overflow:auto;
margin-bottom:10px;
padding: 15px;
outline: none;
border:1px solid #409EFF;
border-radius: 10px;
}
.hidden-tooltip{
width: 330px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
position: relative;
}
.itemlist{
display: flex;
justify-content: space-between;
align-items: center;
margin: 20px 0;
}
.item-input{
width: 100%;
padding: 4px 10px;
outline: none;
border:1px solid #409EFF;
border-radius: 4px;
overflow:auto;
line-height: 24px;
min-height: 24px;
max-height: 100px;
margin: 0 10px;
}
.el-dialog{
width: 800px
}
.el-dialog__body{
max-height: 400px;
overflow: auto;
}
</style>

+ 811
- 0
src/views/Template/modelTodo.vue Dosyayı Görüntüle

@@ -0,0 +1,811 @@
<template>
<div class="box-center">
<div class="app-top">
<div class="control-tab">
<el-radio-group v-model="type" size="medium" @change="changeType">
<el-radio-button :label="1">待处理</el-radio-button>
<el-radio-button :label="0">已处理</el-radio-button>
</el-radio-group>
</div>
<div class="app-titel" style="margin-top: 5px">
<div class="label">更新时间:</div>
<el-date-picker
style="width: 270px;"
@change="confirmtime"
v-model="searchForm.customtime"
type="daterange"
range-separator="-"
value-format="yyyy-MM-dd"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
<div class="label">名称:
<el-input placeholder="处理人" v-model="searchForm.updateUserName" style="width:140px"></el-input>
</div>
<div class="label" v-if="type==0">处理人:
<el-input placeholder="处理人" v-model="searchForm.updateUserName" style="width:140px"></el-input>
</div>
<div class="label">项目名称:
<el-input placeholder="处理人" v-model="searchForm.updateUserName" style="width:140px"></el-input>
</div>
<div style="margin-left: 20px">
<el-button @click="Screening" type="primary">筛选</el-button>
</div>
</div>
</div>
<el-table
:data="tableData"
stripe
:header-cell-style="{
background: '#F7F8FA',
borderColor: '#E0E0E0',
color: '#606775',
}"
style="width: 100%;margin-top:20px"
>
<el-table-column
prop="createTime"
label="更新时间"
align="center"
>
</el-table-column>
<el-table-column prop="type" label="类型" align="center">
<template slot-scope="scope">
<!-- 0需求挖掘 1销讲词 2客户标签 3违禁 -->
<span v-if="scope.row.type==1">销讲话术</span>
<span v-if="scope.row.type==2">客户标签</span>
<span v-if="scope.row.type==0">挖掘话术</span>
<span v-if="scope.row.type==3">违禁词</span>
</template>
</el-table-column>
<el-table-column prop="level1Name" label="一级分类" align="center">
</el-table-column>
<el-table-column prop="name" label="名称" align="center">
</el-table-column>
<el-table-column prop="houseName" label="项目" align="center">
</el-table-column>
<el-table-column prop="updateTime" label="处理时间" align="center" v-if="type==0">
</el-table-column>
<el-table-column prop="updateUserName" label="处理人" v-if="type==0" align="center">
</el-table-column>
<el-table-column label="操作" align="center">
<template slot-scope="scope">
<div
style="color: #2671e2; cursor: pointer"
@click="editFun(scope.row)"
>
匹配模型
</div>
</template>
</el-table-column>
</el-table>
<div class="block">
<div class="blockbox">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="pageNum"
:page-sizes="[10, 20, 30, 40, 50, 100]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
>
</el-pagination>
</div>
</div>
<el-dialog
title="编辑模型"
@open="openModel"
:close-on-click-modal="false"
:visible.sync="dialogVisible"
>
<el-dialog
title="匹配规则"
:visible.sync="innerVisible"
append-to-body>
<div style="max-height: 430px;overflow: auto">
<p><b>匹配距离</b>:关键字匹配字数距离,主要针对near/after/w- 生效,w- 的字数统计从挖掘话术后,客户的第一句话开始统计;</p>
<p style="padding-left: 15px">例如:设置为10,面积 after 140</br>
命中话术:"想买一个面积在140平的"</br>
超出10个字,有关键字不命中话术参考:“房屋面积啊,你们这边有多大的,有没有140平左右的”;</p>
<p><b>or(或)</b>:多个关键词,匹配上1个就行</p>
<p style="padding-left: 15px">例如标签:自住</br>
匹配模型:自己住 or 给自己</br>
命中话术:我们自己住/给自己买的</p>
<p><b>and not(非)</b>:排除反面意思</p>
<p style="padding-left: 15px">例如:北京户口,</br>
匹配模型:是北京户口 and not 不是北京户口</br>
命中话术:我是北京户口;</br>
反面话术:我不是北京户口</p>
<p><b>near ( 临近)</b>:1个关键词前后一定范围出现过另一个关键词就算命中,有距离限制</p>
<p style="padding-left: 15px">例如标签:购房预算 140w</br>
匹配模型:预算 near 140</br>
命中话术:我的预算是140w或大概140w的购房预算</p>
<p><b>after(后面)</b>:一段文本出现2个关键词,并且按照先后的顺序即算命中,有距离限制</p>
<p style="padding-left: 15px">例如标签:意向面积 140平</br>
匹配模型: 面积 after 140 ,140在面积后面才算生效;</br>
命中话术:想买一个面积在140平的;</p>
<p><b>answer(挖掘话术业务)</b>:顾问执行了挖掘话术,客户回答结果,有距离限制,从客户话术文本开始</p>
<p style="padding-left: 15px">例如标签:北京户口</br>
匹配模型:(w-是 or w-有) and not (w-不是 or w-没有)</br>
命中话术:销售:你有北京户口吗?</br>
客户:有的;</p>
<p><b>-n(命中距离)</b>:near/after-n ,单个near/after的匹配距离,n大于0,小于500</p>
<p style="padding-left: 15px">例如 :面积 after-10 140</br>
10个字内,命中话术参考:"想买一个面积在140平的"</br>
超出10个字,有关键字不命中话术参考:“房屋面积啊,你们这边有多大的,有没有140平左右的”;</p>
<p><b>注意</b>:-n 与 near/after/answer 之间<b>不能有空格</b>;</p></br>
<p><b>注意</b>:优先匹配关键词可以排在前面;</p>
</div>
</el-dialog>
<el-form ref="form" size="mini" :inline="true" :model="form" label-position="right">
<el-form-item :label="keyType==0?'挖掘话术:':'标签名称:'">
<div style="max-width:400px;min-width: 200px;font-weight: bold">{{form.keywordsName}}</div>
</el-form-item>
<el-form-item label="场景描述:" >
<div v-if="form.sceneDesc" style="width:500px;border: 1px dashed #ccc;padding:5px 10px;line-height: 25px"> {{form.sceneDesc}}</div>
<div v-else>暂无描述</div>
</el-form-item>
<el-form-item label="after,near,answer 匹配距离:">
<el-input-number v-model="form.distance" controls-position="right" :min="0" :max="500"></el-input-number></el-input><span style="color:red;margin-left:10px">*请输入0~500内的整数</span> <el-button type="text" style="margin-left:30px" @click="innerVisible=true">规则说明</el-button>
</el-form-item>
<div contentEditable="true"
@click="myeditorenter($event)"
@keypress.enter="myeditorenter($event)"
@blur="saveRange"
class="editDiv"
id="huashuModel">
</div>
<div v-if="keyType==0||dynamiclist.length">{{keyType==0?'匹配标签:':'需求挖掘匹配:'}}</div>
<!-- keyType 0需求挖掘 1销讲词 2客户标签 3违禁 -->
<div class="dynamicbox" v-if="keyType==0">
<div v-for="(item,index) in dynamiclist" :key="item" class="itemlist">
<el-select size="small" v-model="item.markid" @change="checkrepeat" placeholder="标签" clearable>
<el-option v-for="(mark,i) in wajueList" :disabled="mark.disabled" :key="i" :label="mark.name" :value="mark.id"></el-option>
</el-select>
<div contentEditable="true"
@click="myeditorenter($event)"
@keypress.enter="myeditorenter($event)"
@blur="saveRange"
class="item-input"
v-html="item.editValue"
:id="'huashuModel'+index"></div>
<el-button size="small" type="primary" @click="delItemFun(index)">删除</el-button>
</div>
</div>
<div class="dynamicbox" v-else>
<div v-for="item in dynamiclist" :key="item" class="itemlist">
<el-input size="small" disabled style="width: 220px" v-model="item.question"></el-input>
<div class="item-input" style="pointer-events: none;" v-html="item.editValue"></div>
</div>
</div>
<div>
<el-button :disabled="dynamiclist.length==wajueList.length" v-if="keyType==0" @click="addItemFun" icon="el-icon-plus" type="primary" size="mini" style="width: 120px;margin:20px 0;"></el-button>
</div>
<el-form-item label="插入节点:">
<el-button size="mini" type="primary" plain style="margin-left:8px;" v-for="(item,index) in taglist" :key="index" @click="insertTag(item,index)">{{item.label}}</el-button>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取 消</el-button>
<el-button type="primary" @click="saveFun">保 存</el-button>
</span>
</el-dialog>
</div>
</template>

<script>
export default {
data() {
return {
huashu: '',
wajueList: [],
level: '',
innerVisible:false,
dialogVisible: false,
dynamiclist: [],
taglist:[
{
label: 'or (或)',
value: 'or'
},
{
label: 'near (临近)',
value: 'near'
},
{
label: 'after (后面)',
value: 'after'
},
{
label: 'and not (非)',
value: 'andnot'
},
// {
// label: '~ (至)',
// value: '~'
// },
],
form: {
keywordsName: '',
id: '',
keywordsId: '',
distance: 10,
sceneDesc:'',
originalExpression: ''
},
searchForm:{
updateUserName: '',
houseName: '',
name:'',
customtime: [],
startTime:'',//搜索开始时间
endTime:'' //搜索结束时间
},
tableData: [{}],
type: 1,
keyType: '',
pageSize: 10,
total: 0,
pageNum: 1,
currentHouseId: ''
};
},
mounted() {
// this.houseId = localStorage.getItem("AitemId");
// this.gettableList()
},
methods: {
// keytype =0,获取需求挖掘类型的数据回显接口
wajuehuixian(){
// 获取模型数据回显
axios({
url: `${jypath}/zk/keymodel/findById`,
method: 'get',
params: {
houseId: this.currentHouseId,
level: this.level,
keyType: 0,
questionId: this.questionId,
keywordsId: this.form.keywordsId
}
}).then(res => {
this.dialogVisible = true
if (res.data.res == 1) {
let obj = res.data.obj
if(obj!=null){
this.form.distance=obj.distance||10
this.form.originalExpression= obj.originalExpression
this.form.id = obj.id
this.form.sceneDesc = obj.desc||''

// 回显标签模型数据
if(obj.answerList&&obj.answerList.length){
this.dynamiclist = obj.answerList.map(obj1=>{
return {
id: obj1.id,
mark: obj1.name,
level: obj1.level3Id?3:2,
editValue: obj1.originalExpression,
markid:obj1.level3Id?obj1.level3Id:obj1.level2Id
}
})
this.wajueList.forEach(item=>{
this.dynamiclist.forEach(obj=>{
if(item.id==obj.markid){
item.disabled = true
}
})
})
// console.log(this.dynamiclist)
}else{
this.dynamiclist = []
}
}else{
this.form.distance=10
this.form.originalExpression= ''
this.form.id =''
this.form.sceneDesc = ''
this.dynamiclist = []
}
}else{
this.form.sceneDesc = ''
this.form.distance = 10
this.form.originalExpression = data.name
}
this.$nextTick(()=>{
let huashuModel = document.getElementById('huashuModel')
huashuModel.innerHTML = this.form.originalExpression
})
}).catch((e)=>{
this.dialogVisible = true
})
},
otherhuixian(type){
// 获取模型数据回显
axios({
url: `${jypath}/zk/keymodel/findById`,
method: 'get',
params: {
houseId: this.currentHouseId,
level: type==3?1:this.level,
keyType: type,
keywordsId: this.form.keywordsId
}
}).then(res => {
this.dialogVisible = true
if (res.data.res == 1) {
let obj = res.data.obj
if(obj!=null){
this.form.distance=obj.distance||10
this.form.originalExpression= obj.originalExpression||''
this.form.id =obj.id||''
this.form.sceneDesc = obj.desc||''
// 回显标签模型数据
if(obj.answerList&&obj.answerList.length){
this.dynamiclist = obj.answerList.map(cont=>{
return {
question: cont.question,
editValue: cont.originalExpression
}
})
console.log(this.dynamiclist)
}else{
this.dynamiclist = []
}
}else{
this.form.distance=10
this.form.originalExpression= ''
this.form.id =''
this.form.sceneDesc = ''
}
}else{
this.form.distance = 10
this.form.originalExpression = ''
this.form.sceneDesc = ''
}
this.$nextTick(()=>{
let huashuModel = document.getElementById('huashuModel')
huashuModel.innerHTML = this.form.originalExpression
})
}).catch((e)=>{
this.dialogVisible = true
})
},
//点击编辑按钮
async editFun(item){
this.keyType = item.type
this.todoId = item.id
this.level = item.level
this.form.keywordsId = item.keywordsId
this.currentHouseId = item.houseId
this.form.distance = item.distance||10
let level1Id = item.level1
this.form.keywordsName = item.name
await this.findKeywordsById(level1Id)// 获取标签下拉数据
if(item.type==0){// 挖掘话术类型
this.questionId = item.wordId
this.wajuehuixian(level1Id)
}else{
this.otherhuixian(item.type)
}
},
// 处理标签,删除不需要的标签格式
delMark(str) {
const hasStr = (str) => {
let index = str.indexOf('<')
let index1 = str.indexOf('>')
if(index>0&&index1>0){
let replaceStr= str.substring(index, index1+1)
str= str.replace(replaceStr,'')
hasStr(str)
}
}
hasStr(str)
},
// 处理模型 关键词加#号
replaceFun(str){
let temp = str
temp = temp.replace(/<text>\&nbsp\;<\/text>/g,'');
temp = temp.replace(/\&nbsp\;/g,'');
temp = temp.replace(/<text>/g,'');
temp = temp.replace(/<\/text>/g,'');
temp = temp.replace(/<span contenteditable="false" style="color:red">/g,' #');// 后台返回是这样的,变了,需要也处理一下
temp = temp.replace(/<span style="color:red" contenteditable="false">/g,' #');
temp = temp.replace(/<\/span>/g,'#')
// console.log(temp);
// console.log('截取前',temp);
this.delMark(temp);
return temp
},
//修改保存标签模型
saveFun(){
if(this.cansave) return ; // 防止多次点击
this.cansave = true;
let text = document.getElementById('huashuModel');
let answerList = []
// console.log(text.innerHTML);
// console.log(text.innerText);
let temp = text.innerHTML
if(this.form.distance=='') {this.$message.error('请输入距离'); return;}
if(text.innerText=='') {this.$message.error('请输入标签模型'); return;}
if(this.keyType==0){
for (var i=0;i<this.dynamiclist.length;i++){
if(!this.dynamiclist[i].markid) {this.$message.error('请完善要选择的标签'); return ;}
this.wajueList.map(item1=>{
if(item1.id==this.dynamiclist[i].markid){
this.dynamiclist[i].level = item1.level
this.dynamiclist[i].name = item1.name
}
})
let huashuModel = document.getElementById('huashuModel'+i);
if(huashuModel.innerText=='') {this.$message.error('请完善选择的标签模型'); return ;}
this.dynamiclist[i].editValue = huashuModel.innerHTML
this.dynamiclist[i].editText = huashuModel.innerText
}
answerList = this.dynamiclist.map(item=>{
return {
keyType: 2,
id: item.id||null,
level: item.level,
keywordsId: item.markid,
showFormatExpression: item.editText,//文本形式
formatExpression: this.replaceFun(item.editValue),// 问题表达式,关键词加#号
originalExpression: item.editValue // html==带span标签
}
})
}
axios({
url: `${jypath}/zk/keymodel/updateKeywordsModel`,
method: 'post',
data: {
id:this.form.id,
questionId: this.keyType==0?this.questionId:'',
houseId:this.currentHouseId,
keyType: this.keyType,
level: this.keyType==3?1:this.level,
todoId: this.todoId,
keywordsId: this.form.keywordsId,
keywordsName: this.form.keywordsName,
formatExpression: this.replaceFun(temp),// 问题表达式,关键词加#号
originalExpression: temp, // html==带span标签
answerList: answerList,
showFormatExpression: text.innerText,
}
}).then(data => {
this.cansave = false;
this.dialogVisible = false
if(data.data.res==1){
this.$message.success(data.data.obj)
this.gettableList()
}else{
this.$message.error(data.data.resMsg)
}
}).catch((e)=>{
this.cansave = false;
this.dialogVisible = false
})
},
// 校验是否已经选择过此标签
checkrepeat(){
this.wajueList.forEach(item=>{
item.disabled = false
})
this.wajueList.forEach(item=>{
this.dynamiclist.forEach(obj=>{
if(item.id==obj.markid){
item.disabled = true
}
})
})
},
addItemFun(){
this.dynamiclist.push({
editValue: '',//匹配模型正则表达式
markid:'',
level: '',
name:''
})
},

delItemFun(index){
this.wajueList.forEach(item=>{
if(item.id==this.dynamiclist[index].markid){
item.disabled = false
}
})
this.dynamiclist.splice(index,1);
document.getElementById('huashuModel').focus();
},
// 基于保存的光标插入内容 --用于失去焦点后再继续插入内容
insertContent(str) {
let selection, range = window._range;// 当前光标位置对象
if (!window.getSelection) {
range.pasteHTML(str);
range.collapse(false);
range.select();
} else {
selection = window.getSelection ? window.getSelection() : document.selection;
range.collapse(false);
let hasR = range.createContextualFragment(str);
let hasR_lastChild = hasR.lastChild;
while (hasR_lastChild && hasR_lastChild.nodeName.toLowerCase() == "br" && hasR_lastChild.previousSibling && hasR_lastChild.previousSibling.nodeName.toLowerCase() == "br") {
let e = hasR_lastChild;
hasR_lastChild = hasR_lastChild.previousSibling;
hasR.removeChild(e);
}
range.insertNode(hasR);
if (hasR_lastChild) {
range.setEndAfter(hasR_lastChild);
range.setStartAfter(hasR_lastChild);
}
selection.removeAllRanges();
selection.addRange(range);
}
},
// 失去焦点时保存光标位置,记录光标位置
saveRange: () => {
let selection = window.getSelection ? window.getSelection() : document.selection;
if (!selection.rangeCount) return;
let range = selection.createRange ? selection.createRange() : selection.getRangeAt(0);
window._range = range;
},
// 回显模型数据,Dialog 的内容是懒渲染的,即在第一次被打开之前,传入的默认 slot 不会被渲染到 DOM 上,so在 open 事件回调中进行
openModel(){
this.$nextTick(()=>{
let huashuModel = document.getElementById('huashuModel')
huashuModel.innerHTML = this.form.originalExpression
})
},
//格式化粘贴文本方法
onPaste(event) {
var e = event || window.event
// 阻止默认粘贴
e.preventDefault();
// 粘贴事件有一个clipboardData的属性,提供了对剪贴板的访问
// clipboardData的getData(fomat) 从剪贴板获取指定格式的数据
var text = (e.originalEvent || e).clipboardData.getData('text/plain') || prompt('在这里输入文本');
//清除回车
text = text.replace(/\[\d+\]|\n|\r/ig,"")
// 插入
document.execCommand("insertText", false, text);
},
myeditorenter(e) {
e.preventDefault();
},
// 插入节点
insertHtmlAtCaret(html) {
document.getElementById('huashuModel').focus();
var sel, range;
if (window.getSelection) {
// IE9 and non-IE
sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
range = sel.getRangeAt(0);
range.deleteContents();
// Range.createContextualFragment() would be useful here but is
// non-standard and not supported in all browsers (IE9, for one)
var el = document.createElement("div");
el.innerHTML = html;
var frag = document.createDocumentFragment(),
node, lastNode;
while ((node = el.firstChild)) {
lastNode = frag.appendChild(node);
}
range.insertNode(frag);
// Preserve the selection
if (lastNode) {
range = range.cloneRange();
range.setStartAfter(lastNode);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
}
}
} else if (document.selection && document.selection.type != "Control") {
// IE < 9
document.selection.createRange().pasteHTML(html);
}
},
// 拿问题获取所在的标签数据
findKeywordsById(level1Id){
this.wajueList = []
axios({
url: `${jypath}/zk/keywords/findKeywordsById`,
method: 'get',
params: {
houseId: this.currentHouseId,
keywordsId: level1Id,
level: 1
}
}).then(res => {
if (res.data.res == 1){
console.log(res.data)
this.wajueList = res.data.obj
if(this.wajueList&&this.wajueList.length){
this.wajueList.forEach(item=>{
item.disabled = false
})
// this.wajueList.forEach(item=>{
// this.dynamiclist.forEach(obj=>{
// if(item.id==obj.markid){
// item.disabled = true
// }
// })
// })
}
}
});
},
// 插入节点
insertTag(item,index){
if(window._range){
this.insertContent("<span contentEditable='false' style='color:red'>"+item.value+"</span><text>&nbsp;</text>");
}else{
this.insertHtmlAtCaret("<span contentEditable='false' style='color:red'>"+item.value+"</span><text>&nbsp;</text>");
}
},
searchFun(){
this.tableData = []
this.pageNum = 1
this.gettableList()
},
//时间选择
confirmtime() {
this.searchForm.startTime = this.searchForm.customtime[0];
this.searchForm.endTime = this.searchForm.customtime[1];
},
Emptycondition(){
this.searchForm.updateUserName = ''
this.searchForm.houseName = ''
this.searchForm.name = ''
this.searchForm.startTime = ''
this.searchForm.endTime = ''
this.searchForm.customtime = []
this.gettableList()
},
changeType(){
this.searchFun()
},
gettableList() {
axios({
url: `${jypath}/zk/todo/list`,
method: "get",
params: {
pageNum: this.pageNum,
pageSize: this.pageSize,
houseName: this.searchForm.houseName,
updateUserName: this.searchForm.updateUserName,
name: this.searchForm.name,
status: this.type, //status 状态 0已处理 1未处理
startTime: this.searchForm.startTime,
endTime: this.searchForm.endTime
},
}).then((data) => {
console.log(data);
if (data.data.res == 1) {
this.tableData = data.data.obj.results||[]
this.total = data.data.obj.totalRecord
}else{
this.tableData = []
}
});
},
handleSizeChange(val) {
console.log("每页条" + val);
this.pageSize = val;
this.gettableList();
},
handleCurrentChange(val) {
console.log("当前页" + val);
this.pageNum = val;
this.gettableList();
}
}
};
</script>

<style scoped="scoped" lang="scss" >
// /deep/ .el-table__header-wrapper{
// thead{
// tr{
// th{
// background: #F5F7FA;
// color: #333333;
// }
// }
// }
// }



.box-center {
width: 100%;
padding: 5px 15px 40px;
min-width: 1000px;
}


.app-top {
width: 100%;
background: #ffffff;
box-shadow: 0px 0px 12px 0px rgba(0, 0, 0, 0.04);
border-radius: 4px;
padding-top: 15px;
padding-bottom: 15px;

.app-titel {
width: 100%;
display: flex;
align-items: center;

.label {
font-size: 14px;
font-weight: 400;
color: #32363d;
line-height: 14px;
margin-left: 15px;
// min-width: 100px;
text-align: right;
}
}
.control-tab{
margin-left: 15px;
}
}
.button1 {
padding-left: 16px;
padding-right: 16px;
height: 32px;
line-height: 32px;
background: #2671e2;
border-radius: 2px;
color: #ffffff;
font-size: 16px;
border: none;
cursor: pointer;
}
.el-pagination{
display: flex;
justify-content: flex-end;
margin-top: 20px;
}
.editDiv{
width:100%;
min-height:60px;
max-height:120px;
overflow:auto;
margin-bottom:10px;
padding: 15px;
outline: none;
border:1px solid #409EFF;
border-radius: 10px;
box-sizing: border-box;
}
.itemlist{
display: flex;
justify-content: space-between;
align-items: center;
margin: 20px 0;
}
.item-input{
width: 100%;
padding: 4px 10px;
outline: none;
border:1px solid #409EFF;
border-radius: 4px;
overflow:auto;
line-height: 24px;
min-height: 24px;
max-height: 100px;
margin: 0 10px;
}
.el-dialog{
width: 800px
}
.el-dialog__body{
max-height: 400px;
overflow: auto;
}

</style>

Yükleniyor…
İptal
Kaydet