|
@@ -0,0 +1,455 @@
|
|
|
+<template>
|
|
|
+ <div class="app-container goods-add">
|
|
|
+ <el-divider content-position="left">商品信息</el-divider>
|
|
|
+ <el-form ref="addItem" :rules="rules" :model="addData" label-width="120px">
|
|
|
+ <el-row :gutter="40">
|
|
|
+ <el-col :span="23">
|
|
|
+ <el-form-item label="商品名称:" prop="title">
|
|
|
+ <el-input v-model="addData.title" placeholder="请输入商品名称"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="23">
|
|
|
+ <el-form-item label="商品主图:" prop="picUrl">
|
|
|
+ <!-- <Upload v-model="addData.picUrl" :limit="1" :type="4" :beforeUpload="beforeUpload" /> -->
|
|
|
+ <div class="tip">支持jpg、png格式上传,建议使用尺寸800x800像素以上、大小不超过1M的正方形图片;</div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="23">
|
|
|
+ <el-form-item label="支持盲豆兑换:" prop="exchangeShow">
|
|
|
+ <el-switch
|
|
|
+ v-model="addData.exchangeShow"
|
|
|
+ active-color="#13ce66"
|
|
|
+ inactive-color="#ff4949"
|
|
|
+ :active-value="1"
|
|
|
+ :inactive-value="0"
|
|
|
+ active-text="支持"
|
|
|
+ inactive-text="不支持">
|
|
|
+ </el-switch>
|
|
|
+ <div class="tip">关闭则不再兑换大厅显示,不支持盲豆兑换。</div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <el-divider content-position="left">价格库存</el-divider>
|
|
|
+ <el-row :gutter="40">
|
|
|
+ <el-col :span="23">
|
|
|
+ <el-form-item label="启用多SKU:" prop="multiSku">
|
|
|
+ <!-- <el-switch
|
|
|
+ v-model="addData.multiSku"
|
|
|
+ active-color="#13ce66"
|
|
|
+ inactive-color="#ff4949"
|
|
|
+ :active-value="1"
|
|
|
+ :inactive-value="0"
|
|
|
+ active-text="是"
|
|
|
+ inactive-text="否"
|
|
|
+ /> -->
|
|
|
+ <el-radio-group v-model="addData.multiSku">
|
|
|
+ <el-radio-button :label="0">单规格</el-radio-button>
|
|
|
+ <el-radio-button :label="1">多规格</el-radio-button>
|
|
|
+ </el-radio-group>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <el-row v-if="!multipleSpec" :gutter="40">
|
|
|
+ <el-col :span="11">
|
|
|
+ <el-form-item label="价格:" prop="value">
|
|
|
+ <el-input v-model="addData.value" placeholder="请输入商品价格">
|
|
|
+ <template slot="append">元</template>
|
|
|
+ </el-input>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="兑换价格:" prop="exchangePrice">
|
|
|
+ <el-input v-model="addData.exchangePrice" placeholder="请输入兑换价格" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="11">
|
|
|
+ <el-form-item label="成本:" prop="cost">
|
|
|
+ <el-input v-model="addData.cost" placeholder="请输入商品成本">
|
|
|
+ <template slot="append">元</template>
|
|
|
+ </el-input>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="库存:" prop="quantity">
|
|
|
+ <el-input v-model="addData.quantity" placeholder="请输入商品库存" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-form>
|
|
|
+ <div class="list-group" v-if="multipleSpec">
|
|
|
+ <div style="margin-bottom: 16px">
|
|
|
+ <el-button :plain="true" type="primary" size="small" @click="handleSpecificationShow">添加规格</el-button>
|
|
|
+ </div>
|
|
|
+ <div class="list-group-header">
|
|
|
+ <div class="f2">规格名</div>
|
|
|
+ <div class="f2">规格值</div>
|
|
|
+ <div class="f1 tr">操作</div>
|
|
|
+ </div>
|
|
|
+ <Draggable v-if="products.length > 0" v-model="specifications" :options="dragOptions">
|
|
|
+ <transition-group type="transition" :name="'flip-list'">
|
|
|
+ <div class="list-group-item" v-for="item in specifications" :key="item.specification + item.value">
|
|
|
+ <div class="f2">{{item.specification}}</div>
|
|
|
+ <div class="f2">{{item.value}}</div>
|
|
|
+ <div class="f1 tr"><el-button type="danger" size="mini" @click="handleSpecificationDelete(item)">删除</el-button></div>
|
|
|
+ </div>
|
|
|
+ </transition-group>
|
|
|
+ </Draggable>
|
|
|
+ <div v-else class="list-empty">无内容</div>
|
|
|
+ </div>
|
|
|
+ <el-dialog :visible.sync="specVisiable" title="设置规格" :close-on-click-modal="false" width="400px">
|
|
|
+ <el-form ref="specForm" :model="specForm" label-width="80px">
|
|
|
+ <el-form-item label="规格名:" prop="specification">
|
|
|
+ <el-input v-model="specForm.specification"/>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="规格值:" prop="value">
|
|
|
+ <el-input v-model="specForm.value"/>
|
|
|
+ <div class="tip">多个规格值请用逗号隔开</div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <div slot="footer" class="dialog-footer">
|
|
|
+ <el-button @click="specVisiable = false">取消</el-button>
|
|
|
+ <el-button type="primary" @click="handleSpecificationAdd">确定</el-button>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
+ <br>
|
|
|
+ <div class="list-group" v-if="multipleSpec">
|
|
|
+ <div class="list-group-header">
|
|
|
+ <div class="f2">商品规格</div>
|
|
|
+ <div class="f1">商品售价</div>
|
|
|
+ <div class="f1">折扣价格</div>
|
|
|
+ <div class="f1">商品数量</div>
|
|
|
+ <div class="f2">商品图片</div>
|
|
|
+ <div class="f1 tr">操作</div>
|
|
|
+ </div>
|
|
|
+ <Draggable v-if="products.length > 0" v-model="products" :options="dragOptions">
|
|
|
+ <transition-group type="transition" :name="'flip-list'">
|
|
|
+ <div class="list-group-item" v-for="item in products" :key="item.specificationList.toString()">
|
|
|
+ <div class="f2">{{item.specificationList.toString()}}</div>
|
|
|
+ <div class="f1">{{item.price}}</div>
|
|
|
+ <div class="f1">{{item.discountPrice}}</div>
|
|
|
+ <div class="f1">{{item.number}}</div>
|
|
|
+ <div class="f2"><img v-if="item.imgUrl" :src="IMG_URL+item.imgUrl" width="40"></div>
|
|
|
+ <div class="f1 tr"><el-button type="primary" size="mini" @click="handleProductShow(item)">设置</el-button></div>
|
|
|
+ </div>
|
|
|
+ </transition-group>
|
|
|
+ </Draggable>
|
|
|
+ <div v-else class="list-empty">无内容</div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <el-dialog :visible.sync="productVisiable" title="设置商品" :close-on-click-modal="false" width="400px">
|
|
|
+ <el-form ref="productForm" :model="productForm" label-width="100px">
|
|
|
+ <el-form-item label="商品规格:" prop="specificationList">
|
|
|
+ <el-tag v-for="tag in productForm.specificationList" :key="tag">
|
|
|
+ {{ tag }}
|
|
|
+ </el-tag>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="商品原价:" prop="price">
|
|
|
+ <el-input-number v-model="productForm.price"/>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="折扣价:" prop="discountPrice">
|
|
|
+ <el-input-number v-model="productForm.discountPrice"/>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="商品数量:" prop="number">
|
|
|
+ <el-input-number v-model="productForm.number"/>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="商品图片:" prop="imgUrl">
|
|
|
+ <Upload v-model="productForm.imgUrl" :limit="1" :type="4" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <div slot="footer" class="dialog-footer">
|
|
|
+ <el-button @click="productVisiable = false">取消</el-button>
|
|
|
+ <el-button type="primary" @click="handleProductEdit">确定</el-button>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
+ <br>
|
|
|
+ <el-row>
|
|
|
+ <el-col :span="24" style="text-align: right">
|
|
|
+ <el-button type="primary" @click="updateItem()">发布</el-button>
|
|
|
+ <el-button type="info" @click="$router.replace('/goods/list')">取消</el-button>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+<script>
|
|
|
+import Draggable from 'vuedraggable'
|
|
|
+import { getGoodsDetail, addGoods } from '@/api/business/goods'
|
|
|
+
|
|
|
+export default {
|
|
|
+ components: {
|
|
|
+ Draggable
|
|
|
+ },
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ id: this.$route.query.id,
|
|
|
+ addData: {
|
|
|
+ multiSku: 0
|
|
|
+ },
|
|
|
+ rules: {
|
|
|
+ title: [{ required: true, message: '请输入商品名称', trigger: 'blur' }]
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+ dragOptions: {
|
|
|
+ animation: 100,
|
|
|
+ },
|
|
|
+ specifications: [],
|
|
|
+ products: [],
|
|
|
+
|
|
|
+ IMG_URL: process.env.IMG_URL,
|
|
|
+
|
|
|
+ specVisiable: false, // 规格弹窗
|
|
|
+ specForm: { specification: '', value: '', imgUrl: '' }, // 规格暂存
|
|
|
+ productVisiable: false,
|
|
|
+ productForm: { id: 0, specificationList: [], price: 0.00, discountPrice: 0.00, number: 0, imgUrl: '' },
|
|
|
+ }
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ multipleSpec() {
|
|
|
+ return this.addData.multiSku === 1
|
|
|
+ }
|
|
|
+ },
|
|
|
+ created() {
|
|
|
+ if (this.id) {
|
|
|
+ getGoodsDetail(this.id).then(res => {
|
|
|
+ this.addData = res.data
|
|
|
+ })
|
|
|
+ }
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ beforeUpload(e) {
|
|
|
+ if (e.size > 1024000) {
|
|
|
+ this.$message({
|
|
|
+ message: '图片过大,请调整大小确保不超过1M!',
|
|
|
+ type: 'warning'
|
|
|
+ });
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ handleSpecificationShow() {
|
|
|
+ this.specForm = { specification: '', value: '', imgUrl: '' }
|
|
|
+ this.specVisiable = true
|
|
|
+ },
|
|
|
+ handleSpecificationDelete(row) {
|
|
|
+ const index = this.specifications.indexOf(row)
|
|
|
+ this.specifications.splice(index, 1)
|
|
|
+ this.specToProduct()
|
|
|
+ },
|
|
|
+ specToProduct() {
|
|
|
+ if (this.specifications.length === 0) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 根据specifications创建临时规格列表
|
|
|
+ var specValues = []
|
|
|
+ var spec = this.specifications[0].specification
|
|
|
+ var values = []
|
|
|
+ values.push(0)
|
|
|
+
|
|
|
+ for (var i = 1; i < this.specifications.length; i++) {
|
|
|
+ const aspec = this.specifications[i].specification
|
|
|
+
|
|
|
+ if (aspec === spec) {
|
|
|
+ values.push(i)
|
|
|
+ } else {
|
|
|
+ specValues.push(values)
|
|
|
+ spec = aspec
|
|
|
+ values = []
|
|
|
+ values.push(i)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ specValues.push(values)
|
|
|
+
|
|
|
+ // 根据临时规格列表生产商品规格
|
|
|
+ // 算法基于 https://blog.csdn.net/tyhj_sf/article/details/53893125
|
|
|
+ var productsIndex = 0
|
|
|
+ var products = []
|
|
|
+ var combination = []
|
|
|
+ var n = specValues.length
|
|
|
+ for (var s = 0; s < n; s++) {
|
|
|
+ combination[s] = 0
|
|
|
+ }
|
|
|
+ var index = 0
|
|
|
+ var isContinue = false
|
|
|
+ do {
|
|
|
+ var specifications = []
|
|
|
+ for (var x = 0; x < n; x++) {
|
|
|
+ var z = specValues[x][combination[x]]
|
|
|
+ specifications.push(this.specifications[z].value)
|
|
|
+ }
|
|
|
+ products[productsIndex] = {
|
|
|
+ id: productsIndex,
|
|
|
+ specificationList: specifications,
|
|
|
+ price: 0.00,
|
|
|
+ discountPrice: 0.00,
|
|
|
+ number: 0,
|
|
|
+ imgUrl: ''
|
|
|
+ }
|
|
|
+ productsIndex++
|
|
|
+
|
|
|
+ index++
|
|
|
+ combination[n - 1] = index
|
|
|
+ for (var j = n - 1; j >= 0; j--) {
|
|
|
+ if (combination[j] >= specValues[j].length) {
|
|
|
+ combination[j] = 0
|
|
|
+ index = 0
|
|
|
+ if (j - 1 >= 0) {
|
|
|
+ combination[j - 1] = combination[j - 1] + 1
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ isContinue = false
|
|
|
+ for (var p = 0; p < n; p++) {
|
|
|
+ if (combination[p] !== 0) {
|
|
|
+ isContinue = true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } while (isContinue)
|
|
|
+
|
|
|
+ this.products = products
|
|
|
+ },
|
|
|
+ handleSpecificationAdd() {
|
|
|
+ const valueArr = this.specForm.value.replace(/,/ig,',').replace(/\s*/g,"").split(',') // 替换所有的中文逗号为英文逗号并去空格
|
|
|
+ valueArr.forEach(element => {
|
|
|
+ let index = this.specifications.length - 1
|
|
|
+ for (var i = 0; i < this.specifications.length; i++) {
|
|
|
+ const v = this.specifications[i]
|
|
|
+ if (v.specification === this.specForm.specification) {
|
|
|
+ if (v.value !== element) {
|
|
|
+ index = i
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ this.specifications.splice(index + 1, 0, {
|
|
|
+ specification: this.specForm.specification,
|
|
|
+ value: element,
|
|
|
+ imgUrl: ''
|
|
|
+ })
|
|
|
+
|
|
|
+ });
|
|
|
+ this.specToProduct()
|
|
|
+ this.specVisiable = false
|
|
|
+ },
|
|
|
+ handleProductShow(row) {
|
|
|
+ this.productForm = Object.assign({}, row)
|
|
|
+ this.productVisiable = true
|
|
|
+ },
|
|
|
+ handleProductEdit() {
|
|
|
+ if (this.multipleSpec) {
|
|
|
+ for (var i = 0; i < this.products.length; i++) {
|
|
|
+ const v = this.products[i]
|
|
|
+ if (v.id === this.productForm.id) {
|
|
|
+ this.products.splice(i, 1, this.productForm)
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ for (var i = 0; i < this.productSingle.length; i++) {
|
|
|
+ const v = this.productSingle[i]
|
|
|
+ if (v.id === this.productForm.id) {
|
|
|
+ this.productSingle.splice(i, 1, this.productForm)
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ this.productVisiable = false
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+ updateItem() {
|
|
|
+ this.$refs.addItem.validate((valid, items) => {
|
|
|
+ if (valid) {
|
|
|
+ const action = this.addData.id ? editGoods : publishProduct
|
|
|
+ action({
|
|
|
+ product: this.addData,
|
|
|
+ appProductCommodities: this.multipleSpec? this.products:this.productSingle
|
|
|
+ }).then(res => {
|
|
|
+ if (res.code === 0) {
|
|
|
+ this.$message({
|
|
|
+ message: this.addData.id ? '修改成功!' : '添加成功!',
|
|
|
+ type: 'success'
|
|
|
+ })
|
|
|
+ this.$router.replace('/goods/list')
|
|
|
+ }
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ if (items && Object.keys(items).length>0) {
|
|
|
+ this.$message({
|
|
|
+ message: items[Object.keys(items)[0]][0].message,
|
|
|
+ type: 'warning'
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|
|
|
+<style lang="scss">
|
|
|
+.goods-add {
|
|
|
+ .tip {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #999;
|
|
|
+ }
|
|
|
+ .el-divider {
|
|
|
+ .is-left {
|
|
|
+ color: #409EFF;
|
|
|
+ font-size: 20px;
|
|
|
+ font-weight: bolder;
|
|
|
+ left: 10px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .flip-list-move {
|
|
|
+ transition: transform 0.5s;
|
|
|
+ }
|
|
|
+ .no-move {
|
|
|
+ transition: transform 0s;
|
|
|
+ }
|
|
|
+ .ghost {
|
|
|
+ opacity: 0.5;
|
|
|
+ background: #c8ebfb;
|
|
|
+ }
|
|
|
+ .list-group {
|
|
|
+ min-height: 120px;
|
|
|
+ padding: 20px;
|
|
|
+ border: 1px solid #EEE;
|
|
|
+ }
|
|
|
+ .list-empty {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ font-size: 14px;
|
|
|
+ color: #888;
|
|
|
+ height: 100px;
|
|
|
+ }
|
|
|
+ .list-group-item, .list-group-header {
|
|
|
+ padding: 0 16px;
|
|
|
+ height: 40px;
|
|
|
+ border-bottom: 1px solid #EBEEF5;
|
|
|
+ cursor: move;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ font-size: 14px;
|
|
|
+ color: #606266;
|
|
|
+ justify-content: space-between;
|
|
|
+ .f1 {
|
|
|
+ flex: 1;
|
|
|
+ }
|
|
|
+ .f2 {
|
|
|
+ flex: 2;
|
|
|
+ }
|
|
|
+ .tr {
|
|
|
+ text-align: right;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .list-group-header {
|
|
|
+ cursor: default;
|
|
|
+ background: #EBEEF5;
|
|
|
+ }
|
|
|
+ .list-group-item:hover {
|
|
|
+ background: #F3F6F9;
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|