From a1c7ae62ba7d00f06e7b2b223eb29da40716b523 Mon Sep 17 00:00:00 2001
From: jsasg <735273025@qq.com>
Date: Fri, 27 Mar 2026 14:03:14 +0800
Subject: [PATCH 01/58] =?UTF-8?q?feat:=20=E4=BA=A7=E5=93=81=E5=88=86?=
=?UTF-8?q?=E7=B1=BB=E6=8E=A8=E8=8D=90=E6=95=B0=E6=8D=AE=E7=AE=A1=E7=90=86?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.gitignore | 3 +
.../v1/ProductCategoryRecommend.php | 200 ++++++++++++++++++
.../v1/ProductCategoryRecommendModel.php | 47 ++++
app/admin/route/v1.php | 22 +-
.../v1/ProductCategoryRecommendValidate.php | 63 ++++++
app/admin/validate/v1/ProductValidate.php | 2 -
.../ProductCategoryRecommendBaseModel.php | 33 +++
...3640_create_product_category_recommend.php | 50 +++++
8 files changed, 416 insertions(+), 4 deletions(-)
create mode 100644 app/admin/controller/v1/ProductCategoryRecommend.php
create mode 100644 app/admin/model/v1/ProductCategoryRecommendModel.php
create mode 100644 app/admin/validate/v1/ProductCategoryRecommendValidate.php
create mode 100644 app/common/model/ProductCategoryRecommendBaseModel.php
create mode 100644 database/migrations/20260326073640_create_product_category_recommend.php
diff --git a/.gitignore b/.gitignore
index 8c9279ce..1ee66503 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,3 +23,6 @@ public/.well-known
/.settings
/.buildpath
/.project
+
+CLAUDE.md
+/skills
diff --git a/app/admin/controller/v1/ProductCategoryRecommend.php b/app/admin/controller/v1/ProductCategoryRecommend.php
new file mode 100644
index 00000000..2eae9dd0
--- /dev/null
+++ b/app/admin/controller/v1/ProductCategoryRecommend.php
@@ -0,0 +1,200 @@
+server();
+ $image_host = $server['REQUEST_SCHEME'] . "://" . $server['SERVER_NAME'] . '/';
+ $param = request()->get([
+ 'keywords',
+ 'page/d' => 1,
+ 'size/d' => 10
+ ]);
+
+ // 查询数据
+ $data = ProductCategoryRecommendModel::with(['category' => function($query) {
+ $query->field(['id', 'name']);
+ }])
+ ->withoutField(['language_id', 'updated_at', 'deleted_at'])
+ ->withSearch(['keywords'], ['keywords' => $param['keywords']??null])
+ ->language(request()->lang_id)
+ ->order(['sort' => 'asc', 'id' => 'desc'])
+ ->paginate([
+ 'list_rows' => $param['size'],
+ 'page' => $param['page'],
+ ])
+ ->bindAttr('category', ['category_name' => 'name'])
+ ->hidden(['category'])
+ ?->each(function($item) use($image_host) {
+ // 拼接完整图片URL
+ if (!empty($item['image'])) {
+ $item['image'] = url_join($image_host, $item['image']);
+ }
+ });
+
+ return success('获取成功', $data);
+ }
+
+ /**
+ * 导出Excel
+ */
+ public function export()
+ {
+ $schema = [
+ 'id' => 'ID',
+ 'image' => '图片',
+ 'category_name' => '分类名称',
+ 'desc' => '产品介绍',
+ 'link' => '链接地址',
+ 'sort' => '排序',
+ 'disabled' => '状态',
+ 'created_at' => '添加时间'
+ ];
+
+ // 获取导出数据
+ $data = $this->getProductCategoryRecommendData();
+
+ // 导出
+ return xlsx_writer($data, $schema, '产品列表' . date('YmdHis'));
+ }
+ // 获取要导出的推荐记录数据
+ private function getProductCategoryRecommendData()
+ {
+ $server = request()->server();
+ $image_host = $server['REQUEST_SCHEME'] . "://" . $server['SERVER_NAME'] . '/';
+ $param = request()->get(['keywords']);
+
+ // 查询数据
+ return ProductCategoryRecommendModel::with(['category' => function($query) {
+ $query->field(['id', 'name']);
+ }])
+ ->withoutField(['language_id', 'updated_at', 'deleted_at'])
+ ->withSearch(['keywords'], ['keywords' => $param['keywords']??null])
+ ->language(request()->lang_id)
+ ->order(['sort' => 'asc', 'id' => 'desc'])
+ ->select()
+ ->each(function($item) use($image_host) {
+ // 拼接完整图片URL
+ if (!empty($item['image'])) {
+ $item['image'] = url_join($image_host, $item['image']);
+ }
+ // 状态
+ $item['disabled'] = $item['disabled'] == 1 ? '禁用' : '启用';
+ })
+ ->toArray();
+ }
+
+ /**
+ * 获取详细数据
+ */
+ public function read()
+ {
+ $id = request()->param('id/d');
+ $record = ProductCategoryRecommendModel::bypk($id)
+ ->withoutField(['language_id', 'created_at', 'updated_at', 'deleted_at'])
+ ->find();
+ if (empty($record)) {
+ return error('推荐数据不存在');
+ }
+
+ return success('success', $record);
+ }
+
+ /**
+ * 新增数据
+ */
+ public function save()
+ {
+ $post = request()->post([
+ 'category_id',
+ 'title',
+ 'image',
+ 'desc',
+ 'link',
+ 'sort',
+ 'disabled'
+ ]);
+ $data = array_merge($post, ['language_id' => request()->lang_id]);
+
+ // 参数校验
+ $validate = new ProductCategoryRecommendValidate();
+ if (!$validate->scene('create')->check($data)) {
+ return error($validate->getError());
+ }
+
+ // 保存推荐数据
+ $recommend = ProductCategoryRecommendModel::create($data);
+ if ($recommend->isEmpty()) {
+ return error('保存失败');
+ }
+ return success('保存成功');
+ }
+
+ /**
+ * 更新数据
+ */
+ public function update()
+ {
+ $id = request()->param('id/d');
+ $post = request()->post([
+ 'category_id',
+ 'title',
+ 'image',
+ 'desc',
+ 'link',
+ 'sort',
+ 'disabled'
+ ]);
+ $data = array_merge($post, ['language_id' => request()->lang_id]);
+
+ // 参数校验
+ $validate = new ProductCategoryRecommendValidate();
+ $check_data = array_merge($data, ['id' => $id]);
+ if (!$validate->scene('update')->check($check_data)) {
+ return error($validate->getError());
+ }
+
+ // 更新推荐数据
+ $recommend = ProductCategoryRecommendModel::bypk($id)->find();
+ if (empty($recommend)) {
+ return error('请确认操作对象是否存在');
+ }
+ if (!$recommend->save($data)) {
+ return error('操作失败');
+ }
+
+ return success('操作成功');
+ }
+
+ /**
+ * 删除
+ */
+ public function delete()
+ {
+ $id = request()->param('id/d');
+
+ // 删除推荐记录数据
+ $record = ProductCategoryRecommendModel::bypk($id)->find();
+ if (empty($record)) {
+ return error('请确认操作对象是否正确');
+ }
+ if (!$record->delete()) {
+ return error('操作失败');
+ }
+
+ return success('操作成功');
+ }
+}
diff --git a/app/admin/model/v1/ProductCategoryRecommendModel.php b/app/admin/model/v1/ProductCategoryRecommendModel.php
new file mode 100644
index 00000000..507681b0
--- /dev/null
+++ b/app/admin/model/v1/ProductCategoryRecommendModel.php
@@ -0,0 +1,47 @@
+belongsTo(\app\index\model\LanguageModel::class, 'language_id', 'id');
+ }
+
+ // 关联产品分类
+ public function category()
+ {
+ return $this->belongsTo(\app\index\model\ProductCategoryModel::class, 'category_id', 'id');
+ }
+
+ // 所属语言范围查询
+ public function scopeLanguage($query, $language)
+ {
+ $query->where('language_id', '=', $language);
+ }
+
+ // 关键词搜索
+ public function searchKeywordsAttr($query, string|null $keywords)
+ {
+ if (is_null($keywords)) {
+ return;
+ }
+ $query->where('title', 'like', "%{$keywords}%")
+ ->whereOr('desc', 'like', "%{$keywords}%");
+ }
+}
diff --git a/app/admin/route/v1.php b/app/admin/route/v1.php
index e2aa35f2..40f261ad 100644
--- a/app/admin/route/v1.php
+++ b/app/admin/route/v1.php
@@ -87,7 +87,7 @@ Route::group('v1', function () {
// 视频分类列表
Route::get('categorys', 'VideoCategory/list');
-
+
// 视频分类
Route::group('category', function () {
// 视频分类分页数据
@@ -311,6 +311,24 @@ Route::group('v1', function () {
// 分类删除
Route::delete('delete/:id', 'ProductCategory/delete');
+
+ // 产品分类推荐数据
+ Route::group('recommend', function () {
+ // 推荐数据分页列表
+ Route::get('index', 'ProductCategoryRecommend/index');
+
+ // 推荐数据详情
+ Route::get('read/:id', 'ProductCategoryRecommend/read');
+
+ // 推荐数据新增
+ Route::post('save', 'ProductCategoryRecommend/save');
+
+ // 推荐数据更新
+ Route::put('update/:id', 'ProductCategoryRecommend/update');
+
+ // 推荐数据删除
+ Route::delete('delete/:id', 'ProductCategoryRecommend/delete');
+ });
});
// 产品购买链接
@@ -483,7 +501,7 @@ Route::group('v1', function () {
// 分页
Route::get('index', 'Navigation/index');
-
+
// 导航详情
Route::get('read/:id', 'Navigation/read');
diff --git a/app/admin/validate/v1/ProductCategoryRecommendValidate.php b/app/admin/validate/v1/ProductCategoryRecommendValidate.php
new file mode 100644
index 00000000..6c0a947b
--- /dev/null
+++ b/app/admin/validate/v1/ProductCategoryRecommendValidate.php
@@ -0,0 +1,63 @@
+ ['规则1','规则2'...]
+ *
+ * @var array
+ */
+ protected $rule = [
+ 'language_id' => 'require|integer',
+ 'category_id' => 'require|integer',
+ 'title' => 'require|max:255',
+ 'image' => 'require|max:255',
+ 'desc' => 'require|max:255',
+ 'link' => 'max:500',
+ 'sort' => 'require|integer',
+ 'disabled' => 'in:0,1'
+ ];
+
+ /**
+ * 定义错误信息
+ * 格式:'字段名.规则名' => '错误信息'
+ *
+ * @var array
+ */
+ protected $message = [
+ 'id.require' => 'ID不能为空',
+ 'id.integer' => 'ID必须是整数',
+ 'language_id.require' => '语言ID不能为空',
+ 'language_id.integer' => '语言ID必须是整数',
+ 'category_id.require' => '分类ID不能为空',
+ 'category_id.integer' => '分类ID必须是整数',
+ 'title.require' => '标题不能为空',
+ 'title.max' => '标题长度不能超过:rule个字符',
+ 'image.require' => '图片不能为空',
+ 'image.max' => '图片长度不能超过:rule个字符',
+ 'desc.require' => '描述不能为空',
+ 'desc.max' => '描述长度不能超过:rule个字符',
+ 'link.max' => '链接长度不能超过:rule个字符',
+ 'sort.require' => '排序不能为空',
+ 'sort.integer' => '排序必须是整数',
+ 'disabled.in' => '禁用状态只能是0或1',
+ ];
+
+ // 新增场景
+ protected function sceneCreate()
+ {
+ return $this->remove('id', 'require|integer');
+ }
+
+ // 更新场景
+ protected function sceneUpdate()
+ {
+ return $this->append('id', 'require|integer');
+ }
+}
diff --git a/app/admin/validate/v1/ProductValidate.php b/app/admin/validate/v1/ProductValidate.php
index 1a1f2d30..0431ba9c 100644
--- a/app/admin/validate/v1/ProductValidate.php
+++ b/app/admin/validate/v1/ProductValidate.php
@@ -5,8 +5,6 @@ namespace app\admin\validate\v1;
use think\Validate;
-use function PHPSTORM_META\type;
-
class ProductValidate extends Validate
{
/**
diff --git a/app/common/model/ProductCategoryRecommendBaseModel.php b/app/common/model/ProductCategoryRecommendBaseModel.php
new file mode 100644
index 00000000..13dff1ac
--- /dev/null
+++ b/app/common/model/ProductCategoryRecommendBaseModel.php
@@ -0,0 +1,33 @@
+ 'int',
+ 'language_id' => 'int',
+ 'category_id' => 'int',
+ 'title' => 'string',
+ 'image' => 'string',
+ 'desc' => 'string',
+ 'link' => 'string',
+ 'sort' => 'int',
+ 'disabled' => 'int',
+ 'created_at' => 'datetime',
+ 'updated_at' => 'datetime',
+ 'deleted_at' => 'datetime'
+ ];
+}
diff --git a/database/migrations/20260326073640_create_product_category_recommend.php b/database/migrations/20260326073640_create_product_category_recommend.php
new file mode 100644
index 00000000..a93bb986
--- /dev/null
+++ b/database/migrations/20260326073640_create_product_category_recommend.php
@@ -0,0 +1,50 @@
+table('product_category_recommend', [
+ 'engine' => 'MyISAM',
+ 'collation' => 'utf8mb4_general_ci',
+ 'comment' => '产品分类推荐表'
+ ]);
+
+ $table->addColumn('language_id', 'integer', ['signed' => false, 'null' => false, 'comment' => '语言ID'])
+ ->addColumn('category_id', 'integer', ['signed' => false, 'null' => false, 'comment' => '分类ID'])
+ ->addColumn('title', 'string', ['limit' => 255, 'comment' => '标题'])
+ ->addColumn('image', 'string', ['limit' => 255, 'default' => '', 'comment' => '图片'])
+ ->addColumn('desc', 'string', ['limit' => 255, 'comment' => '描述'])
+ ->addColumn('link', 'string', ['limit' => 500, 'default' => '', 'comment' => '外链地址'])
+ ->addColumn('sort', 'integer', ['default' => 0, 'comment' => '排序'])
+ ->addColumn('disabled', 'boolean', ['default' => 0, 'comment' => '是否禁用 0:启用 1:禁用'])
+ ->addColumn('created_at', 'timestamp', ["null" => false,'default' => 'CURRENT_TIMESTAMP', 'comment' => '创建时间'])
+ ->addColumn('updated_at', 'timestamp', ["null" => false,'default' => 'CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP', 'comment' => '更新时间'])
+ ->addColumn('deleted_at', 'timestamp', ['null' => true, 'comment' => '删除时间'])
+ ->create();
+ }
+}
From 863fdda9a56edd02b391e9d5b6bbb761081bd6a3 Mon Sep 17 00:00:00 2001
From: jsasg <735273025@qq.com>
Date: Fri, 27 Mar 2026 16:31:29 +0800
Subject: [PATCH 02/58] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=AF=BC?=
=?UTF-8?q?=E5=87=BA=E6=8E=A5=E5=8F=A3=E8=B7=AF=E7=94=B1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
app/admin/controller/v1/ProductCategoryRecommend.php | 2 +-
app/admin/route/v1.php | 3 +++
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/app/admin/controller/v1/ProductCategoryRecommend.php b/app/admin/controller/v1/ProductCategoryRecommend.php
index 2eae9dd0..bd62f86f 100644
--- a/app/admin/controller/v1/ProductCategoryRecommend.php
+++ b/app/admin/controller/v1/ProductCategoryRecommend.php
@@ -68,7 +68,7 @@ class ProductCategoryRecommend
$data = $this->getProductCategoryRecommendData();
// 导出
- return xlsx_writer($data, $schema, '产品列表' . date('YmdHis'));
+ return xlsx_writer($data, $schema, '产品推荐列表' . date('YmdHis'));
}
// 获取要导出的推荐记录数据
private function getProductCategoryRecommendData()
diff --git a/app/admin/route/v1.php b/app/admin/route/v1.php
index 40f261ad..bae867d5 100644
--- a/app/admin/route/v1.php
+++ b/app/admin/route/v1.php
@@ -317,6 +317,9 @@ Route::group('v1', function () {
// 推荐数据分页列表
Route::get('index', 'ProductCategoryRecommend/index');
+ // 推荐数据导出
+ Route::get('export', 'ProductCategoryRecommend/export');
+
// 推荐数据详情
Route::get('read/:id', 'ProductCategoryRecommend/read');
From be26f2d75b4514bbc6c3caaf94c57d17513636ac Mon Sep 17 00:00:00 2001
From: jsasg <735273025@qq.com>
Date: Fri, 27 Mar 2026 17:43:41 +0800
Subject: [PATCH 03/58] =?UTF-8?q?fix:=20=E4=BA=A7=E5=93=81=E6=8E=A8?=
=?UTF-8?q?=E8=8D=90=E5=88=97=E8=A1=A8=E5=8F=8A=E5=AF=BC=E5=87=BA=E9=97=AE?=
=?UTF-8?q?=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../v1/ProductCategoryRecommend.php | 21 ++++++++++++-------
.../v1/ProductCategoryRecommendModel.php | 6 +++---
2 files changed, 16 insertions(+), 11 deletions(-)
diff --git a/app/admin/controller/v1/ProductCategoryRecommend.php b/app/admin/controller/v1/ProductCategoryRecommend.php
index bd62f86f..7d2d42ae 100644
--- a/app/admin/controller/v1/ProductCategoryRecommend.php
+++ b/app/admin/controller/v1/ProductCategoryRecommend.php
@@ -20,15 +20,17 @@ class ProductCategoryRecommend
$image_host = $server['REQUEST_SCHEME'] . "://" . $server['SERVER_NAME'] . '/';
$param = request()->get([
'keywords',
+ 'category_name',
'page/d' => 1,
'size/d' => 10
]);
// 查询数据
- $data = ProductCategoryRecommendModel::with(['category' => function($query) {
- $query->field(['id', 'name']);
+ $data = ProductCategoryRecommendModel::withJoin(['category' => function($query) use ($param) {
+ if (!empty($param['category_name'])) {
+ $query->where('category.name', 'like', '%' . $param['category_name'] . '%');
+ }
}])
- ->withoutField(['language_id', 'updated_at', 'deleted_at'])
->withSearch(['keywords'], ['keywords' => $param['keywords']??null])
->language(request()->lang_id)
->order(['sort' => 'asc', 'id' => 'desc'])
@@ -37,7 +39,7 @@ class ProductCategoryRecommend
'page' => $param['page'],
])
->bindAttr('category', ['category_name' => 'name'])
- ->hidden(['category'])
+ ->hidden(['category', 'language_id', 'updated_at', 'deleted_at'])
?->each(function($item) use($image_host) {
// 拼接完整图片URL
if (!empty($item['image'])) {
@@ -78,15 +80,18 @@ class ProductCategoryRecommend
$param = request()->get(['keywords']);
// 查询数据
- return ProductCategoryRecommendModel::with(['category' => function($query) {
- $query->field(['id', 'name']);
+ return ProductCategoryRecommendModel::withJoin(['category' => function($query) use ($param) {
+ if (!empty($param['category_name'])) {
+ $query->where('category.name', 'like', '%' . $param['category_name'] . '%');
+ }
}])
- ->withoutField(['language_id', 'updated_at', 'deleted_at'])
->withSearch(['keywords'], ['keywords' => $param['keywords']??null])
->language(request()->lang_id)
->order(['sort' => 'asc', 'id' => 'desc'])
->select()
- ->each(function($item) use($image_host) {
+ ->bindAttr('category', ['category_name' => 'name'])
+ ->hidden(['category', 'language_id', 'updated_at', 'deleted_at'])
+ ?->each(function($item) use($image_host) {
// 拼接完整图片URL
if (!empty($item['image'])) {
$item['image'] = url_join($image_host, $item['image']);
diff --git a/app/admin/model/v1/ProductCategoryRecommendModel.php b/app/admin/model/v1/ProductCategoryRecommendModel.php
index 507681b0..317bb968 100644
--- a/app/admin/model/v1/ProductCategoryRecommendModel.php
+++ b/app/admin/model/v1/ProductCategoryRecommendModel.php
@@ -32,7 +32,7 @@ class ProductCategoryRecommendModel extends ProductCategoryRecommendBaseModel
// 所属语言范围查询
public function scopeLanguage($query, $language)
{
- $query->where('language_id', '=', $language);
+ $query->where($this->getTable() . '.language_id', '=', $language);
}
// 关键词搜索
@@ -41,7 +41,7 @@ class ProductCategoryRecommendModel extends ProductCategoryRecommendBaseModel
if (is_null($keywords)) {
return;
}
- $query->where('title', 'like', "%{$keywords}%")
- ->whereOr('desc', 'like', "%{$keywords}%");
+ $query->where($this->getTable() . '.title', 'like', "%{$keywords}%")
+ ->whereOr($this->getTable() . '.desc', 'like', "%{$keywords}%");
}
}
From 8577877f83eaba12b6553eb49b07dc49dd887ee4 Mon Sep 17 00:00:00 2001
From: jsasg <735273025@qq.com>
Date: Fri, 27 Mar 2026 17:50:25 +0800
Subject: [PATCH 04/58] =?UTF-8?q?feat:=20banner=E5=88=86=E7=B1=BB=E6=B7=BB?=
=?UTF-8?q?=E5=8A=A0unique=5Flabel=E5=AD=97=E6=AE=B5=E7=AE=A1=E7=90=86?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
app/admin/controller/v1/Banner.php | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/admin/controller/v1/Banner.php b/app/admin/controller/v1/Banner.php
index a28a041f..52ac5edc 100644
--- a/app/admin/controller/v1/Banner.php
+++ b/app/admin/controller/v1/Banner.php
@@ -91,7 +91,6 @@ class Banner
$banner = SysBannerModel::withoutField([
'at_page',
- 'unique_label',
'language_id',
'created_at',
'updated_at',
@@ -142,10 +141,11 @@ class Banner
'name',
'desc',
'recommend',
+ 'unique_label',
'at_platform' => 'pc',
'status' => 1
]);
-
+
$validate = new SysBannerValidate;
if (!$validate->scene('edit')->check(array_merge($put, ['id' => $id]))) {
return error($validate->getError());
From 9584b817298ff84205ee27610b221ea4608e9e0c Mon Sep 17 00:00:00 2001
From: jsasg <735273025@qq.com>
Date: Fri, 27 Mar 2026 17:59:08 +0800
Subject: [PATCH 05/58] refactor: banner|navigation
---
app/admin/controller/v1/NavigationItem.php | 8 +++++++-
.../validate/v1/NavigationItemValidate.php | 18 +++++++++++-------
.../model/SysNavigationItemBaseModel.php | 2 ++
...241230093811_create_sys_navigation_item.php | 2 ++
4 files changed, 22 insertions(+), 8 deletions(-)
diff --git a/app/admin/controller/v1/NavigationItem.php b/app/admin/controller/v1/NavigationItem.php
index 3875d7f7..fa63be15 100644
--- a/app/admin/controller/v1/NavigationItem.php
+++ b/app/admin/controller/v1/NavigationItem.php
@@ -67,6 +67,8 @@ class NavigationItem
'id',
'pid',
'name',
+ 'desc',
+ 'image',
'nav_id',
'sort',
'status',
@@ -93,7 +95,9 @@ class NavigationItem
'pid',
'nav_id',
'name',
+ 'desc',
'icon',
+ 'image',
'link_to' => 'custom',
'link',
'sort',
@@ -121,7 +125,9 @@ class NavigationItem
'pid',
'nav_id',
'name',
+ 'desc',
'icon',
+ 'image',
'link_to',
'link',
'sort',
@@ -172,7 +178,7 @@ class NavigationItem
if (empty($nav)) {
return error('请确认要操作对象是否存在');
}
-
+
if (!$nav->delete()) {
return error('操作失败');
}
diff --git a/app/admin/validate/v1/NavigationItemValidate.php b/app/admin/validate/v1/NavigationItemValidate.php
index ee3e7c7e..103f2fa3 100644
--- a/app/admin/validate/v1/NavigationItemValidate.php
+++ b/app/admin/validate/v1/NavigationItemValidate.php
@@ -20,7 +20,9 @@ class NavigationItemValidate extends Validate
'nav_id' => 'require|integer',
'pid' => 'integer|different:id|checkPidNotBeChildren',
'name' => 'require|max:64',
+ 'desc' => 'max:255',
'icon' => 'max:64',
+ 'image' => 'max:255',
'link_to' => 'require|max:64|in:article,article_category,product,product_category,system_page,custom',
'link' => 'max:255',
'sort' => 'integer',
@@ -44,7 +46,9 @@ class NavigationItemValidate extends Validate
'pid.checkPidNotBeChildren' => '父级ID不能为自身的子导航',
'name.require' => '导航名称不能为空',
'name.max' => '导航名称最多不能超过64个字符',
+ 'desc.max' => '导航名称最多不能超过:rule个字符',
'icon.max' => '图标最多不能超过64个字符',
+ 'image.max' => '图标最多不能超过:rule个字符',
'link_to.require' => '链接类型不能为空',
'link_to.max' => '链接类型最多不能超过64个字符',
'link_to.in' => '链接类型必须是article,article_category,product_category,product,system_page,custom中之一',
@@ -67,7 +71,7 @@ class NavigationItemValidate extends Validate
if (env('DB_VERSION', '5') == '8') {
$children = Db::query(
preg_replace(
- '/\s+/u',
+ '/\s+/u',
' ',
"WITH RECURSIVE tree_by AS (
SELECT a.id, a.pid FROM $table_name a WHERE a.id = {$data['id']}
@@ -80,13 +84,13 @@ class NavigationItemValidate extends Validate
} else {
$children = \think\facade\Db::query("
SELECT t2.id
- FROM (
- SELECT
+ FROM (
+ SELECT
@r AS _id, (SELECT @r := GROUP_CONCAT(id) FROM $table_name WHERE FIND_IN_SET(pid, _id)) AS parent_id
- FROM
- (SELECT @r := {$data['id']}) vars, $table_name h
- WHERE @r <> 0) t1
- JOIN $table_name t2
+ FROM
+ (SELECT @r := {$data['id']}) vars, $table_name h
+ WHERE @r <> 0) t1
+ JOIN $table_name t2
ON FIND_IN_SET(t2.pid, t1._id)
ORDER BY t2.id;
");
diff --git a/app/common/model/SysNavigationItemBaseModel.php b/app/common/model/SysNavigationItemBaseModel.php
index db3e7597..824604c6 100644
--- a/app/common/model/SysNavigationItemBaseModel.php
+++ b/app/common/model/SysNavigationItemBaseModel.php
@@ -21,7 +21,9 @@ class SysNavigationItemBaseModel extends BaseModel
'nav_id' => 'int',
'pid' => 'int',
'name' => 'string',
+ 'desc' => 'string',
'icon' => 'string',
+ 'image' => 'string',
'link_to' => 'string',
'link' => 'string',
'sort' => 'int',
diff --git a/database/migrations/20241230093811_create_sys_navigation_item.php b/database/migrations/20241230093811_create_sys_navigation_item.php
index 81c193f3..00a8124c 100644
--- a/database/migrations/20241230093811_create_sys_navigation_item.php
+++ b/database/migrations/20241230093811_create_sys_navigation_item.php
@@ -31,7 +31,9 @@ class CreateSysNavigationItem extends Migrator
$table->addColumn('nav_id', 'string', ['limit' => 64, 'null' => false, 'comment' => '所属导航ID'])
->addColumn('pid', 'integer', ['null' => false, 'default' => 0, 'comment' => '父级ID'])
->addColumn('name', 'string', ['limit' => 64, 'null' => false, 'comment' => '名称'])
+ ->addColumn('desc', 'string', ['limit' => 255, 'null' => true, 'default' => null, 'comment' => '描述'])
->addColumn('icon', 'string', ['limit' => 64, 'null' => true, 'default' => null, 'comment' => '图标'])
+ ->addColumn('image', 'string', ['limit' => 255, 'null' => true, 'default' => null, 'comment' => '图片'])
->addColumn('link_to', 'string', ['limit' => 64, 'null' => true, 'default' => null, 'comment' => '链接到(类型): article:文章, article_category:文章分类, product:产品, product_category:产品分类, custom:自定义链接'])
->addColumn('link', 'string', ['limit' => 255, 'null' => true, 'default' => null, 'comment' => '链接'])
->addColumn('sort', 'integer', ['limit' => 11, 'null' => false, 'default' => 0, 'comment' => '排序'])
From ebe1c3015ef8ced5fa50055b2e8df653608b4d5b Mon Sep 17 00:00:00 2001
From: jsasg <735273025@qq.com>
Date: Sat, 28 Mar 2026 09:13:48 +0800
Subject: [PATCH 06/58] =?UTF-8?q?fix:=20=E5=8E=BB=E9=99=A4banner=E4=B8=8D?=
=?UTF-8?q?=E5=85=81=E8=AE=B8=E6=9B=B4=E6=96=B0unique=5Flabel=E9=99=90?=
=?UTF-8?q?=E5=88=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
app/admin/validate/v1/SysBannerValidate.php | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/app/admin/validate/v1/SysBannerValidate.php b/app/admin/validate/v1/SysBannerValidate.php
index 9af8c9a8..584134a4 100644
--- a/app/admin/validate/v1/SysBannerValidate.php
+++ b/app/admin/validate/v1/SysBannerValidate.php
@@ -43,7 +43,6 @@ class SysBannerValidate extends BaseValidate
'at_platform.in' => '显示端口只能是pc或mobile',
'at_page.max' => '页面位置最多255个字符',
'unique_label.max' => '唯一标识最多64个字符',
- 'unique_label.mustOmit' => '更新时不能有unique_label字段',
'name.require' => '名称不能为空',
'name.max' => '名称最多64个字符',
'desc.max' => '描述最多255个字符',
@@ -61,6 +60,6 @@ class SysBannerValidate extends BaseValidate
// 编辑场景
public function sceneEdit()
{
- return $this->remove('language_id', 'require|integer')->append('unique_label', 'mustOmit');
+ return $this->remove('language_id', 'require|integer');
}
}
From 4c592d934704a1a7721471a26f801b1c9de56de4 Mon Sep 17 00:00:00 2001
From: jsasg <735273025@qq.com>
Date: Sat, 28 Mar 2026 11:04:10 +0800
Subject: [PATCH 07/58] =?UTF-8?q?refactor:=20=E4=BA=A7=E5=93=81=E5=88=86?=
=?UTF-8?q?=E7=B1=BB=E6=8E=A8=E8=8D=90=E5=88=97=E8=A1=A8=E5=9B=BE=E7=89=87?=
=?UTF-8?q?=E6=94=B9=E4=B8=BA=E7=BC=A9=E7=95=A5=E5=9B=BE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
app/admin/controller/v1/ProductCategoryRecommend.php | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/app/admin/controller/v1/ProductCategoryRecommend.php b/app/admin/controller/v1/ProductCategoryRecommend.php
index 7d2d42ae..3a1581de 100644
--- a/app/admin/controller/v1/ProductCategoryRecommend.php
+++ b/app/admin/controller/v1/ProductCategoryRecommend.php
@@ -16,8 +16,6 @@ class ProductCategoryRecommend
*/
public function index()
{
- $server = request()->server();
- $image_host = $server['REQUEST_SCHEME'] . "://" . $server['SERVER_NAME'] . '/';
$param = request()->get([
'keywords',
'category_name',
@@ -40,10 +38,10 @@ class ProductCategoryRecommend
])
->bindAttr('category', ['category_name' => 'name'])
->hidden(['category', 'language_id', 'updated_at', 'deleted_at'])
- ?->each(function($item) use($image_host) {
- // 拼接完整图片URL
+ ?->each(function($item) {
+ // 列表页面图片输出缩略图
if (!empty($item['image'])) {
- $item['image'] = url_join($image_host, $item['image']);
+ $item['image'] = thumb($item['image']);
}
});
From 90b4bccbcf2f8441e5ad210c43901ffc6ecc072f Mon Sep 17 00:00:00 2001
From: jsasg <735273025@qq.com>
Date: Sat, 28 Mar 2026 11:37:26 +0800
Subject: [PATCH 08/58] =?UTF-8?q?feat:=20=E7=B3=BB=E7=BB=9F=E5=95=86?=
=?UTF-8?q?=E5=9F=8E=E5=BA=97=E9=93=BA=E5=85=A5=E5=8F=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../controller/v1/SysMallStoreEntrance.php | 201 ++++++++++++++++++
.../model/v1/SysMallStoreEntranceModel.php | 61 ++++++
app/admin/route/v1.php | 23 ++
.../v1/SysMallStoreEntranceValidate.php | 60 ++++++
.../model/SysMallStoreEntranceBaseModel.php | 32 +++
...20260328021117_sys_mall_store_entrance.php | 50 +++++
6 files changed, 427 insertions(+)
create mode 100644 app/admin/controller/v1/SysMallStoreEntrance.php
create mode 100644 app/admin/model/v1/SysMallStoreEntranceModel.php
create mode 100644 app/admin/validate/v1/SysMallStoreEntranceValidate.php
create mode 100644 app/common/model/SysMallStoreEntranceBaseModel.php
create mode 100644 database/migrations/20260328021117_sys_mall_store_entrance.php
diff --git a/app/admin/controller/v1/SysMallStoreEntrance.php b/app/admin/controller/v1/SysMallStoreEntrance.php
new file mode 100644
index 00000000..94f38910
--- /dev/null
+++ b/app/admin/controller/v1/SysMallStoreEntrance.php
@@ -0,0 +1,201 @@
+get([
+ 'name',
+ 'page/d' => 1,
+ 'size/d' => 10
+ ]);
+
+ // 查询数据
+ $data = SysMallStoreEntranceModel::withoutField([
+ 'language_id',
+ 'hover_image',
+ 'updated_at',
+ 'deleted_at'
+ ])
+ ->withSearch(['name'], ['name' => $param['name']??null])
+ ->language(request()->lang_id)
+ ->order(['sort' => 'asc', 'id' => 'desc'])
+ ->paginate([
+ 'list_rows' => $param['size'],
+ 'page' => $param['page'],
+ ])
+ ?->each(function($item) {
+ // 列表页面图片输出缩略图
+ if (!empty($item['image'])) {
+ $item['image'] = thumb($item['image']);
+ }
+ });
+
+ return success('获取成功', $data);
+ }
+
+ /**
+ * 获取详情
+ */
+ public function read()
+ {
+ $id = request()->param('id/d');
+ $record = SysMallStoreEntranceModel::bypk($id)
+ ->withoutField(['language_id', 'created_at', 'updated_at', 'deleted_at'])
+ ->find();
+ if (empty($record)) {
+ return error('商城店铺入口数据不存在');
+ }
+
+ return success('success', $record);
+ }
+
+ /**
+ * 新增数据
+ */
+ public function save()
+ {
+ $post = request()->post([
+ 'name',
+ 'image',
+ 'hover_image',
+ 'link',
+ 'sort',
+ 'disabled'
+ ]);
+ $data = array_merge($post, ['language_id' => request()->lang_id]);
+
+ // 参数校验
+ $validate = new SysMallStoreEntranceValidate();
+ if (!$validate->scene('create')->check($data)) {
+ return error($validate->getError());
+ }
+
+ // 保存数据
+ $entrance = SysMallStoreEntranceModel::create($data);
+ if ($entrance->isEmpty()) {
+ return error('保存失败');
+ }
+ return success('保存成功');
+ }
+
+ /**
+ * 更新数据
+ */
+ public function update()
+ {
+ $id = request()->param('id/d');
+ $post = request()->post([
+ 'name',
+ 'image',
+ 'hover_image',
+ 'link',
+ 'sort',
+ 'disabled'
+ ]);
+ $data = array_merge($post, ['language_id' => request()->lang_id]);
+
+ // 参数校验
+ $validate = new SysMallStoreEntranceValidate();
+ $check_data = array_merge($data, ['id' => $id]);
+ if (!$validate->scene('update')->check($check_data)) {
+ return error($validate->getError());
+ }
+
+ // 更新数据
+ $entrance = SysMallStoreEntranceModel::bypk($id)->find();
+ if (empty($entrance)) {
+ return error('请确认操作对象是否存在');
+ }
+ if (!$entrance->save($data)) {
+ return error('操作失败');
+ }
+
+ return success('操作成功');
+ }
+
+ /**
+ * 删除
+ */
+ public function delete()
+ {
+ $id = request()->param('id/d');
+
+ // 删除数据
+ $record = SysMallStoreEntranceModel::bypk($id)->find();
+ if (empty($record)) {
+ return error('请确认操作对象是否正确');
+ }
+ if (!$record->delete()) {
+ return error('操作失败');
+ }
+
+ return success('操作成功');
+ }
+
+ /**
+ * 导出Excel
+ */
+ public function export()
+ {
+ $schema = [
+ 'id' => 'ID',
+ 'image' => '图片',
+ 'hover_image' => '悬浮图',
+ 'name' => '名称',
+ 'link' => '链接地址',
+ 'sort' => '排序',
+ 'disabled' => '状态',
+ 'created_at' => '添加时间'
+ ];
+
+ // 获取导出数据
+ $data = $this->getExportData();
+
+ // 导出
+ return xlsx_writer($data, $schema, '系统商城店铺入口列表' . date('YmdHis'));
+ }
+
+ // 获取要导出的数据
+ private function getExportData()
+ {
+ $server = request()->server();
+ $image_host = $server['REQUEST_SCHEME'] . "://" . $server['SERVER_NAME'] . '/';
+ $param = request()->get(['name']);
+
+ // 查询数据
+ return SysMallStoreEntranceModel::withoutField([
+ 'language_id',
+ 'updated_at',
+ 'deleted_at'
+ ])
+ ->withSearch(['name'], ['name' => $param['name']??null])
+ ->language(request()->lang_id)
+ ->order(['sort' => 'asc', 'id' => 'desc'])
+ ->select()
+ ?->each(function($item) use($image_host) {
+ // 拼接完整图片URL
+ if (!empty($item['image'])) {
+ $item['image'] = url_join($image_host, $item['image']);
+ }
+ if (!empty($item['hover_image'])) {
+ $item['hover_image'] = url_join($image_host, $item['hover_image']);
+ }
+ // 状态转换
+ $item['disabled'] = $item['disabled'] == 1 ? '禁用' : '启用';
+ })
+ ->toArray();
+ }
+}
diff --git a/app/admin/model/v1/SysMallStoreEntranceModel.php b/app/admin/model/v1/SysMallStoreEntranceModel.php
new file mode 100644
index 00000000..f1cd1a81
--- /dev/null
+++ b/app/admin/model/v1/SysMallStoreEntranceModel.php
@@ -0,0 +1,61 @@
+belongsTo(\app\index\model\LanguageModel::class, 'language_id', 'id');
+ }
+
+ // 所属语言范围查询
+ public function scopeLanguage($query, $language)
+ {
+ $query->where($this->getTable() . '.language_id', '=', $language);
+ }
+
+ // 查询启用状态
+ public function scopeEnabled($query)
+ {
+ $query->where('disabled', '=', 0);
+ }
+
+ // 查询禁用状态
+ public function scopeDisabled($query)
+ {
+ $query->where('disabled', '=', 1);
+ }
+
+ // 按名称搜索
+ public function searchNameAttr($query, $value, $data)
+ {
+ if (is_null($value)) {
+ return;
+ }
+ $query->where('name', 'like', "%{$value}%");
+ }
+
+ // 按链接地址搜索
+ public function searchLinkAttr($query, $value, $data)
+ {
+ if (is_null($value)) {
+ return;
+ }
+ $query->where('link_url', 'like', "%{$value}%");
+ }
+}
diff --git a/app/admin/route/v1.php b/app/admin/route/v1.php
index bae867d5..b8e56853 100644
--- a/app/admin/route/v1.php
+++ b/app/admin/route/v1.php
@@ -595,6 +595,29 @@ Route::group('v1', function () {
// 反馈管理 - 产品询盘列表
Route::get('product/inquiry/index', 'ProductInquiry/index');
+ // 系统商城店铺入口
+ Route::group('mall', function() {
+ Route::group('store', function() {
+ // 店铺入口列表分页
+ Route::get('index', 'SysMallStoreEntrance/index');
+
+ // 店铺入口导出
+ Route::get('export', 'SysMallStoreEntrance/export');
+
+ // 店铺入口详情
+ Route::get('read/:id', 'SysMallStoreEntrance/read');
+
+ // 店铺入口新增
+ Route::post('save', 'SysMallStoreEntrance/save');
+
+ // 店铺入口更新
+ Route::put('update/:id', 'SysMallStoreEntrance/update');
+
+ // 店铺入口删除
+ Route::delete('delete/:id', 'SysMallStoreEntrance/delete');
+ });
+ });
+
// 配置项列表
Route::group('config', function() {
// 配置分组
diff --git a/app/admin/validate/v1/SysMallStoreEntranceValidate.php b/app/admin/validate/v1/SysMallStoreEntranceValidate.php
new file mode 100644
index 00000000..31d8b33d
--- /dev/null
+++ b/app/admin/validate/v1/SysMallStoreEntranceValidate.php
@@ -0,0 +1,60 @@
+ ['规则1','规则2'...]
+ *
+ * @var array
+ */
+ protected $rule = [
+ 'id' => 'require|integer',
+ 'language_id' => 'require|integer',
+ 'name' => 'require|max:255',
+ 'image' => 'require|max:255',
+ 'hover_image' => 'max:255',
+ 'link' => 'max:500',
+ 'sort' => 'require|integer',
+ 'disabled' => 'in:0,1',
+ ];
+
+ /**
+ * 定义错误信息
+ * 格式:'字段名.规则名' => '错误信息'
+ *
+ * @var array
+ */
+ protected $message = [
+ 'id.require' => 'ID不能为空',
+ 'id.integer' => 'ID必须是整数',
+ 'language_id.require' => '语言ID不能为空',
+ 'language_id.integer' => '语言ID必须是整数',
+ 'name.require' => '商城名称不能为空',
+ 'name.max' => '商城名称长度不能超过:rule个字符',
+ 'image.require' => '图片不能为空',
+ 'image.max' => '图片长度不能超过:rule个字符',
+ 'hover_image.max' => '悬浮图长度不能超过:rule个字符',
+ 'link.max' => '链接地址长度不能超过:rule个字符',
+ 'sort.require' => '排序不能为空',
+ 'sort.integer' => '排序必须是整数',
+ 'disabled.in' => '禁用状态只能是0或1',
+ ];
+
+ // 新增场景
+ protected function sceneCreate()
+ {
+ return $this->remove('id', 'require|integer');
+ }
+
+ // 更新场景
+ protected function sceneUpdate()
+ {
+ return $this->append('id', 'require|integer');
+ }
+}
diff --git a/app/common/model/SysMallStoreEntranceBaseModel.php b/app/common/model/SysMallStoreEntranceBaseModel.php
new file mode 100644
index 00000000..7e4115c2
--- /dev/null
+++ b/app/common/model/SysMallStoreEntranceBaseModel.php
@@ -0,0 +1,32 @@
+ 'int',
+ 'language_id' => 'int',
+ 'name' => 'string',
+ 'image' => 'string',
+ 'hover_image' => 'string',
+ 'link' => 'string',
+ 'sort' => 'int',
+ 'disabled' => 'int',
+ 'created_at' => 'datetime',
+ 'updated_at' => 'datetime',
+ 'deleted_at' => 'datetime',
+ ];
+}
diff --git a/database/migrations/20260328021117_sys_mall_store_entrance.php b/database/migrations/20260328021117_sys_mall_store_entrance.php
new file mode 100644
index 00000000..4180738c
--- /dev/null
+++ b/database/migrations/20260328021117_sys_mall_store_entrance.php
@@ -0,0 +1,50 @@
+table('sys_mall_store_entrance', [
+ 'engine' => 'MyISAM',
+ 'collation' => 'utf8mb4_general_ci',
+ 'comment' => '系统商城店铺入口表'
+ ]);
+
+ $table->addColumn('language_id', 'integer', ['signed' => false, 'null' => false, 'comment' => '语言ID'])
+ ->addColumn('name', 'string', ['limit' => 255, 'null' => false, 'comment' => '商城名称'])
+ ->addColumn('image', 'string', ['limit' => 255, 'default' => '', 'comment' => '图片'])
+ ->addColumn('hover_image', 'string', ['limit' => 255, 'default' => '', 'comment' => '悬浮图'])
+ ->addColumn('link', 'string', ['limit' => 500, 'default' => '', 'comment' => '链接地址'])
+ ->addColumn('sort', 'integer', ['default' => 0, 'comment' => '排序'])
+ ->addColumn('disabled', 'boolean', ['default' => 0, 'comment' => '是否禁用 0:启用 1:禁用'])
+ ->addColumn('created_at', 'timestamp', ['null' => false, 'default' => 'CURRENT_TIMESTAMP', 'comment' => '创建时间'])
+ ->addColumn('updated_at', 'timestamp', ['null' => false, 'default' => 'CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP', 'comment' => '更新时间'])
+ ->addColumn('deleted_at', 'timestamp', ['null' => true, 'comment' => '删除时间'])
+ ->addIndex(['language_id'], ['name' => 'idx_language_id'])
+ ->create();
+ }
+}
From 8a9d66f5d3f0945b58ccbd02d4016a13d98afdda Mon Sep 17 00:00:00 2001
From: jsasg <735273025@qq.com>
Date: Sat, 28 Mar 2026 11:46:23 +0800
Subject: [PATCH 09/58] =?UTF-8?q?fix:=20=E4=BA=A7=E5=93=81=E8=AF=A6?=
=?UTF-8?q?=E6=83=85=E6=8E=A5=E5=8F=A3=E6=9C=AA=E6=89=BE=E5=88=B0=E4=BA=A7?=
=?UTF-8?q?=E5=93=81=E6=97=B6=E6=8A=A5=E9=94=99=E8=AF=AF=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
app/admin/controller/v1/Product.php | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/app/admin/controller/v1/Product.php b/app/admin/controller/v1/Product.php
index 3b635255..5914a8ed 100644
--- a/app/admin/controller/v1/Product.php
+++ b/app/admin/controller/v1/Product.php
@@ -24,7 +24,7 @@ class Product
'category_id',
'created_at',
'is_show',
- 'page/d' => 1,
+ 'page/d' => 1,
'size/d' => 10
]);
@@ -83,7 +83,7 @@ class Product
])
->bypk(request()->param('id'))
->find()
- ->bindAttr('category', ['category_name'])
+ ?->bindAttr('category', ['category_name'])
->hidden(['category']);
if (empty($product)) {
return error('产品不存在');
@@ -108,7 +108,7 @@ class Product
// 获取关联产品
$product->related = ProductRelatedModel::field([
- 'related_product_id',
+ 'related_product_id',
'sort'
])
->with(['product' => function($query) {
@@ -149,8 +149,8 @@ class Product
'seo_desc'
]);
$put = array_merge(
- $put,
- ['skus' => json_decode($put['skus'], true)],
+ $put,
+ ['skus' => json_decode($put['skus'], true)],
['related' => json_decode($put['related'], true)],
);
@@ -305,7 +305,7 @@ class Product
return success('操作成功');
}
-
+
// 导出
public function export()
{
@@ -444,7 +444,7 @@ class Product
});
}
}
-
+
return $products->toArray();
}
}
From 29abb5e23032cc1a703b5e6ef2fad34d9da6dd3e Mon Sep 17 00:00:00 2001
From: jsasg <735273025@qq.com>
Date: Mon, 30 Mar 2026 18:03:03 +0800
Subject: [PATCH 10/58] =?UTF-8?q?refactor:=20pc=E5=AF=BC=E8=88=AA=E9=87=8D?=
=?UTF-8?q?=E6=9E=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
app/index/controller/Common.php | 59 +-
app/index/lang/en-us/pc.php | 9 +-
app/index/model/ProductCategoryModel.php | 6 +
.../model/ProductCategoryRecommendModel.php | 25 +
app/index/model/SysMallStoreEntranceModel.php | 43 +
app/index/view/pc/public/20230331.header.html | 287 +++
app/index/view/pc/public/header.html | 1618 ++++++++++++++---
7 files changed, 1763 insertions(+), 284 deletions(-)
create mode 100644 app/index/model/ProductCategoryRecommendModel.php
create mode 100644 app/index/model/SysMallStoreEntranceModel.php
create mode 100644 app/index/view/pc/public/20230331.header.html
diff --git a/app/index/controller/Common.php b/app/index/controller/Common.php
index 703890c8..a4de17b2 100644
--- a/app/index/controller/Common.php
+++ b/app/index/controller/Common.php
@@ -8,6 +8,7 @@ use app\index\model\LanguageModel;
use app\index\model\ProductCategoryModel;
use app\index\model\ProductModel;
use app\index\model\SysConfigModel;
+use app\index\model\SysMallStoreEntranceModel;
use app\index\model\SysNavigationItemModel;
use think\facade\Lang;
use think\facade\View;
@@ -38,7 +39,7 @@ abstract class Common extends BaseController
}
// 获取产品分类
- $categorys = $this->getProductCategory($this->lang_id);
+ $categorys = $this->getProductCategory($this->lang_id, true);
// 输出产品分类
View::assign('header_categorys', $categorys);
@@ -47,6 +48,9 @@ abstract class Common extends BaseController
// 输出热销产品
View::assign('header_hot_products', $hot_products);
+ // 获取商品购买入口
+ View::assign("header_mall_entrance", $this->getMallStoreEntrance($this->lang_id));
+
// 输出顶部导航
View::assign('header_navigation', $this->getNavigation('NAV_67f3701f3e831', $this->lang_id));
@@ -78,7 +82,7 @@ abstract class Common extends BaseController
}
// 获取产品分类
- protected function getProductCategory($language = 1)
+ protected function getProductCategory($language = 1, $with_recommends = false)
{
$categorys = ProductCategoryModel::field([
'id',
@@ -87,15 +91,42 @@ abstract class Common extends BaseController
'icon',
'level'
])
+ ->when($with_recommends, function($query) {
+ $query->with(['recommends' => function($query) {
+ $query->field(['id', 'category_id', 'title', 'image', 'desc', 'link'])
+ ->order(['sort' => 'asc', 'id' => 'desc']);
+ }]);
+ })
->language($language)
->displayed()
->order(['sort' => 'asc', 'id' => 'desc'])
- ->select();
+ ->select()
+ ->hidden(["recommends.category_id"]);
if ($categorys->isEmpty()) {
return [];
}
- return array_to_tree($categorys->toArray(), 0, 'pid', 1, false);
+ return $this->toTreeAndChunk($categorys->toArray(), 0, 1);
+ }
+ private function toTreeAndChunk(array $categorys, int $pid, int|bool $level): array
+ {
+ $ret = [];
+ foreach ($categorys as $item) {
+ if ($item['pid'] == $pid) {
+ $lv = $level;
+ if ($level !== false) {
+ $item['level'] = $level;
+ $lv = $level + 1;
+ }
+ $children = $this->toTreeAndChunk($categorys, $item['id'], $lv);
+ if (!empty($children)) {
+ $item['children'] = $item['level'] == 1 ? array_chunk($children, 2) : $children;
+ }
+ $ret[] = $item;
+ }
+ }
+
+ return $ret;
}
// 获取顶部导航
@@ -164,6 +195,26 @@ abstract class Common extends BaseController
return $languages;
}
+ // 获取商品购买入口
+ private function getMallStoreEntrance($language = 1)
+ {
+ return SysMallStoreEntranceModel::field([
+ 'id',
+ 'name',
+ 'image',
+ 'hover_image',
+ 'link'
+ ])
+ ->language($language)
+ ->enabled()
+ ->order(['sort' => 'asc', 'id' => 'desc'])
+ ->select()
+ ?->each(function($item) {
+ $item->image = thumb($item->image);
+ return $item;
+ });
+ }
+
// 获取系统联系方式配置
protected function getSysConfig($language, $group = [])
{
diff --git a/app/index/lang/en-us/pc.php b/app/index/lang/en-us/pc.php
index 4aa02b3f..73452f7a 100644
--- a/app/index/lang/en-us/pc.php
+++ b/app/index/lang/en-us/pc.php
@@ -5,10 +5,17 @@ return [
'产品列表' => 'Products',
'店铺' => 'Store',
'搜索记录' => 'Search History',
- '热销产品' => 'Popular Products',
'产品' => 'Product',
'联系我们' => 'Contact',
+ // 新导航栏 - 2023-03-31
+ '搜索' => 'Search',
+ '搜索产品、分类...' => 'Search products and categories...',
+ '最近搜索' => 'Recent Searches',
+ '清空' => 'Clear',
+ '热销产品' => 'Popular Products',
+ '购买' => 'Buy',
+
// 返回文本
'提交成功' => 'success',
'提交失败' => 'fail',
diff --git a/app/index/model/ProductCategoryModel.php b/app/index/model/ProductCategoryModel.php
index a6ea7e5e..7846820a 100644
--- a/app/index/model/ProductCategoryModel.php
+++ b/app/index/model/ProductCategoryModel.php
@@ -17,6 +17,12 @@ class ProductCategoryModel extends ProductCategoryBaseModel
// 软件删除时间字段
protected $deleteTime = 'deleted_at';
+ // 关联产品推荐
+ public function recommends()
+ {
+ return $this->hasMany(ProductCategoryRecommendModel::class, 'category_id', 'id');
+ }
+
// 所属语言范围查询
public function scopeLanguage($query, $language)
{
diff --git a/app/index/model/ProductCategoryRecommendModel.php b/app/index/model/ProductCategoryRecommendModel.php
new file mode 100644
index 00000000..122e4dc0
--- /dev/null
+++ b/app/index/model/ProductCategoryRecommendModel.php
@@ -0,0 +1,25 @@
+where($this->getTable() . '.language_id', '=', $language);
+ }
+}
diff --git a/app/index/model/SysMallStoreEntranceModel.php b/app/index/model/SysMallStoreEntranceModel.php
new file mode 100644
index 00000000..df07e042
--- /dev/null
+++ b/app/index/model/SysMallStoreEntranceModel.php
@@ -0,0 +1,43 @@
+belongsTo(\app\index\model\LanguageModel::class, 'language_id', 'id');
+ }
+
+ // 所属语言范围查询
+ public function scopeLanguage($query, $language)
+ {
+ $query->where($this->getTable() . '.language_id', '=', $language);
+ }
+
+ // 查询启用状态
+ public function scopeEnabled($query)
+ {
+ $query->where('disabled', '=', 0);
+ }
+
+ // 查询禁用状态
+ public function scopeDisabled($query)
+ {
+ $query->where('disabled', '=', 1);
+ }
+}
diff --git a/app/index/view/pc/public/20230331.header.html b/app/index/view/pc/public/20230331.header.html
new file mode 100644
index 00000000..b29fb394
--- /dev/null
+++ b/app/index/view/pc/public/20230331.header.html
@@ -0,0 +1,287 @@
+
+
+
diff --git a/app/index/view/pc/public/header.html b/app/index/view/pc/public/header.html
index b29fb394..f4d5c5a3 100644
--- a/app/index/view/pc/public/header.html
+++ b/app/index/view/pc/public/header.html
@@ -1,287 +1,1347 @@
-