Bladeren bron

需求挖掘

newStyle
corala 2 jaren geleden
bovenliggende
commit
3e5856fae6
9 gewijzigde bestanden met toevoegingen van 4063 en 3153 verwijderingen
  1. +344
    -4
      src/views/Customer/label.vue
  2. +0
    -3078
      py.vue
  3. +174
    -51
      src/views/Receive/index.vue
  4. +104
    -0
      src/views/ReceivingRecords/index.vue
  5. +8
    -7
      src/views/Statistics/MentoringAbility.vue
  6. +1606
    -0
      src/views/Template/requiremining.vue
  7. +1286
    -0
      src/views/Template/requireminingAnalysis.vue
  8. +476
    -10
      src/views/Template/taboo.vue
  9. +65
    -3
      src/views/building/index.vue

+ 344
- 4
src/views/Customer/label.vue Bestand weergeven

@@ -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>&nbsp;</text>");
}else{
this.insertHtmlAtCaret("<span contentEditable='false' style='color:red'>"+item.value+"</span><text>&nbsp;</text>");
}
},
// 处理标签,删除不需要的标签格式
delMark(str) {
const hasStr = (str) => {
let index = str.indexOf('<')
let index1 = str.indexOf('>')
if(index>0&&index1>0){
let replaceStr= str.substring(index, index1+1)
str= str.replace(replaceStr,'')
hasStr(str)
}
}
hasStr(str)
},
// 处理模型 关键词加#号
replaceFun(str){
let temp = str
temp = temp.replace(/<text>\&nbsp\;<\/text>/g,'');
temp = temp.replace(/\&nbsp\;/g,'');
temp = temp.replace(/<text>/g,'');
temp = temp.replace(/<\/text>/g,'');
temp = temp.replace(/<span contenteditable="false" style="color:red">/g,' #');// 后台返回是这样的,变了,需要也处理一下
temp = temp.replace(/<span style="color:red" contenteditable="false">/g,' #');
temp = temp.replace(/<\/span>/g,'#')
// console.log(temp);
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;


py.vue
Diff onderdrukt omdat het te groot bestand
Bestand weergeven


+ 174
- 51
src/views/Receive/index.vue Bestand weergeven

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


+ 104
- 0
src/views/ReceivingRecords/index.vue Bestand weergeven

@@ -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) => {


+ 8
- 7
src/views/Statistics/MentoringAbility.vue Bestand weergeven

@@ -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%;


+ 1606
- 0
src/views/Template/requiremining.vue
Diff onderdrukt omdat het te groot bestand
Bestand weergeven


+ 1286
- 0
src/views/Template/requireminingAnalysis.vue
Diff onderdrukt omdat het te groot bestand
Bestand weergeven


+ 476
- 10
src/views/Template/taboo.vue Bestand weergeven

@@ -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>&nbsp;</text>");
}else{
this.insertHtmlAtCaret("<span contentEditable='false' style='color:red'>"+item.value+"</span><text>&nbsp;</text>");
}
},
// 处理标签,删除不需要的标签格式
delMark(str) {
const hasStr = (str) => {
let index = str.indexOf('<')
let index1 = str.indexOf('>')
if(index>0&&index1>0){
let replaceStr= str.substring(index, index1+1)
str= str.replace(replaceStr,'')
hasStr(str)
}
}
hasStr(str)
},
// 确认插入选择的话术
clickOK(){
this.innerVisible1 = false
if(!this.huashu) {this.$message.error('请选择话术'); return ;}
this.insertContent("<text>"+this.huashu+"</text><span contentEditable='false' style='color:red'>answer</span><text>&nbsp;</text>");
},

//点击编辑按钮
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>\&nbsp\;<\/text>/g,'');
temp = temp.replace(/\&nbsp\;/g,'');
temp = temp.replace(/<text>/g,'');
temp = temp.replace(/<\/text>/g,'');
temp = temp.replace(/<span contenteditable="false" style="color:red">/g,' #');// 后台返回是这样的,变了,需要也处理一下
temp = temp.replace(/<span style="color:red" contenteditable="false">/g,' #');
temp = temp.replace(/<\/br>/g,'')
temp = temp.replace(/<br>/g,'')
temp = temp.replace(/<\/span>/g,'#')
// console.log('处理前temp',temp);
this.delMark(temp);
// console.log(temp);
return temp
},
// 模型保存
saveFun(){
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;


+ 65
- 3
src/views/building/index.vue Bestand weergeven

@@ -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: [],


Laden…
Annuleren
Opslaan