userProfile.html 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111
  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>微分机器人</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. <link rel="stylesheet" href="css/page-return.css">
  13. <!-- 必须先引入vue, 后使用element-ui -->
  14. <script
  15. src="https://wl-1306604067.cos.ap-guangzhou.myqcloud.com/production/ct/103548289110001/1742017957144/vue.js"></script>
  16. <!-- 引入element 的组件库-->
  17. <script
  18. src="https://wl-1306604067.cos.ap-guangzhou.myqcloud.com/production/ct/103548289110001/1742017747738/element-ui.js"></script>
  19. <script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
  20. <script src="https://open.work.weixin.qq.com/wwopen/js/jwxwork-1.0.0.js"></script>
  21. <script src="js/page-return.js"></script>
  22. <script src="js/vconsole.min.js"></script>
  23. <script>
  24. var vConsole = new window.VConsole();
  25. </script>
  26. </head>
  27. <style>
  28. body {
  29. margin: 0;
  30. padding: 0;
  31. }
  32. .box {
  33. width: 100vw;
  34. height: auto;
  35. box-sizing: border-box;
  36. background: #FAFAFA;
  37. }
  38. .page6 {
  39. width: 100vw;
  40. box-sizing: border-box;
  41. padding-bottom: 39px;
  42. }
  43. .tab_list {
  44. width: 100%;
  45. height: 48px;
  46. background: #FFFFFF;
  47. }
  48. .client_detail {
  49. width: 100vw;
  50. min-height: 100vh;
  51. padding: 10px 10px 12px;
  52. box-sizing: border-box;
  53. }
  54. .client_msg {
  55. width: 100%;
  56. background: #FFFFFF;
  57. border-radius: 10px;
  58. padding: 15px 15px 20px;
  59. box-sizing: border-box;
  60. margin-bottom: 10px;
  61. }
  62. .client_wx {
  63. display: flex;
  64. align-items: center;
  65. margin-bottom: 15px;
  66. }
  67. .client_wx img {
  68. width: 64px;
  69. height: 64px;
  70. border-radius: 8px;
  71. margin-right: 12px;
  72. }
  73. .wx_msg {
  74. display: flex;
  75. flex-direction: column;
  76. font-weight: 500;
  77. font-size: 16px;
  78. color: #222222;
  79. }
  80. .wx_tag {
  81. color: #05D167;
  82. padding-left: 10px;
  83. }
  84. .add_time {
  85. font-weight: 400;
  86. font-size: 14px;
  87. color: #CCCCCC;
  88. padding-top: 10px;
  89. }
  90. .msg_title {
  91. font-weight: 400;
  92. font-size: 14px;
  93. color: #222222;
  94. line-height: 20px;
  95. position: relative;
  96. display: flex;
  97. align-items: center;
  98. padding-left: 5px;
  99. margin-bottom: 15px;
  100. &::before {
  101. content: '';
  102. position: absolute;
  103. width: 3px;
  104. height: 12px;
  105. left: 0;
  106. background: #1677FF;
  107. border-radius: 2px;
  108. }
  109. }
  110. .msg_title_line {
  111. padding-top: 15px;
  112. border-top: 1px dashed #E5E8ED;
  113. }
  114. .basic_msg {
  115. display: flex;
  116. flex-wrap: wrap;
  117. }
  118. .basic_msg_item {
  119. font-weight: 400;
  120. font-size: 14px;
  121. color: #222222;
  122. line-height: 20px;
  123. margin-bottom: 15px;
  124. display: flex;
  125. align-items: flex-start;
  126. justify-content: flex-start;
  127. word-break: break-all;
  128. }
  129. .basic_msg_item img {
  130. width: 16px;
  131. height: 16px;
  132. margin-left: 4px;
  133. margin-top: 2px;
  134. }
  135. .basic_msg_data {
  136. color: #999999;
  137. padding-right: 5px;
  138. flex-shrink: 0;
  139. }
  140. .three_tab_list {
  141. width: 100%;
  142. height: 58px;
  143. background: #FFFFFF;
  144. }
  145. .three_tab {
  146. width: 100%;
  147. background: #FFFFFF;
  148. border-radius: 10px;
  149. padding: 0 10px 10px;
  150. box-sizing: border-box;
  151. }
  152. .data_box {
  153. width: 100%;
  154. padding: 15px;
  155. background: #F7F9FC;
  156. border-radius: 5px;
  157. margin-bottom: 20px;
  158. box-sizing: border-box;
  159. }
  160. .belong_item {
  161. display: flex;
  162. align-items: center;
  163. margin-bottom: 15px;
  164. font-weight: 400;
  165. font-size: 14px;
  166. color: #222222;
  167. }
  168. .belong_item:last-child {
  169. margin-bottom: 0;
  170. }
  171. .belong_title {
  172. color: #999999;
  173. margin-right: 5px;
  174. flex-shrink: 0;
  175. }
  176. .client_followUp {
  177. width: 100vw;
  178. height: calc(100vh - 48px - 39px);
  179. padding: 20px 10px 60px;
  180. box-sizing: border-box;
  181. overflow-y: auto;
  182. }
  183. .stage {
  184. width: 100%;
  185. background: #F5F7FA;
  186. border-radius: 5px;
  187. padding: 15px;
  188. display: flex;
  189. align-items: center;
  190. font-weight: 400;
  191. font-size: 16px;
  192. color: #222222;
  193. box-sizing: border-box;
  194. margin-bottom: 20px;
  195. }
  196. .stage img {
  197. width: 24px;
  198. height: 24px;
  199. margin-right: 5px;
  200. }
  201. .stage_data {
  202. font-weight: 500;
  203. font-size: 16px;
  204. color: #1677FF;
  205. }
  206. .followup_list {
  207. width: 100%;
  208. padding: 15px 10px;
  209. background: #FFFFFF;
  210. border-radius: 10px;
  211. box-sizing: border-box;
  212. }
  213. .follow_time {
  214. display: flex;
  215. align-items: center;
  216. font-weight: 400;
  217. font-size: 14px;
  218. color: #222222;
  219. margin-bottom: 15px;
  220. }
  221. .time_dot {
  222. background: #E6F0FF;
  223. padding: 3px;
  224. border-radius: 50%;
  225. margin-right: 10px;
  226. box-sizing: border-box;
  227. }
  228. .dot {
  229. width: 6px;
  230. height: 6px;
  231. background: #1677FF;
  232. border-radius: 50%;
  233. }
  234. .follow_item {
  235. margin-left: 22px;
  236. padding-right: 10px;
  237. padding-left: 14px;
  238. padding-bottom: 6px;
  239. }
  240. .follow_padding {
  241. padding-top: 15px;
  242. padding-bottom: 4px;
  243. background: #F7F9FC;
  244. border-radius: 5px;
  245. }
  246. .follow_item_time {
  247. font-weight: 500;
  248. font-size: 14px;
  249. color: #222222;
  250. display: flex;
  251. align-items: center;
  252. margin-bottom: 3px;
  253. }
  254. .item_time {
  255. font-weight: 400;
  256. font-size: 12px;
  257. color: #999999;
  258. padding-right: 10px;
  259. }
  260. .item_box {
  261. display: flex;
  262. align-items: flex-end;
  263. }
  264. .follow_item_line {
  265. display: flex;
  266. flex-direction: column;
  267. align-items: center;
  268. padding-left: 13px;
  269. padding-right: 21px;
  270. }
  271. .line_dot {
  272. width: 6px;
  273. height: 6px;
  274. background: #1677FF;
  275. border-radius: 50%;
  276. margin-bottom: 5px;
  277. }
  278. .line {
  279. width: 1px;
  280. height: 66px;
  281. border-left: 1px dashed #1677FF;
  282. }
  283. .item_content {
  284. background: #FFFFFF;
  285. border-radius: 5px;
  286. padding: 10px;
  287. font-weight: 400;
  288. font-size: 12px;
  289. color: #222222;
  290. width: 100%;
  291. height: 100%;
  292. }
  293. .item_tip {
  294. font-weight: 400;
  295. font-size: 12px;
  296. color: #999999;
  297. margin-top: 6px;
  298. }
  299. .item_p {
  300. color: #1677FF;
  301. }
  302. .tag_list {
  303. display: flex;
  304. flex-wrap: wrap;
  305. }
  306. .tag_item {
  307. font-weight: 400;
  308. font-size: 12px;
  309. color: #1677FF;
  310. padding: 6px 10px;
  311. border-radius: 5px;
  312. border: 1px solid #1677FF;
  313. margin-right: 10px;
  314. margin-bottom: 15px;
  315. }
  316. .tag_item:last-child {
  317. margin-right: 0;
  318. }
  319. .read_content {
  320. display: flex;
  321. }
  322. .read_box {
  323. width: 100%;
  324. padding: 15px 15px 10px;
  325. font-weight: 400;
  326. font-size: 14px;
  327. color: #222222;
  328. background: #F7F9FC;
  329. border-radius: 5px;
  330. }
  331. .read_title {
  332. padding-bottom: 10px;
  333. }
  334. .read_num {
  335. font-weight: bold;
  336. font-size: 20px;
  337. color: #222222;
  338. line-height: 29px;
  339. }
  340. .foot_btn {
  341. position: fixed;
  342. bottom: 49px;
  343. padding: 0 35px;
  344. width: 100%;
  345. box-sizing: border-box;
  346. }
  347. .signUp_btn {
  348. height: 50px;
  349. width: 100%;
  350. background: #1677FF;
  351. border-radius: 10px;
  352. font-weight: bold;
  353. font-size: 14px;
  354. color: #FFFFFF;
  355. text-align: center;
  356. }
  357. .el-dialog {
  358. width: 335px;
  359. border-radius: 20px;
  360. }
  361. .el-dialog__header {
  362. text-align: center;
  363. }
  364. .el-dialog__title {
  365. font-weight: 500;
  366. font-size: 16px;
  367. color: #222222;
  368. }
  369. .el-dialog__body {
  370. height: 260px;
  371. overflow-y: auto;
  372. }
  373. .prize_form .el-form-item {
  374. padding: 0;
  375. }
  376. .prize_form .el-form-item__label {
  377. line-height: 20px;
  378. }
  379. .prize_form .el-input {
  380. width: 295px;
  381. }
  382. .prize_form .el-input__inner {
  383. text-align: left;
  384. }
  385. .prize_form .el-textarea.is-active .el-textarea__inner,
  386. .prize_form .el-textarea__inner:focus {
  387. border: 1px solid #1677FF;
  388. }
  389. .el-radio__input.is-checked .el-radio__inner {
  390. border-color: #1677FF;
  391. background: #1677FF;
  392. }
  393. .el-radio__input.is-checked+.el-radio__label {
  394. color: #1677FF;
  395. }
  396. .el-radio {
  397. margin-top: 10px;
  398. }
  399. .el-select .el-input.is-focus .el-input__inner {
  400. border-color: #1677FF;
  401. }
  402. .dialog-footer {
  403. text-align: center;
  404. }
  405. .dialog-footer button {
  406. width: 100%;
  407. height: 46px;
  408. background: #1677FF;
  409. border-radius: 20px;
  410. font-weight: 500;
  411. font-size: 16px;
  412. color: #FFFFFF;
  413. border: none;
  414. }
  415. </style>
  416. <body>
  417. <div id="box" class="box">
  418. <div class="page6">
  419. <el-tabs class="tab_list" v-model="activeName" stretch @tab-click="handleClick">
  420. <el-tab-pane label="客户画像" name="first"></el-tab-pane>
  421. <el-tab-pane label="客户跟进" name="second"></el-tab-pane>
  422. </el-tabs>
  423. <div class="client_detail" v-if="activeName ==='first'">
  424. <div class="client_msg">
  425. <div class="client_wx">
  426. <img :src="clientData.avatar" />
  427. <div class="wx_msg">
  428. <div>{{clientData.name}}<span class="wx_tag">@微信</span></div>
  429. <div class="add_time">添加时间:{{timeFormat(clientData.addTime, 'yyyy-MM-dd hh:mm:ss')}}</div>
  430. </div>
  431. </div>
  432. <div class="msg_title msg_title_line">基本信息</div>
  433. <div class="basic_msg" style="width: 100%;">
  434. <div class="basic_msg_item" style="width: 50%;">
  435. <span class="basic_msg_data">姓名:</span>
  436. <span>{{clientData.realName}}</span>
  437. </div>
  438. <div class="basic_msg_item" style="width: 50%;">
  439. <span class="basic_msg_data">手机:</span>
  440. <span>{{clientData.phone}}</span>
  441. <img src="./img/edit.png" alt="" @click="handleEdit('手机', clientData.phone, 'phone')">
  442. </div>
  443. <div class="basic_msg_item" style="width: 50%;">
  444. <span class="basic_msg_data">省市:</span>
  445. <span>{{clientData.province}}-{{clientData.city}}</span>
  446. </div>
  447. <div class="basic_msg_item" style="width: 50%;">
  448. <span class="basic_msg_data">意向车型:</span>
  449. <span>{{clientData.intenModel}}</span>
  450. </div>
  451. <div class="basic_msg_item" style="width: 50%;">
  452. <span class="basic_msg_data">已购车型:</span>
  453. <span>{{clientData.buyModel}}</span>
  454. </div>
  455. <div class="basic_msg_item" style="width: 50%;">
  456. <span class="basic_msg_data">vin码:</span>
  457. <span>{{clientData.vin}}</span>
  458. <img src="./img/edit.png" alt="" @click="handleEditOther('vin')">
  459. </div>
  460. <div class="basic_msg_item" style="width: 50%;" v-for="(item, index) in clientData.cols">
  461. <span class="basic_msg_data">{{item.name}}:</span>
  462. <span v-if="item.value">{{item.value[0]}}</span>
  463. <img src="./img/edit.png" alt="" @click="handleEditOther('cols')">
  464. </div>
  465. </div>
  466. <!-- <div class="msg_title msg_title_line">服务信息</div>
  467. <div class="basic_msg">
  468. <div class="basic_msg_item">
  469. <span class="basic_msg_data">上次到店时间:</span>
  470. <span>2024/03/10 12:30:53</span>
  471. </div>
  472. <div class="basic_msg_item">
  473. <span class="basic_msg_data">到店业务类型:</span>
  474. <span>维修</span>
  475. </div>
  476. <div class="basic_msg_item">
  477. <span class="basic_msg_data">备注:</span>
  478. <span>水电费收到水电费收到水电费收到水电费收到水电费收到</span>
  479. </div>
  480. </div> -->
  481. </div>
  482. <div class="three_tab">
  483. <el-tabs class="three_tab_list" v-model="itemActiveName" stretch @tab-click="handleDetailTab">
  484. <el-tab-pane label="画像" name="first"></el-tab-pane>
  485. <el-tab-pane label="记录" name="second"></el-tab-pane>
  486. <!-- <el-tab-pane label="车辆" name="third"></el-tab-pane> -->
  487. </el-tabs>
  488. <div v-if="itemActiveName === 'first'">
  489. <div class="data_box">
  490. <div class="belong_item">
  491. <div class="belong_title">所属成员:</div>
  492. <template v-if="clientData.memberList">
  493. <div v-if="clientData.memberList.length > 1">
  494. {{ clientData.memberList[0] }}等<span style="color: #1677FF;">{{ clientData.memberList.length }}</span>人
  495. </div>
  496. <div v-else-if="clientData.memberList.length === 1">
  497. {{ clientData.memberList[0] }}
  498. </div>
  499. </template>
  500. </div>
  501. <div class="belong_item">
  502. <div class="belong_title">所在群聊:</div>
  503. <template v-if="clientData.groupChatList">
  504. <div v-if="clientData.groupChatList.length > 1">
  505. 「{{ clientData.groupChatList[0] }}」等<span style="color: #1677FF;">{{ clientData.groupChatList.length }}</span>个群聊
  506. </div>
  507. <div v-else-if="clientData.groupChatList.length === 1">
  508. 「{{ clientData.groupChatList[0] }}」
  509. </div>
  510. </template>
  511. </div>
  512. </div>
  513. <div class="msg_title">客户阶段</div>
  514. <div class="data_box">
  515. <div class="belong_item">
  516. <div class="belong_title">当前阶段:</div>
  517. <div style="color: #1677FF;" v-if="clientData.stepList && clientData.stepList.length">
  518. <span v-for="(item, i) in clientData.stepList" :key="i">「{{ item }}」</span>
  519. </div>
  520. </div>
  521. </div>
  522. <div class="msg_title">客户标签</div>
  523. <div class="data_box" style="padding-bottom: 0;">
  524. <div class="tag_list">
  525. <span class="tag_item" v-for="(item, i) in clientData.tagList" :key="i">{{ item }}</span>
  526. </div>
  527. </div>
  528. </div>
  529. <div v-if="itemActiveName === 'second'">
  530. <!-- <div class="read_content">
  531. <div class="read_box" style="margin-right: 10px;">
  532. <div class="read_title">阅读次数</div>
  533. <div><span class="read_num">7282</span> 次</div>
  534. </div>
  535. <div class="read_box">
  536. <div class="read_title">平均阅读时长</div>
  537. <div><span class="read_num">7282</span> min</div>
  538. </div>
  539. </div> -->
  540. <div class="followup_list" v-for="(item, key) in recordList" :key="key">
  541. <div class="follow_time">
  542. <div class="time_dot">
  543. <div class="dot"></div>
  544. </div>
  545. <span>{{key}}</span>
  546. </div>
  547. <div class="follow_padding">
  548. <div class="follow_item" v-for="(dt, dtIndex) in item" :key="dtIndex">
  549. <div class="follow_item_time">
  550. <span class="item_time">{{timeFormat(dt.createdTime, 'hh:mm')}}</span>
  551. <!-- <span v-if="dt.type === 1">阅读日志</span>
  552. <span v-else-if="dt.type === 2">行为日志</span>
  553. <span v-else-if="dt.type === 3">修改日志</span> -->
  554. </div>
  555. <div class="item_box">
  556. <div class="follow_item_line">
  557. <div class="line_dot"></div>
  558. <div class="line"></div>
  559. </div>
  560. <div class="item_content">
  561. <div>{{dt.title}}</div>
  562. <div class="item_tip">{{dt.content}}</div>
  563. <div class="item_tip" v-if="dt.type === 1">持续时间:<span class="item_p">{{dt.duration}}</span>秒</div>
  564. </div>
  565. </div>
  566. </div>
  567. </div>
  568. </div>
  569. </div>
  570. </div>
  571. </div>
  572. <div class="client_followUp" v-if="activeName === 'second'">
  573. <div class="stage">
  574. <img src="./img/stage.png" />
  575. <div>当前阶段:<span class="stage_data" v-for="(item, key) in stepData" :key="key">「{{item}}」</span></div>
  576. </div>
  577. <div class="followup_list" v-for="(item, key) in stepFollowList" :key="key">
  578. <div class="follow_time">
  579. <div class="time_dot">
  580. <div class="dot"></div>
  581. </div>
  582. <span>{{item.date}}</span>
  583. </div>
  584. <div class="follow_padding">
  585. <div class="follow_item" v-for="(c, cIndex) in item.content" :key="cIndex">
  586. <div class="follow_item_time">
  587. <span class="item_time">{{timeFormat(c.updatedTime, 'hh:mm')}}</span>跟进方式:
  588. <span v-if="c.followType === 0">电话联系</span>
  589. <span v-else-if="c.followType === 1">面聊</span>
  590. <span v-else-if="c.followType === 2">微信联系</span>
  591. <span v-else-if="c.followType === 3">线上会议</span>
  592. <span v-else-if="c.followType === 4">其他</span>
  593. </div>
  594. <div class="item_box">
  595. <div class="follow_item_line">
  596. <div class="line_dot"></div>
  597. <div class="line"></div>
  598. </div>
  599. <div class="item_content">
  600. <div>{{c.remark}}</div>
  601. <div class="item_tip">跟进成员:由<span class="item_p">「{{c.memberName}}」</span>跟进</div>
  602. </div>
  603. </div>
  604. </div>
  605. </div>
  606. </div>
  607. <div class="foot_btn">
  608. <el-button class="signUp_btn" :loading="loading" @click="toSignUp">去跟进</el-button>
  609. </div>
  610. </div>
  611. </div>
  612. <!-- 底部返回栏 -->
  613. <page-return></page-return>
  614. <el-dialog title="添加客户跟进" :visible.sync="showDialog">
  615. <el-form :model="form" class="prize_form">
  616. <el-form-item label="跟进方式:" required>
  617. <el-radio-group v-model="form.followType">
  618. <el-radio :label="0">电话联系</el-radio>
  619. <el-radio :label="1">面聊</el-radio>
  620. <el-radio :label="2">微信联系</el-radio>
  621. <el-radio :label="3">线上会议</el-radio>
  622. <el-radio :label="4">其他跟进</el-radio>
  623. </el-radio-group>
  624. </el-form-item>
  625. <el-form-item label="跟进内容:" required>
  626. <el-input v-model="form.remark" type="textarea" autosize></el-input>
  627. </el-form-item>
  628. </el-form>
  629. <div slot="footer" class="dialog-footer">
  630. <el-button type="primary" @click="handleSave">确定</el-button>
  631. </div>
  632. </el-dialog>
  633. <el-dialog title="编辑信息" :visible.sync="showEditDialog">
  634. <el-form class="prize_form" @submit.native.prevent>
  635. <el-form-item :label="item.name" required v-for="(item, index) in editFormList" :key="index">
  636. <el-input v-model="item.value[0]"></el-input>
  637. </el-form-item>
  638. </el-form>
  639. <div slot="footer" class="dialog-footer">
  640. <el-button type="primary" @click="handleEditSave">确定</el-button>
  641. </div>
  642. </el-dialog>
  643. </div>
  644. </body>
  645. <script>
  646. new Vue({
  647. el: '#box',
  648. data() {
  649. return {
  650. httpUrl: '',
  651. bId: null,
  652. env: '',
  653. memberId: null,
  654. externalUserId: null,
  655. activeName: 'first',
  656. itemActiveName: 'first',
  657. clientData: {},
  658. recordList: {},
  659. stepData: [], // 阶段
  660. stepFollowList: [], // 跟进数据
  661. showDialog: false,
  662. form: {
  663. followType: 0,
  664. remark: ''
  665. },
  666. loading: false,
  667. showEditDialog: false,
  668. editFormList: [],
  669. editType: '',
  670. }
  671. },
  672. created() {
  673. this.bId = this.getQueryParam('bId')
  674. this.env = this.getQueryParam('env')
  675. if (!this.env || this.env === 'prod') {
  676. this.httpUrl = 'https://wlapi.wefanbot.com'
  677. } else {
  678. this.httpUrl = 'http://test.wefanbot.com:18993'
  679. }
  680. if (this.getQueryParam('memberId')) {
  681. // 已授权
  682. this.memberId = this.getQueryParam('memberId')
  683. if (this.getQueryParam('externalUserId')) {
  684. this.externalUserId = this.getQueryParam('externalUserId')
  685. this.getClientExt()
  686. } else {
  687. this.getQyWxSign()
  688. }
  689. } else {
  690. // 授权
  691. this.getAuth()
  692. }
  693. // this.memberId = "woU17nDAAAnoSca19vZVKiNEKdc9tyYQ"
  694. // this.externalUserId = "wmU17nDAAAqZkemA_hZuM5JIIYoA_GIA"
  695. // this.getClientExt()
  696. },
  697. methods: {
  698. getAuth() {
  699. fetch(this.httpUrl + `/p/insuite/p/getRedirectUri?env=${this.env}&bId=${this.bId}`)
  700. .then(res => {
  701. return res.json()
  702. }).then(result => {
  703. let { data, code, msg } = result
  704. if (code === 1) {
  705. window.location.replace(data)
  706. } else {
  707. this.$message({
  708. message: msg,
  709. type: 'warning'
  710. })
  711. }
  712. })
  713. },
  714. getQyWxSign() {
  715. fetch(this.httpUrl + '/scrm/v1/wxcp-corp/p/getAgentConfig', {
  716. method: 'post',
  717. body: JSON.stringify({
  718. bid: this.bId,
  719. url: window.location.href,
  720. }),
  721. headers: {
  722. 'Content-Type': 'application/json'
  723. }
  724. }).then(res => {
  725. return res.json()
  726. }).then(result => {
  727. let { data, code, msg } = result
  728. if (code === 1) {
  729. let that = this
  730. wx.agentConfig({
  731. corpid: data.corpid,
  732. agentid: data.agentId,
  733. timestamp: data.timestamp, // 必填,生成签名的时间戳
  734. nonceStr: data.nonceStr, // 必填,生成签名的随机串
  735. signature: data.agentSignature, // 必填,签名,见附录1
  736. jsApiList: ['getCurExternalContact'], // 必填,需要使用的JS接口列表
  737. success: function (res) {
  738. // 回调
  739. // 此处直接在注入回调中调用了获取当前外部联系人userId的接口,注意此接口是从聊天框的工具栏进入才能获取
  740. wx.invoke('getCurExternalContact', {
  741. }, function (res) {
  742. if (res.err_msg == "getCurExternalContact:ok") {
  743. console.log('invoke成功:', res.userId);
  744. that.externalUserId = res.userId
  745. that.getClientExt()
  746. } else {
  747. //错误处理
  748. console.log('invoke失败:', res)
  749. }
  750. });
  751. },
  752. fail: function (res) {
  753. if (res.errMsg.indexOf('function not exist') > -1) {
  754. alert('版本过低请升级');
  755. }
  756. },
  757. complete: function (res) {
  758. // 回调
  759. console.log('complete1', res);
  760. }
  761. });
  762. } else {
  763. this.$message({
  764. message: msg,
  765. type: 'warning'
  766. })
  767. }
  768. })
  769. },
  770. handleClick(tab) {
  771. this.activeName = tab.name
  772. if (tab.name ==='first') {
  773. this.getClientExt()
  774. } else if (tab.name === 'second' && this.externalUserId) {
  775. this.getClientFollow()
  776. }
  777. },
  778. getClientFollow() {
  779. // 获取阶段
  780. this.getStep()
  781. // 获取阶段跟进数据
  782. this.getStepFollow()
  783. },
  784. // 获取客户画像
  785. getClientExt() {
  786. fetch(this.httpUrl + `/scrm/v1/wxcp-client-pro/p/clientExt?bId=${this.bId}&externalUserId=${this.externalUserId}&memberId=${this.memberId}`)
  787. .then(res => {
  788. return res.json()
  789. }).then(result => {
  790. let { data, code, msg } = result
  791. if (code === 1) {
  792. this.clientData = data
  793. if (this.clientData.cols && this.clientData.cols.length) {
  794. this.clientData.cols.forEach(item => {
  795. if (!item.value) {
  796. item.value = []
  797. }
  798. })
  799. }
  800. } else {
  801. this.$message({
  802. message: msg,
  803. type: 'warning'
  804. })
  805. }
  806. })
  807. },
  808. handleDetailTab(tab) {
  809. this.itemActiveName = tab.name
  810. if (tab.name === 'second') {
  811. this.getClientLog()
  812. }
  813. },
  814. // 客户画像-记录
  815. getClientLog() {
  816. fetch(this.httpUrl + '/scrm/v1/wxcp-client-pro/p/clientLog', {
  817. method: 'post',
  818. body: JSON.stringify({
  819. bid: this.bId,
  820. externalUserId: this.externalUserId,
  821. memberId: this.memberId,
  822. page: 1,
  823. pageCount: 1000,
  824. }),
  825. headers: {
  826. 'Content-Type': 'application/json'
  827. }
  828. }).then(res => {
  829. return res.json()
  830. }).then(result => {
  831. let { data, code, msg } = result
  832. if (code === 1) {
  833. this.recordList = data
  834. } else {
  835. this.$message({
  836. message: msg,
  837. type: 'warning'
  838. })
  839. }
  840. })
  841. },
  842. // 获取阶段
  843. getStep() {
  844. fetch(this.httpUrl + `/scrm/v1/wxcp-client-pro/p/clientStepList?bId=${this.bId}&externalUserId=${this.externalUserId}`)
  845. .then(res => {
  846. return res.json()
  847. }).then(result => {
  848. let { data, code, msg } = result
  849. if (code === 1) {
  850. this.stepData = data
  851. } else {
  852. this.$message({
  853. message: msg,
  854. type: 'warning'
  855. })
  856. }
  857. })
  858. },
  859. // 获取阶段跟进数据
  860. getStepFollow() {
  861. fetch(this.httpUrl + '/scrm/v1/wxcp-client-pro/p/clientFollow', {
  862. method: 'post',
  863. body: JSON.stringify({
  864. bid: this.bId,
  865. externalUserId: this.externalUserId,
  866. memberId: this.memberId,
  867. page: 1,
  868. pageCount: 1000,
  869. }),
  870. headers: {
  871. 'Content-Type': 'application/json'
  872. }
  873. }).then(res => {
  874. return res.json()
  875. }).then(result => {
  876. let { data, code, msg } = result
  877. if (code === 1) {
  878. let arr = this.groupDataByDate(data.records)
  879. this.stepFollowList = arr.reverse()
  880. } else {
  881. this.$message({
  882. message: msg,
  883. type: 'warning'
  884. })
  885. }
  886. })
  887. },
  888. // 按日期分组数据
  889. groupDataByDate(data) {
  890. const grouped = {}
  891. // 按日期分组
  892. data.forEach(item => {
  893. const dateStr = item.updatedTime.slice(0, 10); // 提取年月日
  894. if (!grouped[dateStr]) {
  895. grouped[dateStr] = []
  896. }
  897. grouped[dateStr].push(item)
  898. })
  899. // 转换为指定格式并排序
  900. return Object.keys(grouped)
  901. .sort() // 按日期升序排列
  902. .map(date => ({
  903. date,
  904. content: grouped[date]
  905. }))
  906. },
  907. toSignUp() {
  908. this.showDialog = true
  909. this.form = {
  910. followType: 0,
  911. remark: ''
  912. }
  913. },
  914. // 添加客户跟进
  915. handleSave() {
  916. fetch(this.httpUrl + '/scrm/v1/wxcp-client-pro/p/addFollow', {
  917. method: 'post',
  918. body: JSON.stringify({
  919. bid: this.bId,
  920. externalUserId: this.externalUserId,
  921. memberId: this.memberId,
  922. followType: this.form.followType,
  923. remark: this.form.remark,
  924. }),
  925. headers: {
  926. 'Content-Type': 'application/json'
  927. }
  928. }).then(res => {
  929. return res.json()
  930. }).then(result => {
  931. let { data, code, msg } = result
  932. if (code === 1) {
  933. this.showDialog = false
  934. this.getClientFollow()
  935. this.$message({
  936. message: '添加成功',
  937. type: 'success'
  938. })
  939. } else {
  940. this.$message({
  941. message: msg,
  942. type: 'warning'
  943. })
  944. }
  945. })
  946. },
  947. // 编辑手机号
  948. handleEdit(title, value, type) {
  949. this.editFormList = [{
  950. name: title,
  951. type: 0,
  952. value: [value]
  953. }]
  954. this.editType = type
  955. this.showEditDialog = true
  956. },
  957. // 编辑其他字段
  958. handleEditOther(type) {
  959. if (type === 'vin') {
  960. this.editFormList = [{
  961. name: 'vin码',
  962. type: 0,
  963. value: [this.clientData.vin]
  964. }]
  965. } else if (type === 'cols') {
  966. this.editFormList = this.clientData.cols
  967. }
  968. this.editType = 'other'
  969. this.showEditDialog = true
  970. },
  971. handleEditSave() {
  972. if (this.editType === 'phone') {
  973. this.editPhone()
  974. } else if (this.editType === 'other') {
  975. this.editOthers()
  976. }
  977. },
  978. // 修改手机号
  979. editPhone() {
  980. fetch(this.httpUrl + `/scrm/v1/wxcp-client-pro/p/updatePhone?bId=${this.bId}&externalUserId=${this.externalUserId}&memberId=${this.memberId}&phone=${this.editFormList[0].value[0]}`)
  981. .then(res => {
  982. return res.json()
  983. }).then(result => {
  984. let { data, code, msg } = result
  985. if (code === 1) {
  986. this.showEditDialog = false
  987. this.getClientExt()
  988. this.$message({
  989. message: '编辑成功',
  990. type: 'success'
  991. })
  992. } else {
  993. this.$message({
  994. message: msg,
  995. type: 'warning'
  996. })
  997. }
  998. })
  999. },
  1000. // 编辑其他字段
  1001. editOthers() {
  1002. fetch(this.httpUrl + '/scrm/v1/wxcp-client-pro/p/updateExt', {
  1003. method: 'post',
  1004. body: JSON.stringify({
  1005. bid: this.bId,
  1006. externalUserId: this.externalUserId,
  1007. memberId: this.memberId,
  1008. cols: this.clientData.cols,
  1009. vin: this.editFormList[0].value[0],
  1010. }),
  1011. headers: {
  1012. 'Content-Type': 'application/json'
  1013. }
  1014. }).then(res => {
  1015. return res.json()
  1016. }).then(result => {
  1017. let { data, code, msg } = result
  1018. if (code === 1) {
  1019. this.showEditDialog = false
  1020. this.getClientExt()
  1021. this.$message({
  1022. message: '添加成功',
  1023. type: 'success'
  1024. })
  1025. } else {
  1026. this.$message({
  1027. message: msg,
  1028. type: 'warning'
  1029. })
  1030. }
  1031. })
  1032. },
  1033. // 截取url中的数据
  1034. getQueryParam(paramName) {
  1035. // 获取当前URL的查询字符串部分
  1036. const queryString = window.location.search;
  1037. // 创建一个URLSearchParams对象
  1038. const urlParams = new URLSearchParams(queryString);
  1039. // 返回指定参数的值,如果不存在则返回null
  1040. return urlParams.get(paramName);
  1041. },
  1042. timeFormat(time, format = 'yyyy-MM-dd hh:mm:ss') {
  1043. if (time === undefined || time === '' || time === null) {
  1044. return '/';
  1045. }
  1046. const date = new Date(time);
  1047. const o = {
  1048. 'M+': date.getMonth() + 1, // 月份
  1049. 'd+': date.getDate(), // 日
  1050. 'h+': date.getHours(), // 小时
  1051. 'm+': date.getMinutes(), // 分钟
  1052. 's+': date.getSeconds(), // 秒
  1053. 'q+': Math.floor((date.getMonth() + 3) / 3), // 季度
  1054. 'S': date.getMilliseconds() // 毫秒
  1055. };
  1056. // 处理年份
  1057. if (/(y+)/.test(format)) {
  1058. format = format.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length));
  1059. }
  1060. // 处理日期和时间部分
  1061. for (let k in o) {
  1062. if (new RegExp('(' + k + ')').test(format)) {
  1063. let value = o[k];
  1064. let padding = RegExp.$1.length === 1 ? '' : '00'; // 根据格式字符串中的长度决定是否补零
  1065. format = format.replace(RegExp.$1, ('' + value).padStart(padding.length + value.toString().length - value.toString().length, '0'));
  1066. }
  1067. }
  1068. // 如果格式只包含时间部分,移除日期部分可能的占位符
  1069. if (!/(M+|d+)/.test(format)) {
  1070. // 移除任何可能存在的日期占位符(如:'yyyy-MM-dd ')
  1071. format = format.replace(/(\s*-\s*){2}/g, ''); // 移除两个'-'之间的任何内容
  1072. format = format.replace(/^\s*yyyy-\s*/, ''); // 移除开头的'yyyy-'
  1073. }
  1074. // 如果格式只包含日期部分,移除时间部分可能的占位符
  1075. if (!/(h+|m+|s+)/.test(format)) {
  1076. // 移除任何可能存在的时间占位符(如:' hh:mm:ss')
  1077. format = format.replace(/(\s*:\s*){2}/g, ''); // 移除两个':'之间的任何内容
  1078. format = format.replace(/\s*hh:\s*$/, ''); // 移除结尾的' hh:'
  1079. }
  1080. return format;
  1081. }
  1082. }
  1083. })
  1084. </script>
  1085. </html>