This commit is contained in:
周中平 2021-07-16 16:26:32 +08:00
parent 68bc138f6a
commit 45423fa76c
23 changed files with 587 additions and 127 deletions

View File

@ -10,6 +10,15 @@
## 更新日志 ## 更新日志
### v1.1.45
新增:优惠券支持指定商品
新增:小程序端底部购物车数量(角标)
优化微信退款API记录日志
优化:整点秒杀页面倒计时结束刷新
修复:后台满额包邮地区总数错误
修复:后台分销商确认打款时报错
### v1.1.44 ### v1.1.44
``` ```

View File

@ -56,7 +56,8 @@ CREATE TABLE `yoshop_coupon` (
`expire_day` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '领取后生效-有效天数', `expire_day` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '领取后生效-有效天数',
`start_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '固定时间-开始时间', `start_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '固定时间-开始时间',
`end_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '固定时间-结束时间', `end_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '固定时间-结束时间',
`apply_range` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '适用范围(10全部商品 20指定商品)', `apply_range` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '适用范围(10全部商品 20指定商品 30排除商品)',
`apply_range_config` text NOT NULL COMMENT '适用范围配置(json格式)',
`total_num` int(11) NOT NULL DEFAULT '0' COMMENT '发放总数量(-1为不限制)', `total_num` int(11) NOT NULL DEFAULT '0' COMMENT '发放总数量(-1为不限制)',
`receive_num` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '已领取数量', `receive_num` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '已领取数量',
`sort` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '排序方式(数字越小越靠前)', `sort` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '排序方式(数字越小越靠前)',
@ -68,17 +69,6 @@ CREATE TABLE `yoshop_coupon` (
) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='优惠券记录表'; ) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='优惠券记录表';
CREATE TABLE `yoshop_coupon_goods` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id',
`coupon_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '优惠券id',
`goods_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '商品id',
`wxapp_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '小程序id',
`create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 COMMENT='优惠券指定商品记录表';
CREATE TABLE `yoshop_delivery` ( CREATE TABLE `yoshop_delivery` (
`delivery_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '模板id', `delivery_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '模板id',
`name` varchar(255) NOT NULL DEFAULT '' COMMENT '模板名称', `name` varchar(255) NOT NULL DEFAULT '' COMMENT '模板名称',
@ -4200,7 +4190,8 @@ CREATE TABLE `yoshop_user_coupon` (
`expire_day` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '领取后生效-有效天数', `expire_day` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '领取后生效-有效天数',
`start_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '有效期开始时间', `start_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '有效期开始时间',
`end_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '有效期结束时间', `end_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '有效期结束时间',
`apply_range` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '适用范围(10全部商品 20指定商品)', `apply_range` tinyint(3) unsigned NOT NULL DEFAULT '10' COMMENT '适用范围(10全部商品 20指定商品 30排除商品)',
`apply_range_config` text NOT NULL COMMENT '适用范围配置(json格式)',
`is_expire` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否过期(0未过期 1已过期)', `is_expire` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否过期(0未过期 1已过期)',
`is_use` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否已使用(0未使用 1已使用)', `is_use` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否已使用(0未使用 1已使用)',
`user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id', `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户id',

View File

@ -0,0 +1,17 @@
ALTER TABLE `yoshop_coupon`
ADD COLUMN `apply_range_config` text NOT NULL COMMENT '适用范围配置(json格式)' AFTER `apply_range`;
ALTER TABLE `yoshop_user_coupon`
ADD COLUMN `apply_range_config` text NOT NULL COMMENT '适用范围配置(json格式)' AFTER `apply_range`;
ALTER TABLE `yoshop_coupon`
MODIFY COLUMN `apply_range` tinyint(3) UNSIGNED NOT NULL DEFAULT 10 COMMENT '适用范围(10全部商品 20指定商品 30排除商品)' AFTER `end_time`;
ALTER TABLE `yoshop_user_coupon`
MODIFY COLUMN `apply_range` tinyint(3) UNSIGNED NOT NULL DEFAULT 10 COMMENT '适用范围(10全部商品 20指定商品 30排除商品)' AFTER `end_time`;

View File

@ -1,4 +1,30 @@
 
### v1.1.45 更新日志 ###
新增:优惠券支持指定商品
新增:小程序端底部购物车数量(角标)
优化微信退款API记录日志
优化:整点秒杀页面倒计时结束刷新
修复:后台满额包邮地区总数错误
修复:后台分销商确认打款时报错
--------------------------------
注:本次更新须重新发布小程序
### v1.1.44 更新日志 ###
新增:小程序转发到朋友圈(灰度安卓用户)
优化:微信打款时验证冻结资金是否合法
优化mysql报错时记录Error SQL
修复:后台权限缺少文件列表和回收站
修复:地区表中部分地区不存在
修复:获取直播房间列表接口报错
修复:后台订单详情页报错
修复:小程序端富文本视频高度不生效
--------------------------------
注:本次更新须重新发布小程序
### v1.1.43 更新日志 ### ### v1.1.43 更新日志 ###
优化mysql5.7环境数据排序问题 优化mysql5.7环境数据排序问题

View File

@ -66,7 +66,7 @@ class Cart extends Controller
return $this->renderError($this->model->getError() ?: '加入购物车失败'); return $this->renderError($this->model->getError() ?: '加入购物车失败');
} }
// 购物车商品总数量 // 购物车商品总数量
$totalNum = $this->model->getGoodsNum(); $totalNum = $this->model->getTotalNum();
return $this->renderSuccess(['cart_total_num' => $totalNum], '加入购物车成功'); return $this->renderSuccess(['cart_total_num' => $totalNum], '加入购物车成功');
} }

View File

