order-food-admin/resources/views/admin/extensions/sku.blade.php

505 lines
22 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<div class="{{$viewClass['form-group']}}">
<label class="{{$viewClass['label']}} control-label">{{$label}}</label>
<div class="{{$viewClass['field']}}">
@include('admin::form.error')
<div {!! $attributes !!} style="width: 100%; height: 100%;">
<div class="card">
<div class="card-body">
<div v-for="(item, index) in specification" :key="index">
<span v-if="!cacheSpecification[index].status">@{{ item.name }}</span>
<span style="float:right;cursor: pointer" v-on:click="delSpec(index)"><i class="feather icon-x"></i></span>
<div class="input-group mb-2" v-if="cacheSpecification[index].status" style="width: 25%">
<input type="text" class="form-control" v-model="cacheSpecification[index].name" placeholder="输入产品规格" @keyup.native.enter="saveSpec(index)">
<div class="input-group-prepend" v-on:click="saveSpec(index)">
<span class="input-group-text"><i class="feather icon-check"></i></span>
</div>
</div>
<div class="row" style="padding-top: 10px">
<div class="col-sm-3 input-group mb-2"
v-for="(tag, j) in item.value" :key="j"
>
<input type="text"
class="form-control"
:value="tag"
>
<div class="input-group-append" v-on:click="delSpecTag(index, j)">
<span style="cursor: pointer" class="input-group-text"><i class="feather icon-x"></i></span>
</div>
</div>
</div>
<div class="input-group mb-3" style="width: 25%;cursor: pointer">
<input type="text" class="form-control" v-model="addValues[index]" placeholder="多个产品属性以空格隔开">
<div class="input-group-append" v-on:click="addSpecTag(index)">
<span class="input-group-text" ><i class="feather icon-check"></i></span>
</div>
</div>
<hr>
</div>
</div>
<div class="card-footer">
<button type="button" :disabled="specification.length >= 5" class="btn btn-primary btn-sm" v-on:click="addSpec">添加规格值</button>
</div>
</div>
<div class="card">
<div class="card-header">
规格表格
</div>
<div class="card-body">
<table class="table" cellspacing="0" cellpadding="0">
<thead>
<tr>
<th
v-for="(item, index) in specification"
:key="index">
@{{item.name}}
</th>
{{-- <th>规格编码</th>--}}
<th>成本价(元)</th>
<th>销售价(元)</th>
<th>库存</th>
<th>是否启用</th>
</tr>
</thead>
<tbody v-if="specification[0] && specification[0].value.length">
<tr
:key="index"
v-for="(item, index) in countSum(0)">
<template v-for="(n, specIndex) in specification.length">
<td
style="vertical-align: middle!important;text-align:right"
v-if="showTd(specIndex, index)"
:key="n"
:rowspan="countSum(n)">
@{{getSpecAttr(specIndex, index)}}
</td>
</template>
{{-- <td>--}}
{{-- <input--}}
{{-- type="text"--}}
{{-- class="form-control"--}}
{{-- :disabled="!childProductArray[index].isUse"--}}
{{-- v-model="childProductArray[index].childProductNo"--}}
{{-- v-on:blur="handleNo(index)"--}}
{{-- placeholder="输入商品规格编号">--}}
{{-- </td>--}}
<td>
<input
type="text"
class="form-control"
v-model.number="childProductArray[index].childProductCost"
placeholder="输入成本价"
:disabled="!childProductArray[index].isUse">
</td>
<td>
<input
type="text"
class="form-control"
v-model.number="childProductArray[index].childProductPrice"
placeholder="输入销售价"
:disabled="!childProductArray[index].isUse">
</td>
<td>
<input
type="text"
class="form-control"
v-model.number="childProductArray[index].childProductStock"
placeholder="输入库存"
:disabled="!childProductArray[index].isUse">
</td>
<td>
<input type="checkbox" name="on_sale" v-on:change="(val) => {handleUserChange(index, val)}" class="field_on_sale" data-size="small" data-color="#586cb1" v-model="childProductArray[index].isUse" data-plugin="form-W8oh1vxmswitchery">
</td>
</tr>
<tr>
<td colspan="8" class="wh-foot">
<span class="label">批量设置:</span>
<template v-if="isSetListShow">
<button type="button" class="btn btn-primary" v-on:click="openBatch('childProductCost')">成本价</button>
<button type="button" class="btn btn-primary" v-on:click="openBatch('childProductStock')">库存</button>
<button type="button" class="btn btn-primary" v-on:click="openBatch('childProductPrice')">销售价</button>
</template>
<template v-else>
<form class="form-inline">
<input type="number" class="form-control" style="width:200px;" v-model.number="batchValue" placeholder="输入要设置的数量"></input>
<button type="button" v-on:click="setBatch" class="btn btn-primary"><i class="feather icon-check"></i></button>
<button type="button" v-on:click="cancelBatch" class="btn btn-danger"><i class="feather icon-x"></i></button>
</form>
</template>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<input type="hidden" name="{{$name}}" :value="getSku" />
</div>
@include('admin::form.help-block')
</div>
</div>
<script init="{!! $selector !!}">
// 为Object添加一个原型方法判断两个对象是否相等
function objEquals (object1, object2) {
// For the first loop, we only check for types
for (let propName in object1) {
// Check for inherited methods and properties - like .equals itself
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty
// Return false if the return value is different
if (object1.hasOwnProperty(propName) !== object2.hasOwnProperty(propName)) {
return false
// Check instance type
} else if (typeof object1[propName] !== typeof object2[propName]) {
// Different types => not equal
return false
}
}
// Now a deeper check using other objects property names
for (let propName in object2) {
// We must check instances anyway, there may be a property that only exists in object2
// I wonder, if remembering the checked values from the first loop would be faster or not
if (object1.hasOwnProperty(propName) !== object2.hasOwnProperty(propName)) {
return false
} else if (typeof object1[propName] !== typeof object2[propName]) {
return false
}
// If the property is inherited, do not check any more (it must be equa if both objects inherit it)
if (!object1.hasOwnProperty(propName)) {
continue
}
// Now the detail check and recursion
// This returns the script back to the array comparing
/** REQUIRES Array.equals**/
if (object1[propName] instanceof Array && object2[propName] instanceof Array) {
// recurse into the nested arrays
if (objEquals(!object1[propName], object2[propName])) {
return false
}
} else if (object1[propName] instanceof Object && object2[propName] instanceof Object) {
// recurse into another objects
// console.log("Recursing to compare ", this[propName],"with",object2[propName], " both named \""+propName+"\"");
if (objEquals(!object1[propName], object2[propName])) {
return false
}
// Normal value comparison for strings and numbers
} else if (object1[propName] !== object2[propName]) {
return false
}
}
// If everything passed, let's say YES
return true
}
var vm = new Vue({
el: '#' + id,
data() {
return {
specificationStatus: false, // 显示规格列表
// 规格
specification: [],
// 子规格
childProductArray: [],
// 用来存储要添加的规格属性
addValues: [],
// 默认商品编号
defaultProductNo: 'PRODUCTNO_',
// 批量设置相关
isSetListShow: true,
batchValue: '', // 批量设置所绑定的值
currentType: '', // 要批量设置的类型
cacheSpecification: [] // 缓存规格名称
}
},
computed: {
// 所有sku的id
stockSpecArr () {
return this.childProductArray.map(item => item.childProductSpec)
},
getSku() {
return JSON.stringify(this.childProductArray)
}
},
created() {
this.createData()
},
methods: {
// 创建模拟数据
createData() {
const arr = ['颜色', '尺寸']
const arr2 = ['黑色 白色 蓝色 红色', 's m xl']
for (let i = 0; i < 2; i++) {
// 添加数据
this.addSpec()
// 数据
this.specification[i].name = arr[i]
this.addValues[i] = arr2[i]
// 缓存按钮键值
this.cacheSpecification[i].status = false
this.cacheSpecification[i].name = arr[i]
// 构建
this.addSpecTag(i)
}
},
// 添加规格项目
addSpec () {
if (this.specification.length < 5) {
this.cacheSpecification.push({
status: true,
name: ''
})
this.specification.push({
name: '',
value: []
})
}
},
// 添加规格属性
addSpecTag (index) {
let str = this.addValues[index] || ''
if (!str.trim() || !this.cacheSpecification[index].name.trim()) {
Dcat.error('名称不能为空,请注意修改')
return
} // 判空
str = str.trim()
let arr = str.split(/\s+/) // 使用空格分割成数组
let newArr = this.specification[index].value.concat(arr)
newArr = Array.from(new Set(newArr)) // 去重
this.$set(this.specification[index], 'value', newArr)
this.clearAddValues(index)
this.handleSpecChange('add')
this.specification[index].name = this.cacheSpecification[index].name
this.cacheSpecification[index].status = false
},
// 清空 addValues
clearAddValues (index) {
this.$set(this.addValues, index, '')
},
/**
* [handleSpecChange 监听标签的变化,当添加标签时求出每一行的hash id再动态变更库存信息当删除标签时先清空已有库存信息然后将原有库存信息暂存到stockCopy中方便后面调用]
* @param {[string]} option ['add'|'del' 操作类型,添加或删除]
* @return {[type]} [description]
*/
handleSpecChange (option) {
const stockCopy = JSON.parse(JSON.stringify(this.childProductArray))
if (option === 'del') {
this.childProductArray = []
}
for (let i = 0; i < this.countSum(0); i++) {
this.changeStock(option, i, stockCopy)
}
},
/*
计算属性的乘积
@params
specIndex 规格项目在 advancedSpecification 中的序号
*/
countSum (specIndex) {
let num = 1
this.specification.forEach((item, index) => {
if (index >= specIndex && item.value.length) {
num *= item.value.length
}
})
return num
},
/**
* [根据规格,动态改变库存相关信息]
* @param {[string]} option ['add'|'del' 操作类型,添加或删除]
* @param {[array]} stockCopy [库存信息的拷贝]
* @return {[type]} [description]
*/
changeStock (option, index, stockCopy) {
let childProduct = {
childProductId: 0,
childProductSpec: this.getChildProductSpec(index),
// childProductNo: this.defaultProductNo + index,
childProductStock: 0,
childProductPrice: 0,
childProductCost: 0,
isUse: true
}
const spec = childProduct.childProductSpec
if (option === 'add') {
// 如果此id不存在说明为新增属性则向 childProductArray 中添加一条数据
if (this.stockSpecArr.findIndex((item) => objEquals(spec, item)) === -1) {
this.$set(this.childProductArray, index, childProduct)
}
} else if (option === 'del') {
// 因为是删除操作理论上所有数据都能从stockCopy中获取到
let origin = ''
stockCopy.forEach(item => {
if (objEquals(spec, item.childProductSpec)) {
origin = item
return false
}
})
this.childProductArray.push(origin || childProduct)
}
},
// 获取childProductArray的childProductSpec属性
getChildProductSpec (index) {
let obj = {}
this.specification.forEach((item, specIndex) => {
obj[item.name] = this.getSpecAttr(specIndex, index)
})
return obj
},
/*
根据传入的属性值,拿到相应规格的属性
@params
specIndex 规格项目在 advancedSpecification 中的序号
index 所有属性在遍历时的序号
*/
getSpecAttr (specIndex, index) {
// 获取当前规格项目下的属性值
const currentValues = this.specification[specIndex].value
let indexCopy
// 判断是否是最后一个规格项目
if (this.specification[specIndex + 1] && this.specification[specIndex + 1].value.length) {
indexCopy = index / this.countSum(specIndex + 1)
} else {
indexCopy = index
}
const i = Math.floor(indexCopy % currentValues.length)
if (i.toString() !== 'NaN') {
return currentValues[i]
} else {
return ''
}
},
// 删除规格属性
delSpecTag (index, num) {
this.specification[index].value.splice(num, 1)
this.handleSpecChange('del')
},
// 保存规格名
saveSpec(index) {
if (!this.cacheSpecification[index].name.trim()) {
this.$message.error('名称不能为空,请注意修改')
return
}
// 保存需要验证名称是否重复
if (this.specification[index].name === this.cacheSpecification[index].name) {
this.cacheSpecification[index].status = false
} else {
if (this.verifyRepeat(index)) {
// 如果有重复的,禁止修改
this.$message.error('名称重复,请注意修改')
} else {
this.specification[index].name = this.cacheSpecification[index].name
this.cacheSpecification[index].status = false
}
}
},
// 删除规格项目
delSpec (index) {
this.specification.splice(index, 1)
this.handleSpecChange('del')
},
verifyRepeat(index) {
let flag = false
this.specification.forEach((value, j) => {
// 检查除了当前选项以外的值是否和新的值想等,如果相等,则禁止修改
if (index !== j) {
if (this.specification[j].name === this.cacheSpecification[index].name) {
flag = true
}
}
})
return flag
},
// 根据传入的条件来判断是否显示该td
showTd (specIndex, index) {
// 如果当前项目下没有属性,则不显示
if (!this.specification[specIndex]) {
return false
// 自己悟一下吧
} else if (index % this.countSum(specIndex + 1) === 0) {
return true
} else {
return false
}
},
// 监听规格启用操作
handleUserChange (index, value) {
// 启用规格时,生成不重复的商品编号;关闭规格时,清空商品编号
if (value) {
let No = this.makeProductNoNotRepet(index)
this.$set(this.childProductArray[index], 'childProductNo', No)
} else {
this.$set(this.childProductArray[index], 'childProductNo', '')
}
},
// 监听商品编号的blur事件
handleNo (index) {
// 1.当用户输入完商品编号时,判断是否重复,如有重复,则提示客户并自动修改为不重复的商品编号
const value = this.childProductArray[index].childProductNo
let isRepet
this.childProductArray.forEach((item, i) => {
if (item.childProductNo === value && i !== index) {
isRepet = true
}
})
if (isRepet) {
this.$message({
type: 'warning',
message: '不允许输入重复的商品编号'
})
this.$set(this.childProductArray[index], 'childProductNo', this.makeProductNoNotRepet(index))
}
},
// 生成不重复的商品编号
makeProductNoNotRepet (index) {
let No
let i = index
let isRepet = true
while (isRepet) {
No = this.defaultProductNo + i
isRepet = this.isProductNoRepet(No)
i++
}
return No
},
// 商品编号判重
isProductNoRepet (No) {
const result = this.childProductArray.findIndex((item) => {
return item.childProductNo === No
})
return result > -1
},
// 打开设置框
openBatch (type) {
this.currentType = type
this.isSetListShow = false
},
// 批量设置
setBatch () {
if (typeof this.batchValue === 'string') {
this.$message({
type: 'warning',
message: '请输入正确的值'
})
return
}
this.childProductArray.forEach(item => {
if (item.isUse) {
item[this.currentType] = this.batchValue
}
})
this.cancelBatch()
},
// 取消批量设置
cancelBatch () {
this.batchValue = ''
this.currentType = ''
this.isSetListShow = true
}
}
})
</script>