初始上传

This commit is contained in:
2026-04-04 17:27:12 +08:00
parent 4d80d28eb4
commit b7e11774ee
11191 changed files with 1588469 additions and 0 deletions

View File

@@ -0,0 +1,316 @@
<?php
/**
* Niushop商城系统 - 团队十年电商经验汇集巨献!
* =========================================================
* Copy right 2019-2029 杭州牛之云科技有限公司, 保留所有权利。
* ----------------------------------------------
* 官方网址: https://www.niushop.com
* =========================================================
*/
namespace addon\cardservice\api\controller;
use addon\cardservice\model\CardGoods;
use app\api\controller\BaseApi;
use app\model\goods\Goods;
use app\model\goods\GoodsService;
use app\model\store\Store;
use app\model\storegoods\StoreGoods;
use app\model\web\Config as ConfigModel;
use think\facade\Db;
/**
* 卡项
*/
class Card extends BaseApi
{
public function __construct()
{
parent::__construct();
$this->initStoreData();
}
public function detail()
{
$sku_id = $this->params['sku_id'] ?? 0;
$goods_id = $this->params['goods_id'] ?? 0;
$goods = new CardGoods();
if (empty($sku_id) && !empty($goods_id)) {
$sku_id = $goods->getGoodsInfo([ [ 'goods_id', '=', $goods_id ] ], 'sku_id')[ 'data' ][ 'sku_id' ] ?? 0;
}
if (empty($sku_id) && empty($goods_id)) {
return $this->response($this->error('', 'REQUEST_ID'));
}
$condition = [
[ 'gs.sku_id', '=', $sku_id ],
[ 'gs.site_id', '=', $this->site_id ],
[ 'gs.is_delete', '=', 0 ],
[ 'g.goods_class', '=', $goods->getGoodsClass()[ 'id' ] ]
];
$field = 'gs.goods_id,gs.sku_id,gs.qr_id,gs.goods_name,gs.sku_name,gs.sku_spec_format,gs.price,gs.market_price,gs.discount_price,gs.promotion_type,gs.start_time
,gs.end_time,gs.stock,gs.click_num,(g.sale_num + g.virtual_sale) as sale_num,gs.collect_num,gs.sku_image,gs.sku_images
,gc.card_type,gc.card_type_name,gc.renew_price,gc.recharge_money,gc.common_num,gc.discount_goods_type,gc.discount,gc.validity_type,gc.validity_day,gc.validity_time
,gs.goods_content,gs.goods_state,gs.is_free_shipping,gs.goods_spec_format,gs.goods_attr_format,gs.introduction,gs.unit,gs.video_url
,gs.is_virtual,gs.goods_service_ids,gs.max_buy,gs.min_buy,gs.is_limit,gs.limit_type,gs.support_trade_type,g.goods_image,g.keywords,g.stock_show,g.sale_show,g.market_price_show,g.barrage_show,g.evaluate,g.sale_store,g.sale_channel';
$join = [
[ 'goods g', 'g.goods_id = gs.goods_id', 'inner' ]
];
// 如果是连锁运营模式
if ($this->store_data[ 'config' ][ 'store_business' ] == 'store') {
$join[] = [ 'store_goods_sku sgs', 'gs.goods_id = sgs.goods_id and sgs.store_id=' . $this->store_id, 'left' ];
$field .= ',IFNULL(sgs.status, 0) as store_goods_status';
$field = str_replace('gs.price', 'IFNULL(IF(g.is_unify_price = 1,gs.price,sgs.price), gs.price) as price', $field);
$field = str_replace('gs.discount_price', 'IFNULL(IF(g.is_unify_price = 1,gs.discount_price,sgs.price), gs.discount_price) as discount_price', $field);
if ($this->store_data[ 'store_info' ][ 'stock_type' ] == 'store') {
$field = str_replace('gs.stock', 'IFNULL(sgs.stock, 0) as stock', $field);
}
}
$goods_sku_detail = $goods->getGoodsSkuInfo($condition, $field, 'gs', $join)[ 'data' ];
if (empty($goods_sku_detail)) return $this->response($this->error());
if ($this->store_data[ 'config' ][ 'store_business' ] == 'store') {
// 销售渠道设置为线上销售时门店商品状态为1
if ($goods_sku_detail[ 'sale_channel' ] == 'online') {
$goods_sku_detail[ 'store_goods_status' ] = 1;
}
}
$goods_sku_detail[ 'purchased_num' ] = 0; // 该商品已购数量
$res[ 'goods_sku_detail' ] = $goods_sku_detail;
// 商品服务
$goods_service = new GoodsService();
$goods_service_list = $goods_service->getServiceList([ [ 'site_id', '=', $this->site_id ], [ 'id', 'in', $res[ 'goods_sku_detail' ][ 'goods_service_ids' ] ] ], 'service_name,desc,icon');
$res[ 'goods_sku_detail' ][ 'goods_service' ] = $goods_service_list[ 'data' ];
return $this->response($this->success($res));
}
/**
* 列表信息
*/
public function page()
{
$page = $this->params['page'] ?? 1;
$page_size = $this->params['page_size'] ?? PAGE_LIST_ROWS;
$goods_id_arr = $this->params['goods_id_arr'] ?? '';//goods_id数组
$keyword = isset($this->params[ 'keyword' ]) ? trim($this->params[ 'keyword' ]) : '';//关键词
$service_category = $this->params['service_category'] ?? 0;//分类
$is_free_shipping = $this->params['is_free_shipping'] ?? 0;//是否免邮
$order = $this->params['order'] ?? '';//排序(综合、销量、价格)
$sort = $this->params['sort'] ?? '';//升序、降序
$card_type = $this->params['card_type'] ?? '';
$condition = [];
$condition[] = [ 'gs.site_id', '=', $this->site_id ];
$condition[] = [ 'g.goods_class', '=', ( new CardGoods() )->getGoodsClass()[ 'id' ] ];
$condition[] = [ '', 'exp', Db::raw("(g.sale_channel = 'all' OR g.sale_channel = 'online')") ];
if (!empty($goods_id_arr)) {
$condition[] = [ 'gs.goods_id', 'in', $goods_id_arr ];
}
if (!empty($card_type)) {
$condition[] = [ 'gc.card_type', '=', $card_type ];
}
if (!empty($service_category)) {
$condition[] = [ 'g.service_category', 'like', '%,' . $service_category . ',%' ];
}
if (!empty($keyword)) {
$condition[] = [ 'g.goods_name|gs.sku_name|gs.keywords', 'like', '%' . $keyword . '%' ];
}
if (!empty($is_free_shipping)) {
$condition[] = [ 'gs.is_free_shipping', '=', $is_free_shipping ];
}
// 非法参数进行过滤
if ($sort != 'desc' && $sort != 'asc') {
$sort = '';
}
// 非法参数进行过滤
if ($order != '') {
if ($order != 'sale_num' && $order != 'discount_price') {
$order = 'gs.sort';
} elseif ($order == 'sale_num') {
$order = 'sale_sort';
} else {
$order = 'gs.' . $order;
}
$order_by = $order . ' ' . $sort;
} else {
$config_model = new ConfigModel();
$sort_config = $config_model->getGoodsSort($this->site_id)[ 'data' ][ 'value' ];
$order_by = 'g.sort ' . $sort_config[ 'type' ] . ',g.create_time desc';
}
$condition[] = [ 'g.goods_state', '=', 1 ];
$condition[] = [ 'g.is_delete', '=', 0 ];
$condition[] = [ '', 'exp', Db::raw("(g.sale_channel = 'all' OR g.sale_channel = 'offline')") ];
$alias = 'gs';
$field = 'gs.is_consume_discount,gs.discount_config,gs.discount_method,gs.member_price,gs.goods_id,gs.sort,gs.sku_id,gs.sku_name,gs.price,gs.market_price,gs.discount_price,gs.stock,(g.sale_num + g.virtual_sale) as sale_num,(gs.sale_num + gs.virtual_sale) as sale_sort,gs.sku_image,gs.goods_name,gs.site_id,gs.is_free_shipping,gs.introduction,gs.promotion_type,g.goods_image,g.promotion_addon,gs.is_virtual,g.goods_spec_format,g.recommend_way,gs.max_buy,gs.min_buy,gs.unit,gs.is_limit,gs.limit_type,g.label_name,g.stock_show,g.sale_show,g.market_price_show,g.barrage_show,g.sale_channel,g.sale_store,
gc.card_type,gc.card_type_name,gc.renew_price,gc.recharge_money,gc.common_num,gc.discount_goods_type,gc.discount,gc.validity_type,gc.validity_day,gc.validity_time';
$join = [
[ 'goods g', 'gs.sku_id = g.sku_id', 'inner' ],
[ 'goods_card gc', 'gc.goods_id=gs.goods_id', 'left' ],
];
// 如果是连锁运营模式
if ($this->store_data[ 'config' ][ 'store_business' ] == 'store') {
$join[] = [ 'store_goods_sku sgs', 'sgs.status = 1 and g.sku_id = sgs.sku_id and sgs.store_id=' . $this->store_id, 'right' ];
$condition[] = [ 'g.sale_store', 'like', [ '%all%', '%,' . $this->store_id . ',%' ], 'or' ];
$field = str_replace('gs.price', 'IFNULL(IF(g.is_unify_price = 1,gs.price,sgs.price), gs.price) as price', $field);
$field = str_replace('gs.discount_price', 'IFNULL(IF(g.is_unify_price = 1,gs.price,sgs.price), gs.price) as discount_price', $field);
if ($this->store_data[ 'store_info' ][ 'stock_type' ] == 'store') {
$field = str_replace('gs.stock', 'IFNULL(sgs.stock, 0) as stock', $field);
}
}
$goods = new Goods();
$list = $goods->getGoodsSkuPageList($condition, $page, $page_size, $order_by, $field, $alias, $join);
return $this->response($list);
}
public function getCardListByType()
{
$goods = new CardGoods();
$list = $goods->getCardType();
$condition = [
[ 'gs.site_id', '=', $this->site_id ],
[ 'g.goods_class', '=', $goods->getGoodsClass()[ 'id' ] ],
[ 'g.goods_state', '=', 1 ],
[ 'g.is_delete', '=', 0 ],
[ '', 'exp', Db::raw("(g.sale_channel = 'all' OR g.sale_channel = 'online')") ]
];
$field = 'gs.is_consume_discount,gs.discount_config,gs.discount_method,gs.member_price,gs.goods_id,gs.sort,gs.sku_id,gs.sku_name,gs.price,gs.market_price,gs.discount_price,gs.stock,(g.sale_num + g.virtual_sale) as sale_num,(gs.sale_num + gs.virtual_sale) as sale_sort,gs.sku_image,gs.goods_name,gs.site_id,gs.is_free_shipping,gs.introduction,gs.promotion_type,g.goods_image,g.promotion_addon,gs.is_virtual,g.goods_spec_format,g.recommend_way,gs.max_buy,gs.min_buy,gs.unit,gs.is_limit,gs.limit_type,g.label_name,g.stock_show,g.sale_show,g.market_price_show,g.barrage_show,g.sale_channel,g.sale_store,
gc.card_type,gc.card_type_name,gc.renew_price,gc.recharge_money,gc.common_num,gc.discount_goods_type,gc.discount,gc.validity_type,gc.validity_day,gc.validity_time';
$join = [
[ 'goods_sku gs', 'gs.sku_id = g.sku_id', 'inner' ],
[ 'goods_card gc', 'gc.goods_id=gs.goods_id', 'left' ],
];
// 如果是连锁运营模式
if ($this->store_data[ 'config' ][ 'store_business' ] == 'store') {
$join[] = [ 'store_goods_sku sgs', 'sgs.status = 1 and g.sku_id = sgs.sku_id and sgs.store_id=' . $this->store_id, 'right' ];
$condition[] = [ 'g.sale_store', 'like', [ '%all%', '%,' . $this->store_id . ',%' ], 'or' ];
$field = str_replace('gs.price', 'IFNULL(IF(g.is_unify_price = 1,gs.price,sgs.price), gs.price) as price', $field);
$field = str_replace('gs.discount_price', 'IFNULL(IF(g.is_unify_price = 1,gs.price,sgs.price), gs.price) as discount_price', $field);
if ($this->store_data[ 'store_info' ][ 'stock_type' ] == 'store') {
$field = str_replace('gs.stock', 'IFNULL(sgs.stock, 0) as stock', $field);
}
}
$card_list = $goods->getGoodsList($condition, $field, 'g.sort asc', 'g', $join);
foreach ($list as $k => $v) {
$list[ $k ][ 'card_list' ] = [];
foreach ($card_list[ 'data' ] as $key => $val) {
if ($val[ 'card_type' ] == $v[ 'type' ]) {
$list[ $k ][ 'card_list' ][] = $val;
}
}
if (empty($list[ $k ][ 'card_list' ])) unset($list[ $k ]);
}
return $this->response($this->success($list));
}
/**
* 获取商品关联的卡项套餐
* @return array|false|string
*/
public function getRelationCardGoods()
{
$goods_id = $this->params[ 'goods_id' ] ?? '';
$store_id = $this->params[ 'store_id' ] ?? '';
if(empty($goods_id)){
return $this->response($this->error('', 'REQUEST_ID'));
}
$condition = [
[ 'goods_id', '=', $goods_id ]
];
$goods_card_res = (new CardGoods())->getGoodSCardItem($condition,'card_goods_id','id desc','','','card_goods_id');
if($goods_card_res['code'] < 0){
return $this->response($this->error([]));
}
$card_goods_id = array_column($goods_card_res['data'],'card_goods_id');
$goods_condition = [
['gci.card_goods_id','in',$card_goods_id],
[ 'g.goods_state', '=', 1 ],
[ 'g.is_delete', '=', 0 ],
];
$join = [
[ 'goods_card gc', 'gci.card_goods_id = gc.goods_id', 'left'],
[ 'goods_sku gs', 'gci.card_goods_id = gs.goods_id', 'left' ],
[ 'goods g', 'g.goods_id = gs.goods_id', 'left' ],
[ 'goods_sku gck', 'gck.sku_id = gci.sku_id', 'left' ],
];
if($store_id){
if(addon_is_exit('store')){
$config_model = new \addon\store\model\Config();
$business_config = $config_model->getStoreBusinessConfig($this->site_id)['data']['value'];
if ($business_config['store_business'] == 'store'){
$goods_condition[] = ['', 'exp', Db::raw("g.sale_store = 'all' or FIND_IN_SET('{$store_id}', g.sale_store)")];
//连锁门店模式
$join[] = ['store_goods sg','sg.goods_id = gci.card_goods_id','left'];
$goods_condition[] = [ 'sg.store_id', '=', $store_id];
$goods_condition[] = [ 'sg.status', '=', 1];
}
}
}
$alias = 'gci';
$field = "gc.card_type,gc.card_type_name,
gci.num,gci.goods_id,gci.card_goods_id as goods_id,
gs.is_consume_discount,gs.discount_config,gs.discount_method,gs.member_price,gs.sort,gs.sku_id,gs.sku_name,gs.price,gs.market_price,gs.discount_price,gs.stock,gs.sku_image,gs.goods_name,gs.site_id,gs.is_free_shipping,gs.introduction,gs.promotion_type,g.goods_stock,g.goods_image,g.promotion_addon,gs.is_virtual,g.goods_spec_format,g.recommend_way,gs.max_buy,gs.min_buy,gs.unit,gs.is_limit,gs.limit_type,g.label_name,g.stock_show,g.sale_show,g.market_price_show,g.barrage_show,g.sale_channel,g.sale_store,
gck.sku_image as item_sku_image,gck.sku_name as item_sku_name,gck.goods_name as item_goods_name,gck.price as item_price,gck.goods_id as item_goods_id,gck.sku_id as item_sku_id";
$all_goods_arr = (new CardGoods())->getGoodSCardItem($goods_condition,$field,'gci.id desc',$alias,$join);
if($all_goods_arr['code'] < 0){
return $this->response($this->error([]));
}
$data = [];
foreach ($all_goods_arr['data'] as $k=>$v){
if(!isset($data[$v['goods_id']])){
$data[$v['goods_id']] = $v;
$data[$v['goods_id']]['goods'] = [];
}
$group = $data[$v['goods_id']];
$group['goods'][$v['item_sku_id']] = [
'goods_id' => $v['item_goods_id'],
'sku_id'=>$v['item_sku_id'],
'sku_image' => $v['item_sku_image'],
'goods_name' => $v['item_goods_name'],
'sku_name' => $v['item_sku_name'],
'price'=>$v['item_price'],
'num'=>$v['num']
];
unset($group['item_goods_id'],$group['item_sku_image'],$group['item_sku_name'],$group['item_goods_name'],$group['item_price'],$group['num']);
$data[$v['goods_id']] = $group;
}
$data = array_values($data);
foreach ($data as $k=>$v){
$data[$k]['goods'] = array_values($v['goods']);
}
$token = $this->checkToken();
if ($token[ 'code' ] >= 0) {
$goods = new Goods();
$data = $goods->getGoodsListMemberPrice($data, $this->member_id);
}
return $this->response($this->success($data));
}
}

View File

@@ -0,0 +1,144 @@
<?php
/**
* Niushop商城系统 - 团队十年电商经验汇集巨献!
* =========================================================
* Copy right 2019-2029 杭州牛之云科技有限公司, 保留所有权利。
* ----------------------------------------------
* 官方网址: https://www.niushop.com
* =========================================================
*/
namespace addon\cardservice\api\controller;
use addon\cardservice\model\MemberCard as MemberCardModel;
use app\api\controller\BaseApi;
use app\model\verify\Verify;
/**
* 会员卡项
*/
class Membercard extends BaseApi
{
public function detail()
{
$this->initStoreData();
$card_id = $this->params['card_id'] ?? 0;
if (empty($card_id)) {
return $this->response($this->error('', 'REQUEST_ID'));
}
$token = $this->checkToken();
if ($token[ 'code' ] < 0) return $this->response($token);
$model = new MemberCardModel();
$condition = [
[ 'mgc.site_id', '=', $this->site_id ],
[ 'mgc.member_id', '=', $this->member_id ],
[ 'mgc.card_id', '=', $card_id ],
[ 'g.is_delete', '=', 0 ],
];
$field = 'mgc.*, g.goods_name,g.price,g.goods_image,g.introduction,g.goods_content';
$join = [
[ 'goods g', 'mgc.goods_id = g.goods_id', 'inner' ],
];
$info = $model->getCardInfo($condition, $field, 'mgc', $join)[ 'data' ];
$condition = [
[ 'mgci.card_id', '=', $info[ 'card_id' ] ],
];
$stock_field = 'sku.stock';
$join = [
[ 'goods_sku sku', 'mgci.sku_id = sku.sku_id', 'inner' ],
[ 'verify v', 'mgci.member_verify_id = v.id', 'left' ],
];
if ($this->store_data[ 'config' ][ 'store_business' ] == 'store') {
$stock_field = 'sgs.stock';
$join[] = [ 'store_goods_sku sgs', 'sku.sku_id = sgs.sku_id and sgs.store_id = '.$this->store_id, 'left'];
}
$info[ 'card_item' ] = $model->getCartItemList($condition, 'mgci.*,sku.sku_name,sku.price,sku.sku_image,sku.sku_images,sku.goods_class_name,'.$stock_field.',
v.verify_code,v.verify_type,v.verify_type_name,v.verify_content_json,v.verifier_id,v.verifier_name,v.is_verify,v.verify_time,v.expire_time,v.verify_from,v.verify_remark,v.verify_total_count,v.verify_use_num', 'mgci.card_id asc', 'mgci', $join)[ 'data' ] ?? [];
$verify = new Verify();
foreach ($info[ 'card_item' ] as $k => $v) {
if ($v[ 'member_verify_id' ] > 0) {
$info[ 'card_item' ][ $k ][ 'verify_code_data' ] = $verify->qrcode($v[ 'verify_code' ], 'h5', 'pickup', $this->site_id, 'create')[ 'data' ] ?? [];
$info[ 'card_item' ][ $k ][ 'barcode' ] = getBarcode($v[ 'verify_code' ], 'upload/qrcode/pickup');
$info[ 'card_item' ][ $k ][ 'stock' ] = numberFormat($info[ 'card_item' ][ $k ][ 'stock' ]);
}
}
return $this->response($this->success($info));
}
/**
* 列表信息
*/
public function page()
{
$token = $this->checkToken();
if ($token[ 'code' ] < 0) return $this->response($token);
$page = $this->params['page'] ?? 1;
$page_size = $this->params['page_size'] ?? PAGE_LIST_ROWS;
$status = $this->params['status'] ?? 'all';
$condition = [];
$condition[] = [ 'mgc.site_id', '=', $this->site_id ];
$condition[] = [ 'mgc.member_id', '=', $this->member_id ];
if ($status !== 'all') {
$condition[] = [ 'mgc.status', '=', $status ];
}
$condition[] = [ 'g.is_delete', '=', 0 ];
$alias = 'mgc';
$field = 'mgc.*, g.goods_name,g.price,g.goods_image,g.introduction';
$join = [
[ 'goods g', 'mgc.goods_id = g.goods_id', 'inner' ],
];
$model = new MemberCardModel();
$list = $model->getCardPageList($condition, $field, 'mgc.create_time desc', $page, $page_size, $alias, $join);
return $this->response($list);
}
/**
* 使用记录
*/
public function records()
{
$token = $this->checkToken();
if ($token[ 'code' ] < 0) return $this->response($token);
$card_id = $this->params['card_id'] ?? 0;
$item_id = $this->params['item_id'] ?? 0;
if (empty($card_id) && empty($item_id)) {
return $this->response($this->error([], '请传入必要参数'));
}
$condition = [];
$condition[] = [ 'cr.site_id', '=', $this->site_id ];
$condition[] = [ 'ci.member_id', '=', $this->member_id ];
if (!empty($item_id)) {
$condition[] = [ 'cr.card_item_id', '=', $item_id ];
}
if (!empty($card_id)) {
$condition[] = [ 'cr.card_id', '=', $card_id ];
}
$alias = 'cr';
$prefix = config('database.connections.mysql.prefix');
$field = 'cr.*, sku.sku_name,sku.sku_image,sku.sku_images,sku.price,ci.num as item_num,
IF(cr.type = \'order\', (select order_id from `' . $prefix . 'order_goods` og where og.order_goods_id = cr.relation_id), 0) as order_id';
$join = [
[ 'member_goods_card_item ci', 'ci.item_id = cr.card_item_id', 'left' ],
[ 'goods_sku sku', 'ci.sku_id = sku.sku_id', 'left' ],
];
$model = new MemberCardModel();
$list = $model->getMemberCardRecordsList($condition, $field, 'cr.create_time desc', $alias, $join);
return $this->response($list);
}
}

View File

@@ -0,0 +1,76 @@
<?php
/**
* Index.php
* Niushop商城系统 - 团队十年电商经验汇集巨献!
* =========================================================
* Copy right 2015-2025 杭州牛之云科技有限公司, 保留所有权利。
* ----------------------------------------------
* 官方网址: https://www.niushop.com
* =========================================================
* @author : niuteam
* @date : 2022.8.8
* @version : v5.0.0.1
*/
namespace addon\cardservice\api\controller;
use addon\cardservice\model\order\MembercardOrderCreate 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'] ?? '',
];
$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'] ?? '',//订单缓存
];
$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 = [
'member_card_id' => $this->params[ 'member_card_id' ] ?? 0,//卡项id
'member_card_item' => json_decode($this->params[ 'member_card_item' ] ?? '[]', true),//商品组id
];
if (empty($data[ 'member_card_id' ]) || empty($data[ 'member_card_item' ])) return $this->response($this->error('', '缺少必填参数商品数据'));
$res = $order_create->setParam(array_merge($data, $this->getCommonParam(), $this->getDeliveryParam()))->orderPayment();
return $this->response($this->success($res));
}
}

View File

@@ -0,0 +1,306 @@
<?php
/**
* Niushop商城系统 - 团队十年电商经验汇集巨献!
* =========================================================
* Copy right 2019-2029 上海牛之云网络科技有限公司, 保留所有权利。
* ----------------------------------------------
* 官方网址: https://www.niushop.com
* 这不是一个自由软件!您只能在不用于商业目的的前提下对程序代码进行修改和使用。
* 任何企业和个人不允许对程序代码以任何形式任何目的再发布。
* =========================================================
*/
namespace addon\cardservice\api\controller;
use addon\cardservice\model\Reserve as ReserveModel;
use app\api\controller\BaseApi;
use app\model\system\UserGroup;
class Reserve extends BaseApi
{
/**
* 预约状态
* @return false|string
*/
public function status()
{
$reserve_state = ( new ReserveModel )->reserve_state;
return $this->response($this->success($reserve_state));
}
/**
* 添加预约
* @return mixed
*/
public function addReserve()
{
$token = $this->checkToken();
if ($token[ 'code' ] < 0) {
return $this->response($token);
}
$goods = json_decode($this->params[ 'goods' ], true) ?? [];
$store_id = $this->params[ 'store_id' ] ?? 0;
$date = $this->params[ 'date' ] ?? '';
$time = $this->params[ 'time' ] ?? '';
$remark = $this->params[ 'remark' ] ?? '';
$reserve_model = new ReserveModel();
$res = $reserve_model->addReserve([
'site_id' => $this->site_id,
'app_module' => $this->app_module,
'member_id' => $this->member_id,
'goods' => $goods,
'store_id' => $store_id,
'date' => $date,
'time' => $time,
'remark' => $remark,
'source' => 'member'
]);
return $this->response($res);
}
/**
* 修改预约
* @return mixed|void
*/
public function updateReserve()
{
$token = $this->checkToken();
if ($token[ 'code' ] < 0) {
return $this->response($token);
}
$goods = json_decode($this->params[ 'goods' ], true) ?? [];
$store_id = $this->params[ 'store_id' ] ?? 0;
$date = $this->params[ 'date' ] ?? '';
$time = $this->params[ 'time' ] ?? '';
$remark = $this->params[ 'remark' ] ?? '';
$reserve_id = $this->params[ 'reserve_id' ] ?? 0;
$reserve_model = new ReserveModel();
$res = $reserve_model->editReserve([
'site_id' => $this->site_id,
'app_module' => $this->app_module,
'member_id' => $this->member_id,
'goods' => $goods,
'store_id' => $store_id,
'date' => $date,
'time' => $time,
'remark' => $remark,
'reserve_id' => $reserve_id,
]);
return $this->response($res);
}
/**
* 预约列表
*/
public function lists()
{
$token = $this->checkToken();
if ($token[ 'code' ] < 0) {
return $this->response($token);
}
$page = $this->params['page'] ?? 1;
$page_size = $this->params['page_size'] ?? PAGE_LIST_ROWS;
$search_text = $this->params['search_text'] ?? '';
$reserve_state = $this->params['reserve_state'] ?? 'all';
$condition = [
[ 'noy.site_id', '=', $this->site_id ],
[ 'noy.member_id', '=', $this->member_id ],
];
if ($reserve_state != 'all') {
$condition[] = [ 'noy.reserve_state', '=', $reserve_state ];
}
if (!empty($search_text)) {
$condition[] = [ 'noy.reserve_item', 'like', '%' . $search_text . '%' ];
}
$field = 'noy.store_id, noy.member_id, noy.remark, noy.reserve_id, noy.reserve_name, noy.reserve_state_name, noy.reserve_state, noy.reserve_time, noy.reserve_item, noy.create_time, noy.source, nm.headimg, nm.nickname, nm.mobile, os.store_name';
$reserve_model = new ReserveModel();
$list = $reserve_model->getReservePageList($condition, $page, $page_size, 'noy.create_time desc', $field);
foreach ($list[ 'data' ][ 'list' ] as $k => $v) {
$list[ 'data' ][ 'list' ][ $k ][ 'item' ] = $reserve_model->getReserveItemList([
[
'oyi.reserve_id', '=', $v[ 'reserve_id' ]
]
], 'g.goods_name,sku.service_length,g.goods_id,g.sku_id,g.price,ys.username,oyi.reserve_user_id,sku.sku_image,sku.sku_images', 'reserve_item_id desc', 'oyi',
[
[ 'goods g', 'g.sku_id = oyi.reserve_goods_sku_id', 'right' ],
[ 'goods_sku sku', 'sku.sku_id = oyi.reserve_goods_sku_id', 'right' ],
[ 'user ys', 'oyi.reserve_user_id = ys.uid', 'left' ]
])[ 'data' ];
}
return $this->response($list);
}
/**
* 预约设置
* @return mixed
*/
public function getConfig()
{
$model = new ReserveModel();
$store_id = $this->params['store_id'] ?? 0;
$config = $model->getReserveConfig($this->site_id, $store_id);
return $this->response($config);
}
/**
* 取消预约
* @return array
*/
public function cancel()
{
$token = $this->checkToken();
if ($token[ 'code' ] < 0) {
return $this->response($token);
}
$reserve_id = $this->params[ 'reserve_id' ] ?? 0;
$reserve_model = new ReserveModel();
$res = $reserve_model->cancelReserve($reserve_id, $this->site_id, $this->member_id);
return $this->response($res);
}
/**
* 删除预约
* @return array
*/
public function deleteReserve()
{
$token = $this->checkToken();
if ($token[ 'code' ] < 0) {
return $this->response($token);
}
$reserve_id = $this->params[ 'reserve_id' ] ?? 0;
$reserve_model = new ReserveModel();
$res = $reserve_model->deleteReserve($reserve_id, $this->site_id, $this->member_id);
return $this->response($res);
}
/**
* 预约详情
* @return mixed|void
*/
public function detail()
{
$token = $this->checkToken();
if ($token[ 'code' ] < 0) {
return $this->response($token);
}
$reserve_id = $this->params[ 'reserve_id' ] ?? 0;
$model = new ReserveModel();
$info = $model->getReserveInfo([
[ 'oy.reserve_id', '=', $reserve_id ],
[ 'oy.site_id', '=', $this->site_id ],
[ 'oy.member_id', '=', $this->member_id ],
], 'oy.*, nm.headimg, nm.nickname, nm.mobile,os.store_name, os.longitude,os.latitude,os.province_id,os.city_id,os.district_id,os.community_id,os.address,os.full_address', 'oy', [
[ 'member nm', 'oy.member_id = nm.member_id', 'left' ],
[ 'store os', 'oy.store_id = os.store_id', 'left' ]
])[ 'data' ];
if (empty($info)) {
return $this->response($this->error('', '未获取到预约信息'));
}
$info[ 'item' ] = $model->getReserveItemList([
[
'oyi.reserve_id', '=', $reserve_id
]
], 'g.goods_name,sku.service_length,g.goods_id,g.sku_id,g.price,ys.username,oyi.reserve_user_id,sku.sku_image', 'reserve_item_id desc', 'oyi',
[
[ 'goods g', 'g.sku_id = oyi.reserve_goods_sku_id', 'right' ],
[ 'goods_sku sku', 'sku.sku_id = oyi.reserve_goods_sku_id', 'right' ],
[ ' user ys', 'oyi.reserve_user_id = ys.uid', 'left' ]
])[ 'data' ];
return $this->response($this->success($info));
}
/**
* 员工管理
* @return mixed
*/
public function servicer()
{
$token = $this->checkToken();
if ($token[ 'code' ] < 0) return $this->response($token);
$page_index = $this->params['page'] ?? 1;
$page_size = $this->params['page_size'] ?? PAGE_LIST_ROWS;
$search_text = $this->params['search_text'] ?? '';
$store_id = $this->params['store_id'] ?? '';
$condition = [
[ 'u.site_id', '=', $this->site_id ],
];
$condition[] = [ 'ug.store_id', '=', $store_id ];
if (!empty($search_text)) {
$condition[] = [ 'u.username', 'like', "%{$search_text}%" ];
}
$user_model = new UserGroup();
$result = $user_model->getUserPageList($condition, $page_index, $page_size, 'u.uid desc', 'u.username,u.status,u.uid', 'ug', [
[ 'user u', 'ug.uid=u.uid', 'left' ]
]);
return $this->response($result);
}
/**
* 查询所有员工
* @return mixed
*/
public function servicerList()
{
$token = $this->checkToken();
if ($token[ 'code' ] < 0) return $this->response($token);
$search_text = $this->params['search_text'] ?? '';
$store_id = $this->params['store_id'] ?? '';
$condition = [
[ 'u.site_id', '=', $this->site_id ],
];
$condition[] = [ 'ug.store_id', '=', $store_id ];
if (!empty($search_text)) {
$condition[] = [ 'u.username', 'like', "%{$search_text}%" ];
}
$user_model = new UserGroup();
$result = $user_model->getUserList($condition, 'u.username,u.status,u.uid,u.group_name', 'u.uid desc', 'ug', [
[ 'user u', 'ug.uid=u.uid', 'left' ]
]);
return $this->response($result);
}
/**
* 预约时间设置
* @return mixed
*/
public function getTimeConfig()
{
$token = $this->checkToken();
if ($token[ 'code' ] < 0) return $this->response($token);
$model = new ReserveModel();
$store_id = $this->params['store_id'] ?? '';
$config = $model->getReserveConfig($this->site_id, $store_id);
$time = strtotime(date('Y-m-d'));
if ($config[ 'data' ][ 'value' ][ 'interval' ] == 30) $config[ 'data' ][ 'value' ][ 'interval' ] = "0.5";
if ($config[ 'data' ][ 'value' ][ 'interval' ] == 60) $config[ 'data' ][ 'value' ][ 'interval' ] = "1";
if ($config[ 'data' ][ 'value' ][ 'interval' ] == 90) $config[ 'data' ][ 'value' ][ 'interval' ] = "1.5";
if ($config[ 'data' ][ 'value' ][ 'interval' ] == 120) $config[ 'data' ][ 'value' ][ 'interval' ] = "2";
$config[ 'data' ][ 'value' ][ 'start_time' ] = time_to_date($time + $config[ 'data' ][ 'value' ][ 'start' ], "H:i");
$config[ 'data' ][ 'value' ][ 'end_time' ] = time_to_date($time + $config[ 'data' ][ 'value' ][ 'end' ], "H:i");
return $this->response($config);
}
}

View File

@@ -0,0 +1,172 @@
<?php
/**
* Niushop商城系统 - 团队十年电商经验汇集巨献!
* =========================================================
* Copy right 2019-2029 杭州牛之云科技有限公司, 保留所有权利。
* ----------------------------------------------
* 官方网址: https://www.niushop.com
* =========================================================
*/
namespace addon\cardservice\api\controller;
use app\api\controller\BaseApi;
use app\model\goods\Goods;
use app\model\goods\GoodsService;
use app\model\web\Config as ConfigModel;
use addon\cardservice\model\ServiceGoods;
use think\facade\Db;
/**
* 服务
*/
class Service extends BaseApi
{
public function __construct()
{
parent::__construct();
$this->initStoreData();
}
public function detail()
{
$sku_id = $this->params['sku_id'] ?? 0;
$goods_id = $this->params['goods_id'] ?? 0;
$goods = new Goods();
if (empty($sku_id) && !empty($goods_id)) {
$sku_id = $goods->getGoodsInfo([ [ 'goods_id', '=', $goods_id ] ], 'sku_id')[ 'data' ][ 'sku_id' ] ?? 0;
}
if (empty($sku_id) && empty($goods_id)) {
return $this->response($this->error('', 'REQUEST_ID'));
}
$condition = [
[ 'gs.sku_id', '=', $sku_id ],
[ 'gs.site_id', '=', $this->site_id ]
];
$field = 'gs.goods_id,gs.sku_id,gs.qr_id,gs.goods_name,gs.sku_name,gs.sku_spec_format,gs.price,gs.market_price,gs.discount_price,gs.promotion_type,gs.start_time
,gs.end_time,gs.stock,gs.click_num,(g.sale_num + g.virtual_sale) as sale_num,gs.collect_num,gs.sku_image,gs.sku_images
,gs.goods_content,gs.goods_state,gs.is_free_shipping,gs.goods_spec_format,gs.goods_attr_format,gs.introduction,gs.unit,gs.video_url
,gs.is_virtual,gs.goods_service_ids,gs.max_buy,gs.min_buy,gs.is_limit,gs.limit_type,gs.support_trade_type,g.goods_image,g.keywords,g.stock_show,g.sale_show,g.market_price_show,g.barrage_show,g.evaluate,g.goods_class,g.sale_store,g.sale_channel';
$join = [
[ 'goods g', 'g.goods_id = gs.goods_id', 'inner' ]
];
// 如果是连锁运营模式
if ($this->store_data[ 'config' ][ 'store_business' ] == 'store') {
$join[] = [ 'store_goods_sku sgs', 'gs.sku_id = sgs.sku_id and sgs.store_id=' . $this->store_id, 'left' ];
$field .= ',IFNULL(sgs.status, 0) as store_goods_status';
$field = str_replace('gs.price', 'IFNULL(IF(g.is_unify_price = 1,gs.price,sgs.price), gs.price) as price', $field);
$field = str_replace('gs.discount_price', 'IFNULL(IF(g.is_unify_price = 1,gs.discount_price,sgs.price), gs.discount_price) as discount_price', $field);
if ($this->store_data[ 'store_info' ][ 'stock_type' ] == 'store') {
$field = str_replace('gs.stock', 'IFNULL(sgs.stock, 0) as stock', $field);
}
}
$goods_sku_detail = $goods->getGoodsSkuInfo($condition, $field, 'gs', $join)[ 'data' ];
if (empty($goods_sku_detail) || $goods_sku_detail[ 'goods_class' ] != ( new ServiceGoods() )->getGoodsClass()[ 'id' ]) return $this->response($this->error());
if ($this->store_data[ 'config' ][ 'store_business' ] == 'store') {
// 销售渠道设置为线上销售时门店商品状态为1
if ($goods_sku_detail[ 'sale_channel' ] == 'online') {
$goods_sku_detail[ 'store_goods_status' ] = 1;
}
}
$goods_sku_detail[ 'purchased_num' ] = 0; // 该商品已购数量
$res[ 'goods_sku_detail' ] = $goods_sku_detail;
// 商品服务
$goods_service = new GoodsService();
$goods_service_list = $goods_service->getServiceList([ [ 'site_id', '=', $this->site_id ], [ 'id', 'in', $res[ 'goods_sku_detail' ][ 'goods_service_ids' ] ] ], 'service_name,desc,icon');
$res[ 'goods_sku_detail' ][ 'goods_service' ] = $goods_service_list[ 'data' ];
return $this->response($this->success($res));
}
/**
* 列表信息
*/
public function page()
{
$page = $this->params['page'] ?? 1;
$page_size = $this->params['page_size'] ?? PAGE_LIST_ROWS;
$goods_id_arr = $this->params['goods_id_arr'] ?? '';//goods_id数组
$keyword = isset($this->params[ 'keyword' ]) ? trim($this->params[ 'keyword' ]) : '';//关键词
$service_category = $this->params['service_category'] ?? 0;//分类
$is_free_shipping = $this->params['is_free_shipping'] ?? 0;//是否免邮
$order = $this->params['order'] ?? "";//排序(综合、销量、价格)
$sort = $this->params['sort'] ?? "";//升序、降序
$condition = [];
$condition[] = [ 'gs.site_id', '=', $this->site_id ];
$condition[] = [ 'g.goods_class', '=', ( new ServiceGoods() )->getGoodsClass()[ 'id' ] ];
$condition[] = [ '', 'exp', Db::raw("(g.sale_channel = 'all' OR g.sale_channel = 'online')") ];
if (!empty($goods_id_arr)) {
$condition[] = [ 'gs.goods_id', 'in', $goods_id_arr ];
}
if (!empty($service_category)) {
$condition[] = [ 'g.service_category', 'like', '%,' . $service_category . ',%' ];
}
if (!empty($keyword)) {
$condition[] = [ 'g.goods_name|gs.sku_name|gs.keywords', 'like', '%' . $keyword . '%' ];
}
if (!empty($is_free_shipping)) {
$condition[] = [ 'gs.is_free_shipping', '=', $is_free_shipping ];
}
// 非法参数进行过滤
if ($sort != "desc" && $sort != "asc") {
$sort = "";
}
// 非法参数进行过滤
if ($order != '') {
if ($order != "sale_num" && $order != "discount_price") {
$order = 'gs.sort';
} elseif ($order == "sale_num") {
$order = 'sale_sort';
} else {
$order = 'gs.' . $order;
}
$order_by = $order . ' ' . $sort;
} else {
$config_model = new ConfigModel();
$sort_config = $config_model->getGoodsSort($this->site_id)[ 'data' ][ 'value' ];
$order_by = 'g.sort ' . $sort_config[ 'type' ] . ',g.create_time desc';
}
$condition[] = [ 'g.goods_state', '=', 1 ];
$condition[] = [ 'g.is_delete', '=', 0 ];
$alias = 'gs';
$field = 'gs.is_consume_discount,gs.discount_config,gs.discount_method,gs.member_price,gs.goods_id,gs.sort,gs.sku_id,gs.sku_name,gs.price,gs.market_price,gs.discount_price,gs.stock,(g.sale_num + g.virtual_sale) as sale_num,(gs.sale_num + gs.virtual_sale) as sale_sort,gs.sku_image,gs.goods_name,gs.site_id,gs.is_free_shipping,gs.introduction,gs.promotion_type,g.goods_image,g.promotion_addon,gs.is_virtual,g.goods_spec_format,g.recommend_way,gs.max_buy,gs.min_buy,gs.unit,gs.is_limit,gs.limit_type,g.label_name,g.stock_show,g.sale_show,g.market_price_show,g.barrage_show,g.sale_channel,g.sale_store';
$join = [
[ 'goods g', 'gs.sku_id = g.sku_id', 'inner' ]
];
// 如果是连锁运营模式
if ($this->store_data[ 'config' ][ 'store_business' ] == 'store') {
$join[] = [ 'store_goods_sku sgs', 'sgs.status = 1 and g.sku_id = sgs.sku_id and sgs.store_id=' . $this->store_id, 'right' ];
$condition[] = [ 'g.sale_store', 'like', [ '%all%', '%,' . $this->store_id . ',%' ], 'or' ];
$field = str_replace('gs.price', 'IFNULL(IF(g.is_unify_price = 1,gs.price,sgs.price), gs.price) as price', $field);
$field = str_replace('gs.discount_price', 'IFNULL(IF(g.is_unify_price = 1,gs.price,sgs.price), gs.price) as discount_price', $field);
if ($this->store_data[ 'store_info' ][ 'stock_type' ] == 'store') {
$field = str_replace('gs.stock', 'IFNULL(sgs.stock, 0) as stock', $field);
}
}
$goods = new Goods();
$list = $goods->getGoodsSkuPageList($condition, $page, $page_size, $order_by, $field, $alias, $join);
return $this->response($list);
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace addon\cardservice\api\controller;
use app\model\goods\ServiceCategory as ServiceCategoryModel;
use app\api\controller\BaseApi;
/**
* 分类
*/
class Servicescategory extends BaseApi
{
public function lists()
{
$goods_category_model = new ServiceCategoryModel();
$condition = [
[ 'site_id', '=', $this->site_id ],
[ 'is_show', '=', 0 ],//是否显示0显示 -1不显示
];
$data = $goods_category_model->getCategoryList($condition);
return $this->response($data);
}
}

View File

@@ -0,0 +1,77 @@
<?php
/**
* Niushop商城系统 - 团队十年电商经验汇集巨献!
* =========================================================
* Copy right 2019-2029 杭州牛之云科技有限公司, 保留所有权利。
* ----------------------------------------------
* 官方网址: https://www.niushop.com
* =========================================================
*/
return [
[
'name' => 'reserve_manage',
'title' => '预约',
'type' => 'page',
'url' => '',
'children' => [
[
'name' => 'reserve_index',
'title' => '查看',
'type' => 'page',
'url' => 'pages/reserve/index',
],
[
'name' => 'reserve_add',
'title' => '添加预约',
'type' => 'api',
'url' => 'cardservice/storeapi/reserve/add',
],
[
'name' => 'reserve_edit',
'title' => '修改预约',
'type' => 'api',
'url' => 'cardservice/storeapi/reserve/update',
],
[
'name' => 'reserve_confirm',
'title' => '确认预约',
'type' => 'api',
'url' => 'cardservice/storeapi/reserve/confirm',
],
[
'name' => 'reserve_complete',
'title' => '完成预约',
'type' => 'api',
'url' => 'cardservice/storeapi/reserve/complete',
],
[
'name' => 'reserve_cancel',
'title' => '取消预约',
'type' => 'api',
'url' => 'cardservice/storeapi/reserve/cancel',
],
[
'name' => 'reserve_confirm_tostore',
'title' => '确认到店',
'type' => 'api',
'url' => 'cardservice/storeapi/reserve/confirmtostore',
]
]
],
[
'name' => 'reserve_config',
'title' => '预约设置',
'type' => 'page',
'url' => 'pages/reserve/config',
'parent' => 'config',
'children' => [
[
'name' => 'set_reserve_config',
'title' => '配置',
'type' => 'api',
'url' => 'cardservice/storeapi/reserve/setconfig',
],
]
],
];

View File

@@ -0,0 +1,116 @@
<?php
/**
* Niushop商城系统 - 团队十年电商经验汇集巨献!
* =========================================================
* Copy right 2019-2029 杭州牛之云科技有限公司, 保留所有权利。
* ----------------------------------------------
* 官方网址: https://www.niushop.com
* =========================================================
*/
return [
// 自定义模板页面类型,格式:[ 'title' => '页面类型名称', 'name' => '页面标识', 'path' => '页面路径', 'value' => '页面数据json格式' ]
'template' => [],
// 后台自定义组件——装修
'util' => [],
// 自定义页面路径
'link' => [
[
'name' => 'CARDS_SERVICE',
'title' => '项目管理',
'parent' => '',
'wap_url' => '',
'web_url' => '',
'sort' => 2,
'child_list' => [
[
'name' => 'CARDS_SERVICE_LINK',
'title' => '服务项目',
'wap_url' => '',
'web_url' => '',
'sort' => 1,
'child_list' => [
[
'name' => 'CARDS_SERVICE_BASIC',
'title' => '基础链接',
'wap_url' => '',
'web_url' => '',
'sort' => 1,
'child_list' => [
[
'name' => 'CARDS_ERVICE_SERVICE',
'title' => '项目专区',
'wap_url' => '/pages_promotion/cardservice/service_goods/service_list',
'web_url' => '',
],
[
'name' => 'CARDS_ERVICE_CARD',
'title' => '卡项专区',
'wap_url' => '/pages_promotion/cardservice/card/list',
'web_url' => '',
],
[
'name' => 'CARDS_ERVICE_RESERVE',
'title' => '项目预约',
'wap_url' => '/pages_promotion/cardservice/service_goods/reserve_list',
'web_url' => '',
],
]
],
[
'name' => 'CARDS_SERVICE_MEMBER',
'title' => '会员链接',
'wap_url' => '',
'web_url' => '',
'sort' => 2,
'child_list' => [
[
'name' => 'MY_RESERVE',
'title' => '我的预约',
'wap_url' => '/pages_promotion/cardservice/service_goods/my_reserve_list',
'web_url' => '',
'child_list' => []
],
[
'name' => 'MY_CARD',
'title' => '我的卡包',
'wap_url' => '/pages_promotion/cardservice/card/my_card',
'web_url' => '',
'child_list' => []
],
]
]
]
],
[
'name' => 'CARDS_SERVICE_CATEGORY_LINK',
'title' => '项目分类',
'wap_url' => '/pages_promotion/cardservice/service_goods/service_list',
'web_url' => '',
'sort' => 2,
]
]
]
],
// 自定义图标库
'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' => [
//展示活动
'ShowPromotion' => [
'addon\cardservice\event\ShowPromotion',
],
'GoodsClass' => [
'addon\cardservice\event\ServiceGoodsClass',
'addon\cardservice\event\CardGoodsClass'
],
'Verify' => [
'addon\cardservice\event\CardGoodsVerify',//卡项商品核销
],
'VerifyType' => [
'addon\cardservice\event\VerifyType',
],
'CronMemberCardExpire' => [
'addon\cardservice\event\CronMemberCardExpire'
],
// 订单营销活动类型
'OrderPromotionType' => [
'addon\cardservice\event\OrderPromotionType',
],
// 活动专区——秒杀页面配置
'PromotionZoneConfig' => [
'addon\cardservice\event\CardServiceZoneConfig',
],
'MemberDetail' => [
'addon\cardservice\event\MemberDetail',
],
//退款完成
'OrderRefundFinish' => [
'addon\cardservice\event\OrderRefundFinish',
],
//订单关闭
'OrderClose' => [
'addon\cardservice\event\OrderClose',
],
'DeleteGoodsCheck' => [
'addon\cardservice\event\DeleteGoodsCheck',
],
],
'subscribe' => [
],
];

View File

@@ -0,0 +1,20 @@
<?php
/**
* Niushop商城系统 - 团队十年电商经验汇集巨献!
* =========================================================
* Copy right 2019-2029 杭州牛之云科技有限公司, 保留所有权利。
* ----------------------------------------------
* 官方网址: https://www.niushop.com
* =========================================================
*/
return [
'name' => 'cardservice',
'title' => '卡项与服务商品',
'description' => '创建卡项与服务商品',
'type' => 'tool', //插件类型 system :系统插件(自动安装), promotion:扩展营销插件 tool:工具插件
'status' => 1,
'author' => '',
'version' => '5.5.3',
'version_no' => '553250709001',
'content' => '',
];

View File

@@ -0,0 +1,164 @@
<?php
// +----------------------------------------------------------------------
// | 店铺端菜单设置
// +----------------------------------------------------------------------
return [
[
'name' => 'SERVICE_GOODS_ADD',
'title' => '发布项目商品',
'parent' => 'GOODS_LIST',
'url' => 'cardservice://shop/service/addgoods',
'sort' => 13,
'is_show' => 0,
'type' => 'button',
],
[
'name' => 'SERVICE_GOODS_EDIT',
'title' => '编辑项目商品',
'parent' => 'GOODS_LIST',
'url' => 'cardservice://shop/service/editgoods',
'sort' => 14,
'is_show' => 0,
'type' => 'button',
],
[
'name' => 'CARD_GOODS_ADD',
'title' => '发布卡项商品',
'parent' => 'GOODS_LIST',
'url' => 'cardservice://shop/card/addgoods',
'sort' => 13,
'is_show' => 0,
'type' => 'button',
],
[
'name' => 'CARD_GOODS_EDIT',
'title' => '编辑卡项商品',
'parent' => 'GOODS_LIST',
'url' => 'cardservice://shop/card/editgoods',
'sort' => 14,
'is_show' => 0,
'type' => 'button',
],
[
'name' => 'SERVICE_CATEGORY',
'title' => '项目分类',
'parent' => 'GOODS_MANAGE',
'url' => 'cardservice://shop/servicecategory/lists',
'is_show' => 1,
'is_control' => 1,
'is_icon' => 0,
'sort' => 3,
'picture' => 'app/shop/view/public/img/icon_new/category_new.png',
'picture_selected' => 'app/shop/view/public/img/icon_new/category_select.png',
'child_list' => [
[
'name' => 'SERVICE_CATEGORY_ADD',
'title' => '项目分类添加',
'url' => 'cardservice://shop/servicecategory/addcategory',
'is_show' => 0,
'type' => 'button',
],
[
'name' => 'SERVICE_CATEGORY_EDIT',
'title' => '项目分类编辑',
'url' => 'cardservice://shop/servicecategory/editcategory',
'is_show' => 0,
'type' => 'button',
],
[
'name' => 'SERVICE_CATEGORY_DELETE',
'title' => '项目分类删除',
'url' => 'cardservice://shop/servicecategory/deletecategory',
'is_show' => 0,
'type' => 'button',
],
[
'name' => 'SERVICE_CATEGORY_MODIFY_SORT',
'title' => '项目分类排序',
'url' => 'cardservice://shop/servicecategory/modifySort',
'is_show' => 0,
'type' => 'button',
],
]
],
[
'name' => 'SERVICE_YUYUE',
'title' => '预约',
'url' => 'cardservice://shop/reserve/index',
'parent' => 'ORDER_ROOT',
'is_show' => 1,
'is_control' => 1,
'is_icon' => 0,
'picture' => 'iconyuyueguanli',
'picture_select' => '',
'sort' => 3,
'child_list' => [
[
'name' => 'YUYUE_MAMAGE',
'title' => '预约管理',
'url' => 'cardservice://shop/reserve/index',
'is_show' => 1,
'is_control' => 1,
'is_icon' => 0,
'picture' => 'iconyuyueguanli',
'picture_select' => '',
'sort' => 1,
'child_list' => [
[
'name' => 'YUYUE_DETAIL',
'title' => '预约详情',
'url' => 'cardservice://shop/reserve/detail',
'is_show' => 0,
'type' => 'button',
],
[
'name' => 'YUYUE_CONFIRM',
'title' => '确认预约',
'url' => 'cardservice://shop/reserve/confirm',
'is_show' => 0,
'type' => 'button',
],
[
'name' => 'YUYUE_CANCEL',
'title' => '取消预约',
'url' => 'cardservice://shop/reserve/cancel',
'is_show' => 0,
'type' => 'button',
],
[
'name' => 'YUYUE_TO_STORE',
'title' => '确认到店',
'url' => 'cardservice://shop/reserve/confirmtostore',
'is_show' => 0,
'type' => 'button',
]
]
],
]
],
/*[
'name' => 'GOODS_CARD',
'title' => '卡项',
'parent' => 'GOODS_LIST',
'url' => 'cardservice://shop/card/goodscard',
'sort' => 15,
'is_show' => 0
],
[
'name' => 'MEMBER_GOODS_CARD',
'title' => '会员卡项',
'parent' => 'MEMBER_INDEX',
'url' => 'cardservice://shop/card/membergoodscard',
'sort' => 14,
'is_show' => 0
],
[
'name' => 'MEMBER_CARD_GOODS_DETAIL',
'title' => '卡项详情',
'parent' => 'GOODS_LIST',
'url' => 'cardservice://shop/card/detail',
'sort' => 16,
'is_show' => 0
],*/
];

View File

@@ -0,0 +1,38 @@
<?php
/**
* Niushop商城系统 - 团队十年电商经验汇集巨献!
* =========================================================
* Copy right 2019-2029 杭州牛之云科技有限公司, 保留所有权利。
* ----------------------------------------------
* 官方网址: https://www.niushop.com
* 这不是一个自由软件!您只能在不用于商业目的的前提下对程序代码进行修改和使用。
* 任何企业和个人不允许对程序代码以任何形式任何目的再发布。
* =========================================================
*/
namespace addon\cardservice\event;
use addon\cardservice\model\CardGoods;
/**
*
*/
class CardGoodsClass
{
/**
* 活动展示
* @return array
*/
public function handle()
{
return [
'goods_class' => (new CardGoods())->getGoodsClass()['id'],
'goods_class_name' => (new CardGoods())->getGoodsClass()['name'],
'is_virtual' => 1,
'add_url' => 'cardservice://shop/card/addGoods',
'edit_url' => 'cardservice://shop/card/editGoods'
];
}
}

View File

@@ -0,0 +1,30 @@
<?php
/**
* Niushop商城系统 - 团队十年电商经验汇集巨献!
* =========================================================
* Copy right 2019-2029 杭州牛之云科技有限公司, 保留所有权利。
* ----------------------------------------------
* 官方网址: https://www.niushop.com
* =========================================================
*/
namespace addon\cardservice\event;
use addon\cardservice\model\MemberCard;
/**
* 卡密商品核销
*/
class CardGoodsVerify
{
/**
* 执行安装
*/
public function handle($data)
{
if ($data[ 'verify_type' ] == 'cardgoods') {
return ( new MemberCard() )->verify($data);
}
}
}

View File

@@ -0,0 +1,37 @@
<?php
/**
* Niushop商城系统 - 团队十年电商经验汇集巨献!
* =========================================================
* Copy right 2019-2029 杭州牛之云科技有限公司, 保留所有权利。
* ----------------------------------------------
* 官方网址: https://www.niushop.com
* =========================================================
*/
namespace addon\cardservice\event;
/**
* 活动专区——卡项专区页面配置
*/
class CardServiceZoneConfig
{
public function handle($params)
{
if (empty($params) || $params[ 'name' ] == 'cardservice') {
$data = [
'name' => 'cardservice', // 标识
'title' => '卡项', // 名称
'url' => 'shop/adv/lists?keyword=NS_CARD', // 自定义跳转链接
'preview' => 'addon/cardservice/shop/view/public/img/zone_preview.png', // 预览图
// 页面配置
'value' => [
'bg_color' => '#F9FBFF'
],
];
return $data;
}
}
}

View File

@@ -0,0 +1,32 @@
<?php
/**
* Niushop商城系统 - 团队十年电商经验汇集巨献!
* =========================================================
* Copy right 2019-2029 杭州牛之云科技有限公司, 保留所有权利。
* ----------------------------------------------
* 官方网址: https://www.niushop.com
* 这不是一个自由软件!您只能在不用于商业目的的前提下对程序代码进行修改和使用。
* 任何企业和个人不允许对程序代码以任何形式任何目的再发布。
* =========================================================
*/
namespace addon\cardservice\event;
use addon\cardservice\model\MemberCard;
/**
*
*/
class CronMemberCardExpire
{
/**
* 活动展示
* @return array
*/
public function handle($param)
{
return ( new MemberCard() )->memberOncecardInvalid([['card_id', '=', $param[ 'relate_id' ]]], 'expired');
}
}

View File

@@ -0,0 +1,64 @@
<?php
/**
* Niushop商城系统 - 团队十年电商经验汇集巨献!
* =========================================================
* Copy right 2019-2029 杭州牛之云科技有限公司, 保留所有权利。
* ----------------------------------------------
* 官方网址: https://www.niushop.com
* 这不是一个自由软件!您只能在不用于商业目的的前提下对程序代码进行修改和使用。
* 任何企业和个人不允许对程序代码以任何形式任何目的再发布。
* =========================================================
*/
namespace addon\cardservice\event;
use addon\cardservice\model\MemberCard as MemberCardModel;
use app\model\order\Order as OrderModel;
class DeleteGoodsCheck
{
public function handle($param)
{
if(in_array($param['field'], ['goods_id', 'sku_id'])){
$cannot_delete_goods_list = array_merge($this->getInUseGoodsList($param), $this->getToBuyGoodsList($param));
$cannot_delete_ids = array_unique(array_column($cannot_delete_goods_list, $param['field']));
return [
'reason' => '存在待付款或未兑换的卡包商品',
'cannot_delete_ids' => $cannot_delete_ids,
];
}
}
protected function getInUseGoodsList($param)
{
$alias = 'mgc';
$join = [
['member_goods_card_item mgci', 'mgc.card_id = mgci.card_id', 'inner'],
];
$field = 'mgci.'.$param['field'];
$condition = [
['mgc.status', '=', MemberCardModel::STATUS_NORMAL],
['mgci.'.$param['field'], 'in', $param['ids']],
];
$cannot_delete_goods_list = model('member_goods_card')->getList($condition, $field, '', $alias, $join);
return $cannot_delete_goods_list;
}
protected function getToBuyGoodsList($param)
{
$alias = 'o';
$join = [
['order_goods og', 'og.order_id = o.order_id', 'inner'],
['goods_card_item gci', 'gci.card_goods_id = og.goods_id', 'inner'],
];
$condition = [
['gci.'.$param['field'], 'in', $param['ids']],
['o.order_status', '=', OrderModel::ORDER_CREATE],
];
$field = 'gci.'.$param['field'];
$cannot_delete_goods_list = model('order')->getList($condition, $field, '', $alias, $join);
return $cannot_delete_goods_list;
}
}

View File

@@ -0,0 +1,26 @@
<?php
/**
* Niushop商城系统 - 团队十年电商经验汇集巨献!
* =========================================================
* Copy right 2019-2029 杭州牛之云科技有限公司, 保留所有权利。
* ----------------------------------------------
* 官方网址: https://www.niushop.com
* =========================================================
*/
namespace addon\cardservice\event;
/**
* 应用安装
*/
class Install
{
/**
* 执行安装
*/
public function handle()
{
return success();
}
}

View File

@@ -0,0 +1,31 @@
<?php
/**
* Niushop商城系统 - 团队十年电商经验汇集巨献!
* =========================================================
* Copy right 2019-2029 杭州牛之云科技有限公司, 保留所有权利。
* ----------------------------------------------
* 官方网址: https://www.niushop.com
* =========================================================
*/
namespace addon\cardservice\event;
use app\Controller;
/**
* Class MemberDetail
* @package addon\cardservice\event
*/
class MemberDetail extends Controller
{
public function handle($params)
{
if ($params[ 'type' ] == 'member_goods_card') {
$this->assign('member_id', $params['member_id']);
$template = dirname(realpath(__DIR__)) . '/shop/view/card/member_goods_card.html';
return $this->fetch($template);
}
}
}

View File

@@ -0,0 +1,40 @@
<?php
/**
* Niushop商城系统 - 团队十年电商经验汇集巨献!
* =========================================================
* Copy right 2019-2029 杭州牛之云科技有限公司, 保留所有权利。
* ----------------------------------------------
* 官方网址: https://www.niushop.com
* 这不是一个自由软件!您只能在不用于商业目的的前提下对程序代码进行修改和使用。
* 任何企业和个人不允许对程序代码以任何形式任何目的再发布。
* =========================================================
*/
namespace addon\cardservice\event;
use addon\cardservice\model\MemberCard;
use app\model\order\OrderCommon;
/**
* 订单关闭后抵扣的卡项退还
*/
class OrderClose
{
public function handle($param)
{
$order_info = $param;
$order_common = new OrderCommon();
$order_goods_list = $order_common->getOrderGoodsList([['order_id', '=', $order_info['order_id']]], 'order_goods_id,card_item_id')['data'];
$card_refund_data = [];
foreach($order_goods_list as $order_goods){
if($order_info['pay_status'] == 0 && !empty($order_goods['card_item_id'])){
$card_refund_data[] = ['type' => 'order', 'relation_id' => $order_goods['order_goods_id']];
}
}
if(!empty($card_refund_data)){
$member_card_model = new MemberCard();
return $member_card_model->memberOncecardItemRefund($card_refund_data);
}
}
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* Niushop商城系统 - 团队十年电商经验汇集巨献!
* =========================================================
* Copy right 2019-2029 杭州牛之云科技有限公司, 保留所有权利。
* ----------------------------------------------
* 官方网址: https://www.niushop.com
* =========================================================
*/
namespace addon\cardservice\event;
/**
* 订单营销活动类型
*/
class OrderPromotionType
{
/**
* 订单营销活动类型
* @return array
*/
public function handle()
{
return [ "name" => "卡项提货", "type" => "cardservice" ];
}
}

View File

@@ -0,0 +1,40 @@
<?php
/**
* Niushop商城系统 - 团队十年电商经验汇集巨献!
* =========================================================
* Copy right 2019-2029 杭州牛之云科技有限公司, 保留所有权利。
* ----------------------------------------------
* 官方网址: https://www.niushop.com
* 这不是一个自由软件!您只能在不用于商业目的的前提下对程序代码进行修改和使用。
* 任何企业和个人不允许对程序代码以任何形式任何目的再发布。
* =========================================================
*/
namespace addon\cardservice\event;
use addon\cardservice\model\MemberCard;
/**
*
*/
class OrderRefundFinish
{
/**
* 活动展示
* @return array
*/
public function handle($param)
{
$order_id = $param['order_info']['order_id'] ?? 0;
$order_goods_id = $param['order_goods_info']['order_goods_id'] ?? 0;
$member_card_model = new MemberCard();
//两种业务
//1、如果是卡项商品订单 退款结束后用户的卡包要作废
//2、如果是卡包中的商品使用订单则使用记录删除恢复未使用状态
$member_card_model->memberOncecardInvalid([['order_id', '=', $order_id]], 'refunded');
$member_card_model->memberOncecardItemRefund([['type' => 'order', 'relation_id' => $order_goods_id]]);
return $member_card_model->success();
}
}

View File

@@ -0,0 +1,36 @@
<?php
/**
* Niushop商城系统 - 团队十年电商经验汇集巨献!
* =========================================================
* Copy right 2019-2029 杭州牛之云科技有限公司, 保留所有权利。
* ----------------------------------------------
* 官方网址: https://www.niushop.com
* 这不是一个自由软件!您只能在不用于商业目的的前提下对程序代码进行修改和使用。
* 任何企业和个人不允许对程序代码以任何形式任何目的再发布。
* =========================================================
*/
namespace addon\cardservice\event;
use addon\cardservice\model\ServiceGoods;
/**
*/
class ServiceGoodsClass
{
/**
* 活动展示
* @return array
*/
public function handle()
{
return [
'goods_class' => (new ServiceGoods())->getGoodsClass()['id'],
'goods_class_name' => (new ServiceGoods())->getGoodsClass()['name'],
'is_virtual' => 1,
'add_url' => 'cardservice://shop/service/addGoods',
'edit_url' => 'cardservice://shop/service/editGoods'
];
}
}

View File

@@ -0,0 +1,48 @@
<?php
/**
* Niushop商城系统 - 团队十年电商经验汇集巨献!
* =========================================================
* Copy right 2019-2029 杭州牛之云科技有限公司, 保留所有权利。
* ----------------------------------------------
* 官方网址: https://www.niushop.com
* 这不是一个自由软件!您只能在不用于商业目的的前提下对程序代码进行修改和使用。
* 任何企业和个人不允许对程序代码以任何形式任何目的再发布。
* =========================================================
*/
namespace addon\cardservice\event;
/**
* 活动展示
*/
class ShowPromotion
{
/**
* 活动展示
* @return array
*/
public function handle()
{
$data = [
'shop' => [
[
//插件名称
'name' => 'cardservice',
//店铺端展示分类 shop:营销活动 member:互动营销
'show_type' => 'tool',
//展示主题
'title' => '卡项与服务商品',
//展示介绍
'description' => '卡项与服务商品',
//展示图标
'icon' => 'addon/cardservice/icon.png',
//跳转链接
'url' => 'cardservice://shop/service/addgoods',
]
]
];
return $data;
}
}

View File

@@ -0,0 +1,26 @@
<?php
/**
* Niushop商城系统 - 团队十年电商经验汇集巨献!
* =========================================================
* Copy right 2019-2029 杭州牛之云科技有限公司, 保留所有权利。
* ----------------------------------------------
* 官方网址: https://www.niushop.com
* =========================================================
*/
namespace addon\cardservice\event;
/**
* 应用卸载
*/
class UnInstall
{
/**
* 执行卸载
*/
public function handle()
{
return error("系统插件不能删除");
}
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* Niushop商城系统 - 团队十年电商经验汇集巨献!
* =========================================================
* Copy right 2019-2029 杭州牛之云科技有限公司, 保留所有权利。
* ----------------------------------------------
* 官方网址: https://www.niushop.com
* =========================================================
*/
namespace addon\cardservice\event;
class VerifyType
{
/**
* 执行卸载
*/
public function handle()
{
return ['cardgoods' => [ 'name' => '卡项商品' ] ];
}
}

BIN
addon/cardservice/icon.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 982 B

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,616 @@
<?php
/**
* Niushop商城系统 - 团队十年电商经验汇集巨献!
* =========================================================
* Copy right 2019-2029 上海牛之云网络科技有限公司, 保留所有权利。
* ----------------------------------------------
* 官方网址: https://www.niushop.com
* 这不是一个自由软件!您只能在不用于商业目的的前提下对程序代码进行修改和使用。
* 任何企业和个人不允许对程序代码以任何形式任何目的再发布。
* =========================================================
*/
namespace addon\cardservice\model;
use app\model\BaseModel;
use app\model\system\Cron;
use app\model\verify\Verify;
use think\facade\Db;
use think\facade\Log;
class MemberCard extends BaseModel
{
//卡包状态
const STATUS_NORMAL = 1;
const STATUS_INVALID = 0;
/**
* 创建会员卡项
* @param $param
* @return array
*/
public function create($param)
{
$store_id = $param['store_id'] ?? 0;
$goods_info = model('goods')->getInfo([
['goods_id', '=', $param['goods_id']],
['goods_state', '=', 1],
['is_delete', '=', 0],
], 'goods_name,goods_class,sku_id');
if (empty($goods_info)) {
return $this->error('', '未获取到商品信息');
}
$verify_model = new Verify();
$end_time = 0;
$card_info = model('goods_card')->getInfo([['goods_id', '=', $param['goods_id']]], 'site_id, goods_id, card_id, card_type, card_type_name, renew_price, recharge_money, common_num, discount_goods_type, discount, validity_type, validity_day, validity_time');
if (empty($card_info)) {
return $this->error('', '未获取到卡项信息');
}
$card_item = model('goods_card_item')->getList([['ngci.card_goods_id', '=', $param['goods_id']]], 'ngci.goods_id, ngci.sku_id, ngci.num, ngci.discount, ngci.card_goods_id, ngci.id, ngs.goods_class, ngs.sku_name, ngs.sku_image, ngs.price, ngs.is_virtual', '', 'ngci', [['goods_sku ngs', 'ngci.sku_id = ngs.sku_id', 'inner']]);
switch ($card_info['validity_type']) {
case 1:
$end_time = strtotime('+' . $card_info['validity_day'] . ' day');
break;
case 2:
$end_time = $card_info['validity_time'];
if ($end_time < time()) {
return $this->error('', '卡项已超出有效期');
}
break;
}
model('member_goods_card')->startTrans();
try {
$delivery_method = 'verify';
$create_num = $param['num'] ?? 1;
for ($i = 1; $i <= $create_num; $i++) {
$card_id = model('member_goods_card')->add([
'site_id' => $param['site_id'] ?? 0,
'card_code' => $this->createNo(),
'member_id' => $param['member_id'],
'goods_id' => $param['goods_id'],
'create_time' => time(),
'end_time' => $end_time,
'order_id' => $param['order_id'] ?? 0,
'card_type' => $card_info['card_type'],
'total_num' => $card_info['common_num'],
'delivery_method' => $delivery_method,
'store_id' => $store_id,
'goods_name' => $goods_info['goods_name']
]);
$total_num = 0;
foreach ($card_item as $item) {
if (isset($card_info) && $card_info['card_type'] == 'commoncard') {
$item['num'] = $card_info['common_num'];
$total_num = $item['num'];
} else {
$total_num += $item['num'];
}
$item_data = [
'site_id' => $param['site_id'] ?? 0,
'card_id' => $card_id,
'member_id' => $param['member_id'],
'goods_id' => $item['goods_id'],
'sku_id' => $item['sku_id'],
'num' => $item['num'],
'end_time' => $end_time,
'card_type' => isset($card_info) ? $card_info['card_type'] : '',
'goods_class' => $item['goods_class'],
'member_verify_id' => 0,
'store_id' => $store_id
];
$item_array = [
[
'img' => $item['sku_image'],
'name' => $item['sku_name'],
'price' => $item['price'],
'num' => $item['num'],
'remark_array' => []
]
];
$verify_content_json = $verify_model->getVerifyJson($item_array, []);
//创建核销码
$verify_res = $verify_model->addVerify("cardgoods", $item_data['site_id'], "", $verify_content_json, $end_time, $item_data['num'], 0, $param['member_id']);
if ($verify_res['code'] != 0) {
model('member_goods_card')->rollback();
return $this->error([], '核销码创建失败');
}
$item_data['member_verify_id'] = $verify_res['data']['verify_id'];
model('member_goods_card_item')->add($item_data);
}
model('member_goods_card')->update(['total_num' => $total_num, 'delivery_method' => $delivery_method], [['card_id', '=', $card_id]]);
if ($end_time > 0) (new Cron())->addCron(1, 0, "会员卡项过期失效", "CronMemberCardExpire", $end_time, $card_id);
}
model('member_goods_card')->commit();
return $this->success($card_id);
} catch (\Exception $e) {
model('member_goods_card')->rollback();
return $this->error([], '会员卡项创建失败' . $e->getMessage());
}
}
/**
* 生成订单编号
* @param array $site_id
*/
public function createNo()
{
$time_str = date('YmdHi');
$card_no = $time_str . (string)rand(11111, 99999);
return $card_no;
}
/**
* 获取会员卡项信息
* @param array $condition
* @param string $field
* @param string $alias
* @param array $join
* @return array
*/
public function getCardInfo($condition = [], $field = '*', $alias = '', $join = [])
{
$data = model('member_goods_card')->getInfo($condition, $field, $alias, $join);
return $this->success($data);
}
/**
* 获取会员卡项数量
* @param array $condition
* @param string $field
* @param string $alias
* @param array $join
* @return array
*/
public function getCardCount($condition = [], $field = '*', $alias = 'a', $join = null)
{
$data = model('member_goods_card')->getCount($condition, $field, $alias, $join);
return $this->success($data);
}
/**
* 获取会员卡项分页列表
* @param array $condition
* @param bool $field
* @param string $order
* @param int $page
* @param int $list_rows
* @param string $alias
* @param array $join
* @return array
*/
public function getCardPageList($condition = [], $field = true, $order = '', $page = 1, $list_rows = PAGE_LIST_ROWS, $alias = 'a', $join = [])
{
$data = model('member_goods_card')->pageList($condition, $field, $order, $page, $list_rows, $alias, $join);
return $this->success($data);
}
/**
* 获取会员卡项项列表
* @param array $condition
* @param bool $field
* @param string $order
* @param string $alias
* @param array $join
* @return array
*/
public function getCartItemList($condition = [], $field = true, $order = '', $alias = 'a', $join = [])
{
$data = model('member_goods_card_item')->getList($condition, $field, $order, $alias, $join);
return $this->success($data);
}
/**
* 获取会员卡项项分页列表
* @param array $condition
* @param bool $field
* @param string $order
* @param int $page
* @param int $list_rows
* @param string $alias
* @param array $join
* @return array
*/
public function getCartItemPageList($condition = [], $field = true, $order = '', $page = 1, $list_rows = PAGE_LIST_ROWS, $alias = 'a', $join = [])
{
$data = model('member_goods_card_item')->pageList($condition, $field, $order, $page, $list_rows, $alias, $join);
return $this->success($data);
}
/**
* 获取会员卡项项详情
*/
public function getCartItemInfo($condition = [], $field = '*', $alias = '', $join = [])
{
$data = model('member_goods_card_item')->getInfo($condition, $field, $alias, $join);
return $this->success($data);
}
/**
* 卡项核销
* @param $param
*/
public function verify($param)
{
$card_item_info = model('member_goods_card_item')->getInfo([['member_verify_id', '=', $param['verify_id']]], 'item_id');
if (empty($card_item_info)) return $this->error('', '未获取到卡项信息');
return $this->cardUse([
'item_id' => $card_item_info['item_id'],
'num' => 1,
'type' => 'verify',
'relation_id' => $param['verify_id'],
'store_id' => $param['store_id'] ?? 0
]);
}
/**
* 卡项使用
* array 两种参数形式 @param
* array('item_id' => $card_item_id,'num' => $num,'type' => $type,'relation_id' => $order_goods_id);
* array(('item_id' => $card_item_id,'num' => $num,'type' => $type,'relation_id' => $order_goods_id));
*/
public function cardUse($param)
{
$temp_item_id = $param['item_id'] ?? 0;
if ($temp_item_id > 0) {
$item_list = [$param];
} else {
$item_list = $param;
}
model('member_goods_card_item')->startTrans();
try {
$out_relation_ids = [];
foreach ($item_list as $item_v) {
$item_id = $item_v['item_id'];
$item_num = $item_v['num'] ?? 1;
$item_type = $item_v['type'];
$item_relation_id = $item_v['relation_id'];
$item_store_id = $item_v['store_id'] ?? 0;
$card_item_info = model('member_goods_card_item')->getInfo([['mci.item_id', '=', $item_id]],
'mci.site_id,mci.card_id,mci.num,mci.use_num,mci.item_id,mci.goods_id,mci.sku_id,mci.end_time,mci.member_verify_id,mc.card_type,mc.total_num,mc.total_use_num,mc.delivery_method', 'mci',
[
['member_goods_card mc', 'mc.card_id = mci.card_id', 'left']
]);
if (empty($card_item_info)) {
model('member_goods_card_item')->rollback();
return $this->error('', '未获取到卡项信息');
}
if ($card_item_info['end_time'] > 0 && $card_item_info['end_time'] < time()) {
model('member_goods_card_item')->rollback();
return $this->error('', '已超出有效期');
}
if ($card_item_info['card_type'] != 'timecard' && ($card_item_info['num'] - $card_item_info['use_num'] - $item_num) < 0) {
model('member_goods_card_item')->rollback();
return $this->error('', '卡项可用次数不足');
}
// 如果是通用卡项
if ($card_item_info['card_type'] == 'commoncard') {
model('member_goods_card_item')->setInc([['card_id', '=', $card_item_info['card_id']]], 'use_num', $item_num);
// 同步核销码使用次数
if ($item_type == 'order') {
$verify_ids = model('member_goods_card_item')->getColumn([['card_id', '=', $card_item_info['card_id']]], 'member_verify_id');
} else {
$verify_ids = model('member_goods_card_item')->getColumn([['card_id', '=', $card_item_info['card_id']], ['item_id', '<>', $card_item_info['item_id']]], 'member_verify_id');
}
if (!empty($verify_ids)) {
model('verify')->setInc([['id', 'in', $verify_ids]], 'verify_use_num', $item_num);
model('verify')->update(['is_verify' => Verify::STATUS_IS_VERIFY, 'verify_time' => time()], [
['id', 'in', $verify_ids],
['verify_total_count', '>', 0],
['', 'exp', Db::raw('verify_use_num >= verify_total_count')]
]);
}
} else {
model('member_goods_card_item')->setInc([['item_id', '=', $card_item_info['item_id']]], 'use_num', $item_num);
// 同步核销码使用次数
if ($item_type == 'order') {
model('verify')->setInc([['id', '=', $card_item_info['member_verify_id']]], 'verify_use_num', $item_num);
model('verify')->update(['is_verify' => Verify::STATUS_IS_VERIFY, 'verify_time' => time()], [
['id', '=', $card_item_info['member_verify_id']],
['verify_total_count', '>', 0],
['', 'exp', Db::raw('verify_use_num >= verify_total_count')]
]);
}
}
model('member_goods_card')->setInc([['card_id', '=', $card_item_info['card_id']]], 'total_use_num', $item_num);
// 如果卡项次数已使用完
if ($card_item_info['card_type'] != 'timecard' && ($card_item_info['total_num'] - $card_item_info['total_use_num'] - $item_num) == 0) {
model('member_goods_card')->update(['status' => 0], [['card_id', '=', $card_item_info['card_id']]]);
(new Cron())->deleteCron([['event', '=', 'CronMemberCardExpire'], ['relate_id', '=', $card_item_info['card_id']]]);
}
// 添加使用记录
model('member_goods_card_records')->add([
'card_id' => $card_item_info['card_id'],
'site_id' => $card_item_info['site_id'],
'card_item_id' => $card_item_info['item_id'],
'type' => $item_type,
'relation_id' => $item_relation_id,
'create_time' => time(),
'store_id' => $item_store_id,
'num' => $item_num
]);
}
model('member_goods_card_item')->commit();
return $this->success(['out_relation_ids' => $out_relation_ids]);
} catch (\Exception $e) {
model('member_goods_card_item')->rollback();
Log::write('卡项使用错误,错误原因:' . $e->getMessage() . $e->getFile() . $e->getLine() . '请求参数:' . json_encode($param));
return $this->error('', '卡项使用失败');
}
}
/**
* 获取会员卡项使用记录列表
* @param array $condition
* @param bool $field
* @param string $order
* @param string $alias
* @param array $join
* @return array
*/
public function getMemberCardRecordsList($condition = [], $field = true, $order = '', $alias = 'a', $join = [])
{
$data = model('member_goods_card_records')->getList($condition, $field, $order, $alias, $join);
return $this->success($data);
}
public function getMemberCardRecordsPageList($condition = [], $field = true, $order = '', $page = 1, $list_rows = PAGE_LIST_ROWS, $alias = 'a', $join = [])
{
$data = model('member_goods_card_records')->pageList($condition, $field, $order, $page, $list_rows, $alias, $join);
return $this->success($data);
}
/**
* 查询当前会员是否可以使用卡项
* @param $params
* @param bool $is_buy
* @return array
*/
public function getMemberCardUse($params)
{
if (!empty($params['card_item_id'])) {
$card_condition = [
['moi.site_id', '=', $params['site_id']],
['moi.member_id', '=', $params['member_id']],
['', 'exp', Db::raw('(mo.end_time = 0 or mo.end_time > ' . time() . ') and mo.status = ' . self::STATUS_NORMAL)],
['moi.item_id', '=', $params['card_item_id']]
];
$card_field = 'moi.end_time, moi.card_id, moi.item_id';
$card_join = [
['member_goods_card mo', 'mo.card_id = moi.card_id', 'left'],
['goods_card gc', 'gc.goods_id = mo.goods_id', 'left']
];
$card_item_info = $this->getCartItemInfo($card_condition, $card_field, 'moi', $card_join)['data'];
return $this->success($card_item_info);
}
return $this->error();
}
/**
* 查询会员可用的卡项(关联商品查询)
* @param $params
*/
public function getMemberUseCardList($params)
{
$site_id = $params['site_id'] ?? 0;
$member_id = $params['member_id'] ?? 0;
$goods_id = $params['goods_id'] ?? 0;
$sku_id = $params['sku_id'];
$alias = 'moi';
$condition = array(
[$alias . '.member_id', '=', $member_id],
['mo.status', '=', 1],
);
if ($goods_id > 0) {
$condition[] = [$alias . '.goods_id', '=', $goods_id];
}
if ($sku_id > 0) {
$condition[] = [$alias . '.sku_id', '=', $sku_id];
}
$field = 'moi.*,mo.status';
$join = [
['member_goods_card mo', 'mo.card_id = moi.card_id', 'left'],
['goods_card gc', 'gc.goods_id = mo.goods_id', 'left']
];
$list = model('member_goods_card_item')->getList($condition, $field, '', $alias, $join);
return $this->success($list);
}
/**
* 检验商品是否可被卡项抵扣
* @param $params
*/
public function getUseCardNum($params)
{
$member_id = $params['member_id'];
$sku_id = $params['sku_id'];
$item_id = $params['item_id'];
$card_item_condition = array(
['member_id', '=', $member_id],
['sku_id', '=', $sku_id],
['item_id', '=', $item_id]
);
$card_item_info = model('member_goods_card_item')->getInfo($card_item_condition);
if (empty($card_item_info))
return $this->error([], '没有可用的卡项');
$card_type = $card_item_info['card_type'];
$card_id = $card_item_info['card_id'];
$card_condition = array(
['card_id', '=', $card_id]
);
$card_info = model('member_goods_card')->getInfo($card_condition);
if (empty($card_item_info))
return $this->error([], '没有可用的卡项');
$return_params = array(
'card_item_info' => $card_item_info,
'card_info' => $card_info
);
$status = $card_info['status'];
if ($status != self::STATUS_NORMAL)
return $this->error($return_params, '卡包已失效');
$total_num = $card_info['total_num'];
$total_use_num = $card_info['total_use_num'];
switch ($card_type) {
case 'oncecard'://限次
$item_num = $card_item_info['num'];
$item_use_num = $card_item_info['use_num'];
$surplus_num = $item_num - $item_use_num;
break;
case 'timecard'://限时
$surplus_num = 0;
break;
case 'commoncard'://通用共享次数
$surplus_num = $total_num - $total_use_num;
break;
case ''://通用共享次数
$item_num = $card_item_info['num'];
$item_use_num = $card_item_info['use_num'];
$surplus_num = $item_num - $item_use_num;
break;
}
$return_params['card_num'] = $surplus_num;
return $this->success($return_params);
}
/**
* 关闭会员卡项
* @param $card_id
*/
public function memberOncecardClose($card_ids)
{
model('member_goods_card')->startTrans();
try {
// 关闭会员卡项
model('member_goods_card')->update(['status' => 0], [['card_id', 'in', $card_ids]]);
(new Cron())->deleteCron([['event', '=', 'CronMemberCardExpire'], ['relate_id', 'in', $card_ids]]);
// 关闭核销码
$verify_ids = model('member_goods_card_item')->getColumn([['card_id', 'in', $card_ids]], 'member_verify_id');
model('member_verify')->update(['state' => -1], [['id', 'in', $verify_ids]]);
(new Cron())->deleteCron([['event', 'in', ['CronMemberVerifyClose', 'CronVerifyClosePreRemind']], ['relate_id', '=', $verify_ids]]);
model('member_goods_card')->commit();
return $this->success();
} catch (\Exception $e) {
model('member_goods_card')->rollback();
return $this->error();
}
}
/**
* 卡包失效
* @param $condition
* @param $invalid_type
* @return array
*/
public function memberOncecardInvalid($condition, $invalid_type)
{
$config = [
'expired' => ['reason' => '已过期', 'verify_status' => Verify::STATUS_EXPIRED],
'refunded' => ['reason' => '已退款', 'verify_status' => Verify::STATUS_REFUNDED],
];
if (!in_array($invalid_type, ['expired', 'refunded'])) return $this->error(null, '非法的失效类型');
$member_card_list = model('member_goods_card')->getList($condition);
if (!empty($member_card_list)) {
foreach ($member_card_list as $member_card_info) {
model('member_goods_card')->update([
'status' => self::STATUS_INVALID,
'invalid_reason' => $config[$invalid_type]['reason'],
], [['card_id', '=', $member_card_info['card_id']]]);
$invalid_verify_ids = model('member_goods_card_item')->getColumn([
['card_id', '=', $member_card_info['card_id']],
['num', '<>', Db::raw('use_num')],
], 'member_verify_id');
if (!empty($invalid_verify_ids)) {
$verify_model = new Verify();
$verify_model->editVerify(['is_verify' => $config[$invalid_type]['verify_status']], [['id', 'in', $invalid_verify_ids]]);
}
}
}
return $this->success();
}
/**
* 卡项使用退还
* @param $item_list [ ['type' => '', 'relation_id' => ''] ]
*/
public function memberOncecardItemRefund($item_list)
{
model('member_goods_card_item')->startTrans();
try {
$records_ids = [];
foreach ($item_list as $item) {
$item_info = model('member_goods_card_records')->getInfo([
['mgcr.type', '=', $item['type']],
['mgcr.relation_id', '=', $item['relation_id']],
], 'mgcr.id,mgcr.card_id,mgcr.num,mgci.item_id,mgci.card_type,mgci.member_verify_id,mgc.status,mgc.end_time', 'mgcr', [
['member_goods_card_item mgci', 'mgci.item_id = mgcr.card_item_id', 'inner'],
['member_goods_card mgc', 'mgci.card_id = mgc.card_id', 'inner']
]);
if (!empty($item_info)) {
if ($item_info['card_type'] == 'commoncard') {
model('member_goods_card_item')->setDec([['card_id', '=', $item_info['card_id']]], 'use_num', $item_info['num']);
$verify_ids = model('member_goods_card_item')->getColumn([['card_id', '=', $item_info['card_id']]], 'member_verify_id');
if (!empty($verify_ids)) {
model('verify')->setDec([['id', 'in', $verify_ids]], 'verify_use_num', $item_info['num']);
if ($item_info['status'] == 0 && ($item_info['end_time'] == 0 || $item_info['end_time'] > time())) {
model('verify')->update(['is_verify' => Verify::STATUS_NOT_VERIFY, 'verify_time' => 0], [['id', 'in', $verify_ids]]);
}
}
} else {
model('member_goods_card_item')->setDec([['item_id', '=', $item_info['item_id']]], 'use_num', $item_info['num']);
model('verify')->setDec([['id', '=', $item_info['member_verify_id']]], 'verify_use_num', $item_info['num']);
// 恢复核销码状态
if ($item_info['status'] == 0 && ($item_info['end_time'] == 0 || $item_info['end_time'] > time())) {
model('verify')->update(['is_verify' => Verify::STATUS_NOT_VERIFY, 'verify_time' => 0], [['id', '=', $item_info['member_verify_id']]]);
}
}
model('member_goods_card')->setDec([['card_id', '=', $item_info['card_id']]], 'total_use_num', $item_info['num']);
$records_ids[] = $item_info['id'];
// 判断卡是否为不可用状态
if ($item_info['status'] == 0 && ($item_info['end_time'] == 0 || $item_info['end_time'] > time())) {
model('member_goods_card')->update([
'status' => 1
], [['card_id', '=', $item_info['card_id']]]);
}
}
}
// 删除使用记录
if (!empty($records_ids)) model('member_goods_card_records')->delete([['id', 'in', $records_ids]]);
model('member_goods_card_item')->commit();
return $this->success();
} catch (\Exception $e) {
model('member_goods_card_item')->rollback();
Log::write('卡项退还错误,错误原因:' . $e->getMessage() . $e->getFile() . $e->getLine());
return $this->error('', '卡项退还失败');
}
}
/**
* 查询卡项活动信息
* @param $condition
*/
public function getCardSelect($condition)
{
$info = model('goods_card')->getInfo($condition);
return $this->success($info);
}
}

View File

@@ -0,0 +1,653 @@
<?php
/**
* Niushop商城系统 - 团队十年电商经验汇集巨献!
* =========================================================
* Copy right 2019-2029 上海牛之云网络科技有限公司, 保留所有权利。
* ----------------------------------------------
* 官方网址: https://www.niushop.com
* =========================================================
*/
namespace addon\cardservice\model;
use app\model\BaseModel;
use app\model\member\Member;
/**
* 预约
*/
class Reserve extends BaseModel
{
/**
* 预约状态
* @var array
*/
public $reserve_state = [
'wait_confirm' => [
'state' => 'wait_confirm',
'name' => '待确认',
'color' => '#8558FA'
],
'wait_to_store' => [
'state' => 'wait_to_store',
'name' => '待到店',
'color' => '#1475FA'
],
'arrived_store' => [
'state' => 'arrived_store',
'name' => '已到店',
'color' => '#FA5B14'
],
'completed' => [
'state' => 'completed',
'name' => '已完成',
'color' => '#10C610'
],
'cancelled' => [
'state' => 'cancelled',
'name' => '已取消',
'color' => '#CCCCCC'
]
];
/**
* 设置预约配置
* @param $data
* @param $site_id
* @param $app_module
* @return array
*/
public function setReserveConfig($data, $site_id, $store_id)
{
$save_data[ 'config_desc' ] = '预约配置';
$save_data[ 'is_use' ] = 1;
$save_data[ 'value' ] = json_encode($data);
$config_model = model('reserve_config');
$condition = [
[ 'site_id', '=', $site_id ],
[ 'store_id', '=', $store_id ],
[ 'config_key', '=', 'RESERVE_CONFIG' ]
];
$info = $config_model->getInfo($condition, 'id');
if (empty($info)) {
$save_data[ 'create_time' ] = time();
$save_data[ 'site_id' ] = $site_id;
$save_data[ 'store_id' ] = $store_id;
$save_data[ 'config_key' ] = 'RESERVE_CONFIG';
$res = $config_model->add($save_data);
} else {
$save_data[ 'modify_time' ] = time();
$res = $config_model->update($save_data, $condition);
}
return $this->success($res);
}
/**
* 获取预约配置
* @param $site_id
* @param $app_module
* @return array
*/
public function getReserveConfig($site_id, $store_id)
{
$key = 'RESERVE_CONFIG';
$info = model('reserve_config')->getInfo([ [ 'site_id', '=', $site_id ], [ 'store_id', '=', $store_id ], [ 'config_key', '=', $key ] ], 'site_id, store_id, config_key, value, config_desc, is_use, create_time, modify_time');
if (!empty($info)) {
$info[ 'value' ] = json_decode($info[ 'value' ], true);
} else {
$info = [
'site_id' => $site_id,
'store_id' => $store_id,
'config_key' => $key,
'value' => [
'week' => '[1,2,3,4,5]',
'start' => 32400,
'end' => 79200,
'interval' => 30,
'advance' => 1,
'max' => 10
],
'config_desc' => '',
'is_use' => 0,
'create_time' => 0,
'modify_time' => 0
];
}
$info[ 'value' ][ 'week' ] = json_decode($info[ 'value' ][ 'week' ], true);
return $this->success($info);
}
/**
* 添加预约
* @param array $param
* @return array
*/
public function addReserve(array $param)
{
$member_info = ( new Member() )->getMemberInfo([ [ 'member_id', '=', $param[ 'member_id' ] ], [ 'site_id', '=', $param[ 'site_id' ] ] ])[ 'data' ];
if (empty($member_info)) return $this->error('', '未查找到会员信息');
$store_info = model('store')->getInfo([ [ 'store_id', '=', $param[ 'store_id' ] ], [ 'site_id', '=', $param[ 'site_id' ] ] ], 'status');
if (empty($store_info)) return $this->error('', '未查找到门店信息');
$check_res = $this->checkReserve($param);
if ($check_res[ 'code' ] != 0) return $check_res;
model('reserve')->startTrans();
try {
$data = [
'site_id' => $param[ 'site_id' ],
'member_id' => $param[ 'member_id' ],
'reserve_name' => $member_info[ 'nickname' ],
'reserve_time' => strtotime("{$param['date']} {$param['time']}"),
'reserve_state' => $this->reserve_state[ 'wait_confirm' ][ 'state' ],
'reserve_state_name' => $this->reserve_state[ 'wait_confirm' ][ 'name' ],
'remark' => $param[ 'remark' ],
'store_id' => $param[ 'store_id' ],
'source' => $param[ 'source' ] ?? 'member',
'create_time' => time()
];
$reserve_id = model('reserve')->add($data);
$reserve_item = [];
$reserve_item_data = [];
foreach ($param[ 'goods' ] as $item) {
$goods_info = model('goods')->getInfo([ [ 'site_id', '=', $param[ 'site_id' ] ], [ 'sku_id', '=', $item[ 'sku_id' ] ], [ 'goods_state', '=', 1 ], [ 'is_delete', '=', 0 ] ], 'goods_name');
if (empty($goods_info)) {
model('reserve')->rollback();
return $this->error('', '未查找到所预约的服务');
}
// 同一时间,一个服务人员只能预约一个客户
if (!empty($item[ 'uid' ])) {
$count = model('reserve_item')->getCount([
[ 'site_id', '=', $param[ 'site_id' ] ],
[ 'member_id', '=', $param[ 'member_id' ] ],
[ 'reserve_user_id', '=', $item[ 'uid' ] ],
[ 'reserve_goods_sku_id', '=', $item[ 'sku_id' ] ],
[ 'store_id', '=', $param[ 'store_id' ] ],
[ 'reserve_time', '=', strtotime("{$param['date']} {$param['time']}") ]
]);
if ($count) {
model('reserve')->rollback();
return $this->error('', '请勿重复预约服务');
}
$count = model('reserve_item')->getCount([
[ 'site_id', '=', $param[ 'site_id' ] ],
[ 'reserve_user_id', '=', $item[ 'uid' ] ],
[ 'store_id', '=', $param[ 'store_id' ] ],
[ 'reserve_time', '=', strtotime("{$param['date']} {$param['time']}") ]
]);
if ($count) {
model('reserve')->rollback();
return $this->error('', '同一时间,一个服务人员只能预约一个客户');
}
}
$reserve_item[] = $goods_info['goods_name'];
$reserve_item_data[] = [
'reserve_id' => $reserve_id,
'site_id' => $param['site_id'],
'member_id' => $param['member_id'],
'reserve_name' => $member_info['nickname'],
'reserve_time' => strtotime("{$param['date']} {$param['time']}"),
'remark' => $param['remark'],
'reserve_user_id' => $item['uid'] ?? 0,
'reserve_goods_sku_id' => $item['sku_id'],
'reserve_state' => $this->reserve_state['wait_confirm']['state'],
'store_id' => $param['store_id'],
];
}
model('reserve_item')->addList($reserve_item_data);
model('reserve')->update([ 'reserve_item' => implode($reserve_item) ], [ [ 'reserve_id', '=', $reserve_id ] ]);
model('reserve')->commit();
return $this->success($reserve_id);
} catch (\Exception $e) {
model('reserve')->rollback();
return $this->error('添加失败' . $e->getMessage() . $e->getFile() . $e->getLine());
}
}
/**
* 编辑预约
* @param $param
* @return array
*/
public function editReserve($param)
{
$condition = [
[ 'reserve_id', '=', $param[ 'reserve_id' ] ],
[ 'site_id', '=', $param[ 'site_id' ] ]
];
if (isset($param[ 'member_id' ])) $condition[] = [ 'member_id', '=', $param[ 'member_id' ] ];
$info = model('reserve')->getInfo($condition);
if (empty($info)) return $this->error('', '未获取到预约信息');
if (!in_array($info[ 'reserve_state' ], [ $this->reserve_state[ 'wait_confirm' ][ 'state' ], $this->reserve_state[ 'wait_to_store' ][ 'state' ] ]))
return $this->error('', '该预约已不可更改');
$check_res = $this->checkReserve($param);
if ($check_res[ 'code' ] != 0) return $check_res;
model('reserve')->startTrans();
try {
$res = model('reserve')->update([
'reserve_time' => strtotime("{$param['date']} {$param['time']}"),
'remark' => $param[ 'remark' ],
], $condition);
// 删除原预约项
$reserve_item = [];
$reserve_item_data = [];
foreach ($param[ 'goods' ] as $item) {
$goods_info = model('goods')->getInfo([ [ 'site_id', '=', $param[ 'site_id' ] ], [ 'sku_id', '=', $item[ 'sku_id' ] ], [ 'goods_state', '=', 1 ], [ 'is_delete', '=', 0 ] ], 'goods_name');
if (empty($goods_info)) {
model('reserve')->rollback();
return $this->error('', '未查找到所预约的服务');
}
// 同一时间,一个服务人员只能预约一个客户
if (!empty($item[ 'uid' ])) {
$count = model('reserve_item')->getCount([
[ 'site_id', '=', $param[ 'site_id' ] ],
[ 'member_id', '=', $info[ 'member_id' ] ],
[ 'reserve_user_id', '=', $item[ 'uid' ] ],
[ 'reserve_goods_sku_id', '=', $item[ 'sku_id' ] ],
[ 'store_id', '=', $info[ 'store_id' ] ],
[ 'reserve_time', '=', strtotime("{$param['date']} {$param['time']}") ]
]);
if ($count) {
model('reserve')->rollback();
return $this->error('', '请勿重复预约服务');
}
$count = model('reserve_item')->getCount([
[ 'site_id', '=', $param[ 'site_id' ] ],
[ 'reserve_user_id', '=', $item[ 'uid' ] ],
[ 'store_id', '=', $info[ 'store_id' ] ],
[ 'reserve_time', '=', strtotime("{$param['date']} {$param['time']}") ]
]);
if ($count) {
model('reserve')->rollback();
return $this->error('', '同一时间,一个服务人员只能预约一个客户');
}
}
$reserve_item[] = $goods_info['goods_name'];
$reserve_item_data[] = [
'reserve_id' => $param['reserve_id'],
'site_id' => $param['site_id'],
'member_id' => $info['member_id'],
'reserve_name' => $info['reserve_name'],
'reserve_time' => strtotime("{$param['date']} {$param['time']}"),
'remark' => $param['remark'],
'reserve_user_id' => $item['uid'] ?? 0,
'reserve_goods_sku_id' => $item['sku_id'],
'reserve_state' => $info['reserve_state'],
'store_id' => $info['store_id']
];
}
model('reserve_item')->delete([ [ 'reserve_id', '=', $param[ 'reserve_id' ] ] ]);
model('reserve_item')->addList($reserve_item_data);
model('reserve')->update([ 'reserve_item' => implode($reserve_item) ], [ [ 'reserve_id', '=', $param[ 'reserve_id' ] ] ]);
model('reserve')->commit();
return $this->success($res);
} catch (\Exception $e) {
model('reserve')->rollback();
return $this->error();
}
}
/**
* 校验预约是否可添加
* @param $param
* @return array
*/
public function checkReserve($param)
{
$site_id = $param[ 'site_id' ] ?? 0;
$date = $param[ 'date' ] ?? 0;
$time = $param[ 'time' ] ?? 0;
$store_id = $param[ 'store_id' ] ?? 0;
$reserve_id = $param[ 'reserve_id' ] ?? 0;
$app_module = $param[ 'app_module' ] ?? '';
$config = $this->getReserveConfig($site_id, $store_id)[ 'data' ][ 'value' ];
// 预约时间
$reserve_time = strtotime($date . ' ' . $time);
if (( $reserve_time - time() ) < ( $config[ 'advance' ] * 3600 )) return $this->error('', '需提前' . $config[ 'advance' ] . '小时预约');
$week = date('w', strtotime($date));
if (!in_array($week, $config[ 'week' ])) return $this->error('', '所选时间不在可预约时间内');
$time = strtotime(date('Y-m-d') . ' ' . $time) - strtotime(date('Y-m-d'));
if ($time < $config[ 'start' ] || $time > $config[ 'end' ]) return $this->error('', '所选时间不在可预约时间内');
$max_condition = [
[ 'site_id', '=', $site_id ],
[ 'reserve_time', '=', $reserve_time ],
[ 'reserve_state', '<>', $this->reserve_state[ 'cancelled' ][ 'state' ] ]
];
if ($reserve_id > 0) $max_condition[] = [ 'reserve_id', '<>', $reserve_id ];
$max = model('reserve')->getCount($max_condition);
if ($max > $config[ 'max' ]) return $this->error('', '所选时段内预约人数已达上限');
return $this->success();
}
/**
* 获取预约列表
* @param array $condition
* @param string $field
* @param string $order
* @param null $limit
* @return array
*/
public function getReserveList($condition = [], $field = '*', $order = '', $limit = null)
{
$list = model('reserve')->getList($condition, $field, $order, '', '', '', $limit);
return $this->success($list);
}
/**
* 获取预约数量
* @param array $condition
* @return array
*/
public function getReserveCount($condition = [], $field = '*')
{
$list = model('reserve')->getCount($condition, $field);
return $this->success($list);
}
/**
* 获取预约分页列表
* @param array $condition
* @param int $page
* @param int $page_size
* @param string $order
* @param string $field
* @return array
*/
public function getReservePageList($condition = [], $page = 1, $page_size = PAGE_LIST_ROWS, $order = '', $field = '*')
{
$join = [
[
'member nm',
'noy.member_id = nm.member_id',
'left'
],
[
'store os',
'noy.store_id = os.store_id',
'left'
]
];
$list = model('reserve')->pageList($condition, $field, $order, $page, $page_size, 'noy', $join);
return $this->success($list);
}
/**
* 获取预约项列表
* @param $condition
* @param $field
* @param $order
* @param string $alias
* @param null $join
* @return array
*/
public function getReserveItemList($condition, $field, $order, $alias = 'oyi', $join = null)
{
$list = model('reserve_item')->getList($condition, $field, $order, $alias, $join);
return $this->success($list);
}
/**
* 获取预约详情
* @param $condition
* @param string $field
* @param string $alias
* @param null $join
* @return array
*/
public function getReserveInfo($condition, $field = '*', $alias = 'a', $join = null)
{
$res = model('reserve')->getInfo($condition, $field, $alias, $join);
return $this->success($res);
}
/**
* 确认预约
* @param $reserve_id
* @param $site_id
* @return array
*/
public function confirmReserve($reserve_id, $site_id)
{
$condition = [
[ 'reserve_id', '=', $reserve_id ],
[ 'site_id', '=', $site_id ],
[ 'reserve_state', '=', $this->reserve_state[ 'wait_confirm' ][ 'state' ] ]
];
model('reserve')->update([
'reserve_state' => $this->reserve_state[ 'wait_to_store' ][ 'state' ],
'reserve_state_name' => $this->reserve_state[ 'wait_to_store' ][ 'name' ]
], $condition);
model('reserve_item')->update([ 'reserve_state' => $this->reserve_state[ 'wait_to_store' ][ 'state' ] ], $condition);
return $this->success();
}
/**
* 取消预约
* @param $reserve_id
* @param $site_id
* @return array
*/
public function cancelReserve($reserve_id, $site_id, $member_id = 0)
{
model('reserve')->startTrans();
try {
$condition = [
[ 'reserve_id', '=', $reserve_id ],
[ 'site_id', '=', $site_id ],
[ 'reserve_state', 'in', [ $this->reserve_state[ 'wait_confirm' ][ 'state' ], $this->reserve_state[ 'wait_to_store' ][ 'state' ] ] ]
];
if ($member_id) $condition[] = [ 'member_id', '=', $member_id ];
model('reserve')->update([
'reserve_state' => $this->reserve_state[ 'cancelled' ][ 'state' ],
'reserve_state_name' => $this->reserve_state[ 'cancelled' ][ 'name' ],
'cancel_time' => time()
], $condition);
model('reserve_item')->update([ 'reserve_state' => $this->reserve_state[ 'cancelled' ][ 'state' ] ], $condition);
model('reserve')->commit();
return $this->success();
} catch (\Exception $e) {
model('reserve')->rollback();
return $this->error();
}
}
/**
* 删除预约
* @param $reserve_id
* @param $site_id
* @return array
*/
public function deleteReserve($reserve_id, $site_id, $member_id = 0)
{
$condition = [
[ 'reserve_id', '=', $reserve_id ],
[ 'site_id', '=', $site_id ],
[ 'reserve_state', '=', $this->reserve_state[ 'cancelled' ][ 'state' ] ]
];
if ($member_id) $condition[] = [ 'member_id', '=', $member_id ];
model('reserve')->delete($condition);
model('reserve_item')->delete($condition);
return $this->success();
}
/**
* 确认到店
* @param $reserve_id
* @param $site_id
* @return array
*/
public function confirmToStore($reserve_id, $site_id)
{
$condition = [
[ 'reserve_id', '=', $reserve_id ],
[ 'site_id', '=', $site_id ],
[ 'reserve_state', '=', $this->reserve_state[ 'wait_to_store' ][ 'state' ] ]
];
model('reserve')->update([
'reserve_state' => $this->reserve_state[ 'arrived_store' ][ 'state' ],
'reserve_state_name' => $this->reserve_state[ 'arrived_store' ][ 'name' ],
'to_store_time' => time()
], $condition);
model('reserve_item')->update([ 'reserve_state' => $this->reserve_state[ 'arrived_store' ][ 'state' ] ], $condition);
return $this->success();
}
/**
* 确认完成
* @param $reserve_id
* @param $site_id
* @return array
*/
public function confirmComplete($reserve_id, $site_id)
{
$condition = [
[ 'reserve_id', '=', $reserve_id ],
[ 'site_id', '=', $site_id ],
[ 'reserve_state', '=', $this->reserve_state[ 'arrived_store' ][ 'state' ] ]
];
model('reserve')->update([
'reserve_state' => $this->reserve_state[ 'completed' ][ 'state' ],
'reserve_state_name' => $this->reserve_state[ 'completed' ][ 'name' ],
'complete_time' => time()
], $condition);
model('reserve_item')->update([ 'reserve_state' => $this->reserve_state[ 'completed' ][ 'state' ] ], $condition);
return $this->success();
}
/**
* 获取周看板日期数据
* @param $week_offset
* @return array
*/
public function getWeekDays($week_offset)
{
$first_day = mktime(0, 0, 0, date("m"), date("d") - date("w") + 1, date("Y"));
$first_day = strtotime($week_offset . ' week', $first_day);
$week_names = [ '周日', '周一', '周二', '周三', '周四', '周五', '周六' ];
$data = [];
for ($i = 0; $i < 7; $i++) {
$time = strtotime("+ {$i} day", $first_day);
$data[] = [
'start_time' => $time,
'end_time' => strtotime(date('Y-m-d 23:59:59', $time)),
'year' => date('Y', $time),
'month' => date('m', $time),
'day' => date('d', $time),
'week' => date('w', $time),
'week_name' => $week_names[ date('w', $time) ],
'is_curr_day' => date('Y-m-d', $time) == date('Y-m-d') ? 1 : 0
];
}
return $this->success($data);
}
/**
* 获取月看板日期数据
* @param $year
* @param $month
* @return array
*/
public function getMonthDays($year, $month)
{
$month_start_day = mktime(0, 0, 0, $month, 1, $year);
//获取日历的第一天
$first_day = mktime(0, 0, 0, $month, 1 - ( date("w", $month_start_day) - 1 ), $year);
//获取日历的最后一天
if ($month < 12) {
$next_month = $month + 1;
$next_month_year = $year;
} else {
$next_month = 1;
$next_month_year = $year + 1;
}
$month_end_day = mktime(0, 0, 0, $next_month, 0, $next_month_year);
$end_day = $month_end_day + ( 7 - date('w', $month_end_day) ) * 3600 * 24;
$data = [];
for ($timestamp = $first_day; $timestamp <= $end_day; $timestamp += 3600 * 24) {
$data_item = [
'start_time' => $timestamp,
'end_time' => $timestamp + 3600 * 24 - 1,
'year' => date('Y', $timestamp),
'month' => date('m', $timestamp),
'day' => date('d', $timestamp),
'week' => date('w', $timestamp),
];
$data_item[ 'is_curr_month' ] = ( $month == $data_item[ 'month' ] );
$data[] = $data_item;
}
return $this->success($data);
}
/**
* 通过给定时间获取预约数据
* @param $param
*/
public function getReserveDataByDays($param)
{
$days_data = $param[ 'days_data' ] ?? [];
$query_num = $param[ 'query_num' ] ?? 10;
$site_id = $param[ 'site_id' ] ?? 0;
$reserve_ids = [];
foreach ($days_data as $key => $val) {
$field = 'noy.reserve_id,noy.reserve_state,noy.reserve_time,nm.nickname';
$list_data = $this->getReservePageList([
[ 'noy.site_id', '=', $site_id ],
[ 'noy.reserve_time', 'between', [ $val[ 'start_time' ], $val[ 'end_time' ] ] ]
], 1, $query_num, 'noy.create_time desc', $field)[ 'data' ];
$reserve_ids = array_merge($reserve_ids, array_column($list_data[ 'list' ], 'reserve_id'));
$days_data[ $key ][ 'data' ] = $list_data;
}
//查询所有预约项 并按照预约分配数据
$reserve_item_list = $this->getReserveItemList([
[ 'oyi.reserve_id', 'in', $reserve_ids ],
], 'oyi.reserve_id,g.goods_name,g.goods_id,g.sku_id', 'reserve_item_id desc', 'oyi',
[ [ 'goods g', 'g.sku_id = oyi.reserve_goods_sku_id', 'right' ] ])[ 'data' ];
$reserve_data = [];
foreach ($reserve_item_list as $val) {
if (!isset($reserve_data[ $val[ 'reserve_id' ] ])) {
$reserve_data[ $val[ 'reserve_id' ] ] = [];
}
$reserve_data[ $val[ 'reserve_id' ] ][] = $val;
}
//预约项关联预约
foreach ($days_data as $key => $val) {
foreach ($val[ 'data' ][ 'list' ] as $k => $item) {
$days_data[ $key ][ 'data' ][ 'list' ][ $k ][ 'item' ] = $reserve_data[ $item[ 'reserve_id' ] ] ?? [];
}
}
return $this->success($days_data);
}
}

View File

@@ -0,0 +1,855 @@
<?php
/**
* Niushop商城系统 - 团队十年电商经验汇集巨献!
* =========================================================
* Copy right 2019-2029 杭州牛之云科技有限公司, 保留所有权利。
* ----------------------------------------------
* 官方网址: https://www.niushop.com
* =========================================================
*/
namespace addon\cardservice\model;
use addon\discount\model\Discount;
use app\model\BaseModel;
use app\model\order\OrderCommon;
use app\model\storegoods\StoreGoods;
use app\model\system\Cron;
use app\model\system\Stat;
use app\model\goods\GoodsCommon;
/**
* 服务项目商品
*/
class ServiceGoods extends GoodsCommon
{
private $goods_class = array ( 'id' => 4, 'name' => '服务项目' );
private $goods_state = array (
1 => '销售中',
0 => '仓库中'
);
public function getGoodsState()
{
return $this->goods_state;
}
public function getGoodsClass()
{
return $this->goods_class;
}
/**
* 商品添加
* @param $data
*/
public function addGoods($data)
{
model('goods')->startTrans();
try {
if (!empty($data[ 'goods_attr_format' ])) {
$goods_attr_format = json_decode($data[ 'goods_attr_format' ], true);
$keys = array_column($goods_attr_format, 'sort');
if (!empty($keys)) {
array_multisort($keys, SORT_ASC, SORT_NUMERIC, $goods_attr_format);
$data[ 'goods_attr_format' ] = json_encode($goods_attr_format);
}
}
$goods_image = $data[ 'goods_image' ];
$first_image = explode(",", $goods_image)[ 0 ];
//SKU商品数据
if (!empty($data[ 'goods_sku_data' ])) {
$data[ 'goods_sku_data' ] = json_decode($data[ 'goods_sku_data' ], true);
}
//商品编码检测
$sku_no_check = $this->checkSkuNoRepeat(['sku_list' => $data['goods_sku_data'], 'site_id' => $data['site_id'], 'goods_id' => $data['goods_id'] ?? 0]);
if($sku_no_check['code'] < 0){
model('goods')->rollback();
return $sku_no_check;
}
//检测商品核销日期
$virtualDate_check = $this->checkVirtualDate($data[ 'verify_validity_type' ],$data[ 'virtual_indate' ]??'');
if($virtualDate_check['code'] < 0){
model('goods')->rollback();
return $virtualDate_check;
}
$goods_data = array (
'goods_image' => $goods_image,
'price' => $data[ 'goods_sku_data' ][ 0 ][ 'price' ],
'market_price' => $data[ 'goods_sku_data' ][ 0 ][ 'market_price' ],
'cost_price' => $data[ 'goods_sku_data' ][ 0 ][ 'cost_price' ],
'goods_spec_format' => $data[ 'goods_spec_format' ],
'category_id' => '',
'category_json' => '',
'label_id' => $data[ 'label_id' ],
'label_name' => '',
'timer_on' => $data[ 'timer_on' ],
'timer_off' => $data[ 'timer_off' ],
'sale_show' => $data[ 'sale_show' ] ?? 1,
'stock_show' => $data[ 'stock_show' ] ?? 1,
'market_price_show' => $data[ 'market_price_show' ] ?? 1,
'barrage_show' => $data[ 'barrage_show' ] ?? 1,
'is_consume_discount' => $data[ 'is_consume_discount' ],
'service_category' => $data[ 'category_id' ],
'is_need_verify' => 1,
'is_reserve' => $data['is_reserve'],
'service_mode' => $data['service_mode'],
'service_price_way' => $data['service_price_way']
);
$common_data = array (
'goods_name' => $data[ 'goods_name' ],
'goods_class' => $this->goods_class[ 'id' ],
'goods_class_name' => $this->goods_class[ 'name' ],
'goods_attr_class' => $data[ 'goods_attr_class' ],
'goods_attr_name' => $data[ 'goods_attr_name' ],
'is_limit' => $data[ 'is_limit' ] ?? 0,
'limit_type' => $data[ 'limit_type' ] ?? 1,
'site_id' => $data[ 'site_id' ],
'goods_content' => $data[ 'goods_content' ],
'goods_state' => $data[ 'goods_state' ],
'goods_stock_alarm' => $data[ 'goods_stock_alarm' ],
'is_virtual' => 1,
'verify_validity_type' => $data[ 'verify_validity_type' ],
'virtual_indate' => $data[ 'virtual_indate' ] ?? 0,
'goods_attr_format' => $data[ 'goods_attr_format' ],
'introduction' => $data[ 'introduction' ],
'keywords' => $data[ 'keywords' ],
'unit' => $data[ 'unit' ],
'video_url' => $data[ 'video_url' ],
'sort' => $data[ 'sort' ],
'goods_service_ids' => $data[ 'goods_service_ids' ],
'create_time' => time(),
'virtual_sale' => $data[ 'virtual_sale' ],
'max_buy' => $data[ 'max_buy' ],
'min_buy' => $data[ 'min_buy' ],
'recommend_way' => $data[ 'recommend_way' ],
'qr_id' => $data[ 'qr_id' ] ?? 0,
'template_id' => $data[ 'template_id' ] ?? 0,
'form_id' => $data[ 'form_id' ] ?? 0,
'sale_channel' => $data[ 'sale_channel' ] ?? 'all',
'sale_store' => $data[ 'sale_store' ] ?? 'all',
'is_unify_price' => $data[ 'is_unify_price' ] ?? 1,
);
$goods_id = model('goods')->add(array_merge($goods_data, $common_data));
$goods_stock = 0;
$sku_arr = array ();
$sku_stock_list = [];
//添加sku商品
foreach ($data[ 'goods_sku_data' ] as $item) {
$goods_stock += $item[ 'stock' ];
$sku_data = array (
'sku_name' => $data[ 'goods_name' ] . ' ' . $item[ 'spec_name' ],
'spec_name' => $item[ 'spec_name' ],
'sku_no' => $item[ 'sku_no' ],
'sku_spec_format' => !empty($item[ 'sku_spec_format' ]) ? json_encode($item[ 'sku_spec_format' ]) : "",
'price' => $item[ 'price' ],
'market_price' => $item[ 'market_price' ],
'cost_price' => $item[ 'cost_price' ],
'discount_price' => $item[ 'price' ],//sku折扣价默认等于单价
'stock' => $item[ 'stock' ],
'stock_alarm' => $item[ 'stock_alarm' ],
'sku_image' => !empty($item[ 'sku_image' ]) ? $item[ 'sku_image' ] : $first_image,
'sku_images' => $item[ 'sku_images' ],
'goods_id' => $goods_id,
'is_default' => $item[ 'is_default' ] ?? 0,
'is_consume_discount' => $data[ 'is_consume_discount' ],
'site_id' => $data[ 'site_id' ],
'service_length' => $item[ 'service_length' ] ?? 0
);
$sku_arr[] = array_merge($sku_data, $common_data);
$sku_stock_list[] = [ 'stock' => $item[ 'stock' ], 'site_id' => $data[ 'site_id' ], 'goods_class' => $common_data[ 'goods_class' ] ];
}
model('goods_sku')->addList($sku_arr);
// 赋值第一个商品sku_id
$first_info = model('goods_sku')->getFirstData([ 'goods_id' => $goods_id ], 'sku_id', 'is_default desc,sku_id asc');
model('goods')->update([ 'sku_id' => $first_info[ 'sku_id' ] ], [ [ 'goods_id', '=', $goods_id ] ]);
if (!empty($data[ 'goods_spec_format' ])) {
// 刷新SKU商品规格项 / 规格值JSON字符串
$this->dealGoodsSkuSpecFormat($goods_id, $data[ 'goods_spec_format' ]);
}
$cron = new Cron();
//定时上下架
if ($goods_data[ 'timer_on' ] > 0) {
$cron->addCron(1, 0, "商品定时上架", "CronGoodsTimerOn", $goods_data[ 'timer_on' ], $goods_id);
}
if ($goods_data[ 'timer_off' ] > 0) {
$cron->addCron(1, 0, "商品定时下架", "CronGoodsTimerOff", $goods_data[ 'timer_off' ], $goods_id);
}
//虚拟商品指定审核有效期后自动下架
if ($data[ 'verify_validity_type' ] == 2 && $data['goods_state'] == 1) {
$cron->addCron(1, 0, "虚拟商品定时下架", "CronVirtualGoodsVerifyOff", $data[ 'virtual_indate' ], $goods_id);
}else{
$cron->deleteCron([ [ 'event', '=', 'CronVirtualGoodsVerifyOff' ], [ 'relate_id', '=', $goods_id ] ]);
}
//添加统计
$stat = new Stat();
$stat->switchStat([ 'type' => 'add_goods', 'data' => [ 'add_goods_count' => 1, 'site_id' => $data[ 'site_id' ] ] ]);
if ($common_data[ 'sale_store' ] != 'all') {
$sale_store = explode(',', $common_data[ 'sale_store' ]);
$sale_store = array_filter($sale_store);
foreach ($sale_store as $k => $v) {
// 同步商品成本价
( new StoreGoods() )->setSkuPrice([ 'goods_id' => $goods_id, 'site_id' => $data[ 'site_id' ], 'store_id' => $v ]);
}
} else {
// 同步商品成本价
( new StoreGoods() )->setSkuPrice([ 'goods_id' => $goods_id, 'site_id' => $data[ 'site_id' ] ]);
}
// 商品设置库存
$goods_stock_model = new \app\model\stock\GoodsStock();
$sku_list = model('goods_sku')->getList([ 'goods_id' => $goods_id ], 'sku_id');
foreach ($sku_stock_list as $k => $v) {
$v[ 'sku_id' ] = $sku_list[ $k ][ 'sku_id' ];
$goods_stock_model->changeGoodsStock($v);
}
$stat = new Stat();
$stat->switchStat(['type' => 'add_goods', 'data' => ['add_goods_count' => 1, 'site_id' => $data['site_id']]]);
$stat->switchStat(['type' => 'goods_on', 'data' => ['site_id' => $data['site_id']]]);
model('goods')->commit();
return $this->success($goods_id);
} catch (\Exception $e) {
model('goods')->rollback();
return $this->error($e->getMessage());
}
}
/**
* 商品编辑
* @param $data
*/
public function editGoods($data)
{
model('goods')->startTrans();
try {
if (!empty($data[ 'goods_attr_format' ])) {
$goods_attr_format = json_decode($data[ 'goods_attr_format' ], true);
$keys = array_column($goods_attr_format, 'sort');
if (!empty($keys)) {
array_multisort($keys, SORT_ASC, SORT_NUMERIC, $goods_attr_format);
$data[ 'goods_attr_format' ] = json_encode($goods_attr_format);
}
}
$goods_id = $data[ 'goods_id' ];
$goods_image = $data[ 'goods_image' ];
$first_image = explode(",", $goods_image)[ 0 ];
//SKU商品数据
if (!empty($data[ 'goods_sku_data' ])) {
$data[ 'goods_sku_data' ] = json_decode($data[ 'goods_sku_data' ], true);
}
//商品编码检测
$sku_no_check = $this->checkSkuNoRepeat(['sku_list' => $data['goods_sku_data'], 'site_id' => $data['site_id'], 'goods_id' => $data['goods_id'] ?? 0]);
if($sku_no_check['code'] < 0){
model('goods')->rollback();
return $sku_no_check;
}
//检测商品核销日期
$virtualDate_check = $this->checkVirtualDate($data[ 'verify_validity_type' ],$data[ 'virtual_indate' ] ?? '');
if($virtualDate_check['code'] < 0){
model('goods')->rollback();
return $virtualDate_check;
}
$goods_data = array (
'goods_image' => $goods_image,
'price' => $data[ 'goods_sku_data' ][ 0 ][ 'price' ],
'market_price' => $data[ 'goods_sku_data' ][ 0 ][ 'market_price' ],
'cost_price' => $data[ 'goods_sku_data' ][ 0 ][ 'cost_price' ],
'goods_spec_format' => $data[ 'goods_spec_format' ],
'category_id' => '',
'category_json' => '',
'label_id' => $data[ 'label_id' ],
'label_name' => '',
'timer_on' => $data[ 'timer_on' ],
'timer_off' => $data[ 'timer_off' ],
'sale_show' => $data[ 'sale_show' ],
'stock_show' => $data[ 'stock_show' ],
'market_price_show' => $data[ 'market_price_show' ],
'barrage_show' => $data[ 'barrage_show' ],
'is_consume_discount' => $data[ 'is_consume_discount' ],
'service_category' => $data[ 'category_id' ],
'is_need_verify' => 1,
'is_reserve' => $data['is_reserve'],
'service_mode' => $data['service_mode'],
'service_price_way' => $data['service_price_way']
);
$common_data = array (
'goods_name' => $data[ 'goods_name' ],
'goods_class' => $this->goods_class[ 'id' ],
'goods_class_name' => $this->goods_class[ 'name' ],
'goods_attr_class' => $data[ 'goods_attr_class' ],
'goods_attr_name' => $data[ 'goods_attr_name' ],
'is_limit' => $data[ 'is_limit' ] ?? 0,
'limit_type' => $data[ 'limit_type' ] ?? 1,
'site_id' => $data[ 'site_id' ],
'goods_content' => $data[ 'goods_content' ],
'goods_state' => $data[ 'goods_state' ],
'goods_stock_alarm' => $data[ 'goods_stock_alarm' ],
'is_virtual' => 1,
'verify_validity_type' => $data[ 'verify_validity_type' ],
'virtual_indate' => $data[ 'virtual_indate' ] ?? 0,
'goods_attr_format' => $data[ 'goods_attr_format' ],
'introduction' => $data[ 'introduction' ],
'keywords' => $data[ 'keywords' ],
'unit' => $data[ 'unit' ],
'video_url' => $data[ 'video_url' ],
'sort' => $data[ 'sort' ],
'goods_service_ids' => $data[ 'goods_service_ids' ],
'modify_time' => time(),
'virtual_sale' => $data[ 'virtual_sale' ],
'max_buy' => $data[ 'max_buy' ],
'min_buy' => $data[ 'min_buy' ],
'recommend_way' => $data[ 'recommend_way' ],
'qr_id' => $data[ 'qr_id' ] ?? 0,
'template_id' => $data[ 'template_id' ] ?? 0,
'form_id' => $data[ 'form_id' ] ?? 0,
'sale_channel' => $data[ 'sale_channel' ] ?? 'all',
'sale_store' => $data[ 'sale_store' ] ?? 'all',
'is_unify_price' => $data[ 'is_unify_price' ] ?? 1,
);
model('goods')->update(array_merge($goods_data, $common_data), [ [ 'goods_id', '=', $goods_id ], [ 'goods_class', '=', $this->goods_class[ 'id' ] ] ]);
$goods_stock = 0;
$goods_stock_model = new \app\model\stock\GoodsStock();
$sku_stock_list = [];
$is_off_store_goods = 0; // 是否下架门店商品
$discount_model = new Discount();
$sku_id_arr = [];
foreach ($data[ 'goods_sku_data' ] as $item) {
$discount_info = [];
if (!empty($item[ 'sku_id' ])) {
$discount_info_result = $discount_model->getDiscountGoodsInfo([ [ 'pdg.sku_id', '=', $item[ 'sku_id' ] ], [ 'pd.status', '=', 1 ] ], 'id');
$discount_info = $discount_info_result[ 'data' ];
}
$goods_stock += $item[ 'stock' ];
$sku_data = array (
'sku_name' => $data[ 'goods_name' ] . ' ' . $item[ 'spec_name' ],
'spec_name' => $item[ 'spec_name' ],
'sku_no' => $item[ 'sku_no' ],
'sku_spec_format' => !empty($item[ 'sku_spec_format' ]) ? json_encode($item[ 'sku_spec_format' ]) : "",
'goods_spec_format' => '',
'price' => $item[ 'price' ],
'market_price' => $item[ 'market_price' ],
'cost_price' => $item[ 'cost_price' ],
// 'stock' => $item[ 'stock' ],
'stock_alarm' => $item[ 'stock_alarm' ],
'sku_image' => !empty($item[ 'sku_image' ]) ? $item[ 'sku_image' ] : $first_image,
'sku_images' => $item[ 'sku_images' ],
'goods_id' => $goods_id,
'is_default' => $item[ 'is_default' ] ?? 0,
'service_length' => $item[ 'service_length' ] ?? 0,
'is_consume_discount' => $data[ 'is_consume_discount' ]
);
if (empty($discount_info)) {
$sku_data[ 'discount_price' ] = $item[ 'price' ];
}
if (!empty($item[ 'sku_id' ])) {
$sku_id_arr[] = $item[ 'sku_id' ];
model('goods_sku')->update(array_merge($sku_data, $common_data), [ [ 'sku_id', '=', $item[ 'sku_id' ] ], [ 'goods_class', '=', $this->goods_class[ 'id' ] ] ]);
} else {
$sku_id = model('goods_sku')->add(array_merge($sku_data, $common_data));
$item[ 'sku_id' ] = $sku_id;
$sku_id_arr[] = $sku_id;
}
$sku_stock_list[] = [ 'stock' => $item[ 'stock' ], 'sku_id' => $item[ 'sku_id' ], 'site_id' => $data[ 'site_id' ], 'goods_class' => $common_data[ 'goods_class' ] ];
}
// 移除不存在的商品SKU
$sku_id_list = model('goods_sku')->getList([ [ 'goods_id', '=', $goods_id ] ], 'sku_id');
$sku_id_list = array_column($sku_id_list, 'sku_id');
foreach ($sku_id_list as $k => $v) {
foreach ($sku_id_arr as $ck => $cv) {
if ($v == $cv) {
unset($sku_id_list[ $k ]);
}
}
}
$sku_id_list = array_values($sku_id_list);
if (!empty($sku_id_list)) {
$check = $this->deleteGoodsSkuCheck($sku_id_list);
if ($check[ 'code' ] < 0) {
model('goods')->rollback();
return $check;
}
$is_off_store_goods = 1;
model('goods_sku')->delete([ [ 'sku_id', 'in', implode(",", $sku_id_list) ] ]);
}
// 赋值第一个商品sku_id
$first_info = model('goods_sku')->getFirstData([ 'goods_id' => $goods_id ], 'sku_id', 'is_default desc,sku_id asc');
model('goods')->update([ 'sku_id' => $first_info[ 'sku_id' ] ], [ [ 'goods_id', '=', $goods_id ] ]);
if (!empty($data[ 'goods_spec_format' ])) {
// 刷新SKU商品规格项 / 规格值JSON字符串
$this->dealGoodsSkuSpecFormat($goods_id, $data[ 'goods_spec_format' ]);
}
if($is_off_store_goods == 1) {
(new StoreGoods())->modifyStoreGoodsState($goods_id, 0);
}
$cron = new Cron();
$cron->deleteCron([ [ 'event', '=', 'CronGoodsTimerOn' ], [ 'relate_id', '=', $goods_id ] ]);
$cron->deleteCron([ [ 'event', '=', 'CronGoodsTimerOff' ], [ 'relate_id', '=', $goods_id ] ]);
//定时上下架
if ($goods_data[ 'timer_on' ] > 0) {
$cron->addCron(1, 0, "商品定时上架", "CronGoodsTimerOn", $goods_data[ 'timer_on' ], $goods_id);
}
if ($goods_data[ 'timer_off' ] > 0) {
$cron->addCron(1, 0, "商品定时下架", "CronGoodsTimerOff", $goods_data[ 'timer_off' ], $goods_id);
}
//虚拟商品指定审核有效期后自动下架
if ($data[ 'verify_validity_type' ] == 2 && $data['goods_state'] == 1) {
$cron->addCron(1, 0, "虚拟商品定时下架", "CronVirtualGoodsVerifyOff", $data[ 'virtual_indate' ], $goods_id);
}else{
$cron->deleteCron([ [ 'event', '=', 'CronVirtualGoodsVerifyOff' ], [ 'relate_id', '=', $goods_id ] ]);
}
// 商品设置库存
foreach ($sku_stock_list as $k => $v) {
$goods_stock_model->changeGoodsStock($v);
}
if ($common_data[ 'sale_store' ] != 'all') {
$sale_store = explode(',', $common_data[ 'sale_store' ]);
$sale_store = array_filter($sale_store);
foreach ($sale_store as $k => $v) {
// 同步商品成本价
( new StoreGoods() )->setSkuPrice([ 'goods_id' => $goods_id, 'site_id' => $data[ 'site_id' ], 'store_id' => $v ]);
}
} else {
//同步商品成本价
( new StoreGoods() )->setSkuPrice([ 'goods_id' => $goods_id, 'site_id' => $data[ 'site_id' ] ]);
}
$stat = new Stat();
$stat->switchStat(['type' => 'goods_on', 'data' => ['site_id' => $data['site_id']]]);
model('goods')->commit();
return $this->success($goods_id);
} catch (\Exception $e) {
model('goods')->rollback();
return $this->error($e->getMessage() . $e->getFile() . $e->getLine());
}
}
/**
* 获取商品信息
* @param array $condition
* @param string $field
*/
public function getGoodsInfo($condition, $field = 'goods_id,goods_name,goods_class,goods_class_name,goods_attr_class,goods_attr_name,goods_image,goods_content,goods_state,price,market_price,cost_price,goods_stock,goods_stock_alarm,goods_spec_format,goods_attr_format,introduction,keywords,unit,sort,video_url,evaluate,virtual_indate')
{
$info = model('goods')->getInfo($condition, $field);
if (!empty($info)) {
if (isset($info[ 'service_category' ])) {
$service_category = explode(',', $info[ 'service_category' ]);
$goods_category_data = [];
foreach ($service_category as $k => $v) {
if (!empty($v)) {
$category_list = model('service_category')->getList([ [ 'category_id', 'in', $v ] ], 'category_id,category_name,level', 'level asc');
$goods_category_data[] = $category_list;
}
}
$info[ 'goods_category_data' ] = $goods_category_data;
}
if (isset($info[ 'goods_stock' ])) {
$info[ 'goods_stock' ] = numberFormat($info[ 'goods_stock' ]);
}
if (isset($info[ 'sale_num' ])) {
$info[ 'sale_num' ] = numberFormat($info[ 'sale_num' ]);
}
if (isset($info[ 'virtual_sale' ])) {
$info[ 'virtual_sale' ] = numberFormat($info[ 'virtual_sale' ]);
}
if (isset($info[ 'real_stock' ])) {
$info[ 'real_stock' ] = numberFormat($info[ 'real_stock' ]);
}
}
return $this->success($info);
}
/**
* 获取商品详情
* @param $goods_id
* @return array
*/
public function getGoodsDetail($goods_id, $site_id)
{
$info = model('goods')->getInfo([ [ 'is_delete', '=', 0 ], [ 'goods_id', '=', $goods_id ], [ 'site_id', '=', $site_id ], [ 'goods_class', '=', 3 ] ], "*");
$field = 'sku_id, sku_name,spec_name, sku_no, sku_spec_format, price, market_price, cost_price, discount_price, stock,
weight, volume, sku_image, sku_images, sort,member_price,fenxiao_price';
$sku_data = model('goods_sku')->getList([ [ 'goods_id', '=', $goods_id ] ], $field);
if (!empty($sku_data)) {
foreach ($sku_data as $k => $v) {
$sku_data[ $k ][ 'member_price' ] = $v[ 'member_price' ] == '' ? '' : json_decode($v[ 'member_price' ], true);
if (isset($v[ 'stock' ])) {
$sku_data[ $k ][ 'stock' ] = numberFormat($sku_data[ $k ][ 'stock' ]);
}
}
}
if (!empty($info)) {
if (isset($info[ 'goods_stock' ])) {
$info[ 'goods_stock' ] = numberFormat($info[ 'goods_stock' ]);
}
if (isset($info[ 'sale_num' ])) {
$info[ 'sale_num' ] = numberFormat($info[ 'sale_num' ]);
}
if (isset($info[ 'virtual_sale' ])) {
$info[ 'virtual_sale' ] = numberFormat($info[ 'virtual_sale' ]);
}
if (isset($info[ 'real_stock' ])) {
$info[ 'real_stock' ] = numberFormat($info[ 'real_stock' ]);
}
$info[ 'sku_data' ] = $sku_data;
}
return $this->success($info);
}
/**
* 获取商品列表
* @param array $condition
* @param string $field
* @param string $order
* @param string $limit
*/
public function getGoodsList($condition = [], $field = 'goods_id,goods_class,goods_class_name,goods_attr_name,goods_name,site_id,,sort,goods_image,goods_content,goods_state,price,market_price,cost_price,goods_stock,goods_stock_alarm,is_virtual,goods_spec_format,goods_attr_format,create_time', $order = 'create_time desc', $limit = null)
{
$list = model('goods')->getList($condition, $field, $order, '', '', '', $limit);
if (!empty($list)) {
foreach ($list as $k => $v) {
if (isset($v[ 'goods_stock' ])) {
$list[ $k ][ 'goods_stock' ] = numberFormat($list[ $k ][ 'goods_stock' ]);
}
if (isset($v[ 'sale_num' ])) {
$list[ $k ][ 'sale_num' ] = numberFormat($list[ $k ][ 'sale_num' ]);
}
if (isset($v[ 'virtual_sale' ])) {
$list[ $k ][ 'virtual_sale' ] = numberFormat($list[ $k ][ 'virtual_sale' ]);
}
if (isset($v[ 'real_stock' ])) {
$list[ $k ][ 'real_stock' ] = numberFormat($list[ $k ][ 'real_stock' ]);
}
}
}
return $this->success($list);
}
/************************************************************************* 购买的虚拟产品 start *******************************************************************/
/**
* 生成购买的虚拟产品
* @param $site_id
* @param $order_id
* @param $order_no
* @param $sku_id
* @param $sku_name
* @param $code
* @param $member_id
* @param $sku_image
*/
public function addGoodsVirtual($site_id, $goods_id, $sku_id, $data)
{
if (is_array($data) && count($data)) {
$virtual_goods = [];
foreach ($data as $carmichael_item) {
$carmichael_item = htmlspecialchars(addslashes($carmichael_item));
$card = explode(' ', $carmichael_item);
$card_arr = [
'cardno' => $card[ 0 ] ?? '',
'password' => $card[ 1 ] ?? ''
];
$virtual_goods[] = [
'site_id' => $site_id,
'sku_id' => $sku_id,
'card_info' => json_encode($card_arr),
'goods_id' => $goods_id
];
}
model("goods_virtual")->startTrans();
try {
$res = model("goods_virtual")->addList($virtual_goods);
model('goods')->setInc([ [ 'goods_id', '=', $goods_id ], [ 'site_id', '=', $site_id ] ], 'goods_stock', count($virtual_goods)); // 增加商品总库存
model('goods_sku')->setInc([ [ 'sku_id', '=', $sku_id ], [ 'site_id', '=', $site_id ] ], 'stock', count($virtual_goods)); // 增加sku库存
model("goods_virtual")->commit();
return $this->success($res);
} catch (\Exception $e) {
model("goods_virtual")->rollback();
return $this->error('', $e->getMessage());
}
} else {
return $this->error('', '请输入要添加的卡密数据');
}
}
/**
* 删除
* @param $condition
*/
public function deleteGoodsVirtual($condition)
{
$res = model("goods_virtual")->delete($condition);
if ($res === false)
return $this->error();
$check_condition = array_column($condition, 2, 0);
$goods_id = $check_condition[ 'goods_id' ] ?? 0;
return $this->success();
}
/************************************************************************* 购买的虚拟产品 end *******************************************************************/
/**
* 从excel中读取卡密数据
* @param $path
*/
public function importData($path)
{
$PHPReader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader('Xlsx');
//载入文件
$PHPExcel = $PHPReader->load($path);
//获取表中的第一个工作表如果要获取第二个把0改为1依次类推
$currentSheet = $PHPExcel->getSheet(0);
//获取总行数
$allRow = $currentSheet->getHighestRow();
if ($allRow < 2) {
return $this->error('', '导入了一个空文件');
}
$data = [];
for ($i = 2; $i <= $allRow; $i++) {
//卡号
$cardno = $PHPExcel->getActiveSheet()->getCell('A' . $i)->getValue();
//卡号
$password = $PHPExcel->getActiveSheet()->getCell('B' . $i)->getValue();
$data[] = $cardno . ' ' . $password;
}
return $this->success(implode("\n", $data));
}
/**
* 商品导入
* @param $goods_data
* @param $site_id
* @return array
*/
public function importGoods($goods_data, $site_id)
{
try {
if (empty($goods_data[ 'goods_name' ])) return $this->error('', '商品名称不能为空');
if (empty($goods_data[ 'goods_image' ])) return $this->error('', '商品主图不能为空');
if (empty($goods_data[ 'category_1' ]) && empty($goods_data[ 'category_2' ]) && empty($goods_data[ 'category_3' ])) return $this->error('', '商品分类不能为空');
// 处理商品分类
$category_id = '';
$category_json = [];
if (!empty($goods_data[ 'category_3' ])) {
$category_info = model('goods_category')->getInfo([ [ 'level', '=', 3 ], [ 'site_id', '=', $site_id ], [ 'category_full_name', '=', "{$goods_data['category_1']}/{$goods_data['category_2']}/{$goods_data['category_3']}" ] ], 'category_id_1,category_id_2,category_id_3');
if (!empty($category_info)) {
$category_id = "{$category_info['category_id_1']},{$category_info['category_id_2']},{$category_info['category_id_3']}";
}
}
if (!empty($goods_data[ 'category_2' ]) && empty($category_id)) {
$category_info = model('goods_category')->getInfo([ [ 'level', '=', 2 ], [ 'site_id', '=', $site_id ], [ 'category_full_name', '=', "{$goods_data['category_1']}/{$goods_data['category_2']}" ] ], 'category_id_1,category_id_2');
if (!empty($category_info)) {
$category_id = "{$category_info['category_id_1']},{$category_info['category_id_2']}";
}
}
if (!empty($goods_data[ 'category_1' ]) && empty($category_id)) {
$category_info = model('goods_category')->getInfo([ [ 'level', '=', 1 ], [ 'site_id', '=', $site_id ], [ 'category_name', '=', "{$goods_data['category_1']}" ] ], 'category_id_1');
if (!empty($category_info)) {
$category_id = "{$category_info['category_id_1']}";
}
}
if (empty($category_id)) return $this->error('', '未找到所填商品分类');
$category_json = [ $category_id ];
$sku_data = [];
$goods_spec_format = [];
$tag = 0;
// 处理sku数据
if (isset($goods_data[ 'sku' ])) {
foreach ($goods_data[ 'sku' ] as $sku_item) {
if (empty($sku_item[ 'sku_data' ])) return $this->error('', '规格数据不能为空');
$spec_name = '';
$spec_data = explode(';', $sku_item[ 'sku_data' ]);
$sku_spec_format = [];
foreach ($spec_data as $item) {
$spec_item = explode(':', $item);
$spec_name .= ' ' . $spec_item[ 1 ];
// 规格项
$spec_index = array_search($spec_item[ 0 ], array_column($goods_spec_format, 'spec_name'));
if (empty($goods_spec_format) || $spec_index === false) {
$spec = [
'spec_id' => -( $tag + getMillisecond() ),
'spec_name' => $spec_item[ 0 ],
'value' => []
];
$goods_spec_format[] = $spec;
$tag++;
} else {
$spec = $goods_spec_format[ $spec_index ];
}
// 规格值
$spec_index = array_search($spec_item[ 0 ], array_column($goods_spec_format, 'spec_name'));
$spec_value_index = array_search($spec_item[ 1 ], array_column($spec[ 'value' ], 'spec_value_name'));
if (empty($spec[ 'value' ]) || $spec_value_index === false) {
$spec_value = [
'spec_id' => $spec[ 'spec_id' ],
'spec_name' => $spec[ 'spec_name' ],
'spec_value_id' => -( $tag + getMillisecond() ),
'spec_value_name' => $spec_item[ 1 ],
'image' => '',
];
$goods_spec_format[ $spec_index ][ 'value' ][] = $spec_value;
$tag++;
} else {
$spec_value = $spec[ 'value' ][ $spec_value_index ];
}
$sku_spec_format[] = [
'spec_id' => $spec[ 'spec_id' ],
'spec_name' => $spec[ 'spec_name' ],
'spec_value_id' => $spec_value[ 'spec_value_id' ],
'spec_value_name' => $spec_value[ 'spec_value_name' ],
'image' => '',
];
}
$sku_images_arr = explode(',', $sku_item[ 'sku_image' ]);
$sku_temp = [
'spec_name' => trim($spec_name),
'sku_no' => $sku_item[ 'sku_code' ],
'sku_spec_format' => $sku_spec_format,
'price' => $sku_item[ 'price' ],
'market_price' => $sku_item[ 'market_price' ],
'cost_price' => $sku_item[ 'cost_price' ],
'stock_alarm' => $sku_item[ 'stock_alarm' ],
'sku_image' => empty($sku_item[ 'sku_image' ]) ? '' : $sku_images_arr[ 0 ],
'sku_images' => empty($sku_item[ 'sku_image' ]) ? '' : $sku_item[ 'sku_image' ],
'sku_images_arr' => empty($sku_item[ 'sku_image' ]) ? [] : $sku_images_arr,
'is_default' => 0,
'carmichael' => empty($sku_item[ 'carmichael' ]) ? [] : explode("\n", $sku_item[ 'carmichael' ])
];
$sku_data[] = $sku_temp;
}
} else {
$goods_img = explode(',', $goods_data[ 'goods_image' ]);
$sku_data = [
[
'sku_id' => 0,
'sku_name' => $goods_data[ 'goods_name' ],
'spec_name' => '',
'sku_spec_format' => '',
'price' => empty($goods_data[ 'price' ]) ? 0 : $goods_data[ 'price' ],
'market_price' => empty($goods_data[ 'market_price' ]) ? 0 : $goods_data[ 'market_price' ],
'cost_price' => empty($goods_data[ 'cost_price' ]) ? 0 : $goods_data[ 'cost_price' ],
'sku_no' => $goods_data[ 'goods_code' ],
'stock_alarm' => empty($goods_data[ 'stock_alarm' ]) ? 0 : $goods_data[ 'stock_alarm' ],
'sku_image' => $goods_img[ 0 ],
'sku_images' => $goods_data[ 'goods_image' ],
'carmichael' => empty($goods_data[ 'carmichael' ]) ? [] : explode("\n", $goods_data[ 'carmichael' ])
]
];
}
if (count($goods_spec_format) > 4) return $this->error('', '最多支持四种规格项');
$data = [
'goods_name' => $goods_data[ 'goods_name' ],// 商品名称,
'goods_attr_class' => '',// 商品类型id,
'goods_attr_name' => '',// 商品类型名称,
'site_id' => $site_id,
'category_id' => ',' . $category_id . ',',
'category_json' => json_encode($category_json),
'goods_image' => $goods_data[ 'goods_image' ],// 商品主图路径
'goods_content' => '',// 商品详情
'goods_state' => 0, //$goods_data['goods_state'] == 1 || $goods_data['goods_state'] == '是' ? 1 : 0,// 商品状态1.正常0下架
'price' => empty($goods_data[ 'price' ]) ? 0 : $goods_data[ 'price' ],// 商品价格取第一个sku
'market_price' => empty($goods_data[ 'market_price' ]) ? 0 : $goods_data[ 'market_price' ],// 市场价格取第一个sku
'cost_price' => empty($goods_data[ 'cost_price' ]) ? 0 : $goods_data[ 'cost_price' ],// 成本价取第一个sku
'sku_no' => $goods_data[ 'goods_code' ],// 商品sku编码
'goods_stock_alarm' => empty($goods_data[ 'goods_stock_alarm' ]) ? 0 : $goods_data[ 'goods_stock_alarm' ],// 库存预警
'goods_spec_format' => empty($goods_spec_format) ? '' : json_encode($goods_spec_format, JSON_UNESCAPED_UNICODE),// 商品规格格式
'goods_attr_format' => '',// 商品参数格式
'introduction' => $goods_data[ 'introduction' ],// 促销语
'keywords' => $goods_data[ 'keywords' ],// 关键词
'unit' => $goods_data[ 'unit' ],// 单位
'sort' => '',// 排序,
'qr_id' => empty($goods_data[ 'qr_id' ]) ? 0 : $goods_data[ 'qr_id' ],// 社群二维码id
'template_id' => empty($goods_data[ 'template_id' ]) ? 0 : $goods_data[ 'template_id' ],// 海报id
'is_limit' => empty($goods_data[ 'is_limit' ]) ? 0 : $goods_data[ 'is_limit' ],// 是否限购
'limit_type' => empty($goods_data[ 'limit_type' ]) ? 0 : $goods_data[ 'limit_type' ],// 限购类型
'video_url' => '',// 视频
'goods_sku_data' => json_encode($sku_data, JSON_UNESCAPED_UNICODE),// SKU商品数据
'goods_service_ids' => '',// 商品服务id集合
'label_id' => '',// 商品分组id
'virtual_sale' => 0,// 虚拟销量
'max_buy' => 0,// 限购
'min_buy' => 0,// 起售
'recommend_way' => 0, // 推荐方式1新品2精品3推荐
'timer_on' => 0,//定时上架
'timer_off' => 0,//定时下架
'brand_id' => 0,
'is_consume_discount' => $goods_data[ 'is_consume_discount' ] == 1 || $goods_data[ 'is_consume_discount' ] == '是' ? 1 : 0, //是否参与会员折扣
];
$res = $this->addGoods($data);
return $res;
} catch (\Exception $e) {
return $this->error('', $e->getMessage());
}
}
}

View File

@@ -0,0 +1,303 @@
<?php
/**
* Niushop商城系统 - 团队十年电商经验汇集巨献!
* =========================================================
* Copy right 2019-2029 杭州牛之云科技有限公司, 保留所有权利。
* ----------------------------------------------
* 官方网址: https://www.niushop.com
* =========================================================
*/
namespace addon\cardservice\model\order;
use addon\cardservice\model\MemberCard;
use app\model\BaseModel;
use app\model\order\Config;
use app\model\order\OrderCreate;
use app\model\order\OrderCreateTool;
use app\model\store\Store;
use app\model\express\Express;
use app\model\system\Pay;
use app\model\express\Config as ExpressConfig;
use app\model\express\Local;
use app\model\goods\Goods as GoodsModel;
use extend\exception\OrderException;
/**
* 订单创建(会员卡项提货)
* @author Administrator
*
*/
class MembercardOrderCreate extends BaseModel
{
use OrderCreateTool;
public $member_card_id = 0;
public $card_id = 0;
public $card_info = [];
public function __construct()
{
$this->promotion_type = 'cardservice';
$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) {
$item_order_goods_insert_data = $this->getOrderGoodsInsertData($order_goods_v);
$item_order_goods_insert_data[ 'card_item_id' ] = $order_goods_v[ 'item_id' ];
$order_goods_insert_data[] = $item_order_goods_insert_data;
}
model('order_goods')->addList($order_goods_insert_data);
//卡项使用记录
$card_use_params = [];
foreach ($this->getOrderGoodsList() as $k => $v) {
$goods_item = $this->goods_list[ $k ];
$card_use_params[] = [
'item_id' => $goods_item[ 'item_id' ],
'num' => $goods_item[ 'num' ],
'type' => 'order',
'relation_id' => $v[ 'order_goods_id' ],
'store_id' => $this->store_id
];
}
$member_card = new MemberCard();
$use_result = $member_card->cardUse($card_use_params);
if ($use_result[ 'code' ] != 0) {
model('order')->rollback();
return $use_result;
}
//批量库存处理(卡密商品支付后在扣出库存)//todo 可以再商品中设置扣除库存步骤
$this->batchDecOrderGoodsStock();
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', '/pages/order/detail?order_id=' . $this->order_id, $this->order_id, $this->member_id);
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->pay_money = $this->order_money - $this->balance_money;
//设置过的商品项信息
return get_object_vars($this);
}
/**
* 订单计算
*/
public function calculate()
{
//初始化会员收货地址
$this->initMemberAddress();
//初始化会员账户
$this->initMemberAccount();
//初始化门店信息
$this->initStore();
$this->member_card_id = $this->param[ 'member_card_id' ];
$member_card_model = new MemberCard();
$condition = array (
[ 'site_id', '=', $this->site_id ],
[ 'card_id', '=', $this->member_card_id ],
[ 'member_id', '=', $this->member_id ],
[ 'status', '=', 1 ]
);
$this->card_info = $member_card_model->getCardInfo($condition)[ 'data' ] ?? [];
if (empty($this->card_info)) throw new OrderException('该卡项不存在!');
$this->card_id = $this->card_info[ 'card_id' ];
//销售门店信息
$goods_model = new GoodsModel();
$card_goods_info = $goods_model->getGoodsInfo([['goods_id', '=', $this->card_info['goods_id']]], 'sale_store')['data'];
if(empty($card_goods_info)) throw new OrderException('该卡项商品不存在!');
if($card_goods_info['sale_store'] != 'all'){
$this->available_store_ids = trim($card_goods_info['sale_store'], ',');
}
//商品列表信息
$this->getOrderGoodsCalculate();
//订单计算
$this->shopOrderCalculate();
//获取发票相关
$this->getInovice();
$this->order_key = create_no();
$this->setOrderCache(get_object_vars($this), $this->order_key);
return true;
}
/**
* 待付款订单
*/
public function orderPayment()
{
$this->calculate();
//查询配送信息
$this->getDeliveryData();
//订单初始项
event('OrderPayment', [ 'order_object' => $this ]);
return get_object_vars($this);
}
/**
* 获取商品的计算信息
*/
public function getOrderGoodsCalculate()
{
$this->getCardGoodsList();
return true;
}
/**
* 获取组合套餐商品列表信息
*/
public function getCardGoodsList()
{
$card_info = $this->card_info;
$card_type = $this->card_info[ 'card_type' ];
$member_card_item = $this->param[ 'member_card_item' ] ?? [];
if (!$member_card_item) throw new OrderException('商品不存在');
foreach ($member_card_item as $item) {
$join = [
[ 'goods_sku ngs', 'mgci.sku_id = ngs.sku_id', 'inner' ],
[ 'site ns', 'mgci.site_id = ns.site_id', 'inner' ]
];
$condition = [
[ 'mgci.item_id', '=', $item[ 'item_id' ] ],
[ 'mgci.site_id', '=', $this->site_id ],
[ 'mgci.card_id', '=', $this->member_card_id ],
[ 'ngs.goods_state', '=', 1 ],
[ 'ngs.is_virtual', '=', 0 ]
];
$field = 'mgci.item_id,mgci.num as total_num,mgci.use_num,ngs.sku_id,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, ngs.goods_state, ngs.is_virtual,ngs.is_free_shipping, ngs.shipping_template, ngs.goods_class, ngs.goods_class_name, ngs.goods_id,ngs.sku_spec_format,ngs.goods_name,ns.site_name,ngs.support_trade_type,ngs.supplier_id';
$goods_info = model('member_goods_card_item')->getInfo($condition, $field, 'mgci', $join);
if (!empty($goods_info)) {
$this->is_virtual = $goods_info[ 'is_virtual' ];
if ($card_type == 'oncecard') {
$item[ 'num' ] = min($item[ 'num' ], $goods_info[ 'total_num' ] - $goods_info[ 'use_num' ]);
} else if ($card_type == 'commoncard') {
$item[ 'num' ] = min($item[ 'num' ], $card_info[ 'total_num' ] - $card_info[ 'total_use_num' ]);
$card_info[ 'total_use_num' ] += $item[ 'num' ];
}
if ($item[ 'num' ] < 1) continue;
$goods_info[ 'num' ] = $item[ 'num' ];
$price = $goods_info[ 'discount_price' ];
$goods_info[ 'price' ] = $price;
$goods_money = $price * $goods_info[ 'num' ];
$goods_info[ 'goods_money' ] = $goods_money;
$promotion_money = 0;
$goods_info[ 'real_goods_money' ] = $goods_money;
$goods_info[ 'coupon_money' ] = 0;//优惠券金额
$goods_info[ 'promotion_money' ] = $promotion_money;//优惠金额
$this->site_name = $goods_info[ 'site_name' ];
$this->goods_list[] = $goods_info;
$order_name = $this->order_name ?? '';
if ($order_name) {
$len = strlen_mb($order_name);
if ($len > 200) {
$this->order_name = str_sub($order_name, 200);
} else {
$this->order_name = string_split($order_name, ',', $goods_info[ 'sku_name' ]);
}
} else {
$this->order_name = string_split('', ',', $goods_info[ 'sku_name' ]);
}
$this->goods_num += $goods_info[ 'num' ];
$this->goods_money += $goods_info[ 'goods_money' ];
//以;隔开的商品项
$goods_list_str = $this->goods_list_str ?? '';
if ($goods_list_str) {
$this->goods_list_str = $goods_list_str . ';' . $goods_info[ 'sku_id' ] . ':' . $goods_info[ 'num' ];
} else {
$this->goods_list_str = $goods_info[ 'sku_id' ] . ':' . $goods_info[ 'num' ];
}
}
}
if (!$this->goods_list) throw new OrderException('卡项中没有可以提货的商品!');
return true;
}
/**
* 获取订单计算
*/
public function shopOrderCalculate()
{
$promotion_money = 0;
foreach ($this->goods_list as &$v) {
$item_goods_money = $v[ 'goods_money' ];
$item_promotion_money = $item_goods_money;
$real_goods_money = $item_goods_money - $item_promotion_money;
$v[ 'real_goods_money' ] = $real_goods_money;
$v[ 'promotion_money' ] = $item_promotion_money;
$promotion_money += $item_promotion_money;
}
$this->promotion_money = $promotion_money;
$this->is_free_delivery = false;//
//重新计算订单总额
$this->getOrderMoney();
//理论上是多余的操作
if ($this->order_money < 0) {
$this->order_money = 0;
}
//总结计算
$this->pay_money = $this->order_money;
return true;
}
}

View File

@@ -0,0 +1,474 @@
<?php
/**
* Niushop商城系统 - 团队十年电商经验汇集巨献!
* =========================================================
* Copy right 2019-2029 杭州牛之云科技有限公司, 保留所有权利。
* ----------------------------------------------
* 官方网址: https://www.niushop.com
* =========================================================
*/
namespace addon\cardservice\shop\controller;
use addon\cardservice\model\CardGoods;
use addon\cardservice\model\MemberCard as MemberCardModel;
use addon\form\model\Form;
use addon\supply\model\Supplier as SupplierModel;
use app\model\goods\Goods as GoodsModel;
use app\model\goods\GoodsBrand as GoodsBrandModel;
use app\model\goods\GoodsService as GoodsServiceModel;
use app\model\store\Store as StoreModel;
use app\model\web\Config as ConfigModel;
use app\shop\controller\BaseShop;
use think\App;
/**
* 虚拟商品
* Class Virtualgoods
* @package app\shop\controller
*/
class Card extends BaseShop
{
public function __construct(App $app = null)
{
$this->replace = [
'ADDON_CARDSERVICE_CSS' => __ROOT__ . '/addon/cardservice/shop/view/public/css',
'ADDON_CARDSERVICE_JS' => __ROOT__ . '/addon/cardservice/shop/view/public/js',
'ADDON_CARDSERVICE_IMG' => __ROOT__ . '/addon/cardservice/shop/view/public/img',
];
parent::__construct($app);
}
/**
* 添加商品
* @return mixed
*/
public function addGoods()
{
if (request()->isJson()) {
$category_id = input('category_id', 0);// 分类id
$category_json = json_encode($category_id);//分类字符串
$category_id = '';
$data = [
'goods_name' => input('goods_name', ''),// 商品名称,
'goods_attr_class' => input('goods_attr_class', ''),// 商品类型id,
'goods_attr_name' => input('goods_attr_name', ''),// 商品类型名称,
'is_limit' => input('is_limit', '0'),// 商品是否限购,
'limit_type' => input('limit_type', '1'),// 商品限购类型,
'site_id' => $this->site_id,
'category_id' => $category_id,
'category_json' => $category_json,
'goods_image' => input('goods_image', ''),// 商品主图路径
'goods_content' => input('goods_content', ''),// 商品详情
'goods_state' => input('goods_state', ''),// 商品状态1.正常0下架
'price' => input('price', 0),// 商品价格取第一个sku
'renew_price' => input('renew_price', 0), // 续费价格
'market_price' => input('market_price', 0),// 市场价格取第一个sku
'cost_price' => input('cost_price', 0),// 成本价取第一个sku
'sku_no' => input('sku_no', ''),// 商品sku编码
'weight' => input('weight', ''),// 重量
'volume' => input('volume', ''),// 体积
'goods_stock' => input('goods_stock', 0),// 商品库存(总和)
'goods_stock_alarm' => input('goods_stock_alarm', 0),// 库存预警
'goods_spec_format' => input('goods_spec_format', ''),// 商品规格格式
'goods_attr_format' => input('goods_attr_format', ''),// 商品参数格式
'introduction' => input('introduction', ''),// 促销语
'keywords' => input('keywords', ''),// 关键词
'unit' => input('unit', ''),// 单位
'sort' => input('sort', 0),// 排序,
'video_url' => input('video_url', ''),// 视频
'goods_sku_data' => input('goods_sku_data', ''),// SKU商品数据
'goods_service_ids' => input('goods_service_ids', ''),// 商品服务id集合
'label_id' => input('label_id', ''),// 商品分组id
'brand_id' => input('brand_id', 0),//品牌id
'virtual_sale' => input('virtual_sale', 0),// 虚拟销量
'max_buy' => input('max_buy', 0),// 限购
'min_buy' => input('min_buy', 0),// 起售
'recommend_way' => input('recommend_way', 0), // 推荐方式1新品2精品3推荐
'timer_on' => strtotime(input('timer_on', 0)),//定时上架
'timer_off' => strtotime(input('timer_off', 0)),//定时下架
'is_consume_discount' => input('is_consume_discount', 0),//是否参与会员折扣
'qr_id' => input('qr_id', 0),// 社群二维码id
'template_id' => input('template_id', 0), // 商品海报id
'sale_show' => input('sale_show', 0),//
'stock_show' => input('stock_show', 0),//
'market_price_show' => input('market_price_show', 0),//
'barrage_show' => input('barrage_show', 0),//
'form_id' => input('form_id', 0),
'sale_channel' => input('sale_channel', 'all'),
'sale_store' => input('sale_store', 'all'),
'card_type' => input('card_type', ''),
'validity_type' => input('validity_type', 0),
'discount_goods_type' => input('discount_goods_type', 'all'),
'discount' => input('discount', 0),
'common_num' => input('common_num', 0),
'relation_goods' => input('relation_goods', '[]'),
'is_unify_price' => input('is_unify_price', 1),
'supplier_id' => input('supplier_id', 0)
];
if ($data[ 'validity_type' ] == 1) {
$data[ 'validity_day' ] = input('validity_day', 0);
} else if ($data[ 'validity_type' ] == 2) {
$data[ 'validity_time' ] = strtotime(input('validity_time', ''));
}
$virtual_goods_model = new CardGoods();
$res = $virtual_goods_model->addGoods($data);
return $res;
} else {
$virtual_goods_model = new CardGoods();
// 商品服务
$goods_service_model = new GoodsServiceModel();
$service_list = $goods_service_model->getServiceList([ [ 'site_id', '=', $this->site_id ] ], 'id,service_name,icon')[ 'data' ];
$this->assign('service_list', $service_list);
// 商品品牌
$goods_brand_model = new GoodsBrandModel();
$brand_list = $goods_brand_model->getBrandList([ [ 'site_id', '=', $this->site_id ] ], 'brand_id,brand_name', 'sort asc')[ 'data' ];
$this->assign('brand_list', $brand_list);
//商品默认排序值
$config_model = new ConfigModel();
$sort_config = $config_model->getGoodsSort($this->site_id)[ 'data' ][ 'value' ];
$this->assign('sort_config', $sort_config);
//获取商品海报
$poster_list = event('PosterTemplate', [ 'site_id' => $this->site_id ], true);
if (!empty($poster_list)) {
$poster_list = $poster_list[ 'data' ];
}
$this->assign('poster_list', $poster_list);
$this->assign('virtualcard_exit', addon_is_exit('virtualcard', $this->site_id));
$form_is_exit = addon_is_exit('form', $this->site_id);
if ($form_is_exit) {
$form_list = ( new Form() )->getFormList([ [ 'site_id', '=', $this->site_id ], [ 'form_type', '=', 'goods' ], [ 'is_use', '=', 1 ] ], 'id desc', 'id, form_name')[ 'data' ];
$this->assign('form_list', $form_list);
}
$this->assign('form_is_exit', $form_is_exit);
$this->assign('all_goodsclass', event('GoodsClass'));
$this->assign('goods_class', $virtual_goods_model->getGoodsClass());
$this->assign('card_type', $virtual_goods_model->getCardType());
$this->assign('store_is_exit', addon_is_exit('store', $this->site_id));
$is_install_supply = addon_is_exit('supply');
if ($is_install_supply) {
$supplier_model = new SupplierModel();
$supplier_list = $supplier_model->getSupplyList([ [ 'supplier_site_id', '=', $this->site_id ] ], 'supplier_id,title', 'supplier_id desc')['data'];
$this->assign('supplier_list', $supplier_list);
}
$this->assign('is_install_supply', $is_install_supply);
return $this->fetch('card/add_goods');
}
}
/**
* 编辑商品
* @return mixed
*/
public function editGoods()
{
$virtual_goods_model = new CardGoods();
if (request()->isJson()) {
$category_id = input('category_id', 0);// 分类id
$category_json = json_encode($category_id);//分类字符串
$category_id = '';
$data = [
'goods_id' => input('goods_id', 0),// 商品id
'goods_name' => input('goods_name', ''),// 商品名称,
'goods_attr_class' => input('goods_attr_class', ''),// 商品类型id,
'goods_attr_name' => input('goods_attr_name', ''),// 商品类型名称,
'is_limit' => input('is_limit', '0'),// 商品是否限购,
'limit_type' => input('limit_type', '1'),// 商品限购类型,
'site_id' => $this->site_id,
'category_id' => $category_id,
'category_json' => $category_json,
'goods_image' => input('goods_image', ''),// 商品主图路径
'goods_content' => input('goods_content', ''),// 商品详情
'goods_state' => input('goods_state', ''),// 商品状态1.正常0下架
'price' => input('price', 0),// 商品价格取第一个sku
'renew_price' => input('renew_price', 0), // 续费价格
'market_price' => input('market_price', 0),// 市场价格取第一个sku
'cost_price' => input('cost_price', 0),// 成本价取第一个sku
'sku_no' => input('sku_no', ''),// 商品sku编码
'weight' => input('weight', ''),// 重量
'volume' => input('volume', ''),// 体积
'goods_stock' => input('goods_stock', 0),// 商品库存(总和)
'goods_stock_alarm' => input('goods_stock_alarm', 0),// 库存预警
'goods_spec_format' => input('goods_spec_format', ''),// 商品规格格式
'goods_attr_format' => input('goods_attr_format', ''),// 商品参数格式
'introduction' => input('introduction', ''),// 促销语
'keywords' => input('keywords', ''),// 关键词
'unit' => input('unit', ''),// 单位
'sort' => input('sort', 0),// 排序,
'video_url' => input('video_url', ''),// 视频
'goods_sku_data' => input('goods_sku_data', ''),// SKU商品数据
'goods_service_ids' => input('goods_service_ids', ''),// 商品服务id集合
'label_id' => input('label_id', ''),// 商品分组id
'brand_id' => input('brand_id', 0),//品牌id
'virtual_sale' => input('virtual_sale', 0),// 虚拟销量
'max_buy' => input('max_buy', 0),// 限购
'min_buy' => input('min_buy', 0),// 起售
'recommend_way' => input('recommend_way', 0), // 推荐方式1新品2精品3推荐
'timer_on' => strtotime(input('timer_on', 0)),//定时上架
'timer_off' => strtotime(input('timer_off', 0)),//定时下架
'spec_type_status' => input('spec_type_status', 0),
'is_consume_discount' => input('is_consume_discount', 0),//是否参与会员折扣
'qr_id' => input('qr_id', 0),// 社群二维码id
'template_id' => input('template_id', 0), // 商品海报id
'sale_show' => input('sale_show', 0),//
'stock_show' => input('stock_show', 0),//
'market_price_show' => input('market_price_show', 0),//
'barrage_show' => input('barrage_show', 0),//
'form_id' => input('form_id', 0),
'sale_channel' => input('sale_channel', 'all'),
'sale_store' => input('sale_store', 'all'),
'validity_type' => input('validity_type', 0),
'discount_goods_type' => input('discount_goods_type', 'all'),
'discount' => input('discount', 0),
'common_num' => input('common_num', 0),
'relation_goods' => input('relation_goods', '[]'),
'is_unify_price' => input('is_unify_price', 1),
'supplier_id' => input('supplier_id', 0)
];
if ($data[ 'validity_type' ] == 1) {
$data[ 'validity_day' ] = input('validity_day', 0);
} else if ($data[ 'validity_type' ] == 2) {
$data[ 'validity_time' ] = strtotime(input('validity_time', ''));
}
$res = $virtual_goods_model->editGoods($data);
return $res;
} else {
$goods_model = new GoodsModel();
$goods_id = input('goods_id', 0);
$goods_info = $virtual_goods_model->editGetGoodsInfo([ 'goods_id' => $goods_id, 'site_id' => $this->site_id ])[ 'data' ];
if (empty($goods_info)) $this->error('未获取到商品数据', href_url('shop/goods/lists'));
$goods_sku_list = $goods_model->getGoodsSkuList([ [ 'goods_id', '=', $goods_id ], [ 'site_id', '=', $this->site_id ] ], 'sku_id,sku_name,sku_no,sku_spec_format,price,market_price,cost_price,stock,virtual_indate,sku_image,sku_images,goods_spec_format,spec_name,stock_alarm,is_default', '')[ 'data' ];
$goods_info[ 'sku_list' ] = $goods_sku_list;
$this->assign('goods_info', $goods_info);
// 商品服务
$goods_service_model = new GoodsServiceModel();
$service_list = $goods_service_model->getServiceList([ [ 'site_id', '=', $this->site_id ] ], 'id,service_name,icon')[ 'data' ];
$this->assign('service_list', $service_list);
// 商品品牌
$goods_brand_model = new GoodsBrandModel();
$brand_list = $goods_brand_model->getBrandList([ [ 'site_id', '=', $this->site_id ] ], 'brand_id,brand_name', 'sort asc')[ 'data' ];
$this->assign('brand_list', $brand_list);
//获取商品海报
$poster_list = event('PosterTemplate', [ 'site_id' => $this->site_id ], true);
if (!empty($poster_list)) {
$poster_list = $poster_list[ 'data' ];
}
$this->assign('poster_list', $poster_list);
$form_is_exit = addon_is_exit('form', $this->site_id);
if ($form_is_exit) {
$form_list = ( new Form() )->getFormList([ [ 'site_id', '=', $this->site_id ], [ 'form_type', '=', 'goods' ], [ 'is_use', '=', 1 ] ], 'id desc', 'id, form_name')[ 'data' ];
$this->assign('form_list', $form_list);
}
$this->assign('form_is_exit', $form_is_exit);
$store_is_exit = addon_is_exit('store', $this->site_id);
if ($store_is_exit && $goods_info[ 'sale_store' ] != 'all') {
$store_list = ( new StoreModel() )->getStoreList([ [ 'site_id', '=', $this->site_id ], [ 'store_id', 'in', $goods_info[ 'sale_store' ] ] ], 'store_id,store_name,status,address,full_address,is_frozen');
$this->assign('store_list', $store_list[ 'data' ]);
}
$this->assign('store_is_exit', $store_is_exit);
$this->assign('card_type', $virtual_goods_model->getCardType());
$is_install_supply = addon_is_exit('supply');
if ($is_install_supply) {
$supplier_model = new SupplierModel();
$supplier_list = $supplier_model->getSupplyList([ [ 'supplier_site_id', '=', $this->site_id ] ], 'supplier_id,title', 'supplier_id desc')['data'];
$this->assign('supplier_list', $supplier_list);
}
$this->assign('is_install_supply', $is_install_supply);
return $this->fetch('card/edit_goods');
}
}
public function goodscard()
{
$goods_id = input('goods_id', 0);
$model = new MemberCardModel();
if (request()->isJson()) {
$page_index = input('page', 1);
$page_size = input('page_size', PAGE_LIST_ROWS);
$search_text = input('search_text', '');
$condition = [
[ 'mgc.site_id', '=', $this->site_id ],
[ 'mgc.goods_id', '=', $goods_id ],
];
if (!empty($search_text)) {
$condition[] = [ 'm.nickname', 'like', '%' . $search_text . '%' ];
}
$field = 'mgc.*, g.goods_name,g.price,g.goods_image,m.username,m.nickname,m.headimg';
$join = [
[ 'goods g', 'mgc.goods_id = g.goods_id', 'inner' ],
[ 'member m', 'mgc.member_id = m.member_id', 'left' ],
];
$list = $model->getCardPageList($condition, $field, 'mgc.create_time desc', $page_index, $page_size, 'mgc', $join);
return $list;
} else {
$this->assign('goods_id', $goods_id);
$goods_model = new GoodsModel();
$goods_info = $goods_model->getGoodsInfo([ [ 'goods_id', '=', $goods_id ], [ 'site_id', '=', $this->site_id ] ], 'goods_id, goods_name,goods_image,price,goods_state,goods_stock,sale_num');
$this->assign('goods_info', $goods_info[ 'data' ]);
$card_stat = $model->getCardInfo([ [ 'goods_id', '=', $goods_id ], [ 'site_id', '=', $this->site_id ] ], 'sum(total_num) as total_num, sum(total_use_num) as total_use_num')[ 'data' ] ?? [];
$this->assign('total_num', $card_stat[ 'total_num' ] ?? 0);
$this->assign('total_use_num', $card_stat[ 'total_use_num' ] ?? 0);
$card_info = $model->getCardSelect([ [ 'goods_id', '=', $goods_id ], [ 'site_id', '=', $this->site_id ] ])[ 'data' ] ?? [];
$this->assign('card_info', $card_info);
return $this->fetch('card/goods_card');
}
}
public function membergoodscard()
{
$member_id = input('member_id', 0);
if (request()->isJson()) {
$page_index = input('page', 1);
$page_size = input('page_size', PAGE_LIST_ROWS);
$search_text = input('search_text', '');
$condition = [
[ 'mgc.site_id', '=', $this->site_id ],
[ 'mgc.member_id', '=', $member_id ],
];
if (!empty($search_text)) {
$condition[] = [ 'g.goods_name', 'like', '%' . $search_text . '%' ];
}
$model = new MemberCardModel();
$card_goods = new CardGoods();
$field = 'mgc.*, g.goods_name,g.price,g.goods_image,m.username,m.nickname,m.headimg';
$join = [
[ 'goods g', 'mgc.goods_id = g.goods_id', 'inner' ],
[ 'member m', 'mgc.member_id = m.member_id', 'left' ],
];
$list = $model->getCardPageList($condition, $field, 'mgc.create_time desc', $page_index, $page_size, 'mgc', $join);
foreach ($list[ 'data' ][ 'list' ] as $k => $v) {
$list[ 'data' ][ 'list' ][ $k ][ 'card_type_name' ] = $card_goods->getCardType($v[ 'card_type' ])[ 'title' ];
}
return $list;
} else {
$this->assign('member_id', $member_id);
return $this->fetch('card/member_goods_card');
}
}
public function detail()
{
$card_id = input('card_id', 0);
$model = new MemberCardModel();
$card_goods = new CardGoods();
$condition = [
[ 'mgc.card_id', '=', $card_id ],
[ 'mgc.site_id', '=', $this->site_id ],
];
$field = 'mgc.*, g.goods_name,g.price,g.goods_image,m.username,m.nickname,m.headimg';
$join = [
[ 'goods g', 'mgc.goods_id = g.goods_id', 'inner' ],
[ 'member m', 'mgc.member_id = m.member_id', 'left' ],
];
$detail = $model->getCardInfo($condition, $field, 'mgc', $join)[ 'data' ] ?? [];
$detail[ 'card_type_name' ] = $card_goods->getCardType($detail[ 'card_type' ])[ 'title' ];
$this->assign('detail', $detail);
$condition = [];
$condition[] = [ 'mgc.card_id', '=', $card_id ];
$condition[] = [ 'g.goods_state', '=', 1 ];
$condition[] = [ 'g.is_delete', '=', 0 ];
$field = 'mgc.*, g.sku_name';
$join = [
[ 'goods_sku g', 'mgc.sku_id = g.sku_id', 'left' ],
];
$item_list = $model->getCartItemList($condition, $field, 'mgc.item_id asc', 'mgc', $join)[ 'data' ] ?? [];
$this->assign('item_list', $item_list);
return $this->fetch('card/detail');
}
public function getCardItem()
{
if (request()->isJson()) {
$page_index = input('page', 1);
$page_size = input('page_size', PAGE_LIST_ROWS);
$card_id = input('card_id', 0);
$condition = [];
$condition[] = [ 'mgc.site_id', '=', $this->site_id ];
$condition[] = [ 'mgc.card_id', '=', $card_id ];
$condition[] = [ 'g.goods_state', '=', 1 ];
$condition[] = [ 'g.is_delete', '=', 0 ];
$alias = 'mgc';
$field = 'mgc.*, g.goods_name,g.price,g.goods_image,g.introduction,m.nickname,m.headimg,m.mobile';
$join = [
[ 'goods g', 'mgc.goods_id = g.goods_id', 'inner' ],
[ 'member m', 'mgc.member_id = m.member_id', 'left' ],
];
$model = new MemberCardModel();
$card_goods = new CardGoods();
$list = $model->getCartItemPageList($condition, $field, 'mgc.item_id asc', $page_index, $page_size, $alias, $join);
foreach ($list[ 'data' ][ 'list' ] as $k => $v) {
$list[ 'data' ][ 'list' ][ $k ][ 'card_type_name' ] = $card_goods->getCardType($v[ 'card_type' ])[ 'title' ];
}
return $list;
}
}
public function records()
{
if (request()->isJson()) {
$page_index = input('page', 1);
$page_size = input('page_size', PAGE_LIST_ROWS);
$card_id = input('card_id', 0);
$item_id = input('item_id', 0);
$condition = [];
$condition[] = [ 'cr.site_id', '=', $this->site_id ];
if (!empty($item_id)) {
$condition[] = [ 'cr.card_item_id', '=', $item_id ];
}
if (!empty($card_id)) {
$condition[] = [ 'cr.card_id', '=', $card_id ];
}
$alias = 'cr';
$prefix = config('database.connections.mysql.prefix');
$field = 'cr.*, sku.sku_name,sku.sku_image,sku.sku_images,sku.price,ci.num as item_num,
IF(cr.type = \'order\', (select order_id from `' . $prefix . 'order_goods` og where og.order_goods_id = cr.relation_id), 0) as order_id, s.store_name';
$join = [
[ 'member_goods_card_item ci', 'ci.item_id = cr.card_item_id', 'left' ],
[ 'goods_sku sku', 'ci.sku_id = sku.sku_id', 'left' ],
[ 'store s', 'cr.store_id = s.store_id', 'left' ],
];
$model = new MemberCardModel();
$list = $model->getMemberCardRecordsPageList($condition, $field, 'cr.create_time desc', $page_index, $page_size, $alias, $join);
return $list;
}
}
}

View File

@@ -0,0 +1,464 @@
<?php
/**
* Niushop商城系统 - 团队十年电商经验汇集巨献!
* =========================================================
* Copy right 2019-2029 上海牛之云网络科技有限公司, 保留所有权利。
* ----------------------------------------------
* 官方网址: https://www.niushop.com
* 这不是一个自由软件!您只能在不用于商业目的的前提下对程序代码进行修改和使用。
* 任何企业和个人不允许对程序代码以任何形式任何目的再发布。
* =========================================================
*/
namespace addon\cardservice\shop\controller;
use addon\cardservice\model\Reserve as ReserveModel;
use app\dict\goods\GoodsDict;
use app\model\store\Store as StoreModel;
use app\model\goods\Goods as GoodsModel;
use app\model\system\User;
use app\model\system\UserGroup;
use app\shop\controller\BaseShop;
use think\App;
class Reserve extends BaseShop
{
public function __construct(App $app = null)
{
$this->replace = [
'ADDON_CARDSERVICE_CSS' => __ROOT__ . '/addon/cardservice/shop/view/public/css',
'ADDON_CARDSERVICE_JS' => __ROOT__ . '/addon/cardservice/shop/view/public/js',
'ADDON_CARDSERVICE_IMG' => __ROOT__ . '/addon/cardservice/shop/view/public/img',
];
parent::__construct($app);
}
/**
* 预约看板
* @return mixed
*/
public function index()
{
$this->assign('reserve_state', ( new ReserveModel )->reserve_state);
$data = $this->getReserveByWeek();
$this->assign('data', $data[ 'data' ]);
$store_model = new StoreModel();
$store_list = $store_model->getStoreList([ [ 'site_id', '=', $this->site_id ], [ 'status', '=', 1 ] ])[ 'data' ] ?? [];
$this->assign('store_list', $store_list);
return $this->fetch('reserve/index');
}
/**
* 获取一周内的时间段
* @param $length
* @return array
*/
public function getWeekDay($length = 0)
{
$first_day = mktime(0, 0, 0, date("m"), date("d") - date("w") + 1, date("Y"));
$first_day = strtotime($length . ' week', $first_day);
$week = [ '周日', '周一', '周二', '周三', '周四', '周五', '周六' ];
$data = [];
for ($i = 0; $i < 7; $i++) {
$time = strtotime("+ {$i} day", $first_day);
$data[] = [
'start' => $time,
'end' => strtotime(date('Y-m-d 23:59:59', $time)),
'date' => date('m/d', $time),
'w' => date('w', $time),
'week' => $week[date('w', $time)],
'currday' => date('Y-m-d', $time) == date('Y-m-d') ? 1 : 0
];
}
return success(0, '', $data);
}
public function getMonthDays()
{
if (request()->isJson()) {
$reserve_model = new ReserveModel();
$year = input('year', '');
$month = input('month', '');
$days_data = $reserve_model->getMonthDays($year, $month)[ 'data' ];
return success(0, '', $days_data);
}
}
public function getReserveByWeek()
{
$reserve_model = new ReserveModel();
$length = input('length', 0);
$data = $this->getWeekDay($length)[ 'data' ];
foreach ($data as $wk => $w_item) {
$field = 'noy.reserve_id,noy.reserve_state,noy.reserve_time,nm.nickname,noy.reserve_item';
$list = $reserve_model->getReservePageList([
[ 'noy.site_id', '=', $this->site_id ],
[ 'noy.reserve_time', 'between', [ $w_item[ 'start' ], $w_item[ 'end' ] ] ]
], 1, PAGE_LIST_ROWS, 'noy.create_time desc', $field);
$data[ $wk ][ 'data' ] = $list[ 'data' ];
}
return success(0, '', $data);
}
/**
* 获取预约周数据
* @return array
*/
public function getReserveWeekData()
{
if (request()->isJson()) {
$reserve_model = new ReserveModel();
$week_offset = input('week_offset', 0);
$days_data = $reserve_model->getWeekDays($week_offset)[ 'data' ];
$res = $reserve_model->getReserveDataByDays([
'days_data' => $days_data,
'query_num' => 4,
'site_id' => $this->site_id,
]);
return $res;
}
}
/**
* 获取预约月数据
* @return array
*/
public function getYuYueMonthData()
{
if (request()->isJson()) {
$reserve_model = new ReserveModel();
$year = input('year', date('Y'));
$month = input('year', date('m'));
$days_data = $reserve_model->getMonthDays($year, $month)[ 'data' ];
$res = $reserve_model->getReserveDataByDays([
'days_data' => $days_data,
'query_num' => 3,
'site_id' => $this->site_id,
]);
return $res;
}
}
/**
* 添加预约
* @return mixed
*/
public function addReserve()
{
$reserve_model = new ReserveModel();
if (request()->isJson()) {
return $reserve_model->addReserve([
'site_id' => $this->site_id,
'app_module' => $this->app_module,
'member_id' => input('member_id'),
'goods' => json_decode(input('goods'), true),
'store_id' => input('store_id'),
'date' => input('date'),
'time' => input('time'),
'remark' => input('remark', ''),
'source' => 'store'
]);
}
$service_model = new GoodsModel();
$condition = [
[ 'g.site_id', '=', $this->site_id ],
[ 'g.goods_state', '=', 1 ],
[ 'g.goods_class', '=', GoodsDict::service ]
];
$service = $service_model->getGoodsList($condition, 'g.goods_id,g.sku_id,g.goods_name,g.price,sku.service_length', 'g.create_time desc'
, 0, 'g', [
[ 'goods_sku sku', 'sku.goods_id=g.goods_id', 'inner' ]
]);
$this->assign('service', $service[ 'data' ]);
$store_model = new StoreModel();
$store_list = $store_model->getStoreList([ [ 'site_id', '=', $this->site_id ], [ 'status', '=', 1 ] ])[ 'data' ] ?? [];
$this->assign('store_list', $store_list);
$config = $reserve_model->getReserveConfig($this->site_id, $store_list[ 0 ][ 'store_id' ]);
$this->assign('config', $config[ 'data' ][ 'value' ]);
$user_model = new User();
$user_list = $user_model->getUserList([ [ 'site_id', '=', $this->site_id ], [ 'app_module', '=', 'servicer' ], [ 'status', '=', 1 ] ])[ 'data' ] ?? [];
$this->assign('user_list', $user_list);
return $this->fetch('reserve/add_reserve');
}
/**
* 修改预约
* @return mixed|void
*/
public function updateReserve()
{
$model = new ReserveModel();
if (request()->isJson()) {
$reserve_id = input('reserve_id', 0);
return $model->editReserve([
'site_id' => $this->site_id,
'app_module' => $this->app_module,
'reserve_id' => $reserve_id,
'store_id' => input('store_id'),
'goods' => json_decode(input('goods'), true),
'date' => input('date'),
'time' => input('time'),
'remark' => input('remark', '')
]);
} else {
$reserve_id = input('id', 0);
// 查询预约信息
$info = $model->getReserveInfo([
[ 'reserve_id', '=', $reserve_id ],
[ 'oy.site_id', '=', $this->site_id ]
], 'oy.*, nm.headimg, nm.nickname, nm.mobile, os.store_name', 'oy', [
[ 'member nm', 'oy.member_id = nm.member_id', 'left' ],
[ 'store os', 'oy.store_id = os.store_id', 'left' ]
])[ 'data' ];
if (empty($info)) {
$this->error('未获取到预约信息');
return;
}
$info[ 'item' ] = $model->getReserveItemList([
[
'oyi.reserve_id', '=', $reserve_id
],
], 'g.goods_name,sku.service_length,g.goods_id,g.sku_id,g.price,ys.username,oyi.reserve_user_id', 'reserve_item_id desc', 'oyi',
[
[ 'goods g', 'g.sku_id = oyi.reserve_goods_sku_id', 'right' ],
[ 'goods_sku sku', 'sku.sku_id = oyi.reserve_goods_sku_id', 'right' ],
[ 'user ys', 'oyi.reserve_user_id = ys.uid', 'left' ]
])[ 'data' ];
$this->assign('info', $info);
// 查询预约配置
$config = $model->getReserveConfig($this->site_id, $info[ 'store_id' ]);
$this->assign('config', $config[ 'data' ][ 'value' ]);
// 查询可预约服务
$service_model = new GoodsModel();
$condition = [
[ 'g.site_id', '=', $this->site_id ],
[ 'g.goods_state', '=', 1 ],
[ 'g.goods_class', '=', GoodsDict::service ]
];
$service = $service_model->getGoodsList($condition, 'g.goods_id,g.sku_id,g.goods_name,g.price,sku.service_length', 'g.create_time desc'
, 0, 'g', [
[ 'goods_sku sku', 'sku.goods_id=g.goods_id', 'inner' ]
]);
$this->assign('service', $service[ 'data' ]);
$store_model = new StoreModel();
$store_list = $store_model->getStoreList([ [ 'site_id', '=', $this->site_id ], [ 'status', '=', 1 ] ])[ 'data' ] ?? [];
$this->assign('store_list', $store_list);
$user_model = new User();
$user_list = $user_model->getUserList([ [ 'site_id', '=', $this->site_id ], [ 'app_module', '=', 'servicer' ], [ 'status', '=', 1 ] ])[ 'data' ] ?? [];
$this->assign('user_list', $user_list);
return $this->fetch('reserve/add_reserve');
}
}
/**
* 预约列表
*/
public function lists()
{
$reserve_model = new ReserveModel();
if (request()->isJson()) {
$page = input('page', 1);
$page_size = input('page_size', PAGE_LIST_ROWS);
$search_text = input('search_text', '');
$reserve_state = input('reserve_state', 'all');
$start = input('start_time', 0);
$end = input('end_time', 0);
$store_id = !empty(input('store_id', 0)) ? input('store_id', 0) : $this->store_id;
$condition = [
[ 'noy.site_id', '=', $this->site_id ]
];
if ($reserve_state != 'all') {
$condition[] = [ 'noy.reserve_state', '=', $reserve_state ];
}
if (!empty($search_text)) {
$condition[] = [ 'nm.mobile|nm.nickname', 'like', '%' . $search_text . '%' ];
}
if (!empty($store_id)) {
$condition[] = [ 'noy.store_id', 'in', $store_id ];
}
if ($start && $end) {
$condition[] = [ 'noy.reserve_time', 'between', [ $start, $end ] ];
} else {
if ($start && !$end) {
$condition[] = [ 'noy.reserve_time', '>=', $start ];
} else {
if (!$start && $end) {
$condition[] = [ 'noy.reserve_time', '<=', $end ];
}
}
}
$field = 'noy.store_id, noy.member_id, noy.remark, noy.reserve_id, noy.reserve_name, noy.reserve_state_name, noy.reserve_state, noy.reserve_time, noy.reserve_item, noy.create_time, noy.source, nm.headimg, nm.nickname, nm.mobile, os.store_name';
$result = $reserve_model->getReservePageList($condition, $page, $page_size, 'noy.create_time desc', $field);
return $result;
} else {
$this->assign('reserve_state', $reserve_model->reserve_state);
$start_time = input('start_time', '');
$end_time = input('end_time', '');
$this->assign('start_time', $start_time);
$this->assign('end_time', $end_time);
$store_model = new StoreModel();
$store_list = $store_model->getStoreList([ [ 'site_id', '=', $this->site_id ], [ 'status', '=', 1 ] ])[ 'data' ] ?? [];
$this->assign('store_list', $store_list);
return $this->fetch('reserve/lists');
}
}
public function servicerList()
{
if (request()->isJson()) {
$store_id = !empty(input('store_id', 0)) ? input('store_id', 0) : $this->store_id;
$condition = [
[ 'u.site_id', '=', $this->site_id ],
];
$condition[] = [ 'ug.store_id', '=', $store_id ];
$user_model = new UserGroup();
$result = $user_model->getUserList($condition, 'u.username,u.status,u.uid,u.group_name', 'u.uid desc', 'ug', [
[ 'user u', 'ug.uid=u.uid', 'left' ]
]);
return $result;
}
}
/**
* 预约设置
* @return mixed
*/
public function getConfig()
{
if (request()->isJson()) {
$model = new ReserveModel();
$store_id = input('store_id', 0);
$config = $model->getReserveConfig($this->site_id, $store_id);
return $config;
}
}
/**
* 确认预约
* @return array
*/
public function confirm()
{
if (request()->isJson()) {
$reserve_id = input('reserve_id', 0);
$reserve_model = new ReserveModel();
return $reserve_model->confirmReserve($reserve_id, $this->site_id);
}
}
/**
* 取消预约
* @return array
*/
public function cancel()
{
if (request()->isJson()) {
$reserve_id = input('reserve_id', 0);
$reserve_model = new ReserveModel();
return $reserve_model->cancelReserve($reserve_id, $this->site_id);
}
}
/**
* 删除预约
* @return array
*/
public function deleteReserve()
{
if (request()->isJson()) {
$reserve_id = input('reserve_id', 0);
$reserve_model = new ReserveModel();
return $reserve_model->deleteReserve($reserve_id, $this->site_id);
}
}
/**
* 确认到店
* @return array
*/
public function confirmToStore()
{
if (request()->isJson()) {
$reserve_id = input('reserve_id', 0);
$reserve_model = new ReserveModel();
return $reserve_model->confirmToStore($reserve_id, $this->site_id);
}
}
/**
* 确认完成
* @return array
*/
public function complete()
{
if (request()->isJson()) {
$reserve_id = input('reserve_id', 0);
$reserve_model = new ReserveModel();
return $reserve_model->confirmComplete($reserve_id, $this->site_id);
}
}
/**
* 预约详情
* @return mixed|void
*/
public function detail()
{
$reserve_id = input('id', 0);
$model = new ReserveModel();
$info = $model->getReserveInfo([
[ 'reserve_id', '=', $reserve_id ],
[ 'oy.site_id', '=', $this->site_id ]
], 'oy.*, nm.headimg, nm.nickname, nm.mobile,os.store_name', 'oy', [
[ 'member nm', 'oy.member_id = nm.member_id', 'left' ],
[ 'store os', 'oy.store_id = os.store_id', 'left' ]
])[ 'data' ];
if (empty($info)) {
$this->error('未获取到预约信息');
return;
}
$info[ 'item' ] = $model->getReserveItemList([
[
'oyi.reserve_id', '=', $reserve_id
]
], 'g.goods_name,sku.service_length,g.goods_id,g.sku_id,g.price,ys.username,oyi.reserve_user_id', 'reserve_item_id desc', 'oyi',
[
[ 'goods g', 'g.sku_id = oyi.reserve_goods_sku_id', 'right' ],
[ 'goods_sku sku', 'sku.sku_id = oyi.reserve_goods_sku_id', 'right' ],
[ 'user ys', 'oyi.reserve_user_id = ys.uid', 'left' ]
])[ 'data' ];
$this->assign('info', $info);
return $this->fetch('reserve/detail');
}
}

View File

@@ -0,0 +1,267 @@
<?php
/**
* Niushop商城系统 - 团队十年电商经验汇集巨献!
* =========================================================
* Copy right 2019-2029 杭州牛之云科技有限公司, 保留所有权利。
* ----------------------------------------------
* 官方网址: https://www.niushop.com
* =========================================================
*/
namespace addon\cardservice\shop\controller;
use addon\cardservice\model\ServiceGoods;
use addon\form\model\Form;
use app\model\goods\Goods;
use app\model\goods\Goods as GoodsModel;
use app\model\goods\GoodsService as GoodsServiceModel;
use app\model\store\Store as StoreModel;
use app\model\system\User;
use app\model\web\Config as ConfigModel;
use app\shop\controller\BaseShop;
use think\App;
/**
* 服务项目商品
* Class Service
* @package addon\cardservice\shop\controller
*/
class Service extends BaseShop
{
public function __construct(App $app = null)
{
$this->replace = [
'ADDON_CARDSERVICE_CSS' => __ROOT__ . '/addon/cardservice/shop/view/public/css',
'ADDON_CARDSERVICE_JS' => __ROOT__ . '/addon/cardservice/shop/view/public/js',
'ADDON_CARDSERVICE_IMG' => __ROOT__ . '/addon/cardservice/shop/view/public/img',
];
parent::__construct($app);
}
/**
* 添加商品
* @return mixed
*/
public function addGoods()
{
if (request()->isJson()) {
$category_id = input("category_id", 0);// 分类id
$category_json = json_encode($category_id);//分类字符串
$category_id = ',' . implode(',', $category_id) . ',';
$data = [
'goods_name' => input("goods_name", ""),// 商品名称,
'goods_attr_class' => input("goods_attr_class", ""),// 商品类型id,
'goods_attr_name' => input("goods_attr_name", ""),// 商品类型名称,
'is_limit' => input("is_limit", "0"),// 商品是否限购,
'limit_type' => input("limit_type", "1"),// 商品限购类型,
'site_id' => $this->site_id,
'category_id' => $category_id,
'category_json' => $category_json,
'goods_image' => input("goods_image", ""),// 商品主图路径
'goods_content' => input("goods_content", ""),// 商品详情
'goods_state' => input("goods_state", ""),// 商品状态1.正常0下架
'price' => input("price", 0),// 商品价格取第一个sku
'market_price' => input("market_price", 0),// 市场价格取第一个sku
'cost_price' => input("cost_price", 0),// 成本价取第一个sku
'sku_no' => input("sku_no", ""),// 商品sku编码
'weight' => input("weight", ""),// 重量
'volume' => input("volume", ""),// 体积
'goods_stock' => input("goods_stock", 0),// 商品库存(总和)
'goods_stock_alarm' => input("goods_stock_alarm", 0),// 库存预警
'goods_spec_format' => input("goods_spec_format", ""),// 商品规格格式
'goods_attr_format' => input("goods_attr_format", ""),// 商品参数格式
'introduction' => input("introduction", ""),// 促销语
'keywords' => input("keywords", ""),// 关键词
'unit' => input("unit", ""),// 单位
'sort' => input("sort", 0),// 排序,
'video_url' => input("video_url", ""),// 视频
'goods_sku_data' => input("goods_sku_data", ""),// SKU商品数据
'goods_service_ids' => input("goods_service_ids", ''),// 商品服务id集合
'label_id' => input("label_id", ''),// 商品分组id
'virtual_sale' => input("virtual_sale", 0),// 虚拟销量
'max_buy' => input("max_buy", 0),// 限购
'min_buy' => input("min_buy", 0),// 起售
'recommend_way' => input('recommend_way', 0), // 推荐方式1新品2精品3推荐
'timer_on' => strtotime(input('timer_on', 0)),//定时上架
'timer_off' => strtotime(input('timer_off', 0)),//定时下架
'is_consume_discount' => input('is_consume_discount', 0),//是否参与会员折扣
'qr_id' => input('qr_id', 0),// 社群二维码id
'template_id' => input('template_id', 0), // 商品海报id
'sale_show' => input('sale_show', 0),//
'stock_show' => input('stock_show', 0),//
'market_price_show' => input('market_price_show', 0),//
'barrage_show' => input('barrage_show', 0),//
'form_id' => input('form_id', 0),
'sale_channel' => input('sale_channel', 'all'),
'sale_store' => input('sale_store', 'all'),
'is_unify_price' => input('is_unify_price', '1'),
'verify_validity_type' => input("verify_validity_type", 0),// 服务有效期类型
'is_reserve' => input('is_reserve', 0),
'service_mode' => input('service_mode', ''),
'service_price_way' => input('service_price_way', '')
];
if ($data[ 'verify_validity_type' ] == 1) {
$data[ 'virtual_indate' ] = input("virtual_indate", 0);
} else if ($data[ 'verify_validity_type' ] == 2) {
$data[ 'virtual_indate' ] = strtotime(input("virtual_time", ''));
}
$service_goods_model = new ServiceGoods();
$res = $service_goods_model->addGoods($data);
return $res;
} else {
// 商品服务
$goods_service_model = new GoodsServiceModel();
$service_list = $goods_service_model->getServiceList([ [ 'site_id', '=', $this->site_id ] ], 'id,service_name,icon')[ 'data' ];
$this->assign("service_list", $service_list);
//商品默认排序值
$config_model = new ConfigModel();
$sort_config = $config_model->getGoodsSort($this->site_id)[ 'data' ][ 'value' ];
$this->assign("sort_config", $sort_config);
//获取商品海报
$poster_list = event('PosterTemplate', [ 'site_id' => $this->site_id ], true);
if (!empty($poster_list)) {
$poster_list = $poster_list[ 'data' ];
}
$this->assign('poster_list', $poster_list);
$this->assign('virtualcard_exit', addon_is_exit('virtualcard', $this->site_id));
$form_is_exit = addon_is_exit('form', $this->site_id);
if ($form_is_exit) {
$form_list = ( new Form() )->getFormList([ [ 'site_id', '=', $this->site_id ], [ 'form_type', '=', 'goods' ], [ 'is_use', '=', 1 ] ], 'id desc', 'id, form_name')[ 'data' ];
$this->assign('form_list', $form_list);
}
$this->assign('form_is_exit', $form_is_exit);
$this->assign('all_goodsclass', event('GoodsClass'));
$this->assign('goods_class', ( new ServiceGoods() )->getGoodsClass());
$this->assign('store_is_exit', addon_is_exit('store', $this->site_id));
return $this->fetch("service/add_goods");
}
}
/**
* 编辑商品
* @return mixed
*/
public function editGoods()
{
$service_goods_model = new ServiceGoods();
if (request()->isJson()) {
$category_id = input("category_id", 0);// 分类id
$category_json = json_encode($category_id);//分类字符串
$category_id = ',' . implode(',', $category_id) . ',';
$data = [
'goods_id' => input("goods_id", 0),// 商品id
'goods_name' => input("goods_name", ""),// 商品名称,
'goods_attr_class' => input("goods_attr_class", ""),// 商品类型id,
'goods_attr_name' => input("goods_attr_name", ""),// 商品类型名称,
'is_limit' => input("is_limit", "0"),// 商品是否限购,
'limit_type' => input("limit_type", "1"),// 商品限购类型,
'site_id' => $this->site_id,
'category_id' => $category_id,
'category_json' => $category_json,
'goods_image' => input("goods_image", ""),// 商品主图路径
'goods_content' => input("goods_content", ""),// 商品详情
'goods_state' => input("goods_state", ""),// 商品状态1.正常0下架
'price' => input("price", 0),// 商品价格取第一个sku
'market_price' => input("market_price", 0),// 市场价格取第一个sku
'cost_price' => input("cost_price", 0),// 成本价取第一个sku
'sku_no' => input("sku_no", ""),// 商品sku编码
'weight' => input("weight", ""),// 重量
'volume' => input("volume", ""),// 体积
'goods_stock' => input("goods_stock", 0),// 商品库存(总和)
'goods_stock_alarm' => input("goods_stock_alarm", 0),// 库存预警
'goods_spec_format' => input("goods_spec_format", ""),// 商品规格格式
'goods_attr_format' => input("goods_attr_format", ""),// 商品参数格式
'introduction' => input("introduction", ""),// 促销语
'keywords' => input("keywords", ""),// 关键词
'unit' => input("unit", ""),// 单位
'sort' => input("sort", 0),// 排序,
'video_url' => input("video_url", ""),// 视频
'goods_sku_data' => input("goods_sku_data", ""),// SKU商品数据
'goods_service_ids' => input("goods_service_ids", ''),// 商品服务id集合
'label_id' => input("label_id", ''),// 商品分组id
'virtual_sale' => input("virtual_sale", 0),// 虚拟销量
'max_buy' => input("max_buy", 0),// 限购
'min_buy' => input("min_buy", 0),// 起售
'recommend_way' => input('recommend_way', 0), // 推荐方式1新品2精品3推荐
'timer_on' => strtotime(input('timer_on', 0)),//定时上架
'timer_off' => strtotime(input('timer_off', 0)),//定时下架
'spec_type_status' => input('spec_type_status', 0),
'is_consume_discount' => input('is_consume_discount', 0),//是否参与会员折扣
'qr_id' => input('qr_id', 0),// 社群二维码id
'template_id' => input('template_id', 0), // 商品海报id
'sale_show' => input('sale_show', 0),//
'stock_show' => input('stock_show', 0),//
'market_price_show' => input('market_price_show', 0),//
'barrage_show' => input('barrage_show', 0),//
'form_id' => input('form_id', 0),
'sale_channel' => input('sale_channel', 'all'),
'sale_store' => input('sale_store', 'all'),
'is_unify_price' => input('is_unify_price', '1'),
'verify_validity_type' => input("verify_validity_type", 0),
'is_reserve' => input('is_reserve', 0),
'service_mode' => input('service_mode', ''),
'service_price_way' => input('service_price_way', '')
];
if ($data[ 'verify_validity_type' ] == 1) {
$data[ 'virtual_indate' ] = input("virtual_indate", 0);
} else if ($data[ 'verify_validity_type' ] == 2) {
$data[ 'virtual_indate' ] = strtotime(input("virtual_time", ''));
}
$res = $service_goods_model->editGoods($data);
return $res;
} else {
$goods_model = new GoodsModel();
$goods_id = input("goods_id", 0);
$goods_info = $service_goods_model->getGoodsInfo([ [ 'goods_id', '=', $goods_id ], [ 'site_id', '=', $this->site_id ] ], '*')[ 'data' ];
if (empty($goods_info)) $this->error('未获取到商品信息');
$goods_sku_list = $goods_model->getGoodsSkuList([ [ 'goods_id', '=', $goods_id ], [ 'site_id', '=', $this->site_id ] ], "sku_id,sku_name,sku_no,sku_spec_format,price,market_price,cost_price,stock,virtual_indate,sku_image,sku_images,goods_spec_format,spec_name,stock_alarm,is_default,service_length", '')[ 'data' ];
$goods_info[ 'sku_list' ] = $goods_sku_list;
$this->assign("goods_info", $goods_info);
// 商品服务
$goods_service_model = new GoodsServiceModel();
$service_list = $goods_service_model->getServiceList([ [ 'site_id', '=', $this->site_id ] ], 'id,service_name,icon')[ 'data' ];
$this->assign("service_list", $service_list);
//获取商品海报
$poster_list = event('PosterTemplate', [ 'site_id' => $this->site_id ], true);
if (!empty($poster_list)) {
$poster_list = $poster_list[ 'data' ];
}
$this->assign('poster_list', $poster_list);
$form_is_exit = addon_is_exit('form', $this->site_id);
if ($form_is_exit) {
$form_list = ( new Form() )->getFormList([ [ 'site_id', '=', $this->site_id ], [ 'form_type', '=', 'goods' ], [ 'is_use', '=', 1 ] ], 'id desc', 'id, form_name')[ 'data' ];
$this->assign('form_list', $form_list);
}
$this->assign('form_is_exit', $form_is_exit);
$store_is_exit = addon_is_exit('store', $this->site_id);
if ($store_is_exit && $goods_info[ 'sale_store' ] != 'all') {
$store_list = ( new StoreModel() )->getStoreList([ [ 'site_id', '=', $this->site_id ], [ 'store_id', 'in', $goods_info[ 'sale_store' ] ] ], 'store_id,store_name,status,address,full_address,is_frozen');
$this->assign('store_list', $store_list[ 'data' ]);
}
$this->assign('store_is_exit', $store_is_exit);
return $this->fetch("service/edit_goods");
}
}
}

View File

@@ -0,0 +1,282 @@
<?php
/**
* Niushop商城系统 - 团队十年电商经验汇集巨献!
* =========================================================
* Copy right 2019-2029 杭州牛之云科技有限公司, 保留所有权利。
* ----------------------------------------------
* 官方网址: https://www.niushop.com
* =========================================================
*/
namespace addon\cardservice\shop\controller;
use app\model\goods\ServiceCategory as ServiceCategoryModel;
use app\shop\controller\BaseShop;
use think\App;
/**
* 项目分类管理 控制器
*/
class Servicecategory extends BaseShop
{
public function __construct(App $app = null)
{
$this->replace = [
'ADDON_CARDSERVICE_CSS' => __ROOT__ . '/addon/cardservice/shop/view/public/css',
'ADDON_CARDSERVICE_JS' => __ROOT__ . '/addon/cardservice/shop/view/public/js',
'ADDON_CARDSERVICE_IMG' => __ROOT__ . '/addon/cardservice/shop/view/public/img',
];
parent::__construct($app);
}
/**
* 服务分类列表
*/
public function lists()
{
$goods_category_model = new ServiceCategoryModel();
$condition[] = [ 'site_id', '=', $this->site_id ];
$field = 'category_id,category_name,short_name,pid,level,is_show,sort,image,category_id_1,category_id_2,category_id_3';
$order = 'pid asc,category_id asc';
$list = $goods_category_model->getCategoryTree($condition, $field);
if (request()->isJson()) return $list;
$list = $list[ 'data' ];
$this->assign("list", $list);
return $this->fetch('category/lists');
}
/**
* 服务分类添加
*/
public function addCategory()
{
$goods_category_model = new ServiceCategoryModel();
if (request()->isJson()) {
$category_name = input('category_name', ''); // 分类名称
$short_name = input('short_name', ''); // 简称
$pid = input('pid', 0); //默认添加的服务分类为顶级
$level = input('level', 1); // 层级
$is_show = input('is_show', ''); // 是否显示
$image = input('image', ''); // 分类图片
$image_adv = input('image_adv', ''); // 分类广告图片
$category_id_1 = input('category_id_1', 0); // 一级分类id
$category_id_2 = input('category_id_2', 0); // 二级分类id
$category_full_name = input('category_full_name', ''); // 组装名称
$link_url = input('link_url', '');// 广告链接
$data = [
'site_id' => $this->site_id,
'category_name' => $category_name,
'short_name' => $short_name,
'pid' => $pid,
'level' => $level,
'is_show' => $is_show,
'image' => $image,
'image_adv' => $image_adv,
'category_id_1' => $category_id_1,
'category_id_2' => $category_id_2,
'category_full_name' => $category_full_name,
'link_url' => $link_url
];
$res = $goods_category_model->addCategory($data);
if (!empty($res[ 'data' ])) {
//修改category_id_
$update_data = [
'category_id' => $res[ 'data' ],
'category_id_' . $level => $res[ 'data' ],
'site_id' => $this->site_id
];
$goods_category_model->editCategory($update_data);
}
return $res;
} else {
return $this->fetch('category/add_category');
}
}
/**
* 服务分类编辑
*/
public function editCategory()
{
$goods_category_model = new ServiceCategoryModel();
if (request()->isJson()) {
$category_id = input('category_id', '');// 分类id
$category_name = input('category_name', '');// 分类名称
$short_name = input('short_name', '');// 简称
$pid = input('pid', 0);//默认添加的服务分类为顶级
$level = input('level', 1);// 层级
$is_show = input('is_show', 0);// 是否显示
$image = input('image', '');// 分类图片
$image_adv = input('image_adv', '');// 分类广告图片
$link_url = input('link_url', '');// 广告链接
$data = [
'site_id' => $this->site_id,
'category_id' => $category_id,
'category_name' => $category_name,
'short_name' => $short_name,
'pid' => $pid,
'level' => $level,
'is_show' => $is_show,
'image' => $image,
'image_adv' => $image_adv,
'link_url' => $link_url
];
$this->addLog("编辑服务分类:" . $category_name);
$res = $goods_category_model->editCategory($data);
return $res;
} else {
$category_id = input('category_id', '');// 分类id
if (empty($category_id)) {
$this->error("缺少参数category_id");
}
$goods_category_info = $goods_category_model->getCategoryInfo([ [ 'category_id', '=', $category_id ], [ 'site_id', '=', $this->site_id ] ])[ 'data' ];
if (empty($goods_category_info)) $this->error('未获取到分类数据', href_url('shop/category/lists'));
$this->assign("goods_category_info", $goods_category_info);
//父级
$goods_category_parent_info = $goods_category_model->getCategoryInfo([ [ 'category_id', '=', $goods_category_info[ 'pid' ] ], [ 'site_id', '=', $this->site_id ] ], 'category_name');
$this->assign("goods_category_parent_info", $goods_category_parent_info[ 'data' ]);
$condition = [];
$condition[] = [ 'site_id', '=', $this->site_id ];
$condition[] = [ 'category_id', '<>', $category_id ];
$field = 'category_id,category_name,short_name,pid,level,is_show,sort,image,category_id_1,category_id_2,category_id_3';
$list = $goods_category_model->getCategoryTree($condition, $field);
$this->assign("list", $list[ 'data' ]);
return $this->fetch('category/edit_category');
}
}
/**
* 服务分类删除
*/
public function deleteCategory()
{
if (request()->isJson()) {
$category_id = input('category_id', '');// 分类id
$goods_category_model = new ServiceCategoryModel();
$res = $goods_category_model->deleteCategory($category_id, $this->site_id);
$this->addLog("删除服务分类id:" . $category_id);
return $res;
}
}
/**
* 获取服务分类列表
* @return \multitype
*/
public function getCategoryList()
{
$pid = input('pid', 0);// 上级id
$level = input('level', 0);// 层级
$goods_category_model = new ServiceCategoryModel();
if (!empty($level)) {
$condition = [
[ 'level', '=', $level ]
];
} else {
$condition = [
[ 'pid', '=', $pid ]
];
}
$condition[] = [ 'site_id', '=', $this->site_id ];
$list = $goods_category_list = $goods_category_model->getCategoryList($condition, 'category_id,category_name,pid,level,category_id_1,category_id_2,category_id_3', 'sort asc,category_id desc');
return $list;
}
/**
* 获取服务分类信息
* @return \multitype
*/
public function getCategoryInfo()
{
$category_id = input('category_id', '');// 分类id
$goods_category_model = new ServiceCategoryModel();
$condition = [
[ 'category_id', '=', $category_id ]
];
$res = $goods_category_model->getCategoryInfo($condition, 'category_name');
return $res;
}
/**
* 获取服务分类
* @return \multitype
*/
public function getCategoryByParent()
{
$pid = input('pid', 0);// 上级id
$level = input('level', 0);// 层级
$goods_category_model = new ServiceCategoryModel();
if (!empty($level)) {
$condition[] = [ 'level', '=', $level ];
}
if (!empty($pid)) {
$condition[] = [ 'pid', '=', $pid ];
}
$condition[] = [ 'site_id', '=', $this->site_id ];
$list = $goods_category_list = $goods_category_model->getCategoryByParent($condition, 'category_id,category_name,pid,level,category_id_1,category_id_2,category_id_3');
return $list;
}
/**
* 修改服务分类排序
*/
public function modifySort()
{
if (request()->isJson()) {
$sort = input('sort', 0);
$category_id = input('category_id', 0);
$category_sort_array = input('category_sort_array', '');
$goods_category_model = new ServiceCategoryModel();
if (!empty($category_sort_array)) {
$category_sort_array = json_decode($category_sort_array, true);
foreach ($category_sort_array as $k => $v) {
$res = $goods_category_model->modifyGoodsCategorySort($v[ 'sort' ], $v[ 'category_id' ], $this->site_id);
}
} else {
$res = $goods_category_model->modifyGoodsCategorySort($sort, $category_id, $this->site_id);
}
return $res;
}
}
public function checkEditCategory()
{
if (request()->isJson()) {
$pid = input('pid', 0);
$category_id = input('category_id', 0);
$goods_category_model = new ServiceCategoryModel();
$res = $goods_category_model->checkEditCategory([
'pid' => $pid,
'category_id' => $category_id,
'site_id' => $this->site_id
]);
return $res;
}
}
/**
* 显示/隐藏
* @return array
*/
public function modifyShow(){
$category_id = input('id','');
$is_show = input('is_show',0);
$goods_category_model = new ServiceCategoryModel();
return $goods_category_model->modifyCategoryShow($category_id,$is_show);
}
}

View File

@@ -0,0 +1,764 @@
<link rel="stylesheet" href="__STATIC__/ext/video/video.css">
<link rel="stylesheet" type="text/css" href="__STATIC__/ext/searchable_select/searchable_select.css" />
<link rel="stylesheet" type="text/css" href="__STATIC__/ext/layui/extend/cascader/cascader.css"/>
<link rel="stylesheet" type="text/css" href="SHOP_CSS/goods_edit.css?time=20250527" />
<link rel="stylesheet" type="text/css" href="ADDON_CARDSERVICE_CSS/card_goods_edit.css" />
<div class="layui-form">
<div class="layui-tab layui-tab-brief" lay-filter="goods_tab">
<ul class="layui-tab-title">
<li class="layu1i-this" lay-id="basic">基础设置</li>
<li lay-id="price-stock">价格库存</li>
<li lay-id="detail">卡项详情</li>
<li lay-id="senior">高级设置</li>
</ul>
<div class="layui-tab-content">
<!-- 基础设置 -->
<div class="layui-tab-item layui-show">
<!-- 卡项类型 -->
<div class="layui-card card-common card-brief head">
<div class="layui-card-header">
<span class="card-title">商品类型</span>
</div>
<div class="layui-card-body commodity-type-box" >
{foreach name="all_goodsclass" item="vo"}
<div class="commodity-type-item {if $vo.goods_class eq $goods_class.id}border-color{/if}" onclick="location.hash = ns.hash('{$vo.add_url}')">
<span>{$vo.goods_class_name}</span>
<span>{$vo.is_virtual ? '(无需物流)' : '(需要物流)'}</span>
</div>
{/foreach}
</div>
</div>
<div class="layui-card card-common card-brief head">
<div class="layui-card-header">
<span class="card-title">基础信息</span>
</div>
<div class="layui-card-body">
<div class="layui-form-item">
<label class="layui-form-label"><span class="required">*</span>卡项类型:</label>
<div class="layui-input-block card-type">
{foreach name="card_type" item="vo" index="index"}
<div class="card-type-item {if $index == 1}active{/if}" data-value="{$vo.type}">
<div class="title">{$vo.title}</div>
<div class="desc">{$vo.desc}</div>
<i class="iconfont iconxuanzhongjiaobiao text-color"></i>
</div>
{/foreach}
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"><span class="required">*</span>卡项名称:</label>
<div class="layui-input-inline">
<input name="goods_name" type="text" placeholder="请输入卡项名称不能超过60个字符" maxlength="60" autocomplete="off" lay-verify="goods_name" class="layui-input len-long">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">促销语:</label>
<div class="layui-input-inline">
<textarea class="layui-textarea len-long" name="introduction" maxlength="100" lay-verify="introduction" placeholder="请输入促销语不能超过100个字符"></textarea>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">关键词:</label>
<div class="layui-input-block">
<input type="text" name="keywords" placeholder="卡项关键词用于SEO搜索不能超过100个字符" maxlength="100" autocomplete="off" class="layui-input len-long">
</div>
</div>
<div class="layui-form-item goods-image-wrap">
<label class="layui-form-label"><span class="required">*</span>卡项主图:</label>
<div class="layui-input-block">
<!--卡项主图项-->
<div class="js-goods-image"></div>
</div>
<div class="word-aux">第一张图片将作为卡项主图,支持同时上传多张图片,多张图片之间可随意调整位置;</div>
<div class="word-aux">支持jpg、gif、png格式上传或从图片空间中选择建议使用尺寸800x800像素以上、大小不超过1M的正方形图片</div>
<div class="word-aux">上传后的图片将会自动保存在图片空间的默认分类中最多上传10张至少1张</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">卡项视频:</label>
<div class="layui-input-block">
<div class="video-thumb">
<video id="goods_video" class="video-js vjs-big-play-centered" controls="" poster="SHOP_IMG/goods_video_preview.png" preload="auto"></video>
</div>
<div id="videoUpload2" class="up-video " title="卡项视频" >
<span class="delete-video hide" onclick="deleteVideo()"><img class="del-img" src="SHOP_IMG/delete.png">删除</span>
<span class="replace-video hide js-add-goods-video" ><img class="up-img" src="SHOP_IMG/upload.png">上传视频</span>
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"></label>
<div class="layui-input-block">
<input type="text" name="video_url" placeholder="在此输入外链视频地址" autocomplete="off" class="layui-input len-long">
</div>
<div class="file-title word-aux">
<div>注意事项:</div>
<ul>
<li>1、检查upload文件夹是否有读写权限。</li>
<li>2、PHP默认上传限制为2MB需要在php.ini配置文件中修改“post_max_size”和“upload_max_filesize”的大小。</li>
<li>3、视频支持手动输入外链视频地址或者上传本地视频文件</li>
<li>4、必须上传.mp4视频格式</li>
<li>5、视频文件大小不能超过500MB</li>
</ul>
</div>
</div>
<div class="card-type-content discountcard" style="display: none">
<div class="layui-form-item">
<label class="layui-form-label"><span class="required">*</span>适用商品:</label>
<div class="layui-input-block">
<input type="radio" name="discount_goods_type" value="all" title="全部商品" checked lay-filter="discount_goods_type">
<input type="radio" name="discount_goods_type" value="part" title="部分商品" lay-filter="discount_goods_type">
</div>
</div>
<div class="layui-form-item discount-goods all">
<label class="layui-form-label"><span class="required">*</span>折扣卡折扣:</label>
<div class="layui-input-inline">
<input type="number" name="discount" placeholder="0" class="layui-input len-short" lay-verify="common_discount" autocomplete="off">
</div>
<div class="layui-form-mid layui-word-aux">%</div>
<div class="layui-form-mid word-aux">购卡后购买商品或服务时可享折扣 1-99之间</div>
</div>
<div class="layui-form-item discount-goods part" style="display: none">
<label class="layui-form-label"></label>
<div class="layui-input-block">
<button class="layui-btn add-relation-goods">选择商品</button>
<div class="relation-goods-table discountcard" lay-verify="relation_discount_goods">
<table id="relationDiscountGoods"></table>
</div>
</div>
<div class="word-aux batch-set">
<div class="batch-set-wrap">
<span>批量操作:</span>
<div class="set-item">
<a href="javascript:;" class="text-color set">可享受折扣</a>
</div>
<div class="set-content-wrap">
<input type="number" placeholder="0" class="layui-input len-short value" autocomplete="off" data-type="discount">
<button class="layui-btn confirm">确认</button>
<button class="layui-btn layui-btn-primary cancel">取消</button>
</div>
</div>
</div>
</div>
</div>
<div class="layui-form-item card-type-content oncecard timecard commoncard">
<label class="layui-form-label"><span class="required">*</span>卡项内容:</label>
<div class="layui-input-block">
<a href="javascript:;" class="text-color add-relation-goods" data-goods-class="4">添加卡项项目</a>
<a href="javascript:;" class="text-color add-relation-goods" data-goods-class="1">添加实物商品</a>
<div class="relation-goods-table oncecard" lay-verify="relation_goods">
<table id="relationGoods"></table>
</div>
</div>
<div class="word-aux batch-set">
<div class="batch-set-wrap">
<span>批量操作:</span>
<div class="set-item">
<a href="javascript:;" class="text-color set">可用次数</a>
</div>
<div class="set-content-wrap">
<input type="number" placeholder="0" class="layui-input len-short value" autocomplete="off" data-type="num">
<button class="layui-btn confirm">确认</button>
<button class="layui-btn layui-btn-primary cancel">取消</button>
</div>
</div>
</div>
</div>
<div class="layui-form-item card-type-content commoncard" style="display: none">
<label class="layui-form-label"><span class="required">*</span>可用次数/数量:</label>
<div class="layui-input-block">
<input type="text" name="common_num" placeholder="0" lay-verify="common_num" class="layui-input len-short" autocomplete="off">
</div>
<div class="layui-form-mid word-aux">卡项内项目/商品总的可用次数</div>
</div>
{notempty name="$service_list"}
<div class="layui-form-item">
<label class="layui-form-label">卡项服务:</label>
<div class="layui-input-block">
{foreach name="$service_list" item="vo"}
<input type="checkbox" name="goods_service_ids" value="{$vo.id}" title="{$vo.service_name}" lay-skin="primary">
{/foreach}
</div>
</div>
{/notempty}
<div class="layui-form-item goods_state">
<label class="layui-form-label"><span class="required">*</span>是否上架:</label>
<div class="layui-input-block">
<input type="radio" name="goods_state" value="1" title="立刻上架" checked lay-filter="goods_state">
<input type="radio" name="goods_state" value="0" title="放入仓库" lay-filter="goods_state">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">定时下架:</label>
<div class="layui-input-block">
<input type="radio" name="timer_off_status" value="1" title="启用" lay-filter="timer_off">
<input type="radio" name="timer_off_status" value="2" title="不启用" lay-filter="timer_off" checked>
</div>
<div class="word-aux">启用定时下架后,到达设定时间,此卡项将自动下架。</div>
</div>
<div class="layui-form-item timer_off" style="display: none;">
<label class="layui-form-label"></label>
<div class="layui-input-inline">
<input type="text" id="timer_off" name="timer_off" class="layui-input len-mid" autocomplete="off" readonly>
<i class=" iconrili iconfont calendar"></i>
</div>
</div>
{if addon_is_exit('cashier') == 1}
<div class="layui-form-item">
<label class="layui-form-label">销售渠道:</label>
<div class="layui-input-block">
<input type="radio" name="sale_channel" value="all" title="线上线下销售" checked>
<input type="radio" name="sale_channel" value="online" title="线上销售">
<input type="radio" name="sale_channel" value="offline" title="线下销售">
</div>
</div>
{/if}
{if $store_is_exit}
<div class="layui-form-item">
<label class="layui-form-label">适用门店:</label>
<div class="layui-input-block">
<input type="radio" name="sale_store" value="all" title="全部门店" checked lay-filter="sale_store">
<input type="radio" name="sale_store" value="" title="部分门店" lay-filter="sale_store">
</div>
</div>
<div class="layui-form-item sale-store-select" style="display: none" lay-verify="sale_store">
<label class="layui-form-label"></label>
<div class="layui-input-block">
<button class="layui-btn select-store">选择门店</button>
<div style="width: 700px">
<table class="layui-table" lay-skin="nob">
<colgroup>
<col width="30%">
<col width="60%">
<col width="10%">
</colgroup>
<tr>
<th>门店名称</th>
<th>门店地址</th>
<th>操作</th>
</tr>
<tbody class="sale-store"></tbody>
</table>
</div>
</div>
</div>
{/if}
<div class="layui-form-item">
<label class="layui-form-label">卡项有效期:</label>
<div class="layui-input-block">
<input type="radio" name="validity_type" value="0" title="永久" checked lay-filter="validity_type">
<input type="radio" name="validity_type" value="1" title="购买后几日有效" lay-filter="validity_type">
<input type="radio" name="validity_type" value="2" title="指定过期日期" lay-filter="validity_type">
</div>
</div>
<div class="layui-form-item validity-type validity-type-1 layui-hide">
<label class="layui-form-label"><span class="required">*</span>有效期:</label>
<div class="layui-input-inline">
<input type="text" name="validity_day" placeholder="0" class="layui-input len-short" lay-verify="validity_day" autocomplete="off">
</div>
<div class="layui-form-mid layui-word-aux"></div>
</div>
<div class="layui-form-item validity-type validity-type-2 layui-hide">
<label class="layui-form-label"><span class="required">*</span>有效期:</label>
<div class="layui-input-inline">
<input type="text" id="validity_time" name="validity_time" class="layui-input len-mid" lay-verify="validity_time" autocomplete="off" readonly>
<i class=" iconrili iconfont calendar"></i>
</div>
</div>
{if $store_is_exit}
<div class="layui-form-item">
<label class="layui-form-label">是否统一售价:</label>
<div class="layui-input-block">
<input type="radio" name="is_unify_price" value="1" title="是" checked >
<input type="radio" name="is_unify_price" value="0" title="否" >
</div>
<div class="word-aux">价格设置之后门店不能修改价格,门店按照平台设置的价格售卖</div>
</div>
{/if}
</div>
</div>
</div>
<!-- 价格库存 -->
<div class="layui-tab-item">
<div class="layui-form-item layui-hide">
<label class="layui-form-label">启用多规格:</label>
<div class="layui-input-inline">
<input type="checkbox" value="1" lay-skin="switch" name="spec_type" lay-filter="spec_type" lay-verify="spec_type">
<input type="hidden" id="spec_type_status" value="0">
</div>
</div>
<!-- 单规格 -->
<div class="js-single-spec">
<div class="layui-form-item">
<label class="layui-form-label"><span class="required">*</span>开卡价格:</label>
<div class="layui-input-block">
<input type="text" name="price" placeholder="0.00" lay-verify="price" class="layui-input len-short" autocomplete="off">
<div class="layui-form-mid"></div>
</div>
<div class="word-aux">卡项购买价格</div>
</div>
<!--<div class="layui-form-item">
<label class="layui-form-label"><span class="required">*</span>续费价格:</label>
<div class="layui-input-block">
<input type="text" name="renew_price" placeholder="0.00" lay-verify="renew_price" class="layui-input len-short" autocomplete="off">
<div class="layui-form-mid">元</div>
</div>
<div class="word-aux">卡项续费价格</div>
</div>-->
<div class="layui-form-item">
<label class="layui-form-label">划线价:</label>
<div class="layui-input-block">
<input type="text" name="market_price" placeholder="0.00" lay-verify="market_price" class="layui-input len-short" autocomplete="off">
<div class="layui-form-mid"></div>
</div>
<div class="word-aux">卡项没有优惠活动显示的划线价格,如果卡项有折扣等优惠活动划线价显示销售价</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">成本价:</label>
<div class="layui-input-block">
<input type="text" name="cost_price" placeholder="0.00" class="layui-input len-short" lay-verify="cost_price" autocomplete="off">
<div class="layui-form-mid"></div>
</div>
<div class="word-aux">成本价将不会对前台会员展示,用于商家统计使用</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">卡项编码:</label>
<div class="layui-input-inline">
<input type="text" name="sku_no" placeholder="请输入卡项编码" maxlength="50" class="layui-input len-long" autocomplete="off">
</div>
</div>
</div>
<!-- 多规格 -->
<div class="js-more-spec">
<!--规格项/规格值-->
<div class="spec-edit-list"></div>
<div class="layui-form-item js-add-spec">
<label class="layui-form-label"></label>
<div class="layui-input-inline">
<button class="layui-btn" type="button">添加规格</button>
</div>
</div>
<div class="layui-form-item batch-operation-sku">
<label class="layui-form-label">批量操作:</label>
<div class="layui-input-inline">
<span class="text-color" data-field="spec_name">副标题</span>
<span class="text-color" data-field="price" data-verify="price">销售价</span>
<span class="text-color" data-field="market_price" data-verify="market_price">划线价</span>
<span class="text-color" data-field="cost_price" data-verify="cost_price">成本价</span>
<span class="text-color" data-field="stock" data-verify="stock">库存</span>
<span class="text-color" data-field="stock_alarm" data-verify="stock_alarm">库存预警</span>
<span class="text-color" data-field="sku_no" data-verify="">卡项编码</span>
<input type="text" class="layui-input len-short" name="batch_operation_sku" autocomplete="off" />
<button class="layui-btn confirm" type="button">确定</button>
<button class="layui-btn layui-btn-primary cancel" type="button">取消</button>
</div>
</div>
<!--sku列表-->
<div class="layui-form-item sku-table">
<label class="layui-form-label"></label>
<div class="layui-input-block"></div>
</div>
</div>
<div class="layui-form-item js-goods-stock-wrap">
<label class="layui-form-label"><span class="required">*</span>库存:</label>
<div class="layui-input-block">
<input type="number" name="goods_stock" placeholder="0" lay-verify="goods_stock" class="layui-input len-short" autocomplete="off">
<div class="layui-form-mid"></div>
</div>
</div>
<div class="layui-form-item js-goods-stock-wrap">
<label class="layui-form-label">库存预警:</label>
<div class="layui-input-block">
<input type="number" name="goods_stock_alarm" placeholder="0" lay-verify="goods_stock_alarm" class="layui-input len-short" autocomplete="off">
<div class="layui-form-mid"></div>
</div>
<div class="word-aux">卡项库存少于预警数量卡项列表库存数量标红显示0为不预警。</div>
</div>
<div class="layui-form-item js-goods-stock-wrap">
<label class="layui-form-label">服务时长:</label>
<div class="layui-input-block">
<input type="number" name="service_length" placeholder="0" lay-verify="service_length" class="layui-input len-short" autocomplete="off">
<div class="layui-form-mid">分钟</div>
</div>
</div>
<div class="layui-form-item ">
<label class="layui-form-label">虚拟销量:</label>
<div class="layui-input-block">
<input type="number" name="virtual_sale" placeholder="0" lay-verify="virtual_sale" class="layui-input len-short" autocomplete="off">
<div class="layui-form-mid"></div>
</div>
<div class="word-aux">该设置不计入卡项统计数据</div>
</div>
<div class="layui-form-item is_limit">
<label class="layui-form-label">是否限购:</label>
<div class="layui-input-block">
<input type="radio" name="is_limit" value="0" title="否" lay-filter="is_limit" checked>
<input type="radio" name="is_limit" value="1" title="是" lay-filter="is_limit">
</div>
<div class="word-aux">启用限购后,购买卡项时,会对该卡项购买量做限制判断。</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">起售:</label>
<div class="layui-input-block">
<input type="number" name="min_buy" placeholder="" lay-verify="min_buy" class="layui-input len-short" autocomplete="off">
<div class="layui-form-mid"></div>
</div>
<div class="word-aux">起售数量超出卡项库存时,买家无法购买该卡项</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">会员等级折扣:</label>
<div class="layui-input-block">
<div class="layui-input-inline">
<input type="radio" name="is_consume_discount" value="1" title="参与" checked>
<input type="radio" name="is_consume_discount" value="0" title="不参与">
</div>
</div>
<div class="word-aux">按照默认会员等级折扣优惠</div>
</div>
</div>
<!-- 卡项详情 -->
<div class="layui-tab-item">
<div class="layui-form-item">
<label class="layui-form-label sm"></label>
<div class="layui-input-inline special-length">
<script id="editor" type="text/plain" style="width:100%;height:500px;"></script>
</div>
</div>
<script type="text/javascript" charset="utf-8" src="__STATIC__/ext/ueditor/ueditor.config.js"></script>
<script type="text/javascript" charset="utf-8" src="__STATIC__/ext/ueditor/ueditor.all.js"> </script>
<script type="text/javascript" charset="utf-8" src="__STATIC__/ext/ueditor/lang/zh-cn/zh-cn.js"></script>
</div>
<div class="layui-tab-item layui-card card-common card-brief head">
<div class="layui-card-header">
<span class="card-title">高级设置</span>
</div>
<div class="layui-card-body">
<div class="layui-form-item">
<label class="layui-form-label">排序:</label>
<div class="layui-input-block">
<input type="number" name="sort" class="layui-input len-short" value="{$sort_config['default_value']}" placeholder="0" autocomplete="off">
</div>
<div class="word-aux">卡项默认排序号为0数字越大排序越靠前数字重复则最新添加的靠前。</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">卡项详情显示库存:</label>
<div class="layui-input-block">
<input type="radio" name="stock_show" value="1" title="显示" checked >
<input type="radio" name="stock_show" value="0" title="隐藏" >
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">划线价显示:</label>
<div class="layui-input-block">
<input type="radio" name="market_price_show" value="1" title="显示" checked>
<input type="radio" name="market_price_show" value="0" title="隐藏" >
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">卡项详情显示弹幕:</label>
<div class="layui-input-block">
<input type="radio" name="barrage_show" value="1" title="显示" checked>
<input type="radio" name="barrage_show" value="0" title="隐藏" >
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">卡项详情显示销量:</label>
<div class="layui-input-block">
<input type="radio" name="sale_show" value="1" title="显示" checked>
<input type="radio" name="sale_show" value="0" title="隐藏" >
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">卡项海报:</label>
<div class="layui-input-inline">
<select name="template_id" lay-search="" lay-verify="">
<option value="">请选择卡项海报</option>
{foreach name="$poster_list" item="vo"}
<option value="{$vo['template_id']}">{$vo['poster_name']}</option>
{/foreach}
</select>
</div>
</div>
{if $form_is_exit}
<div class="layui-form-item">
<label class="layui-form-label">卡项表单:</label>
<div class="layui-input-block len-mid">
<select name="form_id">
<option value="0">请选择卡项表单</option>
{foreach name="$form_list" item="vo"}
<option value="{$vo.id}">{$vo.form_name}</option>
{/foreach}
</select>
</div>
<div class="word-aux">
<a href="{:href_url('form://shop/form/addform?form_type=goods')}" class="text-color" target="_blank">创建卡项表单</a>
<a href="javascript:;" onclick="refreshFormList()" class="text-color">刷新</a>
</div>
</div>
{/if}
</div>
</div>
</div>
</div>
<div class="fixed-btn">
<button class="layui-btn layui-btn-primary border-color text-color js-prev" lay-filter="prev">上一步</button>
<button class="layui-btn js-save" lay-submit="" lay-filter="save">保存</button>
<button class="layui-btn layui-btn-primary border-color text-color js-next" lay-submit="" lay-filter="next">下一步</button>
</div>
</div>
<!--规格项模板-->
<script type="text/html" id="specTemplate">
{{# for(var i=0;i<d.list.length;i++){ }}
<div class="spec-item" data-index="{{i}}">
<div class="layui-form-item spec">
<label class="layui-form-label">规格项{{i+1}}</label>
<div class="layui-input-inline">
<select name="spec_item">
<option value="0"></option>
{{# if(d.list[i].spec_name != ''){ }}
<option value="{{d.list[i].spec_id}}" data-attr-name="{{d.list[i].spec_name}}" selected>{{d.list[i].spec_name}}</option>
{{# }else{ }}
{{# } }}
</select>
<i class="layui-icon layui-icon-close" data-index="{{i}}"></i>
</div>
{{# if(i==0){ }}
<div class="layui-input-inline">
{{# if(d.add_spec_img){ }}
<input type="checkbox" name="add_spec_img" title="添加规格图片" lay-skin="primary" lay-filter="add_spec_img" checked>
{{# }else{ }}
<input type="checkbox" name="add_spec_img" title="添加规格图片" lay-skin="primary" lay-filter="add_spec_img">
{{# } }}
</div>
{{# } }}
</div>
{{# if(d.list[i].spec_name != ''){ }}
<div class="layui-form-item spec-value">
{{# }else{ }}
<div class="layui-form-item spec-value" style="display:none;">
{{# } }}
<label class="layui-form-label"></label>
<div class="layui-input-block spec-value">
{{# if(d.list[i].value.length){ }}
<ul>
{{# for(var j=0;j<d.list[i].value.length;j++){ }}
<li data-index="{{j}}" data-parent-index="{{i}}" >
{{# if(i==0 && d.add_spec_img){ }}
<div class="img-wrap">
{{# if(d.list[i].value[j].image){ }}
<img src="{{ns.img(d.list[i].value[j].image)}}" alt="">
{{# }else{ }}
<img src="SHOP_IMG/goods_spec_value_empty.png" alt="">
{{# } }}
</div>
{{# } }}
<span title="双击可编辑规格值" ondblclick="$(this).attr('contenteditable',true);$(this).focus()" class="spec-txt" data-spec_value_name="{{d.list[i].value[j].spec_value_name}}" data-parent-index="{{i}}" data-index="{{j}}">{{d.list[i].value[j].spec_value_name}}</span>
<i class="layui-icon layui-icon-close" data-parent-index="{{i}}" data-index="{{j}}"></i>
</li>
{{# } }}
</ul>
{{# } }}
<a class="text-color" href="javascript:;" data-index="{{i}}">+添加规格值</a>
<div class="add-spec-value-popup" data-index="{{i}}">
<select name="spec_value_item"></select>
<button class="layui-btn layui-btn-primary border-color text-color js-cancel-spec-value">取消</button>
</div>
</div>
</div>
</div>
{{# } }}
</script>
<!--SKU列表模板-->
<script type="text/html" id="skuTableTemplate">
{{# if(d.skuList.length){ }}
<table class="layui-table">
<colgroup></colgroup>
<thead>
<tr>
{{# if(d.showSpecName){ }}
<th colspan="{{d.colSpan}}" style="min-width: 60px;">卡项规格</th>
{{# } }}
<th rowspan="{{d.rowSpan}}">SKU图片</th>
<th rowspan="{{d.rowSpan}}">副标题</th>
<th rowspan="{{d.rowSpan}}"><span class="required">*</span></th>
<th rowspan="{{d.rowSpan}}">划线价</th>
<th rowspan="{{d.rowSpan}}">成本价</th>
<th rowspan="{{d.rowSpan}}"><span class="required">*</span></th>
<th rowspan="{{d.rowSpan}}">库存预警</th>
<th rowspan="{{d.rowSpan}}">服务时长</th>
<th rowspan="{{d.rowSpan}}">商品编码多个编码以英文逗号分割</th>
<th rowspan="{{d.rowSpan}}" style="white-space: nowrap;">默认展示</th>
</tr>
{{# if(d.colSpan>1){ }}
<tr>
{{# for(var i=0;i<d.specList.length;i++){ }}
{{# if(d.specList[i].spec_name && d.specList[i].value.length> 0){ }}
<th>{{d.specList[i].spec_name}}</th>
{{# } }}
{{# } }}
</tr>
{{# } }}
</thead>
<tbody>
{{# for(var i=0;i<d.skuList.length;i++){ }}
<tr>
<td class="sku_imgs" id="sku_img_{{i}}" style="width: 130px;">
{{# for(var j=0;j<d.skuList[i].sku_images_arr.length;j++){ }}
<div class="img-wrap" data-index="{{j}}" data-parent-index="{{i}}">
<a href="javascript:void(0)">
<img src="{{ns.img(d.skuList[i].sku_images_arr[j])}}" layer-src />
</a>
<div class="operation">
<i title="图片预览" class="iconfont iconreview js-preview"></i>
<i title="删除图片" class="layui-icon layui-icon-delete js-delete"></i>
</div>
</div>
{{# } }}
{{# if(d.skuList[i].sku_images_arr.length<d.goods_sku_max){ }}
<div class="upload-sku-img" data-index="{{i}}"><i class="layui-icon layui-icon-add-1"></i></div>
{{# } }}
</td>
<td>
<input type="text" name="spec_name" placeholder="副标题" maxlength="100" value="{{d.skuList[i].spec_name}}" class="layui-input" autocomplete="off" data-index="{{i}}">
</td>
<td>
<input type="text" name="price" placeholder="销售价" lay-verify="sku_price" value="{{d.skuList[i].price}}" class="layui-input" autocomplete="off" data-index="{{i}}">
</td>
<td>
<input type="text" name="market_price" placeholder="划线价" value="{{d.skuList[i].market_price}}" lay-verify="sku_market_price" class="layui-input" autocomplete="off" data-index="{{i}}">
</td>
<td>
<input type="text" name="cost_price" placeholder="成本价" value="{{d.skuList[i].cost_price}}" lay-verify="sku_cost_price" class="layui-input" autocomplete="off" data-index="{{i}}">
</td>
<td>
<input type="text" name="stock" placeholder="库存" value="{{d.skuList[i].stock}}" lay-verify="sku_stock" class="layui-input" autocomplete="off" data-index="{{i}}">
</td>
<td>
<input type="text" name="stock_alarm" placeholder="库存预警" value="{{d.skuList[i].stock_alarm}}" lay-verify="sku_stock_alarm" class="layui-input" autocomplete="off" data-index="{{i}}">
</td>
<td>
<input type="number" name="service_length" placeholder="服务时长" value="{{d.skuList[i].service_length}}" maxlength="50" class="layui-input" autocomplete="off" data-index="{{i}}" lay-verify="service_length">
</td>
<td>
<input type="text" name="sku_no" placeholder="商品编码" value="{{d.skuList[i].sku_no}}" maxlength="50" class="layui-input" autocomplete="off" data-index="{{i}}">
</td>
<td style="min-width: 40px;">
{{# if(d.skuList[i].is_default == 1) { }}
<input type="checkbox" data-index="{{i}}" name="is_default" lay-filter="is_default" lay-skin="switch" checked>
{{# }else { }}
<input type="checkbox" data-index="{{i}}" name="is_default" lay-filter="is_default" lay-skin="switch">
{{# } }}
</td>
</tr>
{{# } }}
</tbody>
</table>
{{# } }}
<div class="word-aux text-color" style="margin: 10px 0 0 0;">默认展示是多规格卡项在客户访问卡项时默认显示的卡项规格</div>
</script>
<!--卡项主图列表-->
<script type="text/html" id="goodsImage">
{{# if(d.list.length){ }}
{{# for(var i=0;i<d.list.length;i++){ }}
<div class="item upload_img_square_item" data-index="{{i}}">
<div class="img-wrap">
<img src="{{ns.img(d.list[i],'small')}}" layer-src="{{ns.img(d.list[i],'big')}}">
</div>
<div class="operation">
<i title="图片预览" class="iconfont iconreview js-preview"></i>
<i title="删除图片" class="layui-icon layui-icon-delete js-delete" data-index="{{i}}"></i>
<div class="replace_img" data-index="{{i}}">点击替换</div>
</div>
</div>
{{# } }}
{{# if(d.list.length < d.max){ }}
<div class="item js-add-goods-image upload_img_square">+</div>
{{# } }}
{{# }else{ }}
<div class="item js-add-goods-image upload_img_square">+</div>
{{# } }}
</script>
<script>
var relationGoods = [];
</script>
<script src="__STATIC__/ext/drag-arrange.js"></script>
<script src="__STATIC__/ext/video/videojs-ie8.min.js"></script>
<script src="__STATIC__/ext/video/video.min.js"></script>
<script src="__STATIC__/ext/searchable_select/searchable_select.js"></script>
<script src="SHOP_JS/goods_edit_common.js?time=20250527"></script>
<script src="ADDON_CARDSERVICE_JS/card_goods_edit.js?v=1"></script>

View File

@@ -0,0 +1,247 @@
<style>
.card-common {margin-top: 15px;margin-bottom: 0;box-shadow: initial;}
.card-common .layui-card-body{padding-top: 0;}
.promotion-view{display:flex;flex-wrap:wrap}
.promotion-view-item{width:33.3%;padding-right:10px;box-sizing:border-box;line-height:30px}
.promotion-view-item-line{padding-right:10px;box-sizing:border-box;line-height:30px;overflow: hidden;width: 100%}
.promotion-view-item-custom-label{float:left}
.promotion-view-item-custom-box{float:left}
.promotion-stat-view{display:flex;flex-wrap:wrap}
.promotion-stat-view .promotion-stat-item{width:25%;padding:0 15px 10px 15px;box-sizing:border-box}
.promotion-stat-view .promotion-stat-item .promotion-stat-item-title{color:#909399;font-size:14px;margin-top:5px}
.promotion-stat-view .promotion-stat-item .promotion-stat-item-value{color:#303133;font-size:26px;margin-top:10px}
.todo-list .promotion-stat-item{flex:1;width:0;cursor:pointer}
.layui-layout-admin .layui-body .body-content {background: 0 0;padding: 0;}
.gift-card-goods span{cursor: pointer; color: var(--base-color);}
.layui-tab-title{margin-bottom: 15px;}
.layui-layout-admin .single-filter-box.top {padding-top: 0 !important;}
.add-way .add-way-item{display: flex;margin: 8px 0;align-items: center;}
.add-way .add-way-item input{margin: 0 10px;}
.add-way .add-way-item .layui-form-radio{margin-right: 0;}
.disabled-click{pointer-events: none;color: #999 !important;}
#addFile{margin-left: 10px; cursor: pointer;color: var(--base-color);}
.card-goods .layui-table{margin-bottom: 0;margin-top: 0;}
#card_right_type_goods #goods{border: 0;}
.card-goods .layui-table .goods-title{display: flex;align-items: center;}
.card-goods .layui-table .goods-title .goods-img{width: 55px;height: 55px;line-height: 55px;flex-shrink: 0;margin-right: 10px;}
.card-goods .layui-table .goods-title .goods-img img{max-width: 100%;max-height: 100%;}
.card-goods .layui-table-body{overflow: auto;max-height: 425px;margin-bottom: 15px;border-bottom: 1px solid #e6e6e6;}
.card-goods .layui-table-body .layui-table{border: none;}
.card-goods .layui-table-body tbody tr:last-of-type td{border: none;}
.card-goods .layui-table-head tr th:last-of-type{text-align: right;}
.card-goods .layui-table-body tr td:last-of-type{text-align: right;}
.card-tab{margin-bottom: 15px;margin-top: 15px;}
/* 商品列表 */
.shop-information-table > p{padding-left: 5px;padding-bottom: 5px;}
.shop-information-table table {width: 100%;border: 1px solid rgb(238,238,238);}
.shop-information-table .table-body {max-height: 400px;overflow: auto;}
.table-trOne{height: 48px;background:rgb(245,245,245) ;}
.shop-information-table th{text-align: left;padding-left:28px;box-sizing: border-box;font-weight: 500;color:#333333;}
.shop-information-table th:last-child{border:none;}
.table-trTow{width:100%;height:60px;border-top:1px solid rgb(238,238,238);}
.table-trTow>td{text-align: left;padding-left:28px;box-sizing: border-box;font-weight: 500;color:#333333;border-right:1px solid rgb(238,238,238);}
.table-trTow>td:nth-child(5){color:var(--base-color)}
.layui-tab-content {
padding: 0;
}
.layui-layout-admin .screen {
margin-bottom: 15px;
}
</style>
<div class="layui-card card-common card-brief">
<div class="layui-card-header">
<span class="card-title">基本信息</span>
</div>
<div class="layui-card-body">
<div class="promotion-view">
<div class="promotion-view-item">
<label>卡项名称:</label>
<span>{$detail.goods_name}</span>
</div>
<div class="promotion-view-item grouping">
<label>价格:</label>
<span>¥{$detail.price}</span>
</div>
<div class="promotion-view-item grouping">
<label>所属会员:</label>
<span>{$detail.nickname}</span>
</div>
<div class="promotion-view-item grouping">
<label>卡类型:</label>
<span>{$detail.card_type_name}</span>
</div>
<div class="promotion-view-item grouping">
<label>总次数/已使用:</label>
<span>{if $detail.card_type == "timecard"}不限{else/}{$detail.total_num} {/if}/{$detail.total_use_num}</span>
</div>
<div class="promotion-view-item grouping">
<label>获取时间:</label>
<span>{:date('Y-m-d H:i:s',$detail.create_time)}</span>
</div>
<div class="promotion-view-item grouping">
<label>过期时间:</label>
{if $detail.end_time > 0}
<span>{:date('Y-m-d H:i:s',$detail.end_time)}</span>
{else /}
<span>永久有效</span>
{/if}
</div>
<div class="promotion-view-item grouping">
<label>卡状态:</label>
{$detail.status == 1 ? '正常' : '已失效'}
</div>
</div>
</div>
</div>
<div class="layui-card card-common card-brief">
<div class="layui-card-header">
<span class="card-title">卡项商品</span>
</div>
<div class="layui-card-body layui-tab layui-tab-brief" lay-filter="edit_user_tab">
<ul class="layui-tab-title">
<li class="layui-this" lay-id="basic_info">商品信息</li>
<li lay-id="basic_info">使用记录</li>
</ul>
<div class="layui-tab-content">
<div class="layui-tab-item layui-show">
<table id="data_list" lay-filter="data_list"></table>
</div>
<div class="layui-tab-item">
<table id="recrods_list" lay-filter="recrods_list"></table>
</div>
</div>
</div>
</div>
<!-- 工具栏操作 -->
<script type="text/html" id="operation">
<div class="table-btn">
<a class="layui-btn" lay-event="order">查看订单</a>
</div>
</script>
<script type="text/html" id="memberInfo">
<div class='table-title'>
<div class='title-pic'>
<img layer-src src="{{ns.img(d.headimg)}}" onerror="this.src = '{:img('public/static/img/default_img/head.png')}' ">
</div>
<div class='title-content' onclick="location.hash = ns.hash('shop/member/editmember?member_id={{d.member_id}}')">
<p class="layui-elip">{{d.nickname}}</p>
<p class="layui-elip">{{d.mobile}}</p>
</div>
</div>
</script>
<script type="text/html" id="operation">
<div class="table-btn">
{{# if(d.status == 'to_activate'){ }}
<a class="layui-btn" lay-event="delete">删除</a>
{{# } }}
{{# if(d.status != 'to_activate'){ }}
<a class="layui-btn" lay-event="info">详情</a>
{{# } }}
</div>
</script>
<script>
var table,element,form,laytpl,repeat_flag=false,records_table;
layui.use(['form', 'element', 'laytpl','laydate'], function () {
laytpl = layui.laytpl;
element = layui.element;
form = layui.form;
form.render();
table = new Table({
elem: '#data_list',
url: ns.url("cardservice://shop/card/getcarditem", {'card_id':"{$detail['card_id']}"}),
cols: [
[{
field: 'goods_name',
title: '商品名称',
unresize: 'false',
width: '50%',
}, {
field: '',
title: '总次数',
unresize: 'false',
templet: function (data) {
var totalNum = data.card_type == 'timecard' ? '不限' : data.num;
return totalNum;
}
}, {
field: '',
title: '已使用',
unresize: 'false',
templet: function (data) {
return data.use_num;
}
}]
]
});
records_table = new Table({
elem: '#recrods_list',
url: ns.url("cardservice://shop/card/records", {'card_id':"{$detail['card_id']}"}),
cols: [
[{
field: 'sku_name',
title: '卡项名称',
unresize: 'false',
width: '20%',
},{
field: 'store_name',
title: '使用门店',
unresize: 'false',
width: '15%',
}, {
field: '',
title: '使用次数',
unresize: 'false',
width: '15%',
templet: function (data) {
return data.num;
}
}, {
title: '使用时间',
unresize: 'false',
width: '15%',
templet: function (data) {
return ns.time_to_date(data.create_time);
}
}, {
title: '操作',
toolbar: '#operation',
unresize: 'false',
align : 'right'
}]
]
});
/**
* 监听工具栏操作
*/
records_table.tool(function(obj) {
var data = obj.data;
switch (obj.event) {
case 'order':
window.open(ns.href("shop/order/detail?order_id=" + data.order_id));
break;
}
});
/**
* 搜索功能
*/
form.on('submit(search)', function (data) {
records_table.reload({
page: {curr: 1},
where: data.field
});
return false;
});
})
</script>

View File

@@ -0,0 +1,857 @@
<link rel="stylesheet" href="__STATIC__/ext/video/video.css">
<link rel="stylesheet" type="text/css" href="__STATIC__/ext/searchable_select/searchable_select.css" />
<link rel="stylesheet" type="text/css" href="__STATIC__/ext/layui/extend/cascader/cascader.css"/>
<link rel="stylesheet" type="text/css" href="SHOP_CSS/goods_edit.css?time=20250527" />
<link rel="stylesheet" type="text/css" href="ADDON_CARDSERVICE_CSS/card_goods_edit.css" />
<div class="layui-form">
<div class="layui-tab layui-tab-brief" lay-filter="goods_tab">
<ul class="layui-tab-title">
<li class="layu1i-this" lay-id="basic">基础设置</li>
<li lay-id="price-stock">价格库存</li>
<li lay-id="detail">卡项详情</li>
<li lay-id="senior">高级设置</li>
</ul>
<div class="layui-tab-content">
<!-- 基础设置 -->
<div class="layui-tab-item layui-show">
<div class="layui-card card-common card-brief head">
<div class="layui-card-header">
<span class="card-title">基础信息</span>
</div>
<div class="layui-card-body">
<div class="layui-form-item">
<label class="layui-form-label"><span class="required">*</span>卡项类型:</label>
<div class="layui-input-block card-type">
{foreach name="card_type" item="vo" index="index"}
{if $vo.type eq $goods_info.card_type}
<div class="card-type-item active" data-value="{$vo.type}">
<div class="title">{$vo.title}</div>
<div class="desc">{$vo.desc}</div>
<i class="iconfont iconxuanzhongjiaobiao text-color"></i>
</div>
{/if}
{/foreach}
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"><span class="required">*</span>卡项名称:</label>
<div class="layui-input-inline">
<input name="goods_name" type="text" value="{$goods_info['goods_name']}" placeholder="请输入卡项名称不能超过60个字符" maxlength="60" autocomplete="off" lay-verify="goods_name" class="layui-input len-long">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">促销语:</label>
<div class="layui-input-inline">
<textarea class="layui-textarea len-long" name="introduction" maxlength="100" lay-verify="introduction" placeholder="请输入促销语不能超过100个字符">{$goods_info['introduction']}</textarea>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">关键词:</label>
<div class="layui-input-block">
<input type="text" name="keywords" value="{$goods_info['keywords']}" placeholder="卡项关键词用于SEO搜索不能超过100个字符" maxlength="100" autocomplete="off" class="layui-input len-long">
</div>
</div>
<div class="layui-form-item goods-image-wrap">
<label class="layui-form-label"><span class="required">*</span>卡项主图:</label>
<div class="layui-input-block">
<!--卡项主图项-->
<div class="js-goods-image"></div>
</div>
<div class="word-aux">第一张图片将作为卡项主图,支持同时上传多张图片,多张图片之间可随意调整位置;</div>
<div class="word-aux">支持jpg、gif、png格式上传或从图片空间中选择建议使用尺寸800x800像素以上、大小不超过1M的正方形图片</div>
<div class="word-aux">上传后的图片将会自动保存在图片空间的默认分类中最多上传10张至少1张</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">卡项视频:</label>
<div class="layui-input-block">
<div class="video-thumb">
<video id="goods_video" class="video-js vjs-big-play-centered" controls="" poster="SHOP_IMG/goods_video_preview.png" preload="auto"></video>
</div>
<div id="videoUpload2" class="up-video " title="卡项视频" >
<span class="delete-video hide" onclick="deleteVideo()"><img class="del-img" src="SHOP_IMG/delete.png">删除</span>
<span class=" replace-video hide js-add-goods-video" ><img class="up-img" src="SHOP_IMG/upload.png">上传视频</span>
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"></label>
<div class="layui-input-block">
<input type="text" name="video_url" placeholder="在此输入外链视频地址" value="{$goods_info['video_url']}" autocomplete="off" class="layui-input len-long">
</div>
<div class="file-title word-aux">
<div>注意事项:</div>
<ul>
<li>1、检查upload文件夹是否有读写权限。</li>
<li>2、PHP默认上传限制为2MB需要在php.ini配置文件中修改“post_max_size”和“upload_max_filesize”的大小。</li>
<li>3、视频支持手动输入外链视频地址或者上传本地视频文件</li>
<li>4、必须上传.mp4视频格式</li>
<li>5、视频文件大小不能超过500MB</li>
</ul>
</div>
</div>
<div class="card-type-content discountcard" {if $goods_info.card_type neq 'discountcard'}style="display: none"{/if}>
<div class="layui-form-item">
<label class="layui-form-label"><span class="required">*</span>适用商品:</label>
<div class="layui-input-block">
<input type="radio" name="discount_goods_type" value="all" title="全部商品" {if $goods_info.discount_goods_type eq 'all'}checked{/if} lay-filter="discount_goods_type">
<input type="radio" name="discount_goods_type" value="part" title="部分商品" {if $goods_info.discount_goods_type eq 'part'}checked{/if} lay-filter="discount_goods_type">
</div>
</div>
<div class="layui-form-item discount-goods all">
<label class="layui-form-label"><span class="required">*</span>折扣卡折扣:</label>
<div class="layui-input-inline">
<input type="number" name="discount" value="{$goods_info.discount}" placeholder="0" class="layui-input len-short" lay-verify="common_discount" autocomplete="off">
</div>
<div class="layui-form-mid layui-word-aux">%</div>
<div class="layui-form-mid word-aux">购卡后购买商品或服务时可享折扣 1-99之间</div>
</div>
<div class="layui-form-item discount-goods part" {if $goods_info.discount_goods_type eq 'all'}style="display: none"{/if}>
<label class="layui-form-label"></label>
<div class="layui-input-block">
<button class="layui-btn add-relation-goods">选择商品</button>
<div class="relation-goods-table discountcard" lay-verify="relation_discount_goods">
<table id="relationDiscountGoods"></table>
</div>
</div>
<div class="word-aux batch-set">
<div class="batch-set-wrap">
<span>批量操作:</span>
<div class="set-item">
<a href="javascript:;" class="text-color set">可享受折扣</a>
</div>
<div class="set-content-wrap">
<input type="number" placeholder="0" class="layui-input len-short value" autocomplete="off" data-type="discount">
<button class="layui-btn confirm">确认</button>
<button class="layui-btn layui-btn-primary cancel">取消</button>
</div>
</div>
</div>
</div>
</div>
<div class="layui-form-item card-type-content oncecard timecard commoncard" {if $goods_info.card_type eq 'discountcard'}style="display: none"{/if}>
<label class="layui-form-label"><span class="required">*</span>卡项内容:</label>
<div class="layui-input-block">
<a href="javascript:;" class="text-color add-relation-goods" data-goods-class="4">添加卡项项目</a>
<a href="javascript:;" class="text-color add-relation-goods" data-goods-class="1">添加实物商品</a>
<div class="relation-goods-table oncecard" lay-verify="relation_goods">
<table id="relationGoods"></table>
</div>
</div>
<div class="word-aux batch-set">
<div class="batch-set-wrap">
<span>批量操作:</span>
<div class="set-item">
<a href="javascript:;" class="text-color set">可用次数</a>
</div>
<div class="set-content-wrap">
<input type="number" placeholder="0" class="layui-input len-short value" autocomplete="off" data-type="num">
<button class="layui-btn confirm">确认</button>
<button class="layui-btn layui-btn-primary cancel">取消</button>
</div>
</div>
</div>
</div>
<div class="layui-form-item card-type-content commoncard" {if $goods_info.card_type neq 'commoncard'}style="display: none"{/if}>
<label class="layui-form-label"><span class="required">*</span>可用次数/数量:</label>
<div class="layui-input-block">
<input type="text" name="common_num" value="{$goods_info.common_num}" placeholder="0" lay-verify="common_num" class="layui-input len-short" autocomplete="off">
</div>
<div class="layui-form-mid word-aux">卡项内项目/商品总的可用次数</div>
</div>
{notempty name="$service_list"}
<div class="layui-form-item">
<label class="layui-form-label">卡项服务:</label>
<div class="layui-input-block">
{foreach name="$service_list" item="vo"}
<input type="checkbox" name="goods_service_ids" value="{$vo.id}" title="{$vo.service_name}" lay-skin="primary" {if strpos($goods_info['goods_service_ids'],(string)$vo['id'])>-1}checked{/if}>
{/foreach}
</div>
</div>
{/notempty}
<div class="layui-form-item">
<label class="layui-form-label">商品品牌:</label>
<div class="layui-input-inline">
<select name="brand_id" lay-search="" lay-filter="brand_id">
<option value="">请选择商品品牌</option>
{foreach name="$brand_list" item="vo"}
<option value="{$vo['brand_id']}" {if $goods_info['brand_id'] == $vo['brand_id'] }selected{/if}>{$vo['brand_name']}</option>
{/foreach}
</select>
</div>
</div>
{if $is_install_supply}
<div class="layui-form-item">
<label class="layui-form-label">供应商:</label>
<div class="layui-input-inline">
<select name="supplier_id" lay-search="" lay-verify="supplier_id">
<option value="">请选择供应商</option>
{foreach name="$supplier_list" item="vo"}
<option value="{$vo['supplier_id']}" {if $goods_info['supplier_id']==$vo['supplier_id'] }selected{/if}>{$vo['title']}</option>
{/foreach}
</select>
</div>
</div>
{/if}
<div class="layui-form-item goods_state">
<label class="layui-form-label"><span class="required">*</span>是否上架:</label>
<div class="layui-input-block">
<input type="radio" name="goods_state" value="1" title="立刻上架" lay-filter="goods_state" {if $goods_info['goods_state'] == 1 }checked{/if}>
<input type="radio" name="goods_state" value="0" title="放入仓库" lay-filter="goods_state" {if $goods_info['goods_state'] == 0 }checked{/if}>
</div>
</div>
{if $goods_info['goods_state'] == 0}
<div class="layui-form-item timer_on">
<label class="layui-form-label">定时上架:</label>
<div class="layui-input-block">
<input type="radio" name="timer_on_status" class="timer_on_status_true" value="1" title="启用" lay-filter="timer_on" {if $goods_info['timer_on'] > 0} checked {/if}>
<input type="radio" name="timer_on_status" value="2" title="不启用" lay-filter="timer_on" {if $goods_info['timer_on'] == 0} checked {/if}>
</div>
<div class="word-aux">启用定时上架后,到达设定时间,此卡项将自动上架。</div>
</div>
{if $goods_info['timer_on'] > 0}
<div class="layui-form-item timer_on_time">
<label class="layui-form-label"></label>
<div class="layui-input-inline">
<input type="text" id="timer_on" name="timer_on" value="{:date('Y-m-d H:i:s',$goods_info['timer_on'])}" lay-verify="required" class="layui-input len-mid" autocomplete="off" readonly>
<i class=" iconrili iconfont calendar"></i>
</div>
</div>
{else/}
<div class="layui-form-item timer_on_time layui-hide">
<label class="layui-form-label"></label>
<div class="layui-input-inline">
<input type="text" id="timer_on" name="timer_on" lay-verify="" class="layui-input len-mid" autocomplete="off" readonly>
<i class=" iconrili iconfont calendar"></i>
</div>
</div>
{/if}
{/if}
<div class="layui-form-item">
<label class="layui-form-label">定时下架:</label>
<div class="layui-input-block">
<input type="radio" name="timer_off_status" value="1" title="启用" lay-filter="timer_off" {if $goods_info['timer_off'] > 0} checked {/if}>
<input type="radio" name="timer_off_status" value="2" title="不启用" lay-filter="timer_off" {if $goods_info['timer_off'] == 0} checked {/if}>
</div>
<div class="word-aux">启用定时下架后,到达设定时间,此卡项将自动下架。</div>
</div>
{if $goods_info['timer_off'] > 0}
<div class="layui-form-item timer_off">
<label class="layui-form-label"></label>
<div class="layui-input-inline">
<input type="text" id="timer_off" name="timer_off" value="{:date('Y-m-d H:i:s',$goods_info['timer_off'])}" lay-verify="required" class="layui-input len-mid" autocomplete="off" readonly>
<i class=" iconrili iconfont calendar"></i>
</div>
</div>
{else/}
<div class="layui-form-item timer_off" style="display:none">
<label class="layui-form-label"></label>
<div class="layui-input-inline">
<input type="text" id="timer_off" name="timer_off" lay-verify="" class="layui-input len-mid" autocomplete="off" readonly>
<i class=" iconrili iconfont calendar"></i>
</div>
</div>
{/if}
{if addon_is_exit('cashier') == 1}
<div class="layui-form-item">
<label class="layui-form-label">销售渠道:</label>
<div class="layui-input-block">
<input type="radio" name="sale_channel" value="all" title="线上线下销售" {if $goods_info.sale_channel eq 'all'}checked{/if}>
<input type="radio" name="sale_channel" value="online" title="线上销售" {if $goods_info.sale_channel eq 'online'}checked{/if}>
<input type="radio" name="sale_channel" value="offline" title="线下销售" {if $goods_info.sale_channel eq 'offline'}checked{/if}>
</div>
</div>
{/if}
{if $store_is_exit}
<div class="layui-form-item">
<label class="layui-form-label">适用门店:</label>
<div class="layui-input-block">
<input type="radio" name="sale_store" value="all" title="全部门店" {if $goods_info.sale_store eq 'all'}checked{/if} lay-filter="sale_store">
<input type="radio" name="sale_store" value="" title="部分门店" {if $goods_info.sale_store neq 'all'}checked{/if} lay-filter="sale_store">
</div>
</div>
<div class="layui-form-item sale-store-select" {if $goods_info.sale_store eq 'all'}style="display: none"{/if} lay-verify="sale_store">
<label class="layui-form-label"></label>
<div class="layui-input-block">
<button class="layui-btn select-store">选择门店</button>
<div style="width: 700px">
<table class="layui-table" lay-skin="nob">
<colgroup>
<col width="30%">
<col width="60%">
<col width="10%">
</colgroup>
<tr>
<th>门店名称</th>
<th>门店地址</th>
<th>操作</th>
</tr>
<tbody class="sale-store">
{if isset($store_list) && !empty($store_list)}
{foreach name="$store_list" item="vo"}
<tr data-store="{$vo.store_id}">
<td>{$vo.store_name}</td>
<td>{$vo.full_address}{$vo.address}</td>
<td><a href="javascript:;" class="del">删除</a></td>
</tr>
{/foreach}
{/if}
</tbody>
</table>
</div>
</div>
</div>
{/if}
<div class="layui-form-item">
<label class="layui-form-label">卡项有效期:</label>
<div class="layui-input-block">
<input type="radio" name="validity_type" value="0" title="永久" {if $goods_info.validity_type eq 0}checked{/if} lay-filter="validity_type">
<input type="radio" name="validity_type" value="1" title="购买后几日有效" {if $goods_info.validity_type eq 1}checked{/if} lay-filter="validity_type">
<input type="radio" name="validity_type" value="2" title="指定过期日期" {if $goods_info.validity_type eq 2}checked{/if} lay-filter="validity_type">
</div>
</div>
<div class="layui-form-item validity-type validity-type-1 {if $goods_info.validity_type neq 1}layui-hide{/if}">
<label class="layui-form-label"><span class="required">*</span>有效期:</label>
<div class="layui-input-inline">
<input type="text" name="validity_day" placeholder="0" class="layui-input len-short" value="{$goods_info.validity_day}" lay-verify="validity_day" autocomplete="off">
</div>
<div class="layui-form-mid layui-word-aux"></div>
</div>
<div class="layui-form-item validity-type validity-type-2 {if $goods_info.validity_type neq 2}layui-hide{/if}">
<label class="layui-form-label"><span class="required">*</span>有效期:</label>
<div class="layui-input-inline">
<input type="text" id="validity_time" name="validity_time" class="layui-input len-mid" value="{$goods_info.validity_time ? time_to_date($goods_info.validity_time) : ''}" lay-verify="validity_time" autocomplete="off" readonly>
<i class=" iconrili iconfont calendar"></i>
</div>
</div>
{if $store_is_exit}
<div class="layui-form-item">
<label class="layui-form-label">是否统一售价:</label>
<div class="layui-input-block">
<input type="radio" name="is_unify_price" value="1" title="是" {if $goods_info.is_unify_price eq '1'}checked{/if} >
<input type="radio" name="is_unify_price" value="0" title="否" {if $goods_info.is_unify_price eq '0'}checked{/if}>
</div>
<div class="word-aux">价格设置之后门店不能修改价格,门店按照平台设置的价格售卖</div>
</div>
{/if}
</div>
</div>
</div>
<!-- 价格库存 -->
<div class="layui-tab-item">
<div class="layui-form-item layui-hide">
<label class="layui-form-label">启用多规格:</label>
<div class="layui-input-inline">
<input type="checkbox" value="1" lay-skin="switch" name="spec_type" lay-filter="spec_type" lay-verify="spec_type" {notempty name="$goods_info['goods_spec_format']" }checked{/notempty}>
<input type="hidden" id="spec_type_status" {if empty($goods_info['goods_spec_format'])} value="0" {else/} value="1" {/if}>
</div>
</div>
<!-- 单规格 -->
<div class="js-single-spec" {notempty name="$goods_info['goods_spec_format']" }style="display:none;"{/notempty}>
<div class="layui-form-item">
<label class="layui-form-label"><span class="required">*</span>开卡价格:</label>
<div class="layui-input-block">
<input type="text" name="price" value="{$goods_info['price']}" placeholder="0.00" lay-verify="price" class="layui-input len-short" autocomplete="off">
<div class="layui-form-mid"></div>
</div>
<div class="word-aux">卡项购买价格</div>
</div>
<!--<div class="layui-form-item">
<label class="layui-form-label"><span class="required">*</span>续费价格:</label>
<div class="layui-input-block">
<input type="text" name="renew_price" value="{$goods_info['renew_price']}" placeholder="0.00" lay-verify="renew_price" class="layui-input len-short" autocomplete="off">
<div class="layui-form-mid">元</div>
</div>
<div class="word-aux">卡项续费价格</div>
</div>-->
<div class="layui-form-item">
<label class="layui-form-label">划线价:</label>
<div class="layui-input-block">
<input type="text" name="market_price" value="{$goods_info['market_price']}" placeholder="0.00" lay-verify="market_price" class="layui-input len-short" autocomplete="off">
<div class="layui-form-mid"></div>
</div>
<div class="word-aux">卡项没有优惠活动显示的划线价格,如果卡项有折扣等优惠活动划线价显示销售价</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">成本价:</label>
<div class="layui-input-block">
<input type="text" name="cost_price" value="{$goods_info['cost_price']}" placeholder="0.00" class="layui-input len-short" lay-verify="cost_price" autocomplete="off">
<div class="layui-form-mid"></div>
</div>
<div class="word-aux">成本价将不会对前台会员展示,用于商家统计使用</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">卡项编码:</label>
<div class="layui-input-inline">
<input type="text" name="sku_no" value="{$goods_info['sku_list'][0]['sku_no']}" placeholder="请输入卡项编码" maxlength="50" class="layui-input len-long" autocomplete="off">
</div>
</div>
</div>
<!-- 多规格 -->
<div class="js-more-spec" {notempty name="$goods_info['goods_spec_format']" }style="display:block;"{/notempty}>
<!--规格项/规格值-->
<div class="spec-edit-list"></div>
<div class="layui-form-item js-add-spec">
<label class="layui-form-label"></label>
<div class="layui-input-inline">
<button class="layui-btn" type="button">添加规格</button>
</div>
</div>
<div class="layui-form-item batch-operation-sku">
<label class="layui-form-label">批量操作:</label>
<div class="layui-input-inline">
<span class="text-color" data-field="spec_name">副标题</span>
<span class="text-color" data-field="price" data-verify="price">销售价</span>
<span class="text-color" data-field="market_price" data-verify="market_price">划线价</span>
<span class="text-color" data-field="cost_price" data-verify="cost_price">成本价</span>
<span class="text-color" data-field="stock" data-verify="stock">库存</span>
<span class="text-color" data-field="stock_alarm" data-verify="stock_alarm">库存预警</span>
<span class="text-color" data-field="sku_no" data-verify="">卡项编码</span>
<input type="text" class="layui-input len-short" name="batch_operation_sku" autocomplete="off" />
<button class="layui-btn confirm" type="button">确定</button>
<button class="layui-btn layui-btn-primary cancel" type="button">取消</button>
</div>
</div>
<!--编辑时用到的SKU列表-->
<div class="js-edit-sku-list">
{foreach name="$goods_info['sku_list']" item="vo" key="k"}
<div data-index="{$k}">
<input type="hidden" name="edit_sku_id" value="{$vo['sku_id']}" />
<input type="hidden" name="edit_spec_name" value="{$vo['spec_name']}" />
<input type="hidden" name="edit_sku_no" value="{$vo['sku_no']}" />
<input type="hidden" name="edit_sku_spec_format" value="{$vo['sku_spec_format']}" />
<input type="hidden" name="edit_price" value="{$vo['price']}" />
<input type="hidden" name="edit_market_price" value="{$vo['market_price']}" />
<input type="hidden" name="edit_cost_price" value="{$vo['cost_price']}" />
<input type="hidden" name="edit_stock" value="{$vo['stock']}" />
<input type="hidden" name="edit_stock_alarm" value="{$vo['stock_alarm']}" />
<input type="hidden" name="edit_sku_image" value="{$vo['sku_image']}" />
<input type="hidden" name="edit_sku_images" value="{$vo['sku_images']}" />
<input type="hidden" name="edit_is_default" value="{$vo['is_default']}" />
</div>
{/foreach}
</div>
<!--sku列表-->
<div class="layui-form-item sku-table">
<label class="layui-form-label"></label>
<div class="layui-input-block"></div>
</div>
</div>
<div class="layui-form-item js-goods-stock-wrap" {notempty name="$goods_info['goods_spec_format']" }style="display:none;"{/notempty}>
<label class="layui-form-label"><span class="required">*</span>库存:</label>
<div class="layui-input-block">
<input type="number" name="goods_stock" value="{$goods_info['goods_stock']}" placeholder="0" lay-verify="goods_stock" class="layui-input len-short" autocomplete="off" {notempty name="$goods_info['goods_spec_format']" }disabled{/notempty}>
<div class="layui-form-mid"></div>
</div>
</div>
<div class="layui-form-item js-goods-stock-wrap" {notempty name="$goods_info['goods_spec_format']" }style="display:none;"{/notempty}>
<label class="layui-form-label">库存预警:</label>
<div class="layui-input-block">
<input type="number" name="goods_stock_alarm" value="{$goods_info['goods_stock_alarm']}" placeholder="0" lay-verify="goods_stock_alarm" class="layui-input len-short" autocomplete="off">
<div class="layui-form-mid"></div>
</div>
<div class="word-aux">卡项库存少于预警数量卡项列表库存数量标红显示0为不预警。</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">虚拟销量:</label>
<div class="layui-input-block">
<input type="number" name="virtual_sale" placeholder="0" value="{$goods_info['virtual_sale']}" lay-verify="virtual_sale" class="layui-input len-short" autocomplete="off">
<div class="layui-form-mid"></div>
</div>
<div class="word-aux">该设置不计入卡项统计数据</div>
</div>
<div class="layui-form-item is_limit">
<label class="layui-form-label">是否限购:</label>
<div class="layui-input-block">
<input type="radio" name="is_limit" value="0" title="否" lay-filter="is_limit" {if $goods_info['is_limit'] == 0} checked {/if}>
<input type="radio" name="is_limit" value="1" title="是" lay-filter="is_limit" {if $goods_info['is_limit'] == 1} checked {/if}>
</div>
<div class="word-aux">启用限购后,购买卡项时,会对该卡项购买量做限制判断。</div>
</div>
{if $goods_info['is_limit'] == 1}
<div class="layui-form-item limit_type" >
<label class="layui-form-label">限购类型:</label>
<div class="layui-input-block">
<input type="radio" name="limit_type" class="limit_type" value="1" title="单次限购" lay-filter="limit_type" {if $goods_info['limit_type'] == 1 } checked {/if}>
<input type="radio" name="limit_type" class="limit_type" value="2" title="长期限购" lay-filter="limit_type" {if $goods_info['limit_type'] == 2} checked {/if}>
<input type="number" name="max_buy" placeholder="" lay-verify="max_buy" value="{$goods_info['max_buy']}" class="layui-input len-short" autocomplete="off">
<div class="layui-form-mid">&nbsp件</div>
</div>
<div class="word-aux">单次限购是针对于每次下单不能超过限购数量,长期限购是针对于会员账号购买这个卡项的总数不能超过限购数量。</div>
</div>
{/if}
<div class="layui-form-item">
<label class="layui-form-label">起售:</label>
<div class="layui-input-block">
<input type="number" name="min_buy" placeholder="" lay-verify="min_buy" value="{$goods_info['min_buy']}" class="layui-input len-short" autocomplete="off">
<div class="layui-form-mid"></div>
</div>
<div class="word-aux">起售数量超出卡项库存时,买家无法购买该卡项</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">会员等级折扣:</label>
<div class="layui-input-block">
<div class="layui-input-inline">
<input type="radio" name="is_consume_discount" value="1" title="参与" {if $goods_info['is_consume_discount'] > 0} checked {/if}>
<input type="radio" name="is_consume_discount" value="0" title="不参与" {if $goods_info['is_consume_discount'] == 0} checked {/if}>
</div>
</div>
<div class="word-aux">如果该卡项未单独配置过优惠规则,则按照默认会员等级折扣优惠</div>
</div>
</div>
<!-- 卡项详情 -->
<div class="layui-tab-item">
<div class="layui-form-item">
<label class="layui-form-label sm"></label>
<div class="layui-input-inline special-length">
<input type="hidden" name="goods_content" value="{$goods_info['goods_content']}" />
<script id="editor" type="text/plain" style="width:100%;height:500px;"></script>
</div>
</div>
<script type="text/javascript" charset="utf-8" src="__STATIC__/ext/ueditor/ueditor.config.js"></script>
<script type="text/javascript" charset="utf-8" src="__STATIC__/ext/ueditor/ueditor.all.js"> </script>
<script type="text/javascript" charset="utf-8" src="__STATIC__/ext/ueditor/lang/zh-cn/zh-cn.js"></script>
</div>
<div class="layui-tab-item layui-card card-common card-brief head">
<div class="layui-card-header">
<span class="card-title">高级设置</span>
</div>
<div class="layui-card-body">
<div class="layui-form-item">
<label class="layui-form-label">排序:</label>
<div class="layui-input-block">
<input type="number" name="sort" value="{$goods_info['sort']}" class="layui-input len-short" placeholder="0" autocomplete="off">
</div>
<div class="word-aux">卡项默认排序号为0数字越大排序越靠前数字重复则最新添加的靠前。</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">卡项详情显示库存:</label>
<div class="layui-input-block">
<input type="radio" name="stock_show" value="1" title="显示" {if $goods_info['stock_show'] > 0} checked {/if}>
<input type="radio" name="stock_show" value="0" title="隐藏" {if $goods_info['stock_show'] == 0} checked {/if}>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">划线价显示:</label>
<div class="layui-input-block">
<input type="radio" name="market_price_show" value="1" title="显示" {if $goods_info['market_price_show'] > 0} checked {/if}>
<input type="radio" name="market_price_show" value="0" title="隐藏" {if $goods_info['market_price_show'] == 0} checked {/if}>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">卡项详情显示弹幕:</label>
<div class="layui-input-block">
<input type="radio" name="barrage_show" value="1" title="显示" {if $goods_info['barrage_show'] > 0} checked {/if}>
<input type="radio" name="barrage_show" value="0" title="隐藏" {if $goods_info['barrage_show'] == 0} checked {/if}>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">卡项详情显示销量:</label>
<div class="layui-input-block">
<input type="radio" name="sale_show" value="1" title="显示" {if $goods_info['sale_show'] > 0} checked {/if}>
<input type="radio" name="sale_show" value="0" title="隐藏" {if $goods_info['sale_show'] == 0} checked {/if}>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">卡项海报:</label>
<div class="layui-input-inline">
<select name="template_id" lay-search="" lay-verify="">
<option value="">请选择卡项海报</option>
{foreach name="$poster_list" item="vo"}
<option value="{$vo['template_id']}" {if $goods_info.template_id == $vo.template_id} selected {/if}>{$vo['poster_name']}</option>
{/foreach}
</select>
</div>
</div>
{if $form_is_exit}
<div class="layui-form-item">
<label class="layui-form-label">卡项表单:</label>
<div class="layui-input-block len-mid">
<select name="form_id">
<option value="0">请选择卡项表单</option>
{foreach name="$form_list" item="vo"}
<option value="{$vo.id}" {if $goods_info.form_id == $vo.id} selected {/if}>{$vo.form_name}</option>
{/foreach}
</select>
</div>
<div class="word-aux">
<a href="{:href_url('form://shop/form/addform?form_type=goods')}" class="text-color" target="_blank">创建卡项表单</a>
<a href="javascript:;" onclick="refreshFormList()" class="text-color">刷新</a>
</div>
</div>
{/if}
</div>
</div>
</div>
</div>
<input type="hidden" name="goods_id" value="{$goods_info['goods_id']}" />
<input type="hidden" name="goods_spec_format" value="{$goods_info['goods_spec_format']}" />
<input type="hidden" name="goods_image" value="{$goods_info['goods_image']}" />
<input type="hidden" name="goods_attr_format" value="{$goods_info['goods_attr_format']}" />
<input type="hidden" name="relation_goods" value="{$goods_info['relation_goods']|json_encode}" />
<input type="hidden" name="has_stock_records" value='{$has_stock_records ?? 0}' />
<div class="fixed-btn">
<button class="layui-btn layui-btn-primary border-color text-color js-prev" lay-filter="prev">上一步</button>
<button class="layui-btn js-save" lay-submit="" lay-filter="save">保存</button>
<button class="layui-btn layui-btn-primary border-color text-color js-next" lay-submit="" lay-filter="next">下一步</button>
</div>
</div>
<!--规格项模板-->
<script type="text/html" id="specTemplate">
{{# for(var i=0;i<d.list.length;i++){ }}
<div class="spec-item" data-index="{{i}}">
<div class="layui-form-item spec">
<label class="layui-form-label">规格项{{i+1}}</label>
<div class="layui-input-inline">
<select name="spec_item">
<option value="0"></option>
{{# if(d.list[i].spec_name != ''){ }}
<option value="{{d.list[i].spec_id}}" data-attr-name="{{d.list[i].spec_name}}" selected>{{d.list[i].spec_name}}</option>
{{# }else{ }}
{{# } }}
</select>
<i class="layui-icon layui-icon-close" data-index="{{i}}"></i>
</div>
{{# if(i==0){ }}
<div class="layui-input-inline">
{{# if(d.add_spec_img){ }}
<input type="checkbox" name="add_spec_img" title="添加规格图片" lay-skin="primary" lay-filter="add_spec_img" checked>
{{# }else{ }}
<input type="checkbox" name="add_spec_img" title="添加规格图片" lay-skin="primary" lay-filter="add_spec_img">
{{# } }}
</div>
{{# } }}
</div>
{{# if(d.list[i].spec_name != ''){ }}
<div class="layui-form-item spec-value">
{{# }else{ }}
<div class="layui-form-item spec-value" style="display:none;">
{{# } }}
<label class="layui-form-label"></label>
<div class="layui-input-block spec-value">
{{# if(d.list[i].value.length){ }}
<ul>
{{# for(var j=0;j<d.list[i].value.length;j++){ }}
<li data-index="{{j}}" data-parent-index="{{i}}" >
{{# if(i==0 && d.add_spec_img){ }}
<div class="img-wrap">
{{# if(d.list[i].value[j].image){ }}
<img src="{{ns.img(d.list[i].value[j].image)}}" alt="">
{{# }else{ }}
<img src="SHOP_IMG/goods_spec_value_empty.png" alt="">
{{# } }}
</div>
{{# } }}
<span title="双击可编辑规格值" ondblclick="$(this).attr('contenteditable',true);$(this).focus()" class="spec-txt" data-spec_value_name="{{d.list[i].value[j].spec_value_name}}" data-parent-index="{{i}}" data-index="{{j}}">{{d.list[i].value[j].spec_value_name}}</span>
<!--{{1# if(d.list[i].value[j].is_delete === undefined){ }}-->
<i class="layui-icon layui-icon-close" data-parent-index="{{i}}" data-index="{{j}}"></i>
<!--{{1# } }}-->
</li>
{{# } }}
</ul>
{{# } }}
<a class="text-color" href="javascript:;" data-index="{{i}}">+添加规格值</a>
<div class="add-spec-value-popup" data-index="{{i}}">
<select name="spec_value_item"></select>
<button class="layui-btn layui-btn-primary border-color text-color js-cancel-spec-value">取消</button>
</div>
</div>
</div>
</div>
{{# } }}
</script>
<!--SKU列表模板-->
<script type="text/html" id="skuTableTemplate">
{{# if(d.skuList.length){ }}
<table class="layui-table">
<colgroup></colgroup>
<thead>
<tr>
{{# if(d.showSpecName){ }}
<th colspan="{{d.colSpan}}" style="min-width: 60px;">卡项规格</th>
{{# } }}
<th rowspan="{{d.rowSpan}}">SKU图片</th>
<th rowspan="{{d.rowSpan}}">副标题</th>
<th rowspan="{{d.rowSpan}}"><span class="required">*</span></th>
<th rowspan="{{d.rowSpan}}">划线价</th>
<th rowspan="{{d.rowSpan}}">成本价</th>
<th rowspan="{{d.rowSpan}}"><span class="required">*</span></th>
<th rowspan="{{d.rowSpan}}">库存预警</th>
<th rowspan="{{d.rowSpan}}">服务时长</th>
<th rowspan="{{d.rowSpan}}">商品编码多个编码以英文逗号分割</th>
<th rowspan="{{d.rowSpan}}" style="white-space: nowrap;">默认展示</th>
</tr>
{{# if(d.colSpan>1){ }}
<tr>
{{# for(var i=0;i<d.specList.length;i++){ }}
{{# if(d.specList[i].spec_name && d.specList[i].value.length> 0){ }}
<th>{{d.specList[i].spec_name}}</th>
{{# } }}
{{# } }}
</tr>
{{# } }}
</thead>
<tbody>
{{# for(var i=0;i<d.skuList.length;i++){ }}
<tr>
<td class="sku_imgs" id="sku_img_{{i}}" style="width: 130px;">
{{# for(var j=0;j<d.skuList[i].sku_images_arr.length;j++){ }}
<div class="img-wrap" data-index="{{j}}" data-parent-index="{{i}}">
<a href="javascript:void(0)">
<img src="{{ns.img(d.skuList[i].sku_images_arr[j])}}" layer-src />
</a>
<div class="operation">
<i title="图片预览" class="iconfont iconreview js-preview"></i>
<i title="删除图片" class="layui-icon layui-icon-delete js-delete"></i>
</div>
</div>
{{# } }}
{{# if(d.skuList[i].sku_images_arr.length<d.goods_sku_max){ }}
<div class="upload-sku-img" data-index="{{i}}"><i class="layui-icon layui-icon-add-1"></i></div>
{{# } }}
</td>
<td>
<input type="text" name="spec_name" placeholder="副标题" maxlength="100" value="{{d.skuList[i].spec_name}}" class="layui-input" autocomplete="off" data-index="{{i}}">
</td>
<td>
<input type="text" name="price" placeholder="销售价" lay-verify="sku_price" value="{{d.skuList[i].price}}" class="layui-input" autocomplete="off" data-index="{{i}}">
</td>
<td>
<input type="text" name="market_price" placeholder="划线价" value="{{d.skuList[i].market_price}}" lay-verify="sku_market_price" class="layui-input" autocomplete="off" data-index="{{i}}">
</td>
<td>
<input type="text" name="cost_price" placeholder="成本价" value="{{d.skuList[i].cost_price}}" lay-verify="sku_cost_price" class="layui-input" autocomplete="off" data-index="{{i}}">
</td>
<td>
<input type="text" name="stock" placeholder="库存" value="{{d.skuList[i].stock}}" lay-verify="sku_stock" class="layui-input" autocomplete="off" data-index="{{i}}">
</td>
<td>
<input type="text" name="stock_alarm" placeholder="库存预警" value="{{d.skuList[i].stock_alarm}}" lay-verify="sku_stock_alarm" class="layui-input" autocomplete="off" data-index="{{i}}">
</td>
<td>
<input type="number" name="service_length" placeholder="服务时长" value="{{d.skuList[i].service_length}}" maxlength="50" class="layui-input" autocomplete="off" data-index="{{i}}" lay-verify="verify_num">
</td>
<td>
<input type="text" name="sku_no" placeholder="商品编码" value="{{d.skuList[i].sku_no}}" maxlength="50" class="layui-input" autocomplete="off" data-index="{{i}}">
</td>
<td style="min-width: 40px;">
{{# if(d.skuList[i].is_default == 1) { }}
<input type="checkbox" data-index="{{i}}" name="is_default" lay-filter="is_default" lay-skin="switch" checked>
{{# }else { }}
<input type="checkbox" data-index="{{i}}" name="is_default" lay-filter="is_default" lay-skin="switch">
{{# } }}
</td>
</tr>
{{# } }}
</tbody>
</table>
{{# } }}
<div class="word-aux text-color" style="margin: 10px 0 0 0;">默认展示是多规格卡项在客户访问卡项时默认显示的卡项规格</div>
</script>
<!--卡项主图列表-->
<script type="text/html" id="goodsImage">
{{# if(d.list.length){ }}
{{# for(var i=0;i<d.list.length;i++){ }}
<div class="item upload_img_square_item" data-index="{{i}}">
<div class="img-wrap">
<img src="{{ns.img(d.list[i],'small')}}" layer-src="{{ns.img(d.list[i],'big')}}">
</div>
<div class="operation">
<i title="图片预览" class="iconfont iconreview js-preview"></i>
<i title="删除图片" class="layui-icon layui-icon-delete js-delete" data-index="{{i}}"></i>
<div class="replace_img" data-index="{{i}}">点击替换</div>
</div>
</div>
{{# } }}
{{# if(d.list.length < d.max){ }}
<div class="item js-add-goods-image upload_img_square">+</div>
{{# } }}
{{# }else{ }}
<div class="item js-add-goods-image upload_img_square">+</div>
{{# } }}
</script>
<script>
{notempty name="$goods_info.relation_goods"}
var relationGoods = {:json_encode($goods_info.relation_goods)};
{else/}
var relationGoods = [];
{/notempty}
</script>
<script src="__STATIC__/ext/drag-arrange.js"></script>
<script src="__STATIC__/ext/video/videojs-ie8.min.js"></script>
<script src="__STATIC__/ext/video/video.min.js"></script>
<script src="__STATIC__/ext/searchable_select/searchable_select.js"></script>
<script src="SHOP_JS/goods_edit_common.js?time=20250527"></script>
<script src="ADDON_CARDSERVICE_JS/card_goods_edit.js?v=1"></script>

View File

@@ -0,0 +1,296 @@
<style>
.screen{margin-bottom: 15px;}
.contraction span {
cursor: pointer;
display: inline-block;
width: 17px;
height: 17px;
text-align: center;
line-height: 14px;
user-select: none;
}
.card-list {
overflow: hidden;
padding: 0 45px;
}
.card-list li .img-wrap {
vertical-align: middle;
margin-right: 8px;
width: 80px;
height: 80px;
text-align: center;
border: 1px solid #e2e2e2;
}
.card-list li .img-wrap img {
max-width: 100%;
max-height: 100%;
}
.card-list li .info-wrap{
flex: 1;
display: flex;
flex-direction: column;
}
.card-list li .name-wrap{
flex: 1;
}
.card-list li .info-wrap span.sku-name {
-webkit-line-clamp: 2;
margin-bottom: 5px;
line-height: 1.3;
margin-top: 0;
color: #333;
font-size: 14px;
}
.card-list li .info-wrap span {
display: -webkit-box;
margin-top: 5px;
overflow: hidden;
text-overflow: ellipsis;
white-space: normal;
word-break: break-all;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
line-height: 1;
font-size: 12px;
}
.card-list li {
float: left;
display: flex;
padding: 10px;
margin-right: 10px;
margin-bottom: 10px;
border: 1px solid #EFEFEF;
width: 294px;
align-items: center;
}
.table-title .title-content p{overflow: hidden;text-overflow:ellipsis;white-space: nowrap;}
.goods-box{
display: flex;
padding: 10px;
justify-content: space-between;
margin-bottom: 20px;
}
.goods-box .goods-info{
width: 700px;
}
.goods-box .goods-info .goods-content{
display: flex;
justify-content: space-between;
}
.goods-box .goods-info .goods-name{
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
display: -moz-box;
-moz-line-clamp: 2;
-moz-box-orient: vertical;
overflow-wrap: break-word;
word-break: break-all;
white-space: normal;
overflow: hidden;
height: 38px;
}
.goods-box .goods-img{
margin-right: 15px;
display: flex;
align-items: center;
}
.goods-box .goods-img img{
width: 80px;
max-height: 80px;
}
.goods-box .box-left{
display: flex;
}
.goods-box .box-right{
display: flex;
align-items: center;
}
.user-detail{
cursor: pointer;
}
.table-title .title-content{
overflow: unset;
}
</style>
<!--商品信息-->
<div class="goods-box">
<div class="box-left">
<div class="goods-img">
<img src="{:img(explode(',', $goods_info['goods_image'])[0])}">
</div>
<div class="goods-info">
<div class="goods-name">{$goods_info['goods_name']}</div>
<div class="goods-content">
<div>卡类型:{$card_info.card_type_name}</div>
<div>价格:{$goods_info['price']}</div>
<div>库存:{$goods_info['goods_stock']}</div>
<div>销量:{$goods_info['sale_num']}</div>
</div>
</div>
</div>
<div class="box-right">
<button class="layui-btn layui-btn-primary" onclick="location.hash='{:hash_url('shop/goods/lists')}'">返回</button>
</div>
</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="search_text" placeholder="请输入购买人名称" autocomplete="off" class="layui-input">
</div>
</div>
</div>
<div class="form-row">
<button class="layui-btn" lay-submit lay-filter="search">筛选</button>
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
</div>
</form>
</div>
</div>
<!-- 列表 -->
<table id="card_list" lay-filter="card_list"></table>
<!-- 工具栏操作 -->
<script type="text/html" id="operation">
<div class="table-btn">
<a class="layui-btn" lay-event="order">订单</a>
<a class="layui-btn" lay-event="detail">详情</a>
</div>
</script>
<script type="text/html" id="userdetail">
<div class='table-title user-detail' onclick="memberDetail({{d.member_id}})">
<div class='title-pic'>
<img layer-src src="{{ns.img(d.headimg)}}" onerror="this.src = '{:img('public/static/img/default_img/head.png')}' ">
</div>
<div class='title-content'>{{d.nickname}}</div>
</div>
</script>
<script type="text/html" id="goodsDetail">
<div class="table-title">
<div class=" table-title">
<div class="title-pic"><img src="{{ns.img(d.goods_image.split(',')[0], 'mid')}}"></div>
<div class="title-content">
<p title="{{ d.sku_name }}">{{ d.goods_name }}</p>
</div>
</div>
</div>
</script>
<script>
var laytpl;
$(function () {
layui.use(['form', 'laydate','laytpl'], function () {
laytpl = layui.laytpl;
var table,
form = layui.form,
laydate = layui.laydate;
form.render();
//渲染时间
laydate.render({
elem: '#start_time',
type: 'datetime'
});
laydate.render({
elem: '#end_time',
type: 'datetime'
});
/**
* 加载表格
*/
table = new Table({
elem: '#card_list',
url: ns.url("cardservice://shop/card/goodscard"), //数据接口
where: {goods_id:"{$goods_id}"},
cols: [
[{
field: 'nickname',
title: '买家信息',
templet: '#userdetail'
},
{
title: '总次数/已使用',
templet: function (data) {
var totalNum = data.card_type == 'timecard' ? '不限' : data.total_num;
return totalNum + '/' + data.total_use_num;
}
},{
title: '创建时间',
width: '15%',
templet: function (data) {
return ns.time_to_date(data.create_time)
}
},{
title: '到期时间',
width: '15%',
templet: function (data) {
if (data.end_time > 0) {
return ns.time_to_date(data.end_time);
} else {
return '永久有效';
}
}
}, {
title: '操作',
toolbar: '#operation',
unresize: 'false',
align : 'right'
}]
]
});
/**
* 监听工具栏操作
*/
table.tool(function(obj) {
var data = obj.data;
switch (obj.event) {
case 'order':
window.open(ns.href("shop/order/detail?order_id=" + data.order_id));
break;
case 'detail':
window.open(ns.href("cardservice://shop/card/detail?card_id=" + data.card_id));
break;
}
});
/**
* 搜索功能
*/
form.on('submit(search)', function (data) {
table.reload({
page: {
curr: 1
},
where: data.field
});
return false;
});
//批量导出
form.on('submit(export_card)', function (data) {
location.href = ns.url("shop/card/exportVerify?request_mode=download", data.field);
return false;
});
});
});
function memberDetail(member_id){
window.open(ns.href("shop/member/editmember", {'member_id':member_id}))
}
</script>

View File

@@ -0,0 +1,123 @@
<!-- 搜索框 -->
<div class="single-filter-box" style="margin:15px 0;">
<div class="layui-form">
<div class="layui-input-inline">
<input type="text" name="search_text" placeholder="请输入商品名称" class="layui-input" autocomplete="off">
<button type="button" class="layui-btn layui-btn-primary" lay-filter="goods_card_search" lay-submit>
<i class="layui-icon">&#xe615;</i>
</button>
</div>
</div>
</div>
<!-- 列表 -->
<table id="card_list" lay-filter="card_list"></table>
<!-- 商品 -->
<script type="text/html" id="goodsCardInfo">
<div class="table-title">
<div class="title-pic">
{{# if(d.goods_image){ }}
<img layer-src src="{{ns.img(d.goods_image.split(',')[0],'small')}}"/>
{{# } }}
</div>
<div class="title-content">
<a href="javascript:;" class="multi-line-hiding text-color-sub">{{d.goods_name}}</a>
</div>
</div>
</script>
<!-- 工具栏操作 -->
<script type="text/html" id="goods_card_operation">
<div class="table-btn">
<a class="layui-btn" lay-event="detail">详情</a>
</div>
</script>
<script>
var form, goods_card_table, laytpl;
layui.use(['form', 'laytpl'], function () {
form = layui.form;
laytpl = layui.laytpl;
form.render();
goods_card_table = new Table({
elem: '#card_list',
url: ns.url("cardservice://shop/card/membergoodscard"),
async: false,
where: {'member_id': "{$member_id}"},
parseData: function (res) {
return {
"code": res.code,
"msg": res.message,
"count": res.data.count,
"data": res.data.list,
};
},
cols: [
[{
title: '商品',
unresize: 'false',
width: '25%',
templet: '#goodsCardInfo'
}, {
field: 'card_code',
title: '卡号',
unresize: 'false',
width: '15%',
align: 'left',
}, {
field: 'card_type_name',
title: '卡类型',
unresize: 'false',
width: '10%',
align: 'left',
}, {
field: '',
title: '总次数/已使用',
templet: function (data) {
return data.total_num + '/' + data.total_use_num;
}
}, {
title: '创建时间',
width: '15%',
templet: function (data) {
return ns.time_to_date(data.create_time)
}
}, {
title: '到期时间',
width: '15%',
templet: function (data) {
return data.end_time > 0 ? ns.time_to_date(data.end_time) : '永久有效';
}
}, {
title: '操作',
toolbar: '#goods_card_operation',
unresize: 'false',
align: 'right'
}]
]
});
goods_card_table.tool(function (obj) {
var data = obj.data;
switch (obj.event) {
case 'detail':
window.open(ns.href("cardservice://shop/card/detail?card_id=" + data.card_id))
break;
}
});
/**
* 搜索功能
*/
form.on('submit(goods_card_search)', function (data) {
goods_card_table.reload({
page: {
curr: 1
},
where: data.field
});
});
});
</script>

View File

@@ -0,0 +1,145 @@
<style>
.layui-input-inline.js-pid a{margin-left: 20px;}
.link-url-show{margin-right: 10px}
.click-link{height: 34px;line-height: 34px;display: inline-block;white-space: nowrap;text-align: center;border-radius: 2px;cursor: pointer;padding: 0 16px;border: 1px solid #C9C9C9;background-color: #fff;color: #555;}
</style>
<form 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 len-long">
<input name="category_name" type="text" placeholder="请输入分类名称" maxlength="30" lay-verify="required" class="layui-input" autocomplete="off">
</div>
<div class="word-aux">
<p>分类名称最长不超过30个字符</p>
</div>
</div>
<div class="layui-form-item layui-hide">
<label class="layui-form-label">上级分类:</label>
<div class="layui-input-block js-pid">
<span class="input-text">顶级分类</span>
<input type="hidden" name="pid" value="0">
<input type="hidden" name="level" value="1">
<input type="hidden" name="category_id_1" value="0">
<input type="hidden" name="category_id_2" value="0">
<a class="text-color" href="javascript:selectedCategoryPopup();">选择分类</a>
</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">
<div class="upload-img-block">
<div class="upload-img-box">
<div class="upload-default" id="imgUpload">
<div class="upload">
<i class="iconfont iconshangchuan"></i>
<p>点击上传</p>
</div>
</div>
<div class="operation">
<div>
<i title="图片预览" class="iconfont iconreview js-preview" style="margin-right: 20px;"></i>
<i title="删除图片" class="layui-icon layui-icon-delete js-delete"></i>
</div>
<div class="replace_img js-replace">点击替换</div>
</div>
<input type="hidden" name="image">
</div>
</div>
</div>
<div class="word-aux">
<p>建议图片尺寸不能大于100k。图片格式jpg、png、jpeg。</p>
</div>
<a id="imageUploadAction"></a>
</div>
<div class="layui-form-item">
<label class="layui-form-label">分类广告图:</label>
<div class="layui-input-block">
<div class="upload-img-block">
<div class="upload-img-box">
<div class="upload-default" id="imgUploadAdv">
<div class="upload">
<i class="iconfont iconshangchuan"></i>
<p>点击上传</p>
</div>
</div>
<div class="operation">
<div>
<i title="图片预览" class="iconfont iconreview js-preview" style="margin-right: 20px;"></i>
<i title="删除图片" class="layui-icon layui-icon-delete js-delete"></i>
</div>
<div class="replace_img js-replace">点击替换</div>
</div>
<input type="hidden" name="image_adv" value="">
</div>
</div>
</div>
<div class="word-aux">
<p>该图片只对一级使用, 建议图片尺寸550px * 250px。图片格式jpg、png、jpeg。</p>
</div>
<a id="imageAdvUploadAction"></a>
</div>
<div class="layui-form-item link-url-show-wrap">
<label class="layui-form-label">广告链接:</label>
<div class="layui-input-block">
<input name="link_url" type="hidden" class="layui-input len-long" autocomplete="off">
<a class="click-link" onclick="selectedLink()">选择链接</a>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">是否显示</label>
<div class="layui-input-block">
<input type="radio" name="is_show" value="0" title="显示" checked="">
<input type="radio" name="is_show" value="-1" title="不显示">
</div>
</div>
<div class="form-row">
<button class="layui-btn bg-color" lay-submit lay-filter="save">保存</button>
<button type="reset" class="layui-btn layui-btn-primary" onclick="backCardServiceCategoryList()">返回</button>
</div>
</form>
<script type="text/html" id="selectedCategory">
<form class="layui-form">
<div class="layui-form-item">
<label class="layui-form-label sm">一级分类</label>
<div class="layui-input-block len-mid">
<select name="category_id_1" lay-filter="category_id_1">
<option value="0" data-level="0">顶级分类</option>
{{# for(var i=0;i<d.category_list_1.length;i++){ }}
{{# if(d.category_id_1 ==d.category_list_1[i].category_id){ }}
<option value="{{ d.category_list_1[i].category_id }}" data-level="{{d.category_list_1[i].level}}" selected>{{ d.category_list_1[i].category_name }}</option>
{{# }else{ }}
<option value="{{ d.category_list_1[i].category_id }}" data-level="{{d.category_list_1[i].level}}">{{ d.category_list_1[i].category_name }}</option>
{{# } }}
{{# } }}
</select>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label sm">二级分类</label>
<div class="layui-input-block len-mid">
<select name="category_id_2" lay-filter="category_id_2"></select>
</div>
</div>
<div class="form-row sm">
<button class="layui-btn bg-color" lay-submit lay-filter="save_pid">保存</button>
</div>
</form>
</script>
<script src="ADDON_CARDSERVICE_JS/edit_category.js"></script>

View File

@@ -0,0 +1,266 @@
<style>
.js-pid a{
margin-left: 20px;
}
.form-wrap {
margin-top: 0;
}
.link-url-show{margin-right: 10px}
.click-link{height: 34px;line-height: 34px;display: inline-block;white-space: nowrap;text-align: center;border-radius: 2px;cursor: pointer;padding: 0 16px;border: 1px solid #C9C9C9;background-color: #fff;color: #555;}
.goods-category-list .layui-table td{border-left: 0;border-right: 0;}
.goods-category-list .layui-table .switch{font-size: 16px;cursor: pointer;width: 12px;line-height: 1;display: inline-block;text-align: center;vertical-align: middle;}
.goods-category-list .layui-table img{width: 40px;}
/* 分类样式*/
.table_div{color:#666}
.table_head{display: flex;font-weight: bold;background-color: #F7F7F7;}
.table_head li{height: 50px;line-height: 50px;border: 0;padding: 0 15px;font-size: 14px;font-weight: 400;color: #333;}
.table_head .operate{text-align: right;}
.table_head li:first-child{padding-right: 0;}
.table_tr{display: flex;border-bottom: 1px solid #e6e6e6;background: #fff;align-items: center;}
.table_tr .table_td{position: relative;padding: 5px 15px;font-size: 14px;display: flex;align-items: center;}
.table_tr .table_td span{cursor: pointer;}
.table_tr .table_td span>img{width:12px;height:12px}
.table_tr .table_td span>img.rotate{transform:rotate(90deg);}
.table_tr .table_td .img-box{width:30px;height:30px;line-height: 30px;}
.table_tr .table_td:first-child{padding-right:0}
.table_tr .table-btn{display: flex;flex-wrap: wrap;justify-content: flex-end;}
.table_tr .layui-btn{display: flex;justify-content: center;align-items: center;height: 23px;border-radius: 50px;background-color: transparent;color: var(--base-color);text-align: center;padding: 2px 0;margin: 5px 0 5px 10px;position: relative;}
.table_two_div{display: none;}
.table_three{display: none;}
.empty_switch{display: inline-block;width:30px;height:25px;padding-right:15px;}
.js-switch{display: inline-block;width:30px;text-align: center;}
.table_move{float:left;margin-right: 10px;}
.table_moves{float:left;margin-right: 10px;}
.tables_move{float:left;margin-right: 20px;padding-left: 70px;}
.select-category .layui-layer-content{overflow: auto!important;}
.table_three .table_td.checkbox{
padding-left: 70px;
}
</style>
<form 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 name="category_name" type="text" value="{$goods_category_info['category_name']}" placeholder="请输入分类名称" maxlength="30" lay-verify="required" class="layui-input len-long" autocomplete="off">
</div>
<div class="word-aux">
<p>分类名称最长不超过30个字符</p>
</div>
</div>
<div class="layui-form-item layui-hide">
<label class="layui-form-label">上级分类:</label>
<div class="layui-input-block len-mid js-pid">
{if $goods_category_info['pid'] == 0}
<span class="input-text">顶级分类</span>
{else/}
<span class="input-text">{$goods_category_parent_info['category_name']}</span>
{/if}
<input type="hidden" name="pid" value="{$goods_category_info['pid']}">
<input type="hidden" name="level" value="{$goods_category_info['level']}">
<input type="hidden" name="category_id_1" value="{$goods_category_info['category_id_1']}">
<input type="hidden" name="category_id_2" value="{$goods_category_info['category_id_2']}">
<input type="hidden" name="category_id_3" value="{$goods_category_info['category_id_3']}">
<input type="hidden" name="category_name_1" value="">
<a class="text-color" href="javascript:selectedCategoryPopup();">选择分类</a>
</div>
<div class="word-aux">
{if $goods_category_info['level'] == 1}
<p>注意:顶级分类不能修改</p>
{elseif $goods_category_info['level'] == 2}
<p>注意:二级分类可以修改一级分类</p>
{elseif $goods_category_info['level'] == 3}
<p>注意:三级分类可以修改二级分类</p>
{/if}
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">分类图片:</label>
<div class="layui-input-block">
<div class="upload-img-block">
<div class="upload-img-box {notempty name="$goods_category_info['image']"}hover{/notempty}">
<div class="upload-default" id="imgUpload">
{notempty name="$goods_category_info['image']"}
<div id="preview_imgUpload" class="preview_img">
<img layer-src src="{:img($goods_category_info['image'])}" class="img_prev"/>
</div>
{else/}
<div class="upload">
<i class="iconfont iconshangchuan"></i>
<p>点击上传</p>
</div>
{/notempty}
</div>
<div class="operation">
<div>
<i title="图片预览" class="iconfont iconreview js-preview" style="margin-right: 20px;"></i>
<i title="删除图片" class="layui-icon layui-icon-delete js-delete"></i>
</div>
<div class="replace_img js-replace">点击替换</div>
</div>
<input type="hidden" name="image" value="{$goods_category_info['image']}">
</div>
</div>
</div>
<div class="word-aux">
<p>建议图片尺寸不能大于100k。图片格式jpg、png、jpeg。</p>
</div>
<a id="imageUploadAction"></a>
</div>
<div class="layui-form-item">
<label class="layui-form-label">分类广告图:</label>
<div class="layui-input-block">
<div class="upload-img-block ">
<div class="upload-img-box {notempty name="$goods_category_info['image_adv']"} hover {/notempty}" >
<div class="upload-default" id="imgUploadAdv">
{notempty name="$goods_category_info['image_adv']"}
<div id="preview_imgUploadAdv" class="preview_img">
<img layer-src src="{:img($goods_category_info['image_adv'])}" class="img_prev"/>
</div>
{else/}
<div class="upload">
<i class="iconfont iconshangchuan"></i>
<p>点击上传</p>
</div>
{/notempty}
</div>
<div class="operation">
<div>
<i title="图片预览" class="iconfont iconreview js-preview" style="margin-right: 20px;"></i>
<i title="删除图片" class="layui-icon layui-icon-delete js-delete"></i>
</div>
<div class="replace_img js-replace">点击替换</div>
</div>
<input type="hidden" name="image_adv" value="{$goods_category_info['image_adv']}">
</div>
</div>
</div>
<div class="word-aux">
<p>该图片只对一级使用, 建议图片尺寸550px * 250px。图片格式jpg、png、jpeg。</p>
</div>
<a id="imageAdvUploadAction"></a>
</div>
<div class="layui-form-item link-url-show-wrap">
<label class="layui-form-label">广告链接:</label>
<div class="layui-input-block">
<input name="link_url" type="hidden" class="layui-input len-long" autocomplete="off" value="{$goods_category_info.link_url}">
<a class="click-link" onclick="selectedLink()">选择链接</a>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">是否显示</label>
<div class="layui-input-block">
<input type="radio" name="is_show" value="0" title="显示" {if $goods_category_info['is_show'] == 0} checked="" {/if}>
<input type="radio" name="is_show" value="-1" title="不显示" {if $goods_category_info['is_show'] == -1} checked="" {/if}>
</div>
</div>
<input type="hidden" id="category_id" name="category_id" value="{$goods_category_info['category_id']}">
<input type="hidden" name="category_full_name" value="{$goods_category_info['category_full_name']}">
<div class="form-row">
<button class="layui-btn" lay-submit lay-filter="save">保存</button>
<button type="reset" class="layui-btn layui-btn-primary" onclick="backCardServiceCategoryList()">返回</button>
</div>
</form>
<script type="text/html" id="selectedCategory">
<div class="goods-category-list layui-form">
<div class="table_div" >
<ul class="table_head">
<li style="width:60px"></li>
<li style="flex:6">分类名称</li>
<li style="flex:2">是否显示</li>
</ul>
<div class="table_body">
{if condition="$list"}
{foreach name="$list" item="vo" key="index"}
<li class="table_one" data-index="{$index}" data-sort="{$vo['sort']}" data-cateid="{$vo['category_id']}">
<div class="table_tr">
<div class="table_td" style="width:60px">
<div class="table_move">
<input type="checkbox" name="category_id" title="" lay-skin="primary" data-name="{$vo['category_name']}" value="{$vo['category_id']}" data-category-id="{$vo['category_id']}" data-level="{$vo['level']}" data-open="0" lay-filter="category">
</div>
{notempty name="$vo['child_list']"}
<span class="switch text-color js-switch" data-category-id="{$vo['category_id']}" data-level="{$vo['level']}" data-open="0">
+
</span>
{/notempty}
</div>
<div class="table_td" style="flex:6">{$vo['category_name']}</div>
<div class="table_td" style="flex:2">
{if $vo['is_show'] == 0} 显示 {else /} {/if}
</div>
</div>
{notempty name="$vo['child_list']"}
<div class="table_two_div">
{foreach name="$vo['child_list']" item="second"}
<div class="table_two" data-index="{$index}" data-sort="{$second['sort']}" data-cateid="{$second['category_id']}">
<div class="table_tr">
<div class="table_td" style="width: 36px">
</div>
<div class="table_td" style="flex:6.4">
<div class="table_move">
<input type="checkbox" name="category_id" title="" lay-skin="primary" value="{$second['category_id']}" data-category-id="{$second['category_id']}" data-level="{$second['level']}" data-open="0" data-name="{$second['category_name']}" lay-filter="category">
</div>
<!-- <span class="switch text-color empty_switch" > </span>-->
<span>{$second['category_name']}</span>
</div>
<div class="table_td" style="flex:2">
{if $second['is_show'] == 0} 显示 {else /} {/if}
</div>
</div>
{notempty name="$second['child_list']"}
<div class="table_three layui-hide">
{foreach name="$second['child_list']" item="third"}
<div class="table_tr table_three_tr" data-sort="{$third['sort']}" data-cateid="{$third['category_id']}">
<div class="table_td" style="width: 36px">
</div>
<div class="table_td checkbox" style="flex:5.5">
<input type="checkbox" name="category_id" title="" lay-skin="primary" data-name="{$third['category_name']}" value="{$third['category_id']}" data-category-id="{$third['category_id']}" data-level="{$third['level']}" data-open="0">
<div>{$third['category_name']}</div>
</div>
<div class="table_td" style="flex:2">
{if $third['is_show'] == 0} 显示 {else /} {/if}
</div>
</div>
{/foreach}
</div>
{/notempty}
</div>
{/foreach}
</div>
{/notempty}
</li>
{/foreach}
{else/}
<div class="table_tr">
<div class="table_td" style="flex:1;text-align: center;">暂无数据</div>
</div>
{/if}
</div>
</div>
</div>
</script>
<script src="ADDON_CARDSERVICE_JS/edit_category.js"></script>

View File

@@ -0,0 +1,368 @@
<style>
.table_body{font-family: arial;-webkit-user-select: none;-moz-user-select: none;-ms-user-select: none;user-select: none;}
.goods-category-list .layui-table td{border-left: 0;border-right: 0;}
.goods-category-list .layui-table .switch{font-size: 16px;cursor: pointer;width: 12px;line-height: 1;display: inline-block;text-align: center;vertical-align: middle;}
.goods-category-list .layui-table img{width: 40px;}
/* 分类样式*/
.table_div{color:#666}
.table_head{display: flex;font-weight: bold;background-color: #F7F7F7;}
.table_head li{height: 50px;line-height: 50px;border: 0;padding: 0 15px;font-size: 14px;font-weight: 400;color: #333;}
.table_head .operate{text-align: right;}
.table_head li:first-child{padding-right: 0;}
.table_tr{display: flex;border-bottom: 1px solid #e6e6e6;background: #fff;align-items: center;}
.table_tr .table_td{position: relative;padding: 5px 15px;font-size: 14px;}
.table_tr .table_td span{cursor: pointer;}
.table_tr .table_td span>img{width:12px;height:12px}
.table_tr .table_td span>img.rotate{transform:rotate(90deg);}
.table_tr .table_td .img-box{width:30px;height:30px;line-height: 30px;}
.table_tr .table_td:first-child{padding-right:0}
.table_tr .table-btn{display: flex;flex-wrap: wrap;justify-content: flex-end;}
.table_tr .layui-btn{display: flex;justify-content: center;align-items: center;height: 23px;border-radius: 50px;background-color: transparent;color: var(--base-color);text-align: center;padding: 2px 0;margin: 5px 0 5px 10px;position: relative;}
.table_two_div{display: none;}
.table_three{display: none;}
.empty_switch{display: inline-block;width:30px;height:25px;padding-right:15px;}
.js-switch{display: inline-block;height:30px;width:30px;text-align: center;}
.table_move{cursor: move;float:left;margin-right: 10px;}
.table_moves{cursor: move;float:left;margin-right: 10px;}
.tables_move{cursor: move;float:left;margin-right: 20px;padding-left: 70px;}
</style>
<div class="single-filter-box">
<button class="layui-btn" onclick="addCategory()">添加项目分类</button>
</div>
<div class="goods-category-list">
<div class="table_div" >
<ul class="table_head">
<li style="width:60px"></li>
<li style="flex:6">分类名称</li>
<li style="flex:2">图片</li>
<!-- <li style="flex:2">排序</li> -->
<li style="flex:2">是否显示</li>
<li class="operate" style="flex:2">操作</li>
</ul>
<div class="table_body">
{if condition="$list"}
{foreach name="$list" item="vo" key="index"}
<li class="table_one" data-index="{$index}" data-sort="{$vo['sort']}" data-cateid="{$vo['category_id']}">
<div class="table_tr">
<div class="table_td" style="width:60px">
<div class="table_move iconfont icontuodong"></div>
{notempty name="$vo['child_list']"}
<span class="switch text-color js-switch" data-category-id="{$vo['category_id']}" data-level="{$vo['level']}" data-open="0">+</span>
{/notempty}
</div>
<div class="table_td" style="flex:6">{$vo['category_name']}</div>
<div class="table_td" style="flex:2">
{notempty name="$vo['image']"}
<div class="img-box">
<img layer-src src="{:img($vo['image'])}"/>
</div>
{/notempty}
</div>
<!-- <div class="table_td" style="flex:2">
<input type="number" class="layui-input len-short" value="{$vo['sort']}" onchange="editSort('{$vo.category_id}')" id="category_sort{$vo.category_id}">
</div> -->
<!-- <div class="table_td" style="flex:2">-->
<!-- {if $vo['is_show'] == 0} 显示 {else /} 不显示 {/if}-->
<!-- </div>-->
<div class="table_td layui-form" style="flex:2">
<input type="checkbox" name="is_show" value="{$vo['category_id']}" lay-skin="switch" lay-text="" lay-filter="is_show" {if $vo['is_show'] == 0} checked {/if}>
</div>
<div class="table_td" style="flex:2">
<div class="table-btn">
<a class="layui-btn" href="{:href_url('cardservice://shop/servicecategory/editcategory',['category_id'=>$vo['category_id']])}">编辑</a>
<a class="layui-btn" href="javascript:deleteCategory({$vo['category_id']});">删除</a>
</div>
</div>
</div>
{notempty name="$vo['child_list']"}
<div class="table_two_div">
{foreach name="$vo['child_list']" item="second"}
<div class="table_two" data-index="{$index}" data-sort="{$second['sort']}" data-cateid="{$second['category_id']}">
<div class="table_tr">
<div class="table_td" style="width: 36px">
</div>
<div class="table_td" style="flex:6.2">
<div class="table_moves iconfont icontuodong"></div>
{notempty name="$second['child_list']"}
<span class="switch text-color js-switch" data-category-id="{$second['category_id']}" data-level="{$second['level']}" data-open="0" style="padding-right: 15px;">+</span>
{else /}
<span class="switch text-color empty_switch" > </span>
{/notempty}
<span>{$second['category_name']}</span>
</div>
<div class="table_td" style="flex:2">
{notempty name="$second['image']"}
<div class="img-box">
<img layer-src src="{:img($second['image'])}"/>
</div>
{/notempty}
</div>
<!-- <div class="table_td" style="flex:2">
<input type="number" class="layui-input len-short" value="{$second['sort']}" onchange="editSort('{$second.category_id}')" id="category_sort{$second.category_id}">
</div> -->
<div class="table_td" style="flex:2">
{if $second['is_show'] == 0} 显示 {else /} 不显示 {/if}
</div>
<div class="table_td" style="flex:2">
<div class="table-btn">
<a class="layui-btn" href="{:href_url('cardservice://shop/servicecategory/editcategory',['category_id'=>$second['category_id']])}">编辑</a>
<a class="layui-btn" href="javascript:deleteCategory({$second['category_id']});">删除</a>
</div>
</div>
</div>
{notempty name="$second['child_list']"}
<div class="table_three">
{foreach name="$second['child_list']" item="third"}
<div class="table_tr table_three_tr" data-sort="{$third['sort']}" data-cateid="{$third['category_id']}">
<div class="table_td" style="width: 36px">
</div>
<div class="table_td" style="flex:6.2">
<div class="tables_move iconfont icontuodong"></div>
<div>{$third['category_name']}</div>
</div>
<div class="table_td" style="flex:2">
{notempty name="$third['image']"}
<div class="img-box">
<img layer-src src="{:img($third['image'])}"/>
</div>
{/notempty}
</div>
<!-- <div class="table_td" style="flex:2">
<input type="number" class="layui-input len-short" value="{$second['sort']}" onchange="editSort('{$second.category_id}')" id="category_sort{$second.category_id}">
</div> -->
<div class="table_td" style="flex:2">
{if $third['is_show'] == 0} 显示 {else /} 不显示 {/if}
</div>
<div class="table_td" style="flex:2">
<div class="table-btn">
<a class="layui-btn" href="{:href_url('cardservice://shop/servicecategory/editcategory',['category_id'=>$third['category_id']])}">编辑</a>
<a class="layui-btn" href="javascript:deleteCategory({$third['category_id']});">删除</a>
</div>
</div>
</div>
{/foreach}
</div>
{/notempty}
</div>
{/foreach}
</div>
{/notempty}
</li>
{/foreach}
{else/}
<div class="table_tr">
<div class="table_td" style="flex:1;text-align: center;">暂无数据</div>
</div>
{/if}
</div>
</div>
</div>
<script src="STATIC_EXT/drag-arrange.js"></script>
<script src="STATIC_EXT/diyview/js/ddsort.js"></script>
<script>
var form;
layui.use(['form'], function(){
form = layui.form;
form.on('switch(is_show)', function(data){
$.ajax({
url: ns.url("cardservice://shop/servicecategory/modifyShow"),
data: {id:data.value,is_show:data.elem.checked ? 0 : -1},
dataType: 'JSON',
type: 'POST',
async: false,
success: function (res) {
layer.msg(res.message);
}
});
console.log(data.elem); // 获取当前 checkbox 的 DOM 对象
console.log(data.elem.checked); // 获取当前 checkbox 的选中状态true 或 false
console.log(data.value); // 获取当前 checkbox 的 value 值,即 name 属性的值或者自定义的 value 值(如果有的话)
console.log(data.othis); // 获取当前 checkbox 的 jQuery 对象
});
form.render();
});
$(function() {
var tempPos = '';
$('li').arrangeable({
dragSelector: '.table_move',
callback:function(e){
var temparr = [];
$('.table_one').each(function(index,item){
var tempObj = {};
tempObj.category_id = item.getAttribute('data-cateid');
tempObj.sort = index;
temparr.push(tempObj)
})
setTimeout(function(){
$.ajax({
url: ns.url("cardservice://shop/servicecategory/modifySort"),
data: {category_sort_array : JSON.stringify(temparr)},
dataType: 'JSON',
type: 'POST',
async: false,
success: function (res) {
layer.msg(res.message);
}
});
},100);
}
});
$('.table_two').arrangeable({
dragSelector: '.table_moves',
callback:function(e){
var temparrs = [];
$('.table_two').each(function(index,item){
var tempObjs = {};
tempObjs.category_id = item.getAttribute('data-cateid');
tempObjs.sort = index;
temparrs.push(tempObjs)
});
setTimeout(function(){
$.ajax({
url: ns.url("cardservice://shop/servicecategory/modifySort"),
data: {category_sort_array : JSON.stringify(temparrs)},
dataType: 'JSON',
type: 'POST',
async: false,
success: function (res) {
layer.msg(res.message);
}
});
},100);
}
});
$('.table_three_tr').arrangeable({
dragSelector: '.tables_move',
callback:function(e){
var temparres = [];
$('.table_three_tr').each(function(index,item){
var tempObjes = {};
tempObjes.category_id = item.getAttribute('data-cateid');
tempObjes.sort = index;
temparres.push(tempObjes)
});
setTimeout(function(){
$.ajax({
url: ns.url("cardservice://shop/servicecategory/modifySort"),
data: {category_sort_array : JSON.stringify(temparres)},
dataType: 'JSON',
type: 'POST',
async: false,
success: function (res) {
layer.msg(res.message);
}
});
},100);
}
});
});
// var tempPos = '';
// bindDragSort('.table_body' ,'.table_one');
// bindDragSort('.table_two_div' ,'.table_two');
// bindDragSort('.table_three' ,'.table_tr');
// function bindDragSort(paremtElem,childElem){
// $(paremtElem ).DDSort({
// target: childElem,
// floatStyle: {
// 'border': '1px solid #ccc',
// 'background-color': '#fff'
// },
// down:function(e){
// tempPos = $(this).data('sort');
// },
// up:function(e){
// var index = $(this).index(),self = $(this);
// if(index != tempPos){
// var temparr = [];
// $(childElem).each(function(index,item){
// var tempObj = {};
// tempObj.category_id = item.getAttribute('data-cateid');
// tempObj.sort = index;
// temparr.push(tempObj)
// })
// setTimeout(function(){
// $.ajax({
// url: ns.url("cardservice://shop/servicecategory/modifySort"),
// data: {category_sort_array : JSON.stringify(temparr)},
// dataType: 'JSON',
// type: 'POST',
// async: false,
// success: function (res) {
// self.data('sort',index)
// layer.msg(res.message);
// }
// });
// },100);
// }
//
// }
// });
// }
$(function () {
loadImgMagnify(); //图片放大
//展开收齐点击事件
$(".js-switch").click(function (event) {
event.stopPropagation();
var category_id = $(this).attr("data-category-id");
var level = $(this).attr("data-level");
var open = parseInt($(this).attr("data-open").toString());
if(open){
$(".goods-category-list .layui-table tr[data-category-id-"+ level+"='" + category_id + "']").hide();
// $(this).children("img").removeClass('rotate');
$(this).text("+");
if(level == 1) $(this).parents('.table_tr').siblings('.table_two_div').hide();
else if(level == 2) $(this).parents('.table_tr').siblings('.table_three').hide();
}else{
$(".goods-category-list .layui-table tr[data-category-id-"+ level+"='" + category_id + "']").show();
$(this).text("-");
// $(this).children("img").addClass('rotate');
if(level == 1) $(this).parents('.table_tr').siblings('.table_two_div').show();
else if(level == 2) $(this).parents('.table_tr').siblings('.table_three').show();
}
$(this).attr("data-open", (open ? 0 : 1));
});
});
function deleteCategory(category_id) {
layer.confirm('确认要删除该分类吗,请谨慎操作', function(index) {
$.ajax({
url: ns.url("cardservice://shop/servicecategory/deleteCategory"),
data: {category_id : category_id},
dataType: 'JSON',
type: 'POST',
async: false,
success: function (res) {
layer.msg(res.message);
layer.close(index);
if (res.code == 0) {
listenerHash(); // 刷新页面
}
}
});
});
}
function addCategory() {
location.hash = ns.hash("cardservice://shop/servicecategory/addcategory");
}
</script>

View File

@@ -0,0 +1,242 @@
html,body,.main-wrap {
height: 100%;
width: 100%;
background: #fff;
}
.main-wrap {
padding: 20px;
box-sizing: border-box;
}
.error {
font-size: 12px;
color: #f00;
line-height: 1;
margin-top: 10px;
display: none;
margin-left: 130px;
}
.layui-input-inline i {
position: absolute;
top: 50%;
right: 10px;
transform: translateY(-50%);
}
.search-wrap {
position: relative;
}
.search-wrap i {
border-left: 1px solid #E6E6E6;
line-height: 30px;
padding-left: 10px;
cursor: pointer;
}
.layui-form-label {
width: 130px;
}
.word-aux {
margin-left: 130px!important;
}
.layui-form-label + .layui-input-block {
margin-left: 130px;
}
.select-time .time-wrap {
width: 330px;
padding: 15px;
display: flex;
flex-wrap: wrap;
}
.select-time .no-today {
display: none;
}
.select-time .time-item {
height: 30px;
line-height: 30px;
width: 60px;
cursor: pointer;
}
.select-time .time-item:hover,.select-time .time-item.active {
color: var(--base-color);
}
.select-time .time-item.no-select {
color: #999;
cursor: not-allowed;
}
.align-center {
text-align: center!important;
}
.service-item,.servicer-item {
display: flex;
border: 1px solid #E6E6E6;
align-items: center;
cursor: pointer;
height: 52px;
}
.service-item .iconfont, .servicer-item .iconfont {
margin-right: 5px;
}
.service-item .info, .servicer-item .info {
flex: 1;
padding: 6px 10px;
}
.service-item .info .desc, .servicer-item .info .desc{
font-size: 12px;
color: #999;
}
.service-item .info .title, .servicer-item .info .title {
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.select-service {
width: 660px;
height: 240px;
box-sizing: border-box;
padding: 15px;
}
.select-service .service-wrap {
overflow-y: scroll;
height: 100%;
}
.select-service .service-wrap .empty {
line-height: 200px;
text-align: center;
width: 100%;
}
.select-service .service-wrap::-webkit-scrollbar {
width: 6px;
height: 6px;
background-color: transparent;
}
.select-service .service-wrap::-webkit-scrollbar-button {
display: none;
}
.select-service .service-wrap::-webkit-scrollbar-thumb {
border-radius: .06rem;
box-shadow: inset 0 0 .06rem rgba(45,43,43,.45);
background-color: #ddd;
}
.select-service .service-wrap::-webkit-scrollbar-track {
background-color: transparent;
}
.select-service .service-wrap .flex-wrap{
display: flex;
flex-wrap: wrap;
}
.select-service .service-wrap .item {
margin: 0 8px 8px 0;
background: #eee;
padding: 8px;
width: 186px;
cursor: pointer;
transition: all .3s;
}
.select-service .service-wrap .item:hover{
background: #fff5ed;
}
.select-service .service-wrap .item:nth-child(3n+3) {
margin-right: 0;
}
.select-service .service-wrap .title{
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.select-service .service-wrap .desc {
font-size: 12px;
color: #999;
}
.save-wrap {
position: fixed;
bottom: 0;
left: 0;
background: #fff;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
border-top: 1px solid #eee;
width: 100%;
}
.member-info {
display: inline-flex;
padding: 10px;
border: 1px solid #e6e6e6;
min-width: 300px;
max-width: 590px;
}
.member-info img {
width: 50px;
height: 50px;
}
.member-info .info {
flex: 1;
display: flex;
flex-direction: column;
padding-left: 10px;
justify-content: space-around;
}
.member-info .info div {
line-height: 1;
}
.member-info .info span{
margin-right: 10px;
}
.select-servicer {
width: 150px;
height: 240px;
box-sizing: border-box;
padding: 15px 10px;
overflow-y: scroll;
}
.select-servicer .select-item {
width: 100%;
height: 40px;
line-height: 40px;
overflow: hidden;
text-overflow: ellipsis;
padding: 0 10px;
background: #f5f5f5;
cursor: pointer;
margin-bottom: 10px;
transition: all .3s;
box-sizing: border-box;
}
.select-servicer .select-item:hover {
background: #fff5ed;
}
.select-servicer::-webkit-scrollbar {
width: 6px;
height: 6px;
background-color: transparent;
}
.select-servicer::-webkit-scrollbar-button {
display: none;
}
.select-servicer::-webkit-scrollbar-thumb {
border-radius: .06rem;
box-shadow: inset 0 0 .06rem rgba(45,43,43,.45);
background-color: #ddd;
}
.select-servicer::-webkit-scrollbar-track {
background-color: transparent;
}
.layui-dropdown {
position: absolute;
left: -999999px;
top: -999999px;
z-index: 66666666;
margin: 5px 0;
min-width: 100px
}
.layui-dropdown:before {
content: "";
position: absolute;
width: 100%;
height: 6px;
left: 0;
top: -6px
}

View File

@@ -0,0 +1,68 @@
.card-type {
display: flex;
flex-wrap: wrap;
}
.card-type .card-type-item {
padding: 15px;
border: 1px solid #ddd;
line-height: 1;
margin: 0 10px 10px 0;
/*border-radius: 4px;*/
cursor: pointer;
position: relative;
overflow: hidden;
width: 285px;
}
.card-type .card-type-item:hover,.card-type .card-type-item.active {
border-color: var(--base-color);
color: var(--base-color);
}
.card-type .card-type-item .title{
font-size: 14px;
font-weight: bold;
}
.card-type .card-type-item .desc{
margin-top: 8px;
color: #999;
}
.card-type .card-type-item .iconfont {
position: absolute;
right: 0;
display: none;
}
.card-type .card-type-item.active .iconfont {
display: block;
}
.relation-goods-table {
margin-right: 50px;
max-width: 800px;
}
.relation-goods-table .layui-table-body {
max-height: 300px;
}
.relation-goods-table .table-bottom{
display: none;
}
.relation-goods-table .len-short {
width: 80px!important;
}
.relation-goods-table .layui-table-view {
margin-top: 10px;
}
.card-type-content .batch-set {
font-size: 14px;
display: none;
}
.card-type-content .batch-set .batch-set-wrap{
display: flex;
align-items: center;
}
.card-type-content .batch-set .len-short {
width: 80px!important;
}
.card-type-content .batch-set .set-content-wrap {
display: none;
}
.card-type-content .batch-set .layui-btn+.layui-btn{
margin-left: 0;
}

View File

@@ -0,0 +1,337 @@
.layui-body {
padding-bottom: 0!important;
}
.uni-flex {
display: flex;
flex-direction: row;
}
.uni-flex-item {
flex: 1;
}
.uni-row {
flex-direction: row;
}
.uni-column {
flex-direction: column;
}
.panel-head {
align-items: center;
}
.panel-head button {
margin: 0 10px 0 0;
}
.panel-head .status {
align-items: center;
}
.panel-head .status div {
line-height: 1;
}
.panel-head .status .color {
width: 16px;
height: 16px;
margin: 0 10px 0 30px;
}
.stay-confirm {
background: #8558FA;
border-color: #8558FA;
}
.stay-tostore {
background: #1475FA;
border-color: #1475FA;
}
.arrived-store {
background: #FA5B14;
border-color: #FA5B14;
}
.completed {
background-color: #10C610;
border-color: #10C610;
}
.cancelled {
background-color: #E6E6E6;
border-color: #E6E6E6;
}
.panel-body {
margin-top: 20px;
position: relative;
}
.panel-body .time-type{
position:absolute;
top:14px;
right:0px;
display:flex;
flex-direction: row;
}
.panel-body .time-type span{
width:32px;
line-height:32px;
text-align: center;
border:1px solid #E6E6E6;
font-size:14px;
cursor: pointer;
}
.panel-body .time-type span:nth-child(1){
border-right:none;
}
.panel-body .time-type span:nth-child(2){
border-left:none;
}
.panel-body .time-type span.on{
background: #8558FA;
border-color:#8558FA;
color:#fff;
}
.panel-body .time-data{
height:100%;
}
.panel-body .head > div, .panel-body .body > div {
display: flex;
align-items: center;
justify-content: center;
flex: 1;
border-right: 1px solid #E6E6E6;
}
.panel-body .head > div:last-child, .panel-body .body > div:last-child {
border-right: 0;
}
.panel-body .head {
height: 60px;
border: 1px solid #E6E6E6;
}
.panel-body .head button {
font-size: 12px;
padding: 0 10px;
}
.panel-body .head span {
font-size: 12px;
margin-left: 5px;
}
.panel-body .body {
height: 50vh;
border: 1px solid #E6E6E6;
border-top:none;
}
.panel-body .time-wrap {
font-size: 18px;
align-items: center;
justify-content: center;
height: 60px;
}
.panel-body .time-wrap .date{
flex: unset;
margin: 0 20px;
border: none;
}
.panel-body .time-wrap .iconfont {
font-size: 18px;
cursor: pointer;
font-weight: bold;
}
.panel-body .prev,.panel-body .next {
cursor: pointer;
}
.panel-body .body > div {
height: 100%;
}
.panel-body .body .common-scrollbar {
overflow-y: scroll;
}
.panel-body .body .iconqianhou1, .panel-body .body .iconqianhou2 {
font-size: 0.28rem;
color: #E6E6E6;
}
.panel-body .body .box {
width: 100%;
}
.panel-body .body .panel-item {
width: calc(100% - 20px);
margin: 20px 10px 0 10px;
padding: 10px;
border-width: 4px 1px 1px 1px;
border-style: solid;
box-sizing: border-box;
border-radius: 4px;
background-color: #fff !important;
}
.panel-body .body .panel-item:last-child {
margin-bottom: 20px;
}
.panel-body .body .common-scrollbar {
display: block;
}
.panel-body .body .username {
font-size: 14px;
line-height: 1;
}
.panel-body .body .time {
color: #fff;
font-size: 12px;
padding: 5px;
line-height: 1;
width: auto;
display: inline-block;
margin-top: 10px;
border-radius: 2px;
}
.panel-body .body .service {
margin-top: 10px;
line-height: 1;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.panel-body .body .action {
text-align: right;
margin-top: 5px;
}
.panel-body .body .action:after{
clear: both;
}
.panel-body .body .action .iconfont {
font-size: 14px;
color: #E6E6E6;
cursor: pointer;
border: 1.5px solid #ccc;
border-radius: 50%;
padding: 2px;
}
.common-scrollbar {
overflow-y: scroll;
box-sizing: border-box;
}
.common-scrollbar::-webkit-scrollbar {
width: 6px;
height: 6px;
background-color: rgba(0, 0, 0, 0);
}
.common-scrollbar::-webkit-scrollbar-button {
display: none;
}
.common-scrollbar::-webkit-scrollbar-thumb {
border-radius: 6px;
box-shadow: inset 0 0 6px rgba(45, 43, 43, 0.45);
background-color: #ddd;
}
.common-scrollbar::-webkit-scrollbar-track {
background-color: transparent;
}
.panel-body .month-table .table-tr{
display:flex;
flex-direction: row;
border:1px solid #E6E6E6;
border-bottom:none;
}
.panel-body .month-table .table-body .table-tr:last-child{
border-bottom:1px solid #E6E6E6;
}
.panel-body .month-table .table-tr .table-td{
width:100%;
border-right:1px solid #E6E6E6;
}
.panel-body .month-table .table-tr .table-td:last-child{
border-right: none;
}
.panel-body .month-table .table-head .table-td{
text-align: center;
line-height:40px;
}
.panel-body .month-table .table-body .table-td{
text-align: left;
}
.panel-body .month-table .table-body .table-td .top{
border-bottom:1px solid #E6E6E6;
line-height: 26px;
text-indent: 8px;
}
.panel-body .month-table .table-body .table-td.not-curr-month .top{
color:#909399;
}
.panel-body .month-table .table-body .table-td .bottom{
height:78px;
}
.panel-body .month-table .table-body .table-td .bottom .item-box{
position: relative;
}
.panel-body .month-table .table-body .table-td .bottom .item{
display:flex;
flex-direction: row;
align-items: center;
cursor: pointer;
}
.panel-body .month-table .table-body .table-td .bottom .item span:first-child{
display:block;
width:4px;
height:4px;
min-width: 4px;
border-radius: 50%;
/*background: #60BECA;*/
margin:0 6px;
}
.panel-body .month-table .table-body .table-td .bottom .item span:last-child{
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
overflow: hidden;
}
.panel-body .month-table .table-body .table-td .bottom .item-box .detail-card{
position: absolute;
bottom:25px;
left:30px;
border:1px solid #ccc;
width:150px;
padding:8px 8px;
background: #fff;
z-index:1;
border-radius: 3px;
display:none;
}
.panel-body .month-table .table-body .table-td .bottom .item-box .detail-card:after {
position: absolute;
left: 40px;
bottom: -6px;
width: 10px;
height: 10px;
margin-top: -2px;
border-right: 1px solid #ccc;
border-bottom: 1px solid #ccc;
-webkit-transform: rotate(45deg);
content: '';
background: #ffffff;
}
.panel-body .month-table .table-body .table-td .bottom .item-box .detail-card .time{
display:inline-block;
color:#fff;
padding:2px 5px;
border-radius: 2px;
margin-top:2px;
margin-bottom:2px;
}
.panel-body .month-table .table-body .table-td .bottom .item-box .detail-card .state{
text-align: right;
margin-top:2px;
}
.panel-body .month-table .table-body .table-td .bottom .item-box .detail-card .service{
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
.panel-body .month-table .table-body .table-td .bottom .item-box:hover .detail-card{
display: block;
}
.panel-body .month-table .table-body .table-td .bottom .more{
text-indent: 6px;
cursor: pointer;
}
.panel-body .month-table .table-body .table-td .bottom .more a{
color:#8558FA;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

View File

@@ -0,0 +1,347 @@
requestAdd = 'cardservice://shop/card/addGoods';
requestEdit = 'cardservice://shop/card/editGoods';
goodsTag = '卡项';
var relationGoods = [];
// 追加刷新商品sku数据
appendRefreshGoodsSkuData = {
service_length: 0
};
// 追加单规格数据
function appendSingleGoodsData(data) {
return {
service_length: data.field.service_length
};
}
// 追加保存数据
function appendSaveData(data) {
var card_type = $('.card-type-item.active').attr('data-value');
if (card_type == 'oncecard') {
relationGoods.forEach(function (item, index) {
item.num = $('.relation-goods-table.oncecard .layui-table-body tr:eq(' + index + ') .num').val();
})
} else {
relationGoods.forEach(function (item, index) {
item.discount = $('.relation-goods-table.discountcard .layui-table-body tr:eq(' + index + ') .discount').val();
})
}
return {
card_type: card_type,
validity_type: $('[name="validity_type"]:checked').val(),
relation_goods: JSON.stringify(relationGoods) // 卡项参数格式
};
}
// 编辑初始化数据回调
function initEditDataCallBack() {
relationGoods = $('[name="relation_goods"]').val() ? JSON.parse($('[name="relation_goods"]').val()) : [];
}
$(function () {
layui.use(['element', 'laytpl', 'form', 'laydate', 'table'], function () {
form = layui.form;
element = layui.element;
laytpl = layui.laytpl;
laydate = layui.laydate;
laytable = layui.table;
form.render();
var time = new Date();
var currentTime = time.toLocaleDateString + " " + time.getHours() + ":" + time.getMinutes() + ":" + time.getSeconds();
//卡项有效期
laydate.render({
elem: '#validity_time', //指定元素
type: 'datetime',
min: currentTime
});
//核销有效期类型
form.on('radio(validity_type)', function (data) {
var value = parseInt(data.value);
$('.validity-type').addClass('layui-hide');
$('.validity-type.validity-type-' + value).removeClass('layui-hide');
});
// 选择卡项类型
$('.card-type-item').click(function () {
if ($(this).hasClass('active')) return;
var value = $(this).attr('data-value');
$(this).addClass('active').siblings('.card-type-item').removeClass('active');
$('.card-type-content').hide();
$('.card-type-content.' + value).show();
fetchRelationGoods();
});
// 选择关联商品
$('.add-relation-goods').click(function () {
var skuids = [];
relationGoods.forEach(function (item) {
skuids.push(item.sku_id)
});
var select_goods_class = $(this).data('goods-class');
goodsSelect(function (data) {
var skuList = [];
for (var key in data) {
for (var sku in data[key].selected_sku_list) {
var item = data[key].selected_sku_list[sku];
skuList.push(item);
}
}
relationGoods = skuList;
fetchRelationGoods();
}, skuids, {mode: 'sku', goods_class: select_goods_class ?? '1,4'});
});
function fetchRelationGoods() {
var cardType = $('.card-type-item.active').attr('data-value');
var elem = cardType == 'discountcard' ? '#relationDiscountGoods' : '#relationGoods';
var cols = [
{
field: 'sku_name',
title: '商品/项目名称',
width: '35%'
},
{
field: 'goods_class_name',
title: '商品类型',
},
{
field: 'sex',
title: '售价',
width: '15%',
align: 'right',
templet: function (data) {
return '¥' + data.price;
}
},
{
title: '操作',
align: 'right',
templet: function (data) {
return `<a href="javascript:;" class="text-color delete">删除</a>`;
}
}
];
switch (cardType) {
case 'oncecard':
cols.splice(2, 0, {
title: '可用次数/数量',
align: 'center',
templet: function (data) {
return `<input type="text" placeholder="0" lay-verify="use_num" value="` + (data.num ? data.num : '') + `" class="layui-input len-short num" autocomplete="off">`;
}
});
break;
case 'discountcard':
cols.splice(2, 0, {
title: '可享折扣',
align: 'center',
templet: function (data) {
return `<input type="text" placeholder="0" lay-verify="discount" value="` + (data.discount ? data.discount : '') + `" class="layui-input len-short discount" autocomplete="off"> %`;
}
});
break;
}
var _table = laytable.render({
elem: elem,
data: relationGoods,
cols: [cols],
skin: 'nob',
done: function () {
if (cardType == 'oncecard' || cardType == 'discountcard') $('.card-type-content .batch-set').show();
else $('.card-type-content .batch-set').hide();
$('body').off('click', '.relation-goods-table .delete').on('click', '.relation-goods-table .delete', function () {
$(this).parents('tr').remove();
relationGoods.splice($(this).parents('tr').index(), 1);
})
}
});
if (!relationGoods.length) {
$(elem).next('.layui-table-view').remove();
$('.card-type-content .batch-set').hide();
}
}
fetchRelationGoods();
// 关联商品批量设置
$('.batch-set .set').click(function () {
var parents = $(this).parents('.batch-set');
parents.find('.set-item').hide();
parents.find('.set-content-wrap').show();
});
$('.batch-set .cancel').click(function () {
var parents = $(this).parents('.batch-set');
parents.find('.value').val('');
parents.find('.set-item').show();
parents.find('.set-content-wrap').hide();
});
$('.batch-set .confirm').click(function () {
var parents = $(this).parents('.batch-set');
var value = parents.find('.value').val();
var type = parents.find('.value').attr('data-type');
if (!regExp.required.test(value)) {
layer.msg('请输入要设置的值');
return;
}
if (type == 'num') {
if (!regExp.number.test(value)) {
layer.msg('可用次数格式错误');
return;
}
if (parseInt(value) < 1) {
layer.msg('可用次数不能小于等于0');
return;
}
}
if (type == 'discount') {
if (!regExp.number.test(value)) {
layer.msg('折扣格式错误');
return;
}
if (parseInt(value) < 1 || parseInt(value) > 99) {
layer.msg('折扣需在[1-99]之间设置');
return;
}
}
// 批量设置值
$(this).parents('.layui-form-item').find('.' + type).val(value);
parents.find('.value').val('');
parents.find('.set-item').show();
parents.find('.set-content-wrap').hide();
});
form.on('radio(discount_goods_type)', function (data) {
$('.discount-goods').hide();
$('.discount-goods.' + data.value).show();
});
form.verify({
//销售价
price: function (value) {
if (!$("input[name='spec_type']").is(":checked")) {
if (value.length == 0) {
element.tabChange('goods_tab', "price-stock");
return "请输入卡项开卡价";
}
if (isNaN(value) || !regExp.digit.test(value)) {
element.tabChange('goods_tab', "price-stock");
return '[卡项开卡价]格式输入错误';
}
}
},
renew_price: function (value) {
if (value.length == 0) {
element.tabChange('goods_tab', "price-stock");
return "请输入卡项续费价";
}
if (isNaN(parseInt(value)) || !regExp.digit.test(value)) {
element.tabChange('goods_tab', "price-stock");
return '[卡项续费价]格式输入错误';
}
},
//有效期
validity_day: function (value) {
var verify_validity_type = $('[name="validity_type"]:checked').val();
if (verify_validity_type == 1) {
if (isNaN(value) || !regExp.number.test(value)) {
element.tabChange('goods_tab', "basic");
return '[卡项有效期]格式输入错误';
}
if (value < 1) {
element.tabChange('goods_tab', "basic");
return '卡项有效期不能小于1天';
}
}
},
validity_time: function (value) {
var verify_validity_type = $('[name="validity_type"]:checked').val();
if (verify_validity_type == 2 && value.length == 0) {
element.tabChange('goods_tab', "basic");
return "请输入有效期";
}
},
relation_goods: function () {
var cardType = $('.card-type-item.active').attr('data-value');
if (cardType != 'discountcard' && !$('.relation-goods-table.oncecard .layui-table-body tr').length) {
return '请选择卡项内容';
}
},
relation_discount_goods: function () {
var cardType = $('.card-type-item.active').attr('data-value');
if (cardType == 'discountcard' && $('[name="discount_goods_type"]:checked').val() != 'all' && !$('.relation-goods-table.discountcard .layui-table-body tr').length) {
return '请选择卡项内容';
}
},
use_num: function (value) {
var cardType = $('.card-type-item.active').attr('data-value');
if (cardType == 'oncecard') {
if (!regExp.required.test(value)) {
return '请输入可用次数/数量';
}
if (!regExp.number.test(value)) {
return '次数/数量格式错误';
}
if (parseInt(value) < 1) {
return '可用次数/数量不能小于等于0';
}
}
},
discount: function (value) {
var cardType = $('.card-type-item.active').attr('data-value');
if (cardType == 'discountcard' && $('[name="discount_goods_type"]:checked').val() != 'all') {
if (!regExp.required.test(value)) {
return '请输入折扣卡折扣';
}
if (!regExp.number.test(value)) {
return '折扣卡折扣格式错误';
}
if (parseInt(value) < 1 || parseInt(value) > 99) {
return '折扣卡折扣需在[1-99]之间设置';
}
}
},
common_discount: function (value) {
var cardType = $('.card-type-item.active').attr('data-value');
if (cardType == 'discountcard' && $('[name="discount_goods_type"]:checked').val() == 'all') {
if (!regExp.required.test(value)) {
return '请输入折扣卡折扣';
}
if (!regExp.number.test(value)) {
return '折扣卡折扣格式错误';
}
if (parseInt(value) < 1 || parseInt(value) > 99) {
return '折扣卡折扣需在[1-99]之间设置';
}
}
},
common_num: function (value) {
var cardType = $('.card-type-item.active').attr('data-value');
if (cardType == 'commoncard') {
if (!regExp.required.test(value)) {
return '请输入卡项可用次数/数量';
}
if (!regExp.number.test(value)) {
return '卡项可用次数/数量格式错误';
}
if (parseInt(value) < 1) {
return '卡项可用次数/数量不能小于等于0';
}
}
}
});
});
});

View File

@@ -0,0 +1,109 @@
var layCascader, goodsCategory = [];
layui.use(['layCascader'], function () {
layCascader = layui.layCascader;
$('.goods-category-con-wrap .layui-block').each(function () {
var category_id = $(this).find('.category_id').val();
var _this = this;
fetchCategory({elem: $(this).find('.select-category'), value: category_id ? parseInt(category_id.split(',').splice(-1)) : ''},
function (value, node) {
var categoryId = [];
node.path.forEach(function (item) {
categoryId.push(item.value)
});
$(_this).find('.category_id').val(categoryId.toString())
}
)
});
});
// 刷新项目分类,更新选择项目分类数据
$('body').off('click', '.goods-category-con-wrap .js-refresh-category').on('click', '.goods-category-con-wrap .js-refresh-category', function () {
$.ajax({
url : ns.url("cardservice://shop/servicecategory/lists"),
dataType: 'JSON',
type: 'POST',
async: false,
success: function(res) {
goodsCategory = res.data;
// 刷新商品分类下拉框数据
$('.goods-category-con-wrap .layui-block').each(function () {
$(this).find('.el-cascader').remove(); // 清空渲染
var category_id = $(this).find('.category_id').val();
var _this = this;
fetchCategory({elem: $(this).find('.select-category'), value: category_id ? parseInt(category_id.split(',').splice(-1)) : ''},
function (value, node) {
var categoryId = [];
node.path.forEach(function (item) {
categoryId.push(item.value)
});
$(_this).find('.category_id').val(categoryId.toString())
}
)
});
}
});
});
$('body').off('click', '.goods-category-wrap-box .js-add-category').on('click', '.goods-category-wrap-box .js-add-category', function () {
if ($('.goods-category-con-wrap .layui-block').length >= 10) {
layer.msg('最多添加十个分类');
return;
}
var h = `<div class="layui-block">
<div class="layui-input-inline cate-input-default">
<input type="text" readonly lay-verify="required" autocomplete="off" class="layui-input len-mid select-category" />
<input type="hidden" class="category_id" />
</div>
<a href="javascript:;" class="text-color js-delete-category">删除</a>
</div>`;
$('.goods-category-con-wrap').append(h);
fetchCategory({elem: $('.goods-category-con-wrap .layui-block:last-child').find('.select-category')}, function (value, node) {
var categoryId = [];
node.path.forEach(function (item) {
categoryId.push(item.value)
});
$('.goods-category-con-wrap .layui-block:last-child').find('.category_id').val(categoryId.toString());
})
});
$('body').off('click', '.goods-category-con-wrap .js-delete-category').on('click', '.goods-category-con-wrap .js-delete-category', function () {
$(this).parents('.layui-block').remove();
});
/**
* 渲染分类选择
* @param option
* @param callback
*/
function fetchCategory(option, callback){
if (!goodsCategory.length) {
$.ajax({
url : ns.url("cardservice://shop/servicecategory/lists"),
dataType: 'JSON',
type: 'POST',
async: false,
success: function(res) {
goodsCategory = res.data;
}
})
}
var _option = {
options: goodsCategory,
props: {
value: 'category_id',
label: 'category_name',
children: 'child_list'
}
};
if (option) Object.assign(_option, option);
var _cascader = layCascader(_option);
_cascader.changeEvent(function (value, node) {
typeof callback == 'function' && callback(value, node)
});
}

View File

@@ -0,0 +1,407 @@
var link_url_json = $("input[name='link_url']").val();
if(link_url_json) {
link_url_json = JSON.parse(link_url_json);
$(".link-url-show").text(link_url_json.title);
}
var laytpl, form, layerIndex;
var categoryFullName = [];//组装名称
var saveData = null;
var totalUploadNum = 0;
var completeUploadNum = 0;
$(function () {
//编辑时赋值组装名称
if ($("input[name='category_full_name']").length > 0) {
categoryFullName = $("input[name='category_full_name']").val().split("/").slice(0, $("input[name='category_full_name']").val().split("/").length - 1);
}
layui.use(['form', 'laytpl'], function () {
var repeat_flag = false;//防重复标识
laytpl = layui.laytpl;
form = layui.form;
/**
* 表单验证
*/
form.verify({
commission_rate: function (value) {
var reg = /^\d{0,2}(.?\d{0,2})$/;
if (value.length > 0) {
if (isNaN(value)) {
return '佣金比率输入错误';
}
if (!reg.test(value) || value < 0 || value > 100) {
return '佣金比率范围:0~100%';
}
}
},
num: function (value) {
if (value == '') {
return;
}
if (value % 1 != 0) {
return '排序数值必须为整数';
}
if (value < 0) {
return '排序数值必须为大于0';
}
}
});
var upload = new Upload({
elem: '#imgUpload',
size:100,
auto:false,
bindAction:'#imageUploadAction',
callback: function(res) {
uploadComplete('image', res.data.pic_path);
}
});
var adv_upload = new Upload({
elem: '#imgUploadAdv',
auto:false,
bindAction:'#imageAdvUploadAction',
callback: function(res) {
uploadComplete('image_adv', res.data.pic_path);
}
});
function uploadComplete(field, pic_path) {
saveData.field[field] = pic_path;
completeUploadNum += 1;
if(completeUploadNum == totalUploadNum){
saveFunc();
}
}
form.on('submit(save)', function (data) {
saveData = data;
var obj = $("img.img_prev[data-prev='1']");
totalUploadNum = obj.length;
if(totalUploadNum > 0){
obj.each(function(){
var actionId = $(this).attr('data-action-id');
$(actionId).click();
})
}else{
saveFunc();
}
return false;
});
function saveFunc(){
var data = saveData;
categoryFullName.push(data.field.category_name);
data.field.category_full_name = categoryFullName.join("/");
data.field.attr_class_name = $("select[name='attr_class_id'] option:checked").text();
// 删除图片
if(!data.field.image) upload.delete();
if(!data.field.image_adv) adv_upload.delete();
if (repeat_flag) return false;
repeat_flag = true;
var url = ns.url("cardservice://shop/servicecategory/addCategory");
if (data.field.category_id) url = ns.url("cardservice://shop/servicecategory/editCategory");
$.ajax({
url: url,
data: data.field,
dataType: 'json',
type: 'post',
success: function (data) {
layer.msg(data.message);
if (data.code == 0) {
location.hash = ns.hash("cardservice://shop/servicecategory/lists");
} else {
repeat_flag = false;
}
}
});
}
//保存上级分类
form.on('submit(save_pid)', function (data) {
var option_category_id_1 = $("select[name='category_id_1'] option:checked");
var option_category_id_2 = $("select[name='category_id_2'] option:checked[value!='0']");
categoryFullName = [];
var level, category_name, pid;
if (option_category_id_1.length) {
level = parseInt(option_category_id_1.attr("data-level"));
category_name = option_category_id_1.text();
pid = option_category_id_1.val();//上级分类id
var category_id_1 = option_category_id_1.val();//一级分类id
if (category_id_1 > 0) {
$("input[name='category_id_1']").val(category_id_1);
categoryFullName.push(category_name);
}
}
if($("input[name='category_name_1']").length){
categoryFullName.push($("input[name='category_name_1']").val());
}
// 选中了二级商品分类
if (option_category_id_2.length) {
level = parseInt(option_category_id_2.attr("data-level"));
category_name = option_category_id_2.text();
pid = option_category_id_2.val();
var category_id_2 = option_category_id_2.val();//二级分类id
if (category_id_2 > 0) {
$("input[name='category_id_2']").val(category_id_2);
categoryFullName.push(category_name);
}
}
$(".js-pid span").text(category_name);
$("input[name='pid']").val(pid);
$("input[name='level']").val(level + 1);//当前添加的层级+1
layer.close(layerIndex);
return false;
});
setTimeout(()=>{
form.render();
},600)
});
});
//选择商品分类弹出框
function selectedCategoryPopup() {
if ($("input[name='category_id']").length) {
// 修改
editSelectedPid();
} else {
//添加
addSelectedPid();
}
}
/**
* 获取商品分类列表
* @param data
* @param callback
*/
function getCategoryList(data, callback) {
$.ajax({
url: ns.url("cardservice://shop/servicecategory/getCategoryList"),
data: data,
dataType: 'json',
type: 'post',
async: false,
success: function (res) {
var data = res.data;
if (callback) callback(data);
}
});
}
/**
* 添加时,选择上级分类
*/
function addSelectedPid() {
//查询一级商品分类
getCategoryList({pid: 0}, function (list) {
var html = $("#selectedCategory").html();
var data = {
category_id_1: $("input[name='category_id_1']").val(),
category_list_1: list
};
laytpl(html).render(data, function (html) {
layerIndex = layer.open({
title: '选择商品分类',
skin: 'layer-tips-class',
type: 1,
area: ['450px'],
content: html,
success: function () {
form.render();
form.on('select(category_id_1)', function (item) {
if (item.value > 0) {
getCategoryList({pid: item.value}, function (list) {
var h = '<option value="0">请选择</option>';
for (var i = 0; i < list.length; i++) {
if ($("input[name='category_id_2']").val() == list[i].category_id) {
h += '<option value="' + list[i].category_id + '" data-level="' + list[i].level + '" selected>' + list[i].category_name + '</option>';
} else {
h += '<option value="' + list[i].category_id + '" data-level="' + list[i].level + '">' + list[i].category_name + '</option>';
}
}
$("select[name='category_id_2']").html(h);
form.render("select");
});
} else {
//顶级分类不需要查询
$("select[name='category_id_2']").html('<option value="0">请选择</option>');
form.render("select");
}
});
$("select[name='category_id_1']").siblings("div.layui-form-select").find("dl dd[lay-value='" + $("input[name='category_id_1']").val() + "']").click();
}
});
});
});
}
/**
* 编辑时,选择上级分类
*/
function editSelectedPid() {
var html = $("#selectedCategory").html();
laytpl(html).render({}, function (html) {
layerIndex = layer.open({
title: '选择商品分类',
skin: 'select-category',
type: 1,
area: ['650px'],
content: html,
btn: ['确定', '取消'],
success: function () {
var pid = $('input[name="pid"]').val();
var level = $('input[name="level"]').val();
$('.table_div input[name="category_id"][data-category-id="'+pid+'"]').attr('checked', true);
if(level == 3) $('.table_div div[data-cateid="'+pid+'"]').parents('.table_two_div').show().prev('.table_tr').find('.switch').attr('data-open', 1).html('-');
form.render();
$(".js-switch").click(function (event) {
event.stopPropagation();
var category_id = $(this).attr("data-category-id");
var level = $(this).attr("data-level");
var open = parseInt($(this).attr("data-open").toString());
if(open){
$(".goods-category-list .layui-table tr[data-category-id-"+ level+"='" + category_id + "']").hide();
// $(this).children("img").removeClass('rotate');
$(this).text("+");
if(level == 1) $(this).parents('.table_tr').siblings('.table_two_div').hide();
else if(level == 2) $(this).parents('.table_tr').siblings('.table_three').hide();
}else{
$(".goods-category-list .layui-table tr[data-category-id-"+ level+"='" + category_id + "']").show();
$(this).text("-");
// $(this).children("img").addClass('rotate');
if(level == 1) $(this).parents('.table_tr').siblings('.table_two_div').show();
else if(level == 2) $(this).parents('.table_tr').siblings('.table_three').show();
}
$(this).attr("data-open", (open ? 0 : 1));
});
form.on('checkbox(category)', function (data) {
if(data.elem.checked==true){
$('.table_move').children('div').removeClass('layui-form-checked');
$(".table_move input").prop("checked",false);
}
$(this).parents('.table_move').children('div').addClass('layui-form-checked');
$(this).parents('.table_move').find('input').prop("checked",true);
return false;
});
},
yes: function(index, layero){
var obj = $('.table_div input[name="category_id"]:checked');
var num = $(obj).length;
if(num > 1){
layer.msg('只能选择一个上级');
return false;
}
var parent_level = $(obj).attr('data-level');
var pid = $(obj).val();
var parent_name = $(obj).attr('data-name');
if(num < 1){
parent_level = 0;
pid = 0;
parent_name = '顶级分类';
}
$.ajax({
url: ns.url("cardservice://shop/servicecategory/checkEditCategory"),
data: {
category_id: $('#category_id').val(),
pid : pid
},
dataType: 'json',
type: 'post',
async: false,
success: function (res) {
if(res.code >= 0){
$(".js-pid span").text(parent_name);
$("input[name='pid']").val(pid);
$("input[name='level']").val(parseInt(parent_level) + 1);//当前添加的层级+1
layer.close(layerIndex);
}else{
layer.msg(res.message)
}
}
});
}
});
});
}
/**
* 获取商品分类信息
* @param category_id
* @param callback
*/
function getCategoryInfo(category_id, callback) {
$.ajax({
url: ns.url("cardservice://shop/servicecategory/getCategoryInfo"),
data: {category_id: category_id},
dataType: 'json',
type: 'post',
async: false,
success: function (res) {
var data = res.data;
if (callback) callback(data);
}
});
}
function backCardServiceCategoryList() {
location.hash = ns.hash("cardservice://shop/servicecategory/lists")
}
function selectedLink() {
if (link_url_json == "") {
link_url_json = {};
}
ns.select_link(link_url_json, function (data) {
for (var o in data) {
if (data[o] == null) delete data[o];
}
$("input[name='link_url']").val(JSON.stringify(data));
$(".link-url-show-wrap .layui-input-block").find('.link-url-show').remove();
$(".link-url-show-wrap .layui-input-block").prepend(`<span class="link-url-show">${data.title}</span>`);
});
}

View File

@@ -0,0 +1,104 @@
requestAdd = 'cardservice://shop/service/addGoods';
requestEdit = 'cardservice://shop/service/editGoods';
goodsTag = '项目';
// 追加刷新商品sku数据
appendRefreshGoodsSkuData = {
service_length: 0
};
// 追加单规格数据
function appendSingleGoodsData(data) {
return {
service_length: data.field.service_length
};
}
// 追加保存数据
function appendSaveData(data) {
return {
verify_validity_type: $('[name="verify_validity_type"]:checked').val()
};
}
$(function () {
layui.use(['element', 'laytpl', 'form', 'laydate'], function () {
form = layui.form;
element = layui.element;
laytpl = layui.laytpl;
laydate = layui.laydate;
form.render();
var time = new Date();
var currentTime = time.toLocaleDateString + " " + time.getHours() + ":" + time.getMinutes() + ":" + time.getSeconds();
//核销有效期
laydate.render({
elem: '#virtual_time', //指定元素
type: 'datetime',
min: currentTime
});
//卡项有效期类型
form.on('radio(verify_validity_type)', function (data) {
var value = parseInt(data.value);
$('.validity-type').addClass('layui-hide');
$('.validity-type.validity-type-' + value).removeClass('layui-hide');
});
form.verify({
//有效期
virtual_indate: function (value) {
var verify_validity_type = $('[name="verify_validity_type"]:checked').val();
if (verify_validity_type == 1) {
if (isNaN(value) || !regExp.number.test(value)) {
element.tabChange('goods_tab', "basic");
return '[核销有效期]格式输入错误';
}
if (value < 1) {
element.tabChange('goods_tab', "basic");
return '核销有效期不能小于1天';
}
}
},
virtual_time: function (value) {
var verify_validity_type = $('[name="verify_validity_type"]:checked').val();
if (value.length == 0 && verify_validity_type == 2) {
element.tabChange('goods_tab', "basic");
return "请输入有效期";
}
},
service_length: function (value) {
if (value.length > 0) {
value = parseInt(value);
if (isNaN(value) || !regExp.number.test(value)) {
element.tabChange('goods_tab', "price-stock");
return '[服务时长]格式输入错误';
}
if (value < 0) {
element.tabChange('goods_tab', "price-stock");
return '[服务时长]不能小于0';
}
}
},
//销售价
service_price: function (value) {
if (!$("input[name='spec_type']").is(":checked")) {
if (value.length == 0) {
element.tabChange('goods_tab', "basic");
return "请输入销售价";
}
if (isNaN(value) || !regExp.digit.test(value)) {
element.tabChange('goods_tab', "basic");
return '[销售价]格式输入错误';
}
}
},
});
});
});

View File

@@ -0,0 +1,537 @@
<link rel="stylesheet" href="ADDON_CARDSERVICE_CSS/add_reserve.css">
<div class="main-wrap">
<div class="layui-form" lay-filter="form">
<br>
{empty name="info"}
<div class="layui-form-item">
<label class="layui-form-label"><span class="required">*</span>手机号:</label>
<div class="layui-input-inline search-wrap">
<input type="text" class="layui-input len-mid" name="mobile" placeholder="请输入客户手机号" autocomplete="off" lay-verify="required">
<i class="iconfont iconsousuo"></i>
</div>
</div>
{else/}
<input type="hidden" name="reserve_id" value="{$info.reserve_id}">
{/empty}
<div class="error">请输入正确的手机号</div>
<div class="layui-form-item" lay-verify="member">
<label class="layui-form-label"><span class="required">*</span>客户:</label>
<div class="layui-input-block member">
{notempty name="info"}
<div class="member-info">
<input type="hidden" name="member_id" value="{$info.member_id}">
{notempty name="$info.headimg"}
<img src="{:img($info.headimg)}" onerror="this.src = 'STATIC_IMG/default_img/head.png' " alt="">
{else/}
<img src="STATIC_IMG/default_img/head.png" alt="">
{/notempty}
<div class="info">
<div class="name">{$info.nickname}</div>
<div>
<span>手机号:{$info.mobile}</span>
</div>
</div>
</div>
{/notempty}
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"><span class="required">*</span>预约门店:</label>
<div class="layui-input-inline len-mid">
<select name="store_id" lay-filter="store" {notempty name="info"}disabled{/notempty}>
{foreach $store_list as $k=> $v}
<option value="{$v.store_id}" {if !empty($info) && $info['store_id'] == $v['store_id']} selected {/if}>{$v.store_name}</option>
{/foreach}
</select>
</div>
</div>
<div class="layui-form-item" lay-verify="service">
<label class="layui-form-label"><span class="required">*</span>项目:</label>
<div class="layui-input-block service-table">
<table class="layui-table" lay-skin="line">
<colgroup>
<col width="50%">
<col width="30%">
<col width="20%">
</colgroup>
<thead>
<tr>
<th>预约项目</th>
<th>员工</th>
<th class="align-center">操作</th>
</tr>
</thead>
<tbody>
{notempty name="info"}
{foreach name="info.item" item="vo"}
<tr>
<td>
<div class="service-item service-item-btn">
<div class="info" >
<input type="hidden" name="sku" value="{$vo.sku_id}">
<input type="hidden" name="goods" value="{$vo.goods_id}">
<div class="title" title="{$vo.goods_name}">{$vo.goods_name}</div>
<div class="desc">项目时长:{$vo.service_length}分钟 ¥{$vo.price}</div>
</div>
<i class="iconfont iconlower-triangle"></i>
</div>
</td>
<td>
<div class="servicer-item servicer-item-btn">
{if $vo.reserve_user_id}
<div class="info">
<input type="hidden" name="servicer_id" value="{$vo.reserve_user_id}">
<div class="title" title="{$vo.username}">{$vo.username}</div>
</div>
{else/}
<div class="info">请选择员工</div>
{/if}
<i class="iconfont iconlower-triangle"></i>
</div>
</td>
<td class="align-center"><a href="javascript:;" class="text-color delete">删除</a></td>
</tr>
{/foreach}
{else/}
<tr>
<td>
<div class="service-item service-item-btn" id="">
<div class="info">请选择项目</div>
<i class="iconfont iconlower-triangle"></i>
</div>
</td>
<td>
<div class="servicer-item servicer-item-btn">
<div class="info">请选择员工</div>
<i class="iconfont iconlower-triangle"></i>
</div>
</td>
<td class="align-center"><a href="javascript:;" class="text-color delete">删除</a></td>
</tr>
{/notempty}
</tbody>
</table>
<button class="layui-btn layui-btn-primary add">添加</button>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"><span class="required">*</span>到店时间:</label>
<div class="layui-input-block">
<div class="layui-input-inline">
{notempty name="info"}
<input type="text" id="reserveDate" name="date" autocomplete="off" class="layui-input" placeholder="请选择到店日期" value="{:date('Y-m-d', $info.reserve_time)}" lay-verify="required">
{else/}
<input type="text" id="reserveDate" name="date" autocomplete="off" class="layui-input" placeholder="请选择到店日期" value="{:date('Y-m-d')}" lay-verify="required">
{/notempty}
<i class="iconfont iconriqi"></i>
</div>
<div class="layui-input-inline">
{notempty name="info"}
<input type="text" id="reserveTime" name="time" autocomplete="off" class="layui-input" value="{:date('H:i', $info.reserve_time)}" placeholder="请选择到店时间" readonly lay-verify="required">
{else/}
<input type="text" id="reserveTime" name="time" autocomplete="off" class="layui-input" placeholder="请选择到店时间" readonly lay-verify="required">
{/notempty}
</div>
</div>
<div class="word-aux">预约需提前{$config.advance}小时预约</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">备注:</label>
<div class="layui-input-block">
<textarea name="remark" cols="30" rows="6" class="layui-textarea len-long">{$info.remark ?? ''}</textarea>
</div>
</div>
<div class="save-wrap">
<button class="layui-btn" lay-submit lay-filter="save">保存</button>
</div>
</div>
</div>
{php}$today = strtotime(date('Y-m-d')) {/php}
<script type="text/html" id="selectTime">
<div class="select-time">
<div class="time-wrap today">
{for start="$config.start" end="$config.end + 1800" name="i" step="1800"}
{if $today + $i > time()}
<div class="time-item" data-value="{:date('H:i', ($today + $i) )}">{:date('H:i', ($today + $i) )}</div>
{/if}
{/for}
</div>
<div class="time-wrap no-today">
{for start="$config.start" end="$config.end + 1800" name="i" step="1800"}
<div class="time-item" data-value="{:date('H:i', ($today + $i) )}">{:date('H:i', ($today + $i) )}</div>
{/for}
</div>
</div>
</script>
<script type="text/html" id="selectService">
<div class="select-service">
<div class="service-wrap">
{notempty name="$service"}
<div class="flex-wrap">
{foreach name="$service" item="vo"}
<div class="item">
<input type="hidden" name="sku" value="{$vo.sku_id}">
<input type="hidden" name="goods" value="{$vo.goods_id}">
<div class="title" title="{$vo.goods_name}">{$vo.goods_name}</div>
<div class="desc">项目时长{$vo.service_length}分钟 {$vo.price}</div>
</div>
{/foreach}
</div>
{else/}
<div class="flex-wrap">
<div class="empty">暂无可预约的项目</div>
<div>
{/notempty}
</div>
</div>
</script>
<script type="text/html" id="selectServicer">
</script>
<script>
var today = "{$today}";
var laydate, dropdown, form, target, repeat = false;
layui.use(['laytpl', 'laydate', 'dropdown', 'form'], function(){
laytpl = layui.laytpl;
laydate = layui.laydate;
dropdown = layui.dropdown;
form = layui.form;
$('.search-wrap .iconfont').click(function () {
var mobile = $('[name="mobile"]').val();
if (mobile.length == 0) {
$('.error').text('请输入客户手机号').show();
return;
}
if (!ns.parse_mobile(mobile)) {
$('.error').text('请输入正确的手机号').show();
return;
}
$('.error').hide();
$.ajax({
url: ns.url("shop/member/searchMember"),
data: {
search_text: mobile
},
dataType: 'JSON',
type: 'POST',
success: function (res) {
if (res.code == 0 && res.data) {
$('.error').hide();
var info = res.data;
$('.member').html(` <div class="member-info">
<input type="hidden" name="member_id" value="` + info.member_id + `">
`+ (info.headimg ? `<img src="${ns.img(info.headimg)}" onerror="this.src = \'STATIC_IMG/default_img/head.png\' " alt="">` : '<img src="STATIC_IMG/default_head_square.png" alt="">') + `
<div class="info">
<div class="name">` + info.nickname + `</div>
<div>
<span>手机号:` + info.mobile + `</span>
</div>
</div>
</div>`);
} else {
$('.member').html('');
$('.error').text('未查找到该客户').show();
}
}
});
});
// 切换门店
form.on('select(store)', function (data) {
$.ajax({
url: ns.url("cardservice://shop/reserve/getconfig"),
data: {'store_id' : data.value},
dataType: 'JSON',
type: 'POST',
success: function (res) {
if (res.code == 0) {
let data = res.data.value;
let time = "{:time()}";
let html = `
<div class="select-time">
<div class="time-wrap today">
`;
for (let i = data.start; i <= (data.end + 1800); i+=1800){
if(parseInt(today) + parseInt(i) > time){
let data_value = ns.time_to_date(parseInt(today)+parseInt(i), 'H:i');
data_value = $.trim(data_value);
html += `<div class="time-item" data-value="${data_value}">${data_value}</div>`;
}
}
html += '</div>';
html += '<div class="time-wrap no-today">';
for (let i = data.start; i <= (data.end + 1800); i+=1800){
let data_value = ns.time_to_date(parseInt(today)+parseInt(i), 'H:i');
data_value = $.trim(data_value);
html += `
<div class="time-item" data-value="${data_value}">${data_value}</div>
`;
}
html += `
</div>
</div>
`;
$('#selectTime').html(html);
$('#reserveTime').val('');
initTime(data.week);
$('.servicer-item-btn .info').html('请选择员工');
getServicer();
}
}
})
});
laydate.render({
elem: '#reserveDate',
type: 'date',
min: 0,
done: function(value, date, endDate){
$('[name="time"]').val('');
}
});
initTime("{:implode($config.week)}");
$('body').off('click', '.time-wrap .time-item').on('click', '.time-wrap .time-item', function () {
if ($(this).hasClass('no-select')) {
layer.msg('不在可预约时间内');
return;
}
dropdown.hide('#reserveTime');
$('[name="time"]').val($(this).text());
});
getServicer();
initDropdown();
$('.service-table .add').click(function () {
var service_id = ns.gen_non_duplicate(5);
var servicer_id = ns.gen_non_duplicate(5);
$('.service-table tbody').append(`<tr>
<td>
<div class="service-item service-item-btn" id="${service_id}">
<div class="info">请选择项目</div>
<i class="iconfont iconlower-triangle"></i>
</div>
</td>
<td>
<div class="servicer-item servicer-item-btn" id="${servicer_id}">
<div class="info">请选择员工</div>
<i class="iconfont iconlower-triangle"></i>
</div>
</td>
<td class="align-center"><a href="javascript:;" class="text-color delete">删除</a></td>
</tr>`);
dropdown.suite("#"+service_id, {
template: "selectService",
maxWidth:'660',
success: function ($dom) {
$dom.addClass('border-color').css({
'outline':'none',
'border':'2px solid'
});
}
});
dropdown.suite("#"+servicer_id, {
template: "selectServicer",
maxWidth:'660',
success: function ($dom) {
$dom.addClass('border-color').css({
'outline':'none',
'border':'2px solid'
});
}
});
});
// 选择项目
$('body').off('click', '.service-wrap .item').on('click', '.service-wrap .item', function () {
var id = $(this).parents('.layu-dropdown-root').prev().attr('id');
dropdown.hide('#'+id);
var target = $('#'+id).find('.info');
if ($(this).find('[name="sku"]').val() != $(target).find('[name="sku"]').val()) {
$(target).html($(this).html());
$(target).parents('tr').find('.servicer-item .info').html('请选择员工');
}
});
// 选择员工
$('body').off('click', '.select-servicer .select-item').on('click', '.select-servicer .select-item', function () {
var id = $(this).parents('.layu-dropdown-root').prev().attr('id');
dropdown.hide('#'+id);
var target = $('#'+id).find('.info');
$(target).html($(this).html());
});
$('body').off('click', '.service-table .delete').on('click', '.service-table .delete', function () {
if ($('.service-table tbody tr').length == 1) {
layer.msg('至少需要有一项项目');
return;
}
$(this).parents('tr').remove();
});
form.verify({
member: function () {
if (!$('[name="member_id"]').val()) {
return '请选择客户';
}
},
service: function () {
if (!$('[name="sku"]').val()) {
return '请选择项目';
}
}
});
form.on('submit(save)', function (data) {
if (repeat) return;
repeat = false;
data.field.goods = [];
$('.service-table tbody tr').each(function () {
data.field.goods.push({
sku_id: $(this).find('[name="sku"]').val(),
uid: $(this).find('[name="servicer"]').val() ? $(this).find('[name="servicer"]').val() : 0
})
});
data.field.goods = JSON.stringify(data.field.goods);
$.ajax({
url: data.field.reserve_id ? ns.url("cardservice://shop/reserve/updatereserve") : ns.url("cardservice://shop/reserve/addreserve"),
data: data.field,
dataType: 'JSON',
type: 'POST',
success: function (res) {
if (res.code == 0) {
layer.msg("保存成功");
setTimeout(function(){
parent.listenerHash(); // 刷新页面
parent.layer.closeAll();
},500)
} else {
layer.msg(res.message);
repeat = false;
}
}
})
})
});
function initDropdown(){
$('.service-item-btn').each(function (i, e) {
var id = ns.gen_non_duplicate(5);
if($(e).attr('id') == '' || $(e).attr('id') == undefined) $(e).attr('id', id);
id = $(e).attr('id');
dropdown.suite("#"+id, {
template: "selectService",
maxWidth:'660',
success: function ($dom) {
$dom.addClass('border-color').css({
'outline':'none',
'border':'2px solid'
});
}
});
})
}
function initTime(config){
// 选择预约时间
dropdown.suite("#reserveTime", {
template: "selectTime",
maxWidth:'660',
success: function ($dom) {
var date = $('[name="date"]').val();
$('.time-wrap').css('display', 'none');
if (date == "{:date('Y-m-d')}") {
$('.today').css('display', 'flex')
} else {
$('.no-today').css('display', 'flex')
}
var dateArr = date.split('-'),
dateEl = new Date(dateArr[0], parseInt(dateArr[1] - 1), dateArr[2]);
if (config.indexOf(dateEl.getDay()) == -1) {
$('.time-wrap .time-item').addClass('no-select');
}
var time = $('[name="time"]').val();
$('.time-wrap .time-item[data-value="'+ time +'"]').addClass('active');
}
});
}
function getServicer(){
let store_id = $('[name="store_id"]').val();
$.ajax({
url: ns.url("cardservice://shop/reserve/servicerlist"),
data: {'store_id':store_id},
dataType: 'JSON',
type: 'POST',
success: function (res) {
if (res.code == 0) {
let html = '';
let store_html = '';
for(let i in res.data){
let item = res.data[i];
store_html += `
<div class="select-item">
<div class="title">${item.username}</div>
<input type="hidden" name="servicer" value="${item.uid}">
</div>
`;
}
html += `
<div class="select-servicer">
<div class="select-item">
<div class="title">不选择员工</div>
<input type="hidden" name="servicer" value="0">
</div>
${store_html}
</div>
`;
$('#selectServicer').html(html);
$('.servicer-item-btn').each(function (i, e) {
var id = ns.gen_non_duplicate(5);
if($(e).attr('id') == '' || $(e).attr('id') == undefined) $(e).attr('id', id);
id = $(e).attr('id');
dropdown.suite("#"+id, {
template: "selectServicer",
maxWidth:'660',
success: function ($dom) {
$dom.addClass('border-color').css({
'outline':'none',
'border':'2px solid'
});
}
});
})
}
}
})
}
</script>

View File

@@ -0,0 +1,108 @@
<style>
.layui-input {
display: inline-block;
}
</style>
<div class="layui-form main-wrap form-wrap" lay-filter="form">
<div class="layui-form-item">
<label class="layui-form-label"><span class="required">*</span>预约时间:</label>
<div class="layui-input-block">
<input type="checkbox" title="周一" lay-skin="primary" name="week[]" value="1" {if in_array(1, $config.week)}checked{/if}>
<input type="checkbox" title="周二" lay-skin="primary" name="week[]" value="2" {if in_array(2, $config.week)}checked{/if}>
<input type="checkbox" title="周三" lay-skin="primary" name="week[]" value="3" {if in_array(3, $config.week)}checked{/if}>
<input type="checkbox" title="周四" lay-skin="primary" name="week[]" value="4" {if in_array(4, $config.week)}checked{/if}>
<input type="checkbox" title="周五" lay-skin="primary" name="week[]" value="5" {if in_array(5, $config.week)}checked{/if}>
<input type="checkbox" title="周六" lay-skin="primary" name="week[]" value="6" {if in_array(6, $config.week)}checked{/if}>
<input type="checkbox" title="周日" lay-skin="primary" name="week[]" value="0" {if in_array(0, $config.week)}checked{/if}>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"></label>
<div class="layui-input-inline len-short">
{php}$today = strtotime(date('Y-m-d')) {/php}
<select name="start">
{for start="0" end="48" name="i"}
<option value="{$i * 1800}" {if $config.start == ($i * 1800)}selected{/if}>{:date('H:i', $today + ($i * 1800))}</option>
{/for}
</select>
</div>
<div class="layui-form-mid layui-word-aux">-</div>
<div class="layui-input-inline len-short">
<select name="end" id="">
{for start="0" end="48" name="i"}
<option value="{$i * 1800}" {if $config.end == ($i * 1800)}selected{/if}>{:date('H:i', $today + ($i * 1800))}</option>
{/for}
</select>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"><span class="required">*</span>预约时间间隔:</label>
<div class="layui-input-block">
<input type="radio" title="30分钟" lay-skin="primary" name="interval" value="30" {if empty($config) || $config.interval == 30}checked{/if}>
<input type="radio" title="1小时" lay-skin="primary" name="interval" value="60" {if !empty($config) && $config.interval == 60}checked{/if}>
<input type="radio" title="90分钟" lay-skin="primary" name="interval" value="90" {if !empty($config) && $config.interval == 90}checked{/if}>
<input type="radio" title="2小时" lay-skin="primary" name="interval" value="120" {if !empty($config) && $config.interval == 120}checked{/if}>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"><span class="required">*</span>预约提前:</label>
<div class="layui-input-block">
<input type="number" name="advance" value="{$config.advance ?? 1}" class="layui-input len-short" lay-verify="advance">
<div class="layui-word-aux">小时</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"><span class="required">*</span>每时段可预约:</label>
<div class="layui-input-block">
<input type="number" name="max" value="{$config.max ?? 1}" class="layui-input len-short" lay-verify="max">
<div class="layui-word-aux"></div>
</div>
</div>
<div class="form-row">
<button class="layui-btn bg-color " lay-submit="" lay-filter="save">保存</button>
</div>
</div>
<script>
var repeat_flag = false, form;
layui.use(['form'], function () {
form = layui.form;
form.verify({
advance(value) {
if (isNaN(value) || !/(^[1-9]\d*$)/.test(value)) {
return "预约提前时间格式输入错误";
}
if (value < 0) {
return "预约提前时间不能小于0";
}
},
max(value) {
if (isNaN(value) || !/(^[1-9]\d*$)/.test(value)) {
return "每时段可预约人数格式输入错误";
}
if (value < 0) {
return "每时段可预约人数不能小于0";
}
}
})
form.on('submit(save)', function (data) {
if (repeat_flag) return false;
repeat_flag = true;
$.ajax({
url: ns.url("cardservice://shop/reserve/config"),
data: data.field,
dataType: 'JSON',
type: 'POST',
success: function (res) {
layer.msg(res.message, { time: 1000 }, function () {});
repeat_flag = false;
}
});
})
})
</script>

View File

@@ -0,0 +1,75 @@
<style>
.align-right {text-align: right!important;}
.service-table {width: 600px!important;}
</style>
<div class="main-wrap">
<div class="layui-form-item">
<label class="layui-form-label">预约客户:</label>
<div class="layui-input-inline">
<a href="{:href_url('shop/member/editmember?member_id='.$info.member_id)}" target="_blank">{$info.nickname} {$info.mobile}</a>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">预约门店:</label>
<div class="layui-input-inline">{$info.store_name ? $info.store_name : ''}</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">预约状态:</label>
<div class="layui-input-inline">{$info.reserve_state_name}</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">预约到店时间:</label>
<div class="layui-input-inline">{:date('Y-m-d H:i', $info.reserve_time)}</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">创建时间:</label>
<div class="layui-input-inline">{:date('Y-m-d H:i', $info.create_time)}</div>
</div>
{if $info.to_store_time}
<div class="layui-form-item">
<label class="layui-form-label">到店时间:</label>
<div class="layui-input-inline">{:date('Y-m-d H:i', $info.to_store_time)}</div>
</div>
{/if}
{if $info.cancel_time}
<div class="layui-form-item">
<label class="layui-form-label">取消时间:</label>
<div class="layui-input-inline">{:date('Y-m-d H:i', $info.cancel_time)}</div>
</div>
{/if}
<div class="layui-form-item">
<label class="layui-form-label">预约项目:</label>
<div class="layui-input-inline service-table">
<table class="layui-table" lay-skin="line">
<colgroup>
<col width="62%">
<col width="18%">
<col>
</colgroup>
<thead>
<tr>
<th>项目</th>
<th>技师</th>
<th class="align-right">项目时长</th>
</tr>
</thead>
<tbody>
{foreach name="info.item" item="item"}
<tr>
<td>{$item.goods_name}</td>
<td>{if $item.reserve_user_id}{$item.username}{else/}未选择员工{/if}</td>
<td class="align-right">{$item.service_length}分钟</td>
</tr>
{/foreach}
</tbody>
</table>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">备注:</label>
<div class="layui-input-inline">{$info.remark ? $info.remark : '-'}</div>
</div>
<div class="form-row">
<button class="layui-btn layui-btn-primary" onclick="layer.closeAll();">返回</button>
</div>
</div>

View File

@@ -0,0 +1,461 @@
<link rel="stylesheet" href="ADDON_CARDSERVICE_CSS/reserve_index.css">
<style>
.layui-layer-loading {
box-shadow: unset!important;
}
.screen.reserve {
padding: 0;
margin-top: 20px;
}
{foreach name="reserve_state" item="vo"}
.{$vo.state} {
background-color: {$vo.color};
border-color: {$vo.color};
}
.{$vo.state}-color {
color: {$vo.color};
}
{/foreach}
</style>
<div class="layui-card layui-form reserve-data">
<div class="uni-flex panel-head">
<button class="layui-btn" onclick="addReserve()">添加预约</button>
<div class="status uni-flex">
{foreach name="reserve_state" item="vo"}
<div class="color {$vo.state}"></div>
<div>{$vo.name}</div>
{/foreach}
</div>
</div>
<div class="screen reserve">
<div class="layui-colla-item">
<div 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="search_text" 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="store_id">
<option value="">全部</option>
{foreach $store_list as $k=> $v}
<option value="{$v.store_id}">{$v.store_name}</option>
{/foreach}
</select>
</div>
</div>
</div>
<div class="form-row">
<button class="layui-btn bg-color" lay-submit lay-filter="search">筛选</button>
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
</div>
</div>
</div>
</div>
<div class="panel-body">
<!-- 看板类型 -->
<!-- <div class="time-type">-->
<!-- <span class="on" data-type="week">周</span>-->
<!-- <span data-type="month">月</span>-->
<!-- </div>-->
<!-- 看板数据 -->
<div class="time-data" id="time-data">
<div class="uni-flex time-wrap">
<span class="iconfont iconback_light" onclick="prevWeek()"></span>
<div class="date">{$data[0]['date']} - {$data[6]['date']}</div>
<span class="iconfont iconyoujiantou" onclick="nextWeek()"></span>
</div>
<div class="head uni-flex">
{foreach name="$data" item="item"}
<div>
<button class="layui-btn {if $item.currday eq 0}layui-btn-primary{/if}">{$item.week}<span>{$item.date}</span></button>
</div>
{/foreach}
</div>
<div class="body uni-flex">
{foreach name="$data" item="item"}
<div class="common-scrollbar" data-page="1" data-total="{$item.data.page_count}" data-start="{$item.start}" data-end="{$item.end}">
<div class="box">
{notempty name="item.data.list"}
{foreach name="item.data.list" item="vo"}
<div class="panel-item {$vo.reserve_state}">
<div class="username">{$vo.nickname}</div>
<div class="time {$vo.reserve_state}">{:date('H:i', $vo.reserve_time)}</div>
{php}
$vo['reserve_item'] = explode(',', $vo['reserve_item']);
{/php}
{foreach name="$vo.reserve_item" item="goods_name"}
<div class="service">{$goods_name}</div>
{/foreach}
<div class="action" data-state="{$vo.reserve_state}" data-id="{$vo.reserve_id}">
<span class="iconfont iconyuandian"></span>
</div>
</div>
{/foreach}
{/notempty}
</div>
</div>
{/foreach}
</div>
</div>
</div>
</div>
<!-- 周看板数据 -->
<script type="text/html" id="dataTpl">
<div class="uni-flex time-wrap">
<span class="iconfont iconback_light" onclick="prevWeek()"></span>
<div class="date">{{ d[0].date }} - {{ d[6].date }}</div>
<span class="iconfont iconyoujiantou" onclick="nextWeek()"></span>
</div>
<div class="head uni-flex">
{{# layui.each(d, function(index, item){ }}
<div>
<button class="layui-btn {{# if(item.currday == 0){ }}layui-btn-primary{{# } }}">{{ item.week }}<span>{{ item.date }}</span></button>
</div>
{{# }); }}
</div>
<div class="body uni-flex">
{{# layui.each(d, function(index, item){ }}
<div class="common-scrollbar" data-page="0" data-total="1" data-start="{{item.start}}" data-end="{{item.end}}">
<div class="box"></div>
</div>
{{# }); }}
</div>
</script>
<script type="text/html" id="reserveTpl">
{{# layui.each(d, function(vindex, vo){ }}
<div class="panel-item {{ vo.reserve_state }}">
<div class="username">{{ vo.nickname }}</div>
<div class="time {{ vo.reserve_state }}">{{ ns.time_to_date(vo.reserve_time, 'H:i') }}</div>
{{# vo.reserve_item.split(',').forEach(function(goods_name){ }}
<div class="service">{{ goods_name }}</div>
{{# }); }}
<div class="action" data-state="{{ vo.reserve_state }}" data-id="{{ vo.reserve_id }}">
<span class="iconfont iconyuandian"></span>
</div>
</div>
{{# }); }}
</script>
<!-- 月看板日期模板 -->
<script type="text/html" id="month-table-tpl">
<div class="uni-flex time-wrap">
<span class="iconfont iconback_light" onclick="prevMonth()"></span>
<div class="date">{{ data_year }}/{{ data_month < 10 ? '0' + data_month : data_month }}</div>
<span class="iconfont iconyoujiantou" onclick="nextMonth()"></span>
</div>
<div class="month-table">
<div class="table-head">
<div class="table-tr">
<div class="table-td">周一</div>
<div class="table-td">周二</div>
<div class="table-td">周三</div>
<div class="table-td">周四</div>
<div class="table-td">周五</div>
<div class="table-td">周六</div>
<div class="table-td">周日</div>
</div>
</div>
<div class="table-body">
{{# d.forEach(function(week_days, week_index){ }}
<div class="table-tr">
{{# week_days.forEach(function(item, index){ }}
<div class="table-td {{ item.is_curr_month ? '' : 'not-curr-month' }}">
<div class="top">{{ item.day }}</div>
<div class="bottom" id="{{item.month}}_month_{{item.day}}_day">
<!-- 每日数据 -->
</div>
</div>
{{# }) }}
</div>
{{# }) }}
</div>
</div>
</script>
<!-- 月看板每日数据模板 -->
<script type="text/html" id="month-table-td-tpl">
{{# d.list.forEach(function(item, index){ }}
<div class="item-box">
<div class="item" data-reserve_id="{{ item.reserve_id }}">
<span class="{{ item.reserve_state }}"></span>
<span>{{ item.reserve_item }}</span>
</div>
<div class="detail-card">
<div class="username">{{ item.nickname }}</div>
<div class="time {{ item.reserve_state }}">{{ ns.time_to_date(item.reserve_time, 'H:i') }}</div>
{{# item.reserve_item.split(',').forEach(function(goods_name){ }}
<div class="service">{{ goods_name }}</div>
{{# }); }}
<div class="state {{ item.reserve_state }}-color">{{ item.reserve_state_name }}</div>
</div>
</div>
{{# }) }}
{{# if(d.count > d.list.length){ }}
{{# let more_url = ns.href('cardservice://shop/reserve/lists', {start_time:d.start_time, end_time:d.end_time}); }}
<div class="more"><a href="{{ more_url }}" target="_blank">查看全部{{ d.count }}条预约 ></a></div>
{{# } }}
</script>
{include file="reserve/reserve_action"}
<script>
var dropdown, laytpl, form, _dropdown = {};
var data_year = (new Date()).getFullYear();
var data_month = (new Date()).getMonth() + 1;
layui.use(['form', 'laytpl', 'dropdown'], function(){
dropdown = layui.dropdown;
laytpl = layui.laytpl;
form = layui.form;
init();
/**
* 搜索功能
*/
form.on('submit(search)', function(data) {
curr = 0;
getWeekData();
return false;
});
});
// 触底加载
$('.panel-body .common-scrollbar').scroll(function () {
var top = $(this).scrollTop();
var windowHeight = $(this).height();
var documentHeight = $(this).find('.box').height();
if (documentHeight - top - windowHeight < 20) {
loadinfo($(this))
}
});
function addReserve() {
layer.open({
title: '添加预约',
type: 2,
content: ns.url('cardservice://shop/reserve/addreserve?request_mode=iframe'),
area: ['800px', '690px'],
success: function (layero, index) {
}
})
}
var curr = 0, repeat = false;
function prevWeek() { curr -= 1; getWeekData(); }
function nextWeek() { curr += 1; getWeekData(); }
function getWeekData() {
if (repeat) return;
repeat = true;
layer.load(3,{shade: [0.8, '#fff']});
$.ajax({
url: ns.url("cardservice://shop/reserve/getweekday"),
data: {
length: curr
},
dataType: 'JSON',
type: 'POST',
success: function (res) {
repeat = false;
if (res.code == 0) {
laytpl($('#dataTpl').html()).render(res.data, function(string){
$('#time-data').html(string);
layer.closeAll();
$('.panel-body .common-scrollbar').scroll(function () {
var top = $(this).scrollTop();
var windowHeight = $(this).height();
var documentHeight = $(this).find('.box').height();
if (documentHeight - top - windowHeight < 20) {
loadinfo($(this))
}
});
$('.panel-body .common-scrollbar').each(function (index, item) {
loadinfo($(item))
})
});
} else {
layer.msg('请求错误');
}
}
});
}
function loadinfo(elem) {
var page = parseInt(elem.attr('data-page')),
total = elem.attr('data-total');
if (page >= total) return;
page += 1;
$.ajax({
url: ns.url("cardservice://shop/reserve/lists"),
data: {
page: page,
start_time: elem.attr('data-start'),
end_time: elem.attr('data-end'),
store_id: $('[name="store_id"]').val(),
search_text: $('[name="search_text"]').val(),
},
dataType: 'JSON',
type: 'POST',
success: function (res) {
if (res.code == 0 && res.data.list.length) {
elem.attr('data-page', page);
laytpl($('#reserveTpl').html()).render(res.data.list, function(string) {
elem.find('.box').append(string);
init();
})
}
}
});
}
function getMonthDays() {
layer.load(3,{shade: [0.8, '#fff']});
$.ajax({
url: ns.url("cardservice://shop/reserve/getMonthDays"),
data: {
year : data_year,
month : data_month,
},
dataType: 'JSON',
type: 'POST',
success: function (res) {
if (res.code == 0) {
var month_data = [];
var week_data = [];
var month_day_num = res.data.length;
res.data.forEach(function(item, index){
week_data.push(item);
if(week_data.length === 7 || index === month_day_num - 1){
month_data.push(week_data);
week_data = [];
}
});
laytpl($('#month-table-tpl').html()).render(month_data, function(string){
$("#time-data").html(string);
layer.closeAll();
res.data.forEach(function(item, index){
getDayData(item);
})
});
}
}
});
}
function getDayData(item) {
$.ajax({
url: ns.url("cardservice://shop/reserve/lists"),
data: {
page: 1,
page_size:3,
start_time: ns.date_to_time(item.start_time),
end_time: ns.date_to_time(item.end_time),
store_id: $('[name="store_id"]').val(),
search_text: $('[name="search_text"]').val(),
},
dataType: 'JSON',
type: 'POST',
success: function (res) {
if (res.code == 0 && res.data.list.length) {
res.data.start_time = item.start_time;
res.data.end_time = item.end_time;
laytpl($('#month-table-td-tpl').html()).render(res.data, function(string) {
$(`#${item.month}_month_${item.day}_day`).html(string);
init();
})
}
}
});
}
$("#time-data").on('click', '.table-td .bottom .item', function(){
let reserve_id = $(this).attr('data-reserve_id');
reserveEvent('detail', {reserve_id: reserve_id});
});
function prevMonth(){
if(data_month > 1){
data_month --;
}else{
data_month = 12;
data_year --;
}
getMonthDays();
}
function nextMonth(){
if(data_month < 12){
data_month ++;
}else{
data_month = 1;
data_year ++;
}
getMonthDays();
}
$(".time-type span").on('click', function(){
$(this).addClass('on').siblings().removeClass('on');
var type = $(this).attr('data-type');
if(type === 'week'){
curr = 0;
getWeekData();
}else{
data_year = (new Date()).getFullYear();
data_month = (new Date()).getMonth() + 1;
getMonthDays();
}
});
function init(){
$('body .panel-item .action').each(function () {
var state = $(this).attr('data-state');
var reserveId = $(this).attr('data-id');
var menuData = getMemuData(state);
var elem = $(this);
let id = $(elem).attr('id');
if(id == '' || id == undefined){
id = ns.gen_non_duplicate(5);
$(elem).attr('id', id)
}
if (!_dropdown['reserve_id' + reserveId]) {
dropdown.suite("#"+id, {
menus: menuData,
success: function ($dom) {
$dom.addClass('border-color').css({
'outline':'none',
'border':'2px solid'
});
},
onItemClick: function (event, menu) {
let data_id = $('#'+id).attr('data-id');
reserveEvent(event, {reserve_id: data_id}, function (res) {
getWeekData()
});
},
});
}
})
}
</script>

View File

@@ -0,0 +1,373 @@
<style>
.layui-layout-admin .layui-form-item .layui-input-inline {
background-color: #fff;
}
.table-btn .more-operation {
display: none;
font-size: 14px;
line-height: 20px;
background-color: #fff;
box-shadow: 0 2px 8px 0 rgba(200, 201, 204, .5);
position: absolute;
z-index: 2000;
border-radius: 2px;
padding: 13px 12px;
top: 40px;
right: 5px;
transform: translateX(10px);
}
.main-wrap{
padding-bottom: 20px;
}
.body-content {
padding-top: 0!important;
}
.table-btn .more-operation:before {
right: 7px;
top: -14px;
border: solid transparent;
content: "";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
border-bottom-color: #fff;
border-width: 8px;
}
.table-btn .more-operation .operation {
display: block;
text-align: right;
margin-bottom: 12px;
cursor: pointer;
}
.table-btn .more-operation .operation:last-child {
margin-bottom: 0
}
.layui-table-view td:last-child>div {
overflow: inherit;
}
.operation-wrap {
position: relative;
}
</style>
<div class="main-wrap">
<div class="single-filter-box single-filter-box">
<button class="layui-btn" onclick="addReserve()">添加预约</button>
</div>
<div class="screen layui-collapse" lay-filter="selection_panel">
<div class="layui-colla-item">
<div class="layui-form 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="search_text" 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="store_id">
<option value="">全部</option>
{foreach $store_list as $k=> $v}
<option value="{$v.store_id}">{$v.store_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>
<button class="layui-btn layui-btn-primary date-picker-btn" onclick="datePick(7, this);return false;">近7天</button>
<button class="layui-btn layui-btn-primary date-picker-btn" onclick="datePick(30, this);return false;">近30天</button>
</div>
</div>
<div class="form-row">
<button class="layui-btn bg-color" lay-submit lay-filter="search">筛选</button>
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
</div>
</div>
</div>
</div>
<!-- 列表 -->
<div class="table-tab layui-tab" lay-filter="list_tab">
<div class="table-tab-list">
<ul class="layui-tab-title">
<li class="layui-this" lay-id="all">全部</li>
{foreach name="$reserve_state" item="vo"}
<li lay-id="{$vo.state}" data-type="reserve_state">{$vo.name}</li>
{/foreach}
</ul>
</div>
<div class="layui-tab-content">
<table id="reserve_list" lay-filter="reserve_list"></table>
</div>
</div>
</div>
<!-- 用户信息 -->
<script type="text/html" id="detail">
<div class='table-title'>
<div class='title-pic'>
<img layer-src src="{{ns.img(d.headimg)}}" onerror="this.src = 'STATIC_IMG/default_img/head.png' ">
</div>
<div class='title-content'>
<p class="layui-elip" title="{{d.nickname}}">{{d.nickname}}</p>
{{# if(d.member_id != 0 && d.mobile){ }}
<p title="{{d.mobile}}">{{d.mobile}}</p>
{{# } }}
</div>
</div>
</script>
<!-- 工具栏操作 -->
<script type="text/html" id="operation">
<div class="operation-wrap">
<div class="table-btn">
<a class="layui-btn" lay-event="detail">详情</a>
{{# if(d.reserve_state == 'cancelled'){ }} <a class="layui-btn" lay-event="delete">删除预约</a> {{# } }}
{{# if(d.reserve_state != 'completed' && d.reserve_state != 'cancelled'){ }} <a class="layui-btn" lay-event="more">更多</a>{{# } }}
<div class="more-operation">
{{# if(d.reserve_state == 'wait_confirm'){ }}
<a class="operation" lay-event="confirm">确认预约</a>
<a class="operation" lay-event="update">更改预约</a>
{{# } }}
{{# if(d.reserve_state == 'wait_to_store'){ }}
<a class="operation" lay-event="tostore">确认到店</a>
<a class="operation" lay-event="update">更改预约</a>
{{# } }}
{{# if(d.reserve_state == 'arrived_store'){ }} <a class="operation" lay-event="complet">确认完成</a> {{# } }}
{{# if(d.reserve_state == 'wait_confirm' || d.reserve_state == 'wait_to_store' || d.reserve_state == 'arrived_store'){ }} <a class="operation" lay-event="cancel">取消预约</a> {{# } }}
</div>
</div>
</div>
</script>
{include file="reserve/reserve_action"}
<script type="text/javascript">
var table, form, laytpl, element, currentDate = new Date(), minDate = "",laydate;
layui.use(['form', 'element', 'laydate'], function() {
form = layui.form;
laydate = layui.laydate;
element = layui.element;
currentDate.setDate(currentDate.getDate() - 7);
form.render();
laydate.render({
elem: '#start_time'
,type: 'datetime'
,change: function(value, date, endDate){
$(".date-picker-btn").removeClass("selected");
}
});
laydate.render({
elem: '#end_time'
,type: 'datetime'
,change: function(value, date, endDate){
$(".date-picker-btn").removeClass("selected");
}
});
element.on('tab(list_tab)', function (data) {
var state = $(data.elem).find('li:eq('+ data.index + ')').attr('lay-id');
$('[name="reserve_state"]').val(state);
table.reload({
where: {
reserve_state: state,
search_text: $('input[name="search_text"]').val(),
store_id: $('select[name="store_id"]').val(),
start: $('input[name="start"]').val() ? new Date($('input[name="start"]').val()).valueOf / 100 : '',
end: $('input[name="end"]').val() ? new Date($('input[name="end"]').val()).valueOf / 100 : ''
},
page: {
curr: 1
}
});
});
var cols = [
[
{
title: '客户信息',
width: '15%',
templet: '#detail'
},
{
field: 'service',
title: '预约项目',
width: '15%',
templet: function (data) {
var text = '';
data.reserve_item.split(',').forEach(function (goods_name) {
text += '<div class="goods-name">'+ goods_name +'</div>';
});
return text;
}
},
{
field: 'service',
title: '预约门店',
width: '15%',
templet: function (data) {
return data.store_name ? data.store_name : '';
}
},
{
title: '预约到店时间',
width: '10%',
templet: function (data) {
if (data.reserve_time) {
let time = ns.time_to_date(data.reserve_time).split(' ');
let text = '<div class="time-line-height"><p>'+ time[0] +'</p><p>'+ time[1] +'</p></div>';
if ((data.reserve_time == 'wait_confirm' || data.reserve_time == 'wait_to_store') && data.reserve_time < {:time()}) text += '<div class="error">已超时</div>';
return text;
} else {
return '';
}
}
},
{
title: '下单时间',
width: '10%',
templet: function (data) {
let time = ns.time_to_date(data.create_time).split(' ');
return '<div class="time-line-height"><p>'+ time[0] +'</p><p>'+ time[1] +'</p></div>';
}
},
{
field: 'reserve_state_name',
title: '预约状态',
width: '10%',
align: 'center',
},
{
title: '来源',
width: '10%',
align: 'center',
templet: function (data) {
return data.source == 'store' ? '门店添加' : '客户预约';
}
},
{
title: '操作',
width: '15%',
align: 'right',
toolbar: '#operation'
}
]
];
/**
* 加载表格
*/
table = new Table({
elem: '#reserve_list',
url: ns.url("cardservice://shop/reserve/lists"),
where:{
start_time : '{$start_time}',
end_time : '{$end_time}',
},
cols: cols
});
/**
* 监听工具栏操作
*/
table.tool(function(obj) {
var data = obj.data;
switch (obj.event) {
case 'more': //更多
$('.more-operation').css('display', 'none');
$(obj.tr).find('.more-operation').css('display', 'block');
break;
default:
reserveEvent(obj.event, data, function () {
table.reload();
});
break;
}
});
$(document).click(function(event) {
if ($(event.target).attr('lay-event') != 'more' && $('.more-operation').not(':hidden').length) {
$('.more-operation').css('display', 'none');
}
});
/**
* 搜索功能
*/
form.on('submit(search)', function(data) {
if (data.field.start_time && data.field.start_time != '') data.field.start_time = new Date(data.field.start_time).valueOf() / 1000;
if (data.field.end_time && data.field.end_time != '') data.field.end_time = new Date(data.field.end_time).valueOf() / 1000;
table.reload({
page: {
curr: 1
},
where: data.field
});
return false;
});
});
function addReserve() {
layer.open({
title: '添加预约',
type: 2,
content: ns.url('cardservice://shop/reserve/addreserve', {request_mode: 'iframe'}),
area: ['800px', '690px']
})
}
/**
* 七天时间
*/
function datePick(date_num,event_obj){
$(".date-picker-btn").removeClass("selected");
$(event_obj).addClass('selected');
var now_date = new Date();
Date.prototype.Format = function (fmt,date_num) { //author: meizz
this.setDate(this.getDate()-date_num);
var o = {
"M+": this.getMonth() + 1, //月份
"d+": this.getDate(), //日
"H+": this.getHours(), //小时
"m+": this.getMinutes(), //分
"s+": this.getSeconds(), //秒
"q+": Math.floor((this.getMonth() + 3) / 3), //季度
"S": this.getMilliseconds() //毫秒
};
if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
for (var k in o)
if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
return fmt;
};
// var now_time = new Date().Format("yyyy-MM-dd HH:mm:ss",0);//当前日期
var now_time = new Date().Format("yyyy-MM-dd 23:59:59",0);//当前日期
var before_time = new Date().Format("yyyy-MM-dd 00:00:00",date_num-1);//前几天日期
$("input[name=start_time]").val(before_time,0);
$("input[name=end_time]").val(now_time,date_num-1);
}
</script>

View File

@@ -0,0 +1,253 @@
<script>
var show_link_box_flag = true;
function reserveEvent(event, data, callback) {
switch (event) {
case 'detail':
var url = ns.url("cardservice://shop/reserve/detail");
if (show_link_box_flag) {
show_link_box_flag = false;
$.post(url, {
id: data.reserve_id
}, function (str) {
window.linkIndex = layer.open({
type: 1,
title: "预约详情",
content: str,
btn: [],
area: ['850px'], //宽高
maxWidth: 1920,
cancel: function (index, layero) {
show_link_box_flag = true;
},
end: function () {
show_link_box_flag = true;
}
});
});
}
break;
case 'confirm':
confirmReserve(data, callback);
break;
case 'update':
updateReserve(data);
break;
case 'tostore':
confirmToStore(data, callback);
break;
case 'complet':
confirmComplete(data, callback);
break;
case 'cancel':
cancelReserve(data, callback);
break;
case 'delete':
deleteReserve(data, callback);
break;
}
}
function getMemuData(state) {
var menuData = [{txt: '预约详情', event: 'detail'}];
switch (state) {
case 'wait_confirm':
menuData.push({txt: '确认预约', event: 'confirm'});
menuData.push({txt: '更改预约', event: 'update'});
menuData.push({txt: '取消预约', event: 'cancel'});
break;
case 'wait_to_store':
menuData.push({txt: '确认到店', event: 'tostore'});
menuData.push({txt: '更改预约', event: 'update'});
menuData.push({txt: '取消预约', event: 'cancel'});
break;
case 'arrived_store':
menuData.push({txt: '确认完成', event: 'complet'});
break;
case 'cancelled':
menuData.push({txt: '删除预约', event: 'delete'});
break;
}
return menuData;
}
var repeat = false;
function confirmReserve(data, callback) {
layer.confirm('是否要确认该预约?', function(index){
if (repeat) return;
repeat = true;
layer.close(index);
$.ajax({
url: ns.url("cardservice://shop/reserve/confirm"),
data: {
reserve_id: data.reserve_id
},
dataType: 'JSON',
type: 'POST',
success: function (res) {
repeat = false;
if (res.code == 0) {
try {
data.elem.attr('data-state', 'wait_to_store');
data.elem.parents('.panel-item').attr('class', 'panel-item wait_to_store');
data.elem.parents('.panel-item').find('.time').attr('class', 'time wait_to_store');
var menuData = getMemuData('wait_to_store');
_dropdown['reserve_id' + data.reserve_id].reload({
data: menuData,
show: false
})
} catch (e) {
}
typeof callback == 'function' && callback(data);
} else {
layer.msg(res.message)
}
}
})
})
}
function confirmToStore(data, callback) {
layer.confirm('是否确认客户已经到店?', function(index){
if (repeat) return;
repeat = true;
layer.close(index);
$.ajax({
url: ns.url("cardservice://shop/reserve/confirmtostore"),
data: {
reserve_id: data.reserve_id
},
dataType: 'JSON',
type: 'POST',
success: function (res) {
repeat = false;
if (res.code == 0) {
try {
data.elem.attr('data-state', 'arrived_store');
data.elem.parents('.panel-item').attr('class', 'panel-item arrived_store');
data.elem.parents('.panel-item').find('.time').attr('class', 'time arrived_store');
var menuData = getMemuData('arrived_store');
_dropdown['reserve_id' + data.reserve_id].reload({
data: menuData,
show: false
})
} catch (e) {
}
typeof callback == 'function' && callback(data);
} else {
layer.msg(res.message)
}
}
})
})
}
function confirmComplete(data, callback) {
layer.confirm('确认已完成预约?', function(index){
if (repeat) return;
repeat = true;
layer.close(index);
$.ajax({
url: ns.url("cardservice://shop/reserve/complete"),
data: {
reserve_id: data.reserve_id
},
dataType: 'JSON',
type: 'POST',
success: function (res) {
repeat = false;
if (res.code == 0) {
try {
data.elem.attr('data-state', 'completed');
data.elem.parents('.panel-item').attr('class', 'panel-item completed');
data.elem.parents('.panel-item').find('.time').attr('class', 'time completed');
var menuData = getMemuData('completed');
_dropdown['reserve_id' + data.reserve_id].reload({
data: menuData,
show: false
})
} catch (e) {
}
typeof callback == 'function' && callback(data);
} else {
layer.msg(res.message)
}
}
})
})
}
function cancelReserve(data, callback) {
layer.confirm('确认要取消该预约?', function(index){
if (repeat) return;
repeat = true;
layer.close(index);
$.ajax({
url: ns.url("cardservice://shop/reserve/cancel"),
data: {
reserve_id: data.reserve_id
},
dataType: 'JSON',
type: 'POST',
success: function (res) {
repeat = false;
if (res.code == 0) {
try {
data.elem.attr('data-state', 'cancelled');
data.elem.parents('.panel-item').attr('class', 'panel-item cancelled');
data.elem.parents('.panel-item').find('.time').attr('class', 'time cancelled');
var menuData = getMemuData('cancelled');
_dropdown['reserve_id' + data.reserve_id].reload({
data: menuData,
show: false
})
} catch (e) {
}
typeof callback == 'function' && callback(data);
} else {
layer.msg(res.message)
}
}
})
});
}
function deleteReserve(data, callback) {
layer.confirm('确认要删除该预约?', function(index){
if (repeat) return;
repeat = true;
layer.close(index);
$.ajax({
url: ns.url("cardservice://shop/reserve/deletereserve"),
data: {
reserve_id: data.reserve_id
},
dataType: 'JSON',
type: 'POST',
success: function (res) {
repeat = false;
if (res.code == 0) {
try {
data.elem.parents('.panel-item').remove();
} catch (e) {
}
typeof callback == 'function' && callback(data);
} else {
layer.msg(res.message)
}
}
})
});
}
function updateReserve(data) {
layer.open({
title: '修改预约',
type: 2,
content: ns.url('cardservice://shop/reserve/updatereserve',{
request_mode: 'iframe',
id:data.reserve_id
}),
area: ['800px', '620px'],
})
}
</script>

View File

@@ -0,0 +1,701 @@
<link rel="stylesheet" href="__STATIC__/ext/video/video.css">
<link rel="stylesheet" type="text/css" href="__STATIC__/ext/searchable_select/searchable_select.css" />
<link rel="stylesheet" type="text/css" href="__STATIC__/ext/layui/extend/cascader/cascader.css"/>
<link rel="stylesheet" type="text/css" href="SHOP_CSS/goods_edit.css?time=20250527" />
<div class="layui-form">
<div class="layui-tab layui-tab-brief" lay-filter="goods_tab">
<ul class="layui-tab-title">
<li class="layu1i-this" lay-id="basic">基础设置</li>
<li lay-id="price-stock">价格库存</li>
<li lay-id="detail">项目详情</li>
<li lay-id="senior">高级设置</li>
</ul>
<div class="layui-tab-content">
<!-- 基础设置 -->
<div class="layui-tab-item layui-show">
<!-- 项目类型 -->
<div class="layui-card card-common card-brief head">
<div class="layui-card-header">
<span class="card-title">商品类型</span>
</div>
<div class="layui-card-body commodity-type-box" >
{foreach name="all_goodsclass" item="vo"}
<div class="commodity-type-item {if $vo.goods_class eq $goods_class.id}border-color{/if}" onclick="location.hash = ns.hash('{$vo.add_url}')">
<span>{$vo.goods_class_name}</span>
<span>{$vo.is_virtual ? '(无需物流)' : '(需要物流)'}</span>
</div>
{/foreach}
</div>
</div>
<div class="layui-card card-common card-brief head">
<div class="layui-card-header">
<span class="card-title">基础信息</span>
</div>
<div class="layui-card-body">
<div class="layui-form-item">
<label class="layui-form-label"><span class="required">*</span>项目名称:</label>
<div class="layui-input-inline">
<input name="goods_name" type="text" placeholder="请输入项目名称不能超过60个字符" maxlength="60" autocomplete="off" lay-verify="goods_name" class="layui-input len-long">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">促销语:</label>
<div class="layui-input-inline">
<textarea class="layui-textarea len-long" name="introduction" maxlength="100" lay-verify="introduction" placeholder="请输入促销语不能超过100个字符"></textarea>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">关键词:</label>
<div class="layui-input-block">
<input type="text" name="keywords" placeholder="项目关键词用于SEO搜索不能超过100个字符" maxlength="100" autocomplete="off" class="layui-input len-long">
</div>
</div>
<div class="layui-form-item goods-image-wrap">
<label class="layui-form-label"><span class="required">*</span>项目主图:</label>
<div class="layui-input-block">
<!--项目主图项-->
<div class="js-goods-image"></div>
</div>
<div class="word-aux">第一张图片将作为项目主图,支持同时上传多张图片,多张图片之间可随意调整位置;</div>
<div class="word-aux">支持jpg、gif、png格式上传或从图片空间中选择建议使用尺寸800x800像素以上、大小不超过1M的正方形图片</div>
<div class="word-aux">上传后的图片将会自动保存在图片空间的默认分类中最多上传10张至少1张</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">项目视频:</label>
<div class="layui-input-block">
<div class="video-thumb">
<video id="goods_video" class="video-js vjs-big-play-centered" controls="" poster="SHOP_IMG/goods_video_preview.png" preload="auto"></video>
</div>
<div id="videoUpload2" class="up-video " title="项目视频" >
<span class="delete-video hide" onclick="deleteVideo()"><img class="del-img" src="SHOP_IMG/delete.png">删除</span>
<span class=" replace-video hide js-add-goods-video" ><img class="up-img" src="SHOP_IMG/upload.png">上传视频</span>
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"></label>
<div class="layui-input-block">
<input type="text" name="video_url" placeholder="在此输入外链视频地址" autocomplete="off" class="layui-input len-long">
</div>
<div class="file-title word-aux">
<div>注意事项:</div>
<ul>
<li>1、检查upload文件夹是否有读写权限。</li>
<li>2、PHP默认上传限制为2MB需要在php.ini配置文件中修改“post_max_size”和“upload_max_filesize”的大小。</li>
<li>3、视频支持手动输入外链视频地址或者上传本地视频文件</li>
<li>4、必须上传.mp4视频格式</li>
<li>5、视频文件大小不能超过500MB</li>
</ul>
</div>
</div>
{notempty name="$service_list"}
<div class="layui-form-item">
<label class="layui-form-label">项目服务:</label>
<div class="layui-input-block">
{foreach name="$service_list" item="vo"}
<input type="checkbox" name="goods_service_ids" value="{$vo.id}" title="{$vo.service_name}" lay-skin="primary">
{/foreach}
</div>
</div>
{/notempty}
<div class="layui-form-item goods-category-wrap">
<label class="layui-form-label"><span class="required">*</span>项目分类:</label>
<div class="layui-input-block" id="category_select_box">
</div>
<input type="hidden" lay-verify="category_id"/>
<div class="word-aux">项目可以属于多个分类最多10个</div>
</div>
<div class="layui-form-item goods_state">
<label class="layui-form-label"><span class="required">*</span>是否上架:</label>
<div class="layui-input-block">
<input type="radio" name="goods_state" value="1" title="立刻上架" checked lay-filter="goods_state">
<input type="radio" name="goods_state" value="0" title="放入仓库" lay-filter="goods_state">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">定时下架:</label>
<div class="layui-input-block">
<input type="radio" name="timer_off_status" value="1" title="启用" lay-filter="timer_off">
<input type="radio" name="timer_off_status" value="2" title="不启用" lay-filter="timer_off" checked>
</div>
<div class="word-aux">启用定时下架后,到达设定时间,此项目将自动下架。</div>
</div>
<div class="layui-form-item timer_off" style="display: none;">
<label class="layui-form-label"></label>
<div class="layui-input-inline">
<input type="text" id="timer_off" name="timer_off" class="layui-input len-mid" autocomplete="off" readonly>
<i class=" iconrili iconfont calendar"></i>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">是否需要预约:</label>
<div class="layui-input-block">
<input type="radio" name="is_reserve" value="1" title="是" >
<input type="radio" name="is_reserve" value="0" title="否"checked>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">服务模式:</label>
<div class="layui-input-block">
<input type="radio" name="service_mode" value="onsite" title="上门服务" lay-filter="service_mode">
<input type="radio" name="service_mode" value="in_store" title="到店服务" lay-filter="service_mode" checked>
</div>
</div>
<div class="layui-form-item onsite-price" style="display: none">
<label class="layui-form-label">费用形式:</label>
<div class="layui-input-block">
<input type="radio" name="service_price_way" value="reserve_price" title="预约定金">
<input type="radio" name="service_price_way" value="fixed_price" title="一口价格" checked>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"><span class="required">*</span>销售价:</label>
<div class="layui-input-block">
<input type="text" name="price" placeholder="0.00" lay-verify="service_price" class="layui-input len-short" autocomplete="off">
<div class="layui-form-mid"></div>
</div>
</div>
{if addon_is_exit('cashier') == 1}
<div class="layui-form-item">
<label class="layui-form-label">销售渠道:</label>
<div class="layui-input-block">
<input type="radio" name="sale_channel" value="all" title="线上线下销售" checked>
<input type="radio" name="sale_channel" value="online" title="线上销售">
<input type="radio" name="sale_channel" value="offline" title="线下销售">
</div>
</div>
{/if}
{if $store_is_exit}
<div class="layui-form-item">
<label class="layui-form-label">适用门店:</label>
<div class="layui-input-block">
<input type="radio" name="sale_store" value="all" title="全部门店" checked lay-filter="sale_store">
<input type="radio" name="sale_store" value="" title="部分门店" lay-filter="sale_store">
</div>
</div>
<div class="layui-form-item sale-store-select" style="display: none" lay-verify="sale_store">
<label class="layui-form-label"></label>
<div class="layui-input-block">
<button class="layui-btn select-store">选择门店</button>
<div style="width: 700px">
<table class="layui-table" lay-skin="nob">
<colgroup>
<col width="30%">
<col width="60%">
<col width="10%">
</colgroup>
<tr>
<th>门店名称</th>
<th>门店地址</th>
<th>操作</th>
</tr>
<tbody class="sale-store"></tbody>
</table>
</div>
</div>
</div>
<div class="need-verify">
<div class="layui-form-item">
<label class="layui-form-label">项目有效期:</label>
<div class="layui-input-block">
<input type="radio" name="verify_validity_type" value="0" title="永久" checked lay-filter="verify_validity_type">
<input type="radio" name="verify_validity_type" value="1" title="购买后几日有效" lay-filter="verify_validity_type">
<input type="radio" name="verify_validity_type" value="2" title="指定过期日期" lay-filter="verify_validity_type">
</div>
</div>
<div class="layui-form-item validity-type validity-type-1 layui-hide">
<label class="layui-form-label"><span class="required">*</span>有效期:</label>
<div class="layui-input-inline">
<input type="text" name="virtual_indate" placeholder="0" class="layui-input len-short" lay-verify="virtual_indate" autocomplete="off">
</div>
<div class="layui-form-mid layui-word-aux"></div>
</div>
<div class="layui-form-item validity-type validity-type-2 layui-hide">
<label class="layui-form-label"><span class="required">*</span>有效期:</label>
<div class="layui-input-inline">
<input type="text" id="virtual_time" name="virtual_time" class="layui-input len-mid" lay-verify="virtual_time" autocomplete="off" readonly>
<i class=" iconrili iconfont calendar"></i>
</div>
<div class="word-aux" style="clear:both;top: 5px;position: relative;">无论何时购买此项目,到达指定时间后都将过期,无法核销。</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">是否统一售价:</label>
<div class="layui-input-block">
<input type="radio" name="is_unify_price" value="1" title="是" checked >
<input type="radio" name="is_unify_price" value="0" title="否" >
</div>
<div class="word-aux">价格设置之后门店不能修改价格,门店按照平台设置的价格售卖</div>
</div>
{/if}
</div>
</div>
</div>
<!-- 价格库存 -->
<div class="layui-tab-item">
<div class="layui-form-item layui-hide">
<label class="layui-form-label">启用多规格:</label>
<div class="layui-input-inline">
<input type="checkbox" value="1" lay-skin="switch" name="spec_type" lay-filter="spec_type" lay-verify="spec_type">
<input type="hidden" id="spec_type_status" value="0">
</div>
</div>
<!-- 单规格 -->
<div class="js-single-spec">
<div class="layui-form-item">
<label class="layui-form-label">划线价:</label>
<div class="layui-input-block">
<input type="text" name="market_price" placeholder="0.00" lay-verify="market_price" class="layui-input len-short" autocomplete="off">
<div class="layui-form-mid"></div>
</div>
<div class="word-aux">项目没有优惠活动显示的划线价格,如果项目有折扣等优惠活动划线价显示销售价</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">成本价:</label>
<div class="layui-input-block">
<input type="text" name="cost_price" placeholder="0.00" class="layui-input len-short" lay-verify="cost_price" autocomplete="off">
<div class="layui-form-mid"></div>
</div>
<div class="word-aux">成本价将不会对前台会员展示,用于商家统计使用</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">项目编码:</label>
<div class="layui-input-inline">
<input type="text" name="sku_no" placeholder="请输入项目编码" maxlength="50" class="layui-input len-long" autocomplete="off">
</div>
</div>
</div>
<!-- 多规格 -->
<div class="js-more-spec">
<!--规格项/规格值-->
<div class="spec-edit-list"></div>
<div class="layui-form-item js-add-spec">
<label class="layui-form-label"></label>
<div class="layui-input-inline">
<button class="layui-btn" type="button">添加规格</button>
</div>
</div>
<div class="layui-form-item batch-operation-sku">
<label class="layui-form-label">批量操作:</label>
<div class="layui-input-inline">
<span class="text-color" data-field="spec_name">副标题</span>
<span class="text-color" data-field="price" data-verify="price">销售价</span>
<span class="text-color" data-field="market_price" data-verify="market_price">划线价</span>
<span class="text-color" data-field="cost_price" data-verify="cost_price">成本价</span>
<span class="text-color" data-field="stock" data-verify="stock">库存</span>
<span class="text-color" data-field="stock_alarm" data-verify="stock_alarm">库存预警</span>
<span class="text-color" data-field="sku_no" data-verify="">项目编码</span>
<input type="text" class="layui-input len-short" name="batch_operation_sku" autocomplete="off" />
<button class="layui-btn confirm" type="button">确定</button>
<button class="layui-btn layui-btn-primary cancel" type="button">取消</button>
</div>
</div>
<!--sku列表-->
<div class="layui-form-item sku-table">
<label class="layui-form-label"></label>
<div class="layui-input-block"></div>
</div>
</div>
<div class="layui-form-item js-goods-stock-wrap">
<label class="layui-form-label"><span class="required">*</span>库存:</label>
<div class="layui-input-block">
<input type="number" name="goods_stock" placeholder="0" lay-verify="goods_stock" class="layui-input len-short" autocomplete="off">
<div class="layui-form-mid"></div>
</div>
</div>
<div class="layui-form-item js-goods-stock-wrap">
<label class="layui-form-label">库存预警:</label>
<div class="layui-input-block">
<input type="number" name="goods_stock_alarm" placeholder="0" lay-verify="goods_stock_alarm" class="layui-input len-short" autocomplete="off">
<div class="layui-form-mid"></div>
</div>
<div class="word-aux">项目库存少于预警数量项目列表库存数量标红显示0为不预警。</div>
</div>
<div class="layui-form-item js-goods-stock-wrap">
<label class="layui-form-label">服务时长:</label>
<div class="layui-input-block">
<input type="number" name="service_length" placeholder="0" lay-verify="service_length" class="layui-input len-short" autocomplete="off">
<div class="layui-form-mid">分钟</div>
</div>
</div>
<div class="layui-form-item ">
<label class="layui-form-label">虚拟销量:</label>
<div class="layui-input-block">
<input type="number" name="virtual_sale" placeholder="0" lay-verify="virtual_sale" class="layui-input len-short" autocomplete="off">
<div class="layui-form-mid"></div>
</div>
<div class="word-aux">该设置不计入项目统计数据</div>
</div>
<div class="layui-form-item is_limit">
<label class="layui-form-label">是否限购:</label>
<div class="layui-input-block">
<input type="radio" name="is_limit" value="0" title="否" lay-filter="is_limit" checked>
<input type="radio" name="is_limit" value="1" title="是" lay-filter="is_limit">
</div>
<div class="word-aux">启用限购后,购买项目时,会对该项目购买量做限制判断。</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">起售:</label>
<div class="layui-input-block">
<input type="number" name="min_buy" placeholder="" lay-verify="min_buy" class="layui-input len-short" autocomplete="off">
<div class="layui-form-mid"></div>
</div>
<div class="word-aux">起售数量超出项目库存时,买家无法购买该项目</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">会员等级折扣:</label>
<div class="layui-input-block">
<div class="layui-input-inline">
<input type="radio" name="is_consume_discount" value="1" title="参与" checked>
<input type="radio" name="is_consume_discount" value="0" title="不参与">
</div>
</div>
<div class="word-aux">按照默认会员等级折扣优惠</div>
</div>
</div>
<!-- 项目详情 -->
<div class="layui-tab-item">
<div class="layui-form-item">
<label class="layui-form-label sm"></label>
<div class="layui-input-inline special-length">
<script id="editor" type="text/plain" style="width:100%;height:500px;"></script>
</div>
</div>
<script type="text/javascript" charset="utf-8" src="__STATIC__/ext/ueditor/ueditor.config.js"></script>
<script type="text/javascript" charset="utf-8" src="__STATIC__/ext/ueditor/ueditor.all.js"> </script>
<script type="text/javascript" charset="utf-8" src="__STATIC__/ext/ueditor/lang/zh-cn/zh-cn.js"></script>
</div>
<div class="layui-tab-item layui-card card-common card-brief head">
<div class="layui-card-header">
<span class="card-title">高级设置</span>
</div>
<div class="layui-card-body">
<div class="layui-form-item">
<label class="layui-form-label">排序:</label>
<div class="layui-input-block">
<input type="number" name="sort" class="layui-input len-short" value="{$sort_config['default_value']}" placeholder="0" autocomplete="off">
</div>
<div class="word-aux">项目默认排序号为0数字越大排序越靠前数字重复则最新添加的靠前。</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">项目详情显示库存:</label>
<div class="layui-input-block">
<input type="radio" name="stock_show" value="1" title="显示" checked >
<input type="radio" name="stock_show" value="0" title="隐藏" >
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">划线价显示:</label>
<div class="layui-input-block">
<input type="radio" name="market_price_show" value="1" title="显示" checked>
<input type="radio" name="market_price_show" value="0" title="隐藏" >
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">项目详情显示弹幕:</label>
<div class="layui-input-block">
<input type="radio" name="barrage_show" value="1" title="显示" checked>
<input type="radio" name="barrage_show" value="0" title="隐藏" >
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">项目详情显示销量:</label>
<div class="layui-input-block">
<input type="radio" name="sale_show" value="1" title="显示" checked>
<input type="radio" name="sale_show" value="0" title="隐藏" >
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">项目海报:</label>
<div class="layui-input-inline">
<select name="template_id" lay-search="" lay-verify="">
<option value="">请选择项目海报</option>
{foreach name="$poster_list" item="vo"}
<option value="{$vo['template_id']}">{$vo['poster_name']}</option>
{/foreach}
</select>
</div>
</div>
{if $form_is_exit}
<div class="layui-form-item">
<label class="layui-form-label">项目表单:</label>
<div class="layui-input-block len-mid">
<select name="form_id">
<option value="0">请选择项目表单</option>
{foreach name="$form_list" item="vo"}
<option value="{$vo.id}">{$vo.form_name}</option>
{/foreach}
</select>
</div>
<div class="word-aux">
<a href="{:href_url('form://shop/form/addform?form_type=goods')}" class="text-color" target="_blank">创建项目表单</a>
<a href="javascript:;" onclick="refreshFormList()" class="text-color">刷新</a>
</div>
</div>
{/if}
</div>
</div>
</div>
</div>
<div class="fixed-btn">
<button class="layui-btn layui-btn-primary border-color text-color js-prev" lay-filter="prev">上一步</button>
<button class="layui-btn js-save" lay-submit="" lay-filter="save">保存</button>
<button class="layui-btn layui-btn-primary border-color text-color js-next" lay-submit="" lay-filter="next">下一步</button>
</div>
</div>
<!--规格项模板-->
<script type="text/html" id="specTemplate">
{{# for(var i=0;i<d.list.length;i++){ }}
<div class="spec-item" data-index="{{i}}">
<div class="layui-form-item spec">
<label class="layui-form-label">规格项{{i+1}}</label>
<div class="layui-input-inline">
<select name="spec_item">
<option value="0"></option>
{{# if(d.list[i].spec_name != ''){ }}
<option value="{{d.list[i].spec_id}}" data-attr-name="{{d.list[i].spec_name}}" selected>{{d.list[i].spec_name}}</option>
{{# }else{ }}
{{# } }}
</select>
<i class="layui-icon layui-icon-close" data-index="{{i}}"></i>
</div>
{{# if(i==0){ }}
<div class="layui-input-inline">
{{# if(d.add_spec_img){ }}
<input type="checkbox" name="add_spec_img" title="添加规格图片" lay-skin="primary" lay-filter="add_spec_img" checked>
{{# }else{ }}
<input type="checkbox" name="add_spec_img" title="添加规格图片" lay-skin="primary" lay-filter="add_spec_img">
{{# } }}
</div>
{{# } }}
</div>
{{# if(d.list[i].spec_name != ''){ }}
<div class="layui-form-item spec-value">
{{# }else{ }}
<div class="layui-form-item spec-value" style="display:none;">
{{# } }}
<label class="layui-form-label"></label>
<div class="layui-input-block spec-value">
{{# if(d.list[i].value.length){ }}
<ul>
{{# for(var j=0;j<d.list[i].value.length;j++){ }}
<li data-index="{{j}}" data-parent-index="{{i}}" >
{{# if(i==0 && d.add_spec_img){ }}
<div class="img-wrap">
{{# if(d.list[i].value[j].image){ }}
<img src="{{ns.img(d.list[i].value[j].image)}}" alt="">
{{# }else{ }}
<img src="SHOP_IMG/goods_spec_value_empty.png" alt="">
{{# } }}
</div>
{{# } }}
<span title="双击可编辑规格值" ondblclick="$(this).attr('contenteditable',true);$(this).focus()" class="spec-txt" data-spec_value_name="{{d.list[i].value[j].spec_value_name}}" data-parent-index="{{i}}" data-index="{{j}}">{{d.list[i].value[j].spec_value_name}}</span>
<i class="layui-icon layui-icon-close" data-parent-index="{{i}}" data-index="{{j}}"></i>
</li>
{{# } }}
</ul>
{{# } }}
<a class="text-color" href="javascript:;" data-index="{{i}}">+添加规格值</a>
<div class="add-spec-value-popup" data-index="{{i}}">
<select name="spec_value_item"></select>
<button class="layui-btn layui-btn-primary border-color text-color js-cancel-spec-value">取消</button>
</div>
</div>
</div>
</div>
{{# } }}
</script>
<!--SKU列表模板-->
<script type="text/html" id="skuTableTemplate">
{{# if(d.skuList.length){ }}
<table class="layui-table">
<colgroup></colgroup>
<thead>
<tr>
{{# if(d.showSpecName){ }}
<th colspan="{{d.colSpan}}" style="min-width: 60px;">项目规格</th>
{{# } }}
<th rowspan="{{d.rowSpan}}">SKU图片</th>
<th rowspan="{{d.rowSpan}}">副标题</th>
<th rowspan="{{d.rowSpan}}"><span class="required">*</span></th>
<th rowspan="{{d.rowSpan}}">划线价</th>
<th rowspan="{{d.rowSpan}}">成本价</th>
<th rowspan="{{d.rowSpan}}"><span class="required">*</span></th>
<th rowspan="{{d.rowSpan}}">库存预警</th>
<th rowspan="{{d.rowSpan}}">服务时长</th>
<th rowspan="{{d.rowSpan}}">商品编码多个编码以英文逗号分割</th>
<th rowspan="{{d.rowSpan}}" style="white-space: nowrap;">默认展示</th>
</tr>
{{# if(d.colSpan>1){ }}
<tr>
{{# for(var i=0;i<d.specList.length;i++){ }}
{{# if(d.specList[i].spec_name && d.specList[i].value.length> 0){ }}
<th>{{d.specList[i].spec_name}}</th>
{{# } }}
{{# } }}
</tr>
{{# } }}
</thead>
<tbody>
{{# for(var i=0;i<d.skuList.length;i++){ }}
<tr>
<td class="sku_imgs" id="sku_img_{{i}}" style="width: 130px;">
{{# for(var j=0;j<d.skuList[i].sku_images_arr.length;j++){ }}
<div class="img-wrap" data-index="{{j}}" data-parent-index="{{i}}">
<a href="javascript:void(0)">
<img src="{{ns.img(d.skuList[i].sku_images_arr[j])}}" layer-src />
</a>
<div class="operation">
<i title="图片预览" class="iconfont iconreview js-preview"></i>
<i title="删除图片" class="layui-icon layui-icon-delete js-delete"></i>
</div>
</div>
{{# } }}
{{# if(d.skuList[i].sku_images_arr.length<d.goods_sku_max){ }}
<div class="upload-sku-img" data-index="{{i}}"><i class="layui-icon layui-icon-add-1"></i></div>
{{# } }}
</td>
<td>
<input type="text" name="spec_name" placeholder="副标题" maxlength="100" value="{{d.skuList[i].spec_name}}" class="layui-input" autocomplete="off" data-index="{{i}}">
</td>
<td>
<input type="text" name="price" placeholder="销售价" lay-verify="sku_price" value="{{d.skuList[i].price}}" class="layui-input" autocomplete="off" data-index="{{i}}">
</td>
<td>
<input type="text" name="market_price" placeholder="划线价" value="{{d.skuList[i].market_price}}" lay-verify="sku_market_price" class="layui-input" autocomplete="off" data-index="{{i}}">
</td>
<td>
<input type="text" name="cost_price" placeholder="成本价" value="{{d.skuList[i].cost_price}}" lay-verify="sku_cost_price" class="layui-input" autocomplete="off" data-index="{{i}}">
</td>
<td>
<input type="text" name="stock" placeholder="库存" value="{{d.skuList[i].stock}}" lay-verify="sku_stock" class="layui-input" autocomplete="off" data-index="{{i}}">
</td>
<td>
<input type="text" name="stock_alarm" placeholder="库存预警" value="{{d.skuList[i].stock_alarm}}" lay-verify="sku_stock_alarm" class="layui-input" autocomplete="off" data-index="{{i}}">
</td>
<td>
<input type="number" name="service_length" placeholder="服务时长" value="{{d.skuList[i].service_length}}" maxlength="50" class="layui-input" autocomplete="off" data-index="{{i}}" lay-verify="service_length">
</td>
<td>
<input type="text" name="sku_no" placeholder="商品编码" value="{{d.skuList[i].sku_no}}" maxlength="50" class="layui-input" autocomplete="off" data-index="{{i}}">
</td>
<td style="min-width: 40px;">
{{# if(d.skuList[i].is_default == 1) { }}
<input type="checkbox" data-index="{{i}}" name="is_default" lay-filter="is_default" lay-skin="switch" checked>
{{# }else { }}
<input type="checkbox" data-index="{{i}}" name="is_default" lay-filter="is_default" lay-skin="switch">
{{# } }}
</td>
</tr>
{{# } }}
</tbody>
</table>
{{# } }}
<div class="word-aux text-color" style="margin: 10px 0 0 0;">默认展示是多规格项目在客户访问项目时默认显示的项目规格</div>
</script>
<!--项目主图列表-->
<script type="text/html" id="goodsImage">
{{# if(d.list.length){ }}
{{# for(var i=0;i<d.list.length;i++){ }}
<div class="item upload_img_square_item" data-index="{{i}}">
<div class="img-wrap">
<img src="{{ns.img(d.list[i],'small')}}" layer-src="{{ns.img(d.list[i],'big')}}">
</div>
<div class="operation">
<i title="图片预览" class="iconfont iconreview js-preview"></i>
<i title="删除图片" class="layui-icon layui-icon-delete js-delete" data-index="{{i}}"></i>
<div class="replace_img" data-index="{{i}}">点击替换</div>
</div>
</div>
{{# } }}
{{# if(d.list.length < d.max){ }}
<div class="item js-add-goods-image upload_img_square">+</div>
{{# } }}
{{# }else{ }}
<div class="item js-add-goods-image upload_img_square">+</div>
{{# } }}
</script>
<script src="__STATIC__/ext/drag-arrange.js"></script>
<script src="__STATIC__/ext/video/videojs-ie8.min.js"></script>
<script src="__STATIC__/ext/video/video.min.js"></script>
<script src="__STATIC__/ext/searchable_select/searchable_select.js"></script>
<script src="SHOP_JS/category_select.js?time=20240821"></script>
<script src="SHOP_JS/goods_edit_common.js?time=20250527"></script>
<script src="ADDON_CARDSERVICE_JS/service_goods_edit.js?v=1.1"></script>

View File

@@ -0,0 +1,773 @@
<link rel="stylesheet" href="__STATIC__/ext/video/video.css">
<link rel="stylesheet" type="text/css" href="__STATIC__/ext/searchable_select/searchable_select.css" />
<link rel="stylesheet" type="text/css" href="__STATIC__/ext/layui/extend/cascader/cascader.css"/>
<link rel="stylesheet" type="text/css" href="SHOP_CSS/goods_edit.css?time=20250527" />
<div class="layui-form">
<div class="layui-tab layui-tab-brief" lay-filter="goods_tab">
<ul class="layui-tab-title">
<li class="layu1i-this" lay-id="basic">基础设置</li>
<li lay-id="price-stock">价格库存</li>
<li lay-id="detail">项目详情</li>
<li lay-id="senior">高级设置</li>
</ul>
<div class="layui-tab-content">
<!-- 基础设置 -->
<div class="layui-tab-item layui-show">
<div class="layui-card card-common card-brief head">
<div class="layui-card-header">
<span class="card-title">基础信息</span>
</div>
<div class="layui-card-body">
<div class="layui-form-item">
<label class="layui-form-label"><span class="required">*</span>项目名称:</label>
<div class="layui-input-inline">
<input name="goods_name" type="text" value="{$goods_info['goods_name']}" placeholder="请输入项目名称不能超过60个字符" maxlength="60" autocomplete="off" lay-verify="goods_name" class="layui-input len-long">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">促销语:</label>
<div class="layui-input-inline">
<textarea class="layui-textarea len-long" name="introduction" maxlength="100" lay-verify="introduction" placeholder="请输入促销语不能超过100个字符">{$goods_info['introduction']}</textarea>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">关键词:</label>
<div class="layui-input-block">
<input type="text" name="keywords" value="{$goods_info['keywords']}" placeholder="项目关键词用于SEO搜索不能超过100个字符" maxlength="100" autocomplete="off" class="layui-input len-long">
</div>
</div>
<div class="layui-form-item goods-image-wrap">
<label class="layui-form-label"><span class="required">*</span>项目主图:</label>
<div class="layui-input-block">
<!--项目主图项-->
<div class="js-goods-image"></div>
</div>
<div class="word-aux">第一张图片将作为项目主图,支持同时上传多张图片,多张图片之间可随意调整位置;</div>
<div class="word-aux">支持jpg、gif、png格式上传或从图片空间中选择建议使用尺寸800x800像素以上、大小不超过1M的正方形图片</div>
<div class="word-aux">上传后的图片将会自动保存在图片空间的默认分类中最多上传10张至少1张</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">项目视频:</label>
<div class="layui-input-block">
<div class="video-thumb">
<video id="goods_video" class="video-js vjs-big-play-centered" controls="" poster="SHOP_IMG/goods_video_preview.png" preload="auto"></video>
</div>
<div id="videoUpload2" class="up-video " title="项目视频" >
<span class="delete-video hide" onclick="deleteVideo()"><img class="del-img" src="SHOP_IMG/delete.png">删除</span>
<span class=" replace-video hide js-add-goods-video" ><img class="up-img" src="SHOP_IMG/upload.png">上传视频</span>
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"></label>
<div class="layui-input-block">
<input type="text" name="video_url" placeholder="在此输入外链视频地址" value="{$goods_info['video_url']}" autocomplete="off" class="layui-input len-long">
</div>
<div class="file-title word-aux">
<div>注意事项:</div>
<ul>
<li>1、检查upload文件夹是否有读写权限。</li>
<li>2、PHP默认上传限制为2MB需要在php.ini配置文件中修改“post_max_size”和“upload_max_filesize”的大小。</li>
<li>3、视频支持手动输入外链视频地址或者上传本地视频文件</li>
<li>4、必须上传.mp4视频格式</li>
<li>5、视频文件大小不能超过500MB</li>
</ul>
</div>
</div>
{notempty name="$service_list"}
<div class="layui-form-item">
<label class="layui-form-label">项目服务:</label>
<div class="layui-input-block">
{foreach name="$service_list" item="vo"}
<input type="checkbox" name="goods_service_ids" value="{$vo.id}" title="{$vo.service_name}" lay-skin="primary" {if strpos($goods_info['goods_service_ids'],(string)$vo['id'])>-1}checked{/if}>
{/foreach}
</div>
</div>
{/notempty}
<div class="layui-form-item goods-category-wrap">
<label class="layui-form-label"><span class="required">*</span>项目分类:</label>
<div class="layui-input-block" id="category_select_box">
</div>
<input type="hidden" lay-verify="category_id"/>
<input type="hidden" id="category_data" value='{:json_encode($goods_info.goods_category_data)}'>
<div class="word-aux">项目可以属于多个分类最多10个</div>
</div>
<div class="layui-form-item goods_state">
<label class="layui-form-label"><span class="required">*</span>是否上架:</label>
<div class="layui-input-block">
<input type="radio" name="goods_state" value="1" title="立刻上架" lay-filter="goods_state" {if $goods_info['goods_state'] == 1 }checked{/if}>
<input type="radio" name="goods_state" value="0" title="放入仓库" lay-filter="goods_state" {if $goods_info['goods_state'] == 0 }checked{/if}>
</div>
</div>
{if $goods_info['goods_state'] == 0}
<div class="layui-form-item timer_on">
<label class="layui-form-label">定时上架:</label>
<div class="layui-input-block">
<input type="radio" name="timer_on_status" class="timer_on_status_true" value="1" title="启用" lay-filter="timer_on" {if $goods_info['timer_on'] > 0} checked {/if}>
<input type="radio" name="timer_on_status" value="2" title="不启用" lay-filter="timer_on" {if $goods_info['timer_on'] == 0} checked {/if}>
</div>
<div class="word-aux">启用定时上架后,到达设定时间,此项目将自动上架。</div>
</div>
{if $goods_info['timer_on'] > 0}
<div class="layui-form-item timer_on_time">
<label class="layui-form-label"></label>
<div class="layui-input-inline">
<input type="text" id="timer_on" name="timer_on" value="{:date('Y-m-d H:i:s',$goods_info['timer_on'])}" lay-verify="required" class="layui-input len-mid" autocomplete="off" readonly>
<i class=" iconrili iconfont calendar"></i>
</div>
</div>
{else/}
<div class="layui-form-item timer_on_time layui-hide">
<label class="layui-form-label"></label>
<div class="layui-input-inline">
<input type="text" id="timer_on" name="timer_on" lay-verify="" class="layui-input len-mid" autocomplete="off" readonly>
<i class=" iconrili iconfont calendar"></i>
</div>
</div>
{/if}
{/if}
<div class="layui-form-item">
<label class="layui-form-label">定时下架:</label>
<div class="layui-input-block">
<input type="radio" name="timer_off_status" value="1" title="启用" lay-filter="timer_off" {if $goods_info['timer_off'] > 0} checked {/if}>
<input type="radio" name="timer_off_status" value="2" title="不启用" lay-filter="timer_off" {if $goods_info['timer_off'] == 0} checked {/if}>
</div>
<div class="word-aux">启用定时下架后,到达设定时间,此项目将自动下架。</div>
</div>
{if $goods_info['timer_off'] > 0}
<div class="layui-form-item timer_off">
<label class="layui-form-label"></label>
<div class="layui-input-inline">
<input type="text" id="timer_off" name="timer_off" value="{:date('Y-m-d H:i:s',$goods_info['timer_off'])}" lay-verify="required" class="layui-input len-mid" autocomplete="off" readonly>
<i class=" iconrili iconfont calendar"></i>
</div>
</div>
{else/}
<div class="layui-form-item timer_off" style="display:none">
<label class="layui-form-label"></label>
<div class="layui-input-inline">
<input type="text" id="timer_off" name="timer_off" lay-verify="" class="layui-input len-mid" autocomplete="off" readonly>
<i class=" iconrili iconfont calendar"></i>
</div>
</div>
{/if}
<div class="layui-form-item">
<label class="layui-form-label">是否需要预约:</label>
<div class="layui-input-block">
<input type="radio" name="is_reserve" value="1" title="是" {if $goods_info['is_reserve'] == 1 }checked{/if}>
<input type="radio" name="is_reserve" value="0" title="否" {if $goods_info['is_reserve'] == 0 }checked{/if}>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">服务模式:</label>
<div class="layui-input-block">
<input type="radio" name="service_mode" value="onsite" title="上门服务" lay-filter="service_mode" {if $goods_info['service_mode'] == 'onsite' }checked{/if}>
<input type="radio" name="service_mode" value="in_store" title="到店服务" lay-filter="service_mode" {if $goods_info['service_mode'] == 'in_store' }checked{/if}>
</div>
</div>
<div class="layui-form-item onsite-price" {if $goods_info['service_mode'] == 'in_store' }style="display: none"{/if}>
<label class="layui-form-label">费用形式:</label>
<div class="layui-input-block">
<input type="radio" name="service_price_way" value="reserve_price" title="预约定金" {if $goods_info['service_price_way'] == 'reserve_price' }checked{/if}>
<input type="radio" name="service_price_way" value="fixed_price" title="一口价格" {if $goods_info['service_price_way'] == 'fixed_price' }checked{/if}>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"><span class="required">*</span>销售价:</label>
<div class="layui-input-block">
<input type="text" name="price" value="{$goods_info['price']}" placeholder="0.00" lay-verify="service_price" class="layui-input len-short" autocomplete="off">
<div class="layui-form-mid"></div>
</div>
</div>
{if addon_is_exit('cashier') == 1}
<div class="layui-form-item">
<label class="layui-form-label">销售渠道:</label>
<div class="layui-input-block">
<input type="radio" name="sale_channel" value="all" title="线上线下销售" {if $goods_info.sale_channel eq 'all'}checked{/if}>
<input type="radio" name="sale_channel" value="online" title="线上销售" {if $goods_info.sale_channel eq 'online'}checked{/if}>
<input type="radio" name="sale_channel" value="offline" title="线下销售" {if $goods_info.sale_channel eq 'offline'}checked{/if}>
</div>
</div>
{/if}
{if $store_is_exit}
<div class="layui-form-item">
<label class="layui-form-label">适用门店:</label>
<div class="layui-input-block">
<input type="radio" name="sale_store" value="all" title="全部门店" {if $goods_info.sale_store eq 'all'}checked{/if} lay-filter="sale_store">
<input type="radio" name="sale_store" value="" title="部分门店" {if $goods_info.sale_store neq 'all'}checked{/if} lay-filter="sale_store">
</div>
</div>
<div class="layui-form-item sale-store-select" {if $goods_info.sale_store eq 'all'}style="display: none"{/if} lay-verify="sale_store">
<label class="layui-form-label"></label>
<div class="layui-input-block">
<button class="layui-btn select-store">选择门店</button>
<div style="width: 700px">
<table class="layui-table" lay-skin="nob">
<colgroup>
<col width="30%">
<col width="60%">
<col width="10%">
</colgroup>
<tr>
<th>门店名称</th>
<th>门店地址</th>
<th>操作</th>
</tr>
<tbody class="sale-store">
{if isset($store_list) && !empty($store_list)}
{foreach name="$store_list" item="vo"}
<tr data-store="{$vo.store_id}">
<td>{$vo.store_name}</td>
<td>{$vo.full_address}{$vo.address}</td>
<td><a href="javascript:;" class="del">删除</a></td>
</tr>
{/foreach}
{/if}
</tbody>
</table>
</div>
</div>
</div>
<div class="need-verify">
<div class="layui-form-item">
<label class="layui-form-label">核销有效期:</label>
<div class="layui-input-block">
<input type="radio" name="verify_validity_type" value="0" title="永久" lay-filter="verify_validity_type" {if $goods_info['verify_validity_type'] eq 0}checked{/if}>
<input type="radio" name="verify_validity_type" value="1" title="购买后几日有效" lay-filter="verify_validity_type" {if $goods_info['verify_validity_type'] eq 1}checked{/if}>
<input type="radio" name="verify_validity_type" value="2" title="指定过期日期" lay-filter="verify_validity_type" {if $goods_info['verify_validity_type'] eq 2}checked{/if}>
</div>
</div>
<div class="layui-form-item validity-type validity-type-1 {if $goods_info['verify_validity_type'] neq 1}layui-hide{/if}">
<label class="layui-form-label"><span class="required">*</span>有效期:</label>
<div class="layui-input-inline">
<input type="text" name="virtual_indate" placeholder="0" class="layui-input len-short" lay-verify="virtual_indate" autocomplete="off" {if $goods_info['verify_validity_type'] eq 1}value="{$goods_info['virtual_indate']}"{/if}>
</div>
<div class="layui-form-mid layui-word-aux"></div>
</div>
<div class="layui-form-item validity-type validity-type-2 {if $goods_info['verify_validity_type'] neq 2}layui-hide{/if}">
<label class="layui-form-label"><span class="required">*</span>有效期:</label>
<div class="layui-input-inline">
<input type="text" id="virtual_time" name="virtual_time" class="layui-input len-mid" lay-verify="virtual_time" autocomplete="off" readonly {if $goods_info['verify_validity_type'] eq 2}value="{:time_to_date($goods_info['virtual_indate'])}"{/if}>
<i class=" iconrili iconfont calendar"></i>
</div>
<div class="word-aux" style="clear:both;top: 5px;position: relative;">无论何时购买此商品,到达指定时间后都将过期,无法核销。</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">是否统一售价:</label>
<div class="layui-input-block">
<input type="radio" name="is_unify_price" value="1" title="是" {if $goods_info.is_unify_price eq '1'}checked{/if} >
<input type="radio" name="is_unify_price" value="0" title="否" {if $goods_info.is_unify_price eq '0'}checked{/if}>
</div>
<div class="word-aux">价格设置之后门店不能修改价格,门店按照平台设置的价格售卖</div>
</div>
{/if}
</div>
</div>
</div>
<!-- 价格库存 -->
<div class="layui-tab-item">
<div class="layui-form-item layui-hide">
<label class="layui-form-label">启用多规格:</label>
<div class="layui-input-inline">
<input type="checkbox" value="1" lay-skin="switch" name="spec_type" lay-filter="spec_type" lay-verify="spec_type" {notempty name="$goods_info['goods_spec_format']" }checked{/notempty}>
<input type="hidden" id="spec_type_status" {if empty($goods_info['goods_spec_format'])} value="0" {else/} value="1" {/if}>
</div>
</div>
<!-- 单规格 -->
<div class="js-single-spec" {notempty name="$goods_info['goods_spec_format']" }style="display:none;"{/notempty}>
<div class="layui-form-item">
<label class="layui-form-label">划线价:</label>
<div class="layui-input-block">
<input type="text" name="market_price" value="{$goods_info['market_price']}" placeholder="0.00" lay-verify="market_price" class="layui-input len-short" autocomplete="off">
<div class="layui-form-mid"></div>
</div>
<div class="word-aux">项目没有优惠活动显示的划线价格,如果项目有折扣等优惠活动划线价显示销售价</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">成本价:</label>
<div class="layui-input-block">
<input type="text" name="cost_price" value="{$goods_info['cost_price']}" placeholder="0.00" class="layui-input len-short" lay-verify="cost_price" autocomplete="off">
<div class="layui-form-mid"></div>
</div>
<div class="word-aux">成本价将不会对前台会员展示,用于商家统计使用</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">项目编码:</label>
<div class="layui-input-inline">
<input type="text" name="sku_no" value="{$goods_info['sku_list'][0]['sku_no']}" placeholder="请输入项目编码" maxlength="50" class="layui-input len-long" autocomplete="off">
</div>
</div>
</div>
<!-- 多规格 -->
<div class="js-more-spec" {notempty name="$goods_info['goods_spec_format']" }style="display:block;"{/notempty}>
<!--规格项/规格值-->
<div class="spec-edit-list"></div>
<div class="layui-form-item js-add-spec">
<label class="layui-form-label"></label>
<div class="layui-input-inline">
<button class="layui-btn" type="button">添加规格</button>
</div>
</div>
<div class="layui-form-item batch-operation-sku">
<label class="layui-form-label">批量操作:</label>
<div class="layui-input-inline">
<span class="text-color" data-field="spec_name">副标题</span>
<span class="text-color" data-field="price" data-verify="price">销售价</span>
<span class="text-color" data-field="market_price" data-verify="market_price">划线价</span>
<span class="text-color" data-field="cost_price" data-verify="cost_price">成本价</span>
<span class="text-color" data-field="stock" data-verify="stock">库存</span>
<span class="text-color" data-field="stock_alarm" data-verify="stock_alarm">库存预警</span>
<span class="text-color" data-field="sku_no" data-verify="">项目编码</span>
<input type="text" class="layui-input len-short" name="batch_operation_sku" autocomplete="off" />
<button class="layui-btn confirm" type="button">确定</button>
<button class="layui-btn layui-btn-primary cancel" type="button">取消</button>
</div>
</div>
<!--编辑时用到的SKU列表-->
<div class="js-edit-sku-list">
{foreach name="$goods_info['sku_list']" item="vo" key="k"}
<div data-index="{$k}">
<input type="hidden" name="edit_sku_id" value="{$vo['sku_id']}" />
<input type="hidden" name="edit_spec_name" value="{$vo['spec_name']}" />
<input type="hidden" name="edit_sku_no" value="{$vo['sku_no']}" />
<input type="hidden" name="edit_sku_spec_format" value="{$vo['sku_spec_format']}" />
<input type="hidden" name="edit_price" value="{$vo['price']}" />
<input type="hidden" name="edit_market_price" value="{$vo['market_price']}" />
<input type="hidden" name="edit_cost_price" value="{$vo['cost_price']}" />
<input type="hidden" name="edit_stock" value="{$vo['stock']}" />
<input type="hidden" name="edit_stock_alarm" value="{$vo['stock_alarm']}" />
<input type="hidden" name="edit_sku_image" value="{$vo['sku_image']}" />
<input type="hidden" name="edit_sku_images" value="{$vo['sku_images']}" />
<input type="hidden" name="edit_is_default" value="{$vo['is_default']}" />
<input type="hidden" name="edit_service_length" value="{$vo['service_length']}" />
</div>
{/foreach}
</div>
<!--sku列表-->
<div class="layui-form-item sku-table">
<label class="layui-form-label"></label>
<div class="layui-input-block"></div>
</div>
</div>
<div class="layui-form-item js-goods-stock-wrap" {notempty name="$goods_info['goods_spec_format']" }style="display:none;"{/notempty}>
<label class="layui-form-label"><span class="required">*</span>库存:</label>
<div class="layui-input-block">
<input type="number" name="goods_stock" value="{$goods_info['goods_stock']}" placeholder="0" lay-verify="goods_stock" class="layui-input len-short" autocomplete="off" {notempty name="$goods_info['goods_spec_format']" }disabled{/notempty}>
<div class="layui-form-mid"></div>
</div>
</div>
<div class="layui-form-item js-goods-stock-wrap" {notempty name="$goods_info['goods_spec_format']" }style="display:none;"{/notempty}>
<label class="layui-form-label">库存预警:</label>
<div class="layui-input-block">
<input type="number" name="goods_stock_alarm" value="{$goods_info['goods_stock_alarm']}" placeholder="0" lay-verify="goods_stock_alarm" class="layui-input len-short" autocomplete="off">
<div class="layui-form-mid"></div>
</div>
<div class="word-aux">项目库存少于预警数量项目列表库存数量标红显示0为不预警。</div>
</div>
<div class="layui-form-item js-goods-stock-wrap">
<label class="layui-form-label">服务时长:</label>
<div class="layui-input-block">
<input type="number" name="service_length" value="{$goods_info['sku_list'][0]['service_length']}" placeholder="请输入服务时长" autocomplete="off" class="layui-input len-short" lay-verify="service_length">
<div class="layui-form-mid">分钟</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">虚拟销量:</label>
<div class="layui-input-block">
<input type="number" name="virtual_sale" placeholder="0" value="{$goods_info['virtual_sale']}" lay-verify="virtual_sale" class="layui-input len-short" autocomplete="off">
<div class="layui-form-mid"></div>
</div>
<div class="word-aux">该设置不计入项目统计数据</div>
</div>
<div class="layui-form-item is_limit">
<label class="layui-form-label">是否限购:</label>
<div class="layui-input-block">
<input type="radio" name="is_limit" value="0" title="否" lay-filter="is_limit" {if $goods_info['is_limit'] == 0} checked {/if}>
<input type="radio" name="is_limit" value="1" title="是" lay-filter="is_limit" {if $goods_info['is_limit'] == 1} checked {/if}>
</div>
<div class="word-aux">启用限购后,购买项目时,会对该项目购买量做限制判断。</div>
</div>
{if $goods_info['is_limit'] == 1}
<div class="layui-form-item limit_type" >
<label class="layui-form-label">限购类型:</label>
<div class="layui-input-block">
<input type="radio" name="limit_type" class="limit_type" value="1" title="单次限购" lay-filter="limit_type" {if $goods_info['limit_type'] == 1 } checked {/if}>
<input type="radio" name="limit_type" class="limit_type" value="2" title="长期限购" lay-filter="limit_type" {if $goods_info['limit_type'] == 2} checked {/if}>
<input type="number" name="max_buy" placeholder="" lay-verify="max_buy" value="{$goods_info['max_buy']}" class="layui-input len-short" autocomplete="off">
<div class="layui-form-mid">&nbsp件</div>
</div>
<div class="word-aux">单次限购是针对于每次下单不能超过限购数量,长期限购是针对于会员账号购买这个项目的总数不能超过限购数量。</div>
</div>
{/if}
<div class="layui-form-item">
<label class="layui-form-label">起售:</label>
<div class="layui-input-block">
<input type="number" name="min_buy" placeholder="" lay-verify="min_buy" value="{$goods_info['min_buy']}" class="layui-input len-short" autocomplete="off">
<div class="layui-form-mid"></div>
</div>
<div class="word-aux">起售数量超出项目库存时,买家无法购买该项目</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">会员等级折扣:</label>
<div class="layui-input-block">
<div class="layui-input-inline">
<input type="radio" name="is_consume_discount" value="1" title="参与" {if $goods_info['is_consume_discount'] > 0} checked {/if}>
<input type="radio" name="is_consume_discount" value="0" title="不参与" {if $goods_info['is_consume_discount'] == 0} checked {/if}>
</div>
</div>
<div class="word-aux">如果该项目未单独配置过优惠规则,则按照默认会员等级折扣优惠</div>
</div>
</div>
<!-- 项目详情 -->
<div class="layui-tab-item">
<div class="layui-form-item">
<label class="layui-form-label sm"></label>
<div class="layui-input-inline special-length">
<input type="hidden" name="goods_content" value="{$goods_info['goods_content']}" />
<script id="editor" type="text/plain" style="width:100%;height:500px;"></script>
</div>
</div>
<script type="text/javascript" charset="utf-8" src="__STATIC__/ext/ueditor/ueditor.config.js"></script>
<script type="text/javascript" charset="utf-8" src="__STATIC__/ext/ueditor/ueditor.all.js"> </script>
<script type="text/javascript" charset="utf-8" src="__STATIC__/ext/ueditor/lang/zh-cn/zh-cn.js"></script>
</div>
<div class="layui-tab-item layui-card card-common card-brief head">
<div class="layui-card-header">
<span class="card-title">高级设置</span>
</div>
<div class="layui-card-body">
<div class="layui-form-item">
<label class="layui-form-label">排序:</label>
<div class="layui-input-block">
<input type="number" name="sort" value="{$goods_info['sort']}" class="layui-input len-short" placeholder="0" autocomplete="off">
</div>
<div class="word-aux">项目默认排序号为0数字越大排序越靠前数字重复则最新添加的靠前。</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">项目详情显示库存:</label>
<div class="layui-input-block">
<input type="radio" name="stock_show" value="1" title="显示" {if $goods_info['stock_show'] > 0} checked {/if}>
<input type="radio" name="stock_show" value="0" title="隐藏" {if $goods_info['stock_show'] == 0} checked {/if}>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">划线价显示:</label>
<div class="layui-input-block">
<input type="radio" name="market_price_show" value="1" title="显示" {if $goods_info['market_price_show'] > 0} checked {/if}>
<input type="radio" name="market_price_show" value="0" title="隐藏" {if $goods_info['market_price_show'] == 0} checked {/if}>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">项目详情显示弹幕:</label>
<div class="layui-input-block">
<input type="radio" name="barrage_show" value="1" title="显示" {if $goods_info['barrage_show'] > 0} checked {/if}>
<input type="radio" name="barrage_show" value="0" title="隐藏" {if $goods_info['barrage_show'] == 0} checked {/if}>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">项目详情显示销量:</label>
<div class="layui-input-block">
<input type="radio" name="sale_show" value="1" title="显示" {if $goods_info['sale_show'] > 0} checked {/if}>
<input type="radio" name="sale_show" value="0" title="隐藏" {if $goods_info['sale_show'] == 0} checked {/if}>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">项目海报:</label>
<div class="layui-input-inline">
<select name="template_id" lay-search="" lay-verify="">
<option value="">请选择项目海报</option>
{foreach name="$poster_list" item="vo"}
<option value="{$vo['template_id']}" {if $goods_info.template_id == $vo.template_id} selected {/if}>{$vo['poster_name']}</option>
{/foreach}
</select>
</div>
</div>
{if $form_is_exit}
<div class="layui-form-item">
<label class="layui-form-label">项目表单:</label>
<div class="layui-input-block len-mid">
<select name="form_id">
<option value="0">请选择项目表单</option>
{foreach name="$form_list" item="vo"}
<option value="{$vo.id}" {if $goods_info.form_id == $vo.id} selected {/if}>{$vo.form_name}</option>
{/foreach}
</select>
</div>
<div class="word-aux">
<a href="{:href_url('form://shop/form/addform?form_type=goods')}" class="text-color" target="_blank">创建项目表单</a>
<a href="javascript:;" onclick="refreshFormList()" class="text-color">刷新</a>
</div>
</div>
{/if}
</div>
</div>
</div>
</div>
<input type="hidden" name="goods_id" value="{$goods_info['goods_id']}" />
<input type="hidden" name="goods_spec_format" value="{$goods_info['goods_spec_format']}" />
<input type="hidden" name="goods_image" value="{$goods_info['goods_image']}" />
<input type="hidden" name="goods_attr_format" value="{$goods_info['goods_attr_format']}" />
<div class="fixed-btn">
<button class="layui-btn layui-btn-primary border-color text-color js-prev" lay-filter="prev">上一步</button>
<button class="layui-btn js-save" lay-submit="" lay-filter="save">保存</button>
<button class="layui-btn layui-btn-primary border-color text-color js-next" lay-submit="" lay-filter="next">下一步</button>
</div>
</div>
<!--规格项模板-->
<script type="text/html" id="specTemplate">
{{# for(var i=0;i<d.list.length;i++){ }}
<div class="spec-item" data-index="{{i}}">
<div class="layui-form-item spec">
<label class="layui-form-label">规格项{{i+1}}</label>
<div class="layui-input-inline">
<select name="spec_item">
<option value="0"></option>
{{# if(d.list[i].spec_name != ''){ }}
<option value="{{d.list[i].spec_id}}" data-attr-name="{{d.list[i].spec_name}}" selected>{{d.list[i].spec_name}}</option>
{{# }else{ }}
{{# } }}
</select>
<i class="layui-icon layui-icon-close" data-index="{{i}}"></i>
</div>
{{# if(i==0){ }}
<div class="layui-input-inline">
{{# if(d.add_spec_img){ }}
<input type="checkbox" name="add_spec_img" title="添加规格图片" lay-skin="primary" lay-filter="add_spec_img" checked>
{{# }else{ }}
<input type="checkbox" name="add_spec_img" title="添加规格图片" lay-skin="primary" lay-filter="add_spec_img">
{{# } }}
</div>
{{# } }}
</div>
{{# if(d.list[i].spec_name != ''){ }}
<div class="layui-form-item spec-value">
{{# }else{ }}
<div class="layui-form-item spec-value" style="display:none;">
{{# } }}
<label class="layui-form-label"></label>
<div class="layui-input-block spec-value">
{{# if(d.list[i].value.length){ }}
<ul>
{{# for(var j=0;j<d.list[i].value.length;j++){ }}
<li data-index="{{j}}" data-parent-index="{{i}}" >
{{# if(i==0 && d.add_spec_img){ }}
<div class="img-wrap">
{{# if(d.list[i].value[j].image){ }}
<img src="{{ns.img(d.list[i].value[j].image)}}" alt="">
{{# }else{ }}
<img src="SHOP_IMG/goods_spec_value_empty.png" alt="">
{{# } }}
</div>
{{# } }}
<span title="双击可编辑规格值" ondblclick="$(this).attr('contenteditable',true);$(this).focus()" class="spec-txt" data-spec_value_name="{{d.list[i].value[j].spec_value_name}}" data-parent-index="{{i}}" data-index="{{j}}">{{d.list[i].value[j].spec_value_name}}</span>
<!--{{1# if(d.list[i].value[j].is_delete === undefined){ }}-->
<i class="layui-icon layui-icon-close" data-parent-index="{{i}}" data-index="{{j}}"></i>
<!--{{1# } }}-->
</li>
{{# } }}
</ul>
{{# } }}
<a class="text-color" href="javascript:;" data-index="{{i}}">+添加规格值</a>
<div class="add-spec-value-popup" data-index="{{i}}">
<select name="spec_value_item"></select>
<button class="layui-btn layui-btn-primary border-color text-color js-cancel-spec-value">取消</button>
</div>
</div>
</div>
</div>
{{# } }}
</script>
<!--SKU列表模板-->
<script type="text/html" id="skuTableTemplate">
{{# if(d.skuList.length){ }}
<table class="layui-table">
<colgroup></colgroup>
<thead>
<tr>
{{# if(d.showSpecName){ }}
<th colspan="{{d.colSpan}}" style="min-width: 60px;">项目规格</th>
{{# } }}
<th rowspan="{{d.rowSpan}}">SKU图片</th>
<th rowspan="{{d.rowSpan}}">副标题</th>
<th rowspan="{{d.rowSpan}}"><span class="required">*</span></th>
<th rowspan="{{d.rowSpan}}">划线价</th>
<th rowspan="{{d.rowSpan}}">成本价</th>
<th rowspan="{{d.rowSpan}}"><span class="required">*</span></th>
<th rowspan="{{d.rowSpan}}">库存预警</th>
<th rowspan="{{d.rowSpan}}">服务时长</th>
<th rowspan="{{d.rowSpan}}">商品编码多个编码以英文逗号分割</th>
<th rowspan="{{d.rowSpan}}" style="white-space: nowrap;">默认展示</th>
</tr>
{{# if(d.colSpan>1){ }}
<tr>
{{# for(var i=0;i<d.specList.length;i++){ }}
{{# if(d.specList[i].spec_name && d.specList[i].value.length> 0){ }}
<th>{{d.specList[i].spec_name}}</th>
{{# } }}
{{# } }}
</tr>
{{# } }}
</thead>
<tbody>
{{# for(var i=0;i<d.skuList.length;i++){ }}
<tr>
<td class="sku_imgs" id="sku_img_{{i}}" style="width: 130px;">
{{# for(var j=0;j<d.skuList[i].sku_images_arr.length;j++){ }}
<div class="img-wrap" data-index="{{j}}" data-parent-index="{{i}}">
<a href="javascript:void(0)">
<img src="{{ns.img(d.skuList[i].sku_images_arr[j])}}" layer-src />
</a>
<div class="operation">
<i title="图片预览" class="iconfont iconreview js-preview"></i>
<i title="删除图片" class="layui-icon layui-icon-delete js-delete"></i>
</div>
</div>
{{# } }}
{{# if(d.skuList[i].sku_images_arr.length<d.goods_sku_max){ }}
<div class="upload-sku-img" data-index="{{i}}"><i class="layui-icon layui-icon-add-1"></i></div>
{{# } }}
</td>
<td>
<input type="text" name="spec_name" placeholder="副标题" maxlength="100" value="{{d.skuList[i].spec_name}}" class="layui-input" autocomplete="off" data-index="{{i}}">
</td>
<td>
<input type="text" name="price" placeholder="销售价" lay-verify="sku_price" value="{{d.skuList[i].price}}" class="layui-input" autocomplete="off" data-index="{{i}}">
</td>
<td>
<input type="text" name="market_price" placeholder="划线价" value="{{d.skuList[i].market_price}}" lay-verify="sku_market_price" class="layui-input" autocomplete="off" data-index="{{i}}">
</td>
<td>
<input type="text" name="cost_price" placeholder="成本价" value="{{d.skuList[i].cost_price}}" lay-verify="sku_cost_price" class="layui-input" autocomplete="off" data-index="{{i}}">
</td>
<td>
<input type="text" name="stock" placeholder="库存" value="{{d.skuList[i].stock}}" lay-verify="sku_stock" class="layui-input" autocomplete="off" data-index="{{i}}">
</td>
<td>
<input type="text" name="stock_alarm" placeholder="库存预警" value="{{d.skuList[i].stock_alarm}}" lay-verify="sku_stock_alarm" class="layui-input" autocomplete="off" data-index="{{i}}">
</td>
<td>
<input type="number" name="service_length" placeholder="服务时长" value="{{d.skuList[i].service_length}}" maxlength="50" class="layui-input" autocomplete="off" data-index="{{i}}" lay-verify="verify_num">
</td>
<td>
<input type="text" name="sku_no" placeholder="商品编码" value="{{d.skuList[i].sku_no}}" maxlength="50" class="layui-input" autocomplete="off" data-index="{{i}}">
</td>
<td style="min-width: 40px;">
{{# if(d.skuList[i].is_default == 1) { }}
<input type="checkbox" data-index="{{i}}" name="is_default" lay-filter="is_default" lay-skin="switch" checked>
{{# }else { }}
<input type="checkbox" data-index="{{i}}" name="is_default" lay-filter="is_default" lay-skin="switch">
{{# } }}
</td>
</tr>
{{# } }}
</tbody>
</table>
{{# } }}
<div class="word-aux text-color" style="margin: 10px 0 0 0;">默认展示是多规格项目在客户访问项目时默认显示的项目规格</div>
</script>
<!--项目主图列表-->
<script type="text/html" id="goodsImage">
{{# if(d.list.length){ }}
{{# for(var i=0;i<d.list.length;i++){ }}
<div class="item upload_img_square_item" data-index="{{i}}">
<div class="img-wrap">
<img src="{{ns.img(d.list[i],'small')}}" layer-src="{{ns.img(d.list[i],'big')}}">
</div>
<div class="operation">
<i title="图片预览" class="iconfont iconreview js-preview"></i>
<i title="删除图片" class="layui-icon layui-icon-delete js-delete" data-index="{{i}}"></i>
<div class="replace_img" data-index="{{i}}">点击替换</div>
</div>
</div>
{{# } }}
{{# if(d.list.length < d.max){ }}
<div class="item js-add-goods-image upload_img_square">+</div>
{{# } }}
{{# }else{ }}
<div class="item js-add-goods-image upload_img_square">+</div>
{{# } }}
</script>
<script src="__STATIC__/ext/drag-arrange.js"></script>
<script src="__STATIC__/ext/video/videojs-ie8.min.js"></script>
<script src="__STATIC__/ext/video/video.min.js"></script>
<script src="__STATIC__/ext/searchable_select/searchable_select.js"></script>
<script src="SHOP_JS/category_select.js?time=20240821"></script>
<script src="SHOP_JS/goods_edit_common.js?time=20250527"></script>
<script src="ADDON_CARDSERVICE_JS/service_goods_edit.js?v=1.1"></script>

View File

@@ -0,0 +1,133 @@
<?php
/**
* Niushop商城系统 - 团队十年电商经验汇集巨献!
* =========================================================
* Copy right 2019-2029 杭州牛之云科技有限公司, 保留所有权利。
* ----------------------------------------------
* 官方网址: https://www.niushop.com
* 这不是一个自由软件!您只能在不用于商业目的的前提下对程序代码进行修改和使用。
* 任何企业和个人不允许对程序代码以任何形式任何目的再发布。
* =========================================================
*/
namespace addon\cardservice\shopapi\controller;
use addon\cardservice\model\CardGoods;
use addon\cardservice\model\MemberCard as MemberCardModel;
use app\shopapi\controller\BaseApi;
/**
*
* Class Goods
*/
class Goods extends BaseApi
{
public function __construct()
{
//执行父类构造函数
parent::__construct();
$token = $this->checkToken();
if ($token[ 'code' ] < 0) {
echo $this->response($token);
exit;
}
}
/**
* 卡项购买记录
*/
public function cardList(){
$model = new MemberCardModel();
$page_index = $this->params['page'] ?? 1;
$page_size = $this->params['page_size'] ?? PAGE_LIST_ROWS;
$goods_id = $this->params['goods_id'] ?? 0;
$search_text = $this->params['search_text'] ?? '';
$condition = [
[ 'mgc.site_id', '=', $this->site_id ],
[ 'mgc.goods_id', '=', $goods_id ],
];
if (!empty($search_text)) {
$condition[] = [ 'm.nickname', 'like', '%' . $search_text . '%' ];
}
$field = 'mgc.*, g.goods_name,g.price,g.goods_image,m.username,m.nickname,m.headimg';
$join = [
[ 'goods g', 'mgc.goods_id = g.goods_id', 'inner' ],
[ 'member m', 'mgc.member_id = m.member_id', 'left' ],
];
$list = $model->getCardPageList($condition, $field, 'mgc.create_time desc', $page_index, $page_size, 'mgc', $join);
return $this->response($list);
}
/**
* 卡项购买记录详情
*/
public function cardDetail(){
$card_id = $this->params['card_id'] ?? 0;
$model = new MemberCardModel();
$card_goods = new CardGoods();
$condition = [
[ 'mgc.card_id', '=', $card_id ],
[ 'mgc.site_id', '=', $this->site_id ],
];
$field = 'mgc.*, g.goods_name,g.price,g.goods_image,m.username,m.nickname,m.headimg';
$join = [
[ 'goods g', 'mgc.goods_id = g.goods_id', 'inner' ],
[ 'member m', 'mgc.member_id = m.member_id', 'left' ],
];
$detail = $model->getCardInfo($condition, $field, 'mgc', $join)[ 'data' ] ?? [];
$detail[ 'card_type_name' ] = $card_goods->getCardType($detail[ 'card_type' ])[ 'title' ];
$condition = [
[ 'mgc.card_id', '=', $card_id ],
[ 'g.goods_state', '=', 1 ],
[ 'g.is_delete', '=', 0 ]
];
$join = [
[ 'goods_sku g', 'mgc.sku_id = g.sku_id', 'left' ],
];
$item_list = $model->getCartItemList($condition, 'mgc.*, g.sku_name', 'mgc.item_id asc', 'mgc', $join)[ 'data' ] ?? [];
$detail['item_list'] = $item_list;
return $this->response($this->success($detail));
}
/**
* 卡项使用记录
* @return false|string
*/
public function cardUseRecord()
{
$page_index = $this->params['page'] ?? 1;
$page_size = $this->params['page_size'] ?? PAGE_LIST_ROWS;
$card_id = $this->params['card_id'] ?? 0;
$item_id = $this->params['item_id'] ?? 0;
$condition = [];
$condition[] = [ 'cr.site_id', '=', $this->site_id ];
if (!empty($item_id)) {
$condition[] = [ 'cr.card_item_id', '=', $item_id ];
}
if (!empty($card_id)) {
$condition[] = [ 'cr.card_id', '=', $card_id ];
}
$alias = 'cr';
$prefix = config('database.connections.mysql.prefix');
$field = 'cr.*, sku.sku_name,sku.sku_image,sku.sku_images,sku.price,ci.num as item_num,
IF(cr.type = \'order\', (select order_id from `' . $prefix . 'order_goods` og where og.order_goods_id = cr.relation_id), 0) as order_id, s.store_name';
$join = [
[ 'member_goods_card_item ci', 'ci.item_id = cr.card_item_id', 'left' ],
[ 'goods_sku sku', 'ci.sku_id = sku.sku_id', 'left' ],
[ 'store s', 'cr.store_id = s.store_id', 'left' ],
];
$model = new MemberCardModel();
$list = $model->getMemberCardRecordsPageList($condition, $field, 'cr.create_time desc', $page_index, $page_size, $alias, $join);
return $this->response($list);
}
}

View File

@@ -0,0 +1,151 @@
<?php
/**
* Niushop商城系统 - 团队十年电商经验汇集巨献!
* =========================================================
* Copy right 2019-2029 上海牛之云网络科技有限公司, 保留所有权利。
* ----------------------------------------------
* 官方网址: https://www.niushop.com
* 这不是一个自由软件!您只能在不用于商业目的的前提下对程序代码进行修改和使用。
* 任何企业和个人不允许对程序代码以任何形式任何目的再发布。
* =========================================================
*/
namespace addon\cardservice\storeapi\controller;
use addon\cardservice\model\MemberCard as MemberCardModel;
use app\storeapi\controller\BaseStoreApi;
/**
* 会员管理 控制器
*/
class Membercard extends BaseStoreApi
{
/**
* 会员卡项
*/
public function lists()
{
$member_id = $this->params[ 'member_id' ] ?? 0;
$page = $this->params[ 'page' ] ?? 1;
$page_size = $this->params[ 'page_size' ] ?? PAGE_LIST_ROWS;
$status = $this->params[ 'status' ] ?? 'all';
$member_oncecard_model = new MemberCardModel();
$condition = [
[ 'mgc.member_id', '=', $member_id ],
[ 'mgc.site_id', '=', $this->site_id ],
[ 'g.is_delete', '=', 0 ]
];
if ($status != 'all') $condition[] = [ 'mgc.status', '=', $status ];
$field = 'mgc.card_id, mgc.create_time, mgc.end_time, mgc.status, mgc.card_code, mgc.card_type, mgc.goods_name, mgc.total_num, mgc.total_use_num';
$res = $member_oncecard_model->getCardPageList($condition, $field, 'mgc.create_time desc', $page, $page_size, 'mgc', [
[ 'goods g', 'g.goods_id = mgc.goods_id', 'inner' ]
]);
if (empty($res[ 'data' ])) return $res;
foreach ($res[ 'data' ][ 'list' ] as $k => &$v) {
$alias = 'i';
$join = [
[ 'goods g', 'i.goods_id = g.goods_id', 'inner' ],
[ 'goods_sku gs', 'i.sku_id = gs.sku_id', 'inner' ],
[ 'verify mv', 'i.member_verify_id = mv.id', 'inner' ],
[ 'store_goods_sku sgs', 'i.sku_id=sgs.sku_id and sgs.store_id=' . $this->store_id, 'inner' ],
[ 'store s', 's.store_id = sgs.store_id', 'left' ]
];
$field = 'i.*, gs.sku_name,gs.sku_image, gs.is_virtual, mv.verify_code, IF(g.is_unify_price = 1,gs.price,sgs.price) as price,IF(s.stock_type = "store",sgs.stock, gs.stock) as stock';
$order = 'i.item_id asc';
$res[ 'data' ][ 'list' ][ $k ][ 'item_list' ] = $member_oncecard_model->getCartItemList([
[ 'i.card_id', '=', $v[ 'card_id' ] ],
[ 'i.site_id', '=', $this->site_id ],
[ 'i.member_id', '=', $member_id ],
[ 'sgs.status', '=', 1 ],
[ 'g.is_delete', '=', 0 ]
], $field, $order, $alias, $join)[ 'data' ];
foreach ($res[ 'data' ][ 'list' ][ $k ][ 'item_list' ] as $ck => $cv) {
$res[ 'data' ][ 'list' ][ $k ][ 'item_list' ][ $ck ][ 'stock' ] = numberFormat($res[ 'data' ][ 'list' ][ $k ][ 'item_list' ][ $ck ][ 'stock' ]);
}
}
return $this->response($res);
}
/**
* 卡项详情
*/
public function detail()
{
$card_id = $this->params[ 'card_id' ] ?? 0;
$member_id = $this->params[ 'member_id' ] ?? 0;
if (empty($card_id)) {
return $this->response($this->error('', 'REQUEST_ID'));
}
$model = new MemberCardModel();
$condition = [
[ 'mgc.site_id', '=', $this->site_id ],
[ 'mgc.member_id', '=', $member_id ],
[ 'mgc.card_id', '=', $card_id ],
[ 'g.is_delete', '=', 0 ],
];
$field = 'mgc.*, g.goods_name,g.price,g.goods_image,g.introduction,g.goods_content';
$join = [
[ 'goods g', 'mgc.goods_id = g.goods_id', 'inner' ],
];
$info = $model->getCardInfo($condition, $field, 'mgc', $join)[ 'data' ];
if (empty($info)) return $this->response($this->error('', '未获取到卡项信息'));
$condition = [
[ 'mgci.card_id', '=', $info[ 'card_id' ] ],
[ 'sku.is_delete', '=', 0 ],
];
$info[ 'card_item' ] = $model->getCartItemList($condition, 'mgci.*,sku.sku_name,sku.price,sku.sku_image,sku.sku_images,sku.goods_class_name,sku.stock,
v.verify_code,v.verify_type,v.verify_type_name,v.verify_content_json,v.verifier_id,v.verifier_name,v.is_verify,v.verify_time,v.expire_time,v.verify_from,v.verify_remark,v.verify_total_count,v.verify_use_num', 'mgci.card_id asc', 'mgci', [
[ 'goods_sku sku', 'mgci.sku_id = sku.sku_id', 'inner' ],
[ 'verify v', 'mgci.member_verify_id = v.id', 'left' ],
])[ 'data' ] ?? [];
foreach ($info[ 'card_item' ] as $k => $v) {
$info[ 'card_item' ][ $k ][ 'stock' ] = numberFormat($info[ 'card_item' ][ $k ][ 'stock' ]);
}
return $this->response($this->success($info));
}
/**
* 使用记录
*/
public function records()
{
$card_id = $this->params[ 'card_id' ] ?? 0;
$item_id = $this->params[ 'item_id' ] ?? 0;
$member_id = $this->params[ 'member_id' ] ?? 0;
$page = $this->params[ 'page' ] ?? 1;
$page_size = $this->params[ 'page_size' ] ?? PAGE_LIST_ROWS;
if (empty($member_id) || ( empty($card_id) && empty($item_id) )) {
return $this->response($this->error([], '请传入必要参数'));
}
$condition = [];
$condition[] = [ 'cr.site_id', '=', $this->site_id ];
$condition[] = [ 'ci.member_id', '=', $member_id ];
if (!empty($item_id)) {
$condition[] = [ 'cr.card_item_id', '=', $item_id ];
}
if (!empty($card_id)) {
$condition[] = [ 'cr.card_id', '=', $card_id ];
}
$alias = 'cr';
$prefix = config('database.connections.mysql.prefix');
$field = 'cr.*, sku.sku_name,sku.sku_image,sku.sku_images,sku.price,ci.num as item_num,
IF(cr.type = \'order\', (select order_id from `' . $prefix . 'order_goods` og where og.order_goods_id = cr.relation_id), 0) as order_id';
$join = [
[ 'member_goods_card_item ci', 'ci.item_id = cr.card_item_id', 'left' ],
[ 'goods_sku sku', 'ci.sku_id = sku.sku_id', 'left' ],
];
$model = new MemberCardModel();
$list = $model->getMemberCardRecordsPageList($condition, $field, 'cr.create_time desc', $page, $page_size, $alias, $join);
return $this->response($list);
}
}

View File

@@ -0,0 +1,388 @@
<?php
/**
* Niushop商城系统 - 团队十年电商经验汇集巨献!
* =========================================================
* Copy right 2019-2029 上海牛之云网络科技有限公司, 保留所有权利。
* ----------------------------------------------
* 官方网址: https://www.niushop.com
* 这不是一个自由软件!您只能在不用于商业目的的前提下对程序代码进行修改和使用。
* 任何企业和个人不允许对程序代码以任何形式任何目的再发布。
* =========================================================
*/
namespace addon\cardservice\storeapi\controller;
use addon\cardservice\model\Reserve as ReserveModel;
use app\dict\goods\GoodsDict;
use app\model\goods\Goods as GoodsModel;
use app\model\member\Member as MemberModel;
use app\model\system\UserGroup;
use app\storeapi\controller\BaseStoreApi;
class Reserve extends BaseStoreApi
{
/**
* 预约状态
* @return false|string
*/
public function status()
{
$reserve_state = ( new ReserveModel )->reserve_state;
return $this->response($this->success($reserve_state));
}
/**
* 预约记录(按周)
* @return false|string
*/
public function getReserveByWeek()
{
$token = $this->checkToken();
if ($token[ 'code' ] < 0) return $this->response($token);
$reserve_model = new ReserveModel();
$length = $this->params[ 'length' ] ?? 0;
$data = $this->getWeekDay();
foreach ($data as $wk => $w_item) {
$field = 'noy.reserve_id,noy.reserve_state,noy.reserve_time,nm.nickname';
$list = $reserve_model->getReservePageList([
[ 'noy.site_id', '=', $this->site_id ],
[ 'noy.reserve_time', 'between', [ $w_item[ 'start' ], $w_item[ 'end' ] ] ]
], 1, PAGE_LIST_ROWS, 'noy.create_time desc', $field);
if (!empty($list[ 'data' ][ 'list' ])) {
foreach ($list[ 'data' ][ 'list' ] as $k => $item) {
$list[ 'data' ][ 'list' ][ $k ][ 'item' ] = $reserve_model->getReserveItemList([
[
'oyi.reserve_id', '=', $item[ 'reserve_id' ]
]
], 'g.goods_name,g.goods_id,g.sku_id', 'reserve_item_id desc', 'oyi',
[ [ 'goods g', 'g.sku_id = oyi.reserve_goods_sku_id', 'right' ] ])[ 'data' ];
}
}
$data[ $wk ][ 'data' ] = $list[ 'data' ];
}
return $this->response($this->success($data));
}
/**
* 添加预约
* @return mixed
*/
public function add()
{
$token = $this->checkToken();
if ($token[ 'code' ] < 0) return $this->response($token);
$reserve_model = new ReserveModel();
$data = $reserve_model->addReserve([
'site_id' => $this->site_id,
'app_module' => $this->app_module,
'member_id' => $this->params[ 'member_id' ] ?? 0,
'goods' => isset($this->params[ 'goods' ]) ? json_decode($this->params[ 'goods' ], true) : [],
'store_id' => $this->store_id,
'date' => $this->params[ 'date' ] ?? '',
'time' => $this->params[ 'time' ] ?? '',
'remark' => $this->params[ 'desc' ] ?? '',
'source' => 'store'
]);
return $this->response($data);
}
/**
* 修改预约
* @return mixed
*/
public function update()
{
$token = $this->checkToken();
if ($token[ 'code' ] < 0) return $this->response($token);
$reserve_model = new ReserveModel();
$res = $reserve_model->editReserve([
'site_id' => $this->site_id,
'app_module' => $this->app_module,
'reserve_id' => $this->params[ 'reserve_id' ] ?? 0,
'goods' => isset($this->params[ 'goods' ]) ? json_decode($this->params[ 'goods' ], true) : [],
'date' => $this->params[ 'date' ] ?? '',
'time' => $this->params[ 'time' ] ?? '',
'remark' => $this->params[ 'desc' ] ?? '',
]);
return $this->response($res);
}
/**
* 预约配置
*/
public function getConfig()
{
$token = $this->checkToken();
if ($token[ 'code' ] < 0) return $this->response($token);
$reserve_model = new ReserveModel();
$config = $reserve_model->getReserveConfig($this->site_id, $this->store_id);
return $this->response($this->success($config[ 'data' ][ 'value' ]));
}
/**
* 预约配置
*/
public function setConfig()
{
$token = $this->checkToken();
if ($token[ 'code' ] < 0) return $this->response($token);
$week = $this->params[ 'week' ] ?? '';
$data = [
'week' => json_encode(explode(',', $week)),
'start' => $this->params[ 'start' ] ?? 32400,
'end' => $this->params[ 'end' ] ?? 79200,
'interval' => $this->params[ 'interval' ] ?? 30,
'advance' => $this->params[ 'advance' ] ?? 1,
'max' => $this->params[ 'max' ] ?? 1,
];
$reserve_model = new ReserveModel();
$res = $reserve_model->setReserveConfig($data, $this->site_id, $this->store_id);
return $this->response($res);
}
/**
* 预约列表
*/
public function lists()
{
$token = $this->checkToken();
if ($token[ 'code' ] < 0) return $this->response($token);
$page = $this->params['page'] ?? 1;
$page_size = $this->params['page_size'] ?? PAGE_LIST_ROWS;
$search_text = $this->params['search_text'] ?? '';
$reserve_state = $this->params['reserve_state'] ?? 'all';
$start = $this->params['start'] ?? 0;
$end = $this->params['end'] ?? 0;
$condition = [
[ 'noy.site_id', '=', $this->site_id ],
[ 'noy.store_id', '=', $this->store_id ],
];
if ($reserve_state != 'all') {
$condition[] = [ 'noy.reserve_state', '=', $reserve_state ];
}
if (!empty($search_text)) {
$condition[] = [ 'nm.mobile|nm.nickname', 'like', '%' . $search_text . '%' ];
}
if ($start && $end) {
$condition[] = [ 'noy.reserve_time', 'between', [ $start, $end ] ];
} else {
if ($start && !$end) {
$condition[] = [ 'noy.reserve_time', '>=', $start ];
} else {
if (!$start && $end) {
$condition[] = [ 'noy.reserve_time', '<=', $end ];
}
}
}
$field = 'noy.member_id, noy.remark, noy.reserve_id, noy.reserve_name, noy.reserve_state_name, noy.reserve_state, noy.reserve_time, noy.create_time, nm.headimg, nm.nickname, nm.mobile, nm.headimg';
$reserve_model = new ReserveModel();
$result = $reserve_model->getReservePageList($condition, $page, $page_size, 'noy.create_time desc', $field);
if (!empty($result[ 'data' ][ 'list' ])) {
foreach ($result[ 'data' ][ 'list' ] as $k => $item) {
$result[ 'data' ][ 'list' ][ $k ][ 'item' ] = $reserve_model->getReserveItemList([
[
'oyi.reserve_id', '=', $item[ 'reserve_id' ]
]
], 'g.goods_name,g.goods_id,g.sku_id', 'reserve_item_id desc', 'oyi',
[ [ 'goods g', 'g.sku_id = oyi.reserve_goods_sku_id', 'right' ] ])[ 'data' ];
}
}
return $this->response($result);
}
/**
* 员工管理
* @return mixed
*/
public function servicer()
{
$token = $this->checkToken();
if ($token[ 'code' ] < 0) return $this->response($token);
$store_id = $this->store_id;
$condition = [
[ 'u.site_id', '=', $this->site_id ],
];
$condition[] = [ 'ug.store_id', '=', $store_id ];
if (!empty($search_text)) {
$condition[] = [ 'u.username', 'like', "%{$search_text}%" ];
}
$user_model = new UserGroup();
$result = $user_model->getUserList($condition, 'u.username,u.status,u.uid,u.group_name', 'u.uid desc', 'ug', [
[ 'user u', 'ug.uid=u.uid', 'left' ]
]);
return $this->response($result);
}
/**
* 获取一周内的时间段
* @return false|string
*/
public function getWeekDay()
{
$token = $this->checkToken();
if ($token[ 'code' ] < 0) return $this->response($token);
$length = $this->params[ 'length' ] ?? 0;
$first_day = mktime(0, 0, 0, date("m"), date("d") - date("w") + 1, date("Y"));
$first_day = strtotime($length . ' week', $first_day);
$week = [ '周日', '周一', '周二', '周三', '周四', '周五', '周六' ];
$data = [];
for ($i = 0; $i < 7; $i++) {
$time = strtotime("+ {$i} day", $first_day);
$data[] = [
'start' => $time,
'end' => strtotime(date('Y-m-d 23:59:59', $time)),
'date' => date('m/d', $time),
'w' => date('w', $time),
'week' => $week[date('w', $time)],
'currday' => date('Y-m-d', $time) == date('Y-m-d') ? 1 : 0
];
}
return $this->response($this->success($data));
}
/**
* 确认预约
* @return false|string
*/
public function confirm()
{
$token = $this->checkToken();
if ($token[ 'code' ] < 0) return $this->response($token);
$reserve_id = $this->params['reserve_id'] ?? 0;
$reserve_model = new ReserveModel();
$res = $reserve_model->confirmReserve($reserve_id, $this->site_id);
return $this->response($res);
}
/**
* 取消预约
* @return false|string
*/
public function cancel()
{
$token = $this->checkToken();
if ($token[ 'code' ] < 0) return $this->response($token);
$reserve_id = $this->params['reserve_id'] ?? 0;
$reserve_model = new ReserveModel();
$res = $reserve_model->cancelReserve($reserve_id, $this->site_id);
return $this->response($res);
}
/**
* 确认到店
* @return false|string
*/
public function confirmToStore()
{
$token = $this->checkToken();
if ($token[ 'code' ] < 0) return $this->response($token);
$reserve_id = $this->params['reserve_id'] ?? 0;
$reserve_model = new ReserveModel();
$res = $reserve_model->confirmToStore($reserve_id, $this->site_id);
return $this->response($res);
}
/**
* 确认完成
* @return false|string
*/
public function complete()
{
$token = $this->checkToken();
if ($token[ 'code' ] < 0) return $this->response($token);
$reserve_id = $this->params['reserve_id'] ?? 0;
$reserve_model = new ReserveModel();
$res = $reserve_model->confirmComplete($reserve_id, $this->site_id);
return $this->response($res);
}
/**
* 服务列表
* @return false|string
*/
public function serviceList()
{
$token = $this->checkToken();
if ($token[ 'code' ] < 0) return $this->response($token);
$page_index = $this->params['page'] ?? 1;
$page_size = $this->params['page_size'] ?? PAGE_LIST_ROWS;
$goods = new GoodsModel();
$condition = [];
$condition[] = [ 'gs.site_id', '=', $this->site_id ];
$condition[] = [ 'g.goods_class', '=', GoodsDict::service ];
$condition[] = [ 'g.goods_state', '=', 1 ];
$condition[] = [ 'g.is_delete', '=', 0 ];
$alias = 'gs';
$field = 'gs.is_consume_discount,gs.discount_config,gs.discount_method,gs.member_price,gs.goods_id,gs.sort,gs.sku_id,gs.sku_name,gs.price,gs.market_price,gs.discount_price,gs.stock,(g.sale_num + g.virtual_sale) as sale_num,(gs.sale_num + gs.virtual_sale) as sale_sort,gs.sku_image,gs.goods_name,gs.site_id,gs.is_free_shipping,gs.introduction,gs.promotion_type,g.goods_image,g.promotion_addon,gs.is_virtual,g.goods_spec_format,g.recommend_way,gs.max_buy,gs.min_buy,gs.unit,gs.is_limit,gs.limit_type,g.label_name,g.stock_show,g.sale_show,g.market_price_show,g.barrage_show,g.sale_channel,g.sale_store';
$join = [
[ 'goods g', 'gs.sku_id = g.sku_id', 'inner' ]
];
$list = $goods->getGoodsSkuPageList($condition, $page_index, $page_size, 'g.sort desc', $field, $alias, $join);
return $this->response($list);
}
/**
* 预约详情
* @return mixed|void
*/
public function detail()
{
$token = $this->checkToken();
if ($token[ 'code' ] < 0) return $this->response($token);
$reserve_id = $this->params['reserve_id'] ?? 0;
$model = new ReserveModel();
$info = $model->getReserveInfo([
[ 'reserve_id', '=', $reserve_id ],
[ 'oy.site_id', '=', $this->site_id ]
], 'oy.*, nm.headimg, nm.nickname, nm.mobile,os.store_name', 'oy', [
[ 'member nm', 'oy.member_id = nm.member_id', 'left' ],
[ 'store os', 'oy.store_id = os.store_id', 'left' ]
])[ 'data' ];
if (empty($info)) return $this->response($this->error('', '缺少必须参数'));
$info[ 'item' ] = $model->getReserveItemList([
[
'oyi.reserve_id', '=', $reserve_id
],
], 'g.goods_name,g.goods_id,g.sku_id,g.price,sku.service_length,ys.username,oyi.reserve_user_id as uid', 'reserve_item_id desc', 'oyi',
[ [ 'goods g', 'g.sku_id = oyi.reserve_goods_sku_id', 'right' ], [ 'goods_sku sku', 'sku.sku_id = oyi.reserve_goods_sku_id', 'right' ], [ 'user ys', 'oyi.reserve_user_id = ys.uid', 'left' ] ])[ 'data' ];
$member_model = new MemberModel();
$info[ 'member' ] = $member_model->getMemberInfo([ [ 'member_id', '=', $info[ 'member_id' ] ], [ 'site_id', '=', $this->site_id ] ], 'nickname,mobile,member_id')[ 'data' ] ?? [];
return $this->response($this->success($info));
}
}