inventoryTable.vue 101 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237
  1. <template>
  2. <el-form ref="form" :model="form" :rules="rules">
  3. <!-- 自己渲染 loading 遮罩:ele-pro-table 内部的 loading 会在每次 reload 时被重置 -->
  4. <div class="inventory-table-wrap">
  5. <ele-pro-table
  6. ref="table"
  7. :needPage="false"
  8. row-key="key"
  9. :columns="columns"
  10. :datasource="form.datasource"
  11. class="time-form"
  12. :class="{ 'time-form-busy': tableBusy }"
  13. height="300"
  14. full-height="calc(100vh - 76px)"
  15. @columns-change="handleColumnChange"
  16. @header-dragend="handleHeaderDragend"
  17. :cache-key="cacheKeyUrl"
  18. :selection.sync="selection"
  19. >
  20. <!-- :show-summary="showSummary"
  21. :summary-method="getSummaries" -->
  22. <!-- 表头工具栏 -->
  23. <template v-slot:toolbar>
  24. <div class="headbox">
  25. <div>
  26. <el-button
  27. size="small"
  28. type="primary"
  29. icon="el-icon-plus"
  30. class="ele-btn-icon"
  31. :disabled="tableBusy"
  32. @click="handParent('', -1)"
  33. >
  34. 新增
  35. </el-button>
  36. <el-button
  37. size="small"
  38. type="primary"
  39. icon="el-icon-plus"
  40. class="ele-btn-icon"
  41. v-if="isGoods"
  42. :disabled="tableBusy"
  43. @click="handGoods(-1)"
  44. >
  45. 选择商品
  46. </el-button>
  47. <el-button
  48. size="small"
  49. type="primary"
  50. icon="el-icon-plus"
  51. class="ele-btn-icon"
  52. v-if="isTemporary"
  53. :disabled="tableBusy"
  54. @click="handlAdd"
  55. >
  56. 新增临时产品
  57. </el-button>
  58. <el-button
  59. size="small"
  60. type="primary"
  61. class="ele-btn-icon"
  62. @click="save"
  63. v-if="isDrawer"
  64. :disabled="tableBusy"
  65. >
  66. 保存
  67. </el-button>
  68. <setAllValue
  69. :disabled="tableBusy || !selection.length"
  70. title="客户期望交期"
  71. @success="setAllValueChange"
  72. valueKey="customerExpectDeliveryDeadline"
  73. />
  74. <setAllValue
  75. :disabled="tableBusy || !selection.length"
  76. title="生产交付交期"
  77. valueKey="produceDeliveryDeadline"
  78. @success="setAllValueChange"
  79. />
  80. <setAllValue
  81. :disabled="tableBusy || !selection.length"
  82. inputType="number"
  83. title="税率"
  84. valueKey="taxRate"
  85. @success="setAllTaxRateChange"
  86. />
  87. </div>
  88. <div class="pricebox">
  89. <span class="amount" v-if="showSummary"
  90. >数量合计:{{ allQuantity }}</span
  91. >
  92. <span class="amount" v-if="batchLoading"
  93. >正在加载:{{ loadedCount }}/{{ totalCount }}</span
  94. >
  95. <span class="amount">总计:{{ allPrice }}元</span>
  96. <el-form-item
  97. style="width: 300px"
  98. v-if="isDiscountTotalPrice"
  99. label="优惠后总金额:"
  100. prop="discountTotalPrice"
  101. :rules="{
  102. required: true,
  103. message: '请输入优惠后总金额',
  104. trigger: 'change'
  105. }"
  106. >
  107. <el-input
  108. type="number"
  109. :min="0"
  110. :max="allPrice"
  111. :disabled="!allPrice"
  112. v-model="form.discountTotalPrice"
  113. style="width: 180px"
  114. placeholder="请输入"
  115. @input="discountInputByOrder(form.discountTotalPrice)"
  116. >
  117. <template slot="append">元</template>
  118. </el-input>
  119. </el-form-item>
  120. </div>
  121. </div>
  122. </template>
  123. <template v-slot:productName="{ row, $index }">
  124. <el-form-item
  125. :prop="'datasource.' + $index + '.productName'"
  126. :rules="{
  127. required: true,
  128. message: '请输入',
  129. trigger: 'change'
  130. }"
  131. >
  132. <el-input
  133. :disabled="!!row.productId && row.productId != 0"
  134. v-model="row.productName"
  135. placeholder="请输入"
  136. style="width: 60%; margin-right: 10px"
  137. ></el-input>
  138. <el-button
  139. size="small"
  140. type="primary"
  141. @click.native="handParent(row, $index)"
  142. >选择
  143. </el-button>
  144. </el-form-item>
  145. </template>
  146. <template v-slot:sortCode="scope">
  147. <el-form-item prop="sortCode">
  148. <el-input
  149. v-model="scope.row.sortCode"
  150. placeholder="请输入"
  151. type="number"
  152. min="1"
  153. ></el-input>
  154. </el-form-item>
  155. </template>
  156. <template v-slot:entrustedEnterpriseId="scope">
  157. <el-form-item prop="entrustedEnterpriseId">
  158. <el-select
  159. v-model="scope.row.entrustedEnterpriseId"
  160. clearable
  161. filterable
  162. >
  163. <el-option
  164. v-for="i in scope.row.entrustedEnterpriseIdList"
  165. :key="i.id"
  166. :value="i.id"
  167. :label="i.name"
  168. ></el-option>
  169. </el-select>
  170. </el-form-item>
  171. </template>
  172. <template v-slot:headerCustomerMark="{ column }">
  173. <span class="is-required">{{ column.label }}</span>
  174. </template>
  175. <template v-slot:customerMark="{ row, $index }">
  176. <el-form-item
  177. :prop="'datasource.' + $index + '.customerMark'"
  178. :rules="{
  179. required: isCustomerMark ? true : false,
  180. message: '请输入客户代号',
  181. trigger: 'change'
  182. }"
  183. >
  184. <el-input
  185. v-model="row.customerMark"
  186. :disabled="quoteType === 2"
  187. placeholder="请输入"
  188. ></el-input>
  189. </el-form-item>
  190. </template>
  191. <template v-slot:headerProductName="{ column }">
  192. <span class="is-required">{{ column.label }}</span>
  193. </template>
  194. <template v-slot:productCode="scope">
  195. <el-form-item :prop="'datasource.' + scope.$index + '.productCode'">
  196. <el-input
  197. v-model="scope.row.productCode"
  198. :disabled="
  199. quoteType != 2 ||
  200. (!!scope.row.productId && scope.row.productId != 0)
  201. "
  202. ></el-input>
  203. </el-form-item>
  204. </template>
  205. <template v-slot:productCategoryName="scope">
  206. <el-form-item
  207. :prop="'datasource.' + scope.$index + '.productCategoryName'"
  208. >
  209. <el-input v-model="scope.row.productCategoryName" disabled></el-input>
  210. </el-form-item>
  211. </template>
  212. <template v-slot:headerTotalCount="{ column }">
  213. <span :class="{ 'is-required': isTotalCount }">{{ column.label }}</span>
  214. </template>
  215. <template v-slot:headerTaxRate="{ column }">
  216. <span class="is-required">{{ column.label }}</span>
  217. </template>
  218. <template v-slot:totalPrice="scope">
  219. <el-form-item :prop="'datasource.' + scope.$index + '.totalPrice'">
  220. {{ formatPrice(Number(scope.row.totalPrice) || 0) }}元
  221. </el-form-item>
  222. </template>
  223. <template v-slot:productBrand="scope">
  224. <el-form-item :prop="'datasource.' + scope.$index + '.productBrand'">
  225. <el-input
  226. v-model="scope.row.productBrand"
  227. :disabled="!!scope.row.productId && scope.row.productId != 0"
  228. ></el-input>
  229. </el-form-item>
  230. </template>
  231. <template v-slot:modelType="scope">
  232. <el-form-item :prop="'datasource.' + scope.$index + '.modelType'">
  233. <el-input
  234. v-model="scope.row.modelType"
  235. :disabled="!!scope.row.productId && scope.row.productId != 0"
  236. ></el-input>
  237. </el-form-item>
  238. </template>
  239. <template v-slot:specification="scope">
  240. <el-form-item :prop="'datasource.' + scope.$index + '.specification'">
  241. <el-input
  242. v-model="scope.row.specification"
  243. :disabled="!!scope.row.productId && scope.row.productId != 0"
  244. ></el-input>
  245. </el-form-item>
  246. </template>
  247. <template v-slot:guaranteePeriod="scope">
  248. <div class="period">
  249. <div class="borderleftnone">
  250. <el-form-item
  251. :prop="'datasource.' + scope.$index + '.guaranteePeriod'"
  252. :rules="{
  253. pattern: numberReg,
  254. message: '请输入有效期',
  255. trigger: 'blur'
  256. }"
  257. >
  258. <el-input
  259. v-model="scope.row.guaranteePeriod"
  260. @change="
  261. setDeliveryDays(scope.row, scope.$index, 'guaranteePeriod')
  262. "
  263. placeholder="请输入"
  264. ></el-input>
  265. </el-form-item>
  266. </div>
  267. <div class="borderrightnone">
  268. <DictSelection
  269. dictName="质保期单位"
  270. clearable
  271. v-model="scope.row.guaranteePeriodUnitCode"
  272. @change="
  273. setDeliveryDays(scope.row, scope.$index, 'guaranteePeriod')
  274. "
  275. :isOne="scope.$index === 0"
  276. >
  277. </DictSelection>
  278. </div>
  279. </div>
  280. </template>
  281. <!-- <template v-slot:measuringUnit="scope">
  282. <el-form-item :prop="'datasource.' + scope.$index + '.measuringUnit'">
  283. <el-input
  284. v-model="scope.row.measuringUnit"
  285. :disabled="!!scope.row.productCode"
  286. placeholder="请输入"
  287. ></el-input>
  288. </el-form-item>
  289. </template> -->
  290. <template v-slot:remark="scope">
  291. <el-form-item :prop="'datasource.' + scope.$index + '.remark'">
  292. <el-input
  293. v-model="scope.row.remark"
  294. type="textarea"
  295. placeholder="请输入"
  296. ></el-input>
  297. </el-form-item>
  298. </template>
  299. <template v-slot:singlePrice="scope">
  300. <el-form-item
  301. :prop="'datasource.' + scope.$index + '.singlePrice'"
  302. :rules="{
  303. required: quoteType !== 2 && isSinglePrice ? true : false,
  304. message: '请输入单价',
  305. trigger: 'change'
  306. }"
  307. >
  308. <el-input
  309. v-model="scope.row.singlePrice"
  310. placeholder="请输入"
  311. :disabled="quoteType === 2"
  312. @input="handleCountChange(scope.row, scope.$index)"
  313. type="number"
  314. >
  315. <template slot="append">元</template>
  316. </el-input>
  317. </el-form-item>
  318. </template>
  319. <template v-slot:taxRate="scope">
  320. <el-form-item
  321. :prop="'datasource.' + scope.$index + '.taxRate'"
  322. :rules="{
  323. required: isTaxRate == 1 ? true : false,
  324. message: '请输入税率',
  325. trigger: 'change'
  326. }"
  327. >
  328. <el-input
  329. v-model="scope.row.taxRate"
  330. placeholder="请输入"
  331. type="number"
  332. :disabled="quoteType === 2"
  333. @input="getNotaxSinglePrice"
  334. >
  335. <template slot="append">%</template>
  336. </el-input>
  337. </el-form-item>
  338. </template>
  339. <template v-slot:headerSinglePrice="{ column }">
  340. <span :class="isSinglePrice ? 'is-required' : ''">{{
  341. column.label
  342. }}</span>
  343. </template>
  344. <template v-slot:headerCustomerExpectDeliveryDeadline="{ column }">
  345. <span :class="isDate == 1 ? 'is-required' : ''">{{
  346. column.label
  347. }}</span>
  348. </template>
  349. <template v-slot:technicalAnswerName="{ row, $index }">
  350. <el-form-item :prop="'datasource.' + $index + '.technicalAnswerName'">
  351. <el-input
  352. v-model="row.technicalAnswerName"
  353. placeholder="请输入"
  354. @click.native="handHead(row, $index)"
  355. ></el-input>
  356. </el-form-item>
  357. </template>
  358. <template v-slot:technicalParams="scope">
  359. <el-form-item :prop="'datasource.' + scope.$index + '.technicalParams'">
  360. <el-input
  361. type="textarea"
  362. v-model="scope.row.technicalParams"
  363. placeholder="请输入"
  364. ></el-input>
  365. </el-form-item>
  366. </template>
  367. <template v-slot:technicalDrawings="scope">
  368. <el-form-item
  369. :prop="'datasource.' + scope.$index + '.technicalDrawings'"
  370. >
  371. <fileMain
  372. v-model="scope.row.technicalDrawings"
  373. lazy-render
  374. ></fileMain>
  375. </el-form-item>
  376. </template>
  377. <template v-slot:customerReqFiles="scope">
  378. <el-form-item
  379. :prop="'datasource.' + scope.$index + '.customerReqFiles'"
  380. >
  381. <fileMain v-model="scope.row.customerReqFiles" lazy-render></fileMain>
  382. </el-form-item>
  383. </template>
  384. <template v-slot:industryArtFiles="scope">
  385. <el-form-item
  386. :prop="'datasource.' + scope.$index + '.industryArtFiles'"
  387. :rules="{
  388. required: false,
  389. message: '请输入',
  390. trigger: 'change'
  391. }"
  392. >
  393. <fileMain v-model="scope.row.industryArtFiles" lazy-render></fileMain>
  394. </el-form-item>
  395. </template>
  396. <template v-slot:otherFiles="scope">
  397. <el-form-item :prop="'datasource.' + scope.$index + '. otherFiles'">
  398. <fileMain v-model="scope.row.otherFiles" lazy-render></fileMain>
  399. </el-form-item>
  400. </template>
  401. <template v-slot:headerProduceDeliveryDeadline="{ column }">
  402. <span
  403. :class="isProduceDeliveryDeadline && isDate == 1 ? 'is-required' : ''"
  404. >{{ column.label }}</span
  405. >
  406. </template>
  407. <template v-slot:produceDeliveryDeadline="scope">
  408. <el-form-item
  409. :prop="'datasource.' + scope.$index + '.produceDeliveryDeadline'"
  410. :rules="{
  411. required: isProduceDeliveryDeadline && isDate == 1 ? true : false,
  412. message: '选择生产交付交期',
  413. trigger: 'change'
  414. }"
  415. >
  416. <el-date-picker
  417. style="width: 140px"
  418. value-format="yyyy-MM-dd"
  419. v-model="scope.row.produceDeliveryDeadline"
  420. @change="
  421. setDeliveryDays(scope.row, scope.$index, 'guaranteePeriod')
  422. "
  423. type="date"
  424. placeholder="选择日期"
  425. >
  426. </el-date-picker>
  427. </el-form-item>
  428. </template>
  429. <template v-slot:customerExpectDeliveryDeadline="scope">
  430. <el-form-item
  431. v-if="scope.row.arrivalWay == 1 || !scope.row.arrivalWay"
  432. :rules="{
  433. required: isDate == 1 && quoteType != 2 ? true : false,
  434. message:
  435. contractBookType == 1 ? '请选择客户期望交期' : '请选择交付日期',
  436. trigger: 'change'
  437. }"
  438. :prop="
  439. 'datasource.' + scope.$index + '.customerExpectDeliveryDeadline'
  440. "
  441. >
  442. <el-date-picker
  443. style="width: 140px"
  444. v-model="scope.row.customerExpectDeliveryDeadline"
  445. value-format="yyyy-MM-dd"
  446. @change="
  447. setDeliveryDays(scope.row, scope.$index, 'guaranteePeriod')
  448. "
  449. type="date"
  450. placeholder="选择日期"
  451. >
  452. </el-date-picker>
  453. </el-form-item>
  454. <el-form-item v-if="scope.row.arrivalWay == 2">
  455. <el-link
  456. type="primary"
  457. :underline="false"
  458. @click.native="handleMethod(scope.row, scope.$index)"
  459. >
  460. 设置分批时间
  461. </el-link>
  462. </el-form-item>
  463. </template>
  464. <template v-slot:provenance="scope">
  465. <el-form-item :prop="'datasource.' + scope.$index + '.provenance'">
  466. <DictSelection
  467. dictName="产地"
  468. clearable
  469. v-model="scope.row.provenance"
  470. :disabled="quoteType === 2"
  471. collapse-tags
  472. multiple
  473. :isOne="scope.$index === 0"
  474. >
  475. </DictSelection>
  476. </el-form-item>
  477. </template>
  478. <template v-slot:guaranteePeriodDeadline="scope">
  479. <el-form-item
  480. :prop="'datasource.' + scope.$index + '.guaranteePeriodDeadline'"
  481. >
  482. <el-date-picker
  483. style="width: 140px"
  484. v-model="scope.row.guaranteePeriodDeadline"
  485. type="date"
  486. value-format="yyyy-MM-dd"
  487. placeholder="选择日期"
  488. >
  489. </el-date-picker>
  490. </el-form-item>
  491. </template>
  492. <template v-slot:singleWeight="scope">
  493. <el-form-item :prop="'datasource.' + scope.$index + '.singleWeight'">
  494. <el-input
  495. v-model="scope.row.singleWeight"
  496. @input="handleCountChange(scope.row, scope.$index)"
  497. placeholder="请输入"
  498. >
  499. <template slot="append">
  500. <span v-if="!!scope.row.productId && scope.row.productId != 0">
  501. {{ scope.row.weightUnit }}
  502. </span>
  503. <el-input
  504. v-else
  505. v-model="scope.row.weightUnit"
  506. placeholder="单位"
  507. style="width: 80px; padding: 0"
  508. ></el-input>
  509. </template>
  510. </el-input>
  511. </el-form-item>
  512. </template>
  513. <template v-slot:totalWeight="scope">
  514. <el-form-item
  515. style="margin-bottom: 20px"
  516. :rules="{
  517. required: false,
  518. pattern: numberReg,
  519. trigger: 'change',
  520. message: '请输入正确的重量'
  521. }"
  522. :prop="'datasource.' + scope.$index + '.totalWeight'"
  523. >
  524. <el-input
  525. v-model="scope.row.totalWeight"
  526. @input="handleCountChange(scope.row, scope.$index, 'totalWeight')"
  527. placeholder="请输入"
  528. >
  529. <template slot="append">
  530. {{ scope.row.weightUnit }}
  531. </template>
  532. </el-input>
  533. </el-form-item>
  534. </template>
  535. <template v-slot:increaseTotalWeight="scope">
  536. <el-form-item
  537. :prop="'datasource.' + scope.$index + '.increaseTotalWeight'"
  538. >
  539. <el-input
  540. v-model="scope.row.increaseTotalWeight"
  541. @input="setIncreaseTotalWeight(scope.row, scope.$index)"
  542. placeholder="请输入"
  543. >
  544. <template slot="append">
  545. <span v-if="!!scope.row.productId && scope.row.productId != 0">
  546. {{ scope.row.weightUnit }}
  547. </span>
  548. <el-input
  549. v-else
  550. v-model="scope.row.weightUnit"
  551. placeholder="单位"
  552. style="width: 80px; padding: 0"
  553. ></el-input>
  554. </template>
  555. </el-input>
  556. </el-form-item>
  557. </template>
  558. <template v-slot:technologyRouteName="scope">
  559. <el-form-item
  560. :prop="'datasource.' + scope.$index + '.technologyRouteName'"
  561. :rules="{
  562. required: false,
  563. message: '请选择',
  564. trigger: 'change'
  565. }"
  566. >
  567. <el-input
  568. v-model="scope.row.technologyRouteName"
  569. placeholder="请选择"
  570. @click.native="openVersion(scope.$index)"
  571. ></el-input>
  572. </el-form-item>
  573. </template>
  574. <template v-slot:pricingWay="scope">
  575. <el-form-item :prop="'datasource.' + scope.$index + '.pricingWay'">
  576. <el-select
  577. v-model="scope.row.pricingWay"
  578. placeholder="请选择"
  579. :rules="{
  580. required: true,
  581. message: '请选择计价方式',
  582. trigger: 'change'
  583. }"
  584. @change="changeCount(scope.row, scope.$index)"
  585. >
  586. <el-option
  587. v-for="item in pricingWayList"
  588. :key="item.id"
  589. :label="item.name"
  590. :value="item.id"
  591. >
  592. </el-option>
  593. </el-select>
  594. </el-form-item>
  595. </template>
  596. <template v-slot:headerPricingWay="{ column }">
  597. <span class="is-required">{{ column.label }}</span>
  598. </template>
  599. <template v-slot:taskName="scope">
  600. <el-form-item :prop="'datasource.' + scope.$index + '.taskName'">
  601. <el-input
  602. v-model="scope.row.taskName"
  603. placeholder="请选择"
  604. style="width: 60%; margin-right: 10px"
  605. disabled
  606. ></el-input>
  607. <el-button
  608. size="small"
  609. type="primary"
  610. @click.native="handleTaskinstance(scope.row, scope.$index)"
  611. >选择
  612. </el-button>
  613. </el-form-item>
  614. </template>
  615. <template v-slot:arrivalWay="scope">
  616. <el-form-item :prop="'datasource.' + scope.$index + '.arrivalWay'">
  617. <el-select
  618. v-model="scope.row.arrivalWay"
  619. clearable
  620. style="width: 100%"
  621. >
  622. <el-option
  623. v-for="item in arrivalWayList"
  624. :key="item.value"
  625. :label="item.label"
  626. :value="item.value"
  627. >
  628. </el-option>
  629. </el-select>
  630. </el-form-item>
  631. </template>
  632. <template v-slot:batchNo="scope">
  633. <el-form-item
  634. :prop="'datasource.' + scope.$index + '.batchNo'"
  635. :rules="[
  636. {
  637. required: isBatchNo == 1 ? true : false,
  638. message: '请输入批号',
  639. trigger: 'blur'
  640. }
  641. ]"
  642. >
  643. <el-input
  644. v-model="scope.row.batchNo"
  645. placeholder="请输入"
  646. :disabled="quoteType === 2"
  647. >
  648. <!-- 批次号可以为中文 -->
  649. <!-- v-no-chinese -->
  650. </el-input>
  651. </el-form-item>
  652. </template>
  653. <template v-slot:headerBatchNo="{ column }">
  654. <span class="is-required">{{ column.label }}</span>
  655. </template>
  656. <template v-slot:orderNo="{ row, $index }">
  657. <el-form-item :prop="'datasource.' + $index + '.orderNo'">
  658. <el-select v-model="row.orderNo" style="width: 100%">
  659. <el-option
  660. :label="item.orderNo"
  661. :value="item.orderNo"
  662. @click.native="orderNoChange(row, item)"
  663. v-for="(item, index) in orderOption"
  664. :key="index"
  665. ></el-option>
  666. </el-select>
  667. </el-form-item>
  668. </template>
  669. <template v-slot:goodsLevel="{ row, $index }">
  670. <el-form-item :prop="'datasource.' + $index + '.goodsLevel'">
  671. <el-select
  672. v-model="row.goodsLevel"
  673. style="width: 100%"
  674. :disabled="quoteType === 2"
  675. >
  676. <el-option
  677. :label="item.label"
  678. :value="item.value"
  679. v-for="(item, index) in levelList"
  680. :key="index"
  681. ></el-option>
  682. </el-select>
  683. </el-form-item>
  684. </template>
  685. <template v-slot:goodsPriceType="{ row, $index }">
  686. <el-form-item :prop="'datasource.' + $index + '.goodsPriceType'">
  687. <DictSelection
  688. dictName="商品价格类型"
  689. clearable
  690. :disabled="quoteType === 2"
  691. v-model="row.goodsPriceType"
  692. @change="goodsPriceTypeChange(row, $index)"
  693. :isOne="$index === 0"
  694. >
  695. </DictSelection>
  696. </el-form-item>
  697. </template>
  698. <template v-slot:saleCount="scope">
  699. <el-form-item
  700. :prop="'datasource.' + scope.$index + '.' + countObj.countKey"
  701. :rules="{
  702. required: quoteType !== 2 && isTotalCount ? true : false,
  703. message: '请输入数量',
  704. trigger: 'change'
  705. }"
  706. >
  707. <el-input
  708. v-model="scope.row[countObj.countKey]"
  709. style="width: calc(100% - 36px)"
  710. placeholder="请输入"
  711. type="number"
  712. :min="0"
  713. @input="handleCountChange(scope.row, scope.$index)"
  714. >
  715. <template slot="append">
  716. <el-select
  717. v-model="scope.row[countObj.unitIdKey]"
  718. style="width: 100px"
  719. v-if="!!scope.row.productId && scope.row.productId != 0"
  720. @change="handleCountChange(scope.row, scope.$index)"
  721. >
  722. <el-option
  723. :label="item.conversionUnit"
  724. :value="item.id"
  725. v-for="(item, index) in scope.row.packageDispositionList"
  726. :key="index"
  727. ></el-option>
  728. </el-select>
  729. <el-input
  730. v-else
  731. v-model="scope.row.measuringUnit"
  732. placeholder="单位"
  733. style="width: 100px; padding: 0"
  734. ></el-input>
  735. </template>
  736. </el-input>
  737. </el-form-item>
  738. </template>
  739. <template v-slot:quoteWay="scope">
  740. <el-form-item
  741. :prop="'datasource.' + scope.$index + '.quoteWay'"
  742. :rules="{
  743. required: true,
  744. message: '请选择报价方式',
  745. trigger: 'change'
  746. }"
  747. >
  748. <el-select v-model="scope.row.quoteWay" clearable filterable>
  749. <el-option
  750. v-for="i in quoteTypeOp"
  751. :key="i.value"
  752. :value="i.value"
  753. :label="i.label"
  754. ></el-option>
  755. </el-select>
  756. </el-form-item>
  757. </template>
  758. <template v-slot:headerQuoteWay="{ column }">
  759. <span class="is-required">{{ column.label }}</span>
  760. </template>
  761. <template v-slot:discountRatio="scope">
  762. <el-form-item
  763. :prop="'datasource.' + scope.$index + '.discountRatio'"
  764. :rules="{
  765. required: true,
  766. message: '请输入折让比例',
  767. trigger: 'change'
  768. }"
  769. >
  770. <el-input
  771. v-model="scope.row.discountRatio"
  772. placeholder="请输入"
  773. type="number"
  774. :min="0"
  775. :max="100"
  776. :disabled="quoteType === 2"
  777. @input="getDiscountRatioPrice(scope.row)"
  778. >
  779. <template slot="append">%</template>
  780. </el-input>
  781. </el-form-item>
  782. </template>
  783. <template v-slot:headerDiscountRatio="{ column }">
  784. <span :class="isDiscount ? 'is-required' : ''">{{ column.label }}</span>
  785. </template>
  786. <!-- <template v-slot:saleUnit="scope">
  787. <el-form-item
  788. :prop="'datasource.' + scope.$index + '.' + countObj.unitIdKey"
  789. >
  790. <el-select
  791. v-model="scope.row[countObj.unitIdKey]"
  792. style="width: 100%"
  793. @change="changeCount(scope.row, scope.$index)"
  794. >
  795. <el-option
  796. :label="item.conversionUnit"
  797. :value="item.id"
  798. @click.native="packingChange(item, scope.$index)"
  799. v-for="(item, index) in scope.row.packageDispositionList"
  800. :key="index"
  801. ></el-option>
  802. </el-select>
  803. </el-form-item>
  804. </template> -->
  805. <template v-slot:modelKey="scope">
  806. <el-form-item :prop="'datasource.' + scope.$index + '.' + 'modelKey'">
  807. <DictSelection
  808. dictName="物品机型"
  809. clearable
  810. :disabled="quoteType === 2"
  811. v-model="scope.row.modelKey"
  812. :isOne="scope.$index === 0"
  813. filterable
  814. allow-create
  815. default-first-option
  816. >
  817. </DictSelection>
  818. </el-form-item>
  819. </template>
  820. <template v-slot:colorKey="scope">
  821. <el-form-item :prop="'datasource.' + scope.$index + '.' + 'colorKey'">
  822. <DictSelection
  823. dictName="物品颜色"
  824. clearable
  825. v-model="scope.row.colorKey"
  826. :isOne="scope.$index === 0"
  827. filterable
  828. allow-create
  829. default-first-option
  830. >
  831. </DictSelection>
  832. </el-form-item>
  833. </template>
  834. <!-- 生产加工类型特有列的模板 -->
  835. <template v-slot:thickNess="{ row, $index }">
  836. <el-form-item :prop="'datasource.' + $index + '.thickNess'">
  837. <el-input v-model="row.thickNess" placeholder="请输入">
  838. <template slot="append">mm</template></el-input
  839. >
  840. </el-form-item>
  841. </template>
  842. <template v-slot:squareNumber="{ row, $index }">
  843. <el-form-item :prop="'datasource.' + $index + '.squareNumber'">
  844. <el-input v-model="row.squareNumber" placeholder="请输入">
  845. <template slot="append">m²</template></el-input
  846. >
  847. </el-form-item>
  848. </template>
  849. <template v-slot:processingFeeBeforeTax="{ row, $index }">
  850. <el-form-item
  851. :prop="'datasource.' + $index + '.processingFeeBeforeTax'"
  852. >
  853. <el-input
  854. v-model="row.processingFeeBeforeTax"
  855. type="number"
  856. placeholder="请输入"
  857. @input="calculateIncludingTaxPrice(row, $index)"
  858. >
  859. <template slot="append">元</template>
  860. </el-input>
  861. </el-form-item>
  862. </template>
  863. <template v-slot:packagingFeeNotTaxed="{ row, $index }">
  864. <el-form-item :prop="'datasource.' + $index + '.packagingFeeNotTaxed'">
  865. <el-input
  866. v-model="row.packagingFeeNotTaxed"
  867. type="number"
  868. placeholder="请输入"
  869. @input="calculateIncludingTaxPrice(row, $index)"
  870. >
  871. <template slot="append">元</template>
  872. </el-input>
  873. </el-form-item>
  874. </template>
  875. <template v-slot:transportationFeeWithoutTax="{ row, $index }">
  876. <el-form-item
  877. :prop="'datasource.' + $index + '.transportationFeeWithoutTax'"
  878. >
  879. <el-input
  880. v-model="row.transportationFeeWithoutTax"
  881. type="number"
  882. placeholder="请输入"
  883. @input="calculateIncludingTaxPrice(row, $index)"
  884. >
  885. <template slot="append">元</template>
  886. </el-input>
  887. </el-form-item>
  888. </template>
  889. <!-- 新增未税小记列模板 -->
  890. <template v-slot:quotationSubtotalBeforeTax="{ row, $index }">
  891. <el-form-item
  892. :prop="'datasource.' + $index + '.quotationSubtotalBeforeTax'"
  893. >
  894. {{ (Number(row.quotationSubtotalBeforeTax) || 0).toFixed(2) }}元
  895. </el-form-item>
  896. </template>
  897. <template v-slot:extraTax="{ row, $index }">
  898. <el-form-item :prop="'datasource.' + $index + '.extraTax'">
  899. <el-input
  900. v-model="row.extraTax"
  901. type="number"
  902. placeholder="请输入"
  903. @input="calculateIncludingTaxPrice(row, $index)"
  904. >
  905. <template slot="append">%</template>
  906. </el-input>
  907. </el-form-item>
  908. </template>
  909. <!-- 含税单价改为含税小计 -->
  910. <template v-slot:quotationSubtotalTax="{ row, $index }">
  911. <el-form-item :prop="'datasource.' + $index + '.quotationSubtotalTax'">
  912. {{ (Number(row.quotationSubtotalTax) || 0).toFixed(2) }}元
  913. <!-- <el-input
  914. v-model="row.quotationSubtotalTax"
  915. type="number"
  916. placeholder="请输入"
  917. >
  918. <template slot="append">元</template>
  919. </el-input> -->
  920. </el-form-item>
  921. </template>
  922. <template v-slot:productionRequirements="{ row, $index }">
  923. <el-form-item
  924. :prop="'datasource.' + $index + '.productionRequirements'"
  925. >
  926. <el-input
  927. v-model="row.productionRequirements"
  928. placeholder="请输入"
  929. type="textarea"
  930. >
  931. </el-input>
  932. </el-form-item>
  933. </template>
  934. <template v-slot:cuttingLength="{ row, $index }">
  935. <el-form-item :prop="'datasource.' + $index + '.cuttingLength'">
  936. <el-input v-model="row.cuttingLength" placeholder="请输入">
  937. </el-input>
  938. </el-form-item>
  939. </template>
  940. <!-- 操作列 -->
  941. <template v-slot:action="scope">
  942. <el-popconfirm
  943. class="ele-action"
  944. title="确定要删除吗?"
  945. :disabled="tableBusy"
  946. @confirm="remove(scope.$index)"
  947. >
  948. <template v-slot:reference>
  949. <el-link
  950. type="danger"
  951. :underline="false"
  952. :disabled="tableBusy"
  953. icon="el-icon-delete"
  954. >
  955. 删除
  956. </el-link>
  957. </template>
  958. </el-popconfirm>
  959. </template>
  960. </ele-pro-table>
  961. <!-- 遮罩放在表格之后,保证盖在内容上方;v-if 直接绑定 tableLoading,全部追加完成后才卸载 -->
  962. <div v-if="tableLoading" class="table-loading-mask">
  963. <i class="el-icon-loading"></i>
  964. <span class="table-loading-text">
  965. {{
  966. batchLoading
  967. ? `正在加载 ${loadedCount}/${totalCount}`
  968. : '正在加载,请稍候'
  969. }}
  970. </span>
  971. </div>
  972. </div>
  973. <product-list
  974. ref="productListRef"
  975. classType="1"
  976. :is-get-inventory-total="true"
  977. :show-stock-count="showStockCount"
  978. @changeParent="changeParent"
  979. :isSupplier="isSupplier"
  980. :isSalesRecord="isSalesRecord"
  981. ></product-list>
  982. <head-list ref="headRef" @changeParent="changeAnswer"></head-list>
  983. <ProductionVersion
  984. ref="versionRefs"
  985. @changeProduct="changeProduct"
  986. ></ProductionVersion>
  987. <taskinstance-dialog
  988. ref="taskinstanceDialogRef"
  989. v-if="taskinstanceDialogFlag"
  990. @saveTaskInstance="saveTaskInstance"
  991. :visible.sync="taskinstanceDialogFlag"
  992. ></taskinstance-dialog>
  993. <timeDialog
  994. @chooseTime="chooseTime"
  995. ref="timeDialogRef"
  996. :isBatch="true"
  997. ></timeDialog>
  998. <selectStockLedgerDialog
  999. ref="selectStockLedgerDialogRef"
  1000. @changeParent="replaceTable"
  1001. :isSupplier="isSupplier"
  1002. :showSaleCount="showSaleCount"
  1003. :show-stock-count="showStockCount"
  1004. :cacheKeyUrl="cacheKeyUrl + 'saleOrders'"
  1005. ></selectStockLedgerDialog>
  1006. <commodityPriceListDialog
  1007. ref="commodityPriceListDialogRef"
  1008. @changeParent="changeParent"
  1009. ></commodityPriceListDialog>
  1010. </el-form>
  1011. </template>
  1012. <script>
  1013. import { numberReg } from 'ele-admin';
  1014. import productList from '@/BIZComponents/product-list.vue';
  1015. import dictMixins from '@/mixins/dictMixins';
  1016. import fileUpload from '@/components/upload/fileUpload';
  1017. import headList from '@/BIZComponents/user-select/user-select.vue';
  1018. import ProductionVersion from '@/components/ProductionVersion2/index.vue';
  1019. import {
  1020. getInventoryTotalAPI,
  1021. getWarehouseOutStockBatchAPI,
  1022. buildInventoryTotalQueryKey,
  1023. matchInventoryTotal
  1024. } from '@/api/wms';
  1025. import { pricingWayList, lbjtList } from '@/enum/dict.js';
  1026. import {
  1027. changeCount,
  1028. getAllPrice,
  1029. getAllDiscountPrice,
  1030. formatPrice,
  1031. getAllQuantity
  1032. } from '@/BIZComponents/setProduct.js';
  1033. import { contactQueryByCategoryIdsAPI } from '@/api/saleManage/contact';
  1034. import taskinstanceDialog from '@/BIZComponents/procedure/taskinstanceDialog.vue';
  1035. import timeDialog from '@/components/timeDialog/index.vue';
  1036. import { copyObj } from '@/utils/util';
  1037. import selectStockLedgerDialog from '@/BIZComponents/selectStockLedger/selectStockLedgerDialog.vue'; //库存台账
  1038. import setAllValue from '@/BIZComponents/setAllValue.vue'; //批量修改
  1039. import tabMixins from '@/mixins/tableColumnsMixin';
  1040. import commodityPriceListDialog from '@/views/commodityManagement/commodityPriceList/components/commodityPriceListDialog.vue';
  1041. const dayjs = require('dayjs');
  1042. import { levelList, quoteTypeOp } from '@/enum/dict.js';
  1043. import {
  1044. getGoodsPriceByCondition,
  1045. getGoodsByCategoryId
  1046. } from '@/api/goodsManage/index';
  1047. import { parameterGetByCode } from '@/api/main/index.js';
  1048. import { getSummaries } from '@/utils/util.js';
  1049. export default {
  1050. mixins: [dictMixins, tabMixins],
  1051. components: {
  1052. fileUpload,
  1053. productList,
  1054. headList,
  1055. ProductionVersion,
  1056. taskinstanceDialog,
  1057. timeDialog,
  1058. selectStockLedgerDialog,
  1059. commodityPriceListDialog,
  1060. setAllValue
  1061. },
  1062. props: {
  1063. pageName: {
  1064. default: '',
  1065. type: String
  1066. },
  1067. isDiscountTotalPrice: {
  1068. default: false,
  1069. type: Boolean
  1070. },
  1071. quoteType: {
  1072. type: Number,
  1073. default: 1
  1074. },
  1075. isGuaranteePeriod: {
  1076. default: true,
  1077. type: Boolean
  1078. },
  1079. customerMark: {
  1080. default: '',
  1081. type: String
  1082. },
  1083. isContractId: {
  1084. type: Boolean,
  1085. default: false
  1086. },
  1087. isSinglePrice: {
  1088. //单价必填
  1089. type: Boolean,
  1090. default: true
  1091. },
  1092. isTotalCount: {
  1093. //数量必填
  1094. type: Boolean,
  1095. default: true
  1096. },
  1097. isCustomerMark: {
  1098. //客户代号必填
  1099. type: Boolean,
  1100. default: false
  1101. },
  1102. isDiscount: {
  1103. //折让
  1104. type: Boolean,
  1105. default: true
  1106. },
  1107. isProduceDeliveryDeadline: {
  1108. //生产交付交期必填
  1109. type: Boolean,
  1110. default: false
  1111. },
  1112. isDrawer: {
  1113. type: Boolean,
  1114. default: false
  1115. },
  1116. isChangeCount: {
  1117. //默认计算
  1118. type: Boolean,
  1119. default: true
  1120. },
  1121. // 是否合同
  1122. isContractBook: {
  1123. type: Boolean,
  1124. default: false
  1125. },
  1126. contractBookType: {
  1127. //合同类型 1销售 2采购
  1128. type: [String, Number],
  1129. default: 1
  1130. },
  1131. isTemporary: {
  1132. //临时
  1133. type: Boolean,
  1134. default: false
  1135. },
  1136. isSupplier: {
  1137. //供应商
  1138. type: Boolean,
  1139. default: false
  1140. },
  1141. isSalesRecord: {
  1142. default: ''
  1143. },
  1144. isArrivalWay: {
  1145. //分批到货时间
  1146. type: Boolean,
  1147. default: false
  1148. },
  1149. //订单类型
  1150. needProduce: {
  1151. type: [String, Number],
  1152. default: 1
  1153. },
  1154. cacheKeyUrl: '',
  1155. //是否显示订单编码
  1156. isOrderNo: {
  1157. type: Boolean,
  1158. default: false
  1159. },
  1160. isBatchNo: {
  1161. type: Boolean, //批次号是否必填
  1162. default: false
  1163. },
  1164. orderOption: {
  1165. default: () => {
  1166. return [];
  1167. }
  1168. },
  1169. //是否商品
  1170. isGoods: {
  1171. type: Boolean,
  1172. default: false
  1173. },
  1174. // 是否税率必填
  1175. isTaxRate: {
  1176. type: [Number, String],
  1177. default: 0
  1178. },
  1179. defTaxRate: {
  1180. type: Number,
  1181. default: undefined
  1182. },
  1183. //是否显示增重重量
  1184. isIncreaseTotalWeight: {
  1185. type: Boolean,
  1186. default: false
  1187. },
  1188. countObj: {
  1189. type: Object,
  1190. default: () => {
  1191. return {
  1192. countKey: 'saleCount',
  1193. unitKey: 'saleUnit',
  1194. unitIdKey: 'saleUnitId'
  1195. };
  1196. }
  1197. },
  1198. showSummary: {
  1199. type: Boolean,
  1200. default: false
  1201. },
  1202. // 是否显示排序字段
  1203. showSortLine: {
  1204. type: Boolean,
  1205. default: false
  1206. },
  1207. // 是否显示库存式订单可填订单数量
  1208. showSaleCount: {
  1209. type: Boolean,
  1210. default: false
  1211. },
  1212. // 是否同时显示库存数量和可用数量
  1213. showStockCount: {
  1214. type: Boolean,
  1215. default: false
  1216. },
  1217. // 一次追加的行数较多时,按批渲染,避免一次渲染整表长时间阻塞主线程
  1218. appendBatchSize: {
  1219. type: Number,
  1220. default: 20
  1221. }
  1222. },
  1223. data() {
  1224. const defaultForm = {
  1225. key: null,
  1226. endTime: '',
  1227. isFirst: 0,
  1228. name: '',
  1229. startTime: '',
  1230. workHour: '',
  1231. guaranteePeriodUnitCode: '',
  1232. technicalDrawings: [],
  1233. arrivalWay: 1,
  1234. // 生产加工类型特有字段
  1235. thickNess: '',
  1236. squareNumber: '',
  1237. processingFeeBeforeTax: '',
  1238. packagingFeeNotTaxed: '',
  1239. transportationFeeWithoutTax: '',
  1240. extraTax: '',
  1241. quotationSubtotalTax: '',
  1242. // 新增未税小记字段
  1243. quotationSubtotalBeforeTax: '',
  1244. quoteWay: 1,
  1245. discountRatio: 100
  1246. };
  1247. return {
  1248. levelList,
  1249. taskinstanceDialogFlag: false,
  1250. tableLoading: false,
  1251. // 分批追加进度
  1252. batchLoading: false,
  1253. loadedCount: 0,
  1254. totalCount: 0,
  1255. allPrice: 0,
  1256. allQuantity: 0,
  1257. numberReg,
  1258. defaultForm,
  1259. form: {
  1260. datasource: [],
  1261. discountTotalPrice: 0
  1262. },
  1263. isDate: 1,
  1264. pricingWayList,
  1265. curIndex: '',
  1266. rules: {},
  1267. dictList: {},
  1268. arrivalWayList: [
  1269. { label: '一次性到货', value: 1 },
  1270. { label: '分批到货', value: 2 }
  1271. ],
  1272. columnsVersion: 1,
  1273. selection: [],
  1274. quoteTypeOp,
  1275. widthsVersion: 0
  1276. };
  1277. },
  1278. computed: {
  1279. // 批量追加进行中:锁住会改动数据源的操作,避免行下标错位
  1280. tableBusy() {
  1281. return this.batchLoading;
  1282. },
  1283. canHandl() {
  1284. return this.form.datasource.length;
  1285. },
  1286. columns() {
  1287. // 强制重新计算(每次进入时读取缓存宽度)
  1288. this.widthsVersion;
  1289. let columnsVersion = this.columnsVersion;
  1290. const cacheWidths = this.getStorage(this.cacheKeyUrl + 'Widths') || {};
  1291. const applyWidth = (col) => {
  1292. if (col.prop && cacheWidths[col.prop]) {
  1293. return {
  1294. ...col,
  1295. width: cacheWidths[col.prop],
  1296. minWidth: undefined
  1297. };
  1298. }
  1299. return col;
  1300. };
  1301. // 基础列定义
  1302. let baseColumns = [
  1303. {
  1304. label: '选择',
  1305. width: 45,
  1306. type: 'selection',
  1307. columnKey: 'selection',
  1308. align: 'center',
  1309. fixed: 'left',
  1310. // 分批追加会多次替换数据源,靠 rowKey 保住勾选状态
  1311. reserveSelection: true
  1312. },
  1313. {
  1314. width: 65,
  1315. type: 'index',
  1316. columnKey: 'index',
  1317. align: 'center',
  1318. fixed: 'left'
  1319. },
  1320. {
  1321. minWidth: 100,
  1322. prop: 'sortCode',
  1323. slot: 'sortCode',
  1324. label: '排序',
  1325. isNone: !this.showSortLine,
  1326. showOverflowTooltip: true,
  1327. align: 'center'
  1328. },
  1329. {
  1330. minWidth: 200,
  1331. prop: 'orderNo',
  1332. slot: 'orderNo',
  1333. label: '订单编码',
  1334. isNone: !this.isOrderNo,
  1335. showOverflowTooltip: true,
  1336. align: 'center'
  1337. },
  1338. {
  1339. width: 280,
  1340. prop: 'productName',
  1341. label: '名称',
  1342. slot: 'productName',
  1343. headerSlot: 'headerProductName',
  1344. align: 'center'
  1345. },
  1346. {
  1347. width: 120,
  1348. prop: 'productCode',
  1349. label: '编码',
  1350. slot: 'productCode',
  1351. align: 'center'
  1352. },
  1353. {
  1354. width: 120,
  1355. prop: 'specification',
  1356. label: '规格',
  1357. slot: 'specification',
  1358. align: 'center'
  1359. },
  1360. {
  1361. minWidth: 240,
  1362. prop: 'taskName',
  1363. label: '工序',
  1364. slot: 'taskName',
  1365. align: 'center'
  1366. },
  1367. {
  1368. minWidth: 120,
  1369. prop: 'entrustedEnterpriseId',
  1370. label: '受托企业',
  1371. slot: 'entrustedEnterpriseId',
  1372. // isNone: !this.isCustomerMark,
  1373. align: 'center'
  1374. },
  1375. {
  1376. width: 140,
  1377. prop: 'pricingWay',
  1378. label: '计价方式',
  1379. headerSlot: 'headerPricingWay',
  1380. slot: 'pricingWay',
  1381. isNone: this.quoteType === 2,
  1382. fixed: 'left',
  1383. align: 'center'
  1384. },
  1385. {
  1386. width: 250,
  1387. prop: 'saleCount',
  1388. label: '数量',
  1389. slot: 'saleCount',
  1390. headerSlot: this.isTotalCount ? 'headerTotalCount' : '',
  1391. align: 'center'
  1392. },
  1393. {
  1394. width: 250,
  1395. prop: 'quoteWay',
  1396. label: '报价方式',
  1397. slot: 'quoteWay',
  1398. headerSlot: 'headerQuoteWay',
  1399. align: 'center'
  1400. }
  1401. ];
  1402. // 当quoteType为2时添加生产加工相关列
  1403. if (this.quoteType === 2) {
  1404. baseColumns.push(
  1405. {
  1406. width: 150,
  1407. prop: 'thickNess',
  1408. label: '厚度',
  1409. slot: 'thickNess',
  1410. align: 'center'
  1411. },
  1412. {
  1413. width: 150,
  1414. prop: 'squareNumber',
  1415. label: '平方数',
  1416. slot: 'squareNumber',
  1417. align: 'center'
  1418. },
  1419. {
  1420. width: 150,
  1421. prop: 'processingFeeBeforeTax',
  1422. label: '加工费(未税)',
  1423. slot: 'processingFeeBeforeTax',
  1424. align: 'center'
  1425. },
  1426. {
  1427. width: 150,
  1428. prop: 'packagingFeeNotTaxed',
  1429. label: '包装费(未税)',
  1430. slot: 'packagingFeeNotTaxed',
  1431. align: 'center'
  1432. },
  1433. {
  1434. width: 150,
  1435. prop: 'transportationFeeWithoutTax',
  1436. label: '运输费(未税)',
  1437. slot: 'transportationFeeWithoutTax',
  1438. align: 'center'
  1439. },
  1440. {
  1441. width: 150,
  1442. prop: 'quotationSubtotalBeforeTax',
  1443. label: '未税小计',
  1444. slot: 'additionalTaxRate_untaxed',
  1445. align: 'center'
  1446. },
  1447. {
  1448. width: 150,
  1449. prop: 'extraTax',
  1450. label: '税率(%)',
  1451. slot: 'extraTax',
  1452. align: 'center'
  1453. },
  1454. {
  1455. width: 150,
  1456. prop: 'quotationSubtotalTax',
  1457. label: '含税小计',
  1458. slot: 'quotationSubtotalTax',
  1459. align: 'center'
  1460. }
  1461. );
  1462. }
  1463. // 其他列
  1464. const otherColumns = [
  1465. {
  1466. width: 160,
  1467. prop: 'customerExpectDeliveryDeadline',
  1468. label: this.contractBookType == 1 ? '客户期望交期' : '交付日期',
  1469. slot: 'customerExpectDeliveryDeadline',
  1470. headerSlot:
  1471. this.isDate == 1 && this.quoteType != 2
  1472. ? 'headerCustomerExpectDeliveryDeadline'
  1473. : '',
  1474. align: 'center'
  1475. },
  1476. {
  1477. width: 180,
  1478. prop: 'singleWeight',
  1479. label: '单重',
  1480. slot: 'singleWeight',
  1481. headerSlot: 'headerSingleWeight',
  1482. align: 'center'
  1483. },
  1484. {
  1485. minWidth: 120,
  1486. prop: 'goodsLevel',
  1487. label: '物品级别',
  1488. slot: 'goodsLevel',
  1489. align: 'center',
  1490. isNone: this.quoteType === 2
  1491. },
  1492. {
  1493. width: 200,
  1494. prop: 'totalCount',
  1495. label: '计量数量',
  1496. headerSlot: 'headerTotalCount',
  1497. align: 'center',
  1498. formatter: (_row, _column, cellValue) => {
  1499. if (_row.totalCount) {
  1500. return _row.totalCount + ' ' + (_row.measuringUnit || '');
  1501. }
  1502. },
  1503. isNone: this.quoteType === 2
  1504. },
  1505. {
  1506. width: 200,
  1507. prop: 'customerMark',
  1508. label: this.contractBookType == 1 ? '客户代号' : '供应商代号',
  1509. slot: 'customerMark',
  1510. headerSlot: this.isCustomerMark ? 'headerCustomerMark' : '',
  1511. align: 'center',
  1512. isNone: this.quoteType === 2
  1513. },
  1514. {
  1515. minWidth: 160,
  1516. prop: 'productionCodes',
  1517. label: '生产编号',
  1518. align: 'center',
  1519. isNone: this.quoteType === 2
  1520. },
  1521. {
  1522. minWidth: 280,
  1523. prop: 'productionRequirements',
  1524. label: '生产要求',
  1525. align: 'center',
  1526. slot: 'productionRequirements',
  1527. isNone: !this.isProductionRequirements && this.quoteType === 2
  1528. },
  1529. {
  1530. minWidth: 280,
  1531. prop: 'cuttingLength',
  1532. label: '裁线米段',
  1533. align: 'center',
  1534. slot: 'cuttingLength',
  1535. isNone: this.quoteType === 2
  1536. },
  1537. {
  1538. width: 200,
  1539. prop: 'productCategoryName',
  1540. label: '类型',
  1541. slot: 'productCategoryName',
  1542. align: 'center',
  1543. isNone: this.quoteType === 2
  1544. },
  1545. {
  1546. width: 120,
  1547. prop: 'packingSpecification',
  1548. align: 'center',
  1549. label: '包装规格',
  1550. showOverflowTooltip: true,
  1551. isNone: this.quoteType === 2
  1552. },
  1553. {
  1554. width: 160,
  1555. prop: 'goodsPriceType',
  1556. label: '价格类型',
  1557. slot: 'goodsPriceType',
  1558. align: 'center',
  1559. isNone: this.quoteType === 2
  1560. },
  1561. {
  1562. width: 200,
  1563. prop: 'singlePrice',
  1564. label: '单价',
  1565. slot: 'singlePrice',
  1566. headerSlot: this.isSinglePrice ? 'headerSinglePrice' : '',
  1567. align: 'center',
  1568. isNone: this.quoteType === 2
  1569. },
  1570. {
  1571. width: 160,
  1572. prop: 'taxRate',
  1573. label: '税率',
  1574. slot: 'taxRate',
  1575. align: 'center',
  1576. headerSlot: this.isTaxRate == 1 ? 'headerTaxRate' : '',
  1577. isNone: this.quoteType === 2
  1578. },
  1579. {
  1580. width: 180,
  1581. prop: 'notaxSinglePrice',
  1582. label: '不含税单价',
  1583. slot: 'notaxSinglePrice',
  1584. align: 'center',
  1585. isNone: this.quoteType === 2
  1586. },
  1587. {
  1588. width: 160,
  1589. prop: 'discountRatio',
  1590. label: '折让比例',
  1591. align: 'center',
  1592. isNone: !this.isDiscount,
  1593. slot: 'discountRatio',
  1594. headerSlot: 'headerDiscountRatio'
  1595. },
  1596. {
  1597. width: 160,
  1598. prop: 'discountSinglePrice',
  1599. label: '折让单价',
  1600. align: 'center',
  1601. isNone: !this.isDiscount,
  1602. formatter: (_row, _column, cellValue) => {
  1603. return _row.discountSinglePrice
  1604. ? formatPrice(Number(_row.discountSinglePrice))
  1605. : '';
  1606. }
  1607. },
  1608. {
  1609. width: 120,
  1610. prop: 'totalPrice',
  1611. label: '合计',
  1612. slot: 'totalPrice',
  1613. align: 'center'
  1614. },
  1615. {
  1616. width: 160,
  1617. prop: 'discountTotalPrice',
  1618. label: '折让合计',
  1619. align: 'center',
  1620. isNone: !this.isDiscount,
  1621. formatter: (_row, _column, cellValue) => {
  1622. return _row.discountTotalPrice
  1623. ? formatPrice(Number(_row.discountTotalPrice))
  1624. : '';
  1625. }
  1626. },
  1627. {
  1628. width: 110,
  1629. prop: 'batchNo',
  1630. label: '批次号',
  1631. slot: 'batchNo',
  1632. headerSlot: this.isBatchNo ? 'headerBatchNo' : '',
  1633. align: 'center',
  1634. isNone: this.quoteType === 2
  1635. },
  1636. {
  1637. minWidth: 180,
  1638. prop: 'systemBatchNo',
  1639. label: '系统内部批次号',
  1640. align: 'center',
  1641. isNone: this.quoteType === 2,
  1642. formatter: (row) => row.systemBatchNo || '-'
  1643. },
  1644. {
  1645. width: 120,
  1646. prop: 'warehouseName',
  1647. label: '仓库名称',
  1648. slot: 'warehouseName',
  1649. align: 'center',
  1650. isNone: this.quoteType === 2
  1651. },
  1652. {
  1653. width: 120,
  1654. prop: 'stockCountBase',
  1655. label: '库存数量',
  1656. align: 'center',
  1657. isNone: this.quoteType === 2 || !this.showStockCount
  1658. },
  1659. {
  1660. width: 120,
  1661. prop: 'availableCountBase',
  1662. label: this.showStockCount ? '可用数量' : '库存数量',
  1663. slot: 'availableCountBase',
  1664. align: 'center',
  1665. isNone: this.quoteType === 2
  1666. },
  1667. {
  1668. prop: 'provenance',
  1669. label: '产地',
  1670. slot: 'provenance',
  1671. align: 'center',
  1672. showOverflowTooltip: true,
  1673. minWidth: 200,
  1674. isNone: this.quoteType === 2
  1675. },
  1676. {
  1677. width: 180,
  1678. prop: 'totalWeight',
  1679. label: '总重',
  1680. slot: 'totalWeight',
  1681. formatter: (_row, _column, cellValue) => {
  1682. if (_row.totalWeight) {
  1683. return _row.totalWeight + ' ' + (_row.weightUnit || '');
  1684. }
  1685. },
  1686. align: 'center'
  1687. },
  1688. {
  1689. width: 180,
  1690. prop: 'increaseTotalWeight',
  1691. label: '增重重量',
  1692. slot: 'increaseTotalWeight',
  1693. isNone: this.quoteType === 2 || !this.isIncreaseTotalWeight,
  1694. align: 'center'
  1695. },
  1696. {
  1697. width: 160,
  1698. prop: 'productBrand',
  1699. label: '牌号',
  1700. slot: 'productBrand',
  1701. align: 'center',
  1702. isNone: this.quoteType === 2
  1703. },
  1704. {
  1705. width: 120,
  1706. prop: 'modelType',
  1707. label: '型号',
  1708. slot: 'modelType',
  1709. align: 'center',
  1710. isNone: this.quoteType === 2
  1711. },
  1712. {
  1713. width: 160,
  1714. prop: 'modelKey',
  1715. label: '机型',
  1716. slot: 'modelKey',
  1717. align: 'center',
  1718. isNone: this.quoteType === 2
  1719. },
  1720. {
  1721. width: 160,
  1722. prop: 'colorKey',
  1723. label: '颜色',
  1724. slot: 'colorKey',
  1725. align: 'center',
  1726. isNone: this.quoteType === 2
  1727. },
  1728. {
  1729. width: 120,
  1730. prop: 'produceType',
  1731. align: 'center',
  1732. label: '属性类型',
  1733. isNone: this.quoteType === 2,
  1734. showOverflowTooltip: true,
  1735. formatter: (row, column) => {
  1736. if (row.produceType) {
  1737. return row.produceType
  1738. .map((item) => {
  1739. return lbjtList[item];
  1740. })
  1741. .toString();
  1742. }
  1743. }
  1744. },
  1745. {
  1746. width: 160,
  1747. prop: 'arrivalWay',
  1748. label: '到货方式',
  1749. slot: 'arrivalWay',
  1750. align: 'center',
  1751. isNone: !this.isArrivalWay || this.quoteType === 2
  1752. },
  1753. {
  1754. width: 160,
  1755. prop: 'produceDeliveryDeadline',
  1756. label: '生产交付交期',
  1757. slot: 'produceDeliveryDeadline',
  1758. headerSlot:
  1759. this.isDate == 1 && this.isProduceDeliveryDeadline
  1760. ? 'headerProduceDeliveryDeadline'
  1761. : '',
  1762. isNone: this.contractBookType != 1,
  1763. align: 'center'
  1764. },
  1765. {
  1766. width: 200,
  1767. prop: 'guaranteePeriod',
  1768. label: '有效期',
  1769. slot: 'guaranteePeriod',
  1770. align: 'center',
  1771. isNone: this.quoteType === 2
  1772. },
  1773. {
  1774. width: 200,
  1775. prop: 'guaranteePeriodDeadline',
  1776. label: '有效期截止日期',
  1777. slot: 'guaranteePeriodDeadline',
  1778. isNone: !this.isGuaranteePeriod || this.quoteType === 2,
  1779. align: 'center'
  1780. },
  1781. {
  1782. width: 120,
  1783. prop: 'imgCode',
  1784. align: 'center',
  1785. label: '图号/件号',
  1786. showOverflowTooltip: true,
  1787. isNone: this.quoteType === 2
  1788. },
  1789. {
  1790. width: 220,
  1791. prop: 'customerReqFiles',
  1792. label: '客户需求',
  1793. slot: 'customerReqFiles',
  1794. align: 'center',
  1795. isNone: this.quoteType === 2
  1796. },
  1797. {
  1798. width: 130,
  1799. prop: 'technicalAnswerName',
  1800. label: '技术答疑人',
  1801. slot: 'technicalAnswerName',
  1802. align: 'center',
  1803. isNone: this.quoteType === 2
  1804. },
  1805. {
  1806. width: 220,
  1807. prop: 'technicalParams',
  1808. label: '技术参数',
  1809. slot: 'technicalParams',
  1810. align: 'center',
  1811. isNone: this.quoteType === 2
  1812. },
  1813. {
  1814. width: 240,
  1815. prop: 'technicalDrawings',
  1816. label: '技术图纸',
  1817. slot: 'technicalDrawings',
  1818. align: 'center'
  1819. },
  1820. {
  1821. width: 120,
  1822. prop: 'drawingVersion',
  1823. label: '图纸版本',
  1824. align: 'center',
  1825. isNone: this.quoteType === 2
  1826. },
  1827. {
  1828. width: 240,
  1829. prop: 'technologyRouteName',
  1830. label: '工艺路线',
  1831. slot: 'technologyRouteName',
  1832. align: 'center',
  1833. isNone: this.quoteType === 2
  1834. },
  1835. {
  1836. width: 240,
  1837. prop: 'industryArtFiles',
  1838. label: '工艺附件',
  1839. slot: 'industryArtFiles',
  1840. align: 'center',
  1841. isNone: this.quoteType === 2
  1842. },
  1843. {
  1844. width: 240,
  1845. prop: 'otherFiles',
  1846. label: '其他附件',
  1847. slot: 'otherFiles',
  1848. align: 'center',
  1849. isNone: this.quoteType === 2
  1850. },
  1851. {
  1852. width: 220,
  1853. prop: 'remark',
  1854. label: '备注',
  1855. slot: 'remark',
  1856. align: 'center'
  1857. },
  1858. {
  1859. columnKey: 'action',
  1860. label: '操作',
  1861. width: 120,
  1862. align: 'center',
  1863. resizable: false,
  1864. slot: 'action',
  1865. fixed: 'right',
  1866. showOverflowTooltip: true
  1867. }
  1868. ];
  1869. // 合并所有列,统一应用列宽缓存(无缓存时保持默认宽度)
  1870. return [...baseColumns, ...otherColumns]
  1871. .filter((item) => !item.isNone)
  1872. .map(applyWidth);
  1873. }
  1874. },
  1875. created() {
  1876. parameterGetByCode({
  1877. code: 'eom_inventoryTable_customerExpectDeliveryDeadline'
  1878. }).then((res) => {
  1879. if (res.value) {
  1880. this.isDate = res.value;
  1881. }
  1882. });
  1883. },
  1884. methods: {
  1885. formatPrice,
  1886. getSummaries(param) {
  1887. return getSummaries(param, ['saleCount'], ' ');
  1888. },
  1889. handleCountChange(row, index, weightType) {
  1890. // 数量变化时,若为生产加工类型,触发含税小计计算
  1891. if (this.quoteType === 2) {
  1892. this.calculateIncludingTaxPrice(row, index);
  1893. } else {
  1894. this.changeCount(row, index, weightType);
  1895. }
  1896. },
  1897. packingChange(item, index) {
  1898. this.$set(
  1899. this.form.datasource[index],
  1900. this.countObj.unitKey,
  1901. item.conversionUnit
  1902. );
  1903. this.handleCountChange(this.form.datasource[index], index);
  1904. },
  1905. openVersion(index) {
  1906. this.$refs.versionRefs.open(index);
  1907. },
  1908. //设置分批到货时间
  1909. handleMethod(row, index) {
  1910. this.$refs.timeDialogRef.open(row, index);
  1911. },
  1912. chooseTime({ arrivalBatch, index }) {
  1913. this.$set(
  1914. this.form.datasource[index],
  1915. 'arrivalBatch',
  1916. copyObj(arrivalBatch)
  1917. );
  1918. },
  1919. //工艺路线
  1920. changeProduct(data, index) {
  1921. this.$set(
  1922. this.form.datasource[index],
  1923. 'technologyRouteName',
  1924. data.name
  1925. );
  1926. this.$set(this.form.datasource[index], 'technologyRouteId', data.id);
  1927. },
  1928. handleTaskinstance(row, index) {
  1929. this.taskinstanceDialogFlag = true;
  1930. this.$nextTick(() => {
  1931. this.$refs.taskinstanceDialogRef.open(row, index);
  1932. });
  1933. },
  1934. saveTaskInstance(row = {}) {
  1935. this.$set(this.form.datasource[row.index], 'taskId', row.id);
  1936. this.$set(this.form.datasource[row.index], 'taskName', row.name);
  1937. this.$set(
  1938. this.form.datasource[row.index],
  1939. 'routingId',
  1940. row.produceRoutingId
  1941. );
  1942. },
  1943. async getSupplierObj(productList, queryName) {
  1944. try {
  1945. const categoryIds = [
  1946. ...new Set(
  1947. productList
  1948. .map((item) => item[queryName])
  1949. .filter((categoryId) => categoryId)
  1950. )
  1951. ];
  1952. if (categoryIds.length > 0) {
  1953. const supplierObj = await contactQueryByCategoryIdsAPI({
  1954. categoryIds
  1955. });
  1956. Object.keys(supplierObj || {}).forEach((categoryId) => {
  1957. const supplierIds = new Set();
  1958. supplierObj[categoryId] = (supplierObj[categoryId] || []).filter(
  1959. (supplier) => {
  1960. const supplierId = String(supplier.id);
  1961. if (supplierIds.has(supplierId)) {
  1962. return false;
  1963. }
  1964. supplierIds.add(supplierId);
  1965. return true;
  1966. }
  1967. );
  1968. });
  1969. return supplierObj;
  1970. } else {
  1971. return Promise.resolve({});
  1972. }
  1973. } catch (e) {
  1974. return Promise.resolve({});
  1975. }
  1976. },
  1977. setDeliveryDays(row, index, type, isAll) {
  1978. if (isAll) {
  1979. this.form.datasource.forEach((item, i) => {
  1980. let guaranteePeriodUnitName = this.guaranteePeriodUnit(
  1981. item.guaranteePeriodUnitCode
  1982. );
  1983. this.$set(
  1984. this.form.datasource[i],
  1985. 'guaranteePeriodDeadline',
  1986. guaranteePeriodUnitName != 'second'
  1987. ? this.setDay(
  1988. item.guaranteePeriod,
  1989. guaranteePeriodUnitName,
  1990. item
  1991. )
  1992. : ''
  1993. );
  1994. });
  1995. return;
  1996. }
  1997. if (type == 'guaranteePeriod') {
  1998. let guaranteePeriodUnitName = this.guaranteePeriodUnit(
  1999. row.guaranteePeriodUnitCode
  2000. );
  2001. this.$set(
  2002. this.form.datasource[index],
  2003. 'guaranteePeriodDeadline',
  2004. guaranteePeriodUnitName != 'second'
  2005. ? this.setDay(row.guaranteePeriod, guaranteePeriodUnitName, row)
  2006. : ''
  2007. );
  2008. }
  2009. },
  2010. guaranteePeriodUnit(code) {
  2011. return code == 3
  2012. ? 'day'
  2013. : code == 4
  2014. ? 'month'
  2015. : code == 5
  2016. ? 'year'
  2017. : 'second';
  2018. },
  2019. setDay(addDay, dateType = 'day', item) {
  2020. let tiem =
  2021. this.contractBookType == 1
  2022. ? item.produceDeliveryDeadline
  2023. : item.customerExpectDeliveryDeadline;
  2024. return dayjs(tiem || new Date())
  2025. .add(addDay, dateType)
  2026. .format('YYYY-MM-DD');
  2027. },
  2028. // 兼容未返回 stockCountBase 的旧库存接口
  2029. getStockCount(item) {
  2030. if (item.stockCountBase !== undefined && item.stockCountBase !== null) {
  2031. return Number(item.stockCountBase);
  2032. }
  2033. if (
  2034. item.measureQuantity !== undefined &&
  2035. item.measureQuantity !== null
  2036. ) {
  2037. return Number(item.measureQuantity);
  2038. }
  2039. return Number(item.availableCountBase || 0);
  2040. },
  2041. // 可用数量统一按库存余额扣减锁库数量计算
  2042. getAvailableStockCount(item) {
  2043. const availableCount =
  2044. this.getStockCount(item) - Number(item.lockQuantity || 0);
  2045. return Number(availableCount.toFixed(4));
  2046. },
  2047. getInventoryIdentity(item) {
  2048. return [
  2049. item.productCode,
  2050. item.warehouseId,
  2051. item.batchNo,
  2052. item.systemBatchNo,
  2053. item.colorKey,
  2054. item.modelType
  2055. ]
  2056. .map((value) => (value == null ? '' : String(value).trim()))
  2057. .join('|');
  2058. },
  2059. buildExactInventoryRequest(item) {
  2060. return {
  2061. code: item.productCode,
  2062. warehouseId: item.warehouseId,
  2063. batchNo: item.batchNo,
  2064. systemBatchNo: item.systemBatchNo,
  2065. colorKey: item.colorKey,
  2066. modelType: item.modelType
  2067. };
  2068. },
  2069. async refreshStockOrderInventory(validateQuantity = false) {
  2070. if (this.needProduce != 0) {
  2071. return true;
  2072. }
  2073. const inventoryGroups = new Map();
  2074. this.form.datasource.forEach((item, index) => {
  2075. if (!item.productCode || !item.warehouseId) {
  2076. return;
  2077. }
  2078. const identity = this.getInventoryIdentity(item);
  2079. const group = inventoryGroups.get(identity) || {
  2080. request: this.buildExactInventoryRequest(item),
  2081. rows: [],
  2082. requiredQuantity: 0,
  2083. productCode: item.productCode,
  2084. productName: item.productName
  2085. };
  2086. group.rows.push(index);
  2087. group.requiredQuantity += Number(
  2088. item.totalCount || item.saleCount || 0
  2089. );
  2090. inventoryGroups.set(identity, group);
  2091. });
  2092. const groups = Array.from(inventoryGroups.values());
  2093. if (groups.length === 0) {
  2094. return true;
  2095. }
  2096. const inventoryList = await getWarehouseOutStockBatchAPI(
  2097. groups.map((group) => group.request)
  2098. );
  2099. const insufficient = [];
  2100. groups.forEach((group, groupIndex) => {
  2101. const inventory = inventoryList[groupIndex] || {};
  2102. const stockCountBase = Number(inventory.stockCountBase || 0);
  2103. const availableCountBase = Number(inventory.availableCountBase || 0);
  2104. group.rows.forEach((rowIndex) => {
  2105. if (this.showStockCount) {
  2106. this.$set(
  2107. this.form.datasource[rowIndex],
  2108. 'stockCountBase',
  2109. stockCountBase
  2110. );
  2111. }
  2112. this.$set(
  2113. this.form.datasource[rowIndex],
  2114. 'availableCountBase',
  2115. availableCountBase
  2116. );
  2117. });
  2118. if (validateQuantity && group.requiredQuantity > availableCountBase) {
  2119. insufficient.push(
  2120. `${group.productCode}-${group.productName || ''}需求${
  2121. group.requiredQuantity
  2122. },可用${availableCountBase}`
  2123. );
  2124. }
  2125. });
  2126. if (insufficient.length > 0) {
  2127. this.$message.error(`库存不足:${insufficient.join(';')}`);
  2128. return false;
  2129. }
  2130. return true;
  2131. },
  2132. async validateInventoryAvailability() {
  2133. return this.refreshStockOrderInventory(true);
  2134. },
  2135. // 返回列表数据
  2136. getTableValue() {
  2137. let comitDatasource = copyObj(this.form.datasource);
  2138. if (comitDatasource.length === 0) return [];
  2139. comitDatasource.forEach((v) => {
  2140. // 计量数量在后端是非空字段,库存台账多选等入口只带回销售数量,
  2141. // 未触发数量换算时会留空,提交前统一用销售数量兜底,避免提交报 SQL 错误
  2142. const saleCount = v[this.countObj.countKey];
  2143. if (
  2144. (v.totalCount === undefined ||
  2145. v.totalCount === null ||
  2146. v.totalCount === '') &&
  2147. saleCount !== undefined &&
  2148. saleCount !== null &&
  2149. saleCount !== ''
  2150. ) {
  2151. v.totalCount = saleCount;
  2152. }
  2153. if (v.guaranteePeriodUnitCode) {
  2154. v.guaranteePeriodUnitName = this.getDictValue(
  2155. '保质期单位',
  2156. v.guaranteePeriodUnitCode
  2157. );
  2158. }
  2159. if (v.modelKey) {
  2160. v.modelKey = v.modelKey;
  2161. }
  2162. if (v.colorKey) {
  2163. v.colorKey = v.colorKey;
  2164. }
  2165. v.technicalDrawings = v.technicalDrawings ? v.technicalDrawings : [];
  2166. v.customerReqFiles = v.customerReqFiles || [];
  2167. v.industryArtFiles = v.industryArtFiles || [];
  2168. v.otherFiles = v.otherFiles || [];
  2169. });
  2170. console.log('comitDatasource~~~', comitDatasource);
  2171. return comitDatasource;
  2172. },
  2173. getPrice() {
  2174. return [this.allPrice, this.form.discountTotalPrice];
  2175. },
  2176. //改变数量
  2177. changeCount(row, index, weightType) {
  2178. if (!row) {
  2179. this.form.datasource = this.form.datasource.map((item, i) => {
  2180. return changeCount(item, this.countObj, false);
  2181. });
  2182. this.form.datasource.forEach((item, i) => {
  2183. this.setIncreaseTotalWeight(item, i);
  2184. });
  2185. } else {
  2186. const updatedRow = changeCount(row, this.countObj, false, weightType);
  2187. this.$set(this.form.datasource, index, updatedRow);
  2188. this.setIncreaseTotalWeight(row, index);
  2189. }
  2190. this.$nextTick(() => {
  2191. // 生产加工类型不需要计算不含税单价,但需要计算含税小计
  2192. this.getNotaxSinglePrice();
  2193. this.changeAll();
  2194. });
  2195. },
  2196. setIncreaseTotalWeight(row, index) {
  2197. if (row.pricingWay == 3) {
  2198. this.$set(
  2199. this.form.datasource[index],
  2200. 'totalPrice',
  2201. formatPrice((row.increaseTotalWeight || 0) * row.singlePrice)
  2202. );
  2203. this.changeAll();
  2204. }
  2205. },
  2206. // 计算含税小计(原含税单价逻辑)
  2207. calculateIncludingTaxPrice(row, index) {
  2208. if (!row) return;
  2209. // 计算未税小记
  2210. const quotationSubtotalBeforeTax =
  2211. (Number(row.processingFeeBeforeTax) || 0) +
  2212. (Number(row.packagingFeeNotTaxed) || 0) +
  2213. (Number(row.transportationFeeWithoutTax) || 0);
  2214. // 使用 $nextTick 确保响应式更新完成
  2215. // this.$nextTick(() => {
  2216. this.$set(
  2217. this.form.datasource[index],
  2218. 'quotationSubtotalBeforeTax',
  2219. quotationSubtotalBeforeTax.toFixed(2)
  2220. );
  2221. // 计算含税小计
  2222. const taxRate = (Number(row.extraTax) || 0) / 100;
  2223. const quotationSubtotalTax = quotationSubtotalBeforeTax * (1 + taxRate);
  2224. this.$set(
  2225. this.form.datasource[index],
  2226. 'quotationSubtotalTax',
  2227. quotationSubtotalTax.toFixed(2)
  2228. );
  2229. const quantity = Number(row[this.countObj.countKey]) || 1;
  2230. const totalPrice = quotationSubtotalTax * quantity;
  2231. this.$set(
  2232. this.form.datasource[index],
  2233. 'totalPrice',
  2234. formatPrice(totalPrice)
  2235. );
  2236. if (
  2237. row[this.countObj.unitKey] == row.weightUnit &&
  2238. row.pricingWay == 1
  2239. ) {
  2240. this.$set(this.form.datasource[index], 'totalWeight', quantity);
  2241. } else {
  2242. if (quantity && row.singleWeight) {
  2243. this.$set(
  2244. this.form.datasource[index],
  2245. 'totalWeight',
  2246. (quantity * row.singleWeight).toFixed(2)
  2247. );
  2248. }
  2249. }
  2250. this.changeAll();
  2251. // });
  2252. },
  2253. changeAll() {
  2254. this.allPrice = getAllPrice(this.form.datasource) || 0;
  2255. this.allQuantity =
  2256. getAllQuantity(this.form.datasource, this.countObj) || 0;
  2257. this.form.discountTotalPrice =
  2258. getAllDiscountPrice(this.form.datasource) || 0;
  2259. if (this.isDiscountTotalPrice) {
  2260. this.form.discountTotalPrice = this.form.discountTotalPrice;
  2261. this.$emit('setDiscountTotalPrice', this.form.discountTotalPrice);
  2262. }
  2263. this.$emit(
  2264. 'setCountAmount',
  2265. this.allPrice,
  2266. this.form.discountTotalPrice
  2267. );
  2268. },
  2269. setCustomerMark(customerMark) {
  2270. this.form.datasource.forEach((item) => {
  2271. item['customerMark'] = customerMark;
  2272. this.$forceUpdate();
  2273. });
  2274. },
  2275. // 计算折让单价,折让总价
  2276. getDiscountRatioPrice(row) {
  2277. this.form.datasource.forEach((item, index) => {
  2278. if (item.singlePrice && item.discountRatio) {
  2279. this.$set(
  2280. this.form.datasource[index],
  2281. 'discountSinglePrice',
  2282. formatPrice(item.singlePrice * (item.discountRatio / 100))
  2283. );
  2284. this.$set(
  2285. this.form.datasource[index],
  2286. 'discountTotalPrice',
  2287. formatPrice(item.totalPrice * (item.discountRatio / 100))
  2288. );
  2289. } else {
  2290. this.$set(this.form.datasource[index], 'discountSinglePrice', 0);
  2291. this.$set(this.form.datasource[index], 'discountTotalPrice', 0);
  2292. }
  2293. this.$set(
  2294. this.form,
  2295. 'discountTotalPrice',
  2296. this.form.datasource.reduce(
  2297. (acc, cur) => acc + Number(cur.discountTotalPrice || 0),
  2298. 0
  2299. )
  2300. );
  2301. this.changeAll();
  2302. });
  2303. },
  2304. //计算不含税单价
  2305. getNotaxSinglePrice() {
  2306. this.form.datasource.forEach((item, index) => {
  2307. if (item.singlePrice && item.taxRate) {
  2308. this.$set(
  2309. this.form.datasource[index],
  2310. 'notaxSinglePrice',
  2311. formatPrice(item.singlePrice / (1 + item.taxRate / 100))
  2312. );
  2313. } else {
  2314. this.$set(this.form.datasource[index], 'notaxSinglePrice', '');
  2315. }
  2316. });
  2317. },
  2318. //设置优惠后总金额修改产品单价
  2319. discountInputByOrder(val) {
  2320. this.form.discountTotalPrice = val;
  2321. this.form.datasource.forEach((item, index) => {
  2322. this.$set(
  2323. this.form.datasource[index],
  2324. 'discountSinglePrice',
  2325. this.getDiscountSinglePrice(item)
  2326. );
  2327. this.$set(
  2328. this.form.datasource[index],
  2329. 'discountTotalPrice',
  2330. this.getDiscountTotalPrice(item)
  2331. );
  2332. this.$set(
  2333. this.form.datasource[index],
  2334. 'discountRatio',
  2335. this.getDiscountRatio(item)
  2336. );
  2337. });
  2338. this.$emit('setDiscountTotalPrice', val);
  2339. this.$forceUpdate();
  2340. this.$refs.table.reRenderTable();
  2341. },
  2342. //批量设置税率
  2343. setAllTaxRateChange({ key, value }) {
  2344. if (this.tableBusy) return;
  2345. let indexS = this.selection.map((item) => item.key - 1);
  2346. indexS.forEach((i) => {
  2347. this.$set(this.form.datasource[i], key, value);
  2348. this.getNotaxSinglePrice();
  2349. });
  2350. },
  2351. setAllValueChange({ key, value }) {
  2352. if (this.tableBusy) return;
  2353. let indexS = this.selection.map((item) => item.key - 1);
  2354. indexS.forEach((i) => {
  2355. this.$set(this.form.datasource[i], key, value);
  2356. this.setDeliveryDays(this.form.datasource[i], i, 'guaranteePeriod');
  2357. });
  2358. },
  2359. //获取折让单价
  2360. getDiscountSinglePrice(row) {
  2361. let num =
  2362. (Number(this.form.discountTotalPrice) / Number(this.allPrice)) *
  2363. Number(row.singlePrice);
  2364. return isNaN(num) ? '' : formatPrice(num);
  2365. },
  2366. //获取折让比例
  2367. getDiscountRatio(row) {
  2368. let num = 100;
  2369. // if(Number(row.discountRatio)) {
  2370. // num =
  2371. // ((Number(this.form.discountTotalPrice) / Number(this.allPrice)) * Number(row.discountRatio)).toFixed(2);
  2372. // console.log('折让比例', row.discountRatio, num, (Number(this.form.discountTotalPrice) / Number(this.allPrice)))
  2373. // } else {
  2374. num = (
  2375. (Number(this.form.discountTotalPrice) / Number(this.allPrice)) *
  2376. 100
  2377. ).toFixed(2);
  2378. // }
  2379. return isNaN(num) ? '' : num;
  2380. },
  2381. //获取折让合计
  2382. getDiscountTotalPrice(row) {
  2383. let num = 0;
  2384. num =
  2385. Number(row.discountSinglePrice) * Number(row[this.countObj.countKey]);
  2386. return isNaN(num) ? '' : formatPrice(num);
  2387. },
  2388. orderNoChange(row, item) {
  2389. row['orderId'] = item.orderId;
  2390. },
  2391. //修改回显
  2392. async putTableValue(data) {
  2393. let productList =
  2394. (data &&
  2395. (data.quoteProductList || data.productList || data.detailList)) ||
  2396. [];
  2397. if (productList) {
  2398. let supplierObj = await this.getSupplierObj(productList, 'productId');
  2399. productList.forEach((item, index) => {
  2400. item['key'] = index + 1;
  2401. if (item.modelKey) {
  2402. item.modelKey = item.modelKey;
  2403. }
  2404. if (item.colorKey) {
  2405. item.colorKey = item.colorKey;
  2406. }
  2407. item.guaranteePeriodUnitCode = item.guaranteePeriodUnitCode
  2408. ? item.guaranteePeriodUnitCode + ''
  2409. : '';
  2410. let guaranteePeriodUnitName = this.guaranteePeriodUnit(
  2411. item.guaranteePeriodUnitCode
  2412. );
  2413. if (item.guaranteePeriod && item.guaranteePeriodUnitCode) {
  2414. item['guaranteePeriodDeadline'] =
  2415. item['guaranteePeriodDeadline'] ||
  2416. guaranteePeriodUnitName != 'second'
  2417. ? this.setDay(
  2418. item.guaranteePeriod,
  2419. guaranteePeriodUnitName,
  2420. item
  2421. )
  2422. : '';
  2423. }
  2424. item.entrustedEnterpriseIdList = supplierObj[item.productId];
  2425. // if (supplierObj[item.productId]?.length === 1) {
  2426. // item.entrustedEnterpriseId = supplierObj[item.productId][0].id;
  2427. // }
  2428. // item.entrustedEnterpriseId = item.entrustedEnterpriseId || undefined;
  2429. this.$set(
  2430. item,
  2431. 'entrustedEnterpriseId',
  2432. item.entrustedEnterpriseId || ''
  2433. );
  2434. item['customerMark'] = item.customerMark || this.customerMark;
  2435. item['arrivalWay'] = item.arrivalWay || 1;
  2436. item.discountRatio = Number(item.discountRatio) || 100;
  2437. item.taxRate =
  2438. item.taxRate !== undefined && item.taxRate !== null
  2439. ? item.taxRate
  2440. : this.isContractBook
  2441. ? this.defTaxRate
  2442. : this.isTaxRate == 1
  2443. ? this.defTaxRate
  2444. : undefined;
  2445. item.quoteWay = item.quoteWay || 1;
  2446. });
  2447. this.form.datasource = productList;
  2448. if (this.needProduce == 0) {
  2449. await this.refreshStockOrderInventory();
  2450. } else {
  2451. let codeList = this.form.datasource
  2452. .filter((item) => item.productCode)
  2453. .map((item) => buildInventoryTotalQueryKey(item));
  2454. // 非库存式业务保留原有物品/批次汇总库存口径。
  2455. let inventoryTotalList = await getInventoryTotalAPI(codeList);
  2456. this.form.datasource
  2457. .filter((item) => item.productCode)
  2458. .forEach((item, index) => {
  2459. let find =
  2460. inventoryTotalList.find((key) =>
  2461. matchInventoryTotal(item, key)
  2462. ) || {};
  2463. this.form.datasource;
  2464. if (this.showStockCount) {
  2465. this.$set(
  2466. this.form.datasource[index],
  2467. 'stockCountBase',
  2468. this.getStockCount(find)
  2469. );
  2470. }
  2471. this.$set(
  2472. this.form.datasource[index],
  2473. 'availableCountBase',
  2474. this.showStockCount
  2475. ? this.getAvailableStockCount(find)
  2476. : find.availableCountBase
  2477. );
  2478. });
  2479. }
  2480. if (this.isChangeCount) {
  2481. this.changeCount();
  2482. } else {
  2483. this.allPrice = data.totalAmount || data?.contractVO?.totalPrice;
  2484. this.allQuantity = getAllQuantity(
  2485. this.form.datasource,
  2486. this.countObj
  2487. );
  2488. }
  2489. if (this.isDiscountTotalPrice) {
  2490. this.form.discountTotalPrice =
  2491. data.payAmount || data?.contractVO?.discountTotalPrice;
  2492. }
  2493. this.$refs.table.reload();
  2494. }
  2495. },
  2496. //选择产品
  2497. handParent(row, index) {
  2498. if (this.tableBusy) return;
  2499. let item = {
  2500. id: row?.productCode
  2501. };
  2502. if (row?.goodsId) {
  2503. this.handGoods(index);
  2504. return;
  2505. }
  2506. if (this.needProduce == 1 || this.needProduce == 2) {
  2507. this.$refs.productListRef.open(item, index);
  2508. } else {
  2509. this.$refs.selectStockLedgerDialogRef.open(item, index);
  2510. }
  2511. },
  2512. handGoods(index) {
  2513. if (this.tableBusy) return;
  2514. this.$refs.commodityPriceListDialogRef.open(index);
  2515. },
  2516. //选择技术人回调
  2517. changeAnswer(obj, idx) {
  2518. this.$set(this.form.datasource[idx], 'technicalAnswerId', obj.id);
  2519. this.$set(this.form.datasource[idx], 'technicalAnswerName', obj.name);
  2520. },
  2521. handHead(row, index) {
  2522. let item = {
  2523. id: row.technicalAnswerId
  2524. };
  2525. this.$refs.headRef.open(item, index);
  2526. },
  2527. buildProductRow(item, oldRow, key) {
  2528. const row = oldRow ? copyObj(oldRow) : copyObj(this.defaultForm);
  2529. const warrantyPeriodUnit = item.warrantyPeriodUnit
  2530. ? item.warrantyPeriodUnit + ''
  2531. : '';
  2532. Object.assign(row, {
  2533. key,
  2534. productId: item.id,
  2535. categoryName: item.name,
  2536. productCategoryId: item.categoryLevelId,
  2537. productBrand: item.brandNum,
  2538. productCategoryName: item.categoryLevelPath,
  2539. productCode: item.code,
  2540. productName: item.name,
  2541. modelType: item.modelType,
  2542. availableCountBase: this.showStockCount
  2543. ? this.getAvailableStockCount(item)
  2544. : item.availableCountBase,
  2545. measuringUnit: item.measuringUnit,
  2546. specification: item.specification,
  2547. weightUnit: item.weightUnit,
  2548. singleWeight: item.netWeight,
  2549. pricingWay: 1,
  2550. goodsLevel: item.goodsLevel,
  2551. guaranteePeriod: item.warrantyPeriod,
  2552. customerExpectDeliveryDeadline: dayjs(new Date()).format(
  2553. 'YYYY-MM-DD'
  2554. ),
  2555. modelKey: item.modelKey?.split(',')?.[0] || '',
  2556. colorKey: item.colorKey?.split(',')?.[0] || '',
  2557. technologyRouteName: item.technologyRouteName || '',
  2558. technologyRouteId: item.technologyRouteId || '',
  2559. guaranteePeriodUnitCode: warrantyPeriodUnit,
  2560. packageDispositionList: item.packageDispositionList,
  2561. arrivalWay: item.arrivalWay || 1,
  2562. imgCode: item.imgCode,
  2563. produceType: item.componentAttribute,
  2564. approvalNumber: item.extField?.approvalNumber,
  2565. packingSpecification: item.extField?.packingSpecification,
  2566. customerMark: this.customerMark,
  2567. provenance: item.purchaseOrigins?.map((val) => val + '') || []
  2568. });
  2569. if (this.showStockCount) {
  2570. row.stockCountBase = this.getStockCount(item);
  2571. }
  2572. if (this.defTaxRate) {
  2573. row.taxRate =
  2574. row.taxRate !== undefined && row.taxRate !== null
  2575. ? row.taxRate
  2576. : this.isContractBook
  2577. ? this.defTaxRate
  2578. : this.isTaxRate == 1
  2579. ? this.defTaxRate
  2580. : undefined;
  2581. }
  2582. if (item.warrantyPeriod && item.warrantyPeriodUnit) {
  2583. row.guaranteePeriodDeadline = this.setDay(
  2584. item.warrantyPeriod,
  2585. this.guaranteePeriodUnit(item.warrantyPeriodUnit),
  2586. {}
  2587. );
  2588. }
  2589. if (item.packageDispositionList?.length) {
  2590. row[this.countObj.unitIdKey] = item.packageDispositionList[0].id;
  2591. row[this.countObj.unitKey] =
  2592. item.packageDispositionList[0]?.conversionUnit || '';
  2593. }
  2594. if (this.isOrderNo) {
  2595. row.orderNo = this.orderOption[0]?.orderNo;
  2596. row.orderId = this.orderOption[0]?.orderId;
  2597. }
  2598. if (this.isGoods && item.goodsId) {
  2599. Object.assign(row, {
  2600. goodsId: item.goodsId,
  2601. goodsPriceId: item.goodsPriceId,
  2602. goodsPriceType: item.goodsPriceType,
  2603. singlePrice: item.singlePrice,
  2604. notaxSinglePrice: item.notaxSinglePrice,
  2605. taxRate: item.taxRate,
  2606. discountSinglePrice: item.singlePrice,
  2607. totalCount: '',
  2608. discountRatio: item.discountRatio,
  2609. quoteWay: item.quoteWay || 1
  2610. });
  2611. } else {
  2612. row.singlePrice = row.singlePrice || 0;
  2613. row.discountRatio = row.discountRatio || 100;
  2614. row.quoteWay = row.quoteWay || 1;
  2615. }
  2616. if (this.quoteType === 2) {
  2617. this.processSetValue(row);
  2618. }
  2619. if (this.isSupplier) {
  2620. row.entrustedEnterpriseIdList = item.entrustedEnterpriseIdList;
  2621. row.entrustedEnterpriseId = item.entrustedEnterpriseId;
  2622. }
  2623. return row;
  2624. },
  2625. /**
  2626. * 重算表格。dirtyIndexes 只指定本次真正变动的行下标,
  2627. * 其余行直接复用原对象引用,省掉深拷贝与重复观测的成本。
  2628. * 不传 dirtyIndexes 时退化为全量重算(数量/税率等手工输入场景)。
  2629. */
  2630. recalculateTableOnce(dirtyIndexes) {
  2631. const dirty = Array.isArray(dirtyIndexes)
  2632. ? new Set(dirtyIndexes)
  2633. : null;
  2634. this.form.datasource = this.form.datasource.map((item, index) => {
  2635. if (dirty && !dirty.has(index)) {
  2636. return item;
  2637. }
  2638. const row = changeCount(item, this.countObj, false);
  2639. if (row.pricingWay == 3) {
  2640. row.totalPrice = formatPrice(
  2641. (row.increaseTotalWeight || 0) * row.singlePrice
  2642. );
  2643. }
  2644. row.notaxSinglePrice =
  2645. row.singlePrice && row.taxRate
  2646. ? formatPrice(row.singlePrice / (1 + row.taxRate / 100))
  2647. : '';
  2648. return row;
  2649. });
  2650. this.changeAll();
  2651. },
  2652. /**
  2653. * 大批量回显时先让 loading 遮罩绘制出来,再执行会阻塞主线程的计算。
  2654. * pro-table 内部在同一个 tick 里把 tableLoading 置 true 又置 false,
  2655. * 遮罩来不及显示,所以这里必须跨帧控制。
  2656. */
  2657. runAfterPaint(task) {
  2658. this._loadingStart = Date.now();
  2659. this.tableLoading = true;
  2660. this.$nextTick(() => {
  2661. requestAnimationFrame(task);
  2662. });
  2663. },
  2664. // 保证遮罩最短展示时间,避免一闪而过
  2665. endTableLoading(minDuration = 200) {
  2666. const start = this._loadingStart || Date.now();
  2667. const rest = minDuration - (Date.now() - start);
  2668. if (rest > 0) {
  2669. setTimeout(() => {
  2670. this.tableLoading = false;
  2671. }, rest);
  2672. } else {
  2673. this.tableLoading = false;
  2674. }
  2675. },
  2676. //选择产品回调
  2677. changeParent(obj, idx) {
  2678. if (idx == -1) {
  2679. // 新增:先构造好所有行,再分批追加并渲染
  2680. const startIndex = this.form.datasource.length;
  2681. const maxKey = this.form.datasource.reduce(
  2682. (max, item) => Math.max(max, Number(item.key) || 0),
  2683. 0
  2684. );
  2685. const rows = obj.map((item, index) =>
  2686. this.buildProductRow(item, null, maxKey + index + 1)
  2687. );
  2688. this.runAfterPaint(() => this.appendRowsInBatches(rows, startIndex));
  2689. return;
  2690. }
  2691. // 编辑已有行:单行替换
  2692. this.runAfterPaint(() => {
  2693. try {
  2694. this.updateProductRow(obj[0], idx);
  2695. } finally {
  2696. this.endTableLoading();
  2697. }
  2698. });
  2699. },
  2700. updateProductRow(item, idx) {
  2701. this.$set(
  2702. this.form.datasource,
  2703. idx,
  2704. this.buildProductRow(
  2705. item,
  2706. this.form.datasource[idx],
  2707. this.form.datasource[idx]?.key || idx + 1
  2708. )
  2709. );
  2710. this.recalculateTableOnce([idx]);
  2711. },
  2712. /**
  2713. * 分批追加:一次选中上百个物品时,一次性塞进表格会让主线程长时间阻塞,
  2714. * 这里按 appendBatchSize 分批写入并重算,每批之间让出一帧,
  2715. * 浏览器可以正常绘制 loading 遮罩和已经渲染出来的行。
  2716. */
  2717. appendRowsInBatches(
  2718. rows,
  2719. startIndex = this.form.datasource.length,
  2720. onDone
  2721. ) {
  2722. if (!rows.length) {
  2723. if (onDone) onDone();
  2724. this.endTableLoading();
  2725. return;
  2726. }
  2727. const batchSize = Math.max(1, this.appendBatchSize || 20);
  2728. const total = rows.length;
  2729. this.batchLoading = true;
  2730. this.loadedCount = 0;
  2731. this.totalCount = total;
  2732. let done = 0;
  2733. const step = () => {
  2734. const batch = rows.slice(done, done + batchSize);
  2735. if (batch.length) {
  2736. const from = startIndex + done;
  2737. this.form.datasource = this.form.datasource.concat(batch);
  2738. // 只重算本批新增的行,其余行复用原对象
  2739. this.recalculateTableOnce(batch.map((row, i) => from + i));
  2740. done += batch.length;
  2741. this.loadedCount = done;
  2742. }
  2743. if (done < total) {
  2744. requestAnimationFrame(step);
  2745. return;
  2746. }
  2747. // 全部批次都写完并渲染完,才收尾关闭 loading
  2748. this.batchLoading = false;
  2749. if (onDone) onDone();
  2750. this.endTableLoading();
  2751. };
  2752. requestAnimationFrame(step);
  2753. },
  2754. async replaceTable(obj, idx) {
  2755. // 先按物品ID去重并并行查询商品信息,避免逐行串行请求,物品较多时明显加快回显
  2756. const goodsMap = await this.loadGoodsByCategoryIds(obj);
  2757. this.runAfterPaint(() => {
  2758. try {
  2759. if (idx == -1) {
  2760. // 新增:先把行构造在普通对象上(不触发任何渲染),最后统一分批入表
  2761. const startIndex = this.form.datasource.length;
  2762. const maxKey = this.form.datasource.reduce(
  2763. (max, item) => Math.max(max, Number(item.key) || 0),
  2764. 0
  2765. );
  2766. const pendingRows = [];
  2767. for (let index = 0; index < obj.length; index++) {
  2768. pendingRows.push(
  2769. this.buildStockLedgerRow(
  2770. obj[index],
  2771. goodsMap,
  2772. maxKey + index + 1
  2773. )
  2774. );
  2775. }
  2776. this.appendRowsInBatches(
  2777. pendingRows,
  2778. startIndex,
  2779. this.afterReplaceTable
  2780. );
  2781. return;
  2782. }
  2783. // 编辑已有行:就地更新
  2784. this.buildStockLedgerRow(
  2785. obj[0],
  2786. goodsMap,
  2787. this.form.datasource[idx]?.key || idx + 1,
  2788. this.form.datasource[idx]
  2789. );
  2790. this.afterReplaceTable();
  2791. this.endTableLoading();
  2792. } catch (error) {
  2793. this.endTableLoading();
  2794. throw error;
  2795. }
  2796. });
  2797. },
  2798. afterReplaceTable() {
  2799. // 计量数量为后端非空字段:只补齐留空的行,避免覆盖已按计量单位换算好的数据
  2800. this.form.datasource.forEach((item) => {
  2801. const count = item[this.countObj.countKey];
  2802. if (
  2803. (item.totalCount === undefined ||
  2804. item.totalCount === null ||
  2805. item.totalCount === '') &&
  2806. count !== undefined &&
  2807. count !== null &&
  2808. count !== ''
  2809. ) {
  2810. this.$set(item, 'totalCount', count);
  2811. }
  2812. });
  2813. if (this.showSaleCount) {
  2814. this.changeCount();
  2815. }
  2816. },
  2817. /**
  2818. * 构造库存台账选择回显的一行。
  2819. * 不传 targetRow 时返回普通对象(尚未进入响应式数组,赋值零成本)。
  2820. */
  2821. buildStockLedgerRow(item, goodsMap, key, targetRow) {
  2822. let row = targetRow || JSON.parse(JSON.stringify(this.defaultForm));
  2823. row.key = key;
  2824. let parasm = row;
  2825. this.$set(parasm, 'customerMark', this.customerMark);
  2826. this.$set(parasm, 'saleCount', item.saleCount);
  2827. this.$set(parasm, 'productId', item.productId);
  2828. this.$set(parasm, 'categoryName', item.productName);
  2829. this.$set(parasm, 'productCategoryId', item.categoryLevelId);
  2830. this.$set(parasm, 'productBrand', item.productBrand);
  2831. this.$set(parasm, 'productCategoryName', item.categoryLevelPath);
  2832. this.$set(parasm, 'productCode', item.productCode);
  2833. this.$set(parasm, 'productName', item.productName);
  2834. this.$set(parasm, 'modelType', item.categoryModel);
  2835. this.$set(parasm, 'warehouseId', item.warehouseId);
  2836. this.$set(parasm, 'warehouseName', item.warehouseName);
  2837. if (this.showStockCount) {
  2838. this.$set(parasm, 'stockCountBase', this.getStockCount(item));
  2839. }
  2840. this.$set(
  2841. parasm,
  2842. 'availableCountBase',
  2843. this.showStockCount
  2844. ? this.getAvailableStockCount(item)
  2845. : item.measureQuantity
  2846. );
  2847. this.$set(parasm, 'measuringUnit', item.measuringUnit);
  2848. this.$set(parasm, 'specification', item.specification);
  2849. this.$set(parasm, 'weightUnit', item.weightUnit);
  2850. this.$set(parasm, 'singleWeight', item.weight);
  2851. this.$set(parasm, 'pricingWay', 1);
  2852. this.$set(parasm, 'arrivalWay', item.arrivalWay || 1);
  2853. this.$set(parasm, 'batchNo', item.batchNo);
  2854. this.$set(parasm, 'systemBatchNo', item.systemBatchNo);
  2855. this.$set(parasm, 'imgCode', '');
  2856. this.$set(parasm, 'produceType', item.componentAttribute);
  2857. this.$set(parasm, 'reelSpecification', item.reelSpecification);
  2858. this.$set(
  2859. parasm,
  2860. 'taxRate',
  2861. item.taxRate !== undefined && item.taxRate !== null
  2862. ? item.taxRate
  2863. : this.isContractBook
  2864. ? this.defTaxRate
  2865. : this.isTaxRate == 1
  2866. ? this.defTaxRate
  2867. : undefined
  2868. );
  2869. let goodsData = goodsMap.get(item.productId) || [];
  2870. goodsData = goodsData.filter((goods) => goods.approvalStatus == 2);
  2871. if (goodsData?.length) {
  2872. let goodsPriceList = goodsData[0]?.goodsPriceList[0];
  2873. this.$set(parasm, 'goodsId', goodsData[0].id);
  2874. this.$set(parasm, 'goodsPriceId', goodsPriceList.id);
  2875. this.$set(parasm, 'goodsPriceType', goodsPriceList.priceType);
  2876. this.$set(parasm, 'singlePrice', goodsPriceList.unitPrice || 0);
  2877. this.$set(
  2878. parasm,
  2879. 'notaxSinglePrice',
  2880. goodsPriceList.excludeTaxPrice
  2881. );
  2882. this.$set(parasm, 'taxRate', goodsPriceList.taxRate);
  2883. this.$set(parasm, 'discountSinglePrice', goodsPriceList.unitPrice);
  2884. this.$set(parasm, 'totalCount', '');
  2885. } else {
  2886. this.$set(parasm, 'singlePrice', 0);
  2887. }
  2888. this.$set(parasm, 'modelKey', item.modelKey?.split(',')?.[0] || '');
  2889. this.$set(parasm, 'colorKey', item.colorKey?.split(',')?.[0] || '');
  2890. if (this.isSupplier) {
  2891. this.$set(
  2892. parasm,
  2893. 'entrustedEnterpriseIdList',
  2894. item.entrustedEnterpriseIdList
  2895. );
  2896. this.$set(parasm, 'entrustedEnterpriseId', item.entrustedEnterpriseId);
  2897. }
  2898. this.$set(
  2899. parasm,
  2900. 'packageDispositionList',
  2901. item.packageDispositionList
  2902. );
  2903. if (item.packageDispositionList?.length) {
  2904. this.$set(
  2905. parasm,
  2906. this.countObj.unitIdKey,
  2907. item.packageDispositionList[0].id
  2908. );
  2909. this.$set(
  2910. parasm,
  2911. this.countObj.unitKey,
  2912. item.packageDispositionList[0].conversionUnit
  2913. );
  2914. }
  2915. if (item.purchaseOrigins?.length > 0) {
  2916. item.purchaseOrigins = item.purchaseOrigins.map((val) => val + '');
  2917. }
  2918. this.$set(parasm, 'provenance', item.purchaseOrigins || []);
  2919. // 初始化生产加工特有字段及未税小计
  2920. // 当quoteType为2时,数量默认设置为1
  2921. if (this.quoteType === 2) {
  2922. this.processSetValue(parasm);
  2923. }
  2924. return parasm;
  2925. },
  2926. /**
  2927. * 批量查询物品对应的商品信息。
  2928. * 按物品ID去重后并行请求,替代逐行 await 的串行查询,
  2929. * 同一物品选中多行(多批次)时只请求一次。
  2930. */
  2931. async loadGoodsByCategoryIds(list) {
  2932. const goodsMap = new Map();
  2933. const productIds = [
  2934. ...new Set(
  2935. (list || []).map((item) => item.productId).filter((id) => id)
  2936. )
  2937. ];
  2938. await Promise.all(
  2939. productIds.map(async (productId) => {
  2940. const goodsList = await getGoodsByCategoryId(productId);
  2941. goodsMap.set(productId, goodsList || []);
  2942. })
  2943. );
  2944. return goodsMap;
  2945. },
  2946. remove(index) {
  2947. if (this.tableBusy) return;
  2948. this.form.datasource.splice(index, 1);
  2949. this.setSort();
  2950. this.changeAll();
  2951. },
  2952. // 重新排序
  2953. setSort() {
  2954. this.form.datasource.forEach((n, index) => {
  2955. n.key = index + 1;
  2956. });
  2957. },
  2958. // 添加临时产品
  2959. handlAdd() {
  2960. if (this.tableBusy) return;
  2961. let item = JSON.parse(JSON.stringify(this.defaultForm));
  2962. item.key = this.form.datasource.length + 1;
  2963. item.customerMark = this.customerMark;
  2964. item.customerExpectDeliveryDeadline = dayjs(new Date()).format(
  2965. 'YYYY-MM-DD'
  2966. );
  2967. // 初始化生产加工特有字段及未税小记
  2968. // 当quoteType为2时,数量默认设置为1
  2969. if (this.quoteType === 2) {
  2970. this.processSetValue(item);
  2971. }
  2972. this.form.datasource.push(item);
  2973. },
  2974. processSetValue(parasm) {
  2975. this.$set(parasm, this.countObj.countKey, 1);
  2976. this.$set(parasm, 'thickNess', '');
  2977. this.$set(parasm, 'squareNumber', '');
  2978. this.$set(parasm, 'processingFeeBeforeTax', '');
  2979. this.$set(parasm, 'packagingFeeNotTaxed', '');
  2980. this.$set(parasm, 'transportationFeeWithoutTax', '');
  2981. this.$set(parasm, 'extraTax', 13);
  2982. this.$set(parasm, 'quotationSubtotalBeforeTax', '0.00');
  2983. this.$set(parasm, 'quotationSubtotalTax', '');
  2984. },
  2985. validateForm(callback) {
  2986. this.$refs.form.validate((valid, obj) => {
  2987. if (obj) {
  2988. console.log('validateForm', obj);
  2989. let messages = Object.keys(obj).map((key) => obj[key][0]);
  2990. if (messages.length > 0) {
  2991. this.$message.warning(messages[0].message);
  2992. }
  2993. }
  2994. callback(valid);
  2995. });
  2996. },
  2997. goodsPriceTypeChange(row, index) {
  2998. if (row.goodsId) {
  2999. getGoodsPriceByCondition({
  3000. id: row.goodsId,
  3001. priceType: row.goodsPriceType
  3002. }).then((res) => {
  3003. if (res.id) {
  3004. this.$set(this.form.datasource[index], 'goodsPriceId', res.id);
  3005. this.$set(
  3006. this.form.datasource[index],
  3007. 'goodsPriceType',
  3008. res.priceType
  3009. );
  3010. this.$set(
  3011. this.form.datasource[index],
  3012. 'singlePrice',
  3013. res.unitPrice
  3014. );
  3015. this.$set(
  3016. this.form.datasource[index],
  3017. 'notaxSinglePrice',
  3018. res.excludeTaxPrice
  3019. );
  3020. this.$set(this.form.datasource[index], 'taxRate', res.taxRate);
  3021. this.$set(
  3022. this.form.datasource[index],
  3023. 'discountSinglePrice',
  3024. res.unitPrice
  3025. );
  3026. }
  3027. this.changeCount(row, index);
  3028. });
  3029. }
  3030. },
  3031. save() {
  3032. this.$emit('save');
  3033. },
  3034. // 列头拖拽结束 - 保存列宽到本地缓存
  3035. handleHeaderDragend(newWidth, oldWidth, column) {
  3036. if (!column.property) return;
  3037. const widths = this.getStorage(this.cacheKeyUrl + 'Widths') || {};
  3038. widths[column.property] = newWidth;
  3039. this.setStorage(this.cacheKeyUrl + 'Widths', widths);
  3040. }
  3041. }
  3042. };
  3043. </script>
  3044. <style lang="scss" scoped>
  3045. .headbox {
  3046. display: flex;
  3047. justify-content: space-between;
  3048. align-items: center;
  3049. .amount {
  3050. font-size: 14px;
  3051. font-weight: bold;
  3052. padding-right: 30px;
  3053. }
  3054. }
  3055. .time-form .el-form-item {
  3056. margin-bottom: 0 !important;
  3057. }
  3058. ::v-deep .period {
  3059. display: flex;
  3060. .borderleftnone {
  3061. .el-input--medium .el-input__inner {
  3062. border-top-right-radius: 0;
  3063. border-bottom-right-radius: 0;
  3064. }
  3065. }
  3066. .borderrightnone {
  3067. .el-input--medium .el-input__inner {
  3068. border-top-left-radius: 0;
  3069. border-bottom-left-radius: 0;
  3070. }
  3071. }
  3072. }
  3073. ::v-deep .time-form tbody > tr:hover > td {
  3074. background-color: transparent !important;
  3075. }
  3076. ::v-deep .time-form .el-table tr {
  3077. background-color: #ffffff;
  3078. }
  3079. .inventory-table-wrap {
  3080. position: relative;
  3081. }
  3082. .table-loading-mask {
  3083. position: absolute;
  3084. top: 0;
  3085. left: 0;
  3086. right: 0;
  3087. bottom: 0;
  3088. z-index: 10;
  3089. display: flex;
  3090. flex-direction: column;
  3091. align-items: center;
  3092. justify-content: center;
  3093. background-color: rgba(255, 255, 255, 0.75);
  3094. }
  3095. .table-loading-mask .el-icon-loading {
  3096. font-size: 30px;
  3097. color: #409eff;
  3098. }
  3099. .table-loading-text {
  3100. margin-top: 10px;
  3101. font-size: 13px;
  3102. color: #909399;
  3103. }
  3104. /* 批量追加期间禁止单元格交互:只关掉 .cell 的指针事件,
  3105. 滚轮事件仍会落在滚动容器上,所以表格照常可以滚动 */
  3106. ::v-deep .time-form-busy .el-table__body-wrapper .cell,
  3107. ::v-deep .time-form-busy .el-table__fixed-body-wrapper .cell {
  3108. pointer-events: none;
  3109. }
  3110. ::v-deep .time-form-busy .el-table__body-wrapper,
  3111. ::v-deep .time-form-busy .el-table__fixed-body-wrapper {
  3112. cursor: progress;
  3113. }
  3114. .pricebox {
  3115. display: flex;
  3116. justify-content: flex-start;
  3117. align-items: center;
  3118. font-weight: bold;
  3119. }
  3120. :deep(.el-input-group__append) {
  3121. padding: 0;
  3122. }
  3123. </style>