Contract.vue 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165
  1. <template>
  2. <div style="width: 100%; padding: 0px 15px">
  3. <byForm :formConfig="formConfig" :formOption="formOption" v-model="formData.data" :rules="rules" ref="submit">
  4. <template #seller>
  5. <div style="width: 100%">
  6. <el-form-item prop="sellCorporationId">
  7. <el-select v-model="formData.data.sellCorporationId" style="width: 100%" disabled>
  8. <el-option v-for="item in corporationList" :key="item.value" :label="item.label" :value="item.value" />
  9. </el-select>
  10. </el-form-item>
  11. <el-row style="margin-top: 20px; width: 100%">
  12. <el-col :span="8">
  13. <el-form-item label="地址" prop="sellCountryName">
  14. <el-input v-model="formData.data.sellCountryName" placeholder="请输入国家" />
  15. </el-form-item>
  16. </el-col>
  17. <el-col :span="8">
  18. <el-form-item label=" " prop="sellProvinceName">
  19. <el-input v-model="formData.data.sellProvinceName" placeholder="请输入省/州" />
  20. </el-form-item>
  21. </el-col>
  22. <el-col :span="8">
  23. <el-form-item label=" " prop="sellCityName">
  24. <el-input v-model="formData.data.sellCityName" placeholder="请输入城市" />
  25. </el-form-item>
  26. </el-col>
  27. </el-row>
  28. <el-row style="margin-top: 20px; width: 100%">
  29. <el-col :span="24">
  30. <el-form-item prop="sellAddress">
  31. <el-input v-model="formData.data.sellAddress" type="textarea"> </el-input>
  32. </el-form-item>
  33. </el-col>
  34. </el-row>
  35. <el-row style="margin-top: 20px; width: 100%">
  36. <el-col :span="8">
  37. <el-form-item label="联系人" prop="sellContactName">
  38. <el-input v-model="formData.data.sellContactName" placeholder="请输入联系人" />
  39. </el-form-item>
  40. </el-col>
  41. <el-col :span="16">
  42. <el-form-item label=" " prop="sellContactNumber">
  43. <el-input v-model="formData.data.sellContactNumber" placeholder="请输入联系人电话" />
  44. </el-form-item>
  45. </el-col>
  46. </el-row>
  47. </div>
  48. </template>
  49. <template #buyer>
  50. <div style="width: 100%">
  51. <div style="width: 100%">
  52. <el-form-item prop="buyCorporationId">
  53. <el-select v-model="formData.data.buyCorporationId" style="width: 100%" @change="changeCustomer">
  54. <el-option v-for="item in customerList" :key="item.value" :label="item.label" :value="item.value" />
  55. </el-select>
  56. </el-form-item>
  57. <el-row style="margin-top: 20px; width: 100%">
  58. <el-col :span="6">
  59. <el-form-item label="地址" prop="countryId">
  60. <el-select v-model="formData.data.countryId" placeholder="国家" filterable @change="(val) => getCityData(val, '20', true)">
  61. <el-option v-for="item in countryData" :label="item.chineseName" :value="item.id"> </el-option>
  62. </el-select>
  63. </el-form-item>
  64. </el-col>
  65. <el-col :span="6">
  66. <el-form-item label=" " prop="provinceName">
  67. <selectCity
  68. placeholder="省/洲"
  69. @change="(val) => getCityData(val, '30', true)"
  70. addressId="provinceId"
  71. addressName="provinceName"
  72. v-model="formData.data"
  73. :data="provinceData">
  74. </selectCity>
  75. </el-form-item>
  76. </el-col>
  77. <el-col :span="6">
  78. <el-form-item label=" " prop="cityName">
  79. <selectCity placeholder="城市" addressId="cityId" addressName="cityName" v-model="formData.data" :data="cityData"> </selectCity>
  80. </el-form-item>
  81. </el-col>
  82. <el-col :span="6">
  83. <el-form-item label=" " prop="buyPostalCode">
  84. <el-input v-model="formData.data.buyPostalCode" placeholder="请输入邮编" />
  85. </el-form-item>
  86. </el-col>
  87. </el-row>
  88. <el-row style="margin-top: 20px; width: 100%">
  89. <el-col :span="24">
  90. <el-form-item prop="buyAddress">
  91. <el-input v-model="formData.data.buyAddress" type="textarea"> </el-input>
  92. </el-form-item>
  93. </el-col>
  94. </el-row>
  95. <el-row style="margin-top: 20px; width: 100%">
  96. <el-col :span="8">
  97. <el-form-item label="联系人" prop="buyContactName">
  98. <el-autocomplete
  99. v-model="formData.data.buyContactName"
  100. :fetch-suggestions="querySearchPerson"
  101. clearable
  102. class="inline-input w-50"
  103. placeholder="请输入联系人"
  104. @select="handlePerson">
  105. </el-autocomplete>
  106. </el-form-item>
  107. </el-col>
  108. <el-col :span="16">
  109. <el-form-item label=" " prop="buyContactNumber">
  110. <el-input v-model="formData.data.buyContactNumber" placeholder="请输入联系人电话" />
  111. </el-form-item>
  112. </el-col>
  113. </el-row>
  114. </div>
  115. </div>
  116. </template>
  117. <template #commodity>
  118. <div style="width: 100%">
  119. <el-button @click="openProduct = true">添加商品</el-button>
  120. <el-table :data="formData.data.contractProductList" style="width: 100%; margin-top: 16px">
  121. <el-table-column label="商品图片" width="80">
  122. <template #default="{ row }">
  123. <div v-if="row.productId">
  124. <img :src="row.fileUrl" class="pic" @click="onPicture(row.fileUrl)" />
  125. </div>
  126. <div v-else></div>
  127. </template>
  128. </el-table-column>
  129. <el-table-column prop="code" label="商品编码" width="120" />
  130. <el-table-column label="商品名称" min-width="200">
  131. <template #default="{ row, $index }">
  132. <div style="width: 100%">
  133. <el-form-item :prop="'contractProductList.' + $index + '.productName'" :rules="rules.productName" :inline-message="true">
  134. <el-input v-model="row.productName" placeholder="请输入商品名称" />
  135. </el-form-item>
  136. </div>
  137. </template>
  138. </el-table-column>
  139. <el-table-column label="规格型号" width="180">
  140. <template #default="{ row, $index }">
  141. <div style="width: 100%">
  142. <!-- :rules="rules.productModel" -->
  143. <el-form-item :prop="'contractProductList.' + $index + '.productModel'" :inline-message="true">
  144. <el-input v-model="row.productModel" placeholder="请输入规格型号" />
  145. </el-form-item>
  146. </div>
  147. </template>
  148. </el-table-column>
  149. <el-table-column prop="unit" label="单位" width="100" :formatter="(row) => dictValueLabel(row.unit, productUnit)" />
  150. <el-table-column label="数量" width="160">
  151. <template #default="{ row, $index }">
  152. <div style="width: 100%">
  153. <el-form-item :prop="'contractProductList.' + $index + '.quantity'" :rules="rules.quantity" :inline-message="true">
  154. <el-input-number
  155. onmousewheel="return false;"
  156. v-model="row.quantity"
  157. placeholder="请输入数量"
  158. style="width: 100%"
  159. :precision="0"
  160. :controls="false"
  161. :min="0"
  162. @change="calculationAmount()" />
  163. </el-form-item>
  164. </div>
  165. </template>
  166. </el-table-column>
  167. <el-table-column label="单价" width="160">
  168. <template #default="{ row, $index }">
  169. <div style="width: 100%">
  170. <el-form-item :prop="'contractProductList.' + $index + '.price'" :rules="rules.price" :inline-message="true">
  171. <el-input-number
  172. onmousewheel="return false;"
  173. v-model="row.price"
  174. placeholder="请输入单价"
  175. style="width: 100%"
  176. :precision="2"
  177. :controls="false"
  178. :min="0"
  179. @change="calculationAmount()" />
  180. </el-form-item>
  181. </div>
  182. </template>
  183. </el-table-column>
  184. <el-table-column prop="amount" label="金额" width="140" />
  185. <el-table-column align="center" label="操作" width="120" fixed="right">
  186. <template #default="{ row, $index }">
  187. <el-button type="primary" link @click="handleHandover(row, $index)">交接单</el-button>
  188. <el-button type="primary" link @click="handleRemove($index, row)">删除</el-button>
  189. </template>
  190. </el-table-column>
  191. </el-table>
  192. </div>
  193. </template>
  194. <template #otherCharge>
  195. <div style="width: 100%">
  196. <el-button type="primary" @click="clickAdd()">添加行</el-button>
  197. <el-table :data="formData.data.contractProjectList" style="width: 100%; margin-top: 16px">
  198. <el-table-column label="收费项目" width="220">
  199. <template #default="{ row, $index }">
  200. <div style="width: 100%">
  201. <el-form-item :prop="'contractProjectList.' + $index + '.payName'" :rules="rules.payName" :inline-message="true">
  202. <el-autocomplete v-model="row.payName" :fetch-suggestions="querySearch" clearable class="inline-input w-50" placeholder="请输入收费项目" />
  203. </el-form-item>
  204. </div>
  205. </template>
  206. </el-table-column>
  207. <el-table-column label="金额" width="180">
  208. <template #default="{ row, $index }">
  209. <div style="width: 100%">
  210. <el-form-item :prop="'contractProjectList.' + $index + '.amount'" :rules="rules.amount" :inline-message="true">
  211. <el-input-number
  212. onmousewheel="return false;"
  213. v-model="row.amount"
  214. placeholder="请输入金额"
  215. style="width: 100%"
  216. :precision="2"
  217. :controls="false"
  218. :min="0"
  219. @change="totalAmount()" />
  220. </el-form-item>
  221. </div>
  222. </template>
  223. </el-table-column>
  224. <el-table-column label="备注">
  225. <template #default="{ row, $index }">
  226. <div style="width: 100%">
  227. <el-form-item :prop="'contractProjectList.' + $index + '.remark'">
  228. <el-input v-model="row.remark" placeholder="请输入备注" />
  229. </el-form-item>
  230. </div>
  231. </template>
  232. </el-table-column>
  233. <el-table-column align="center" label="操作" width="80" fixed="right">
  234. <template #default="{ row, $index }">
  235. <el-button type="primary" link @click="handleDelete($index)">删除</el-button>
  236. </template>
  237. </el-table-column>
  238. </el-table>
  239. </div>
  240. </template>
  241. <template #offerMoney>
  242. <div style="width: 100%">
  243. <el-row style="margin-top: 20px; width: 100%">
  244. <el-col :span="4">
  245. <el-form-item label="币种" prop="currency">
  246. <el-select v-model="formData.data.currency" placeholder="请选择币种" style="width: 100%">
  247. <el-option v-for="item in accountCurrency" :key="item.value" :label="item.label" :value="item.value" />
  248. </el-select>
  249. </el-form-item>
  250. </el-col>
  251. <el-col :span="6">
  252. <el-form-item label="合同总金额" prop="amount">
  253. <el-input v-model="formData.data.amount" placeholder="合同总金额" disabled />
  254. </el-form-item>
  255. </el-col>
  256. <!-- <el-col :span="4">
  257. <el-form-item label="报价有效期 (天)" prop="effective">
  258. <el-input-number onmousewheel="return false;"
  259. v-model="formData.data.effective"
  260. placeholder="请输入有效期"
  261. style="width: 100%"
  262. :precision="0"
  263. :controls="false"
  264. :min="0"
  265. />
  266. </el-form-item>
  267. </el-col> -->
  268. </el-row>
  269. <el-row style="margin-top: 20px; width: 100%">
  270. <el-col :span="7">
  271. <el-form-item label="付款方式" prop="paymentMethod">
  272. <el-select v-model="formData.data.paymentMethod" placeholder="请选择付款方式" style="width: 100%">
  273. <el-option v-for="item in fundsPaymentMethod" :key="item.value" :label="item.label" :value="item.value" />
  274. </el-select>
  275. </el-form-item>
  276. </el-col>
  277. <el-col :span="7">
  278. <el-form-item label="预付比例 (%)" prop="advanceRatio">
  279. <el-input-number
  280. onmousewheel="return false;"
  281. v-model="formData.data.advanceRatio"
  282. placeholder="请输入预付比例"
  283. style="width: 100%"
  284. :precision="2"
  285. :controls="false"
  286. :min="0"
  287. :max="100" />
  288. </el-form-item>
  289. </el-col>
  290. <el-col :span="7">
  291. <el-form-item label="收款账号" prop="shroffAccountId">
  292. <el-select v-model="formData.data.shroffAccountId" placeholder="请选择收款账号" style="width: 100%" @change="changeShroffAccount">
  293. <el-option v-for="item in accountList" :key="item.value" :label="item.label" :value="item.value" />
  294. </el-select>
  295. </el-form-item>
  296. </el-col>
  297. <el-col :span="3">
  298. <el-form-item label=" ">
  299. <el-button type="primary" @click="changeActiveName" text>
  300. <span v-if="activeName == '1'">收起</span>
  301. <span v-else>展开</span>
  302. </el-button>
  303. </el-form-item>
  304. </el-col>
  305. </el-row>
  306. <div style="width: 100%; margin-top: 34px">
  307. <el-collapse v-model="activeName" class="hideCollapse" accordion>
  308. <el-collapse-item title="" name="1">
  309. <el-row style="width: 100%">
  310. <el-col :span="9">
  311. <el-form-item label="Beneficiary Name" prop="beneficiaryName">
  312. <el-input v-model="formData.data.beneficiaryName" placeholder="请输入Beneficiary Name" />
  313. </el-form-item>
  314. <div style="height: 20px"></div>
  315. <el-form-item label="Beneficiary Bank" prop="beneficiaryBank">
  316. <el-input v-model="formData.data.beneficiaryBank" placeholder="请输入Beneficiary Bank" />
  317. </el-form-item>
  318. <div style="height: 20px"></div>
  319. <el-form-item label="Beneficiary Bank Address" prop="beneficiaryBankAddress">
  320. <el-input v-model="formData.data.beneficiaryBankAddress" placeholder="请输入Beneficiary Bank Address" />
  321. </el-form-item>
  322. </el-col>
  323. <el-col :span="9">
  324. <el-form-item label="Beneficiary Account Number" prop="beneficiaryAccountNumber">
  325. <el-input v-model="formData.data.beneficiaryAccountNumber" placeholder="请输入Beneficiary Account Number" />
  326. </el-form-item>
  327. <div style="height: 20px"></div>
  328. <el-form-item label="Swift Code" prop="swiftCode">
  329. <el-input v-model="formData.data.swiftCode" placeholder="请输入Swift Code" />
  330. </el-form-item>
  331. <div style="height: 20px"></div>
  332. <el-form-item label="Beneficiary Address" prop="beneficiaryAddress">
  333. <el-input v-model="formData.data.beneficiaryAddress" placeholder="请输入Beneficiary Address" />
  334. </el-form-item>
  335. </el-col>
  336. </el-row>
  337. </el-collapse-item>
  338. </el-collapse>
  339. </div>
  340. </div>
  341. </template>
  342. <template #delivery>
  343. <div style="width: 100%">
  344. <el-row style="margin-top: 20px; width: 100%">
  345. <el-col :span="7">
  346. <el-form-item label="贸易方式" prop="tradeMethods">
  347. <el-select v-model="formData.data.tradeMethods" placeholder="请选择贸易方式" style="width: 100%">
  348. <el-option v-for="item in tradeMethods" :key="item.value" :label="item.label" :value="item.value" />
  349. </el-select>
  350. </el-form-item>
  351. </el-col>
  352. </el-row>
  353. <el-row style="margin-top: 20px; width: 100%">
  354. <el-col :span="7">
  355. <el-form-item label="运输方式" prop="transportMethod">
  356. <el-select v-model="formData.data.transportMethod" placeholder="请选择运输方式" style="width: 100%">
  357. <el-option v-for="item in shippingMethod" :key="item.value" :label="item.label" :value="item.value" />
  358. </el-select>
  359. </el-form-item>
  360. </el-col>
  361. <el-col :span="7">
  362. <el-form-item label="运输说明" prop="transportRemark">
  363. <el-input v-model="formData.data.transportRemark" placeholder="请输入运输说明" />
  364. </el-form-item>
  365. </el-col>
  366. </el-row>
  367. <el-row style="margin-top: 20px; width: 100%">
  368. <el-col :span="14">
  369. <el-form-item label="付款条件" prop="remark">
  370. <el-input v-model="formData.data.remark" :rows="2" type="textarea" placeholder="请输入付款条件" />
  371. </el-form-item>
  372. </el-col>
  373. </el-row>
  374. <el-row style="margin-top: 20px; width: 100%">
  375. <el-col :span="7">
  376. <el-form-item label="交货期限" prop="deliveryTime">
  377. <el-date-picker v-model="formData.data.deliveryTime" type="date" placeholder="请选择交货期限" value-format="YYYY-MM-DD" />
  378. </el-form-item>
  379. </el-col>
  380. <el-col :span="7">
  381. <el-form-item label="质保期 (天)" prop="warranty">
  382. <el-input-number
  383. onmousewheel="return false;"
  384. v-model="formData.data.warranty"
  385. placeholder="请输入质保期"
  386. style="width: 100%"
  387. :precision="0"
  388. :controls="false"
  389. :min="0" />
  390. </el-form-item>
  391. </el-col>
  392. </el-row>
  393. </div>
  394. </template>
  395. <template #shipment>
  396. <div style="width: 100%">
  397. <el-table :data="formData.data.contractShipmentList" style="width: 100%; margin-top: 16px">
  398. <el-table-column prop="code" label="商品编码" width="120" />
  399. <el-table-column prop="productName" label="商品名称" />
  400. <el-table-column label="出货日期" width="220">
  401. <template #default="{ row, $index }">
  402. <div style="width: 100%">
  403. <el-form-item :prop="'contractShipmentList.' + $index + '.shipmentTime'" :rules="rules.shipmentTime" :inline-message="true">
  404. <el-date-picker v-model="row.shipmentTime" type="date" placeholder="请选择出货日期" value-format="YYYY-MM-DD" />
  405. </el-form-item>
  406. </div>
  407. </template>
  408. </el-table-column>
  409. <el-table-column label="数量" width="160">
  410. <template #default="{ row, $index }">
  411. <div style="width: 100%">
  412. <el-form-item :prop="'contractShipmentList.' + $index + '.quantity'" :inline-message="true">
  413. <el-input-number
  414. onmousewheel="return false;"
  415. v-model="row.quantity"
  416. placeholder="请输入数量"
  417. style="width: 100%"
  418. :precision="0"
  419. :controls="false"
  420. :min="0"
  421. @change="calculationAmount()" />
  422. </el-form-item>
  423. </div>
  424. </template>
  425. </el-table-column>
  426. <el-table-column align="center" label="操作" width="120" fixed="right">
  427. <template #default="{ row, $index }">
  428. <el-button type="primary" link @click="clickSplit(row)">拆分</el-button>
  429. <el-button type="primary" link @click="clickDelete($index)">删除</el-button>
  430. </template>
  431. </el-table-column>
  432. </el-table>
  433. </div>
  434. </template>
  435. </byForm>
  436. <el-dialog v-model="openProduct" title="选择商品" width="70%" append-to-body>
  437. <SelectGoods @cancel="openProduct = false" @pushGoods="pushGoods"></SelectGoods>
  438. </el-dialog>
  439. <el-dialog title="交接单" v-if="openHandover" v-model="openHandover" width="800">
  440. <byForm :formConfig="formHandoverConfig" :formOption="formOption" v-model="productRow.data">
  441. <template #remark>
  442. <div style="width: 100%">
  443. <Editor :value="productRow.data.remark" @updateValue="updateContent" />
  444. </div>
  445. </template>
  446. <template #file>
  447. <div style="width: 100%">
  448. <el-upload
  449. v-model:fileList="fileList"
  450. action="https://winfaster.obs.cn-south-1.myhuaweicloud.com"
  451. :data="uploadData"
  452. multiple
  453. :before-upload="uploadFile"
  454. :on-success="handleSuccess"
  455. :on-preview="onPreviewFile">
  456. <el-button>选择</el-button>
  457. </el-upload>
  458. </div>
  459. </template>
  460. </byForm>
  461. <template #footer>
  462. <el-button @click="openHandover = false" size="large">取 消</el-button>
  463. <el-button type="primary" @click="submitHandoverForm()" size="large">确 定</el-button>
  464. </template>
  465. </el-dialog>
  466. </div>
  467. </template>
  468. <script setup>
  469. import byForm from "@/components/byForm/index";
  470. import SelectGoods from "@/components/product/SelectGoods";
  471. import { ElMessage } from "element-plus";
  472. import Editor from "@/components/Editor/index.vue";
  473. import selectCity from "@/components/selectCity/index.vue";
  474. import { useRoute } from "vue-router";
  475. const route = useRoute();
  476. // 接收父组件的传值
  477. const props = defineProps({
  478. queryData: String,
  479. });
  480. const { proxy } = getCurrentInstance();
  481. const contractType = ref([]);
  482. const accountCurrency = ref([]);
  483. const fundsPaymentMethod = ref([]);
  484. const tradeMethods = ref([]);
  485. const shippingMethod = ref([]);
  486. const templateList = ref([]);
  487. const corporationList = ref([]);
  488. const customerList = ref([]);
  489. const countryData = ref([]);
  490. const provinceData = ref([]);
  491. const cityData = ref([]);
  492. const customerUserList = ref([]);
  493. const accountList = ref([]);
  494. const productUnit = ref([]);
  495. const openProduct = ref(false);
  496. const activeName = ref("");
  497. const formData = reactive({
  498. data: {
  499. amount: undefined,
  500. contractProductList: [],
  501. contractProjectList: [],
  502. contractShipmentList: [],
  503. },
  504. });
  505. const submit = ref(null);
  506. const judgeStatus = () => {
  507. if (props.queryData.recordList && props.queryData.recordList.length > 0) {
  508. let data = props.queryData.recordList.filter((item) => item.status === 2 && item.nodeType !== 1);
  509. if (data && data.length > 0) {
  510. return true;
  511. }
  512. }
  513. return false;
  514. };
  515. const formOption = reactive({
  516. inline: true,
  517. labelWidth: 100,
  518. itemWidth: 100,
  519. rules: [],
  520. disabled: false,
  521. });
  522. const formConfig = computed(() => {
  523. return [
  524. {
  525. type: "title",
  526. title: "合同模板",
  527. label: "",
  528. },
  529. {
  530. type: "select",
  531. label: "合同类型",
  532. prop: "contractType",
  533. data: contractType.value,
  534. itemWidth: 25,
  535. },
  536. {
  537. type: "select",
  538. label: "选择合同模板",
  539. prop: "contractTemplateId",
  540. data: templateList.value,
  541. fn: (val) => {
  542. changeTemplate(val);
  543. },
  544. itemWidth: 26,
  545. },
  546. {
  547. type: "slot",
  548. slotName: "seller",
  549. label: "卖方信息",
  550. itemWidth: 50,
  551. },
  552. {
  553. type: "slot",
  554. slotName: "buyer",
  555. label: "买方信息",
  556. itemWidth: 50,
  557. },
  558. {
  559. type: "slot",
  560. slotName: "commodity",
  561. label: "商品信息",
  562. },
  563. {
  564. type: "slot",
  565. slotName: "otherCharge",
  566. label: "其他收费项目",
  567. },
  568. {
  569. type: "slot",
  570. slotName: "offerMoney",
  571. label: "收款信息",
  572. },
  573. {
  574. type: "slot",
  575. slotName: "delivery",
  576. label: "交付信息",
  577. },
  578. {
  579. type: "slot",
  580. slotName: "shipment",
  581. label: "出货计划",
  582. },
  583. ];
  584. });
  585. const rules = ref({
  586. contractType: [{ required: true, message: "请选择合同类型", trigger: "change" }],
  587. contractTemplateId: [{ required: true, message: "请选择合同模板", trigger: "change" }],
  588. buyCorporationId: [{ required: true, message: "请选择公司", trigger: "change" }],
  589. countryId: [{ required: true, message: "请选择国家", trigger: "change" }],
  590. sellAddress: [{ required: true, message: "请输入详细地址", trigger: "blur" }],
  591. buyAddress: [{ required: true, message: "请输入详细地址", trigger: "blur" }],
  592. buyContactName: [{ required: true, message: "请输入联系人", trigger: ["change", "blur"] }],
  593. buyContactNumber: [{ required: true, message: "请输入联系电话", trigger: "blur" }],
  594. productName: [{ required: true, message: "请输入商品名称", trigger: "blur" }],
  595. productModel: [{ required: true, message: "请输入规格型号", trigger: "blur" }],
  596. quantity: [{ required: true, message: "请输入数量", trigger: "blur" }],
  597. price: [{ required: true, message: "请输入单价", trigger: "blur" }],
  598. amount: [{ required: true, message: "请输入金额", trigger: "blur" }],
  599. payName: [{ required: true, message: "请输入收费项目", trigger: ["change", "blur"] }],
  600. currency: [{ required: true, message: "请选择币种", trigger: "change" }],
  601. effective: [{ required: true, message: "请输入报价有效期", trigger: "blur" }],
  602. deliveryTime: [{ required: true, message: "请选择交货期限", trigger: "change" }],
  603. paymentMethod: [{ required: true, message: "请选择付款方式", trigger: "change" }],
  604. advanceRatio: [{ required: true, message: "请输入预付比例", trigger: "blur" }],
  605. shroffAccountId: [{ required: true, message: "请选择收款账号", trigger: "change" }],
  606. tradeMethods: [{ required: true, message: "请选择贸易方式", trigger: "change" }],
  607. transportMethod: [{ required: true, message: "请选择运输方式", trigger: "change" }],
  608. transportRemark: [{ required: true, message: "请输入运输说明", trigger: "blur" }],
  609. remark: [{ required: true, message: "请输入付款条件", trigger: "blur" }],
  610. });
  611. const getDict = () => {
  612. proxy.getDictOne(["account_currency", "funds_payment_method", "trade_mode", "shipping_method", "contract_type", "unit"]).then((res) => {
  613. accountCurrency.value = res["account_currency"].map((x) => ({
  614. label: x.dictValue,
  615. value: x.dictKey,
  616. }));
  617. fundsPaymentMethod.value = res["funds_payment_method"].map((x) => ({
  618. label: x.dictValue,
  619. value: x.dictKey,
  620. }));
  621. tradeMethods.value = res["trade_mode"].map((x) => ({
  622. label: x.dictValue,
  623. value: x.dictKey,
  624. }));
  625. shippingMethod.value = res["shipping_method"].map((x) => ({
  626. label: x.dictValue,
  627. value: x.dictKey,
  628. }));
  629. contractType.value = res["contract_type"].map((x) => ({
  630. label: x.dictValue,
  631. value: x.dictKey,
  632. }));
  633. productUnit.value = res["unit"].map((x) => ({
  634. label: x.dictValue,
  635. value: x.dictKey,
  636. }));
  637. });
  638. proxy.post("/contractTemplate/page", { pageNum: 1, pageSize: 999 }).then((res) => {
  639. templateList.value = res.rows.map((item) => {
  640. return {
  641. ...item,
  642. label: item.templateName,
  643. value: item.id,
  644. };
  645. });
  646. });
  647. proxy.post("/corporation/page", { pageNum: 1, pageSize: 999 }).then((res) => {
  648. corporationList.value = res.rows.map((item) => {
  649. return {
  650. ...item,
  651. label: item.name,
  652. value: item.id,
  653. };
  654. });
  655. });
  656. proxy.post("/customer/page", { pageNum: 1, pageSize: 999 }).then((res) => {
  657. customerList.value = res.rows.map((item) => {
  658. return {
  659. ...item,
  660. label: item.name,
  661. value: item.id,
  662. };
  663. });
  664. });
  665. proxy.post("/accountManagement/page", { pageNum: 1, pageSize: 999 }).then((res) => {
  666. accountList.value = res.rows.map((item) => {
  667. return {
  668. ...item,
  669. label: item.alias,
  670. value: item.id,
  671. };
  672. });
  673. });
  674. };
  675. getDict();
  676. const changeTemplate = (val) => {
  677. formData.data.sellCorporationId = "";
  678. formData.data.sellContactName = "";
  679. formData.data.sellContactNumber = "";
  680. formData.data.sellCountryName = "";
  681. formData.data.sellProvinceName = "";
  682. formData.data.sellCityName = "";
  683. formData.data.sellAddress = "";
  684. if (val) {
  685. proxy.post("/contractTemplate/detail", { id: val }).then((res) => {
  686. formData.data.sellCorporationId = res.corporationId;
  687. if (res.corporationId) {
  688. proxy.post("/corporation/detail", { id: res.corporationId }).then((detailCorporation) => {
  689. proxy.post("/customizeArea/list", { parentId: "0" }).then((resCountry) => {
  690. let sellCountryData = resCountry.filter((item) => item.id === detailCorporation.countryId);
  691. if (sellCountryData && sellCountryData.length > 0) {
  692. formData.data.sellCountryName = sellCountryData[0].chineseName;
  693. } else {
  694. formData.data.sellCountryName = "";
  695. }
  696. });
  697. if (detailCorporation.countryId) {
  698. proxy
  699. .post("/customizeArea/list", {
  700. parentId: detailCorporation.countryId,
  701. })
  702. .then((resProvince) => {
  703. let sellProvinceData = resProvince.filter((item) => item.id === detailCorporation.provinceId);
  704. if (sellProvinceData && sellProvinceData.length > 0) {
  705. formData.data.sellProvinceName = sellProvinceData[0].name;
  706. } else {
  707. formData.data.sellProvinceName = "";
  708. }
  709. });
  710. } else {
  711. formData.data.sellProvinceName = "";
  712. }
  713. if (detailCorporation.provinceId) {
  714. proxy
  715. .post("/customizeArea/list", {
  716. parentId: detailCorporation.provinceId,
  717. })
  718. .then((resCity) => {
  719. let sellCityData = resCity.filter((item) => item.id === detailCorporation.cityId);
  720. if (sellCityData && sellCityData.length > 0) {
  721. formData.data.sellCityName = sellCityData[0].name;
  722. } else {
  723. formData.data.sellCityName = "";
  724. }
  725. });
  726. } else {
  727. formData.data.sellCityName = "";
  728. }
  729. formData.data.sellAddress = detailCorporation.address;
  730. });
  731. }
  732. formData.data.sellContactName = res.contactName;
  733. formData.data.sellContactNumber = res.contactNumber;
  734. });
  735. }
  736. };
  737. const getCityData = (id, type, isChange) => {
  738. proxy.post("/customizeArea/list", { parentId: id }).then((res) => {
  739. if (type === "20") {
  740. provinceData.value = res;
  741. if (isChange) {
  742. formData.data.provinceId = "";
  743. formData.data.provinceName = "";
  744. formData.data.cityId = "";
  745. formData.data.cityName = "";
  746. }
  747. } else if (type === "30") {
  748. cityData.value = res;
  749. if (isChange) {
  750. formData.data.cityId = "";
  751. formData.data.cityName = "";
  752. }
  753. } else {
  754. countryData.value = res;
  755. }
  756. });
  757. };
  758. getCityData("0");
  759. const changeCustomer = (val) => {
  760. formData.data.buyContactName = "";
  761. formData.data.buyContactNumber = "";
  762. if (val) {
  763. proxy.post("/customer/detail", { id: val }).then(
  764. (res) => {
  765. if (res.customerUserList && res.customerUserList.length > 0) {
  766. formData.data.buyContactName = res.customerUserList[0].name;
  767. if (res.customerUserList[0].contactJson) {
  768. let contactJson = JSON.parse(res.customerUserList[0].contactJson);
  769. if (contactJson && contactJson.length > 0) {
  770. formData.data.buyContactNumber = contactJson[0].contactNo;
  771. }
  772. }
  773. customerUserList.value = res.customerUserList.map((item) => {
  774. return {
  775. ...item,
  776. value: item.name,
  777. };
  778. });
  779. }
  780. formData.data.countryId = res.countryId;
  781. formData.data.provinceId = res.provinceId;
  782. formData.data.cityId = res.cityId;
  783. formData.data.buyPostalCode = res.zipCode;
  784. formData.data.buyAddress = res.address;
  785. getCityData(formData.data.countryId, "20");
  786. if (formData.data.provinceId) {
  787. getCityData(formData.data.provinceId, "30");
  788. }
  789. },
  790. (err) => {
  791. console.log(err);
  792. formData.data.countryId = "";
  793. formData.data.provinceId = "";
  794. formData.data.cityId = "";
  795. formData.data.buyPostalCode = "";
  796. formData.data.buyAddress = "";
  797. }
  798. );
  799. } else {
  800. formData.data.countryId = "";
  801. formData.data.provinceId = "";
  802. formData.data.cityId = "";
  803. formData.data.buyPostalCode = "";
  804. formData.data.buyAddress = "";
  805. }
  806. };
  807. const createFilter = (queryString) => {
  808. return (restaurant) => {
  809. return restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0;
  810. };
  811. };
  812. const querySearchPerson = (queryString, callback) => {
  813. const results = queryString ? customerUserList.value.filter(createFilter(queryString)) : customerUserList.value;
  814. callback(results);
  815. };
  816. const handlePerson = (item) => {
  817. formData.data.buyContactNumber = item.phone;
  818. };
  819. const pushGoods = (goods) => {
  820. if (goods && goods.length > 0) {
  821. formData.data.contractProductList = formData.data.contractProductList.concat(
  822. goods.map((item) => {
  823. let fileUrl = "";
  824. if (item.fileList && item.fileList.length > 0) {
  825. fileUrl = item.fileList[0].fileUrl;
  826. }
  827. return {
  828. fileUrl: fileUrl,
  829. code: item.code,
  830. productId: item.id,
  831. productName: item.name,
  832. productModel: item.spec,
  833. unit: item.unit,
  834. quantity: undefined,
  835. price: undefined,
  836. amount: "",
  837. remark: "",
  838. fileList: [],
  839. };
  840. })
  841. );
  842. formData.data.contractShipmentList = formData.data.contractShipmentList.concat(
  843. goods.map((item) => {
  844. return {
  845. code: item.code,
  846. productId: item.id,
  847. productName: item.name,
  848. shipmentTime: "",
  849. quantity: undefined,
  850. };
  851. })
  852. );
  853. ElMessage({
  854. message: "添加成功!",
  855. type: "success",
  856. });
  857. openProduct.value = false;
  858. } else {
  859. ElMessage("请选择至少一件商品");
  860. }
  861. };
  862. const onPicture = (path) => {
  863. window.open(path, "_blank");
  864. };
  865. const productRow = reactive({
  866. data: {
  867. productName: "",
  868. productModel: "",
  869. remark: "",
  870. },
  871. });
  872. const productIndex = ref(0);
  873. const openHandover = ref(false);
  874. const fileList = ref([]);
  875. const uploadData = ref({});
  876. const formHandoverConfig = computed(() => {
  877. return [
  878. {
  879. type: "title",
  880. title: "产品信息",
  881. label: "",
  882. },
  883. {
  884. type: "input",
  885. prop: "productName",
  886. label: "产品名称",
  887. itemType: "text",
  888. disabled: true,
  889. },
  890. {
  891. type: "input",
  892. prop: "productModel",
  893. label: "规格型号",
  894. itemType: "text",
  895. disabled: true,
  896. },
  897. {
  898. type: "slot",
  899. slotName: "remark",
  900. label: "交接单",
  901. },
  902. {
  903. type: "slot",
  904. prop: "file",
  905. slotName: "file",
  906. label: "上传附件",
  907. },
  908. ];
  909. });
  910. const handleHandover = (row, index) => {
  911. productRow.data = {
  912. productName: row.productName,
  913. productModel: row.productModel,
  914. remark: row.remark,
  915. };
  916. if (row.fileList && row.fileList.length > 0) {
  917. fileList.value = row.fileList.map((item) => {
  918. return {
  919. raw: item,
  920. name: item.fileName,
  921. url: item.fileUrl,
  922. };
  923. });
  924. } else {
  925. fileList.value = [];
  926. }
  927. productIndex.value = index;
  928. openHandover.value = true;
  929. };
  930. const updateContent = (val) => {
  931. productRow.data.remark = val;
  932. };
  933. const changeActiveName = () => {
  934. if (activeName.value) {
  935. activeName.value = "";
  936. } else {
  937. activeName.value = "1";
  938. }
  939. };
  940. const uploadFile = async (file) => {
  941. const res = await proxy.post("/fileInfo/getSing", { fileName: file.name });
  942. uploadData.value = res.uploadBody;
  943. file.id = res.id;
  944. file.fileName = res.fileName;
  945. file.fileUrl = res.fileUrl;
  946. file.uploadState = true;
  947. return true;
  948. };
  949. const handleSuccess = (any, UploadFile) => {
  950. UploadFile.raw.uploadState = false;
  951. };
  952. const onPreviewFile = (file) => {
  953. window.open(file.raw.fileUrl, "_blank");
  954. };
  955. const submitHandoverForm = () => {
  956. if (fileList.value && fileList.value.length > 0) {
  957. for (let i = 0; i < fileList.value.length; i++) {
  958. if (fileList.value[i].raw.uploadState) {
  959. ElMessage("文件上传中,请稍后提交");
  960. return;
  961. }
  962. }
  963. formData.data.contractProductList[productIndex.value].fileList = fileList.value.map((item) => {
  964. return {
  965. id: item.raw.id,
  966. fileName: item.raw.fileName,
  967. fileUrl: item.raw.fileUrl,
  968. uploadState: item.raw.uploadState,
  969. };
  970. });
  971. } else {
  972. formData.data.contractProductList[productIndex.value].fileList = [];
  973. }
  974. formData.data.contractProductList[productIndex.value].remark = productRow.data.remark;
  975. openHandover.value = false;
  976. };
  977. const handleRemove = async (index, row) => {
  978. formData.data.contractShipmentList = formData.data.contractShipmentList.filter((item) => item.productId !== row.productId);
  979. await formData.data.contractProductList.splice(index, 1);
  980. totalAmount();
  981. };
  982. const calculationAmount = () => {
  983. nextTick(() => {
  984. if (formData.data.contractProductList && formData.data.contractProductList.length > 0) {
  985. for (let i = 0; i < formData.data.contractProductList.length; i++) {
  986. let money = 0;
  987. if (formData.data.contractProductList[i].quantity && formData.data.contractProductList[i].price) {
  988. money = parseFloat(Number(formData.data.contractProductList[i].quantity) * Number(formData.data.contractProductList[i].price)).toFixed(2);
  989. }
  990. formData.data.contractProductList[i].amount = money;
  991. }
  992. }
  993. nextTick(() => {
  994. totalAmount();
  995. });
  996. });
  997. };
  998. const totalAmount = () => {
  999. let money = 0;
  1000. if (formData.data.contractProductList && formData.data.contractProductList.length > 0) {
  1001. for (let i = 0; i < formData.data.contractProductList.length; i++) {
  1002. if (formData.data.contractProductList[i].amount) {
  1003. money = parseFloat(Number(money) + Number(formData.data.contractProductList[i].amount)).toFixed(2);
  1004. }
  1005. }
  1006. }
  1007. if (formData.data.contractProjectList && formData.data.contractProjectList.length > 0) {
  1008. for (let i = 0; i < formData.data.contractProjectList.length; i++) {
  1009. if (formData.data.contractProjectList[i].amount) {
  1010. money = parseFloat(Number(money) + Number(formData.data.contractProjectList[i].amount)).toFixed(2);
  1011. }
  1012. }
  1013. }
  1014. formData.data.amount = money;
  1015. };
  1016. const clickAdd = () => {
  1017. if (formData.data.contractProjectList && formData.data.contractProjectList.length > 0) {
  1018. formData.data.contractProjectList.push({
  1019. payName: "",
  1020. amount: undefined,
  1021. remark: "",
  1022. });
  1023. } else {
  1024. formData.data.contractProjectList = [{ payName: "", amount: undefined, remark: "" }];
  1025. }
  1026. };
  1027. const handleDelete = async (index) => {
  1028. await formData.data.contractProjectList.splice(index, 1);
  1029. totalAmount();
  1030. };
  1031. const querySearch = (queryString, callback) => {
  1032. proxy.post("/quotationPay/page", { payName: queryString }).then((res) => {
  1033. if (res.rows && res.rows.length > 0) {
  1034. res.rows = res.rows.map((item) => {
  1035. return {
  1036. ...item,
  1037. value: item.payName,
  1038. };
  1039. });
  1040. callback(res.rows);
  1041. } else {
  1042. callback([]);
  1043. }
  1044. });
  1045. };
  1046. const clickSplit = (item) => {
  1047. formData.data.contractShipmentList.push({
  1048. code: item.code,
  1049. productId: item.productId,
  1050. productName: item.productName,
  1051. shipmentTime: "",
  1052. quantity: undefined,
  1053. });
  1054. };
  1055. const clickDelete = (index) => {
  1056. formData.data.contractShipmentList.splice(index, 1);
  1057. };
  1058. const handleSubmit = async () => {
  1059. let status = await submit.value.handleSubmit(() => {});
  1060. if (status) {
  1061. if (!(formData.data.contractProductList && formData.data.contractProductList.length > 0)) {
  1062. ElMessage("请添加至少一件商品");
  1063. return false;
  1064. }
  1065. if (formData.data.contractShipmentList && formData.data.contractShipmentList.length > 0) {
  1066. for (let i = 0; i < formData.data.contractProductList.length; i++) {
  1067. let data = formData.data.contractShipmentList.filter((item) => item.productId === formData.data.contractProductList[i].productId);
  1068. if (data && data.length > 0) {
  1069. let quantity = 0;
  1070. for (let j = 0; j < data.length; j++) {
  1071. quantity = parseFloat(Number(quantity) + Number(data[j].quantity));
  1072. }
  1073. if (quantity > formData.data.contractProductList[i].quantity) {
  1074. ElMessage("出货数量不能大于商品数量");
  1075. return false;
  1076. }
  1077. }
  1078. }
  1079. }
  1080. return true;
  1081. } else {
  1082. setTimeout(() => {
  1083. const errorDiv = document.getElementsByClassName("is-error");
  1084. errorDiv[0].scrollIntoView();
  1085. }, 0);
  1086. }
  1087. return false;
  1088. };
  1089. const getFormData = () => {
  1090. return formData.data;
  1091. };
  1092. // 向父组件暴露
  1093. defineExpose({
  1094. getFormData,
  1095. handleSubmit,
  1096. });
  1097. const changeShroffAccount = (val) => {
  1098. if (val) {
  1099. let data = accountList.value.filter((item) => item.value === val);
  1100. if (data && data.length > 0) {
  1101. formData.data.beneficiaryName = data[0].beneficiaryName;
  1102. formData.data.beneficiaryBank = data[0].beneficiaryBank;
  1103. formData.data.beneficiaryBankAddress = data[0].beneficiaryBankAddress;
  1104. formData.data.beneficiaryAccountNumber = data[0].beneficiaryAccountNumber;
  1105. formData.data.swiftCode = data[0].swiftCode;
  1106. formData.data.beneficiaryAddress = data[0].beneficiaryAddress;
  1107. }
  1108. }
  1109. };
  1110. watch(
  1111. props.queryData,
  1112. () => {
  1113. formOption.disabled = judgeStatus();
  1114. if (props.queryData && (route.query.processType == 10 || route.query.processType == 20)) {
  1115. for (var text in props.queryData) {
  1116. formData.data[text] = props.queryData[text];
  1117. }
  1118. if (formData.data.countryId) {
  1119. getCityData(formData.data.countryId, "20");
  1120. }
  1121. if (formData.data.provinceId) {
  1122. getCityData(formData.data.provinceId, "30");
  1123. }
  1124. }
  1125. },
  1126. {
  1127. deep: true,
  1128. }
  1129. );
  1130. </script>
  1131. <style lang="scss" scoped>
  1132. ::v-deep(.el-input-number .el-input__inner) {
  1133. text-align: left;
  1134. }
  1135. .pic {
  1136. object-fit: contain;
  1137. width: 50px;
  1138. height: 50px;
  1139. cursor: pointer;
  1140. vertical-align: middle;
  1141. }
  1142. .shrinkPadding {
  1143. padding-right: 0 !important;
  1144. }
  1145. .hideCollapse {
  1146. margin-top: -62px;
  1147. border: 0 !important;
  1148. }
  1149. ::v-deep(.el-collapse-item__arrow) {
  1150. display: none !important;
  1151. }
  1152. ::v-deep(.el-collapse-item__wrap) {
  1153. border: 0 !important;
  1154. }
  1155. ::v-deep(.el-collapse-item__header) {
  1156. border: 0 !important;
  1157. }
  1158. </style>