@ -48,15 +48,11 @@ class Goods extends Controller
if ($goods === false) { if ($goods === false) {
return $this->renderError($model->getError() ?: '商品信息不存在'); return $this->renderError($model->getError() ?: '商品信息不存在');
} }
// 多规格商品sku信息, todo: 已废弃 v1.1.25
$specData = $goods['spec_type'] == 20 ? $model->getManySpecData($goods['spec_rel'], $goods['sku']) : null;
return $this->renderSuccess([ return $this->renderSuccess([
// 商品详情 // 商品详情
'detail' => $goods, 'detail' => $goods,
// 购物车商品总数量 // 购物车商品总数量
'cart_total_num' => $user ? (new CartModel($user))->getGoodsNum() : 0, 'cart_total_num' => $user ? (new CartModel($user))->getTotalNum() : 0,
// 多规格商品sku信息
'specData' => $specData,
]); ]);
} }

View File

@ -37,7 +37,7 @@ class Goods extends Controller
{ {
// 获取秒杀活动商品详情 // 获取秒杀活动商品详情
$service = new ActiveService; $service = new ActiveService;
$data = $service->getyActiveGoodsDetail($active_time_id, $sharp_goods_id); $data = $service->getActiveGoodsDetail($active_time_id, $sharp_goods_id);
if ($data === false) { if ($data === false) {
return $this->renderError($service->getError()); return $this->renderError($service->getError());
} }
@ -56,7 +56,7 @@ class Goods extends Controller
{ {
// 获取秒杀活动商品详情 // 获取秒杀活动商品详情
$service = new ActiveService; $service = new ActiveService;
$data = $service->getyActiveGoodsDetail($active_time_id, $sharp_goods_id); $data = $service->getActiveGoodsDetail($active_time_id, $sharp_goods_id);
if ($data === false) { if ($data === false) {
return $this->renderError($service->getError()); return $this->renderError($service->getError());
} }

View File

@ -12,22 +12,29 @@ use app\common\model\UserCoupon as UserCouponModel;
*/ */
class UserCoupon extends UserCouponModel class UserCoupon extends UserCouponModel
{ {
/** /**
* 获取用户优惠券列表 * 获取用户优惠券列表
* @param $user_id * @param $userId
* @param bool $is_use 是否已使用 * @param bool $isUse 是否已使用
* @param bool $is_expire 是否已过期 * @param bool $isExpire 是否已过期
* @param float $amount 订单消费金额
* @return false|\PDOStatement|string|\think\Collection * @return false|\PDOStatement|string|\think\Collection
* @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException * @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException * @throws \think\exception\DbException
*/ */
public function getList($user_id, $is_use = false, $is_expire = false) public function getList($userId, $isUse = false, $isExpire = false, $amount = null)
{ {
return $this->where('user_id', '=', $user_id) // 构建查询对象
->where('is_use', '=', $is_use ? 1 : 0) $query = $this->where('user_id', '=', $userId)
->where('is_expire', '=', $is_expire ? 1 : 0) ->where('is_use', '=', $isUse)
->select(); ->where('is_expire', '=', $isExpire);
// 最低消费金额
if (!is_null($amount) && $amount > 0) {
$query->where('min_price', '<=', $amount);
}
return $query->select();
} }
/** /**
@ -103,6 +110,7 @@ class UserCoupon extends UserCouponModel
'start_time' => $start_time, 'start_time' => $start_time,
'end_time' => $end_time, 'end_time' => $end_time,
'apply_range' => $coupon['apply_range'], 'apply_range' => $coupon['apply_range'],
'apply_range_config' => $coupon['apply_range_config'],
'user_id' => $user['user_id'], 'user_id' => $user['user_id'],
'wxapp_id' => self::$wxapp_id 'wxapp_id' => self::$wxapp_id
]; ];
@ -144,22 +152,21 @@ class UserCoupon extends UserCouponModel
/** /**
* 订单结算优惠券列表 * 订单结算优惠券列表
* @param int $user_id 用户id * @param int $userId 用户id
* @param double $orderPayPrice 订单商品总金额 * @param double $orderPayPrice 订单商品总金额
* @return mixed * @return mixed
* @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException * @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException * @throws \think\exception\DbException
*/ */
public static function getUserCouponList($user_id, $orderPayPrice) public static function getUserCouponList($userId, $orderPayPrice)
{ {
// todo: 新增筛选条件: 最低消费金额
// 获取用户可用的优惠券列表 // 获取用户可用的优惠券列表
$list = (new self)->getList($user_id); $list = (new static)->getList($userId, false, false, $orderPayPrice);
$data = []; $data = [];
foreach ($list as $coupon) { foreach ($list as $coupon) {
// 最低消费金额 // 最低消费金额
if ($orderPayPrice < $coupon['min_price']) continue; // if ($orderPayPrice < $coupon['min_price']) continue;
// 有效期范围内 // 有效期范围内
if ($coupon['start_time']['value'] > time()) continue; if ($coupon['start_time']['value'] > time()) continue;
$key = $coupon['user_coupon_id']; $key = $coupon['user_coupon_id'];
@ -174,10 +181,11 @@ class UserCoupon extends UserCouponModel
'expire_type' => $coupon['expire_type'], 'expire_type' => $coupon['expire_type'],
'start_time' => $coupon['start_time'], 'start_time' => $coupon['start_time'],
'end_time' => $coupon['end_time'], 'end_time' => $coupon['end_time'],
'apply_range' => $coupon['apply_range'],
'apply_range_config' => $coupon['apply_range_config']
]; ];
// 计算打折金额 // 计算打折金额
if ($coupon['coupon_type']['value'] == 20) { if ($coupon['coupon_type']['value'] == 20) {
// $reduce_price = $orderPayPrice * ($coupon['discount'] / 10);
$reducePrice = helper::bcmul($orderPayPrice, $coupon['discount'] / 10); $reducePrice = helper::bcmul($orderPayPrice, $coupon['discount'] / 10);
$data[$key]['reduced_price'] = bcsub($orderPayPrice, $reducePrice, 2); $data[$key]['reduced_price'] = bcsub($orderPayPrice, $reducePrice, 2);
} else } else
@ -187,4 +195,32 @@ class UserCoupon extends UserCouponModel
return array_sort($data, 'reduced_price', true); return array_sort($data, 'reduced_price', true);
} }
/**
* 判断当前优惠券是否满足订单使用条件
* @param $couponList
* @param $orderGoodsIds
* @return mixed
*/
public static function couponListApplyRange($couponList, $orderGoodsIds)
{
// 名词解释(is_apply):允许用于抵扣当前订单
foreach ($couponList as &$item) {
if ($item['apply_range'] == 10) {
// 1. 全部商品
$item['is_apply'] = true;
} elseif ($item['apply_range'] == 20) {
// 2. 指定商品, 判断订单商品是否存在可用
$applyGoodsIds = array_intersect($item['apply_range_config']['applyGoodsIds'], $orderGoodsIds);
$item['is_apply'] = !empty($applyGoodsIds);
} elseif ($item['apply_range'] == 30) {
// 2. 排除商品, 判断订单商品是否全部都在排除行列
$excludedGoodsIds = array_intersect($item['apply_range_config']['excludedGoodsIds'], $orderGoodsIds);
$item['is_apply'] = count($excludedGoodsIds) != count($orderGoodsIds);
}
!$item['is_apply'] && $item['not_apply_info'] = '该优惠券不支持当前商品';
}
return $couponList;
}
} }

View File

@ -367,7 +367,13 @@ class Checkout
if (!$this->checkoutRule['is_coupon']) { if (!$this->checkoutRule['is_coupon']) {
return []; return [];
} }
return UserCouponModel::getUserCouponList($this->user['user_id'], $orderTotalPrice); // 整理当前订单所有商品ID集
$orderGoodsIds = helper::getArrayColumn($this->goodsList, 'goods_id');
// 当前用户可用的优惠券列表
$couponList = UserCouponModel::getUserCouponList($this->user['user_id'], $orderTotalPrice);
// 判断当前优惠券是否满足订单使用条件 ( 优惠券适用范围 )
$couponList = UserCouponModel::couponListApplyRange($couponList, $orderGoodsIds);
return $couponList;
} }
/** /**
@ -529,19 +535,12 @@ class Checkout
helper::setDataAttribute($this->goodsList, [ helper::setDataAttribute($this->goodsList, [
'coupon_money' => 0, // 优惠券抵扣金额 'coupon_money' => 0, // 优惠券抵扣金额
], true); ], true);
// 是否开启优惠券折扣 // 验证选择的优惠券ID是否合法
if (!$this->checkoutRule['is_coupon']) { if (!$this->verifyOrderCouponId($couponId, $couponList)) {
return false; return false;
} }
// 如果没有可用的优惠券,直接返回
if ($couponId <= 0 || empty($couponList)) {
return true;
}
// 获取优惠券信息 // 获取优惠券信息
$couponInfo = helper::getArrayItemByColumn($couponList, 'user_coupon_id', $couponId); $couponInfo = $this->getCouponInfo($couponId, $couponList);
if ($couponInfo == false) {
throw new BaseException(['msg' => '未找到优惠券信息']);
}
// 计算订单商品优惠券抵扣金额 // 计算订单商品优惠券抵扣金额
$goodsListTemp = helper::getArrayColumns($this->goodsList, ['total_price']); $goodsListTemp = helper::getArrayColumns($this->goodsList, ['total_price']);
$CouponMoney = new GoodsDeductService; $CouponMoney = new GoodsDeductService;
@ -556,6 +555,40 @@ class Checkout
return true; return true;
} }
/**
* 验证用户选择的优惠券ID是否合法
* @param $couponId
* @param $couponList
* @return bool
* @throws BaseException
*/
private function verifyOrderCouponId($couponId, $couponList)
{
// 是否开启优惠券折扣
if (!$this->checkoutRule['is_coupon']) {
return false;
}
// 如果没有可用的优惠券,直接返回
if ($couponId <= 0 || empty($couponList)) {
return false;
}
// 判断优惠券是否存在
$couponInfo = $this->getCouponInfo($couponId, $couponList);
if (!$couponInfo) {
throw new BaseException(['msg' => '未找到优惠券信息']);
}
// 判断优惠券适用范围是否合法
if (!$couponInfo['is_apply']) {
throw new BaseException(['msg' => $couponInfo['not_apply_info']]);
}
return true;
}
private function getCouponInfo($couponId, $couponList)
{
return helper::getArrayItemByColumn($couponList, 'user_coupon_id', $couponId);
}
/** /**
* 订单配送-快递配送 * 订单配送-快递配送
* @return bool * @return bool

View File

@ -88,7 +88,7 @@ class Active extends Basics
* @return array|bool * @return array|bool
* @throws \think\exception\DbException * @throws \think\exception\DbException
*/ */
public function getyActiveGoodsDetail($activeTimeId, $sharpGoodsId) public function getActiveGoodsDetail($activeTimeId, $sharpGoodsId)
{ {
// 活动详情 // 活动详情
$active = $this->getGoodsActive($activeTimeId, $sharpGoodsId); $active = $this->getGoodsActive($activeTimeId, $sharpGoodsId);
@ -160,7 +160,7 @@ class Active extends Basics
$startTime = $model['active']['active_date'] + ($model->active_time->getData('active_time') * 60 * 60); $startTime = $model['active']['active_date'] + ($model->active_time->getData('active_time') * 60 * 60);
$endTime = $startTime + (1 * 60 * 60); $endTime = $startTime + (1 * 60 * 60);
$activeStatus = $this->getActivcGoodsStatus($startTime, $endTime); $activeStatus = $this->getActivcGoodsStatus($startTime, $endTime);
$data = [ return [
'active_id' => $model['active_id'], 'active_id' => $model['active_id'],
'active_time_id' => $model['active_time_id'], 'active_time_id' => $model['active_time_id'],
'active_time' => $model['active_time']['active_time'], 'active_time' => $model['active_time']['active_time'],
@ -171,7 +171,6 @@ class Active extends Basics
'count_down_time' => $this->getGoodsActiveCountDownTime($activeStatus, $startTime, $endTime), 'count_down_time' => $this->getGoodsActiveCountDownTime($activeStatus, $startTime, $endTime),
'wxapp_id' => $model['wxapp_id'], 'wxapp_id' => $model['wxapp_id'],
]; ];
return $data;
} }
/** /**

View File

@ -191,6 +191,12 @@ class WxPay extends WxBase
} }
// 格式化返回结果 // 格式化返回结果
$prepay = $this->fromXml($result); $prepay = $this->fromXml($result);
// 记录日志
log_write(['describe' => '微信退款API', [
'params' => $params,
'result' => $result,
'prepay' => $prepay
]]);
// 请求失败 // 请求失败
if ($prepay['return_code'] === 'FAIL') { if ($prepay['return_code'] === 'FAIL') {
throw new BaseException(['msg' => 'return_msg: ' . $prepay['return_msg']]); throw new BaseException(['msg' => 'return_msg: ' . $prepay['return_msg']]);

View File

@ -21,6 +21,15 @@ class Coupon extends BaseModel
'state' 'state'
]; ];
/**
* 默认数据: 适用范围配置
* @var array[]
*/
protected $applyRangeConfig = [
'applyGoodsIds' => [],
'excludedGoodsIds' => []
];
/** /**
* 优惠券状态 (是否可领取) * 优惠券状态 (是否可领取)
* @param $value * @param $value
@ -93,6 +102,27 @@ class Coupon extends BaseModel
return ['text' => date('Y/m/d', $value), 'value' => $value]; return ['text' => date('Y/m/d', $value), 'value' => $value];
} }
/**
* 获取器:适用范围配置
* @param $value
* @return mixed
*/
public function getApplyRangeConfigAttr($value)
{
$array = $value ? helper::jsonDecode($value) : [];
return array_merge($this->applyRangeConfig, $array);
}
/**
* 修改器:适用范围配置
* @param $array
* @return mixed
*/
public function setApplyRangeConfigAttr($array)
{
return helper::jsonEncode(array_merge($this->applyRangeConfig, $array));
}
/** /**
* 修改器:折扣率 * 修改器:折扣率
* @param $value * @param $value

View File

@ -3,6 +3,7 @@
namespace app\common\model; namespace app\common\model;
use think\Hook; use think\Hook;
use app\common\library\helper;
/** /**
* 用户优惠券模型 * 用户优惠券模型
@ -108,6 +109,26 @@ class UserCoupon extends BaseModel
return ['text' => date('Y/m/d', $value), 'value' => $value]; return ['text' => date('Y/m/d', $value), 'value' => $value];
} }
/**
* 获取器:适用范围配置
* @param $value
* @return mixed
*/
public function getApplyRangeConfigAttr($value)
{
return $value ? helper::jsonDecode($value) : [];
}
/**
* 修改器:适用范围配置
* @param $array
* @return mixed
*/
public function setApplyRangeConfigAttr($array)
{
return helper::jsonEncode($array);
}
/** /**
* 优惠券详情 * 优惠券详情
* @param $coupon_id * @param $coupon_id

View File

@ -54,9 +54,7 @@ class Withdraw extends Controller
{ {
// 提现记录详情 // 提现记录详情
$model = WithdrawModel::detail($id); $model = WithdrawModel::detail($id);
// 验证已冻结佣金是否合法 if ($model->money()) {
// 合法 -> 确认打款
if (!$model->verifyUserFreezeMoney($model['user_id'], $model['money']) && $model->money()) {
return $this->renderSuccess('操作成功'); return $this->renderSuccess('操作成功');
} }
return $this->renderError($model->getError() ?: '操作失败'); return $this->renderError($model->getError() ?: '操作失败');
@ -73,6 +71,11 @@ class Withdraw extends Controller
public function wechat_pay($id) public function wechat_pay($id)
{ {
$model = WithdrawModel::detail($id); $model = WithdrawModel::detail($id);
// 验证已冻结佣金是否合法
if (!$model->verifyUserFreezeMoney($model['user_id'], $model['money'])) {
return $this->renderError($model->getError());
}
// 合法 -> 确认打款
if ($model->wechatPay()) { if ($model->wechatPay()) {
return $this->renderSuccess('操作成功'); return $this->renderSuccess('操作成功');
} }

View File

@ -3,7 +3,7 @@
namespace app\store\controller\market; namespace app\store\controller\market;
use app\store\controller\Controller; use app\store\controller\Controller;
use app\store\model\Goods; use app\store\model\Goods as GoodsModel;
use app\store\model\Region as RegionModel; use app\store\model\Region as RegionModel;
use app\store\model\Setting as SettingModel; use app\store\model\Setting as SettingModel;
@ -22,10 +22,16 @@ class Basic extends Controller
public function full_free() public function full_free()
{ {
if (!$this->request->isAjax()) { if (!$this->request->isAjax()) {
// 满额包邮设置
$values = SettingModel::getItem('full_free'); $values = SettingModel::getItem('full_free');
return $this->fetch('full_free', [ return $this->fetch('full_free', [
'goodsList' => (new Goods)->getListByIds($values['notin_goods']), // 不参与包邮的商品列表
'regionData' => RegionModel::getCacheTree(), // 所有地区 'goodsList' => (new GoodsModel)->getListByIds($values['notin_goods']),
// 获取所有地区(树状结构)
'regionData' => RegionModel::getCacheTree(),
// 地区总数
'cityCount' => RegionModel::getCacheCounts()['city'],
// 满额包邮设置
'values' => $values 'values' => $values
]); ]);
} }
@ -36,4 +42,4 @@ class Basic extends Controller
return $this->renderError($model->getError() ?: '操作失败'); return $this->renderError($model->getError() ?: '操作失败');
} }
} }

View File

@ -3,6 +3,7 @@
namespace app\store\controller\market; namespace app\store\controller\market;
use app\store\controller\Controller; use app\store\controller\Controller;
use app\store\model\Goods as GoodsModel;
use app\store\model\Coupon as CouponModel; use app\store\model\Coupon as CouponModel;
use app\store\model\UserCoupon as UserCouponModel; use app\store\model\UserCoupon as UserCouponModel;
@ -67,7 +68,14 @@ class Coupon extends Controller
// 优惠券详情 // 优惠券详情
$model = CouponModel::detail($coupon_id); $model = CouponModel::detail($coupon_id);
if (!$this->request->isAjax()) { if (!$this->request->isAjax()) {
return $this->fetch('edit', compact('model')); // 适用范围商品列表
$goodsModel = new GoodsModel;
// 指定的商品列表
$applyGoodsList = $goodsModel->getListByIds($model['apply_range_config']['applyGoodsIds']);
// 指定的商品列表
$excludedGoodsList = $goodsModel->getListByIds($model['apply_range_config']['excludedGoodsIds']);
// 加载模板输出
return $this->fetch('edit', compact('model', 'applyGoodsList', 'excludedGoodsList'));
} }
// 更新记录 // 更新记录
if ($model->edit($this->postData('coupon'))) { if ($model->edit($this->postData('coupon'))) {

View File

@ -11,45 +11,5 @@ use app\common\model\DeliveryRule as DeliveryRuleModel;
*/ */
class DeliveryRule extends DeliveryRuleModel class DeliveryRule extends DeliveryRuleModel
{ {
protected $append = ['region_content'];
static $regionAll;
static $regionTree;
/**
* 可配送区域
* @param $value
* @param $data
* @return string
*/
public function getRegionContentAttr($value, $data)
{
// 当前区域记录转换为数组
$regionIds = explode(',', $data['region']);
if (count($regionIds) === 373) return '全国';
// 所有地区
if (empty(self::$regionAll)) {
self::$regionAll = Region::getCacheAll();
self::$regionTree = Region::getCacheTree();
}
// 将当前可配送区域格式化为树状结构
$alreadyTree = [];
foreach ($regionIds as $regionId)
$alreadyTree[self::$regionAll[$regionId]['pid']][] = $regionId;
$str = '';
foreach ($alreadyTree as $provinceId => $citys) {
$str .= self::$regionTree[$provinceId]['name'];
if (count($citys) !== count(self::$regionTree[$provinceId]['city'])) {
$cityStr = '';
foreach ($citys as $cityId)
$cityStr .= self::$regionTree[$provinceId]['city'][$cityId]['name'];
$str .= ' (<span class="am-link-muted">' . mb_substr($cityStr, 0, -1, 'utf-8') . '</span>)';
}
$str .= '、';
}
return mb_substr($str, 0, -1, 'utf-8');
}
} }

View File

@ -63,7 +63,7 @@
</a> </a>
<div class="help-block"> <div class="help-block">
<small class="x-color-555"> <small class="x-color-555">
<span v-if="checked.citys.length == 373">全国</span> <span v-if="checked.citys.length == <?= $cityCount ?>">全国</span>
<template v-else v-for="(province, index) in checked.treeData"> <template v-else v-for="(province, index) in checked.treeData">
<span>{{ province.name }}</span> <span>{{ province.name }}</span>
<template v-if="!province.isAllCitys"> <template v-if="!province.isAllCitys">

View File

@ -79,7 +79,61 @@
value="" placeholder="请输入最低消费金额" required> value="" placeholder="请输入最低消费金额" required>
</div> </div>
</div> </div>
<div class="am-form-group" data-x-switch>
<div class="am-form-group am-padding-top" data-x-switch>
<label class="am-u-sm-3 am-u-lg-2 am-form-label form-require">适用范围 </label>
<div class="am-u-sm-9 am-u-end">
<label class="am-radio-inline">
<input type="radio" name="coupon[apply_range]" value="10"
data-am-ucheck
data-switch-box="switch-apply_range"
data-switch-item="apply_range__10" checked>
全部商品
</label>
<label class="am-radio-inline">
<input type="radio" name="coupon[apply_range]" value="20"
data-am-ucheck
data-switch-box="switch-apply_range"
data-switch-item="apply_range__20">
指定商品
</label>
<label class="am-radio-inline">
<input type="radio" name="coupon[apply_range]" value="30"
data-am-ucheck
data-switch-box="switch-apply_range"
data-switch-item="apply_range__30">
排除商品
</label>
</div>
</div>
<div class="am-form-group switch-apply_range apply_range__20 hide">
<label class="am-u-sm-3 am-u-lg-2 am-form-label"> </label>
<div class="am-u-sm-9 am-u-end">
<div class="widget-become-goods am-form-file am-margin-top-xs">
<button type="button" @click.stop="onSelectGoods"
class="j-selectGoods upload-file am-btn am-btn-secondary am-radius">
<i class="am-icon-cloud-upload"></i> 选择商品
</button>
<div class="widget-goods-list uploader-list am-cf">
</div>
</div>
</div>
</div>
<div class="am-form-group switch-apply_range apply_range__30 hide">
<label class="am-u-sm-3 am-u-lg-2 am-form-label"> </label>
<div class="am-u-sm-9 am-u-end">
<div class="widget-become-goods am-form-file am-margin-top-xs">
<button type="button" @click.stop="onSelectGoods"
class="j-selectGoods2 upload-file am-btn am-btn-secondary am-radius">
<i class="am-icon-cloud-upload"></i> 选择商品
</button>
<div class="widget-goods-list2 uploader-list am-cf">
</div>
</div>
</div>
</div>
<div class="am-form-group am-padding-top" data-x-switch>
<label class="am-u-sm-3 am-u-lg-2 am-form-label form-require">到期类型 </label> <label class="am-u-sm-3 am-u-lg-2 am-form-label form-require">到期类型 </label>
<div class="am-u-sm-9 am-u-end"> <div class="am-u-sm-9 am-u-end">
<label class="am-radio-inline"> <label class="am-radio-inline">
@ -142,6 +196,38 @@
</div> </div>
</div> </div>
</div> </div>
<!-- 文件库弹窗 -->
{{include file="layouts/_template/file_library" /}}
<!-- 指定的 商品列表 -->
<script id="tpl-goods-list-item" type="text/template">
{{ each $data }}
<div class="file-item">
<a href="{{ $value.image }}" title="{{ $value.goods_name }}" target="_blank">
<img src="{{ $value.image }}">
</a>
<input type="hidden" name="coupon[apply_range_config][applyGoodsIds][]" value="{{ $value.goods_id }}">
<i class="iconfont icon-shanchu file-item-delete" data-name="商品"></i>
</div>
{{ /each }}
</script>
<!-- 排除的 商品列表 -->
<script id="tpl-goods-list-item2" type="text/template">
{{ each $data }}
<div class="file-item">
<a href="{{ $value.image }}" title="{{ $value.goods_name }}" target="_blank">
<img src="{{ $value.image }}">
</a>
<input type="hidden" name="coupon[apply_range_config][excludedGoodsIds][]" value="{{ $value.goods_id }}">
<i class="iconfont icon-shanchu file-item-delete" data-name="商品"></i>
</div>
{{ /each }}
</script>
<script src="assets/common/js/vue.min.js?v=<?= $version ?>"></script>
<script src="assets/store/js/select.data.js?v=<?= $version ?>"></script>
<script> <script>
/** /**
* 时间选择 * 时间选择
@ -216,6 +302,38 @@
$mySwitchBox.hide().filter('.' + $(this).data('switch-item')).show(); $mySwitchBox.hide().filter('.' + $(this).data('switch-item')).show();
}); });
// 选择商品
var $goodsList = $('.widget-goods-list');
$('.j-selectGoods').selectData({
title: '选择商品',
uri: 'goods/lists',
dataIndex: 'goods_id',
done: function (data) {
var $html = $(template('tpl-goods-list-item', data));
// 删除文件
$html.find('.file-item-delete').on('click', function () {
$(this).parent().remove();
});
$goodsList.append($html);
}
});
// 选择商品
var $goodsList2 = $('.widget-goods-list2');
$('.j-selectGoods2').selectData({
title: '选择商品',
uri: 'goods/lists',
dataIndex: 'goods_id',
done: function (data) {
var $html = $(template('tpl-goods-list-item2', data));
// 删除文件
$html.find('.file-item-delete').on('click', function () {
$(this).parent().remove();
});
$goodsList2.append($html);
}
});
/** /**
* 表单验证提交 * 表单验证提交
* @type {*} * @type {*}

View File

@ -89,7 +89,89 @@
value="<?= $model['min_price'] ?>" placeholder="请输入最低消费金额" required> value="<?= $model['min_price'] ?>" placeholder="请输入最低消费金额" required>
</div> </div>
</div> </div>
<div class="am-form-group" data-x-switch>
<div class="am-form-group am-padding-top" data-x-switch>
<label class="am-u-sm-3 am-u-lg-2 am-form-label form-require">适用范围 </label>
<div class="am-u-sm-9 am-u-end">
<label class="am-radio-inline">
<input type="radio" name="coupon[apply_range]" value="10"
data-am-ucheck
data-switch-box="switch-apply_range"
data-switch-item="apply_range__10"
<?= $model['apply_range'] == 10 ? 'checked' : '' ?>>
全部商品
</label>
<label class="am-radio-inline">
<input type="radio" name="coupon[apply_range]" value="20"
data-am-ucheck
data-switch-box="switch-apply_range"
data-switch-item="apply_range__20"
<?= $model['apply_range'] == 20 ? 'checked' : '' ?>>
指定商品
</label>
<label class="am-radio-inline">
<input type="radio" name="coupon[apply_range]" value="30"
data-am-ucheck
data-switch-box="switch-apply_range"
data-switch-item="apply_range__30"
<?= $model['apply_range'] == 30 ? 'checked' : '' ?>>
排除商品
</label>
</div>
</div>
<div class="am-form-group switch-apply_range apply_range__20 <?= $model['apply_range'] == 20 ? '' : 'hide' ?>">
<label class="am-u-sm-3 am-u-lg-2 am-form-label"> </label>
<div class="am-u-sm-9 am-u-end">
<div class="widget-become-goods am-form-file am-margin-top-xs">
<button type="button" @click.stop="onSelectGoods"
class="j-selectGoods upload-file am-btn am-btn-secondary am-radius">
<i class="am-icon-cloud-upload"></i> 选择商品
</button>
<div class="widget-goods-list uploader-list am-cf">
<?php if (!$applyGoodsList->isEmpty()): foreach ($applyGoodsList as $goods): ?>
<div class="file-item">
<a href="<?= $goods['goods_image'] ?>"
title="<?= $goods['goods_name'] ?>" target="_blank">
<img src="<?= $goods['goods_image'] ?>">
</a>
<input type="hidden"
name="coupon[apply_range_config][applyGoodsIds][]"
value="<?= $goods['goods_id'] ?>">
<i class="iconfont icon-shanchu file-item-delete"
data-name="商品"></i>
</div>
<?php endforeach; endif; ?>
</div>
</div>
</div>
</div>
<div class="am-form-group switch-apply_range apply_range__30 <?= $model['apply_range'] == 30 ? '' : 'hide' ?>">
<label class="am-u-sm-3 am-u-lg-2 am-form-label"> </label>
<div class="am-u-sm-9 am-u-end">
<div class="widget-become-goods am-form-file am-margin-top-xs">
<button type="button" @click.stop="onSelectGoods"
class="j-selectGoods2 upload-file am-btn am-btn-secondary am-radius">
<i class="am-icon-cloud-upload"></i> 选择商品
</button>
<div class="widget-goods-list2 uploader-list am-cf">
<?php if (!$excludedGoodsList->isEmpty()): foreach ($excludedGoodsList as $goods): ?>
<div class="file-item">
<a href="<?= $goods['goods_image'] ?>"
title="<?= $goods['goods_name'] ?>" target="_blank">
<img src="<?= $goods['goods_image'] ?>">
</a>
<input type="hidden"
name="coupon[apply_range_config][excludedGoodsIds][]"
value="<?= $goods['goods_id'] ?>">
<i class="iconfont icon-shanchu file-item-delete"
data-name="商品"></i>
</div>
<?php endforeach; endif; ?>
</div>
</div>
</div>
</div>
<div class="am-form-group am-padding-top" data-x-switch>
<label class="am-u-sm-3 am-u-lg-2 am-form-label form-require">到期类型 </label> <label class="am-u-sm-3 am-u-lg-2 am-form-label form-require">到期类型 </label>
<div class="am-u-sm-9 am-u-end"> <div class="am-u-sm-9 am-u-end">
<label class="am-radio-inline"> <label class="am-radio-inline">
@ -159,6 +241,38 @@
</div> </div>
</div> </div>
</div> </div>
<!-- 文件库弹窗 -->
{{include file="layouts/_template/file_library" /}}
<!-- 指定的 商品列表 -->
<script id="tpl-goods-list-item" type="text/template">
{{ each $data }}
<div class="file-item">
<a href="{{ $value.image }}" title="{{ $value.goods_name }}" target="_blank">
<img src="{{ $value.image }}">
</a>
<input type="hidden" name="coupon[apply_range_config][applyGoodsIds][]" value="{{ $value.goods_id }}">
<i class="iconfont icon-shanchu file-item-delete" data-name="商品"></i>
</div>
{{ /each }}
</script>
<!-- 排除的 商品列表 -->
<script id="tpl-goods-list-item2" type="text/template">
{{ each $data }}
<div class="file-item">
<a href="{{ $value.image }}" title="{{ $value.goods_name }}" target="_blank">
<img src="{{ $value.image }}">
</a>
<input type="hidden" name="coupon[apply_range_config][excludedGoodsIds][]" value="{{ $value.goods_id }}">
<i class="iconfont icon-shanchu file-item-delete" data-name="商品"></i>
</div>
{{ /each }}
</script>
<script src="assets/common/js/vue.min.js?v=<?= $version ?>"></script>
<script src="assets/store/js/select.data.js?v=<?= $version ?>"></script>
<script> <script>
/** /**
* 时间选择 * 时间选择
@ -233,6 +347,38 @@
$mySwitchBox.hide().filter('.' + $(this).data('switch-item')).show(); $mySwitchBox.hide().filter('.' + $(this).data('switch-item')).show();
}); });
// 选择商品
var $goodsList = $('.widget-goods-list');
$('.j-selectGoods').selectData({
title: '选择商品',
uri: 'goods/lists',
dataIndex: 'goods_id',
done: function (data) {
var $html = $(template('tpl-goods-list-item', data));
// 删除文件
$html.find('.file-item-delete').on('click', function () {
$(this).parent().remove();
});
$goodsList.append($html);
}
});
// 选择商品
var $goodsList2 = $('.widget-goods-list2');
$('.j-selectGoods2').selectData({
title: '选择商品',
uri: 'goods/lists',
dataIndex: 'goods_id',
done: function (data) {
var $html = $(template('tpl-goods-list-item2', data));
// 删除文件
$html.find('.file-item-delete').on('click', function () {
$(this).parent().remove();
});
$goodsList2.append($html);
}
});
/** /**
* 表单验证提交 * 表单验证提交
* @type {*} * @type {*}

View File

@ -10,6 +10,21 @@
<link rel="icon" type="image/png" href="assets/common/i/favicon.ico"/> <link rel="icon" type="image/png" href="assets/common/i/favicon.ico"/>
<link rel="stylesheet" href="assets/store/css/login/style.css?v=<?= $version ?>"/> <link rel="stylesheet" href="assets/store/css/login/style.css?v=<?= $version ?>"/>
</head> </head>
<style>
.copyright {
display: none;
margin: -40px auto 0 auto;
padding: 2px 0;
width: 600px;
color: #fff;
background: #ccc;
background: rgba(0, 0, 0, 0.4);
}
.copyright a {
color: #fff;
}
</style>
<body class="page-login-v3"> <body class="page-login-v3">
<div class="container"> <div class="container">
<div id="wrapper" class="login-body"> <div id="wrapper" class="login-body">
@ -33,6 +48,9 @@
</form> </form>
</div> </div>
</div> </div>
<div class="copyright">
<span>Copyright ©2020 xxx版权所有 <a href="https://www.baidu.com/" target="_blank">粤ICP备18030800号-1</a></span>
</div>
</div> </div>
</body> </body>
<script src="assets/common/js/jquery.min.js"></script> <script src="assets/common/js/jquery.min.js"></script>

View File

@ -1,3 +1,3 @@
{ {
"version": "1.1.44" "version": "1.1.45"
} }

View File

@ -48,7 +48,9 @@
this.appVue = new Vue({ this.appVue = new Vue({
el: setting.el, el: setting.el,
data: { data: {
// 规格组/值
spec_attr: spec_attr, spec_attr: spec_attr,
// sku列表
spec_list: spec_list, spec_list: spec_list,
// 显示添加规格组按钮 // 显示添加规格组按钮
showAddGroupBtn: true, showAddGroupBtn: true,
@ -121,8 +123,8 @@
onSubmitAddValue: function (index) { onSubmitAddValue: function (index) {
var _this = this var _this = this
, specAttr = _this.spec_attr[index]; , specAttr = _this.spec_attr[index];
if (!specAttr.hasOwnProperty('tempValue') || specAttr.tempValue === '') { // 验证新增规格值
layer.msg('规格值不能为空'); if (!this.verifyAddValue(specAttr)) {
return false; return false;
} }
// 添加到数据库 // 添加到数据库
@ -148,53 +150,88 @@
}); });
}, },
/**
* 验证新增规格值
* @param specAttr
* @returns {boolean}
*/
verifyAddValue(specAttr) {
if (!specAttr.tempValue || specAttr.tempValue === '') {
layer.msg('规格值不能为空');
return false
}
// 验证规格值是否重复
var specValues = []
specAttr.spec_items.forEach(function (item) {
specValues.push(item.spec_value)
})
if (specValues.indexOf(specAttr.tempValue) > -1) {
layer.msg('规格值不能重复');
return false
}
return true
},
/** /**
* 构建规格组合列表 * 构建规格组合列表
*/ */
buildSkulist: function () { buildSkulist: function () {
var _this = this; var _this = this;
// 规格组合总数 (table行数) // 计算skuList总数 (table行数)
var totalRow = 1; var skuDataCount = 1;
for (var i = 0; i < _this.spec_attr.length; i++) { for (var i = 0; i < _this.spec_attr.length; i++) {
totalRow *= _this.spec_attr[i].spec_items.length; skuDataCount *= _this.spec_attr[i].spec_items.length;
} }
// 遍历tr 行 // 遍历skuList列表生成tr(行)
var specList = []; var skuList = [];
for (i = 0; i < totalRow; i++) { for (i = 0; i < skuDataCount; i++) {
var rowData = [], rowCount = 1, specSkuIdAttr = []; // skuItem的数据
// 遍历td 列 var skuItemRows = [],
rowCount = 1, specSkuIdAttr = [];
// 遍历规格属性, 生成td row(列)
for (var j = 0; j < _this.spec_attr.length; j++) { for (var j = 0; j < _this.spec_attr.length; j++) {
var skuValues = _this.spec_attr[j].spec_items; // 每组规格的值
rowCount *= skuValues.length; var specValues = _this.spec_attr[j].spec_items;
var anInterBankNum = (totalRow / rowCount)
, point = ((i / anInterBankNum) % skuValues.length); // 合并后的规格值显示的区块数量
rowCount = rowCount * specValues.length;
// 合并后的规格值的rowspan
var anInterBankNum = skuDataCount / rowCount;
var point = (i / anInterBankNum) % specValues.length;
// rowspan能被i整除 才写入skuItemRows
// i % anInterBankNum
if (0 === (i % anInterBankNum)) { if (0 === (i % anInterBankNum)) {
rowData.push({ skuItemRows.push({
rowspan: anInterBankNum, rowspan: anInterBankNum,
item_id: skuValues[point].item_id, item_id: specValues[point].item_id,
spec_value: skuValues[point].spec_value spec_value: specValues[point].spec_value
}); });
} }
specSkuIdAttr.push(skuValues[parseInt(point.toString())].item_id); const pointInt = parseInt(point.toString());
specSkuIdAttr.push(specValues[pointInt].item_id);
} }
specList.push({ skuList.push({
spec_sku_id: specSkuIdAttr.join('_'), spec_sku_id: specSkuIdAttr.join('_'),
rows: rowData, rows: skuItemRows,
form: {} form: {}
}); });
} }
// return false; // return false;
// 合并旧sku数据 // 合并旧sku数据
if (_this.spec_list.length > 0 && specList.length > 0) { if (_this.spec_list.length > 0 && skuList.length > 0) {
for (i = 0; i < specList.length; i++) { for (i = 0; i < skuList.length; i++) {
var overlap = _this.spec_list.filter(function (val) { var overlap = _this.spec_list.filter(function (val) {
return val.spec_sku_id === specList[i].spec_sku_id; return val.spec_sku_id === skuList[i].spec_sku_id;
}); });
if (overlap.length > 0) specList[i].form = overlap[0].form; if (overlap.length > 0) skuList[i].form = overlap[0].form;
} }
} }
_this.spec_list = specList; _this.spec_list = skuList;
// 注册上传sku图片事件 // 注册上传sku图片事件
_this.onSelectImagesEvent(); _this.onSelectImagesEvent();
}, },
@ -238,7 +275,7 @@
onSubmitBatchData: function () { onSubmitBatchData: function () {
var _this = this; var _this = this;
_this.spec_list.forEach(function (value) { _this.spec_list.forEach(function (value) {
for (var key in _this.batchData) { for (var key in _this.batchData) {
if (_this.batchData.hasOwnProperty(key) && _this.batchData[key]) { if (_this.batchData.hasOwnProperty(key) && _this.batchData[key]) {
_this.$set(value.form, key, _this.batchData[key]); _this.$set(value.form, key, _this.batchData[key]);
} }