plm前端
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1880 lines
63 KiB

2 years ago
1 year ago
2 years ago
2 years ago
1 year ago
2 years ago
2 years ago
1 year ago
2 years ago
1 year ago
2 years ago
2 years ago
3 months ago
2 years ago
2 years ago
2 years ago
2 years ago
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
1 year ago
1 year ago
2 years ago
2 years ago
2 years ago
5 months ago
2 years ago
1 year ago
2 years ago
1 year ago
2 years ago
1 year ago
2 years ago
7 months ago
7 months ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
1 year ago
2 years ago
2 years ago
1 year ago
2 years ago
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
1 year ago
2 years ago
3 months ago
2 years ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
2 years ago
2 years ago
2 years ago
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
3 months ago
2 years ago
3 months ago
2 years ago
2 years ago
2 years ago
2 years ago
12 months ago
7 months ago
2 years ago
12 months ago
7 months ago
2 years ago
12 months ago
2 years ago
12 months ago
2 years ago
12 months ago
2 years ago
12 months ago
2 years ago
2 years ago
12 months ago
2 years ago
12 months ago
2 years ago
2 years ago
12 months ago
2 years ago
1 year ago
2 years ago
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
1 year ago
2 years ago
1 year ago
2 years ago
1 year ago
1 year ago
2 years ago
  1. <script>
  2. import QuoteSearch from "./primary/quoteSearch.vue";
  3. import QuoteTable from "./primary/quoteTable.vue";
  4. import {
  5. againQuote,
  6. queryQuotePage, queryQuotePageByAnyField,
  7. queryQuoteRoleUser,
  8. removeQuote,
  9. saveQuote,
  10. updateQuote,
  11. updateQuoteStatus,
  12. quoteEditStatus
  13. } from "../../../api/quote/quote";
  14. import QuoteDetail from "./detail/quoteDetail.vue";
  15. import ChooseList from '@/views/modules/common/Chooselist';
  16. import {queryCustomer} from "../../../api/customer/customerInformation";
  17. import DictDataSelect from "../sys/dict-data-select.vue";
  18. import {queryProjectByCustomer} from "../../../api/project/project";
  19. import quotationCustomerContact from "../quotation/sellForQuotation/quotationCustomerContact.vue";
  20. import CustomerInfo from "../quotation/sellForQuotation/customerInfo.vue";
  21. import OssComponents from "../oss/ossComponents.vue";
  22. import quotationProjectInformation from "../quotation/sellForQuotation/quotationProjectInformation.vue";
  23. import {
  24. quotationInformationSearchByAnyField,
  25. searchQuotationByQuotationNo
  26. } from "../../../api/quotation/quotationInformation";
  27. import PriceCheckProperties from "../quotation/priceCheckProperties.vue";
  28. import ApprovalInformation from "../changeManagement/approvalInformation.vue";
  29. import {getPriceCheckPropertiesList} from "../../../api/quotation/priceCheckProperties";
  30. import {getApprovalList, getNodeAuthority, checkSuperAdmin} from "../../../api/changeManagement/changeManagement";
  31. import QuoteGroupDetail from "./detail/quoteGroupDetail.vue";
  32. import {submitChange} from "../../../api/quote/quote";
  33. import {queryQuoteDetail} from "../../../api/quote/quoteDetail";
  34. import FilterSearch from "../../common/filterSearch.vue";
  35. import {queryQuoteGroupDetail} from "../../../api/quote/quoteGroupDetail";
  36. import {checkZeroUnitPrice} from "../../../api/quote/quoteDetailBom";
  37. import {getNodeList} from "@/api/sampleManagement/technicalSpecificationList.js";
  38. export default {
  39. name: "quote",
  40. components: {
  41. FilterSearch,
  42. QuoteGroupDetail,
  43. ApprovalInformation, PriceCheckProperties,
  44. quotationProjectInformation, OssComponents, CustomerInfo, quotationCustomerContact,
  45. DictDataSelect,
  46. QuoteDetail,
  47. ChooseList,
  48. QuoteTable, QuoteSearch
  49. },
  50. props:{
  51. isMenu:{
  52. type:Boolean,
  53. default:true,
  54. },
  55. projectNo:{
  56. type:String,
  57. },
  58. height:{
  59. type:[Number,String],
  60. default:'35vh'
  61. }
  62. },
  63. menuId:5011,
  64. data(){
  65. return{
  66. exportLoading: false,
  67. total: 0,
  68. no:1,
  69. size: 20,
  70. quote:{
  71. id: null,
  72. site:"",
  73. quoteNo: "",
  74. customerInquiryNo: "",
  75. insideInquiryNo: "",
  76. buNo: "",
  77. buId: null,
  78. versionNo: "",
  79. status:'',
  80. quoteVersionNo: "",
  81. customerNo: "",
  82. customerDesc: "",
  83. projectNo: "",
  84. projectDesc: "",
  85. finalCustomerNo: "",
  86. finalCustomerDesc:"",
  87. currency: "",
  88. quoteDate: "",
  89. quoter: "",
  90. quoterName: "",
  91. purchase: "",
  92. purchaseName: "",
  93. remark: "",
  94. createBy: "",
  95. createDate: "",
  96. updateBy: "",
  97. updateDate: "",
  98. application: "",
  99. annualDemand: "",
  100. costModel: "UFIDA",
  101. markup:0,
  102. chipPrice:'',
  103. approvalUsername: '',
  104. nodeId: '',
  105. },
  106. quoteForm:{
  107. },
  108. nodeOptions: [],
  109. saveQuote:{
  110. },
  111. dataList:[],
  112. columns: [
  113. {
  114. userId: this.$store.state.user.name,
  115. functionId: 5011,
  116. serialNumber: '5011Table1QuoteVersionNo',
  117. tableId: '5011Table1',
  118. tableName: '报价信息表',
  119. columnProp: 'quoteVersionNo',
  120. headerAlign: 'center',
  121. align: 'center',
  122. columnLabel: '报价单号',
  123. columnHidden: false,
  124. columnImage: false,
  125. columnSortable: false,
  126. sortLv: 0,
  127. status: true,
  128. fixed: '',
  129. columnWidth: 180
  130. },
  131. {
  132. userId: this.$store.state.user.name,
  133. functionId: 5011,
  134. serialNumber: '5011Table1QuoteVersionNo',
  135. tableId: '5011Table1',
  136. tableName: '报价信息表',
  137. columnProp: 'buDesc',
  138. headerAlign: 'center',
  139. align: 'center',
  140. columnLabel: 'BU',
  141. columnHidden: false,
  142. columnImage: false,
  143. columnSortable: false,
  144. sortLv: 0,
  145. status: true,
  146. fixed: '',
  147. columnWidth: 80
  148. },
  149. {
  150. userId: this.$store.state.user.name,
  151. functionId: 5011,
  152. serialNumber: '5011Table1QuoteVersionNo',
  153. tableId: '5011Table1',
  154. tableName: '报价信息表',
  155. columnProp: 'inquiryCreateBy',
  156. headerAlign: 'center',
  157. align: 'center',
  158. columnLabel: '销售',
  159. columnHidden: false,
  160. columnImage: false,
  161. columnSortable: false,
  162. sortLv: 0,
  163. status: true,
  164. fixed: '',
  165. columnWidth: 80
  166. },
  167. {
  168. userId: this.$store.state.user.name,
  169. functionId: 5011,
  170. serialNumber: '5011Table1CustomerNo',
  171. tableId: '5011Table1',
  172. tableName: '报价信息表',
  173. columnProp: 'customerNo',
  174. headerAlign: 'center',
  175. align: 'center',
  176. columnLabel: '客户编码',
  177. columnHidden: false,
  178. columnImage: false,
  179. columnSortable: false,
  180. sortLv: 0,
  181. status: true,
  182. fixed: '',
  183. columnWidth: 120
  184. },
  185. {
  186. userId: this.$store.state.user.name,
  187. functionId: 5011,
  188. serialNumber: '5011Table1CustomerDesc',
  189. tableId: '5011Table1',
  190. tableName: '报价信息表',
  191. columnProp: 'customerDesc',
  192. headerAlign: 'center',
  193. align: 'left',
  194. columnLabel: '客户名称',
  195. columnHidden: false,
  196. columnImage: false,
  197. columnSortable: false,
  198. sortLv: 0,
  199. status: true,
  200. fixed: '',
  201. columnWidth: 120
  202. },
  203. {
  204. userId: this.$store.state.user.name,
  205. functionId: 5011,
  206. serialNumber: '5011Table1ProjectNo',
  207. tableId: '5011Table1',
  208. tableName: '报价信息表',
  209. columnProp: 'projectNo',
  210. headerAlign: 'center',
  211. align: 'left',
  212. columnLabel: '项目号',
  213. columnHidden: false,
  214. columnImage: false,
  215. columnSortable: false,
  216. sortLv: 0,
  217. status: true,
  218. fixed: '',
  219. columnWidth: 120
  220. },
  221. {
  222. userId: this.$store.state.user.name,
  223. functionId: 5011,
  224. serialNumber: '5011Table1ProjectDesc',
  225. tableId: '5011Table1',
  226. tableName: '报价信息表',
  227. columnProp: 'projectDesc',
  228. headerAlign: 'center',
  229. align: 'left',
  230. columnLabel: '项目名称',
  231. columnHidden: false,
  232. columnImage: false,
  233. columnSortable: false,
  234. sortLv: 0,
  235. status: true,
  236. fixed: '',
  237. columnWidth: 120
  238. },
  239. {
  240. userId: this.$store.state.user.name,
  241. functionId: 5011,
  242. serialNumber: '5011Table1QuoterName',
  243. tableId: '5011Table1',
  244. tableName: '报价信息表',
  245. columnProp: 'quoterName',
  246. headerAlign: 'center',
  247. align: 'left',
  248. columnLabel: '报价专员',
  249. columnHidden: false,
  250. columnImage: false,
  251. columnSortable: false,
  252. sortLv: 0,
  253. status: true,
  254. fixed: '',
  255. columnWidth: 120
  256. },
  257. {
  258. userId: this.$store.state.user.name,
  259. functionId: 5011,
  260. serialNumber: '5011Table1PurchaseName',
  261. tableId: '5011Table1',
  262. tableName: '报价信息表',
  263. columnProp: 'purchaseName',
  264. headerAlign: 'center',
  265. align: 'left',
  266. columnLabel: '采购专员',
  267. columnHidden: false,
  268. columnImage: false,
  269. columnSortable: false,
  270. sortLv: 0,
  271. status: true,
  272. fixed: '',
  273. columnWidth: 120
  274. },
  275. {
  276. userId: this.$store.state.user.name,
  277. functionId: 5011,
  278. serialNumber: '5011Table1Status',
  279. tableId: '5011Table1',
  280. tableName: '报价信息表',
  281. columnProp: 'status',
  282. headerAlign: 'center',
  283. align: 'center',
  284. columnLabel: '状态',
  285. columnHidden: false,
  286. columnImage: false,
  287. columnSortable: false,
  288. sortLv: 0,
  289. status: true,
  290. fixed: '',
  291. columnWidth: 120
  292. },
  293. {
  294. userId: this.$store.state.user.name,
  295. functionId: 5011,
  296. serialNumber: '5011Table1NodeName',
  297. tableId: '5011Table1',
  298. tableName: '报价信息表',
  299. columnProp: 'nodeName',
  300. headerAlign: 'center',
  301. align: 'center',
  302. columnLabel: '当前审批节点',
  303. columnHidden: false,
  304. columnImage: false,
  305. columnSortable: false,
  306. sortLv: 0,
  307. status: true,
  308. fixed: '',
  309. columnWidth: 120
  310. },
  311. {
  312. userId: this.$store.state.user.name,
  313. functionId: 5011,
  314. serialNumber: '5011Table1ApprovalUsername',
  315. tableId: '5011Table1',
  316. tableName: '报价信息表',
  317. columnProp: 'approvalUsername',
  318. headerAlign: 'center',
  319. align: 'center',
  320. columnLabel: '当前审批人',
  321. columnHidden: false,
  322. columnImage: false,
  323. columnSortable: false,
  324. sortLv: 0,
  325. status: true,
  326. fixed: '',
  327. columnWidth: 120
  328. },
  329. {
  330. userId: this.$store.state.user.name,
  331. functionId: 5011,
  332. serialNumber: '5011Table1QuoteDate',
  333. tableId: '5011Table1',
  334. tableName: '报价信息表',
  335. columnProp: 'quoteDate',
  336. headerAlign: 'center',
  337. align: 'center',
  338. columnLabel: '报价日期',
  339. columnHidden: false,
  340. columnImage: false,
  341. columnSortable: false,
  342. sortLv: 0,
  343. status: true,
  344. fixed: '',
  345. columnWidth: 100
  346. },
  347. {
  348. userId: this.$store.state.user.name,
  349. functionId: 5011,
  350. serialNumber: '5011Table1CustomerInquiryNo',
  351. tableId: '5011Table1',
  352. tableName: '报价信息表',
  353. columnProp: 'customerInquiryNo',
  354. headerAlign: 'center',
  355. align: 'center',
  356. columnLabel: '客户询价单号',
  357. columnHidden: false,
  358. columnImage: false,
  359. columnSortable: false,
  360. sortLv: 0,
  361. status: true,
  362. fixed: '',
  363. columnWidth: 120
  364. },
  365. {
  366. userId: this.$store.state.user.name,
  367. functionId: 5011,
  368. serialNumber: '5011Table1InsideInquiryNo',
  369. tableId: '5011Table1',
  370. tableName: '报价信息表',
  371. columnProp: 'insideInquiryNo',
  372. headerAlign: 'center',
  373. align: 'center',
  374. columnLabel: '内部询价单号',
  375. columnHidden: false,
  376. columnImage: false,
  377. columnSortable: false,
  378. sortLv: 0,
  379. status: true,
  380. fixed: '',
  381. columnWidth: 120
  382. },
  383. {
  384. userId: this.$store.state.user.name,
  385. functionId: 5011,
  386. serialNumber: '5011Table1PlmPartNo',
  387. tableId: '5011Table1',
  388. tableName: '报价信息表',
  389. columnProp: 'plmPartNo',
  390. headerAlign: 'center',
  391. align: 'left',
  392. columnLabel: 'PLM物料编码',
  393. columnHidden: false,
  394. columnImage: false,
  395. columnSortable: false,
  396. sortLv: 0,
  397. status: true,
  398. fixed: '',
  399. columnWidth: 120
  400. },
  401. {
  402. userId: this.$store.state.user.name,
  403. functionId: 5011,
  404. serialNumber: '5011Table1IfsPartNo',
  405. tableId: '5011Table1',
  406. tableName: '报价信息表',
  407. columnProp: 'ifsPartNo',
  408. headerAlign: 'center',
  409. align: 'left',
  410. columnLabel: 'IFS物料编码',
  411. columnHidden: false,
  412. columnImage: false,
  413. columnSortable: false,
  414. sortLv: 0,
  415. status: true,
  416. fixed: '',
  417. columnWidth: 120
  418. },
  419. {
  420. userId: this.$store.state.user.name,
  421. functionId: 5011,
  422. serialNumber: '5011Table1PartDesc',
  423. tableId: '5011Table1',
  424. tableName: '报价信息表',
  425. columnProp: 'partDesc',
  426. headerAlign: 'center',
  427. align: 'left',
  428. columnLabel: '物料名称',
  429. columnHidden: false,
  430. columnImage: false,
  431. columnSortable: false,
  432. sortLv: 0,
  433. status: true,
  434. fixed: '',
  435. columnWidth: 200
  436. },
  437. {
  438. userId: this.$store.state.user.name,
  439. functionId: 5011,
  440. serialNumber: '5011Table1CreateBy',
  441. tableId: '5011Table1',
  442. tableName: '报价信息表',
  443. columnProp: 'createBy',
  444. headerAlign: 'center',
  445. align: 'center',
  446. columnLabel: '创建人',
  447. columnHidden: false,
  448. columnImage: false,
  449. columnSortable: false,
  450. sortLv: 0,
  451. status: true,
  452. fixed: '',
  453. columnWidth: 120
  454. },
  455. {
  456. userId: this.$store.state.user.name,
  457. functionId: 5011,
  458. serialNumber: '5011Table1CreateDate',
  459. tableId: '5011Table1',
  460. tableName: '报价信息表',
  461. columnProp: 'createDate',
  462. headerAlign: 'center',
  463. align: 'center',
  464. columnLabel: '创建时间',
  465. columnHidden: false,
  466. columnImage: false,
  467. columnSortable: false,
  468. sortLv: 0,
  469. status: true,
  470. fixed: '',
  471. columnWidth: 130
  472. },
  473. {
  474. userId: this.$store.state.user.name,
  475. functionId: 5011,
  476. serialNumber: '5011Table1UpdateBy',
  477. tableId: '5011Table1',
  478. tableName: '报价信息表',
  479. columnProp: 'updateBy',
  480. headerAlign: 'center',
  481. align: 'center',
  482. columnLabel: '更新人',
  483. columnHidden: false,
  484. columnImage: false,
  485. columnSortable: false,
  486. sortLv: 0,
  487. status: true,
  488. fixed: '',
  489. columnWidth: 120
  490. },
  491. {
  492. userId: this.$store.state.user.name,
  493. functionId: 5011,
  494. serialNumber: '5011Table1UpdateDate',
  495. tableId: '5011Table1',
  496. tableName: '报价信息表',
  497. columnProp: 'updateDate',
  498. headerAlign: 'center',
  499. align: 'center',
  500. columnLabel: '更新时间',
  501. columnHidden: false,
  502. columnImage: false,
  503. columnSortable: false,
  504. sortLv: 0,
  505. status: true,
  506. fixed: '',
  507. columnWidth: 130
  508. },
  509. ],
  510. searchLoading: false,
  511. saveVisible: false,
  512. saveLoading: false,
  513. activeName:'detail2',
  514. // OA审批相关
  515. superAdmin: false,
  516. rejectVisible: false,
  517. rejectOpinion: '',
  518. plmQuoteArr: [], // plm_quote表字段配置(如annual_sales)
  519. plmQuoteDetailArr: [],
  520. defaultQuoteDetailRow: null, // 默认报价行数据
  521. userBuList: [],
  522. saveRules: {
  523. buId:[{required: true, message: '请选择BU', trigger: ['blur','change']}],
  524. customerNo:[{required: true, message: '请输入客户编码', trigger: ['blur','change']}],
  525. customerDesc:[{required: true, message: '请输入客户名称', trigger: ['blur','change']}],
  526. projectNo:[{required: true, message: '请输入项目编码', trigger: ['blur','change']}],
  527. projectDesc:[{required: true, message: '请输入项目名称', trigger: ['blur','change']}],
  528. // currency:[{required: true, message: '请输入币种', trigger: ['blur','change']}],
  529. quoteDate:[{required: true, message: '请选择报价日期', trigger: ['blur','change']}],
  530. quoter:[{required: true, message: '请输入报价专员', trigger: ['blur','change']}],
  531. },
  532. customerVisible:false,
  533. projectVisible:false,
  534. quoterVisible:false,
  535. purchaseVisible:false,
  536. filterVisible:false,
  537. isFilterSearch: false,
  538. filterSearchData: {},
  539. currentQuote:{
  540. },
  541. userVisible:false,
  542. userTitle:'人员信息',
  543. user:{
  544. username:'',
  545. userDisplay:'',
  546. active:'',
  547. buId:null,
  548. roleNo:'',
  549. },
  550. userDataList:[],
  551. tagNo:undefined,
  552. insideInquiryVisible:false,
  553. detailFlag:false,
  554. ossColumns:[
  555. {
  556. userId: this.$store.state.user.name,
  557. functionId: 5011,
  558. serialNumber: '5011Table2FileName',
  559. tableId: '5011Table2',
  560. tableName: '文件信息表',
  561. columnProp: 'fileName',
  562. headerAlign: 'center',
  563. align: 'left',
  564. columnLabel: '文件名称',
  565. columnHidden: false,
  566. columnImage: false,
  567. columnSortable: false,
  568. sortLv: 0,
  569. status: true,
  570. fixed: '',
  571. columnWidth: 140
  572. },
  573. {
  574. userId: this.$store.state.user.name,
  575. functionId: 5011,
  576. serialNumber: '5011Table2FileRemark',
  577. tableId: '5011Table2',
  578. tableName: '文件信息表',
  579. columnProp: 'fileRemark',
  580. headerAlign: 'center',
  581. align: 'left',
  582. columnLabel: '备注',
  583. columnHidden: false,
  584. columnImage: false,
  585. columnSortable: false,
  586. sortLv: 0,
  587. status: true,
  588. fixed: '',
  589. columnWidth: 240
  590. },
  591. {
  592. userId: this.$store.state.user.name,
  593. functionId: 5011,
  594. serialNumber: '5011Table2CreateDate',
  595. tableId: '5011Table2',
  596. tableName: '文件信息表',
  597. columnProp: 'createDate',
  598. headerAlign: 'center',
  599. align: 'center',
  600. columnLabel: '上传时间',
  601. columnHidden: false,
  602. columnImage: false,
  603. columnSortable: false,
  604. sortLv: 0,
  605. status: true,
  606. fixed: '',
  607. columnWidth: 140
  608. },
  609. {
  610. userId: this.$store.state.user.name,
  611. functionId: 5011,
  612. serialNumber: '5011Table2CreatedBy',
  613. tableId: '5011Table2',
  614. tableName: '文件信息表',
  615. columnProp: 'createBy',
  616. headerAlign: 'center',
  617. align: 'center',
  618. columnLabel: '上传人',
  619. columnHidden: false,
  620. columnImage: false,
  621. columnSortable: false,
  622. sortLv: 0,
  623. status: true,
  624. fixed: '',
  625. columnWidth: 140
  626. }
  627. ],
  628. insideInquiry:{
  629. },
  630. insideInquiryList:[],
  631. priceCheckPropertiesList:[],
  632. approvalList:[],
  633. exportData: [],
  634. exportName: '项目报价'+this.dayjs().format('YYYYMMDDHHmmss'),
  635. exportHeader: ["项目报价"],
  636. exportFooter: [],
  637. searchIndex:0,
  638. // OA审批相关
  639. submitLoading: false,
  640. }
  641. },
  642. methods:{
  643. fetchNodeOptions() {
  644. getNodeList({
  645. site: this.$store.state.user.site,
  646. menuId: this.$route.meta.menuId
  647. }).then(({ data }) => {
  648. if (data && data.code === 0) {
  649. this.nodeOptions = data.rows.map(item => ({
  650. id: item.nodeId,
  651. name: item.nodeName
  652. }))
  653. }
  654. }).catch(() => {})
  655. },
  656. handleSearch(index){
  657. let params = {
  658. ...this.quoteForm,
  659. no: this.no,
  660. size: this.size,
  661. }
  662. // 查询前先清空当前选中的报价,确保子组件能正确响应数据变化
  663. this.currentQuote = {}
  664. this.searchLoading = true
  665. queryQuotePage(params).then(({data})=>{
  666. if (data && data.code === 0){
  667. this.dataList = data.rows
  668. this.total = data.total
  669. // ❌ 性能优化:注释掉N+1查询问题(每个报价都会发起一次额外查询)
  670. // 这会导致:如果有20条报价,就会执行1+20=21次数据库查询
  671. // 解决方案:如果需要显示明细,应该在SQL中一次性JOIN查询,而不是循环查询
  672. // this.loadQuoteDetailInfo()
  673. if (this.dataList[0] !== undefined) {
  674. this.handleSelect(this.dataList[0])
  675. } else {
  676. this.currentQuote = {}
  677. }
  678. // if (index !== undefined){
  679. // this.handleSelect(this.dataList[index])
  680. // }
  681. }else {
  682. this.$message.error(data.msg)
  683. }
  684. this.searchLoading = false
  685. }).catch(error=>{
  686. this.$message.error(error)
  687. this.searchLoading = false
  688. })
  689. this.isFilterSearch = false
  690. },
  691. loadQuoteDetailInfo(){
  692. // 批量加载每个报价的首条明细数据
  693. this.dataList.forEach(quote => {
  694. queryQuoteGroupDetail({quoteId: quote.id}).then(({data}) => {
  695. if (data && data.code === 0 && data.rows && data.rows.length > 0) {
  696. const firstDetail = data.rows[0]
  697. // 使用$set确保响应式更新
  698. this.$set(quote, 'plmPartNo', firstDetail.plmPartNo)
  699. this.$set(quote, 'ifsPartNo', firstDetail.ifsPartNo)
  700. this.$set(quote, 'partDesc', firstDetail.partDesc)
  701. } else {
  702. // 如果没有明细数据,清空这三个字段
  703. this.$set(quote, 'plmPartNo', '')
  704. this.$set(quote, 'ifsPartNo', '')
  705. this.$set(quote, 'partDesc', '')
  706. }
  707. }).catch(() => {
  708. // 忽略错误,只是显示为空
  709. })
  710. })
  711. },
  712. // 刷新指定报价的首条明细信息
  713. handleRefreshQuoteInfo(quoteId){
  714. console.log('刷新报价明细信息, quoteId:', quoteId)
  715. // 直接重新查询当前页数据,这样可以确保数据同步
  716. this.handleSearch()
  717. },
  718. // ========== OA审批相关方法 ==========
  719. // 校验是否为超级管理员
  720. checkSuperAdmin() {
  721. checkSuperAdmin().then(({data}) => {
  722. this.superAdmin = data.superAdmin
  723. })
  724. },
  725. // 判断当前用户是否是审批人
  726. isApprovalUser() {
  727. if (this.superAdmin) return true
  728. if (!this.currentQuote.approvalUsername) return false
  729. return this.currentQuote.approvalUsername.split(';').includes(this.$store.state.user.name)
  730. },
  731. // 获取流程的配置权限
  732. async getNodeAuthority() {
  733. let tempData = {
  734. site: this.currentQuote.site,
  735. stepId: this.currentQuote.stepId,
  736. menuId: this.$route.meta.menuId
  737. }
  738. await getNodeAuthority(tempData).then(({data}) => {
  739. if (data && data.code === 0) {
  740. this.plmQuoteArr = data.rows.plm_quote || []
  741. this.plmQuoteDetailArr = data.rows.plm_quote_detail || []
  742. }
  743. })
  744. },
  745. // 获取默认报价行的条目明细数据
  746. async getDefaultQuoteDetailRow() {
  747. // 1. 先获取第一个报价明细行(QuoteGroupDetail)
  748. let groupParams = {
  749. quoteId: this.currentQuote.id,
  750. site: this.currentQuote.site,
  751. }
  752. const groupRes = await queryQuoteGroupDetail(groupParams)
  753. if (!groupRes.data || groupRes.data.code !== 0 || !groupRes.data.rows || groupRes.data.rows.length === 0) {
  754. this.defaultQuoteDetailRow = null
  755. return
  756. }
  757. const firstGroupDetail = groupRes.data.rows[0]
  758. // 2. 根据 quoteGroupDetailId 和 currentQuoteDetailItemNo 获取对应的条目明细
  759. let detailParams = {
  760. quoteGroupDetailId: firstGroupDetail.id,
  761. site: this.currentQuote.site,
  762. }
  763. const detailRes = await queryQuoteDetail(detailParams)
  764. if (!detailRes.data || detailRes.data.code !== 0 || !detailRes.data.rows || detailRes.data.rows.length === 0) {
  765. this.defaultQuoteDetailRow = null
  766. return
  767. }
  768. // 3. 如果设置了 currentQuoteDetailItemNo,则按 itemNo 匹配,否则取第一条
  769. const currentItemNo = firstGroupDetail.currentQuoteDetailItemNo
  770. if (currentItemNo) {
  771. this.defaultQuoteDetailRow = detailRes.data.rows.find(row => row.itemNo === currentItemNo) || detailRes.data.rows[0]
  772. } else {
  773. this.defaultQuoteDetailRow = detailRes.data.rows[0]
  774. }
  775. },
  776. // 同意提交
  777. agreeSubmit() {
  778. // 校验报价主表的必填字段
  779. if (this.plmQuoteArr && this.plmQuoteArr.length > 0) {
  780. for (let i = 0; i < this.plmQuoteArr.length; i++) {
  781. const field = this.plmQuoteArr[i]
  782. if (field.required === 'Y') {
  783. const fieldValue = this.saveQuote[field.fieldId]
  784. // 统一判断是否有值(包括勾选字段)
  785. if (!fieldValue && fieldValue !== 0) {
  786. this.$message.warning(field.fieldName + '不能为空!')
  787. return
  788. }
  789. }
  790. }
  791. }
  792. // 校验默认报价行的必填字段
  793. if (this.plmQuoteDetailArr && this.plmQuoteDetailArr.length > 0 && this.defaultQuoteDetailRow) {
  794. for (let i = 0; i < this.plmQuoteDetailArr.length; i++) {
  795. const field = this.plmQuoteDetailArr[i]
  796. if (field.required === 'Y' && !this.defaultQuoteDetailRow[field.fieldId] && this.defaultQuoteDetailRow[field.fieldId] !== 0) {
  797. this.$message.warning(field.fieldName + '不能为空!')
  798. return
  799. }
  800. }
  801. }
  802. this.$confirm('是否确认提交?', '提示', {
  803. confirmButtonText: '确定',
  804. cancelButtonText: '取消',
  805. type: 'warning'
  806. }).then(() => {
  807. this.submitLoading = true
  808. // 提交审批流程(后端会在事务中先更新主表,再提交OA)
  809. let params = {
  810. ...this.currentQuote,
  811. annualSales: this.saveQuote.annualSales, // 传递annualSales
  812. userName: this.$store.state.user.name,
  813. nodeConclusion: 'Y',
  814. menuId: this.$route.meta.menuId
  815. }
  816. submitChange(params).then(({data}) => {
  817. if (data && data.code === 0) {
  818. this.$message({message: '操作成功', type: 'success'})
  819. this.saveVisible = false
  820. this.handleSearch()
  821. } else {
  822. this.$alert(data.msg, '错误', { confirmButtonText: '确定' })
  823. }
  824. this.submitLoading = false
  825. }).catch((error) => {
  826. this.$message.error(error)
  827. this.submitLoading = false
  828. })
  829. })
  830. },
  831. // 驳回提交
  832. rejectSubmit() {
  833. // 校验报价主表的必填字段
  834. if (this.plmQuoteArr && this.plmQuoteArr.length > 0) {
  835. for (let i = 0; i < this.plmQuoteArr.length; i++) {
  836. const field = this.plmQuoteArr[i]
  837. if (field.required === 'Y') {
  838. const fieldValue = this.saveQuote[field.fieldId]
  839. // 统一判断是否有值(包括勾选字段)
  840. if (!fieldValue && fieldValue !== 0) {
  841. this.$message.warning(field.fieldName + '不能为空!')
  842. return
  843. }
  844. }
  845. }
  846. }
  847. // 校验默认报价行的必填字段
  848. if (this.plmQuoteDetailArr && this.plmQuoteDetailArr.length > 0 && this.defaultQuoteDetailRow) {
  849. for (let i = 0; i < this.plmQuoteDetailArr.length; i++) {
  850. const field = this.plmQuoteDetailArr[i]
  851. if (field.required === 'Y' && !this.defaultQuoteDetailRow[field.fieldId] && this.defaultQuoteDetailRow[field.fieldId] !== 0) {
  852. this.$message.warning('默认报价行的' + field.fieldName + '不能为空!')
  853. return
  854. }
  855. }
  856. }
  857. this.$confirm('是否确认驳回?', '提示', {
  858. confirmButtonText: '确定',
  859. cancelButtonText: '取消',
  860. type: 'warning'
  861. }).then(() => {
  862. this.submitLoading = true
  863. // 提交驳回流程(后端会在事务中先更新主表,再提交OA)
  864. let params = {
  865. ...this.currentQuote,
  866. annualSales: this.saveQuote.annualSales, // 传递annualSales
  867. userName: this.$store.state.user.name,
  868. nodeConclusion: 'N',
  869. rejectOpinion: this.rejectOpinion,
  870. menuId: this.$route.meta.menuId
  871. }
  872. submitChange(params).then(({data}) => {
  873. if (data && data.code === 0) {
  874. this.$message({message: '操作成功', type: 'success'})
  875. this.rejectVisible = false
  876. this.saveVisible = false
  877. this.handleSearch()
  878. } else {
  879. this.$alert(data.msg, '错误', { confirmButtonText: '确定' })
  880. }
  881. this.submitLoading = false
  882. }).catch((error) => {
  883. this.$message.error(error)
  884. this.submitLoading = false
  885. })
  886. })
  887. },
  888. // 关闭驳回表单
  889. closeRejectForm() {
  890. this.rejectOpinion = ''
  891. this.rejectVisible = false
  892. },
  893. // ========== OA审批相关方法结束 ==========
  894. handleSelect(row){
  895. if (row){
  896. this.currentQuote = {...row}
  897. } else {
  898. this.currentQuote = {}
  899. }
  900. },
  901. async handleSave(row){
  902. this.$nextTick(()=>{
  903. if (this.$refs.saveForm){
  904. this.$refs.saveForm.clearValidate();
  905. }
  906. })
  907. if (row){
  908. // 先更新 currentQuote,确保 getNodeAuthority 能获取到正确的 stepId
  909. this.currentQuote = {...row}
  910. this.saveQuote = {
  911. ...row
  912. }
  913. // 如果是审批中状态,获取权限配置和默认报价行数据
  914. if (row.status === '审批中') {
  915. await this.getNodeAuthority()
  916. await this.getDefaultQuoteDetailRow()
  917. }
  918. }else {
  919. this.saveQuote = {
  920. ...this.quote,
  921. buNo:'*',
  922. site:this.$store.state.user.site
  923. }
  924. this.$nextTick(()=>{
  925. this.saveQuote.buId = this.userBuList.length > 0? this.userBuList[0].id:null;
  926. })
  927. }
  928. this.saveVisible = true
  929. },
  930. handleSizeChange(size){
  931. this.size = size
  932. if ( this.isFilterSearch === false){
  933. this.handleSearch()
  934. } else {
  935. this.searchByAnyField(this.filterSearchData)
  936. }
  937. },
  938. handlePageChange(no) {
  939. this.no = no
  940. if ( this.isFilterSearch === false){
  941. this.handleSearch()
  942. } else {
  943. this.searchByAnyField(this.filterSearchData)
  944. }
  945. },
  946. handleQueryBu(){
  947. let params = {
  948. username: this.$store.state.user.name,
  949. }
  950. // getSiteAndBuByUserName(params).then(({data}) => {
  951. // if (data && data.code === 0) {
  952. // this.userBuList = data.rows
  953. // }else {
  954. // this.$message.warning(data.msg)
  955. // }
  956. // }).catch((error)=>{
  957. // this.$message.error(error)
  958. // })
  959. },
  960. handleSaveOrUpdateQuote(){
  961. this.$refs.saveForm.validate((valid,obj) => {
  962. if (valid){
  963. if (this.saveQuote.id){
  964. this.handleUpdateQuote();
  965. }else {
  966. this.handleSaveQuote();
  967. }
  968. }else {
  969. let i = 1;
  970. for (let key in obj){
  971. this.$message.warning(obj[key][0].message)
  972. if (i === 1){
  973. return
  974. }
  975. i++;
  976. }
  977. }
  978. })
  979. },
  980. handleSaveQuote(){
  981. let params = {
  982. ...this.saveQuote,
  983. status: '草稿',
  984. action: 'Y',
  985. createBy: this.$store.state.user.name,
  986. }
  987. this.saveLoading = true
  988. saveQuote(params).then(({data})=>{
  989. if (data && data.code === 0){
  990. this.saveVisible = false
  991. this.quoteForm.quoteVersionNo = data.quoteVersionNo
  992. this.$message.success(data.msg)
  993. }else {
  994. this.$message.warning(data.msg)
  995. }
  996. this.handleSearch(0);
  997. this.saveLoading = false
  998. }).catch((error)=>{
  999. this.$message.error(error)
  1000. this.saveLoading = false
  1001. })
  1002. },
  1003. handleUpdateQuote(){
  1004. let params = {
  1005. ...this.saveQuote,
  1006. updateBy: this.$store.state.user.name,
  1007. }
  1008. this.saveLoading = true
  1009. updateQuote(params).then(({data})=>{
  1010. if (data && data.code === 0){
  1011. this.saveVisible = false
  1012. this.currentQuote = {
  1013. ...this.currentQuote
  1014. }
  1015. this.$message.success(data.msg)
  1016. }else {
  1017. this.$message.warning(data.msg)
  1018. }
  1019. this.handleSearch();
  1020. this.saveLoading = false
  1021. }).catch((error)=>{
  1022. this.$message.error(error)
  1023. this.saveLoading = false
  1024. })
  1025. },
  1026. handleRemove(row){
  1027. this.$confirm('确认删除该报价信息吗?', '提示', {
  1028. confirmButtonText: '确定',
  1029. cancelButtonText: '取消',
  1030. type: 'warning'
  1031. }).then(() => {
  1032. this.handleRemoveQuote(row)
  1033. }).catch(() => {
  1034. })
  1035. },
  1036. handleUpdateStatus(row){
  1037. let params = {
  1038. ...row
  1039. }
  1040. updateQuoteStatus(params).then(({data})=>{
  1041. if (data && data.code === 0){
  1042. this.$message.success(data.msg)
  1043. this.handleSearch();
  1044. this.currentQuote = {
  1045. ...row
  1046. }
  1047. }else {
  1048. this.$message.warning(data.msg)
  1049. }
  1050. }).catch((error)=>{
  1051. this.$message.error(error)
  1052. })
  1053. },
  1054. handleQueryById(row){
  1055. this.saveQuote = {
  1056. ...row
  1057. }
  1058. this.detailFlag = true;
  1059. this.saveVisible = true;
  1060. },
  1061. handleRemoveQuote(row){
  1062. let params = {
  1063. id: row.id,
  1064. }
  1065. removeQuote(params).then(({data})=>{
  1066. if (data && data.code === 0){
  1067. this.$message.success(data.msg)
  1068. if (row.id === this.currentQuote.id){
  1069. this.handleSearch(0);
  1070. }else {
  1071. this.handleSearch();
  1072. }
  1073. }else {
  1074. this.$message.warning(data.msg)
  1075. }
  1076. }).catch((error)=>{
  1077. this.$message.error(error)
  1078. })
  1079. },
  1080. customerNoBlur(){
  1081. let params = {
  1082. site:this.$store.state.user.site,
  1083. customerNo:this.saveQuote.customerNo
  1084. }
  1085. queryCustomer(params).then(({data})=>{
  1086. if (data && data.code === 0 ) {
  1087. if (data.rows && data.rows.length === 1){
  1088. this.saveQuote.customerDesc = data.rows[0].customerDesc
  1089. }else {
  1090. this.saveQuote.customerDesc = ''
  1091. }
  1092. }else {
  1093. this.$message.warning(data.msg)
  1094. }
  1095. }).catch((error)=>{
  1096. this.$message.error(error)
  1097. })
  1098. },
  1099. projectNoBlur(){
  1100. let params = {
  1101. site:this.$store.state.user.site,
  1102. customerId:this.saveQuote.customerNo,
  1103. projectId:this.saveQuote.projectNo
  1104. }
  1105. queryProjectByCustomer(params).then(({data})=>{
  1106. if (data && data.code === 0 ){
  1107. if (data.rows && data.rows.length === 1){
  1108. this.saveQuote.projectDesc = data.rows[0].projectName
  1109. this.saveQuote.finalCustomerNo = data.rows[0].finalCustomerId
  1110. this.saveQuote.finalCustomerDesc = data.rows[0].finalCustomerName
  1111. }else {
  1112. this.saveQuote.projectDesc = ''
  1113. this.saveQuote.finalCustomerNo = ''
  1114. this.saveQuote.finalCustomerDesc = ''
  1115. }
  1116. }else {
  1117. this.$message.warning(data.msg)
  1118. }
  1119. }).catch((error)=>{
  1120. this.$message.error(error)
  1121. })
  1122. },
  1123. handleRowClick(row){
  1124. this.currentQuote = {...row}
  1125. },
  1126. userDblClick(row){
  1127. if (this.quoterVisible){
  1128. this.saveQuote.quoter = row.username
  1129. this.quoterVisible = false
  1130. }else if (this.purchaseVisible){
  1131. this.saveQuote.purchase = row.username
  1132. this.purchaseVisible = false
  1133. }
  1134. this.userVisible = false
  1135. },
  1136. handleQuoterClick(){
  1137. this.userTitle = '报价专员'
  1138. this.user.username = this.saveQuote.quoter
  1139. this.user.userDisplay = ''
  1140. this.user.active = ''
  1141. this.user.buId = this.saveQuote.buId
  1142. this.user.roleNo = 'QUOTER'
  1143. this.quoterVisible = true
  1144. this.handleQueryQuoteRoleUser();
  1145. this.userVisible = true
  1146. },
  1147. handlePurchaseClick(){
  1148. this.userTitle = '采购专员'
  1149. this.user.username = this.saveQuote.purchase
  1150. this.user.userDisplay = ''
  1151. this.user.active = ''
  1152. this.user.buId = this.saveQuote.buId
  1153. this.user.roleNo = 'PURCHASE'
  1154. this.purchaseVisible = true
  1155. this.handleQueryQuoteRoleUser();
  1156. this.userVisible = true
  1157. },
  1158. handleQueryQuoteRoleUser(){
  1159. let params = {
  1160. ...this.user,
  1161. }
  1162. this.userDataList = []
  1163. queryQuoteRoleUser(params).then(({data})=>{
  1164. if (data && data.code === 0){
  1165. this.userDataList = data.rows
  1166. }else {
  1167. this.$message.warning(data.msg)
  1168. }
  1169. }).catch((error)=>{
  1170. this.$message.error(error)
  1171. })
  1172. },
  1173. getBaseList(val){
  1174. this.tagNo = val
  1175. this.$nextTick(() => {
  1176. let strVal = ''
  1177. let conSql = ''
  1178. if (val === 102) {
  1179. strVal = this.saveQuote.customerNo
  1180. }
  1181. if (val === 104) {
  1182. strVal = this.saveQuote.projectNo
  1183. conSql = " and customer_id = '" + this.saveQuote.customerNo + "'"
  1184. }
  1185. if (val === 2000) {
  1186. strVal = this.saveQuote.purchase
  1187. conSql = " and b.site = '" + this.$store.state.user.site + "'"
  1188. }
  1189. if (val === 2002) {
  1190. strVal = this.saveQuote.quoter
  1191. conSql = " and b.site = '" + this.$store.state.user.site + "'"
  1192. }
  1193. this.$refs.baseList.init(val, strVal, conSql)
  1194. })
  1195. },
  1196. getBaseData(val){
  1197. switch (this.tagNo){
  1198. case 102:
  1199. if (this.saveQuote.customerNo !== val.Customer_no) {
  1200. this.saveQuote.projectNo = '';
  1201. this.saveQuote.projectDesc = '';
  1202. }
  1203. this.saveQuote.customerNo = val.Customer_no
  1204. this.saveQuote.customerDesc = val.Customer_desc
  1205. break;
  1206. case 104:
  1207. this.saveQuote.projectNo = val.project_id
  1208. this.saveQuote.projectDesc = val.project_name
  1209. break;
  1210. case 2000:
  1211. this.saveQuote.purchase = val.username
  1212. this.saveQuote.purchaseName = val.user_display
  1213. break
  1214. case 2002:
  1215. this.saveQuote.quoter = val.username
  1216. this.saveQuote.quoterName = val.user_display
  1217. break;
  1218. }
  1219. },
  1220. handleQueryByIds(){
  1221. let params = {
  1222. ids:this.$route.params.ids,
  1223. no:this.no,
  1224. size:this.size,
  1225. createBy:this.$store.state.user.name,
  1226. }
  1227. this.searchLoading = true
  1228. queryQuotePage(params).then(({data})=>{
  1229. if (data && data.code === 0){
  1230. this.dataList = data.rows
  1231. this.total = data.total
  1232. if (this.total > 0){
  1233. this.currentQuote = {...this.dataList[0]}
  1234. // 跳转成功后清空路由参数,避免后续刷新时重复触发
  1235. this.$router.replace({name: 'quote-index', params: {}})
  1236. // 提示用户已定位到新建的报价单
  1237. this.$message.success(`已定位到新建的报价单:${this.dataList[0].quoteVersionNo || ''}`)
  1238. }else {
  1239. this.currentQuote = {}
  1240. this.$message.warning('未查询到对应的报价单')
  1241. }
  1242. }else {
  1243. this.$message.error(data.msg)
  1244. }
  1245. this.searchLoading = false
  1246. }).catch(error=>{
  1247. this.$message.error(error)
  1248. this.searchLoading = false
  1249. })
  1250. },
  1251. handleInquiryBlur(){
  1252. let params = {
  1253. site:this.$store.state.user.site,
  1254. quotationNo:this.saveQuote.insideInquiryNo,
  1255. }
  1256. searchQuotationByQuotationNo(params).then(({data}) => {
  1257. if (data && data.code === 0){
  1258. if (data.data.length === 1){
  1259. this.dblclickInsideInquiry(data.data[0])
  1260. }
  1261. }
  1262. })
  1263. },
  1264. handleAgainQuote(row){
  1265. let params = {
  1266. id: row.id,
  1267. createBy: this.$store.state.user.name,
  1268. }
  1269. this.queryLoading = true
  1270. againQuote(params).then(({data}) => {
  1271. if (data && data.code === 0) {
  1272. this.$message.success(data.msg)
  1273. this.handleSearch();
  1274. } else {
  1275. this.$message.warning(data.msg)
  1276. this.queryLoading = false
  1277. }
  1278. }).catch((error) => {
  1279. this.$message.error(error)
  1280. this.queryLoading = false
  1281. })
  1282. },
  1283. clearModalData(field){
  1284. this.$set(this.saveQuote,field,'')
  1285. },
  1286. handleQueryInquiry(){
  1287. this.insideInquiry = {
  1288. site:this.$store.state.user.site,
  1289. quotationNo:this.saveQuote.insideInquiryNo,
  1290. quotationBatchNo:'',
  1291. quotationItemNo:'',
  1292. testPartNo:'',
  1293. projectName:'',
  1294. finalCustomerName:'',
  1295. quoterName:'',
  1296. partName:'',
  1297. quotationStatus:'审批中',
  1298. }
  1299. this.searchInsideInquiry();
  1300. this.insideInquiryVisible = true
  1301. },
  1302. dblclickInsideInquiry(row){
  1303. this.saveQuote.insideInquiryNo = row.quotationNo
  1304. this.saveQuote.customerNo = row.customerNo
  1305. this.saveQuote.customerDesc = row.customerDesc
  1306. this.saveQuote.projectNo = row.projectId
  1307. this.saveQuote.projectDesc = row.projectName
  1308. this.saveQuote.quoter = row.quoter
  1309. this.saveQuote.quoterName = row.quoterName
  1310. this.saveQuote.finalCustomerNo = row.finalCustomerId
  1311. this.saveQuote.finalCustomerDesc = row.finalCustomerName
  1312. this.saveQuote.purchase = row.tracker
  1313. this.saveQuote.purchaseName = row.trackerName
  1314. this.insideInquiryVisible = false
  1315. },
  1316. searchInsideInquiry(){
  1317. searchQuotationByQuotationNo(this.insideInquiry).then(({data}) => {
  1318. if (data && data.code === 0){
  1319. this.insideInquiryList = data.data;
  1320. }
  1321. })
  1322. },
  1323. getPriceCheckProperties() {
  1324. if (!this.currentQuote && !this.currentQuote.insideInquiryNo){
  1325. this.priceCheckPropertiesList = []
  1326. return
  1327. }
  1328. let params = {
  1329. site: this.saveQuote.site,
  1330. quotationNo: this.currentQuote.insideInquiryNo
  1331. }
  1332. getPriceCheckPropertiesList(params).then(({data}) => {
  1333. if (data && data.code === 0) {
  1334. this.priceCheckPropertiesList = data.rows
  1335. } else {
  1336. this.$message.warning(data.msg)
  1337. }
  1338. }).catch((error) => {
  1339. this.$message.error(error)
  1340. })
  1341. },
  1342. // 查询审批信息
  1343. getApprovalList () {
  1344. let tempData = {
  1345. site: this.$store.state.user.site,
  1346. menuId: this.$route.meta.menuId,
  1347. documentNo: this.currentQuote.quoteVersionNo
  1348. }
  1349. getApprovalList(tempData).then(({data}) => {
  1350. if (data && data.code === 0) {
  1351. this.approvalList = data.rows
  1352. } else {
  1353. this.approvalList = []
  1354. }
  1355. })
  1356. }, startDownload() {
  1357. this.exportLoading = true
  1358. },
  1359. finishDownload() {
  1360. this.exportLoading = false
  1361. },
  1362. fields() {
  1363. let json = "{"
  1364. this.columns.forEach((item, index) => {
  1365. if (index === this.columns.length - 1) {
  1366. json += "\"" + item.columnLabel + "\"" + ":" + "\"" + item.columnProp + "\""
  1367. } else {
  1368. json += "\"" + item.columnLabel + "\"" + ":" + "\"" + item.columnProp + "\"" + ","
  1369. }
  1370. })
  1371. json += "}"
  1372. return eval("(" + json + ")")
  1373. },
  1374. createExportData() {
  1375. return this.dataList;
  1376. },
  1377. searchByAnyField(params){
  1378. params.site = this.$store.state.user.site
  1379. params.no = this.no
  1380. params.size = this.size
  1381. params.userId = this.$store.state.user.id.toString()
  1382. queryQuotePageByAnyField(params).then(({data})=>{
  1383. if (data && data.code === 0){
  1384. this.dataList = data.page.list
  1385. this.total = data.page.totalCount
  1386. // 加载每个报价的明细首条数据
  1387. this.loadQuoteDetailInfo()
  1388. }else {
  1389. this.$message.warning(data.msg)
  1390. }
  1391. }).catch((error)=>{
  1392. this.$message.error(error)
  1393. })
  1394. this.filterSearchData = params
  1395. this.isFilterSearch = true
  1396. this.filterVisible = false
  1397. },
  1398. // 下达(首次提交到OA)
  1399. handleEditStatus(row) {
  1400. const submitIssue = () => {
  1401. this.submitLoading = true
  1402. let params = {
  1403. ...row,
  1404. userName: this.$store.state.user.name,
  1405. menuId: this.$route.meta.menuId
  1406. }
  1407. quoteEditStatus(params).then(({data}) => {
  1408. if (data && data.code === 0) {
  1409. this.$message({message: '操作成功', type: 'success'})
  1410. this.handleSearch()
  1411. } else {
  1412. this.$alert(data.msg, '错误', {
  1413. confirmButtonText: '确定'
  1414. })
  1415. }
  1416. }).catch((error) => {
  1417. this.$message.error(error)
  1418. }).finally(() => {
  1419. this.submitLoading = false
  1420. })
  1421. }
  1422. const doIssue = () => {
  1423. this.$confirm('是否确认下达?', '提示', {
  1424. confirmButtonText: '确定',
  1425. cancelButtonText: '取消',
  1426. type: 'warning'
  1427. }).then(() => {
  1428. submitIssue()
  1429. })
  1430. }
  1431. checkZeroUnitPrice({ quoteId: row.id }).then(({ data }) => {
  1432. if (data && data.code === 0 && data.hasZero) {
  1433. this.$confirm('存在单位成本为0的材料,是否继续下达?', '提示', {
  1434. confirmButtonText: '继续下达',
  1435. cancelButtonText: '取消',
  1436. type: 'warning'
  1437. }).then(() => {
  1438. submitIssue()
  1439. }).catch(() => {})
  1440. } else {
  1441. doIssue()
  1442. }
  1443. }).catch(() => {
  1444. doIssue()
  1445. })
  1446. },
  1447. },
  1448. created() {
  1449. this.checkSuperAdmin() // 校验超级管理员
  1450. this.fetchNodeOptions()
  1451. // 如果是从OA系统跳转来的(tokenLogin),不在created中查询,由activated处理
  1452. if (this.isMenu && this.searchIndex === 0 && this.$route.params.type !== 'tokenLogin'){
  1453. this.quoteForm = {
  1454. ...this.quote,
  1455. site: this.$store.state.user.site,
  1456. createBy: this.$store.state.user.name,
  1457. }
  1458. this.handleQueryBu();// 查询 BU
  1459. this.handleSearch(0);// 查询报价信息
  1460. }
  1461. this.searchIndex++;
  1462. },
  1463. watch:{
  1464. 'quoteForm.customerNo'(newVal, oldVal){
  1465. this.quoteForm.customerNo = newVal.toUpperCase()
  1466. },
  1467. 'quoteForm.projectNo'(newVal, oldVal){
  1468. this.quoteForm.projectNo = newVal.toUpperCase()
  1469. },
  1470. 'quoteForm.quoteNo'(newVal, oldVal){
  1471. this.quoteForm.quoteNo = newVal.toUpperCase()
  1472. },
  1473. 'saveQuote.customerNo'(newVal, oldVal){
  1474. if (newVal){
  1475. this.saveQuote.customerNo = newVal.toUpperCase()
  1476. }else {
  1477. this.saveQuote.customerDesc = ''
  1478. this.saveQuote.projectNo = ''
  1479. this.saveQuote.projectDesc = ''
  1480. }
  1481. },
  1482. 'saveQuote.projectNo'(newVal, oldVal){
  1483. if (newVal){
  1484. this.saveQuote.projectNo = newVal.toUpperCase()
  1485. this.projectNoBlur();
  1486. }else {
  1487. this.saveQuote.projectDesc = ''
  1488. this.saveQuote.finalCustomerNo = ''
  1489. this.saveQuote.finalCustomerDesc = ''
  1490. }
  1491. },
  1492. 'saveQuote.quoter'(newVal, oldVal){
  1493. if (!newVal){
  1494. this.saveQuote.quoterName = ''
  1495. }
  1496. },
  1497. 'saveQuote.purchase'(newVal, oldVal){
  1498. if (!newVal){
  1499. this.saveQuote.purchaseName = ''
  1500. }
  1501. },
  1502. saveVisible(newVal, oldVal){
  1503. if (newVal === false){
  1504. this.detailFlag = false;
  1505. }
  1506. },
  1507. // 'saveQuote.currency'(newVal, oldVal){
  1508. // this.saveQuote.currency = newVal.toUpperCase()
  1509. // },
  1510. 'currentQuote'(newVal, oldVal){
  1511. if (newVal && this.isMenu){
  1512. this.getPriceCheckProperties();
  1513. this.getApprovalList();
  1514. }
  1515. },
  1516. projectNo(newVal, oldVal){
  1517. if (newVal && !this.isMenu){
  1518. this.quoteForm.projectNo = newVal.toUpperCase()
  1519. this.quoteForm.site = this.$store.state.user.site
  1520. this.handleSearch(0);
  1521. }else {
  1522. this.dataList = []
  1523. this.total = 0
  1524. this.no = 1
  1525. }
  1526. },
  1527. },
  1528. activated() {
  1529. if (!this.isMenu){
  1530. return
  1531. }
  1532. // 优先级最高:从待报价清单页面跳转过来,带有新建的报价单ids
  1533. if (this.$route.params.ids){
  1534. this.handleQueryByIds();
  1535. return // 直接返回,避免执行其他逻辑
  1536. }
  1537. // 从OA系统跳转过来
  1538. if (this.$route.params.type === 'tokenLogin') {
  1539. if (this.$route.params.docNo) {
  1540. this.quoteForm = {
  1541. ...this.quote,
  1542. site: this.$store.state.user.site,
  1543. quoteVersionNo: this.$route.params.docNo,
  1544. }
  1545. }
  1546. this.handleSearch(0);
  1547. return
  1548. }
  1549. // 从项目页面跳转过来
  1550. if (this.$route.params.type === 'project'){
  1551. this.quoteForm = {
  1552. ...this.quote,
  1553. site: this.$store.state.user.site,
  1554. quoteVersionNo: this.$route.params.quoteVersionNo,
  1555. }
  1556. this.handleSearch(0);
  1557. return
  1558. }
  1559. }
  1560. }
  1561. </script>
  1562. <template>
  1563. <div>
  1564. <download-excel v-if="!isMenu"
  1565. :fields="fields()"
  1566. :data="exportData"
  1567. type="xls"
  1568. :name="exportName"
  1569. :header="exportHeader"
  1570. :footer="exportFooter"
  1571. :fetch="createExportData"
  1572. :before-generate="startDownload"
  1573. :before-finish="finishDownload"
  1574. worksheet="导出信息"
  1575. :class="['el-button', 'el-button--primary', 'el-button--medium', {'is-loading': exportLoading}]">
  1576. <i v-if="exportLoading" class="el-icon-loading"></i>
  1577. <span>{{ '导出' }}</span>
  1578. </download-excel>
  1579. <quote-search v-if="isMenu" v-model:quote="quoteForm" :node-options="nodeOptions" @filterSearch="filterVisible = true" @search="handleSearch" @save="handleSave"></quote-search>
  1580. <quote-table v-loading="searchLoading"
  1581. :current-row="currentQuote"
  1582. :columns="columns"
  1583. style="margin-top: 5px"
  1584. :is-menu="isMenu"
  1585. @save="handleSave"
  1586. @remove="handleRemove"
  1587. @rowClick="handleRowClick"
  1588. @updateStatus="handleUpdateStatus"
  1589. @queryById="handleQueryById"
  1590. @againQuote="handleAgainQuote"
  1591. @editStatus="handleEditStatus"
  1592. :data-list="dataList"
  1593. :issue-loading="submitLoading"
  1594. :height="height">
  1595. </quote-table>
  1596. <el-pagination @size-change="handleSizeChange"
  1597. @current-change="handlePageChange"
  1598. :current-page="no"
  1599. :page-sizes="[20, 50, 100, 200, 500]"
  1600. :page-size="size"
  1601. :total="total"
  1602. layout="total,sizes, prev, pager, next, jumper">
  1603. </el-pagination>
  1604. <el-tabs v-if="isMenu" v-model="activeName" type="border-card" style="margin-top: 0;" class="customer-tab">
  1605. <el-tab-pane v-if="isAuth('5011:pane:quoteGroupDetail')" label="报价明细" name="detail">
  1606. <quote-group-detail :quote="currentQuote" :auth-flag="false" :height="'28vh'" @refresh-quote-info="handleRefreshQuoteInfo"></quote-group-detail>
  1607. </el-tab-pane>
  1608. <el-tab-pane v-if="isAuth('5011:pane:quoteDetail')" label="条目明细" name="detail2">
  1609. <quote-detail v-if="activeName === 'detail2'" :save-auth="false" :is-export="true" :quote="currentQuote" :auth-flag="false" :height="'30vh'" @refresh-quote-info="handleRefreshQuoteInfo"></quote-detail>
  1610. </el-tab-pane>
  1611. <el-tab-pane v-if="isAuth('5011:pane:quotationProjectInformation')" label="项目信息" name="quotation_project_information">
  1612. <quotation-project-information height="31vh" :quotation-header="currentQuote"></quotation-project-information>
  1613. </el-tab-pane>
  1614. <el-tab-pane v-if="isAuth('5011:pane:customerInfo')" label="客户信息" name="quotation_customer_information">
  1615. <customer-info height="31vh" :project="currentQuote"></customer-info>
  1616. </el-tab-pane>
  1617. <el-tab-pane v-if="isAuth('5011:pane:quotationCustomerContact')" label="客户联系人" name="quotation_customer_contact">
  1618. <quotation-customer-contact height="28vh" :quotation-header="currentQuote"></quotation-customer-contact>
  1619. </el-tab-pane>
  1620. <el-tab-pane v-if="isAuth('5011:pane:ossComponents1')" label="报价-附件信息" name="quote_oss">
  1621. <oss-components
  1622. :save-visible="isAuth('5011:tab6:save')"
  1623. :download-visible="isAuth('5011:tab6:download')"
  1624. :remove-visible="isAuth('5011:tab6:remove')"
  1625. :preview-visible="isAuth('5011:tab6:preview')"
  1626. label="报价单号"
  1627. height="28vh"
  1628. :columns="ossColumns"
  1629. :order-ref1="currentQuote.site?currentQuote.site:''"
  1630. :order-ref2="currentQuote.quoteVersionNo?currentQuote.quoteVersionNo:''">
  1631. </oss-components>
  1632. </el-tab-pane>
  1633. <el-tab-pane v-if="isAuth('5011:pane:priceCheckProperties')" label="询价-基本信息" name="request">
  1634. <price-check-properties ref="tabProperties"
  1635. v-model:data-list="priceCheckPropertiesList"
  1636. height="31vh"></price-check-properties>
  1637. </el-tab-pane>
  1638. <el-tab-pane v-if="isAuth('5011:pane:ossComponents2')" label="询价-附件信息" name="oss">
  1639. <oss-components
  1640. :save-visible="isAuth('102001001:tab6:save')"
  1641. :download-visible="isAuth('102001001:tab6:download')"
  1642. :remove-visible="isAuth('102001001:tab6:remove')"
  1643. :preview-visible="isAuth('102001001:tab6:preview')"
  1644. label="询价单号"
  1645. height="28vh"
  1646. :columns="ossColumns"
  1647. :order-ref1="currentQuote.site"
  1648. :order-ref2="currentQuote.insideInquiryNo?currentQuote.insideInquiryNo.split('-')[0]:''">
  1649. </oss-components>
  1650. </el-tab-pane>
  1651. <!-- 审批信息 -->
  1652. <el-tab-pane v-if="isAuth('5011:pane:approvalInformation')" label="审批信息" name="approvalInformation">
  1653. <approval-information ref="approvalTable" v-model:data-list="approvalList" :height="300"></approval-information>
  1654. </el-tab-pane>
  1655. </el-tabs>
  1656. <el-dialog :title="saveQuote.id? '报价信息:'+ saveQuote.quoteVersionNo : '报价信息'" v-drag :close-on-click-modal="false" :visible.sync="saveVisible" width="500px" >
  1657. <el-form ref="saveForm" :model="saveQuote" :rules="saveRules" label-position="top" label-width="100px">
  1658. <el-row :gutter="10">
  1659. <el-col :span="8">
  1660. <el-form-item label="内部询价单号" prop="insideInquiryNo" :show-message="false">
  1661. <span slot="label" v-if="!detailFlag">
  1662. <a @click="handleQueryInquiry">内部询价单号</a>
  1663. </span>
  1664. <el-input v-model="saveQuote.insideInquiryNo" readonly @change="handleInquiryBlur" :disabled="detailFlag"></el-input>
  1665. </el-form-item>
  1666. </el-col>
  1667. <el-col :span="8">
  1668. <el-form-item label="报价日期" prop="quoteDate" :show-message="false">
  1669. <el-date-picker style="width: 100%" :disabled="detailFlag" v-model="saveQuote.quoteDate" type="date" value-format='yyyy-MM-dd' format='yyyy-MM-dd'></el-date-picker>
  1670. </el-form-item>
  1671. </el-col>
  1672. <el-col :span="8">
  1673. <el-form-item label="客户询价单号" prop="customerInquiryNo" :show-message="false">
  1674. <el-input v-model="saveQuote.customerInquiryNo" :disabled="detailFlag"></el-input>
  1675. </el-form-item>
  1676. </el-col>
  1677. </el-row>
  1678. <el-row :gutter="10">
  1679. <el-col :span="8">
  1680. <el-form-item label="客户编码" prop="customerNo" :show-message="false">
  1681. <span slot="label" v-if="!detailFlag">
  1682. <a @click="getBaseList(102)">客户编码</a>
  1683. </span>
  1684. <el-input v-model="saveQuote.customerNo" :disabled="detailFlag" @change="customerNoBlur"></el-input>
  1685. </el-form-item>
  1686. </el-col>
  1687. <el-col :span="16">
  1688. <el-form-item label="客户描述" prop="customerDesc" :show-message="false">
  1689. <el-input v-model="saveQuote.customerDesc" disabled></el-input>
  1690. </el-form-item>
  1691. </el-col>
  1692. </el-row>
  1693. <el-row :gutter="10">
  1694. <el-col :span="8">
  1695. <el-form-item label="项目号" prop="projectNo" :show-message="false">
  1696. <span slot="label" v-if="saveQuote.customerNo && !detailFlag">
  1697. <a @click="getBaseList(104)">项目号</a>
  1698. </span>
  1699. <el-input v-model="saveQuote.projectNo" :disabled="!saveQuote.customerNo || detailFlag"></el-input>
  1700. </el-form-item>
  1701. </el-col>
  1702. <el-col :span="16">
  1703. <el-form-item label="项目描述" prop="projectDesc" :show-message="false">
  1704. <el-input v-model="saveQuote.projectDesc" disabled></el-input>
  1705. </el-form-item>
  1706. </el-col>
  1707. </el-row>
  1708. <el-row :gutter="10">
  1709. <el-col :span="8">
  1710. <el-form-item label="终端客户编码" prop="finalCustomerNo" :show-message="false">
  1711. <el-input v-model="saveQuote.finalCustomerNo" disabled></el-input>
  1712. </el-form-item>
  1713. </el-col>
  1714. <el-col :span="16">
  1715. <el-form-item label="终端客户描述" prop="finalCustomerDesc" :show-message="false">
  1716. <el-input v-model="saveQuote.finalCustomerDesc" disabled></el-input>
  1717. </el-form-item>
  1718. </el-col>
  1719. </el-row>
  1720. <el-row :gutter="10">
  1721. <el-col :span="8">
  1722. <el-form-item label="报价专员" prop="quoter" :show-message="false">
  1723. <a slot="label" v-if="!detailFlag" @click="getBaseList(2002)">报价专员</a>
  1724. <el-input v-model="saveQuote.quoterName" readonly :disabled="detailFlag">
  1725. <span slot="suffix" v-show="saveQuote.quoter && !detailFlag" @click="clearModalData('quoter')"><i class="el-icon-circle-close" style="margin-left: 5px;cursor: pointer;"></i></span>
  1726. </el-input>
  1727. </el-form-item>
  1728. </el-col>
  1729. <el-col :span="8">
  1730. <el-form-item label="采购专员" prop="purchase" :show-message="false">
  1731. <a slot="label" v-if="!detailFlag" @click="getBaseList(2000)">采购专员</a>
  1732. <el-input v-model="saveQuote.purchaseName" readonly :disabled="detailFlag">
  1733. <span slot="suffix" v-show="saveQuote.purchase && !detailFlag" @click="clearModalData('purchase')"><i class="el-icon-circle-close" style="margin-left: 5px;cursor: pointer;"></i></span>
  1734. </el-input>
  1735. </el-form-item>
  1736. </el-col>
  1737. <el-col :span="8">
  1738. <el-form-item label=" " prop="purchase" :show-message="false">
  1739. <el-checkbox v-model="saveQuote.annualSales" :disabled="detailFlag" true-label="Y" false-label="N">项目年销售额10M</el-checkbox>
  1740. </el-form-item>
  1741. </el-col>
  1742. <el-col :span="24">
  1743. <el-form-item label="备注" prop="remark" class="auto" :show-message="false">
  1744. <el-input v-model="saveQuote.remark" :disabled="detailFlag" :rows="3" type="textarea"></el-input>
  1745. </el-form-item>
  1746. </el-col>
  1747. </el-row>
  1748. </el-form>
  1749. <div slot="footer" class="dialog-footer">
  1750. <el-button type="primary" v-if="!detailFlag" @click="handleSaveOrUpdateQuote"> </el-button>
  1751. <el-button @click="saveVisible = false"> </el-button>
  1752. <template v-if="saveQuote.status === '审批中' && isApprovalUser()">
  1753. <el-button v-if="isAuth('5011:submit')" type="primary" :loading="submitLoading" @click="agreeSubmit">同意</el-button>
  1754. <el-button v-if="isAuth('5011:reject') && currentQuote.isReject === 'Y'" type="primary" @click="rejectVisible = true">驳回</el-button>
  1755. </template>
  1756. </div>
  1757. </el-dialog>
  1758. <!-- 驳回意见弹窗 -->
  1759. <el-dialog title="驳回" top="30vh" :close-on-click-modal="false" v-drag :visible.sync="rejectVisible" width="500px" append-to-body>
  1760. <el-form label-position="top">
  1761. <el-form-item label="驳回意见" class="auto">
  1762. <el-input type="textarea" v-model="rejectOpinion" :rows="3"></el-input>
  1763. </el-form-item>
  1764. </el-form>
  1765. <el-footer style="text-align:center;height: 30px;line-height: 30px;">
  1766. <el-button type="primary" :loading="submitLoading" @click="rejectSubmit">确定</el-button>
  1767. <el-button type="primary" @click="closeRejectForm">取消</el-button>
  1768. </el-footer>
  1769. </el-dialog>
  1770. <el-dialog title="询价申请" v-drag :visible.sync="insideInquiryVisible" width="1000px" modal-append-to-body :close-on-click-modal="false">
  1771. <el-form label-position="top"
  1772. :model="insideInquiry"
  1773. size="mini">
  1774. <el-row :gutter="10">
  1775. <el-col :span="3">
  1776. <el-form-item label="询价单号">
  1777. <el-input v-model="insideInquiry.quotationNo" clearable/>
  1778. </el-form-item>
  1779. </el-col>
  1780. <el-col :span="3">
  1781. <el-form-item label="申请批次号">
  1782. <el-input v-model="insideInquiry.quotationBatchNo" clearable></el-input>
  1783. </el-form-item>
  1784. </el-col>
  1785. <el-col :span="3">
  1786. <el-form-item label="序号">
  1787. <el-input v-model="insideInquiry.quotationItemNo" clearable></el-input>
  1788. </el-form-item>
  1789. </el-col>
  1790. <el-col :span="3">
  1791. <el-form-item label="项目名称">
  1792. <el-input v-model="insideInquiry.projectName" clearable></el-input>
  1793. </el-form-item>
  1794. </el-col>
  1795. <el-col :span="3">
  1796. <el-form-item label="直接客户名称">
  1797. <el-input v-model="insideInquiry.finalCustomerName" clearable></el-input>
  1798. </el-form-item>
  1799. </el-col>
  1800. <el-col :span="3">
  1801. <el-form-item label="报价专员">
  1802. <el-input v-model="insideInquiry.quoterName" clearable></el-input>
  1803. </el-form-item>
  1804. </el-col>
  1805. <el-col :span="3">
  1806. <el-form-item label="状态">
  1807. <el-select v-model="insideInquiry.quotationStatus" style="width:100%">
  1808. <el-option label="全部" value=""></el-option>
  1809. <el-option label="草稿" value="草稿"></el-option>
  1810. <el-option label="审批中" value="审批中"></el-option>
  1811. <el-option label="已完成" value="已完成"></el-option>
  1812. </el-select>
  1813. </el-form-item>
  1814. </el-col>
  1815. <el-col :span="2">
  1816. <el-form-item label=" ">
  1817. <el-button type="primary" style="padding: 3px 12px" @click="searchInsideInquiry">查询</el-button>
  1818. </el-form-item>
  1819. </el-col>
  1820. </el-row>
  1821. </el-form>
  1822. <el-table :data="insideInquiryList" height="300" stripe border @row-dblclick="dblclickInsideInquiry">
  1823. <el-table-column prop="quotationNo" header-align="center" min-width="120" label="询价单号"/>
  1824. <el-table-column prop="quotationBatchNo" header-align="center" label="询价批次号"/>
  1825. <el-table-column width="60" align="center" header-align="center" prop="quotationItemNo" label="询价序号"/>
  1826. <el-table-column prop="projectName" header-align="center" label="项目名称"/>
  1827. <el-table-column prop="quotationStatus" header-align="center" label="状态"/>
  1828. <el-table-column label="报价专员" header-align="center" prop="quoterName"/>
  1829. <el-table-column label="直接客户编码" header-align="center" prop="finalCustomerId"/>
  1830. <el-table-column label="直接客户名称" header-align="center" prop="finalCustomerName"/>
  1831. </el-table>
  1832. <el-footer style="height:30px;margin-top: 20px;text-align:center">
  1833. <el-button type="primary" @click="insideInquiryVisible = false">关闭</el-button>
  1834. </el-footer>
  1835. </el-dialog>
  1836. <choose-list ref="baseList" @getBaseData="getBaseData"></choose-list>
  1837. <filter-search :visible.sync="filterVisible" ref="filter-search" @search="searchByAnyField"></filter-search>
  1838. </div>
  1839. </template>
  1840. <style scoped>
  1841. .auto /deep/ .el-form-item__content{
  1842. height: auto;
  1843. line-height: 1.5;
  1844. }
  1845. </style>