detailsPage.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746
  1. <template>
  2. <div class="borderless">
  3. <div class="customer-head">
  4. <div class="customer-back">
  5. <!-- <i class="back-icon el-icon-arrow-left"></i><i style="font-style:normal">返回管理列表</i>-->
  6. <el-button
  7. type="danger"
  8. style="border: none;background: none;color: red"
  9. icon="el-icon-arrow-left"
  10. @click="backToList"
  11. >返回列表
  12. </el-button>
  13. </div>
  14. <el-button
  15. class="el-button--small-yh add-customer-btn"
  16. type="primary"
  17. :disabled="disabled"
  18. @click="editCustomer"
  19. >{{ form.id ? "确认修改" : "确认新增" }}
  20. </el-button>
  21. </div>
  22. <div style="margin-top: 60px;margin-bottom:35px">
  23. <containerTitle title="基础信息"></containerTitle>
  24. <basic-container style="margin-bottom: 10px">
  25. <avue-form ref="form" v-model="form" :option="option">
  26. <template slot="portOfLoad">
  27. <port-info
  28. v-model="form.portOfLoad"
  29. :disabled="$route.query.status == 1"
  30. />
  31. </template>
  32. <template slot="portOfDestination">
  33. <port-info
  34. v-model="form.portOfDestination"
  35. :disabled="$route.query.status == 1"
  36. />
  37. </template>
  38. <template slot="corpId">
  39. <select-component
  40. v-model="form.corpId"
  41. :configuration="configuration"
  42. ></select-component>
  43. </template>
  44. <template slot="exchangeRate">
  45. <el-input
  46. size="mini"
  47. v-model="form.exchangeRate"
  48. oninput='this.value=this.value.replace(/[^(\d.)]/g,"").replace(/^(\d+)\.(\d\d).*$/, "$1.$2")'
  49. @change="rateChange"
  50. placeholder="请输入 汇率"
  51. ><template slot="append">%</template></el-input
  52. >
  53. </template>
  54. </avue-form>
  55. </basic-container>
  56. <containerTitle title="商品信息"></containerTitle>
  57. <basic-container>
  58. <avue-crud
  59. ref="crud"
  60. :data="data"
  61. :option="tableOption"
  62. @row-del="rowDel"
  63. @saveColumn="saveColumn"
  64. :summary-method="summaryMethod"
  65. >
  66. <template slot="price" slot-scope="{ row }">
  67. <el-input
  68. v-if="row.$cellEdit"
  69. v-model="row.price"
  70. size="small"
  71. oninput='this.value=this.value.replace(/[^(\d.)]/g,"").replace(/^(\d+)\.(\d\d).*$/, "$1.$2")'
  72. @change="priceChange(row)"
  73. ></el-input>
  74. <span v-else>{{ row.price }}</span>
  75. </template>
  76. <template slot="orderQuantity" slot-scope="{ row }">
  77. <el-input
  78. v-if="row.$cellEdit"
  79. v-model="row.orderQuantity"
  80. size="small"
  81. oninput='this.value=this.value.replace(/[^(\d.)]/g,"").replace(/^(\d+)\.(\d\d).*$/, "$1.$2")'
  82. @change="quantityChange(row)"
  83. ></el-input>
  84. <span v-else>{{ row.orderQuantity }}</span>
  85. </template>
  86. <template slot="itemType" slot-scope="{ row }">
  87. <el-select
  88. v-if="row.$cellEdit"
  89. v-model="row.itemType"
  90. filterable
  91. allow-create
  92. default-first-option
  93. placeholder="请输入"
  94. @focus="itemTypeFocus(row)"
  95. >
  96. <el-option
  97. v-for="(item, index) in itemtypeList"
  98. :key="index"
  99. :label="item.value"
  100. :value="item.value"
  101. >
  102. </el-option>
  103. </el-select>
  104. <span v-else>{{ row.itemType }}</span>
  105. </template>
  106. <template slot="menuLeft">
  107. <el-button
  108. type="primary"
  109. icon="el-icon-plus"
  110. size="small"
  111. @click.stop="newDetails"
  112. >新增明细</el-button
  113. >
  114. <el-button
  115. type="info"
  116. icon="el-icon-printer"
  117. size="small"
  118. @click.stop="openReport()"
  119. >报 表</el-button
  120. >
  121. </template>
  122. <template slot="menu" slot-scope="{ row, index }">
  123. <el-button
  124. size="small"
  125. icon="el-icon-edit"
  126. type="text"
  127. @click="rowCell(row, index)"
  128. :disabled="disabled"
  129. >{{ row.$cellEdit ? "保存" : "修改" }}</el-button
  130. >
  131. <el-button
  132. size="small"
  133. icon="el-icon-edit"
  134. type="text"
  135. @click="rowDel(row, index)"
  136. :disabled="detailData.status == 1"
  137. >删 除</el-button
  138. >
  139. </template>
  140. </avue-crud>
  141. </basic-container>
  142. </div>
  143. <el-dialog
  144. title="导入商品"
  145. append-to-body
  146. class="el-dialogDeep"
  147. :visible.sync="dialogVisible"
  148. width="60%"
  149. :close-on-click-modal="false"
  150. :destroy-on-close="true"
  151. :close-on-press-escape="false"
  152. @close="closeGoods"
  153. top="10vh"
  154. >
  155. <span>
  156. <el-row>
  157. <el-col :span="5">
  158. <div>
  159. <el-scrollbar>
  160. <basic-container style="margin-top:45px">
  161. <avue-tree :option="treeOption" @node-click="nodeClick" />
  162. </basic-container>
  163. </el-scrollbar>
  164. </div>
  165. </el-col>
  166. <el-col :span="19">
  167. <avue-crud
  168. :option="goodsOption"
  169. :table-loading="loading"
  170. :data="goodsList"
  171. ref="goodsCrud"
  172. @refresh-change="refreshChange"
  173. @selection-change="selectionChange"
  174. @row-click="rowClick"
  175. :page.sync="page"
  176. @on-load="onLoad"
  177. @saveColumn="saveGoodsColumn"
  178. ></avue-crud>
  179. </el-col>
  180. </el-row>
  181. </span>
  182. <span slot="footer" class="dialog-footer">
  183. <el-button @click="dialogVisible = false">取 消</el-button>
  184. <el-button
  185. type="primary"
  186. @click="importGoods"
  187. :disabled="selectionList.length == 0"
  188. >导入</el-button
  189. >
  190. </span>
  191. </el-dialog>
  192. <report-dialog
  193. :switchDialog="switchDialog"
  194. :reportId="form.id"
  195. reportName="客户询价"
  196. @onClose="onClose()"
  197. ></report-dialog>
  198. </div>
  199. </template>
  200. <script>
  201. import tableOption from "./config/customerContact.json";
  202. import goodsOption from "./config/commodity.json";
  203. import {
  204. detail,
  205. submit,
  206. delItem,
  207. getDeptLazyTree,
  208. getGoods,
  209. getPorts,
  210. getSpecification
  211. } from "@/api/basicData/purchaseInquiry";
  212. import reportDialog from "@/components/report-dialog/main";
  213. import {
  214. isvalidatemobile,
  215. validatename,
  216. micrometerFormat
  217. } from "@/util/validate";
  218. import _ from "lodash";
  219. export default {
  220. name: "detailsPageEdit",
  221. data() {
  222. const validatePhone = (rule, value, callback) => {
  223. if (value != "") {
  224. if (isvalidatemobile(value)[0]) {
  225. this.$message.error("手机号码格式不正确");
  226. callback(new Error(isvalidatemobile(value)[1]));
  227. } else {
  228. callback();
  229. }
  230. } else {
  231. callback();
  232. }
  233. };
  234. const validateName = (rule, value, callback) => {
  235. if (value != "") {
  236. if (validatename(value)) {
  237. this.$message.error("联系人格式不正确");
  238. callback(new Error(validatename(value)));
  239. } else {
  240. callback();
  241. }
  242. } else {
  243. callback();
  244. }
  245. };
  246. return {
  247. configuration: {
  248. multipleChoices: false,
  249. multiple: false,
  250. collapseTags: false,
  251. placeholder: "请点击右边按钮选择",
  252. dicData: []
  253. },
  254. switchDialog: false,
  255. form: {},
  256. disabled: false,
  257. dialogVisible: false,
  258. tableOption: tableOption,
  259. option: {
  260. menuBtn: false,
  261. labelWidth: 100,
  262. column: [
  263. {
  264. label: "客户名称",
  265. prop: "corpId",
  266. rules: [
  267. {
  268. required: true,
  269. message: "",
  270. trigger: "blur"
  271. }
  272. ],
  273. span: 8,
  274. slot: true
  275. },
  276. {
  277. label: "系统号",
  278. prop: "sysNo",
  279. span: 8
  280. },
  281. {
  282. label: "订单状态",
  283. prop: "orderStatus",
  284. span: 8,
  285. type: "select",
  286. dicUrl: "/api/blade-system/dict-biz/dictionary?code=order_status",
  287. props: {
  288. label: "dictValue",
  289. value: "dictValue"
  290. }
  291. },
  292. {
  293. label: "联系人",
  294. prop: "corpAttn",
  295. span: 8,
  296. rules: [{ validator: validateName, trigger: "blur" }]
  297. },
  298. {
  299. label: "电话",
  300. prop: "corpTel",
  301. span: 8,
  302. rules: [{ validator: validatePhone, trigger: "blur" }]
  303. },
  304. {
  305. label: "起运港",
  306. prop: "portOfLoad",
  307. span: 8,
  308. type: "select",
  309. filterable: true,
  310. dicData: [],
  311. props: {
  312. label: "name",
  313. value: "name"
  314. }
  315. },
  316. {
  317. label: "目的港",
  318. prop: "portOfDestination",
  319. span: 8,
  320. type: "select",
  321. filterable: true,
  322. dicData: [],
  323. props: {
  324. label: "name",
  325. value: "name"
  326. }
  327. },
  328. {
  329. label: "运输方式",
  330. prop: "transport",
  331. span: 8,
  332. type: "select",
  333. dicUrl: "/api/blade-system/dict-biz/dictionary?code=mode_transport",
  334. props: {
  335. label: "dictValue",
  336. value: "dictValue"
  337. }
  338. },
  339. {
  340. label: "价格条款",
  341. prop: "priceTerms",
  342. span: 8,
  343. type: "select",
  344. dicUrl: "/api/blade-system/dict-biz/dictionary?code=pricing_terms",
  345. props: {
  346. label: "dictValue",
  347. value: "dictValue"
  348. }
  349. },
  350. {
  351. label: "条款说明",
  352. prop: "priceTermsDescription",
  353. span: 8
  354. },
  355. {
  356. label: "订单日期",
  357. prop: "businesDate",
  358. span: 8,
  359. type: "date",
  360. format: "yyyy-MM-dd",
  361. valueFormat: "yyyy-MM-dd 00:00:00"
  362. },
  363. {
  364. label: "有效日期",
  365. prop: "dateValidity",
  366. span: 8,
  367. type: "date",
  368. format: "yyyy-MM-dd",
  369. valueFormat: "yyyy-MM-dd 00:00:00"
  370. },
  371. {
  372. label: "收款方式",
  373. prop: "paymentType",
  374. span: 8,
  375. type: "select",
  376. dicUrl: "/api/blade-system/dict-biz/dictionary?code=payment_term",
  377. props: {
  378. label: "dictValue",
  379. value: "dictValue"
  380. }
  381. },
  382. {
  383. label: "收款说明",
  384. prop: "paymentTypeDescription",
  385. span: 8
  386. },
  387. {
  388. label: "币别",
  389. prop: "currency",
  390. span: 8,
  391. type: "select",
  392. dicUrl: "/api/blade-system/dict-biz/dictionary?code=currency",
  393. props: {
  394. label: "dictValue",
  395. value: "dictValue"
  396. }
  397. },
  398. {
  399. label: "汇率",
  400. prop: "exchangeRate",
  401. span: 8,
  402. slot: true
  403. },
  404. {
  405. label: "备注",
  406. prop: "orderRemark",
  407. type: "textarea",
  408. minRows: 2,
  409. span: 16
  410. }
  411. ]
  412. },
  413. treeOption: {
  414. nodeKey: "id",
  415. lazy: true,
  416. treeLoad: function(node, resolve) {
  417. const parentId = node.level === 0 ? 0 : node.data.id;
  418. getDeptLazyTree(parentId).then(res => {
  419. resolve(
  420. res.data.data.map(item => {
  421. return {
  422. ...item,
  423. leaf: !item.hasChildren
  424. };
  425. })
  426. );
  427. });
  428. },
  429. addBtn: false,
  430. menu: false,
  431. size: "small",
  432. props: {
  433. label: "title",
  434. value: "value",
  435. children: "children"
  436. }
  437. },
  438. page: {
  439. pageSize: 10,
  440. currentPage: 1,
  441. total: 0
  442. },
  443. loading: false,
  444. goodsOption: {},
  445. data: [],
  446. goodsList: [],
  447. selectionList: [],
  448. treeDeptId: null,
  449. itemtypeList: []
  450. };
  451. },
  452. props: {
  453. detailData: {
  454. type: Object
  455. }
  456. },
  457. components: {
  458. reportDialog
  459. },
  460. async created() {
  461. this.tableOption = await this.getColumnData(
  462. this.getColumnName(11),
  463. tableOption
  464. );
  465. this.goodsOption = await this.getColumnData(
  466. this.getColumnName(31),
  467. goodsOption
  468. );
  469. if (this.detailData.id) {
  470. this.getDetail(this.detailData.id);
  471. }
  472. if (this.detailData.status == 1) {
  473. this.option.disabled = true;
  474. }
  475. let _this = this;
  476. this.tableOption.column.forEach(e => {
  477. if (e.prop == "taxRate") {
  478. e.formatter = function(row) {
  479. return _this.textFormat(
  480. Number(row.taxRate ? row.taxRate : 0) / 100,
  481. "0.00%"
  482. );
  483. };
  484. }
  485. if (e.prop == "amount" || e.prop == "price") {
  486. e.formatter = function(row) {
  487. return _this.textFormat(
  488. Number(row.amount ? row.amount : 0),
  489. "#,##0.00"
  490. );
  491. };
  492. }
  493. });
  494. getPorts().then(res => {
  495. this.findObject(this.option.column, "portOfLoad").dicData = res.data;
  496. this.findObject(this.option.column, "portOfDestination").dicData =
  497. res.data;
  498. });
  499. },
  500. methods: {
  501. rowCell(row, index) {
  502. if (row.$cellEdit == true) {
  503. this.$set(row, "$cellEdit", false);
  504. } else {
  505. this.$set(row, "$cellEdit", true);
  506. }
  507. },
  508. itemTypeFocus(row) {
  509. this.itemtypeList = [];
  510. getSpecification({ goodId: row.itemId }).then(res => {
  511. const data = res.data.data;
  512. this.itemtypeList = data.map(item => ({ value: item }));
  513. });
  514. },
  515. priceChange(row) {
  516. console.log(row);
  517. if (!row.price) {
  518. row.price = 0;
  519. } else {
  520. row.amount = _.multiply(row.price, row.orderQuantity).toFixed(2);
  521. }
  522. },
  523. quantityChange(row) {
  524. if (!row.orderQuantity) {
  525. row.orderQuantity = 0;
  526. } else {
  527. row.amount = _.multiply(row.price, row.orderQuantity).toFixed(2);
  528. }
  529. },
  530. rateChange(row) {
  531. console.log(row);
  532. if (row >= 100) {
  533. this.form.exchangeRate = 0;
  534. this.$message.error("汇率不能超过100%");
  535. }
  536. },
  537. rowSave(row) {
  538. console.log(row);
  539. this.$set(row, "$cellEdit", false);
  540. },
  541. rowDel(row, index) {
  542. if (row.id) {
  543. delItem(row.id).then(res => {
  544. this.$message({
  545. type: "success",
  546. message: "操作成功!"
  547. });
  548. this.data.splice(index, 1);
  549. });
  550. } else {
  551. this.$message({
  552. type: "success",
  553. message: "操作成功!"
  554. });
  555. this.data.splice(index, 1);
  556. }
  557. },
  558. importGoods() {
  559. this.selectionList.forEach(e => {
  560. this.data.push({
  561. itemId: e.id,
  562. code: e.code,
  563. cname: e.cname,
  564. priceCategory: e.goodsTypeName,
  565. itemUrl: e.url,
  566. itemProp: null,
  567. itemDescription: null,
  568. itemType: null,
  569. orderQuantity: 0,
  570. tradeTerms: null,
  571. price: 0,
  572. amount: 0,
  573. taxRate: 0,
  574. unit: e.unit,
  575. remarks: null,
  576. $cellEdit: true
  577. });
  578. });
  579. this.dialogVisible = false;
  580. },
  581. closeGoods() {
  582. this.selectionList = [];
  583. this.treeDeptId = "";
  584. },
  585. selectionChange(list) {
  586. this.selectionList = list;
  587. },
  588. rowClick(row) {
  589. this.$refs.goodsCrud.toggleSelection([this.goodsList[row.$index]]);
  590. },
  591. nodeClick(data) {
  592. this.treeDeptId = data.id;
  593. this.page.currentPage = 1;
  594. this.onLoad(this.page);
  595. },
  596. //费用查询
  597. onLoad(page, params = {}) {
  598. this.loading = true;
  599. getGoods(page.currentPage, page.pageSize, this.treeDeptId).then(res => {
  600. const data = res.data.data;
  601. this.page.total = data.total;
  602. this.goodsList = data.records;
  603. this.loading = false;
  604. if (this.page.total) {
  605. this.goodsOption.height = window.innerHeight - 550;
  606. } else {
  607. this.goodsOption.height = window.innerHeight - 475;
  608. }
  609. });
  610. },
  611. //商品明细导入
  612. newDetails() {
  613. this.dialogVisible = !this.dialogVisible;
  614. },
  615. getDetail(id) {
  616. console.log(id);
  617. detail(id).then(res => {
  618. this.form = res.data.data;
  619. this.data = res.data.data.itemsVOList ? res.data.data.itemsVOList : [];
  620. this.configuration.dicData = this.form.corpsName;
  621. });
  622. },
  623. //修改提交触发
  624. editCustomer() {
  625. this.$refs["form"].validate((valid, done) => {
  626. done();
  627. if (valid) {
  628. submit({
  629. ...this.form,
  630. orderItemsList: this.data
  631. }).then(res => {
  632. this.$message.success(this.form.id ? "修改成功" : "提交成功");
  633. });
  634. } else {
  635. return false;
  636. }
  637. });
  638. },
  639. //返回列表
  640. backToList() {
  641. this.$emit("goBack");
  642. },
  643. openReport() {
  644. this.switchDialog = !this.switchDialog;
  645. },
  646. onClose(val) {
  647. this.switchDialog = val;
  648. },
  649. summaryMethod({ columns, data }) {
  650. const sums = [];
  651. if (columns.length > 0) {
  652. columns.forEach((item, index) => {
  653. sums[0] = "合计";
  654. if (item.property == "orderQuantity" || item.property == "amount") {
  655. let qtySum = 0;
  656. let amountSum = 0;
  657. data.forEach(e => {
  658. qtySum = _.add(qtySum, Number(e.orderQuantity));
  659. amountSum = _.add(amountSum, Number(e.amount));
  660. });
  661. //数量总计
  662. if (item.property == "orderQuantity") {
  663. sums[index] = qtySum ? qtySum.toFixed(2) : "0.00";
  664. }
  665. //金额总计
  666. if (item.property == "amount") {
  667. sums[index] = micrometerFormat(amountSum);
  668. }
  669. }
  670. });
  671. }
  672. return sums;
  673. },
  674. async saveColumn() {
  675. const inSave = await this.saveColumnData(
  676. this.getColumnName(11),
  677. this.tableOption
  678. );
  679. if (inSave) {
  680. this.$message.success("保存成功");
  681. //关闭窗口
  682. this.$refs.crud.$refs.dialogColumn.columnBox = false;
  683. }
  684. },
  685. async saveGoodsColumn() {
  686. const inSave = await this.saveColumnData(
  687. this.getColumnName(31),
  688. this.goodsOption
  689. );
  690. if (inSave) {
  691. this.$message.success("保存成功");
  692. //关闭窗口
  693. this.$refs.goodsCrud.$refs.dialogColumn.columnBox = false;
  694. }
  695. }
  696. }
  697. };
  698. </script>
  699. <style lang="scss" scoped>
  700. .customer-head {
  701. position: fixed;
  702. top: 105px;
  703. width: 100%;
  704. margin-left: -10px;
  705. height: 62px;
  706. background: #ffffff;
  707. box-shadow: 0 4px 12px 0px rgba(232, 232, 235, 1);
  708. z-index: 999;
  709. }
  710. .customer-back {
  711. cursor: pointer;
  712. line-height: 62px;
  713. font-size: 16px;
  714. color: #323233;
  715. font-weight: 400;
  716. }
  717. .back-icon {
  718. line-height: 64px;
  719. font-size: 20px;
  720. margin-right: 8px;
  721. }
  722. .copy-customer-btn {
  723. position: fixed;
  724. right: 140px;
  725. top: 115px;
  726. }
  727. .add-customer-btn {
  728. position: fixed;
  729. right: 36px;
  730. top: 115px;
  731. }
  732. ::v-deep .el-form-item {
  733. margin-bottom: 8px;
  734. }
  735. ::v-deep .el-form-item__error {
  736. display: none;
  737. }
  738. ::v-deep .select-component {
  739. display: flex;
  740. }
  741. </style>