<template> <view class="page"> <view class="mode"> <view class="line"> <view class="title">规格模板</view> <picker style="flex: 1;" mode="selector" :range="attr_mode_list" range-key="template_name" @change="changAttrMode"> <view class="input"> <view>{{template_name}}</view> <uni-icons type="right"></uni-icons> </view> </picker> <view class="btn" @click="preAddMode(3)">添加模板</view> </view> <view class="btn-box" @click="settingSpec()"> <view class="btn">点击设置规格</view> </view> <uni-popup ref="modeRef" type="top"> <view class="mode-ref" style="min-height: 300rpx;width: 100%;background-color: #fff;"> <view style="height: 1rpx;"></view> <scroll-view scroll-y style="max-height: 550rpx;min-height: 220rpx;"> <view class="mode-item" v-for="(item, index) in attr" :key="item+index"> <view class="mode-head">{{item.value}}</view> <view class="mode-body"> <view class="box mode-body-box" v-for="(t, i) in item.detail" :key="t+i" @click="deleteAttrTow(index, i)"> {{t}}<uni-icons type="close" color="#f84221" style="margin-left: 10rpx;"></uni-icons> </view> <view class="box mode-body-add" @click="addSpecTow(index)"><uni-icons type="plus" color="#333" style="margin-right: 10rpx;"></uni-icons>添加</view> </view> <view class="delete-spec" @click="deleteAttrOne(index)"><uni-icons type="close" color="#f84221" style="margin-right: 10rpx;"></uni-icons>删除规格</view> </view> <view v-if="attr.length==0" style="color: #ccc;height: 220rpx;text-align: center;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <view>暂无规格</view> <view>点击下方按钮添加新规格</view> </view> </scroll-view> <view class="tab"> <view class="add-btn" @click="addSpecOne()">添加新规格</view> <view class="save" @click="saveAttrMode()">保存模板</view> </view> </view> </uni-popup> <uni-popup ref="inputModeRef" type="center"> <view class="input-mode"> <view class="head-tips"> <view v-if="addAttrType==1">添加规格值</view> <view v-else-if="addAttrType==2">添加新规格</view> <view v-else>添加模板</view> </view> <view class="input-box"> <input v-if="addAttrType==4||addAttrType==3" v-model="attrMode" placeholder="请输入模板名称" /> <input v-if="addAttrType==2||addAttrType==3" v-model="attrName" placeholder="请输入规格名称" /> <input v-if="addAttrType!=4" v-model="attrDetail" placeholder="请输入规格值" /> </view> <view class="show-btn-box"> <view class="cof prai" @click="addAttr()">确认</view> <view class="can" @click="closeAddAttr()">取消</view> </view> </view> </uni-popup> </view> <view style="height: 160rpx;"></view> <avatar style="height: 1px;" @upload="doUpload" @getName="getImgName" quality="1" ref="avatar" selWidth="250upx" selHeight="250upx"> </avatar> <block v-for="(item,index) in attrValue" :key="item.uuid"> <view class="popup_group head_close"> <view class="popup_group_item " v-for="(value, key) in item.detail" :key="key"> <view class="popup_group_item_label">{{key}}</view> <view class="popup_group_item_value"> <input :value="value" type="text" disabled placeholder="请填写规格名称" /> </view> </view> <view class="delete_btn" @click="deleteByIndex(index)"> <image style="width: 50%;height: 50%;" src="../static/images/close.png"></image> </view> <view class="popup_group_item"> <view class="popup_group_item_label">规格图片</view> <view style="width: 120rpx;height: 120rpx;position: relative;"> <block v-if="item.image"> <image @click="clk(item.uuid)" style="width: 120rpx;height: 120rpx;border-radius: 10rpx;" :src="item.image"></image> <view class="close-icon" @click="deleteImage(item)"> <image style="width: 50%;height: 50%;" src="../static/images/close.png"></image> </view> </block> <view v-else style="width: 120rpx;height: 120rpx;display: flex;justify-content: center;align-items: center;border: 1rpx solid #ccc;border-radius: 10rpx;"> <image @click="clk(item.uuid)" style="height: 60rpx;width: 60rpx;" src="../static/images/creamer.png"> </image> </view> </view> </view> </view> <priceComponent ref="priceRef" :product_id="product_id" :datas="item" :bar_code="item.bar_code" @updateCode="updateCode" :show_sku="true"> </priceComponent> </block> <!-- <button class="add_btn" @click="addAttrValue">点击添加规格</button> --> <view class="submit"> <button class="btn" @click="submitAttr">确认<text>({{attrValue.length}})</text></button> </view> <u-modal title="警告" content="删除后数据不可恢复,是否确认删除?" :show="showDelete" show-cancel-button @cancel="showDelete=false" @confirm="deleteAttr()"></u-modal> </view> </template> <script> import priceComponent from "./components/price.vue"; import avatar from "@/components/yq-avatar/yq-avatar.vue"; import { TOKENNAME, HTTP_REQUEST_URL } from '@/config/app.js'; import { Toast } from "../../../libs/uniApi"; import { specificationUpdate, attrList, specificationAdd } from "@/api/product.js" export default { components: { priceComponent, avatar }, data() { return { attrValue: [], attr: [], attr_mode_list: [], //规格模板列表 template_name: '点击选择模板', //选中的模板名字 attr_mode: { // 规格字段模板 "detail": {}, "sku": "", "stock": 0, "image": "", "bar_code": "", "cost": "", "ot_price": "", "price": "", "volume": "", "weight": "", }, product_id: '', uuid: '', //选择图片的id deleteIndex: '', //删除图片下标 showDelete: false, userInfo: {}, addAttrType: 1, // 规格类型, 1为添加规格值, 2为添加新规格, 3为添加新模板, 4为保存模板 attrName: '', // 规格名称 attrDetail: '', // 规格值 attrMode: '', //新增模板名称 changeAttr: 0, // 选中的规格 } }, onLoad(options) { this.product_id = options.product_id; this.attrValue = JSON.parse(uni.getStorageSync('attrValue') || '[]'); this.attr = JSON.parse(uni.getStorageSync('attr') || '[]'); this.getOpenerEventChannel().once('updateAttrValue', (e) => { if(e.attrValue.length>0) e.attrValue.forEach((item, index) => { item.uuid = Date.now() + '-' + Math.floor(Math.random() * 10000); }) this.attrValue = e.attrValue; uni.setStorageSync('attrValue', JSON.stringify(e.attrValue)); this.attr = e.attr; uni.setStorageSync('attr', JSON.stringify(e.attr)); }) this.userInfo = this.$store.state.app.userInfo; if (typeof this.userInfo == 'string') this.userInfo = JSON.parse(this.userInfo); this.initAttrModeList(); }, methods: { updateCode(e) { console.log(e); }, // 点击设置规格 settingSpec() { this.$refs.modeRef.open(); }, // 添加规格二级 addSpecTow(index) { this.attrDetail = ''; this.addAttrType = 1; this.changeAttr = index; this.$refs.inputModeRef.open(); }, // 添加规格一级 addSpecOne() { this.attrName = ''; this.attrDetail = ''; this.addAttrType = 2; this.$refs.inputModeRef.open(); }, // 删除规格二级 deleteAttrTow(i, j) { if(this.attr[i].detail.length<=1){ return this.deleteAttrOne(i); // 当规格的最后一个属性被删除时,直接删除该规格 } this.attrValue = this.attrValue.filter(item=>{ return !(item.detail[this.attr[i].value] == this.attr[i].detail[j]); }) this.attr[i].detail.splice(j, 1); }, // 删除规格一级 deleteAttrOne(i) { let attr = this.attr[i]; if(this.attr.length==1){ // 如果只有一个规格, 并且删除这个规格时, 直接清空规格 this.$set(this, 'attrValue', []); this.attr.splice(i, 1); return ; } // 删除多余的规格值 this.attrValue.forEach(item => { let a = item.sku.split(','); a = a.filter(e=>e!=item.detail[attr.value]); item.sku = a.join(','); delete item.detail[attr.value]; }) // 过滤掉重复的值 this.attrValue = Array.from(new Set(this.attrValue.map(item => item.sku))).map(sku => { return this.attrValue.find(item => item.sku === sku); }); this.attr.splice(i, 1); }, // 添加商品规格卡片 addAttrValueCard(){ let arr = this.computeAttr(this.attr); // 计算出sku的值 let t = []; let keys = this.attr.map(e => { // 获取规格名称 return e.value; }) let temp = []; arr.forEach((sku) => { // 根据sku遍历当前规格数组 if (this.attrValue.find(e => e.sku == sku)) return; else { let detail = {}; let t = sku.split(','); keys.forEach((e, i) => { // 设置detail的值 detail[e] = t[i]; }) temp.push(Object.assign({}, this.attr_mode, { sku: sku, detail: detail, uuid: Date.now() + '-' + Math.floor(Math.random() * 10000) // 重设uuid })) // 追加数据 } }) setTimeout(() => { this.attrValue = [...this.attrValue, ...temp] // 延后追加数据 }) }, // 关闭添加规格 closeAddAttr() { this.$refs.inputModeRef.close(); }, // 获取产品的组合 computeAttr(arr) { let s = []; arr.forEach(item => { s.push(item.detail); }) let t = this.getCombination(s); t = t.map(item => { if (typeof item == 'object') { return item.join(',') } else return item; }) return t; }, // 递归计算 getCombination(arr) { if (arr.length === 1) { return arr[0]; } var result = []; var first = arr[0]; var rest = arr.slice(1); for (var i = 0; i < first.length; i++) { var subResult = this.getCombination(rest); for (var j = 0; j < subResult.length; j++) { result.push([first[i]].concat(subResult[j])); } } return result; }, // 确认添加规格 addAttr() { this.attrDetail = this.attrDetail.trim(); this.attrDetail = this.attrDetail.trim(); if (this.addAttrType == 1) { // 2级 if (this.attr[this.changeAttr].detail.find(name => name == this.attrDetail)) { this.closeAddAttr(); this.$nextTick(() => { Toast('该规格已存在') }) } else { this.attr[this.changeAttr].detail.push(this.attrDetail); this.closeAddAttr(); setTimeout(()=>{ this.addAttrValueCard(); }) } } else if (this.addAttrType == 2) { // 1级 if (this.attr.find(item => item.value == this.attrName)) { this.closeAddAttr(); this.$nextTick(() => { Toast('该规格已存在') }) } else { console.log('添加新规格'); this.attr.push({ value: this.attrName, detail: [this.attrDetail] }); if(this.attrValue.length){ this.attrValue.forEach((item) => { item.detail[this.attrName] = this.attrDetail; item.sku = item.sku + ',' + this.attrDetail; }) } else { let detail = {}; detail[this.attrName] = this.attrDetail; this.attrValue.push(Object.assign({}, this.attr_mode, { sku: this.attrDetail, detail: detail, uuid: Date.now() + '-' + Math.floor(Math.random() * 10000) })) } this.closeAddAttr(); } } else if (this.addAttrType == 3 || this.addAttrType == 4){ this.addNewAttrMode(); } }, // 保存模板 saveAttrMode() { if(this.attr.length<=0)return Toast('不可保存空模板'); if(this.attr_template_id){ this.addAttrType = 4; let template = this.attr_mode_list.find(e=>e.attr_template_id==this.attr_template_id); specificationUpdate(this.userInfo.service.mer_id, this.attr_template_id, { attr_template_id: template.attr_template_id, mer_id: template.attr_template_id, template_name: template.template_name, template_value: this.attr }).then((res)=>{ this.$refs.modeRef.close(); this.$nextTick(()=>{ Toast('保存成功'); }) }).catch(err=>{ Toast(err) }) }else { this.preAddMode(4) } }, // 添加新模板 addNewAttrMode(){ this.attrMode = this.attrMode.trim(' '); if(this.attrMode==''||this.attrMode===undefined||this.attrMode===null){ return Toast('模板名称不可为空') } let attr = []; if(this.addAttrType==3){ attr[0] = {}; attr[0].value = this.attrName; attr[0].detail = []; attr[0].detail[0] = this.attrDetail; }else attr = this.attr; specificationAdd(this.userInfo.service.mer_id, { template_name: this.attrMode, template_value: attr }).then((res)=>{ this.initAttrModeList(); this.closeAddAttr(); this.$nextTick(()=>{ this.$refs.modeRef.close(); this.$nextTick(()=>{ Toast('保存成功'); }) }) }).catch(err=>{ Toast(err) }) }, // 预添加模板 preAddMode(type=3){ this.attrName = ''; this.attrDetail = ''; this.attrMode = ''; this.addAttrType = type; this.$refs.inputModeRef.open(); }, // 提交数据 submitAttr() { if (this.attrValue.length < 1) return Toast('请至少添加一种规格'); let flag = true; let str = '请填写完整信息'; this.attrValue.forEach((item, index) => { Object.keys(this.$refs.priceRef[index].singleSpecification).forEach(key => { item[key] = this.$refs.priceRef[index].singleSpecification[key]; }) if (flag) { if (item.sku == undefined || item.sku == null || item.sku == '') { flag = false; str = '规格不能为空'; } else if (!item.price||+item.price<=0) { flag = false; str = '零售价不能小于等于0'; } else if (!item.stock||+item.stock<=0) { flag = false; str = '库存不能小于等于0'; } // else if (!item.cost||+item.cost<=0) { // flag = false; // str = '成本价不能小于等于0'; // } else if ((!item.procure_price||+item.procure_price<=0) && this.userInfo.mer_info.type_code == 'TypeSupplyChain') { flag = false; str = '批发价不能小于等于0'; } } }) if (!flag) return Toast(str); else { uni.$emit('updateSpecType', { attr: this.attr, attrValue: this.attrValue }); uni.navigateBack(); } }, // 选择图片 clk(uuid) { this.uuid = uuid; let avatar = this.$refs.avatar; avatar.fChooseImg(1, { selWidth: '350upx', selHeight: '350upx', inner: true }); }, doUpload(rsp) { // console.log(rsp); let that = this uni.uploadFile({ url: HTTP_REQUEST_URL + '/api/upload/image/field', filePath: rsp.path, name: 'field', formData: { 'filename': rsp.path, 'name': that.imgName }, header: { // #ifdef MP "Content-Type": "multipart/form-data", // #endif [TOKENNAME]: 'Bearer ' + this.$store.state.app.token }, success: (uploadFileRes) => { let imgData = JSON.parse(uploadFileRes.data) let item = this.attrValue.find(item => item.uuid == this.uuid); item.image = imgData.data.path; }, complete(res) { // console.log(res) } }); }, getImgName(name) { this.imgName = name }, // 删除图片 deleteImage(item) { item.image = ''; }, // 添加规格 addAttrValue() { this.attrValue.push(Object.assign({}, this.attr_mode, { uuid: Date.now() + '-' + Math.floor(Math.random() * 10000) // 重设uuid })) }, // 预删除 deleteByIndex(index) { this.deleteIndex = index; this.showDelete = true; }, // 删除规格 deleteAttr() { this.attrValue.splice(this.deleteIndex, 1); this.showDelete = false; }, // 初始化规格模板 initAttrModeList(){ attrList(this.userInfo.service.mer_id,{ page: 1, limit: 10000 }).then((res)=>{ console.log(res); this.attr_mode_list = res.data.list; }) }, changAttrMode(e){ this.attr_template_id = this.attr_mode_list[e.detail.value].attr_template_id; this.template_name = this.attr_mode_list[e.detail.value].template_name; this.attr = JSON.parse(JSON.stringify(this.attr_mode_list[e.detail.value].template_value)); this.$set(this, 'attrValue', []); this.addAttrValueCard(); } }, onBackPress: () => { } } </script> <style lang="scss"> @import './scss/index.scss'; .page{ padding-bottom: 150rpx; } .popup_group { margin-bottom: 0; } .popup_group_item { border-bottom: 1rpx solid #eee; padding-left: 18rpx !important; .close-icon { position: absolute; top: 0; right: 0; width: 30rpx; height: 30rpx; border-radius: 10rpx; overflow: hidden; display: flex; justify-content: center; align-items: center; background-color: rgba(#000, 0.4); color: #fff; } } .head_close { position: relative; .delete_btn { position: absolute; top: -15rpx; right: -15rpx; width: 40rpx; height: 40rpx; display: flex; justify-content: center; align-items: center; background-color: red; color: #fff; border-radius: 50%; } } .add_btn { width: 710rpx; margin: 28rpx auto; margin-bottom: 160rpx; font-size: 30rpx; padding: 28rpx; color: #333; } .submit { position: fixed; bottom: 0; left: 0; width: 100%; background-color: #fff; padding: 20rpx 0; .btn { width: 694rpx; height: 80rpx; margin: 0 auto; background: #e93323; border-radius: 43px; display: flex; align-items: center; justify-content: center; font-size: 15px; color: #ffffff; } } .mode { width: 100%; background-color: #fff; position: fixed; top: 0; left: 0; z-index: 1; .line { padding: 0 28rpx; height: 80rpx; display: flex; justify-content: space-between; align-items: center; font-size: 28rpx; .btn { background-color: #f84221; color: #fff; width: 140rpx; height: 50rpx; display: flex; justify-content: center; align-items: center; border-radius: 10rpx; } .input { flex: 1; padding: 0 16rpx; display: flex; justify-content: space-between; align-items: center; } } .btn-box { height: 80rpx; display: flex; justify-content: center; align-items: center; border-top: 1rpx solid #eee; border-bottom: 1rpx solid #eee; } .mode-ref { .mode-item { margin: 20rpx; border: 1rpx solid #eee; border-radius: 10rpx; font-size: 26rpx; position: relative; overflow: hidden; .mode-head { padding-top: 20rpx; padding-left: 20rpx; font-size: 32rpx; font-weight: bold; } .mode-body { display: flex; flex-wrap: wrap; padding: 20rpx; padding-left: 0; .box { padding: 10rpx 20rpx; border: 1px solid #f84221; margin-left: 20rpx; border-radius: 10rpx; margin-bottom: 10rpx; background-color: rgba(#f84221, 0.1); color: #f84221; display: flex; justify-content: center; align-items: center; } .mode-body-add { border: 1rpx solid #ddd; background-color: #fff; color: #333; } } .delete-spec { position: absolute; display: flex; justify-content: center; align-items: center; top: 0; right: 0; background-color: #eee; padding: 5rpx; border-radius: 0 0 0 10rpx; color: #f84221; } } .tab { padding: 20rpx; border-top: 1rpx solid #eee; display: flex; font-size: 28rpx; .add-btn { flex: 1; background-color: #f84221; color: #fff; padding: 10rpx; text-align: center; border-radius: 10rpx; } .save { color: #333; border: 1rpx solid #ddd; padding: 10rpx; text-align: center; border-radius: 10rpx; margin-left: 20rpx; } } } .input-mode { width: 694rpx; padding: 28rpx; font-size: 28rpx; border-radius: 10rpx; background-color: #fff; .head-tips { font-size: 32rpx; font-weight: bold; text-align: center; padding-bottom: 20rpx; } .input-box { input { height: 80rpx; margin-bottom: 20rpx; font-size: 28rpx; border: 1rpx solid #ddd; padding: 0 20rpx; border-radius: 10rpx; } } .show-btn-box { width: 100%; display: flex; justify-content: space-between; .cof, .can { height: 80rpx; line-height: 80rpx; width: 300rpx; text-align: center; border: 1rpx solid #ddd; border-radius: 10rpx; } .prai { background-color: #f84221; border: 1rpx solid #f84221; color: #fff; } } } } </style>