71 Commits

Author SHA1 Message Date
8daf4d0696 Merge branch 'dev' 2026-04-17 10:09:53 +08:00
322cfad5d9 调整弹窗和防抖
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 3s
2026-04-17 10:08:15 +08:00
2b2dc2021a Merge branch 'dev' 2026-04-16 15:58:40 +08:00
9b732b19dc 修改变量重名
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 3s
2026-04-16 15:57:48 +08:00
654caf58c2 Merge branch 'dev' 2026-04-16 15:04:09 +08:00
1d62a64014 删除无用备份
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 3s
2026-04-16 15:02:02 +08:00
4447aa4a39 整体滚动
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 4s
2026-04-16 14:42:13 +08:00
53a317c8cf 侧边栏圆角动画
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 3s
2026-04-16 11:03:14 +08:00
b269e18547 fix: 系统商城购买入口地址必须是有效url
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 3s
2026-04-16 09:35:01 +08:00
04efd7ef4a 修改圆角和背景色
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 3s
2026-04-16 09:30:37 +08:00
dc74a1ee10 修复样式问题
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 2s
2026-04-15 17:10:45 +08:00
9b661405c9 refactor: mobile 产品classify.html
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 3s
2026-04-15 16:41:33 +08:00
1ed900d2b5 修改移动端样式
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 3s
2026-04-15 16:37:32 +08:00
d6ce8aa2be refactor: pc header.html
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 3s
2026-04-15 14:19:44 +08:00
4b5dae8714 fix: pc header修复产品分类关联推荐产品未过滤已禁用的
Some checks failed
Gitea Actions Official-website / deploy-dev (push) Failing after 3s
2026-04-15 14:05:56 +08:00
cf670b24f8 refactor: pc header 一级产品分类添加点击跳转链接
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 2s
2026-04-15 11:08:55 +08:00
cf1c78c882 跳转
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 2s
2026-04-15 11:05:47 +08:00
adef3c1e2e 样式调整
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 3s
2026-04-15 11:00:27 +08:00
180b5db570 refactor: pc header 三级产品分类改为显示5个
Some checks failed
Gitea Actions Official-website / deploy-dev (push) Failing after 4s
2026-04-15 10:52:02 +08:00
a18ff844a4 调整产品列表
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 4s
2026-04-14 17:35:27 +08:00
80d3aa8160 refactor: pc header数据改两列显示
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 4s
2026-04-14 16:57:19 +08:00
d79b418310 样式调整
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 4s
2026-04-14 16:23:17 +08:00
816d85a744 购买弹窗交互修改
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 3s
2026-04-14 10:45:22 +08:00
2d078033fa 悬浮图判断
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 4s
2026-04-13 15:15:29 +08:00
d430f04b41 定位层级调整
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 3s
2026-04-11 15:30:57 +08:00
aaf73a0803 修复下划线不显示
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 3s
2026-04-11 11:34:58 +08:00
7baac33d0d 弹窗关闭
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 3s
2026-04-11 11:11:48 +08:00
f2f59e9528 隐藏下拉框
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 3s
2026-04-11 10:54:53 +08:00
a9abb16fcb 样式调整
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 2s
2026-04-11 10:45:49 +08:00
7ff746267b 产品列表图片倒序调整
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 3s
2026-04-11 10:27:20 +08:00
bc9f233cb2 修改边距
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 3s
2026-04-11 10:12:00 +08:00
2c4eb38495 调整列表
Some checks failed
Gitea Actions Official-website / deploy-dev (push) Failing after 3s
2026-04-11 10:11:25 +08:00
347ebddb97 移动端header间距兼容
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 2s
2026-04-10 15:27:36 +08:00
93a72c7826 兼容header
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 4s
2026-04-10 15:18:53 +08:00
9748a3008e fix: pc header
Some checks failed
Gitea Actions Official-website / deploy-dev (push) Failing after 3s
2026-04-10 09:26:02 +08:00
2c20bd2649 返回功能
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 4s
2026-04-09 13:58:36 +08:00
ff8e23766a 固定图片宽高
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 3s
2026-04-09 11:45:15 +08:00
81f01e1424 替换pc图片
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 3s
2026-04-09 10:52:52 +08:00
b6b5ea5308 拆分css样式
Some checks failed
Gitea Actions Official-website / deploy-dev (push) Failing after 2s
2026-04-09 10:52:13 +08:00
62db7e8cb2 分离css代码
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 3s
2026-04-09 10:38:17 +08:00
ba2b343d0a 添加箭头
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 3s
2026-04-08 17:40:31 +08:00
83be5a7c0b feat: mobile端classify页面
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 4s
2026-04-08 16:26:29 +08:00
ce38172cd6 样式合并
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 2s
2026-04-08 14:26:50 +08:00
5262ece276 产品分类
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 3s
2026-04-08 14:25:09 +08:00
86cc41b78c logo跳首页
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 2s
2026-04-08 13:59:22 +08:00
f5a891e00e 菜单固定定位
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 2s
2026-04-08 13:55:52 +08:00
184e81737e header搜索框样式错乱调整
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 4s
2026-04-08 13:54:02 +08:00
eb015c552f header悬浮图
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 3s
2026-04-08 11:28:21 +08:00
685cd53b1a refactor: mobile header
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 4s
2026-04-08 11:20:54 +08:00
2c3e309d2a fix: pc header 2026-04-08 11:20:39 +08:00
853da3c948 header静态图片
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 3s
2026-04-08 10:13:57 +08:00
a68499255d 移动端header
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 4s
2026-04-08 09:30:06 +08:00
23f9a8fc9a 修复导航
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 4s
2026-03-31 17:30:22 +08:00
29abb5e230 refactor: pc导航重构
Some checks failed
Gitea Actions Official-website / deploy-dev (push) Failing after 2s
2026-03-31 10:33:22 +08:00
8a9d66f5d3 fix: 产品详情接口未找到产品时报错误问题
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 2s
2026-03-28 11:46:23 +08:00
90b4bccbcf feat: 系统商城店铺入口
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 2s
2026-03-28 11:42:42 +08:00
4c592d9347 refactor: 产品分类推荐列表图片改为缩略图
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 2s
2026-03-28 11:04:22 +08:00
ebe1c3015e fix: 去除banner不允许更新unique_label限制
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 4s
2026-03-28 09:13:48 +08:00
9584b81729 refactor: banner|navigation
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 4s
2026-03-27 17:59:08 +08:00
8577877f83 feat: banner分类添加unique_label字段管理
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 3s
2026-03-27 17:50:25 +08:00
be26f2d75b fix: 产品推荐列表及导出问题
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 3s
2026-03-27 17:43:41 +08:00
863fdda9a5 feat: 添加导出接口路由
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 2s
2026-03-27 16:32:19 +08:00
a1c7ae62ba feat: 产品分类推荐数据管理
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 3s
2026-03-27 15:19:22 +08:00
49240893b5 Merge branch 'dev' 2026-01-13 17:31:40 +08:00
61728434d3 refactor: 修改tco产品数据同步
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 5s
2026-01-13 17:30:56 +08:00
5736d09dbe refactor: image_migrate.py添加迁移后的目录及文件所有者修改
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 4s
2026-01-04 11:00:52 +08:00
f4646f1e3a Merge branch 'dev' 2025-12-31 17:07:35 +08:00
5b3e505e9b 删除视频提示语
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 3s
2025-12-31 17:06:51 +08:00
624d7fde2f Merge branch 'master' of http://gitea.f2b211.com/jsasg/orico-official-website 2025-12-31 17:00:00 +08:00
a85f2c1b3a Merge branch 'dev' 2025-12-31 16:55:53 +08:00
7ec97c39a7 注释initVideoTip
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 3s
2025-12-31 16:55:30 +08:00
71 changed files with 4555 additions and 920 deletions

3
.gitignore vendored
View File

@@ -23,3 +23,6 @@ public/.well-known
/.settings /.settings
/.buildpath /.buildpath
/.project /.project
CLAUDE.md
/skills

View File

@@ -201,15 +201,16 @@ class ReceiveProductSync
throw new \Exception('产品创建失败'); throw new \Exception('产品创建失败');
} }
} }
else if (strtotime($product['updated_at']) < strtotime($data['created_at'])) { // 注释更新同步防止tco同步修改官网手动调整数据
$product->spu = $data['spu']; // else if (strtotime($product['updated_at']) < strtotime($data['created_at'])) {
$product->name = $data['name']; // $product->spu = $data['spu'];
$product->category_id = $product_category['id']; // $product->name = $data['name'];
$product->status = Operate_Of_ReceiveSync::Disable == $data['operate'] ? -1 : 1; // $product->category_id = $product_category['id'];
if (!$product->save()) { // $product->status = Operate_Of_ReceiveSync::Disable == $data['operate'] ? -1 : 1;
throw new \Exception('产品更新失败'); // if (!$product->save()) {
} // throw new \Exception('产品更新失败');
} // }
// }
} catch (\Throwable $th) { } catch (\Throwable $th) {
return error(sprintf('%s %s:%d', $th->getMessage(), $th->getFile(), $th->getLine())); return error(sprintf('%s %s:%d', $th->getMessage(), $th->getFile(), $th->getLine()));
} }

View File

@@ -91,7 +91,6 @@ class Banner
$banner = SysBannerModel::withoutField([ $banner = SysBannerModel::withoutField([
'at_page', 'at_page',
'unique_label',
'language_id', 'language_id',
'created_at', 'created_at',
'updated_at', 'updated_at',
@@ -142,6 +141,7 @@ class Banner
'name', 'name',
'desc', 'desc',
'recommend', 'recommend',
'unique_label',
'at_platform' => 'pc', 'at_platform' => 'pc',
'status' => 1 'status' => 1
]); ]);

View File

@@ -67,6 +67,8 @@ class NavigationItem
'id', 'id',
'pid', 'pid',
'name', 'name',
'desc',
'image',
'nav_id', 'nav_id',
'sort', 'sort',
'status', 'status',
@@ -93,7 +95,9 @@ class NavigationItem
'pid', 'pid',
'nav_id', 'nav_id',
'name', 'name',
'desc',
'icon', 'icon',
'image',
'link_to' => 'custom', 'link_to' => 'custom',
'link', 'link',
'sort', 'sort',
@@ -121,7 +125,9 @@ class NavigationItem
'pid', 'pid',
'nav_id', 'nav_id',
'name', 'name',
'desc',
'icon', 'icon',
'image',
'link_to', 'link_to',
'link', 'link',
'sort', 'sort',

View File

