PriceSheet.vue 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267
  1. <template>
  2. <div style="width: 100%; padding: 0px 15px">
  3. <byForm :formConfig="formConfig" :formOption="formOption" v-model="formData.data" :rules="rules" ref="formDom">
  4. <template #btn>
  5. <div>
  6. <el-button type="primary" v-if="
  7. [30].includes(route.query.processType) || !route.query.processType
  8. " @click="clickCopy(1)">复制合同</el-button>
  9. </div>
  10. </template>
  11. <template #seller>
  12. <div style="width: 100%">
  13. <el-form-item prop="sellCorporationId" label="卖方信息" class="wid100">
  14. <el-select v-model="formData.data.sellCorporationId" placeholder="请选择卖方公司" style="width: 100%" @change="sellCorporationIdChange"
  15. filterable>
  16. <el-option v-for="item in corporationList" :key="item.value" :label="item.label" :value="item.value" />
  17. </el-select>
  18. </el-form-item>
  19. <el-form-item label="地址" class="wid100">
  20. <el-row style="width:100%">
  21. <el-col :span="8">
  22. <el-form-item label="" prop="sellCountryName" label-width="0px" class="margin-b-0 wid100">
  23. <el-input v-model="formData.data.sellCountryName" placeholder="请输入国家" />
  24. </el-form-item>
  25. </el-col>
  26. <el-col :span="8">
  27. <el-form-item label="" prop="sellProvinceName" label-width="0px" class="margin-b-0 wid100">
  28. <el-input v-model="formData.data.sellProvinceName" placeholder="请输入省/州" />
  29. </el-form-item>
  30. </el-col>
  31. <el-col :span="8">
  32. <el-form-item label="" prop="sellCityName" label-width="0px" class="margin-b-0 wid100">
  33. <el-input v-model="formData.data.sellCityName" placeholder="请输入城市" />
  34. </el-form-item>
  35. </el-col>
  36. </el-row>
  37. </el-form-item>
  38. <el-form-item label="详细地址" prop="sellAddress" class="wid100">
  39. <el-input v-model="formData.data.sellAddress" type="textarea">
  40. </el-input>
  41. </el-form-item>
  42. <el-form-item label="联系人" class="wid100">
  43. <el-row style="width:100%">
  44. <el-col :span="8">
  45. <el-form-item label="" prop="sellContactName" label-width="0px" class="margin-b-0 wid100">
  46. <el-input v-model="formData.data.sellContactName" placeholder="请输入联系人" />
  47. </el-form-item>
  48. </el-col>
  49. <el-col :span="16">
  50. <el-form-item label="" prop="sellContactNumber" label-width="0px" class="margin-b-0 wid100">
  51. <el-input v-model="formData.data.sellContactNumber" placeholder="请输入联系人电话" />
  52. </el-form-item>
  53. </el-col>
  54. </el-row>
  55. </el-form-item>
  56. </div>
  57. </template>
  58. <template #buyer>
  59. <div style="width: 100%">
  60. <el-form-item label="买方信息" prop="buyCorporationId" class="wid100">
  61. <el-select v-model="formData.data.buyCorporationId" filterable remote reserve-keyword placeholder="请输入关键字" remote-show-suffix
  62. :remote-method="remoteMethod" :loading="loadingSearch" @input="remoteMethod" style="width: 100%" @change="changeCustomer" v-if="
  63. [30].includes(route.query.processType) ||
  64. !route.query.processType
  65. ">
  66. <el-option v-for="item in customerList" :key="item.value" :label="item.label" :value="item.value" />
  67. </el-select>
  68. <el-select v-model="formData.data.buyCorporationName" disabled v-else style="width: 100%">
  69. </el-select>
  70. </el-form-item>
  71. <el-form-item label="地址" class="wid100" required>
  72. <el-row style="width: 100%">
  73. <el-col :span="6">
  74. <el-form-item label="" prop="countryId" class="margin-b-0">
  75. <el-select v-model="formData.data.countryId" placeholder="国家" filterable @change="(val) => getCityData(val, '20', true)">
  76. <el-option v-for="item in countryData" :label="item.name" :value="item.id">
  77. </el-option>
  78. </el-select>
  79. </el-form-item>
  80. </el-col>
  81. <el-col :span="6">
  82. <el-form-item label="" prop="provinceName" class="margin-b-0">
  83. <selectCity placeholder="省/洲" @change="(val) => getCityData(val, '30', true)" addressId="provinceId" addressName="provinceName"
  84. v-model="formData.data" :data="provinceData">
  85. </selectCity>
  86. </el-form-item>
  87. </el-col>
  88. <el-col :span="6">
  89. <el-form-item label="" prop="cityName" class="margin-b-0">
  90. <selectCity placeholder="城市" addressId="cityId" addressName="cityName" v-model="formData.data" :data="cityData">
  91. </selectCity>
  92. </el-form-item>
  93. </el-col>
  94. <el-col :span="6">
  95. <el-form-item label="" prop="buyPostalCode" class="margin-b-0">
  96. <el-input v-model="formData.data.buyPostalCode" placeholder="请输入邮编" />
  97. </el-form-item>
  98. </el-col>
  99. </el-row>
  100. </el-form-item>
  101. <el-form-item label="详细地址" prop="buyAddress" class="wid100">
  102. <el-input v-model="formData.data.buyAddress" type="textarea">
  103. </el-input>
  104. </el-form-item>
  105. <el-form-item label="联系人" class="wid100" required>
  106. <el-row style="width: 100%">
  107. <el-col :span="8">
  108. <el-form-item label="" prop="buyContactName" label-width="0px" class="margin-b-0 wid100">
  109. <el-autocomplete v-model="formData.data.buyContactName" :fetch-suggestions="querySearchPerson" style="width:100%" clearable
  110. class="inline-input w-50" placeholder="请输入联系人" @select="handlePerson">
  111. </el-autocomplete>
  112. </el-form-item>
  113. </el-col>
  114. <el-col :span="16">
  115. <el-form-item label="" prop="buyContactNumber" label-width="0px" class="margin-b-0 wid100">
  116. <el-input v-model="formData.data.buyContactNumber" placeholder="请输入联系人电话" />
  117. </el-form-item>
  118. </el-col>
  119. </el-row>
  120. </el-form-item>
  121. </div>
  122. </template>
  123. <template #commodity>
  124. <div style="width: 100%">
  125. <el-button type="primary" @click="openProductCompany = true" plain style="margin-bottom: 16px" v-if="!judgeStatus()">标准产品库</el-button>
  126. <el-table :data="formData.data.quotationProductList" style="width: 100%; ">
  127. <el-table-column label="商品图片" width="80">
  128. <template #default="{ row }">
  129. <div v-if="row.fileUrl">
  130. <img :src="row.fileUrl" class="pic" @click="onPicture(row.fileUrl)" />
  131. </div>
  132. <div v-else></div>
  133. </template>
  134. </el-table-column>
  135. <el-table-column prop="productCnName" label="商品名称" min-width="130" />
  136. <el-table-column prop="productCode" label="商品编码" width="130" />
  137. <el-table-column label="尺寸 cm*cm*cm" width="180">
  138. <template #default="{ row, $index }">
  139. <div style="width: 100%">
  140. {{row.productLength}} * {{row.productWidth}} * {{row.productHeight}}
  141. </div>
  142. </template>
  143. </el-table-column>
  144. <el-table-column label="设计图稿" width="80">
  145. <template #default="{ row, $index }">
  146. <div style="width: 100%">
  147. <el-upload action="https://winfaster.obs.cn-south-1.myhuaweicloud.com" accept=".gif, .jpeg, .jpg, .png" :show-file-list="false"
  148. :data="uploadData" :before-upload="(file)=>handleBeforeUpload(file,$index)" :on-success="()=>handleSuccess($index)">
  149. <img v-if="row.imageUrl" :src="row.imageUrl" class="pic" />
  150. <el-icon v-else class="avatar-uploader-icon">
  151. <Plus />
  152. </el-icon>
  153. </el-upload>
  154. </div>
  155. </template>
  156. </el-table-column>
  157. <el-table-column label="生产源文件" width="140">
  158. <template #default="{ row, $index }">
  159. <!-- <span class="el-click" v-if="!row.prodFilePath" @click="handleClickUpload('prodFilePath',false)">点击</span> -->
  160. <span class="el-click" v-if="row.prodFilePath" @click="handleClickUpload('prodFilePath',false,$index)">点击上传 (查看)</span>
  161. </template>
  162. </el-table-column>
  163. <el-table-column label="数量" width="150">
  164. <template #default="{ row, $index }">
  165. <div style="width: 100%">
  166. <el-form-item :prop="'quotationProductList.' + $index + '.quantity'" :rules="rules.quantity" :inline-message="true"
  167. class="margin-b-0 wid100">
  168. <el-input-number onmousewheel="return false;" v-model="row.quantity" placeholder="请输入" style="width: 100%" :precision="0"
  169. :controls="false" :min="0" @change="calculationAmount('quantity')" />
  170. </el-form-item>
  171. </div>
  172. </template>
  173. </el-table-column>
  174. <el-table-column label="单价" width="150">
  175. <template #default="{ row, $index }">
  176. <div style="width: 100%">
  177. <el-form-item :prop="'quotationProductList.' + $index + '.price'" :rules="rules.price" :inline-message="true"
  178. class="margin-b-0 wid100">
  179. <div style="display:flex;">
  180. <el-input-number onmousewheel="return false;" v-model="row.price" placeholder="请输入" style="width: 100%" :precision="2"
  181. :controls="false" :min="0" @change="calculationAmount()" />
  182. <!-- <el-popover placement="top-start" :width="400" trigger="hover" @show="showEcharts(row,$index)">
  183. <template #default>
  184. <div>
  185. <div>
  186. <img src="@/assets/images/money1.png" alt="" class="img" /> <span
  187. style="font-size:14px;font-weight:700;color:#000">销售指导价:</span>
  188. <div style="padding:5px 0px 0px 20px">
  189. {{row.currency}} {{moneyFormat(row.salePrice,2)}}
  190. </div>
  191. </div>
  192. <div style="margin-top:15px">
  193. <img src="@/assets/images/money2.png" alt="" class="img" /> <span
  194. style="font-size:14px;font-weight:700;color:#000">客户近期购买价格:</span>
  195. <div style="padding:5px 0px 0px 20px">
  196. <div v-for="item in row.customerContractProductList">
  197. <span>{{item.createTime.slice(0,10)}} </span>
  198. <span style="margin-left:40px">{{item.code}} </span>
  199. <span style="margin-left:40px"> {{item.currency}} {{moneyFormat(item.price,2)}} </span>
  200. </div>
  201. </div>
  202. </div>
  203. <div style="margin-top:15px">
  204. <img src="@/assets/images/money3.png" alt="" class="img" /> <span
  205. style="font-size:14px;font-weight:700;color:#000">产品近期销售价格:</span>
  206. <div style="padding:5px 0px 0px 20px">
  207. <div v-for="item in row.quotationProductList">
  208. <span>{{item.createTime.slice(0,10)}} </span>
  209. <span style="margin-left:40px">{{item.code}} </span>
  210. <span style="margin-left:40px">{{item.currency}} {{moneyFormat(item.price,2)}} </span>
  211. </div>
  212. </div>
  213. </div>
  214. <div :ref="row.productId+$index" style="height:180px">
  215. </div>
  216. </div>
  217. </template>
  218. <template #reference>
  219. <div style="margin-left:10px;cursor:pointer;position:relative;top:4px">
  220. <el-icon :size="20" color="#85c1a6">
  221. <WarningFilled />
  222. </el-icon>
  223. </div>
  224. </template>
  225. </el-popover> -->
  226. </div>
  227. </el-form-item>
  228. </div>
  229. </template>
  230. </el-table-column>
  231. <el-table-column prop="amount" label="小计" width="120" />
  232. <el-table-column label="操作" width="120" align="center" fixed="right" v-if="!judgeStatus()">
  233. <template #default="{ $index }">
  234. <el-button type="primary" link @click="handleClickUpload('prodFilePath',true,$index)">重置</el-button>
  235. <el-button type="primary" link @click="handleRemove($index)">删除</el-button>
  236. </template>
  237. </el-table-column>
  238. </el-table>
  239. </div>
  240. </template>
  241. <template #otherCharge>
  242. <div style="width: 100%">
  243. <el-button type="primary" @click="clickAdd()" plain style="margin-bottom: 16px" v-if="!judgeStatus()">添加行</el-button>
  244. <el-table :data="formData.data.quotationPayList" style="width: 100%;">
  245. <el-table-column label="收费项目" width="220">
  246. <template #default="{ row, $index }">
  247. <div style="width: 100%">
  248. <el-form-item :prop="'quotationPayList.' + $index + '.payName'" :rules="rules.payName" :inline-message="true"
  249. class="margin-b-0 wid100">
  250. <el-autocomplete v-model="row.payName" :fetch-suggestions="querySearch" clearable class="inline-input w-50"
  251. placeholder="请输入收费项目" />
  252. </el-form-item>
  253. </div>
  254. </template>
  255. </el-table-column>
  256. <el-table-column label="备注">
  257. <template #default="{ row, $index }">
  258. <div style="width: 100%">
  259. <el-form-item :prop="'quotationPayList.' + $index + '.remark'" class="margin-b-0 wid100">
  260. <el-input v-model="row.remark" placeholder="请输入备注" />
  261. </el-form-item>
  262. </div>
  263. </template>
  264. </el-table-column>
  265. <el-table-column :label="'金额'" width="130">
  266. <template #default="{ row, $index }">
  267. <div style="width: 100%">
  268. <el-form-item :prop="'quotationPayList.' + $index + '.amount'" :rules="rules.amount" :inline-message="true"
  269. class="margin-b-0 wid100">
  270. <el-input-number onmousewheel="return false;" v-model="row.amount" placeholder="请输入金额" style="width: 100%" :precision="2"
  271. :controls="false" :min="0" @change="totalAmount()" />
  272. </el-form-item>
  273. </div>
  274. </template>
  275. </el-table-column>
  276. <el-table-column label="操作" width="60" align="center" fixed="right" v-if="!judgeStatus()">
  277. <template #default="{ $index }">
  278. <el-button type="primary" link @click="handleDelete($index)">删除</el-button>
  279. </template>
  280. </el-table-column>
  281. </el-table>
  282. </div>
  283. </template>
  284. </byForm>
  285. <el-dialog v-if="openProductCompany" v-model="openProductCompany" title="公司产品库" width="90%" append-to-body>
  286. <!-- <SelectCompanyProduct @selectProduct="selectProduct" :alreadySelectData="formData.data.quotationProductList"></SelectCompanyProduct> -->
  287. <SelectProduct @selectProduct="selectProduct"></SelectProduct>
  288. </el-dialog>
  289. <!-- <el-dialog v-if="copyContract" v-model="copyContract" :title="copyType === 1 ? '合同选择' : '样品单选择'" width="90%" append-to-body>
  290. <SelectContract @select="selectContract" v-if="copyType === 1"></SelectContract>
  291. <SelectSample @select="selectContract" v-if="copyType === 2"></SelectSample>
  292. </el-dialog> -->
  293. </div>
  294. </template>
  295. <script setup>
  296. import byForm from "@/components/byForm/index";
  297. import SelectCompanyProduct from "@/components/product/SelectCompanyProduct.vue";
  298. import SelectProduct from "@/components/product/SelectProduct.vue";
  299. // import SelectCustomerProduct from "@/components/product/SelectCustomerProduct.vue";
  300. import selectCity from "@/components/selectCity/index.vue";
  301. import { useRoute } from "vue-router";
  302. import SelectContract from "@/components/contractCom/selectContract.vue";
  303. import SelectSample from "@/components/contractCom/selectSample.vue";
  304. // import * as echarts from "echarts";
  305. // import $bus from "@/bus/index.js";
  306. const route = useRoute();
  307. const { proxy } = getCurrentInstance();
  308. // 接收父组件的传值
  309. const props = defineProps({
  310. queryData: Object,
  311. });
  312. const currencyData = computed(
  313. () => proxy.useUserStore().allDict["account_currency"]
  314. );
  315. const fundsPaymentMethod = computed(
  316. () => proxy.useUserStore().allDict["funds_payment_method"]
  317. );
  318. const shippingMethod = computed(
  319. () => proxy.useUserStore().allDict["shipping_method"]
  320. );
  321. const accountList = ref([]);
  322. const customerList = ref([]);
  323. const corporationList = ref([]);
  324. const customerUserList = ref([]);
  325. const countryData = ref([]);
  326. const provinceData = ref([]);
  327. const cityData = ref([]);
  328. const openProductCompany = ref(false);
  329. const copyType = ref(1);
  330. const copyContract = ref(false);
  331. const formData = reactive({
  332. data: {
  333. contractType: "2",
  334. rate: 1,
  335. quotationProductList: [],
  336. },
  337. });
  338. const uploadData = ref({});
  339. const formDom = ref(null);
  340. const judgeStatus = () => {
  341. if (route.query.processType == 20 || route.query.processType == 10) {
  342. return true;
  343. }
  344. if (props.queryData.recordList && props.queryData.recordList.length > 0) {
  345. let data = props.queryData.recordList.filter(
  346. (item) => item.status === 2 && item.nodeType !== 1
  347. );
  348. if (data && data.length > 0) {
  349. return true;
  350. }
  351. }
  352. return false;
  353. };
  354. const formOption = reactive({
  355. inline: true,
  356. labelWidth: 100,
  357. itemWidth: 100,
  358. disabled: false,
  359. });
  360. const formConfig = computed(() => {
  361. return [
  362. // {
  363. // type: "slot",
  364. // slotName: "btn",
  365. // label: "",
  366. // itemWidth: 50,
  367. // },
  368. {
  369. type: "title",
  370. title: "合同类型",
  371. },
  372. {
  373. type: "select",
  374. prop: "contractType",
  375. label: "合同类型",
  376. data: [
  377. {
  378. dictKey: "2",
  379. dictValue: "内销",
  380. },
  381. {
  382. dictKey: "1",
  383. dictValue: "外销",
  384. },
  385. ],
  386. fn: (val) => {
  387. if (val == "2") {
  388. formData.data.currency = currencyData.value[0].dictKey;
  389. formData.data.rate = 1;
  390. }
  391. if (formData.data.sellCorporationId) {
  392. sellCorporationIdChange(formData.data.sellCorporationId);
  393. }
  394. if (formData.data.shroffAccountId) {
  395. changeShroffAccount(formData.data.shroffAccountId);
  396. }
  397. },
  398. },
  399. {
  400. type: "title",
  401. title: "贸易信息",
  402. haveLine: true,
  403. },
  404. {
  405. type: "slot",
  406. slotName: "seller",
  407. label: "",
  408. itemWidth: 50,
  409. },
  410. {
  411. type: "slot",
  412. slotName: "buyer",
  413. label: "",
  414. itemWidth: 50,
  415. },
  416. {
  417. type: "title",
  418. title: "付款信息",
  419. haveLine: true,
  420. },
  421. {
  422. type: "select",
  423. prop: "currency",
  424. label: "币种",
  425. data: currencyData.value,
  426. itemWidth: 25,
  427. disabled: formData.data.contractType == "2",
  428. },
  429. {
  430. type: "number",
  431. prop: "rate",
  432. label: "汇率",
  433. precision: 2,
  434. min: 0,
  435. controls: false,
  436. itemWidth: 25,
  437. disabled: formData.data.contractType == "2",
  438. },
  439. {
  440. type: "select",
  441. prop: "paymentMethod",
  442. label: "付款方式",
  443. data: fundsPaymentMethod.value,
  444. itemWidth: 25,
  445. },
  446. {
  447. type: "number",
  448. prop: "advanceRatio",
  449. label: "预付款比列(%)",
  450. precision: 2,
  451. min: 0,
  452. controls: false,
  453. itemWidth: 25,
  454. },
  455. {
  456. type: "input",
  457. prop: "remark",
  458. label: "付款条件",
  459. itemType: "textarea",
  460. itemWidth: 50,
  461. },
  462. {
  463. type: "select",
  464. prop: "shroffAccountId",
  465. label: "收款账号",
  466. data: accountList.value,
  467. itemWidth: 100,
  468. fn: (val) => {
  469. changeShroffAccount(val);
  470. },
  471. },
  472. {
  473. type: "input",
  474. prop: "beneficiaryName",
  475. label: " ",
  476. placeholder: "请输入Beneficiary Name",
  477. itemWidth: 50,
  478. isShow: formData.data.contractType == "1",
  479. },
  480. {
  481. type: "input",
  482. prop: "beneficiaryAccountNumber",
  483. label: " ",
  484. placeholder: "请输入Beneficiary Account Number",
  485. itemWidth: 50,
  486. isShow: formData.data.contractType == "1",
  487. },
  488. {
  489. type: "input",
  490. prop: "beneficiaryBank",
  491. label: " ",
  492. placeholder: "请输入Beneficiary Bank",
  493. itemWidth: 50,
  494. isShow: formData.data.contractType == "1",
  495. },
  496. {
  497. type: "input",
  498. prop: "swiftCode",
  499. label: " ",
  500. placeholder: "请输入Swift Code",
  501. itemWidth: 50,
  502. isShow: formData.data.contractType == "1",
  503. },
  504. {
  505. type: "input",
  506. prop: "beneficiaryBankAddress",
  507. label: " ",
  508. placeholder: "请输入Beneficiary Bank Address",
  509. itemWidth: 50,
  510. isShow: formData.data.contractType == "1",
  511. },
  512. {
  513. type: "input",
  514. prop: "beneficiaryAddress",
  515. label: " ",
  516. placeholder: "请输入Beneficiary Address",
  517. itemWidth: 50,
  518. isShow: formData.data.contractType == "1",
  519. },
  520. {
  521. type: "input",
  522. prop: "accountName",
  523. label: "户名",
  524. placeholder: "请输入户名",
  525. itemWidth: 50,
  526. isShow: formData.data.contractType == "2",
  527. },
  528. {
  529. type: "input",
  530. prop: "openingBank",
  531. label: "开户行",
  532. placeholder: "请输入开户行",
  533. itemWidth: 50,
  534. isShow: formData.data.contractType == "2",
  535. },
  536. {
  537. type: "input",
  538. prop: "accountOpening",
  539. label: "账号",
  540. placeholder: "请输入账号",
  541. itemWidth: 50,
  542. isShow: formData.data.contractType == "2",
  543. },
  544. // {
  545. // type: "slot",
  546. // slotName: "payment",
  547. // label: "",
  548. // },
  549. {
  550. type: "title",
  551. title: "交付信息",
  552. haveLine: true,
  553. },
  554. {
  555. type: "date",
  556. prop: "deliveryTime",
  557. label: "交货期限",
  558. itemWidth: 50,
  559. },
  560. {
  561. type: "select",
  562. prop: "transportMethod",
  563. label: "运输方式",
  564. data: shippingMethod.value,
  565. itemWidth: 50,
  566. },
  567. {
  568. type: "input",
  569. prop: "transportRemark",
  570. label: "运输说明",
  571. itemWidth: 50,
  572. },
  573. // {
  574. // type: "slot",
  575. // slotName: "delivery",
  576. // label: "",
  577. // },
  578. {
  579. type: "title",
  580. title: "商品信息",
  581. haveLine: true,
  582. },
  583. {
  584. type: "slot",
  585. slotName: "commodity",
  586. label: "",
  587. },
  588. {
  589. type: "title",
  590. title: "其他收费项目",
  591. haveLine: true,
  592. },
  593. {
  594. type: "slot",
  595. slotName: "otherCharge",
  596. label: "",
  597. },
  598. {
  599. type: "title",
  600. title: "合同总金额",
  601. haveLine: true,
  602. },
  603. {
  604. type: "input",
  605. prop: "amount",
  606. label: "合同总金额",
  607. itemWidth: 25,
  608. disabled: true,
  609. },
  610. ];
  611. });
  612. const rules = ref({
  613. contractType: [
  614. { required: true, message: "请选择合同类型", trigger: "change" },
  615. ],
  616. sellCorporationId: [
  617. { required: true, message: "请选择卖方公司", trigger: "change" },
  618. ],
  619. buyCorporationId: [
  620. { required: true, message: "请选择买方公司", trigger: "change" },
  621. ],
  622. countryId: [{ required: true, message: "请选择国家", trigger: "change" }],
  623. sellAddress: [{ required: true, message: "请输入详细地址", trigger: "blur" }],
  624. buyAddress: [{ required: true, message: "请输入详细地址", trigger: "blur" }],
  625. buyContactName: [
  626. { required: true, message: "请输入联系人", trigger: ["change", "blur"] },
  627. ],
  628. buyContactNumber: [
  629. { required: true, message: "请输入联系电话", trigger: "blur" },
  630. ],
  631. quantity: [{ required: true, message: "请输入数量", trigger: "blur" }],
  632. price: [{ required: true, message: "请输入单价", trigger: "blur" }],
  633. payName: [
  634. { required: true, message: "请输入收费项目", trigger: ["change", "blur"] },
  635. ],
  636. currency: [{ required: true, message: "请选择币种", trigger: "change" }],
  637. paymentMethod: [
  638. { required: true, message: "请选择付款方式", trigger: "change" },
  639. ],
  640. advanceRatio: [
  641. { required: true, message: "请输入预付比例", trigger: "blur" },
  642. ],
  643. transportMethod: [
  644. { required: true, message: "请选择运输方式", trigger: "change" },
  645. ],
  646. remark: [{ required: true, message: "请输入付款条件", trigger: "blur" }],
  647. rate: [{ required: true, message: "请输入汇率", trigger: "blur" }],
  648. shroffAccountId: [
  649. { required: true, message: "请选择收款账号", trigger: "change" },
  650. ],
  651. });
  652. const getDict = () => {
  653. proxy
  654. .post("/customer/selPage", {
  655. pageNum: 1,
  656. pageSize: 50,
  657. })
  658. .then((res) => {
  659. customerList.value = res.rows.map((x) => ({
  660. ...x,
  661. label: x.name,
  662. value: x.id,
  663. }));
  664. });
  665. proxy.post("/corporation/page", { pageNum: 1, pageSize: 999 }).then((res) => {
  666. corporationList.value = res.rows.map((item) => {
  667. return {
  668. ...item,
  669. label: item.name,
  670. value: item.id,
  671. };
  672. });
  673. });
  674. proxy
  675. .post("/accountManagement/page", { pageNum: 1, pageSize: 999 })
  676. .then((res) => {
  677. accountList.value = res.rows.map((item) => {
  678. return {
  679. ...item,
  680. label: item.alias,
  681. value: item.id,
  682. };
  683. });
  684. });
  685. };
  686. const getCityData = (id, type, isChange) => {
  687. proxy.post("/customizeArea/list", { parentId: id }).then((res) => {
  688. if (type === "20") {
  689. provinceData.value = res;
  690. if (isChange) {
  691. formData.data.provinceId = "";
  692. formData.data.provinceName = "";
  693. formData.data.cityId = "";
  694. formData.data.cityName = "";
  695. }
  696. } else if (type === "30") {
  697. cityData.value = res;
  698. if (isChange) {
  699. formData.data.cityId = "";
  700. formData.data.cityName = "";
  701. }
  702. } else {
  703. countryData.value = res;
  704. }
  705. });
  706. };
  707. getDict();
  708. getCityData("0");
  709. const sellCorporationIdChange = (val) => {
  710. if (val) {
  711. proxy.post("/corporation/detail", { id: val }).then((res) => {
  712. if (formData.data.contractType == "2") {
  713. formData.data.sellCountryName = res.countryName;
  714. formData.data.sellProvinceName = res.provinceName;
  715. formData.data.sellCityName = res.cityName;
  716. formData.data.sellAddress = res.address;
  717. } else {
  718. formData.data.sellCountryName = res.countryEnStr;
  719. formData.data.sellProvinceName = res.provinceEnStr;
  720. formData.data.sellCityName = res.cityEnStr;
  721. formData.data.sellAddress = res.addressEn;
  722. }
  723. });
  724. }
  725. };
  726. const changeCustomer = (val) => {
  727. formData.data.quotationProductList = [];
  728. if (val) {
  729. proxy.post("/customer/detail", { id: val }).then(
  730. (res) => {
  731. formData.data.buyCorporationName = res.name;
  732. if (res.customerUserList && res.customerUserList.length > 0) {
  733. formData.data.buyContactName = res.customerUserList[0].name;
  734. if (res.customerUserList[0].contactJson) {
  735. let contactJson = JSON.parse(res.customerUserList[0].contactJson);
  736. if (contactJson && contactJson.length > 0) {
  737. formData.data.buyContactNumber = contactJson[0].contactNo;
  738. }
  739. }
  740. customerUserList.value = res.customerUserList.map((item) => {
  741. return {
  742. ...item,
  743. value: item.name,
  744. };
  745. });
  746. }
  747. // 回填客户的账户信息
  748. // formData.data.beneficiaryName = res.beneficiaryName;
  749. // formData.data.beneficiaryBank = res.beneficiaryBank;
  750. // formData.data.beneficiaryBankAddress = res.beneficiaryBankAddress;
  751. // formData.data.beneficiaryAccountNumber = res.beneficiaryAccountNumber;
  752. // formData.data.swiftCode = res.swiftCode;
  753. // formData.data.beneficiaryAddress = res.beneficiaryAddress;
  754. formData.data.countryId = res.countryId;
  755. formData.data.provinceId = res.provinceId;
  756. formData.data.cityId = res.cityId;
  757. formData.data.buyPostalCode = res.zipCode;
  758. formData.data.buyAddress = res.address;
  759. getCityData(formData.data.countryId, "20");
  760. if (formData.data.provinceId) {
  761. getCityData(formData.data.provinceId, "30");
  762. }
  763. },
  764. (err) => {
  765. formData.data.countryId = "";
  766. formData.data.provinceId = "";
  767. formData.data.cityId = "";
  768. formData.data.buyPostalCode = "";
  769. formData.data.buyAddress = "";
  770. }
  771. );
  772. } else {
  773. formData.data.countryId = "";
  774. formData.data.provinceId = "";
  775. formData.data.cityId = "";
  776. formData.data.buyPostalCode = "";
  777. formData.data.buyAddress = "";
  778. }
  779. };
  780. const createFilter = (queryString) => {
  781. return (restaurant) => {
  782. return (
  783. restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0
  784. );
  785. };
  786. };
  787. const querySearchPerson = (queryString, callback) => {
  788. const results = queryString
  789. ? customerUserList.value.filter(createFilter(queryString))
  790. : customerUserList.value;
  791. callback(results);
  792. };
  793. const handlePerson = (item) => {
  794. if (item.contactJson) {
  795. let data = JSON.parse(item.contactJson);
  796. formData.data.buyContactNumber = data[0].contactNo;
  797. }
  798. };
  799. const selectProduct = (goods) => {
  800. if (goods && goods.id) {
  801. let fileUrl = "";
  802. if (goods.fileList && goods.fileList.length > 0) {
  803. fileUrl = goods.fileList[0].fileUrl;
  804. }
  805. formData.data.quotationProductList.push({
  806. fileUrl: fileUrl,
  807. productId: goods.id,
  808. productCnName: goods.name,
  809. productCode: goods.customCode,
  810. productLength: goods["length"],
  811. productWidth: goods.width,
  812. productHeight: goods.height,
  813. prodFilePath: goods.prodFilePath,
  814. quantity: null,
  815. price: null,
  816. amount: "",
  817. fileList: [],
  818. });
  819. proxy.msgTip("添加成功", 1);
  820. } else {
  821. return proxy.msgTip("选择错误", 2);
  822. }
  823. };
  824. const changeProductPrice = () => {
  825. let productIds = formData.data.quotationProductList.map((x) => x.productId);
  826. if (productIds && productIds.length > 0) {
  827. proxy
  828. .post("/contract/getProductPriceInfo", {
  829. productIds: productIds,
  830. customerId: formData.data.buyCorporationId
  831. ? formData.data.buyCorporationId
  832. : "",
  833. })
  834. .then((res) => {
  835. for (let i = 0; i < formData.data.quotationProductList.length; i++) {
  836. const iele = formData.data.quotationProductList[i];
  837. for (const key in res) {
  838. if (key == iele.productId) {
  839. iele.salePrice = res[key].price;
  840. iele.currency = res[key].currency;
  841. iele.saleCostPrice = res[key].costPrice;
  842. iele.quotationProductList = res[key].quotationProductList
  843. .map((x) => ({
  844. createTime: x.createTime,
  845. price: x.price,
  846. currency: x.currency,
  847. }))
  848. .filter((y, index) => index < 3);
  849. iele.contractProductListOne = res[key].quotationProductList.map(
  850. (x) => ({
  851. createTime: x.createTime,
  852. price: x.price,
  853. currency: x.currency,
  854. })
  855. );
  856. iele.customerContractProductList = res[
  857. key
  858. ].customerContractProductList.map((x) => ({
  859. createTime: x.createTime,
  860. price: x.price,
  861. currency: x.currency,
  862. }));
  863. }
  864. }
  865. }
  866. });
  867. }
  868. };
  869. const onPicture = (path) => {
  870. window.open(path, "_blank");
  871. };
  872. const handleRemove = (index) => {
  873. formData.data.quotationProductList.splice(index, 1);
  874. totalAmount();
  875. };
  876. const calculationAmount = (att = "") => {
  877. nextTick(() => {
  878. if (
  879. formData.data.quotationProductList &&
  880. formData.data.quotationProductList.length > 0
  881. ) {
  882. for (let i = 0; i < formData.data.quotationProductList.length; i++) {
  883. let money = 0;
  884. money = parseFloat(
  885. Number(formData.data.quotationProductList[i].quantity) *
  886. Number(formData.data.quotationProductList[i].price)
  887. ).toFixed(2);
  888. formData.data.quotationProductList[i].amount = money;
  889. }
  890. }
  891. nextTick(() => {
  892. totalAmount();
  893. });
  894. });
  895. };
  896. const totalAmount = () => {
  897. let money = 0;
  898. if (
  899. formData.data.quotationProductList &&
  900. formData.data.quotationProductList.length > 0
  901. ) {
  902. for (let i = 0; i < formData.data.quotationProductList.length; i++) {
  903. if (formData.data.quotationProductList[i].amount) {
  904. money = parseFloat(
  905. Number(money) + Number(formData.data.quotationProductList[i].amount)
  906. ).toFixed(2);
  907. }
  908. }
  909. }
  910. if (
  911. formData.data.quotationPayList &&
  912. formData.data.quotationPayList.length > 0
  913. ) {
  914. for (let i = 0; i < formData.data.quotationPayList.length; i++) {
  915. if (formData.data.quotationPayList[i].amount) {
  916. money = parseFloat(
  917. Number(money) + Number(formData.data.quotationPayList[i].amount)
  918. ).toFixed(2);
  919. }
  920. }
  921. }
  922. formData.data.amount = money;
  923. };
  924. const clickAdd = () => {
  925. if (
  926. formData.data.quotationPayList &&
  927. formData.data.quotationPayList.length > 0
  928. ) {
  929. formData.data.quotationPayList.push({
  930. payName: "",
  931. amount: null,
  932. remark: "",
  933. });
  934. } else {
  935. formData.data.quotationPayList = [
  936. { payName: "", amount: null, remark: "" },
  937. ];
  938. }
  939. };
  940. const handleDelete = (index) => {
  941. formData.data.quotationPayList.splice(index, 1);
  942. totalAmount();
  943. };
  944. const querySearch = (queryString, callback) => {
  945. proxy.post("/quotationPay/page", { payName: queryString }).then((res) => {
  946. if (res.rows && res.rows.length > 0) {
  947. res.rows = res.rows.map((item) => {
  948. return {
  949. ...item,
  950. value: item.payName,
  951. };
  952. });
  953. callback(res.rows);
  954. } else {
  955. callback([]);
  956. }
  957. });
  958. };
  959. const handleBeforeUpload = async (file, index) => {
  960. const res = await proxy.post("/fileInfo/getSing", { fileName: file.name });
  961. uploadData.value = res.uploadBody;
  962. formData.data.quotationProductList[index].fileList = [
  963. {
  964. id: res.id,
  965. fileName: res.fileName,
  966. fileUrl: res.fileUrl,
  967. uploadState: false,
  968. },
  969. ];
  970. formData.data.quotationProductList[index].imageUrl = res.fileUrl;
  971. return true;
  972. };
  973. const handleSuccess = (index) => {
  974. formData.data.quotationProductList[index].uploadState = true;
  975. };
  976. const loadingSearch = ref(false);
  977. const remoteMethod = (keyword) => {
  978. if (keyword && typeof keyword === "string") {
  979. loadingSearch.value = true;
  980. proxy.post("/customer/selPage", { keyword }).then((res) => {
  981. customerList.value = res.rows.map((x) => ({
  982. ...x,
  983. label: x.name,
  984. value: x.id,
  985. }));
  986. loadingSearch.value = false;
  987. });
  988. }
  989. return;
  990. };
  991. const handleSubmit = async () => {
  992. let flag = await formDom.value.handleSubmit(() => {});
  993. if (flag) {
  994. if (
  995. formData.data.quotationProductList &&
  996. formData.data.quotationProductList.length > 0
  997. ) {
  998. return true;
  999. } else {
  1000. proxy.msgTip("请添加至少一件商品", 2);
  1001. return false;
  1002. }
  1003. } else {
  1004. setTimeout(() => {
  1005. const errorDiv = document.getElementsByClassName("is-error");
  1006. errorDiv[0].scrollIntoView();
  1007. }, 0);
  1008. }
  1009. return flag;
  1010. };
  1011. const getFormData = () => {
  1012. return proxy.deepClone(formData.data);
  1013. };
  1014. // 向父组件暴露
  1015. defineExpose({
  1016. getFormData,
  1017. handleSubmit,
  1018. });
  1019. const changeShroffAccount = (val) => {
  1020. if (val) {
  1021. let data = accountList.value.find((item) => item.value == val);
  1022. if (formData.data.contractType == "2") {
  1023. if (data) {
  1024. formData.data.beneficiaryName = "";
  1025. formData.data.beneficiaryBank = "";
  1026. formData.data.beneficiaryBankAddress = "";
  1027. formData.data.beneficiaryAccountNumber = "";
  1028. formData.data.swiftCode = "";
  1029. formData.data.beneficiaryAddress = "";
  1030. formData.data.accountName = data.name;
  1031. formData.data.openingBank = data.openingBank;
  1032. formData.data.accountOpening = data.accountOpening;
  1033. }
  1034. } else {
  1035. if (data) {
  1036. formData.data.accountName = "";
  1037. formData.data.openingBank = "";
  1038. formData.data.accountOpening = "";
  1039. formData.data.beneficiaryName = data.beneficiaryName;
  1040. formData.data.beneficiaryBank = data.beneficiaryBank;
  1041. formData.data.beneficiaryBankAddress = data.beneficiaryBankAddress;
  1042. formData.data.beneficiaryAccountNumber = data.beneficiaryAccountNumber;
  1043. formData.data.swiftCode = data.swiftCode;
  1044. formData.data.beneficiaryAddress = data.beneficiaryAddress;
  1045. }
  1046. }
  1047. }
  1048. };
  1049. const handleClickUpload = async (att, flag, index) => {
  1050. let res = null;
  1051. let path = "";
  1052. if (flag) {
  1053. proxy.msgTip("请稍后", 2);
  1054. res = await proxy.post("/fileService/createTempFolder");
  1055. if (res && res.path) {
  1056. formData.data.quotationProductList[index][att] = res.path;
  1057. path = res.path;
  1058. }
  1059. } else {
  1060. path = formData.data.quotationProductList[index][att];
  1061. }
  1062. let a = document.createElement("a");
  1063. a.href = "printer://" + "ftp://192.168.1.13/" + path + "/";
  1064. a.style.display = "none";
  1065. document.body.appendChild(a);
  1066. a.click();
  1067. document.body.removeChild(a);
  1068. };
  1069. const getAllData = (businessId) => {
  1070. proxy.post("/saleQuotation/detail", { id: businessId }).then((res) => {
  1071. res.countryId = res.buyCountryId;
  1072. res.provinceId = res.buyProvinceId;
  1073. res.cityId = res.buyCityId;
  1074. for (const key in res) {
  1075. formData.data[key] = res[key];
  1076. }
  1077. if (
  1078. formData.data.quotationProductList &&
  1079. formData.data.quotationProductList.length > 0
  1080. ) {
  1081. let productIds = formData.data.quotationProductList.map(
  1082. (x) => x.productId
  1083. );
  1084. proxy
  1085. .post("/fileInfo/getList", {
  1086. businessIdList: productIds,
  1087. })
  1088. .then((fileObj) => {
  1089. for (let i = 0; i < formData.data.quotationProductList.length; i++) {
  1090. const e = formData.data.quotationProductList[i];
  1091. for (const key in fileObj) {
  1092. if (e.productId === key) {
  1093. if (fileObj[key] && fileObj[key].length > 0) {
  1094. e.fileUrl = fileObj[key][0].fileUrl;
  1095. }
  1096. }
  1097. }
  1098. }
  1099. });
  1100. let ids = formData.data.quotationProductList.map((x) => x.id);
  1101. proxy.getFile(
  1102. ids,
  1103. formData.data.quotationProductList,
  1104. "id",
  1105. "fileList",
  1106. "imageUrl"
  1107. );
  1108. }
  1109. if (formData.data.countryId) {
  1110. getCityData(formData.data.countryId, "20");
  1111. }
  1112. if (formData.data.provinceId) {
  1113. getCityData(formData.data.provinceId, "30");
  1114. }
  1115. });
  1116. };
  1117. onMounted(() => {
  1118. if (currencyData.value && currencyData.value.length > 0) {
  1119. formData.data.currency = currencyData.value[0].dictKey;
  1120. }
  1121. formOption.disabled = judgeStatus();
  1122. if (route.query && route.query.businessId && route.query.processType) {
  1123. let businessId = route.query.businessId;
  1124. getAllData(businessId);
  1125. }
  1126. });
  1127. watch(
  1128. () => props.queryData,
  1129. (val) => {
  1130. nextTick(() => {
  1131. formOption.disabled = judgeStatus();
  1132. });
  1133. if (val.businessId && val.processType) {
  1134. getAllData(val.businessId);
  1135. }
  1136. },
  1137. {
  1138. deep: true,
  1139. immediate: true,
  1140. }
  1141. );
  1142. const showPriceInfo = () => {
  1143. if (props.queryData.processType) {
  1144. return false;
  1145. }
  1146. if (route.query.processType) {
  1147. return false;
  1148. } else {
  1149. return true;
  1150. }
  1151. };
  1152. const optionTwo = reactive({
  1153. data: {
  1154. tooltip: {
  1155. trigger: "axis",
  1156. },
  1157. // legend: {
  1158. // data: ["价格"],
  1159. // },
  1160. grid: {
  1161. left: "3%",
  1162. right: "4%",
  1163. top: "10%",
  1164. bottom: "3%",
  1165. containLabel: true,
  1166. },
  1167. // toolbox: {
  1168. // feature: {
  1169. // saveAsImage: {},
  1170. // },
  1171. // },
  1172. xAxis: {
  1173. type: "category",
  1174. boundaryGap: false,
  1175. data: [],
  1176. },
  1177. yAxis: {
  1178. type: "value",
  1179. },
  1180. series: [
  1181. {
  1182. name: "价格",
  1183. type: "line",
  1184. data: [],
  1185. },
  1186. ],
  1187. },
  1188. });
  1189. const showEcharts = (row, index) => {
  1190. let myChart = null;
  1191. myChart = echarts.init(proxy.$refs[row.productId + index]);
  1192. window.addEventListener("resize", () => {
  1193. myChart.resize();
  1194. });
  1195. if (row.contractProductListOne && row.contractProductListOne.length > 0) {
  1196. optionTwo.data.xAxis.data = row.contractProductListOne.map((item) => {
  1197. return item.createTime.slice(0, 10);
  1198. });
  1199. optionTwo.data.series[0].data = row.contractProductListOne.map((item) => {
  1200. return item.price;
  1201. });
  1202. } else {
  1203. optionTwo.data.xAxis.data = [];
  1204. optionTwo.data.series[0].data = [];
  1205. }
  1206. myChart.setOption(optionTwo.data);
  1207. myChart.resize();
  1208. };
  1209. const clickCopy = (type) => {
  1210. copyType.value = type;
  1211. copyContract.value = true;
  1212. };
  1213. </script>
  1214. <style lang="scss" scoped>
  1215. .img {
  1216. object-fit: contain;
  1217. width: 16px;
  1218. height: 16px;
  1219. vertical-align: middle;
  1220. }
  1221. .pic {
  1222. object-fit: contain;
  1223. width: 50px;
  1224. height: 50px;
  1225. cursor: pointer;
  1226. vertical-align: middle;
  1227. }
  1228. .el-icon.avatar-uploader-icon {
  1229. font-size: 20px;
  1230. color: #8c939d;
  1231. width: 50px;
  1232. height: 50px;
  1233. text-align: center;
  1234. border: 1px dashed var(--el-border-color);
  1235. }
  1236. </style>