| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858 |
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport"
- content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no, viewport-fit=cover" />
- <title>微分AI</title>
- <!--引入 element-ui 的样式,-->
- <link rel="stylesheet"
- href="https://wl-1306604067.cos.ap-guangzhou.myqcloud.com/production/ct/103548289110001/1742018383195/element-ui.css">
- <!-- 必须先引入vue, 后使用element-ui -->
- <script
- src="https://wl-1306604067.cos.ap-guangzhou.myqcloud.com/production/ct/103548289110001/1742017957144/vue.js"></script>
- <!-- 引入element 的组件库-->
- <script
- src="https://wl-1306604067.cos.ap-guangzhou.myqcloud.com/production/ct/103548289110001/1742017747738/element-ui.js"></script>
- <script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
- <script src="https://open.work.weixin.qq.com/wwopen/js/jwxwork-1.0.0.js"></script>
- <!-- <script src="js/vconsole.min.js"></script>
- <script>
- var vConsole = new window.VConsole();
- </script> -->
- </head>
- <style>
- body {
- margin: 0;
- padding: 0;
- }
- .box {
- width: 100vw;
- height: auto;
- box-sizing: border-box;
- background: #FAFAFA;
- }
- .page6 {
- width: 100vw;
- box-sizing: border-box;
- }
- .tab_list {
- width: 100%;
- height: 50px;
- background: #FAFCFF;
- position: absolute;
- top: 90px;
- border-radius: 15px 15px 0 0;
- display: flex;
- padding-top: 15px;
- padding-left: 15px;
- box-sizing: border-box;
- }
- .tab_item {
- font-weight: 500;
- font-size: 16px;
- color: #222222;
- line-height: 24px;
- margin-right: 40px;
- display: flex;
- align-items: center;
- flex-direction: column;
- }
- .tab_item img {
- width: 12px;
- }
- .show_active {
- color: #1677FF;
- }
- .smart_reply {
- width: 100%;
- height: calc(100vh - 120px);
- box-sizing: border-box;
- background: linear-gradient( 180deg, #FAFCFF 0%, #FAFAFA 100%);
- padding: 25px 15px 60px 15px;
- overflow-y: auto;
- display: flex;
- flex-direction: column;
- justify-content: space-between;
- }
- .smart_see {
- width: 100%;
- height: calc(100vh - 140px);
- box-sizing: border-box;
- background: linear-gradient( 180deg, #FAFCFF 0%, #FAFAFA 100%);
- padding: 25px 15px 85px 15px;
- overflow-y: auto;
- }
- .smart_form {
- padding: 15px;
- background: #FFFFFF;
- border-radius: 10px;
- margin-bottom: 15px;
- }
- .intent_car {
- padding: 15px;
- background: #FFFFFF;
- border-radius: 10px;
- font-weight: 400;
- font-size: 14px;
- color: #666666;
- }
- .intent {
- font-weight: 500;
- font-size: 16px;
- color: #1677FF;
- padding: 8px 12px 8px 10px;
- background: #FFFFFF;
- border-radius: 5px;
- border: 1px solid #1677FF;
- width: fit-content;
- margin-top: 15px;
- }
- .sync_btn {
- background: #1677FF;
- border-radius: 20px;
- font-weight: 500;
- font-size: 16px;
- color: #FFFFFF;
- text-align: center;
- padding: 15px 96px;
- position: fixed;
- bottom: 34px;
- margin: 0 20px;
- }
- .item_lable {
- display: flex;
- align-items: center;
- justify-content: space-between;
- font-weight: 400;
- font-size: 14px;
- color: #666666;
- margin-bottom: 10px;
- }
- .edit_btn {
- color: #1677FF;
- }
- .el-input__inner{
- border-top: none;
- border-left: none;
- border-right: none;
- border-bottom: 1px solid #F4F3FD;
- padding: 0;
- margin-bottom: 10px;
- border-radius: 0;
- background-color: #FFFFFF !important;
- }
- .el-textarea__inner{
- border: none;
- padding: 0;
- border-radius: 10px;
- padding: 10px;
- }
- .client_msg {
- width: 100%;
- height: 120px;
- background: url('./img/smart_bg.png');
- background-size: 100% 120px;
- background-repeat: no-repeat;
- position: relative;
- padding-top: 20px;
- padding-left: 20px;
- box-sizing: border-box;
- }
- .client_wx {
- display: flex;
- align-items: center;
- }
- .client_wx img {
- width: 56px;
- height: 56px;
- border-radius: 10px ;
- margin-right: 12px;
- }
- .wx_msg {
- display: flex;
- flex-direction: column;
- align-items: flex-start;
- justify-content: center;
- font-weight: 500;
- color: #FFFFFF;
- }
- .wx_name {
- font-size: 16px;
- padding-bottom: 5px;
- }
- .wx_tag {
- font-size: 10px;
- }
- .loading_text {
- font-size: 14px;
- color: #999999;
- padding-top: 90px;
- text-align: center;
- }
- .input_style {
- display: flex;
- align-items: center;
- position: fixed;
- bottom: 10px;
- }
- .smart_input {
- width: 288px;
- margin-right: 15px;
- }
- .smart_input .el-input__inner {
- height: 50px;
- border: none;
- padding: 15px;
- background: #F6F6F7;
- border-radius: 20px;
- font-size: 14px;
- line-height: 20px;
- margin-bottom: 0;
- }
- .send_btn {
- width: 42px;
- height: 42px;
- }
- .reply_content {
- background: #F0F4FA;
- border-radius: 10px;
- font-size: 14px;
- color: #222222;
- line-height: 28px;
- padding: 15px;
- margin-bottom: 7px;
- }
- .reply_title {
- color: #1677FF;
- }
- .copy_div {
- display: flex;
- align-items: center;
- justify-content: flex-end;
- border-top: 1px solid #CCD8E8;
- font-size: 14px;
- color: #1677FF;
- line-height: 20px;
- padding-top: 13px;
- margin-top: 15px;
- }
- .copy_div img {
- width: 14px;
- height: 14px;
- margin-right: 8px;
- }
- .send_msg {
- font-size: 14px;
- color: #222222;
- padding: 12px 10px 12px 15px;
- background: #FFFFFF;
- display: flex;
- align-items: center;
- width: fit-content;
- margin-bottom: 10px;
- }
- .msg_list {
- font-size: 12px;
- color: #222222;
- padding: 12px 10px 12px 15px;
- background: #FFFFFF;
- display: flex;
- align-items: center;
- width: fit-content;
- margin-bottom: 10px;
- }
- .msg_list img {
- width: 16px;
- height: 16px;
- margin-left: 8px;
- }
- </style>
- <body>
- <div id="box" class="box">
- <div class="page6">
- <div class="client_msg">
- <div class="client_wx">
- <img v-if="clientData.avatar" :src="clientData.avatar" />
- <div class="wx_msg" v-if="clientData.name">
- <div class="wx_name">{{clientData.name}}</div>
- <div class="wx_tag">@微信</div>
- </div>
- </div>
- </div>
- <div class="tab_list">
- <div class="tab_item" @click="handleClick('first')">
- <div :class="activeName === 'first' ? 'show_active' : ''">智能回复</div>
- <img src="./img/tab_line.png" v-show="activeName ==='first'" />
- </div>
- <div class="tab_item" @click="handleClick('second')">
- <div :class="activeName === 'second' ? 'show_active' : ''">智能洞察</div>
- <img src="./img/tab_line.png" v-show="activeName ==='second'" />
- </div>
- </div>
- <div class="smart_reply" v-if="activeName ==='first'">
- <div class="loading_text" v-if="!lastMsgData.length && !replyData.problem">暂无新提问或正在同步中…</div>
- <div>
- <div class="send_msg" v-if="replyData.problem">
- <span>{{replyData.problem}}</span>
- </div>
- <div class="reply_content" v-if="loading || replyData.reply">
- <div class="reply_title">智能回复:</div>
- <div>{{loading ? '思考中...' : replyData.reply}}</div>
- <div class="copy_div" @click="handleCopy(replyData.reply)">
- <img src="./img/copy_icon2.png" alt="">
- <span>复制</span>
- </div>
- </div>
- </div>
- <div>
- <div class="msg_list" v-for="(item, index) in lastMsgData" :key="index" @click="sendMsg(item)">
- <span>{{item.content}}</span>
- <img src="./img/msg_icon.png" alt="">
- </div>
- </div>
- <div class="input_style">
- <el-input class="smart_input" placeholder="请输入您的问题" v-model="problem">
- </el-input>
- <img class="send_btn" src="./img/send_btn2.png" @click="sendMsg('')"></img>
- </div>
- </div>
- <div class="smart_see" v-if="activeName === 'second'">
- <div class="smart_form">
- <div class="item_lable">
- <div>手机号</div>
- <div class="edit_btn" @click="handleSave(1, disabled1)">{{disabled1 ? '编辑' : '确定'}}</div>
- </div>
- <el-input v-model="insightData.phone" type="number" :readonly="disabled1" placeholder="请输入内容"></el-input>
- <div class="item_lable">
- <div>地区</div>
- <div class="edit_btn" @click="handleSave(2, disabled2)">{{disabled2 ? '编辑' : '确定'}}</div>
- </div>
- <el-cascader v-model="areaData" :options="transformedData" style="width: 100%;" :disabled="disabled2"
- @change="handleInfoCity">
- </el-cascader>
- <div class="item_lable">
- <div>详细地址</div>
- <div class="edit_btn" @click="handleSave(3, disabled3)">{{disabled3 ? '编辑' : '确定'}}</div>
- </div>
- <el-input type="textarea" :autosize="{ minRows: 3 }" placeholder="暂无" v-model="insightData.address" :disabled="disabled3">
- </el-input>
- </div>
- <div class="intent_car">
- <div>意向车型</div>
- <div class="intent" v-if="insightData.intent">{{insightData.intent}}</div>
- </div>
- <div class="sync_btn" @click="handleSync">同步到用户画像</div>
- </div>
- </div>
- </div>
- </body>
- <script>
- new Vue({
- el: '#box',
- data() {
- return {
- httpUrl: '',
- bId: null,
- env: '',
- memberId: null,
- externalUserId: null,
- activeName: 'first',
- clientData: {
- avatar: '',
- name: '',
- },
- lastMsgData: [],
- problem: '',
- replyData: {
- messageId: '',
- problem: '',
- reply: '',
- },
- timer: null,
- secondTimer: null,
- loading: false,
- disabled1: true,
- disabled2: true,
- disabled3: true,
- insightData: {
- phone: '',
- province: '',
- city: '',
- address: '',
- intent: '',
- },
- transformedData: [],
- areaData: [],
- titleTimer: null,
- titleOutput: false
- }
- },
- created() {
- this.bId = this.getQueryParam('bId')
- this.env = this.getQueryParam('env')
- if (!this.env || this.env === 'prod') {
- this.httpUrl = 'https://wlapi.wefanbot.com'
- } else {
- this.httpUrl = 'http://test.wefanbot.com:18993'
- }
-
- if (this.getQueryParam('memberId')) {
- // 已授权
- this.memberId = this.getQueryParam('memberId')
- if (this.getQueryParam('externalUserId')) {
- this.externalUserId = this.getQueryParam('externalUserId')
- this.getClientExt()
- this.getLastMsg()
- // 设置定时器:每60秒刷新一次
- this.timer = setInterval(() => {
- this.getLastMsg()
- }, 5 * 1000)
- this.insight()
- this.secondTimer = setInterval(() => {
- this.insight()
- }, 5 * 1000)
- this.getCityLevel()
- } else {
- this.getQyWxSign()
- }
- } else {
- // 授权
- this.getAuth()
- }
- // this.memberId = "woU17nDAAAfzP9lzjd-G8iwCiveKR8GA"
- // this.externalUserId = "wmU17nDAAALFy-xUBJDwHbB5CsjzeY_g"
- // this.getClientExt()
- // this.getLastMsg()
- // // 设置定时器:每60秒刷新一次
- // this.timer = setInterval(() => {
- // this.getLastMsg()
- // }, 60 * 1000)
- // this.getCityLevel()
- },
- methods: {
- getAuth() {
- fetch(this.httpUrl + `/p/insuite/p/getRedirectUri?env=${this.env}&bId=${this.bId}`)
- .then(res => {
- return res.json()
- }).then(result => {
- let { data, code, msg } = result
- if (code === 1) {
- window.location.replace(data)
- } else {
- this.$message({
- message: msg,
- type: 'warning'
- })
- }
- })
- },
- getQyWxSign() {
- fetch(this.httpUrl + '/scrm/v1/wxcp-corp/p/getAgentConfig', {
- method: 'post',
- body: JSON.stringify({
- bid: this.bId,
- url: window.location.href,
- }),
- headers: {
- 'Content-Type': 'application/json'
- }
- }).then(res => {
- return res.json()
- }).then(result => {
- let { data, code, msg } = result
- if (code === 1) {
- let that = this
- wx.agentConfig({
- corpid: data.corpid,
- agentid: data.agentId,
- timestamp: data.timestamp, // 必填,生成签名的时间戳
- nonceStr: data.nonceStr, // 必填,生成签名的随机串
- signature: data.agentSignature, // 必填,签名,见附录1
- jsApiList: ['getCurExternalContact'], // 必填,需要使用的JS接口列表
- success: function (res) {
- // 回调
- // 此处直接在注入回调中调用了获取当前外部联系人userId的接口,注意此接口是从聊天框的工具栏进入才能获取
- wx.invoke('getCurExternalContact', {
- }, function (res) {
- if (res.err_msg == "getCurExternalContact:ok") {
- console.log('invoke成功:', res.userId);
- that.externalUserId = res.userId
- that.getClientExt()
- that.getLastMsg()
- // 设置定时器:每60秒刷新一次
- that.timer = setInterval(() => {
- that.getLastMsg()
- }, 5 * 1000)
- that.insight()
- that.secondTimer = setInterval(() => {
- that.insight()
- }, 5 * 1000)
- that.getCityLevel()
- } else {
- //错误处理
- console.log('invoke失败:', res)
- }
- });
- },
- fail: function (res) {
- if (res.errMsg.indexOf('function not exist') > -1) {
- alert('版本过低请升级');
- }
- },
- complete: function (res) {
- // 回调
- console.log('complete1', res);
- }
- });
- } else {
- this.$message({
- message: msg,
- type: 'warning'
- })
- }
- })
- },
- handleClick(tabName) {
- this.activeName = tabName
- if (tabName === 'second') {
- this.insight()
- }
- },
- // 获取客户画像
- getClientExt() {
- fetch(this.httpUrl + '/scrm/v1/wxcp-client/p/h5clientInfo', {
- method: 'post',
- body: JSON.stringify({
- bid: this.bId,
- externalUserId: this.externalUserId,
- }),
- headers: {
- 'Content-Type': 'application/json'
- }
- }).then(res => {
- return res.json()
- }).then(result => {
- let { data, code, msg } = result
- if (code === 1) {
- this.clientData = data
- } else {
- this.$message({
- message: msg,
- type: 'warning'
- })
- }
- })
- },
- // 获取智能回复提示
- getLastMsg() {
- fetch(this.httpUrl + '/scrm/v1/wxcp-message/p/getLastMessagesByH5', {
- method: 'post',
- body: JSON.stringify({
- bid: this.bId,
- externalUserId: this.externalUserId,
- memberId: this.memberId,
- }),
- headers: {
- 'Content-Type': 'application/json'
- }
- }).then(res => {
- return res.json()
- }).then(result => {
- let { data, code, msg } = result
- if (code === 1) {
- this.lastMsgData = data
- } else {
- this.$message({
- message: msg,
- type: 'warning'
- })
- }
- })
- },
- sendMsg(item) {
- if (!item.id && !this.problem) {
- this.$message({
- message: '请输入内容',
- type: 'warning'
- })
- return
- }
- this.replyData.problem = item.id ? item.content : this.problem
- this.loading = true
- fetch(this.httpUrl + '/scrm/v1/tenancy-ai/p/h5AiReply', {
- method: 'post',
- body: JSON.stringify({
- bid: this.bId,
- externalUserId: this.externalUserId,
- memberId: this.memberId,
- endMsgId: item.id,
- problem: item.id ? '' : this.problem,
- }),
- headers: {
- 'Content-Type': 'application/json'
- }
- }).then(res => {
- return res.json()
- }).then(result => {
- let { data, code, msg } = result
- if (code === 1) {
- this.replyData = {
- messageId: item.id,
- problem: item.id ? item.content : data.problem,
- reply: '',
- }
- this.titleWriter(data.reply)
- } else {
- this.$message({
- message: msg,
- type: 'warning'
- })
- }
- }).finally(() => {
- this.problem = ''
- this.loading = false
- })
- },
- titleWriter (text) {
- clearTimeout(this.titleTimer)
- this.titleTimer = null
- let index = 0
- this.replyData.reply = ''
- this.titleOutput = true
- const writeText = () => {
- if (index < text.length) {
- this.replyData.reply += text.charAt(index)
- index++
- this.titleTimer = setTimeout(writeText, 50)
- } else {
- this.titleOutput = false
- }
- }
- writeText()
- },
- // 客户洞察
- insight () {
- if (!this.externalUserId) {
- return
- }
- fetch(this.httpUrl + '/scrm/v1/wxcp-chat-tool/p/h5Insight', {
- method: 'post',
- body: JSON.stringify({
- bid: this.bId,
- externalUserId: this.externalUserId,
- memberId: this.memberId,
- }),
- headers: {
- 'Content-Type': 'application/json'
- }
- }).then(res => {
- return res.json()
- }).then(result => {
- let { data, code, msg } = result
- if (code === 1) {
- this.insightData = data
- this.areaData = [data.province, data.city]
- } else {
- this.$message({
- message: msg,
- type: 'warning'
- })
- }
- })
- },
- handleSave (num, status) {
- if (num === 1) {
- if (this.insightData.phone && this.insightData.phone.length !== 11) {
- this.$message({
- message: '请输入正确的手机号',
- type: 'warning'
- })
- return
- }
- this.disabled1 = !status
- if (status) {
- return
- }
- } else if (num === 2) {
- this.disabled2 = !status
- if (status) {
- return
- }
- } else if (num === 3) {
- this.disabled3 = !status
- if (status) {
- return
- }
- }
- fetch(this.httpUrl + '/scrm/v1/wxcp-chat-tool/p/update2Insight', {
- method: 'post',
- body: JSON.stringify({
- bid: this.bId,
- externalUserId: this.externalUserId,
- memberId: this.memberId,
- ...this.insightData
- }),
- headers: {
- 'Content-Type': 'application/json'
- }
- }).then(res => {
- return res.json()
- }).then(result => {
- let { data, code, msg } = result
- if (code === 1) {
- this.$message({
- message: '保存成功',
- type: 'success'
- })
- } else {
- this.$message({
- message: msg,
- type: 'warning'
- })
- }
- })
- },
- handleSync () {
- fetch(this.httpUrl + '/scrm/v1/wxcp-chat-tool/p/sync2Ext', {
- method: 'post',
- body: JSON.stringify({
- bid: this.bId,
- externalUserId: this.externalUserId,
- memberId: this.memberId,
- ...this.insightData
- }),
- headers: {
- 'Content-Type': 'application/json'
- }
- }).then(res => {
- return res.json()
- }).then(result => {
- let { data, code, msg } = result
- if (code === 1) {
- this.$message({
- message: '同步成功',
- type: 'success'
- })
- } else {
- this.$message({
- message: msg,
- type: 'warning'
- })
- }
- }).finally(() => {
- this.disabled1 = true
- this.disabled2 = true
- this.disabled3 = true
- })
- },
- // 获取城市数据
- getCityLevel() {
- fetch('https://wl-1306604067.cos.ap-guangzhou.myqcloud.com/production/ct/1000/1720161853377/pc-code.json')
- .then(res => {
- return res.json()
- }).then(result => {
- this.transformedData = this.transformData(result)
- })
- },
- handleInfoCity(value) {
- this.insightData.province = value[0]
- this.insightData.city = value[1]
- },
- // 省市数据做转换
- transformData(data) {
- return data.map(item => {
- // 创建一个新对象,避免直接修改原始对象
- const newItem = { ...item };
- // 重命名 text 为 label
- if (newItem.text) {
- newItem.label = newItem.text;
- newItem.value = newItem.text;
- delete newItem.text;
- }
- // 如果存在 children 数组,则递归处理每个子项
- if (newItem.children && Array.isArray(newItem.children)) {
- newItem.children = this.transformData(newItem.children);
- }
- return newItem;
- })
- },
- // 截取url中的数据
- getQueryParam(paramName) {
- // 获取当前URL的查询字符串部分
- const queryString = window.location.search;
- // 创建一个URLSearchParams对象
- const urlParams = new URLSearchParams(queryString);
- // 返回指定参数的值,如果不存在则返回null
- return urlParams.get(paramName);
- },
- // 检测是否iOS端
- iosAgent() {
- return navigator.userAgent.match(/(iPhone|iPod|iPad);?/i);
- },
- // 复制文本函数,微信端,需要在用户触发 Click 事件里面才能执行成功
- handleCopy(message) {
- if (this.loading) {
- this.$message({
- message: '请等待思考完成后再复制',
- type: 'warning'
- })
- return
- }
- if (this.iosAgent()) {
- let inputObj = document.createElement("input");
- inputObj.value = message;
- document.body.appendChild(inputObj);
- inputObj.select();
- inputObj.setSelectionRange(0, inputObj.value.length);
- this.execCommand('Copy');
- document.body.removeChild(inputObj);
- } else {
- let domObj = document.createElement("span");
- domObj.innerHTML = message;
- document.body.appendChild(domObj);
- let selection = window.getSelection();
- let range = document.createRange();
- range.selectNodeContents(domObj);
- selection.removeAllRanges();
- selection.addRange(range);
- this.execCommand('Copy');
- document.body.removeChild(domObj);
- }
- },
- // 执行浏览器命令 Copy 顺便输出一下日志,如果在移动端推荐写个方法展示日志或者用alert(msg)也行。
- execCommand(action) {
- let is = document.execCommand(action);
- if (is) {
- this.$message({
- message: '复制成功',
- type: 'success'
- })
- } else {
- this.$message({
- message: '复制失败',
- type: 'error'
- })
- }
- },
- }
- })
- </script>
- </html>
|