@@ -83,7 +83,7 @@ class Product
]) ])
->bypk(request()->param('id')) ->bypk(request()->param('id'))
->find() ->find()
->bindAttr('category', ['category_name']) ?->bindAttr('category', ['category_name'])
->hidden(['category']); ->hidden(['category']);
if (empty($product)) { if (empty($product)) {
return error('产品不存在'); return error('产品不存在');

View File

@@ -0,0 +1,203 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\v1;
use app\admin\model\v1\ProductCategoryRecommendModel;
use app\admin\validate\v1\ProductCategoryRecommendValidate;
/**
* 产品分类推荐管理控制器
*/
class ProductCategoryRecommend
{
/**
* 分页列表
*/
public function index()
{
$param = request()->get([
'keywords',
'category_name',
'page/d' => 1,
'size/d' => 10
]);
// 查询数据
$data = ProductCategoryRecommendModel::withJoin(['category' => function($query) use ($param) {
if (!empty($param['category_name'])) {
$query->where('category.name', 'like', '%' . $param['category_name'] . '%');
}
}])
->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', 'language_id', 'updated_at', 'deleted_at'])
?->each(function($item) {
// 列表页面图片输出缩略图
if (!empty($item['image'])) {
$item['image'] = thumb($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::withJoin(['category' => function($query) use ($param) {
if (!empty($param['category_name'])) {
$query->where('category.name', 'like', '%' . $param['category_name'] . '%');
}
}])
->withSearch(['keywords'], ['keywords' => $param['keywords']??null])
->language(request()->lang_id)
->order(['sort' => 'asc', 'id' => 'desc'])
->select()
->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']);
}
// 状态
$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('操作成功');
}
}

View File

@@ -0,0 +1,201 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\v1;
use app\admin\model\v1\SysMallStoreEntranceModel;
use app\admin\validate\v1\SysMallStoreEntranceValidate;
/**
* 系统商城店铺入口管理控制器
*/
class SysMallStoreEntrance
{
/**
* 分页列表
*/
public function index()
{
$param = request()->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();
}
}

View File

@@ -0,0 +1,47 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\ProductCategoryRecommendBaseModel;
use think\model\concern\SoftDelete;
/**
* 产品分类推荐模型
* @mixin \think\Model
*/
class ProductCategoryRecommendModel extends ProductCategoryRecommendBaseModel
{
// 启用软件删除
use SoftDelete;
// 软件删除时间字段
protected $deleteTime = 'deleted_at';
// 关联语言
public function language()
{
return $this->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($this->getTable() . '.language_id', '=', $language);
}
// 关键词搜索
public function searchKeywordsAttr($query, string|null $keywords)
{
if (is_null($keywords)) {
return;
}
$query->where($this->getTable() . '.title', 'like', "%{$keywords}%")
->whereOr($this->getTable() . '.desc', 'like', "%{$keywords}%");
}
}

View File

@@ -0,0 +1,61 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\SysMallStoreEntranceBaseModel;
use think\model\concern\SoftDelete;
/**
* 系统商城店铺入口模型
* @mixin \think\Model
*/
class SysMallStoreEntranceModel extends SysMallStoreEntranceBaseModel
{
// 启用软删除
use SoftDelete;
// 软删除字段
protected $deleteTime = 'deleted_at';
// 关联语言
public function language()
{
return $this->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}%");
}
}

View File

@@ -311,6 +311,27 @@ Route::group('v1', function () {
// 分类删除 // 分类删除
Route::delete('delete/:id', 'ProductCategory/delete'); Route::delete('delete/:id', 'ProductCategory/delete');
// 产品分类推荐数据
Route::group('recommend', function () {
// 推荐数据分页列表
Route::get('index', 'ProductCategoryRecommend/index');
// 推荐数据导出
Route::get('export', 'ProductCategoryRecommend/export');
// 推荐数据详情
Route::get('read/:id', 'ProductCategoryRecommend/read');
// 推荐数据新增
Route::post('save', 'ProductCategoryRecommend/save');
// 推荐数据更新
Route::put('update/:id', 'ProductCategoryRecommend/update');
// 推荐数据删除
Route::delete('delete/:id', 'ProductCategoryRecommend/delete');
});
}); });
// 产品购买链接 // 产品购买链接
@@ -574,6 +595,29 @@ Route::group('v1', function () {
// 反馈管理 - 产品询盘列表 // 反馈管理 - 产品询盘列表
Route::get('product/inquiry/index', 'ProductInquiry/index'); 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() { Route::group('config', function() {
// 配置分组 // 配置分组

View File

@@ -20,7 +20,9 @@ class NavigationItemValidate extends Validate
'nav_id' => 'require|integer', 'nav_id' => 'require|integer',
'pid' => 'integer|different:id|checkPidNotBeChildren', 'pid' => 'integer|different:id|checkPidNotBeChildren',
'name' => 'require|max:64', 'name' => 'require|max:64',
'desc' => 'max:255',
'icon' => 'max:64', 'icon' => 'max:64',
'image' => 'max:255',
'link_to' => 'require|max:64|in:article,article_category,product,product_category,system_page,custom', 'link_to' => 'require|max:64|in:article,article_category,product,product_category,system_page,custom',
'link' => 'max:255', 'link' => 'max:255',
'sort' => 'integer', 'sort' => 'integer',
@@ -44,7 +46,9 @@ class NavigationItemValidate extends Validate
'pid.checkPidNotBeChildren' => '父级ID不能为自身的子导航', 'pid.checkPidNotBeChildren' => '父级ID不能为自身的子导航',
'name.require' => '导航名称不能为空', 'name.require' => '导航名称不能为空',
'name.max' => '导航名称最多不能超过64个字符', 'name.max' => '导航名称最多不能超过64个字符',
'desc.max' => '导航名称最多不能超过:rule个字符',
'icon.max' => '图标最多不能超过64个字符', 'icon.max' => '图标最多不能超过64个字符',
'image.max' => '图标最多不能超过:rule个字符',
'link_to.require' => '链接类型不能为空', 'link_to.require' => '链接类型不能为空',
'link_to.max' => '链接类型最多不能超过64个字符', 'link_to.max' => '链接类型最多不能超过64个字符',
'link_to.in' => '链接类型必须是article,article_category,product_category,product,system_page,custom中之一', 'link_to.in' => '链接类型必须是article,article_category,product_category,product,system_page,custom中之一',

View File

@@ -0,0 +1,63 @@
<?php
declare (strict_types = 1);
namespace app\admin\validate\v1;
use think\Validate;
class ProductCategoryRecommendValidate extends Validate
{
/**
* 定义验证规则
* 格式:'字段名' => ['规则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');
}
}

View File

@@ -5,8 +5,6 @@ namespace app\admin\validate\v1;
use think\Validate; use think\Validate;
use function PHPSTORM_META\type;
class ProductValidate extends Validate class ProductValidate extends Validate
{ {
/** /**

View File

@@ -43,7 +43,6 @@ class SysBannerValidate extends BaseValidate
'at_platform.in' => '显示端口只能是pc或mobile', 'at_platform.in' => '显示端口只能是pc或mobile',
'at_page.max' => '页面位置最多255个字符', 'at_page.max' => '页面位置最多255个字符',
'unique_label.max' => '唯一标识最多64个字符', 'unique_label.max' => '唯一标识最多64个字符',
'unique_label.mustOmit' => '更新时不能有unique_label字段',
'name.require' => '名称不能为空', 'name.require' => '名称不能为空',
'name.max' => '名称最多64个字符', 'name.max' => '名称最多64个字符',
'desc.max' => '描述最多255个字符', 'desc.max' => '描述最多255个字符',
@@ -61,6 +60,6 @@ class SysBannerValidate extends BaseValidate
// 编辑场景 // 编辑场景
public function sceneEdit() public function sceneEdit()
{ {
return $this->remove('language_id', 'require|integer')->append('unique_label', 'mustOmit'); return $this->remove('language_id', 'require|integer');
} }
} }

View File

@@ -0,0 +1,61 @@
<?php
declare (strict_types = 1);
namespace app\admin\validate\v1;
use think\Validate;
class SysMallStoreEntranceValidate extends Validate
{
/**
* 定义验证规则
* 格式:'字段名' => ['规则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' => 'url|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.url' => '链接地址必须是有效的URL',
'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');
}
}

View File

@@ -0,0 +1,33 @@
<?php
declare (strict_types = 1);
namespace app\common\model;
/**
* 产品分类推荐模型
* @mixin \think\Model
*/
class ProductCategoryRecommendBaseModel extends BaseModel
{
// 表名
protected $name = 'product_category_recommend';
// 主键
protected $pk = 'id';
// 字段信息
protected $schema = [
'id' => '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'
];
}

View File

@@ -0,0 +1,32 @@
<?php
declare (strict_types = 1);
namespace app\common\model;
/**
* 系统商城店铺入口模型
* @mixin \think\Model
*/
class SysMallStoreEntranceBaseModel extends BaseModel
{
// 表名
protected $name = 'sys_mall_store_entrance';
// 主键
protected $pk = 'id';
// 字段信息
protected $schema = [
'id' => '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',
];
}

View File

@@ -21,7 +21,9 @@ class SysNavigationItemBaseModel extends BaseModel
'nav_id' => 'int', 'nav_id' => 'int',
'pid' => 'int', 'pid' => 'int',
'name' => 'string', 'name' => 'string',
'desc' => 'string',
'icon' => 'string', 'icon' => 'string',
'image' => 'string',
'link_to' => 'string', 'link_to' => 'string',
'link' => 'string', 'link' => 'string',
'sort' => 'int', 'sort' => 'int',

View File

@@ -8,6 +8,7 @@ use app\index\model\LanguageModel;
use app\index\model\ProductCategoryModel; use app\index\model\ProductCategoryModel;
use app\index\model\ProductModel; use app\index\model\ProductModel;
use app\index\model\SysConfigModel; use app\index\model\SysConfigModel;
use app\index\model\SysMallStoreEntranceModel;
use app\index\model\SysNavigationItemModel; use app\index\model\SysNavigationItemModel;
use think\facade\Lang; use think\facade\Lang;
use think\facade\View; 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); View::assign('header_categorys', $categorys);
@@ -47,6 +48,9 @@ abstract class Common extends BaseController
// 输出热销产品 // 输出热销产品
View::assign('header_hot_products', $hot_products); 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)); 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([ $categorys = ProductCategoryModel::field([
'id', 'id',
@@ -87,15 +91,48 @@ abstract class Common extends BaseController
'icon', 'icon',
'level' 'level'
]) ])
->when($with_recommends, function($query) {
$query->with(['recommends' => function($query) {
$query->field(['id', 'category_id', 'title', 'image', 'desc', 'link'])->disabled(false)
->order(['sort' => 'asc', 'id' => 'desc']);
}]);
})
->language($language) ->language($language)
->displayed() ->displayed()
->order(['sort' => 'asc', 'id' => 'desc']) ->order(['sort' => 'asc', 'id' => 'desc'])
->select(); ->select()
->hidden(["recommends.category_id"]);
if ($categorys->isEmpty()) { if ($categorys->isEmpty()) {
return []; 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)) {
if ($lv == 1) {
$item['children'] = array_chunk($children, 2);
} else if ($lv == 2) {
$item['children'] = array_chunk($children, 3);
} else {
$item['children'] = $children;
}
}
$ret[] = $item;
}
}
return $ret;
} }
// 获取顶部导航 // 获取顶部导航
@@ -164,6 +201,26 @@ abstract class Common extends BaseController
return $languages; 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 = []) protected function getSysConfig($language, $group = [])
{ {

View File

@@ -267,6 +267,59 @@ class Product extends Common
return View::fetch('subcategory'); return View::fetch('subcategory');
} }
/**
* 单纯分类页
*/
public function classify()
{
$pid = request()->param('id/d', 0);
// 获取当前选中的父分类
$parent = ProductCategoryModel::field(['id', 'name'])->find($pid);
View::assign('parent', $parent);
// 获取分类及产品信息
$categorys = ProductCategoryModel::field(['id', 'pid', 'name', 'path', 'level'])
->language($this->lang_id)
->displayed(true)
->children($pid)
->order(['pid' => 'asc', 'sort' => 'asc', 'id' => 'desc'])
->select()
->toArray();
// 组装第三级分类所属产品数据
$lv3_id_arr = array_column(array_filter($categorys, fn($item)=> $item['level'] === 3), 'id');
$products = ProductModel::field([
'id',
'category_id',
'name',
'short_name',
'cover_image',
'spu'
])
->byCategory($lv3_id_arr)
->language($this->lang_id)
->enabled(true)
->onSale(true)
->onShelves(true)
->select()
->toArray();
$product_groups = [];
foreach ($products as $product) {
$product_groups[$product['category_id']][] = $product;
}
foreach ($categorys as $key => $category) {
if ($category['level'] < 3) continue;
$categorys[$key]['products'] = $product_groups[$category['id']] ?? [];
}
$tree = array_to_tree($categorys, $pid);
View::assign('categorys', $tree);
return View::fetch('classify');
}
/** /**
* 产品搜索 * 产品搜索
*/ */

View File

