diff --git a/app/index/controller/Product.php b/app/index/controller/Product.php new file mode 100644 index 00000000..06a8ca5d --- /dev/null +++ b/app/index/controller/Product.php @@ -0,0 +1,359 @@ +param(['id']); + + $focus_image = []; + // 获取产品分类页焦点横幅 + $banner = SysBannerModel::with(['items' => function($query) { + $query->withoutField([ + 'status', + 'created_at', + 'updated_at', + 'deleted_at' + ]) + ->where('status', '=', 1) + ->order(['sort' => 'asc', 'id' => 'desc']); + }]) + ->uniqueLabel(['BANNER_6808abd813d78']) + ->language($this->lang_id) + ->enabled(true) + ->select(); + if (!$banner->isEmpty()) { + $banner_map = []; + foreach ($banner as $v) { + $banner_map[$v->unique_label] = $v; + } + $focus_image = data_get($banner_map, 'BANNER_6808abd813d78')?->items->toArray(); + } + View::assign('focus_image', $focus_image); + + // 获取分类及产品信息 + $categorys_data = ProductCategoryModel::field(['id', 'name', 'level']) + ->language($this->lang_id) + ->displayed(true) + ->child($param['id'], true) + ->order(['sort' => 'asc', 'id' => 'desc']) + ->select(); + if (!$categorys_data->isEmpty()) { + if ($categorys_data->count() > 1) { + // 当分类数不只一个时,当前分类下有子分类,移除当前分类,只输出子分类 + $categorys_data = $categorys_data->filter(function($item) use($param) { + return $item->id != $param['id']; + }); + } + $categorys_data = $categorys_data->toArray(); + + $products = ProductModel::field([ + 'id', + 'category_id', + 'spu', + 'name', + 'short_name', + 'cover_image', + 'desc' + ]) + ->byCategory(array_column($categorys_data, 'id')) + ->language($this->lang_id) + ->enabled(true) + ->onSale(true) + ->onShelves(true) + ->order(['sort' => 'asc', 'id' => 'desc']) + ->select(); + if (!$products->isEmpty()) { + // 获取sku信息 + $skus_map = []; + $color_map = []; + $skus = ProductSkuModel::withoutField(['sort', 'created_at', 'updated_at']) + ->byProductId(Arr::pluck($products, 'id')) + ->order(['sort' => 'asc', 'id' => 'asc']) + ->select(); + if (!$skus->isEmpty()) { + // 获取产品sku属性信息 + $sku_attrs_map = []; + $sku_attrs = ProductSkuAttrModel::bySkuId(array_unique(Arr::pluck($skus, 'id')))->select()->toArray(); + if (!empty($sku_attrs)) { + // 获取属性名称 + $attrs = ProductAttrModel::bypks(array_unique(Arr::pluck($sku_attrs, 'attr_id')))->column(['attr_name'], 'id'); + // 获取属性名称 + foreach ($sku_attrs as $v) { + $sku_attrs_map[$v['sku_id']][] = [ + 'sku_id' => $v['sku_id'], + 'attr_id' => $v['attr_id'], + 'attr_name' => $attrs[$v['attr_id']]?? '', + 'attr_value' => $v['attr_value'] + ]; + } + } + // sku匹配属性 + foreach ($skus as $v) { + // 找到相应产品的sku图片信息 + $skus_map[$v['product_id']][] = [ + 'id' => $v['id'], + 'sku' => $v['sku'], + 'main_image' => $v['main_image'], + ]; + // 找到相应产品的sku产色属性信息 + $attr = $sku_attrs_map[$v['id']]?? []; + if (!empty($attr)) { + foreach ($attr as $at) { + if ($at['attr_name'] == '颜色') { + $color_map[$v['product_id']][] = $at; + } + } + } + } + } + + // 按分类分组产品 + $products_map = []; + foreach ($products as $v) { + $products_map[$v['category_id']][] = [ + 'id' => $v['id'], + 'spu' => $v['spu'], + 'name' => $v['name'], + 'short_name' => $v['short_name'], + 'cover_image' => $v['cover_image'], + 'desc' => $v['desc'], + 'sku' => $skus_map[$v['id']]?? [], + 'colors' => $color_map[$v['id']]?? [] + ]; + } + foreach ($categorys_data as $k => $v) { + $categorys_data[$k]['products'] = $products_map[$v['id']] ?? []; + } + } + } + View::assign('categorys_data', $categorys_data); + + return View::fetch('category'); + } + + /** + * 产品详情页 + */ + public function detail() + { + $param = request()->param(['id']); + + // 获取产品信息 + $product = ProductModel::withoutField([ + 'language_id', + 'status', + 'created_at', + 'updated_at', + 'deleted_at' + ]) + ->bypk($param['id']) + ->find(); + View::assign('product', $product); + + $product_categorys = []; + $product_params = []; + $product_skus = []; + $product_sku_attrs = []; + $product_purchase_links = []; + if (!empty($product)) { + // 获取产品分类信息 + $product_categorys = ProductCategoryModel::field(['id', 'pid', 'name']) + ->bypk($product['category_id']) + ->union(function($query) use($product) { + $query->name('product_category') + ->field(['id', 'pid', 'name']) + ->where('id', 'IN', function($sub_query) use($product) { + $sub_query->name('product_category') + ->where('id', '=', $product['category_id']) + ->field('path'); + }); + }) + ->order(['id' => 'asc']) + ->select() + ->toArray(); + + // 获取产品参数信息 + $product_params = ProductParamsModel::field(['id', 'name', 'value']) + ->byProductId($product['id']) + ->order(['id' => 'asc']) + ->select() + ->toArray(); + + // 获取产品sku信息 + $skus = ProductSkuModel::withoutField(['created_at', 'updated_at']) + ->byProductId($product['id']) + ->order(['sort' => 'asc', 'id' => 'asc']) + ->select(); + if (!empty($skus)) { + $product_skus = $skus->toArray(); + + // 获取产品sku属性信息 + $sku_attrs = ProductSkuAttrModel::bySkuId(array_unique(Arr::pluck($skus, 'id')))->select()->toArray(); + if (!empty($sku_attrs)) { + // 获取属性名称 + $attrs = ProductAttrModel::bypks(array_unique(Arr::pluck($sku_attrs, 'attr_id')))->column(['attr_name'], 'id'); + foreach ($sku_attrs as $v) { + $v['attr_name'] = $attrs[$v['attr_id']]?? ''; + // 按属性分组 + $product_sku_attrs[$v['attr_id']]['attr_id'] = $v['attr_id']; + $product_sku_attrs[$v['attr_id']]['attr_name'] = $v['attr_name']; + $product_sku_attrs[$v['attr_id']]['attr_values'][] = [ + 'sku_id' => $v['sku_id'], + 'attr_value' => $v['attr_value'], + ]; + } + $product_sku_attrs = array_values($product_sku_attrs); + } + } + + // 获取产品购买链接信息 + $product_purchase_links = ProductPurchaseLinkModel::with(['platform']) + ->field(['platform_id', 'link']) + ->byProductId($product['id']) + ->order(['sort' => 'asc', 'id' => 'desc']) + ->select() + ->hidden(['platform']) + ->bindAttr('platform', ['platform_name' => 'platform']) + ->toArray(); + } + View::assign('product_categorys', $product_categorys); + View::assign('product_params', $product_params); + View::assign('product_skus', $product_skus); + View::assign('product_sku_attrs', $product_sku_attrs); + View::assign('product_purchase_links', $product_purchase_links); + + // 获取询盘可选国家 + $config = $this->basic_config['optional_country_for_product_inquiry']; + View::assign('country_list', explode(',', preg_replace('/\r?\n/', ',', $config['value']?? ''))); + + return View::fetch('detail'); + } + + /** + * 产品询盘 + */ + public function inquiry() + { + $post = request()->post([ + 'first_name', + 'last_name', + 'email', + 'phone', + 'country_name', + 'corp_name', + 'industry', + 'message' + ]); + + // 输出校验 + $validate = new ProductInquiryValidate; + if (!$validate->check($post)) { + return error($validate->getError()); + } + + // 保存询盘信息 + $ret = ProductInquiryModel::create([ + 'language_id' => $this->lang_id, + 'first_name' => $post['first_name'], + 'last_name' => $post['last_name'], + 'email' => $post['email'], + 'phone' => $post['phone'], + 'country_name' => $post['country_name'], + 'corp_name' => $post['corp_name'], + 'industry' => $post['industry'], + 'referer_url' => request()->header('referer'), + 'message' => $post['message'], + ]); + if ($ret->isEmpty()) { + return error(lang('product_detail.send_fail')); + } + return success(lang('product_detail.send_success')); + } + + /** + * 新品上市 + */ + public function newpro() + { + $focus_image = []; + // 获取焦点图 + $banner = SysBannerModel::with(['items' => function($query) { + $query->withoutField([ + 'status', + 'created_at', + 'updated_at', + 'deleted_at' + ]) + ->where('status', '=', 1) + ->order(['sort' => 'asc', 'id' => 'desc']); + }]) + ->uniqueLabel(['BANNER_680dd7ceaa529']) + ->language($this->lang_id) + ->enabled(true) + ->select(); + if (!$banner->isEmpty()) { + $focus_image = $banner->first()?->items ?? []; + } + View::assign('focus_image', $focus_image); + + $newpros = []; + // 获取新品上市产品 + $products = ProductModel::field(['id', 'category_id', 'name', 'spu', 'cover_image']) + ->language($this->lang_id) + ->enabled(true) + ->onSale(true) + ->onShelves(true) + ->isNew(true) + ->order(['sort' => 'asc', 'id' => 'desc']) + ->select(); + if ($products->isEmpty()) { + return []; + } + + // 按分类分组产品 + $products_map = []; + foreach ($products as $product) { + $products_map[$product['category_id']][] = $product; + } + // 获取产品分类信息 + $categorys = ProductCategoryModel::field(['id', 'name']) + ->byPks(array_keys($products_map)) + ->language($this->lang_id) + ->displayed(true) + ->order(['sort' => 'asc', 'id' => 'desc']) + ->select(); + if (!$categorys->isEmpty()) { + foreach ($categorys as $category) { + $newpros[] = [ + 'category' => $category, + 'products' => $products_map[$category['id']] ?? [], + ]; + } + } + View::assign('newpros', $newpros); + + return View::fetch('newpro'); + } +} diff --git a/app/index/model/ProductModel.php b/app/index/model/ProductModel.php index 2bf9fee7..b3d8ee9b 100644 --- a/app/index/model/ProductModel.php +++ b/app/index/model/ProductModel.php @@ -46,4 +46,10 @@ class ProductModel extends ProductBaseModel { $query->where('is_hot', '=', (int)$stat); } + + // 新品状态范围查询 + public function scopeIsNew($query, bool $stat = true) + { + $query->where('is_new', '=', (int)$stat); + } } diff --git a/app/index/route/route.php b/app/index/route/route.php index 891c12da..11ba8770 100644 --- a/app/index/route/route.php +++ b/app/index/route/route.php @@ -18,6 +18,10 @@ Route::group('product', function() { Route::get('index/:id', 'Product/index')->name('product_index'); // 产品详情页 Route::get('detail/:id', 'Product/detail')->name('product_detail'); + // 产品询盘 + Route::post('inquiry', 'Product/inquiry'); + // 新品上市 + Route::get('newpro', 'Product/newpro'); // 产品搜索页 Route::get('search', 'Product/search'); }); diff --git a/app/index/view/product/detail.html b/app/index/view/product/detail.html new file mode 100644 index 00000000..75817f73 --- /dev/null +++ b/app/index/view/product/detail.html @@ -0,0 +1,273 @@ +{extend name="public/base" /} +{block name="title"} + {notempty name="product.seo_title"}{$product.seo_title}{else /}{__BLOCK__}{/notempty} +{/block} +{block name="seo"} + {notempty name=""} + + + {else/} + {__BLOCK__} + {/notempty} +{/block} +{block name="style"} + +{/block} +{block name="main"} +
+ +
+ +
+ 首页 + {volist name="product_categorys" id="ca"} +
+ {$ca.name} + {/volist} +
+ +
+ +
+ {if condition="!empty($product.video_img) && !empty($product.video_url)"} +
+
+ +
+
    +
  • +
