MiniSpec.vue 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. <template>
  2. <scroll-view class="mini-spec" scroll-y="true" show-scrollbar="false">
  3. <view class="spec-item" v-for="(item,i) in demoData" :key="i">
  4. <view class="spec-title">{{ item }}</view>
  5. <!--多个选项-->
  6. <view class="spec-list">
  7. <view class="spec-value" v-for="(sku,index) in showSpec[item]" :key="index" @tap="chooseSpec(item, sku)"
  8. :class="[{active:selectedArr[item] === sku}]">
  9. <!-- wx:class="{{ { theme_main_bk:selectedArr[item] === sku , no:filters.noActive(notSelectable,sku)} }}" -->
  10. {{ sku }}
  11. </view>
  12. </view>
  13. </view>
  14. </scroll-view>
  15. </template>
  16. <script lang='ts'>
  17. import {
  18. Component,
  19. Prop,
  20. Watch,
  21. Emit,
  22. Vue
  23. } from 'vue-property-decorator';
  24. @Component({})
  25. export default class MiniSpec extends Vue {
  26. proContent: any = null;
  27. showSpec: any = {}; // 用于展示的规格选项
  28. reSku: Array < any >= []; // 重组后的sku-->[{path: "三人份-BT辣-羊肉-炒",sku: "000UNLI4"}]
  29. selectedArr: Array < any >= []; // 所有选中的规格与key的组合
  30. notSelectable: Array < any >= [];
  31. allPath: any = {};
  32. keys: string = ""; // 所有可选属性行
  33. selectedCache: Array < any >= []; // 已选中的缓存
  34. spliter: string = "-"; // 分隔符
  35. curtSelect: string = ""; // 当前选中的规格
  36. @Prop({
  37. default: {}
  38. }) specData: any;
  39. @Prop({
  40. default: false
  41. }) sellOut!: boolean;
  42. get demoData() {
  43. return Object.keys(this.showSpec) || [];
  44. }
  45. @Watch('specData', {
  46. deep: true,
  47. immediate: true,
  48. })
  49. specDataChange(val: any) {
  50. this.proContent = val;
  51. this.proContent.skuList.length && this.initData();
  52. }
  53. initData() {
  54. // 售罄不执行
  55. if (this.sellOut) {
  56. return;
  57. }
  58. //proContent未准备好 不执行
  59. if (!this.proContent) return;
  60. // 单规格未设置'默认'规格时,不执行
  61. if (!this.proContent.skuList[0].attrs) return;
  62. // 暂时不过滤库存为0 的商品
  63. // this.filterNullSku();
  64. // 过滤 keys 可选的规格名称 ['款式','口味']
  65. this.keys = this.filterKeys(this.proContent);
  66. // console.log('this.keys------>', this.keys);
  67. // 展示给用户的规格选项
  68. this.showSpec = this.getshowSpec(this.proContent.skuList);
  69. // console.log("this.showSpec------>", this.showSpec);
  70. // this.reSku ==》[{path:'大-不辣',sku:111},{path:'大-辣',sku:111}]
  71. this.reSku = this.combineAttr(this.proContent.skuList);
  72. // console.log('this.reSku------>', this.reSku)
  73. this.buildResult(this.reSku);
  74. // 只有一个sku时默认选中所有选项
  75. const oneSku = this.proContent.skuList.length === 1;
  76. // 开局选中
  77. let skuItem = this.proContent.skuList;
  78. console.log('选中的规格', this.selectedArr)
  79. for (let k in this.selectedArr) {
  80. skuItem[0].attrs.map((sku: any) => {
  81. if (sku.keyName == k) {
  82. this.selectedArr[k] = sku.value;
  83. }
  84. });
  85. }
  86. this.updateStatus(this.getSelectedItem());
  87. this.getChoose()
  88. }
  89. filterKeys(proContent: any): any {
  90. // let keys =[]
  91. // proContent.skuList.map((item,index)=>{
  92. // item.attrs.map(res=> keys.push(res.keyName))
  93. // })
  94. // keys = [...new Set(keys)]
  95. // return keys
  96. // console.log("proContent", proContent);
  97. let keys: Array < any >= [];
  98. (proContent.skuList[0] as any).attrs.map((res: any) => keys.push(res.keyName));
  99. keys = [...new Set(keys)];
  100. return keys;
  101. }
  102. // 属性点击事件
  103. chooseSpec(skey: any, sval: any) {
  104. // skey->选中的规格key,sval->选中的规格值
  105. // this.curtSelect = sval;
  106. // console.log(!this.notSelectable.includes(sval))
  107. if (!this.notSelectable.includes(sval)) {
  108. if (this.selectedArr[skey] === sval) {
  109. // this.selectedArr[skey] = "";
  110. this.$set(this.selectedArr, skey, '')
  111. } else {
  112. // this.selectedArr[skey] = sval;
  113. this.$set(this.selectedArr, skey, sval)
  114. }
  115. this.updateStatus(this.getSelectedItem());
  116. // this.$emit('getChoose', this.selectedArr);
  117. this.getChoose()
  118. }
  119. }
  120. // // 将sku stock为0的sku过滤掉 一个sku的不过滤了
  121. // filterNullSku() {
  122. // if (this.proContent && this.proContent.skuList.length > 1) {
  123. // let sku = {};
  124. // sku = this.proContent.skuList.filter((item) => item.stock > 0);
  125. // this.proContent.skuList = sku;
  126. // }
  127. // }
  128. // 将商品数据中的 sku 用 key 过滤出所有可选的商品规格
  129. getshowSpec(skuList: any) {
  130. let keys = this.keys;
  131. let jsons: any = {};
  132. let select: any = {};
  133. for (let key_val of keys) {
  134. jsons[key_val] = [];
  135. select[key_val] = "";
  136. for (let j = 0; j < skuList.length; j++) {
  137. for (let i = 0; i < keys.length; i++) {
  138. if (
  139. skuList[j].attrs[i].keyName &&
  140. skuList[j].attrs[i].keyName === key_val
  141. ) {
  142. jsons[key_val].push(skuList[j].attrs[i].value);
  143. }
  144. }
  145. }
  146. }
  147. for (let i in jsons) {
  148. jsons[i] = [...new Set(jsons[i])];
  149. }
  150. this.selectedArr = select; // 后面储存已选属性要用到 {颜色:'',尺寸: ''...}
  151. // console.log('this.selectedArr------>', this.selectedArr);
  152. return jsons;
  153. }
  154. // 计算组合数据
  155. combineAttr(skuList: any) {
  156. let allKeys: Array < any >= [];
  157. skuList.map((res: any) => {
  158. let values: Array < any > = [];
  159. res.attrs.forEach((key: any) => {
  160. values.push(key.value);
  161. });
  162. allKeys.push({
  163. path: values.join(this.spliter),
  164. kid: res.kid,
  165. });
  166. });
  167. return allKeys;
  168. }
  169. /**
  170. * 生成所有子集是否可选、库存状态 map
  171. * items =》 [{path:'大-不辣',sku:111},{path:'大-辣',sku:111}]
  172. * allKeys =>["大-辣", "大-不辣", "中-不辣", "中-辣", "小-辣", "小-不辣"]
  173. * return =>{ 不辣:{skus:[123,12323]}}
  174. */
  175. buildResult(items: any) {
  176. this.allPath = {};
  177. let allKeys = items.map((res: any) => res.path);
  178. for (let i = 0; i < allKeys.length; i++) {
  179. let sku: string = items[i].kid;
  180. let values: Array < any > = allKeys[i].split(this.spliter);
  181. let allSets = this.powerset(values);
  182. // 每个组合的子集
  183. // 清空allpath,zcj
  184. // this.allPath = {};
  185. for (let j = 0; j < allSets.length; j++) {
  186. let set = allSets[j];
  187. let key = set.join(this.spliter);
  188. if (this.allPath[key]) {
  189. this.allPath[key].skus.push(sku);
  190. } else {
  191. this.allPath[key] = {
  192. skus: [sku],
  193. };
  194. }
  195. }
  196. }
  197. // console.log('this.allPath------>', this.allPath);
  198. }
  199. powerset(arr: any) {
  200. let ps: any = [
  201. []
  202. ];
  203. for (let i = 0; i < arr.length; i++) {
  204. for (let j = 0, len = ps.length; j < len; j++) {
  205. ps.push(ps[j].concat(arr[i]));
  206. }
  207. }
  208. return ps;
  209. }
  210. // 过滤分隔符
  211. trimSpliter(str: any) {
  212. // ⊙abc⊙ => abc
  213. // ⊙a⊙⊙b⊙c⊙ => a⊙b⊙c
  214. let reLeft = new RegExp("^" + this.spliter + "+", "g");
  215. let reRight = new RegExp(this.spliter + "+$", "g");
  216. let reSpliter = new RegExp(this.spliter + "+", "g");
  217. return str
  218. .replace(reLeft, "")
  219. .replace(reRight, "")
  220. .replace(reSpliter, this.spliter);
  221. }
  222. // 获取当前选中的属性 =>['大','不辣']
  223. getSelectedItem() {
  224. let result: any = [];
  225. result = Object.values(this.selectedArr);
  226. return result;
  227. }
  228. // 更新所有属性状态
  229. updateStatus(selected: any) {
  230. this.notSelectable = [];
  231. for (let i = 0, len = this.keys.length; i < len; i++) {
  232. let key: any = this.keys[i];
  233. let data: any = this.showSpec[key];
  234. // console.log('data',data);
  235. let copy: any = selected.slice();
  236. // console.log('copy',copy);
  237. for (let j = 0; j < data.length; j++) {
  238. let item: any = data[j];
  239. if (selected[i] === item) continue;
  240. copy[i] = item;
  241. let curr = this.trimSpliter(copy.join(this.spliter));
  242. if (!this.allPath[curr]) {
  243. this.notSelectable.push(item);
  244. // console.log("this.notSelectable------>", this.notSelectable);
  245. }
  246. }
  247. }
  248. if (!this.getSelectedItem().includes("")) {
  249. let choSku: any = this.getSelectedItem().join(this.spliter);
  250. // console.log(choSku);
  251. // console.log("findSku1", this.allPath[choSku]);
  252. this.findSku(this.allPath[choSku].skus[0])
  253. }
  254. }
  255. // emit===================================
  256. @Emit('getChoose')
  257. getChoose() {
  258. return this.selectedArr
  259. }
  260. @Emit('findSku')
  261. findSku(value: any) {
  262. return value
  263. }
  264. }
  265. </script>
  266. <style lang='scss' scoped>
  267. .mini-spec {
  268. max-height: vw(200);
  269. overflow-y: scroll;
  270. @include hide-scrollbar() .spec-item {
  271. @include word-vw(14, $gray3);
  272. .spec-title {
  273. @include word-vw(16, $gray3);
  274. line-height: vw(40);
  275. }
  276. .spec-list {
  277. display: flex;
  278. flex-wrap: wrap;
  279. margin-top: vw(6);
  280. .spec-value {
  281. min-width: vw(100);
  282. max-width: vw(345);
  283. @include ellipsis();
  284. height: vw(32);
  285. line-height: vw(32);
  286. padding: 0 vw(10);
  287. box-sizing: border-box;
  288. text-align: center;
  289. @include word-vw(14, $gray3);
  290. background: #fff;
  291. border-radius: vw(8);
  292. margin-right: vw(18);
  293. margin-bottom: vw(16);
  294. &.active {
  295. /* background: #fff; */
  296. color: #E33C64;
  297. /* background-color: $bk-color; */
  298. /* border: vw(1) solid $main-color; */
  299. }
  300. &.no {
  301. color: $gray9;
  302. background: #EFEFEF;
  303. }
  304. }
  305. .spec-value:last-child {
  306. margin-right: 0;
  307. }
  308. }
  309. }
  310. }
  311. </style>