@@ -6,6 +6,9 @@ return [
'产品列表' => 'Products', '产品列表' => 'Products',
'搜索' => 'Search', '搜索' => 'Search',
'搜索历史' => 'Search History', '搜索历史' => 'Search History',
'请输入搜索关键词' => 'Please enter a search keyword',
'搜索记录' => 'Search History',
'清空' => 'Clear',
'请择地区' => 'SELECT A REGION', '请择地区' => 'SELECT A REGION',
'产品' => 'Product', '产品' => 'Product',
'联系方式' => 'Contact', '联系方式' => 'Contact',

View File

@@ -5,10 +5,17 @@ return [
'产品列表' => 'Products', '产品列表' => 'Products',
'店铺' => 'Store', '店铺' => 'Store',
'搜索记录' => 'Search History', '搜索记录' => 'Search History',
'热销产品' => 'Popular Products',
'产品' => 'Product', '产品' => 'Product',
'联系我们' => 'Contact', '联系我们' => 'Contact',
// 新导航栏 - 2023-03-31
'搜索' => 'Search',
'搜索产品、分类...' => 'Search products and categories...',
'最近搜索' => 'Recent Searches',
'清空' => 'Clear',
'热销产品' => 'Popular Products',
'购买' => 'Buy',
// 返回文本 // 返回文本
'提交成功' => 'success', '提交成功' => 'success',
'提交失败' => 'fail', '提交失败' => 'fail',

View File

@@ -17,6 +17,12 @@ class ProductCategoryModel extends ProductCategoryBaseModel
// 软件删除时间字段 // 软件删除时间字段
protected $deleteTime = 'deleted_at'; protected $deleteTime = 'deleted_at';
// 关联产品推荐
public function recommends()
{
return $this->hasMany(ProductCategoryRecommendModel::class, 'category_id', 'id');
}
// 所属语言范围查询 // 所属语言范围查询
public function scopeLanguage($query, $language) public function scopeLanguage($query, $language)
{ {

View File

@@ -0,0 +1,30 @@
<?php
declare (strict_types = 1);
namespace app\index\model;
use app\common\model\ProductCategoryRecommendBaseModel;
use think\model\concern\SoftDelete;
/**
* 产品分类推荐模型
* @mixin \think\Model
*/
class ProductCategoryRecommendModel extends ProductCategoryRecommendBaseModel
{
// 启用软件删除
use SoftDelete;
// 软件删除时间字段
protected $deleteTime = 'deleted_at';
// 所属语言范围查询
public function scopeLanguage($query, $language)
{
$query->where($this->getTable() . '.language_id', '=', $language);
}
public function scopeDisabled($query, $disabled = true)
{
$query->where($this->getTable() . '.disabled', '=', (int)$disabled);
}
}

View File

@@ -0,0 +1,43 @@
<?php
declare (strict_types = 1);
namespace app\index\model;
use app\common\model\SysMallStoreEntranceBaseModel;
use think\model\concern\SoftDelete;
/**
* 系统商城店铺入口模型
* @mixin \think\Model
*/
class SysMallStoreEntranceModel extends SysMallStoreEntranceBaseModel
{
// 启用软删除
use SoftDelete;
// 软删除字段
protected $deleteTime = 'deleted_at';
// 关联语言
public function language()
{
return $this->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);
}
}

View File

@@ -18,6 +18,8 @@ Route::group('product', function () {
Route::get('category/:id', 'Product/category'); Route::get('category/:id', 'Product/category');
// 产品分类 - 查看子类 // 产品分类 - 查看子类
Route::get('subcategory/:id', 'Product/subcategory'); Route::get('subcategory/:id', 'Product/subcategory');
// 单纯分类页
Route::get('classify/:id', 'Product/classify');
// 产品详情页 // 产品详情页
Route::get('detail/:id', 'Product/detail'); Route::get('detail/:id', 'Product/detail');
// 产品询盘 // 产品询盘

View File

@@ -9,7 +9,7 @@
<!-- banner--> <!-- banner-->
<div class="oidx-banner"> <div class="oidx-banner">
{notempty name="focus_images"} {notempty name="focus_images"}
<div class="swiper-container bannerswiper"> <div class="swiper-container bannerswiper" style="background: #fff;">
<div class="swiper-wrapper"> <div class="swiper-wrapper">
{volist name="focus_images" id="fi"} {volist name="focus_images" id="fi"}
<div class="swiper-slide"> <div class="swiper-slide">

View File

@@ -0,0 +1,225 @@
{extend name="public/base" /}
{block name="style"}
<link rel="stylesheet" href="__CSS__/category.css">
<script type="text/javascript">
(function (doc, win) {
var docEl = doc.documentElement;
var resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize';
function setRootFontSize() {
var clientWidth = docEl.clientWidth;
if (!clientWidth) return;
var fontSize = clientWidth / 8.04; // 750px/7.5=100px375px/7.5=50px
// 直接修改内联样式,优先级最高
docEl.setAttribute('style', 'font-size: ' + fontSize + 'px !important;');
}
setRootFontSize();
win.addEventListener(resizeEvt, setRootFontSize);
doc.addEventListener('DOMContentLoaded', setRootFontSize);
})(document, window);
</script>
{/block}
{block name="main"}
<!-- 顶部返回栏(已完全按图纸修改) -->
<div class="top-box">
<div class="top-bar">
<div class="back">
<image src="__IMAGES__/left.png"></image>
</div>
<div class="title">{$parent.name|default=''}</div>
</div>
</div>
<div class="main">
<!-- 左侧导航 -->
{if condition="!empty($categorys)"}
<ul class="sidebar">
{volist name="categorys" id="category"}
<li class="nav-item {eq name='$key' value='0'}active{/eq}" data-tab="tab{$category.id}">{$category.name}</li>
{/volist}
<li style="background: #fff;" class="li-bottom"></li>
</ul>
<!-- 右侧分区内容 -->
<div class="right-content">
{volist name="categorys" id="category"}
<div class="tab-pane {eq name='$key' value='0'}active{/eq}" id="tab{$category.id}">
{if condition="!empty($category.children)"}
{volist name="$category.children" id="child"}
<section class="sec-box">
<div class="sec-header">
<div class="sec-title">{$child.name}</div>
<a class="sec-arrow" href="{:url('product/subcategory', ['id' => $child.id])}" target="_self">
<img src="__IMAGES__/y.png" alt="">
</a>
</div>
{if condition="!empty($child.products)"}
<div class="scroll-box">
{volist name="$child.products" id="pro"}
<a class="card" href="{:url('product/detail', ['id' => $pro.id])}" style="background: #fff;">
<div class="card-img">
<img src="{$pro.cover_image}" alt="">
</div>
<div class="card-info">
<div class="card-name">{$pro.name}</div>
<div class="card-model">{$pro.spu}</div>
</div>
</a>
{/volist}
</div>
{/if}
</section>
{/volist}
{/if}
</div>
{/volist}
</div>
{/if}
</div>
{/block}
{block name="script"}
<script>
// 点击右侧箭头滚动
function scrollNext (el)
{
const box = el.closest('section')?.querySelector('.scroll-box')
|| el.parentElement.nextElementSibling
if (box) box.scrollBy({ left: 150, behavior: 'smooth' })
}
const back = document.querySelector('.back')
// 左侧Tab切换
const navItems = document.querySelectorAll('.nav-item')
const tabPanes = document.querySelectorAll('.tab-pane')
navItems.forEach(item =>
{
item.addEventListener('click', () =>
{
// 切换左侧激活状态
navItems.forEach(i => i.classList.remove('active'))
item.classList.add('active')
// 切换右侧内容
const target = item.dataset.tab
tabPanes.forEach(p => p.classList.remove('active'))
document.getElementById(target)?.classList.add('active')
})
})
back.addEventListener('click',()=>{
window.location.href = document.referrer;
})
// 更新侧边栏相邻元素的圆角
function updateSidebarAdjacentRadius() {
const sidebarItems = document.querySelectorAll('.sidebar li');
// 先移除所有相邻类名
sidebarItems.forEach(item => {
item.classList.remove('active-prev', 'active-next');
});
// 找到当前激活的项
let activeIndex = -1;
sidebarItems.forEach((item, index) => {
if (item.classList.contains('active')) {
activeIndex = index;
}
});
// 添加上一个和下一个的类名使用requestAnimationFrame确保DOM更新
requestAnimationFrame(() => {
if (activeIndex > 0) {
sidebarItems[activeIndex - 1].classList.add('active-prev');
}
if (activeIndex < sidebarItems.length - 1) {
sidebarItems[activeIndex + 1].classList.add('active-next');
}
});
}
// 绑定点击事件
document.querySelectorAll('.sidebar li').forEach(item => {
item.addEventListener('click', function() {
// 移除所有active
document.querySelectorAll('.sidebar li').forEach(li => {
li.classList.remove('active');
});
// 添加active
this.classList.add('active');
// 更新相邻圆角
updateSidebarAdjacentRadius();
});
});
// 页面初始化时调用
if (document.querySelector('.sidebar li.active')) {
updateSidebarAdjacentRadius();
}
// 动态计算 li-bottom 的高度
function setLiBottomHeight() {
const sidebar = document.querySelector('.sidebar');
const liBottom = document.querySelector('.sidebar .li-bottom');
const sidebarItems = document.querySelectorAll('.sidebar li:not(.li-bottom)');
if (!sidebar || !liBottom || sidebarItems.length === 0) return;
// 计算所有正常li的总高度
let totalHeight = 0;
sidebarItems.forEach(item => {
totalHeight += item.offsetHeight;
});
// 获取sidebar的高度
const sidebarHeight = sidebar.offsetHeight;
// 计算剩余高度
const remainingHeight = sidebarHeight - totalHeight;
// 赋值给 li-bottom
if (remainingHeight > 0) {
liBottom.style.height = remainingHeight + 'px';
} else {
liBottom.style.height = '0px';
liBottom.style.display = 'none';
}
}
// 页面加载时执行
window.addEventListener('load', function() {
setLiBottomHeight();
});
// 窗口大小改变时重新计算
window.addEventListener('resize', function() {
console.log('切换了吗')
setLiBottomHeight();
});
// 如果侧边栏内容有动态变化,使用 MutationObserver 监听
const observer = new MutationObserver(function() {
setLiBottomHeight();
});
// 监听侧边栏变化
const sidebar = document.querySelector('.sidebar');
if (sidebar) {
observer.observe(sidebar, {
childList: true,
subtree: true,
attributes: true
});
}
</script>
{/block}

View File

@@ -1,161 +1,662 @@
<header class="oircoEgapp-head"> <link rel="stylesheet" href="__CSS__/new_header.css">
<div class="headtop"> <header>
<a href="/"><img src="__IMAGES__/logo.png" class="headerlogimg" /></a> <div class="mobile-header-box">
<div class="action-r"> <div class="mobile-header-left">
<div class="right img-responsive cursor_p"> <img src="__IMAGES__/header/nav.png" class="nav-img">
<span class="icon-category cursor_p top-menu-toggle"><i class="icon-menu-svg"></i></span> <img src="__IMAGES__/header/x.png" class="nav-img1">
<span class="icon-keyword cursor_p top-search-toggle"><i class="icon-search-svg"></i></span> <a href="/" target="_self" style="display: flex; justify-content: center;">
<span class="mask-up cursor_p top-country-toggle"><i class="icon-lag-svg"></i></span> <img src="__IMAGES__/logo.png" class="nav-log">
</div> </a>
</div>
<div class="mobile-header-right">
<img src="__IMAGES__/header/search.png" class="nav-search">
<img src="__IMAGES__/header/lang.png" class="nav-lang">
<img src="__IMAGES__/header/card.png" class="nav-card">
</div> </div>
</div> </div>
<!-- 顶部菜单-->
<div class="top-menu"> <div class="nav-dropdown-menu" style="height: 100vh; overflow-y: scroll;-webkit-overflow-scrolling: touch;padding-bottom:9rem;">
<div class="it-ct"> <!-- 产品列表 - 有子菜单 -->
<div class="it-1"><a href="/">{:lang_i18n('首页')}</a></div> <div class="menu-item has-child">
</div> <div class="menu-item-box">
<div class="it-ct"> <span>{:lang_i18n('产品列表')}</span>
<div class="it-1"> <img src="__IMAGES__/header/b.png" class="menu-item-img">
<div class="it-1-more">{:lang_i18n('产品列表')}<i class="icon-arrow"></i></div>
{notempty name="header_categorys"}
{volist name="header_categorys" id="ca"}
<div class="it-1-2"><a href="{:url('product/category', ['id' => $ca.id])}">{$ca.name}</a></div>
{/volist}
{/notempty}
</div> </div>
{if condition="!empty($header_categorys)"}
<div class="sub-menu" style="overflow-y: auto;">
{volist name="header_categorys" id="vo" key="idx"}
<div class="sub-item"><a href="{:url('product/classify', ['id' => $vo.id])}" target="_self">{$vo.name}</a></div>
{/volist}
</div>
{/if}
</div> </div>
{notempty name="header_navigation"}
{if condition="!empty($header_navigation)"}
{volist name="header_navigation" id="nav"} {volist name="header_navigation" id="nav"}
<div class="it-ct"> {empty name='nav.children'}
<div class="it-1"> <div class="menu-item no-child" data-link="{$nav.link}">
{empty name="nav.children"} {else/}
<a href="{$nav.link}">{$nav.name}</a> <div class="menu-item has-child" data-link="{$nav.link}">
{else/} {/empty}
<div class="it-1-more">{$nav.name}<i class="icon-arrow"></i></div> <div class="menu-item-box">
{volist name="nav.children" id="ch"} <span>{$nav.name}</span>
<div class="it-1-2"><a href="{$ch.link}">{$ch.name}</a></div> <img src="__IMAGES__/header/b.png" class="menu-item-img">
{/volist}
{/empty}
</div> </div>
{if condition="!empty($nav.children)"}
<div class="sub-menu" style="border: none;" >
{volist name="nav.children" id="child"}
<div class="sub-item no-padding ">
<a href="{$child.link}" target="{$child.blank==1?'_blank':'_self'}" class="sub-item-card">
<img src="{$child.image}" alt="" class="sub-item-card-img">
<div class="sub-item-card-title">{$child.name}</div>
<div class="sub-item-card-name">{$child.desc}</div>
</a>
</div>
{/volist}
</div>
{/if}
</div> </div>
{/volist} {/volist}
{/notempty} {/if}
</div> </div>
<!-- 顶部搜索-->
<div class="top-search"> <!-- ====================== 购物车弹窗 ====================== -->
<div class="marsk-container"> <div class="modal-overlay" id="cartModal">
<div class="popup-quick"> <div class="modal-content">
<div class="ac-close float_r "><img src="__IMAGES__/close.png"></div> <img src="__IMAGES__/header/x.png" class="modal-close">
<div class="search-in"> {if condition="!empty($header_mall_entrance)"}
<form action="{:url('product/search')}" method="get"> <div class="modal-items-list">
<input type="text" name="keywords" placeholder="{:lang_i18n('产品')} USB 2.0..."> {volist name="header_mall_entrance" id="ma"}
<button type="submit" id="search-btnput" class="search-button">{:lang_i18n('搜索')}</button> {if condition="!empty($ma.link)"}
</form> <a class="modal-item" href="{$ma.link}" target="_self">
<div class="title-text"> <img src="{$ma.image}" alt="" class="modal-item-img">
<p><a href="#">{:lang_i18n('搜索历史')}</a></p> <div class="modal-item-title">{$ma.name}</div>
<div id="history"></div> </a>
</div> {else/}
<!--悬浮图-->
<div class="modal-item" data-link="{$ma.link}" style="cursor: pointer;">
<img src="{$ma.image}" alt="" class="modal-item-img">
<div class="modal-item-title">{$ma.name}</div>
</div> </div>
<div class="modal-item" style="display:none">
<img src="{$ma.hover_image}" alt="" class="modal-item-img">
</div>
{/if}
{/volist}
</div>
{/if}
</div>
</div>
<!-- ====================== 语言弹窗 ====================== -->
<div class="modal-overlay" id="langModal">
<div class="lang-modal-content">
<img src="__IMAGES__/header/x.png" class="modal-close">
<div class="lang-items-list">
{volist name="header_languages" id="la"}
<a class="lang-item" href="{$la.lang_url}" target="_self">
<img src="{$la.lang_icon}" />
<div>{$la.country_en_name} - {$la.lang_en_name}</div>
</a>
{/volist}
</div> </div>
</div> </div>
</div> </div>
<!-- 顶部国家选择-->
<div class="top-country"> <!-- ====================== 搜索弹窗 ====================== -->
<div class="mask"></div> <div class="modal-overlay" id="searchModal">
<div class="action-sheet"> <div class="search-modal-content">
<div class="menu-title"> <img src="__IMAGES__/header/x.png" class="modal-close">
<div class="menu-name">{:lang_i18n('请择地区')}</div> <div class="search-input-box">
<div class="close-icon"> <input type="text" placeholder="{:lang_i18n('请输入搜索关键词')}" id="searchInput">
<img src="__IMAGES__/close.png">
<div class="search-clear-box">
<img src="__IMAGES__/header/x.png" alt="{:lang_i18n('清除')}" class="search-clear-btn" id="searchClearBtn" style="display: none;">
</div>
<!-- <span style="width: 1px; color:#d9d9d9;margin: 0 10px;">|</span> -->
<div class="search-submit-box">
<img src="__IMAGES__/header/search.png" alt="{:lang_i18n('搜索')}" class="search-submit-icon" id="searchSubmit">
</div>
</div>
<div class="search-history">
<div class="search-history-header">
<div class="search-history-title">{:lang_i18n('搜索记录')}</div>
<div class="search-history-clear" id="clearAllHistory">{:lang_i18n('清空')}</div>
</div>
<div class="search-history-list" id="searchHistoryList">
<!-- 搜索记录会动态显示在这里 -->
</div> </div>
</div> </div>
<ul>
{volist name="header_languages" id="la"}
<li>
<a href="{$la.lang_url}" target="_blank">
<img src="{$la.lang_icon}">{$la.country_en_name} - {$la.lang_en_name}
</a>
</li>
{/volist}
</ul>
</div> </div>
</div> </div>
</header> </header>
<script type="text/javascript"> <script>
$(function() {
// 点击顶部菜单
$(".top-menu-toggle").click(function() {
$(".top-menu").slideToggle(800);
})
// 点击一级菜单显示二级菜单
$(".it-1-more").on("click", function() {
$('.it-1-2').hide();
$('.icon-arrow').removeClass('rotate');
$(this).siblings('.it-1-2').slideToggle(800);
$(this).find('.icon-arrow').addClass('rotate');
});
//点击搜索
$('.top-search-toggle').click(function() {
$(".marsk-container").show();
})
$('.ac-close').click(function() {
$(".marsk-container").hide();
})
// 顶部国家选择
$('.top-country-toggle').click(function(){
$(".mask,.action-sheet").show();
})
$('.top-country .close-icon').click(function(){
$(".mask,.action-sheet").hide();
})
// 移动端顶部宽度设置和主体内容宽度一致 document.addEventListener('DOMContentLoaded', function ()
var pageWidth = $('.oricoEGapp').outerWidth(); {
// 设置.header-PC元素的宽度 const navBtn = document.querySelector('.nav-img');
$('.oircoEgapp-head').css('width', pageWidth + 'px'); const navBtn1 = document.querySelector('.nav-img1');
// 可选:监听窗口大小变化,实时更新宽度 const dropdownMenu = document.querySelector('.nav-dropdown-menu');
$(window).resize(function() {
var newWidth = $('.oricoEGapp').outerWidth();
$('.oircoEgapp-head').css('width', newWidth + 'px');
});
// 回显搜索历史记录 // 禁止body滚动
history(); function disableBodyScroll() {
}) document.body.style.overflow = 'hidden';
document.body.style.position = 'fixed';
function history() { document.body.style.top = `-${window.scrollY}px`;
var keywords = new URL(window.location.href).searchParams.get('keywords') document.body.style.width = '100%';
var history_keywords = localStorage.getItem('header_search_keywords');
if (!history_keywords) {
history_keywords = [];
} else {
history_keywords = JSON.parse(history_keywords);
} }
// 记录搜索关键词 // 恢复body滚动
if (keywords) { function enableBodyScroll() {
if (history_keywords.includes(keywords)) { const scrollY = document.body.style.top;
history_keywords.splice(history_keywords.indexOf(keywords), 1); document.body.style.overflow = '';
document.body.style.position = '';
document.body.style.top = '';
document.body.style.width = '';
if (scrollY) {
window.scrollTo(0, parseInt(scrollY || '0') * -1);
} }
history_keywords.unshift(keywords);
if (history_keywords.length > 3) {
history_keywords.pop();
}
localStorage.setItem('header_search_keywords', JSON.stringify(history_keywords));
} }
// 回显搜索历史 // 复位所有子菜单(关闭并重置滚动位置)
history_keywords.forEach(function(item) { function resetAllSubMenus() {
$('#history').append( let hasOpenMenu = false;
$('<a>') document.querySelectorAll('.has-child .sub-menu').forEach(function(subMenu) {
.css({ if (subMenu.classList.contains('show')) {
'margin-right': '10px' hasOpenMenu = true;
}) }
.attr('href', '{:url("product/search")}?keywords=' + item) subMenu.classList.remove('show');
.text(item) // 重置滚动位置到顶部
); subMenu.scrollTop = 0;
});
document.querySelectorAll('.has-child').forEach(function(item) {
item.classList.remove('open');
});
// 如果没有打开的菜单恢复body滚动
if (!hasOpenMenu) {
enableBodyScroll();
}
}
// 关闭单个子菜单并重置滚动位置
function closeSubMenu(subMenu, menuItem) {
if (subMenu) {
subMenu.classList.remove('show');
subMenu.scrollTop = 0; // 重置滚动位置
}
if (menuItem) {
menuItem.classList.remove('open');
}
// 检查是否还有其他打开的菜单
const anyOpen = document.querySelector('.has-child .sub-menu.show');
if (!anyOpen) {
enableBodyScroll();
}
}
// 打开子菜单
function openSubMenu(subMenu, menuItem) {
// 禁止body滚动
disableBodyScroll();
subMenu.classList.add('show');
menuItem.classList.add('open');
// 确保新打开的菜单滚动位置在顶部
subMenu.scrollTop = 0;
}
// 打开菜单
navBtn.addEventListener('click', function (e)
{
e.stopPropagation();
// 禁止body滚动
disableBodyScroll();
// 关闭所有弹窗
closeAllModals();
dropdownMenu.classList.add('show');
navBtn.style.display = 'none';
navBtn1.style.display = 'block';
}); });
return history_keywords; // 关闭菜单 - 复位所有子菜单
} navBtn1.addEventListener('click', function (e)
{
e.stopPropagation();
dropdownMenu.classList.remove('show');
navBtn.style.display = 'block';
navBtn1.style.display = 'none';
// 复位所有子菜单到初始状态
resetAllSubMenus();
});
// ====================== 处理所有菜单项(同一时间只展开一个) ======================
document.querySelectorAll('.menu-item').forEach(function (item)
{
const isHasChild = item.classList.contains('has-child');
const subMenu = item.querySelector('.sub-menu');
const box = item.querySelector('.menu-item-box');
if (isHasChild && subMenu) {
// 移除原有事件,重新绑定(确保只有一个展开)
const newBox = box.cloneNode(true);
box.parentNode.replaceChild(newBox, box);
newBox.addEventListener('click', function (e)
{
e.stopPropagation();
// 检查当前是否已展开
const isOpen = subMenu.classList.contains('show');
// 关闭所有其他子菜单并重置滚动位置
document.querySelectorAll('.has-child').forEach(function (otherItem) {
const otherSubMenu = otherItem.querySelector('.sub-menu');
if (otherSubMenu && otherSubMenu !== subMenu) {
otherSubMenu.classList.remove('show');
otherSubMenu.scrollTop = 0; // 重置滚动位置
otherItem.classList.remove('open');
}
});
// 切换当前菜单
if (!isOpen) {
openSubMenu(subMenu, item);
} else {
closeSubMenu(subMenu, item);
}
});
} else {
const link = item.getAttribute('data-link');
if (link) {
item.addEventListener('click', function (e)
{
e.stopPropagation();
window.location.href = link;
});
}
}
});
// 阻止子菜单链接冒泡
document.querySelectorAll('.sub-item').forEach(function (sub)
{
sub.addEventListener('click', function (e)
{
e.stopPropagation();
});
});
dropdownMenu.addEventListener('click', function (e)
{
e.stopPropagation();
});
navBtn1.style.display = 'none';
navBtn.style.display = 'block';
// 关闭所有弹窗的公共函数
function closeAllModals ()
{
cartModal.classList.remove('show');
langModal.classList.remove('show');
searchModal.classList.remove('show');
if (searchInput) {
searchInput.value = '';
if (searchClearBtn) searchClearBtn.style.display = 'none';
}
}
// ====================== 购物车弹窗交互 ======================
const cardBtn = document.querySelector('.nav-card');
const cartModal = document.getElementById('cartModal');
const cartModalClose = cartModal.querySelector('.modal-close');
cardBtn.addEventListener('click', function (e)
{
e.stopPropagation();
// 关闭下拉菜单和其他弹窗
dropdownMenu.classList.remove('show');
navBtn.style.display = 'block';
navBtn1.style.display = 'none';
closeAllModals();
// 复位所有子菜单
resetAllSubMenus();
cartModal.classList.add('show');
});
cartModalClose.addEventListener('click', function ()
{
cartModal.classList.remove('show');
});
cartModal.addEventListener('click', function (e)
{
if (e.target === cartModal) {
cartModal.classList.remove('show');
}
});
// ====================== 语言弹窗交互 ======================
const langBtn = document.querySelector('.nav-lang');
const langModal = document.getElementById('langModal');
const langModalClose = langModal.querySelector('.modal-close');
langBtn.addEventListener('click', function (e)
{
e.stopPropagation();
// 关闭下拉菜单和其他弹窗
dropdownMenu.classList.remove('show');
navBtn.style.display = 'block';
navBtn1.style.display = 'none';
closeAllModals();
// 复位所有子菜单
resetAllSubMenus();
langModal.classList.add('show');
});
langModalClose.addEventListener('click', function ()
{
langModal.classList.remove('show');
});
langModal.addEventListener('click', function (e)
{
if (e.target === langModal) {
langModal.classList.remove('show');
}
});
document.querySelectorAll('.lang-item').forEach(function (item)
{
item.addEventListener('click', function (e)
{
e.stopPropagation();
console.log('选择了语言:', this.innerText);
langModal.classList.remove('show');
});
});
// ====================== 搜索记录功能 ======================
// 获取搜索记录
function getSearchHistory ()
{
const history = localStorage.getItem('searchHistory');
return history ? JSON.parse(history) : [];
}
// 保存搜索记录
function saveSearchHistory (history)
{
localStorage.setItem('searchHistory', JSON.stringify(history));
}
// 添加搜索记录
function addSearchHistory (keyword)
{
if (!keyword || !keyword.trim()) return;
keyword = keyword.trim();
let history = getSearchHistory();
history = history.filter(item => item !== keyword);
history.unshift(keyword);
if (history.length > 10) {
history = history.slice(0, 10);
}
saveSearchHistory(history);
renderSearchHistory();
}
// 删除单条搜索记录
function deleteSearchHistoryItem (keyword)
{
let history = getSearchHistory();
history = history.filter(item => item !== keyword);
saveSearchHistory(history);
renderSearchHistory();
}
// 清空所有搜索记录
function clearAllSearchHistory ()
{
saveSearchHistory([]);
renderSearchHistory();
}
// 渲染搜索记录列表
function renderSearchHistory ()
{
const historyList = document.getElementById('searchHistoryList');
const history = getSearchHistory();
if (!historyList) return;
if (history.length === 0) {
historyList.innerHTML = '<div class="empty-history">暂无搜索记录</div>';
return;
}
historyList.innerHTML = '';
history.forEach(function (keyword)
{
const itemDiv = document.createElement('div');
itemDiv.className = 'search-history-item';
itemDiv.innerHTML = `
<span>${escapeHtml(keyword)}</span>
<img src="__IMAGES__/header/x.png" class="delete-icon" data-keyword="${escapeHtml(keyword)}">
`;
itemDiv.addEventListener('click', function (e)
{
if (e.target.classList && e.target.classList.contains('delete-icon')) {
e.stopPropagation();
deleteSearchHistoryItem(keyword);
} else {
e.stopPropagation();
searchInput.value = keyword;
doSearch(keyword);
}
});
historyList.appendChild(itemDiv);
});
}
// 简单的防XSS
function escapeHtml (str)
{
return str.replace(/[&<>]/g, function (m)
{
if (m === '&') return '&amp;';
if (m === '<') return '&lt;';
if (m === '>') return '&gt;';
return m;
});
}
// ====================== 搜索弹窗交互 ======================
const searchBtn = document.querySelector('.nav-search');
const searchModal = document.getElementById('searchModal');
const searchModalClose = searchModal.querySelector('.modal-close');
const searchInput = document.getElementById('searchInput');
const searchSubmit = document.getElementById('searchSubmit');
const searchClearBtn = document.getElementById('searchClearBtn');
const clearAllHistoryBtn = document.getElementById('clearAllHistory');
searchInput.addEventListener('input', function (e)
{
if (searchInput.value.length > 0) {
searchClearBtn.style.display = 'block';
} else {
searchClearBtn.style.display = 'none';
}
});
searchClearBtn.addEventListener('click', function (e)
{
e.stopPropagation();
searchInput.value = '';
searchClearBtn.style.display = 'none';
searchInput.focus();
});
if (clearAllHistoryBtn) {
clearAllHistoryBtn.addEventListener('click', function (e)
{
e.stopPropagation();
clearAllSearchHistory();
});
}
searchBtn.addEventListener('click', function (e)
{
e.stopPropagation();
// 关闭下拉菜单和其他弹窗
dropdownMenu.classList.remove('show');
navBtn.style.display = 'block';
navBtn1.style.display = 'none';
closeAllModals();
// 复位所有子菜单
resetAllSubMenus();
renderSearchHistory();
searchModal.classList.add('show');
setTimeout(() =>
{
searchInput.focus();
}, 100);
});
searchModalClose.addEventListener('click', function ()
{
searchModal.classList.remove('show');
searchInput.value = '';
searchClearBtn.style.display = 'none';
});
searchModal.addEventListener('click', function (e)
{
if (e.target === searchModal) {
searchModal.classList.remove('show');
searchInput.value = '';
searchClearBtn.style.display = 'none';
}
});
// 执行搜索
function doSearch (keyword)
{
if (keyword && keyword.trim()) {
const searchKeyword = keyword.trim();
console.log('搜索关键词:', searchKeyword);
if (searchKeyword) {
addSearchHistory(searchKeyword);
window.location.href = "{:url('product/search')}?keywords=" + encodeURIComponent(searchKeyword);
searchModal.classList.remove('show');
searchInput.value = '';
searchClearBtn.style.display = 'none';
}
}
}
searchSubmit.addEventListener('click', function (e)
{
e.stopPropagation();
doSearch(searchInput.value);
});
searchInput.addEventListener('keypress', function (e)
{
console.log(e,'========e=============')
if (e.key === 'Enter' || e.key === 'enter' || e.keyCode === 13) {
e.preventDefault();
doSearch(searchInput.value);
}
});
// ====================== 添加:点击空白区域关闭所有子菜单 ======================
document.addEventListener('click', function(e) {
// 如果点击的不是菜单项内部,关闭所有子菜单
if (!e.target.closest('.menu-item')) {
resetAllSubMenus();
}
});
// ====================== 购物车弹窗悬浮图功能 ======================
// 处理购物车弹窗中的图片悬浮效果
function initCartModalHover() {
const modalItems = document.querySelectorAll('#cartModal .modal-item');
modalItems.forEach(function(item) {
// 查找当前item中是否有悬浮图隐藏的那个
const defaultImg = item.querySelector('.modal-item-img');
const hiddenItem = item.nextElementSibling;
let hoverImg = null;
// 检查下一个元素是否是隐藏的悬浮图容器
if (hiddenItem && hiddenItem.classList && hiddenItem.classList.contains('modal-item') && hiddenItem.style.display === 'none') {
hoverImg = hiddenItem.querySelector('.modal-item-img');
}
// 如果有悬浮图
if (hoverImg && defaultImg) {
const originalSrc = defaultImg.src;
const hoverSrc = hoverImg.src;
const link = item.getAttribute('data-link');
// 移除原有的href因为需要悬浮图效果
item.style.cursor = 'pointer';
// 鼠标移入:显示悬浮图
item.addEventListener('mouseenter', function(e) {
e.stopPropagation();
defaultImg.src = hoverSrc;
});
// 鼠标移出:恢复普通图
item.addEventListener('mouseleave', function(e) {
e.stopPropagation();
defaultImg.src = originalSrc;
});
// 点击事件:如果有链接就跳转
item.addEventListener('click', function(e) {
e.stopPropagation();
if (link && link !== '#') {
window.location.href = link;
}
});
} else {
// 没有悬浮图,保持原有链接
const link = item.getAttribute('href') || item.getAttribute('data-link');
if (link && link !== '#') {
item.addEventListener('click', function(e) {
e.stopPropagation();
window.location.href = link;
});
}
}
});
}
// 在购物车弹窗打开时初始化悬浮图功能
const cartModalObserver = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.attributeName === 'class') {
if (cartModal.classList.contains('show')) {
setTimeout(function() {
initCartModalHover();
}, 50);
}
}
});
});
if (cartModal) {
cartModalObserver.observe(cartModal, { attributes: true });
// 如果弹窗默认是打开的,也初始化一次
if (cartModal.classList.contains('show')) {
initCartModalHover();
}
}
});
</script> </script>

