product.vue 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979
  1. <template>
  2. <div style="padding: 20px 2vw">
  3. <el-form ref="ruleForm" :model="aftermarketContract" :rules="formRules" :inline="true">
  4. <el-collapse v-model="activeNames">
  5. <div v-for="(item, index) in aftermarketContract.aftermarketContractProductList" :key="index" style="margin-bottom: 20px">
  6. <div style="border: 1px solid #edf0f5">
  7. <table cellspacing="0" cellpadding="0" border="0" class="productTable">
  8. <thead>
  9. <tr>
  10. <th style="width: 230px">
  11. <div>产品</div>
  12. </th>
  13. <th style="width: 150px">
  14. <div>图稿(设计图)</div>
  15. </th>
  16. <th style="width: 330px">
  17. <div>图稿(生产文件)</div>
  18. </th>
  19. <th>
  20. <div>包材配件/单品</div>
  21. </th>
  22. <th style="width: 80px">
  23. <div style="text-align: center">操作</div>
  24. </th>
  25. </tr>
  26. </thead>
  27. <tr class="tr">
  28. <td>
  29. <div style="display: flex; padding: 8px 20px 0px">
  30. <div>
  31. <div style="line-height: 28px">
  32. <span>品号: {{ item.code }}</span>
  33. </div>
  34. <div style="line-height: 28px">
  35. <span>品名: {{ item.colorName }}</span>
  36. </div>
  37. <div style="line-height: 40px">
  38. <span>数量: </span>
  39. <el-form-item :prop="'aftermarketContractProductList.' + index + '.quantity'" :rules="formRules.quantity">
  40. <el-input-number
  41. size="mini"
  42. v-model="item.quantity"
  43. :controls="false"
  44. :min="1"
  45. :max="10000000"
  46. :precision="0"
  47. @change="
  48. (val) => {
  49. return getPrice(item, index)
  50. }
  51. "
  52. />
  53. </el-form-item>
  54. </div>
  55. <div style="line-height: 28px">
  56. <span>加工费: {{ item.processPrice }}</span>
  57. </div>
  58. <div style="line-height: 28px">
  59. <span>代发费: {{ item.deliverGoodsPrice }}</span>
  60. </div>
  61. <div style="line-height: 28px">
  62. <span>快递包材费: {{ item.expressPrice }}</span>
  63. </div>
  64. <div style="line-height: 28px">
  65. <span>包装人工费: {{ item.expensePrice }}</span>
  66. </div>
  67. <div style="line-height: 28px">
  68. <span>单价: ¥{{ moneyFormat(item.productPrice, 2) }}</span>
  69. </div>
  70. <div style="line-height: 28px">
  71. <span>小计: ¥{{ statisticalAmount(item) }}</span>
  72. </div>
  73. <div style="line-height: 40px">
  74. <el-form-item :prop="'aftermarketContractProductList.' + index + '.identification'" :rules="formRules.identification">
  75. <span style="margin-right: 10px">打印: </span>
  76. <el-radio-group v-model="item.identification">
  77. <el-radio :label="1">单面</el-radio>
  78. <el-radio :label="2">双面</el-radio>
  79. </el-radio-group>
  80. </el-form-item>
  81. </div>
  82. </div>
  83. </div>
  84. </td>
  85. <td>
  86. <div style="padding: 8px 20px 0px">
  87. <el-upload
  88. class="avatar-uploader"
  89. :action="action"
  90. :headers="{ 'Blade-Auth': token }"
  91. :show-file-list="false"
  92. :before-upload="beforeAvatarUpload"
  93. :on-success="
  94. (response, file, fileList) => {
  95. return uploadSuccess(response, file, fileList, index)
  96. }
  97. "
  98. :on-error="
  99. (err, file, fileList) => {
  100. return uploadError(err, file, fileList, index)
  101. }
  102. "
  103. :on-progress="
  104. (event, file, fileList) => {
  105. return uploadProgress(event, file, fileList, index)
  106. }
  107. "
  108. :disabled="item.loading"
  109. accept=".jpg, .png, .jpeg"
  110. >
  111. <div v-loading="item.loading">
  112. <div v-if="item.imgPath">
  113. <img v-if="!item.imgPath.includes('https')" class="productImg" style="width: 80px; height: 80px" :src="pathPrefix + item.imgPath" />
  114. <img v-else :src="item.imgPath" class="productImg" style="width: 80px; height: 80px" />
  115. </div>
  116. <i v-else class="el-icon-plus avatar-uploader-icon"></i>
  117. </div>
  118. </el-upload>
  119. </div>
  120. </td>
  121. <td>
  122. <div style="padding: 8px 20px 0px">
  123. <div>共享文件夹路径(点击下方链接并上传文件):</div>
  124. <span style="color: #027db4; cursor: pointer" @click="handleClickUpload(item.artworkDocument)">上传ez3/dxf</span>
  125. </div>
  126. </td>
  127. <td>
  128. <div style="padding: 8px 20px 0px">
  129. <div style="display: flex; margin-bottom: 10px">
  130. <el-button size="mini" type="primary" @click="handleOpenOftenDialog(index)">选择常用包材</el-button>
  131. <el-button size="mini" type="primary" style="background: #20b2aa; border-color: #20b2aa" @click="openSaveOftenDialog(index)"
  132. >保存常用包材</el-button
  133. >
  134. <el-button size="mini" type="primary" @click="handleOpenBOM(index)">选择包材配件</el-button>
  135. </div>
  136. <el-table
  137. :data="item.partsContractProductList"
  138. size="small"
  139. :row-style="{ height: '35px' }"
  140. :cell-style="{ padding: '0' }"
  141. border
  142. ref="table"
  143. >
  144. <el-table-column label="单价¥" prop="price" align="left" width="80">
  145. <template slot-scope="scope">
  146. {{ moneyFormat(scope.row.price, 2) }}
  147. </template>
  148. </el-table-column>
  149. <el-table-column label="数量" prop="singleQuantity" align="left" width="90">
  150. <template slot-scope="scope">
  151. <el-form-item
  152. label-width="0px"
  153. :prop="'aftermarketContractProductList.' + index + '.partsContractProductList.' + scope.$index + '.singleQuantity'"
  154. :rules="formRules.singleQuantity"
  155. class="input"
  156. >
  157. <el-input-number
  158. style="width: 52%"
  159. size="mini"
  160. v-model="scope.row.singleQuantity"
  161. :controls="false"
  162. :min="1"
  163. :max="10000000"
  164. :precision="0"
  165. @change="
  166. (val) => {
  167. return changeProductQuantity(val, index, scope.$index)
  168. }
  169. "
  170. />
  171. </el-form-item>
  172. </template>
  173. </el-table-column>
  174. <el-table-column label="名称" prop="name" align="left" min-width="150" />
  175. <el-table-column label="总量" prop="quantity" align="center" width="70">
  176. <template slot-scope="scope">
  177. {{ scope.row.quantity }}
  178. </template>
  179. </el-table-column>
  180. <el-table-column label="小计¥" align="center" width="100">
  181. <template slot-scope="scope">
  182. {{ partsAmount(scope.row) }}
  183. </template>
  184. </el-table-column>
  185. <el-table-column label="操作" align="center" width="50" fixed="right">
  186. <template slot-scope="scope">
  187. <span
  188. class="el-icon-remove-outline"
  189. style="font-size: 16px; cursor: pointer; color: #d9001b"
  190. @click="removeBOM(index, scope.$index)"
  191. ></span>
  192. </template>
  193. </el-table-column>
  194. </el-table>
  195. </div>
  196. </td>
  197. <td style="width: 80px; text-align: center; vertical-align: inherit">
  198. <el-button type="text" @click="clickRemove(index)" v-if="aftermarketContract.dataResource !== 1" v-db-click>删除</el-button>
  199. </td>
  200. </tr>
  201. </table>
  202. <el-collapse-item title="包装" :name="index">
  203. <div style="display: flex; padding: 8px 10px 0px">
  204. <div style="flex: 1; padding: 0px 10px">
  205. <div>包装要求:</div>
  206. <Editor v-model="item.packageRemarks" :min-height="100" />
  207. </div>
  208. </div>
  209. </el-collapse-item>
  210. </div>
  211. </div>
  212. </el-collapse>
  213. </el-form>
  214. <!-- 选择包材配件-->
  215. <el-dialog title="选择包材配件" v-if="openBOM" top="50px" :visible.sync="openBOM" width="90%" append-to-body center>
  216. <SelectBOM @selectBOM="handleSelectBOM" showType="parts" ref="SelectBOM"></SelectBOM>
  217. <span slot="footer" class="dialog-footer">
  218. <el-button @click="openBOM = false">关 闭</el-button>
  219. </span>
  220. </el-dialog>
  221. <el-dialog v-if="saveOftenDialog" :visible.sync="saveOftenDialog" title="保存常用包材" width="40%" append-to-body>
  222. <el-form label-width="120px" :model="saveform" ref="saveform" :rules="rules">
  223. <el-form-item label="常用包材名称:" prop="name">
  224. <el-input v-model="saveform.name" placeholder="请输入"> </el-input>
  225. </el-form-item>
  226. </el-form>
  227. <el-row style="text-align: center; margin-top: 30px">
  228. <el-button size="small" @click="saveCancel">取 消</el-button>
  229. <el-button size="small" type="primary" @click="saveOften">保 存</el-button>
  230. </el-row>
  231. </el-dialog>
  232. <el-dialog v-if="openOftenDialog" :visible.sync="openOftenDialog" title="选择常用包材" width="60%" append-to-body>
  233. <el-form :model="composeQuery" ref="composeForm" :inline="true">
  234. <el-form-item label="组合名称" prop="name">
  235. <el-input placeholder="请输入" v-model="composeQuery.name" clearable size="small" @keyup.enter.native="handleQuery" />
  236. </el-form-item>
  237. <el-form-item>
  238. <el-button size="mini" @click="handleQuery" class="searchBtn">搜索</el-button>
  239. <el-button size="mini" @click="resetQuery">重置</el-button>
  240. </el-form-item>
  241. </el-form>
  242. <div class="compose-table">
  243. <el-table :data="oftenTableList" size="small" :row-style="{ height: '35px' }" :cell-style="{ padding: '0' }" header-row-class-name="tableHeader">
  244. <el-table-column type="expand" align="left" width="50">
  245. <template slot-scope="scope">
  246. <div>
  247. <el-table :data="scope.row.bomCombinationDetailsList" size="small" :row-style="{ height: '35px' }" :cell-style="{ padding: '0' }">
  248. <el-table-column label="主图" width="80" align="center">
  249. <template slot-scope="scope">
  250. <div v-if="scope.row.magPath">
  251. <img class="img" :src="pathPrefix + scope.row.magPath" @click="openFile(pathPrefix + scope.row.magPath)" />
  252. </div>
  253. </template>
  254. </el-table-column>
  255. <el-table-column label="品号" prop="bomColorspecCode" />
  256. <el-table-column label="品名" prop="bomColorName" />
  257. <el-table-column label="数量" prop="singleQuantity" width="80" />
  258. <el-table-column label="单价¥" prop="price" width="80" align="right" />
  259. </el-table>
  260. </div>
  261. </template>
  262. </el-table-column>
  263. <el-table-column label="组合名称" prop="name" align="left" />
  264. <el-table-column label="操作" align="center" width="120" fixed="right">
  265. <template slot-scope="scope">
  266. <div class="miniBtn">
  267. <el-button size="mini" type="text" @click="removeCompose(scope.row.id)"> 删 除</el-button>
  268. <el-button size="mini" type="text" @click="selectCompose(scope.row)">选择</el-button>
  269. </div>
  270. </template>
  271. </el-table-column>
  272. </el-table>
  273. </div>
  274. <el-row style="text-align: center; margin-top: 30px">
  275. <el-button size="small" @click="openOftenDialog = false">取 消</el-button>
  276. </el-row>
  277. </el-dialog>
  278. </div>
  279. </template>
  280. <script>
  281. import { mapGetters } from 'vuex'
  282. import Editor from '@/components/Editor'
  283. import SelectBOM from '@/components/shengde/SelectBOM/index'
  284. import { createFile, deleteFile, judgeFile } from '@/api/shengde/subsidiary/AddOrModifiedOrder/index.js'
  285. import { addFolder } from '@/api/product/customProductLibrary'
  286. import * as API from '@/api/shengde/subsidiary/compose/index.js'
  287. import { getPrice } from '@/api/product/customProductLibrary'
  288. export default {
  289. name: 'OrderProduct',
  290. props: {
  291. aftermarketContract: Object,
  292. },
  293. components: { Editor, SelectBOM },
  294. data() {
  295. let checkQuantity = (rule, value, callback) => {
  296. if (!value) {
  297. return callback(new Error('请输入数量'))
  298. }
  299. callback()
  300. }
  301. let checkPrint = (rule, value, callback) => {
  302. if (value) {
  303. if (value == 0) {
  304. callback(new Error('请选择打印面数'))
  305. } else {
  306. callback()
  307. }
  308. } else {
  309. callback(new Error('请选择打印面数'))
  310. }
  311. }
  312. return {
  313. formRules: {
  314. singleQuantity: [{ validator: checkQuantity, trigger: 'blur' }],
  315. quantity: [{ validator: checkQuantity, trigger: 'blur' }],
  316. identification: [{ validator: checkPrint, trigger: 'change' }],
  317. },
  318. action: process.env.VUE_APP_ACTION_URL,
  319. pathPrefix: process.env.VUE_APP_IMG_URL,
  320. filePrefix: process.env.VUE_APP_FILE_PREFIX,
  321. openProduct: false,
  322. openBOM: false,
  323. selectIndex: null,
  324. activeNames: [],
  325. saveOftenDialog: false,
  326. saveform: {
  327. name: '',
  328. bomCombinationDetailsList: [],
  329. },
  330. rules: {
  331. name: [{ required: true, message: '请输入常用包材配组合名称', trigger: 'blur' }],
  332. },
  333. saveOftenList: [], //点击保存常用包材保存的数据
  334. openOftenDialog: false,
  335. oftenTableList: [],
  336. composeQuery: {
  337. name: '',
  338. pageSize: 999,
  339. pageNum: 1,
  340. },
  341. separateBillForm: {
  342. id: '',
  343. splitOrderQuantity: undefined,
  344. },
  345. separateBillItem: {},
  346. separateBillFormRules: {
  347. splitOrderQuantity: [{ required: true, message: '请输入拆解数量', trigger: 'blur' }],
  348. },
  349. loadingStatus: false,
  350. }
  351. },
  352. created() {},
  353. mounted() {},
  354. computed: mapGetters(['token']),
  355. methods: {
  356. handleCollapse() {
  357. let arr = []
  358. for (let index = 0; index < this.aftermarketContract.aftermarketContractProductList.length; index++) {
  359. arr.push(index)
  360. }
  361. this.activeNames = arr
  362. },
  363. validateForm() {
  364. let flag = null
  365. this.$refs.ruleForm.validate((valid) => {
  366. if (valid) {
  367. for (let i = 0; i < this.aftermarketContract.aftermarketContractProductList.length; i++) {
  368. const element = this.aftermarketContract.aftermarketContractProductList[i]
  369. element.artworkDocument = element.artworkDocument.replace(/\\/g, '/')
  370. if (!element.imgPath) return this.msgInfo('请上传设计图!')
  371. }
  372. flag = true
  373. } else {
  374. flag = false
  375. }
  376. })
  377. return flag
  378. },
  379. fileUpload(file) {
  380. const isLt50M = file.size / 1024 / 1024 < 50
  381. if (!isLt50M) {
  382. this.$message.error('上传设计素材文件单个不能超过50MB!')
  383. }
  384. return isLt50M
  385. },
  386. clickRemove(index) {
  387. this.$confirm('确认删除该产品?', {
  388. confirmButtonText: '确定',
  389. cancelButtonText: '取消',
  390. type: 'warning',
  391. })
  392. .then(() => {
  393. const row = this.aftermarketContract.aftermarketContractProductList[index]
  394. let path = row.artworkDocument.slice(this.filePrefix.length).replace(/\\/g, '/')
  395. deleteFile(path).then(() => {
  396. this.aftermarketContract.aftermarketContractProductList.splice(index, 1)
  397. this.calculationMoney()
  398. this.msgSuccess('删除成功!')
  399. })
  400. })
  401. .catch()
  402. },
  403. handleSelect(data, index) {
  404. const row = data.colors[index]
  405. if (row.mountingsList && row.mountingsList.length > 0) {
  406. row.mountingsList = row.mountingsList.map((item) => {
  407. if (item.quantity) {
  408. item.singleQuantity = Number(item.quantity)
  409. }
  410. delete item.id
  411. delete item.quantity
  412. return {
  413. ...item,
  414. name: item.bomColorName,
  415. price: item.bomColorPrice,
  416. }
  417. })
  418. }
  419. let path = ''
  420. if (row.artworkDocument !== '') {
  421. judgeFile({ artworkDocument: row.artworkDocument }).then((res) => {
  422. if (res.data.data) {
  423. path = row.artworkDocument.replace(/\//g, '\\')
  424. const product = {
  425. productId: data.id,
  426. productPrice: data.price,
  427. quantity: undefined,
  428. expensePrice: data.expensePrice,
  429. deliverGoodsPrice: data.deliverGoodsPrice,
  430. expressPrice: data.expressPrice,
  431. nameChinese: data.nameChinese,
  432. nameEnglish: data.nameEnglish,
  433. customsCode: data.customsCode,
  434. declarationElements: data.declarationElements,
  435. declarationElementsChinese: data.declarationElementsChinese,
  436. price: data.subsidiaryPrice || data.price,
  437. expressCostPrice: '',
  438. deliverGoodsCostPrice: '',
  439. expenseCostPrice: '',
  440. designRemarks: '',
  441. packageRemarks: '',
  442. pic: data.pic,
  443. code: row.specCode,
  444. partsContractProductList: row.mountingsList,
  445. productColorId: row.id,
  446. colorName: row.nameChinese,
  447. artworkDocument: path,
  448. imgPath: row.designSketch,
  449. imgPathName: row.designSketchName,
  450. loading: false,
  451. identification: 1,
  452. }
  453. this.aftermarketContract.aftermarketContractProductList.push(product)
  454. this.msgSuccess('添加完成')
  455. } else {
  456. createFile().then((resFile) => {
  457. path = resFile.data.data.replace(/\//g, '\\')
  458. const product = {
  459. productId: data.id,
  460. productPrice: data.price,
  461. quantity: undefined,
  462. expensePrice: data.expensePrice,
  463. deliverGoodsPrice: data.deliverGoodsPrice,
  464. expressPrice: data.expressPrice,
  465. nameChinese: data.nameChinese,
  466. nameEnglish: data.nameEnglish,
  467. customsCode: data.customsCode,
  468. declarationElements: data.declarationElements,
  469. declarationElementsChinese: data.declarationElementsChinese,
  470. price: data.subsidiaryPrice || data.price,
  471. expressCostPrice: '',
  472. deliverGoodsCostPrice: '',
  473. expenseCostPrice: '',
  474. designRemarks: '',
  475. packageRemarks: '',
  476. pic: data.pic,
  477. code: row.specCode,
  478. partsContractProductList: row.mountingsList,
  479. productColorId: row.id,
  480. colorName: row.nameChinese,
  481. artworkDocument: path,
  482. imgPath: row.designSketch,
  483. imgPathName: row.designSketchName,
  484. loading: false,
  485. identification: 1,
  486. }
  487. this.aftermarketContract.aftermarketContractProductList.push(product)
  488. this.msgSuccess('添加完成')
  489. })
  490. }
  491. })
  492. } else {
  493. createFile().then((resFile) => {
  494. path = resFile.data.data.replace(/\//g, '\\')
  495. const product = {
  496. productId: data.id,
  497. productPrice: data.price,
  498. quantity: undefined,
  499. expensePrice: data.expensePrice,
  500. deliverGoodsPrice: data.deliverGoodsPrice,
  501. expressPrice: data.expressPrice,
  502. nameChinese: data.nameChinese,
  503. nameEnglish: data.nameEnglish,
  504. customsCode: data.customsCode,
  505. declarationElements: data.declarationElements,
  506. declarationElementsChinese: data.declarationElementsChinese,
  507. price: data.subsidiaryPrice || data.price,
  508. expressCostPrice: '',
  509. deliverGoodsCostPrice: '',
  510. expenseCostPrice: '',
  511. designRemarks: '',
  512. packageRemarks: '',
  513. pic: data.pic,
  514. code: row.specCode,
  515. partsContractProductList: row.mountingsList,
  516. productColorId: row.id,
  517. colorName: row.nameChinese,
  518. artworkDocument: path,
  519. imgPath: row.designSketch,
  520. imgPathName: row.designSketchName,
  521. loading: false,
  522. identification: 1,
  523. }
  524. this.aftermarketContract.aftermarketContractProductList.push(product)
  525. this.msgSuccess('添加完成')
  526. })
  527. }
  528. },
  529. handleOpenBOM(index) {
  530. if (
  531. this.aftermarketContract.aftermarketContractProductList[index].quantity &&
  532. this.aftermarketContract.aftermarketContractProductList[index].quantity > 0
  533. ) {
  534. this.selectIndex = index
  535. this.openBOM = true
  536. } else return this.msgInfo('请先输入产品数量')
  537. },
  538. handleSelectBOM(data, index) {
  539. const row = data.bomColors[index]
  540. const item = {
  541. name: row.nameChinese,
  542. bomId: data.id,
  543. bomColorId: row.id,
  544. price: row.price,
  545. quantity: '',
  546. singleQuantity: undefined,
  547. costPrice: row.costPrice,
  548. }
  549. this.aftermarketContract.aftermarketContractProductList[this.selectIndex].partsContractProductList.push(item)
  550. this.msgSuccess('添加完成')
  551. },
  552. removeBOM(index, j) {
  553. this.$confirm('确认删除该行包材配件?', {
  554. confirmButtonText: '确定',
  555. cancelButtonText: '取消',
  556. type: 'warning',
  557. }).then(() => {
  558. this.aftermarketContract.aftermarketContractProductList[index].partsContractProductList.splice(j, 1)
  559. this.changeProductQuantity(this.aftermarketContract.aftermarketContractProductList[index].quantity, index)
  560. this.msgSuccess('删除成功!')
  561. })
  562. },
  563. openImg(path) {
  564. window.open(path)
  565. },
  566. statisticalAmount(item) {
  567. if (item.quantity && item.quantity > 0) {
  568. let money = item.productPrice || 0
  569. if (item.expensePrice) {
  570. money = parseFloat(Number(money) + Number(item.expensePrice)).toFixed(2)
  571. }
  572. if (item.deliverGoodsPrice) {
  573. money = parseFloat(Number(money) + Number(item.deliverGoodsPrice)).toFixed(2)
  574. }
  575. if (item.expressPrice) {
  576. money = parseFloat(Number(money) + Number(item.expressPrice)).toFixed(2)
  577. }
  578. money = parseFloat(Number(money) * Number(item.quantity)).toFixed(2)
  579. return this.moneyFormat(money, 2)
  580. }
  581. return ''
  582. },
  583. partsAmount(item) {
  584. if (item.quantity && item.quantity > 0) {
  585. return this.moneyFormat(parseFloat(Number(item.price) * Number(item.quantity)).toFixed(2), 2)
  586. }
  587. return ''
  588. },
  589. changeProductQuantity(val, productIndex, partsIndex) {
  590. if (partsIndex || partsIndex === 0) {
  591. // 包材配件的合计
  592. if (
  593. this.aftermarketContract.aftermarketContractProductList[productIndex].partsContractProductList[partsIndex].singleQuantity > 0 &&
  594. this.aftermarketContract.aftermarketContractProductList[productIndex].quantity
  595. ) {
  596. this.aftermarketContract.aftermarketContractProductList[productIndex].partsContractProductList[partsIndex].quantity =
  597. Number(this.aftermarketContract.aftermarketContractProductList[productIndex].partsContractProductList[partsIndex].singleQuantity) *
  598. Number(this.aftermarketContract.aftermarketContractProductList[productIndex].quantity)
  599. } else {
  600. this.aftermarketContract.aftermarketContractProductList[productIndex].partsContractProductList[partsIndex].quantity = 0
  601. }
  602. } else {
  603. if (
  604. this.aftermarketContract.aftermarketContractProductList[productIndex].partsContractProductList &&
  605. this.aftermarketContract.aftermarketContractProductList[productIndex].partsContractProductList.length > 0
  606. ) {
  607. this.aftermarketContract.aftermarketContractProductList[productIndex].partsContractProductList =
  608. this.aftermarketContract.aftermarketContractProductList[productIndex].partsContractProductList.map((item) => {
  609. if (val && item) {
  610. item.quantity = Number(val) * Number(item.singleQuantity)
  611. } else {
  612. item.quantity = 0
  613. }
  614. return {
  615. ...item,
  616. }
  617. })
  618. }
  619. }
  620. this.$nextTick(() => {
  621. this.calculationMoney()
  622. })
  623. },
  624. calculationMoney() {
  625. if (this.aftermarketContract.aftermarketContractProductList && this.aftermarketContract.aftermarketContractProductList.length > 0) {
  626. let productPriceAmount = 0
  627. let deliverGoodsPriceAmount = 0
  628. let expressPriceAmount = 0
  629. let expensePriceAmount = 0
  630. let packingMaterialAmount = 0
  631. this.aftermarketContract.aftermarketContractProductList.map((item) => {
  632. if (item.quantity) {
  633. if (item.productPrice) {
  634. productPriceAmount = parseFloat(Number(productPriceAmount) + Number(item.productPrice) * Number(item.quantity)).toFixed(2)
  635. }
  636. if (item.deliverGoodsPrice) {
  637. deliverGoodsPriceAmount = parseFloat(Number(deliverGoodsPriceAmount) + Number(item.deliverGoodsPrice) * Number(item.quantity)).toFixed(2)
  638. }
  639. if (item.expressPrice) {
  640. expressPriceAmount = parseFloat(Number(expressPriceAmount) + Number(item.expressPrice) * Number(item.quantity)).toFixed(2)
  641. }
  642. if (item.expensePrice) {
  643. expensePriceAmount = parseFloat(Number(expensePriceAmount) + Number(item.expensePrice) * Number(item.quantity)).toFixed(2)
  644. }
  645. if (item.partsContractProductList && item.partsContractProductList.length > 0) {
  646. item.partsContractProductList.map((itemBOM) => {
  647. if (itemBOM.quantity && itemBOM.price) {
  648. packingMaterialAmount = parseFloat(Number(packingMaterialAmount) + Number(itemBOM.price) * Number(itemBOM.quantity)).toFixed(2)
  649. }
  650. })
  651. }
  652. }
  653. })
  654. this.aftermarketContract.productPriceAmount = productPriceAmount
  655. this.aftermarketContract.deliverGoodsPriceAmount = deliverGoodsPriceAmount
  656. this.aftermarketContract.expressPriceAmount = expressPriceAmount
  657. this.aftermarketContract.expensePriceAmount = expensePriceAmount
  658. this.aftermarketContract.packingMaterialAmount = packingMaterialAmount
  659. this.aftermarketContract.contractAmount = parseFloat(
  660. Number(productPriceAmount) + Number(deliverGoodsPriceAmount) + Number(expressPriceAmount) + Number(expensePriceAmount) + Number(packingMaterialAmount)
  661. ).toFixed(2)
  662. this.aftermarketContract.amount = this.aftermarketContract.contractAmount
  663. }
  664. },
  665. beforeAvatarUpload(file) {
  666. let fileType = this.getFileType(file.name)
  667. if (['jpg', 'png', 'jpeg'].includes(fileType)) {
  668. const isLt5M = file.size / 1024 / 1024 < 5
  669. if (!isLt5M) {
  670. this.$message.error('上传图片大小不能超过 5MB!')
  671. }
  672. return isLt5M
  673. } else {
  674. this.msgInfo('请上传jpg,png,jpeg格式的文件')
  675. return false
  676. }
  677. },
  678. uploadSuccess(response, file, fileList, index) {
  679. this.aftermarketContract.aftermarketContractProductList[index].loading = false
  680. this.aftermarketContract.aftermarketContractProductList[index].imgPath = response.data.details.path
  681. this.aftermarketContract.aftermarketContractProductList[index].imgPathName = response.data.details.realName
  682. },
  683. uploadError(response, file, fileList, index) {
  684. this.aftermarketContract.aftermarketContractProductList[index].loading = false
  685. },
  686. uploadProgress(event, file, fileList, index) {
  687. this.aftermarketContract.aftermarketContractProductList[index].loading = true
  688. },
  689. saveCancel() {
  690. this.saveOftenDialog = false
  691. this.saveform = {
  692. name: '',
  693. bomCombinationDetailsList: [],
  694. }
  695. },
  696. openSaveOftenDialog(index) {
  697. if (!this.aftermarketContract.aftermarketContractProductList[index].partsContractProductList.length > 0) return this.msgInfo('请先选择包材配件!')
  698. this.saveOftenList = this.aftermarketContract.aftermarketContractProductList[index].partsContractProductList //记录此次要保存的组合包材数据
  699. this.saveOftenDialog = true
  700. },
  701. saveOften() {
  702. this.$refs['saveform'].validate((valid) => {
  703. if (valid) {
  704. let arr = this.saveOftenList.map((item) => ({ bomColorId: item.bomColorId, singleQuantity: item.singleQuantity, price: item.price }))
  705. this.saveform.bomCombinationDetailsList = arr
  706. API.save(this.saveform).then(
  707. () => {
  708. this.msgSuccess('保存成功!')
  709. this.saveCancel()
  710. },
  711. () => {}
  712. )
  713. } else {
  714. return false
  715. }
  716. })
  717. },
  718. handleOpenOftenDialog(index) {
  719. if (
  720. this.aftermarketContract.aftermarketContractProductList[index].quantity &&
  721. this.aftermarketContract.aftermarketContractProductList[index].quantity > 0
  722. ) {
  723. this.getComposeList()
  724. this.selectIndex = index
  725. this.openOftenDialog = true
  726. } else return this.msgInfo('请先输入产品数量')
  727. },
  728. getComposeList() {
  729. API.list({ name: '', pageSize: 9999, pageNum: 1 }).then(
  730. (res) => {
  731. this.oftenTableList = res.data.data
  732. },
  733. (err) => {
  734. console.log(err)
  735. }
  736. )
  737. },
  738. removeCompose(id) {
  739. this.$confirm('是否确认删除此条包材组合?', '警告', {
  740. confirmButtonText: '确定',
  741. cancelButtonText: '取消',
  742. type: 'warning',
  743. })
  744. .then(() => {
  745. API.del({ id }).then(
  746. () => {
  747. this.msgSuccess('删除成功!')
  748. this.getComposeList()
  749. },
  750. (err) => {
  751. console.log(err, 'ss')
  752. }
  753. )
  754. })
  755. .catch((err) => {
  756. console.log(err)
  757. })
  758. },
  759. handleQuery() {
  760. this.getComposeList()
  761. },
  762. resetQuery() {
  763. this.resetForm('composeForm')
  764. this.handleQuery()
  765. },
  766. selectCompose(row) {
  767. const list = row.bomCombinationDetailsList
  768. let sum = this.aftermarketContract.aftermarketContractProductList[this.selectIndex].quantity
  769. let arr = list.map((item) => {
  770. return {
  771. name: item.bomColorName,
  772. bomColorId: item.bomColorId,
  773. price: item.price,
  774. quantity: Number(item.singleQuantity) * sum,
  775. singleQuantity: Number(item.singleQuantity),
  776. bomId: item.bomId,
  777. costPrice: item.costPrice,
  778. }
  779. })
  780. this.aftermarketContract.aftermarketContractProductList[this.selectIndex].partsContractProductList = arr
  781. //选择完成后重新计算价格
  782. this.$nextTick(() => {
  783. this.changeProductQuantity(this.aftermarketContract.aftermarketContractProductList[this.selectIndex].quantity, this.selectIndex)
  784. this.msgSuccess('选择完成')
  785. })
  786. },
  787. handleClickUpload(item) {
  788. let path = JSON.parse(JSON.stringify(item)).replace(/\\/g, '/')
  789. addFolder(path).then(
  790. () => {
  791. let a = document.createElement('a')
  792. a.href = 'printer://' + (this.filePrefix + path).replace(/\//g, '/') + '/'
  793. a.style.display = 'none'
  794. document.body.appendChild(a)
  795. a.click()
  796. document.body.removeChild(a)
  797. },
  798. (err) => {
  799. console.log('addFolder:' + err)
  800. }
  801. )
  802. },
  803. submitSeparateBill() {
  804. this.$refs.separateBill.validate((valid) => {
  805. if (valid) {
  806. if (this.separateBillForm.splitOrderQuantity >= Number(this.separateBillItem.quantity)) {
  807. return this.msgInfo('拆解数量不能大于等于产品数量!')
  808. }
  809. this.loadingStatus = true
  810. API.splitOrder(this.separateBillForm).then(
  811. () => {
  812. this.$emit('separateBillSubmit', true)
  813. this.msgSuccess('拆解完成')
  814. this.openSeparateBill = false
  815. this.loadingStatus = false
  816. },
  817. (err) => {
  818. console.log('splitOrder: ' + err)
  819. this.loadingStatus = false
  820. }
  821. )
  822. }
  823. })
  824. },
  825. clickCancelSeparateBill(item) {
  826. let data = {
  827. id: item.id,
  828. artworkDocument: JSON.parse(JSON.stringify(item.artworkDocument)).replace(/\\/g, '/'),
  829. parentId: item.parentId,
  830. quantity: item.quantity,
  831. }
  832. API.cancelSplitOrder(data).then(() => {
  833. this.$emit('separateBillSubmit', true)
  834. this.msgSuccess('拆解完成')
  835. })
  836. },
  837. getPrice(item, index) {
  838. if (item.quantity) {
  839. getPrice({ id: item.productColorId, quantity: item.quantity }).then((res) => {
  840. item.productPrice = res.data.data.bomPrice
  841. item.expensePrice = res.data.data.expensePrice
  842. item.deliverGoodsPrice = res.data.data.deliverGoodsPrice
  843. item.expressPrice = res.data.data.expressPrice
  844. item.expressCostPrice = res.data.data.expressCostPrice
  845. item.deliverGoodsCostPrice = res.data.data.deliverGoodsCostPrice
  846. item.expenseCostPrice = res.data.data.expenseCostPrice
  847. this.$nextTick(() => {
  848. this.changeProductQuantity(item.quantity, index)
  849. })
  850. })
  851. }
  852. },
  853. },
  854. }
  855. </script>
  856. <style lang="scss" scoped>
  857. .searchBtn {
  858. background: #20b2aa;
  859. color: #fff;
  860. border: 1px solid #20b2aa;
  861. }
  862. .img {
  863. width: 32px;
  864. height: 32px;
  865. cursor: pointer;
  866. object-fit: contain;
  867. vertical-align: middle;
  868. }
  869. ::v-deep {
  870. .el-input__inner {
  871. border-radius: 1px;
  872. }
  873. .el-button--mini {
  874. border-radius: 1px;
  875. }
  876. .miniBtn .el-button {
  877. padding: 0px !important;
  878. }
  879. .tableHeader th {
  880. background-color: #edf0f5;
  881. height: 35px;
  882. padding: 0;
  883. }
  884. .el-form-item__content {
  885. min-width: 60px !important;
  886. }
  887. .el-collapse-item__header {
  888. justify-content: center;
  889. }
  890. .el-collapse-item__arrow {
  891. margin: 0;
  892. }
  893. .el-input-number {
  894. .el-input__inner {
  895. text-align: left !important;
  896. }
  897. }
  898. .el-input__inner {
  899. height: 28px;
  900. line-height: 28px;
  901. }
  902. .input .el-input__inner {
  903. width: 70px;
  904. }
  905. .smallInput .el-input-number--mini,
  906. .smallInput .el-input__inner {
  907. width: 100px;
  908. }
  909. .el-radio__label {
  910. font-size: 12px;
  911. }
  912. }
  913. .factoryLoad {
  914. background-color: #f59a234d;
  915. padding: 0 8px;
  916. border-radius: 50px;
  917. border: 1px solid #f59a234d;
  918. }
  919. .insufficientInventory {
  920. background-color: #931e2c4d;
  921. padding: 0 8px;
  922. border-radius: 50px;
  923. border: 1px solid #931e2c4d;
  924. }
  925. .productTable {
  926. width: 100%;
  927. table-layout: fixed;
  928. th {
  929. background-color: #edf0f5;
  930. height: 35px;
  931. padding: 0;
  932. div {
  933. text-align: left;
  934. padding: 0 20px;
  935. }
  936. }
  937. .tr {
  938. td {
  939. vertical-align: top;
  940. }
  941. }
  942. }
  943. .avatar-uploader-icon {
  944. font-size: 28px;
  945. color: #8c939d;
  946. border: 1px dashed #ccc;
  947. // background-color: #f9f9f9;
  948. width: 80px;
  949. height: 80px;
  950. line-height: 80px;
  951. text-align: center;
  952. &:hover {
  953. border-color: #409eff;
  954. }
  955. }
  956. .productImg {
  957. width: 60px;
  958. height: 60px;
  959. cursor: pointer;
  960. object-fit: contain;
  961. vertical-align: middle;
  962. border: none;
  963. }
  964. .table {
  965. width: 100%;
  966. }
  967. .compose-table {
  968. td {
  969. padding: 3px;
  970. vertical-align: middle;
  971. }
  972. }
  973. </style>