smartTab.html 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport"
  7. content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no, viewport-fit=cover" />
  8. <title>微分AI</title>
  9. <!--引入 element-ui 的样式,-->
  10. <link rel="stylesheet"
  11. href="https://wl-1306604067.cos.ap-guangzhou.myqcloud.com/production/ct/103548289110001/1742018383195/element-ui.css">
  12. <!-- 必须先引入vue, 后使用element-ui -->
  13. <script
  14. src="https://wl-1306604067.cos.ap-guangzhou.myqcloud.com/production/ct/103548289110001/1742017957144/vue.js"></script>
  15. <!-- 引入element 的组件库-->
  16. <script
  17. src="https://wl-1306604067.cos.ap-guangzhou.myqcloud.com/production/ct/103548289110001/1742017747738/element-ui.js"></script>
  18. <script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
  19. <script src="https://open.work.weixin.qq.com/wwopen/js/jwxwork-1.0.0.js"></script>
  20. <script src="js/vconsole.min.js"></script>
  21. <script>
  22. var vConsole = new window.VConsole();
  23. </script>
  24. </head>
  25. <style>
  26. body {
  27. margin: 0;
  28. padding: 0;
  29. }
  30. .box {
  31. width: 100vw;
  32. height: auto;
  33. box-sizing: border-box;
  34. background: #FAFAFA;
  35. }
  36. .page6 {
  37. width: 100vw;
  38. box-sizing: border-box;
  39. }
  40. .tab_list {
  41. width: 100%;
  42. height: 50px;
  43. background: #FAFCFF;
  44. position: absolute;
  45. top: 90px;
  46. border-radius: 15px 15px 0 0;
  47. display: flex;
  48. padding-top: 15px;
  49. padding-left: 15px;
  50. box-sizing: border-box;
  51. }
  52. .tab_item {
  53. font-weight: 500;
  54. font-size: 16px;
  55. color: #222222;
  56. line-height: 24px;
  57. margin-right: 40px;
  58. display: flex;
  59. align-items: center;
  60. flex-direction: column;
  61. }
  62. .tab_item img {
  63. width: 12px;
  64. }
  65. .show_active {
  66. color: #1677FF;
  67. }
  68. .smart_reply {
  69. width: 100%;
  70. height: calc(100vh - 120px);
  71. box-sizing: border-box;
  72. background: linear-gradient( 180deg, #FAFCFF 0%, #FAFAFA 100%);
  73. padding: 25px 15px 60px 15px;
  74. overflow-y: auto;
  75. display: flex;
  76. flex-direction: column;
  77. justify-content: space-between;
  78. }
  79. .smart_see {
  80. width: 100%;
  81. height: calc(100vh - 140px);
  82. box-sizing: border-box;
  83. background: linear-gradient( 180deg, #FAFCFF 0%, #FAFAFA 100%);
  84. padding: 25px 15px 85px 15px;
  85. overflow-y: auto;
  86. }
  87. .smart_form {
  88. padding: 15px;
  89. background: #FFFFFF;
  90. border-radius: 10px;
  91. margin-bottom: 15px;
  92. }
  93. .intent_car {
  94. padding: 15px;
  95. background: #FFFFFF;
  96. border-radius: 10px;
  97. font-weight: 400;
  98. font-size: 14px;
  99. color: #666666;
  100. }
  101. .intent {
  102. font-weight: 500;
  103. font-size: 16px;
  104. color: #1677FF;
  105. padding: 8px 12px 8px 10px;
  106. background: #FFFFFF;
  107. border-radius: 5px;
  108. border: 1px solid #1677FF;
  109. width: fit-content;
  110. margin-top: 15px;
  111. }
  112. .sync_btn {
  113. background: #1677FF;
  114. border-radius: 20px;
  115. font-weight: 500;
  116. font-size: 16px;
  117. color: #FFFFFF;
  118. text-align: center;
  119. padding: 15px 96px;
  120. position: fixed;
  121. bottom: 34px;
  122. margin: 0 20px;
  123. }
  124. .item_lable {
  125. display: flex;
  126. align-items: center;
  127. justify-content: space-between;
  128. font-weight: 400;
  129. font-size: 14px;
  130. color: #666666;
  131. margin-bottom: 10px;
  132. }
  133. .edit_btn {
  134. color: #1677FF;
  135. }
  136. .el-input__inner{
  137. border-top: none;
  138. border-left: none;
  139. border-right: none;
  140. border-bottom: 1px solid #F4F3FD;
  141. padding: 0;
  142. margin-bottom: 10px;
  143. border-radius: 0;
  144. background-color: #FFFFFF !important;
  145. }
  146. .el-textarea__inner{
  147. border: none;
  148. padding: 0;
  149. border-radius: 10px;
  150. padding: 10px;
  151. }
  152. .client_msg {
  153. width: 100%;
  154. height: 120px;
  155. background: url('./img/smart_bg.png');
  156. background-size: 100% 120px;
  157. background-repeat: no-repeat;
  158. position: relative;
  159. padding-top: 20px;
  160. padding-left: 20px;
  161. box-sizing: border-box;
  162. }
  163. .client_wx {
  164. display: flex;
  165. align-items: center;
  166. }
  167. .client_wx img {
  168. width: 56px;
  169. height: 56px;
  170. border-radius: 10px ;
  171. margin-right: 12px;
  172. }
  173. .wx_msg {
  174. display: flex;
  175. flex-direction: column;
  176. align-items: flex-start;
  177. justify-content: center;
  178. font-weight: 500;
  179. color: #FFFFFF;
  180. }
  181. .wx_name {
  182. font-size: 16px;
  183. padding-bottom: 5px;
  184. }
  185. .wx_tag {
  186. font-size: 10px;
  187. }
  188. .loading_text {
  189. font-size: 14px;
  190. color: #999999;
  191. padding-top: 90px;
  192. text-align: center;
  193. }
  194. .input_style {
  195. display: flex;
  196. align-items: center;
  197. position: fixed;
  198. bottom: 10px;
  199. }
  200. .smart_input {
  201. width: 288px;
  202. margin-right: 15px;
  203. }
  204. .smart_input .el-input__inner {
  205. height: 50px;
  206. border: none;
  207. padding: 15px;
  208. background: #F6F6F7;
  209. border-radius: 20px;
  210. font-size: 14px;
  211. line-height: 20px;
  212. margin-bottom: 0;
  213. }
  214. .send_btn {
  215. width: 42px;
  216. height: 42px;
  217. }
  218. .reply_content {
  219. background: #F0F4FA;
  220. border-radius: 10px;
  221. font-size: 14px;
  222. color: #222222;
  223. line-height: 28px;
  224. padding: 15px;
  225. margin-bottom: 7px;
  226. }
  227. .reply_title {
  228. color: #1677FF;
  229. }
  230. .copy_div {
  231. display: flex;
  232. align-items: center;
  233. justify-content: flex-end;
  234. border-top: 1px solid #CCD8E8;
  235. font-size: 14px;
  236. color: #1677FF;
  237. line-height: 20px;
  238. padding-top: 13px;
  239. margin-top: 15px;
  240. }
  241. .copy_div img {
  242. width: 14px;
  243. height: 14px;
  244. margin-right: 8px;
  245. }
  246. .send_msg {
  247. font-size: 14px;
  248. color: #222222;
  249. padding: 12px 10px 12px 15px;
  250. background: #FFFFFF;
  251. display: flex;
  252. align-items: center;
  253. width: fit-content;
  254. margin-bottom: 10px;
  255. }
  256. .msg_list {
  257. font-size: 12px;
  258. color: #222222;
  259. padding: 12px 10px 12px 15px;
  260. background: #FFFFFF;
  261. display: flex;
  262. align-items: center;
  263. width: fit-content;
  264. margin-bottom: 10px;
  265. }
  266. .msg_list img {
  267. width: 16px;
  268. height: 16px;
  269. margin-left: 8px;
  270. }
  271. </style>
  272. <body>
  273. <div id="box" class="box">
  274. <div class="page6">
  275. <div class="client_msg">
  276. <div class="client_wx">
  277. <img v-if="clientData.avatar" :src="clientData.avatar" />
  278. <div class="wx_msg" v-if="clientData.name">
  279. <div class="wx_name">{{clientData.name}}</div>
  280. <div class="wx_tag">@微信</div>
  281. </div>
  282. </div>
  283. </div>
  284. <div class="tab_list">
  285. <div class="tab_item" @click="handleClick('first')">
  286. <div :class="activeName === 'first' ? 'show_active' : ''">智能回复</div>
  287. <img src="./img/tab_line.png" v-show="activeName ==='first'" />
  288. </div>
  289. <div class="tab_item" @click="handleClick('second')">
  290. <div :class="activeName === 'second' ? 'show_active' : ''">智能洞察</div>
  291. <img src="./img/tab_line.png" v-show="activeName ==='second'" />
  292. </div>
  293. </div>
  294. <div class="smart_reply" v-if="activeName ==='first'">
  295. <div class="loading_text" v-if="(!lastMsgData || !lastMsgData.length) && !replyData.problem">暂无新提问或正在同步中…</div>
  296. <div>
  297. <div class="send_msg" v-if="replyData.problem">
  298. <span>{{replyData.problem}}</span>
  299. </div>
  300. <div class="reply_content" v-if="loading || replyData.reply">
  301. <div class="reply_title">智能回复:</div>
  302. <div>{{loading ? '思考中...' : replyData.reply}}</div>
  303. <div class="copy_div" @click="handleCopy(replyData.reply)">
  304. <img src="./img/copy_icon2.png" alt="">
  305. <span>复制</span>
  306. </div>
  307. </div>
  308. </div>
  309. <div>
  310. <div class="msg_list" v-for="(item, index) in lastMsgData" :key="index" @click="sendMsg(item)">
  311. <span>{{item.content}}</span>
  312. <img src="./img/msg_icon.png" alt="">
  313. </div>
  314. </div>
  315. <div class="input_style">
  316. <el-input class="smart_input" placeholder="请输入您的问题" v-model="problem">
  317. </el-input>
  318. <img class="send_btn" src="./img/send_btn2.png" @click="sendMsg('')"></img>
  319. </div>
  320. </div>
  321. <div class="smart_see" v-if="activeName === 'second'">
  322. <div class="smart_form">
  323. <div class="item_lable">
  324. <div>手机号</div>
  325. <div class="edit_btn" @click="handleSave(1, disabled1)">{{disabled1 ? '编辑' : '确定'}}</div>
  326. </div>
  327. <el-input v-model="insightData.phone" type="number" :readonly="disabled1" placeholder="请输入内容"></el-input>
  328. <div class="item_lable">
  329. <div>地区</div>
  330. <div class="edit_btn" @click="handleSave(2, disabled2)">{{disabled2 ? '编辑' : '确定'}}</div>
  331. </div>
  332. <el-cascader v-model="areaData" :options="transformedData" style="width: 100%;" :disabled="disabled2"
  333. @change="handleInfoCity">
  334. </el-cascader>
  335. <div class="item_lable">
  336. <div>详细地址</div>
  337. <div class="edit_btn" @click="handleSave(3, disabled3)">{{disabled3 ? '编辑' : '确定'}}</div>
  338. </div>
  339. <el-input type="textarea" :autosize="{ minRows: 3 }" placeholder="暂无" v-model="insightData.address" :disabled="disabled3">
  340. </el-input>
  341. </div>
  342. <div class="intent_car">
  343. <div>意向车型</div>
  344. <div class="intent" v-if="insightData.intent">{{insightData.intent}}</div>
  345. </div>
  346. <div class="sync_btn" @click="handleSync">同步到用户画像</div>
  347. </div>
  348. </div>
  349. </div>
  350. </body>
  351. <script>
  352. new Vue({
  353. el: '#box',
  354. data() {
  355. return {
  356. httpUrl: '',
  357. bId: null,
  358. env: '',
  359. memberId: null,
  360. externalUserId: null,
  361. activeName: 'first',
  362. clientData: {
  363. avatar: '',
  364. name: '',
  365. },
  366. lastMsgData: [],
  367. problem: '',
  368. replyData: {
  369. messageId: '',
  370. problem: '',
  371. reply: '',
  372. },
  373. loading: false,
  374. disabled1: true,
  375. disabled2: true,
  376. disabled3: true,
  377. insightData: {
  378. phone: '',
  379. province: '',
  380. city: '',
  381. address: '',
  382. intent: '',
  383. },
  384. transformedData: [],
  385. areaData: [],
  386. titleTimer: null,
  387. titleOutput: false
  388. }
  389. },
  390. created() {
  391. this.bId = this.getQueryParam('bId')
  392. this.env = this.getQueryParam('env')
  393. if (!this.env || this.env === 'prod') {
  394. this.httpUrl = 'https://wlapi.wefanbot.com'
  395. } else {
  396. this.httpUrl = 'http://test.wefanbot.com:18993'
  397. }
  398. this.activeName = 'first'
  399. if (this.getQueryParam('memberId')) {
  400. // 已授权
  401. this.memberId = this.getQueryParam('memberId')
  402. if (this.getQueryParam('externalUserId')) {
  403. this.externalUserId = this.getQueryParam('externalUserId')
  404. this.getClientExt()
  405. this.getCityLevel()
  406. this.getLastMsg()
  407. } else {
  408. this.getQyWxSign()
  409. }
  410. } else {
  411. // 授权
  412. this.getAuth()
  413. }
  414. },
  415. methods: {
  416. getAuth() {
  417. fetch(this.httpUrl + `/p/insuite/p/getRedirectUri?env=${this.env}&bId=${this.bId}`)
  418. .then(res => {
  419. return res.json()
  420. }).then(result => {
  421. let { data, code, msg } = result
  422. if (code === 1) {
  423. window.location.replace(data)
  424. } else {
  425. this.$message({
  426. message: msg,
  427. type: 'warning'
  428. })
  429. }
  430. })
  431. },
  432. getQyWxSign() {
  433. fetch(this.httpUrl + '/scrm/v1/wxcp-corp/p/getAgentConfig', {
  434. method: 'post',
  435. body: JSON.stringify({
  436. bid: this.bId,
  437. url: window.location.href,
  438. }),
  439. headers: {
  440. 'Content-Type': 'application/json'
  441. }
  442. }).then(res => {
  443. return res.json()
  444. }).then(result => {
  445. let { data, code, msg } = result
  446. if (code === 1) {
  447. let that = this
  448. wx.agentConfig({
  449. corpid: data.corpid,
  450. agentid: data.agentId,
  451. timestamp: data.timestamp, // 必填,生成签名的时间戳
  452. nonceStr: data.nonceStr, // 必填,生成签名的随机串
  453. signature: data.agentSignature, // 必填,签名,见附录1
  454. jsApiList: ['getCurExternalContact'], // 必填,需要使用的JS接口列表
  455. success: function (res) {
  456. // 回调
  457. // 此处直接在注入回调中调用了获取当前外部联系人userId的接口,注意此接口是从聊天框的工具栏进入才能获取
  458. wx.invoke('getCurExternalContact', {
  459. }, function (res) {
  460. if (res.err_msg == "getCurExternalContact:ok") {
  461. console.log('invoke成功:', res.userId);
  462. that.externalUserId = res.userId
  463. that.getClientExt()
  464. that.getLastMsg()
  465. that.getCityLevel()
  466. } else {
  467. //错误处理
  468. console.log('invoke失败:', res)
  469. }
  470. });
  471. },
  472. fail: function (res) {
  473. if (res.errMsg.indexOf('function not exist') > -1) {
  474. alert('版本过低请升级');
  475. }
  476. },
  477. complete: function (res) {
  478. // 回调
  479. console.log('complete1', res);
  480. }
  481. });
  482. } else {
  483. this.$message({
  484. message: msg,
  485. type: 'warning'
  486. })
  487. }
  488. })
  489. },
  490. handleClick(tabName) {
  491. this.activeName = tabName
  492. if (tabName === 'second') {
  493. this.insight()
  494. }
  495. },
  496. // 获取客户画像
  497. getClientExt() {
  498. fetch(this.httpUrl + '/scrm/v1/wxcp-client/p/h5clientInfo', {
  499. method: 'post',
  500. body: JSON.stringify({
  501. bid: this.bId,
  502. externalUserId: this.externalUserId,
  503. }),
  504. headers: {
  505. 'Content-Type': 'application/json'
  506. }
  507. }).then(res => {
  508. return res.json()
  509. }).then(result => {
  510. let { data, code, msg } = result
  511. if (code === 1) {
  512. this.clientData = data || {
  513. avatar: '',
  514. name: '',
  515. }
  516. } else {
  517. this.$message({
  518. message: msg,
  519. type: 'warning'
  520. })
  521. }
  522. })
  523. },
  524. // 获取智能回复提示
  525. getLastMsg() {
  526. fetch(this.httpUrl + '/scrm/v1/wxcp-message/p/getLastMessagesByH5', {
  527. method: 'post',
  528. body: JSON.stringify({
  529. bid: this.bId,
  530. externalUserId: this.externalUserId,
  531. memberId: this.memberId,
  532. }),
  533. headers: {
  534. 'Content-Type': 'application/json'
  535. }
  536. }).then(res => {
  537. return res.json()
  538. }).then(result => {
  539. let { data, code, msg } = result
  540. if (code === 1) {
  541. this.lastMsgData = data || []
  542. } else {
  543. this.$message({
  544. message: msg,
  545. type: 'warning'
  546. })
  547. }
  548. }).finally(() => {
  549. // 无论成功失败都等待5秒后重新调用
  550. setTimeout(() => {
  551. this.getLastMsg()
  552. }, 5000)
  553. })
  554. },
  555. sendMsg(item) {
  556. if (!item.id && !this.problem) {
  557. this.$message({
  558. message: '请输入内容',
  559. type: 'warning'
  560. })
  561. return
  562. }
  563. this.replyData.problem = item.id ? item.content : this.problem
  564. this.loading = true
  565. fetch(this.httpUrl + '/scrm/v1/tenancy-ai/p/h5AiReply', {
  566. method: 'post',
  567. body: JSON.stringify({
  568. bid: this.bId,
  569. externalUserId: this.externalUserId,
  570. memberId: this.memberId,
  571. endMsgId: item.id,
  572. problem: item.id ? '' : this.problem,
  573. }),
  574. headers: {
  575. 'Content-Type': 'application/json'
  576. }
  577. }).then(res => {
  578. return res.json()
  579. }).then(result => {
  580. let { data, code, msg } = result
  581. if (code === 1) {
  582. this.replyData = {
  583. messageId: item.id,
  584. problem: item.id ? item.content : data.problem,
  585. reply: '',
  586. }
  587. this.titleWriter(data.reply)
  588. } else {
  589. this.$message({
  590. message: msg,
  591. type: 'warning'
  592. })
  593. }
  594. }).finally(() => {
  595. this.problem = ''
  596. this.loading = false
  597. })
  598. },
  599. titleWriter (text) {
  600. clearTimeout(this.titleTimer)
  601. this.titleTimer = null
  602. let index = 0
  603. this.replyData.reply = ''
  604. this.titleOutput = true
  605. const writeText = () => {
  606. if (index < text.length) {
  607. this.replyData.reply += text.charAt(index)
  608. index++
  609. this.titleTimer = setTimeout(writeText, 50)
  610. } else {
  611. this.titleOutput = false
  612. }
  613. }
  614. writeText()
  615. },
  616. // 客户洞察
  617. insight () {
  618. if (!this.externalUserId) {
  619. return
  620. }
  621. fetch(this.httpUrl + '/scrm/v1/wxcp-chat-tool/p/h5Insight', {
  622. method: 'post',
  623. body: JSON.stringify({
  624. bid: this.bId,
  625. externalUserId: this.externalUserId,
  626. memberId: this.memberId,
  627. }),
  628. headers: {
  629. 'Content-Type': 'application/json'
  630. }
  631. }).then(res => {
  632. return res.json()
  633. }).then(result => {
  634. let { data, code, msg } = result
  635. if (code === 1) {
  636. this.insightData = data
  637. this.areaData = [data.province, data.city]
  638. } else {
  639. this.$message({
  640. message: msg,
  641. type: 'warning'
  642. })
  643. }
  644. }).finally(() => {
  645. // 无论成功失败都等待5秒后重新调用
  646. setTimeout(() => {
  647. this.insight()
  648. }, 5000)
  649. })
  650. },
  651. handleSave (num, status) {
  652. if (num === 1) {
  653. if (this.insightData.phone && this.insightData.phone.length !== 11) {
  654. this.$message({
  655. message: '请输入正确的手机号',
  656. type: 'warning'
  657. })
  658. return
  659. }
  660. this.disabled1 = !status
  661. if (status) {
  662. return
  663. }
  664. } else if (num === 2) {
  665. this.disabled2 = !status
  666. if (status) {
  667. return
  668. }
  669. } else if (num === 3) {
  670. this.disabled3 = !status
  671. if (status) {
  672. return
  673. }
  674. }
  675. fetch(this.httpUrl + '/scrm/v1/wxcp-chat-tool/p/update2Insight', {
  676. method: 'post',
  677. body: JSON.stringify({
  678. bid: this.bId,
  679. externalUserId: this.externalUserId,
  680. memberId: this.memberId,
  681. ...this.insightData
  682. }),
  683. headers: {
  684. 'Content-Type': 'application/json'
  685. }
  686. }).then(res => {
  687. return res.json()
  688. }).then(result => {
  689. let { data, code, msg } = result
  690. if (code === 1) {
  691. this.$message({
  692. message: '保存成功',
  693. type: 'success'
  694. })
  695. } else {
  696. this.$message({
  697. message: msg,
  698. type: 'warning'
  699. })
  700. }
  701. })
  702. },
  703. handleSync () {
  704. fetch(this.httpUrl + '/scrm/v1/wxcp-chat-tool/p/sync2Ext', {
  705. method: 'post',
  706. body: JSON.stringify({
  707. bid: this.bId,
  708. externalUserId: this.externalUserId,
  709. memberId: this.memberId,
  710. ...this.insightData
  711. }),
  712. headers: {
  713. 'Content-Type': 'application/json'
  714. }
  715. }).then(res => {
  716. return res.json()
  717. }).then(result => {
  718. let { data, code, msg } = result
  719. if (code === 1) {
  720. this.$message({
  721. message: '同步成功',
  722. type: 'success'
  723. })
  724. } else {
  725. this.$message({
  726. message: msg,
  727. type: 'warning'
  728. })
  729. }
  730. }).finally(() => {
  731. this.disabled1 = true
  732. this.disabled2 = true
  733. this.disabled3 = true
  734. })
  735. },
  736. // 获取城市数据
  737. getCityLevel() {
  738. fetch('https://wl-1306604067.cos.ap-guangzhou.myqcloud.com/production/ct/1000/1720161853377/pc-code.json')
  739. .then(res => {
  740. return res.json()
  741. }).then(result => {
  742. this.transformedData = this.transformData(result)
  743. })
  744. },
  745. handleInfoCity(value) {
  746. this.insightData.province = value[0]
  747. this.insightData.city = value[1]
  748. },
  749. // 省市数据做转换
  750. transformData(data) {
  751. return data.map(item => {
  752. // 创建一个新对象,避免直接修改原始对象
  753. const newItem = { ...item };
  754. // 重命名 text 为 label
  755. if (newItem.text) {
  756. newItem.label = newItem.text;
  757. newItem.value = newItem.text;
  758. delete newItem.text;
  759. }
  760. // 如果存在 children 数组,则递归处理每个子项
  761. if (newItem.children && Array.isArray(newItem.children)) {
  762. newItem.children = this.transformData(newItem.children);
  763. }
  764. return newItem;
  765. })
  766. },
  767. // 截取url中的数据
  768. getQueryParam(paramName) {
  769. // 获取当前URL的查询字符串部分
  770. const queryString = window.location.search;
  771. // 创建一个URLSearchParams对象
  772. const urlParams = new URLSearchParams(queryString);
  773. // 返回指定参数的值,如果不存在则返回null
  774. return urlParams.get(paramName);
  775. },
  776. // 检测是否iOS端
  777. iosAgent() {
  778. return navigator.userAgent.match(/(iPhone|iPod|iPad);?/i);
  779. },
  780. // 复制文本函数,微信端,需要在用户触发 Click 事件里面才能执行成功
  781. handleCopy(message) {
  782. if (this.loading) {
  783. this.$message({
  784. message: '请等待思考完成后再复制',
  785. type: 'warning'
  786. })
  787. return
  788. }
  789. if (this.iosAgent()) {
  790. let inputObj = document.createElement("input");
  791. inputObj.value = message;
  792. document.body.appendChild(inputObj);
  793. inputObj.select();
  794. inputObj.setSelectionRange(0, inputObj.value.length);
  795. this.execCommand('Copy');
  796. document.body.removeChild(inputObj);
  797. } else {
  798. let domObj = document.createElement("span");
  799. domObj.innerHTML = message;
  800. document.body.appendChild(domObj);
  801. let selection = window.getSelection();
  802. let range = document.createRange();
  803. range.selectNodeContents(domObj);
  804. selection.removeAllRanges();
  805. selection.addRange(range);
  806. this.execCommand('Copy');
  807. document.body.removeChild(domObj);
  808. }
  809. },
  810. // 执行浏览器命令 Copy 顺便输出一下日志,如果在移动端推荐写个方法展示日志或者用alert(msg)也行。
  811. execCommand(action) {
  812. let is = document.execCommand(action);
  813. if (is) {
  814. this.$message({
  815. message: '复制成功',
  816. type: 'success'
  817. })
  818. } else {
  819. this.$message({
  820. message: '复制失败',
  821. type: 'error'
  822. })
  823. }
  824. },
  825. }
  826. })
  827. </script>
  828. </html>