index.vue 54 KB

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