View File

@@ -48,7 +48,7 @@
{block name="main"} {block name="main"}
{notempty name="data.top_focus_images"} {notempty name="data.top_focus_images"}
<!-- 顶部轮播 --> <!-- 顶部轮播 -->
<div class="swiper-container auto-swiper-container" style="margin-top:60px;"> <div class="swiper-container auto-swiper-container" style="margin-top:42px;">
<div class="swiper-wrapper"> <div class="swiper-wrapper">
{volist name="data.top_focus_images" id="tfi"} {volist name="data.top_focus_images" id="tfi"}
<div class="swiper-slide auto-swiper-slide"> <div class="swiper-slide auto-swiper-slide">
@@ -1082,18 +1082,18 @@
const allVideos = document.querySelectorAll('.fs-video'); const allVideos = document.querySelectorAll('.fs-video');
const fsBox = document.querySelector('.fs-box'); const fsBox = document.querySelector('.fs-box');
let isVideoPlaying = false; let isVideoPlaying = false;
let videoTip = null; // let videoTip = null;
// 初始化视频提示 // 初始化视频提示
const initVideoTip = () => // const initVideoTip = () =>
{ // {
if (!fsBox || videoTip) return; // if (!fsBox || videoTip) return;
videoTip = document.createElement('div'); // videoTip = document.createElement('div');
videoTip.className = 'video-tip'; // videoTip.className = 'video-tip';
videoTip.innerText = '点击播放视频'; // videoTip.innerText = '点击播放视频';
fsBox.appendChild(videoTip); // fsBox.appendChild(videoTip);
videoTip.style.display = 'none'; // videoTip.style.display = 'none';
}; // };
// 停止所有视频(仅暂停,无显隐) // 停止所有视频(仅暂停,无显隐)
function stopAllVideos () function stopAllVideos ()
@@ -1103,7 +1103,7 @@
video.pause(); // 仅暂停删除display修改 video.pause(); // 仅暂停删除display修改
}); });
isVideoPlaying = false; isVideoPlaying = false;
if (videoTip) videoTip.style.display = 'none'; // if (videoTip) videoTip.style.display = 'none';
} }
// 播放指定视频(仅播放,无显隐) // 播放指定视频(仅播放,无显隐)
@@ -1117,17 +1117,17 @@
video.play().then(() => video.play().then(() =>
{ {
isVideoPlaying = true; isVideoPlaying = true;
if (videoTip) videoTip.style.display = 'none'; // if (videoTip) videoTip.style.display = 'none';
}).catch((err) => }).catch((err) =>
{ {
console.log('视频自动播放失败:', err); console.log('视频自动播放失败:', err);
if (videoTip) videoTip.style.display = 'block'; // if (videoTip) videoTip.style.display = 'block';
// 点击视频手动播放(仅执行一次) // 点击视频手动播放(仅执行一次)
video.addEventListener('click', () => video.addEventListener('click', () =>
{ {
video.play(); video.play();
isVideoPlaying = true; isVideoPlaying = true;
if (videoTip) videoTip.style.display = 'none'; // if (videoTip) videoTip.style.display = 'none';
}, { once: true }); }, { once: true });
}); });
}; };
@@ -1180,7 +1180,7 @@
}; };
// 初始化执行 // 初始化执行
initVideoTip(); // initVideoTip();
window.addEventListener('scroll', handleVideoScroll, { passive: true }); window.addEventListener('scroll', handleVideoScroll, { passive: true });
window.addEventListener('resize', handleVideoResize); window.addEventListener('resize', handleVideoResize);
// 页面加载完成后尝试播放视频 // 页面加载完成后尝试播放视频

