index.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587
  1. <template>
  2. <div class="business-container">
  3. <el-tabs v-model="activeName" @tab-click="handleTabClick">
  4. <el-tab-pane label="全部项目" name="all" />
  5. <el-tab-pane label="C类项目" name="30" />
  6. <el-tab-pane label="B类项目" name="20" />
  7. <el-tab-pane label="A类项目" name="10" />
  8. <el-tab-pane label="成交项目" name="40" />
  9. <el-tab-pane label="储备项目" name="50" />
  10. </el-tabs>
  11. <vab-query-form>
  12. <vab-query-form-top-panel>
  13. <el-form ref="queryForm" :inline="true" :model="queryForm" @submit.native.prevent>
  14. <!-- <el-form-item prop="nboName">-->
  15. <!-- <el-input v-model="queryForm.nboName" clearable placeholder="项目名称" @keyup.enter.native="queryData" />-->
  16. <!-- </el-form-item>-->
  17. <el-form-item prop="custName">
  18. <el-input
  19. v-model="queryForm.custName"
  20. clearable
  21. placeholder="客户名称"
  22. size="small"
  23. @keyup.enter.native="queryData" />
  24. </el-form-item>
  25. <el-form-item v-show="activeName === 'all'" prop="nboType">
  26. <el-select v-model="queryForm.nboType" clearable placeholder="项目类别">
  27. <el-option v-for="dict in nboTypeOptions" :key="dict.key" :label="dict.value" :value="dict.key" />
  28. </el-select>
  29. </el-form-item>
  30. <el-form-item prop="saleName">
  31. <el-input
  32. v-model="queryForm.saleName"
  33. clearable
  34. placeholder="销售工程师"
  35. size="small"
  36. @keyup.enter.native="queryData" />
  37. </el-form-item>
  38. <el-form-item prop="productLine">
  39. <el-select v-model="queryForm.productLine" clearable placeholder="产品线">
  40. <el-option v-for="item in productLineOptions" :key="item.key" :label="item.value" :value="item.key" />
  41. </el-select>
  42. </el-form-item>
  43. <el-form-item prop="nboSource">
  44. <el-select v-model="queryForm.nboSource" clearable placeholder="项目来源">
  45. <el-option v-for="dict in nboSourceOptions" :key="dict.key" :label="dict.value" :value="dict.key" />
  46. </el-select>
  47. </el-form-item>
  48. <el-form-item prop="distributorName">
  49. <el-input
  50. v-model="queryForm.distributorName"
  51. clearable
  52. placeholder="经销商/代理商"
  53. size="small"
  54. @keyup.enter.native="queryData" />
  55. </el-form-item>
  56. <el-form-item prop="filingTime">
  57. <el-date-picker
  58. v-model="queryForm.filingTime"
  59. end-placeholder="结束"
  60. start-placeholder="备案日期开始"
  61. type="daterange"
  62. value-format="yyyy-MM-dd" />
  63. </el-form-item>
  64. <el-form-item>
  65. <el-button icon="el-icon-search" type="primary" @click="queryData">查询</el-button>
  66. <el-button icon="el-icon-refresh" type="primary" @click="resetQuery">重置</el-button>
  67. </el-form-item>
  68. </el-form>
  69. </vab-query-form-top-panel>
  70. <vab-query-form-left-panel :span="12">
  71. <el-button v-permissions="['proj:business:add']" icon="el-icon-plus" type="primary" @click="handleEdit">
  72. 新增项目
  73. </el-button>
  74. <el-button
  75. v-permissions="['proj:business:transfer']"
  76. icon="el-icon-refresh"
  77. type="primary"
  78. @click="handleTransfer">
  79. 转移项目
  80. </el-button>
  81. <!-- <el-button icon="el-icon-plus" type="primary" @click="handleEdit">创建任务</el-button>-->
  82. <!-- <el-button icon="el-icon-plus" type="primary" @click="handleEdit">创建工单</el-button>-->
  83. <!-- <el-button icon="el-icon-plus" type="primary" @click="handleEdit">创建合同</el-button>-->
  84. <!-- <el-button icon="el-icon-delete" type="danger" @click="handleDelete">删除</el-button>-->
  85. </vab-query-form-left-panel>
  86. <vab-query-form-right-panel :span="12">
  87. <table-tool
  88. ref="tableTool"
  89. :columns.sync="finallyColumns"
  90. :show-columns.sync="showColumns"
  91. :table-type.sync="tableType" />
  92. </vab-query-form-right-panel>
  93. </vab-query-form>
  94. <el-table ref="busTable" v-loading="listLoading" :data="list" :height="height" @selection-change="setSelectRows">
  95. <el-table-column align="center" show-overflow-tooltip type="selection" />
  96. <el-table-column
  97. v-for="(item, index) in showColumns"
  98. :key="index + Math.random()"
  99. align="center"
  100. :label="item.label"
  101. :min-width="item.minWidth"
  102. :prop="item.prop"
  103. show-overflow-tooltip
  104. :sortable="item.sortable"
  105. :width="item.width">
  106. <template #default="{ row }">
  107. <el-button
  108. v-if="item.prop === 'nboCode' || item.prop === 'nboName'"
  109. class="link-button"
  110. type="text"
  111. @click="handleDetail(row)">
  112. {{ row[item.prop] }}
  113. </el-button>
  114. <span v-else-if="item.prop === 'nboType'">
  115. {{ selectDictLabel(nboTypeOptions, row.nboType) }}
  116. </span>
  117. <span v-else-if="item.prop === 'nboSource'">
  118. {{ selectDictLabel(nboSourceOptions, row.nboSource) }}
  119. </span>
  120. <span v-else-if="item.prop === 'salesModel'">
  121. {{ selectDictLabel(salesModelOptions, row.salesModel) }}
  122. </span>
  123. <span v-else-if="item.prop === 'approStatus'">
  124. {{ selectDictLabel(approStatusOptions, row.approStatus) }}
  125. </span>
  126. <span v-else-if="item.prop === 'nboStatus'">
  127. {{ selectDictLabel(nboStatusOptions, row.nboStatus) }}
  128. </span>
  129. <span v-else-if="item.prop === 'nboPhase'">
  130. {{ selectDictLabel(nboPhaseOptions, row.nboPhase) }}
  131. </span>
  132. <span v-else-if="item.prop === 'productLine'">
  133. {{ selectDictLabel(productLineOptions, row.productLine) }}
  134. </span>
  135. <span v-else-if="item.prop === 'isBig'">
  136. {{ selectDictLabel(yesOrNoOptions, row.isBig) }}
  137. </span>
  138. <span v-else-if="item.prop === 'estTransPrice'">
  139. {{ formatPrice(row.estTransPrice) }}
  140. </span>
  141. <span v-else-if="item.prop === 'nboBudget'">
  142. {{ formatPrice(row.nboBudget) }}
  143. </span>
  144. <span v-else-if="item.prop === 'contractAmount'">
  145. {{ formatPrice(row.contractAmount) }}
  146. </span>
  147. <span v-else-if="item.prop === 'filingTime'">
  148. {{ parseTime(row.filingTime, '{y}-{m}-{d}') }}
  149. </span>
  150. <span v-else-if="item.prop === 'finalFollowTime'">
  151. {{ parseTime(row.finalFollowTime, '{y}-{m}-{d}') }}
  152. </span>
  153. <span v-else-if="item.prop === 'nextFollowTime'">
  154. {{ parseTime(row.nextFollowTime, '{y}-{m}-{d}') }}
  155. </span>
  156. <span v-else-if="item.prop === 'projClosingTime'">
  157. {{ parseTime(row.projClosingTime, '{y}-{m}-{d}') }}
  158. </span>
  159. <span v-else-if="item.prop === 'projConversionTime'">
  160. {{ parseTime(row.projConversionTime, '{y}-{m}-{d}') }}
  161. </span>
  162. <span v-else>{{ row[item.prop] }}</span>
  163. </template>
  164. </el-table-column>
  165. <el-table-column align="center" fixed="right" label="操作" width="68">
  166. <template #default="{ row }">
  167. <!-- <el-button type="text" @click="handleFollow(row)">跟进</el-button>-->
  168. <el-button v-permissions="['proj:business:edit']" type="text" @click="handleEdit(row)">编辑</el-button>
  169. <!-- <el-button type="text" @click="handleDelete(row)">删除</el-button>-->
  170. </template>
  171. </el-table-column>
  172. </el-table>
  173. <el-pagination
  174. background
  175. :current-page="queryForm.pageNum"
  176. :layout="layout"
  177. :page-size="queryForm.pageSize"
  178. :total="total"
  179. @current-change="handleCurrentChange"
  180. @size-change="handleSizeChange" />
  181. <!-- 编辑 -->
  182. <edit ref="edit" @fetch-data="fetchData" />
  183. <!-- 转移 -->
  184. <transfer ref="transfer" @fetch-data="fetchData" />
  185. <!-- 添加跟进记录 -->
  186. <follow-add ref="follow" @fetch-data="fetchData" />
  187. </div>
  188. </template>
  189. <script>
  190. import businessApi from '@/api/proj/business'
  191. import Edit from './components/BusinessEdit'
  192. import Transfer from './components/Transfer'
  193. import FollowAdd from './components/FollowAdd'
  194. import TableTool from '@/components/table/TableTool'
  195. export default {
  196. name: 'Business',
  197. components: { Edit, Transfer, TableTool, FollowAdd },
  198. data() {
  199. return {
  200. activeName: '',
  201. height: this.$baseTableHeight(4),
  202. queryForm: {
  203. pageNum: 1,
  204. pageSize: 10,
  205. nboName: undefined,
  206. custName: undefined,
  207. nboType: undefined,
  208. saleName: undefined,
  209. productLine: undefined,
  210. nboSource: undefined,
  211. distributorName: undefined,
  212. filingTime: undefined,
  213. beginTime: undefined,
  214. endTime: undefined,
  215. },
  216. tableType: 'businessTable',
  217. showColumns: [],
  218. columns: [
  219. {
  220. label: '项目编码',
  221. prop: 'nboCode',
  222. width: '120px',
  223. minWidth: '120px',
  224. sortable: false,
  225. disableCheck: true,
  226. },
  227. {
  228. label: '项目名称',
  229. prop: 'nboName',
  230. width: '180px',
  231. minWidth: '180px',
  232. sortable: false,
  233. disableCheck: true,
  234. },
  235. {
  236. label: '所在省',
  237. prop: 'custProvince',
  238. width: '100px',
  239. },
  240. {
  241. label: '所在市',
  242. prop: 'custCity',
  243. width: '100px',
  244. },
  245. {
  246. label: '销售工程师',
  247. prop: 'saleName',
  248. width: '100px',
  249. },
  250. {
  251. label: '客户名称',
  252. prop: 'custName',
  253. width: 'auto',
  254. minWidth: '200px',
  255. },
  256. {
  257. label: '项目备案时间',
  258. prop: 'filingTime',
  259. width: '110px',
  260. },
  261. {
  262. label: '项目来源',
  263. prop: 'nboSource',
  264. width: '140px',
  265. },
  266. {
  267. label: '销售模式',
  268. prop: 'salesModel',
  269. width: '80px',
  270. },
  271. {
  272. label: '经销商/代理商',
  273. prop: 'distributorName',
  274. minWidth: '180px',
  275. width: 'auto',
  276. },
  277. {
  278. label: '产品线',
  279. prop: 'productLine',
  280. width: '140px',
  281. },
  282. {
  283. label: '项目类别',
  284. prop: 'nboType',
  285. width: '80px',
  286. },
  287. {
  288. label: '项目预算',
  289. prop: 'nboBudget',
  290. width: '120px',
  291. },
  292. {
  293. label: '出货金额',
  294. prop: 'estTransPrice',
  295. width: '120px',
  296. },
  297. {
  298. label: '大项目',
  299. prop: 'isBig',
  300. width: '70px',
  301. },
  302. {
  303. label: '最新跟进时间',
  304. prop: 'finalFollowTime',
  305. width: '110px',
  306. },
  307. {
  308. label: '最新跟进人',
  309. prop: 'finalFollowName',
  310. width: '50px',
  311. },
  312. {
  313. label: '项目成交时间',
  314. prop: 'projClosingTime',
  315. width: '110px',
  316. },
  317. {
  318. label: '合同金额',
  319. prop: 'contractAmount',
  320. width: '140px',
  321. },
  322. {
  323. label: '项目转化时间',
  324. prop: 'projConversionTime',
  325. width: '110px',
  326. },
  327. {
  328. label: '转化原因',
  329. prop: 'projConversionReason',
  330. width: '140px',
  331. },
  332. ],
  333. allColumns: [
  334. '项目编码',
  335. '销售工程师',
  336. '所在省',
  337. '所在市',
  338. '客户名称',
  339. '项目备案时间',
  340. '项目来源',
  341. '销售模式',
  342. '经销商/代理商',
  343. '产品线',
  344. '项目类别',
  345. '项目预算',
  346. '出货金额',
  347. '大项目',
  348. '最新跟进时间',
  349. ],
  350. abcColumns: [
  351. '项目编码',
  352. '所在省',
  353. '所在市',
  354. '销售工程师',
  355. '客户名称',
  356. '项目备案时间',
  357. '项目来源',
  358. '销售模式',
  359. '经销商/代理商',
  360. '产品线',
  361. '项目预算',
  362. '出货金额',
  363. '大项目',
  364. '最新跟进时间',
  365. '最新跟进',
  366. ],
  367. dealColumns: [
  368. '项目编码',
  369. '所在省',
  370. '所在市',
  371. '销售工程师',
  372. '客户名称',
  373. '项目备案时间',
  374. '项目来源',
  375. '销售模式',
  376. '经销商/代理商',
  377. '产品线',
  378. '大项目',
  379. '项目成交时间',
  380. '合同金额',
  381. ],
  382. reserveColumns: [
  383. '项目编码',
  384. '所在省',
  385. '所在市',
  386. '销售工程师',
  387. '客户名称',
  388. '项目备案时间',
  389. '项目来源',
  390. '销售模式',
  391. '经销商/代理商',
  392. '产品线',
  393. '项目转化时间',
  394. '转化原因',
  395. ],
  396. list: [],
  397. listLoading: true,
  398. layout: 'total, sizes, prev, pager, next, jumper',
  399. total: 0,
  400. selectRows: '',
  401. yesOrNoOptions: [],
  402. nboTypeOptions: [],
  403. nboSourceOptions: [],
  404. salesModelOptions: [],
  405. productLineOptions: [],
  406. nboPhaseOptions: [],
  407. nboStatusOptions: [],
  408. approStatusOptions: [],
  409. followup: {},
  410. finallyColumns: [],
  411. }
  412. },
  413. watch: {
  414. activeName(newValue) {
  415. this.tableType = 'businessTable' + this.selectDictLabel(this.nboTypeOptions, newValue)
  416. switch (newValue) {
  417. case '10':
  418. case '20':
  419. case '30':
  420. this.finallyColumns = this.columns.filter((item) => this.abcColumns.includes(item.label))
  421. break
  422. case '40':
  423. this.finallyColumns = this.columns.filter((item) => this.dealColumns.includes(item.label))
  424. break
  425. case '50':
  426. this.finallyColumns = this.columns.filter((item) => this.reserveColumns.includes(item.label))
  427. break
  428. default:
  429. this.finallyColumns = this.columns.filter((item) => this.allColumns.includes(item.label))
  430. }
  431. },
  432. },
  433. created() {
  434. this.activeName = 'all'
  435. if (this.$route.params && this.$route.params.length > 0) {
  436. this.queryForm = this.$route.params
  437. this.activeName = this.queryForm.activeName ? this.queryForm.activeName : 'all'
  438. }
  439. this.handleTabClick()
  440. this.getOptions()
  441. },
  442. updated() {
  443. if (this.$refs.busTable && this.$refs.busTable.doLayout) {
  444. this.$refs.busTable.doLayout()
  445. }
  446. },
  447. methods: {
  448. getOptions() {
  449. Promise.all([
  450. this.getDicts('proj_nbo_type'),
  451. this.getDicts('proj_nbo_source'),
  452. this.getDicts('proj_sales_model'),
  453. this.getDicts('sys_product_line'),
  454. this.getDicts('sys_yes_no'),
  455. // this.getDicts('proj_nbo_phase'),
  456. // this.getDicts('proj_nbo_status'),
  457. // this.getDicts('proj_appro_status'),
  458. ])
  459. .then(([nboType, nboSource, salesModel, productLine, yesOrNo]) => {
  460. this.nboTypeOptions = nboType.data.values || []
  461. this.nboSourceOptions = nboSource.data.values || []
  462. this.salesModelOptions = salesModel.data.values || []
  463. this.productLineOptions = productLine.data.values || []
  464. this.yesOrNoOptions = yesOrNo.data.values || []
  465. // this.nboPhaseOptions = nboPhase.data.values || []
  466. // this.nboStatusOptions = nboStatus.data.values || []
  467. // this.approStatusOptions = approStatus.data.values || []
  468. })
  469. .catch((err) => console.log(err))
  470. },
  471. handleTabClick() {
  472. this.queryForm.pageNum = 1
  473. if (this.activeName !== 'all') {
  474. this.queryForm.nboType = this.activeName
  475. } else {
  476. this.queryForm.nboType = undefined
  477. }
  478. this.$nextTick(() => {
  479. this.$refs['tableTool'].getInitData()
  480. })
  481. this.fetchData()
  482. },
  483. handleTransfer() {
  484. if (this.selectRows.length !== 1) {
  485. this.$baseMessage('请选择一个项目', 'warning')
  486. return
  487. }
  488. this.$refs['transfer'].open(this.selectRows[0])
  489. },
  490. // 跳转详情
  491. handleDetail(row) {
  492. this.queryForm.activeName = this.activeName
  493. this.$router.push({
  494. name: 'BusinessDetail',
  495. query: {
  496. id: row.id,
  497. },
  498. params: this.queryForm,
  499. })
  500. },
  501. handleFollow(row) {
  502. this.followup = {
  503. targetId: row.id,
  504. targetType: '20',
  505. targetName: row.nboName,
  506. custId: row.custId,
  507. custName: row.custName,
  508. contactsId: row.contactId,
  509. contactsName: row.contactName,
  510. }
  511. console.log(this.followup)
  512. this.$refs.follow.showEdit(this.followup)
  513. },
  514. handleEdit(row) {
  515. if (row.id) {
  516. this.$refs['edit'].showEdit(row)
  517. } else {
  518. this.$refs['edit'].showEdit()
  519. }
  520. },
  521. handleDelete(row) {
  522. if (row.id) {
  523. this.$baseConfirm('你确定要删除当前项吗', null, async () => {
  524. const { msg } = await businessApi.doDelete({ ids: [row.id] })
  525. this.$baseMessage(msg, 'success')
  526. await this.fetchData()
  527. })
  528. } else {
  529. if (this.selectRows.length > 0) {
  530. const ids = this.selectRows.map((item) => item.id)
  531. this.$baseConfirm('你确定要删除选中项吗', null, async () => {
  532. const { msg } = await businessApi.doDelete({ ids })
  533. this.$baseMessage(msg, 'success')
  534. await this.fetchData()
  535. })
  536. } else {
  537. this.$baseMessage('未选中任何行', 'error')
  538. return false
  539. }
  540. }
  541. },
  542. handleSizeChange(val) {
  543. this.queryForm.pageSize = val
  544. this.fetchData()
  545. },
  546. handleCurrentChange(val) {
  547. this.queryForm.pageNum = val
  548. this.fetchData()
  549. },
  550. queryData() {
  551. this.queryForm.pageNum = 1
  552. this.fetchData()
  553. },
  554. /** 重置按钮操作 */
  555. resetQuery() {
  556. this.resetForm('queryForm')
  557. this.handleTabClick()
  558. },
  559. setSelectRows(val) {
  560. this.selectRows = val
  561. },
  562. async fetchData() {
  563. if (this.queryForm.filingTime && this.queryForm.filingTime.length === 2) {
  564. this.queryForm.beginTime = this.queryForm.filingTime[0] + ' 00:00:00'
  565. this.queryForm.endTime = this.queryForm.filingTime[1] + ' 23:59:59'
  566. } else {
  567. this.queryForm.beginTime = undefined
  568. this.queryForm.endTime = undefined
  569. }
  570. this.listLoading = true
  571. const { data } = await businessApi.getList(this.queryForm)
  572. const { list, total } = data
  573. this.list = list
  574. this.total = total
  575. this.listLoading = false
  576. },
  577. },
  578. }
  579. </script>