detail.vue 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193
  1. <!--
  2. * @Author: wanglj 471442253@qq.com
  3. * @Date: 2022-12-26 09:30:47
  4. * @LastEditors: liuzhenlin
  5. * @LastEditTime: 2023-06-08 09:33:25
  6. * @Description: file content
  7. * @FilePath: \订单全流程管理系统\src\views\customer\detail.vue
  8. -->
  9. <template>
  10. <div class="detail">
  11. <div class="side-layout">
  12. <div class="info">
  13. <div class="title">
  14. <p>客户</p>
  15. <h3>
  16. {{ detail.custName }}
  17. <div>
  18. <el-button @click="handleFollow">添加跟进</el-button>
  19. <span v-show="detail.salesId > 0">
  20. <el-button v-permissions="['cust:list:shift']" style="margin-left: 10px" @click="handleShift">
  21. 转移客户
  22. </el-button>
  23. <el-button v-permissions="['cust:list:open']" @click="handleToOpen">移入公海</el-button>
  24. <el-button v-permissions="['cust:list:project']" @click="handleBusiness">创建项目</el-button>
  25. </span>
  26. <span v-show="detail.salesId == 0">
  27. <el-button v-permissions="['cust:open:receive']" @click="handleReceive">领取客户</el-button>
  28. <el-button v-permissions="['cust:open:allocate']" @click="handleAllocate">分配客户</el-button>
  29. </span>
  30. </div>
  31. </h3>
  32. </div>
  33. <header>
  34. <el-descriptions :colon="false" :column="6" direction="vertical">
  35. <el-descriptions-item content-class-name="my-content" label="客户编码" label-class-name="my-label">
  36. {{ detail.custCode }}
  37. </el-descriptions-item>
  38. <el-descriptions-item content-class-name="my-content" label="助记名" label-class-name="my-label">
  39. {{ detail.abbrName }}
  40. </el-descriptions-item>
  41. <el-descriptions-item content-class-name="my-content" label="客户级别" label-class-name="my-label">
  42. {{ selectDictLabel(levelOptions, detail.custLevel) }}
  43. </el-descriptions-item>
  44. <el-descriptions-item content-class-name="my-content" label="客户类型" label-class-name="my-label">
  45. {{ selectDictLabel(industryOptions, detail.custIndustry) }}
  46. </el-descriptions-item>
  47. <!-- <el-descriptions-item content-class-name="my-content" label="客户状态" label-class-name="my-label">
  48. {{ detail.custStatus == 10 ? '正常' : '异常' }}
  49. </el-descriptions-item> -->
  50. <el-descriptions-item content-class-name="my-content" label="最后跟进时间" label-class-name="my-label">
  51. {{ parseTime(detail.followUpDate, '{y}-{m}-{d}') }}
  52. </el-descriptions-item>
  53. </el-descriptions>
  54. </header>
  55. <el-tabs v-model="activeName" @tab-click="handleClick">
  56. <el-tab-pane label="详细信息" name="detail">
  57. <el-descriptions
  58. border
  59. :column="2"
  60. :content-style="{ width: '25%', 'word-break': 'break-all' }"
  61. :label-style="{ width: '25%' }"
  62. size="medium">
  63. <el-descriptions-item label="客户级别">
  64. {{ selectDictLabel(levelOptions, detail.custLevel) }}
  65. </el-descriptions-item>
  66. <el-descriptions-item label="最后跟进时间">
  67. {{ detail.followUpDate }}
  68. </el-descriptions-item>
  69. <el-descriptions-item label="所在省">
  70. {{ detail.custProvince }}
  71. </el-descriptions-item>
  72. <el-descriptions-item label="所在市">
  73. {{ detail.custCity }}
  74. </el-descriptions-item>
  75. <el-descriptions-item label="所在区">
  76. {{ detail.custRegion }}
  77. </el-descriptions-item>
  78. <el-descriptions-item label="详细地址">
  79. {{ detail.custAddress }}
  80. </el-descriptions-item>
  81. <el-descriptions-item label="创建人">
  82. {{ detail.createdName }}
  83. </el-descriptions-item>
  84. <el-descriptions-item label="创建时间">
  85. {{ detail.createdTime }}
  86. </el-descriptions-item>
  87. <el-descriptions-item label="跟进次数">
  88. {{ abstract.followCount }}
  89. </el-descriptions-item>
  90. <el-descriptions-item label="未跟进时长">
  91. {{ abstract.notFollowDay }}
  92. </el-descriptions-item>
  93. <el-descriptions-item label="项目数量">
  94. {{ abstract.business }}
  95. </el-descriptions-item>
  96. <el-descriptions-item label="项目总额">
  97. {{ formatPrice(abstract.businessTotal) }}
  98. </el-descriptions-item>
  99. <el-descriptions-item label="成交次数">
  100. {{ abstract.dealCotal }}
  101. </el-descriptions-item>
  102. <el-descriptions-item label="成交总额">
  103. {{ formatPrice(abstract.dealTotal) }}
  104. </el-descriptions-item>
  105. <el-descriptions-item label="回款总额">
  106. {{ formatPrice(abstract.paymentTotal) }}
  107. </el-descriptions-item>
  108. <el-descriptions-item label="未回款总额">
  109. {{ formatPrice(abstract.notPaymentTotal) }}
  110. </el-descriptions-item>
  111. <el-descriptions-item label="开票总额" :span="24">
  112. {{ formatPrice(abstract.drawTotal) }}
  113. </el-descriptions-item>
  114. <el-descriptions-item label="备注" :span="24">
  115. {{ detail.remark }}
  116. </el-descriptions-item>
  117. </el-descriptions>
  118. </el-tab-pane>
  119. <el-tab-pane label="跟进记录" name="follow">
  120. <ul v-if="followList.length" class="follow">
  121. <li v-for="(date, index) in followList" :key="index">
  122. <div class="date">
  123. <h2>{{ date.followDay.split('-')[2] }}</h2>
  124. <h3>
  125. {{ date.followDay.split('-').splice(0, 2).join('.') }}
  126. </h3>
  127. </div>
  128. <ul class="content">
  129. <li v-for="(item, idx) in date.followupList" :key="idx">
  130. <!-- <el-avatar class="user-avatar"
  131. :src="avatar" />-->
  132. <div class="text-container">
  133. <vab-icon class="user-avatar" icon="account-circle-fill" />
  134. <div class="text">
  135. <p class="action">
  136. <span>{{ item.createdName }} 跟进({{ formatType(item.followType) }})</span>
  137. <span>
  138. <vab-icon icon="time-line" />
  139. {{ item.followDate }}
  140. </span>
  141. </p>
  142. <p style="white-space: pre-wrap">{{ item.followContent }}</p>
  143. <div class="footer">
  144. <p>
  145. 来自客户:
  146. <span>{{ item.custName }}</span>
  147. </p>
  148. <div>
  149. <el-button icon="el-icon-edit" size="mini" @click="postComments(item)">发表评论</el-button>
  150. <el-button size="mini" @click="showDetail(item)">
  151. <vab-icon icon="arrow-right-circle-fill" />
  152. 详情
  153. </el-button>
  154. <el-button size="mini" @click="showComment(item)">评论({{ item.commentNumber }})</el-button>
  155. </div>
  156. </div>
  157. </div>
  158. </div>
  159. <transition name="height">
  160. <ul v-if="item.showComment" class="comments">
  161. <li v-for="comment in item.comments" :key="comment.id">
  162. <vab-icon class="user-avatar" icon="account-circle-fill" />
  163. <div class="text">
  164. <p>{{ comment.createdName }}</p>
  165. <p>{{ comment.content }}</p>
  166. <p>{{ comment.createdTime }}</p>
  167. </div>
  168. </li>
  169. </ul>
  170. </transition>
  171. </li>
  172. </ul>
  173. </li>
  174. </ul>
  175. <div v-else class="no-follow">暂无跟进记录</div>
  176. </el-tab-pane>
  177. <el-tab-pane label="联系人" name="contact">
  178. <vab-query-form>
  179. <vab-query-form-left-panel :span="12">
  180. <el-input
  181. v-model="cuctName"
  182. placeholder="请输入单据名称/编号"
  183. prefix-icon="el-icon-search"
  184. style="width: 50%"
  185. @blur="handleClick({ name: 'contact' })"
  186. @keyup.enter.native="handleClick({ name: 'contact' })" />
  187. </vab-query-form-left-panel>
  188. <vab-query-form-right-panel :span="12">
  189. <el-button v-permissions="['cust:detail:contact:add']" icon="el-icon-plus" @click="addContact">
  190. 新建联系人
  191. </el-button>
  192. </vab-query-form-right-panel>
  193. </vab-query-form>
  194. <el-table border :data="contactList" height="calc(100% - 42px)" @selection-change="setSelectRows">
  195. <el-table-column align="center" type="selection" />
  196. <el-table-column align="center" label="姓名" prop="cuctName" />
  197. <el-table-column align="center" label="岗位" prop="postion" />
  198. <el-table-column align="center" label="电话" prop="telephone" />
  199. <el-table-column align="center" label="微信" prop="wechat" />
  200. <el-table-column align="center" label="邮箱" prop="email" />
  201. <el-table-column align="center" label="是否决策人">
  202. <template slot-scope="scope">
  203. <el-switch v-model="scope.row.isDecision" active-value="10" disabled inactive-value="20" />
  204. </template>
  205. </el-table-column>
  206. <el-table-column align="center" label="操作">
  207. <template slot-scope="scope">
  208. <el-button v-permissions="['cust:deatil:contact:edit']" type="text" @click="contactEdit(scope.row)">
  209. 编辑
  210. </el-button>
  211. <el-button v-permissions="['cust:detail:contact:delete']" type="text" @click="contactDel(scope.row)">
  212. 删除
  213. </el-button>
  214. </template>
  215. </el-table-column>
  216. </el-table>
  217. </el-tab-pane>
  218. <el-tab-pane label="项目记录" name="item">
  219. <el-table border :data="items" height="calc(100% - 42px)">
  220. <el-table-column align="center" label="项目编号" prop="nboCode" width="130px" />
  221. <el-table-column align="center" label="项目名称" prop="nboName" show-overflow-tooltip width="250px" />
  222. <el-table-column align="center" label="审批状态" prop="approStatus" width="120px">
  223. <template slot-scope="scope">
  224. {{ getStatus(scope.row.approStatus) }}
  225. </template>
  226. </el-table-column>
  227. <!-- <el-table-column align="center" label="项目类别" prop="nboType" width="80px" />-->
  228. <!-- <el-table-column align="center" label="产品线" prop="productLine" width="120px" />-->
  229. <el-table-column
  230. align="center"
  231. label="经销商/代理商"
  232. prop="distributorName"
  233. show-overflow-tooltip
  234. width="200px" />
  235. <el-table-column align="center" label="预算" prop="nboBudget" width="120px" />
  236. <el-table-column align="center" label="项目备案时间" prop="filingTime" width="160px" />
  237. <!-- <el-table-column align="center" label="项目来源" prop="nboSource" width="160px" />-->
  238. <!-- <el-table-column align="center" label="销售模式" prop="salesModel" width="160px" />-->
  239. <el-table-column align="center" label="最后跟进时间" prop="finalFollowTime" width="160px" />
  240. <el-table-column align="center" label="下次跟进时间" prop="nextFollowTime " width="160px" />
  241. </el-table>
  242. </el-tab-pane>
  243. <el-tab-pane label="合同记录" name="contract">
  244. <el-table border :data="contracts" height="calc(100% - 42px)">
  245. <el-table-column align="center" label="合同编号" prop="contractCode" width="140px" />
  246. <el-table-column
  247. align="center"
  248. label="合同名称"
  249. prop="contractName"
  250. show-overflow-tooltip
  251. width="240px" />
  252. <el-table-column align="center" label="项目名称" prop="nboName" show-overflow-tooltip width="240px" />
  253. <el-table-column align="center" label="审批状态" prop="approStatus" width="120px">
  254. <template slot-scope="scope">
  255. {{ getStatus(scope.row.approStatus) }}
  256. </template>
  257. </el-table-column>
  258. <el-table-column align="center" label="合同类型" prop="contractType" width="120px">
  259. <template slot-scope="scope">
  260. {{ selectDictLabel(contractTypeOptions, scope.row.contractType) }}
  261. </template>
  262. </el-table-column>
  263. <el-table-column align="center" label="合同金额" prop="contractAmount" width="120px" />
  264. <el-table-column align="center" label="负责人" prop="inchargeName" width="120px" />
  265. <el-table-column align="center" label="公司签约人" prop="signatoryName" width="140px" />
  266. <el-table-column align="center" label="经销商" prop="distributorName" width="320px" />
  267. </el-table>
  268. </el-tab-pane>
  269. <el-tab-pane label="工单记录" name="worksheet">工单记录</el-tab-pane>
  270. <el-tab-pane label="归属记录" name="belong">
  271. <el-table border :data="belongs" height="calc(100% - 42px)">
  272. <el-table-column align="center" label="归属销售" prop="saleName" />
  273. <el-table-column align="center" label="原来归属" prop="origSaleName" />
  274. <el-table-column align="center" label="操作方式" prop="opnType">
  275. <template slot-scope="scope">
  276. <el-tag v-if="scope.row.opnType == 10">分配</el-tag>
  277. <el-tag v-else-if="scope.row.opnType == 20">转移</el-tag>
  278. <el-tag v-else-if="scope.row.opnType == 30">领取</el-tag>
  279. <el-tag v-else-if="scope.row.opnType == 40">合并</el-tag>
  280. </template>
  281. </el-table-column>
  282. <el-table-column align="center" label="操作人" prop="createdName" />
  283. <el-table-column align="center" label="操作时间" min-width="160px" prop="opnDatetime" />
  284. <el-table-column align="center" label="开始时间" min-width="160px" prop="startDate" />
  285. <el-table-column align="center" label="结束时间" min-width="160px" prop="endDate" />
  286. <el-table-column align="center" label="备注" prop="remark" />
  287. </el-table>
  288. </el-tab-pane>
  289. <el-tab-pane label="招标记录" name="bid">
  290. <vab-query-form>
  291. <vab-query-form-left-panel :span="12">
  292. <el-input
  293. v-model="bidSearchText"
  294. placeholder="招标产品名称/招标信息标题/中标单位"
  295. prefix-icon="el-icon-search"
  296. style="width: 50%"
  297. @blur="handleClick({ name: 'bid' })"
  298. @keyup.enter.native="handleClick({ name: 'bid' })" />
  299. </vab-query-form-left-panel>
  300. <vab-query-form-right-panel :span="12">
  301. <el-button v-permissions="['base:bid:add']" icon="el-icon-plus" @click="addBid">新建招标记录</el-button>
  302. </vab-query-form-right-panel>
  303. </vab-query-form>
  304. <el-table border :data="bidlist" height="calc(100% - 42px)">
  305. <el-table-column align="center" label="客户名称" prop="cuctName" width="120px" />
  306. <el-table-column align="center" label="招标产品名称" prop="productName" width="120px" />
  307. <el-table-column align="center" label="发布日期" prop="publishedTime" width="160px" />
  308. <el-table-column align="center" label="状态" prop="status" width="120px">
  309. <template slot-scope="scope">
  310. {{ selectDictLabel(bidStatus, scope.row.status) }}
  311. </template>
  312. </el-table-column>
  313. <el-table-column align="center" label="预算" prop="budget" width="120px" />
  314. <el-table-column align="center" label="产品线" prop="ProductLine" width="120px">
  315. <template slot-scope="scope">
  316. {{ selectDictLabel(productLineOptions, scope.row.productLine) }}
  317. </template>
  318. </el-table-column>
  319. <el-table-column align="center" label="招标信息标题" prop="title" width="120px" />
  320. <el-table-column align="center" label="信息分类" prop="infoType" width="120px">
  321. <template #default="{ row }">
  322. {{ bidInfoTypeOptions[row.infoType] }}
  323. </template>
  324. </el-table-column>
  325. <el-table-column align="center" label="中标单位" prop="bidder" width="120px" />
  326. <el-table-column align="center" label="创建招标日期" prop="biddingTime" width="160px" />
  327. <el-table-column align="center" label="闭环信息描述" prop="closeLoopMsg" width="120px" />
  328. <el-table-column align="center" label="备注" prop="remark" width="120px" />
  329. <el-table-column align="center" fixed="right" label="操作" width="120px">
  330. <template slot-scope="scope">
  331. <el-button
  332. v-if="scope.row.status == '10' || scope.row.status == '40'"
  333. v-permissions="['base:bid:add']"
  334. type="text"
  335. @click="bidEdit(scope.row)">
  336. 编辑
  337. </el-button>
  338. <el-button
  339. v-if="scope.row.status == '10' || scope.row.status == '40'"
  340. v-permissions="['base:bid:delete']"
  341. type="text"
  342. @click="bidDel(scope.row)">
  343. 删除
  344. </el-button>
  345. <el-button
  346. v-if="scope.row.status == '10' || scope.row.status == '40'"
  347. type="text"
  348. @click="closeLoop(scope.row)">
  349. 闭环
  350. </el-button>
  351. </template>
  352. </el-table-column>
  353. </el-table>
  354. </el-tab-pane>
  355. <el-tab-pane label="开票抬头" name="invoiceHeader">
  356. <vab-query-form>
  357. <vab-query-form-left-panel :span="12">
  358. <el-input
  359. v-model="invoiceHeaderSearchText"
  360. placeholder="客户名称/公司名称/纳税人识别号"
  361. prefix-icon="el-icon-search"
  362. style="width: 50%"
  363. @blur="handleClick({ name: 'invoiceHeader' })"
  364. @keyup.enter.native="handleClick({ name: 'invoiceHeader' })" />
  365. </vab-query-form-left-panel>
  366. <vab-query-form-right-panel :span="12">
  367. <el-button icon="el-icon-plus" @click="addInvoiceHeader">新建开票抬头</el-button>
  368. </vab-query-form-right-panel>
  369. </vab-query-form>
  370. <el-table border :data="invoiceHeaderList" height="calc(100% - 42px)">
  371. <el-table-column align="center" label="客户名称" prop="cuctName" width="220px" />
  372. <el-table-column align="center" label="公司名称" prop="companyName" width="220px" />
  373. <el-table-column align="center" label="纳税人识别号" prop="taxNo" width="120px" />
  374. <el-table-column align="center" label="地址" prop="address" width="160px" />
  375. <el-table-column align="center" label="开户行及账号" prop="bankNo" width="120px" />
  376. <el-table-column align="center" label="备注" prop="remark" width="120px" />
  377. <el-table-column align="center" fixed="right" label="操作" width="90px">
  378. <template slot-scope="scope">
  379. <el-button type="text" @click="invoiceHeaderEdit(scope.row)">编辑</el-button>
  380. <el-button type="text" @click="invoiceHeaderDel(scope.row)">删除</el-button>
  381. </template>
  382. </el-table-column>
  383. </el-table>
  384. </el-tab-pane>
  385. </el-tabs>
  386. </div>
  387. <div class="info-side">
  388. <div class="buttons">
  389. <el-button v-if="checkPermi('edit')" type="primary" @click="handleEdit">编辑</el-button>
  390. <el-button v-if="checkPermi('delete')" @click="handleDelete">删除</el-button>
  391. <el-button @click="$router.go(-1)">返回</el-button>
  392. </div>
  393. <ul class="records">
  394. <li v-for="(value, key) in records" :key="key">
  395. <div class="date">
  396. <h2>{{ key.split('-')[2] }}</h2>
  397. <h3>{{ key.split('-').splice(0, 2).join('.') }}</h3>
  398. </div>
  399. <ul class="content">
  400. <li v-for="(item, index) in records[key]" :key="index">
  401. <!-- <el-avatar class="user-avatar"
  402. :src="avatar" /> -->
  403. <vab-icon class="user-avatar" icon="account-circle-fill" />
  404. <div class="text">
  405. <p class="action">{{ item.opnPeople }} {{ item.opnType }}</p>
  406. <p>{{ item.opnDate }}</p>
  407. <p v-if="item.opnContent.custName">
  408. 客户名称:
  409. <span>{{ item.opnContent.custName }}</span>
  410. </p>
  411. <template v-else-if="item.opnContent.cuctName">
  412. <p>
  413. 联系人名称:
  414. <span>{{ item.opnContent.cuctName }}</span>
  415. </p>
  416. <p>职务:{{ item.opnContent.postion }}</p>
  417. <p>手机:{{ item.opnContent.telephone }}</p>
  418. </template>
  419. </div>
  420. </li>
  421. </ul>
  422. </li>
  423. </ul>
  424. </div>
  425. </div>
  426. <Contact ref="contact" @contactSave="contactSave" />
  427. <Edit ref="edit" @customerSave="customerSave" />
  428. <!-- 分配客户 -->
  429. <Allocate ref="allocate" @refresh="back" />
  430. <!-- 转移客户 -->
  431. <Shift ref="shift" @refresh="back" />
  432. <!-- 移入公海 -->
  433. <ToOpen ref="toOpen" @refresh="backToOpen" />
  434. <!-- 跟进详情 -->
  435. <FollowDetail ref="followDetail" />
  436. <!-- 添加项目 -->
  437. <Businessadd ref="businessadd" :cust-info="custInfo" @fetch-data="getBusiness" />
  438. <!-- 领取客户 -->
  439. <Pick ref="pick" />
  440. <!-- 招标信息 -->
  441. <Bid ref="bid" @bidSave="bidSave" />
  442. <InvoiceHeader ref="invoiceHeader" @save="invoiceHeaderSave" />
  443. <!-- 添加跟进记录 -->
  444. <follow-add ref="follow-add" @fetch-data="handleClick({ name: 'follow' })" />
  445. <!-- 闭环 -->
  446. <closeLoopDialog ref="closeLoopDialog" @doRefesh="handleClick({ name: 'bid' })" />
  447. </div>
  448. </template>
  449. <script>
  450. import { hasPermission } from '@/utils/permission'
  451. import { mapGetters } from 'vuex'
  452. import api from '@/api/customer'
  453. import follow from '@/api/customer/follow'
  454. import to from 'await-to-js'
  455. import businessApi from '@/api/proj/business'
  456. import contractApi from '@/api/contract'
  457. import bidApi from '@/api/customer/bid'
  458. import invoiceHeaderApi from '@/api/customer/invoiceHeader'
  459. import Contact from './components/Contact'
  460. import Edit from './components/Edit'
  461. import Allocate from './components/Allocate'
  462. import Shift from './components/Shift'
  463. import ToOpen from './components/ToOpen'
  464. import FollowDetail from './components/FollowDetail'
  465. import Businessadd from '../proj/business/components/BusinessAdd'
  466. import Pick from './components/Pick'
  467. import Bid from './components/Bid'
  468. import InvoiceHeader from './components/InvoiceHeader'
  469. import FollowAdd from '@/views/proj/business/components/FollowAdd'
  470. import closeLoopDialog from './inviteTenders/components/closeLoop'
  471. export default {
  472. name: 'CustomerDetail',
  473. components: {
  474. FollowAdd,
  475. Edit,
  476. Contact,
  477. Allocate,
  478. Shift,
  479. Pick,
  480. Bid,
  481. InvoiceHeader,
  482. ToOpen,
  483. FollowDetail,
  484. Businessadd,
  485. closeLoopDialog,
  486. },
  487. data() {
  488. return {
  489. id: '',
  490. detail: {
  491. custCode: '', //客户编码
  492. abbrName: '', //助记名
  493. level: '', //客户级别
  494. indusTry: '', //客户类型
  495. custStatus: '', //客户状态
  496. followUpDate: '', //最后跟进时间
  497. },
  498. abstract: {
  499. followContent: '', //跟进次数
  500. notFollowDay: '', //未跟进天数
  501. business: '', //项目数量
  502. businessTotal: '', //项目总额
  503. dealCotal: '', //成交次数
  504. dealTotal: '', //成交总额
  505. paymentTotal: '', //回款总额
  506. notPaymentTotal: '', //未回款总额
  507. drawTotal: '', //开票总额
  508. },
  509. activeName: 'detail',
  510. cuctName: '',
  511. bidSearchText: '',
  512. invoiceHeaderSearchText: '',
  513. contactList: [],
  514. selectRows: [],
  515. records: [], //操作记录
  516. followList: [], //跟进记录
  517. belongs: [],
  518. bidlist: [],
  519. invoiceHeaderList: [],
  520. items: [], //项目
  521. contracts: [], //合同
  522. appro: [],
  523. bidInfoTypeOptions: {},
  524. custInfo: {},
  525. levelOptions: [],
  526. industryOptions: [],
  527. contractTypeOptions: [],
  528. productLineOptions: [],
  529. bidStatus: [],
  530. }
  531. },
  532. computed: {
  533. ...mapGetters({
  534. avatar: 'user/avatar',
  535. username: 'user/username',
  536. }),
  537. },
  538. mounted() {
  539. this.id = this.$route.query.id
  540. this.init()
  541. this.getOptions()
  542. this.getDynamics()
  543. this.handleClick({ name: 'follow' })
  544. },
  545. methods: {
  546. getOptions() {
  547. Promise.all([
  548. this.getDicts('cust_level'),
  549. this.getDicts('cust_idy'),
  550. this.getDicts('contract_type'),
  551. this.getDicts('sys_product_line'),
  552. this.getDicts('bid_status'),
  553. ])
  554. .then(([level, industry, contractType, productLine, status]) => {
  555. this.levelOptions = level.data.values || []
  556. this.industryOptions = industry.data.values || []
  557. this.contractTypeOptions = contractType.data.values || []
  558. this.productLineOptions = productLine.data.values || []
  559. this.bidStatus = status.data.values || []
  560. })
  561. .catch((err) => console.log(err))
  562. },
  563. checkPermi(val) {
  564. let str = 'cust:'
  565. if (this.detail.salesId) str += 'list:'
  566. else str += 'open:'
  567. str += val
  568. return hasPermission([str])
  569. },
  570. async init() {
  571. Promise.all([
  572. api.getDetail({ ids: [parseInt(this.id)] }),
  573. api.getAbstract({ id: parseInt(this.id) }),
  574. this.getDicts('proj_appro_status'),
  575. ]).then(([detail, abstract, appro]) => {
  576. if (detail.data.list[0]) this.detail = detail.data.list[0]
  577. if (abstract.data.list) this.abstract = abstract.data.list
  578. this.appro = appro.data.values || []
  579. })
  580. this.getDicts('bid_info_type').then((response) => {
  581. this.bidInfoTypeOptions = {}
  582. response.data.values.filter((i) => {
  583. this.bidInfoTypeOptions[i.key] = i.value
  584. })
  585. })
  586. },
  587. getStatus(val) {
  588. const obj = this.appro.find((item) => item.key == val)
  589. if (obj) return obj.value
  590. },
  591. async getDynamics() {
  592. const [err, res] = await to(api.dynamicsList({ custId: parseInt(this.id) }))
  593. if (err) return
  594. if (res.data.list && res.data.list[0]) {
  595. let obj = res.data.list[0]
  596. const keys = Object.keys(obj).reverse()
  597. let records = {}
  598. for (const item of keys) {
  599. records[item] = obj[item]
  600. }
  601. this.records = records
  602. }
  603. },
  604. setSelectRows(val) {
  605. this.selectRows = val
  606. },
  607. async handleClick(tab) {
  608. let err, res
  609. if (tab.name == 'contact') {
  610. ;[err, res] = await to(api.getContact({ custId: parseInt(this.id), cuctName: this.cuctName }))
  611. if (err) return
  612. this.contactList = res.data.list || []
  613. } else if (tab.name == 'follow') {
  614. let params = {
  615. custId: String(this.id),
  616. DaysBeforeToday: 99999,
  617. }
  618. ;[err, res] = await to(follow.getListByDay(params))
  619. if (err) return
  620. this.followList = res.data.list || []
  621. } else if (tab.name == 'belong') {
  622. ;[err, res] = await to(api.getBelongs({ custId: parseInt(this.id) }))
  623. if (err) return
  624. this.belongs = res.data.list || []
  625. } else if (tab.name == 'item') {
  626. ;[err, res] = await to(businessApi.getList({ custId: this.detail.id }))
  627. if (err) return
  628. this.items = res.data.list || []
  629. } else if (tab.name == 'contract') {
  630. ;[err, res] = await to(contractApi.getList({ custId: parseInt(this.id) }))
  631. this.contracts = res.data.list || []
  632. } else if (tab.name == 'bid') {
  633. ;[err, res] = await to(api.bidList({ custId: parseInt(this.id), searchText: this.bidSearchText }))
  634. this.bidlist = res.data.list || []
  635. } else if (tab.name == 'invoiceHeader') {
  636. ;[err, res] = await to(
  637. invoiceHeaderApi.list({ custId: parseInt(this.id), searchText: this.invoiceHeaderSearchText })
  638. )
  639. this.invoiceHeaderList = res.data.list || []
  640. }
  641. },
  642. // 添加联系人
  643. addContact() {
  644. this.$refs.contact.contactForm.custId = this.detail.id
  645. this.$refs.contact.contactForm.custName = this.detail.custName
  646. this.$refs.contact.contactVisible = true
  647. },
  648. addBid() {
  649. this.$refs.bid.form.custId = this.detail.id
  650. this.$refs.bid.init()
  651. },
  652. // 添加跟进记录
  653. handleFollow() {
  654. this.followup = {
  655. targetId: this.detail.id,
  656. targetType: '10',
  657. targetName: this.detail.custName,
  658. custId: this.detail.id,
  659. custName: this.detail.custName,
  660. }
  661. console.log(this.followup)
  662. this.$refs['follow-add'].showEdit(this.followup)
  663. },
  664. addInvoiceHeader() {
  665. this.$refs.invoiceHeader.form.custId = this.detail.id
  666. this.$refs.invoiceHeader.init()
  667. },
  668. bidEdit(row) {
  669. this.$refs.bid.init(row.id)
  670. },
  671. invoiceHeaderEdit(row) {
  672. this.$refs.invoiceHeader.init(row.id)
  673. },
  674. invoiceHeaderDel(row) {
  675. this.$confirm('确认删除?', '提示', {
  676. confirmButtonText: '确定',
  677. cancelButtonText: '取消',
  678. type: 'warning',
  679. })
  680. .then(async () => {
  681. const [err, res] = await to(invoiceHeaderApi.delete({ id: [row.id] }))
  682. if (err) return
  683. if (res.code == 200) {
  684. this.$message({
  685. type: 'success',
  686. message: '删除成功!',
  687. })
  688. this.handleClick({ name: 'invoiceHeader' })
  689. }
  690. })
  691. .catch((err) => console.log(err))
  692. },
  693. // 删除招标信息
  694. bidDel(row) {
  695. this.$confirm('确认删除?', '提示', {
  696. confirmButtonText: '确定',
  697. cancelButtonText: '取消',
  698. type: 'warning',
  699. })
  700. .then(async () => {
  701. const [err, res] = await to(bidApi.delete({ id: [row.id] }))
  702. if (err) return
  703. if (res.code == 200) {
  704. this.$message({
  705. type: 'success',
  706. message: '删除成功!',
  707. })
  708. this.handleClick({ name: 'bid' })
  709. }
  710. })
  711. .catch(() => {})
  712. },
  713. // 闭环
  714. closeLoop(row) {
  715. this.$refs.closeLoopDialog.init(row)
  716. },
  717. // 保存联系人
  718. contactSave() {
  719. this.handleClick({ name: 'contact' })
  720. this.getDynamics()
  721. },
  722. bidSave() {
  723. this.handleClick({ name: 'bid' })
  724. },
  725. invoiceHeaderSave() {
  726. this.handleClick({ name: 'invoiceHeader' })
  727. },
  728. // 编辑客户
  729. handleEdit() {
  730. this.$refs.edit.title = '编辑客户'
  731. this.$refs.edit.editForm = { ...this.detail }
  732. this.$refs.edit.editVisible = true
  733. this.$refs.edit.areaEditDisable = true
  734. this.$refs.edit.showLocation()
  735. },
  736. // 编辑联系人
  737. contactEdit(row) {
  738. this.$refs.contact.contactForm = { ...row }
  739. this.$refs.contact.contactForm.custName = this.detail.custName
  740. this.$refs.contact.contactVisible = true
  741. },
  742. // 删除联系人
  743. contactDel(row) {
  744. this.$confirm('确认删除?', '提示', {
  745. confirmButtonText: '确定',
  746. cancelButtonText: '取消',
  747. type: 'warning',
  748. })
  749. .then(async () => {
  750. const [err, res] = await to(api.deleteContact({ Ids: [row.id], custId: parseInt(this.id) }))
  751. if (err) return
  752. if (res.code == 200) {
  753. this.$message({
  754. type: 'success',
  755. message: '删除成功!',
  756. })
  757. this.contactSave()
  758. }
  759. })
  760. .catch(() => {})
  761. },
  762. // 转移客户
  763. handleShift() {
  764. this.$refs.shift.form.Ids = [parseInt(this.id)]
  765. this.$refs.shift.visible = true
  766. },
  767. // 移入公海
  768. handleToOpen() {
  769. this.$refs.toOpen.form.ids = [parseInt(this.id)]
  770. this.$refs.toOpen.visible = true
  771. },
  772. // 客户删除
  773. handleDelete() {
  774. this.$confirm('确认删除?', '提示', {
  775. confirmButtonText: '确定',
  776. cancelButtonText: '取消',
  777. type: 'warning',
  778. })
  779. .then(async () => {
  780. const [err, res] = await to(api.deleteCustomer({ Id: parseInt(this.id) }))
  781. if (err) return
  782. if (res.code == 200) {
  783. this.$message({
  784. type: 'success',
  785. message: '删除成功!',
  786. })
  787. this.$router.go(-1)
  788. }
  789. })
  790. .catch(() => {})
  791. },
  792. backToOpen() {
  793. this.$router.push('/customer/openSea')
  794. },
  795. back() {
  796. this.$router.go(-1)
  797. },
  798. // 领取
  799. handleReceive() {
  800. this.$refs.pick.ids = [parseInt(this.id)]
  801. this.$refs.pick.visible = true
  802. // this.$confirm('确认领取客户?', '提示', {
  803. // confirmButtonText: '确定',
  804. // cancelButtonText: '取消',
  805. // type: 'warning',
  806. // })
  807. // .then(async () => {
  808. // const [err, res] = await to(
  809. // api.receiveCustomer({
  810. // ids: [parseInt(this.id)],
  811. // salesId: this.$store.state.user.id,
  812. // salesName: this.$store.state.user.nickName,
  813. // receive: '1',
  814. // })
  815. // )
  816. // if (err) return
  817. // if (res.code == 200) {
  818. // this.$message({
  819. // type: 'success',
  820. // message: '领取成功!',
  821. // })
  822. // this.$router.push('/customer/list')
  823. // }
  824. // })
  825. // .catch(() => {})
  826. },
  827. handleAllocate() {
  828. this.$refs.allocate.ids = [parseInt(this.id)]
  829. this.$refs.allocate.visible = true
  830. },
  831. customerSave() {
  832. this.init()
  833. this.getDynamics()
  834. },
  835. formatType(val) {
  836. let str = ''
  837. if (val == 10) str = '电话'
  838. else if (val == 20) str = '邮件'
  839. else if (val == 30) str = '拜访'
  840. return str
  841. },
  842. // 跟进记录详情
  843. showDetail(row) {
  844. this.$refs.followDetail.init({ ...row })
  845. },
  846. // 发表评论
  847. postComments(row) {
  848. this.$PostComment({ form: row, visible: true })
  849. .then(() => {
  850. this.handleClick({ name: 'follow' })
  851. })
  852. .catch(() => {})
  853. },
  854. // 展开评论
  855. showComment(row) {
  856. if (!row.comments.length) return this.$message.warning('暂无评论')
  857. row.showComment = !row.showComment
  858. this.$forceUpdate()
  859. },
  860. // 创建项目
  861. handleBusiness() {
  862. this.custInfo = {
  863. custId: parseInt(this.id),
  864. custName: this.detail.custName,
  865. }
  866. this.$refs.businessadd.showEdit({})
  867. },
  868. getBusiness() {
  869. if (this.activeName == 'item') {
  870. this.handleClick({ name: 'item' })
  871. }
  872. },
  873. },
  874. }
  875. </script>
  876. <style lang="scss" scoped>
  877. $base: '.detail';
  878. #{$base} {
  879. height: calc(100vh - 60px - 12px * 2 - 40px);
  880. display: flex;
  881. padding: 20px 40px;
  882. > .el-row {
  883. flex: 1;
  884. width: 100%;
  885. > .el-col {
  886. height: 100%;
  887. }
  888. }
  889. .title {
  890. p,
  891. h3 {
  892. margin: 0;
  893. }
  894. p {
  895. font-size: 14px;
  896. font-weight: 400;
  897. line-height: 22px;
  898. }
  899. h3 {
  900. font-size: 24px;
  901. font-weight: 500;
  902. line-height: 36px;
  903. color: #333;
  904. display: flex;
  905. justify-content: space-between;
  906. }
  907. }
  908. header {
  909. height: 74px;
  910. background: rgba(196, 196, 196, 0.5);
  911. border-radius: 4px;
  912. display: flex;
  913. align-items: center;
  914. padding: 0 20px;
  915. margin-top: 16px;
  916. ::v-deep .el-descriptions__body {
  917. background: transparent;
  918. }
  919. ::v-deep .my-label {
  920. font-size: 14px;
  921. font-weight: 600;
  922. color: #1d66dc;
  923. }
  924. ::v-deep .my-content {
  925. font-size: 14px;
  926. font-weight: 600;
  927. color: #333;
  928. white-space: nowrap;
  929. overflow: hidden;
  930. text-overflow: ellipsis;
  931. }
  932. }
  933. .el-tabs {
  934. height: calc(100% - 148px);
  935. display: flex;
  936. flex-direction: column;
  937. ::v-deep .el-tabs__content {
  938. flex: 1;
  939. .el-tab-pane {
  940. height: 100%;
  941. .el-descriptions {
  942. table-layout: fixed;
  943. .is-bordered {
  944. table-layout: fixed;
  945. }
  946. }
  947. }
  948. }
  949. }
  950. .buttons {
  951. padding-top: 28px;
  952. text-align: right;
  953. }
  954. .records {
  955. margin: 0;
  956. padding: 10px 20px;
  957. list-style: none;
  958. height: calc(100% - 60px);
  959. overflow-y: auto;
  960. > li {
  961. display: flex;
  962. & + li {
  963. margin-top: 10px;
  964. }
  965. }
  966. .date {
  967. width: 100px;
  968. display: flex;
  969. flex-direction: column;
  970. align-items: center;
  971. h2,
  972. h3 {
  973. margin: 0;
  974. }
  975. h2 {
  976. font-size: 26px;
  977. line-height: 32px;
  978. }
  979. }
  980. .content {
  981. flex: 1;
  982. list-style: none;
  983. li {
  984. display: flex;
  985. & + li {
  986. margin-top: 10px;
  987. }
  988. }
  989. .user-avatar {
  990. font-size: 40px;
  991. }
  992. .text {
  993. flex: 1;
  994. padding-left: 20px;
  995. p {
  996. font-weight: 500;
  997. margin: 0;
  998. line-height: 20px;
  999. span {
  1000. color: #1d66dc;
  1001. }
  1002. }
  1003. p:nth-child(2) {
  1004. margin-bottom: 10px;
  1005. }
  1006. .action {
  1007. font-weight: bold;
  1008. color: #333;
  1009. }
  1010. }
  1011. }
  1012. }
  1013. .follow {
  1014. height: 100%;
  1015. padding: 10px 20px;
  1016. overflow: auto;
  1017. > li {
  1018. display: flex;
  1019. + li {
  1020. margin-top: 10px;
  1021. }
  1022. }
  1023. .date {
  1024. width: 100px;
  1025. display: flex;
  1026. flex-direction: column;
  1027. align-items: center;
  1028. h2,
  1029. h3 {
  1030. margin: 0;
  1031. }
  1032. h2 {
  1033. font-size: 26px;
  1034. line-height: 32px;
  1035. }
  1036. }
  1037. .content {
  1038. flex: 1;
  1039. list-style: none;
  1040. > li {
  1041. border: 1px solid rgb(215, 232, 244);
  1042. background: rgb(247, 251, 254);
  1043. border-radius: 4px;
  1044. padding: 8px;
  1045. overflow: hidden;
  1046. .text-container {
  1047. display: flex;
  1048. }
  1049. .comments {
  1050. padding-left: 60px;
  1051. margin-top: 10px;
  1052. max-height: 190px;
  1053. overflow: auto;
  1054. li {
  1055. display: flex;
  1056. border-top: 1px solid #e3e5e7;
  1057. .text {
  1058. flex: 1;
  1059. padding: 0 10px;
  1060. p {
  1061. font-weight: 500;
  1062. margin: 0;
  1063. line-height: 32px;
  1064. }
  1065. p:first-child {
  1066. line-height: 30px;
  1067. font-weight: bold;
  1068. }
  1069. p:last-child {
  1070. font-size: 12px;
  1071. color: #9499a0;
  1072. text-align: right;
  1073. }
  1074. }
  1075. }
  1076. }
  1077. + li {
  1078. margin-top: 10px;
  1079. }
  1080. }
  1081. .user-avatar {
  1082. font-size: 40px;
  1083. }
  1084. .text {
  1085. flex: 1;
  1086. padding-left: 20px;
  1087. padding-right: 10px;
  1088. p {
  1089. font-weight: 500;
  1090. margin: 0;
  1091. line-height: 32px;
  1092. span {
  1093. color: #1d66dc;
  1094. }
  1095. }
  1096. .action {
  1097. display: flex;
  1098. justify-content: space-between;
  1099. span:first-child {
  1100. font-weight: bold;
  1101. color: #333;
  1102. }
  1103. }
  1104. .footer {
  1105. display: flex;
  1106. justify-content: space-between;
  1107. align-items: center;
  1108. }
  1109. }
  1110. }
  1111. }
  1112. .no-follow {
  1113. height: 100%;
  1114. width: 100%;
  1115. display: flex;
  1116. align-items: center;
  1117. justify-content: center;
  1118. font-size: 12px;
  1119. color: rgba(0, 0, 0, 0.65);
  1120. }
  1121. }
  1122. .height-enter-active,
  1123. .height-leave-active {
  1124. transition: all 0.5s;
  1125. }
  1126. .height-enter-to,
  1127. .height-leave {
  1128. height: 190px;
  1129. }
  1130. .height-enter, .height-leave-to /* .fade-leave-active below version 2.1.8 */ {
  1131. height: 0;
  1132. }
  1133. </style>