@@ -63,6 +63,7 @@ | |||||
</span> | </span> | ||||
<span style="margin-left: 10px" v-if="data.isInterval!=0"> | <span style="margin-left: 10px" v-if="data.isInterval!=0"> | ||||
<el-button class="editor" type="text" size="mini" @click="() => ddeditor(node, data)">编辑</el-button> | <el-button class="editor" type="text" size="mini" @click="() => ddeditor(node, data)">编辑</el-button> | ||||
<el-button class="editor" type="text" size="mini" @click="() => moxingEdit(node, data)">编辑模型</el-button> | |||||
</span> | </span> | ||||
</span> | </span> | ||||
@@ -81,14 +82,15 @@ | |||||
<span v-if="data.level == 3" style="display: flex; align-items: center" > | <span v-if="data.level == 3" style="display: flex; align-items: center" > | ||||
<span style="margin-left: 10px"> | <span style="margin-left: 10px"> | ||||
<el-button class="editorcd" type="text" size="mini" @click="moxingEdit(node, data)" >编辑模型</el-button> | |||||
</span> | |||||
<span style="margin-left: 10px"> | |||||
<el-button class="editorcd" type="text" size="mini" @click="ddeditor(node, data)" >编辑</el-button> | <el-button class="editorcd" type="text" size="mini" @click="ddeditor(node, data)" >编辑</el-button> | ||||
</span> | </span> | ||||
<span style="margin-left: 10px"> | <span style="margin-left: 10px"> | ||||
<el-button class="remove" type="text" size="mini" @click="remove(node, data)" >删除</el-button> | <el-button class="remove" type="text" size="mini" @click="remove(node, data)" >删除</el-button> | ||||
</span> | </span> | ||||
</span> | </span> | ||||
</span> | </span> | ||||
</el-tree> | </el-tree> | ||||
</div> | </div> | ||||
@@ -166,8 +168,6 @@ | |||||
</span> | </span> | ||||
</el-dialog> | </el-dialog> | ||||
<!--?编辑弹框?--> | <!--?编辑弹框?--> | ||||
<el-dialog title="编辑" :visible.sync="isshowage" :center="true" width="400px"> | <el-dialog title="编辑" :visible.sync="isshowage" :center="true" width="400px"> | ||||
<div | <div | ||||
@@ -263,7 +263,87 @@ | |||||
<el-button type="primary" @click="addclick()">确 定</el-button> | <el-button type="primary" @click="addclick()">确 定</el-button> | ||||
</span> | </span> | ||||
</el-dialog> | </el-dialog> | ||||
<!-- 匹配模型 --> | |||||
<el-dialog | |||||
title="编辑模型" | |||||
@open="openModel" | |||||
:close-on-click-modal="false" | |||||
:visible.sync="moxingVisible" | |||||
> | |||||
<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>w-(挖掘话术业务)</b>:顾问执行了挖掘话术,客户回答结果,有距离限制,从客户话术文本开始</p> | |||||
<p style="padding-left: 15px">例如标签:北京户口</br> | |||||
匹配模型:(w-是 or w-有) and not (w-不是 or w-没有) | |||||
命中话术:销售:你有北京户口吗?</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="标签名称:"> | |||||
<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,w- 匹配距离:"> | |||||
<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="dynamiclist.length">需求挖掘匹配:</div> | |||||
<div class="dynamicbox"> | |||||
<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> | |||||
<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="moxingVisible = false">取 消</el-button> | |||||
<el-button type="primary" @click="saveFun">保 存</el-button> | |||||
</span> | |||||
</el-dialog> | |||||
</div> | </div> | ||||
</div> | </div> | ||||
@@ -274,6 +354,39 @@ import { mapGetters } from "vuex"; | |||||
export default { | export default { | ||||
data() { | data() { | ||||
return { | return { | ||||
dynamiclist: [], | |||||
taglist:[ | |||||
{ | |||||
label: 'or (或)', | |||||
value: 'or' | |||||
}, | |||||
{ | |||||
label: 'near (临近)', | |||||
value: 'near' | |||||
}, | |||||
{ | |||||
label: 'after (后面)', | |||||
value: 'after' | |||||
}, | |||||
{ | |||||
label: 'and not (非)', | |||||
value: 'and not' | |||||
}, | |||||
// { | |||||
// label: '~ (至)', | |||||
// value: '~' | |||||
// }, | |||||
], | |||||
moxingVisible: false, | |||||
innerVisible: false, | |||||
form: { | |||||
keywordsName: '', | |||||
id: '', | |||||
keywordsId: '', | |||||
distance: 10, | |||||
sceneDesc:'', | |||||
originalExpression: '' | |||||
}, | |||||
formLabelWidth: "120px", | formLabelWidth: "120px", | ||||
dialogFormVisible: false, | dialogFormVisible: false, | ||||
dialogVisible: false, | dialogVisible: false, | ||||
@@ -320,6 +433,222 @@ export default { | |||||
this.zkhousePage(); | this.zkhousePage(); | ||||
}, | }, | ||||
methods: { | methods: { | ||||
// 基于保存的光标插入内容 --用于失去焦点后再继续插入内容 | |||||
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); | |||||
} | |||||
}, | |||||
// 点击插入标签 | |||||
insertTag(item,index){ | |||||
if(window._range){ | |||||
this.insertContent("<span contentEditable='false' style='color:red'>"+item.value+"</span><text> </text>"); | |||||
}else{ | |||||
this.insertHtmlAtCaret("<span contentEditable='false' style='color:red'>"+item.value+"</span><text> </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) | |||||
}, | |||||
// 处理模型 关键词加#号 | |||||
replaceFun(str){ | |||||
let temp = str | |||||
temp = temp.replace(/<text>\ \;<\/text>/g,''); | |||||
temp = temp.replace(/\ \;/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); | |||||
this.delMark(temp); | |||||
return temp | |||||
}, | |||||
// 模型保存 | |||||
saveFun(){ | |||||
let text = document.getElementById('huashuModel'); | |||||
// 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;} | |||||
axios({ | |||||
url: `${jypath}/zk/keymodel/updateKeywordsModel`, | |||||
method: 'post', | |||||
data: { | |||||
id: this.form.id, | |||||
houseId: this.houseId, | |||||
level: this.level, | |||||
keywordsId: this.form.keywordsId, | |||||
keywordsName: this.form.keywordsName, | |||||
answerList: [], | |||||
keyType: 2, | |||||
formatExpression: this.replaceFun(temp),// 问题表达式 | |||||
originalExpression: temp,// html数据 | |||||
showFormatExpression: text.innerText, | |||||
distance: this.form.distance // * 距离 | |||||
} | |||||
}).then(data => { | |||||
this.moxingVisible = false | |||||
if(data.data.res==1){ | |||||
this.$message.success(data.data.obj) | |||||
this.getorgCode() | |||||
}else{ | |||||
this.$message.error(data.data.resMsg) | |||||
} | |||||
}).catch((e)=>{ | |||||
this.moxingVisible = false | |||||
}) | |||||
}, | |||||
moxingEdit(node, data){ | |||||
// console.log(data) | |||||
this.level = data.level | |||||
this.form.keywordsName = data.endName?data.name+'~'+data.endName:data.name | |||||
this.form.keywordsId = data.keywordsId | |||||
this.form.sceneDesc = data.desc ||'' | |||||
// 获取模型数据回显 | |||||
axios({ | |||||
url: `${jypath}/zk/keymodel/findById`, | |||||
method: 'get', | |||||
params: { | |||||
houseId: this.houseId, | |||||
level: this.level, | |||||
keyType: 2, | |||||
keywordsId: data.keywordsId | |||||
} | |||||
}).then(res => { | |||||
this.moxingVisible = 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 | |||||
// 回显标签模型数据 | |||||
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.keywordsName | |||||
this.form.id ='' | |||||
} | |||||
}else{ | |||||
this.form.distance = 10 | |||||
this.form.originalExpression = this.form.keywordsName | |||||
} | |||||
this.$nextTick(()=>{ | |||||
let huashuModel = document.getElementById('huashuModel') | |||||
huashuModel.innerHTML = this.form.originalExpression | |||||
}) | |||||
}).catch((e)=>{ | |||||
this.moxingVisible = true | |||||
}) | |||||
}, | |||||
addclick(){ | addclick(){ | ||||
var orgCode=''; | var orgCode=''; | ||||
if(localStorage.getItem("orgType") == 3){ | if(localStorage.getItem("orgType") == 3){ | ||||
@@ -656,6 +985,17 @@ export default { | |||||
/deep/.el-tree-node__content{ | /deep/.el-tree-node__content{ | ||||
overflow: hidden; | overflow: hidden; | ||||
} | } | ||||
.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-center { | .box-center { | ||||
width: 100%; | width: 100%; | ||||
padding: 15px 15px 20px; | padding: 15px 15px 20px; | ||||
@@ -310,6 +310,18 @@ | |||||
style="flex-shrink: 0; width: 24px; height: 24px" | style="flex-shrink: 0; width: 24px; height: 24px" | ||||
/> | /> | ||||
</div> | </div> | ||||
<!-- 1 客户画像 2销讲词 3违禁 4 需求挖掘 --> | |||||
<div class="showmark"> | |||||
<!-- <div class="showmark" v-if="item.types"> --> | |||||
<div class="mark-item" v-for="(subitem,i) in 3" :key="i"> | |||||
<img class="markicon" :src="imgArr[1]"/> | |||||
<p class="marktext">dcdc</p> | |||||
</div> | |||||
<!-- <div class="mark-item" v-for="(subitem,i) in dealTypes(item.types)" :key="i"> | |||||
<img v-if="dealword(subitem)[0]" class="markicon" :src="imgArr[dealword(subitem)[0]]"/> | |||||
<p class="marktext" :style="dealword(subitem)[0]==3?'color:#E7483C':'color:#3E50E8'">{{dealword(subitem)[1]}}</p> | |||||
</div> --> | |||||
</div> | |||||
</div> | </div> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
@@ -463,52 +475,17 @@ | |||||
height: 100px; | height: 100px; | ||||
display: flex; | display: flex; | ||||
flex-direction: column; | flex-direction: column; | ||||
justify-content: space-between; | |||||
border-bottom: 1px solid #e0e0e0; | border-bottom: 1px solid #e0e0e0; | ||||
" | " | ||||
> | > | ||||
<div | |||||
style=" | |||||
width: 100%; | |||||
height: 44px; | |||||
display: flex; | |||||
align-items: center; | |||||
" | |||||
> | |||||
<div | |||||
style="flex: 1; font-size: 16px; color: #333333; text-indent: 5%" | |||||
> | |||||
销讲执行 | |||||
</div> | |||||
</div> | |||||
<div class="pingfenbox"> | |||||
<el-button | |||||
:class="{ activecllasscet: zhixingcenterindex == 0 }" | |||||
type="" | |||||
style=" | |||||
height: 32px; | |||||
border-right: none; | |||||
border-radius: 5px 0 0 5px; | |||||
" | |||||
@click="recordclick(0)" | |||||
>销讲总执行率{{ userinformationlist.fraction || 0 }}%</el-button | |||||
> | |||||
<el-button | |||||
:class="{ activecllasscet: zhixingcenterindex == 1 }" | |||||
type="" | |||||
style=" | |||||
margin-left: 0px; | |||||
height: 32px; | |||||
width: 126.5px; | |||||
border-left: none; | |||||
border-radius: 0 5px 5px 0; | |||||
" | |||||
@click="recordclick(1)" | |||||
>禁忌执行</el-button | |||||
> | |||||
</div> | |||||
<div style="margin: 10px 0 20px;font-size: 16px; color: #333333; text-indent: 5%"> 销讲执行 </div> | |||||
<el-radio-group v-model="zhixingcenterindex" size="small"> | |||||
<el-radio-button :label="0" >销讲总执行率{{ userinformationlist.fraction || 0 }}%</el-radio-button> | |||||
<el-radio-button :label="1">禁忌执行</el-radio-button> | |||||
<el-radio-button :label="2">需求挖掘率</el-radio-button> | |||||
</el-radio-group> | |||||
</div> | </div> | ||||
<div v-if="zhixingcenterindex == 0"> | <div v-if="zhixingcenterindex == 0"> | ||||
<div | <div | ||||
style=" | style=" | ||||
@@ -535,6 +512,7 @@ | |||||
</div> | </div> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
<div id="zxldiv" v-if="zhixingcenterindex == 0"> | <div id="zxldiv" v-if="zhixingcenterindex == 0"> | ||||
<div class="zxlBox" v-for="(item, index) in ratelist" :key="index"> | <div class="zxlBox" v-for="(item, index) in ratelist" :key="index"> | ||||
@@ -630,6 +608,105 @@ | |||||
{{ index + 1 }}、{{ item }} | {{ index + 1 }}、{{ item }} | ||||
</div> | </div> | ||||
</div> | </div> | ||||
<!-- 需求挖掘 --> | |||||
<div v-if="zhixingcenterindex == 2"> | |||||
<div | |||||
style=" | |||||
width: 100%; | |||||
height: 44px; | |||||
border-bottom: 1px solid #e0e0e0; | |||||
display: flex; | |||||
align-items: center; | |||||
background: #f0f6ff; | |||||
" | |||||
> | |||||
<div style="width: 40%;font-size: 16px;color: #333333;text-indent: 20px;" > 指标 </div> | |||||
<div style="width: 20%; flex: 1; font-size: 16px; color: #333333">执行</div> | |||||
<div style="width: 40%; flex: 1; font-size: 16px; color: #333333"> | |||||
匹配标签 | |||||
</div> | |||||
</div> | |||||
<div id="zxldiv" v-if="zhixingcenterindex == 2"> | |||||
<div class="zxlBox" v-for="(item, index) in ratelist" :key="index"> | |||||
<div class="zxlLev1" @click="changeshow(index)"> | |||||
<div style="width: 40%" class="zxlLev1box"> | |||||
{{ item.name }} | |||||
</div> | |||||
<div style="width: 60%; display: flex; align-items: center"> | |||||
<div | |||||
style=" | |||||
width: 100%; | |||||
height: 16px; | |||||
background: #c8e8e4; | |||||
border-radius: 8px; | |||||
position: relative; | |||||
" | |||||
> | |||||
<div | |||||
:style="{ | |||||
width: | |||||
( | |||||
(item.ratepercent / item.rate).toFixed(2) * 100 | |||||
).toFixed() + '%', | |||||
height: '16px', | |||||
background: '#07B79D', | |||||
borderRadius: '8px', | |||||
}" | |||||
></div> | |||||
<span | |||||
style=" | |||||
position: absolute; | |||||
top: -12px; | |||||
font-size: 10px; | |||||
right: 12px; | |||||
color: #000; | |||||
" | |||||
>{{ | |||||
( | |||||
(item.ratepercent / item.rate).toFixed(2) * 100 | |||||
).toFixed() | |||||
}}%</span | |||||
> | |||||
</div> | |||||
<div style="width: 40px; text-align: center"> | |||||
<i | |||||
v-if="item.show == true" | |||||
style="font-size: 18px" | |||||
class="el-icon-arrow-up" | |||||
></i> | |||||
<i | |||||
v-if="item.show == false" | |||||
style="font-size: 18px" | |||||
class="el-icon-arrow-down" | |||||
></i> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
<div | |||||
class="zxlLev2" | |||||
v-if="item.show" | |||||
v-for="(subitem, i) in item.children" | |||||
:key="i" | |||||
> | |||||
<div | |||||
style=" | |||||
width: 40%; | |||||
overflow: hidden; | |||||
text-overflow: ellipsis; | |||||
white-space: nowrap; | |||||
" | |||||
class="zxlLev2tit" | |||||
> | |||||
{{ subitem.name }} | |||||
</div> | |||||
<div style="width: 60%; display: flex; align-items: center"> | |||||
<div class="zxlzx" v-if="!subitem.selected">已执行</div> | |||||
<div v-else class="zxlzx2">未执行</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | </div> | ||||
</div> | </div> | ||||
@@ -1105,6 +1182,7 @@ import { saveAs } from "file-saver"; | |||||
export default { | export default { | ||||
data() { | data() { | ||||
return { | return { | ||||
imgArr: ['', 'https://static.quhouse.com/37e0de3f8d1c421dac8bf699d5e7992d.png', 'https://static.quhouse.com/b106e8e75db24a59a579a15a78830a76.png', 'https://static.quhouse.com/8443a2ecb81d4639991ab29c422e9949.png', 'https://static.quhouse.com/1cd794cb6c974d9dad948a6dd444518b.png',], | |||||
mg: 0, | mg: 0, | ||||
recordsText: [], | recordsText: [], | ||||
roleVisible: false, | roleVisible: false, | ||||
@@ -1259,6 +1337,26 @@ export default { | |||||
}, | }, | ||||
methods: { | methods: { | ||||
// 对话加命中标签 | |||||
dealTypes(type){ | |||||
if(type){ | |||||
let tem = type.substring(1).split(',') | |||||
return tem | |||||
}else{ | |||||
return [] | |||||
} | |||||
}, | |||||
// 对话加命中标签 | |||||
dealword(type){ | |||||
if(type){ | |||||
let a = type.split('-') | |||||
let rest = type.substring(2).split('-').join(',') | |||||
let arr =[a[0],rest] | |||||
return arr | |||||
}else{ | |||||
return [] | |||||
} | |||||
}, | |||||
chooseRoles(index) { | chooseRoles(index) { | ||||
this.roleFlag = index; | this.roleFlag = index; | ||||
}, | }, | ||||
@@ -1972,10 +2070,6 @@ export default { | |||||
console.log(this.mistakenList, this.correctList); | console.log(this.mistakenList, this.correctList); | ||||
}); | }); | ||||
}, | }, | ||||
// 销讲词禁忌tab | |||||
recordclick(i) { | |||||
this.zhixingcenterindex = i; | |||||
}, | |||||
// 销讲词折叠 | // 销讲词折叠 | ||||
changeshow(index) { | changeshow(index) { | ||||
this.ratelist[index].show = !this.ratelist[index].show; | this.ratelist[index].show = !this.ratelist[index].show; | ||||
@@ -2272,7 +2366,7 @@ export default { | |||||
// // 使用获取到的blob对象创建的url | // // 使用获取到的blob对象创建的url | ||||
// const url = window.URL.createObjectURL(res); | // const url = window.URL.createObjectURL(res); | ||||
// a.href = url; | // a.href = url; | ||||
// // 指定下载的文件名,就‘’写默认的下载名字。不指定他就根据上传名直接下载了宝。 | |||||
// // 指定下载的文件名,就‘’写默认的下载名字。不指定他就根据上传名直接下载了。 | |||||
// a.download = ''; | // a.download = ''; | ||||
// a.click(); | // a.click(); | ||||
// document.body.removeChild(a) | // document.body.removeChild(a) | ||||
@@ -2289,6 +2383,36 @@ export default { | |||||
</script> | </script> | ||||
<style scoped lang="scss" > | <style scoped lang="scss" > | ||||
/deep/.el-radio-button__orig-radio:checked+.el-radio-button__inner{ | |||||
background-color: #2671e2 !important; | |||||
border-color: #2671e2 !important; | |||||
} | |||||
/*对话的标签标识*/ | |||||
.content123 .showmark{ | |||||
font-size: 14px; | |||||
max-width: 310px; | |||||
min-height: 20px; | |||||
line-height: 20px; | |||||
margin: 10px 22px 0; | |||||
.mark-item{ | |||||
display: flex; | |||||
align-items: center; | |||||
margin-bottom: 2px; | |||||
.markicon { | |||||
width: 14px; | |||||
height: 14px; | |||||
margin-right: 6px; | |||||
} | |||||
.marktext{ | |||||
font-size: 12px; | |||||
font-family: PingFangSC-Regular, PingFang SC; | |||||
font-weight: 400; | |||||
color: #3E50E8; | |||||
line-height: 18px; | |||||
word-break: break-all; | |||||
} | |||||
} | |||||
} | |||||
.expIcon { | .expIcon { | ||||
display: block; | display: block; | ||||
width: 16px; | width: 16px; | ||||
@@ -2388,7 +2512,7 @@ export default { | |||||
font-size: 12px; | font-size: 12px; | ||||
line-height: 20px; | line-height: 20px; | ||||
display: flex; | display: flex; | ||||
align-items: center; | |||||
// align-items: center; | |||||
margin: 18px 0px; | margin: 18px 0px; | ||||
position: relative; | position: relative; | ||||
} | } | ||||
@@ -2480,7 +2604,7 @@ export default { | |||||
font-size: 24px; | font-size: 24px; | ||||
display: flex; | display: flex; | ||||
align-items: center; | align-items: center; | ||||
margin-top: 12px; | |||||
// margin-top: 12px; | |||||
justify-content: center; | justify-content: center; | ||||
} | } | ||||
@@ -2507,8 +2631,6 @@ export default { | |||||
.center3 .text .avatar { | .center3 .text .avatar { | ||||
width: 38px; | width: 38px; | ||||
height: 38px; | height: 38px; | ||||
/*background-color: #f2f2f2;*/ | |||||
// background-color: #ccc; | |||||
border-radius: 8px; | border-radius: 8px; | ||||
margin-left: 15px; | margin-left: 15px; | ||||
display: flex; | display: flex; | ||||
@@ -2920,6 +3042,7 @@ export default { | |||||
font-size: 16px; | font-size: 16px; | ||||
} | } | ||||
.activecllasscet { | .activecllasscet { | ||||
background: #2671e2; | background: #2671e2; | ||||
color: #fff; | color: #fff; | ||||
@@ -302,9 +302,36 @@ | |||||
@click="Receivedetailsabout(row)" | @click="Receivedetailsabout(row)" | ||||
>查看</el-button | >查看</el-button | ||||
> | > | ||||
<el-button | |||||
type="text" | |||||
v-if="rec_index_show" | |||||
@click="reWriteagain(row)" | |||||
>重新转写</el-button | |||||
> | |||||
</template> | </template> | ||||
</avue-crud> | </avue-crud> | ||||
</div> | </div> | ||||
<el-dialog | |||||
title="重新转写" | |||||
center | |||||
:visible.sync="dialogVisible" | |||||
width="50%"> | |||||
<div style="text-align: center;font-size: 16px" v-if="show">请等待录音合并之后再重新转写!</div> | |||||
<el-form :model="form" v-else> | |||||
<el-form-item label="转写方式" :label-width="80"> | |||||
<el-select v-model="form.project" placeholder="请选择项目" @change="changeFun"> | |||||
<el-option v-for="(item,index) in projectlist" :label="item.name" :value="item.code"></el-option> | |||||
</el-select> | |||||
<el-select v-model="form.language" placeholder="请选择语种"> | |||||
<el-option v-for="(item,index) in languageList" :label="item.name" :value="item.code"></el-option> | |||||
</el-select> | |||||
</el-form-item> | |||||
</el-form> | |||||
<span slot="footer" class="dialog-footer"> | |||||
<el-button @click="dialogVisible = false">取 消</el-button> | |||||
<el-button type="primary" @click="checkFun">确 定</el-button> | |||||
</span> | |||||
</el-dialog> | |||||
</div> | </div> | ||||
</template> | </template> | ||||
@@ -314,6 +341,14 @@ import { exportMethodPost } from "@/util/util"; | |||||
export default { | export default { | ||||
data() { | data() { | ||||
return { | return { | ||||
desc: '请等待录音合并之后再重新转写!', | |||||
dialogVisible: false, | |||||
projectlist: [], | |||||
languageList: [], | |||||
form: { | |||||
language: '', | |||||
project:'' | |||||
}, | |||||
tableIdName: "ReceivingRecordsIndex", // 当前页面需要的变量 | tableIdName: "ReceivingRecordsIndex", // 当前页面需要的变量 | ||||
tableOption: this.$tableOption.ReceivingRecordsIndex, // 当前table配置项 | tableOption: this.$tableOption.ReceivingRecordsIndex, // 当前table配置项 | ||||
tableLoading: false, // 是否显示加载中 | tableLoading: false, // 是否显示加载中 | ||||
@@ -599,6 +634,75 @@ export default { | |||||
}, | }, | ||||
methods: { | methods: { | ||||
changeFun(value){ | |||||
// console.log(value) | |||||
this.form.language = '' | |||||
this.projectlist.forEach(item=>{ | |||||
if(item.code==value){ | |||||
this.languageList = item.list | |||||
} | |||||
}) | |||||
}, | |||||
// 转写方式数据获取 | |||||
findTransferMethod(){ | |||||
axios({ | |||||
url: `${jypath}/properties/findTransferMethod`, | |||||
method: 'GET', | |||||
params: { | |||||
} | |||||
}).then(res => { | |||||
console.log(res) | |||||
if (res.data.code == 10000) { | |||||
this.projectlist = res.data.data||[] | |||||
this.languageList = res.data.data&&res.data.data[0].list||[] | |||||
} else { | |||||
} | |||||
}).catch(() => { | |||||
}) | |||||
}, | |||||
// 确定重新转写 | |||||
checkFun(){ | |||||
if(this.form.project===''||this.form.language===''){ | |||||
this.$message.error('请先选择转写方式') | |||||
return; | |||||
} | |||||
axios({ | |||||
url: `${jypath}/customer/toTransferData`, | |||||
method: 'GET', | |||||
params: { | |||||
id:this.currentRow.id, | |||||
transferMethod:this.form.project, | |||||
transferLanguage: this.form.language | |||||
} | |||||
}).then(res => { | |||||
// console.log(res) | |||||
if (res.data.res == 1) { | |||||
this.dialogVisible = false | |||||
this.getorgCode() | |||||
this.$message.success('操作成功') | |||||
} else { | |||||
this.$message.error('操作失败') | |||||
} | |||||
}).catch(() => { | |||||
}) | |||||
}, | |||||
reWriteagain(row){ | |||||
this.dialogVisible = true | |||||
this.currentRow = row; | |||||
// mergeFlag; //是否合并 0是 1 否 | |||||
// this.title = row.mergeFlag==1?'提醒':'重新转写'; | |||||
this.form.project = row.transferMethod||''; | |||||
if(row.transferLanguage){ | |||||
this.projectlist.forEach(item=>{ | |||||
if(item.code==row.transferLanguage){ | |||||
this.languageList = item.list | |||||
this.form.language = row.transferLanguage||'' | |||||
} | |||||
}) | |||||
}else{ | |||||
this.form.language='' | |||||
} | |||||
}, | |||||
// 获取当前页面的显隐 | // 获取当前页面的显隐 | ||||
setTableOption() { | setTableOption() { | ||||
this.$db.getDataByKey(this.tableIdName).then((res) => { | this.$db.getDataByKey(this.tableIdName).then((res) => { | ||||
@@ -24,8 +24,8 @@ | |||||
</div> | </div> | ||||
<div class="app-titel projectBackend"> | <div class="app-titel projectBackend"> | ||||
<div class="titel-text">筛选日期:</div> | |||||
<div style="margin-left: 14px"> | |||||
<div class="titel-text">日期:</div> | |||||
<div style="margin-left: 10px"> | |||||
<el-button | <el-button | ||||
:class="{ 'el-button--primary': TimetoAhoose == 0 }" | :class="{ 'el-button--primary': TimetoAhoose == 0 }" | ||||
@click="tabtimetap(0)" | @click="tabtimetap(0)" | ||||
@@ -42,8 +42,9 @@ | |||||
>近30天</el-button | >近30天</el-button | ||||
> | > | ||||
</div> | </div> | ||||
<div style="margin-left: 26px"> | |||||
<div style="margin-left: 10px"> | |||||
<el-date-picker | <el-date-picker | ||||
style="width:300px" | |||||
v-model="customtime" | v-model="customtime" | ||||
@change="confirmtime()" | @change="confirmtime()" | ||||
type="daterange" | type="daterange" | ||||
@@ -57,7 +58,7 @@ | |||||
</el-date-picker> | </el-date-picker> | ||||
</div> | </div> | ||||
<div style="margin-left: 26px;" class="div-lab"> | |||||
<div style="margin-left: 20px;" class="div-lab"> | |||||
<div class="label">顾问:</div> | <div class="label">顾问:</div> | ||||
<el-select | <el-select | ||||
v-model="consultantlistid" | v-model="consultantlistid" | ||||
@@ -74,7 +75,7 @@ | |||||
</el-option> | </el-option> | ||||
</el-select> | </el-select> | ||||
</div> | </div> | ||||
<div style="margin-left: 26px" class="div-lab"> | |||||
<div style="margin-left: 20px" class="div-lab"> | |||||
<div class="label">对比:</div> | <div class="label">对比:</div> | ||||
<el-select | <el-select | ||||
v-model="Pklistid" | v-model="Pklistid" | ||||
@@ -1482,14 +1483,14 @@ export default { | |||||
margin: 5px; | margin: 5px; | ||||
} | } | ||||
.div-inp { | .div-inp { | ||||
width: 200px; | |||||
width: 180px; | |||||
} | } | ||||
.label { | .label { | ||||
font-size: 16px; | font-size: 16px; | ||||
font-weight: 400; | font-weight: 400; | ||||
color: #32363d; | color: #32363d; | ||||
line-height: 32px; | line-height: 32px; | ||||
min-width: 40px; | |||||
min-width: 50px; | |||||
} | } | ||||
.noData { | .noData { | ||||
width: 100%; | width: 100%; | ||||
@@ -64,15 +64,12 @@ | |||||
> | > | ||||
<template slot-scope="{ row }" slot="menu"> | <template slot-scope="{ row }" slot="menu"> | ||||
<el-button v-if="tem_tab_edit" type="text" @click="bianji(row)" | <el-button v-if="tem_tab_edit" type="text" @click="bianji(row)" | ||||
>编辑</el-button | |||||
> | |||||
<el-button v-if="tem_tab_del" type="text" @click="del(row)" | |||||
>删除</el-button | |||||
> | |||||
>编辑</el-button> | |||||
<el-button v-if="tem_tab_del" type="text" @click="del(row)">删除</el-button> | |||||
<el-button type="text" @click="editFun(row.id)">模型匹配</el-button> | |||||
</template> | </template> | ||||
</avue-crud> | </avue-crud> | ||||
</div> | </div> | ||||
<el-dialog | <el-dialog | ||||
:title="editFlag ? '编辑' : '新增'" | :title="editFlag ? '编辑' : '新增'" | ||||
:center="true" | :center="true" | ||||
@@ -85,7 +82,7 @@ | |||||
label-position="labelPosition" | label-position="labelPosition" | ||||
:rules="ruleser" | :rules="ruleser" | ||||
ref="ruleForm" | ref="ruleForm" | ||||
label-width="66px" | |||||
label-width="80px" | |||||
style="width: 100%; margin: 0 auto" | style="width: 100%; margin: 0 auto" | ||||
> | > | ||||
<el-form-item label="敏感词" prop="words"> | <el-form-item label="敏感词" prop="words"> | ||||
@@ -96,6 +93,16 @@ | |||||
clearable | clearable | ||||
></el-input> | ></el-input> | ||||
</el-form-item> | </el-form-item> | ||||
<el-form-item label="场景描述" prop="sceneDesc"> | |||||
<el-input | |||||
type="textarea" | |||||
v-model="ruleForm.sceneDesc" | |||||
placeholder="敏感词" | |||||
maxlength="100" | |||||
show-word-limit | |||||
clearable | |||||
></el-input> | |||||
</el-form-item> | |||||
</el-form> | </el-form> | ||||
<div | <div | ||||
slot="footer" | slot="footer" | ||||
@@ -113,6 +120,81 @@ | |||||
> | > | ||||
</div> | </div> | ||||
</el-dialog> | </el-dialog> | ||||
<el-dialog | |||||
title="编辑模型" | |||||
@open="openModel" | |||||
:close-on-click-modal="false" | |||||
:visible.sync="modelVisible" | |||||
> | |||||
<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="敏感词:"> | |||||
<div style="max-width:400px;min-width: 200px;font-weight: bold">{{form.keywordsName||'XXX'}}</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> | |||||
<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> | |||||
<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="modelVisible = false">取 消</el-button> | |||||
<el-button type="primary" @click="saveFun">保 存</el-button> | |||||
</span> | |||||
</el-dialog> | |||||
</div> | </div> | ||||
</template> | </template> | ||||
@@ -122,6 +204,41 @@ import { exportMethodPost } from "@/util/util"; | |||||
export default { | export default { | ||||
data() { | data() { | ||||
return { | return { | ||||
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: '' | |||||
}, | |||||
modelVisible:false, | |||||
innerVisible:false, | |||||
innerVisible1:false, | |||||
dialogVisible: false, | |||||
tableIdName: "TemplateTaboo", // 当前页面需要的变量 | tableIdName: "TemplateTaboo", // 当前页面需要的变量 | ||||
tableOption: this.$tableOption.TemplateTaboo, // 当前table配置项 | tableOption: this.$tableOption.TemplateTaboo, // 当前table配置项 | ||||
tableLoading: false, // 是否显示加载中 | tableLoading: false, // 是否显示加载中 | ||||
@@ -137,11 +254,11 @@ export default { | |||||
input: "", | input: "", | ||||
tableData: [], | tableData: [], | ||||
multipleSelection: [], | multipleSelection: [], | ||||
dialogVisible: false, | |||||
loadingFlag: false, | loadingFlag: false, | ||||
orgType: localStorage.getItem("orgType"), | orgType: localStorage.getItem("orgType"), | ||||
ruleForm: { | ruleForm: { | ||||
words: "", | words: "", | ||||
sceneDesc: '', | |||||
id: "", | id: "", | ||||
}, | }, | ||||
houseId: "", | houseId: "", | ||||
@@ -180,6 +297,345 @@ export default { | |||||
this.zkhousePage(); | this.zkhousePage(); | ||||
}, | }, | ||||
methods: { | methods: { | ||||
// 插入节点 | |||||
insertTag(item,index){ | |||||
if(window._range){ | |||||
this.insertContent("<span contentEditable='false' style='color:red'>"+item.value+"</span><text> </text>"); | |||||
}else{ | |||||
this.insertHtmlAtCaret("<span contentEditable='false' style='color:red'>"+item.value+"</span><text> </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> </text>"); | |||||
}, | |||||
//点击编辑按钮 | |||||
editFun(id){ | |||||
this.form.keywordsId = id | |||||
this.form.keywordsName = '' | |||||
this.form.id = '' | |||||
// 获取模型数据回显 | |||||
axios({ | |||||
url: `${jypath}/zk/keymodel/findById`, | |||||
method: 'get', | |||||
params: { | |||||
houseId: this.houseId, | |||||
keyType: 3, | |||||
keywordsId: id | |||||
} | |||||
}).then(res => { | |||||
this.modelVisible = true | |||||
if (res.data.res == 1) { | |||||
let obj = res.data.obj | |||||
console.log(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||'' | |||||
this.form.keywordsName = obj.level1Name | |||||
}else{ | |||||
this.form.distance=10 | |||||
this.form.id = '' | |||||
this.form.originalExpression='' | |||||
} | |||||
}else{ | |||||
this.form.distance = 10 | |||||
this.form.originalExpression = '' | |||||
this.form.id = '' | |||||
} | |||||
this.$nextTick(()=>{ | |||||
let huashuModel = document.getElementById('huashuModel') | |||||
huashuModel.innerHTML = this.form.originalExpression | |||||
}) | |||||
}).catch((e)=>{ | |||||
this.modelVisible = true | |||||
}) | |||||
}, | |||||
// 处理模型 关键词加#号 | |||||
replaceFun(str){ | |||||
let temp = str | |||||
temp = temp.replace(/<text>\ \;<\/text>/g,''); | |||||
temp = temp.replace(/\ \;/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(){ | |||||
let text = document.getElementById('huashuModel'); | |||||
// 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;} | |||||
axios({ | |||||
url: `${jypath}/zk/keymodel/updateKeywordsModel`, | |||||
method: 'post', | |||||
data: { | |||||
id: this.form.id||"", | |||||
houseId: this.houseId, | |||||
level: 1, | |||||
keywordsId: this.form.keywordsId, | |||||
keywordsName: this.form.keywordsName, | |||||
answerList: [], | |||||
keyType: 3, | |||||
formatExpression: this.replaceFun(temp),// 问题表达式 | |||||
originalExpression: temp,// html数据 | |||||
showFormatExpression: text.innerText, | |||||
distance: this.form.distance, // * 距离 | |||||
desc: this.form.sceneDesc//场景描述 | |||||
} | |||||
}).then(data => { | |||||
this.dialogVmodelVisibleisible = false | |||||
if(data.data.res==1){ | |||||
this.$message.success(data.data.obj) | |||||
this.getorgCode() | |||||
}else{ | |||||
this.$message.error(data.data.resMsg) | |||||
} | |||||
}).catch((e)=>{ | |||||
this.modelVisible = false | |||||
}) | |||||
}, | |||||
// 失去焦点时保存光标位置,记录光标位置 | |||||
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); | |||||
} | |||||
}, | |||||
// 校验是否已经选择过此标签 | |||||
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); | |||||
} | |||||
}, | |||||
// 获取当前页面的显隐 | // 获取当前页面的显隐 | ||||
setTableOption() { | setTableOption() { | ||||
this.$db.getDataByKey(this.tableIdName).then((res) => { | this.$db.getDataByKey(this.tableIdName).then((res) => { | ||||
@@ -193,7 +649,6 @@ export default { | |||||
houseId: this.houseId, | houseId: this.houseId, | ||||
words: this.words, | words: this.words, | ||||
}; | }; | ||||
// exportMethodPost() | |||||
exportMethodPost("autoSR/sensitivewords/export", "禁忌话术", obj); | exportMethodPost("autoSR/sensitivewords/export", "禁忌话术", obj); | ||||
}, | }, | ||||
houseChange() { | houseChange() { | ||||
@@ -213,7 +668,6 @@ export default { | |||||
} else { | } else { | ||||
this.houseId = res.data[0].id; | this.houseId = res.data[0].id; | ||||
} | } | ||||
// this.houseId = res.data[0].id; | |||||
this.taboofindbypagelist(); | this.taboofindbypagelist(); | ||||
}); | }); | ||||
}, | }, | ||||
@@ -298,6 +752,7 @@ export default { | |||||
.tabooadd({ | .tabooadd({ | ||||
houseId: this.houseId, | houseId: this.houseId, | ||||
words: this.ruleForm.words, | words: this.ruleForm.words, | ||||
sceneDesc: this.ruleForm.sceneDesc// 场景描述 | |||||
}) | }) | ||||
.then((res) => { | .then((res) => { | ||||
if (res.code == 0) { | if (res.code == 0) { | ||||
@@ -368,6 +823,17 @@ export default { | |||||
</script> | </script> | ||||
<style scoped="scoped" lang="scss" > | <style scoped="scoped" lang="scss" > | ||||
.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-center { | .box-center { | ||||
width: 100%; | width: 100%; | ||||
padding: 5px 15px 20px; | padding: 5px 15px 20px; | ||||
@@ -441,6 +441,66 @@ | |||||
:disabled="passFlag" | :disabled="passFlag" | ||||
></el-input> | ></el-input> | ||||
</el-form-item> | </el-form-item> | ||||
<el-form-item | |||||
label="转写方式" | |||||
prop="transferMethod" | |||||
> | |||||
<el-select | |||||
style="width: 300px" | |||||
v-model="ruleForm.transferMethod" | |||||
filterable | |||||
placeholder="转写方式" | |||||
> | |||||
<el-option | |||||
v-for="item in orgList" | |||||
:key="item.id" | |||||
:label="item.name" | |||||
:value="item.orgCode" | |||||
> | |||||
</el-option> | |||||
</el-select> | |||||
<el-select | |||||
style="width: 300px" | |||||
v-model="ruleForm.transferLanguage" | |||||
filterable | |||||
placeholder="语言" | |||||
> | |||||
<el-option | |||||
v-for="item in orgList" | |||||
:key="item.id" | |||||
:label="item.name" | |||||
:value="item.orgCode" | |||||
> | |||||
</el-option> | |||||
</el-select> | |||||
</el-form-item> | |||||
<el-form-item | |||||
label="无效接待审核" | |||||
prop="auditReception" | |||||
> | |||||
<el-radio-group v-model="ruleForm.auditReception"> | |||||
<el-radio :label="0">审核</el-radio> | |||||
<el-radio :label="1">不审核</el-radio> | |||||
</el-radio-group> | |||||
</el-form-item> | |||||
<el-form-item | |||||
label="挖掘话术匹配" | |||||
prop="keywordsModelQuestionMatch" | |||||
> | |||||
<el-radio-group v-model="ruleForm.keywordsModelQuestionMatch"> | |||||
<el-radio :label="0">全部话术</el-radio> | |||||
<el-radio :label="1">顾问话术</el-radio> | |||||
</el-radio-group> | |||||
</el-form-item> | |||||
<el-form-item | |||||
label="挖掘回答匹配" | |||||
prop="keywordsModelAnswerMatch" | |||||
> | |||||
<el-radio-group v-model="ruleForm.keywordsModelAnswerMatch"> | |||||
<el-radio :label="0">下一句话</el-radio> | |||||
<el-radio :label="1">最近客户话术</el-radio> | |||||
</el-radio-group> | |||||
</el-form-item> | |||||
</el-form> | </el-form> | ||||
</div> | </div> | ||||
<div | <div | ||||
@@ -455,9 +515,6 @@ | |||||
> | > | ||||
<el-button @click="addSurequxiao()">取 消</el-button> | <el-button @click="addSurequxiao()">取 消</el-button> | ||||
<el-button type="primary" :loading="loadingFlag" @click="addSure"> | <el-button type="primary" :loading="loadingFlag" @click="addSure"> | ||||
<!-- {{ | |||||
editFlag ? "编辑" : "保存" | |||||
}} --> | |||||
保存 | 保存 | ||||
</el-button> | </el-button> | ||||
</div> | </div> | ||||
@@ -877,6 +934,11 @@ export default { | |||||
offLine: ["08:00", "20:00"], // 离线推送时间段 | offLine: ["08:00", "20:00"], // 离线推送时间段 | ||||
caseShow: '0', // 优秀案例 | caseShow: '0', // 优秀案例 | ||||
tagMatching: '0', // 客户标签匹配 | tagMatching: '0', // 客户标签匹配 | ||||
transferLanguage:'', | |||||
transferMethod: '', | |||||
auditReception:'', | |||||
keywordsModelAnswerMatch:'', | |||||
keywordsModelQuestionMatch:'' | |||||
}, | }, | ||||
ruleForm1: {}, | ruleForm1: {}, | ||||
optionsagentId: [], | optionsagentId: [], | ||||