select-sku.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. <template>
  2. <div>
  3. <el-popover
  4. placement="bottom-start"
  5. width="325"
  6. trigger="click"
  7. v-model="visible"
  8. @show="show"
  9. >
  10. <div class="popover-sku-box">
  11. <div class="sku-item" v-for="(skuLine, key) in skuGroup" :key="key">
  12. <div class="sku-name">{{ key }}</div>
  13. <div class="sku-value">
  14. <div
  15. class="item"
  16. @click="toChooseItem(skuLineItem, key, $event)"
  17. :class="[
  18. selectedProp.find(
  19. el => el.key === key && el.value === skuLineItem
  20. )
  21. ? 'active'
  22. : '',
  23. isSkuLineItemNotOptional(
  24. allProperties,
  25. selectedPropObj,
  26. key,
  27. skuLineItem,
  28. propKeys
  29. )
  30. ? 'not-optional'
  31. : ''
  32. ]"
  33. v-for="(skuLineItem, index) in skuLine"
  34. :key="index">
  35. {{ skuLineItem }}
  36. </div>
  37. </div>
  38. </div>
  39. <div class="footer">
  40. <div class="red btn" :class="{gary: !findSku}" @click="submit">{{ $t("determine") }}</div>
  41. <div class="cancal btn" @click="close">{{ $t("cancel") }}</div>
  42. </div>
  43. </div>
  44. <div class="prod-sku" slot="reference">
  45. {{ skuName }}
  46. <img src="../assets/images/down.png" alt="" />
  47. </div>
  48. </el-popover>
  49. </div>
  50. </template>
  51. <script>
  52. export default {
  53. props: {
  54. skuName: {
  55. type: String,
  56. default: ''
  57. },
  58. skuList: {
  59. type: Array,
  60. default() {
  61. return []
  62. }
  63. },
  64. skuId: {
  65. type: Number,
  66. default: 0
  67. },
  68. prodId: {
  69. type: Number,
  70. default: 0
  71. },
  72. comboId: {
  73. type: Number,
  74. default: 0
  75. }
  76. },
  77. data () {
  78. return {
  79. visible: false,
  80. defaultSku: '',
  81. // 购物车中修改的sku列表
  82. cartChangeSkuList: [],
  83. propKeys: [],
  84. selectedPropObj: {},
  85. skuGroup: {},
  86. allProperties: [],
  87. findSku: false
  88. }
  89. },
  90. methods: {
  91. handleVisible() {
  92. this.visible = true
  93. },
  94. isSelect(specVal, specName) {
  95. let list = this.defaultSku.properties.split(";")
  96. for(let i = 0;i< list.length;i++) {
  97. let spec = list[i].split(":")
  98. if(spec[0] === specName && spec[1] === specVal) {
  99. return true
  100. }
  101. }
  102. return false
  103. },
  104. skuSelect(specVal, specName) {
  105. const skuList = this.cartChangeSkuList.length ? this.cartChangeSkuList : this.skuList
  106. let list = this.defaultSku.properties.split(";")
  107. for(let i = 0;i< list.length;i++) {
  108. let spec = list[i].split(":")
  109. if(spec[0] === specName) {
  110. spec[1] = specVal
  111. spec = spec.join(":")
  112. list[i] = spec
  113. break
  114. }
  115. }
  116. const sku = list.join(";")
  117. for(let i = 0; i<skuList.length; i++) {
  118. if(skuList[i].properties === sku) {
  119. this.defaultSku = skuList[i]
  120. break;
  121. }
  122. }
  123. },
  124. close() {
  125. this.visible = false
  126. },
  127. show() {
  128. if (!this.skuList.length) {
  129. this.getSkuListByProdId()
  130. return
  131. }
  132. this.groupSkuProp()
  133. },
  134. /**
  135. * 组装SKU
  136. */
  137. groupSkuProp: function() {
  138. const skuList = this.skuList.length ? this.skuList : this.cartChangeSkuList
  139. if (skuList.length == 1 && !skuList[0].properties) {
  140. this.defaultSku = skuList[0]
  141. return
  142. }
  143. var skuGroup = {}
  144. var allProperties = []
  145. var propKeys = []
  146. var selectedPropObj = {}
  147. var defaultSku = null
  148. for (var i = 0; i < skuList.length; i++) {
  149. var isDefault = false
  150. if (!defaultSku && skuList[i].skuId == this.skuId) {
  151. defaultSku = skuList[i]
  152. isDefault = true
  153. }
  154. var properties = skuList[i].properties //版本:公开版;颜色:金色;内存:64GB
  155. allProperties.push(properties)
  156. var propList = properties.split(';') // ["版本:公开版","颜色:金色","内存:64GB"]
  157. for (var j = 0; j < propList.length; j++) {
  158. var propval = propList[j].split(':') //["版本","公开版"]
  159. var props = skuGroup[propval[0]] //先取出 版本对应的值数组
  160. //如果当前是默认选中的sku,把对应的属性值 组装到selectedProp
  161. if (isDefault) {
  162. propKeys.push(propval[0])
  163. selectedPropObj[propval[0]] = propval[1]
  164. }
  165. if (props == undefined) {
  166. props = [] //假设还没有版本,新建个新的空数组
  167. props.push(propval[1]) //把 "公开版" 放进空数组
  168. } else {
  169. if (props.indexOf(propval[1]) === -1) {
  170. //如果数组里面没有"公开版"
  171. props.push(propval[1]) //把 "公开版" 放进数组
  172. }
  173. }
  174. skuGroup[propval[0]] = props //最后把数据 放回版本对应的值
  175. }
  176. }
  177. this.defaultSku = defaultSku
  178. this.propKeys = propKeys
  179. this.selectedPropObj = selectedPropObj
  180. this.parseSelectedObjToVals(skuList)
  181. this.skuGroup = skuGroup
  182. this.allProperties = allProperties
  183. },
  184. /**
  185. * 将已选的 {key:val,key2:val2}转换成 [val,val2]
  186. */
  187. parseSelectedObjToVals: function() {
  188. const skuList = this.cartChangeSkuList.length ? this.cartChangeSkuList : this.skuList
  189. var selectedPropObj = this.selectedPropObj
  190. var selectedProperties = ''
  191. var selectedProp = []
  192. for (var key in selectedPropObj) {
  193. // selectedProp.push(selectedPropObj[key]);
  194. selectedProp.push({ key: key, value: selectedPropObj[key] })
  195. selectedProperties += key + ':' + selectedPropObj[key] + ';'
  196. }
  197. selectedProperties = selectedProperties.substring(
  198. 0,
  199. selectedProperties.length - 1
  200. )
  201. this.selectedProp = selectedProp
  202. this.selectedProperties = selectedProperties
  203. this.selectedPropObj = selectedPropObj
  204. var findSku = false
  205. for (var i = 0; i < skuList.length; i++) {
  206. // 解决排序问题导致无法匹配
  207. if (
  208. this.compareArray(
  209. selectedProperties.split(';').sort(),
  210. skuList[i].properties.split(';').sort()
  211. )
  212. ) {
  213. findSku = true
  214. this.defaultSku = skuList[i]
  215. break
  216. }
  217. }
  218. this.findSku = findSku
  219. },
  220. /**
  221. * 比较两个数组中的元素是否相等
  222. * @param a1 第一个数组
  223. * @param a2 第二个数组
  224. * @return boolean 两个数组中的元素都相等则返回 true,反之返回 false
  225. */
  226. compareArray(a1, a2) {
  227. if (!a1 || !a2) {
  228. return false
  229. }
  230. if (a1.length !== a2.length) {
  231. return false
  232. }
  233. for (var i = 0, n = a1.length; i < n; i++) {
  234. if (a1[i] !== a2[i]) {
  235. return false
  236. }
  237. }
  238. return true
  239. },
  240. /**
  241. * 判断当前的规格值 是否可以选
  242. */
  243. isSkuLineItemNotOptional(
  244. allProperties,
  245. selectedPropObj,
  246. key,
  247. item,
  248. propKeys
  249. ) {
  250. var selectedPropObj = Object.assign({}, selectedPropObj)
  251. var properties = ''
  252. selectedPropObj[key] = item
  253. for (var j = 0; j < propKeys.length; j++) {
  254. properties += propKeys[j] + ':' + selectedPropObj[propKeys[j]] + ';'
  255. }
  256. properties = properties.substring(0, properties.length - 1)
  257. for (var i = 0; i < allProperties.length; i++) {
  258. if (properties == allProperties[i]) {
  259. return false
  260. }
  261. }
  262. for (var i = 0; i < allProperties.length; i++) {
  263. if (allProperties[i].indexOf(item) >= 0) {
  264. return true
  265. }
  266. }
  267. return false
  268. },
  269. /**
  270. * 规格点击事件
  271. */
  272. toChooseItem(skuLineItem, key, event) {
  273. this.selectedPropObj[key] = skuLineItem
  274. this.parseSelectedObjToVals()
  275. },
  276. submit() {
  277. if (!this.findSku) return
  278. if(!this.defaultSku.stocks) {
  279. this.$message({
  280. message: this.$t('prodDetail.insufficientInventory'),
  281. type: 'warning',
  282. duration: 1000
  283. })
  284. return
  285. }
  286. this.close()
  287. this.$emit('getSku', this.defaultSku)
  288. },
  289. /**
  290. * 根据商品id获取skuList
  291. */
  292. getSkuListByProdId() {
  293. this.$axios.get(this.comboId ? '/combo/skuList' : '/prod/skuList', {
  294. params: {
  295. comboId: this.comboId,
  296. prodId: this.prodId
  297. }
  298. }).then(({ data }) => {
  299. this.cartChangeSkuList = data
  300. this.groupSkuProp()
  301. })
  302. }
  303. }
  304. }
  305. </script>
  306. <style scoped>
  307. .prod-sku {
  308. display: inline-block;
  309. font-size: 12px;
  310. color: #999999;
  311. cursor: pointer;
  312. }
  313. .prod-sku img {
  314. width: 8px;
  315. height: 4px;
  316. margin-left: 5px;
  317. margin-bottom: 2px;
  318. }
  319. .popover-sku-box {
  320. width: 311px;
  321. }
  322. .popover-sku-box .sku-item .sku-name{
  323. font-size: 12px;
  324. line-height: 12px;
  325. color: #999999;
  326. margin-bottom: 5px;
  327. }
  328. .popover-sku-box .sku-item .sku-value {
  329. margin-bottom: 10px;
  330. }
  331. .popover-sku-box .sku-item .sku-value .item {
  332. box-sizing: border-box;
  333. display: inline-block;
  334. font-size: 12px;
  335. color: #333333;
  336. min-width: 50px;
  337. height: 22px;
  338. line-height: 22px;
  339. padding: 0 8px;
  340. background: #FFFFFF;
  341. border: 1px solid #DDDDDD;
  342. cursor: pointer;
  343. margin-right: 10px;
  344. margin-bottom: 5px;
  345. user-select:none;
  346. }
  347. .popover-sku-box .sku-item .sku-value .item:last-child{
  348. margin-bottom: 0;
  349. }
  350. .popover-sku-box .sku-item .sku-value .item.active {
  351. border: 1px solid #E43130;
  352. }
  353. .popover-sku-box .sku-item .sku-value .item.not-optional {
  354. border: 1px dashed #bbb;
  355. color: #bbb;
  356. }
  357. .popover-sku-box .sku-item .sku-value .item.active.not-optional {
  358. border: 1px dashed #E43130;
  359. }
  360. .popover-sku-box .footer {
  361. margin-top: 15px;
  362. display: flex;
  363. }
  364. .popover-sku-box .footer .btn {
  365. box-sizing: border-box;
  366. width: 50px;
  367. height: 22px;
  368. border-radius: 11px;
  369. font-size: 12px;
  370. display: flex;
  371. align-items: center;
  372. justify-content: center;
  373. cursor: pointer;
  374. }
  375. .popover-sku-box .footer .btn.red {
  376. color: #fff;
  377. background: #E1251B;
  378. margin-right: 10px;
  379. }
  380. .popover-sku-box .footer .btn.gary {
  381. color: #999;
  382. background: #eee;
  383. }
  384. .popover-sku-box .footer .btn.gary:hover {
  385. cursor: not-allowed;
  386. }
  387. .popover-sku-box .footer .btn.cancal {
  388. background: #FFFFFF;
  389. border: 1px solid #DDDDDD;
  390. }
  391. </style>