goodsAttr.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699
  1. <template>
  2. <el-form size="mini" label-width="100px" :rules="attrRules">
  3. <el-form-item label="商品规格" prop="attr">
  4. <div class="attr-container">
  5. <el-form
  6. class="attr-wrap"
  7. v-for="(item, index) in Attr"
  8. ref="attr"
  9. :key="index"
  10. :model="item"
  11. size="mini"
  12. label-width="100px"
  13. >
  14. <el-form-item label="规格名:" class="attr-name">
  15. <el-input
  16. v-model="item.name"
  17. maxlength="8"
  18. placeholder="请输入"
  19. :disabled="locking"
  20. />
  21. <i class="el-icon-circle-close" @click="deleteAttr(index)"></i>
  22. </el-form-item>
  23. <el-form-item label="规格值:" class="attr-detail">
  24. <div
  25. v-for="(item1, index1) in item.value"
  26. :key="index1"
  27. class="detail-wrap"
  28. >
  29. <el-input
  30. v-model="item.value[index1]"
  31. maxlength="16"
  32. placeholder="请输入"
  33. :disabled="locking"
  34. />
  35. <i
  36. class="el-icon-circle-close"
  37. @click="deleteAttrValue(index, index1)"
  38. ></i>
  39. </div>
  40. <el-button
  41. type="text"
  42. :size="inputSize"
  43. @click.native="addAttrValue(index)"
  44. class="text-btn"
  45. >添加规格值</el-button
  46. >
  47. </el-form-item>
  48. </el-form>
  49. <div class="addAttr">
  50. <el-button
  51. :size="inputSize"
  52. @click.native="addAttr"
  53. :class="{ gray: Attr.length >= 3, 'cancel-btn': true }"
  54. >添加规格</el-button
  55. >
  56. <el-button
  57. v-if="!locking"
  58. :size="inputSize"
  59. type="primary"
  60. @click.native="buildDetail"
  61. class="confirm-btn"
  62. >生成明细</el-button
  63. >
  64. <el-button
  65. v-else-if="locking"
  66. :size="inputSize"
  67. type="primary"
  68. @click.native="resolveAttr"
  69. class="confirm-btn"
  70. >解锁规格</el-button
  71. >
  72. <p class="tips" v-if="Attr.length > 0">
  73. 提示:编辑完成后,请点击“生成规格明细”填写相关明细,修改规格将会清空明细
  74. </p>
  75. </div>
  76. </div>
  77. </el-form-item>
  78. <el-form-item label="规格明细" v-if="tableData.length > 0">
  79. <table border="1">
  80. <tr>
  81. <th v-for="(item, index) in Attr" :key="index">{{ item.name }}</th>
  82. <!-- <th>
  83. <span>*</span> 成本价/元
  84. </th> -->
  85. <th><span></span> 规格主图</th>
  86. <th><span></span> 价格/元</th>
  87. <th><span></span> 库存</th>
  88. <th><span></span> 编码</th>
  89. <!-- <th><span></span> 供应链skuID</th> -->
  90. <!-- <th><span></span> 进价</th> -->
  91. </tr>
  92. <tr v-for="(item, index) in tableData" :key="index">
  93. <td v-for="(item1, index1) in item.attribute" :key="index1">
  94. {{ item1 }}
  95. </td>
  96. <!-- <td>
  97. <el-input v-model="item.cost" type="number" />
  98. </td> -->
  99. <td class="upImg">
  100. <img v-if="item.pic" :src="item.pic || ''" class="mainImg" />
  101. <div class="add-img" v-else>+</div>
  102. <input type="file" class="file" :name="index" @change="changeImg" />
  103. </td>
  104. <td>
  105. <el-input v-model="item.price" type="number" @blur="priceFmt" />
  106. </td>
  107. <td>
  108. <el-input v-model="item.stock" type="number" />
  109. </td>
  110. <td>
  111. <el-input v-model="item.code" />
  112. </td>
  113. <!-- <td>
  114. <el-input v-model="item.chainId" type="text" />
  115. </td> -->
  116. <!-- <td>
  117. <el-input v-model="item.cost" type="number" />
  118. </td> -->
  119. </tr>
  120. <tr>
  121. <td :colspan="col">
  122. <div class="batch">
  123. <p>批量设置</p>
  124. <div v-if="batchEdit.type">
  125. <el-input
  126. v-model="batchEdit.value"
  127. type="number"
  128. oninput="value=value.length>9?value.slice(0,9):value"
  129. />
  130. <button class="text-btn" @click="saveBatch">保存</button>
  131. <button class="text-btn" @click="cancelBatch">取消</button>
  132. </div>
  133. <div class="batchBtn" v-else>
  134. <button
  135. v-for="(item, i) in batchList"
  136. :key="i"
  137. class="text-btn"
  138. @click="editBatch(i, item.value)"
  139. >
  140. {{ item.name }}
  141. </button>
  142. <!-- <div class="batchImg">
  143. <input type="file" class="file" @change="batchImg" />
  144. <button>规格主图</button>
  145. </div> -->
  146. </div>
  147. </div>
  148. </td>
  149. </tr>
  150. </table>
  151. </el-form-item>
  152. <el-form-item label="规格明细" v-else>
  153. <p style="line-height: 2">无</p>
  154. </el-form-item>
  155. <!-- <el-form-item label="划线价">
  156. <el-input v-model="oldPrice" type="number" />
  157. </el-form-item> -->
  158. <el-form-item label="商品原价">
  159. <el-input v-model="hypocrisy" type="number" />
  160. </el-form-item>
  161. </el-form>
  162. </template>
  163. <script type="text/ecmascript-6">
  164. export default {
  165. name: 'goodsAttr',
  166. props: {
  167. attrSku: {
  168. type: Array
  169. },
  170. skuTable: {
  171. type: Array
  172. },
  173. OldPrice: {
  174. type: Number
  175. },
  176. sales: {
  177. type: Number
  178. }
  179. },
  180. data() {
  181. return {
  182. locking: false, // 锁定规
  183. inputSize: 'mini',
  184. oldPrice: this.OldPrice, // 划线价
  185. hypocrisy: this.sales, // 划线价
  186. defaultImg: this.$Public.defaultImg, // 默认规格图
  187. ruleForm: {
  188. name: ''
  189. },
  190. attrRules: {
  191. attr: [
  192. {
  193. required: true,
  194. message: '请输入商品规格值'
  195. }
  196. ]
  197. }, // 基础校验
  198. batchList: [
  199. // {
  200. // name: '成本价',
  201. // value: 'cost'
  202. // },
  203. {
  204. name: '价格',
  205. value: 'price'
  206. },
  207. {
  208. name: '库存',
  209. value: 'stock'
  210. }
  211. ], // 批量设置列表,静态数据
  212. batchEdit: {
  213. name: '', // 批量编辑字段
  214. type: false,
  215. value: '' // 批量编辑数值
  216. },
  217. col: 0, // 表格列数,批量排版
  218. Attr: [
  219. {
  220. name: '',
  221. value: ['']
  222. }
  223. ], // 规格列表
  224. tableData: [], // 规格明细表格
  225. attribute: {}, // tableData单list的attribute信息
  226. info: {
  227. stock: '',
  228. code: '',
  229. pic: '',
  230. price: '',
  231. // cost: '',
  232. attribute: {}
  233. } // tableData单list信息
  234. };
  235. },
  236. created() {},
  237. computed: {},
  238. methods: {
  239. changeImg(e) {
  240. // 上传规格图片
  241. let index = e.target.name;
  242. let file = e.target.files[0];
  243. this.request.upPic({ prefix: 'other', file: file }).then(
  244. (res) => {
  245. this.tableData[index].pic = res;
  246. this.tableData = [...this.tableData];
  247. },
  248. (res) => {}
  249. );
  250. },
  251. batchImg(e) {
  252. // 批量上传图片
  253. let file = e.target.files[0];
  254. this.request.upPic({ type: 3, file: file }).then(
  255. (res) => {
  256. this.tableData.forEach((item) => {
  257. item.pic = res;
  258. });
  259. },
  260. (res) => {}
  261. );
  262. },
  263. editBatch(i, name) {
  264. // 批量设置
  265. this.batchEdit.name = name;
  266. this.batchEdit.type = true;
  267. },
  268. saveBatch() {
  269. this.batchEdit.type = false;
  270. // 遍历tableData,批量改数据
  271. this.tableData.forEach((item, index) => {
  272. // 价格和分润结构不同
  273. let name = this.batchEdit.name;
  274. // 分公司和能量站最多4.2折,价格不能小于0.03
  275. // if (name === 'price') {
  276. // if (this.batchEdit.value && +this.batchEdit.value < 0.03) {
  277. // this.$message({
  278. // message: '商品价格请勿小于0.03',
  279. // type: 'warning'
  280. // });
  281. // this.batchEdit.value = '';
  282. // }
  283. // }
  284. item[name] = this.batchEdit.value;
  285. });
  286. // 清空input
  287. this.batchEdit.value = '';
  288. },
  289. cancelBatch() {
  290. this.batchEdit.type = false;
  291. },
  292. addAttr() {
  293. // 添加规格
  294. if (this.locking) {
  295. return;
  296. }
  297. if (this.Attr.length >= 3) {
  298. this.$message({
  299. message: '至多添加三个规格',
  300. type: 'warning'
  301. });
  302. return false;
  303. }
  304. let obj = {
  305. name: '',
  306. value: ['']
  307. };
  308. this.Attr.push(obj);
  309. },
  310. deleteAttr(index) {
  311. // 删除规格
  312. if (this.locking) {
  313. return;
  314. }
  315. this.Attr.splice(index, 1);
  316. },
  317. addAttrValue(index) {
  318. // 添加规格值
  319. if (this.locking) {
  320. return;
  321. }
  322. let val = '';
  323. this.Attr[index].value.push(val);
  324. },
  325. deleteAttrValue(i, j) {
  326. if (this.locking) {
  327. return;
  328. }
  329. // 删除规格值
  330. this.Attr[i].value.splice(j, 1);
  331. },
  332. resolveAttr() {
  333. // 解锁规格
  334. this.$confirm('此操作将清空规格明细, 是否继续?', '提示', {
  335. confirmButtonText: '确定',
  336. cancelButtonText: '取消',
  337. type: 'warning'
  338. })
  339. .then(() => {
  340. // 清空tableData/info/attribute
  341. this.tableData = [];
  342. this.attribute = {};
  343. this.info = {
  344. stock: '',
  345. code: '',
  346. pic: '',
  347. price: '',
  348. // cost: '',
  349. attribute: {}
  350. };
  351. this.locking = false;
  352. })
  353. .catch(() => {
  354. return false;
  355. });
  356. },
  357. getTable(i, len) {
  358. // 根据选择的attr生成规格明细列表
  359. this.Attr[i].value.forEach((item) => {
  360. this.$set(this.attribute, this.Attr[i].name, item);
  361. if (i === len - 1) {
  362. this.$set(this.info, 'attribute', { ...this.attribute });
  363. this.tableData.push({ ...this.info });
  364. return false;
  365. }
  366. this.getTable(i + 1, len);
  367. });
  368. },
  369. buildDetail() {
  370. let isPass = true;
  371. if (this.Attr.length <= 0) {
  372. this.$message({
  373. message: '请添加规格',
  374. type: 'warning'
  375. });
  376. return false;
  377. }
  378. this.Attr.forEach((item, i) => {
  379. if (item.name === '') {
  380. this.$message({
  381. message: '请填写规格名',
  382. type: 'warning'
  383. });
  384. isPass = false;
  385. }
  386. let value = item.value[0];
  387. if (value === '' || typeof value === 'undefined') {
  388. this.$message({
  389. message: '请填写规格值',
  390. type: 'warning'
  391. });
  392. isPass = false;
  393. }
  394. });
  395. if (!isPass) {
  396. return false;
  397. }
  398. // 锁定规格
  399. this.locking = true;
  400. this.col = this.Attr.length + 7;
  401. // 生成规格明细
  402. let len = this.Attr.length;
  403. this.getTable(0, len);
  404. },
  405. priceFmt() {
  406. // 分公司和能量站最多4.2折,价格不能小于0.03
  407. // this.tableData.map((it, index) => {
  408. // if (it.price && +it.price < 0.03) {
  409. // this.$message({
  410. // message: '商品价格请勿小于0.03',
  411. // type: 'warning'
  412. // });
  413. // this.tableData[index].price = '';
  414. // }
  415. // });
  416. }
  417. },
  418. watch: {
  419. tableData: {
  420. handler(val) {
  421. console.log(val, 'aaaaaaaaaaaaaa');
  422. // val.map((it, index) => {
  423. // if (it.price && +it.price < 0.03) {
  424. // this.$message({
  425. // message: '商品价格请勿小于0.03',
  426. // type: 'warning'
  427. // });
  428. // this.tableData[index].price = '';
  429. // }
  430. // });
  431. // 编辑规格明细时,向父组件传规格明细,库存
  432. this.$emit('editAttr', val);
  433. },
  434. deep: true
  435. },
  436. OldPrice(val) {
  437. this.oldPrice = val;
  438. },
  439. oldPrice(val) {
  440. // 编辑划线价
  441. this.$emit('getOldPrice', val);
  442. },
  443. sales(val) {
  444. this.hypocrisy = val;
  445. },
  446. hypocrisy(val) {
  447. // 编辑划线价
  448. this.$emit('getSales', val);
  449. },
  450. skuTable(val) {
  451. this.tableData = val;
  452. },
  453. attrSku: function (val) {
  454. this.Attr = [];
  455. // 根据根据tableData是否为空判断是上传还是编辑
  456. if (this.tableData.length <= 0) {
  457. // 新品页面时,根据传递的attrSku,计算Attr的规格名
  458. val.forEach((item) => {
  459. let list = {
  460. name: item,
  461. value: ['']
  462. };
  463. this.Attr.push(list);
  464. });
  465. } else {
  466. // 编辑页面时,计算Attr的规格名,根据tableData,推算Attr的规格值,并锁住
  467. val.forEach((item) => {
  468. let list = {
  469. name: item,
  470. value: []
  471. };
  472. this.Attr.push(list);
  473. });
  474. this.tableData.forEach((item) => {
  475. let attribute = item.attribute;
  476. for (let i in attribute) {
  477. this.Attr.forEach((item) => {
  478. if (item.name === i) {
  479. item.value.push(attribute[i]);
  480. }
  481. });
  482. }
  483. });
  484. this.Attr.forEach((item) => {
  485. item.value = [...new Set(item.value)];
  486. }); // value去重
  487. this.locking = true;
  488. this.col = this.Attr.length + 7;
  489. }
  490. }
  491. }
  492. };
  493. </script>
  494. <style lang="stylus" rel="stylesheet/stylus" scoped>
  495. @import '~assets/main.styl';
  496. .el-input {
  497. width: 100px;
  498. }
  499. .batch {
  500. flex-x(flex-satrt);
  501. padding: 0 16px;
  502. word(14px, #333);
  503. button {
  504. color: blue;
  505. margin-left: 10px;
  506. }
  507. p {
  508. word(14px, #333);
  509. margin-right: 15px;
  510. }
  511. .batchBtn {
  512. flex-x(flex-satrt);
  513. .batchImg {
  514. position: relative;
  515. input {
  516. opacity: 0;
  517. position: absolute;
  518. left: 0;
  519. top: 0;
  520. width: 60px;
  521. height: 40px;
  522. line-height: 40px;
  523. margin-left: 10px;
  524. }
  525. }
  526. }
  527. }
  528. .upImg {
  529. position: relative;
  530. vertical-align: middle;
  531. height: 100%;
  532. .add-img{
  533. width: 40px;
  534. height: 40px;
  535. border: 1px solid #e8e8e8;
  536. background: #F6F6F6;
  537. box-sizing: border-box;
  538. position: absolute;
  539. left: 0;
  540. top: 0;
  541. right: 0;
  542. bottom: 0;
  543. margin: auto;
  544. font-size: 24px;
  545. color: #999;
  546. }
  547. .mainImg {
  548. width: 40px;
  549. height: 40px;
  550. position: absolute;
  551. left: 0;
  552. top: 0;
  553. right: 0;
  554. bottom: 0;
  555. margin: auto;
  556. }
  557. input {
  558. width: 40px;
  559. height: 40px;
  560. opacity: 0;
  561. position: absolute;
  562. left: 0;
  563. top: 0;
  564. right: 0;
  565. bottom: 0;
  566. margin: auto;
  567. }
  568. }
  569. .attr-container {
  570. padding: 10px;
  571. box-sizing: border-box;
  572. border();
  573. .addAttr {
  574. flex-x(flex-start);
  575. width: 98%;
  576. height: 40px;
  577. line-height: 40px;
  578. background: bk;
  579. padding-left: 20px;
  580. .tips {
  581. margin-left: 20px;
  582. }
  583. .gray {
  584. color: gray9;
  585. background: grayE;
  586. border();
  587. }
  588. }
  589. .attr-wrap {
  590. .el-input {
  591. width: 160px;
  592. margin-bottom: 5px;
  593. }
  594. .attr-name {
  595. position: relative;
  596. width: 100%;
  597. height: 40px;
  598. padding-top: 5px;
  599. box-sizing: border-box;
  600. background: bk;
  601. i {
  602. position: absolute;
  603. right: 20px;
  604. line-height: 30px;
  605. color: gray9;
  606. }
  607. .el-button--mini {
  608. margin-left: 10px;
  609. }
  610. }
  611. .attr-detail {
  612. .detail-wrap {
  613. position: relative;
  614. width: 160px;
  615. display: inline-block;
  616. margin-right: 10px;
  617. i {
  618. position: absolute;
  619. right: 10px;
  620. line-height: 30px;
  621. color: #999;
  622. top: 0;
  623. }
  624. }
  625. }
  626. .el-autocomplete {
  627. width: 200px;
  628. }
  629. }
  630. }
  631. table {
  632. border: 1px solid border-color;
  633. width: 100%;
  634. tr {
  635. height: 40px;
  636. line-height: 40px;
  637. text-align: left;
  638. }
  639. th {
  640. span {
  641. color: red;
  642. }
  643. text-align: center;
  644. font-weight: normal;
  645. color: gray3;
  646. // padding-left: 20px;
  647. }
  648. td {
  649. // padding-left: 20px;
  650. height: 54px;
  651. text-align: center;
  652. bor-top();
  653. bor-bottom();
  654. color: gray9;
  655. width: 80px;
  656. }
  657. }
  658. .confirm-btn {
  659. confirm-btn();
  660. }
  661. .cancel-btn {
  662. cancel-btn();
  663. }
  664. .text-btn {
  665. color: btn-color !important;
  666. }
  667. </style>