detail.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641
  1. <template>
  2. <view class="clueDetail_wrap">
  3. <view class="telPhone">
  4. <view class="left">
  5. <view class="phone">电话:
  6. <show-real-text :real="receiptDetail.phone" :type="params.type"
  7. v-if="receiptDetail.phone"></show-real-text>
  8. </view>
  9. <view class="copy_btn" v-if="params.type != '1'" @click="handleCopy(receiptDetail)">复制</view>
  10. </view>
  11. <view class="right"> 发单人 : {{ receiptDetail.createNickName }} </view>
  12. </view>
  13. <view class="clueDetail_top_info">
  14. <view class="top_info_item">
  15. <view class="top">
  16. {{ defaultText(receiptDetail.clueOwnerName) }}
  17. </view>
  18. <view class="bottom"> 线索所属人 </view>
  19. </view>
  20. <view class="top_info_item">
  21. <view class="top">
  22. {{ defaultText(receiptDetail.brand) }}
  23. </view>
  24. <view class="bottom"> 品牌 </view>
  25. </view>
  26. <view class="top_info_item">
  27. <view class="top">
  28. {{ defaultText(receiptDetail.identificationName) }}
  29. </view>
  30. <view class="bottom"> 接单人 </view>
  31. </view>
  32. <view class="top_info_item">
  33. <view class="top">
  34. {{ crmFormCategoryFormat(receiptDetail.category) }}
  35. </view>
  36. <view class="bottom"> 类别 </view>
  37. </view>
  38. <view class="top_info_item">
  39. <view class="top">
  40. {{ crmFormTacticFormat(receiptDetail.tactic) }}
  41. </view>
  42. <view class="bottom"> 采用战术 </view>
  43. </view>
  44. <view class="top_info_item status-item">
  45. <view class="top">
  46. {{ crmFollowStatusFormat(receiptDetail.status) }}
  47. </view>
  48. <view class="bottom"> 跟进状态 </view>
  49. </view>
  50. </view>
  51. <view class="clue_state_wrap">
  52. <view class="top_left">阶段:</view>
  53. <view class="steps_wrap">
  54. <ld-select v-model="receiptDetail.state" :list="crmFormStateDict" value-key="dictValue"
  55. label-key="dictLabel" placeholder="点击选择阶段" @change="handleStateConfirm">
  56. </ld-select>
  57. </view>
  58. </view>
  59. <!-- 跟进状态 -->
  60. <view class="order_action_wrap">
  61. <view class="action_title">状态:</view>
  62. <view class="action_buttons" v-if="['1', '2'].includes(receiptDetail.status)">
  63. <u-button v-if="receiptDetail.status === '1'" type="success" size="small" text="接单"
  64. @click="handleOrderForm" customStyle="margin-right: 20rpx;"></u-button>
  65. <u-button v-if="receiptDetail.status === '2'" type="primary" size="small" text="收单"
  66. @click="handleReceiptForm" customStyle="margin-right: 20rpx;"></u-button>
  67. <u-button v-if="receiptDetail.status === '2'" type="warning" size="small" text="未收"
  68. @click="handleDenialForm" customStyle="margin-right: 20rpx;"></u-button>
  69. <u-button v-if="receiptDetail.status === '1'" type="error" size="small" text="撤销" @click="handleDelete"
  70. customStyle="margin-right: 20rpx;"></u-button>
  71. </view>
  72. <view v-else class="last_status">
  73. 该订单{{ crmFollowStatusFormat(receiptDetail.status) }}不可再操作
  74. </view>
  75. </view>
  76. <view class="clue_tag_wrap">
  77. <view class="clue_tag_add_btn" @click="handleAddClueTag">
  78. + 添加标签
  79. </view>
  80. <u-tag :text="tag.name" plain plainFill :closable="true" @close="hanldeTagClose(tag)" borderColor="#fff"
  81. v-for="tag in receiptDetail.tags" :key="tag.id" style="margin-left: 10px; margin-bottom: 10px"
  82. :bgColor="tag.color" color="#fff"></u-tag>
  83. </view>
  84. <yui-tabs :tabs="tabs" v-model="activeIndex" :lineWidth="'120rpx'" :isLazyRender="false" color="#108cff"
  85. titleActiveColor="#108cff" :swipeable="true" :swiper="false" :ellipsis="false" :scroll-threshold="3">
  86. <template #chatFile>
  87. <upload-file :clueId="clueId" :sourceId="orderId" ref="uploadFile1" type="2" orderFileType="1"
  88. isDuplicate="1"></upload-file>
  89. </template>
  90. <template #quoteFile>
  91. <upload-file :clueId="clueId" :sourceId="orderId" ref="uploadFile2" type="2" orderFileType="2"
  92. isDuplicate="1"></upload-file>
  93. </template>
  94. <template #hdImageFile>
  95. <upload-file :clueId="clueId" :sourceId="orderId" ref="uploadFile3" type="2" orderFileType="3"
  96. isDuplicate="1"></upload-file>
  97. </template>
  98. <template #otherFile>
  99. <upload-file :clueId="clueId" :sourceId="orderId" ref="uploadFile4" type="2" orderFileType="4"
  100. isDuplicate="1"></upload-file>
  101. </template>
  102. <template #frontendFile>
  103. <upload-file :clueId="clueId" ref="uploadFile5" type="6" isDuplicate="1"></upload-file>
  104. </template>
  105. <template #frontendFollow>
  106. <clue-follow :clueId="clueId" ref="clueFollow" type="4"></clue-follow>
  107. </template>
  108. <template #followRecord>
  109. <clue-follow :clueId="clueId" ref="follow" type="5"></clue-follow>
  110. </template>
  111. <template #receiptInfo>
  112. <receipt-form-list :sendFormId="orderId" :clueId="clueId" :receiptDetail="receiptDetail"
  113. ref="receiptFormList"></receipt-form-list>
  114. </template>
  115. <template #commissionInfo>
  116. <commission-form-list :sendFormId="orderId" :clueId="clueId"
  117. ref="commissionFormList"></commission-form-list>
  118. </template>
  119. <template #sendInfo>
  120. <sendInfo :sendFormId="orderId" :clueId="clueId" :receiptDetail="receiptDetail" ref="sendInfo">
  121. </sendInfo>
  122. </template>
  123. </yui-tabs>
  124. <u-tabbar class="clueDetail_tabber" :fixed="true" inactiveColor="#ffffff" :placeholder="true"
  125. :safeAreaInsetBottom="true">
  126. <u-tabbar-item text="拨打电话" icon="../../static/clueDetail/icon-phone.png"
  127. @click="handleCallPhone"></u-tabbar-item>
  128. <u-tabbar-item text="添加收单" icon="../../static/orderDetail/tjsd.png"
  129. @click="handleAddReceiptForm"></u-tabbar-item>
  130. <u-tabbar-item text="新增分成" icon="../../static/orderDetail/xzfc.png"
  131. @click="handleAddCommission"></u-tabbar-item>
  132. <u-tabbar-item text="添加跟进" icon="../../static/caseDetail/icon-follow.png"
  133. @click="handleAddFollow"></u-tabbar-item>
  134. </u-tabbar>
  135. <group-select class="clueTagsSelect" :list="clueTagGroupVoList" scrollHeight="720rpx" groupName="groupName"
  136. groupChild="clueTagDataList" label-key="name" value-key="id" placeholder="请选择线索标签" v-model="checkTags"
  137. multiple clearable ref="clueTag" @confirm="handleClueTagConfirm"></group-select>
  138. </view>
  139. </template>
  140. <script>
  141. import {
  142. cloneDeep
  143. } from "lodash";
  144. import {
  145. selectDictLabel
  146. } from "@/utils/util";
  147. import uploadFile from "../tabs/uploadFile/index.vue";
  148. import clueFollow from "../tabs/followRecord/index.vue";
  149. import receiptFormList from "../tabs/receiptFormList/receiptFormList.vue";
  150. import commissionFormList from "../tabs/commissionFormList/commissionFormList.vue";
  151. import sendInfo from "../tabs/sendInfo/index.vue";
  152. export default {
  153. components: {
  154. uploadFile,
  155. clueFollow,
  156. receiptFormList,
  157. commissionFormList,
  158. sendInfo
  159. },
  160. props: {
  161. orderId: {
  162. type: [String, Number],
  163. required: true,
  164. },
  165. clueId: {
  166. type: [String, Number],
  167. required: true,
  168. },
  169. params: {
  170. type: Object,
  171. required: true,
  172. },
  173. },
  174. data() {
  175. return {
  176. showModal: false,
  177. showStateSelect: false,
  178. receiptDetail: {},
  179. checkTags: [],
  180. clueTagGroupVoList: [],
  181. crmFormCategoryDict: [],
  182. crmFormTacticDict: [],
  183. crmFormStateDict: [],
  184. crmHandelStatusDict: [],
  185. tabs: [{
  186. label: "发单信息",
  187. slot: "sendInfo",
  188. },
  189. {
  190. label: "聊天附件",
  191. slot: "chatFile",
  192. },
  193. {
  194. label: "报价附件",
  195. slot: "quoteFile",
  196. },
  197. {
  198. label: "高清图附件",
  199. slot: "hdImageFile",
  200. },
  201. {
  202. label: "其他附件",
  203. slot: "otherFile",
  204. },
  205. {
  206. label: "前端附件",
  207. slot: "frontendFile",
  208. },
  209. {
  210. label: "前端跟进",
  211. slot: "frontendFollow",
  212. },
  213. {
  214. label: "跟进记录",
  215. slot: "followRecord",
  216. },
  217. {
  218. label: "收单信息",
  219. slot: "receiptInfo",
  220. },
  221. {
  222. label: "收单分成",
  223. slot: "commissionInfo",
  224. },
  225. ],
  226. activeIndex: 0,
  227. };
  228. },
  229. methods: {
  230. async hanldeTagClose(tag) {
  231. const {
  232. id,
  233. tags
  234. } = this.receiptDetail;
  235. const copyTags = cloneDeep(tags);
  236. if (id == null) {
  237. uni.$u.toast("修改异常");
  238. return;
  239. }
  240. const index = copyTags.findIndex((v) => v.id === tag.id);
  241. if (index !== -1) {
  242. copyTags.splice(index, 1);
  243. const allTags = copyTags.map((v) => v.id).join(",");
  244. await uni.$u.api.updateTags({
  245. id: id,
  246. allTags,
  247. });
  248. this.receiptDetail.tags = copyTags;
  249. this.checkTags = this.receiptDetail.tags.map((v) => v.id);
  250. }
  251. },
  252. async handleClueTagConfirm() {
  253. const allTags = this.checkTags.join(",");
  254. await uni.$u.api.updateTags({
  255. id: this.receiptDetail.id,
  256. allTags,
  257. });
  258. this.getDetail();
  259. },
  260. handleAddClueTag() {
  261. this.$refs.clueTag.showModal();
  262. },
  263. async handleStateConfirm(e) {
  264. const state = e.dictValue;
  265. await uni.$u.api.updateOrderState({
  266. id: this.receiptDetail.id,
  267. state,
  268. });
  269. uni.$u.toast("操作成功");
  270. },
  271. defaultText(text) {
  272. return text ? text : "-";
  273. },
  274. crmFormCategoryFormat(v) {
  275. return v ? selectDictLabel(this.crmFormCategoryDict, v) : "-";
  276. },
  277. crmFormTacticFormat(v) {
  278. return v ? selectDictLabel(this.crmFormTacticDict, v) : "-";
  279. },
  280. crmFollowStatusFormat(v) {
  281. return selectDictLabel(this.crmHandelStatusDict, v);
  282. },
  283. selectDictLabel,
  284. handleCopy(item) {
  285. uni.setClipboardData({
  286. data: item.phone,
  287. success: function () {
  288. uni.$u.toast("复制成功");
  289. },
  290. });
  291. },
  292. // 添加联系人
  293. handleCallPhone() {
  294. uni.makePhoneCall({
  295. phoneNumber: this.receiptDetail.phone,
  296. success: () => {
  297. this.$store.commit("call/SET_FORM", {
  298. clueId: this.receiptDetail.clueId,
  299. type: "3",
  300. callee: this.receiptDetail.phone,
  301. });
  302. },
  303. });
  304. },
  305. // 添加跟进记录
  306. handleAddFollow() {
  307. uni.navigateTo({
  308. url: `/pages/addFollow/index?orderId=${this.orderId}`,
  309. });
  310. },
  311. // 跳转到收单表单页面
  312. handleAddReceiptForm() {
  313. uni.navigateTo({
  314. url: `/pages/receiptForm/index?orderId=${this.orderId}&clueId=${this.clueId}`,
  315. });
  316. },
  317. // 跳转到新增分成页面
  318. handleAddCommission() {
  319. if (this.receiptDetail.status != "3") {
  320. uni.$u.toast("未收单不可添加分成");
  321. return;
  322. }
  323. uni.navigateTo({
  324. url: `/pages/commissionForm/index?sendFormId=${this.orderId}&clueId=${this.clueId}`,
  325. });
  326. },
  327. handleUploadRecord() {
  328. uni.navigateTo({
  329. url: `/pages/uploadRecord/index?clueId=${this.orderId}`,
  330. });
  331. },
  332. // 接单操作
  333. async handleOrderForm() {
  334. if (this.receiptDetail.status === "2") {
  335. uni.$u.toast("当前订单已经被接单");
  336. return;
  337. }
  338. uni.showModal({
  339. title: "提示",
  340. content: "确定要接单吗?",
  341. success: async (res) => {
  342. if (res.confirm) {
  343. try {
  344. await uni.$u.api.oderForm({
  345. status: "2",
  346. id: this.receiptDetail.id,
  347. });
  348. uni.$u.toast("接单成功");
  349. this.getDetail(); // 刷新详情
  350. } catch (error) {
  351. uni.$u.toast("接单失败");
  352. }
  353. }
  354. },
  355. });
  356. },
  357. // 收单操作
  358. async handleReceiptForm() {
  359. if (
  360. this.receiptDetail.status === "3" ||
  361. this.receiptDetail.status === "4"
  362. ) {
  363. uni.$u.toast("当前订单已经被收单或未收");
  364. return;
  365. }
  366. uni.showModal({
  367. title: "提示",
  368. content: "确定要收单吗?",
  369. success: async (res) => {
  370. if (res.confirm) {
  371. try {
  372. await uni.$u.api.oderForm({
  373. status: "3",
  374. id: this.receiptDetail.id,
  375. });
  376. uni.$u.toast("收单成功");
  377. this.getDetail(); // 刷新详情
  378. } catch (error) {
  379. uni.$u.toast("收单失败");
  380. }
  381. }
  382. },
  383. });
  384. },
  385. // 未收操作
  386. async handleDenialForm() {
  387. if (
  388. this.receiptDetail.status === "3" ||
  389. this.receiptDetail.status === "4"
  390. ) {
  391. uni.$u.toast("当前订单已经被收单或未收");
  392. return;
  393. }
  394. uni.showModal({
  395. title: "提示",
  396. content: "确定要标记为未收吗?",
  397. success: async (res) => {
  398. if (res.confirm) {
  399. try {
  400. await uni.$u.api.oderForm({
  401. status: "4",
  402. id: this.receiptDetail.id,
  403. });
  404. uni.$u.toast("标记未收成功");
  405. this.getDetail(); // 刷新详情
  406. } catch (error) {
  407. uni.$u.toast("操作失败");
  408. }
  409. }
  410. },
  411. });
  412. },
  413. // 撤销操作
  414. async handleDelete() {
  415. uni.showModal({
  416. title: "提示",
  417. content: "是否确定撤销?",
  418. success: async (res) => {
  419. if (res.confirm) {
  420. try {
  421. await uni.$u.api.deleteOrder([this.receiptDetail.id]);
  422. uni.$u.toast("撤销成功");
  423. // 撤销后返回上一页
  424. setTimeout(() => {
  425. uni.navigateBack();
  426. }, 1500);
  427. } catch (error) {
  428. uni.$u.toast("撤销失败");
  429. }
  430. }
  431. },
  432. });
  433. },
  434. // 初始化详情页
  435. async handleInit() {
  436. this.getDetail();
  437. this.$getDicts("crm_form_category").then((res) => {
  438. this.crmFormCategoryDict = res;
  439. });
  440. this.$getDicts("crm_form_tactic").then((res) => {
  441. this.crmFormTacticDict = res;
  442. });
  443. this.$getDicts("crm_form_state").then((res) => {
  444. this.crmFormStateDict = res;
  445. });
  446. this.$getDicts("crm_form_status").then((res) => {
  447. this.crmHandelStatusDict = res;
  448. });
  449. uni.$u.api
  450. .getClueTagGroupVoList({
  451. tagGroupApplication: "2",
  452. })
  453. .then(({
  454. data
  455. }) => {
  456. this.clueTagGroupVoList = data;
  457. });
  458. },
  459. getDetail() {
  460. uni.$u.api
  461. .getClueSendFormVoByOrderId({
  462. id: this.orderId,
  463. })
  464. .then((res) => {
  465. this.receiptDetail = res.data;
  466. this.checkTags = this.receiptDetail.tags ?
  467. this.receiptDetail.tags.map((v) => v.id) :
  468. [];
  469. });
  470. },
  471. },
  472. created() {
  473. this.handleInit();
  474. uni.$on('updateSendFormSuccess', () => {
  475. this.getDetail();
  476. });
  477. },
  478. beforeUnmount() {
  479. uni.$off('updateSendFormSuccess');
  480. }
  481. };
  482. </script>
  483. <style lang="scss" scoped>
  484. .clueTagsSelect {
  485. height: 0;
  486. overflow: hidden;
  487. }
  488. .clue_tag_wrap {
  489. display: flex;
  490. align-items: center;
  491. background-color: #fff;
  492. padding: 0 10px;
  493. margin: 20px 0;
  494. flex-wrap: wrap;
  495. min-height: 50px;
  496. .clue_tag_add_btn {
  497. font-size: 14px;
  498. color: #108cff;
  499. }
  500. }
  501. .clue_state_wrap {
  502. background-color: #fff;
  503. padding: 10px;
  504. margin-bottom: 20px;
  505. display: flex;
  506. align-items: center;
  507. .top_left {
  508. font-size: 16px;
  509. flex: 0 0 50px;
  510. }
  511. .steps_wrap {
  512. flex: 1;
  513. }
  514. }
  515. .order_action_wrap {
  516. padding: 10px;
  517. margin: 15px 0;
  518. background: #fff;
  519. display: flex;
  520. align-items: center;
  521. .action_title {
  522. font-size: 16px;
  523. flex: 0 0 50px;
  524. }
  525. .last_status {
  526. color: #c0c0c7;
  527. font-size: 14px;
  528. }
  529. .action_buttons {
  530. display: grid;
  531. grid-template-columns: repeat(2, 1fr);
  532. gap: 10px;
  533. ::v-deep .u-button {
  534. height: 30px !important;
  535. font-size: 12px !important;
  536. border-radius: 8px !important;
  537. border: 1px solid #e2e8f0 !important;
  538. background: #ffffff !important;
  539. color: #2d3748 !important;
  540. font-weight: 500 !important;
  541. box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1) !important;
  542. transition: all 0.3s ease !important;
  543. &.u-button--success {
  544. background: #ffd025 !important;
  545. color: white !important;
  546. border-color: #ffd025 !important;
  547. }
  548. // 特别样式化不同按钮类型
  549. &.u-button--primary {
  550. background: #35dbd9 !important;
  551. color: white !important;
  552. border-color: #35dbd9 !important;
  553. }
  554. &.u-button--warning {
  555. background: #ba9fb0 !important;
  556. color: white !important;
  557. border-color: #ba9fb0 !important;
  558. }
  559. &.u-button--error {
  560. background: #e53e3e !important;
  561. color: white !important;
  562. border-color: #e53e3e !important;
  563. }
  564. }
  565. }
  566. }
  567. .clueDetail_tabber {
  568. ::v-deep .u-tabbar__content {
  569. background: #108cff;
  570. border-top-right-radius: 10px;
  571. border-top-left-radius: 10px;
  572. }
  573. }
  574. .clueDetail_wrap {
  575. .telPhone {
  576. display: flex;
  577. background: #fff;
  578. padding: 20px;
  579. justify-content: space-between;
  580. .left {
  581. display: flex;
  582. }
  583. .copy_btn {
  584. color: #4fa5fe;
  585. margin-left: 10px;
  586. }
  587. }
  588. .clueDetail_top_info {
  589. display: flex;
  590. flex-wrap: wrap;
  591. background-color: #ffffff;
  592. padding-top: 20px;
  593. margin: 18px 0;
  594. .top_info_item {
  595. width: 33.33%;
  596. text-align: center;
  597. margin-bottom: 20px;
  598. .top {
  599. font-size: 15px;
  600. color: #202020;
  601. margin-bottom: 5px;
  602. font-weight: bold;
  603. }
  604. .bottom {
  605. font-size: 15px;
  606. color: #c0c0c7;
  607. }
  608. }
  609. }
  610. }
  611. </style>