View File

@@ -31,15 +31,13 @@
<link rel="stylesheet" href="__CSS__/topic_power_prodline/footer.css"> <link rel="stylesheet" href="__CSS__/topic_power_prodline/footer.css">
{/block} {/block}
{block name="header"}
<!-- 重置header头为空 -->
{/block}
{block name="main"} {block name="main"}
<a class="header" href="/"> <!-- <a class="header" href="/">
<div class="header-img"> <div class="header-img">
<img src="__IMAGES__/logo.png" alt=""> <img src="__IMAGES__/logo.png" alt="">
</div> </div>
</a> </a> -->
<!-- 轮播核心容器 --> <!-- 轮播核心容器 -->
<div class="swiper-container auto-swiper-container" > <div class="swiper-container auto-swiper-container" >
{notempty name="data.focus_image"} {notempty name="data.focus_image"}

View File

@@ -9,7 +9,7 @@
<div class="pageMain"> <div class="pageMain">
<!-- banner --> <!-- banner -->
{notempty name="focus_images"} {notempty name="focus_images"}
<div class="swiper-container bannerswiper"> <div class="swiper-container bannerswiper" style="background: #fff;">
<div class="swiper-wrapper"> <div class="swiper-wrapper">
{volist name="focus_images" id="focus"} {volist name="focus_images" id="focus"}
<div class="swiper-slide"> <div class="swiper-slide">

View File

@@ -6,7 +6,7 @@
<div class="orico_Page_category"> <div class="orico_Page_category">
<!-- banner轮播 --> <!-- banner轮播 -->
{notempty name="focus_image"} {notempty name="focus_image"}
<div class="opdBanner"> <div class="opdBanner" >
{volist name="focus_image" id="fi"} {volist name="focus_image" id="fi"}
<a {notempty name="fi.link" }href="{$fi.link}" {/notempty}> <a {notempty name="fi.link" }href="{$fi.link}" {/notempty}>
<img src="{$fi.image}" class="opdbannerImg" /> <img src="{$fi.image}" class="opdbannerImg" />