+
+
+ +
+ +
+
+ {/if} + {volist name="product_skus" id="sku" key="idx"} + {if condition="!empty($product.video_img) && !empty($product.video_url)"} + + +
+
+

{$product.name|default=''}

+

{$product.desc|default=''}

+
+
    + {volist name="product_params" id="pp"} +
  • +
    {$pp.name}
    +
    +
    {$pp.value}
    +
  • + {/volist} +
+
+ + {volist name="product_sku_attrs" id="ps"} +
+
{$ps.attr_name}
+ +
+ {/volist} +
+ +
+ {volist name="product_purchase_links" id="ppp"} + {$ppp.platform_name} + {/volist} + {:lang('product_detail.display_form')} +
+
+
+ +
+
{:lang('product_detail.detail_section_title')}
+ +
+ {$product.detail|default=''|raw} +
+
+ +
+
+ × +

{:lang('product_detail.display_form')}

+
+
+
+ +
+ + +
+
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ +
+
+
+
+
+{/block} +{block name="script"} + +{/block} \ No newline at end of file diff --git a/app/index/view/product/newpro.html b/app/index/view/product/newpro.html new file mode 100644 index 00000000..a9cd4862 --- /dev/null +++ b/app/index/view/product/newpro.html @@ -0,0 +1,51 @@ +{extend name="public/base" /} +{block name="style"} + +{/block} +{block name="main"} +
+ + +
+ {volist name="focus_image" id="fo"} + + {/volist} +
+ {volist name="newpros" id="vo"} +
+

{$vo.category.name}

+
+ {volist name="vo.products" id="pro"} + + + +
{$pro.name}
+
{$pro.spu}
+
+ {/volist} +
+
+ {/volist} +
+{/block} +{block name="script"} + +{/block} \ No newline at end of file diff --git a/public/static/index/css/new_arrival.css b/public/static/index/css/product_newpro.css similarity index 97% rename from public/static/index/css/new_arrival.css rename to public/static/index/css/product_newpro.css index efe9499d..158d41c2 100755 --- a/public/static/index/css/new_arrival.css +++ b/public/static/index/css/product_newpro.css @@ -2,7 +2,7 @@ .orico_Page_newproducts { width: 100%; position: relative; - height: 100vh; + min-height: 100vh; overflow-y: auto; overflow-x: hidden; background: #f9f9f9; @@ -25,7 +25,7 @@ object-fit: cover; transition: opacity 1s ease-in-out; } -.orico_Page_newproducts .opdbannerImg:first-child { +.orico_Page_newproducts .opdbanner>a:first-child { display: block; } .orico_Page_newproducts .oricoNewPrMain {