You've already forked guangan
商品Address
This commit is contained in:
349
plugs/think-plugs-points-mall/src/view/goods/form.html
Normal file
349
plugs/think-plugs-points-mall/src/view/goods/form.html
Normal file
@ -0,0 +1,349 @@
|
||||
{extend name="main"}
|
||||
|
||||
{block name="button"}
|
||||
<button data-target-submit class='layui-btn layui-btn-sm'>保存数据</button>
|
||||
<button data-target-backup class="layui-btn layui-btn-sm layui-btn-danger" data-confirm="确认要取消编辑吗?">取消编辑</button>
|
||||
{/block}
|
||||
|
||||
{block name="content"}
|
||||
{include file='goods/form_style'}
|
||||
<form action="{:sysuri()}" method="post" data-auto="true" class="layui-form layui-card">
|
||||
<div class="layui-card-body">
|
||||
|
||||
<!--{notempty name='marks'}-->
|
||||
<div class="layui-form-item">
|
||||
<span class="help-label label-required-prev"><b>商品标签</b>Goods Mark</span>
|
||||
<div class="layui-textarea help-checks">
|
||||
{foreach $marks as $mark}
|
||||
<label class="think-checkbox">
|
||||
{if isset($vo.marks) && is_array($vo.marks) && in_array($mark, $vo.marks)}
|
||||
<input name="marks[]" type="checkbox" value="{$mark}" lay-ignore checked> {$mark}
|
||||
{else}
|
||||
<input name="marks[]" type="checkbox" value="{$mark}" lay-ignore> {$mark}
|
||||
{/if}
|
||||
</label>
|
||||
{/foreach}
|
||||
</div>
|
||||
</div>
|
||||
<!--{/notempty}-->
|
||||
|
||||
<!--{notempty name='cates'}-->
|
||||
<div class="layui-form-item block relative">
|
||||
<span class="help-label label-required-prev"><b>所属分类</b>Category Name</span>
|
||||
<select class="layui-select" lay-search name="cates">
|
||||
{foreach $cates as $cate}{if in_array($cate.id, $vo.cates)}
|
||||
<option selected value="{:arr2str($cate.ids)}">{:join(' > ', $cate.names)}</option>
|
||||
{else}
|
||||
<option value="{:arr2str($cate.ids)}">{:join(' > ', $cate.names)}</option>
|
||||
{/if}{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
<!--{/notempty}-->
|
||||
|
||||
<label class="layui-form-item block relative">
|
||||
<span class="help-label"><b>商品名称</b>Product Name</span>
|
||||
<input class="layui-input" name="name" placeholder="请输入商品名称" vali-name="商品名称" required value="{$vo.name|default=''}">
|
||||
</label>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<span class="help-label label-required-prev"><b>商品封面及轮播图片</b>Cover and Pictures</span>
|
||||
<table class="layui-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-center">商品封面</th>
|
||||
<th class="full-width">轮播图片 <span class="color-desc font-w1">( 轮播图片推荐的宽高比为 5:3 )</span></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-center text-top ta-p-0">
|
||||
<div class="help-images">
|
||||
<input name="cover" data-max-width="500" data-max-height="500" type="hidden" value="{$vo.cover|default=''}">
|
||||
<script>$('[name="cover"]').uploadOneImage();</script>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-left ta-p-0">
|
||||
<div class="help-images">
|
||||
<input name="slider" data-max-width="2048" data-max-height="1024" type="hidden" value="{$vo.slider|default=''}">
|
||||
<script>$('[name="slider"]').uploadMultipleImage();</script>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item" id="GoodsSpecsEditor">
|
||||
<span class="help-label label-required-prev">
|
||||
<b>商品规格</b><span class="color-red font-s12">( 规格填写后不允许再次增加规格分组,规格图片推荐的宽高比为 5:3 )</span>
|
||||
</span>
|
||||
<div class="ta-mb-10" v-for="(x,$index) in specs" :key="$index">
|
||||
<div class="goods-spec-box ta-pr-10" style="background:#ddd">
|
||||
<span class="text-center goods-spec-name">分组</span>
|
||||
<label class="label-required-null inline-block">
|
||||
<input v-model.trim="x.name" @change="trimSpace(x,'name')" vali-name="分组" placeholder="请输入分组名称" required>
|
||||
</label>
|
||||
<div class="pull-right flex">
|
||||
<a class="layui-btn layui-btn-sm layui-btn-primary goods-spec-btn" @click="addSpecVal(x.list)">增加</a>
|
||||
<a class="layui-btn layui-btn-sm layui-btn-primary goods-spec-btn" @click="upSpecRow(specs,$index)" :class="{false:'layui-btn-disabled'}[$index>0]">上移</a>
|
||||
<a class="layui-btn layui-btn-sm layui-btn-primary goods-spec-btn" @click="dnSpecRow(specs,$index)" :class="{false:'layui-btn-disabled'}[$index<specs.length-1]">下移</a>
|
||||
<div style="display:none" :class="{true:'layui-show'}[mode==='add' && specs.length>0]">
|
||||
<a class="layui-btn layui-btn-sm layui-btn-primary goods-spec-btn" @click="delSpecRow(specs,$index)" v-if="specs.length>1">删除</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="goods-spec-box" v-if="x.list && x.list.length>0">
|
||||
<label class="label-required-null nowrap" v-for="xx in x.list">
|
||||
<input lay-ignore @click="xx.check=checkListChecked(x.list,$event.target.checked)" v-model.trim="xx.check" type="checkbox">
|
||||
<input v-model.trim="xx.name" @change="trimSpace(xx,'name')" vali-name="规格" placeholder="请输入规格" required type="text">
|
||||
<a class="layui-icon layui-icon-close goods-spec-close" @click="delSpecVal(x.list,$index)" v-if="x.list.length>1"></a>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="mode==='add'">
|
||||
<a class="layui-btn layui-btn-sm layui-btn-primary" @click="addSpecRow(specs)" v-if="specs.length<3">增加规则分组</a>
|
||||
<p class="ta-mt-10"><span class="color-red">请完成属性修改后再编辑下面的规格信息,否则规格数据会丢失!</span></p>
|
||||
</div>
|
||||
<table class="layui-table goods-spec-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="layui-bg-gray" :colspan="specs.length">规格</th>
|
||||
<th colspan="3">商品价格</th>
|
||||
<th colspan="5">其他属性</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="nowrap layui-bg-gray" v-for="x in specs"><b>{{x.name}}</b></th>
|
||||
<th class="nowrap" @click="batchSet('cost',2,'请输入商品成本价格')"><b>成本价</b><i class="layui-icon"></i></th>
|
||||
<th class="nowrap" @click="batchSet('market',2,'请输入商品市场价格')"><b>市场价</b><i class="layui-icon"></i></th>
|
||||
<th class="nowrap" @click="batchSet('selling',2,'请输入商品销售积分格')"><b>销售积分</b><i class="layui-icon"></i></th>
|
||||
<th class="nowrap" @click="batchSet('virtual',0,'请输入虚拟销量数值')"><b>虚拟销量</b><i class="layui-icon"></i></th>
|
||||
<th class="nowrap" @click="batchSet('express',0,'请输入快递计费系数')"><b>快递系数</b><i class="layui-icon"></i></th>
|
||||
<th class="nowrap" width="9%" @click="batchSet('gsku',null,'请输入商品SKU代码')"><b>商品SKU</b> <i class="layui-icon"></i></th>
|
||||
<th class="nowrap" width="6%">规格图片</th>
|
||||
<th class="nowrap" width="6%"><b>销售状态</b></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(item,hash) in items" :key="hash">
|
||||
<td class="layui-bg-gray nowrap text-center" v-if="s.check" v-for="s in item.s">{{s.name}}</td>
|
||||
<td><label><input class="layui-input" @blur="syncSet(hash)" v-model.trim="item.v.cost"></label></td>
|
||||
<td><label><input class="layui-input" @blur="syncSet(hash)" v-model.trim="item.v.market"></label></td>
|
||||
<td><label><input class="layui-input" @blur="syncSet(hash)" v-model.trim="item.v.selling"></label></td>
|
||||
<td><label><input class="layui-input" @blur="syncSet(hash)" v-model.trim="item.v.virtual"></label></td>
|
||||
<td><label><input class="layui-input" @blur="syncSet(hash)" v-model.trim="item.v.express"></label></td>
|
||||
<td><label><input class="layui-input font-code" v-model.trim="item.v.gsku"></label></td>
|
||||
<td class="upload-image-xs ta-p-0 text-center">
|
||||
<upload-image v-model.trim="item.v.image" :showinput="false"></upload-image>
|
||||
</td>
|
||||
<td class="layui-bg-gray"><label class="think-checkbox"><input lay-ignore v-model.trim="item.v.status" type="checkbox"></label></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="color-desc">请注意商品的 sku 在系统中仅作为显示之用,系统会根据规格生成哈希值作为商品唯一区别码!</p>
|
||||
<label class="layui-hide">
|
||||
<textarea class="layui-textarea" name="specs">{{JSON.stringify(specs)}}</textarea>
|
||||
<textarea class="layui-textarea" name="items">{{JSON.stringify(attrs)}}</textarea>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<label class="layui-form-item block">
|
||||
<span class="help-label"><b>商品简介描述</b></span>
|
||||
<textarea class="layui-textarea" name="remark" placeholder="请输入商品简介描述">{$vo.remark|default=''|raw}</textarea>
|
||||
</label>
|
||||
|
||||
<div class="layui-form-item block">
|
||||
<span class="help-label label-required-prev"><b>商品富文本详情</b></span>
|
||||
<textarea class="layui-hide" name="content">{$vo.content|default=''|raw}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="hr-line-dashed ta-mt-40"></div>
|
||||
<input name="code" type="hidden" value="{$vo.code}">
|
||||
|
||||
<div class="layui-form-item text-center">
|
||||
<button class="layui-btn layui-btn-danger" type="button" data-target-backup>取消编辑</button>
|
||||
<button class="layui-btn" type="submit">保存商品</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{/block}
|
||||
|
||||
{block name='script'}
|
||||
<label class="layui-hide">
|
||||
<textarea id="GoodsSpecs">{$vo.specs|raw|default=''}</textarea>
|
||||
<textarea id="GoodsItems">{$vo.items|raw|default=''}</textarea>
|
||||
</label>
|
||||
|
||||
<script>
|
||||
/*! 加载扩展插件 */
|
||||
require(['md5', 'vue', 'ckeditor'], function (md5, Vue) {
|
||||
// 创建富文本
|
||||
window.createEditor('[name=content]', {height: 500});
|
||||
// 图片上传组件
|
||||
Vue.component('UploadImage', {
|
||||
data: () => ({eid: Math.random().toString().replace('0.', 'up')}),
|
||||
model: {prop: 'mvalue', event: 'change'}, props: {mvalue: {type: String, default: ''}},
|
||||
template: '<input class="layui-hide" v-model="mvalue" :id="eid" readonly>',
|
||||
mounted: function () {
|
||||
this.$nextTick(() => $('#' + this.eid).uploadOneImage().on('change', e => this.$emit('change', e.target.value)));
|
||||
}
|
||||
});
|
||||
// 字段格式规则
|
||||
let rules = {
|
||||
image: '_',
|
||||
cost: '(parseFloat(_)||0).toFixed(2)',
|
||||
market: '(parseFloat(_)||0).toFixed(2)',
|
||||
selling: '(parseFloat(_)||0).toFixed(2)',
|
||||
balance: '(parseFloat(_)||0).toFixed(2)',
|
||||
integral: '(parseFloat(_)||0).toFixed(0)',
|
||||
express: '(parseFloat(_)||0).toFixed(0)',
|
||||
virtual: '(parseFloat(_)||0).toFixed(0)',
|
||||
allow_balance: '(parseFloat(_)||0).toFixed(2)',
|
||||
allow_integral: '(parseFloat(_)||0).toFixed(0)',
|
||||
};
|
||||
// 历史及缓存数据
|
||||
let cache = {};
|
||||
layui.each(JSON.parse($('#GoodsItems').val() || '{}') || {}, function (k, v) {
|
||||
for (let i in v) setValue(k, i, v[i]);
|
||||
});
|
||||
// 创建 Vue2 实例
|
||||
let app = new Vue({
|
||||
el: '#GoodsSpecsEditor', data: () => ({
|
||||
mode: '{$mode|default="add"}', items: {}, attrs: {},
|
||||
specs: JSON.parse(JSON.parse($('#GoodsSpecs').val() || '[]')) || []
|
||||
}),
|
||||
created: function () {
|
||||
this.specs.length < 1 && addSpecRow(this.specs);
|
||||
},
|
||||
watch: {
|
||||
specs: {
|
||||
deep: true, immediate: true, handler: function (values) {
|
||||
// 笛卡尔积生成商品SKU多规格算法
|
||||
let items = {}, attrs = [], list = values.reduce((a, b) => {
|
||||
let res = [];
|
||||
a.map(x => b.list.map(y => y.check && res.push(x.concat(Object.assign({group: b.name}, y)))));
|
||||
return res;
|
||||
}, [[]]);
|
||||
// 初始化商品规格同步变量值
|
||||
list.map(function (cols) {
|
||||
let keys = [], specs = [], unids = [];
|
||||
cols.map(v => keys.push(v.group + '::' + v.name) && specs.push(v) && unids.push(v.unid));
|
||||
let hash = md5.hash("{$vo.code}#" + unids.sort().join(';')), values = {
|
||||
hash: hash,
|
||||
spec: keys.join(';;'),
|
||||
gsku: getValue(hash, 'gsku', withRandString(14, 'S')),
|
||||
image: getValue(hash, 'image', ''),
|
||||
status: !!getValue(hash, 'status', 1),
|
||||
cost: getValue(hash, 'cost', '0.00'),
|
||||
market: getValue(hash, 'market', '0.00'),
|
||||
selling: getValue(hash, 'selling', '0.00'),
|
||||
balance: getValue(hash, 'balance', '0.00'),
|
||||
integral: getValue(hash, 'integral', '0'),
|
||||
express: getValue(hash, 'express', '1'),
|
||||
virtual: getValue(hash, 'virtual', '0'),
|
||||
allow_balance: getValue(hash, 'allow_balance', '0.00'),
|
||||
allow_integral: getValue(hash, 'allow_integral', '0'),
|
||||
};
|
||||
items[hash] = {s: specs, v: values};
|
||||
attrs.push(values)
|
||||
})
|
||||
this.attrs = attrs;
|
||||
this.items = items;
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/*! 同步格式化值 */
|
||||
syncSet: function (hash) {
|
||||
let v = this.items[hash].v;
|
||||
for (let k in v) v[k] = setValue(hash, k, v[k])
|
||||
},
|
||||
/*! 批量设置数值 */
|
||||
batchSet: function (name, fixed, title) {
|
||||
let min = (0 / Math.pow(10, parseInt(fixed))).toFixed(fixed), max = (999999).toFixed(fixed);
|
||||
layer.prompt({
|
||||
title: title || (fixed === null ? '请输入内容' : '请输入数量【 取值范围:1 - 999999 】'),
|
||||
formType: 0, value: fixed === null ? '' : min, success: function ($el) {
|
||||
$el.find('.layui-layer-input').attr({'data-value-min': min, 'data-value-max': max, 'data-blur-number': fixed});
|
||||
}
|
||||
}, function (value, index) {
|
||||
if (fixed !== null) value = (parseFloat(value) || 0).toFixed(fixed);
|
||||
for (let i in app.items) app.items[i].v[name] = value;
|
||||
layer.close(index) || app.$forceUpdate()
|
||||
});
|
||||
},
|
||||
/*! 去除空白字符 */
|
||||
trimSpace: function (x, i) {
|
||||
return x[i] = (x[i] || '').replace(/[\s,;'"]+/ig, '');
|
||||
},
|
||||
/*! 判断规则选择 */
|
||||
checkListChecked: function (data, check) {
|
||||
for (let i in data) if (data[i].check) return check;
|
||||
return true;
|
||||
},
|
||||
/*! 下移整行规格分组 */
|
||||
dnSpecRow: function (items, index) {
|
||||
index + 1 < items.length && (item => items.splice(index + 1, 1) && items.splice(index, 0, item))(items[index + 1]);
|
||||
},
|
||||
/*! 上移整行规格分组 */
|
||||
upSpecRow: function (items, index) {
|
||||
index > 0 && (item => items.splice(index - 1, 1) && items.splice(index, 0, item))(items[index - 1]);
|
||||
},
|
||||
/*! 增加整行规格分组 */
|
||||
addSpecRow: function (data) {
|
||||
addSpecRow(data);
|
||||
},
|
||||
/*! 移除整行规格分组 */
|
||||
delSpecRow: function (items, index) {
|
||||
items.splice(index, 1);
|
||||
},
|
||||
/*! 增加分组的属性 */
|
||||
addSpecVal: function (data) {
|
||||
addSpecVal(data);
|
||||
},
|
||||
/*! 移除分组的属性 */
|
||||
delSpecVal: function (data, $index) {
|
||||
data.splice($index, 1);
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
/*! 动态设置内容 */
|
||||
function setValue(hash, name, value, filter) {
|
||||
filter = filter || rules[name] || null;
|
||||
if (typeof filter === 'string' && filter.indexOf('_') > -1) {
|
||||
value = eval(filter.replace('_', "'" + value + "'"));
|
||||
}
|
||||
cache[hash] = cache[hash] || {};
|
||||
return cache[hash][name] = value;
|
||||
}
|
||||
|
||||
/*!动态读取内容 */
|
||||
function getValue(hash, name, value) {
|
||||
let _cache = cache[hash] || {};
|
||||
if (typeof _cache[name] === 'undefined') {
|
||||
setValue(hash, name, value, '_')
|
||||
_cache = cache[hash] || {};
|
||||
}
|
||||
return _cache[name];
|
||||
}
|
||||
|
||||
// 创建分组
|
||||
function addSpecRow(data) {
|
||||
data.push({name: '规格分组' + (data.length || ''), list: addSpecVal([])})
|
||||
}
|
||||
|
||||
// 创建规格
|
||||
function addSpecVal(data) {
|
||||
data.push({name: '规格属性' + (data.length || ''), unid: withRandString(16), check: true});
|
||||
return data;
|
||||
}
|
||||
|
||||
// 随机字符串
|
||||
function withRandString(length, prefix) {
|
||||
return (function (time, code) {
|
||||
code += parseInt(time.substring(0, 1)) + parseInt(time.substring(1, 2)) + time.substring(2);
|
||||
while (code.length < length) code += (Math.random()).toString().replace('.', '');
|
||||
return code.substring(0, length);
|
||||
})(Date.now().toString(), prefix || '')
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{/block}
|
Reference in New Issue
Block a user