File diff suppressed because it is too large Load Diff

View File

@@ -52,7 +52,7 @@
{/block} {/block}
{block name="main"} {block name="main"}
{notempty name="data.top_focus_images"} {notempty name="data.top_focus_images"}
<div class="swiper-container auto-swiper-container" style="margin-top:60px;"> <div class="swiper-container auto-swiper-container">
<div class="swiper-wrapper"> <div class="swiper-wrapper">
{volist name="data.top_focus_images" id="tfi"} {volist name="data.top_focus_images" id="tfi"}
<div class="swiper-slide auto-swiper-slide"> <div class="swiper-slide auto-swiper-slide">
@@ -1037,17 +1037,17 @@
const allVideos = document.querySelectorAll('.fs-video'); const allVideos = document.querySelectorAll('.fs-video');
const fsBox = document.querySelector('.fs-box'); const fsBox = document.querySelector('.fs-box');
let isVideoPlaying = false; let isVideoPlaying = false;
let videoTip = null; // let videoTip = null;
// 初始化视频提示 // 初始化视频提示
const initVideoTip = () => { // const initVideoTip = () => {
if (!fsBox || videoTip) return; // if (!fsBox || videoTip) return;
videoTip = document.createElement('div'); // videoTip = document.createElement('div');
videoTip.className = 'video-tip'; // videoTip.className = 'video-tip';
videoTip.innerText = '点击播放视频'; // videoTip.innerText = '点击播放视频';
fsBox.appendChild(videoTip); // fsBox.appendChild(videoTip);
videoTip.style.display = 'none'; // videoTip.style.display = 'none';
}; // };
// 停止所有视频 // 停止所有视频
function stopAllVideos () function stopAllVideos ()
@@ -1060,7 +1060,7 @@
if (img) img.style.display = 'block'; if (img) img.style.display = 'block';
}); });
isVideoPlaying = false; isVideoPlaying = false;
if (videoTip) videoTip.style.display = 'none'; // if (videoTip) videoTip.style.display = 'none';
} }
// 播放指定视频 // 播放指定视频
@@ -1075,7 +1075,7 @@
video.muted = true; video.muted = true;
video.play().then(() => { video.play().then(() => {
isVideoPlaying = true; isVideoPlaying = true;
if (videoTip) videoTip.style.display = 'none'; // if (videoTip) videoTip.style.display = 'none';
}).catch((err) => { }).catch((err) => {
console.log('视频自动播放失败:', err); console.log('视频自动播放失败:', err);
video.style.display = 'block'; video.style.display = 'block';
@@ -1085,7 +1085,7 @@
video.addEventListener('click', () => { video.addEventListener('click', () => {
video.play(); video.play();
isVideoPlaying = true; isVideoPlaying = true;
if (videoTip) videoTip.style.display = 'none'; // if (videoTip) videoTip.style.display = 'none';
}, { once: true }); }, { once: true });
}); });
}; };
@@ -1140,7 +1140,7 @@
}; };
// 初始化 // 初始化
initVideoTip(); // initVideoTip();
window.addEventListener('scroll', handleVideoScroll, { passive: true }); window.addEventListener('scroll', handleVideoScroll, { passive: true });
window.addEventListener('resize', handleVideoResize); window.addEventListener('resize', handleVideoResize);
// 页面加载完成后尝试播放 // 页面加载完成后尝试播放

View File

@@ -12,7 +12,7 @@
{/block} {/block}
{block name="main"} {block name="main"}
<!-- 轮播核心容器 --> <!-- 轮播核心容器 -->
<div class="swiper-container auto-swiper-container" > <div class="swiper-container auto-swiper-container">
{notempty name="data.focus_image"} {notempty name="data.focus_image"}
<div class="swiper-wrapper"> <div class="swiper-wrapper">
{volist name="data.focus_image" id="fo"} {volist name="data.focus_image" id="fo"}

View File

@@ -31,7 +31,9 @@ class CreateSysNavigationItem extends Migrator
$table->addColumn('nav_id', 'string', ['limit' => 64, 'null' => false, 'comment' => '所属导航ID']) $table->addColumn('nav_id', 'string', ['limit' => 64, 'null' => false, 'comment' => '所属导航ID'])
->addColumn('pid', 'integer', ['null' => false, 'default' => 0, 'comment' => '父级ID']) ->addColumn('pid', 'integer', ['null' => false, 'default' => 0, 'comment' => '父级ID'])
->addColumn('name', 'string', ['limit' => 64, 'null' => false, 'comment' => '名称']) ->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('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_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('link', 'string', ['limit' => 255, 'null' => true, 'default' => null, 'comment' => '链接'])
->addColumn('sort', 'integer', ['limit' => 11, 'null' => false, 'default' => 0, 'comment' => '排序']) ->addColumn('sort', 'integer', ['limit' => 11, 'null' => false, 'default' => 0, 'comment' => '排序'])

View File

@@ -0,0 +1,50 @@
<?php
use think\migration\Migrator;
use think\migration\db\Column;
class CreateProductCategoryRecommend extends Migrator
{
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
*
* The following commands can be used in this method and Phinx will
* automatically reverse them when rolling back:
*
* createTable
* renameTable
* addColumn
* renameColumn
* addIndex
* addForeignKey
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*/
public function change()
{
$table = $this->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();
}
}

View File

@@ -0,0 +1,50 @@
<?php
use think\migration\Migrator;
use think\migration\db\Column;
class SysMallStoreEntrance extends Migrator
{
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
*
* The following commands can be used in this method and Phinx will
* automatically reverse them when rolling back:
*
* createTable
* renameTable
* addColumn
* renameColumn
* addIndex
* addForeignKey
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*/
public function change()
{
$table = $this->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();
}
}

View File

@@ -6,7 +6,7 @@
align-items: center; align-items: center;
position: relative; position: relative;
overflow: auto; overflow: auto;
padding-top: 50px; padding-top: 46px;
} }
.iotb_bgw { .iotb_bgw {

View File

@@ -0,0 +1,246 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: sans-serif;
font-size: inherit !important;
/* font-size:100% !important; */
}
body {
background: #f5f5f5;
font-size: inherit !important;
/* font-size:100% !important; */
height: 100vh;
overflow: hidden;
max-width: 100vw !important;
}
.top-box {
width: 100%;
box-sizing: border-box;
padding: 20px 22px;
background: #fff;
margin-top: 46px;
/* border-bottom: 1px solid #f0f0f0; */
box-shadow: 0 2px 8px rgba(217, 217, 217, 0.2);
}
.top-bar {
display: flex;
align-items: center;
height: 0.82rem;
background: #F1F2F5;
justify-content: space-between;
border-radius: 0.2rem;
padding: 0 16px;
}
.back {
width: 0.28rem;
height: 0.28rem;
color: #333;
cursor: pointer;
display: flex;
align-items: center;
}
.back img {
width: 0.28rem;
height: 0.28rem;
}
.title {
font-size: 0.32rem !important;
font-weight: 500;
}
/* 主体布局 */
.main {
display: flex;
height: calc(100% - 46px);
margin-top:0.018rem;
}
/* 左侧菜单 #FAFAFA;*/
.sidebar {
width: 114px;
background:#fafafa;
overflow-y: auto;
box-sizing: border-box;
}
.sidebar li {
list-style: none;
padding: 24px 16px;
text-align: center;
font-size: 0.28rem !important;
color:#686A70; /* #686A70;*/
cursor: pointer;
background:#fff;
/* 分别控制上下圆角的过渡 */
border-top-right-radius: 0;
border-bottom-right-radius: 0;
transition: border-radius 1s cubic-bezier(0.4, 0, 0.2, 1);
}
.sidebar li.active {
color: #004BFA;
background: #fafafa;
}
.sidebar li.active-prev {
border-bottom-right-radius: 0.17rem !important;
}
.sidebar li.active-next {
border-top-right-radius: 0.17rem !important;
}
/* 右侧内容 */
.right-content {
flex: 1;
overflow-y: auto;
padding-left: 12px;
background: #fafafa;
/* padding-bottom: 100px; */
}
.right-content {
flex: 1;
overflow-y: auto;
padding-left: 12px;
background: #fafafa;
position: relative;
}
/* 在底部添加占位元素 */
.right-content::after {
content: '';
display: block;
height: 80px; /* 根据需要调整 */
width: 100%;
flex-shrink: 0;
}
/* 最后一个元素底部留白 */
.sec-box:last-child {
margin-bottom: 80px;
}
.sec-header {
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 24px;
padding-right: 17px;
padding-bottom: 12px;
}
.sec-title {
font-size: 0.28rem !important;
color: #686A70;
font-weight: 500
}
.sec-arrow {
width: 0.28rem;
height: 0.28rem;
display: flex;
align-items: center;
justify-content: center;
color: #666;
font-size: 20px;
cursor: pointer;
}
.sec-arrow img {
width: 0.28rem;
height: 0.28rem;
}
/* 横向滚动容器 */
.scroll-box {
display: flex;
gap: 10px;
overflow-x: auto;
scroll-behavior: smooth;
scrollbar-width: none;
}
.scroll-box::-webkit-scrollbar {
display: none
}
.right-card {
flex-shrink: 0;
background: #fff;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
display: flex;
flex-direction: column;
align-items: center;
padding-top: 8px;
}
.card {
display: flex;
flex-direction: column;
align-items: center;
border-radius: 12px;
padding-left: 19px;
padding-right: 19px;
}
.card-img {
width: 1.52rem;
background: #f1f1f1;
display: flex;
align-items: center;
justify-content: center;
color: #999;
margin-top: 8px;
}
.card-img img {
width: 100%;
}
.card-info {
text-align: center;
padding-top: 8px;
padding-bottom: 10px;
}
.card-name {
font-size: 0.2rem !important;
width: 1.52rem;
height: auto;
color: #1D1D1F;
line-height: 1.4;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.card-model {
font-size: 0.16rem !important;
color: #686A70;
margin-top: 4px
}
/* 内容区块默认隐藏 */
.tab-pane {
display: none
}
/* 激活时显示 */
.tab-pane.active {
display: block;
}

View File

@@ -156,7 +156,7 @@
align-items: center; align-items: center;
position: relative; position: relative;
overflow: auto; overflow: auto;
padding-top: 50px; padding-top: 46px;
} }
.iotb_bgw { .iotb_bgw {

View File

@@ -156,7 +156,7 @@
align-items: center; align-items: center;
position: relative; position: relative;
overflow: auto; overflow: auto;
padding-top: 50px; padding-top: 46px;
} }
.iotb_bgw { .iotb_bgw {

View File

@@ -13,7 +13,7 @@
overflow-x: hidden; overflow-x: hidden;
} }
.oricoEGapp .oricoEGapp-index .oidx-banner { .oricoEGapp .oricoEGapp-index .oidx-banner {
margin-top: 3.5rem; margin-top: 46px;
z-index: 9; z-index: 9;
} }
.oricoEGapp .oricoEGapp-index .oidx-banner .swiper-container { .oricoEGapp .oricoEGapp-index .oidx-banner .swiper-container {

View File

@@ -0,0 +1,457 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
a {
text-decoration: none;
}
.mobile-header-box {
display: flex;
justify-content: space-between;
align-items: center;
height: 46px;
padding: 0 17px;
background: #fff;
/* position: relative; */
position: fixed;
top:0;
left:0;
z-index: 100;
width: 100%;
}
.mobile-header-left {
display: flex;
align-items: center;
}
.nav-img,
.nav-search,
.nav-lang,
.nav-img1,
.nav-card {
width: 18px;
height: 18px;
cursor: pointer;
}
.nav-log {
width: 90px;
height: 22px;
margin-left: 14px;
cursor: pointer;
}
.nav-lang {
margin: 0 18px;
cursor: pointer;
}
/* 下拉菜单 */
.nav-dropdown-menu {
width: 100%;
background: #fff;
border-top: 1px solid #eee;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
display: none;
position: fixed;
top: 46px;
left: 0;
z-index: 99;
}
.nav-dropdown-menu.show {
display: block;
}
/* 所有菜单项统一用 div */
.menu-item {
margin: 0 22px;
font-size: 15px !important;
color: #333;
border-bottom: 1px solid #f5f5f5;
cursor: pointer;
position: relative;
}
.menu-item-box {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 0;
}
.menu-item-img {
width: 14px;
height: 14px;
display: block;
}
/* 有子菜单才显示箭头 */
.has-child .menu-item-img {
display: block;
}
/* 无子菜单隐藏箭头 */
.no-child .menu-item-img {
display: none;
}
/* 箭头旋转 */
.has-child.open .menu-item-img {
transform: rotate(180deg);
transition: transform 0.3s ease;
}
.nav-img1 {
display: none;
}
/* .sub-menu {
display: none;
} */
.sub-menu {
display: none;
/* height: 100vh; */
/* overflow-y: auto;
overflow-x: hidden;
-webkit-overflow-scrolling: touch; */
}
/* .sub-menu-overflow {
max-height: calc(100vh - 180px);
overflow-y: auto;
overflow-x: hidden;
-webkit-overflow-scrolling: touch;
} */
.sub-menu.show {
display: block;
}
.sub-item {
padding: 0 16px;
font-size: 14px !important;
margin: 16px 0;
}
.sub-item:nth-child(1) {
margin-top: 0;
}
.sub-item a {
color: #686A70;
display: block;
}
.sub-item:last-child {
padding-bottom: 0;
}
.sub-item-card-img {
width: 100%;
height: auto;
aspect-ratio: 358 / 192;
}
.sub-item-card-title {
font-size: 16px !important;
color: #1D1D1F;
margin-top: 10px;
}
.sub-item-card-name {
font-size: 14px !important;
color: #686A70;
margin-top: 4px;
margin-bottom: 24px;
}
.no-padding {
padding: 0;
}
/* ====================== 弹窗样式(不遮盖头部) ====================== */
.modal-overlay {
position: fixed;
top: 46px;
left: 0;
width: 100%;
height: calc(100% - 46px);
background: rgba(0, 0, 0, 0.5);
display: none;
justify-content: center;
align-items: flex-start;
z-index: 98;
}
.modal-overlay.show {
display: flex;
}
.modal-content {
width: 90%;
background: #fff;
border-radius: 12px;
padding: 66px 24px 8px 24px;
margin-top: 20px;
position: relative;
text-align: center;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
}
.modal-close {
position: absolute;
top: 24px;
right: 24px;
width: 18px;
height: 18px;
cursor: pointer;
}
/* 写法2使用 flex 布局实现一行2个 */
.modal-items-list {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.modal-item {
width: calc(50% - 16px);
font-size: 16px !important;
color: #1D1D1F;
cursor: pointer;
text-align: center;
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 32px;
}
.modal-item-img {
width: clamp(48px, 17vw, 128px);
height: auto;
aspect-ratio: 64 / 64;
object-fit: contain;
display: block;
}
.modal-item-title {
margin-top: 10px;
font-size: 16px !important;
color: #1D1D1F;
}
.modal-item:hover {
/* background-color: #f5f5f5; */
border-radius: 8px;
}
/* 语言弹窗样式 - 列表布局 */
.lang-modal-content {
width: 90%;
background: #fff;
border-radius: 12px;
padding: 68px 24px;
margin-top: 20px;
position: relative;
text-align: center;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
}
.lang-items-list {
display: flex;
flex-direction: column;
}
.lang-item {
font-size: 16px !important;
color: #1D1D1F;
cursor: pointer;
display: flex;
align-items: center;
}
.lang-item img {
width: 29px;
height: 20px;
margin-right: 16px;
display: block;
}
.lang-item:last-child {
border-bottom: none;
margin-top: 24px;
}
.lang-item:hover {
background-color: #f5f5f5;
border-radius: 8px;
}
/* 搜索弹窗样式 */
.search-modal-content {
width: 90%;
background: #fff;
border-radius: 12px;
padding: 68px 24px;
margin-top: 20px;
position: relative;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
}
.search-input-box {
display: flex;
align-items: center;
border: 1px solid #e0e0e0;
border-radius: 30px;
padding-left: 24px;
/* padding-right: 13px; */
background: #f8f8f8;
}
.search-input-box input {
border: none;
outline: none;
background: transparent;
font-size: 14px !important;
padding: 10px 0;
flex:1;
}
.search-input-box input::placeholder {
color: #aaa;
}
.search-input-box .search-clear-btn {
width: 14px;
height: 14px;
cursor: pointer;
opacity: 0.6;
}
.search-clear-box {
padding-right: 10px;
padding-left: 10px;
}
/* .search-clear-btn {
border-right: 1px solid #eee;
} */
.search-input-box .search-clear-btn:hover {
opacity: 1;
}
.search-input-box .search-submit-icon {
width: 18px;
height: 18px;
cursor: pointer;
box-sizing: border-box;
}
.search-submit-box {
border-left: 1px solid #d9d9d9;
padding-left:10px;
padding-right: 13px;
max-height: 18px;
}
.search-history {
margin-top: 20px;
}
.search-history-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.search-history-title {
font-size: 14px !important;
color: #888;
}
.search-history-clear {
font-size: 12px !important;
color: #999;
cursor: pointer;
}
.search-history-clear:hover {
color: #666;
}
.search-history-list {
display: flex;
flex-direction: column;
}
.search-history-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 0;
border-bottom: 1px solid #f0f0f0;
cursor: pointer;
}
.search-history-item:hover {
background-color: #f5f5f5;
border-radius: 8px;
padding-left: 8px;
padding-right: 8px;
margin-left: -8px;
margin-right: -8px;
}
.search-history-item span {
font-size: 14px !important;
color: #333;
}
.search-history-item .delete-icon {
width: 14px;
height: 14px;
cursor: pointer;
opacity: 0.5;
}
.search-history-item .delete-icon:hover {
opacity: 1;
}
.empty-history {
text-align: center;
padding: 40px 0;
font-size: 14px !important;
color: #aaa;
}
.mobile-header-right {
display: flex;
align-items: center;
}
/* 产品列表的子菜单不需要滚动 */
.menu-item:first-child .sub-menu {
max-height: none !important;
overflow-y: visible !important;
}
/* 其他导航的子菜单使用JS动态设置高度
.has-child:not(:first-child) .sub-menu {
overflow-y: auto;
overflow-x: hidden;
-webkit-overflow-scrolling: touch;
} */

View File

@@ -2,7 +2,7 @@
position: relative; position: relative;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
margin-top: 3.4rem; margin-top:46px;
} }
.oricoEGapp-newarrival .ona-topimg { .oricoEGapp-newarrival .ona-topimg {
max-width: 100%; max-width: 100%;

View File

@@ -9,7 +9,7 @@
/* max-height: 900px; */ /* max-height: 900px; */
min-height: 300px; min-height: 300px;
position: relative; position: relative;
margin-top:60px; margin-top:46px;
} }
/* 轮播项 - 填充容器高度 */ /* 轮播项 - 填充容器高度 */

