自主产品,供应链食堂系统。将两个端拆开了。
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.

592 lines
16 KiB

5 years ago
  1. <template>
  2. <view>
  3. <view class="box">
  4. <view class="title">报价清单</view>
  5. <view class="lf-font-24 lf-color-gray lf-m-t-5">请在以下物资信息表内填写报价</view>
  6. </view>
  7. <!-- 修饰条 -->
  8. <self-line></self-line>
  9. <view class="box lf-row-between relation">
  10. <view>
  11. <text class="title">关联食堂</text>
  12. <text class="lf-font-24 lf-color-555 lf-m-l-10">(多选)</text>
  13. </view>
  14. <view class="lf-font-24 lf-color-gray lf-text-right lf-row-center" style="width: 370rpx; justify-content: flex-end;" @click="switchRelation">
  15. <view class="lf-line-1">{{ selectName }}</view>
  16. <u-icon name="arrow-right" class="lf-text-vertical"></u-icon>
  17. </view>
  18. <view class="mask" :style="{top: node_top +'px'}" v-if="is_show" @click="is_show = false">
  19. <view class="list">
  20. <view class="lf-row-between item" v-for="(item, index) in relation_list" :key="index" @click.stop="selectItem(index)">
  21. <view>{{ item.canteen_name }}</view>
  22. <u-icon name="checkmark-circle" color="#1833F2" size="40" v-if="item.checked"></u-icon>
  23. </view>
  24. </view>
  25. </view>
  26. </view>
  27. <self-line></self-line>
  28. <!-- 报价时间 -->
  29. <view class="lf-row-between lf-p-30 lf-p-l-32 lf-p-r-32 lf-font-28">
  30. <view class="lf-color-black">报价生效时间</view>
  31. <picker mode="date" :value="deadline_start" @change="pickerChange($event, 'deadline_start')">
  32. <view class="lf-color-555 lf-text-right" style="width: 400rpx;">{{ deadline_start || '请选择...' }}</view>
  33. </picker>
  34. </view>
  35. <view class="lf-row-between lf-p-30 lf-p-l-32 lf-p-r-32 lf-font-28">
  36. <view class="lf-color-black">报价失效时间</view>
  37. <picker mode="date" :value="deadline_end" @change="pickerChange($event, 'deadline_end')">
  38. <view class="lf-color-555 lf-text-right" style="width: 400rpx;">{{ deadline_end || '请选择...' }}</view>
  39. </picker>
  40. </view>
  41. <self-line></self-line>
  42. <!-- 物料table -->
  43. <view class="box">
  44. <view class="lf-m-b-20 lf-row-between">
  45. <view class="lf-font-32 lf-color-black lf-font-bold">报价明细</view>
  46. <view class="lf-flex search-self">
  47. <u-icon name="search" class="search-icon"></u-icon>
  48. <input placeholder="搜索物资" confirm-type="search" @focus="hideIcon()" class="lf-m-l-10 lf-font-28" :value="searchValue" @input="inputChange" @confirm="inputConfirm" />
  49. </view>
  50. </view>
  51. <wyb-table :first-line-fixed="true"
  52. contentBgColor="#eef6fe"
  53. :headers="headers"
  54. :contents="contents"
  55. :scrollToInput="true"
  56. :loading="table_loading"
  57. :searchKey="searchValue"
  58. @onInputChange="onInputChange"
  59. @onButtonClick="onButtonClick"
  60. width="100%" height="80vh"></wyb-table>
  61. </view>
  62. <view style="height: 140rpx;"></view>
  63. <!-- 操作按钮 -->
  64. <view class="fixed-bottom">
  65. <button class="btn btn1" :class="{'lf-btn-disabled': button_click}" @click="save(0)">临时保存</button>
  66. <button class="btn btn2" :class="{'lf-btn-disabled': button_click}" @click="save(1)">直接报价</button>
  67. </view>
  68. </view>
  69. </template>
  70. <script>
  71. import wybTable from '@/components/wyb-table/wyb-table';
  72. let app = getApp();
  73. let timer = null;
  74. export default {
  75. components: {
  76. wybTable
  77. },
  78. data(){
  79. return {
  80. show_icon: true,
  81. headers: [{
  82. label: '物资名称',
  83. key: 'name',
  84. search: true
  85. },{
  86. label: '规格',
  87. key: 'spec'
  88. },{
  89. label: '单位',
  90. key: 'unit'
  91. },{
  92. label: '分类',
  93. key: 'category'
  94. },{
  95. label: '品牌',
  96. key: 'brand'
  97. },{
  98. label: '品级',
  99. key: 'quality_level'
  100. },{
  101. label: '编号',
  102. key: 'number'
  103. },{
  104. label: '起购数',
  105. key: 'purchase_limit'
  106. },{
  107. label: '含税价',
  108. key: 'tax_price'
  109. },{
  110. label: '不含税价',
  111. key: 'non_tax_price'
  112. },{
  113. label: '操作',
  114. key: 'operation'
  115. }],
  116. contents: [],
  117. is_show: false,
  118. relation_list: [], // 关联食堂列表
  119. node_top: 0,
  120. code: '', // 订单号,批次号,如果有
  121. type: 0,
  122. deadline_start: '',
  123. deadline_end: '',
  124. table_loading: true,
  125. searchValue: '', // 搜索内容
  126. button_click: false
  127. }
  128. },
  129. computed: {
  130. selectName(){
  131. let arr = [];
  132. this.relation_list.map(item => {
  133. if(item.checked){
  134. arr.push(item.canteen_name);
  135. }
  136. });
  137. let str = '请选择...';
  138. if(arr.length > 0){
  139. str = arr.join(',');
  140. }
  141. return str;
  142. }
  143. },
  144. onLoad(options){
  145. this.code = options.code || '';
  146. this.type = options.type || 0;
  147. if(options.type == 1){
  148. // 编辑,不可更换食堂
  149. this.materialListByOrder();
  150. }else if(options.type == 2){
  151. // 复用订单号
  152. this.materialListByOrder();
  153. }else if(options.type == 3){
  154. // 复用批次号
  155. this.materialListByBatch();
  156. }else{
  157. // 单纯发起报价
  158. this.getMaterialList();
  159. }
  160. },
  161. onReady(){
  162. let that = this;
  163. let info = uni.createSelectorQuery().select(".relation");
  164.     info.boundingClientRect(function(data) {
  165. let num = app.globalData.customBarH;
  166. // #ifdef MP-WEIXIN
  167. num -= 60;
  168. // #endif
  169. num += data.height;
  170. num += data.top;
  171. that.node_top = num;
  172.    }).exec()
  173. },
  174. onPageScroll(){
  175. if(this.is_show){
  176. this.is_show = false;
  177. }
  178. },
  179. methods: {
  180. hideIcon() {
  181. if(this.searchValue != '') {
  182. this.show_icon = false;
  183. }
  184. },
  185. // 搜索input值被改变
  186. inputChange(event){
  187. if(timer){
  188. clearTimeout(timer);
  189. timer = null;
  190. }
  191. timer = setTimeout(() => {
  192. this.searchValue = event.detail.value;
  193. }, 1000);
  194. if(event.detail.value == '') {
  195. this.show_icon = true;
  196. }else {
  197. this.show_icon = false;
  198. }
  199. },
  200. // 搜索按下回车
  201. inputConfirm(event){
  202. this.searchValue = event.detail.value;
  203. },
  204. // 日期选择器值被改变
  205. pickerChange(event, current_name){
  206. if(current_name == 'deadline_start') {
  207. // PS 获取30天后日期
  208. var date1 = new Date(event.detail.value);
  209. var date2 = new Date(date1);
  210. date2.setDate(date1.getDate() + 30);
  211. var nowTime = new Date(new Date().toLocaleDateString()).getTime();
  212. let oldTime = new Date(new Date(event.detail.value).toLocaleDateString()).getTime();
  213. if(nowTime>oldTime) {
  214. this.$msg('请选择今天或未来的时间')
  215. return
  216. }else {
  217. this[current_name] = event.detail.value;
  218. this.deadline_end = this.$shared.recordTime(date2, '-', 'date')
  219. }
  220. }else {
  221. var nowTime = new Date(new Date().toLocaleDateString()).getTime();
  222. let oldTime = new Date(new Date(event.detail.value).toLocaleDateString()).getTime();
  223. if(nowTime>oldTime) {
  224. this.$msg('请选择今天之后的时间')
  225. return
  226. }else {
  227. if(this.deadline_start >= event.detail.value) {
  228. this.$msg('失效时间不能小于报价生效时间')
  229. return
  230. }else {
  231. this[current_name] = event.detail.value;
  232. }
  233. }
  234. }
  235. },
  236. // 获取物资列表
  237. getMaterialList(){
  238. this.$http(this.API.API_SUPPLIER_MATERIALLIST).then(res => {
  239. let list = res.data.spec || [];
  240. let contents = list.map(item => {
  241. return {
  242. name: item?.material?.m_name || '',
  243. material_id: item?.material?.id || 0,
  244. spec: item.name || '',
  245. spec_id: item.id,
  246. brand: item?.material?.brand || '',
  247. quality_level: item?.material?.quality_level || '',
  248. number: item?.material?.m_sn || '',
  249. purchase_limit: {edit: true, value: 1},
  250. tax_price: {edit: true, value: ''},
  251. non_tax_price: {edit: true, value: ''},
  252. operation: {button: true, key: 'delete', value: '删除'},
  253. unit: item?.material?.unit?.unit_name || '',
  254. category: item?.material?.category?.m_cate_name || ''
  255. }
  256. })
  257. this.contents = contents;
  258. this.table_loading = false;
  259. this.getCanteenList();
  260. })
  261. },
  262. // 复用报价订单号 & 编辑共用
  263. materialListByOrder(){
  264. this.$http(this.API.API_SUPPLIER_QUOTATIONREUSEBYORDER, {
  265. q_sn: this.code
  266. }).then(res => {
  267. let list = res.data.order || [];
  268. let canteen = res.data.canteen || [];
  269. // 处理表格显示
  270. let contents = list.map(item => {
  271. let obj = {
  272. name: item?.material?.m_name || '',
  273. material_id: item?.material?.id || 0,
  274. spec: item.name || '',
  275. spec_id: item.id,
  276. brand: item?.material?.brand || '',
  277. quality_level: item?.material?.quality_level || '',
  278. number: item?.material?.m_sn || '',
  279. purchase_limit: {edit: true, value: item?.quotation_item?.purchase_limit || ''},
  280. tax_price: {edit: true, value: item?.quotation_item?.tax_price || ''},
  281. non_tax_price: {edit: true, value: item?.quotation_item?.non_tax_price || ''},
  282. quotation_id: item?.quotation_item?.id || 0,
  283. operation: {button: true, key: 'delete', value: '删除'},
  284. unit: item?.material?.unit?.unit_name || '',
  285. category: item?.material?.category?.m_cate_name || ''
  286. }
  287. if(item?.material?.state && item?.material?.state != '启用'){
  288. obj.disabled = true;
  289. }
  290. return obj;
  291. })
  292. this.contents = contents;
  293. this.table_loading = false;
  294. // 处理报价生效、失效时间
  295. let deadline_start = '';
  296. let deadline_end = '';
  297. if(list[0] && list[0]?.quotation_item?.quotation){
  298. let quotation = list[0]?.quotation_item?.quotation || {};
  299. if(quotation.deadline_start){
  300. deadline_start = quotation.deadline_start.split(' ')[0];
  301. }
  302. if(quotation.deadline){
  303. deadline_end = quotation.deadline.split(' ')[0];
  304. }
  305. }
  306. this.deadline_start = deadline_start;
  307. this.deadline_end = deadline_end;
  308. this.getCanteenList(canteen);
  309. })
  310. },
  311. // 复用批次号
  312. materialListByBatch(){
  313. this.$http(this.API.API_SUPPLIER_QUOTATIONREUSEBYBATCH, {
  314. batch_sn: this.code
  315. }).then(res => {
  316. console.log("materialListByBatch", res);
  317. let list = res.data.order || [];
  318. let canteen = res.data.canteen || [];
  319. // 处理表格显示
  320. let contents = list.map(item => {
  321. return {
  322. name: item?.material?.m_name || '',
  323. material_id: item?.material?.id || 0,
  324. spec: item.name || '',
  325. spec_id: item.id,
  326. brand: item?.material?.brand || '',
  327. quality_level: item?.material?.quality_level || '',
  328. number: item?.material?.m_sn || '',
  329. purchase_limit: {edit: true, value: item?.quotation?.purchase_limit || ''},
  330. tax_price: {edit: true, value: item?.quotation?.tax_price || ''},
  331. non_tax_price: {edit: true, value: item?.quotation?.non_tax_price || ''}
  332. }
  333. })
  334. this.contents = contents;
  335. this.table_loading = false;
  336. // 处理报价生效、失效时间
  337. let deadline_start = '';
  338. let deadline_end = '';
  339. if(list[0] && list[0]?.quotation_item?.quotation){
  340. let quotation = list[0]?.quotation_item?.quotation || {};
  341. if(quotation.deadline_start){
  342. deadline_start = quotation.deadline_start.split(' ')[0];
  343. }
  344. if(quotation.deadline){
  345. deadline_end = quotation.deadline.split(' ')[0];
  346. }
  347. }
  348. this.deadline_start = deadline_start;
  349. this.deadline_end = deadline_end;
  350. this.getCanteenList(canteen);
  351. })
  352. },
  353. // 关联食堂列表
  354. getCanteenList(canteen = []){
  355. this.$http(this.API.API_SUPPLIER_CANTEENLIST).then(res => {
  356. let list = res.data.list.map(item => {
  357. item.checked = false;
  358. canteen.map(ct => {
  359. if(ct == item.id){
  360. item.checked = true;
  361. }
  362. })
  363. return item;
  364. })
  365. this.relation_list = list;
  366. })
  367. },
  368. // 监听表格被输入
  369. onInputChange(event){
  370. this.contents[event.contentIndex][event.key].value = event.detailValue;
  371. },
  372. // table操作按钮被点击
  373. onButtonClick(event){
  374. if(event.content.key == 'delete'){
  375. let contentIndex = event.contentIndex;
  376. let name = event?.lineData?.name || '';
  377. let spec = event?.lineData?.spec || '';
  378. uni.showModal({
  379. title: '温馨提示',
  380. content: `确定删除 ${name}-${spec} 吗?`,
  381. confirmColor: '#1833F2',
  382. success: result => {
  383. if(result.confirm){
  384. this.contents.splice(contentIndex, 1);
  385. }
  386. }
  387. })
  388. }
  389. },
  390. // 切换显示关联食堂modal
  391. switchRelation(){
  392. if(this.type == 1) return this.$msg('编辑不可更换关联食堂哦');
  393. this.is_show = !this.is_show;
  394. },
  395. // 选择食堂
  396. selectItem(index){
  397. this.relation_list[index].checked = !this.relation_list[index].checked;
  398. },
  399. // 报价订单编辑时保存
  400. editMaterial(_t){
  401. // 物资列表
  402. let list = [];
  403. this.contents.map(item => {
  404. list.push({
  405. id: item.quotation_id,
  406. tax_price: item.tax_price.value,
  407. non_tax_price: item.non_tax_price.value,
  408. purchase_limit: Number(item.purchase_limit.value) || 1
  409. })
  410. });
  411. // 操作状态,是保存还是直接发起
  412. let state = ['待发起', '待审核'][_t];
  413. this.$http(this.API.API_SUPPLIER_QUOTATIONSAVE, {
  414. data: list,
  415. state: state,
  416. q_sn: this.code,
  417. start: this.deadline_start,
  418. end: this.deadline_end
  419. }).then(res => {
  420. this.$msg('操作成功').then(result => {
  421. this.$toBack();
  422. })
  423. }).catch(err => this.button_click = false)
  424. },
  425. // 保存
  426. save(_t){
  427. // 拦截按钮重复点击
  428. if(this.button_click){
  429. return;
  430. }
  431. this.button_click = true;
  432. // 拦截是编辑的情况
  433. if(this.type == 1){
  434. this.editMaterial(_t);
  435. return;
  436. }
  437. // 物资列表
  438. let list = [];
  439. this.contents.map(item => {
  440. if(item.tax_price.value && item.non_tax_price.value){
  441. list.push({
  442. m_id: item.material_id,
  443. m_spec_id: item.spec_id,
  444. tax_price: item.tax_price.value,
  445. non_tax_price: item.non_tax_price.value,
  446. purchase_limit: Number(item.purchase_limit.value) || 1
  447. })
  448. }
  449. });
  450. // 关联食堂
  451. let canteen_ids = [];
  452. this.relation_list.map(item => {
  453. if(item.checked){
  454. canteen_ids.push(item.id);
  455. }
  456. })
  457. if(canteen_ids.length <= 0){
  458. this.button_click = false;
  459. return this.$msg('您未选择关联食堂哦')
  460. }
  461. if(!this.deadline_start){
  462. this.button_click = false;
  463. return this.$msg('您未选择报价生效时间哦')
  464. }
  465. if(!this.deadline_end){
  466. this.button_click = false;
  467. return this.$msg('您未选择报价失效时间哦')
  468. }
  469. if(list.length <= 0){
  470. this.button_click = false;
  471. return this.$msg('请补充完整物资报价信息')
  472. }
  473. // 操作状态,是保存还是直接发起
  474. let state = ['待发起', '待审核'][_t];
  475. this.$http(this.API.API_SUPPLIER_QUOTATIONAPPLY, {
  476. data: list,
  477. state: state,
  478. canteen_ids: canteen_ids,
  479. start: this.deadline_start,
  480. end: this.deadline_end
  481. }).then(res => {
  482. this.$msg(res.data).then(result => {
  483. this.$toBack();
  484. })
  485. }).catch(err => this.button_click = false)
  486. }
  487. }
  488. }
  489. </script>
  490. <style>
  491. page{
  492. overflow-x: hidden;
  493. }
  494. </style>
  495. <style lang="scss" scoped="scoped">
  496. .search-icon {
  497. color: #777;
  498. }
  499. .search-self {
  500. background: #f2f2f2;
  501. padding: 10rpx 0 10rpx 20rpx;
  502. border-radius: 40rpx;
  503. font-size: 28rpx;
  504. }
  505. .search-self .uni-input-placeholder {
  506. margin-right: 500px;
  507. color: #777;
  508. font-size: 28rpx;
  509. }
  510. .lf-m-t-5{
  511. margin-top: 5rpx;
  512. }
  513. .box{
  514. padding: 30rpx 32rpx;
  515. width: 100%;
  516. height: max-content;
  517. box-sizing: border-box;
  518. }
  519. .title{
  520. color: #222222;
  521. font-size: 28rpx;
  522. font-weight: bold;
  523. }
  524. .fixed-bottom{
  525. position: fixed;
  526. bottom: 0rpx;
  527. left: 0rpx;
  528. z-index: 99;
  529. width: 750rpx;
  530. height: 98rpx;
  531. display: flex;
  532. justify-content: center;
  533. align-items: center;
  534. border-top: 1rpx solid #E5E5E5;
  535. background-color: #fff;
  536. .btn{
  537. width: 320rpx;
  538. height: 82rpx;
  539. border-radius: 41rpx;
  540. margin: 0;
  541. padding: 0;
  542. font-size: 32rpx;
  543. display: flex;
  544. justify-content: center;
  545. align-items: center;
  546. }
  547. .btn1{
  548. border: 2rpx solid #555555;
  549. opacity: .5;
  550. }
  551. .btn2{
  552. background: #1833F2;
  553. color: #FFFFFF;
  554. margin-left: 50rpx;
  555. }
  556. }
  557. .relation{
  558. position: relative;
  559. border-bottom: 1rpx solid #E5E5E5;
  560. }
  561. .mask{
  562. position: fixed;
  563. background-color: rgba(0,0,0,0.4);
  564. width: 100%;
  565. // top: 149px;
  566. bottom: 0;
  567. left: 0;
  568. z-index: 100;
  569. .list{
  570. min-height: max-content;
  571. max-height: 500rpx;
  572. overflow: scroll;
  573. background-color: #FFFFFF;
  574. width: 100%;
  575. .item{
  576. height: 92rpx;
  577. padding: 0 32rpx;
  578. border-bottom: 1rpx solid #E5E5E5;
  579. color: #222222;
  580. font-size: 28rpx;
  581. }
  582. }
  583. }
  584. </style>