This commit is contained in:
2025-10-29 15:32:26 +08:00
parent d90614805b
commit b7462657cd
78921 changed files with 2753938 additions and 71 deletions

View File

@@ -0,0 +1,73 @@
<?php
namespace addon\seckill\api\controller;
use app\api\controller\BaseApi;
use addon\seckill\model\SeckillOrderCreate as OrderCreateModel;
use app\api\controller\BaseOrderCreateApi;
/**
* 订单创建
* @author Administrator
*
*/
class Ordercreate extends BaseOrderCreateApi
{
/**
* 创建订单
*/
public function create()
{
$token = $this->checkToken();
if ($token[ 'code' ] < 0) return $this->response($token);
$order_create = new OrderCreateModel();
$data = [
'order_key' => $this->params['order_key'] ?? '',
'is_balance' => $this->params['is_balance'] ?? 0,//是否使用余额
];
$res = $order_create->setParam(array_merge($data, $this->getInputParam(), $this->getCommonParam(), $this->getDeliveryParam(), $this->getInvoiceParam()))->create();
return $this->response($res);
}
/**
* 计算信息
*/
public function calculate()
{
$token = $this->checkToken();
if ($token[ 'code' ] < 0) return $this->response($token);
$order_create = new OrderCreateModel();
$data = [
'order_key' => $this->params['order_key'] ?? '',//订单缓存
'is_balance' => $this->params['is_balance'] ?? 0,//是否使用余额
];
$res = $order_create->setParam(array_merge($data, $this->getCommonParam(), $this->getDeliveryParam(), $this->getInvoiceParam()))->confirm();
return $this->response($this->success($res));
}
/**
* 待支付订单 数据初始化
* @return string
*/
public function payment()
{
$token = $this->checkToken();
if ($token[ 'code' ] < 0) return $this->response($token);
$order_create = new OrderCreateModel();
$data = [
'id' => $this->params[ 'seckill_id' ] ?? '',//秒杀id
'num' => $this->params[ 'num' ] ?? 1,//秒杀商品数量(买几套)
'sku_id' => $this->params[ 'sku_id' ] ?? '',//
];
if (empty($data[ 'id' ])) {
return $this->response($this->error('', '缺少必填参数商品数据'));
}
if ($data[ 'num' ] < 0) {
return $this->response($this->error('', '商品数量不能小于0'));
}
$res = $order_create->setParam(array_merge($data, $this->getCommonParam(), $this->getDeliveryParam()))->orderPayment();
return $this->response($this->success($res));
}
}

View File

@@ -0,0 +1,68 @@
<?php
/**
*/
namespace addon\seckill\api\controller;
use addon\seckill\model\Seckill as SeckillModel;
use app\api\controller\BaseApi;
/**
* 秒杀
*/
class Seckill extends BaseApi
{
/**
* 列表信息
*/
public function lists()
{
$today_time = strtotime(date("Y-m-d"), time());
$time = time() - $today_time;//当日时间戳
$condition = [
[ 'site_id', '=', $this->site_id ],
[ 'seckill_end_time', '>=', $time ]
];
$order = 'seckill_start_time asc';
$field = 'id,name,seckill_start_time,seckill_end_time';
$seckill_model = new SeckillModel();
$today_list = $seckill_model->getGoodsSeckillTimeList($condition, $field, $order);
$today_list = is_array($today_list[ 'data' ]) ? array_values($today_list[ 'data' ]) : [];
foreach ($today_list as $key => $val) {
$val = $seckill_model->transformSeckillTime($val);
$today_list[ $key ][ 'seckill_start_time_show' ] = "{$val['start_hour']}:{$val['start_minute']}:{$val['start_second']}";
$today_list[ $key ][ 'seckill_end_time_show' ] = "{$val['end_hour']}:{$val['end_minute']}:{$val['end_second']}";
$today_list[ $key ][ 'type' ] = "today";
}
$condition = [
[ 'site_id', '=', $this->site_id ],
[ 'seckill_end_time', '<', $time ]
];
$tomorrow_list = $seckill_model->getGoodsSeckillTimeList($condition, $field, $order);
$tomorrow_list = is_array($tomorrow_list[ 'data' ]) ? array_values($tomorrow_list[ 'data' ]) : [];
foreach ($tomorrow_list as $key => $val) {
$val = $seckill_model->transformSeckillTime($val);
$tomorrow_list[ $key ][ 'seckill_start_time_show' ] = "{$val['start_hour']}:{$val['start_minute']}:{$val['start_second']}";
$tomorrow_list[ $key ][ 'seckill_end_time_show' ] = "{$val['end_hour']}:{$val['end_minute']}:{$val['end_second']}";
$tomorrow_list[ $key ][ 'type' ] = "tomorrow";
}
$res = [
'list' => array_merge($today_list, $tomorrow_list)
];
return $this->response($this->success($res));
}
}

View File