Binary file not shown.

After

Width:  |  Height:  |  Size: 356 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 991 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 271 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1021 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 490 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 521 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 271 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 301 B

File diff suppressed because it is too large Load Diff

View File

@@ -17,7 +17,6 @@
width: 100%; width: 100%;
height: auto; height: auto;
position: relative; position: relative;
margin-top: 3.75rem;
} }
.orico_Page_index .pageMain .bannerswiper .swiper-slide img { .orico_Page_index .pageMain .bannerswiper .swiper-slide img {

View File

@@ -36,7 +36,7 @@
flex-direction: column; flex-direction: column;
padding: 2.5rem 0 1.375rem 0; padding: 2.5rem 0 1.375rem 0;
position: relative; position: relative;
margin-top: 3%; /* margin-top: 3%; */
} }
.orico_Page_prdetail .oriprdetail .product_address { .orico_Page_prdetail .oriprdetail .product_address {
width: 70%; width: 70%;

View File

@@ -27,7 +27,7 @@
} }
.orico_Page_subcategory .pageMain { .orico_Page_subcategory .pageMain {
width: 85%; width: 85%;
padding: 7.5rem 0 10px; padding: 54px 0 10px;
margin: 0 auto; margin: 0 auto;
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@@ -1,406 +1,13 @@
@charset "UTF-8"; .header {
/* 全局文字最小尺寸兜底 */ max-width: 100% !important;
.header-PC { width: 100% !important;
width: 100%; display: flex;
justify-content: center;
height: 60px;
background: #fff;
display: flex;
justify-content: center;
position: fixed;
top: 0;
z-index: 100;
} }
.header-PC * { .header-nav-outer {
min-font-size: 16px !important; /* 强制最小16px */ max-width: 1920px;
box-sizing: border-box; width: 100%;
margin:auto 0 !important;
} }
.header-PC #header {
margin: 0 auto;
height: 60px; /* 0.6rem*100=60px */
max-width: var(--max-width);
/* position: fixed;
top: 0; */
display: flex;
flex-direction: row;
align-items: center;
z-index: 999;
background: white;
color: black;
width: 100%;
font-size: 16px; /* 基础字号16px */
}
.header-PC #header .nav1 {
position: relative;
width: 20%;
}
.header-PC #header .nav1 img {
width: 45%;
margin-left: 40%;
}
.header-PC #header .nav2 {
position: relative;
width: 60%;
}
.header-PC #header .nav2 .navItem {
font-size: 16px; /* 强制16px */
position: relative;
display: flex;
flex-direction: row;
align-items: center;
flex-wrap: wrap;
justify-content: center;
width: 12.5%;
height: 60px; /* 0.6rem*100=60px */
text-align: center;
float: left;
text-decoration: none;
transition: all 0.3s ease;
-webkit-transition: all 0.5s ease;
}
.header-PC #header .nav2 .navItem a {
padding-right: 5px; /* 0.05rem*100=5px */
padding-left: 20px; /* 0.2rem*100=20px */
text-decoration: none;
word-break: keep-all;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
word-wrap: break-word;
text-wrap: nowrap;
font-size: 16px; /* 确保16px */
}
.header-PC #header .nav2 .navItem .downimg {
height: 12px; /* 0.12rem*100=12px */
}
.header-PC #header .nav2 .navItem .navItemConten {
width: 100%;
z-index: 999;
background-color: #f2f2f2;
max-height: 660px; /* 6.6rem*100=660px */
box-shadow: 3px 5px 60px -20px #88909a; /* 0.03/0.05/0.6/-0.2rem 转px */
position: fixed;
border: 1px solid lightgray;
top: 60px; /* 0.6rem*100=60px */
transition: max-height 0.5s ease-out, opacity 0.5s ease-out;
left: 0;
display: none;
font-size: 16px; /* 基础字号16px */
}
.header-PC #header .nav2 .navItem .navItemConten .navItem_cyleft {
float: left;
text-align: center;
width: 20%;
max-height: 660px; /* 6.6rem*100=660px */
font-size: 16px; /* 强制16px */
}
.header-PC #header .nav2 .navItem .navItemConten .navItem_cyleft li {
cursor: pointer;
zoom: 1;
clear: both;
border: 1px solid transparent;
}
.header-PC #header .nav2 .navItem .navItemConten .navItem_cyleft li a {
line-height: 44px; /* 0.44rem*100=44px */
color: #656a6d;
font-size: 16px; /* 确保16px */
}
.header-PC #header .nav2 .navItem .navItemConten .navItem_cyleft li a:hover {
color: #004bfa;
}
.header-PC #header .nav2 .navItem .navItemConten .navItem_cyleft li.it_active {
border-color: #dddddd;
background-color: #ffffff;
}
.header-PC #header .nav2 .navItem .navItemConten .navItem_cyright {
max-height: 660px; /* 6.6rem*100=660px */
min-height: 460px; /* 4.6rem*100=460px */
overflow-y: auto;
border-left: 1px solid #dddddd;
font-weight: normal;
background-color: #fff;
margin: 0 auto;
box-shadow: -3px 0 0px #f3f3f3; /* -0.03rem*100=-3px */
text-align: left;
}
.header-PC #header .nav2 .navItem .navItemConten .navItem_cyright .nav_cyrightit dt {
font-size: 16px; /* 原0.14rem=14px → 提升至16px */
line-height: 16px; /* 0.16rem*100=16px */
margin-inline-start: 20px; /* 0.2rem*100=20px */
font-weight: 600;
border-bottom: 1px solid rgba(225, 225, 225, 0.5);
padding-bottom: 13px; /* 0.13rem*100=13px */
padding-top: 16px; /* 0.16rem*100=16px */
}
.header-PC #header .nav2 .navItem .navItemConten .navItem_cyright .nav_cyrightit dt img {
height: 16px; /* 0.16rem*100=16px */
max-width: 100%;
}
.header-PC #header .nav2 .navItem .navItemConten .navItem_cyright .nav_cyrightit dt a {
color: #333;
text-decoration: none;
word-break: keep-all;
overflow: hidden;
text-overflow: ellipsis;
word-wrap: break-word;
text-wrap: nowrap;
font-size: 16px; /* 确保16px */
}
.header-PC #header .nav2 .navItem .navItemConten .navItem_cyright .nav_cyrightit dd {
font-size: 16px; /* 原0.14rem=14px → 提升至16px */
line-height: 40px; /* 0.4rem*100=40px */
padding-top: 0vw;
font-weight: 100;
display: inline-block;
margin-right: 3%;
margin-inline-start: 40px; /* 0.4rem*100=40px */
}
.header-PC #header .nav2 .navItem .navItemConten .navItem_cyright .nav_cyrightit dd a {
font-size: 16px; /* 确保16px */
}
.header-PC #header .nav2 .navItem .navItemConten .navItem_cyright .nav_cyrightit dd a:hover {
color: #004bf9;
font-weight: 600;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
-webkit-transition: all 0.2s linear;
}
.header-PC #header .nav2 .navItem .navItemConten.active {
max-height: 660px; /* 6.6rem*100=660px */
opacity: 1;
}
.header-PC #header .nav2 .navItem .navItemConten1 {
background-color: #fff;
color: black;
position: absolute;
top: 60px; /* 0.6rem*100=60px */
left: 20px; /* 0.2rem*100=20px */
width: auto;
height: auto;
z-index: 9999;
border-radius: 5px; /* 0.05rem*100=5px */
box-shadow: 0 0 1px 0 #88909a; /* 0.01rem*100=1px */
display: none;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 8px 0; /* 0.08rem*100=8px */
font-size: 16px; /* 基础字号16px */
}
.header-PC #header .nav2 .navItem .navItemConten1 li {
color: #fff;
float: left;
text-align: center;
line-height: 24px; /* 0.24rem*100=24px */
padding: 8px 32px; /* 0.08/0.32rem*100=8/32px */
display: list-item;
}
.header-PC #header .nav2 .navItem .navItemConten1 li a {
cursor: pointer;
padding-left: 0;
white-space: nowrap;
font-size: 16px; /* 确保16px */
}
.header-PC #header .nav2 .navItem > a.active,
.header-PC #header .nav2 .navItem .navItemConten1 li a:hover {
color: #004bfa;
}
.header-PC #header .nav3 {
position: relative;
width: 20%;
background-color: transparent;
display: flex;
align-items: center;
cursor: pointer;
font-size: 16px; /* 基础字号16px */
}
.header-PC #header .nav3 .searchimg {
margin-left: 10%;
}
.header-PC #header .nav3 .storetopbt {
background: #004cfa;
color: #fff;
padding: 0 15px; /* 0.15rem*100=15px */
border-radius: 20px; /* 0.2rem*100=20px */
height: 38px; /* 0.38rem*100=38px */
line-height: 40px; /* 0.4rem*100=40px */
margin-left: 15%;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
font-size: 16px; /* 强制16px */
}
.header-PC #header .nav3 .storetopbt .storeImgico {
width: 20px; /* 0.2rem*100=20px */
margin-right: 8px; /* 0.08rem*100=8px */
}
.header-PC #header .nav3 .choesCountry {
position: relative;
margin-left: 15%;
display: flex;
}
.header-PC #header .nav3 .choesCountry .topCountry {
display: none;
width: 340px; /* 3.4rem*100=340px */
background-color: white;
/* position: fixed; */
position: absolute;
right: -150px;
top: 50px; /* 0.8rem*100=80px */
border-radius: 15px; /* 0.15rem*100=15px */
box-shadow: 2px 2px 10px 1px #88909a; /* 0.02/0.02/0.1/0.01rem 转px */
font-size: 16px; /* 基础字号16px */
}
.header-PC #header .nav3 .choesCountry .topCountry li {
width: 100%;
height: 50px; /* 0.5rem*100=50px */
line-height: 50px; /* 0.5rem*100=50px */
text-align: center;
display: flex;
}
.header-PC #header .nav3 .choesCountry .topCountry li .countryName {
width: 70%;
text-align: left;
margin-left: 10px; /* 0.1rem*100=10px */
font-size: 16px; /* 强制16px */
}
.header-PC #header .nav3 .choesCountry .topCountry li .cico {
width: 18%;
margin-left: 0;
}
.header-PC #header .nav3 .choesCountry .topCountry li .cico .countryimg {
margin-left: 0;
margin-left: 20px; /* 0.2rem*100=20px */
margin-top: 15px; /* 0.15rem*100=15px */
vertical-align: top;
}
.header-PC #header .nav3 .choesCountry .topCountry li.closec {
display: flex;
flex-direction: row;
justify-content: end;
height: 30px; /* 0.3rem*100=30px */
}
.header-PC #header .nav3 .choesCountry .topCountry .closecountrybt {
color: #aaa;
margin-top: -5px; /* 0.05rem*100=5px */
cursor: pointer;
height: 20px; /* 0.2rem*100=20px */
width: 20px; /* 0.2rem*100=20px */
margin-right: 10px; /* 0.1rem*100=10px */
font-size: 16px; /* 确保关闭按钮文字16px */
}
.header-PC .searchmodalMian {
position: fixed;
z-index: 999;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.7);
z-index: 998;
display: none;
}
.header-PC .searchmodalMian .searchmodalct {
background-color: #fff;
padding: 20px; /* 0.2rem*100=20px */
border-radius: 20px; /* 0.2rem*100=20px */
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); /* 0.04/0.08rem 转px */
border: 1px solid rgba(0, 0, 0, 0.2);
position: fixed;
right: 2%;
width: 33%;
top: 80px; /* 0.8rem*100=80px */
height: 80%;
overflow-y: auto;
z-index: 998;
font-size: 16px; /* 基础字号16px */
}
.header-PC .searchmodalMian .searchmodalct .close-btn {
color: #aaa;
float: right;
font-size: 24px; /* 0.24rem*100=24px */
cursor: pointer;
}
.header-PC .searchmodalMian .searchmodalct #serrchinput {
margin-left: 10%;
margin-top: 5%;
width: 80%;
height: 44px; /* 0.44rem*100=44px */
border: 1px solid grey;
border-radius: 22px; /* 0.22rem*100=22px */
background-position: 95% 50%;
padding-left: 5%;
font-size: 16px; /* 输入框文字16px */
}
.header-PC .searchmodalMian .searchmodalct .searchhistory,
.header-PC .searchmodalMian .searchmodalct .popProduct {
margin-top: 5%;
margin-left: 10%;
width: 80%;
display: flex;
position: relative;
}
.header-PC .searchmodalMian .searchmodalct .searchhistory .h_title,
.header-PC .searchmodalMian .searchmodalct .popProduct .h_title {
position: absolute;
left: 0;
top: 1%;
font-size: 16px; /* 保持16px */
color: #989898;
}
.header-PC .searchmodalMian .searchmodalct .searchhistory ul,
.header-PC .searchmodalMian .searchmodalct .popProduct ul {
margin-top: 10%;
margin-left: 1%;
}
.header-PC .searchmodalMian .searchmodalct .popProduct .popmain{
width: 100%;
}
.header-PC .searchmodalMian .searchmodalct .searchhistory .popmain,
.header-PC .searchmodalMian .searchmodalct .popProduct .popmain {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.header-PC .searchmodalMian .searchmodalct .searchhistory .popmain .popitem,
.header-PC .searchmodalMian .searchmodalct .popProduct .popmain .popitem {
text-align: center;
margin-top: 7%;
margin-left: 1%;
display: flex;
flex-direction: column;
justify-content: center;
width: 30%;
align-items: center;
overflow: hidden;
}
.header-PC .searchmodalMian .searchmodalct .searchhistory .popmain .popitem .popimg,
.header-PC .searchmodalMian .searchmodalct .popProduct .popmain .popitem .popimg {
width: 115px; /* 1.15rem*100=115px */
height: 115px; /* 1.15rem*100=115px */
margin: 0 auto;
}
.header-PC .searchmodalMian .searchmodalct .searchhistory .popmain .popitem .productName,
.header-PC .searchmodalMian .searchmodalct .popProduct .popmain .popitem .productName {
font-weight: 600;
display: -webkit-box;
-webkit-line-clamp: 1;
text-overflow: ellipsis;
font-size: 16px; /* 原0.128rem=12.8px → 提升至16px */
margin-top: 10%;
}
.header-PC .searchmodalMian .searchmodalct .popProduct .popmain .popitem .productName
{
width: 100%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: block;
font-size: 16px; /* 原14px → 提升至16px */
}
.header-PC .searchmodalMian .searchmodalct .popProduct .popmain .popitem .produc-dec{
font-size: 16px; /* 原10px → 提升至16px */
color: #000000;
margin-top: 1%;
}

View File

@@ -9,13 +9,6 @@ body {
overflow-x: hidden; overflow-x: hidden;
scroll-behavior: smooth !important; scroll-behavior: smooth !important;
-webkit-overflow-scrolling: touch !important; -webkit-overflow-scrolling: touch !important;
/* padding: 0 !important;
margin:0 !important; */
/* max-width:100% !important;
width: 100vw !important;
margin:0 auto; */
/* max-width:100vw !important */
/* margin:0 auto !important; */
} }
/* 当视口宽度大于1920px时生效 */ /* 当视口宽度大于1920px时生效 */
@media screen and (min-width: 1920px) { @media screen and (min-width: 1920px) {
@@ -39,7 +32,3 @@ body {
transform 1.2s ease-in-out, transform 1.2s ease-in-out,
visibility 1.2s ease-in-out; visibility 1.2s ease-in-out;
} }
/* 18-19号防卡顿延迟19号动画 */
/* [data-order="19"] {
transition: all 0.3s ease-out 0.2s !important;
} */

View File

@@ -63,12 +63,12 @@ a {
width: 100%; width: 100%;
height: clamp(2.5rem, 5vw, 15rem); height: clamp(2.5rem, 5vw, 15rem);
} }
.header { /* .header {
width: 100%; width: 100%;
background: #fff; background: #fff;
height: 60px; height: 60px;
} } */
.header-img { /* .header-img {
margin: 0 auto; margin: 0 auto;
width: 90%; width: 90%;
display: flex; display: flex;
@@ -76,4 +76,4 @@ a {
padding-top: 0.5rem; padding-top: 0.5rem;
padding-bottom: 0.5rem; padding-bottom: 0.5rem;
} } */

Binary file not shown.

After

Width:  |  Height:  |  Size: 379 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 868 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 578 B

View File

@@ -244,6 +244,25 @@ class ImageMigrator:
if self.verbose: if self.verbose:
print("连接已关闭") print("连接已关闭")
def chown_target(self, owner: str, target_path: str) -> bool:
command = f"sudo chown {owner} {target_path}"
stdin, stdout, stderr = self.target_client.exec_command(command)
# 如果需要输入sudo密码
if "sudo" in command and self.target_config.password:
stdin.write(self.target_config.password + "\n")
stdin.flush()
if self.verbose:
# 获取输出和错误
# output = stdout.read().decode()
error = stderr.read().decode()
exit_code = stdout.channel.recv_exit_status()
if exit_code == 0:
return True
else:
print(f"DEBUG chown_target: 修改 {target_path} 的所有者失败: {error}")
return False
def ensure_target_directory(self, remote_dir: str) -> bool: def ensure_target_directory(self, remote_dir: str) -> bool:
"""确保目标目录存在(递归创建)""" """确保目标目录存在(递归创建)"""
try: try:
@@ -298,11 +317,17 @@ class ImageMigrator:
f"DEBUG ensure_target_directory: 创建目录: {current_path}" f"DEBUG ensure_target_directory: 创建目录: {current_path}"
) )
try: try:
# 创建目录
self.target_sftp.mkdir(current_path) self.target_sftp.mkdir(current_path)
if self.verbose: if self.verbose:
print( print(
f"DEBUG ensure_target_directory: 目录创建成功: {current_path}" f"DEBUG ensure_target_directory: 目录创建成功: {current_path}"
) )
# 修改目录所有者
if self.chown_target("www", current_path) and self.verbose:
print(
f"DEBUG ensure_target_directory: {current_path} 所有者修改为 www 成功"
)
except Exception as mkdir_e: except Exception as mkdir_e:
# 如果创建失败,可能是权限问题或目录已存在 # 如果创建失败,可能是权限问题或目录已存在
if self.verbose: if self.verbose:
@@ -519,6 +544,8 @@ class ImageMigrator:
print(f"DEBUG: 目录stat也失败: {stat_e}") print(f"DEBUG: 目录stat也失败: {stat_e}")
self.target_sftp.put(temp_path, target_path) self.target_sftp.put(temp_path, target_path)
if self.chown_target("www", target_path) and self.verbose:
print(f"DEBUG transfer_file: {target_path} 所有者修改为 www 成功")
if self.verbose: if self.verbose:
print("DEBUG: 上传完成") print("DEBUG: 上传完成")