index.vue 55 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722
  1. <template>
  2. <div class="compnent-price-calc">
  3. <exportQuota
  4. ref="compExportQuotaRef"
  5. :product-info="productInfo"
  6. :export-form="exportForm"
  7. :step3-form-list="step3FormList"
  8. :step2-form-list="formList"
  9. :creator-options="creatorOptions"
  10. :city2="formData.cal_city.local_city"
  11. :city="exportForm.pdf_city"
  12. ></exportQuota>
  13. <!-- :city="formData.cal_city.local_city" -->
  14. <el-dialog
  15. v-model="show"
  16. class="custom-calc-price-dialog"
  17. title="查看计价结果"
  18. :close-on-click-modal="false"
  19. :close-on-press-escape="false"
  20. :before-close="close"
  21. width="1800px"
  22. >
  23. <div
  24. v-loading="loading"
  25. class="flex items-start flex-wrap"
  26. >
  27. <step1
  28. :computed-box-number="computedBoxNumber"
  29. :computed-total-bulk="computedTotalBulk"
  30. :computed-total-weight="computedTotalWeight"
  31. :computed-total-weight-plus="computedTotalWeightPlus"
  32. :image-list="imageList"
  33. :product-info="productInfo"
  34. ></step1>
  35. <div class="step step-2">
  36. <common-title
  37. :title="$t(prefix + 'label_transport_info')"
  38. ></common-title>
  39. <p style="color: #ef4135; margin-bottom: 12px">
  40. 快速运输天数:2-3天 / 空+派运输天数:8天 /
  41. 海运运输天数:LCL30天、FCL25天
  42. </p>
  43. <div class="flex items-center">
  44. <el-select
  45. v-model="formData.cal_city.local_city"
  46. style="width: 120px; margin-right: 12px"
  47. size="small"
  48. @change="onCityChange"
  49. >
  50. <el-option
  51. v-for="city in cityList"
  52. :key="city"
  53. :label="city"
  54. :value="city"
  55. ></el-option>
  56. </el-select>
  57. <div class="flex items-center">
  58. <el-tooltip content="切换城市后点击">
  59. <el-button
  60. type="primary"
  61. size="small"
  62. @click="reGenerateFormAfterCityChange"
  63. >
  64. 生成运输信息
  65. </el-button>
  66. </el-tooltip>
  67. <el-button
  68. type="warning"
  69. size="small"
  70. @click="componentFreightVisible = true"
  71. >
  72. 设置运费参数
  73. </el-button>
  74. </div>
  75. </div>
  76. <div class="step2-form-area">
  77. <div
  78. v-for="(formItem, index) in formList"
  79. :key="index"
  80. class="step2-form-item"
  81. >
  82. <div class="qty-number">
  83. 采购数量:&nbsp;{{ formItem.number }} ({{ isHeavyGoods.label
  84. }}{{
  85. isHeavyGoodsPlus.flag !== isHeavyGoods.flag
  86. ? ` | ${isHeavyGoodsPlus.label}`
  87. : ''
  88. }})
  89. </div>
  90. <div class="flex items-center">
  91. <el-card
  92. v-for="v in 5"
  93. :key="v"
  94. class="form-item-card"
  95. :class="{
  96. on: formItem[`${v - 1}_${formItem.number}`] === 'on',
  97. }"
  98. >
  99. <template #header>
  100. <div
  101. @click.capture="
  102. ($e) => captureSwitchChange($e, formItem, v - 1)
  103. "
  104. >
  105. <el-tooltip
  106. placement="left"
  107. content="注意, 切换开关会导致右侧的成本数据 以(点过'保存按钮'后的 / 默认的)初始值 计算重置"
  108. >
  109. <el-switch
  110. v-model="formItem[`${v - 1}_${formItem.number}`]"
  111. size="small"
  112. active-color="#409eff"
  113. inactive-color="#ccc"
  114. active-value="on"
  115. inactive-value="off"
  116. @change="() => generateStep3Form()"
  117. />
  118. </el-tooltip>
  119. </div>
  120. </template>
  121. <div v-if="v === 1">
  122. <el-select
  123. v-model="formItem[`method_0_${formItem.number}`]"
  124. size="small"
  125. @change="reGenerateFormAfterMidwayTypeChange"
  126. >
  127. <el-option
  128. v-for="option in airOption"
  129. :key="option.value"
  130. :label="option.label"
  131. :value="option.value"
  132. ></el-option>
  133. </el-select>
  134. <div class="price">
  135. $&nbsp;{{ formItem.midway_price_0.toFixed(2) }}
  136. </div>
  137. </div>
  138. <div v-if="v === 2">
  139. <div>空+派</div>
  140. <div class="price">
  141. $&nbsp;{{ formItem.midway_price_1.toFixed(2) }}
  142. </div>
  143. </div>
  144. <div v-if="v === 3">
  145. <el-select
  146. v-model="formItem[`method_2_${formItem.number}`]"
  147. size="small"
  148. @change="reGenerateFormAfterMidwayTypeChange"
  149. >
  150. <el-option
  151. v-for="option in seaOption"
  152. :key="option.value"
  153. :label="option.label"
  154. :value="option.value"
  155. ></el-option>
  156. </el-select>
  157. <div class="price">
  158. $&nbsp;{{ formItem.midway_price_2.toFixed(2) }}
  159. </div>
  160. </div>
  161. <div v-if="v === 4">
  162. <div>DTD</div>
  163. <div
  164. v-if="formItem.freight_cost_3"
  165. class="price cursor-pointer"
  166. @click="openDTDDialog(3, index)"
  167. >
  168. $&nbsp;{{ formItem.freight_cost_3.toFixed(2) }}
  169. </div>
  170. <div
  171. v-else
  172. class="edit cursor-pointer"
  173. @click="openDTDDialog(3, index)"
  174. >
  175. {{ $t('btn_edit') }}
  176. </div>
  177. </div>
  178. <div v-if="v === 5">
  179. <div>LCL散货</div>
  180. <div
  181. v-if="formItem.freight_cost_4"
  182. class="price cursor-pointer"
  183. @click="openLCLDialog(4, index)"
  184. >
  185. $&nbsp;{{ formItem.freight_cost_4.toFixed(2) }}
  186. </div>
  187. <div
  188. v-else
  189. class="edit cursor-pointer"
  190. @click="openLCLDialog(4, index)"
  191. >
  192. {{ $t('btn_edit') }}
  193. </div>
  194. </div>
  195. </el-card>
  196. </div>
  197. </div>
  198. </div>
  199. </div>
  200. <div class="step step-3">
  201. <div
  202. class="mb-4 border border-x-0 border-t-0 border-solid border-gray-200"
  203. >
  204. <el-check-tag
  205. v-for="(form, index) in step3FormList"
  206. :checked="index === currentTab"
  207. :key="index"
  208. class="border border-solid border-gray-200 bg-white"
  209. style="border-radius: 2px"
  210. @click="currentTab = index"
  211. >
  212. {{ form.tabLabel }}
  213. </el-check-tag>
  214. </div>
  215. <div
  216. data-desc="这层div没用的,只是移除原本的el-tab之后用来减少代码改动量,不然里面的内容全部要减少一个缩进单位了"
  217. >
  218. <div
  219. v-show="index === currentTab"
  220. v-for="(form, index) in step3FormList"
  221. :key="index"
  222. >
  223. <common-title
  224. :title="$t(prefix + 'label_cost_list')"
  225. ></common-title>
  226. <div class="horizontal-table">
  227. <div
  228. class="flex items-center tr"
  229. style="background-color: #ebeef5"
  230. >
  231. <div class="td">项目</div>
  232. <div class="td">总价</div>
  233. <div class="td">单价</div>
  234. </div>
  235. <div class="flex items-center tr">
  236. <div class="td column-label">产品成本(AUD)</div>
  237. <div class="td">
  238. {{ form.product_cost.toFixed(2) }}
  239. </div>
  240. <div class="td">
  241. {{ (form.product_cost / form.number).toFixed(2) }}
  242. </div>
  243. </div>
  244. <div class="flex items-center tr">
  245. <div class="td column-label">制版及其它成本(AUD)</div>
  246. <div class="td">
  247. {{ form.extend_cost.toFixed(2) }}
  248. </div>
  249. <div class="td">
  250. {{ (form.extend_cost / form.number).toFixed(2) }}
  251. </div>
  252. </div>
  253. <div class="flex items-center tr">
  254. <div class="td column-label">运费总成本(AUD)</div>
  255. <div class="td">
  256. {{ form.freight_cost.toFixed(2) }}
  257. </div>
  258. <div class="td">
  259. {{ (form.freight_cost / form.number).toFixed(2) }}
  260. </div>
  261. </div>
  262. <div class="flex items-center tr">
  263. <div class="td column-label">税费(AUD)</div>
  264. <div class="td">
  265. {{ form.tax_fee.toFixed(2) }}
  266. </div>
  267. <div class="td">
  268. {{ (form.tax_fee / form.number).toFixed(2) }}
  269. </div>
  270. </div>
  271. <div class="flex items-center tr">
  272. <div class="td column-label">总成本(AUD)</div>
  273. <div class="td">
  274. {{ form.total_cost.toFixed(2) }}
  275. </div>
  276. <div class="td">
  277. {{ (form.total_cost / form.number).toFixed(2) }}
  278. </div>
  279. </div>
  280. </div>
  281. <el-form
  282. :model="form"
  283. label-width="105px"
  284. >
  285. <common-title
  286. :title="$t(prefix + 'label_other_cost')"
  287. ></common-title>
  288. <div class="">
  289. <el-form-item
  290. label="COO Certificate"
  291. label-width="120px"
  292. >
  293. <el-switch
  294. v-model="form.coo_certificate"
  295. active-color="#409eff"
  296. inactive-color="#ccc"
  297. active-text="是"
  298. inactive-text="否"
  299. active-value="on"
  300. inactive-value="off"
  301. size="small"
  302. @change="($e: any) => changeCOO(form, $e)"
  303. ></el-switch>
  304. </el-form-item>
  305. </div>
  306. <div class="flex justify-between flex-wrap items-center">
  307. <el-form-item label="税点">
  308. <el-input
  309. v-model="form.tax"
  310. :disabled="form.coo_certificate === 'on'"
  311. @input="() => onForm3ItemChange(form, 'tax')"
  312. >
  313. <template #append>%</template>
  314. </el-input>
  315. </el-form-item>
  316. <el-form-item label="税费">
  317. <el-input
  318. v-model="form.tax_fee"
  319. disabled
  320. >
  321. <template #append>AUD</template>
  322. </el-input>
  323. </el-form-item>
  324. <el-form-item label="入口报关费用">
  325. <el-input
  326. v-model="form.gatt_tax_fee"
  327. @input="() => onForm3ItemChange(form, 'gatt_tax_fee')"
  328. >
  329. <template #append>AUD</template>
  330. </el-input>
  331. </el-form-item>
  332. <el-form-item label="验货费">
  333. <el-input
  334. v-model="form.review_cost"
  335. @input="() => onForm3ItemChange(form, 'review_cost')"
  336. >
  337. <template #append>AUD</template>
  338. </el-input>
  339. </el-form-item>
  340. <el-form-item label="国内运费总价">
  341. <el-input
  342. v-model="form.cn_freight_cost"
  343. @input="() => onForm3ItemChange(form, 'cn_freight_cost')"
  344. >
  345. <template #append>RMB</template>
  346. </el-input>
  347. </el-form-item>
  348. <el-form-item label="国外本土运费">
  349. <el-input
  350. v-model="form.local_freight_cost"
  351. @input="
  352. () => onForm3ItemChange(form, 'local_freight_cost')
  353. "
  354. >
  355. <template #append>AUD</template>
  356. </el-input>
  357. </el-form-item>
  358. </div>
  359. <common-title
  360. :title="$t(prefix + 'label_charge')"
  361. ></common-title>
  362. <div class="flex justify-between flex-wrap items-center">
  363. <el-form-item label="Set Up Cost">
  364. <el-input
  365. v-model="form.setup_cost"
  366. @input="() => onForm3ItemChange(form, 'setup_cost')"
  367. >
  368. <template #append>AUD</template>
  369. </el-input>
  370. </el-form-item>
  371. <el-form-item label="本地收费">
  372. <el-input
  373. v-model="form.add_freight_cost"
  374. @input="() => onForm3ItemChange(form, 'add_freight_cost')"
  375. >
  376. <template #append>AUD</template>
  377. </el-input>
  378. </el-form-item>
  379. <el-form-item label="售卖单价">
  380. <el-input
  381. v-model="form.sold_unit"
  382. @input="() => onForm3ItemChange(form, 'sold_unit')"
  383. >
  384. <template #append>AUD</template>
  385. </el-input>
  386. </el-form-item>
  387. <el-form-item label="售卖总价">
  388. <el-input
  389. v-model="form.sold_price"
  390. disabled
  391. >
  392. <template #append>AUD</template>
  393. </el-input>
  394. </el-form-item>
  395. </div>
  396. <common-title
  397. :title="$t(prefix + 'label_analysis')"
  398. ></common-title>
  399. <div class="flex justify-between items-center">
  400. <el-form-item label="利润率">
  401. <el-input
  402. v-model="form.profit_margin"
  403. @input="() => onForm3ItemChange(form, 'profit_margin')"
  404. >
  405. <template #append>%</template>
  406. </el-input>
  407. </el-form-item>
  408. <el-form-item label="利润">
  409. <el-input
  410. v-model="form.profit"
  411. disabled
  412. >
  413. <template #append>AUD</template>
  414. </el-input>
  415. </el-form-item>
  416. </div>
  417. </el-form>
  418. <div class="flex justify-center items-center">
  419. <el-button
  420. type="primary"
  421. size="small"
  422. @click="
  423. () => {
  424. isExport = 0
  425. checkForm(1)
  426. }
  427. "
  428. >
  429. {{ $t('btn_save') }}
  430. </el-button>
  431. <el-button
  432. type="primary"
  433. size="small"
  434. plain
  435. @click="
  436. () => {
  437. isExport = 1
  438. checkForm(2)
  439. }
  440. "
  441. >
  442. 保存并导出
  443. </el-button>
  444. <!-- <el-button @click="generate">test</el-button> -->
  445. </div>
  446. </div>
  447. </div>
  448. </div>
  449. </div>
  450. <setDTD
  451. v-model:visible="dtdVisible"
  452. :form-data="dtdData"
  453. @save="dtdChange"
  454. ></setDTD>
  455. <setLCL
  456. v-model:visible="lclVisible"
  457. :form-data="lclData"
  458. @save="lclChange"
  459. ></setLCL>
  460. <freight
  461. v-model:visible="componentFreightVisible"
  462. @save="getCalcParam"
  463. ></freight>
  464. <CompExportForm
  465. v-model:visible="componentExportFormVisible"
  466. :step3-form-list="step3FormList"
  467. :creator-options="creatorOptions"
  468. :form-data="exportForm"
  469. :city-list="cityList"
  470. @save="onExportFormSave"
  471. ></CompExportForm>
  472. </el-dialog>
  473. </div>
  474. </template>
  475. <script lang="ts" setup>
  476. import { defineComponent, ref, watch, computed, nextTick, inject } from 'vue'
  477. import {
  478. ElButton,
  479. ElNotification,
  480. ElMessage,
  481. ElForm,
  482. ElFormItem,
  483. ElInput,
  484. ElSelect,
  485. ElDialog,
  486. ElOption,
  487. ElSwitch,
  488. ElCard,
  489. ElCheckTag,
  490. ElTooltip,
  491. } from 'element-plus'
  492. import cloneDeep from 'lodash.clonedeep'
  493. import commonTitle from './components/title.vue'
  494. import setDTD from './components/dialogDTD.vue'
  495. import setLCL from './components/dialogLCL.vue'
  496. import freight from '../freight.vue'
  497. import step1 from './components/step1.vue'
  498. import CompExportForm from '../exportForm.vue'
  499. import exportQuota from '../exportQuota2.vue'
  500. import mathjs, { savePrecision } from '@/utils/math.js'
  501. import Cookies from 'js-cookie'
  502. import { getCalcParams, saveCalcData } from '@/api/indent'
  503. import { $t } from '@/i18n/index'
  504. defineComponent({
  505. name: 'ComponentAaaa',
  506. })
  507. const {
  508. visible = false,
  509. dataForCalc = {} as any,
  510. creatorOptions = [],
  511. } = defineProps<{
  512. visible: boolean
  513. dataForCalc: object
  514. creatorOptions: any[]
  515. }>()
  516. const $emit = defineEmits([
  517. 'update:visible',
  518. 'save-price-calc',
  519. 'save-price-calc',
  520. ])
  521. const $mediaRegExp = inject('mediaRegExp') as RegExp
  522. const prefix = 'order.indent_calc.'
  523. const loading = ref(false)
  524. const componentFreightVisible = ref(false)
  525. const componentExportFormVisible = ref(false)
  526. const productInfo = ref({} as any)
  527. const setting = ref({} as any) // 运费设置数据
  528. // 用在step2界面上显示的表单
  529. const formList = ref([] as any[])
  530. // 用在step3界面上显示的表单
  531. const step3FormList = ref([] as any[])
  532. const currentTab = ref(0) // 当前显示的step3表单的tab索引
  533. // 提交的表单, 用来存真正提交的数据.
  534. const formData = ref({
  535. cal_city: {
  536. local_city: '',
  537. },
  538. cal_shipment_method: {},
  539. } as any)
  540. // 导出pdf那个表单
  541. let exportForm = ref({
  542. city: '', // 显示在导出pdf的城市地址, 不是step2的运费选择地址.
  543. exchange: '',
  544. days: '30',
  545. gst_name: '+GST',
  546. notes: '',
  547. other_notes: '',
  548. saleperson: '',
  549. } as any)
  550. const step3FormDemo = ref({
  551. coo_certificate: 'on', // coo 开关
  552. tax: 0, // 税点 coo打开则是0, 关闭默认是5
  553. tax_fee: 0, // 税费 coo打开则是0, 需要根据税点计算
  554. gatt_tax_fee: 60, // 入口报关费用
  555. review_cost: 0, // 验货费
  556. cn_freight_cost: 0, // 国内运费总价
  557. local_freight_cost: 0, // 国外本地运费
  558. add_freight_cost: 0, // 本地收费
  559. freight: 0, // 运费总成本
  560. setup_cost: 0, // Set Up Cost
  561. sold_unit: 0, // 售卖单价
  562. sold_price: 0, // 售卖总价
  563. profit_margin: 30, // mark up 利润率
  564. profit: 0, // 利润
  565. } as any)
  566. const imageList = ref([] as any[])
  567. const cityList = ref([] as any[])
  568. // 空运选项. 参考: https://www.tapd.cn/59388921/prong/stories/view/1159388921001001048
  569. const airOption = [
  570. { label: 'DHL', value: 'dhl' },
  571. { label: 'TNT', value: 'tnt' },
  572. { label: 'Fedex', value: 'fedex' },
  573. ]
  574. // 海运选项
  575. const seaOption = ref([
  576. { label: 'FCL', value: 'fcl' },
  577. { label: 'LCL', value: 'lcl' },
  578. ])
  579. const dtdData = ref({} as any)
  580. const lclData = ref({} as any)
  581. const dtdVisible = ref(false)
  582. const lclVisible = ref(false)
  583. const show = ref(false)
  584. watch(
  585. () => visible,
  586. () => {
  587. show.value = visible
  588. resetData()
  589. if (show.value) {
  590. getCalcParam()
  591. }
  592. },
  593. )
  594. let getCalcParam = () => {
  595. getCalcParams().then((response: any) => {
  596. const res = response.result
  597. if (response.code !== 1) return
  598. cityList.value = res.city_list
  599. if (cityList.value.length) {
  600. formData.value.cal_city.local_city = cityList.value[0]
  601. }
  602. setting.value = Object.assign({}, res.data)
  603. setting.value.dhl_airline = JSON.parse(setting.value.dhl_airline)
  604. if (!Array.isArray(setting.value.dhl_airline)) {
  605. setting.value.dhl_airline = []
  606. } else {
  607. setting.value.dhl_airline = setting.value.dhl_airline.map((i: any) => {
  608. const result: any = {}
  609. for (const key in i) {
  610. if (Object.prototype.hasOwnProperty.call(i, key)) {
  611. result[key] = Number(i[key])
  612. }
  613. }
  614. return result
  615. })
  616. }
  617. setting.value.tnt_airline = JSON.parse(setting.value.tnt_airline)
  618. if (!Array.isArray(setting.value.tnt_airline)) {
  619. setting.value.tnt_airline = []
  620. } else {
  621. setting.value.tnt_airline = setting.value.tnt_airline.map((i: any) => {
  622. const result: any = {}
  623. for (const key in i) {
  624. if (Object.prototype.hasOwnProperty.call(i, key)) {
  625. result[key] = Number(i[key])
  626. }
  627. }
  628. return result
  629. })
  630. }
  631. setting.value.fedex_airline = JSON.parse(setting.value.fedex_airline)
  632. if (!Array.isArray(setting.value.fedex_airline)) {
  633. setting.value.fedex_airline = []
  634. } else {
  635. setting.value.fedex_airline = setting.value.fedex_airline.map(
  636. (i: any) => {
  637. const result: any = {}
  638. for (const key in i) {
  639. if (Object.prototype.hasOwnProperty.call(i, key)) {
  640. result[key] = Number(i[key])
  641. }
  642. }
  643. return result
  644. },
  645. )
  646. }
  647. setting.value.airplus = JSON.parse(setting.value.airplus)
  648. if (!Array.isArray(setting.value.airplus)) {
  649. setting.value.airplus = []
  650. } else {
  651. setting.value.airplus = setting.value.airplus.map((i: any) => {
  652. const result: any = {}
  653. for (const key in i) {
  654. if (Object.prototype.hasOwnProperty.call(i, key)) {
  655. result[key] = Number(i[key])
  656. }
  657. }
  658. return result
  659. })
  660. }
  661. setting.value.cbm = JSON.parse(setting.value.cbm)
  662. if (!Array.isArray(setting.value.cbm)) {
  663. setting.value.cbm = []
  664. } else {
  665. setting.value.cbm = setting.value.cbm.map((i: any) => {
  666. const result: any = {}
  667. for (const key in i) {
  668. if (Object.prototype.hasOwnProperty.call(i, key)) {
  669. result[key] = Number(i[key])
  670. }
  671. }
  672. return result
  673. })
  674. }
  675. delete setting.value.admin_id
  676. delete setting.value.id
  677. delete setting.value.is_del
  678. delete setting.value.update_time
  679. delete setting.value.create_time
  680. delete setting.value.delete_time
  681. // 把浅层的几个属性全部转换成数字. 方便后续的计算
  682. for (const key in setting.value) {
  683. if (
  684. Object.prototype.hasOwnProperty.call(setting.value, key) &&
  685. typeof setting.value[key] === 'string'
  686. ) {
  687. setting.value[key] = Number(setting.value[key])
  688. }
  689. }
  690. initProductInfo()
  691. initForm([], true)
  692. })
  693. }
  694. let close = (done = {} as any) => {
  695. $emit('update:visible', false)
  696. imageList.value = []
  697. if (typeof done === 'function') done()
  698. }
  699. let resetData = () => {
  700. formData.value = {
  701. cal_city: {
  702. local_city: '',
  703. },
  704. cal_shipment_method: {},
  705. }
  706. exportForm.value = {
  707. pdf_city: '',
  708. cycle_name: '',
  709. exchange: '',
  710. days: '30',
  711. gst_name: '+GST',
  712. notes: '',
  713. other_notes: '',
  714. saleperson: '',
  715. }
  716. }
  717. // 总箱数. 购买数量/每箱数量.
  718. let computedBoxNumber = computed(() => {
  719. if (productInfo.value.number?.length) {
  720. return productInfo.value.number.map((i: any) =>
  721. mathjs.chain(i).divide(productInfo.value.in_package).done(),
  722. )
  723. } else {
  724. return []
  725. }
  726. })
  727. // 单箱体积. 立方厘米. 长*宽*高
  728. let computedBulk = computed(() => {
  729. return mathjs
  730. .chain(productInfo.value.package_size_length || 1)
  731. .multiply(productInfo.value.package_size_width || 1)
  732. .multiply(productInfo.value.package_size_height || 1)
  733. .done()
  734. })
  735. // 是否重货. 每箱毛重 大于等于 (每箱体积 / 5000) 则为重货.
  736. let isHeavyGoods = computed(() => {
  737. if (
  738. productInfo.value.package_weight >
  739. mathjs.chain(computedBulk.value).divide(5000).done()
  740. ) {
  741. return {
  742. flag: true,
  743. label: '重货',
  744. }
  745. } else {
  746. return {
  747. flag: false,
  748. label: '轻货',
  749. }
  750. }
  751. })
  752. // 是否重货. 空+派 配送方式专属的 轻重货计算逻辑, 与标准的计算区别在于一个除6000一个除5000
  753. let isHeavyGoodsPlus = computed(() => {
  754. if (
  755. productInfo.value.package_weight >
  756. mathjs.chain(computedBulk.value).divide(6000).done()
  757. ) {
  758. return {
  759. flag: true,
  760. label: '重货',
  761. }
  762. } else {
  763. return {
  764. flag: false,
  765. label: '轻货',
  766. }
  767. }
  768. })
  769. // 界面上写的是总重量, 实际上应该理解成负重系数, 即轻重货占不同体积, 货运得收不同费用维持利润.
  770. // 重货是货物重量本身, 即每箱毛重*箱数, 轻货则是每箱体积*箱数/5000
  771. let computedTotalWeight = computed(() => {
  772. if (!computedBoxNumber.value.length) return []
  773. if (isHeavyGoods.value.flag) {
  774. // 重货
  775. return computedBoxNumber.value.map((i: number) => {
  776. return mathjs
  777. .chain(
  778. // 重量浮动比. 在运费参数里面设置的.
  779. mathjs
  780. .chain(Number(setting.value.rate_weight || 0))
  781. .divide(100)
  782. .add(1)
  783. .done(),
  784. )
  785. .multiply(productInfo.value.package_weight)
  786. .multiply(i)
  787. .done()
  788. })
  789. } else {
  790. // 轻货
  791. return computedBoxNumber.value.map((i: number) => {
  792. return mathjs.chain(computedBulk.value).multiply(i).divide(5000).done()
  793. })
  794. }
  795. })
  796. // 空+派 方式的(总重量)负重系数.
  797. let computedTotalWeightPlus = computed(() => {
  798. if (!computedBoxNumber.value.length) return []
  799. if (isHeavyGoodsPlus.value.flag) {
  800. // 重货
  801. return computedBoxNumber.value.map((i: number) => {
  802. return mathjs
  803. .chain(
  804. // 重量浮动比. 在运费参数里面设置的.
  805. mathjs
  806. .chain(Number(setting.value.rate_weight || 0))
  807. .divide(100)
  808. .add(1)
  809. .done(),
  810. )
  811. .multiply(productInfo.value.package_weight)
  812. .multiply(i)
  813. .done()
  814. })
  815. } else {
  816. // 轻货
  817. return computedBoxNumber.value.map((i: number) => {
  818. return mathjs.chain(computedBulk.value).multiply(i).divide(6000).done()
  819. })
  820. }
  821. })
  822. // 总体积CBM. 单箱体积*箱数*浮动 / 一百万
  823. let computedTotalBulk = computed(() => {
  824. if (!computedBoxNumber.value.length) return []
  825. return computedBoxNumber.value.map((i: number) => {
  826. return mathjs
  827. .chain(
  828. // 体积浮动比. 在运费参数里面设置的.
  829. mathjs.chain(Number(setting.value.rate_bulk)).divide(100).add(1).done(),
  830. )
  831. .multiply(computedBulk.value)
  832. .multiply(i)
  833. .divide(1000000)
  834. .done()
  835. })
  836. })
  837. // 返回值会有两个子数组. 第一个是重货的, 第二个是轻货的,
  838. // 每个数组里有两项, 分别是计算公式的参数1和2.
  839. let computedCityFreightParams = computed(() => {
  840. if (formData.value.cal_city?.local_city) {
  841. return setting.value.city_list[formData.value.cal_city.local_city]
  842. }
  843. return []
  844. })
  845. // 国内运费
  846. // let computedLocalFreight = computed(() => {
  847. // if (isHeavyGoodsPlus.value.flag) {
  848. // return this.computedTotalWeight((i:number) => {
  849. // return mathjs.chain(setting.value.cn_price_heavy).multiply(i).done()
  850. // })
  851. // }
  852. // return computedTotalBulk.value.map((i:number) => {
  853. // return mathjs.chain(setting.value.cn_price).multiply(i).done()
  854. // })
  855. // })
  856. let initProductInfo = () => {
  857. const temp = cloneDeep(dataForCalc)
  858. if (Array.isArray(temp.product_image)) {
  859. imageList.value = temp.product_image.map((img: string) => {
  860. return $mediaRegExp.test(img)
  861. ? img
  862. : import.meta.env.VITE_APP_OSS_PREFIX + img
  863. })
  864. }
  865. temp.cost_name = []
  866. temp.cost_price = []
  867. if (temp.cost_list && temp.cost_list.length > 2) {
  868. const t = JSON.parse(temp.cost_list)
  869. t.forEach((item: any) => {
  870. temp.cost_name.push(item.cost_name)
  871. temp.cost_price.push(parseFloat(item.cost_price))
  872. })
  873. }
  874. temp.number = []
  875. temp.price = []
  876. temp.days = []
  877. if (temp.price_list && temp.price_list.length > 2) {
  878. const t = JSON.parse(temp.price_list)
  879. t.forEach((item: any) => {
  880. temp.number.push(parseFloat(item.number))
  881. temp.price.push(parseFloat(item.price))
  882. temp.days.push(parseFloat(item.days))
  883. })
  884. }
  885. // 之前保存到数据库的计价信息
  886. if (typeof temp.save_cal === 'string' && temp.save_cal.length > 2) {
  887. temp.save_cal = JSON.parse(temp.save_cal)
  888. } else {
  889. temp.save_cal = {}
  890. }
  891. // 初始化之前选择的城市
  892. if (temp.save_cal.cal_city) {
  893. formData.value.cal_city.local_city = temp.save_cal.cal_city.local_city
  894. }
  895. productInfo.value = temp
  896. }
  897. /**
  898. * 根据 productInfo 生成表单, 包括step2的和部分step3的数据.
  899. */
  900. let initForm = (switchStatus = [] as any[], useOldFormData = true) => {
  901. formList.value = []
  902. step3FormList.value = []
  903. if (productInfo.value.number?.length) {
  904. // 保存的数据库里面的中途配送数据
  905. const oldMidWayData = productInfo.value.save_cal.cal_shipment_method || {}
  906. productInfo.value.number.forEach((item: any, index: number) => {
  907. const t: any = {}
  908. t[`method_0_${item}`] = 'dhl'
  909. t[`method_2_${item}`] = 'lcl'
  910. if (useOldFormData) {
  911. t[`method_0_${item}`] = oldMidWayData[`method_0_${item}`] || 'dhl'
  912. t[`method_2_${item}`] = oldMidWayData[`method_2_${item}`] || 'lcl'
  913. }
  914. t[`method_3_${item}`] = ''
  915. t[`method_4_${item}`] = ''
  916. // 这5个是开关
  917. t[`0_${item}`] = 'off'
  918. t[`1_${item}`] = 'off'
  919. t[`2_${item}`] = 'off'
  920. t[`3_${item}`] = 'off'
  921. t[`4_${item}`] = 'off'
  922. if (useOldFormData) {
  923. // 上一次编辑中, 保存到数据库的数据记录
  924. t[`0_${item}`] = oldMidWayData[`0_${item}`] || 'off'
  925. t[`1_${item}`] = oldMidWayData[`1_${item}`] || 'off'
  926. t[`2_${item}`] = oldMidWayData[`2_${item}`] || 'off'
  927. t[`3_${item}`] = oldMidWayData[`3_${item}`] || 'off'
  928. t[`4_${item}`] = oldMidWayData[`4_${item}`] || 'off'
  929. }
  930. if (switchStatus.length) {
  931. // 记录当前页面之前已经打开的开关状态, 用在切换城市后的重新初始化逻辑中.
  932. t[`0_${item}`] = switchStatus[index][`0_${item}`] || 'off'
  933. t[`1_${item}`] = switchStatus[index][`1_${item}`] || 'off'
  934. t[`2_${item}`] = switchStatus[index][`2_${item}`] || 'off'
  935. t[`3_${item}`] = switchStatus[index][`3_${item}`] || 'off'
  936. t[`4_${item}`] = switchStatus[index][`4_${item}`] || 'off'
  937. }
  938. // 目的城市运费
  939. t.city_price_0 = 0
  940. t.city_price_1 = 0
  941. t.city_price_2 = 0
  942. t.city_price_3 = 0
  943. t.city_price_4 = 0
  944. // 中途运费总额. 即海运和空运的.
  945. t.midway_price_0 = 0
  946. t.midway_price_1 = 0
  947. t.midway_price_2 = 0
  948. // 这两个是自行输入然后直接显示在step2的中途运费位置, 其他三个是计算出来的,
  949. // 同时也作为step3的运费总成本(AUD)
  950. t.freight_cost_3 = 0
  951. t.freight_cost_4 = 0
  952. // 国内本地运费
  953. t.cn_price_0 = 0
  954. t.cn_price_1 = 0
  955. t.cn_price_2 = 0
  956. // 成本清单
  957. t.product_cost = calcProductCost(index) // 产品成本(AUD)
  958. t.extend_cost = calcExtendCost() // 制版及其它成本(AUD)
  959. t.total_cost = 0 // 总成本(AUD)
  960. // 给html模版分辨是哪个表单的数据
  961. t.number = item
  962. // 计价步骤上貌似没有用到的数据, 但是保存到数据库, 并且该对话框界面回显.
  963. t.fclData = {}
  964. t.lclData = {}
  965. if (useOldFormData) {
  966. const oldData = productInfo.value.save_cal || {}
  967. if (oldData[`cal_fcl_${item}`]) {
  968. t.fclData = oldData[`cal_fcl_${item}`]
  969. t.freight_cost_3 = oldData[`cal_fcl_${item}`].total_fcl
  970. }
  971. if (oldData[`cal_lcl_${item}`]) {
  972. t.lclData = oldData[`cal_lcl_${item}`]
  973. t.freight_cost_4 = oldData[`cal_lcl_${item}`].total_lcl
  974. }
  975. }
  976. formList.value.push(t)
  977. })
  978. const indentUser = Cookies.get('indent-user')
  979. let r = creatorOptions.filter((i) => i.label === indentUser)
  980. exportForm.value = {
  981. pdf_city: productInfo.value.save_cal.pdf_city || cityList.value[0],
  982. exchange: productInfo.value.save_cal.exchange || '',
  983. days: productInfo.value.save_cal.days || 30,
  984. gst_name: productInfo.value.save_cal.gst_name || '+GST',
  985. notes: productInfo.value.save_cal.notes || '',
  986. other_notes: productInfo.value.save_cal.other_notes || '',
  987. saleperson:
  988. productInfo.value.save_cal.saleperson || (r.length ? r[0].value : ''),
  989. }
  990. }
  991. setFreight()
  992. if (useOldFormData) {
  993. // 仅页面初始化时会执行到这里
  994. generateStep3Form(true)
  995. }
  996. }
  997. const onCityChange = (str: string) => {
  998. // 没保存计价之前, 切换城市会改变pdf_city. 保存之后, 再怎么切换也用保存的数据做默认数据, 除非手动更改pdf_city
  999. exportForm.value.pdf_city =
  1000. productInfo.value.save_cal.pdf_city || str || cityList.value[0]
  1001. }
  1002. let reGenerateFormAfterCityChange = () => {
  1003. const temp: any[] = formList.value.map((i, index) => {
  1004. const result: any = {}
  1005. result[`0_${productInfo.value.number[index]}`] =
  1006. i[`0_${productInfo.value.number[index]}`]
  1007. result[`1_${productInfo.value.number[index]}`] =
  1008. i[`1_${productInfo.value.number[index]}`]
  1009. result[`2_${productInfo.value.number[index]}`] =
  1010. i[`2_${productInfo.value.number[index]}`]
  1011. result[`3_${productInfo.value.number[index]}`] =
  1012. i[`3_${productInfo.value.number[index]}`]
  1013. result[`4_${productInfo.value.number[index]}`] =
  1014. i[`4_${productInfo.value.number[index]}`]
  1015. result[`method_0_${productInfo.value.number[index]}`] =
  1016. i[`method_0_${productInfo.value.number[index]}`]
  1017. result[`method_2_${productInfo.value.number[index]}`] =
  1018. i[`method_2_${productInfo.value.number[index]}`]
  1019. return result
  1020. })
  1021. initForm(temp)
  1022. generateStep3Form()
  1023. }
  1024. /**
  1025. * 切换运送方式之后, 重新计算中途运费和生成step3的表单内容. 开关属于开启状态才有必要重算
  1026. */
  1027. let reGenerateFormAfterMidwayTypeChange = () => {
  1028. setFreight()
  1029. generateStep3Form()
  1030. }
  1031. let generateStep3Form = (withOldData = true) => {
  1032. const labelList = ['快递', '空+派', '海运', 'DTD', 'LCL散货']
  1033. const result: any[] = []
  1034. formList.value.forEach((i: any) => {
  1035. for (let n = 0; n < 5; n++) {
  1036. if (i[`${n}_${i.number}`] === 'on') {
  1037. let temp = Object.assign({}, step3FormDemo.value, {
  1038. tabLabel: `${i.number} ${labelList[n]}`,
  1039. number: i.number,
  1040. typeNumber: n,
  1041. cn_freight_cost: 0,
  1042. product_cost: i.product_cost,
  1043. extend_cost: i.extend_cost,
  1044. total_cost: i.total_cost,
  1045. })
  1046. if (n <= 2) {
  1047. temp.cn_freight_cost = savePrecision(i[`cn_price_${n}`])
  1048. temp.local_freight_cost = savePrecision(i[`city_price_${n}`])
  1049. temp.add_freight_cost = savePrecision(i[`city_price_${n}`])
  1050. // 记录中途运费, 用在step3表单项变更后的重复计算
  1051. temp.midway_price = i[`midway_price_${n}`]
  1052. temp.freight_cost = mathjs
  1053. .chain(i[`midway_price_${n}`])
  1054. .add(i[`city_price_${n}`])
  1055. .done()
  1056. temp.total_cost = mathjs
  1057. .chain(i.product_cost)
  1058. .add(i.extend_cost)
  1059. .add(temp.freight_cost)
  1060. .add(
  1061. mathjs
  1062. .chain(temp.cn_freight_cost)
  1063. .divide(setting.value.rate_rmb_aud)
  1064. .done(),
  1065. )
  1066. .add(step3FormDemo.value.tax_fee)
  1067. .add(step3FormDemo.value.gatt_tax_fee)
  1068. .add(step3FormDemo.value.review_cost)
  1069. .done()
  1070. } else if (n === 3) {
  1071. temp.local_freight_cost = 0
  1072. temp.add_freight_cost = 0
  1073. temp.freight_cost = mathjs
  1074. .chain(i[`freight_cost_${n}`])
  1075. .add(i[`city_price_${n}`])
  1076. .done()
  1077. temp.total_cost = mathjs
  1078. .chain(i.product_cost)
  1079. .add(i.extend_cost)
  1080. .add(temp.freight_cost)
  1081. .add(
  1082. mathjs
  1083. .chain(temp.cn_freight_cost)
  1084. .divide(setting.value.rate_rmb_aud)
  1085. .done(),
  1086. )
  1087. .add(step3FormDemo.value.tax_fee)
  1088. .add(step3FormDemo.value.gatt_tax_fee)
  1089. .add(step3FormDemo.value.review_cost)
  1090. .done()
  1091. temp[`cn_price_${n}`] = 0 // 国内运费. 后两种配送方式默认是0
  1092. } else if (n === 4) {
  1093. temp.local_freight_cost = 0
  1094. temp.add_freight_cost = 0
  1095. temp.freight_cost = mathjs
  1096. .chain(i[`freight_cost_${n}`])
  1097. .add(i[`city_price_${n}`])
  1098. .done()
  1099. // total_lcl 运费总成本
  1100. // price 国内总运费
  1101. temp.total_cost = mathjs
  1102. .chain(i.product_cost)
  1103. .add(i.extend_cost)
  1104. .add(temp.freight_cost)
  1105. .add(
  1106. mathjs
  1107. .chain(temp.cn_freight_cost)
  1108. .divide(setting.value.rate_rmb_aud)
  1109. .done(),
  1110. )
  1111. .add(step3FormDemo.value.tax_fee)
  1112. .add(step3FormDemo.value.gatt_tax_fee)
  1113. .add(step3FormDemo.value.review_cost)
  1114. .done()
  1115. temp[`cn_price_${n}`] = 0 // 国内运费. 后两种配送方式默认是0
  1116. }
  1117. temp.profit = savePrecision(
  1118. // 利润率是整数, 除100算出百分数.
  1119. mathjs
  1120. .chain(step3FormDemo.value.profit_margin)
  1121. .divide(100)
  1122. .multiply(temp.total_cost)
  1123. .done(),
  1124. )
  1125. temp.sold_price = savePrecision(
  1126. mathjs
  1127. .chain(temp.total_cost)
  1128. .multiply(
  1129. mathjs
  1130. .chain(step3FormDemo.value.profit_margin)
  1131. .add(100)
  1132. .divide(100)
  1133. .done(),
  1134. )
  1135. .done(),
  1136. )
  1137. temp.sold_unit = mathjs
  1138. .chain(
  1139. Math.ceil(
  1140. Number(
  1141. mathjs
  1142. .chain(temp.sold_price)
  1143. .subtract(temp.add_freight_cost)
  1144. .subtract(step3FormDemo.value.setup_cost)
  1145. .divide(i.number)
  1146. .multiply(100)
  1147. .done(),
  1148. ),
  1149. ),
  1150. )
  1151. .divide(100)
  1152. .done()
  1153. exportForm.value[`zdy_date_${n}_${i.number}`] = ''
  1154. exportForm.value[`cycle_name_${n}_${i.number}`] = 'weeks'
  1155. if (withOldData === true) {
  1156. const oldData = productInfo.value.save_cal
  1157. if (oldData[`cal_quote_${n}_${i.number}`]) {
  1158. const cloneData = cloneDeep(oldData[`cal_quote_${n}_${i.number}`])
  1159. cloneData.freight_cost = cloneData.freight
  1160. delete cloneData.freight
  1161. temp = Object.assign(temp, cloneData)
  1162. }
  1163. exportForm.value[`zdy_date_${n}_${i.number}`] =
  1164. oldData[`zdy_date_${n}_${i.number}`] || ''
  1165. exportForm.value[`cycle_name_${n}_${i.number}`] =
  1166. oldData[`cycle_name_${n}_${i.number}`] || 'weeks'
  1167. }
  1168. result.push(temp)
  1169. }
  1170. }
  1171. })
  1172. step3FormList.value = result
  1173. }
  1174. // 设置 中途运费 及 国外当地运费
  1175. let setFreight = () => {
  1176. productInfo.value.number.forEach((item: any, index: number) => {
  1177. // 早起的需求限制3个, 现在暂定放开了. 限制的话, 超过3个的货物数量的运费直接就跳过不计算了.
  1178. // if (index > 2) {
  1179. // return
  1180. // }
  1181. const cityPrice = calcCityPrice(
  1182. computedTotalWeight.value[index],
  1183. computedTotalBulk.value[index],
  1184. )
  1185. // 国外本地运费, dtd 和 lcl散货默认是0, 所以这里只给前三个填充了值.
  1186. formList.value[index].city_price_0 = cityPrice
  1187. formList.value[index].city_price_1 = cityPrice
  1188. formList.value[index].city_price_2 = cityPrice
  1189. // 航空:总重量*空运费单价(AUD) * (1+燃油附加)
  1190. formList.value[index].midway_price_0 = mathjs
  1191. .chain(
  1192. getCurrentPrice(
  1193. formList.value[index][`method_0_${item}`],
  1194. computedTotalWeight.value[index],
  1195. ),
  1196. )
  1197. .multiply(computedTotalWeight.value[index])
  1198. .multiply(mathjs.chain(setting.value.rate_fuel).divide(100).add(1).done())
  1199. .done()
  1200. // 空+派 与航空的计算方式基本一致。
  1201. // 差异点为,重轻货判断及负重系数计算时,除以6000, 燃油附加是另外的字段.
  1202. formList.value[index].midway_price_1 = mathjs
  1203. .chain(getCurrentPricePlus(computedTotalWeightPlus.value[index]))
  1204. .multiply(computedTotalWeightPlus.value[index])
  1205. .multiply(
  1206. mathjs.chain(setting.value.rate_fuelplus).divide(100).add(1).done(),
  1207. )
  1208. .done()
  1209. // 海运 CBM * 基础参数里面的海运费. 旧后台这一项的计算有误差, cbm用了约进两位数之后的值来计算.
  1210. formList.value[index].midway_price_2 = mathjs
  1211. .chain(computedTotalBulk.value[index])
  1212. .multiply(setting.value.sea_fee)
  1213. .done()
  1214. const cn_price = calcLocalPrice(index)
  1215. // 国内运费
  1216. formList.value[index].cn_price_0 = cn_price
  1217. formList.value[index].cn_price_1 = cn_price
  1218. formList.value[index].cn_price_2 = cn_price
  1219. })
  1220. }
  1221. // 目标城市的快递费用计算. 这个判断轻重货时不用区分 空+派 的 /6000
  1222. let calcCityPrice = (heavy: number, bulk: number) => {
  1223. const p = computedCityFreightParams.value
  1224. if (!p.length) return 0
  1225. if (isHeavyGoods.value.flag) {
  1226. return p[0][0] + (p[0][1] / 50) * heavy
  1227. } else {
  1228. return p[1][0] + p[1][1] * bulk
  1229. }
  1230. }
  1231. // 目标城市的快递费用计算. 空+派方式的, 这种方式轻重货判断计算公式不一样
  1232. // let calcCityPricePlus = (heavy: number, bulk: number) => {
  1233. // const p = computedCityFreightParams.value
  1234. // if (!p.length) return 0
  1235. // if (isHeavyGoodsPlus.value.flag) {
  1236. // return p[0][0] + (p[0][1] / 50) * heavy
  1237. // } else {
  1238. // return p[1][0] + p[1][1] * bulk
  1239. // }
  1240. // }
  1241. // 国内运费. 轻重货计算公式不同
  1242. let calcLocalPrice = (index: number) => {
  1243. if (isHeavyGoods.value.flag) {
  1244. return mathjs
  1245. .chain(setting.value.cn_price_heavy)
  1246. .multiply(computedTotalWeight.value[index])
  1247. .done()
  1248. } else {
  1249. return mathjs
  1250. .chain(setting.value.cn_price)
  1251. .multiply(computedTotalBulk.value[index])
  1252. .done()
  1253. }
  1254. }
  1255. // 打样费用 和 额外费用(报价信息里面的出厂价-额外费用), 再除以汇率
  1256. let calcExtendCost = () => {
  1257. const reduceCost = productInfo.value.cost_price.reduce(
  1258. (total: number, i: number) => {
  1259. total = total + i
  1260. return total
  1261. },
  1262. 0,
  1263. )
  1264. const extendCost = mathjs
  1265. .chain(reduceCost)
  1266. .add(Number(productInfo.value.demo_cost) || 0)
  1267. .divide(setting.value.rate_rmb_aud)
  1268. .done()
  1269. return extendCost
  1270. }
  1271. // 产品成本 = 单价 * 数量 / 汇率
  1272. let calcProductCost = (index: number) => {
  1273. return mathjs
  1274. .chain(productInfo.value.number[index])
  1275. .multiply(productInfo.value.price[index])
  1276. .divide(setting.value.rate_rmb_aud)
  1277. .done()
  1278. }
  1279. // 获取 当前重量的 运费单价
  1280. let getCurrentPrice = (key: string | number, heavy: number) => {
  1281. const target = setting.value[`${key}_airline`]
  1282. let result = 0
  1283. if (Array.isArray(target) && target.length) {
  1284. result = target.findIndex((i: any) => i.min <= heavy && i.max > heavy)
  1285. }
  1286. return target[result].price
  1287. }
  1288. // 空+ 派
  1289. let getCurrentPricePlus = (heavy: number) => {
  1290. const target = setting.value.airplus
  1291. let result = 0
  1292. if (Array.isArray(target) && target.length) {
  1293. result = target.findIndex((i: any) => i.min <= heavy && i.max > heavy)
  1294. }
  1295. return target[result].price
  1296. }
  1297. // 开关切换前的点击捕获, 检查前提数据是否已输入, 没有的话不允许打开开关. 旧后台的交互.
  1298. let captureSwitchChange = (e: any, obj: any, n: number) => {
  1299. if (n === 3) {
  1300. if (typeof obj.freight_cost_3 !== 'number' || obj.freight_cost_3 <= 0) {
  1301. ElMessage.warning('请先点击编辑, 填写相关数据, 再打开开关')
  1302. e.preventDefault()
  1303. e.stopPropagation()
  1304. }
  1305. } else if (n === 4) {
  1306. if (typeof obj.freight_cost_4 !== 'number' || obj.freight_cost_4 <= 0) {
  1307. ElMessage.warning('请先点击编辑, 填写相关数据, 再打开开关')
  1308. e.preventDefault()
  1309. e.stopPropagation()
  1310. }
  1311. }
  1312. }
  1313. let changeCOO = (form: any, value: any) => {
  1314. // 因为设置了active value, value变量不再是布尔值
  1315. if (value === 'on') {
  1316. form.tax = 0
  1317. onForm3ItemChange(form, 'tax')
  1318. } else {
  1319. form.tax = 5
  1320. onForm3ItemChange(form, 'tax')
  1321. }
  1322. }
  1323. let calcTaxFee = (form: any) => {
  1324. form.tax_fee = savePrecision(
  1325. mathjs.chain(form.product_cost).multiply(form.tax).divide(100).done(),
  1326. )
  1327. }
  1328. let calcProfit = (form: any) => {
  1329. form.profit = savePrecision(
  1330. mathjs.chain(form.sold_price).subtract(form.total_cost).done(),
  1331. )
  1332. }
  1333. let calcProfitMargin = (form: any) => {
  1334. form.profit_margin = savePrecision(
  1335. mathjs.chain(form.profit).divide(form.total_cost).multiply(100).done(),
  1336. )
  1337. }
  1338. let calcTotalCost = (form: any) => {
  1339. form.total_cost = mathjs
  1340. .chain(form.product_cost)
  1341. .add(form.extend_cost)
  1342. .add(form.freight_cost)
  1343. .add(
  1344. mathjs
  1345. .chain(form.cn_freight_cost)
  1346. .divide(setting.value.rate_rmb_aud)
  1347. .done(),
  1348. )
  1349. .add(form.tax_fee)
  1350. .add(form.gatt_tax_fee)
  1351. .add(form.review_cost)
  1352. .done()
  1353. }
  1354. /**
  1355. * 用利润率计算售价总价
  1356. **/
  1357. let calcSoldPriceByProfitMargin = (form: any) => {
  1358. form.sold_price = savePrecision(
  1359. mathjs
  1360. .chain(form.total_cost)
  1361. .multiply(mathjs.chain(form.profit_margin).add(100).divide(100).done())
  1362. .done(),
  1363. )
  1364. }
  1365. /**
  1366. * 用单价计算售价总价. 单价*数量 + setup cost + add freight cost
  1367. **/
  1368. let calcSoldPriceBySoldUnit = (form: any) => {
  1369. form.sold_price = savePrecision(
  1370. mathjs
  1371. .chain(form.sold_unit)
  1372. .multiply(form.number)
  1373. .add(form.setup_cost)
  1374. .add(form.add_freight_cost)
  1375. .done(),
  1376. )
  1377. }
  1378. let onForm3ItemChange = (form: any, key: string) => {
  1379. let dotFlag = false
  1380. if (typeof form[key] === 'string' && /\.$/.test(form[key])) {
  1381. // 处理小数点
  1382. form[key] = form[key].replace(/\.$/, '')
  1383. dotFlag = true
  1384. }
  1385. // 各输入框之间的输入值变动会有联动.
  1386. // 注意, 各 case 里面的调用顺序有影响, 改动之前务必保证你理解当前在做什么.
  1387. switch (key) {
  1388. case 'tax':
  1389. // 影响 税费、总成本、利润率、利润值
  1390. calcTaxFee(form)
  1391. calcTotalCost(form)
  1392. calcProfit(form)
  1393. calcProfitMargin(form)
  1394. break
  1395. case 'review_cost':
  1396. // 影响 总成本、利润率、利润值
  1397. calcTotalCost(form)
  1398. calcProfit(form)
  1399. calcProfitMargin(form)
  1400. break
  1401. case 'cn_freight_cost':
  1402. // 影响 总成本、利润率、利润值
  1403. calcTotalCost(form)
  1404. calcProfit(form)
  1405. calcProfitMargin(form)
  1406. break
  1407. case 'local_freight_cost':
  1408. // 影响 运费总成本、总成本、售卖总价、利润率. 利润值(不影响)
  1409. form.add_freight_cost = form.local_freight_cost
  1410. form.freight_cost = mathjs
  1411. .chain(form.local_freight_cost)
  1412. .add(form.midway_price)
  1413. .done()
  1414. calcTotalCost(form)
  1415. calcProfitMargin(form)
  1416. calcSoldPriceByProfitMargin(form)
  1417. break
  1418. case 'add_freight_cost':
  1419. // 影响 售卖总价、利润率、利润值
  1420. // fallthrough
  1421. case 'setup_cost':
  1422. // 影响 售卖总价、利润值、利润率
  1423. // fallthrough
  1424. case 'sold_unit':
  1425. // 直接乘算出售卖总价, 更新利润率、利润值
  1426. calcSoldPriceBySoldUnit(form)
  1427. calcProfit(form)
  1428. calcProfitMargin(form)
  1429. break
  1430. case 'profit_margin':
  1431. // 影响 利润值、售卖总价、售卖单价
  1432. calcSoldPriceByProfitMargin(form)
  1433. calcProfit(form)
  1434. form.sold_unit = mathjs
  1435. .chain(
  1436. Math.ceil(
  1437. Number(
  1438. mathjs
  1439. .chain(form.sold_price)
  1440. .subtract(form.add_freight_cost)
  1441. .subtract(form.setup_cost)
  1442. .divide(form.number)
  1443. .multiply(100)
  1444. .done(),
  1445. ),
  1446. ),
  1447. )
  1448. .divide(100)
  1449. .done()
  1450. break
  1451. case 'gatt_tax_fee':
  1452. calcTotalCost(form)
  1453. calcProfit(form)
  1454. calcProfitMargin(form)
  1455. break
  1456. default:
  1457. // 默认不做任何处理
  1458. break
  1459. }
  1460. if (dotFlag) {
  1461. form[key] = form[key] + '.'
  1462. }
  1463. }
  1464. let openDTDDialog = (columnNum: number, rowNum: number) => {
  1465. dtdData.value = {
  1466. rowNum,
  1467. columnNum,
  1468. freight_cost: formList.value[rowNum][`freight_cost_${columnNum}`] || '',
  1469. method_fcl: formList.value[rowNum].fclData.method_fcl || '',
  1470. }
  1471. dtdVisible.value = true
  1472. }
  1473. let dtdChange = (data: any) => {
  1474. formList.value[dtdData.value.rowNum][
  1475. `freight_cost_${dtdData.value.columnNum}`
  1476. ] = Number(data.freight_cost) || 0
  1477. formList.value[dtdData.value.rowNum].fclData.method_fcl =
  1478. data.method_fcl || ''
  1479. formList.value[dtdData.value.rowNum].fclData.total_fcl =
  1480. Number(data.freight_cost) || 0
  1481. generateStep3Form()
  1482. }
  1483. let openLCLDialog = (columnNum: number, rowNum: number) => {
  1484. lclData.value = {
  1485. rowNum,
  1486. columnNum,
  1487. heavy: computedTotalWeight.value[rowNum],
  1488. freight_cost: formList.value[rowNum][`freight_cost_${columnNum}`] || '',
  1489. margin_lcl: formList.value[rowNum].lclData.margin_lcl || '',
  1490. unit_lcl: formList.value[rowNum].lclData.unit_lcl || '',
  1491. price: formList.value[rowNum].lclData.price || '',
  1492. }
  1493. lclVisible.value = true
  1494. }
  1495. let lclChange = (data: any) => {
  1496. // 这个是 运输总成本, 会添加到 step3form 里面用于计算
  1497. formList.value[lclData.value.rowNum][
  1498. `freight_cost_${lclData.value.columnNum}`
  1499. ] = Number(data.freight_cost) || 0
  1500. // 这四个保存到数据库的, total_lcl其实就是 运输总成本
  1501. formList.value[lclData.value.rowNum].lclData.unit_lcl =
  1502. Number(data.unit_lcl) || 0
  1503. formList.value[lclData.value.rowNum].lclData.price = Number(data.price) || 0
  1504. formList.value[lclData.value.rowNum].lclData.margin_lcl =
  1505. Number(data.margin_lcl) || 0
  1506. formList.value[lclData.value.rowNum].lclData.total_lcl =
  1507. Number(data.freight_cost) || 0
  1508. generateStep3Form()
  1509. }
  1510. let isExport = ref(0)
  1511. let checkForm = (type = 1) => {
  1512. // 利用引用传值变更 formData 的内容
  1513. const method = formData.value.cal_shipment_method
  1514. formList.value.forEach((item) => {
  1515. for (let i = 0; i < 5; i++) {
  1516. if (item[`${i}_${item.number}`] === 'on') {
  1517. method[`${i}_${item.number}`] = 'on'
  1518. }
  1519. }
  1520. method[`method_0_${item.number}`] = item[`method_0_${item.number}`]
  1521. method[`method_2_${item.number}`] = item[`method_2_${item.number}`]
  1522. if (item.lclData && item.lclData.total_lcl) {
  1523. formData.value[`cal_lcl_${item.number}`] = cloneDeep(item.lclData)
  1524. }
  1525. if (item.fclData && item.fclData.total_fcl) {
  1526. formData.value[`cal_fcl_${item.number}`] = cloneDeep(item.fclData)
  1527. }
  1528. })
  1529. step3FormList.value.forEach((item) => {
  1530. formData.value[`cal_quote_${item.typeNumber}_${item.number}`] = {
  1531. coo_certificate: item.coo_certificate, // coo 开关
  1532. tax: item.tax, // 税点 coo打开则是0, 关闭默认是5
  1533. tax_fee: item.tax_fee, // 税费 coo打开则是0, 需要根据税点计算
  1534. gatt_tax_fee: item.gatt_tax_fee, // 入口报关费用
  1535. review_cost: item.review_cost, // 验货费
  1536. cn_freight_cost: item.cn_freight_cost, // 国内运费总价
  1537. local_freight_cost: item.local_freight_cost, // 国外本地运费
  1538. add_freight_cost: item.add_freight_cost, // 本地收费
  1539. freight: savePrecision(item.freight_cost), // 运费总成本
  1540. setup_cost: item.setup_cost, // Set Up Cost
  1541. sold_unit: item.sold_unit, // 售卖单价
  1542. sold_price: item.sold_price, // 售卖总价
  1543. profit_margin: item.profit_margin, // mark up 利润率
  1544. profit: item.profit, // 利润
  1545. }
  1546. })
  1547. const p = Object.assign(
  1548. {
  1549. id: dataForCalc.id || '',
  1550. },
  1551. cloneDeep(formData.value),
  1552. )
  1553. if (isExport.value === 1) p.is_export = 1 // 后端用来区分是否是点了导出
  1554. if (type === 1) {
  1555. saveCalc(Object.assign(p, exportForm.value))
  1556. } else if (type === 2) {
  1557. componentExportFormVisible.value = true
  1558. }
  1559. }
  1560. let saveCalc = (p: any) => {
  1561. loading.value = true
  1562. saveCalcData(p)
  1563. .then((res: any) => {
  1564. if (res.code !== 1) {
  1565. ElNotification({
  1566. type: 'error',
  1567. title: '保存出错了',
  1568. duration: 3000,
  1569. })
  1570. return
  1571. }
  1572. ElNotification({
  1573. type: 'success',
  1574. title: '保存成功',
  1575. duration: 3000,
  1576. })
  1577. $emit('save-price-calc')
  1578. })
  1579. .finally(() => {
  1580. setTimeout(() => {
  1581. loading.value = false
  1582. }, 300)
  1583. })
  1584. }
  1585. let onExportFormSave = (data: any) => {
  1586. exportForm.value = cloneDeep(data)
  1587. checkForm(1)
  1588. // 生成pdf涉及DOM操作, 故变更数据后需要等界面更新,
  1589. // 此处需要nextTick保证生成时数据已经更新到了界面上.
  1590. nextTick(() => {
  1591. generate()
  1592. })
  1593. }
  1594. const compExportQuotaRef = ref()
  1595. let generate = () => {
  1596. if (
  1597. compExportQuotaRef.value.generatePDF &&
  1598. typeof compExportQuotaRef.value.generatePDF === 'function'
  1599. ) {
  1600. compExportQuotaRef.value.generatePDF()
  1601. }
  1602. }
  1603. </script>
  1604. <style lang="scss">
  1605. .compnent-price-calc {
  1606. .custom-calc-price-dialog {
  1607. margin-top: 0 !important;
  1608. margin-bottom: 0 !important;
  1609. height: 100vh;
  1610. .el-dialog__body {
  1611. max-height: calc(100vh - 56px);
  1612. overflow-y: scroll;
  1613. overflow-x: auto;
  1614. padding: 4px 0 0;
  1615. }
  1616. }
  1617. .step-3 {
  1618. input[type='number'] {
  1619. -moz-appearance: textfield;
  1620. appearance: textfield;
  1621. &:hover {
  1622. -moz-appearance: textfield;
  1623. appearance: textfield;
  1624. &::-webkit-inner-spin-button,
  1625. &::-webkit-outer-spin-button {
  1626. -webkit-appearance: none;
  1627. margin: 0;
  1628. }
  1629. }
  1630. &::-webkit-inner-spin-button,
  1631. &::-webkit-outer-spin-button {
  1632. -webkit-appearance: none;
  1633. margin: 0;
  1634. }
  1635. }
  1636. }
  1637. }
  1638. </style>
  1639. <style lang="scss" scoped>
  1640. @use './styles/index.scss';
  1641. </style>