@@ -0,0 +1,222 @@
<?php
/**
*/
namespace addon\seckill\api\controller;
use addon\seckill\model\Poster;
use addon\seckill\model\Seckill as SeckillModel;
use addon\seckill\model\SeckillOrder;
use addon\seckill\model\SeckillOrderCreate;
use app\api\controller\BaseApi;
use app\api\controller\Goodssku;
use app\model\goods\GoodsApi;
/**
* 秒杀商品
*/
class Seckillgoods extends BaseApi
{
/**
* 【PC端在用】基础信息
*/
public function info()
{
$seckill_id = $this->params[ 'seckill_id' ] ?? 0;
$sku_id = $this->params[ 'sku_id' ] ?? 0;
if (empty($seckill_id)) {
return $this->response($this->error('', 'REQUEST_ID'));
}
if (empty($sku_id)) {
return $this->response($this->error('', 'REQUEST_ID'));
}
$seckill_model = new SeckillModel();
$order_model = new SeckillOrder();
$condition = [
[ 'ps.id', '=', $seckill_id ],
[ 'psg.sku_id', '=', $sku_id ],
[ 'psg.site_id', '=', $this->site_id ],
[ 'psg.status', '=', 1 ],
[ 'ps.status', '=', 1 ],
[ 'g.goods_state', '=', 1 ],
[ 'g.is_delete', '=', 0 ]
];
$goods_sku_detail = $seckill_model->getSeckillGoodsInfo($condition, '')[ 'data' ];
$res[ 'goods_sku_detail' ] = $goods_sku_detail;
if (!empty($goods_sku_detail)) {
$num = $order_model->getGoodsSeckillNum($seckill_id);
$time_data = $seckill_model->getSeckillInfo($seckill_id)[ 'data' ];
$goods_sku_detail[ 'sale_num' ] = $num;
$goods_sku_detail[ 'seckill_start_time' ] = $time_data[ 'seckill_start_time' ];
$goods_sku_detail[ 'seckill_end_time' ] = $time_data[ 'seckill_end_time' ];
//判断商品规格项
$goods_spec_format = $seckill_model->getGoodsSpecFormat($seckill_id, $this->site_id, $goods_sku_detail[ 'goods_spec_format' ]);
$res[ 'goods_sku_detail' ][ 'goods_spec_format' ] = json_encode($goods_spec_format);
} else {
$sku_id = $seckill_model->getGoodsSpecFormat($seckill_id, $this->site_id, '');
$res = [ 'type' => 'again', 'sku_id' => $sku_id ];
}
return $this->response($this->success($res));
}
/**
* 详情信息
*/
public function detail()
{
$this->checkToken();
$seckill_id = $this->params[ 'seckill_id' ] ?? 0;
if (empty($seckill_id)) {
return $this->response($this->error('', 'REQUEST_ID'));
}
$seckill_model = new SeckillModel();
$condition = [
[ 'ps.id', '=', $seckill_id ],
[ 'psg.site_id', '=', $this->site_id ],
[ 'psg.status', '=', 1 ],
[ 'ps.status', '=', 1 ],
[ 'g.goods_state', '=', 1 ],
[ 'g.is_delete', '=', 0 ]
];
$goods_sku_detail = $seckill_model->getSeckillGoodsInfo($condition)[ 'data' ];
$time_data = $seckill_model->getSeckillInfo($seckill_id)[ 'data' ];
$goods_sku_detail[ 'seckill_start_time' ] = $time_data[ 'seckill_start_time' ];
$goods_sku_detail[ 'seckill_end_time' ] = $time_data[ 'seckill_end_time' ];
$res[ 'goods_sku_detail' ] = $goods_sku_detail;
if (empty($goods_sku_detail)) return $this->response($this->error($res));
if (!empty($goods_sku_detail[ 'goods_spec_format' ])) {
//判断商品规格项
$goods_spec_format = $seckill_model->getGoodsSpecFormat($seckill_id, $this->site_id, $goods_sku_detail[ 'goods_spec_format' ]);
$res[ 'goods_sku_detail' ][ 'goods_spec_format' ] = json_encode($goods_spec_format);
}
// 处理公共数据
$goods_sku_api = new GoodsApi();
$goods_sku_api->handleGoodsDetailData($res[ 'goods_sku_detail' ], $this->member_id, $this->site_id);
return $this->response($this->success($res));
}
/**
* 查询商品SKU集合
* @return false|string
*/
public function goodsSku()
{
$goods_id = $this->params[ 'goods_id' ] ?? 0;
$seckill_id = $this->params[ 'seckill_id' ] ?? 0;
if (empty($goods_id)) {
return $this->response($this->error('', 'REQUEST_ID'));
}
if (empty($seckill_id)) {
return $this->response($this->error('', 'REQUEST_ID'));
}
$seckill_model = new SeckillModel();
$condition = [
[ 'ps.id', '=', $seckill_id ],
[ 'psg.goods_id', '=', $goods_id ],
[ 'psg.site_id', '=', $this->site_id ],
[ 'psg.status', '=', 1 ],
[ 'ps.status', '=', 1 ],
[ 'g.goods_state', '=', 1 ],
[ 'g.is_delete', '=', 0 ]
];
$list = $seckill_model->getSeckillGoodsSkuList($condition);
if (!empty($list[ 'data' ])) {
foreach ($list[ 'data' ] as $k => $v) {
if (!empty($v[ 'goods_spec_format' ])) {
//判断商品规格项
$goods_spec_format = $seckill_model->getGoodsSpecFormat($seckill_id, $this->site_id, $v[ 'goods_spec_format' ]);
$list[ 'data' ][ $k ][ 'goods_spec_format' ] = json_encode($goods_spec_format);
}
}
}
return $this->response($list);
}
public function page()
{
$seckill_time_id = $this->params[ 'seckill_time_id' ] ?? 0;
$page = $this->params[ 'page' ] ?? 1;
$page_size = $this->params[ 'page_size' ] ?? PAGE_LIST_ROWS;
if (empty($seckill_time_id)) {
return $this->response($this->error('', 'REQUEST_SECKILL_ID'));
}
$seckill_model = new SeckillModel();
$res = $seckill_model->getSeckillGoodsPageList($seckill_time_id, $this->site_id, $page, $page_size);
foreach ($res[ 'data' ][ 'list' ] as $key => $val) {
if ($val[ 'price' ] != 0) {
$discount_rate = floor($val[ 'seckill_price' ] / $val[ 'price' ] * 100);
} else {
$discount_rate = 100;
}
$res[ 'data' ][ 'list' ][ $key ][ 'discount_rate' ] = $discount_rate;
}
return $this->response($res);
}
public function lists()
{
$seckill_time_id = $this->params[ 'seckill_time_id' ] ?? 0;
$num = $this->params[ 'num' ] ?? null;
if (empty($seckill_time_id)) {
return $this->response($this->error('', 'REQUEST_SECKILL_ID'));
}
$seckill_model = new SeckillModel();
$res = $seckill_model->getSeckillList($seckill_time_id, $this->site_id, $num);
$list = $res[ 'data' ];
foreach ($list as $key => $val) {
if ($val[ 'price' ] != 0) {
$discount_rate = floor($val[ 'seckill_price' ] / $val[ 'price' ] * 100);
} else {
$discount_rate = 100;
}
$list[ $key ][ 'discount_rate' ] = $discount_rate;
}
return $this->response($this->success($list));
}
/**
* 获取商品海报
*/
public function poster()
{
$this->checkToken();
$promotion_type = 'seckill';
$qrcode_param = json_decode($this->params[ 'qrcode_param' ], true);
$qrcode_param[ 'source_member' ] = $this->member_id;
$poster = new Poster();
$res = $poster->goods($this->params[ 'app_type' ], $this->params[ 'page' ], $qrcode_param, $promotion_type, $this->site_id);
return $this->response($res);
}
/**
* 分享图片
* @return false|string
*/
public function shareImg()
{
$qrcode_param = json_decode($this->params[ 'qrcode_param' ], true);
$poster = new Poster();
$res = $poster->shareImg($this->params[ 'page' ] ?? '', $qrcode_param, $this->site_id);
return $this->response($res);
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace addon\seckill\component\controller;
use app\component\controller\BaseDiyView;
/**
* 秒杀模块·组件
*
*/
class Seckill extends BaseDiyView
{
/**
* 设计界面
*/
public function design()
{
return $this->fetch("seckill/design.html");
}
}

View File

@@ -0,0 +1,110 @@
@CHARSET "UTF-8";
.component-seckill .seckill-list{overflow: hidden;}
/* 样式:单列 */
.component-seckill .seckill-list .row1-of1{}
.component-seckill .seckill-list .row1-of1 .item{display: flex;margin-bottom: 10px;padding: 8px;}
.component-seckill .seckill-list .row1-of1 .item:last-child{margin-bottom: 0;}
.component-seckill .seckill-list .row1-of1 .item .img-wrap{width: 100px;height: 100px;}
.component-seckill .seckill-list .row1-of1 .item .img-wrap > img{width: 100%;}
.component-seckill .seckill-list .row1-of1 .item .content{display: flex;justify-content: space-between;flex-direction: column;flex: 1;margin-left: 10px;padding: 3px 0;}
.component-seckill .seckill-list .row1-of1 .item .content .goods-name {}
.component-seckill .seckill-list .row1-of1 .item .content .progress {display: flex;align-items: center;}
.component-seckill .seckill-list .row1-of1 .item .content .progress .bg {width: 120px;height: 10px;border-radius: 10px;margin-right: 10px;}
.component-seckill .seckill-list .row1-of1 .item .content .progress .bg .curr {width: 70px;height: 10px;border-radius: 10px;}
.component-seckill .seckill-list .row1-of1 .item .content .progress .num {font-size: 12px;}
.component-seckill .seckill-list .row1-of1 .item .content .bottom-wrap {display: flex;justify-content: space-between;align-items: center;}
.component-seckill .seckill-list .row1-of1 .item .content .bottom-wrap .price-wrap {display: flex;align-items: baseline;font-size: 12px;flex-wrap: wrap;width: 130px;}
.component-seckill .seckill-list .row1-of1 .item .content .bottom-wrap .price-wrap .discount-price {font-weight: bold;margin-right: 5px;}
.component-seckill .seckill-list .row1-of1 .item .content .bottom-wrap .price-wrap .unit{font-size: 12px;}
.component-seckill .seckill-list .row1-of1 .item .content .bottom-wrap .price-wrap .price{font-size: 16px;}
.component-seckill .seckill-list .row1-of1 .item .content .bottom-wrap .original-price {font-size: 12px;text-decoration: line-through;}
.component-seckill .seckill-list .row1-of1 .item .content .bottom-wrap button {height: 25px;line-height: 25px;padding: 0 8px;font-size: 12px;}
/* 风格二 */
.component-seckill .seckill-list.style-2 .row1-of1 .item{padding: 16px;}
.component-seckill .seckill-list.style-2 .row1-of1 .item .img-wrap{position: relative;overflow: hidden;}
.component-seckill .seckill-list.style-2 .row1-of1 .item .content .bottom-wrap .price-wrap{flex-direction: column-reverse;}
.component-seckill .seckill-list.style-2 .row1-of1 .item .content .progress .bg .curr{position: relative;}
.component-seckill .seckill-list.style-2 .row1-of1 .item .content .progress-bar{position: absolute;right: 0; width: 15px;height: 15px;top: 50%;transform: translateY(-50%);}
.component-seckill .seckill-list.style-2 .row1-of1 .item .content .progress .num{color: #FF3D3D !important;}
/* 样式:双列 */
.component-seckill .seckill-list .row1-of2{display: flex;flex-wrap: wrap;}
.component-seckill .seckill-list .row1-of2 .item{overflow: hidden;margin-right: 10px;margin-top: 10px;width: calc(50% - 5px);display: flex;flex-direction: column;box-sizing: border-box;}
.component-seckill .seckill-list .row1-of2 .item:nth-child(2n){margin-right: 0;}
.component-seckill .seckill-list .row1-of2 .item:nth-of-type(1), .component-seckill .seckill-list .row1-of2 .item:nth-of-type(2){margin-top: 0;}
.component-seckill .seckill-list .row1-of2 .item .img-wrap{height: 170px;text-align: center;width: calc(100% + 2px);}
.component-seckill .seckill-list .row1-of2 .item .img-wrap > img{width: 100%;height: 100%;}
.component-seckill .seckill-list .row1-of2 .item .content{display: flex;flex-direction: column;flex: 1;padding: 5px 10px;position: relative;}
.component-seckill .seckill-list .row1-of2 .item .content .goods-name{}
.component-seckill .seckill-list .row1-of2 .item .content .discount-price{display: flex;align-items: baseline;margin-top: 5px;font-weight: bold;line-height: 1;}
.component-seckill .seckill-list .row1-of2 .item .content .discount-price .unit{font-size: 12px;}
.component-seckill .seckill-list .row1-of2 .item .content .discount-price .price{font-size: 16px;}
.component-seckill .seckill-list .row1-of2 .item .content .bottom-wrap{display: flex;justify-content: space-between;align-items: flex-end;}
.component-seckill .seckill-list .row1-of2 .item .content .bottom-wrap .original-price{font-size: 12px;text-decoration: line-through;}
.component-seckill .seckill-list .row1-of2 .item .content .bottom-wrap button {height: 25px;line-height: 27px;padding: 0 8px;font-size: 12px;}
/* 样式:横向滑动 */
.component-seckill .seckill-list .horizontal-slide{box-sizing: border-box;justify-content: space-between;white-space: nowrap;overflow: hidden;margin: 10px;}
.component-seckill .seckill-list .horizontal-slide .item {width: 97px;overflow: hidden;display: inline-block;margin-right: 10px;box-sizing: border-box;margin-top: 4px;}
.component-seckill .seckill-list .horizontal-slide .item .img-wrap {width: 97px;height: 97px;overflow: hidden;position: relative;}
.component-seckill .seckill-list .horizontal-slide .item .img-wrap > img{width: calc(100% + 1px);}
.component-seckill .seckill-list .horizontal-slide .item .content {padding: 5px 10px;}
.component-seckill .seckill-list .horizontal-slide .item .content .discount-price{margin-top: 5px;font-weight: bold;line-height: 1;display: flex;align-items: flex-end;}
.component-seckill .seckill-list .horizontal-slide .item .content .discount-price .tag{display: none;}
.component-seckill .seckill-list .horizontal-slide .item .content .discount-price .unit{font-size: 12px;}
.component-seckill .seckill-list .horizontal-slide .item .content .discount-price .price{font-size: 16px;}
.component-seckill .seckill-list .horizontal-slide .item .content .original-price{margin-top: 2px;font-size: 12px;text-decoration: line-through;}
/* 横向滑动——样式2 */
.component-seckill .seckill-list.style-2 .horizontal-slide{}
.component-seckill .seckill-list.style-2 .horizontal-slide .item{}
.component-seckill .seckill-list.style-2 .horizontal-slide .item .content{padding:5px 15px;text-align: center;}
.component-seckill .seckill-list.style-2 .horizontal-slide .item .content .discount-price{background: linear-gradient(to right,#FF5F17,#FE2F18);position: relative;padding: 4px 2px 4px 10px;border-top-right-radius: 5px;border-bottom-right-radius: 5px;justify-content: center;}
.component-seckill .seckill-list.style-2 .horizontal-slide .item .content .discount-price .tag{display: block;position: absolute;width: 13px;left:-5px;top:0;}
.component-seckill .seckill-list.style-2 .horizontal-slide .item .content .discount-price span{color: #ffffff;font-size: 13px;}
.component-seckill .seckill-list.style-2 .horizontal-slide .item .content .discount-price .unit{font-size: 13px;}
.component-seckill .seckill-list.style-2 .horizontal-slide .item .content .discount-price .price{margin-left: 3px;}
.component-seckill .seckill-list.style-2 .horizontal-slide .item .content .original-price{margin-top: 5px;}
/* 头部风格公共 */
.component-seckill .title-wrap .left-img{max-height: 20px; max-width: 78px;}
/* 头部风格一 */
.component-seckill .title-wrap.style-1{display: flex;justify-content: space-between;align-items: center;padding: 10px;}
.component-seckill .title-wrap.style-1 .time{display: flex;align-items: center;line-height: 1;margin-right: auto;margin-left: 10px;}
.component-seckill .title-wrap.style-1 .time .seckill-title-name.text{margin-right: 5px;font-size: 12px;}
.component-seckill .title-wrap.style-1 .time .number{font-size: 12px;border-radius: 3px;padding: 2px;}
.component-seckill .title-wrap.style-1 .time .symbol{margin: 0 3px;}
.component-seckill .title-wrap.style-1 .more{display: flex;align-items: center;}
/* 头部风格二 */
.component-seckill .title-wrap.style-2{display: flex;justify-content: space-between;align-items: center;padding: 10px;}
.component-seckill .title-wrap.style-2 .time{display: flex;align-items: center;line-height: 1;margin-right: auto;margin-left: 10px;border-radius: 15px;padding: 0 10px;height: 22px;background-image: radial-gradient(transparent 50%, #fff);font-size: 12px;}
.component-seckill .title-wrap.style-2 .time .seckill-title-name.icon{font-size: 12px;background-image: radial-gradient(transparent 35%, #fff);border-radius: 50%;position: relative;margin-left: -10px;height: 22px;width: 22px;line-height: 22px;text-align: center;margin-right: 3px;}
.component-seckill .title-wrap.style-2 .time .seckill-title-name.text{display: none;}
.component-seckill .title-wrap.style-2 .more{display: flex;align-items: center;}
/* 头部风格三 */
.component-seckill .title-wrap.style-3{display: flex;justify-content: space-between;align-items: center;padding: 10px;margin-bottom: 10px;height: 44px;box-sizing: border-box;background-repeat: no-repeat;background-size: cover;line-height: 1;border-radius: 9px 9px 0 0;}
.component-seckill .title-wrap.style-3 .time{display: flex;align-items: center;line-height: 1;margin-right: auto;margin-left: 10px;}
.component-seckill .title-wrap.style-3 .time .seckill-title-name.text{margin-right: 5px;font-size: 12px;color:#fff;position: relative;}
.component-seckill .title-wrap.style-3 .time .seckill-title-name.text:after{content: "";position: absolute;left: -5px;height: 12px;width: 1px;background-color: #fff;}
.component-seckill .title-wrap.style-3 .time .number{font-size: 12px;border-radius: 3px;padding: 2px;}
.component-seckill .title-wrap.style-3 .time .symbol{margin: 0 3px;}
.component-seckill .title-wrap.style-3 .more{display: flex;align-items: center;}
/* 头部风格四 */
.component-seckill .title-wrap.style-4{display: flex;justify-content: space-between;align-items: center;padding: 10px;margin-bottom: 10px;height: 44px;box-sizing: border-box;background-repeat: no-repeat;background-size: cover;line-height: 1;border-radius: 9px 9px 0 0;}
.component-seckill .title-wrap.style-4 .time{display: flex;align-items: center;line-height: 1;}
.component-seckill .title-wrap.style-4 .time .seckill-title-name.text{margin-right: 10px;font-size: 13px;color:#666666;}
.component-seckill .title-wrap.style-4 .time .number{font-size: 12px;border-radius: 3px;padding: 2px;}
.component-seckill .title-wrap.style-4 .time .symbol{margin: 0 3px;}
/* 秒杀头部弹窗样式 */
.component-seckill .style-list-box-seckill{display: none;}
.style-list-con-seckill{display: flex;flex-wrap: wrap;}
.style-list-con-seckill .style-li-seckill{overflow: hidden;display: flex;align-items: center;justify-content: center;width: 280px;height: 100px;margin-right: 12px;margin-top: 15px;cursor: pointer;border: 1px solid #ededed;background: #f7f8fa;}
.style-list-con-seckill .style-li-seckill img{max-width: 280px;max-height: 220px;}
.style-list-con-seckill .style-li-seckill:nth-child(1), .style-list-con-seckill .style-li-seckill:nth-child(2), .style-list-con-seckill .style-li-seckill:nth-child(3){margin-top: 0;}
.style-list-con-seckill .style-li-seckill:nth-child(3n){margin-right: 0;}

View File

@@ -0,0 +1,489 @@
<nc-component :data="data[index]" class="component-seckill">
<!-- 预览 -->
<template slot="preview">
<div :class="['seckill-list',nc.style]" :style="{ backgroundColor: nc.componentBgColor,
borderTopLeftRadius: (nc.componentAngle == 'round' ? nc.topAroundRadius + 'px' : 0),
borderTopRightRadius: (nc.componentAngle == 'round' ? nc.topAroundRadius + 'px' : 0),
borderBottomLeftRadius: (nc.componentAngle == 'round' ? nc.bottomAroundRadius + 'px' : 0),
borderBottomRightRadius: (nc.componentAngle == 'round' ? nc.bottomAroundRadius + 'px' : 0) }">
<div :class="['title-wrap',nc.titleStyle.style]" v-if="nc.titleStyle.isShow"
:style="{'backgroundImage':'url('+ changeImgUrl(nc.titleStyle.backgroundImage) + '), linear-gradient(to right,' + nc.titleStyle.bgColorStart + ',' + nc.titleStyle.bgColorEnd + ')'}">
<h3 v-if="nc.titleStyle.leftStyle == 'text'" :style="{fontSize: nc.titleStyle.fontSize + 'px',color: nc.titleStyle.textColor,fontWeight: nc.titleStyle.fontWeight ? 'bold' : ''}">{{ nc.titleStyle.leftText }}</h3>
<img v-else class="left-img" :src="changeImgUrl(nc.titleStyle.leftImg)" />
<div class="time" :style="{ background: nc.titleStyle.timeBgColor }">
<span :style="{color: nc.titleStyle.textColor}" class="seckill-title-name text">距离结束</span>
<span v-if="nc.titleStyle.timeImageUrl" :class="['seckill-title-name icon',nc.titleStyle.timeImageUrl]" :style="{color : nc.titleStyle.numTextColor}"></span>
<span class="hour number" :style="{ background: 'linear-gradient(to right,' + nc.titleStyle.numBgColorStart + ',' + nc.titleStyle.numBgColorEnd + ')', color: nc.titleStyle.numTextColor }">22</span>
<span class="symbol" :style="{ color: nc.titleStyle.colonColor }">:</span>
<span class="minute number" :style="{ background: 'linear-gradient(to right,' + nc.titleStyle.numBgColorStart + ',' + nc.titleStyle.numBgColorEnd + ')', color: nc.titleStyle.numTextColor }">30</span>
<span class="symbol" :style="{ color: nc.titleStyle.colonColor }">:</span>
<span class="second number" :style="{ background: 'linear-gradient(to right,' + nc.titleStyle.numBgColorStart + ',' + nc.titleStyle.numBgColorEnd + ')', color: nc.titleStyle.numTextColor }">58</span>
</div>
<div class="more" v-if="nc.titleStyle.moreSupport" :style="{fontSize: nc.titleStyle.moreFontSize + 'px',color: nc.titleStyle.moreColor}">
<span>{{nc.titleStyle.more}}</span>
<i class="iconfont iconyoujiantou"></i>
</div>
</div>
<div :class="nc.template">
<template v-if="nc.tempData.previewList && Object.keys(nc.tempData.previewList).length">
<template v-if="nc.template == 'row1-of1'">
<div class="item" v-for="(item, previewIndex) in nc.tempData.previewList" :key="previewIndex"
:style="{
borderTopLeftRadius: (nc.elementAngle == 'round' ? nc.topElementAroundRadius + 'px' : 0),
borderTopRightRadius: (nc.elementAngle == 'round' ? nc.topElementAroundRadius + 'px' : 0),
borderBottomLeftRadius: (nc.elementAngle == 'round' ? nc.bottomElementAroundRadius + 'px' : 0),
borderBottomRightRadius: (nc.elementAngle == 'round' ? nc.bottomElementAroundRadius + 'px' : 0),
backgroundColor: nc.elementBgColor,
boxShadow: nc.ornament.type == 'shadow' ? ('0 0 5px ' + nc.ornament.color) : '',
border: nc.ornament.type == 'stroke' ? '1px solid ' + nc.ornament.color : ''}">
<div class="img-wrap" :style="{ borderRadius: nc.imgAroundRadius + 'px' }">
<img :style="{ borderRadius: nc.imgAroundRadius + 'px' }" :src="changeImgUrl('public/static/img/default_img/square.png')" />
</div>
<div class="content">
<div class="goods-name" v-if="nc.goodsNameStyle.control" :style="{ color : nc.goodsNameStyle.color,fontWeight : nc.goodsNameStyle.fontWeight ? 'bold' : '' }" :class="[{'using-hidden' : nc.nameLineMode == 'single'},{'multi-hidden' : nc.nameLineMode == 'multiple'}]">{{ item.goods_name }}</div>
<div class="progress" v-if="nc.progressStyle.control">
<div class="bg" :style="{ backgroundColor : nc.progressStyle.bgColor }">
<div class="curr" :style="{ backgroundColor : nc.progressStyle.currColor }">
<img v-if="nc.template == 'row1-of1'&&nc.style == 'style-2'" class="progress-bar" src="{$resource_path}/img/progress_bar_01.png" alt="">
</div>
</div>
<div class="num" :style="{ color : nc.saleStyle.color }">已抢65.32%</div>
</div>
<div class="bottom-wrap">
<div class="price-wrap">
<div class="discount-price" v-if="nc.priceStyle.mainControl">
<span class="unit" :style="{ color : nc.priceStyle.mainColor }">¥</span>
<span class="price" :style="{ color : nc.priceStyle.mainColor }">{{item.discount_price.split(".")[0]}}</span>
<span class="unit" :style="{ color : nc.priceStyle.mainColor }">{{ "."+item.discount_price.split(".")[1]}}</span>
</div>
<div class="original-price" v-if="nc.priceStyle.lineControl && !(nc.template == 'row1-of1'&&nc.style == 'style-2')" :style="{ color : nc.priceStyle.lineColor }">¥{{item.line_price}}</div>
<div v-else :style="{ color : nc.priceStyle.lineColor }">
<span>原价:</span>
<span class="original-price">¥{{item.line_price}}</span>
</div>
</div>
<button class="layui-btn" v-if="nc.btnStyle.control" :style="{ background : 'linear-gradient(to right,' + nc.btnStyle.bgColorStart + ',' + nc.btnStyle.bgColorEnd + ')', color : nc.btnStyle.textColor,borderRadius : nc.btnStyle.aroundRadius + 'px' }">{{ nc.btnStyle.text }}</button>
</div>
</div>
</div>
</template>
<template v-if="nc.template == 'row1-of2'">
<div class="item" v-for="(item, previewIndex) in nc.tempData.previewList" :key="previewIndex"
:style="{
borderTopLeftRadius: (nc.elementAngle == 'round' ? nc.topElementAroundRadius + 'px' : 0),
borderTopRightRadius: (nc.elementAngle == 'round' ? nc.topElementAroundRadius + 'px' : 0),
borderBottomLeftRadius: (nc.elementAngle == 'round' ? nc.bottomElementAroundRadius + 'px' : 0),
borderBottomRightRadius: (nc.elementAngle == 'round' ? nc.bottomElementAroundRadius + 'px' : 0),
backgroundColor: nc.elementBgColor,
boxShadow: nc.ornament.type == 'shadow' ? ('0 0 5px ' + nc.ornament.color) : '',
border: nc.ornament.type == 'stroke' ? '1px solid ' + nc.ornament.color : ''}">
<div class="img-wrap" :style="{ borderRadius: nc.imgAroundRadius + 'px' }">
<img :style="{ borderRadius: nc.imgAroundRadius + 'px' }" :src="changeImgUrl('public/static/img/default_img/square.png')" />
</div>
<div class="content" v-if="nc.goodsNameStyle.control || nc.priceStyle.mainControl || nc.priceStyle.lineControl || nc.btnStyle.control">
<div class="goods-name" v-if="nc.goodsNameStyle.control" :style="{ color : nc.goodsNameStyle.color,fontWeight : nc.goodsNameStyle.fontWeight ? 'bold' : '' }" :class="[{'using-hidden' : nc.nameLineMode == 'single'},{'multi-hidden' : nc.nameLineMode == 'multiple'}]">{{ item.goods_name }}</div>
<div class="discount-price" v-if="nc.priceStyle.mainControl">
<span class="unit" :style="{ color : nc.priceStyle.mainColor }">¥</span>
<span class="price" :style="{ color : nc.priceStyle.mainColor }">{{item.discount_price.split(".")[0]}}</span>
<span class="unit" :style="{ color : nc.priceStyle.mainColor }">{{"."+item.discount_price.split(".")[1]}}</span>
</div>
<div class="bottom-wrap">
<div class="original-price" v-if="nc.priceStyle.lineControl" :style="{ color : nc.priceStyle.lineColor }">¥{{item.line_price}}</div>
<button class="layui-btn" v-if="nc.btnStyle.control" :style="{ background : 'linear-gradient(to right,' + nc.btnStyle.bgColorStart + ',' + nc.btnStyle.bgColorEnd + ')', color : nc.btnStyle.textColor,borderRadius : nc.btnStyle.aroundRadius + 'px' }">{{ nc.btnStyle.text }}</button>
</div>
</div>
</div>
</template>
<template v-if="nc.template == 'horizontal-slide'">
<div v-for="(item,previewIndex) in Object.values(nc.tempData.previewList)" :key="previewIndex" class="item" v-if="previewIndex < 3"
:style="{
borderTopLeftRadius: (nc.elementAngle == 'round' ? nc.topElementAroundRadius + 'px' : 0),
borderTopRightRadius: (nc.elementAngle == 'round' ? nc.topElementAroundRadius + 'px' : 0),
borderBottomLeftRadius: (nc.elementAngle == 'round' ? nc.bottomElementAroundRadius + 'px' : 0),
borderBottomRightRadius: (nc.elementAngle == 'round' ? nc.bottomElementAroundRadius + 'px' : 0),
backgroundColor: nc.elementBgColor,
marginLeft: nc.template == 'horizontal-slide' && (nc.slideMode == 'scroll' && nc.goodsMarginType=='diy' && (nc.goodsMarginNum+'px') || ((60 - nc.margin.both*2) /6 + 'px')) || '',
marginRight: nc.template == 'horizontal-slide' && (nc.slideMode == 'scroll' && nc.goodsMarginType=='diy' && (nc.goodsMarginNum+'px') || ((60 - nc.margin.both*2) /6 + 'px')) || '',
boxShadow: nc.ornament.type == 'shadow' ? ('0 0 5px ' + nc.ornament.color) : '',
border: nc.ornament.type == 'stroke' ? '1px solid ' + nc.ornament.color : ''}">
<div class="img-wrap" :style="{ borderRadius: nc.imgAroundRadius + 'px' }">
<img :style="{ borderRadius: nc.imgAroundRadius + 'px' }" :src="changeImgUrl('public/static/img/default_img/square.png')" />
</div>
<div class="content" v-if="nc.goodsNameStyle.control || nc.priceStyle.mainControl">
<div class="goods-name" v-if="nc.goodsNameStyle.control" :style="{ color : nc.goodsNameStyle.color,fontWeight : nc.goodsNameStyle.fontWeight ? 'bold' : '' }" :class="[{'using-hidden' : nc.nameLineMode == 'single'},{'multi-hidden' : nc.nameLineMode == 'multiple'}]">{{ item.goods_name }}</div>
<div class="discount-price" v-if="nc.priceStyle.mainControl">
<img class="tag" src="{$resource_path}/img/style_4_tag.png">
<span class="unit" :style="{ color : nc.priceStyle.mainColor }">¥</span>
<span class="price" :style="{ color : nc.priceStyle.mainColor }">{{item.discount_price.split(".")[0]}}</span>
<span class="unit" :style="{ color : nc.priceStyle.mainColor }">{{"."+item.discount_price.split(".")[1]}}</span>
</div>
<div class="original-price" v-if="nc.priceStyle.lineControl" :style="{ color : nc.priceStyle.lineColor }">¥{{item.line_price}}</div>
</div>
</div>
</template>
</template>
</div>
</div>
</template>
<!-- 内容编辑 -->
<template slot="edit-content">
<template v-if="nc.lazyLoad">
<seckill-list-sources></seckill-list-sources>
<div class="template-edit-title">
<h3>商品数据</h3>
<slide :data="{ field : 'count', label: '商品数量', min:1, max: 30}"></slide>
</div>
<div class="template-edit-title">
<h3>头部风格</h3>
<div class="layui-form-item">
<label class="layui-form-label sm">头部状态</label>
<div class="layui-input-block">
<div class="layui-unselect layui-form-checkbox" lay-skin="primary" @click="nc.titleStyle.isShow = !nc.titleStyle.isShow" :class="{ 'layui-form-checked' : nc.titleStyle.isShow }">
<span>{{ nc.titleStyle.isShow ? '显示' : '隐藏' }}</span>
<i class="layui-icon layui-icon-ok"></i>
</div>
</div>
</div>
<template v-if="nc.titleStyle.isShow">
<div class="layui-form-item" v-if="nc.tempData.methods">
<label class="layui-form-label sm">选择风格</label>
<div class="layui-input-block">
<div v-if="nc.titleStyle.styleName" class="text-color selected-style" @click="nc.tempData.methods.selectTopStyle">
<span>{{nc.titleStyle.styleName}}</span>
<i class="layui-icon layui-icon-right"></i>
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label sm">主标题类型</label>
<div class="layui-input-block">
<div @click="nc.titleStyle.leftStyle='text'" :class="{ 'layui-unselect layui-form-radio' : true,'layui-form-radioed' : (nc.titleStyle.leftStyle=='text') }">
<i class="layui-anim layui-icon">{{ nc.titleStyle.leftStyle=='text' ? "&#xe643;" : "&#xe63f;" }}</i>
<div>文字</div>
</div>
<div @click="nc.titleStyle.leftStyle='img'" :class="{ 'layui-unselect layui-form-radio' : true,'layui-form-radioed' : (nc.titleStyle.leftStyle=='img') }">
<i class="layui-anim layui-icon">{{ nc.titleStyle.leftStyle=='img' ? "&#xe643;" : "&#xe63f;" }}</i>
<div>图片</div>
</div>
</div>
</div>
<div class="layui-form-item" v-if="nc.titleStyle.leftStyle=='text'">
<label class="layui-form-label sm">标题文字</label>
<div class="layui-input-block">
<input type="text" v-model="nc.titleStyle.leftText" maxlength="4" placeholder="限时秒杀" class="layui-input">
</div>
</div>
<div class="layui-form-item" v-if="nc.titleStyle.leftStyle=='img'">
<label class="layui-form-label sm">标题图片</label>
<div class="layui-input-block">
<img-upload :data="{ data: nc.titleStyle,field:'leftImg'}" data-disabled="1"></img-upload>
</div>
</div>
<div class="layui-form-item" v-if="nc.titleStyle.moreSupport">
<label class="layui-form-label sm">右侧文字</label>
<div class="layui-input-block">
<input type="text" v-model="nc.titleStyle.more" maxlength="8" placeholder="查看更多" class="layui-input">
</div>
</div>
</template>
</div>
<div class="template-edit-title">
<h3>商品风格</h3>
<div class="layui-form-item list-style" v-if="nc.tempData.templateList">
<label class="layui-form-label sm">风格</label>
<div class="layui-input-block">
<div class="source">{{ nc.tempData.templateList[nc.template].text }}</div>
<div class="template-selected">
<div v-for="(item,templateKey) in nc.tempData.templateList" :key="templateKey" class="source-item" :title="item.text"
@click="nc.tempData.methods.selectTemplate(templateKey)"
:class="[(nc.template == templateKey) ? 'text-color border-color' : '' ]">
<i class='iconfont' :class='item.icon'></i>
</div>
</div>
<div class="style-selected">
<div v-for="(item,styleIndex) in nc.tempData.templateList[nc.template].styleList" :key="styleIndex" @click="nc.tempData.methods.selectTemplate('',item)" :class="{ 'layui-unselect layui-form-radio' : true,'layui-form-radioed' : (nc.style==item.value) }">
<i class="layui-anim layui-icon">{{ nc.style == item.value ? "&#xe643;" : "&#xe63f;" }}</i>
<div>{{item.text}}</div>
</div>
</div>
</div>
</div>
</div>
<div class="template-edit-title" v-show="nc.btnStyle.support">
<h3>购买按钮</h3>
<div class="layui-form-item">
<label class="layui-form-label sm">是否显示</label>
<div class="layui-input-block">
<div class="layui-unselect layui-form-checkbox" lay-skin="primary" @click="nc.btnStyle.control = !nc.btnStyle.control" :class="{ 'layui-form-checked' : nc.btnStyle.control }">
<span>{{ nc.btnStyle.control ? '显示' : '隐藏' }}</span>
<i class="layui-icon layui-icon-ok"></i>
</div>
</div>
</div>
<div class="layui-form-item" v-if="nc.btnStyle.control">
<label class="layui-form-label sm">文字</label>
<div class="layui-input-block">
<input type="text" v-model="nc.btnStyle.text" maxlength="6" placeholder="请输入按钮文字" class="layui-input">
</div>
</div>
</div>
<div class="template-edit-title">
<h3>显示内容</h3>
<div class="layui-form-item">
<label class="layui-form-label sm">商品名称</label>
<div class="layui-input-block">
<div class="layui-unselect layui-form-checkbox" lay-skin="primary" @click="nc.goodsNameStyle.control = !nc.goodsNameStyle.control" :class="{ 'layui-form-checked' : nc.goodsNameStyle.control }">
<span>{{ nc.goodsNameStyle.control ? '显示' : '隐藏' }}</span>
<i class="layui-icon layui-icon-ok"></i>
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label sm">销售价</label>
<div class="layui-input-block">
<div class="layui-unselect layui-form-checkbox" lay-skin="primary" @click="nc.priceStyle.mainControl = !nc.priceStyle.mainControl" :class="{ 'layui-form-checked' : nc.priceStyle.mainControl }">
<span>{{ nc.priceStyle.mainControl ? '显示' : '隐藏' }}</span>
<i class="layui-icon layui-icon-ok"></i>
</div>
</div>
</div>
<div class="layui-form-item" v-show="nc.progressStyle.support">
<label class="layui-form-label sm">秒杀进度</label>
<div class="layui-input-block">
<div class="layui-unselect layui-form-checkbox" lay-skin="primary" @click="nc.progressStyle.control = !nc.progressStyle.control" :class="{ 'layui-form-checked' : nc.progressStyle.control }">
<span>{{ nc.progressStyle.control ? '显示' : '隐藏' }}</span>
<i class="layui-icon layui-icon-ok"></i>
</div>
</div>
</div>
<div class="layui-form-item" v-show="nc.priceStyle.lineSupport">
<label class="layui-form-label sm">划线价</label>
<div class="layui-input-block">
<div class="layui-unselect layui-form-checkbox" lay-skin="primary" @click="nc.priceStyle.lineControl = !nc.priceStyle.lineControl" :class="{ 'layui-form-checked' : nc.priceStyle.lineControl }">
<span>{{ nc.priceStyle.lineControl ? '显示' : '隐藏' }}</span>
<i class="layui-icon layui-icon-ok"></i>
</div>
</div>
</div>
<div class="layui-form-item" v-show="nc.saleStyle.support">
<label class="layui-form-label sm">商品销量</label>
<div class="layui-input-block">
<div class="layui-unselect layui-form-checkbox" lay-skin="primary" @click="nc.saleStyle.control = !nc.saleStyle.control" :class="{ 'layui-form-checked' : nc.saleStyle.control }">
<span>{{ nc.saleStyle.control ? '显示' : '隐藏' }}</span>
<i class="layui-icon layui-icon-ok"></i>
</div>
</div>
</div>
</div>
</template>
<!-- 弹框 -->
<article class="style-list-box-seckill">
<div class="style-list-seckill layui-form">
<div class="style-list-con-seckill">
<div class="style-li-seckill" v-for="(value,name,previewIndex) in nc.tempData.styleList" :key="name" :class="{'selected border-color': nc.titleStyle.style == name}" :data_key="name">
<span class="layui-hide">风格{{previewIndex + 1}}</span>
<img :src="'{$resource_path}/img/style_title_' + (previewIndex+1) + '.png'" />
</div>
</div>
<input type="hidden" name="style">
<input type="hidden" name="style_name" />
</div>
</article>
</template>
<!-- 样式编辑 -->
<template slot="edit-style">
<template v-if="nc.lazyLoad">
<template v-if="nc.titleStyle.isShow">
<div class="template-edit-title">
<h3>头部样式</h3>
<template v-if="nc.titleStyle.leftStyle == 'text'">
<div class="layui-form-item">
<label class="layui-form-label sm">标题加粗</label>
<div class="layui-input-block">
<div class="layui-unselect layui-form-checkbox" lay-skin="primary" @click="nc.titleStyle.fontWeight = !nc.titleStyle.fontWeight" :class="{ 'layui-form-checked' : nc.titleStyle.fontWeight }">
<span>加粗</span>
<i class="layui-icon layui-icon-ok"></i>
</div>
</div>
</div>
<color :data="{ field : 'textColor', 'label' : '标题颜色', parent : 'titleStyle', defaultColor : '#303133' }"></color>
<slide :data="{ field : 'fontSize',parent : 'titleStyle', label : '标题大小', min: 12, max : 16 }"></slide>
</template>
<color :data="{ field : 'bgColorStart,bgColorEnd', 'label' : '背景颜色', parent : 'titleStyle', defaultColor : '#FA6400,#FF287A' }"></color>
<div class="layui-form-item">
<label class="layui-form-label sm">背景图片</label>
<div class="layui-input-block">
<img-upload :data="{ data: nc.titleStyle,field:'backgroundImage'}" data-disabled="1"></img-upload>
</div>
</div>
</div>
<div class="template-edit-title">
<h3>倒计时样式</h3>
<nc-icon v-if="nc.titleStyle.style == 'style-2'" :data="{ field : 'timeImageUrl', parent : 'titleStyle'}"></nc-icon>
<color :data="{ field : 'timeBgColor', 'label' : '背景颜色', parent : 'titleStyle', defaultColor : '#303133' }"></color>
<color :data="{ field : 'numBgColorStart,numBgColorEnd', 'label' : '数字背景色', parent : 'titleStyle', defaultColor : '#FFFFFF' }"></color>
<color :data="{ field : 'numTextColor', 'label' : '数字颜色', parent : 'titleStyle', defaultColor : '#303133' }"></color>
<color :data="{ field : 'colonColor', 'label' : '冒号颜色', parent : 'titleStyle', defaultColor : '#FFFFFF' }"></color>
</div>
<div class="template-edit-title" v-if="nc.titleStyle.moreSupport">
<h3>“更多”样式</h3>
<color :data="{ field : 'moreColor', 'label' : '文字颜色', parent : 'titleStyle', defaultColor : '#999999' }"></color>
<slide :data="{ field : 'moreFontSize',parent : 'titleStyle', label : '文字大小', min: 12, max : 14 }"></slide>
</div>
</template>
<div class="template-edit-title">
<h3>商品样式</h3>
<div class="layui-form-item tag-wrap">
<label class="layui-form-label sm">边框</label>
<div class="layui-input-block">
<div v-for="(item,ornamentIndex) in nc.tempData.ornamentList" :key="ornamentIndex" @click="nc.ornament.type=item.type" :class="{ 'layui-unselect layui-form-radio' : true,'layui-form-radioed' : (nc.ornament.type==item.type) }">
<i class="layui-anim layui-icon">{{ nc.ornament.type == item.type ? "&#xe643;" : "&#xe63f;" }}</i>
<div>{{item.text}}</div>
</div>
</div>
</div>
<color v-if="nc.ornament.type != 'default'" :data="{ field : 'color', 'label' : '边框颜色', parent : 'ornament', defaultColor : '#EDEDED' }"></color>
<slide :data="{ field : 'imgAroundRadius', label: '图片圆角', min:0, max: 50 }"></slide>
<div class="layui-form-item" v-if="nc.template == 'horizontal-slide'">
<label class="layui-form-label sm">滚动方式</label>
<div class="layui-input-block">
<div @click="nc.slideMode = 'scroll' " :class="{ 'layui-unselect layui-form-radio' : true,'layui-form-radioed' : (nc.slideMode == 'scroll') }">
<i class="layui-anim layui-icon">{{ nc.slideMode == 'scroll' ? "&#xe643;" : "&#xe63f;" }}</i>
<div>平移</div>
</div>
<div @click="nc.slideMode = 'slide' " :class="{ 'layui-unselect layui-form-radio' : true,'layui-form-radioed' : (nc.slideMode == 'slide') }">
<i class="layui-anim layui-icon">{{ nc.slideMode == 'slide' ? "&#xe643;" : "&#xe63f;" }}</i>
<div>切屏</div>
</div>
</div>
</div>
<div class="layui-form-item" v-show="nc.goodsNameStyle.control">
<label class="layui-form-label sm">商品名称</label>
<div class="layui-input-block">
<div class="layui-unselect layui-form-checkbox" lay-skin="primary" @click="nc.goodsNameStyle.fontWeight = !nc.goodsNameStyle.fontWeight" :class="{ 'layui-form-checked' : nc.goodsNameStyle.fontWeight }">
<span>加粗</span>
<i class="layui-icon layui-icon-ok"></i>
</div>
<div v-for="(item,nameLineIndex) in nc.tempData.nameLineModeList" :key="nameLineIndex" @click="nc.nameLineMode=item.value" :class="{ 'layui-unselect layui-form-radio' : true,'layui-form-radioed' : (nc.nameLineMode==item.value) }">
<i class="layui-anim layui-icon">{{ nc.nameLineMode == item.value ? "&#xe643;" : "&#xe63f;" }}</i>
<div>{{item.text}}</div>
</div>
</div>
</div>
<color :data="{ field : 'elementBgColor', 'label' : '商品背景' }"></color>
<slide v-show="nc.elementAngle == 'round'" :data="{ field : 'topElementAroundRadius', label : '上圆角', max : 50 }"></slide>
<slide v-show="nc.elementAngle == 'round'" :data="{ field : 'bottomElementAroundRadius', label : '下圆角', max : 50 }"></slide>
<div class="layui-form-item">
<label class="layui-form-label sm">色调</label>
<div class="layui-input-block">
<div @click="nc.theme='default'" :class="{ 'layui-unselect layui-form-radio' : true,'layui-form-radioed' : (nc.theme == 'default') }">
<i class="layui-anim layui-icon">{{ nc.theme == 'default' ? "&#xe643;" : "&#xe63f;" }}</i>
<div>跟随主题风格</div>
</div>
<div @click="nc.theme='diy'" :class="{ 'layui-unselect layui-form-radio' : true,'layui-form-radioed' : (nc.theme == 'diy') }">
<i class="layui-anim layui-icon">{{ nc.theme == 'diy' ? "&#xe643;" : "&#xe63f;" }}</i>
<div>自定义</div>
</div>
</div>
</div>
<div v-show="nc.theme == 'diy'">
<color :data="{ field : 'color', 'label' : '商品名称', parent : 'goodsNameStyle', defaultColor : '#303133' }"></color>
<color :data="{ field : 'mainColor', 'label' : '销售价', parent : 'priceStyle', defaultColor : '#FF6A00' }"></color>
<div v-show="nc.progressStyle.support">
<color :data="{ field : 'bgColor,currColor', 'label' : '秒杀进度', parent : 'progressStyle', defaultColor : '#FCECD7,#FDBE6C' }"></color>
</div>
<div v-show="nc.priceStyle.lineSupport">
<color :data="{ field : 'lineColor', 'label' : '划线价', parent : 'priceStyle', defaultColor : '#999CA7' }"></color>
</div>
<div v-show="nc.saleStyle.support">
<color :data="{ field : 'color', 'label' : '商品销量', parent : 'saleStyle', defaultColor : '#999CA7' }"></color>
</div>
</div>
</div>
<div class="template-edit-title" v-show="nc.btnStyle.support && nc.btnStyle.control">
<h3>购买按钮</h3>
<slide :data="{ field : 'aroundRadius', label: '圆角', min:0, max: 50, parent: 'btnStyle' }"></slide>
<div class="layui-form-item">
<label class="layui-form-label sm">色调</label>
<div class="layui-input-block">
<div @click="nc.btnStyle.theme='default'" :class="{ 'layui-unselect layui-form-radio' : true,'layui-form-radioed' : (nc.btnStyle.theme == 'default') }">
<i class="layui-anim layui-icon">{{ nc.btnStyle.theme == 'default' ? "&#xe643;" : "&#xe63f;" }}</i>
<div>跟随主题风格</div>
</div>
<div @click="nc.btnStyle.theme='diy'" :class="{ 'layui-unselect layui-form-radio' : true,'layui-form-radioed' : (nc.btnStyle.theme == 'diy') }">
<i class="layui-anim layui-icon">{{ nc.btnStyle.theme == 'diy' ? "&#xe643;" : "&#xe63f;" }}</i>
<div>自定义</div>
</div>
</div>
</div>
<template v-if="nc.btnStyle.theme == 'diy'">
<color :data="{ field : 'bgColorStart,bgColorEnd', 'label' : '背景颜色', parent : 'btnStyle', defaultColor : '#FF7B1D,#FF1544' }"></color>
<color :data="{ field : 'textColor', 'label' : '文字颜色', parent : 'btnStyle', defaultColor : '#FFFFFF' }"></color>
</template>
</div>
</template>
</template>
<!-- 资源 -->
<template slot="resource">
<js>
var seckillResourcePath = "{$resource_path}"; // http路径
var seckillRelativePath = "{$relative_path}"; // 相对路径
</js>
<js src="{$resource_path}/js/design.js"></js>
<css src="{$resource_path}/css/design.css"></css>
</template>
</nc-component>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@@ -0,0 +1,290 @@
var seckillListHtml = '<div style="display:none;"></div>';
Vue.component("seckill-list-sources", {
template: seckillListHtml,
data: function () {
return {
data: this.$parent.data,
goodsSources: {
initial: {
text: "默认",
icon: "iconmofang"
},
diy: {
text: "手动选择",
icon: "iconshoudongxuanze"
},
},
templateList: {
"row1-of1": {
text: "单列",
icon: "iconiPhone86",
styleList: [
{
text: "样式1",
value: "style-1",
cartSupport: true, // 是否支持按钮
lineSupport: true, // 是否支持划线价
saleSupport: false, // 是否支持销量
progressSupport: true // 是否支持进度
},
{
text: "样式2",
value: "style-2",
cartSupport: true, // 是否支持按钮
lineSupport: true, // 是否支持划线价
saleSupport: false, // 是否支持销量
progressSupport: true // 是否支持进度
}
],
},
"row1-of2": {
text: "两列",
icon: "iconyihanglianglie",
styleList: [
{
text: "样式1",
value: "style-1",
cartSupport: true, // 是否支持购物车按钮
lineSupport: true, // 是否支持划线价
saleSupport: false, // 是否支持销量
progressSupport: false // 是否支持进度
},
],
},
"horizontal-slide": {
text: "横向滑动",
icon: "iconshangpinliebiaohengxianghuadong",
styleList: [
{
text: "样式1",
value: "style-1",
cartSupport: false, // 是否支持按钮
lineSupport: true, // 是否支持划线价
saleSupport: false,// 是否支持销量
progressSupport: false // 是否支持进度
},
{
text: "样式2",
value: "style-2",
cartSupport: false, // 是否支持按钮
lineSupport: true, // 是否支持划线价
saleSupport: false,// 是否支持销量
progressSupport: false, // 是否支持进度
mainColor: "#FFFFFF", // 销售价颜色
goodsNameControl: false // 是否显示商品名称
},
],
},
},
ornamentList: [
{
type: 'default',
text: '默认',
},
{
type: 'shadow',
text: '投影',
},
{
type: 'stroke',
text: '描边',
},
],
nameLineModeList: [
{
text: "单行",
value: "single"
},
{
text: "多行",
value: "multiple"
}
],
styleList: {
"style-1": {
isShow: true,
leftStyle: "text",
leftImg: "",
leftText: "限时秒杀",
backgroundImage: '',
bgColorStart: "#FFFFFF",
bgColorEnd: "#FFFFFF",
textColor: "#303133",
timeImageUrl: "",
timeBgColor: "",
numBgColorStart: "#303133", // 数字背景色开始
numBgColorEnd: "#303133", // 数字背景色结束
numTextColor: "#FFFFFF", // 数字颜色
colonColor: "#303133", // 冒号颜色
moreColor: "#999999",
more: "查看更多",
moreSupport: true
},
"style-2": {
isShow: true,
leftStyle: "text",
leftImg: "",
leftText: "限时秒杀",
backgroundImage: '',
bgColorStart: "#FFFFFF",
bgColorEnd: "#FFFFFF",
textColor: "#303133",
timeBgColor: "#303133",
timeImageUrl: 'icondiy icon-system-seckill-time',
numBgColorStart: "#303133",
numBgColorEnd: "#303133",
numTextColor: "#FFFFFF",
colonColor: "#FFFFFF",
moreColor: "#999999",
more: "查看更多",
moreSupport: true
},
"style-3": {
isShow: true,
leftStyle: "img",
leftImg: seckillRelativePath + '/img/style_title_3_name.png',
leftText: "",
backgroundImage: seckillRelativePath + '/img/style_title_3_bg.png',
bgColorStart: "#FA6400",
bgColorEnd: "#FF287A",
textColor: "#FFFFFF",
timeBgColor: "",
timeImageUrl: '',
numBgColorStart: "#FFFFFF",
numBgColorEnd: "#FFFFFF",
numTextColor: "#FD3B54",
colonColor: "#FFFFFF",
moreColor: "#FFFFFF",
more: "更多",
moreSupport: true
},
"style-4": {
isShow: true,
leftStyle: "img",
leftImg: seckillRelativePath + '/img/style_title_4_name.png',
leftText: "",
backgroundImage: seckillRelativePath + '/img/style_title_4_bg.png',
bgColorStart: "#FFFFFF",
bgColorEnd: "#FFFFFF",
textColor: "#666666",
timeBgColor: "",
timeImageUrl: '',
numBgColorStart: "#FF5F17",
numBgColorEnd: "#FE2F18",
numTextColor: "#FFFFFF",
colonColor: "#FE3718",
moreColor: "",
more: "",
moreSupport: false
}
},
}
},
created: function () {
if (!this.$parent.data.verify) this.$parent.data.verify = [];
this.$parent.data.verify.push(this.verify);//加载验证方法
this.$parent.data.ignore = ['textColor']; //加载忽略内容 -- 其他设置中的属性设置
this.$parent.data.ignoreLoad = true; // 等待忽略数组赋值后加载
var previewList = {};
for (var i = 1; i < 5; i++) {
previewList["goods_id_" + ns.gen_non_duplicate(i)] = {
goods_name: "秒杀商品",
discount_price: (Math.random() * 100 * i + 10).toFixed(2), // 随机价格
line_price: (Math.random() * 100 * i + 100 + 10).toFixed(2), // 随机价格
};
}
// 组件所需的临时数据
this.$parent.data.tempData = {
goodsSources: this.goodsSources,
templateList: this.templateList,
ornamentList: this.ornamentList,
nameLineModeList: this.nameLineModeList,
styleList: this.styleList,
previewList: previewList,
methods: {
addGoods: this.addGoods,
selectTemplate: this.selectTemplate,
selectTopStyle: this.selectTopStyle
}
};
},
methods: {
verify: function (index) {
var res = {code: true, message: ""};
if (vue.data[index].sources === 'diy' && vue.data[index].goodsId.length === 0) {
res.code = false;
res.message = "请选择商品";
}
return res;
},
addGoods: function () {
var self = this;
goodsSelect(function (res) {
self.$parent.data.goodsId = res;
}, self.$parent.data.goodsId, {mode: "spu", promotion: "seckill", disabled: 0, post: ns.appModule});
},
selectTemplate(template, item) {
if (template) {
this.$parent.data.template = template;
item = this.templateList[template].styleList[0];
}
this.$parent.data.style = item.value;
this.$parent.data.btnStyle.support = item.cartSupport;
this.$parent.data.btnStyle.control = item.cartSupport;
this.$parent.data.priceStyle.lineSupport = item.lineSupport;
this.$parent.data.priceStyle.lineControl = item.lineSupport;
this.$parent.data.saleStyle.support = item.saleSupport;
this.$parent.data.saleStyle.control = item.saleSupport;
this.$parent.data.progressStyle.support = item.progressSupport;
this.$parent.data.progressStyle.control = item.progressSupport;
if (item.mainColor) {
this.$parent.data.priceStyle.mainColor = item.mainColor;
} else {
this.$parent.data.priceStyle.mainColor = '#FF1745';
}
if (item.goodsNameControl !== undefined) {
this.$parent.data.goodsNameStyle.control = item.goodsNameControl;
} else {
this.$parent.data.goodsNameStyle.control = true;
}
if (this.data.template == "row1-of1" && this.data.style == "style-2") {
this.$parent.data.progressStyle.bgColor = "#FFD5D5";
this.$parent.data.progressStyle.currColor = "#ff0400";
}
},
selectTopStyle: function () {
var self = this;
layer.open({
type: 1,
title: '风格选择',
area: ['910px', '360px'],
btn: ['确定', '返回'],
content: $(".draggable-element[data-index='" + self.data.index + "'] .edit-attribute .style-list-box-seckill").html(),
success: function (layero, index) {
$(".layui-layer-content input[name='style']").val(self.data.titleStyle.style);
$(".layui-layer-content input[name='style_name']").val(self.data.titleStyle.styleName);
$("body").off("click", ".layui-layer-content .style-list-con-seckill .style-li-seckill").on("click", ".layui-layer-content .style-list-con-seckill .style-li-seckill", function () {
$(this).addClass("selected border-color").siblings().removeClass("selected border-color bg-color-after");
$(".layui-layer-content input[name='style']").val($(this).attr("data_key"));
$(".layui-layer-content input[name='style_name']").val($(this).find("span").text());
});
},
yes: function (index, layero) {
self.data.titleStyle.style = $(".layui-layer-content input[name='style']").val();
Object.assign(self.data.titleStyle, self.styleList[self.data.titleStyle.style]);
self.data.titleStyle.styleName = $(".layui-layer-content input[name='style_name']").val();
layer.closeAll();
loadImgMagnify();
}
});
}
}
});

View File

@@ -0,0 +1,68 @@
<?php
/**
*/
return [
// 自定义模板页面类型,格式:[ 'title' => '页面类型名称', 'name' => '页面标识', 'path' => '页面路径', 'value' => '页面数据json格式' ]
'template' => [],
// 后台自定义组件——装修
'util' => [
[
'name' => 'Seckill',
'title' => '秒杀',
'type' => 'PROMOTION',
'value' => '{"style":"style-1","sources":"initial","count":6,"goodsId":[],"goodsMarginType":"default","goodsMarginNum":10,"ornament":{"type":"default","color":"#EDEDED"},"nameLineMode":"single","template":"row1-of1","btnStyle":{"text":"去秒杀","textColor":"#FFFFFF","theme":"default","aroundRadius":25,"control":true,"support":true,"bgColorStart":"#FF7B1D","bgColorEnd":"#FF1544"},"imgAroundRadius":5,"saleStyle":{"color":"#999CA7","control":true,"support":true},"progressStyle":{"control":true,"support":true,"currColor":"#FDBE6C","bgColor":"#FCECD7"},"titleStyle":{"backgroundImage":"","isShow":true,"leftStyle":"text","leftImg":"","style":"style-1","styleName":"风格1","leftText":"限时秒杀","fontSize":16,"fontWeight":true,"textColor":"#303133","bgColorStart":"#FFFFFF","bgColorEnd":"#FFFFFF","more":"查看更多","moreColor":"#999999","moreFontSize":12,"timeBgColor":"","timeImageUrl":"","moreSupport":true,"colonColor":"#303133","numBgColorStart":"#303133","numBgColorEnd":"#303133","numTextColor":"#FFFFFF"},"slideMode":"scroll","theme":"default","priceStyle":{"mainColor":"#FF1745","mainControl":true,"lineColor":"#999CA7","lineControl":true,"lineSupport":true},"goodsNameStyle":{"color":"#303133","control":true,"fontWeight":false}}',
'sort' => '30003',
'support_diy_view' => '',
'max_count' => 1,
'icon' => 'iconfont iconmiaosha1'
]
],
// 自定义页面路径
'link' => [
[
'name' => 'SECKILL',
'title' => '秒杀',
'parent' => 'MARKETING_LINK',
'wap_url' => '',
'web_url' => '',
'sort' => 0,
'child_list' => [
[
'name' => 'SECKILL_PREFECTURE',
'title' => '秒杀抢购',
'wap_url' => '/pages_promotion/seckill/list',
'web_url' => '',
'sort' => 0
]
]
]
],
// 自定义图标库
'icon_library' => [],
// uni-app 组件,格式:[ 'name' => '组件名称/文件夹名称', 'path' => '文件路径/目录路径' ]多个逗号隔开自定义组件名称前缀必须是diy-,也可以引用第三方组件
'component' => [],
// uni-app 页面,多个逗号隔开
'pages' => [],
// 模板信息,格式:'title' => '模板名称', 'name' => '模板标识', 'cover' => '模板封面图', 'preview' => '模板预览图', 'desc' => '模板描述'
'info' => [],
// 主题风格配色格式可以自由定义扩展【在uni-app中通过this.themeStyle... 获取定义的颜色字段例如this.themeStyle.main_color】
'theme' => [],
// 自定义页面数据,格式:[ 'title' => '页面名称', 'name' => "页面标识", 'value' => [页面数据json格式] ]
'data' => []
];

View File

@@ -0,0 +1,52 @@
<?php
// 事件定义文件
return [
'bind' => [
],
'listen' => [
//秒杀开启
'OpenSeckill' => [
'addon\seckill\event\OpenSeckill',
],
//秒杀关闭
'CloseSeckill' => [
'addon\seckill\event\CloseSeckill',
],
//展示活动
'ShowPromotion' => [
'addon\seckill\event\ShowPromotion',
],
'PromotionType' => [
'addon\seckill\event\PromotionType',
],
// 商品营销活动类型
'GoodsPromotionType' => [
'addon\seckill\event\GoodsPromotionType',
],
// 商品营销活动信息
'GoodsPromotion' => [
'addon\seckill\event\GoodsPromotion',
],
// 订单营销活动类型
'OrderPromotionType' => [
'addon\seckill\event\OrderPromotionType',
],
// 订单营销活动类型
'OrderClose' => [
'addon\seckill\event\CronOrderClose',
],
// 活动专区——秒杀页面配置
'PromotionZoneConfig' => [
'addon\seckill\event\SeckillZoneConfig',
]
],
'subscribe' => [
],
];

View File

@@ -0,0 +1,20 @@
<?php
/**
*/
return [
'name' => 'seckill',
'title' => '秒杀抢购',
'description' => '低价、限购,吸引更多会员',
'type' => 'promotion', //插件类型 system :系统插件 promotion:营销插件 tool:工具插件
'status' => 1,
'author' => '',
'version' => '5.3.1',
'version_no' => '525231212001',
'content' => '',
];

View File

@@ -0,0 +1,91 @@
<?php
// +----------------------------------------------------------------------
// | 平台端菜单设置
// +----------------------------------------------------------------------
return [
[
'name' => 'PROMOTION_SECKILL',
'title' => '秒杀抢购',
'url' => 'seckill://shop/seckill/goodslist',
'parent' => 'PROMOTION_TOOL',
'is_show' => 1,
'is_control' => 1,
'is_icon' => 0,
'picture' => '',
'picture_select' => '',
'sort' => 100,
'child_list' => [
[
'name' => 'PROMOTION_SECKILL_GOODS_LIST',
'title' => '商品管理',
'url' => 'seckill://shop/seckill/goodslist',
'parent' => 'PROMOTION_TOOL',
'is_show' => 1,
'is_control' => 1,
'is_icon' => 0,
'picture' => '',
'picture_select' => '',
'sort' => 1,
'child_list' => [
[
'name' => 'PROMOTION_SECKILL_GOODS_ADD',
'title' => '添加商品',
'url' => 'seckill://shop/seckill/addgoods',
'sort' => 1,
'is_show' => 0
],
[
'name' => 'PROMOTION_SECKILL_GOODS_UPDATE',
'title' => '编辑商品',
'url' => 'seckill://shop/seckill/updategoods',
'sort' => 2,
'is_show' => 0
],
[
'name' => 'PROMOTION_SECKILL_GOODS_DELETE',
'title' => '删除商品',
'url' => 'seckill://shop/seckill/deletegoods',
'sort' => 3,
'is_show' => 0
],
],
],
[
'name' => 'PROMOTION_SECKILL_TIME',
'title' => '场次管理',
'url' => 'seckill://shop/seckill/lists',
'parent' => 'PROMOTION_TOOL',
'is_show' => 1,
'is_control' => 1,
'is_icon' => 0,
'picture' => '',
'picture_select' => '',
'sort' => 2,
'child_list' => [
[
'name' => 'PROMOTION_SECKILL_ADD',
'title' => '添加场次',
'url' => 'seckill://shop/seckill/add',
'sort' => 1,
'is_show' => 0
],
[
'name' => 'PROMOTION_SECKILL_ADD',
'title' => '编辑场次',
'url' => 'seckill://shop/seckill/edit',
'sort' => 2,
'is_show' => 0
],
[
'name' => 'PROMOTION_SECKILL_DELETE',
'title' => '删除场次',
'url' => 'seckill://shop/seckill/delete',
'sort' => 3,
'is_show' => 0
],
],
],
]
],
];

View File

@@ -0,0 +1 @@
SET NAMES 'utf8';

View File

@@ -0,0 +1 @@
SET NAMES 'utf8';

View File

@@ -0,0 +1,27 @@
<?php
/**
*/
namespace addon\seckill\event;
use addon\seckill\model\Seckill;
/**
* 关闭活动
*/
class CloseSeckill
{
public function handle($params)
{
$seckill = new Seckill();
$res = $seckill->cronCloseSeckill($params[ 'relate_id' ]);
return $res;
}
}

View File

@@ -0,0 +1,33 @@
<?php
/**
*/
namespace addon\seckill\event;
use addon\seckill\model\SeckillOrder;
/**
* 订单营销活动类型
*/
class CronOrderClose
{
/**
* 订单关闭
* @param $params
* @return array
*/
public function handle($params)
{
$seckill = new SeckillOrder();
$res = $seckill->cronOrderClose($params[ 'order_id' ]);
return $res;
}
}

View File

@@ -0,0 +1,65 @@
<?php
/**
*/
namespace addon\seckill\event;
use addon\seckill\model\Seckill;
use app\model\goods\Goods as GoodsModel;
/**
* 商品营销活动信息
*/
class GoodsPromotion
{
/**
* 商品营销活动信息
* @param $param
* @return array
*/
public function handle($param)
{
if (isset($param['goods_sku_detail'])) {
$goods_info = $param['goods_sku_detail'];
if (!empty($goods_info['promotion_addon'])) {
$promotion_addon = json_decode($goods_info['promotion_addon'], true);
if (!empty($promotion_addon['seckill'])) {
return [
'promotion_type' => 'seckill',
'promotion_name' => '限时秒杀',
'id' => $promotion_addon['seckill']
];
}
}
} else {
if (empty($param['goods_id'])) return [];
$goods_model = new GoodsModel();
$goods_info = $goods_model->getGoodsInfo([['goods_id', '=', $param['goods_id']]], 'promotion_addon')['data'];
if (!empty($goods_info['promotion_addon'])) {
$promotion_addon = json_decode($goods_info['promotion_addon'], true);
if (!empty($promotion_addon['seckill'])) {
$seckill_model = new Seckill();
$goods_detail = $seckill_model->getSeckillInfo($promotion_addon['seckill'])['data'];
if (!empty($goods_detail)) {
$time = time() - strtotime(date('Y-m-d'), time());
if ($time > $goods_detail['seckill_start_time'] && $time < $goods_detail['seckill_end_time']) {
$goods_detail['promotion_type'] = 'seckill';
$goods_detail['promotion_name'] = '限时秒杀';
return $goods_detail;
}
}
}
}
return [];
}
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
*/
namespace addon\seckill\event;
/**
* 活动类型
*/
class GoodsPromotionType
{
/**
* 活动类型
* @return array
*/
public function handle()
{
return ['name' => '限时秒杀', 'short' => '秒', 'type' => 'seckill', 'color' => '#89689D', 'url' => 'seckill://shop/seckill/goodslist'];
}
}

View File

@@ -0,0 +1,32 @@
<?php
/**
*/
namespace addon\seckill\event;
/**
* 应用安装
*/
class Install
{
/**
* 执行安装
*/
public function handle()
{
/* try{
execute_sql('addon/seckill/data/install.sql');
return success();
}catch (\Exception $e)
{
return error('', $e->getMessage());
} */
return success();
}
}

View File

@@ -0,0 +1,27 @@
<?php
/**
*/
namespace addon\seckill\event;
use addon\seckill\model\Seckill;
/**
* 开启活动
*/
class OpenSeckill
{
public function handle($params)
{
$seckill = new Seckill();
$res = $seckill->cronOpenSeckill($params[ 'relate_id' ]);
return $res;
}
}

View File

@@ -0,0 +1,27 @@
<?php
/**
*/
namespace addon\seckill\event;
/**
* 订单营销活动类型
*/
class OrderPromotionType
{
/**
* 订单营销活动类型
* @return array
*/
public function handle()
{
return [ "name" => "秒杀", "type" => "seckill" ];
}
}

View File

@@ -0,0 +1,27 @@
<?php
/**
*/
namespace addon\seckill\event;
/**
* 活动类型
*/
class PromotionType
{
/**
* 活动类型
* @return array
*/
public function handle()
{
return [ "name" => "秒杀", "type" => "seckill" ];
}
}

View File

@@ -0,0 +1,37 @@
<?php
/**
*/
namespace addon\seckill\event;
/**
* 活动专区——限时秒杀页面配置
*/
class SeckillZoneConfig
{
public function handle($params)
{
if (empty($params) || $params[ 'name' ] == 'seckill') {
$data = [
'name' => 'seckill', // 标识
'title' => '限时秒杀', // 名称
'url' => 'shop/adv/lists?keyword=NS_SECKILL', // 自定义跳转链接
'preview' => 'addon/seckill/shop/view/public/img/zone_preview.png', // 预览图
// 页面配置
'value' => [
'bg_color' => '#F83530'
],
];
return $data;
}
}
}

View File

@@ -0,0 +1,91 @@
<?php
/**
*/
namespace addon\seckill\event;
use think\facade\Db;
/**
* 活动展示
*/
class ShowPromotion
{
public $promotion_type = 'time_limit';
/**
* 活动展示
* @param array $params
* @return array
*/
public function handle($params = [])
{
$data = [
'shop' => [
[
//插件名称
'name' => 'seckill',
//展示分类根据平台端设置shop店铺营销member:会员营销, tool:应用工具)
'show_type' => 'shop',
//展示主题
'title' => '限时秒杀',
//展示介绍
'description' => '限时抢购引导客户快速下单',
//展示图标
'icon' => 'addon/seckill/icon.png',
//跳转链接
'url' => 'seckill://shop/seckill/goodslist',
'summary' => $this->summary($params)
]
]
];
return $data;
}
/**
* 营销活动概况
* @param $params
* @return array
*/
private function summary($params)
{
if (empty($params)) {
return [];
}
if(isset($params['promotion_type']) && $params['promotion_type'] != $this->promotion_type){
return [];
}
//获取活动数量
if (isset($params[ 'count' ])) {
$count = model("promotion_seckill")->getCount([ [ 'site_id', '=', $params[ 'site_id' ] ] ]);
return [
'count' => $count
];
}
//获取活动概况,需要获取开始时间与结束时间
if (isset($params[ 'summary' ])) {
$list = model("promotion_seckill")->getList([
[ '', 'exp', Db::raw('not ( (`start_time` >= ' . $params[ 'end_time' ] . ') or (`end_time` <= ' . $params[ 'start_time' ] . '))') ],
[ 'site_id', '=', $params[ 'site_id' ] ],
[ 'status', '<>', 2 ],
[ 'status', '<>', -1 ]
], 'seckill_name as promotion_name,id as promotion_id,start_time,end_time', '', 'a', null, 'seckill_name');
return !empty($list) ? [
'time_limit' => [
'count' => count($list),
'detail' => $list,
'color' => '#6D66FF'
]
] : [];
}
}
}

View File

@@ -0,0 +1,32 @@
<?php
/**
*/
namespace addon\seckill\event;
/**
* 应用卸载
*/
class UnInstall
{
/**
* 执行卸载
*/
public function handle()
{
/* try{
execute_sql('addon/seckill/data/uninstall.sql');
return success();
}catch (\Exception $e)
{
return error('', $e->getMessage());
} */
return error("系统插件不能删除");
}
}

BIN
src/addon/seckill/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@@ -0,0 +1,618 @@
<?php
/**
*/
namespace addon\seckill\model;
use addon\postertemplate\model\PosterTemplate as PosterTemplateModel;
use app\model\BaseModel;
use app\model\system\Site;
use app\model\upload\Upload;
use app\model\web\Config;
use extend\Poster as PosterExtend;
/**
* 海报生成类
*/
class Poster extends BaseModel
{
/**
* 商品海报
*/
public function goods($app_type, $page, $qrcode_param, $promotion_type, $site_id)
{
try {
$goods_info = $this->getGoodsInfo($qrcode_param[ 'id' ]);
if (empty($goods_info)) return $this->error('未获取到商品信息');
$qrcode_param[ 'id' ] = $qrcode_param[ 'seckillId' ];
unset($qrcode_param[ 'seckillId' ]);
$qrcode_info = $this->getGoodsQrcode($app_type, $page, $qrcode_param, $promotion_type, $site_id);
if ($qrcode_info[ 'code' ] < 0) return $qrcode_info;
//判断海报是否存在或停用
$template_info = $this->getTemplateInfo($goods_info[ 'template_id' ]);
if (!empty($qrcode_param[ 'source_member' ])) {
$member_info = $this->getMemberInfo($qrcode_param[ 'source_member' ]);
}
$upload_config_model = new Config();
$upload_config_result = $upload_config_model->getDefaultImg($site_id);
$site_model = new Site();
$condition = array (
[ "site_id", "=", $site_id ]
);
$site_info = $site_model->getSiteInfo($condition);
if (empty($goods_info[ 'template_id' ]) || empty($template_info) || $template_info[ 'template_status' ] == 0) {
$poster_width = 720;
$poster_height = 1150;
$poster = new PosterExtend($poster_width, $poster_height);
$option = [
[
'action' => 'setBackground', // 设背景色
'data' => [ 255, 255, 255 ]
],
[
'action' => 'imageCopy', // 写入商品图
'data' => [
explode(',', $goods_info[ 'sku_image' ])[ 0 ] ?? '',
50,
165,
620,
620,
'square',
true,
1
]
],
[
'action' => 'imageText', // 写入商品名称
'data' => [
$goods_info[ 'sku_name' ],
22,
[ 35, 35, 35 ],
50,
915,
360,
2,
true,
1
]
],
[
'action' => 'imageCopy', // 写入商品二维码
'data' => [
$qrcode_info[ 'data' ][ 'path' ],
435,
825,
240,
240,
'square',
0,
1
]
],
[
'action' => 'imageText', // 写入提示
'data' => [
'长按识别二维码',
19,
[ 102, 102, 102 ],
465,
1110,
490,
1,
1,
1
]
],
[
'action' => 'imageText', // 写入商品价格单位
'data' => [
'秒杀价:¥',
22,
[ 255, 0, 0 ],
50,
860,
490,
2,
true,
1
]
],
[
'action' => 'imageText', // 写入商品价格
'data' => [
$goods_info[ 'seckill_price' ],
30,
[ 255, 0, 0 ],
188,
862,
490,
2,
true,
1
]
],
];
if (!empty($member_info)) {
$member_option = [
[
'action' => 'imageCircularCopy', // 写入用户头像
'data' => [
!empty($member_info[ 'headimg' ]) ? $member_info[ 'headimg' ] : $upload_config_result[ 'data' ][ 'value' ][ 'head' ],
50,
30,
100,
100
]
],
[
'action' => 'imageText', // 写入分享人昵称
'data' => [
$member_info[ 'nickname' ],
22,
[ 10, 10, 10 ],
170,
80,
580,
1,
1,
1
]
],
[
'action' => 'imageText', // 写入分享人昵称
'data' => [
'限时秒杀,抢到就是赚到',
18,
[ 102, 102, 102 ],
170,
115,
580,
1,
1,
1
]
]
];
$option = array_merge($option, $member_option);
}
} else {
$condition = [
[ 'template_id', '=', $goods_info[ 'template_id' ] ],
[ 'site_id', '=', $site_id ]
];
$poster_template_model = new PosterTemplateModel();
$poster_data = $poster_template_model->getPosterTemplateInfo($condition);
$poster_data[ 'data' ][ 'template_json' ] = json_decode($poster_data[ 'data' ][ 'template_json' ], true);
$poster_width = 720;
$poster_height = 1280;
$poster = new PosterExtend($poster_width, $poster_height);
$fontRate = 0.725; // 20px 等于 14.5磅,换算比率 1px = 0.725磅
if (!empty($poster_data[ 'data' ][ 'background' ])) {
list($width, $height, $type, $attr) = getimagesize(img($poster_data[ 'data' ][ 'background' ]));
$height = 720 * $height / $width;
$back_ground = [
'action' => 'imageCopy', // 写入背景图
'data' => [
img($poster_data[ 'data' ][ 'background' ]),
0,
0,
$poster_width,
$height,
'square',
true,
1
]
];
} else {
$back_ground = [
'action' => 'setBackground', // 设背景色
'data' => [ 255, 255, 255 ]
];
}
$ground = [
[
'action' => 'setBackground',
'data' => [ 255, 255, 255 ]
]
];
$option = [
$back_ground,
[
'action' => 'imageText', // 写入店铺名称
'data' => [
$site_info[ 'data' ][ 'site_name' ],
$poster_data[ 'data' ][ 'template_json' ][ 'store_name_font_size' ] * $fontRate * 2,
hex2rgb($poster_data[ 'data' ][ 'template_json' ][ 'store_name_color' ]),
$poster_data[ 'data' ][ 'template_json' ][ 'store_name_left' ] * 2,
( $poster_data[ 'data' ][ 'template_json' ][ 'store_name_top' ] + $poster_data[ 'data' ][ 'template_json' ][ 'store_name_font_size' ] ) * 2,
$poster_data[ 'data' ][ 'template_json' ][ 'store_name_width' ] * 2,
$poster_data[ 'data' ][ 'template_json' ][ 'store_name_height' ] * 2,
true,
$poster_data[ 'data' ][ 'template_json' ][ 'store_name_is_show' ]
]
],
[
'action' => 'imageCopy', // 店铺logo
'data' => [
!empty($site_info[ 'data' ][ 'logo_square' ]) ? $site_info[ 'data' ][ 'logo_square' ] : getUrl() . '/app/shop/view/public/img/shop_logo.png',
$poster_data[ 'data' ][ 'template_json' ][ 'store_logo_left' ] * 2,
$poster_data[ 'data' ][ 'template_json' ][ 'store_logo_top' ] * 2,
$poster_data[ 'data' ][ 'template_json' ][ 'store_logo_width' ] * 2,
$poster_data[ 'data' ][ 'template_json' ][ 'store_logo_height' ] * 2,
'square',
true,
$poster_data[ 'data' ][ 'template_json' ][ 'store_logo_is_show' ]
]
],
[
'action' => 'imageCopy', // 写入商品图
'data' => [
$goods_info[ 'sku_image' ],
$poster_data[ 'data' ][ 'template_json' ][ 'goods_img_left' ] * 2,
$poster_data[ 'data' ][ 'template_json' ][ 'goods_img_top' ] * 2,
$poster_data[ 'data' ][ 'template_json' ][ 'goods_img_width' ] * 2,
$poster_data[ 'data' ][ 'template_json' ][ 'goods_img_height' ] * 2,
!empty($poster_data[ 'data' ][ 'template_json' ][ 'goods_img_shape' ]) ? $poster_data[ 'data' ][ 'template_json' ][ 'goods_img_shape' ] : 'square',
0,
$poster_data[ 'data' ][ 'template_json' ][ 'goods_img_is_show' ]
]
],
[
'action' => 'imageText', // 写入商品名称
'data' => [
$goods_info[ 'sku_name' ],
$poster_data[ 'data' ][ 'template_json' ][ 'goods_name_font_size' ] * $fontRate * 2,
hex2rgb($poster_data[ 'data' ][ 'template_json' ][ 'goods_name_color' ]),
$poster_data[ 'data' ][ 'template_json' ][ 'goods_name_left' ] * 2,
( $poster_data[ 'data' ][ 'template_json' ][ 'goods_name_top' ] + $poster_data[ 'data' ][ 'template_json' ][ 'goods_name_font_size' ] ) * 2,
$poster_data[ 'data' ][ 'template_json' ][ 'goods_name_width' ] * 2,
1,//文本行数 $poster_data['data']['template_json']['goods_name_height']*2,
true,
$poster_data[ 'data' ][ 'template_json' ][ 'goods_name_is_show' ]
]
],
[
'action' => 'imageCopy', // 写入商品二维码
'data' => [
$qrcode_info[ 'data' ][ 'path' ],
$poster_data[ 'data' ][ 'qrcode_left' ] * 2,
$poster_data[ 'data' ][ 'qrcode_top' ] * 2,
$poster_data[ 'data' ][ 'qrcode_width' ] * 2,
$poster_data[ 'data' ][ 'qrcode_height' ] * 2,
'square',
0,
1
]
],
[
'action' => 'imageText', // 写入商品价格
'data' => [
'¥' . $goods_info[ 'seckill_price' ],
$poster_data[ 'data' ][ 'template_json' ][ 'goods_price_font_size' ] * $fontRate * 2,
hex2rgb($poster_data[ 'data' ][ 'template_json' ][ 'goods_price_color' ]),
$poster_data[ 'data' ][ 'template_json' ][ 'goods_price_left' ] * 2,
( $poster_data[ 'data' ][ 'template_json' ][ 'goods_price_top' ] + $poster_data[ 'data' ][ 'template_json' ][ 'goods_price_font_size' ] ) * 2,
$poster_data[ 'data' ][ 'template_json' ][ 'goods_price_width' ] * 2,
$poster_data[ 'data' ][ 'template_json' ][ 'goods_price_height' ] * 2,
true,
$poster_data[ 'data' ][ 'template_json' ][ 'goods_price_is_show' ]
]
],
];
if ($goods_info[ 'price' ] == 0) {
$line = '一一一';
} else {
$line = '一一一一';
}
$market_price = [
[
'action' => 'imageText', // 写入商品划线价格
'data' => [
'¥' . $goods_info[ 'price' ],
$poster_data[ 'data' ][ 'template_json' ][ 'goods_market_price_font_size' ] * $fontRate * 2,
hex2rgb($poster_data[ 'data' ][ 'template_json' ][ 'goods_market_price_color' ]),
$poster_data[ 'data' ][ 'template_json' ][ 'goods_market_price_left' ] * 2,
( $poster_data[ 'data' ][ 'template_json' ][ 'goods_market_price_top' ] + $poster_data[ 'data' ][ 'template_json' ][ 'goods_market_price_font_size' ] ) * 2,
$poster_data[ 'data' ][ 'template_json' ][ 'goods_market_price_width' ] * 2,
$poster_data[ 'data' ][ 'template_json' ][ 'goods_market_price_height' ] * 2,
true,
$poster_data[ 'data' ][ 'template_json' ][ 'goods_market_price_is_show' ] ?? 0
]
],
[
'action' => 'imageText', // 写入线
'data' => [
$line,
$poster_data[ 'data' ][ 'template_json' ][ 'goods_market_price_font_size' ] * $fontRate * 2,
hex2rgb($poster_data[ 'data' ][ 'template_json' ][ 'goods_market_price_color' ]),
$poster_data[ 'data' ][ 'template_json' ][ 'goods_market_price_left' ] * 2 - 5,
( $poster_data[ 'data' ][ 'template_json' ][ 'goods_market_price_top' ] + $poster_data[ 'data' ][ 'template_json' ][ 'goods_market_price_font_size' ] ) * 2,
$poster_data[ 'data' ][ 'template_json' ][ 'goods_market_price_width' ] * 2,
$poster_data[ 'data' ][ 'template_json' ][ 'goods_market_price_height' ] * 2,
true,
$poster_data[ 'data' ][ 'template_json' ][ 'goods_market_price_is_show' ]
]
],
];
$option = array_merge($option, $market_price);
if (!empty($member_info)) {
$member_option = [
[
'action' => 'imageCopy', // 写入用户头像
'data' => [
!empty($member_info[ 'headimg' ]) ? $member_info[ 'headimg' ] : $upload_config_result[ 'data' ][ 'value' ][ 'head' ],
$poster_data[ 'data' ][ 'template_json' ][ 'headimg_left' ] * 2,
$poster_data[ 'data' ][ 'template_json' ][ 'headimg_top' ] * 2,
$poster_data[ 'data' ][ 'template_json' ][ 'headimg_width' ] * 2,
$poster_data[ 'data' ][ 'template_json' ][ 'headimg_height' ] * 2,
!empty($poster_data[ 'data' ][ 'template_json' ][ 'headimg_shape' ]) ? $poster_data[ 'data' ][ 'template_json' ][ 'headimg_shape' ] : 'square',
0,
$poster_data[ 'data' ][ 'template_json' ][ 'headimg_is_show' ]
]
],
[
'action' => 'imageText', // 写入分享人昵称
'data' => [
$member_info[ 'nickname' ],
$poster_data[ 'data' ][ 'template_json' ][ 'nickname_font_size' ] * $fontRate * 2,
hex2rgb($poster_data[ 'data' ][ 'template_json' ][ 'nickname_color' ]),
$poster_data[ 'data' ][ 'template_json' ][ 'nickname_left' ] * 2,
( $poster_data[ 'data' ][ 'template_json' ][ 'nickname_top' ] + $poster_data[ 'data' ][ 'template_json' ][ 'nickname_font_size' ] ) * 2,
$poster_data[ 'data' ][ 'template_json' ][ 'nickname_width' ] * 2,
$poster_data[ 'data' ][ 'template_json' ][ 'nickname_height' ] * 2,
0,
$poster_data[ 'data' ][ 'template_json' ][ 'nickname_is_show' ]
]
],
];
$option = array_merge($ground, $option, $member_option);
}
}
$option_res = $poster->create($option);
if (is_array($option_res)) return $option_res;
$res = $option_res->jpeg('upload/poster/goods', 'goods_' . $promotion_type . '_' . $qrcode_param[ 'id' ] . '_' . $qrcode_param[ 'source_member' ] . '_' . time() . '_' . $app_type);
if ($res[ 'code' ] == 0) {
$upload = new Upload($site_id);
$cloud_res = $upload->fileCloud($res[ 'data' ][ 'path' ]);
if ($cloud_res[ 'code' ] >= 0) {
return $this->success([ "path" => $cloud_res[ 'data' ] ]);
} else {
return $this->error();
}
}
return $res;
} catch (\Exception $e) {
return $this->error($e->getMessage() . $e->getFile() . $e->getLine());
}
}
/**
* 获取用户信息
* @param unknown $member_id
*/
private function getMemberInfo($member_id)
{
$info = model('member')->getInfo([ 'member_id' => $member_id ], 'nickname,headimg');
return $info;
}
/**
* 获取商品信息
* @param unknown $sku_id
*/
private function getGoodsInfo($id)
{
$alias = 'npsg';
$join = [
[ 'goods_sku ngs', 'npsg.sku_id = ngs.sku_id', 'inner' ]
];
$field = 'ngs.sku_name,ngs.introduction,ngs.sku_image,ngs.sku_id,npsg.seckill_price, npsg.seckill_id,ngs.template_id,ngs.price';
$info = model('promotion_seckill_goods')->getInfo([ 'npsg.goods_id' => $id ], $field, $alias, $join);
return $info;
}
/**
* 获取商品二维码
* @param unknown $app_type 请求类型
* @param unknown $page uniapp页面路径
* @param unknown $qrcode_param 二维码携带参数
* @param string $promotion_type 活动类型 null为无活动
*/
private function getGoodsQrcode($app_type, $page, $qrcode_param, $promotion_type, $site_id)
{
$res = event('Qrcode', [
'site_id' => $site_id,
'app_type' => $app_type,
'type' => 'create',
'data' => $qrcode_param,
'page' => $page,
'qrcode_path' => 'upload/qrcode/goods',
'qrcode_name' => 'goods_' . $promotion_type . '_' . $qrcode_param[ 'id' ] . '_' . $qrcode_param[ 'source_member' ] . '_' . $site_id,
], true);
return $res;
}
/**
* 获取海报信息
* @param unknown $template_id
*/
private function getTemplateInfo($template_id)
{
$info = model('poster_template')->getInfo([ 'template_id' => $template_id ], 'template_id,template_status');
return $info;
}
/**
* 分享图片
* @param $page
* @param $qrcode_param
* @param $site_id
* @return array|\extend\multitype|PosterExtend|string|string[]
*/
public function shareImg($page, $qrcode_param, $site_id)
{
try {
$goods_info = $this->getGoodsInfo($qrcode_param[ 'id' ]);
if (empty($goods_info)) return $this->error('未获取到商品信息');
$poster_width = 600;
$poster_height = 480;
$poster = new PosterExtend($poster_width, $poster_height);
$option = [
[
'action' => 'setBackground', // 设背景色
'data' => [ 255, 255, 255 ]
],
[
'action' => 'imageCopy', // 商品图
'data' => [
$goods_info[ 'sku_image' ],
30,
145,
200,
200,
'square',
50,
1
]
],
[
'action' => 'imageText', // 写入商品名称
'data' => [
$goods_info[ 'sku_name' ],
22,
[ 51, 51, 51 ],
250,
190,
330,
2,
false,
1
]
],
[
'action' => 'imageText', // 写入商品价格
'data' => [
'秒杀价:¥',
15,
[ 255, 0, 0 ],
250,
300,
300,
2,
false,
1
]
],
[
'action' => 'imageText', // 写入商品价格
'data' => [
$goods_info[ 'seckill_price' ],
30,
[ 255, 0, 0 ],
345,
300,
300,
2,
false,
1,
PUBLIC_PATH . 'static/font/custom.ttf'
]
],
[
'action' => 'imageText', // 写入商品原价
'data' => [
'原 价:¥',
15,
[ 153, 153, 153 ],
250,
340,
300,
2,
false,
1
]
],
[
'action' => 'imageText', // 写入商品原价
'data' => [
$goods_info[ 'price' ],
16,
[ 153, 153, 153 ],
345,
338,
300,
2,
false,
1,
PUBLIC_PATH . 'static/font/custom.ttf',
]
],
// 划线(两条线)
[
'action' => 'imageline',
'data' => [
325,
330,
325 + imagettfbbox(16, 0, PUBLIC_PATH . 'static/font/custom.ttf', '¥ ' . $goods_info[ 'price' ])[ 2 ],
330,
[ 153, 153, 153 ],
]
],
[
'action' => 'imageline',
'data' => [
325,
331,
325 + imagettfbbox(16, 0, PUBLIC_PATH . 'static/font/custom.ttf', '¥ ' . $goods_info[ 'price' ])[ 2 ],
331,
[ 153, 153, 153 ],
]
],
[
'action' => 'imageCopy', // 背景图
'data' => [
img('upload/share_img/bg/seckill_1.png'),
0,
0,
600,
480,
'square',
0,
1
]
],
];
$option_res = $poster->create($option);
if (is_array($option_res)) {
return $option_res;
}
$res = $option_res->jpeg('upload/share_img/seckill_' . $goods_info[ 'seckill_id' ], 'sku_' . $goods_info[ 'sku_id' ]);
return $res;
} catch (\Exception $e) {
return $this->error($e->getMessage());
}
}
}

View File

@@ -0,0 +1,901 @@
<?php
/**
*/
namespace addon\seckill\model;
use app\model\BaseModel;
use app\model\goods\Goods;
use app\model\system\Config as ConfigModel;
use app\model\system\Cron;
use think\facade\Cache;
use think\facade\Db;
/**
* 限时秒杀(时段)
*/
class Seckill extends BaseModel
{
/**
* 添加秒杀时段
* @param $data
* @return array
*/
public function addSeckillTime($data)
{
if ($data[ 'seckill_start_time' ] > $data[ 'seckill_end_time' ]) {
return $this->error('', '秒杀时间段设置错误');
}
//时间段检测
$seckill_count = model('promotion_seckill_time')->getCount([
[ 'seckill_start_time|seckill_end_time', 'between', [ $data[ 'seckill_start_time' ], $data[ 'seckill_end_time' ] ] ],
[ 'site_id', '=', $data[ 'site_id' ] ]
]);
if ($seckill_count > 0) {
return $this->error('', '秒杀场次设置冲突');
}
$seckill_count = model('promotion_seckill_time')->getCount([
[ 'seckill_start_time', '<=', $data[ 'seckill_start_time' ] ],
[ 'seckill_end_time', '>=', $data[ 'seckill_end_time' ] ],
[ 'site_id', '=', $data[ 'site_id' ] ]
]);
if ($seckill_count > 0) {
return $this->error('', '秒杀场次设置冲突');
}
//添加数据
$data[ 'create_time' ] = time();
$seckill_id = model('promotion_seckill_time')->add($data);
Cache::tag("promotion_seckill_time")->clear();
return $this->success($seckill_id);
}
/**
* 修改秒杀时段
* @param $data
* @param $site_id
* @return array
*/
public function editSeckillTime($data, $site_id)
{
if ($data[ 'seckill_start_time' ] > $data[ 'seckill_end_time' ]) {
return $this->error('', '秒杀时间段设置错误');
}
//时间段检测
$seckill_count = model('promotion_seckill_time')->getCount([
[ 'seckill_start_time|seckill_end_time', 'between', [ $data[ 'seckill_start_time' ], $data[ 'seckill_end_time' ] ] ],
[ 'site_id', '=', $site_id ],
[ 'id', '<>', $data[ 'id' ] ]
]);
if ($seckill_count > 0) {
return $this->error('', '秒杀场次设置冲突');
}
$seckill_count = model('promotion_seckill_time')->getCount([
[ 'seckill_start_time', '<=', $data[ 'seckill_start_time' ] ],
[ 'seckill_end_time', '>=', $data[ 'seckill_end_time' ] ],
[ 'site_id', '=', $site_id ],
[ 'id', '<>', $data[ 'id' ] ]
]);
if ($seckill_count > 0) {
return $this->error('', '秒杀场次设置冲突');
}
//更新数据
$data[ 'modify_time' ] = time();
$res = model('promotion_seckill_time')->update($data, [ [ 'id', '=', $data[ 'id' ] ] ]);
Cache::tag("promotion_seckill_time")->clear();
return $this->success($res);
}
/**
* 删除秒杀时段
* @param $seckill_time_id
* @return array
*/
public function deleteSeckillTime($seckill_time_id)
{
$res = model('promotion_seckill_time')->delete([ [ 'id', '=', $seckill_time_id ] ]);
if ($res) {
$goods = new Goods();
$condition = [
[ 'seckill_time_id', 'like', '%,' . $seckill_time_id . ',%' ]
];
$seckill_list = model('promotion_seckill')->getList($condition, 'id,seckill_time_id,goods_id');
foreach ($seckill_list as $k => $v) {
$time_ids = explode(',', trim($v[ 'seckill_time_id' ], ','));
unset($time_ids[ array_search($seckill_time_id, $time_ids) ]);
if (empty($time_ids)) {
$goods->modifyPromotionAddon($v[ 'goods_id' ], [ 'seckill' => $v[ 'id' ] ], true);
model('promotion_seckill_goods')->delete([ [ 'seckill_id', '=', $v[ 'id' ] ] ]);
model('promotion_seckill')->delete([ [ 'id', '=', $v[ 'id' ] ] ]);
} else {
$time_id = ',' . implode(',', $time_ids) . ',';
model('promotion_seckill')->update([ 'seckill_time_id' => $time_id ], [ [ 'id', '=', $v[ 'id' ] ] ]);
model('promotion_seckill_goods')->update([ 'seckill_time_id' => $time_id ], [ [ 'seckill_id', '=', $v[ 'id' ] ] ]);
}
}
}
Cache::tag("promotion_seckill_time")->clear();
return $this->success($res);
}
/**
* 获取秒杀时段信息
* @param $condition
* @param string $field
* @return array
*/
public function getSeckillTimeInfo($condition, $field = '*')
{
$data = json_encode([ $condition, $field ]);
$cache = Cache::get("promotion_seckill_time_getSeckillInfo_" . $data);
if (!empty($cache)) {
return $this->success($cache);
}
$res = model('promotion_seckill_time')->getInfo($condition, $field);
Cache::tag("promotion_seckill_time")->set("promotion_seckill_time_getSeckillInfo_" . $data, $res);
return $this->success($res);
}
/**
* 获取秒杀时段列表
* @param array $condition
* @param string $field
* @param string $order
* @param null $limit
* @return array
*/
public function getSeckillTimeList($condition = [], $field = '*', $order = '', $limit = null)
{
$list = model('promotion_seckill_time')->getList($condition, $field, $order, '', '', '', $limit);
return $this->success($list);
}
/**
* 获取有商品的秒杀时段列表
* @param $condition
* @param $field
* @param string $order
* @return array
*/
public function getGoodsSeckillTimeList($condition, $field, $order = 'seckill_start_time asc')
{
if (empty($field)) {
$field = 'id, site_id, name, seckill_start_time, seckill_end_time';
}
$seckill_time = model('promotion_seckill_time')->getList($condition, $field, $order);
foreach ($seckill_time as $k => $v) {
$condition = [
[ 'seckill_time_id', 'like', '%,' . $v[ 'id' ] . ',%' ],
[ 'status', '=', 1 ],
[ 'g.goods_state', '=', 1 ],
[ 'g.is_delete', '=', 0 ]
];
$join = [
[ 'goods g', 'g.goods_id = psg.goods_id', 'inner' ]
];
$goods = model('promotion_seckill_goods')->getInfo($condition, 'id', 'psg', $join);
if (empty($goods)) unset($seckill_time[ $k ]);
}
return $this->success($seckill_time);
}
/**
* 转换秒杀时间
* @param $info
* @return mixed
*/
public function transformSeckillTime($info)
{
$info[ 'start_hour' ] = floor($info[ 'seckill_start_time' ] / 3600);
$info[ 'start_minute' ] = floor(( $info[ 'seckill_start_time' ] % 3600 ) / 60);
$info[ 'start_second' ] = $info[ 'seckill_start_time' ] % 60;
$info[ 'end_hour' ] = floor($info[ 'seckill_end_time' ] / 3600);
$info[ 'end_minute' ] = floor(( $info[ 'seckill_end_time' ] % 3600 ) / 60);
$info[ 'end_second' ] = $info[ 'seckill_end_time' ] % 60;
if ($info[ 'start_hour' ] < 10) $info[ 'start_hour' ] = '0' . $info[ 'start_hour' ];
if ($info[ 'start_minute' ] < 10) $info[ 'start_minute' ] = '0' . $info[ 'start_minute' ];
if ($info[ 'start_second' ] < 10) $info[ 'start_second' ] = '0' . $info[ 'start_second' ];
if ($info[ 'end_hour' ] < 10) $info[ 'end_hour' ] = '0' . $info[ 'end_hour' ];
if ($info[ 'end_minute' ] < 10) $info[ 'end_minute' ] = '0' . $info[ 'end_minute' ];
if ($info[ 'end_second' ] < 10) $info[ 'end_second' ] = '0' . $info[ 'end_second' ];
return $info;
}
/******************************************************秒杀商品*********************************************************************/
/**
* 添加秒杀商品
* @param $data
* @return array
*/
public function addSeckillGoods($data)
{
$cron = new Cron();
$goods_data = $data[ 'goods_data' ];
if (empty($data[ 'seckill_time_id' ])) {
return $this->error('', '请选择秒杀时段');
}
$seckill_time_id = explode(',', $data[ 'seckill_time_id' ]);
//时间段检测
foreach ($seckill_time_id as $v) {
$seckill_count = model('promotion_seckill')->getCount([
[ 'goods_id', 'in', $data[ 'goods_ids' ] ],
[ 'status', 'in', '0,1' ],
[ 'seckill_time_id', 'like', '%,' . $v . ',%' ],
[ 'site_id', '=', $data[ 'site_id' ] ],
[ '', 'exp', Db::raw('not ( (`start_time` > ' . $data[ 'end_time' ] . ' and `start_time` > ' . $data[ 'start_time' ] . ' ) or (`end_time` < ' . $data[ 'start_time' ] . ' and `end_time` < ' . $data[ 'end_time' ] . '))') ]//todo 修正 所有的优惠都要一样
]);
if ($seckill_count > 0) {
return $this->error('', '有商品已设置秒杀,请不要重复设置');
}
}
model('promotion_seckill')->startTrans();
try {
$seckill_data = [
'site_id' => $data[ 'site_id' ],
'seckill_name' => $data[ 'seckill_name' ],
'remark' => $data[ 'remark' ],
'seckill_time_id' => ',' . $data[ 'seckill_time_id' ] . ',',
'start_time' => $data[ 'start_time' ],
'end_time' => $data[ 'end_time' ],
'create_time' => time(),
'sort' => $data[ 'sort' ],
];
$goods = new Goods();
$add_goods_data = [];
foreach ($goods_data as $k => $v) {
$seckill_data[ 'goods_id' ] = $v[ 'goods_id' ];
$seckill_data[ 'goods_name' ] = $v[ 'sku_list' ][ 0 ][ 'sku_name' ];
$seckill_data[ 'goods_image' ] = $v[ 'sku_list' ][ 0 ][ 'sku_image' ];
$seckill_data[ 'seckill_price' ] = $v[ 'sku_list' ][ 0 ][ 'seckill_price' ];
if ($data[ 'start_time' ] <= time()) {
$seckill_data[ 'status' ] = 1;//直接启动
$seckill_id = model('promotion_seckill')->add($seckill_data);
$goods->modifyPromotionAddon($v[ 'goods_id' ], [ 'seckill' => $seckill_id ]);
$cron->addCron(1, 0, "秒杀关闭", "CloseSeckill", $data[ 'end_time' ], $seckill_id);
} else {
$seckill_data[ 'status' ] = 0;
$seckill_id = model('promotion_seckill')->add($seckill_data);
$cron->addCron(1, 0, "秒杀开启", "OpenSeckill", $data[ 'start_time' ], $seckill_id);
$cron->addCron(1, 0, "秒杀关闭", "CloseSeckill", $data[ 'end_time' ], $seckill_id);
}
$goods_stock = 0; // 秒杀商品总库存
foreach ($v[ 'sku_list' ] as $key => $item) {
$sku_info = model('goods_sku')->getInfo([ [ 'sku_id', '=', $item[ 'sku_id' ] ] ], 'goods_id, sku_id, sku_name,price,sku_image');
$add_goods_data[] = [
'site_id' => $data[ 'site_id' ],
'seckill_id' => $seckill_id,
'seckill_time_id' => ',' . $data[ 'seckill_time_id' ] . ',',
'sku_id' => $item[ 'sku_id' ],
'goods_id' => $item[ 'goods_id' ],
'sku_image' => $sku_info[ 'sku_image' ],
'sku_name' => $sku_info[ 'sku_name' ],
'seckill_price' => $item[ 'seckill_price' ],
'price' => $sku_info[ 'price' ],
'stock' => $item[ 'seckill_stock' ],
'max_buy' => $item[ 'max_buy' ],
'status' => $seckill_data[ 'status' ]
];
$goods_stock += $item[ 'seckill_stock' ];
}
model('promotion_seckill')->update([ 'goods_stock' => $goods_stock ], [ [ 'id', '=', $seckill_id ] ]);
}
model('promotion_seckill_goods')->addList($add_goods_data);
foreach ($seckill_time_id as $v) {
$count = model('promotion_seckill_goods')->getCount([ [ 'seckill_time_id', 'like', '%,' . $v . ',%' ] ], '*', 'a', '', 'goods_id');
model('promotion_seckill_time')->update([ 'goods_num' => $count ], [ [ 'id', '=', $v ] ]);
}
model('promotion_seckill')->commit();
return $this->success();
} catch (\Exception $e) {
model('promotion_seckill')->rollback();
return $this->error('', $e->getMessage());
}
}
/**
* 修改秒杀商品
* @param $data
* @return array
*/
public function editSeckillGoods($data)
{
$cron = new Cron();
if (empty($data[ 'seckill_time_id' ])) {
return $this->error('', '请选择秒杀时段');
}
$seckill_time_id = explode(',', $data[ 'seckill_time_id' ]);
//时间段检测
foreach ($seckill_time_id as $v) {
$seckill_count = model('promotion_seckill')->getInfo([
[ 'goods_id', 'in', $data[ 'goods_ids' ] ],
[ 'status', 'in', '0,1' ],
[ 'site_id', '=', $data[ 'site_id' ] ],
[ 'seckill_time_id', 'like', '%,' . $v . ',%' ],
[ 'id', '<>', $data[ 'id' ] ],
[ '', 'exp', Db::raw('not ( (`start_time` > ' . $data[ 'end_time' ] . ' and `start_time` > ' . $data[ 'start_time' ] . ' ) or (`end_time` < ' . $data[ 'start_time' ] . ' and `end_time` < ' . $data[ 'end_time' ] . '))') ]//todo 修正 所有的优惠都要一样
]);
if ($seckill_count > 0) {
return $this->error('', '有商品已设置秒杀,请不要重复设置');
}
}
model('promotion_seckill')->startTrans();
try {
$seckill_data = [
'site_id' => $data[ 'site_id' ],
'seckill_name' => $data[ 'seckill_name' ],
'remark' => $data[ 'remark' ],
'seckill_time_id' => ',' . $data[ 'seckill_time_id' ] . ',',
'start_time' => $data[ 'start_time' ],
'end_time' => $data[ 'end_time' ],
'modify_time' => time(),
'sort' => $data[ 'sort' ],
];
$goods = new Goods();
$add_goods_data = [];
$seckill_data[ 'goods_id' ] = $data[ 'sku_list' ][ 0 ][ 'goods_id' ];
$seckill_data[ 'goods_name' ] = $data[ 'sku_list' ][ 0 ][ 'sku_name' ];
$seckill_data[ 'goods_image' ] = $data[ 'sku_list' ][ 0 ][ 'sku_image' ];
$seckill_data[ 'seckill_price' ] = $data[ 'sku_list' ][ 0 ][ 'seckill_price' ];
$seckill_id = $data[ 'id' ];
if ($data[ 'start_time' ] <= time()) {
$seckill_data[ 'status' ] = 1;//直接启动
model('promotion_seckill')->update($seckill_data, [ [ 'id', '=', $seckill_id ] ]);
$goods->modifyPromotionAddon($data[ 'sku_list' ][ 0 ][ 'goods_id' ], [ 'seckill' => $seckill_id ]);
$cron->addCron(1, 0, "秒杀关闭", "CloseSeckill", $data[ 'end_time' ], $seckill_id);
} else {
$seckill_data[ 'status' ] = 0;
model('promotion_seckill')->update($seckill_data, [ [ 'id', '=', $seckill_id ] ]);
$cron->addCron(1, 0, "秒杀开启", "OpenSeckill", $data[ 'start_time' ], $seckill_id);
$cron->addCron(1, 0, "秒杀关闭", "CloseSeckill", $data[ 'end_time' ], $seckill_id);
}
model('promotion_seckill_goods')->delete([ [ 'seckill_id', '=', $seckill_id ] ]);
$goods_stock = 0; // 秒杀商品总库存
foreach ($data[ 'sku_list' ] as $key => $item) {
$sku_info = model('goods_sku')->getInfo([ [ 'sku_id', '=', $item[ 'sku_id' ] ] ], 'goods_id, sku_id, sku_name,price,sku_image');
$add_goods_data[] = [
'site_id' => $data[ 'site_id' ],
'seckill_id' => $seckill_id,
'seckill_time_id' => ',' . $data[ 'seckill_time_id' ] . ',',
'sku_id' => $item[ 'sku_id' ],
'goods_id' => $item[ 'goods_id' ],
'sku_image' => $sku_info[ 'sku_image' ],
'sku_name' => $sku_info[ 'sku_name' ],
'seckill_price' => $item[ 'seckill_price' ],
'price' => $sku_info[ 'price' ],
'stock' => $item[ 'seckill_stock' ],
'max_buy' => $item[ 'max_buy' ],
'status' => $seckill_data[ 'status' ]
];
$goods_stock += $item[ 'seckill_stock' ];
}
model('promotion_seckill')->update([ 'goods_stock' => $goods_stock ], [ [ 'id', '=', $seckill_id ] ]);
model('promotion_seckill_goods')->addList($add_goods_data);
foreach ($seckill_time_id as $v) {
$count = model('promotion_seckill_goods')->getCount([ [ 'seckill_time_id', 'like', '%,' . $v . ',%' ] ], '*', 'a', '', 'goods_id');
model('promotion_seckill_time')->update([ 'goods_num' => $count ], [ [ 'id', '=', $v ] ]);
}
model('promotion_seckill')->commit();
return $this->success();
} catch (\Exception $e) {
model('promotion_seckill')->rollback();
return $this->error('', $e->getMessage());
}
}
public function seckillSort($id, $sort)
{
$res = model('promotion_seckill')->update([ 'sort' => $sort ], [ [ 'id', '=', $id ] ]);
return $this->success($res);
}
/**
* 秒杀商品详情
* @param array $condition
* @param string $field
* @param string $alias
* @param array $join
* @return array
*/
public function getSeckillDetail($condition = [], $field = '*', $alias = '', $join = [])
{
$info = model("promotion_seckill")->getInfo($condition, $field, $alias, $join);
$goods_sku = model('goods_sku')->getList([ [ 'goods_id', '=', $info[ 'goods_id' ] ], [ 'is_delete', '=', 0 ], [ 'goods_state', '=', 1 ] ], 'stock as goods_stock, goods_id, sku_id, sku_name,price,sku_image,stock');
$discount_goods = model("promotion_seckill_goods")->getList([ [ 'goods_id', '=', $info[ 'goods_id' ] ], [ 'seckill_id', '=', $info[ 'id' ] ] ], '*');
foreach ($goods_sku as $k => $v) {
$goods_sku[ $k ][ 'is_select' ] = 0;
$goods_sku[ $k ][ 'seckill_price' ] = $v[ 'price' ];
$goods_sku[ $k ][ 'seckill_stock' ] = 0;
$goods_sku[ $k ][ 'max_buy' ] = 0;
$goods_sku[ $k ][ 'stock' ] = numberFormat($goods_sku[ $k ][ 'stock' ]);
$goods_sku[ $k ][ 'goods_stock' ] = numberFormat($goods_sku[ $k ][ 'goods_stock' ]);
foreach ($discount_goods as $key => $val) {
if ($val[ 'sku_id' ] == $v[ 'sku_id' ]) {
$goods_sku[ $k ][ 'is_select' ] = 1;
$goods_sku[ $k ][ 'seckill_price' ] = $val[ 'seckill_price' ];
$goods_sku[ $k ][ 'seckill_stock' ] = $val[ 'stock' ];
$goods_sku[ $k ][ 'max_buy' ] = $val[ 'max_buy' ];
}
}
}
$info[ 'goods_sku' ] = $goods_sku;
$info[ 'seckill_goods' ] = $discount_goods;
return $this->success($info);
}
/**
* 修改秒杀商品限购数量
* @param $seckill_id
* @param $site_id
* @param $sku_id
* @param $max_buy
* @return array
*/
public function editSeckillGoodsNum($seckill_id, $site_id, $sku_id, $max_buy)
{
$data = [
'seckill_id' => $seckill_id,
'site_id' => $site_id,
'sku_id' => $sku_id,
'max_buy' => $max_buy
];
model("promotion_seckill_goods")->update($data, [ [ 'seckill_id', '=', $seckill_id ], [ 'sku_id', '=', $sku_id ], [ 'site_id', '=', $site_id ] ]);
return $this->success();
}
/**
* 删除秒杀商品
* @param $seckill_id
* @param $site_id
* @return array
*/
public function deleteSeckillGoods($seckill_id, $site_id)
{
$info = model("promotion_seckill")->getInfo([ [ 'id', '=', $seckill_id ] ], 'goods_id,seckill_time_id,status');
if(empty($info)) return $this->error('', '该秒杀活动不存在');
if($info['status'] == 1) return $this->error('', '该秒杀活动正在进行');
$goods = new Goods();
$goods->modifyPromotionAddon($info[ 'goods_id' ], [ 'seckill' => $seckill_id ], true);
model("promotion_seckill")->delete([ [ 'id', '=', $seckill_id ], [ 'site_id', '=', $site_id ] ]);
$goods_num = model("promotion_seckill_goods")->delete([ [ 'seckill_id', '=', $seckill_id ], [ 'site_id', '=', $site_id ] ]);
$seckill_time_id = explode(',', $info[ 'seckill_time_id' ]);
foreach ($seckill_time_id as $v) {
$count = model('promotion_seckill_goods')->getCount([ [ 'seckill_time_id', 'like', '%,' . $v . ',%' ] ], '*', 'a', '', 'goods_id');
model('promotion_seckill_time')->update([ 'goods_num' => $count ], [ [ 'id', '=', $v ] ]);
}
return $this->success();
}
/**
* 获取秒杀详情
* @param $seckill_id
* @return array
*/
public function getSeckillInfo($seckill_id)
{
$seckill_info = model('promotion_seckill')->getInfo([ [ 'id', '=', $seckill_id ] ]);
if (empty($seckill_info)) {
return $this->success([]);
}
$seckill_info[ 'seckill_start_time' ] = 0;
$seckill_info[ 'seckill_end_time' ] = 0;
$today_time = strtotime(date("Y-m-d"), time());
$time = time() - $today_time;//当日时间戳
$seckill_time_id = trim($seckill_info[ 'seckill_time_id' ], ',');
$condition = [
[ 'id', 'in', $seckill_time_id ],
[ 'seckill_start_time', '<=', $time ],
[ 'seckill_end_time', '>', $time ],
];
$time_list = model('promotion_seckill_time')->getList($condition, 'seckill_start_time, seckill_end_time', 'seckill_start_time asc', '', '', '', 1);
if (count($time_list) > 0) {
$seckill_info[ 'seckill_start_time' ] = $time_list[ 0 ][ 'seckill_start_time' ];
$seckill_info[ 'seckill_end_time' ] = $time_list[ 0 ][ 'seckill_end_time' ];
}
return $this->success($seckill_info);
}
/**
* 获取秒杀列表
* @param array $condition
* @param int $page
* @param int $page_size
* @param string $order
* @param string $field
* @return mixed
*/
public function getSeckillPageList($condition = [], $page = 1, $page_size = PAGE_LIST_ROWS, $order = '', $field = '', $alias = '', $join = [])
{
$list = model('promotion_seckill')->pageList($condition, $field, $order, $page, $page_size, $alias, $join);
return $this->success($list);
}
/**
* 获取秒杀商品列表
* @param $seckill_time_id
* @param $site_id
* @param int $page
* @param int $page_size
* @return array
*/
public function getSeckillGoodsPageList($seckill_time_id, $site_id, $page = 1, $page_size = PAGE_LIST_ROWS)
{
$order = 'nps.sort asc';
$alias = 'nps';
// [ 'nps.goods_stock', '>', 0 ],
$condition = [
[ 'nps.seckill_time_id', 'like', '%,' . $seckill_time_id . ',%' ],
[ 'nps.status', '=', 1 ],
[ 'g.goods_state', '=', 1 ],
[ 'g.is_delete', '=', 0 ],
[ 'nps.site_id', '=', $site_id ]
];
$field = 'nps.id,nps.site_id,nps.seckill_name,nps.status,nps.remark,nps.start_time,nps.end_time,
nps.goods_id,nps.seckill_time_id,nps.seckill_price,nps.sale_num,nps.goods_stock,
g.goods_name,g.goods_image,g.price';
$join = [
[ 'goods g', 'nps.goods_id = g.goods_id', 'inner' ]
];
$res = model('promotion_seckill')->pageList($condition, $field, $order, $page, $page_size, $alias, $join);
foreach ($res[ 'list' ] as $k => $v) {
$res[ 'list' ][ $k ][ 'goods_stock' ] = numberFormat($res[ 'list' ][ $k ][ 'goods_stock' ]);
}
return $this->success($res);
}
/**
* 获取秒杀商品列表
* @param $seckill_time_id
* @param $site_id
* @param null $limit
* @return array
*/
public function getSeckillList($seckill_time_id, $site_id, $limit = null)
{
$order = 'nps.sort asc';
$alias = 'nps';
// [ 'nps.goods_stock', '>', 0 ],
$condition = [
[ 'nps.seckill_time_id', 'like', '%,' . $seckill_time_id . ',%' ],
[ 'nps.status', '=', 1 ],
[ 'g.goods_state', '=', 1 ],
[ 'g.is_delete', '=', 0 ],
[ 'nps.site_id', '=', $site_id ]
];
$field = 'nps.id,nps.site_id,nps.seckill_name,nps.status,nps.remark,nps.start_time,nps.end_time,
nps.goods_id,nps.seckill_time_id,nps.seckill_price,nps.sale_num,nps.goods_stock,
g.goods_name,g.goods_image,g.price';
$join = [
[ 'goods g', 'nps.goods_id = g.goods_id', 'inner' ]
];
$list = model('promotion_seckill')->getList($condition, $field, $order, $alias, $join, '', $limit);
foreach ($list as $k => $v) {
$list[ $k ][ 'goods_stock' ] = numberFormat($list[ $k ][ 'goods_stock' ]);
}
return $this->success($list);
}
/**
* 启动秒杀事件
* @param $seckill_id
* @return array
*/
public function cronOpenSeckill($seckill_id)
{
$seckill_info = model('promotion_seckill')->getInfo([ [ 'id', '=', $seckill_id ] ], 'start_time,status, goods_id');
if (!empty($seckill_info)) {
if ($seckill_info[ 'start_time' ] <= time() && $seckill_info[ 'status' ] == 0) {
model('promotion_seckill')->update([ 'status' => 1 ], [ [ 'id', '=', $seckill_id ] ]);
model('promotion_seckill_goods')->update([ 'status' => 1 ], [ [ 'seckill_id', '=', $seckill_id ] ]);
$goods = new Goods();
$goods->modifyPromotionAddon($seckill_info[ 'goods_id' ], [ 'seckill' => $seckill_id ]);
return $this->success(1);
} else {
return $this->error("", "秒杀已开启或者关闭");
}
} else {
return $this->error("", "秒杀不存在");
}
}
/**
* 结束秒杀事件
* @param $seckill_id
* @return array
*/
public function cronCloseSeckill($seckill_id)
{
$seckill_info = model('promotion_seckill')->getInfo([ [ 'id', '=', $seckill_id ] ], 'start_time,status, goods_id');
if (!empty($seckill_info)) {
//针对正在进行的活动
if ($seckill_info[ 'status' ] == 1) {
model('promotion_seckill')->update([ 'status' => 2 ], [ [ 'id', '=', $seckill_id ] ]);
model('promotion_seckill_goods')->update([ 'status' => 2 ], [ [ 'seckill_id', '=', $seckill_id ] ]);
$goods = new Goods();
$goods->modifyPromotionAddon($seckill_info[ 'goods_id' ], [ 'seckill' => $seckill_id ], true);
return $this->success();
} else {
return $this->error("", "正在进行的秒杀才能进行关闭操作");
}
} else {
return $this->error("", "活动不存在");
}
}
/**
* 手动结束秒杀事件
* @param $seckill_id
* @return array
*/
public function closeSeckill($seckill_id)
{
$seckill_info = model('promotion_seckill')->getInfo([ [ 'id', '=', $seckill_id ] ], 'start_time,status, goods_id');
if (!empty($seckill_info)) {
//针对正在进行的活动
if ($seckill_info[ 'status' ] == 1) {
model('promotion_seckill')->update([ 'status' => -1 ], [ [ 'id', '=', $seckill_id ] ]);
model('promotion_seckill_goods')->update([ 'status' => -1 ], [ [ 'seckill_id', '=', $seckill_id ] ]);
$goods = new Goods();
$goods->modifyPromotionAddon($seckill_info[ 'goods_id' ], [ 'seckill' => $seckill_id ], true);
return $this->success();
} else {
return $this->error("", "正在进行的秒杀才能进行关闭操作");
}
} else {
return $this->error("", "活动不存在");
}
}
/**
* 获取商品列表
* @param $seckill_id
* @return array
*/
public function getSeckillGoodsList($seckill_id)
{
$field = 'psg.*,sku.sku_name,sku.price,sku.sku_image';
$alias = 'psg';
$join = [
[
'goods g',
'g.goods_id = psg.goods_id',
'inner'
],
[
'goods_sku sku',
'sku.sku_id = psg.sku_id',
'inner'
]
];
$condition = [
[ 'psg.seckill_id', '=', $seckill_id ],
[ 'g.is_delete', '=', 0 ], [ 'g.goods_state', '=', 1 ]
];
$list = model('promotion_seckill_goods')->getList($condition, $field, '', $alias, $join);
return $this->success($list);
}
/**
* 获取秒杀商品信息
* @param array $condition
* @param string $field
* @return array
*/
public function getSeckillGoodsInfo($condition = [], $field = '')
{
if (empty($field)) {
$field = 'psg.id,psg.seckill_id,ps.goods_id,psg.sku_id,psg.seckill_price,psg.max_buy,psg.stock,
ps.status,ps.start_time,ps.end_time,(ps.sale_num + g.virtual_sale) as sale_num,ps.remark,
sku.site_id,sku.sku_name,sku.sku_spec_format,sku.price,sku.promotion_type,
sku.click_num,sku.collect_num,sku.sku_image,sku.sku_images,
sku.goods_content,sku.goods_state,sku.is_virtual,sku.is_free_shipping,
sku.goods_spec_format,sku.goods_attr_format,sku.introduction,sku.unit,
sku.video_url,g.evaluate,sku.goods_service_ids,sku.support_trade_type,g.goods_image,
ps.goods_stock,g.goods_name,sku.qr_id,g.stock_show,g.sale_show,g.label_name,g.category_id';
}
$join = [
[ 'goods_sku sku', 'psg.sku_id = sku.sku_id', 'inner' ],
[ 'goods g', 'g.goods_id = sku.goods_id', 'inner' ],
[ 'promotion_seckill ps', 'ps.id = psg.seckill_id', 'inner' ],
];
$seckill_goods_info = model('promotion_seckill_goods')->getInfo($condition, $field, 'psg', $join);
if (!empty($seckill_goods_info)) {
if (isset($seckill_goods_info[ 'sale_num' ])) {
$seckill_goods_info[ 'sale_num' ] = numberFormat($seckill_goods_info[ 'sale_num' ]);
}
if (isset($seckill_goods_info[ 'goods_stock' ])) {
$seckill_goods_info[ 'goods_stock' ] = numberFormat($seckill_goods_info[ 'goods_stock' ]);
}
}
return $this->success($seckill_goods_info);
}
/**
* 获取秒杀商品信息
* @param array $condition
* @return array
*/
public function getSeckillGoodsSkuList($condition = [])
{
$field = 'psg.id,psg.seckill_id,ps.goods_id,psg.sku_id,psg.seckill_price,psg.max_buy,psg.stock,
ps.status,ps.start_time,ps.end_time,ps.sale_num,sku.sku_name,sku.sku_spec_format,sku.price,sku.sku_image,sku.sku_images,sku.goods_spec_format,g.goods_image,ps.goods_stock';
$join = [
[ 'goods_sku sku', 'psg.sku_id = sku.sku_id', 'inner' ],
[ 'goods g', 'g.goods_id = sku.goods_id', 'inner' ],
[ 'promotion_seckill ps', 'ps.id = psg.seckill_id', 'inner' ],
];
$list = model('promotion_seckill_goods')->getList($condition, $field, 'psg.id asc', 'psg', $join);
foreach ($list as $k => $v) {
$list[ $k ][ 'goods_stock' ] = numberFormat($list[ $k ][ 'goods_stock' ]);
}
return $this->success($list);
}
/**
* 判断规格值是否禁用
* @param $seckill_id
* @param $site_id
* @param string $goods_spec_format
* @return int|mixed
*/
public function getGoodsSpecFormat($seckill_id, $site_id, $goods_spec_format = '')
{
//获取活动参与的商品sku_ids
$sku_ids = model('promotion_seckill_goods')->getColumn([ [ 'seckill_id', '=', $seckill_id ], [ 'site_id', '=', $site_id ] ], 'sku_id');
$goods_model = new Goods();
$res = $goods_model->getGoodsSpecFormat($sku_ids, $goods_spec_format);
return $res;
}
/**
* 生成砍价二维码
* @param $seckill_id
* @param string $name
* @param string $type 类型 create创建 get获取
* @return mixed|array
*/
public function qrcode($seckill_id, $name, $site_id, $type = 'create')
{
$data = [
'site_id' => $site_id,
'app_type' => "all", // all为全部
'type' => $type, // 类型 create创建 get获取
'data' => [
"seckill_id" => $seckill_id
],
'page' => '/pages_promotion/seckill/detail',
'qrcode_path' => 'upload/qrcode/seckill',
'qrcode_name' => "seckill_qrcode_" . $seckill_id
];
event('Qrcode', $data, true);
$app_type_list = config('app_type');
$path = [];
foreach ($app_type_list as $k => $v) {
switch ( $k ) {
case 'h5':
$wap_domain = getH5Domain();
$path[ $k ][ 'status' ] = 1;
$path[ $k ][ 'url' ] = $wap_domain . $data[ 'page' ] . '?id=' . $seckill_id;
$path[ $k ][ 'img' ] = "upload/qrcode/seckill/seckill_qrcode_" . $seckill_id . "_" . $k . ".png";
break;
case 'weapp' :
$config = new ConfigModel();
$res = $config->getConfig([ [ 'site_id', '=', $site_id ], [ 'app_module', '=', 'shop' ], [ 'config_key', '=', 'WEAPP_CONFIG' ] ]);
if (!empty($res[ 'data' ])) {
if (empty($res[ 'data' ][ 'value' ][ 'qrcode' ])) {
$path[ $k ][ 'status' ] = 2;
$path[ $k ][ 'message' ] = '未配置微信小程序';
} else {
$path[ $k ][ 'status' ] = 1;
$path[ $k ][ 'img' ] = $res[ 'data' ][ 'value' ][ 'qrcode' ];
}
} else {
$path[ $k ][ 'status' ] = 2;
$path[ $k ][ 'message' ] = '未配置微信小程序';
}
break;
case 'wechat' :
$config = new ConfigModel();
$res = $config->getConfig([ [ 'site_id', '=', $site_id ], [ 'app_module', '=', 'shop' ], [ 'config_key', '=', 'WECHAT_CONFIG' ] ]);
if (!empty($res[ 'data' ])) {
if (empty($res[ 'data' ][ 'value' ][ 'qrcode' ])) {
$path[ $k ][ 'status' ] = 2;
$path[ $k ][ 'message' ] = '未配置微信公众号';
} else {
$path[ $k ][ 'status' ] = 1;
$path[ $k ][ 'img' ] = $res[ 'data' ][ 'value' ][ 'qrcode' ];
}
} else {
$path[ $k ][ 'status' ] = 2;
$path[ $k ][ 'message' ] = '未配置微信公众号';
}
break;
}
}
$return = [
'path' => $path,
'name' => $name,
];
return $this->success($return);
}
public function urlQrcode($page, $qrcode_param, $promotion_type, $app_type, $site_id)
{
$params = [
'site_id' => $site_id,
'data' => $qrcode_param,
'page' => $page,
'promotion_type' => $promotion_type,
'app_type' => $app_type,
'h5_path' => $page . '?id=' . $qrcode_param[ 'id' ],
'qrcode_path' => 'upload/qrcode/seckill',
'qrcode_name' => 'seckill_qrcode_' . $promotion_type . '_' . $qrcode_param[ 'id' ] . '_' . $site_id,
];
$solitaire = event('PromotionQrcode', $params, true);
return $this->success($solitaire);
}
}

View File

@@ -0,0 +1,75 @@
<?php
/**
*/
namespace addon\seckill\model;
use app\dict\order_refund\OrderRefundDict;
use app\model\BaseModel;
use app\model\order\Order;
use app\model\order\OrderCreate;
use app\model\order\OrderCreateTool;
use app\model\order\OrderRefund;
use app\model\system\Pay;
use extend\exception\OrderException;
use think\facade\Cache;
/**
* 订单创建(秒杀)
*
* @author Administrator
*
*/
class SeckillOrder extends BaseModel
{
/**
* 订单关闭
* @param $order_id
*/
public function cronOrderClose($order_id)
{
$order_info = model('order')->getInfo([['order_id', '=', $order_id], ['promotion_type', '=', 'seckill'], ['order_status', '=', -1]], 'promotion_id');
if (!empty($order_info)) {
$condition = array(
["order_id", "=", $order_id]
);
$order_goods_list = model('order_goods')->getList($condition, "order_goods_id,sku_id,num,refund_status,use_point");
foreach ($order_goods_list as $k => $v) {
// 返还库存
model("promotion_seckill")->setInc([['id', '=', $order_info["promotion_id"]]], "goods_stock", $v["num"]);
model("promotion_seckill_goods")->setInc([["sku_id", '=', $v["sku_id"]], ["seckill_id", '=', $order_info["promotion_id"]]], "stock", $v["num"]);
// 减少销量
model("promotion_seckill")->setDec([['id', '=', $order_info["promotion_id"]]], "sale_num", $v["num"]);
}
}
return $this->success();
}
/**
* 获取商品已秒杀数
* @param $goods_id
* @param $member_id
* @return float
*/
public function getGoodsSeckillNum($seckill_id)
{
$join = [
['order o', 'o.order_id = og.order_id', 'left']
];
$num = model('order_goods')->getSum([
['o.order_status', '<>', Order::ORDER_CLOSE],
['o.promotion_type', '=', 'seckill'],
['o.promotion_id', '=', $seckill_id],
['og.refund_status', '<>', OrderRefundDict::REFUND_COMPLETE],
], 'og.num', 'og', $join);
return $num;
}
}

View File

@@ -0,0 +1,310 @@
<?php
/**
*/
namespace addon\seckill\model;
use addon\store\model\StoreGoodsSku;
use app\dict\order_refund\OrderRefundDict;
use app\model\BaseModel;
use app\model\order\Order;
use app\model\order\OrderCreate;
use app\model\order\OrderCreateTool;
use app\model\order\OrderRefund;
use app\model\store\Store;
use extend\exception\OrderException;
use think\facade\Cache;
use app\model\express\Express;
use app\model\system\Pay;
use app\model\express\Config as ExpressConfig;
use app\model\order\Config;
use app\model\express\Local;
/**
* 订单创建(秒杀)
*
* @author Administrator
*
*/
class SeckillOrderCreate extends BaseModel
{
use OrderCreateTool;
public $seckill_id = 0;
public $seckill_info = [];
public function __construct()
{
$this->promotion_type = 'seckill';
$this->promotion_type_name = '秒杀';
}
/**
* 订单创建
*/
public function create()
{
//计算
$this->confirm();
if ($this->error > 0) {
return $this->error(['error_code' => $this->error], $this->error_msg);
}
//订单创建数据
$order_insert_data = $this->getOrderInsertData(['discount'], 'invert');
$order_insert_data['store_id'] = $this->store_id;
$order_insert_data['create_time'] = time();
$order_insert_data['is_enable_refund'] = 0;
//订单类型以及状态
$this->orderType();
$order_insert_data['order_type'] = $this->order_type['order_type_id'];
$order_insert_data['order_type_name'] = $this->order_type['order_type_name'];
$order_insert_data['order_status_name'] = $this->order_type['order_status']['name'];
$order_insert_data['order_status_action'] = json_encode($this->order_type['order_status'], JSON_UNESCAPED_UNICODE);
model('order')->startTrans();
//循环生成多个订单
try {
$this->order_id = model('order')->add($order_insert_data);
$order_goods_insert_data = [];
//订单项目表
foreach ($this->goods_list as &$order_goods_v) {
$order_goods_insert_data[] = $this->getOrderGoodsInsertData($order_goods_v);
model("promotion_seckill_goods")->setDec([['sku_id', '=', $order_goods_v['sku_id']], ['seckill_id', '=', $this->seckill_info["id"]]], "stock", $order_goods_v['num']);
model("promotion_seckill")->setDec([['id', '=', $this->seckill_info["id"]]], "goods_stock", $order_goods_v['num']);
// 增加销量
model("promotion_seckill")->setInc([['id', '=', $this->seckill_info["id"]]], "sale_num", $order_goods_v['num']);
}
model('order_goods')->addList($order_goods_insert_data);
//扣除余额(统一扣除)
$this->useBalance();
//批量库存处理(卡密商品支付后在扣出库存)
$this->batchDecOrderGoodssStock();
model('order')->commit();
//订单创建后事件
$this->orderCreateAfter();
//支付单据
$pay = new Pay();
$pay->addPay($this->site_id, $this->out_trade_no, $this->pay_type, $this->order_name, $this->order_name, $this->pay_money, '', 'OrderPayNotify', '');
return $this->success($this->out_trade_no);
} catch ( \Exception $e ) {
model('order')->rollback();
return $this->error('', $e->getMessage());
}
}
/**
* 计算后的进一步计算(不存缓存,每次都是重新计算)
* @return array
*/
public function confirm()
{
$order_key = $this->param['order_key'];
$this->getOrderCache($order_key);
//初始化地址
$this->initMemberAddress();
//初始化门店信息
$this->initStore();
//配送计算
$this->calculateDelivery();
//批量校验配送方式
$this->batchCheckDeliveryType();
//计算发票相关
$this->calculateInvoice();
//计算余额
$this->calculateBalcnce();
$this->pay_money = $this->order_money - $this->balance_money;
//设置过的商品项信息
return get_object_vars($this);
}
/**
* 订单计算
*/
public function calculate()
{
//初始化会员地址
$this->initMemberAddress();
$this->initMemberAccount();//初始化会员账户
//商品列表信息
$this->getOrderGoodsCalculate();
//查询秒杀信息
$seckill_model = new Seckill();
$seckill_info = $seckill_model->getSeckillInfo($this->seckill_id)['data'] ?? [];
if (empty($seckill_info)) throw new OrderException("找不到可用的秒杀活动");
$this->seckill_info = $seckill_info;
//判断秒杀时间段是否符合
$today_time = strtotime(date("Y-m-d"), time());
$time = time() - $today_time;//当日时间戳
if ($time < $this->seckill_info["seckill_start_time"] || $time > $this->seckill_info["seckill_end_time"]) {
$this->error = 1;
$this->error_msg = "当前商品秒杀活动未开启或已过期!";
}
//秒杀库存
if ($this->goods_list[0]) {
$seckill_goods = $seckill_model->getSeckillGoodsInfo([['psg.seckill_id', '=', $this->seckill_id], ['psg.sku_id', '=', $this->goods_list[0]['sku_id']]], 'psg.stock')['data'] ?? [];
$seckill_goods_stock = $seckill_goods['stock'];
if ($this->goods_list[0]['num'] > $seckill_goods_stock) {
$this->error = 1;
$this->error_msg = "该商品库存不足";
}
}
// 秒杀商品限购 按每日某时段限购
if ($this->goods_list[0]['limit_num'] > 0) {
$purchased_num = $this->getGoodsPurchasedNum($this->goods_list[0]['sku_id'], $this->member_id, $this->seckill_info['id']);
if (($purchased_num + $this->goods_list[0]['num']) > $this->goods_list[0]['limit_num']) {
$this->error = 1;
$this->error_msg = "该商品每人限购{$this->goods_list[ 0 ]['limit_num']}件,您已购买{$purchased_num}";
}
}
$this->shopOrderCalculate();
//获取发票相关
$this->getInovice();
$this->order_key = create_no();
$this->setOrderCache(get_object_vars($this), $this->order_key);
return true;
}
/**
* 待付款订单
* @param unknown $data
*/
public function orderPayment()
{
//计算
$this->calculate();
//查询配送信息
$this->getDeliveryData();
//订单初始项
event('OrderPayment', ['order_object' => $this]);
return get_object_vars($this);
}
/**
* 获取商品的计算信息
* @param unknown $data
*/
public function getOrderGoodsCalculate()
{
$this->getSeckillGoodsInfo();
return true;
}
/**
* 获取秒杀商品列表信息
* @param $id
* @param $num
* @param $data
* @return array
*/
public function getSeckillGoodsInfo()
{
$id = $this->param['id'];
$sku_id = $this->param['sku_id'];
$num = $this->param['num'];
//组装商品列表
$field = 'npsg.sku_id,npsg.seckill_id,npsg.seckill_price,npsg.max_buy as limit_num,ngs.sku_name, ngs.sku_no,
ngs.price, ngs.discount_price, ngs.cost_price, ngs.stock, ngs.weight, ngs.volume, ngs.sku_image,
ngs.site_id, ns.site_name, ngs.goods_state, ngs.is_virtual,ngs.supplier_id,
ngs.is_free_shipping, ngs.shipping_template, ngs.goods_class, ngs.goods_class_name,ngs.goods_id,ngs.sku_spec_format,ngs.goods_name,ngs.support_trade_type';
$alias = 'npsg';
$join = [
[
'goods_sku ngs',
'npsg.sku_id = ngs.sku_id',
'inner'
],
[
'site ns',
'ngs.site_id = ns.site_id',
'inner'
]
];
$condition = [
['npsg.sku_id', '=', $sku_id],
['npsg.seckill_id', '=', $id],
['npsg.site_id', '=', $this->site_id]
];
$info = model("promotion_seckill_goods")->getInfo($condition, $field, $alias, $join);
if (empty($info)) throw new OrderException('无效的商品!');
//判断是否是虚拟订单
$this->seckill_id = $info['seckill_id'];
if ($info['is_virtual']) {
$this->is_virtual = 1;
} else {
$this->is_virtual = 0;
}
$info["num"] = $num;
$price = $info["seckill_price"];//订单项商品单价
$goods_money = $price * $info['num'];
$info["price"] = $price;
$info['goods_money'] = $goods_money;//订单项商品总价
$info['real_goods_money'] = $goods_money;//真实商品金额
$info['coupon_money'] = 0;//优惠券金额
$info['promotion_money'] = 0;//优惠金额
$this->site_name = $info['site_name'];
$this->goods_money = $goods_money;
$this->goods_list_str = $info['sku_id'] . ':' . $info['num'];
$this->order_name = string_split("", ",", $info['sku_name']);
$this->goods_num = $info['num'];
$this->goods_list[] = $info;
return true;
}
/**
* 获取店铺订单计算
*/
public function shopOrderCalculate()
{
//重新计算订单总额
$this->getOrderMoney();
//理论上是多余的操作
if ($this->order_money < 0) {
$this->order_money = 0;
}
//总结计算
$this->pay_money = $this->order_money;
return true;
}
/**
* 获取会员该秒杀时段已购该商品数
* @param $goods_id
* @param $member_id
* @return float
*/
public function getGoodsPurchasedNum($sku_id, $member_id, $seckill_id)
{
$join = [
['order o', 'o.order_id = og.order_id', 'left']
];
$num = model('order_goods')->getSum([
['og.member_id', '=', $member_id],
['og.sku_id', '=', $sku_id],
['o.order_status', '<>', Order::ORDER_CLOSE],
['o.promotion_type', '=', 'seckill'],
['o.promotion_id', '=', $seckill_id],
['og.refund_status', '<>', OrderRefundDict::REFUND_COMPLETE],
['o.create_time', 'between', [date_to_time(date('Y-m-d 00:00:00')), time()]]
], 'og.num', 'og', $join);
return $num;
}
}

View File

@@ -0,0 +1,141 @@
<?php
/**
*/
namespace addon\seckill\model\share;
use addon\seckill\model\Seckill;
use app\model\share\WchatShareBase as BaseModel;
use app\model\system\Config as ConfigModel;
/**
* 分享
*/
class WchatShare extends BaseModel
{
protected $config = [
[
'title' => '秒杀分享',
'config_key' => 'WCHAT_SHARE_CONFIG_SECKILL_PROMOTE',
'path' => [ '/pages_promotion/seckill/detail' ],
'method_prefix' => 'goodsDetail',
],
[
'title' => '秒杀列表',
'config_key' => 'WCHAT_SHARE_CONFIG_SECKILL_LIST_PROMOTE',
'path' => [ '/pages_promotion/seckill/list' ],
'method_prefix' => 'goodsList',
],
];
protected $sort = 8;
/**
* 秒杀列表
* @param $param
* @return array
*/
protected function goodsListShareData($param)
{
//跳转路径
$link = $this->getShareLink($param);
$config_data = $this->goodsListShareConfig($param)[ 'value' ];
$data = [
'link' => $link,
'desc' => $config_data[ 'desc' ],
'imgUrl' => $config_data[ 'imgUrl' ],
'title' => $config_data[ 'title' ]
];
return [
'permission' => [
'hideOptionMenu' => false,
'hideMenuItems' => [],
],
'data' => $data,//分享内容
];
}
/**
* 秒杀列表分享配置
* @param $param
* @return array
*/
public function goodsListShareConfig($param)
{
$site_id = $param[ 'site_id' ];
$config = $param[ 'config' ];
$config_model = new ConfigModel();
$data = $config_model->getConfig([ [ 'site_id', '=', $site_id ], [ 'app_module', '=', 'shop' ], [ 'config_key', '=', $config[ 'config_key' ] ] ])[ 'data' ];
if (empty($data[ 'value' ])) {
$data[ 'value' ] = [
'title' => "秒杀列表",
'desc' => "秒杀进行时\n快来参与吧",
'imgUrl' => ''
];
}
if (empty($data[ 'value' ][ 'imgUrl' ])) {
$data[ 'value' ][ 'imgUrl' ] = img('addon/seckill/icon.png');
}
return [
'value' => $data[ 'value' ],
];
}
/**
* 秒杀分享数据
* @param $param
* @return array
*/
protected function goodsDetailShareData($param)
{
$url = $param[ 'url' ];
$parse_res = parse_url($url);
parse_str($parse_res[ 'query' ] ?? '', $query);
if (isset($query[ 'seckill_id' ]) || $query[ 'id' ]) {
$seckill_id = $query['seckill_id'] ?? $query['id'];
$goods = new Seckill();
$condition = [
[ 'ps.id', '=', $seckill_id ],
[ 'psg.site_id', '=', 1 ],
[ 'psg.status', '=', 1 ],
[ 'ps.status', '=', 1 ],
[ 'g.is_delete', '=', 0 ]
];
$sku_info = $goods->getSeckillGoodsInfo($condition)[ 'data' ];
if (!empty($sku_info)) {
$config_model = new \app\model\share\WchatShare();
$config_data = $config_model->goodsDetailShareConfig($param);
$title = str_replace('{goods_name}', $sku_info[ 'sku_name' ], $config_data[ 'value' ][ 'title' ]);
$desc = str_replace('{price}', $sku_info[ 'seckill_price' ], $config_data[ 'value' ][ 'desc' ]);
$link = $this->getShareLink($param);
$image_url = $sku_info[ 'sku_image' ];
$data = [
'title' => $title,
'desc' => $desc,
'link' => $link,
'imgUrl' => $image_url,
];
return [
'permission' => [
'hideOptionMenu' => false,
'hideMenuItems' => [],
],
'data' => $data,//分享内容
];
}
}
}
}

View File

@@ -0,0 +1,90 @@
<?php
/**
*/
namespace addon\seckill\model\share;
use app\model\share\WeappShareBase;
use app\model\system\Config as ConfigModel;
/**
* 分享
*/
class WeappShare extends WeappShareBase
{
protected $config = [
[
'title' => '秒杀列表',
'config_key' => 'WEAPP_SHARE_CONFIG_SECKILL_LIST',
'path' => ['/pages_promotion/seckill/list'],
'method_prefix' => 'seckillList',
],
];
protected $sort = 9;
/**
* 秒杀列表
* @param $param
* @return array
*/
protected function seckillListShareData($param)
{
//获取和替换配置数据
$config_data = $this->seckillListShareConfig($param);
$title = $config_data['value']['title'];
$image_url = $config_data['value']['imageUrl'] ? img($config_data['value']['imageUrl']) : '';
$path = $this->getSharePath($param);
$data = [
'title' => $title,
'path' => $path,
'imageUrl' => $image_url,
];
return [
'permission' => [
'onShareAppMessage' => true,
'onShareTimeline' => true,
],
'data' => $data,//分享内容
];
}
/**
* 秒杀列表
* @param $param
* @return array
*/
protected function seckillListShareConfig($param)
{
$site_id = $param['site_id'];
$config = $param['config'];
$config_model = new ConfigModel();
$data = $config_model->getConfig([
['site_id', '=', $site_id],
['app_module', '=', 'shop'],
['config_key', '=', $config['config_key']],
])['data'];
if (empty($data['value'])) {
$data['value'] = [
'title' => '参与秒杀,优惠多多!',
'imageUrl' => '',
];
}
$variable = [];
return [
'value' => $data['value'],
'variable' => $variable,
];
}
}

View File

@@ -0,0 +1,394 @@
<?php
/**
*/
namespace addon\seckill\shop\controller;
use app\shop\controller\BaseShop;
use addon\seckill\model\Seckill as SeckillModel;
use think\App;
/**
* 秒杀控制器
*/
class Seckill extends BaseShop
{
public function __construct(App $app = null)
{
$this->replace = [
'SECKILL_CSS' => __ROOT__ . '/addon/seckill/shop/view/public/css',
'SECKILL_JS' => __ROOT__ . '/addon/seckill/shop/view/public/js',
'SECKILL_IMG' => __ROOT__ . '/addon/seckill/shop/view/public/img',
];
parent::__construct($app);
}
/**
* 秒杀时间段列表
*/
public function lists()
{
if (request()->isJson()) {
$condition[] = [ 'site_id', '=', $this->site_id ];
$order = 'seckill_start_time asc';
$field = '*';
$seckill_model = new SeckillModel();
$res = $seckill_model->getSeckillTimeList($condition, $field, $order, null);
foreach ($res[ 'data' ] as $key => $val) {
$val = $seckill_model->transformSeckillTime($val);
$res[ 'data' ][ $key ][ 'seckill_start_time_show' ] = "{$val['start_hour']}:{$val['start_minute']}:{$val['start_second']}";
$res[ 'data' ][ $key ][ 'seckill_end_time_show' ] = "{$val['end_hour']}:{$val['end_minute']}:{$val['end_second']}";
}
return $res;
} else {
return $this->fetch("seckill/lists");
}
}
/**
* 添加秒杀时间段
*/
public function add()
{
if (request()->isJson()) {
$start_hour = input('start_hour', 0);
$start_minute = input('start_minute', 0);
$start_second = input('start_second', 0);
$end_hour = input('end_hour', 0);
$end_minute = input('end_minute', 0);
$end_second = input('end_second', 0);
$data = [
'site_id' => $this->site_id,
'name' => input('name', ''),
'seckill_start_time' => $start_hour * 3600 + $start_minute * 60 + $start_second,
'seckill_end_time' => $end_hour * 3600 + $end_minute * 60 + $end_second,
'create_time' => time(),
];
$seckill_model = new SeckillModel();
return $seckill_model->addSeckillTime($data);
} else {
return $this->fetch("seckill/add");
}
}
/**
* 编辑秒杀时间段
*/
public function edit()
{
$seckill_model = new SeckillModel();
if (request()->isJson()) {
$start_hour = input('start_hour', 0);
$start_minute = input('start_minute', 0);
$start_second = input('start_second', 0);
$end_hour = input('end_hour', 0);
$end_minute = input('end_minute', 0);
$end_second = input('end_second', 0);
$data = [
'name' => input('name', ''),
'seckill_start_time' => $start_hour * 3600 + $start_minute * 60 + $start_second,
'seckill_end_time' => $end_hour * 3600 + $end_minute * 60 + $end_second,
'create_time' => time(),
'id' => input('id', 0),
];
return $seckill_model->editSeckillTime($data, $this->site_id);
} else {
$id = input('id', 0);
$this->assign('id', $id);
//秒杀详情
$time_info = $seckill_model->getSeckillTimeInfo([ [ 'id', '=', $id ] ]);
if (!empty($time_info[ 'data' ])) {
$time_info[ 'data' ] = $seckill_model->transformSeckillTime($time_info[ 'data' ]);
}
$this->assign('time_info', $time_info[ 'data' ]);
return $this->fetch("seckill/edit");
}
}
/**
* 删除秒杀时间段
*/
public function delete()
{
if (request()->isJson()) {
$seckill_time_id = input('id', 0);
$seckill_model = new SeckillModel();
return $seckill_model->deleteSeckillTime($seckill_time_id);
}
}
/**
* 添加秒杀商品
*/
public function addGoods()
{
$seckill_model = new SeckillModel();
if (request()->isJson()) {
$data = [
'site_id' => $this->site_id,
'seckill_name' => input('seckill_name', ''),
'remark' => input('remark', ''),
'seckill_time_id' => input('seckill_time_id', ''),
'start_time' => strtotime(input('start_time', '')),
'end_time' => strtotime(input('end_time', '')),
'goods_data' => input('goods_data', ''),
'goods_ids' => input('goods_ids', ''),
'sort' => input('sort', '')
];
$res = $seckill_model->addSeckillGoods($data);
return $res;
} else {
return $this->fetch("seckill/editgoods");
}
}
/**
* 更新商品(秒杀价格)
*/
public function updateGoods()
{
$seckill_model = new SeckillModel();
if (request()->isJson()) {
$data = [
'site_id' => $this->site_id,
'id' => input('id', ''),
'seckill_name' => input('seckill_name', ''),
'remark' => input('remark', ''),
'seckill_time_id' => input('seckill_time_id', ''),
'start_time' => strtotime(input('start_time', '')),
'end_time' => strtotime(input('end_time', '')),
'sku_list' => input('sku_list', ''),
'goods_ids' => input('goods_ids', ''),
'sort' => input('sort', ''),
];
$res = $seckill_model->editSeckillGoods($data);
return $res;
} else {
$seckill_id = input('id', '');
if (empty($seckill_id)) {
$this->error('缺少参数id');
}
$seckill_info = $seckill_model->getSeckillDetail([ [ 'id', '=', $seckill_id ] ])[ 'data' ];
$seckill_time_id = trim($seckill_info[ 'seckill_time_id' ], ',');
$time_list = $seckill_model->getSeckillTimeList([ [ 'id', 'in', $seckill_time_id ] ])[ 'data' ];
$this->assign('seckill_info', $seckill_info);
$this->assign('time_list', $time_list);
return $this->fetch("seckill/editgoods");
}
}
public function seckillSort()
{
$sort = input('sort', 0);
$id = input('id', 0);
$seckill_model = new SeckillModel();
return $seckill_model->seckillSort($id, $sort);
}
/**
* 删除商品
*/
public function deleteGoods()
{
if (request()->isJson()) {
$seckill_id = input('id', 0);
$site_id = $this->site_id;
$seckill_model = new SeckillModel();
return $seckill_model->deleteSeckillGoods($seckill_id, $site_id);
}
}
/**
* 秒杀商品
*/
public function goodslist()
{
$seckill_time_id = input('seckill_time_id', '');
if (request()->isJson()) {
$page = input('page', 1);
$page_size = input('page_size', PAGE_LIST_ROWS);
$goods_name = input('goods_name', '');
$status = input('status', '');
$link_sort = input('order', 'start_time');
$sort = input('sort', 'desc');
$condition = [];
$condition[] = [ 'site_id', '=', $this->site_id ];
$condition[] = [ 'goods_name', 'like', '%' . $goods_name . '%' ];
if ($status !== '') $condition[] = [ 'status', '=', $status ];
//排序
if ($link_sort == 'sort') {
$order_by = $link_sort . ' ' . $sort;
} else {
$order_by = $link_sort . ' ' . $sort . ',sort desc';
}
if (!empty($seckill_time_id)) {
$condition[] = [ '', 'exp', \think\facade\Db::raw("FIND_IN_SET({$seckill_time_id},seckill_time_id)") ];
}
$start_time = input('start_time', '');
$end_time = input('end_time', '');
if ($start_time && !$end_time) {
$condition[] = [ 'end_time', '>=', date_to_time($start_time) ];
} elseif (!$start_time && $end_time) {
$condition[] = [ 'start_time', '<=', date_to_time($end_time) ];
} elseif ($start_time && $end_time) {
$start_timestamp = date_to_time($start_time);
$end_timestamp = date_to_time($end_time);
$sql = "start_time between {$start_timestamp} and {$end_timestamp}";
$sql .= " or end_time between {$start_timestamp} and {$end_timestamp}";
$sql .= " or (start_time <= {$start_timestamp} and end_time >= {$end_timestamp})";
$condition[] = [ '', 'exp', \think\facade\Db::raw($sql) ];
}
$seckill_model = new SeckillModel();
$seckill_list = $seckill_model->getSeckillPageList($condition, $page, $page_size, $order_by);
$seckill_condition[] = [ 'site_id', '=', $this->site_id ];
$time_list = $seckill_model->getSeckillTimeList($seckill_condition);
foreach ($seckill_list[ 'data' ][ 'list' ] as $k => $v) {
$seckill_list[ 'data' ][ 'list' ][ $k ][ 'time_list' ] = [];
foreach ($time_list[ 'data' ] as $index => $item) {
if (strpos(',' . $v[ 'seckill_time_id' ] . ',', ',' . $item[ 'id' ] . ',') !== false) {
$seckill_list[ 'data' ][ 'list' ][ $k ][ 'time_list' ][] = $item;
}
}
}
return $seckill_list;
} else {
$condition[] = [ 'site_id', '=', $this->site_id ];
$order = 'seckill_start_time asc';
$field = '*';
$seckill_model = new SeckillModel();
$res = $seckill_model->getSeckillTimeList($condition, $field, $order, null);
$this->assign('seckill_time_id', $seckill_time_id);
$this->assign('res', $res[ 'data' ]);
return $this->fetch("seckill/goodslist");
}
}
/**
* 秒杀时段
*/
public function seckilltimeselect()
{
if (request()->isJson()) {
$condition[] = [ 'site_id', '=', $this->site_id ];
$order = 'seckill_start_time asc';
$field = '*';
$seckill_model = new SeckillModel();
$res = $seckill_model->getSeckillTimeList($condition, $field, $order, null);
foreach ($res[ 'data' ] as $key => $val) {
$val = $seckill_model->transformSeckillTime($val);
$res[ 'data' ][ $key ][ 'seckill_start_time_show' ] = "{$val['start_hour']}:{$val['start_minute']}:{$val['start_second']}";
$res[ 'data' ][ $key ][ 'seckill_end_time_show' ] = "{$val['end_hour']}:{$val['end_minute']}:{$val['end_second']}";
}
return $res;
} else {
return $this->fetch("seckill/seckilltimeselect");
}
}
/**
* 获取商品列表
* @return array
*/
public function getSkuList()
{
if (request()->isJson()) {
$seckill_model = new SeckillModel();
$seckill_id = input('seckill_id', '');
$goods_list = $seckill_model->getSeckillGoodsList($seckill_id);
return $goods_list;
}
}
/**
* 手动关闭秒杀
* @return array
*/
public function closeSeckill()
{
if (request()->isJson()) {
$seckill_model = new SeckillModel();
$seckill_id = input('seckill_id', '');
$goods_list = $seckill_model->closeSeckill($seckill_id);
return $goods_list;
}
}
/**
* 秒杀推广
*/
public function seckillUrl()
{
$seckill_id = input('seckill_id', '');
$app_type = input('app_type', 'all');
$seckill_model = new SeckillModel();
$res = $seckill_model->urlQrcode('/pages_promotion/seckill/detail', [ 'id' => $seckill_id ], 'seckill', $app_type, $this->site_id);
return $res;
}
/**
* 批量删除商品
*/
public function deleteGoodsAll()
{
if (request()->isJson()) {
$seckill_id = input('seckill_id', 0);
$seckill_model = new SeckillModel();
foreach ($seckill_id as $k => $v){
$res = $seckill_model->deleteSeckillGoods($v, $this->site_id);
}
return $res;
}
}
/**
* 批量关闭秒杀
* @return array
*/
public function closeSeckillAll()
{
if (request()->isJson()) {
$seckill_model = new SeckillModel();
$seckill_id = input('seckill_id', '');
foreach ($seckill_id as $k => $v){
$res = $seckill_model->closeSeckill($v);
}
return $res;
}
}
}

View File

@@ -0,0 +1,96 @@
.layui-form-item .layui-input-inline.end-time {
float: none;
}
.layui-form-item .layui-input-inline.end-time {
float: none;
}
.time-label {
display: inline-block;
line-height: 30px;
height: 30px;
padding: 0 15px;
border-radius: 2px;
border: 1px solid #e9e9e9;
background: #f7f7f7;
font-size: 12px;
vertical-align: middle;
opacity: 1;
margin: 4px 8px 4px 0;
cursor: pointer;
position: relative;
}
.time-label span {
vertical-align: middle;
}
.time-label i {
font-size: 12px;
position: absolute;
top: -8px;
right: -8px;
width: 16px;
height: 16px;
line-height: 16px;
text-align: center;
color: #fff;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
background: rgba(0, 0, 0, 0.3);
}
.seckill-box .layui-table-col-special {
padding: 5px 0 !important;
}
.layui-table-body {
max-height: 480px !important;
}
.goods-title {
display: flex;
align-items: center;
}
.goods-title .goods-img {
display: flex;
align-items: center;
justify-content: center;
width: 55px;
height: 55px;
margin-right: 5px;
}
.goods-title .goods-img img {
max-height: 100%;
max-width: 100%;
}
.goods-title .goods-name {
flex: 1;
line-height: 1.6;
}
.forbidden {
cursor: not-allowed;
background-color: #eee;
}
.form-wrap {
position: relative;
}
.layui-carousel {
position: absolute;
top: 15px;
left: 1325px;
background: #fff;
}
.goods_num {
padding-left: 20px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 280 KiB

View File

@@ -0,0 +1,566 @@
var selectedSkuId = [], sku_list = [], time_list = [], form, laydate, timeTable, minDate, currentDate = new Date(),
repeat_flag = false; //防重复标识;
layui.use(['form', 'laydate'], function () {
form = layui.form;
laydate = layui.laydate;
minDate = "";
form.render();
currentDate.setDate(currentDate.getDate() + 30);
if ($("input[name='id']").val()) {
time_list = JSON.parse($('input[name="time_list"]').val());
sku_list = JSON.parse($('input[name="sku_list"]').val());
var now_time = ((new Date()).getTime()) / 1000;
var start_time = ((new Date($("#start_time").val())).getTime()) / 1000;
var old_end_time = ((new Date($("#end_time").val())).getTime()) / 1000;
if (now_time <= start_time) {
laydate.render({
elem: '#start_time',
type: 'datetime',
done: function (value) {
minDate = value;
reRender();
}
});
}
if (now_time <= old_end_time) {
laydate.render({
elem: '#end_time',
type: 'datetime'
});
}
} else {
laydate.render({
elem: '#start_time',
type: 'datetime',
value: new Date(),
done: function (value) {
minDate = value;
reRender();
}
});
laydate.render({
elem: '#end_time',
type: 'datetime',
value: new Date(currentDate)
});
}
renderTimeTable();
initTimeList();
renderTable();
form.on('submit(save)', function (data) {
if (verify()) {
var goods_data = [];
if (data.field.id) {
for (var i in sku_list) {
if (sku_list[i]['is_select'] == 1) {
goods_data.push(sku_list[i]);
}
}
data.field.sku_list = goods_data;
} else {
for (var i in sku_list) {
var goods = {};
goods.goods_id = sku_list[i]['goods_id'];
if (goods_data.length == 0) {
goods_data.push(goods);
} else {
$.each(goods_data, function (index, event) {
if (goods_data.length == index + 1 && event.goods_id != goods.goods_id) {
goods_data.push(goods);
}
})
}
}
$.each(goods_data, function (i, e) {
goods_data[i]['sku_list'] = [];
$.each(sku_list, function (index, event) {
if (event.goods_id == e.goods_id) {
goods_data[i]['sku_list'].push(event);
}
})
});
data.field.goods_data = goods_data;
}
var goods_ids = [];
goods_data.forEach(function (item) {
goods_ids.push(item.goods_id);
});
data.field.goods_ids = goods_ids.toString();
if (repeat_flag) return;
repeat_flag = true;
$.ajax({
url: data.field.id ? ns.url("seckill://shop/seckill/updateGoods") : ns.url("seckill://shop/seckill/addGoods"),
data: data.field,
dataType: 'JSON',
type: 'POST',
success: function (res) {
repeat_flag = false;
if (res.code == 0) {
layer.confirm(data.field.id ? '编辑成功' : '添加成功', {
title: '操作提示',
btn: ['返回列表', data.field.id ? '继续操作' : '继续添加'],
closeBtn: 0,
yes: function (index, layero) {
location.hash = ns.hash("seckill://shop/seckill/goodslist")
layer.close(index);
},
btn2: function (index, layero) {
listenerHash(); // 刷新页面
layer.close(index);
}
});
} else {
layer.msg(res.message);
}
}
});
}
});
form.verify({
time: function (value) {
var now_time = (new Date()).getTime();
var start_time = (new Date($("#start_time").val())).getTime();
var end_time = (new Date(value)).getTime();
var old_end_time = $("#old_end_time").val();
if (now_time > end_time) {
return '结束时间不能小于当前时间!'
}
if (start_time > end_time) {
return '结束时间不能小于开始时间!';
}
if (old_end_time && old_end_time > end_time) {
return '结束时间不能小于之前设置的结束时间!';
}
},
seckilltime: function (value, item) {
if (value == "" || value == 0) {
return '请选择时间段';
}
}
});
$("body").off("click", ".no-participation").on("click", ".no-participation", function () {
$(this).text("参与");
$(this).parents("tr").find("input").each(function (index, item) {
$(item).attr("readonly", true);
$(item).attr("disabled", true);
$(item).addClass("forbidden");
$(item).attr("lay-verify", "");
});
$(this).addClass("participation").removeClass("no-participation");
sku_list[$(this).parents("tr").attr("data-index")].is_select = 0;
});
$("body").off("click", ".participation").on("click", ".participation", function () {
$(this).text("不参与");
$(this).parents("tr").find("input").each(function (index, item) {
$(item).attr("readonly", false);
$(item).attr("disabled", false);
$(item).removeClass("forbidden");
});
$(this).removeClass("participation").addClass("no-participation");
sku_list[$(this).parents("tr").attr("data-index")].is_select = 1;
});
});
// 重新渲染结束时间
function reRender() {
$("#end_time").remove();
$(".end-time").html('<input type="text" id="end_time" name="end_time" placeholder="请输入结束时间" lay-verify="required|time" class = "layui-input len-mid" autocomplete="off"> ');
laydate.render({
elem: '#end_time',
type: 'datetime',
min: minDate
});
}
function verify() {
if ($('input[name="seckill_time_id"]').val() == '') {
layer.msg('请选择场次', {icon: 5, anim: 6});
return false;
}
if (sku_list.length == 0) {
layer.msg('请选择商品', {icon: 5, anim: 6});
return false;
}
for (var i in sku_list) {
if (sku_list[i]['max_buy'] < 0) {
layer.msg('商品限购不可小于0', {icon: 5, anim: 6});
return false;
}
if (sku_list[i]['seckill_price'] <= 0) {
layer.msg('秒杀价不可小于等于0', {icon: 5, anim: 6});
return false;
}
if (sku_list[i]['seckill_stock'] > sku_list[i]['stock']) {
layer.msg('秒杀库存不能大于商品库存', {icon: 5, anim: 6});
return false;
}
}
return true;
}
function delGoods(obj, id) {
var sku_ids = [];
for (let i = 0; i < sku_list.length; i++) {
if (sku_list[i].sku_id == parseInt(id)) {
sku_list.splice(i, 1);
}
}
for (let i = 0; i < sku_list.length; i++) {
sku_ids.push(sku_list[i].sku_id);
}
$(obj).parents("tr").remove();
$("#goods_num").html(sku_list.length);
selectedSkuId = sku_ids.toString();
}
// 表格渲染
function renderTable() {
//展示已知数据
table = new Table({
elem: '#selected_goods_list',
page: false,
limit: Number.MAX_VALUE,
cols: [
[
{
width: "3%",
type: 'checkbox',
unresize: 'false'
},
{
field: 'sku_name',
title: '商品名称',
width: '22%',
unresize: 'false',
templet: function (data) {
var html = '';
html += `
<div class="goods-title">
<div class="goods-img">
<img src="${ns.img(data.sku_image)}" alt="">
</div>
<p class="multi-line-hiding goods-name" data-goods_id="${data.goods_id}" data-sku_id="${data.sku_id}" title="${data.sku_name}">${data.sku_name}</p>
</div>
`;
return html;
}
}, {
field: 'price',
title: '原价',
unresize: 'false',
align: 'left',
width: '14%',
templet: function (data) {
return '<p class="line-hiding" title="' + data.price + '">¥<span>' + data.price + '</span></p>';
}
}, {
field: 'stock',
title: '库存',
unresize: 'false',
width: '9%',
templet: function (data) {
return '<p class="stock">' + data.stock + '</p>';
}
}, {
field: 'max_buy',
title: '<span>限购0为不限购</span>',
unresize: 'false',
width: '14%',
templet: '#maxBuy'
}, {
title: '<span title="秒杀价">秒杀价</span>',
unresize: 'false',
width: '15%',
templet: '#seckillPrice'
}, {
title: '<span title="秒杀库存">秒杀库存</span>',
unresize: 'false',
width: '12%',
templet: '#seckillStock'
}, {
title: '操作',
toolbar: '#operation',
unresize: 'false',
}]
],
data: sku_list,
toolbar: '#toolbarOperation'
});
table.toolbar(function (obj) {
if (obj.data.length < 1) {
layer.msg('请选择要操作的数据');
return;
}
switch (obj.event) {
case "seckill-purchase":
editInput(0, obj);
break;
case "seckill-price":
editInput(1, obj);
break;
case "seckill-stock":
editInput(2, obj);
break;
}
});
}
function editInput(textIndex = 0, data) {
var text = [{
name: '限购',
value: 'max_buy'
}, {
name: '秒杀价',
value: 'seckill_price'
}, {
name: '秒杀库存',
value: 'seckill_stock'
}];
layer.open({
type: 1,
title: "修改" + text[textIndex].name,
area: ['600px'],
btn: ["保存", "返回"],
content: `
<div class="layui-form-item">
<label class="layui-form-label"><span class="required">*</span>${text[textIndex].name}</label>
<div class="layui-input-block">
<input type="text" name="bargain_edit_input" lay-verify="required" autocomplete="off" class="layui-input len-mid" placeholder="请输入${text[textIndex].name}">
</div>
</div>
`,
yes: function (index, layero) {
var val = $("input[name='bargain_edit_input']").val();
if (!val) {
layer.msg("请输入" + text[textIndex].name);
return false;
}
data.data.forEach(function (item, index) {
sku_list.forEach(function (skuItem, skuIndex) {
if (item.sku_id == skuItem.sku_id) {
sku_list[skuIndex][text[textIndex].value] = val;
}
})
});
renderTable();
layer.closeAll();
}
});
}
/* 商品 */
function addGoods() {
goodsSelect(function (data) {
if (!Object.keys(data).length) return;
var sku_ids = [];
for (var key in data) {
for (var sku in data[key].selected_sku_list) {
var item = data[key].selected_sku_list[sku];
item.seckill_price = item.price;
item.max_buy = 1;
item.seckill_stock = item.stock;
sku_ids.push(item.sku_id);
sku_list.push(item);
}
}
renderTable();
selectedSkuId = sku_ids;
$("#goods_num").html(sku_list.length)
}, selectedSkuId, {mode: "sku"});
}
function setGoodsSku(type, sku_id, obj) {
$.each(sku_list, function (i, e) {
if (sku_id == e.sku_id) {
sku_list[i][type] = $(obj).val();
}
})
}
function addSeckillTime() {
layer.open({
type: 1,
title: "场次选择",
area: ['900px', '620px'],
btn: ['保存', '返回'],
content: $('#seckillTime').html(),
success: function (layero) {
renderTimeTable();
},
yes: function (index, layero) {
var select_time_id = [];
time_list = [];
$('div[lay-id="seckill_time_list"] .time-select').each(function (i, e) {
if ($(e).is(":checked")) {
let time = {};
time.start_time = $(e).attr('data-time-start');
time.end_time = $(e).attr('data-time-end');
time.id = $(e).attr('data-time-id');
time.name = $(e).attr('data-name');
select_time_id.push($(e).attr('data-time-id'));
time_list.push(time);
}
});
$('input[name="seckill_time_id"]').val(select_time_id.toString());
refreshTimeList();
layer.closeAll();
}
});
}
function refreshTimeList() {
var html = '';
for (let i in time_list) {
let start_time = transformSeckillTime(time_list[i]['start_time']);
let end_time = transformSeckillTime(time_list[i]['end_time']);
let name = time_list[i]['name'];
let id = time_list[i]['id'];
html += `
<li class="time-label" title="${start_time} - ${end_time}">
<span>${name}</span>
<i class="layui-icon layui-icon-close" onclick="delTime(${id}, this)"></i>
</li>
`;
}
$('.time-label-list ul').html(html);
}
function transformSeckillTime(time) {
time = parseFloat(time);
var hour = parseInt(time / 3600);
var minute = parseInt((time % 3600) / 60);
var second = parseInt(time % 60);
if (hour < 10) hour = '0' + hour;
if (minute < 10) minute = '0' + minute;
if (second < 10) second = '0' + second;
return hour + ':' + minute + ':' + second;
}
function delTime(id, obj) {
time_list.splice(time_list.indexOf(id), 1);
let time = [];
for (let i in time_list) {
if (time_list[i]['id'] != id) {
time.push(time_list[i]['id']);
}
}
$('input[name="seckill_time_id"]').val(time.toString());
$(obj).parents('li').remove();
}
// 场次渲染
function renderTimeTable() {
$.ajax({
url: ns.url("seckill://shop/seckill/lists"),
dataType: 'JSON',
type: 'POST',
success: function (res) {
if (res.code < 0) {
layer.msg(res.message);
return false;
}
var time_data = res.data;
for (let i in time_data) {
time_data[i]['is_select'] = 0;
for (let j in time_list) {
if (time_list[j]['id'] == time_data[i]['id']) {
time_data[i]['is_select'] = 1;
}
}
}
timeTable = new Table({
elem: '#seckill_time_list',
page: false,
height: 380,
limit: Number.MAX_VALUE,
cols: [
[{
title: '<input type="checkbox" name="time_checkbox_all" lay-skin="primary" lay-filter="time_checkbox_all">',
unresize: 'false',
width: '10%',
templet: '#timecheckbox',
}, {
field: 'name',
title: '场次名称',
unresize: 'false',
align: 'left',
width: '30%'
}, {
field: 'seckill_start_time_show',
title: '开始时间',
unresize: 'false',
width: '25%'
}, {
field: 'seckill_end_time_show',
title: '结束时间',
unresize: 'false',
width: '25%'
}]
],
data: time_data
});
// 勾选商品
form.on('checkbox(time_checkbox_all)', function (data) {
var all_checked = data.elem.checked;
$("input[name='time_checkbox']").each(function () {
var checked = $(this).prop('checked');
if (all_checked != checked) {
$(this).next().click();
}
});
});
}
});
}
function initTimeList() {
var html = '';
for (let i in time_list) {
let start_time = transformSeckillTime(time_list[i]['seckill_start_time']);
let end_time = transformSeckillTime(time_list[i]['seckill_end_time']);
let name = time_list[i]['name'];
let id = time_list[i]['id'];
html += `
<li class="time-label" title="${start_time} - ${end_time}">
<span>${name}</span>
<i class="layui-icon layui-icon-close" onclick="delTime(${id}, this)"></i>
</li>
`;
}
$('.time-label-list ul').html(html);
}
function back() {
location.hash = ns.hash("seckill://shop/seckill/goodslist");
}

View File

@@ -0,0 +1,127 @@
<div class="layui-form form-wrap">
<div class="layui-form-item">
<label class="layui-form-label"><span class="required">*</span>秒杀场次名称:</label>
<div class="layui-input-block">
<input type="text" id="name" name="name" lay-verify="required" autocomplete="off" class="layui-input len-long">
</div>
</div>
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label"><span class="required">*</span>秒杀时间段:</label>
<div class="layui-inline">
<div class="layui-input-inline">
<input type="text" id="add_start_time" value="00:00:00" lay-verify="required" class="layui-input">
<i class=" iconrili iconfont calendar"></i>
</div>
<span class="layui-form-mid">-</span>
<div class="layui-input-inline">
<input type="text" id="add_end_time" value="00:00:00" lay-verify="required" class="layui-input">
<i class=" iconrili iconfont calendar"></i>
</div>
</div>
<div class="word-aux">
<p>每个场次的时间段不能重叠</p>
</div>
</div>
</div>
<div class="form-row">
<button class="layui-btn" lay-submit lay-filter="save">保存</button>
<button class="layui-btn layui-btn-primary" onclick="back()">返回</button>
</div>
</div>
<script>
layui.use(['form','laydate'], function() {
var form = layui.form,
laydate = layui.laydate;
repeat_flag = false; //防重复标识
form.render();
// 表单提交监听
form.on('submit(save)', function(data) {
if (repeat_flag) return;
repeat_flag = true;
data.name = $('#name').val();
if (!data.name) {
layer.msg("秒杀场次名称不能为空");
return false;
}
if (!$('#add_start_time').val()) {
layer.msg("开始时间不能为空");
return false;
}
if (!$('#add_end_time').val()) {
layer.msg("结束时间不能为空");
return false;
}
data.field.start_hour = $('#add_start_time').val().split(":")[0];
data.field.start_minute = $('#add_start_time').val().split(":")[1];
data.field.start_second = $('#add_start_time').val().split(":")[2];
data.field.end_hour = $('#add_end_time').val().split(":")[0];
data.field.end_minute = $('#add_end_time').val().split(":")[1];
data.field.end_second = $('#add_end_time').val().split(":")[2];
$.ajax({
url: ns.url("seckill://shop/seckill/add"),
data: data.field,
type: "post",
dataType: "JSON",
success: function(res) {
repeat_flag = false;
if (res.code == 0) {
layer.confirm('添加成功', {
title:'操作提示',
btn: ['返回列表', '继续添加'],
closeBtn: 0,
yes: function(index, layero){
location.hash = ns.hash("seckill://shop/seckill/lists");
layer.close(index);
},
btn2: function(index, layero) {
listenerHash(); // 刷新页面
layer.close(index);
}
});
}else{
layer.msg(res.message);
}
}
});
});
form.verify({
timeHour: function(value) {
if (!new RegExp("^0|[1-9]|1[0-9]|2[0-3]$").test(value)) {
return '时段范围为0-23且只能是整数';
}
},
timeMinSend: function(value) {
if (!new RegExp("^(?:0|[0-5][0-9]?)$").test(value)) {
return '分秒范围为0-59且只能是整数';
}
}
});
laydate.render({
elem: '#add_end_time',
type: 'time'
});
laydate.render({
elem: '#add_start_time',
type: 'time'
});
});
function back() {
location.hash = ns.hash("seckill://shop/seckill/lists");
}
</script>

View File

@@ -0,0 +1,5 @@
<script src="SECKILL_JS/goods.js"></script>

View File

@@ -0,0 +1,123 @@
<div class="layui-form form-wrap">
<div class="layui-form-item">
<label class="layui-form-label"><span class="required">*</span>秒杀场次名称:</label>
<div class="layui-input-block">
<input type="text" id="name" name="name" lay-verify="required" value="{$time_info.name}" autocomplete="off" class="layui-input len-long">
</div>
</div>
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label"><span class="required">*</span>秒杀时间段:</label>
<div class="layui-inline">
<div class="layui-input-inline">
<input type="text" id="add_start_time" value="{$time_info.start_hour}:{$time_info.start_minute}:{$time_info.start_second}" lay-verify="required" class="layui-input">
<i class=" iconrili iconfont calendar"></i>
</div>
<span class="layui-form-mid">-</span>
<div class="layui-input-inline">
<input type="text" id="add_end_time" value="{$time_info.end_hour}:{$time_info.end_minute}:{$time_info.end_second}" lay-verify="required" class="layui-input">
<i class=" iconrili iconfont calendar"></i>
</div>
</div>
<div class="word-aux">
<p>每个场次的时间段不能重叠</p>
</div>
</div>
</div>
<div class="form-row">
<button class="layui-btn" lay-submit lay-filter="save">保存</button>
<button class="layui-btn layui-btn-primary" onclick="back()">返回</button>
</div>
<input type="hidden" name="id" value="{$time_info.id}" />
</div>
<script>
layui.use(['form','laydate'], function() {
var form = layui.form,
laydate = layui.laydate;
repeat_flag = false; //防重复标识
form.render();
// 表单提交监听
form.on('submit(save)', function(data) {
if (repeat_flag) return;
repeat_flag = true;
data.field.name = $('#name').val();
if (!data.field.name) {
layer.msg("秒杀场次名称不能为空");
return false;
}
if (!$('#add_start_time').val()) {
layer.msg("开始时间不能为空");
return false;
}
if (!$('#add_end_time').val()) {
layer.msg("结束时间不能为空");
return false;
}
data.field.start_hour = $('#add_start_time').val().split(":")[0];
data.field.start_minute = $('#add_start_time').val().split(":")[1];
data.field.start_second = $('#add_start_time').val().split(":")[2];
data.field.end_hour = $('#add_end_time').val().split(":")[0];
data.field.end_minute = $('#add_end_time').val().split(":")[1];
data.field.end_second = $('#add_end_time').val().split(":")[2];
$.ajax({
url: ns.url("seckill://shop/seckill/edit"),
data: data.field,
type: "post",
dataType: "JSON",
success: function(res) {
repeat_flag = false;
if (res.code == 0) {
layer.confirm('编辑成功', {
title: '操作提示',
btn: ['返回列表', '继续操作'],
yes: function(index, layero) {
location.hash = ns.hash("seckill://shop/seckill/lists");
layer.close(index);
},
btn2: function(index, layero) {
layer.close(index);
}
});
} else {
layer.msg(res.message);
}
}
});
});
form.verify({
timeHour: function(value) {
if (!new RegExp("^0|[1-9]|1[0-9]|2[0-3]$").test(value)) {
return '时段范围为0-23且只能是整数';
}
},
timeMinSend: function(value) {
if (!new RegExp("^(?:0|[0-5][0-9]?)$").test(value)) {
return '分秒范围为0-59且只能是整数';
}
}
});
laydate.render({
elem: '#add_end_time',
type: 'time'
});
laydate.render({
elem: '#add_start_time',
type: 'time'
});
});
function back() {
location.hash = ns.hash("seckill://shop/seckill/lists");
}
</script>

View File

@@ -0,0 +1,191 @@
<link rel="stylesheet" type="text/css" href="SECKILL_CSS/goods.css"/>
<div class="layui-form form-wrap main-form">
<div class="layui-form-item">
<label class="layui-form-label"><span class="required">*</span>活动名称:</label>
<div class="layui-input-block">
<input type="text" name="seckill_name" value="{$seckill_info['seckill_name']??''}" lay-verify="required" placeholder="请输入活动名称" autocomplete="off" class="layui-input len-long" maxlength="40">
</div>
<div class="word-aux">
<p>活动名称将显示在列表中展示,方便商家管理使用</p>
</div>
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">活动规则说明:</label>
<div class="layui-input-block">
<textarea name="remark" class="layui-textarea len-long" maxlength="300" placeholder="请输入活动规则说明">{$seckill_info['remark']??''}</textarea>
</div>
<div class="word-aux">
<p>商家对秒杀的补充说明文字,非必填选项</p>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"><span class="required">*</span>活动时间:</label>
<div class="layui-inline">
<div class="layui-input-inline len-mid">
<input type="text" id="start_time" name="start_time" {notempty name="$seckill_info"}value="{:date('Y-m-d H:i:s', $seckill_info.start_time)}"{/notempty} lay-verify="required" autocomplete="off" class="layui-input" readonly>
<i class="iconrili iconfont calendar"></i>
</div>
<span class="layui-form-mid">-</span>
<div class="layui-input-inline len-mid end-time">
<input type="text" id="end_time" name="end_time" lay-verify="required|time" {notempty name="$seckill_info"}value="{:date('Y-m-d H:i:s', $seckill_info.end_time)}"{/notempty} autocomplete="off" class="layui-input" readonly>
<input type="hidden" value="{$seckill_info.end_time??''}" id="old_end_time">
<i class="iconrili iconfont calendar"></i>
</div>
</div>
<div class="word-aux">
<p>商品只会在活动时间段内的指定场次进行展示</p>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">排序:</label>
<div class="layui-input-block">
<input type="number" name="sort" value="{$seckill_info['sort']??''}" class="layui-input len-short" placeholder="0" autocomplete="off">
</div>
<div class="word-aux">商品默认排序号为0数字越大排序越靠前数字重复则最新添加的靠前。</div>
</div>
<div class="seckill-goods-list">
<div class="layui-form-item">
<label class="layui-form-label"><span class="required">*</span>参与场次:</label>
<div class="layui-input-block time-label-list">
<a href="javascript:addSeckillTime();" class="text-color js-add-time">选择场次</a>
<ul></ul>
</div>
</div>
</div>
<div class="layui-form-item goods_list">
<label class="layui-form-label"><span class="required">*</span>商品选择:</label>
<div class="layui-input-block">
<table id="selected_goods_list" lay-filter="selected_goods_list"></table>
{if empty($seckill_info) }
<button class="layui-btn" onclick="addGoods()">选择商品</button>
<span class="goods_num">已选商品(<span id="goods_num" class="text-color">0</span></span>
{/if}
</div>
</div>
<input type="hidden" name="id" value="{$seckill_info.id??''}">
<input type="hidden" name="seckill_time_id" value="{$seckill_info['seckill_time_id']??''}">
{notempty name="$time_list"}
<input type="hidden" name="time_list" value='{:json_encode($time_list, JSON_UNESCAPED_UNICODE)}'>
{/notempty}
{notempty name="$seckill_info"}
<input type="hidden" name="sku_list" value='{:json_encode($seckill_info.goods_sku, JSON_UNESCAPED_UNICODE)}'>
{/notempty}
<div class="form-row">
<button class="layui-btn" lay-submit lay-filter="save">保存</button>
<button class="layui-btn layui-btn-primary" onclick="back()">返回</button>
</div>
</div>
<!-- 操作 -->
<script type="text/html" id="operation">
<div class="table-btn">
{if !empty($seckill_info) }
{{# if (d.is_select == 1){ }}
<a class="layui-btn no-participation">不参与</a>
{{# }else{ }}
<a class="layui-btn participation">参与</a>
{{# } }}
{else/}
<a class="layui-btn" onclick="delGoods(this,{{d.sku_id}})">删除</a>
{/if}
</div>
</script>
<!-- 价格设置 -->
<script type="text/html" id="seckillPrice">
{{# if (d.is_select == 1){ }}
<input type="number" class="layui-input len-input " value="{{d.seckill_price}}" onchange="setGoodsSku('seckill_price', {{d.sku_id}}, this)" min="0"/>
{{# }else{ }}
<input type="number" class="layui-input len-input" value="{{d.seckill_price}}" onchange="setGoodsSku('seckill_price', {{d.sku_id}}, this)" min="0"/>
{{# } }}
</script>
<script type="text/html" id="toolbarOperation">
<button class="layui-btn layui-btn-primary" lay-event="seckill-purchase">限购</button>
<button class="layui-btn layui-btn-primary" lay-event="seckill-price">秒杀价</button>
<button class="layui-btn layui-btn-primary" lay-event="seckill-stock">秒杀库存</button>
</script>
<!-- 库存设置 -->
<script type="text/html" id="seckillStock">
{{# if (d.is_select == 1){ }}
<input type="number" class="layui-input len-input seckill_stock" value="{{d.seckill_stock}}" onchange="setGoodsSku('seckill_stock', {{d.sku_id}}, this)" min="0.00"/>
{{# }else{ }}
<input type="number" class="layui-input len-input seckill_stock" value="{{d.seckill_stock}}" onchange="setGoodsSku('seckill_stock', {{d.sku_id}}, this)" min="0.00"/>
{{# } }}
</script>
<!-- 限购设置 -->
<script type="text/html" id="maxBuy">
{{# if (d.is_select == 1){ }}
<input type="number" class="layui-input len-input max_buy" value="{{d.max_buy}}" onchange="setGoodsSku('max_buy', {{d.sku_id}}, this)" min="0"/>
{{# }else{ }}
<input type="number" class="layui-input len-input max_buy" value="{{d.max_buy}}" onchange="setGoodsSku('max_buy', {{d.sku_id}}, this)" min="0"/>
{{# } }}
</script>
<!--选择场次弹出-->
<script type="text/html" id="seckillTime">
<div class="seckill-box">
<table id="seckill_time_list" lay-filter="seckill_time_list"></table>
</div>
</script>
<script type="text/html" id="timecheckbox">
{{# if (d.is_select == 1){ }}
<input type="checkbox" name="time_checkbox" data-name="{{d.name}}" data-time-start="{{d.seckill_start_time}}" checked data-time-end="{{d.seckill_end_time}}" data-time-id="{{d.id}}" class="time-select" lay-skin="primary" lay-filter="goods_checkbox">
{{# }else{ }}
<input type="checkbox" name="time_checkbox" data-name="{{d.name}}" data-time-start="{{d.seckill_start_time}}" data-time-end="{{d.seckill_end_time}}" data-time-id="{{d.id}}" class="time-select" lay-skin="primary" lay-filter="goods_checkbox">
{{# } }}
</script>
<!-- 添加场次 -->
<script type="text/html" id="addTime">
<div class="layui-form ">
<div class="layui-form-item len-mid">
<label class="layui-form-label"><span class="required">*</span></label>
<div class="layui-input-block">
{{# if(d.name){ }}
<input type="text" id="add_time_name" value="{{d.name}}" lay-verify="required" autocomplete="off" class="layui-input len-mid">
{{# }else{ }}
<input type="text" id="add_time_name" value="" lay-verify="required" autocomplete="off" class="layui-input len-mid">
{{# } }}
</div>
</div>
<div class="layui-form-item ">
<div class="layui-inline">
<label class="layui-form-label"><span class="required">*</span></label>
<div class="layui-inline">
<div class="layui-input-inline">
<input type="text" id="add_start_time" value="{{d.seckill_start_time_show ? d.seckill_start_time_show : ''}}" lay-verify="required" class="layui-input">
<i class=" iconrili iconfont calendar"></i>
</div>
<span class="layui-form-mid">-</span>
<div class="layui-input-inline">
<input type="text" id="add_end_time" value="{{d.seckill_end_time_show ? d.seckill_end_time_show :''}}" lay-verify="required" class="layui-input">
<i class=" iconrili iconfont calendar"></i>
</div>
</div>
</div>
</div>
</div>
</script>
<script src="SECKILL_JS/goods.js"></script>

View File

@@ -0,0 +1,703 @@
<style>
.contraction span{cursor: pointer;display: inline-block;width: 17px;height: 17px;text-align: center;line-height: 14px;user-select: none;}
.sku-list{overflow: hidden;padding: 0 45px;max-width: 100%;}
.sku-list li .img-wrap{vertical-align: middle;margin-right: 8px;width: 20%;height: 80px;text-align: center;line-height: 70px;}
.sku-list li .img-wrap img{max-width: 100%;max-height: 100%;}
.sku-list li .info-wrap span.sku-name{-webkit-line-clamp: 2;margin-bottom: 5px;}
.sku-list li .info-wrap span{display: -webkit-box;margin-bottom: 5px;overflow: hidden;text-overflow: ellipsis;white-space: normal;word-break: break-all;-webkit-box-orient: vertical;-webkit-line-clamp: 1;}
.sku-list li{float: left;display: flex;padding: 10px;margin-right: 10px;margin-bottom: 10px;border: 1px solid #EFEFEF;width: 294px;height: 180px;align-items: center;}
#time_label_dl span{margin: 3px 5px 3px 0;color: white;padding: 0 5px;border-radius: 5px;line-height: 25px;}
#time_label_dl{display: flex;flex-wrap: wrap;}
.layui-layout-admin.admin-style-2 .layui-form-item .layui-input-inline{background-color: #fff;}
.layui-layout-admin.admin-style-2 .table-tab .layui-tab-title{margin-bottom: 15px;}
</style>
<div class="single-filter-box top">
<button class="layui-btn" onclick="clickAdd()">添加秒杀商品</button>
</div>
<div class="screen layui-collapse" lay-filter="selection_panel">
<div class="layui-colla-item">
<form class="layui-colla-content layui-form layui-show">
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label">商品名称:</label>
<div class="layui-input-inline">
<input type="text" name="goods_name" placeholder="请输入商品名称" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label">参与场次:</label>
<div class="layui-input-inline">
<select name="seckill_time_id">
<option value="">全部</option>
{foreach $res as $k => $v}
<option value="{$v['id']}" {if $v['id'] == $seckill_time_id}selected{/if}>{$v['name']}</option>
{/foreach}
</select>
</div>
</div>
</div>
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label">秒杀时间:</label>
<div class="layui-input-inline">
<input type="text" class="layui-input" name="start_time" placeholder="开始时间" id="start_time" readonly>
<i class=" iconrili iconfont calendar"></i>
</div>
<div class="layui-form-mid">-</div>
<div class="layui-input-inline">
<input type="text" class="layui-input" name="end_time" placeholder="结束时间" id="end_time" readonly>
<i class=" iconrili iconfont calendar"></i>
</div>
</div>
</div>
<div class="form-row">
<button type="button" class="layui-btn" lay-filter="search" lay-submit>筛选</button>
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
</div>
</form>
</div>
</div>
<div class="layui-tab table-tab" lay-filter="bargain_tab">
<ul class="layui-tab-title">
<li class="layui-this" data-status="">全部</li>
<li data-status="0">未开始</li>
<li data-status="1">进行中</li>
<li data-status="2">已结束</li>
<li data-status="-1">已关闭</li>
</ul>
<div class="layui-tab-content">
<!-- 列表 -->
<table id="good_list" lay-filter="good_list"></table>
</div>
</div>
<!--价格-->
<script type="text/html" id="price">
<div class="layui-elip">{{d.seckill_price}}</div>
</script>
<!-- 推广 -->
{include file="app/shop/view/promote.html"}
<!--时间-->
<script type="text/html" id="time">
<div class="layui-elip">开始时间{{ns.time_to_date(d.start_time)}}</div>
<div class="layui-elip">结束时间{{ns.time_to_date(d.end_time)}}</div>
</script>
<!--操作-->
<script type="text/html" id="operation">
<div class="operation-wrap" data-seckill-id="{{d.id}}">
<div class="popup-qrcode-wrap" style="display: none"><img class="popup-qrcode-loadimg" src="__STATIC__/loading/loading.gif"/></div>
<div class="table-btn">
{{# if(d.status == 1){ }}
<a class="layui-btn text-color" lay-event="select">推广</a>
{{# } }}
<a class="layui-btn" lay-event="edit">编辑</a>
<a class="layui-btn" lay-event="delete">删除</a>
{{# if(d.status == 1){ }}
<a class="layui-btn" lay-event="close">关闭</a>
{{# } }}
</div>
</script>
<!-- 批量操作 -->
<script type="text/html" id="toolbarAction">
<button class="layui-btn layui-btn-primary" lay-event="delete">批量删除</button>
<button class="layui-btn layui-btn-primary" lay-event="invalid">批量关闭</button>
</script>
<script type="text/html" id="goods_name">
<div class="table-title">
<div class="contraction" data-id="{{d.id}}" data-open="0">
<span>+</span>
</div>
<div class="title-pic">
{{# if(d.goods_image){ }}
<img layer-src="{{ns.img(d.goods_image.split(',')[0],'big')}}" src="{{ns.img(d.goods_image.split(',')[0],'small')}}" />
{{# } }}
</div>
<div class="title-content">
<a href="javascript:;" class="multi-line-hiding text-color-sub" title="{{d.goods_name}}">{{d.goods_name}}</a>
</div>
</div>
</script>
<script type="text/html" id="skuList">
<tr class="js-list-{{d.index}}" id="sku_img_{{d.index}}">
<td colspan="10">
<ul class="sku-list">
{{# for(var i=0; i<d.list.length; i++){ }}
<li>
<div class="img-wrap">
<img layer-src src="{{ns.img(d.list[i].sku_image)}}">
</div>
<div class="info-wrap">
<span class="sku-name" title="{{d.list[i].sku_name}}">{{d.list[i].sku_name}}</span>
<span class="price">商品价格{{d.list[i].price}}</span>
<span class="sale_num">秒杀价{{d.list[i].seckill_price}}</span>
<span class="price">库存{{d.list[i].stock}}</span>
</div>
</li>
{{# } }}
</ul>
</td>
</tr>
</script>
<!-- 场次 -->
<script id="time_label" type="text/html">
{{# if (d.time_list != []) { }}
<div id="time_label_dl">
{{# for (var index in d.time_list) { }}
{{'<span class="bg-color">' + d.time_list[index]['name'] + '</span>'}}
{{# } }}
</div>
{{# } }}
</script>
<script type="text/html" id="addTime">
<div class="layui-form">
<div class="layui-form-item len-mid">
<label class="layui-form-label"><span class="required">*</span></label>
<div class="layui-input-block">
{{# if(d.name){ }}
<input type="text" id="add_time_name" value="{{d.name}}" lay-verify="required" autocomplete="off" class="layui-input len-mid">
{{# }else{ }}
<input type="text" id="add_time_name" value="" lay-verify="required" autocomplete="off" class="layui-input len-mid">
{{# } }}
</div>
</div>
<div class="layui-form-item ">
<div class="layui-inline">
<label class="layui-form-label"><span class="required">*</span></label>
<div class="layui-inline">
<div class="layui-input-inline">
<input type="text" id="add_start_time" value="{{d.seckill_start_time_show}}" lay-verify="required" class="layui-input">
<i class=" iconrili iconfont calendar"></i>
</div>
<span class="layui-form-mid">-</span>
<div class="layui-input-inline">
<input type="text" id="add_end_time" value="{{d.seckill_end_time_show}}" lay-verify="required" class="layui-input">
<i class=" iconrili iconfont calendar"></i>
</div>
</div>
</div>
</div>
</div>
</script>
<!-- 编辑排序 -->
<script type="text/html" id="editSort">
<input name="sort" type="number" onchange="editSort({{d.id}}, this)" value="{{d.sort}}" class="layui-input edit-sort len-short">
</script>
<script>
var form, table, laytpl,
repeat_flag = false, //防重复标识
arr_id_good = [];
$("body").off("click", ".contraction").on("click", ".contraction", function() {
var seckill_id = $(this).attr("data-id");
var open = $(this).attr("data-open");
var tr = $(this).parent().parent().parent().parent();
var index = tr.attr("data-index");
if (open == 1) {
$(this).children("span").text("+");
$(".js-list-" + index).remove();
} else {
$(this).children("span").text("-");
$.ajax({
url: ns.url("seckill://shop/seckill/getSkuList"),
data: {
seckill_id: seckill_id
},
dataType: 'JSON',
type: 'POST',
async: false,
success: function(res) {
var sku_list = $("#skuList").html();
var data = {
list: res.data,
index: index
};
laytpl(sku_list).render(data, function(html) {
tr.after(html);
});
layer.photos({
photos: '.img-wrap',
anim: 5
});
}
});
}
$(this).attr("data-open", (open == 0 ? 1 : 0));
});
layui.use(['form', 'laytpl','laydate', 'element'], function() {
form = layui.form;
element = layui.element;
laytpl = layui.laytpl;
laydate = layui.laydate;
form.render();
element.on('tab(bargain_tab)', function(data) {
table.reload({
page: {
curr: 1
},
where: {
'status': this.getAttribute('data-status')
}
});
});
table = new Table({
elem: '#good_list',
url: '{:addon_url("seckill://shop/seckill/goodslist")}',
async: false,
parseData: function(res) {
arr_id_good = [];
for (var i in res.data.list) {
arr_id_good.push(res.data.list[i].sku_id);
}
return {
"code": res.code,
"msg": res.message,
"count": res.data.count,
"data": res.data.list,
};
},
where:{"seckill_time_id" : "{$seckill_time_id}"},
cols: [
[{
type: 'checkbox',
width: '3%',
},{
title: '商品',
unresize: 'false',
width: '18%',
templet: '#goods_name'
}, {
title: '秒杀时间',
unresize: 'false',
width: '16%',
templet: '#time'
}, {
title: '参与场次',
unresize: 'false',
align:"left",
width: '18%',
templet: '#time_label'
}, {
title: '秒杀价',
unresize: 'false',
width: '7%',
templet: '#price'
}, {
title: '库存',
unresize: 'false',
width: '6%',
field: 'goods_stock'
}, {
title: '销量',
unresize: 'false',
width: '6%',
field: 'sale_num'
}, {
field: 'sort',
unresize:'false',
title: `排序<i class="iconfont iconwenhao1 required growth" style="color:#000;" title="后台商品默认排序为排序号正序排列(即排序号越小越靠前),如果序号相同,那么按照添加顺序排列,越新添加的越靠前"></i>`,
width: '9%',
align: 'center',
templet: '#editSort',
sort: true
}, {
title: '状态',
unresize: 'false',
width: '10%',
templet: function (data) {
if(data.status == 1){
return '进行中'
}else if(data.status == 0){
return '未开始'
}else if(data.status == 2){
return '已过期'
}else if(data.status == -1){
return '已关闭(手动)'
}
}
}, {
title: '操作',
toolbar: '#operation',
unresize: 'false',
align:'right'
}]
],
toolbar: '#toolbarAction'
});
table.on("sort",function (obj) {
table.reload({
page: {
curr: 1
},
where: {
order:obj.field,
sort:obj.type
}
});
});
//开始时间
laydate.render({
elem: '#start_time', //指定元素
type: 'datetime'
});
//结束时间
laydate.render({
elem: '#end_time', //指定元素
type: 'datetime'
});
/**
* 搜索功能
*/
form.on('submit(search)', function(data) {
table.reload({
page: {
curr: 1
},
where: data.field
});
});
// 监听工具栏操作
table.toolbar(function (obj) {
var data = obj.data;
if(data.length <= 0) return;
var seckillIdAll = [];
for (var i in data){
seckillIdAll.push(data[i].id);
}
switch (obj.event) {
case 'delete':
deleteSeckillAll(seckillIdAll)
break;
case 'invalid':
closeSeckillAll(seckillIdAll)
break;
}
})
//批量删除
function deleteSeckillAll(data){
if (repeat_flag) return false;
repeat_flag = true;
layer.confirm('确定要删除商品吗?', function(index) {
layer.close(index);
$.ajax({
url: '{:addon_url("seckill://shop/seckill/deleteGoodsAll")}',
data: {
"seckill_id": data
},
dataType: 'JSON',
type: 'POST',
success: function(res) {
layer.msg(res.message);
repeat_flag = false;
table.reload({
page: {
curr: 1
},
});
}
});
}, function() {
layer.close();
repeat_flag = false;
});
}
//批量关闭
function closeSeckillAll(data)
{
if (repeat_flag) return false;
repeat_flag = true;
layer.confirm('确定要关闭商品吗?', function(index) {
layer.close(index);
$.ajax({
url: '{:addon_url("seckill://shop/seckill/closeSeckillAll")}',
data: {
"seckill_id": data
},
dataType: 'JSON',
type: 'POST',
success: function(res) {
layer.msg(res.message);
repeat_flag = false;
table.reload();
}
});
}, function() {
layer.close();
repeat_flag = false;
});
}
/**
* 监听工具栏操作
*/
table.tool(function(obj) {
var data = obj.data;
switch (obj.event) {
case 'delete': //查看
delSeckill(data.id);
break;
case 'edit': //查看
location.hash = ns.hash("seckill://shop/seckill/updateGoods?id=" + data.id);
break;
case 'select': //推广
seckillUrl(data);
break;
case 'close': //关闭
closeSeckill(data.id);
break;
}
});
/**
* 删除
*/
function delSeckill(seckill_id) {
if (repeat_flag) return false;
repeat_flag = true;
layer.confirm('确定要删除该商品吗?', function(index) {
layer.close(index);
$.ajax({
url: '{:addon_url("seckill://shop/seckill/deleteGoods")}',
data: {
"id": seckill_id
},
dataType: 'JSON',
type: 'POST',
success: function(res) {
layer.msg(res.message);
repeat_flag = false;
if (res.code == 0) {
table.reload();
}
}
});
}, function() {
layer.close();
repeat_flag = false;
});
}
/**
* 关闭
*/
function closeSeckill(seckill_id) {
if (repeat_flag) return false;
repeat_flag = true;
layer.confirm('确定要关闭该商品吗?', function(index) {
layer.close(index);
$.ajax({
url: '{:addon_url("seckill://shop/seckill/closeSeckill")}',
data: {
"seckill_id": seckill_id
},
dataType: 'JSON',
type: 'POST',
success: function(res) {
layer.msg(res.message);
repeat_flag = false;
if (res.code == 0) {
table.reload();
}
}
});
}, function() {
layer.close();
repeat_flag = false;
});
}
// 秒杀推广
function seckillUrl(data) {
if (repeat_flag) return;
repeat_flag = true;
// 先查询 H5 二维码
getSeckillQrcode(data.id,'h5',function (res) {
res.id = data.id;
repeat_flag = false;
laytpl($("#promote").html()).render(res, function (html) {
layer.open({
type: 1,
area: ['730px', '450px'],
offset: '155px',
title: ['推广'],
content: html,
success: function(){
$('input[name="promote_type"][value="h5"]').attr('data-config',JSON.stringify(res.h5));
form.render();
// 推广渠道监听
promoteTypeSwitch();
// 二次查询微信小程序、支付宝小程序二维码
getSeckillQrcode(data.id,'all',function (res) {
$('input[name="promote_type"]').each(function (index,item) {
if(res[$(item).val()]){
$(item).attr('data-config',JSON.stringify(res[$(item).val()]))
}
})
});
}
});
});
});
}
function getSeckillQrcode(seckill_id,app_type,callback) {
$.ajax({
type: "POST",
url: ns.url("seckill://shop/seckill/seckillUrl"),
data: {
seckill_id,
app_type
},
dataType: 'JSON',
success: function (res) {
if (callback) callback(res.data);
}
});
}
});
function clickAdd() {
location.hash = ns.hash("seckill://shop/seckill/addGoods");
}
function clickAddTime() {
var data = {};
laytpl($('#addTime').html()).render(data, function(html) {
var index = layer.open({
type: 1,
title: "场次添加",
area: ['700px', '300px'],
btn: ['保存', '返回'],
content: html,
yes: function(index, layero) {
var data = {};
data.name = $('#add_time_name').val();
if (!data.name) {
layer.msg("秒杀场次名称不能为空");
return false;
}
if (!$('#add_start_time').val()) {
layer.msg("开始时间不能为空");
return false;
}
if (!$('#add_end_time').val()) {
layer.msg("结束时间不能为空");
return false;
}
data.start_hour = $('#add_start_time').val().split(":")[0];
data.start_minute = $('#add_start_time').val().split(":")[1];
data.start_second = $('#add_start_time').val().split(":")[2];
data.end_hour = $('#add_end_time').val().split(":")[0];
data.end_minute = $('#add_end_time').val().split(":")[1];
data.end_second = $('#add_end_time').val().split(":")[2];
if (repeat_flag) return;
repeat_flag = true;
$.ajax({
url: ns.url("seckill://shop/seckill/add"),
data: data,
dataType: 'JSON',
type: 'POST',
success: function(res) {
repeat_flag = false;
if (res.code == 0) {
layer.msg(res.message, {}, function() {
layer.close(index);
});
} else {
layer.msg(res.message);
}
}
});
}
});
laydate.render({
elem: '#add_end_time',
type: 'time'
});
laydate.render({
elem: '#add_start_time',
type: 'time'
});
});
}
// 监听单元格编辑
function editSort(id, event){
var data = $(event).val();
if (data == '') {
$(event).val(0);
data = 0;
}
if(!new RegExp("^-?[0-9]\\d*$").test(data)){
layer.msg("排序号只能是整数");
return ;
}
if(data<0){
layer.msg("排序号必须大于0");
return ;
}
$.ajax({
type: 'POST',
url: ns.url("seckill://shop/seckill/seckillSort"),
data: {
sort: data,
id: id
},
dataType: 'JSON',
success: function(res) {
layer.msg(res.message);
if(res.code==0){
table.reload();
}
}
});
}
</script>

View File

@@ -0,0 +1,109 @@
<div class="single-filter-box top">
<button class="layui-btn" onclick="clickAdd()">添加秒杀场次</button>
</div>
<table id="seckill_list" lay-filter="seckill_list"></table>
<!-- 操作 -->
<script type="text/html" id="operation">
<div class="table-btn">
<a class="layui-btn" lay-event="check">查看商品</a>
<a class="layui-btn" lay-event="edit">编辑</a>
<a class="layui-btn" lay-event="delete">删除</a>
</div>
</script>
<script>
layui.use('form', function() {
var repeat_flag = false; //防重复标识
var table = new Table({
elem: '#seckill_list',
url: ns.url("seckill://shop/seckill/lists"),
parseData: function(res) { //res 即为原始返回的数据
return {
"code": res.code, //解析接口状态
"msg": res.message,
"data": res.data
};
},
cols: [
[{
field: 'name',
title: '时段名称',
unresize: 'false',
width: '20%'
}, {
field: 'seckill_start_time_show',
title: '开始时间',
unresize: 'false',
width: '20%'
}, {
field: 'seckill_end_time_show',
title: '结束时间',
unresize: 'false',
width: '20%'
},{
field: 'goods_num',
title: '商品数量',
unresize: 'false',
width: '20%',
}, {
title: '操作',
toolbar: '#operation',
unresize: 'false',
align:'right'
}]
],
page: false
});
/**
* 监听工具栏操作
*/
table.tool(function(obj) {
var data = obj.data;
switch (obj.event) {
case 'check': //查看
location.hash = ns.hash("seckill://shop/seckill/goodslist?seckill_time_id=" + data.id);
break;
case 'delete': //删除
layer.confirm('确定要删除该场次吗?', function(index) {
if (repeat_flag) return false;
repeat_flag = true;
layer.close(index);
$.ajax({
url: ns.url("seckill://shop/seckill/delete?id=" + data.id),
data: data,
dataType: 'JSON',
type: 'POST',
success: function(res) {
layer.msg(res.message);
repeat_flag = false;
if (res.code == 0) {
table.reload({
page: {
curr: 1
},
});
}
}
});
});
break;
case 'edit': //编辑
location.hash = ns.hash("seckill://shop/seckill/edit?id=" + data.id);
break;
}
});
});
function clickAdd() {
location.hash = ns.hash("seckill://shop/seckill/add");
}
</script>