@@ -63,6 +63,7 @@ | |||
</span> | |||
<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="() => moxingEdit(node, data)">编辑模型</el-button> | |||
</span> | |||
</span> | |||
@@ -81,14 +82,15 @@ | |||
<span v-if="data.level == 3" style="display: flex; align-items: center" > | |||
<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> | |||
</span> | |||
<span style="margin-left: 10px"> | |||
<el-button class="remove" type="text" size="mini" @click="remove(node, data)" >删除</el-button> | |||
</span> | |||
</span> | |||
</span> | |||
</el-tree> | |||
</div> | |||
@@ -166,8 +168,6 @@ | |||
</span> | |||
</el-dialog> | |||
<!--?编辑弹框?--> | |||
<el-dialog title="编辑" :visible.sync="isshowage" :center="true" width="400px"> | |||
<div | |||
@@ -263,7 +263,87 @@ | |||
<el-button type="primary" @click="addclick()">确 定</el-button> | |||
</span> | |||
</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> | |||
@@ -274,6 +354,39 @@ import { mapGetters } from "vuex"; | |||
export default { | |||
data() { | |||
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", | |||
dialogFormVisible: false, | |||
dialogVisible: false, | |||
@@ -320,6 +433,222 @@ export default { | |||
this.zkhousePage(); | |||
}, | |||
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(){ | |||
var orgCode=''; | |||
if(localStorage.getItem("orgType") == 3){ | |||
@@ -656,6 +985,17 @@ export default { | |||
/deep/.el-tree-node__content{ | |||
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 { | |||
width: 100%; | |||
padding: 15px 15px 20px; | |||
@@ -310,6 +310,18 @@ | |||
style="flex-shrink: 0; width: 24px; height: 24px" | |||
/> | |||
</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> | |||
@@ -463,52 +475,17 @@ | |||
height: 100px; | |||
display: flex; | |||
flex-direction: column; | |||
justify-content: space-between; | |||
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 v-if="zhixingcenterindex == 0"> | |||
<div | |||
style=" | |||
@@ -535,6 +512,7 @@ | |||
</div> | |||
</div> | |||
</div> | |||
<div id="zxldiv" v-if="zhixingcenterindex == 0"> | |||
<div class="zxlBox" v-for="(item, index) in ratelist" :key="index"> | |||
@@ -630,6 +608,105 @@ | |||
{{ index + 1 }}、{{ item }} | |||
</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> | |||
@@ -1105,6 +1182,7 @@ import { saveAs } from "file-saver"; | |||
export default { | |||
data() { | |||
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, | |||
recordsText: [], | |||
roleVisible: false, | |||
@@ -1259,6 +1337,26 @@ export default { | |||
}, | |||
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) { | |||
this.roleFlag = index; | |||
}, | |||
@@ -1972,10 +2070,6 @@ export default { | |||
console.log(this.mistakenList, this.correctList); | |||
}); | |||
}, | |||
// 销讲词禁忌tab | |||
recordclick(i) { | |||
this.zhixingcenterindex = i; | |||
}, | |||
// 销讲词折叠 | |||
changeshow(index) { | |||
this.ratelist[index].show = !this.ratelist[index].show; | |||
@@ -2272,7 +2366,7 @@ export default { | |||
// // 使用获取到的blob对象创建的url | |||
// const url = window.URL.createObjectURL(res); | |||
// a.href = url; | |||
// // 指定下载的文件名,就‘’写默认的下载名字。不指定他就根据上传名直接下载了宝。 | |||
// // 指定下载的文件名,就‘’写默认的下载名字。不指定他就根据上传名直接下载了。 | |||
// a.download = ''; | |||
// a.click(); | |||
// document.body.removeChild(a) | |||
@@ -2289,6 +2383,36 @@ export default { | |||
</script> | |||
<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 { | |||
display: block; | |||
width: 16px; | |||
@@ -2388,7 +2512,7 @@ export default { | |||
font-size: 12px; | |||
line-height: 20px; | |||
display: flex; | |||
align-items: center; | |||
// align-items: center; | |||
margin: 18px 0px; | |||
position: relative; | |||
} | |||
@@ -2480,7 +2604,7 @@ export default { | |||
font-size: 24px; | |||
display: flex; | |||
align-items: center; | |||
margin-top: 12px; | |||
// margin-top: 12px; | |||
justify-content: center; | |||
} | |||
@@ -2507,8 +2631,6 @@ export default { | |||
.center3 .text .avatar { | |||
width: 38px; | |||
height: 38px; | |||
/*background-color: #f2f2f2;*/ | |||
// background-color: #ccc; | |||
border-radius: 8px; | |||
margin-left: 15px; | |||
display: flex; | |||
@@ -2920,6 +3042,7 @@ export default { | |||
font-size: 16px; | |||
} | |||
.activecllasscet { | |||
background: #2671e2; | |||
color: #fff; | |||
@@ -302,9 +302,36 @@ | |||
@click="Receivedetailsabout(row)" | |||
>查看</el-button | |||
> | |||
<el-button | |||
type="text" | |||
v-if="rec_index_show" | |||
@click="reWriteagain(row)" | |||
>重新转写</el-button | |||
> | |||
</template> | |||
</avue-crud> | |||
</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> | |||
</template> | |||
@@ -314,6 +341,14 @@ import { exportMethodPost } from "@/util/util"; | |||
export default { | |||
data() { | |||
return { | |||
desc: '请等待录音合并之后再重新转写!', | |||
dialogVisible: false, | |||
projectlist: [], | |||
languageList: [], | |||
form: { | |||
language: '', | |||
project:'' | |||
}, | |||
tableIdName: "ReceivingRecordsIndex", // 当前页面需要的变量 | |||
tableOption: this.$tableOption.ReceivingRecordsIndex, // 当前table配置项 | |||
tableLoading: false, // 是否显示加载中 | |||
@@ -599,6 +634,75 @@ export default { | |||
}, | |||
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() { | |||
this.$db.getDataByKey(this.tableIdName).then((res) => { | |||
@@ -24,8 +24,8 @@ | |||
</div> | |||
<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 | |||
:class="{ 'el-button--primary': TimetoAhoose == 0 }" | |||
@click="tabtimetap(0)" | |||
@@ -42,8 +42,9 @@ | |||
>近30天</el-button | |||
> | |||
</div> | |||
<div style="margin-left: 26px"> | |||
<div style="margin-left: 10px"> | |||
<el-date-picker | |||
style="width:300px" | |||
v-model="customtime" | |||
@change="confirmtime()" | |||
type="daterange" | |||
@@ -57,7 +58,7 @@ | |||
</el-date-picker> | |||
</div> | |||
<div style="margin-left: 26px;" class="div-lab"> | |||
<div style="margin-left: 20px;" class="div-lab"> | |||
<div class="label">顾问:</div> | |||
<el-select | |||
v-model="consultantlistid" | |||
@@ -74,7 +75,7 @@ | |||
</el-option> | |||
</el-select> | |||
</div> | |||
<div style="margin-left: 26px" class="div-lab"> | |||
<div style="margin-left: 20px" class="div-lab"> | |||
<div class="label">对比:</div> | |||
<el-select | |||
v-model="Pklistid" | |||
@@ -1482,14 +1483,14 @@ export default { | |||
margin: 5px; | |||
} | |||
.div-inp { | |||
width: 200px; | |||
width: 180px; | |||
} | |||
.label { | |||
font-size: 16px; | |||
font-weight: 400; | |||
color: #32363d; | |||
line-height: 32px; | |||
min-width: 40px; | |||
min-width: 50px; | |||
} | |||
.noData { | |||
width: 100%; | |||
@@ -64,15 +64,12 @@ | |||
> | |||
<template slot-scope="{ row }" slot="menu"> | |||
<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> | |||
</avue-crud> | |||
</div> | |||
<el-dialog | |||
:title="editFlag ? '编辑' : '新增'" | |||
:center="true" | |||
@@ -85,7 +82,7 @@ | |||
label-position="labelPosition" | |||
:rules="ruleser" | |||
ref="ruleForm" | |||
label-width="66px" | |||
label-width="80px" | |||
style="width: 100%; margin: 0 auto" | |||
> | |||
<el-form-item label="敏感词" prop="words"> | |||
@@ -96,6 +93,16 @@ | |||
clearable | |||
></el-input> | |||
</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> | |||
<div | |||
slot="footer" | |||
@@ -113,6 +120,81 @@ | |||
> | |||
</div> | |||
</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> | |||
</template> | |||
@@ -122,6 +204,41 @@ import { exportMethodPost } from "@/util/util"; | |||
export default { | |||
data() { | |||
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", // 当前页面需要的变量 | |||
tableOption: this.$tableOption.TemplateTaboo, // 当前table配置项 | |||
tableLoading: false, // 是否显示加载中 | |||
@@ -137,11 +254,11 @@ export default { | |||
input: "", | |||
tableData: [], | |||
multipleSelection: [], | |||
dialogVisible: false, | |||
loadingFlag: false, | |||
orgType: localStorage.getItem("orgType"), | |||
ruleForm: { | |||
words: "", | |||
sceneDesc: '', | |||
id: "", | |||
}, | |||
houseId: "", | |||
@@ -180,6 +297,345 @@ export default { | |||
this.zkhousePage(); | |||
}, | |||
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() { | |||
this.$db.getDataByKey(this.tableIdName).then((res) => { | |||
@@ -193,7 +649,6 @@ export default { | |||
houseId: this.houseId, | |||
words: this.words, | |||
}; | |||
// exportMethodPost() | |||
exportMethodPost("autoSR/sensitivewords/export", "禁忌话术", obj); | |||
}, | |||
houseChange() { | |||
@@ -213,7 +668,6 @@ export default { | |||
} else { | |||
this.houseId = res.data[0].id; | |||
} | |||
// this.houseId = res.data[0].id; | |||
this.taboofindbypagelist(); | |||
}); | |||
}, | |||
@@ -298,6 +752,7 @@ export default { | |||
.tabooadd({ | |||
houseId: this.houseId, | |||
words: this.ruleForm.words, | |||
sceneDesc: this.ruleForm.sceneDesc// 场景描述 | |||
}) | |||
.then((res) => { | |||
if (res.code == 0) { | |||
@@ -368,6 +823,17 @@ export default { | |||
</script> | |||
<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 { | |||
width: 100%; | |||
padding: 5px 15px 20px; | |||
@@ -441,6 +441,66 @@ | |||
:disabled="passFlag" | |||
></el-input> | |||
</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> | |||
</div> | |||
<div | |||
@@ -455,9 +515,6 @@ | |||
> | |||
<el-button @click="addSurequxiao()">取 消</el-button> | |||
<el-button type="primary" :loading="loadingFlag" @click="addSure"> | |||
<!-- {{ | |||
editFlag ? "编辑" : "保存" | |||
}} --> | |||
保存 | |||
</el-button> | |||
</div> | |||
@@ -877,6 +934,11 @@ export default { | |||
offLine: ["08:00", "20:00"], // 离线推送时间段 | |||
caseShow: '0', // 优秀案例 | |||
tagMatching: '0', // 客户标签匹配 | |||
transferLanguage:'', | |||
transferMethod: '', | |||
auditReception:'', | |||
keywordsModelAnswerMatch:'', | |||
keywordsModelQuestionMatch:'' | |||
}, | |||
ruleForm1: {}, | |||
optionsagentId: [], | |||