403 Commits

Author SHA1 Message Date
88a91f3136 删除注释 2025-12-04 10:27:05 +08:00
5fd603cbc6 底部样式 2025-12-04 10:25:06 +08:00
b92753c822 底部 2025-12-04 10:22:56 +08:00
ddd578eb08 Merge branch 'dev' 2025-12-03 17:56:52 +08:00
9db66b7429 七牛云引入字体 2025-12-03 17:56:08 +08:00
c91895f57f 删除备份文件 2025-12-03 17:03:57 +08:00
42d9efdc02 移动端尺寸字体调整 2025-12-03 15:30:19 +08:00
e16b8b6b41 字体切换 2025-12-02 16:33:24 +08:00
8e4befc0ea Merge branch 'font' into dev 2025-12-02 16:12:06 +08:00
1a43ad30fb 移动端 2025-12-02 16:11:12 +08:00
1cdd0536b9 Merge branch 'font' 2025-12-01 15:46:23 +08:00
c28b945858 font 2025-12-01 15:33:55 +08:00
9cc6dc3cd9 Merge branch 'dev' 2025-11-28 16:23:35 +08:00
9845883d8e refactor: 专题 - 电力品线 2025-11-28 16:23:21 +08:00
4d25f6fec8 Merge branch 'dev' 2025-11-28 15:46:00 +08:00
5d44c45c13 footer 2025-11-28 15:45:20 +08:00
2764ce3b86 Merge branch 'dev' 2025-11-28 15:36:31 +08:00
7f472b0351 refactor: 专题 - 电力品线 2025-11-28 15:36:12 +08:00
7cb270b313 Merge branch 'dev' 2025-11-28 15:23:45 +08:00
14df140847 refactor: 专题 - 电力品线 2025-11-28 15:23:29 +08:00
86ffcb99ac Merge branch 'dev' 2025-11-28 14:51:46 +08:00
13e58c33a2 refactor: 专题 - 电力品线 2025-11-28 14:51:30 +08:00
c476193002 Merge branch 'dev' 2025-11-28 14:43:34 +08:00
88e307a10e feat: 添加电力品线专题url 2025-11-28 14:43:17 +08:00
5643faad94 Merge branch 'dev' 2025-11-28 14:36:46 +08:00
989f6c25a0 refactor: 修改..gitignore 2025-11-28 14:35:35 +08:00
2e5103b19a Initial commit 2025-11-28 14:33:22 +08:00
ca3c00f396 电力 2025-11-28 14:18:19 +08:00
c8d5661856 尺寸调整 2025-11-27 18:38:58 +08:00
288c86ffbc 修复bug 2025-11-27 09:42:18 +08:00
ccc551dcba refactor: 专题 - 电力品线 修改 2025-11-26 17:46:09 +08:00
b6e8aa7880 refactor: 专题 - 电力品线 mobile 修改 2025-11-26 17:29:45 +08:00
cc2bb999c0 refactor: 删除gitea webhook文件 2025-11-25 17:45:32 +08:00
e7009c8a49 refactor: migration文件添加banner简称相关 2025-11-25 17:43:09 +08:00
2a8f88637c refactor: .gitignore 2025-11-25 17:38:57 +08:00
1fc6f9067c refactor: 专题 - 电力品线 2025-11-25 17:36:09 +08:00
ffa6f29af8 refactor: 专题 - 电力品线mobile端 2025-11-25 16:15:42 +08:00
476a238d27 refactor: 专题 - 电力品线pc端 2025-11-25 14:56:57 +08:00
18862c584f refactor: banner加入"banner简称"问题修改 2025-11-25 14:05:01 +08:00
ee9174e801 refactor: .gitignore 2025-11-25 14:01:08 +08:00
4c13d31cb9 feat: banner添加/编辑时加入“banner简称”字段 2025-11-25 14:00:06 +08:00
3ed8e1f8fc refactor: 主题 - 电力品线 2025-11-25 13:48:59 +08:00
1c998a4be1 feat: 添加dev-ci.yaml 2025-11-24 15:43:38 +08:00
c3c67c9e59 feat: 专题 - 电力品线首页 2025-11-20 14:29:30 +08:00
ef5d2c6ab9 refactor: 修改.gitignore 2025-11-20 11:00:35 +08:00
80678c23e2 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-09-19 15:22:25 +08:00
53e56b4649 1 2025-09-19 15:22:22 +08:00
ouzhiqiang
75977040f1 修改分类 2025-09-19 15:14:55 +08:00
ouzhiqiang
cf5a1280ee 修改分类 2025-09-19 14:59:45 +08:00
ouzhiqiang
30d99619b2 修改分类 2025-09-19 14:48:10 +08:00
ouzhiqiang
7e877920ee 修改分类 2025-09-19 14:47:05 +08:00
ouzhiqiang
04ede65c93 修改分类 2025-09-19 14:36:08 +08:00
ouzhiqiang
7524a9d572 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-09-19 14:32:27 +08:00
ouzhiqiang
9b5cf2147d 修改分类 2025-09-19 14:31:56 +08:00
9d898b1be3 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-09-19 14:13:56 +08:00
834114261c 1 2025-09-19 14:13:53 +08:00
ouzhiqiang
2dc787bbfd 修改分类 2025-09-19 11:37:41 +08:00
ouzhiqiang
5d61b4995f 修改分类 2025-09-19 11:22:25 +08:00
ouzhiqiang
6800332b5b 修改分类 2025-09-19 11:17:49 +08:00
ouzhiqiang
1c1d075f5e 修改分类 2025-09-19 11:09:29 +08:00
ouzhiqiang
34821fd0bc 修改分类 2025-09-19 10:41:41 +08:00
ouzhiqiang
3492d55707 修改分类 2025-09-19 10:36:26 +08:00
ouzhiqiang
5b901fe43c 修改分类 2025-09-19 10:29:25 +08:00
ouzhiqiang
e6d299df83 修改分类 2025-09-19 10:23:02 +08:00
ouzhiqiang
5d5a338b55 修改分类 2025-09-19 10:04:47 +08:00
ouzhiqiang
4cd2f4ad26 修改分类 2025-09-19 09:53:40 +08:00
ouzhiqiang
14f899bf62 修改分类 2025-09-19 09:51:29 +08:00
ouzhiqiang
f82888c8e3 Merge branch 'ozq-dev' into dev 2025-09-18 17:50:21 +08:00
ouzhiqiang
43c61a1ab5 修改分类 2025-09-18 17:50:05 +08:00
ouzhiqiang
4f2edf5d30 Merge branch 'ozq-dev' into dev 2025-09-18 17:47:43 +08:00
ouzhiqiang
4ca59e81e3 修改分类 2025-09-18 17:47:22 +08:00
ouzhiqiang
9cd7b4f508 Merge branch 'ozq-dev' into dev 2025-09-18 17:23:25 +08:00
ouzhiqiang
e02ad39620 修改分类 2025-09-18 17:23:07 +08:00
ouzhiqiang
0ce0c0a27a Merge branch 'ozq-dev' into dev 2025-09-18 17:16:43 +08:00
ouzhiqiang
db72a5643c 修改分类 2025-09-18 17:16:24 +08:00
ouzhiqiang
60fb6a5336 Merge branch 'ozq-dev' into dev 2025-09-18 17:13:22 +08:00
ouzhiqiang
987e98279a 修改分类 2025-09-18 17:13:04 +08:00
ouzhiqiang
9b926f1bf8 Merge branch 'ozq-dev' into dev 2025-09-18 17:04:44 +08:00
ouzhiqiang
810b581d87 修改分类 2025-09-18 17:04:29 +08:00
ouzhiqiang
6bbc7c55c9 Merge branch 'ozq-dev' into dev 2025-09-18 17:01:25 +08:00
ouzhiqiang
36de45c2e8 修改分类 2025-09-18 17:01:08 +08:00
ouzhiqiang
fd7e136c3a Merge branch 'ozq-dev' into dev 2025-09-18 16:56:01 +08:00
ouzhiqiang
c9987373a4 修改分类 2025-09-18 16:55:47 +08:00
ouzhiqiang
5386610bf9 Merge branch 'ozq-dev' into dev 2025-09-18 16:53:26 +08:00
ouzhiqiang
f1696a88d2 修改分类 2025-09-18 16:53:06 +08:00
ouzhiqiang
7e96a0e538 Merge branch 'ozq-dev' into dev 2025-09-18 16:49:16 +08:00
ouzhiqiang
1f1cc6145d 修改分类 2025-09-18 16:48:45 +08:00
ouzhiqiang
610377172a Merge branch 'ozq-dev' into dev 2025-09-18 16:11:52 +08:00
ouzhiqiang
0d5593e173 修改分类 2025-09-18 16:11:36 +08:00
ouzhiqiang
6dbeb59b36 Merge branch 'ozq-dev' into dev 2025-09-18 16:08:29 +08:00
ouzhiqiang
e54fccca65 修改分类 2025-09-18 16:08:05 +08:00
ouzhiqiang
1a92627ab9 Merge branch 'ozq-dev' into dev 2025-09-18 16:01:20 +08:00
ouzhiqiang
88a7f06499 修改分类 2025-09-18 16:00:49 +08:00
ouzhiqiang
d6e58c78d4 Merge branch 'ozq-dev' into dev 2025-09-18 15:39:28 +08:00
ouzhiqiang
46e78cff08 修改分类 2025-09-18 15:38:59 +08:00
ouzhiqiang
48c589babf Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-09-18 15:26:19 +08:00
ouzhiqiang
72f974c083 修改分类 2025-09-18 15:25:29 +08:00
9def4960c4 1 2025-09-18 11:59:32 +08:00
622f9f1665 1 2025-09-18 11:57:52 +08:00
db7c34f4ad 分类补充 2025-09-18 11:56:48 +08:00
f79dbc1121 移动端帮助中心三级菜单 2025-09-18 11:52:52 +08:00
f3de8b4a26 帮助中心文档三级菜单 2025-09-18 11:21:50 +08:00
85ccecb5bf Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-09-18 11:15:12 +08:00
fa60ec4fcb feat: 添加open api client管理命令 2025-09-17 17:48:27 +08:00
cb128a32a8 Merge branch 'dev' 2025-08-26 14:53:55 +08:00
705566b376 refactor: 去掉多余目录 2025-08-26 14:53:38 +08:00
0b13ec05dc fix: 修复“检测文件地址并根据情况转换为文件系统地址”url为null情况报错问题 2025-08-26 14:53:17 +08:00
7f765f40ee Merge branch 'dev' 2025-08-08 09:18:33 +08:00
2a9c3332f7 fix: 修复产品目录分类树数据问题 2025-08-08 09:18:13 +08:00
12822ccb6b Merge branch 'dev' 2025-08-07 17:08:12 +08:00
4872574eb0 fix: admapi - 产品目录同步分类更新时bug修复 2025-08-07 17:07:52 +08:00
fa893d6b53 Merge branch 'dev' 2025-08-07 11:38:17 +08:00
f9445b6f49 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-08-07 11:37:49 +08:00
fea5f233a9 style:底部样式修改 2025-08-07 11:36:56 +08:00
e2b65856d2 Merge branch 'dev' 2025-08-07 10:20:10 +08:00
3733ce720d fix: mobile - 底部“联系我们”取值问题 2025-08-07 10:19:45 +08:00
0b82dbbf5f Merge branch 'dev' 2025-08-07 10:18:06 +08:00
74d70fe815 fix: mobile - 底部“联系我们”取值问题 2025-08-07 10:17:24 +08:00
0f2dc12752 Merge branch 'dev' 2025-08-07 10:12:04 +08:00
499e86f52b fix: mobile - 底部“联系我们”取值问题 2025-08-07 10:03:50 +08:00
b24987fbd2 Merge branch 'dev' 2025-08-06 15:35:50 +08:00
19aecea497 perf openapi 图片地址拼接 2025-08-06 15:35:35 +08:00
9060ed8274 Merge branch 'dev' 2025-08-06 12:45:49 +08:00
54d15595ce fix: 后台 - 修改各导出图片等文件地址拼接问题 2025-08-06 11:56:53 +08:00
101c1ae807 fix: 后台 产品导出数据问题 2025-08-06 11:40:44 +08:00
ed7f274c6e fix: 后台 产品导出数据问题 2025-08-06 11:40:09 +08:00
1288ffd51b fix: mobile下视频分类地址错误 2025-07-31 10:55:26 +08:00
62e43375ab fix: 获取文件系统磁盘未判断“//”情况 2025-07-31 09:03:49 +08:00
9f01db7d54 fix: 首页“场景介绍”无法点击 2025-07-31 09:03:48 +08:00
ac06090792 1 2025-07-30 17:08:22 +08:00
6e6e4ac47c 1 2025-07-30 17:07:37 +08:00
8ccc4b0cd5 1 2025-07-30 16:48:54 +08:00
55cd7f2b62 1 2025-07-30 16:47:15 +08:00
43e1ae0422 1 2025-07-30 16:44:32 +08:00
7fcb78dde0 1 2025-07-30 16:40:36 +08:00
6872a13141 Merge branch 'dev' 2025-07-30 16:33:15 +08:00
234658f443 style:产品详情 2025-07-30 16:13:37 +08:00
9f9a384075 .ql-editor 2025-07-30 15:43:42 +08:00
047d7979ff .ql-editor 2025-07-30 15:42:52 +08:00
3544c08005 style:产品详情样式 2025-07-30 15:35:16 +08:00
2ff6d274d9 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-07-30 15:25:01 +08:00
d31d2359c6 style:详情样式 2025-07-30 15:24:58 +08:00
6f9c89da7b fix: 字段长度限制 2025-07-30 11:13:17 +08:00
9c6659830c fix: 字段长度限制 2025-07-30 11:12:13 +08:00
4c906723df Merge branch 'dev' 2025-07-29 17:52:13 +08:00
d93aa62fbf refactor: 修改后台banner的“链接地址”长度限制为510 2025-07-29 17:51:56 +08:00
f216ba82b4 Merge branch 'dev' 2025-07-29 16:48:16 +08:00
bcd537ef25 Merge branch 'dev' 2025-07-29 16:47:31 +08:00
589a099cdd 1 2025-07-29 14:16:51 +08:00
e13b30bbb2 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-07-29 14:14:29 +08:00
eb924d42d4 视频宽度限制 2025-07-29 14:14:27 +08:00
9378efe401 文章视频限制 2025-07-29 14:13:34 +08:00
84dd8ea84b fix: migrations/seeds 2025-07-29 14:08:30 +08:00
4c2441b1b6 fix: 产品详情-分类路径bug 2025-07-29 12:00:22 +08:00
db8e0803c1 文章详情样式调整 2025-07-28 14:50:26 +08:00
72b8230d5a Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-07-28 14:34:41 +08:00
5a8f32e0f8 style:宽度调整 2025-07-28 14:25:01 +08:00
517c2e954b chore: composer.json 2025-07-28 11:13:56 +08:00
eee40e6010 perf: migrations/seeds 2025-07-28 10:33:44 +08:00
a7882da734 perf: 七牛云存储 2025-07-28 10:16:03 +08:00
64ea309b30 refactor: .gitignore 2025-07-25 18:00:14 +08:00
708fee0490 refactor: 七牛云上传 2025-07-25 17:57:31 +08:00
5623c4160e 文章详情样式调整 2025-07-25 13:47:37 +08:00
b660d82c94 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-07-24 10:00:18 +08:00
8f5d3ed60f 1 2025-07-24 10:00:14 +08:00
0f7e768bc4 feat: 七牛云上传开发 2025-07-23 17:55:34 +08:00
1d84ba373b perf: 自定义语言检测中间件 2025-07-23 17:55:34 +08:00
76e8372059 补充 2025-07-23 17:25:57 +08:00
fdb91466ca .ql-editor 关于tab切换的样式同步 2025-07-23 17:20:11 +08:00
133c58c268 样式修改 2025-07-22 16:48:22 +08:00
c2ad978614 Merge branch 'dev' 2025-07-21 17:32:36 +08:00
865fe2fc44 fix: pc 产品,附件,视频搜索去除首尾空格 2025-07-21 17:31:17 +08:00
0f4c7fec5d fix: pc 视频搜索 2025-07-21 16:55:06 +08:00
1cfae69a27 fix: pc 视频搜索 2025-07-21 16:54:43 +08:00
aa00a24b89 Merge branch 'dev' 2025-07-19 10:48:27 +08:00
0b2c408525 fix: 图片上传命名问题 2025-07-19 10:48:07 +08:00
e6b255b824 Merge branch 'master' of https://gitea.f2b211.com/jsasg/orico-official-website 2025-07-18 15:19:02 +08:00
d7e8fe1d6e 1 2025-07-18 15:16:09 +08:00
2a31e22ad8 标题提取空过滤 样式修改 2025-07-18 15:15:13 +08:00
99765a6112 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-07-18 14:42:10 +08:00
5ae6d5681d Merge branch 'dev' 2025-07-18 14:40:20 +08:00
d2b274b559 fix: nas 文章详情页分类排序问题 2025-07-18 14:40:02 +08:00
3cb4eacff4 Merge branch 'master' of https://gitea.f2b211.com/jsasg/orico-official-website 2025-07-18 14:39:33 +08:00
2a4349daab 1 2025-07-18 14:39:30 +08:00
4e8320e871 Merge branch 'dev' 2025-07-18 13:36:33 +08:00
3c8f9773be fix: 横幅分类不可小于0 2025-07-18 13:33:58 +08:00
d04343e9c8 fix: nas帮助中心文件分类未排序 2025-07-18 12:36:20 +08:00
ed24f4fcd0 Achievement 2025-07-18 10:29:26 +08:00
c340155bca nas_help_banner 格式修改 2025-07-18 10:16:58 +08:00
78616faa73 indeximg1 ,indeximg2压缩webp 2025-07-18 09:44:41 +08:00
abd3072ada 编辑器样式修改 2025-07-17 17:45:34 +08:00
17f11559d8 1 2025-07-17 17:04:16 +08:00
7b28b2ef20 1 2025-07-17 17:02:29 +08:00
3790816964 1 2025-07-17 17:01:17 +08:00
03fe4ee0ed Merge branch 'master' of https://gitea.f2b211.com/jsasg/orico-official-website 2025-07-17 16:49:08 +08:00
21f600270f 1 2025-07-17 16:49:03 +08:00
43d153c004 Merge branch 'dev' 2025-07-17 16:43:24 +08:00
83468671f1 perf: 优化openapi接口频率限制 2025-07-17 16:43:02 +08:00
f6e52294f0 1 2025-07-17 16:40:23 +08:00
fd64633761 1 2025-07-17 16:35:38 +08:00
19d6944c06 1 2025-07-17 16:31:42 +08:00
7e66a9ecfb 1还原 2025-07-17 16:27:14 +08:00
6925061380 还原 2025-07-17 16:25:01 +08:00
77b988c6b7 1 2025-07-17 16:22:28 +08:00
35723f59a4 1 2025-07-17 16:18:57 +08:00
567798643c 1 2025-07-17 16:11:01 +08:00
912d770c33 ·1 2025-07-17 16:02:31 +08:00
856ce0e7cc 1 2025-07-17 15:40:29 +08:00
7c3c75b22e 1 2025-07-17 15:39:17 +08:00
17ea1ea832 1 2025-07-17 15:32:22 +08:00
693c442060 1 2025-07-17 15:30:43 +08:00
4079574575 格式化 2025-07-17 15:22:56 +08:00
314fb66112 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-07-17 15:20:55 +08:00
d0b4d74b46 1 2025-07-17 15:19:56 +08:00
44e759564f 1 2025-07-17 15:18:38 +08:00
84bf7662e9 1 2025-07-17 15:13:01 +08:00
0b71bb0254 1 2025-07-17 15:08:52 +08:00
5f5c7d7cf4 1 2025-07-17 12:00:46 +08:00
5e9ace5832 Merge branch 'master' of https://gitea.f2b211.com/jsasg/orico-official-website 2025-07-17 11:49:33 +08:00
e00fb9eadb 1 2025-07-17 11:49:31 +08:00
99734ab92a fix: database/seeds 2025-07-17 11:44:00 +08:00
b1fb932aae 1 2025-07-17 11:42:38 +08:00
97b851e8a6 1 2025-07-17 11:30:52 +08:00
e736271536 英文单词不换行 2025-07-17 10:48:37 +08:00
74d1524d8d 1 2025-07-17 10:36:36 +08:00
3bb1909ecd 图片拉伸 2025-07-17 10:24:00 +08:00
2870cbb544 .header-PC #header .nav3 .choesCountry 2025-07-17 10:19:09 +08:00
710d6244d4 fix: database/seeds 2025-07-17 09:58:09 +08:00
b5b27f0555 1 2025-07-17 09:49:05 +08:00
3a2631b99a Merge branch 'master' of https://gitea.f2b211.com/jsasg/orico-official-website 2025-07-17 09:45:12 +08:00
6ddb7df8aa .ql-editor>* 2025-07-17 09:45:07 +08:00
09b28a1159 feat: 后台管理 产品列表添加按添加时间倒序 2025-07-16 18:12:11 +08:00
5949e576ed img {display: block;}注释 2025-07-16 17:44:55 +08:00
e04b66295f .narshelpCenterdetail-app .nhlpapp-search 2025-07-16 16:55:44 +08:00
7802c80009 Merge branch 'master' of https://gitea.f2b211.com/jsasg/orico-official-website 2025-07-16 16:53:03 +08:00
ce347c514b 移动端nas文章底部操作 2025-07-16 16:53:01 +08:00
3d991abcd5 fix: pc nas专题logo中文下要点击,英文下不要 2025-07-16 15:39:35 +08:00
fa1f6a70db fix: 产品分类排序 2025-07-16 15:32:38 +08:00
fb0a3f9371 fix: pc 产品详情添加型号 2025-07-16 15:24:22 +08:00
fc6079ef32 .ql-container 2025-07-16 14:51:07 +08:00
82a6b171b9 文章样式 2025-07-16 14:45:33 +08:00
4891b07ba2 Merge branch 'master' of https://gitea.f2b211.com/jsasg/orico-official-website 2025-07-16 14:14:05 +08:00
9789cec2bf 1 2025-07-16 14:13:13 +08:00
a59bb22c65 1 2025-07-16 14:11:52 +08:00
5b1a281e0f 还原footer 2025-07-16 14:07:43 +08:00
d168e5bb93 1 2025-07-16 14:02:32 +08:00
76685ce103 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-07-16 13:39:34 +08:00
db38224b14 Merge branch 'dev' 2025-07-16 13:39:07 +08:00
a52fb091a8 fix: 产品目录同步产品默认在售 2025-07-16 13:38:54 +08:00
13b4464473 Merge branch 'dev' 2025-07-16 11:52:05 +08:00
2b8f8c8294 fix: 后台管理 产品参数问题 2025-07-16 11:51:48 +08:00
f18afbe6ca Merge branch 'dev' 2025-07-16 10:31:39 +08:00
12422f06b4 fix: 产品搜索无数据语言问题 2025-07-16 10:31:14 +08:00
6b4612cd09 pc编辑文本样式 2025-07-16 10:19:42 +08:00
70ed7d917f 添加编辑器样式 2025-07-16 10:17:58 +08:00
3c994973c1 1 2025-07-16 10:12:28 +08:00
4672f76264 1 2025-07-16 10:10:36 +08:00
98ec9cdc49 1 2025-07-16 10:04:54 +08:00
0ad2a41ab3 1 2025-07-16 10:02:47 +08:00
1553a1f2ed 1 2025-07-16 09:56:05 +08:00
9bdb290760 1 2025-07-16 09:54:28 +08:00
b2014b104f Merge branch 'master' of https://gitea.f2b211.com/jsasg/orico-official-website 2025-07-16 09:51:31 +08:00
791380e53c 1 2025-07-16 09:51:26 +08:00
1c3aa90c3a Merge branch 'dev' 2025-07-15 15:00:51 +08:00
425140d4ac fix: openapi token有效期配置 2025-07-15 15:00:38 +08:00
e32f75234a fix: openapi token有效期配置 2025-07-15 14:59:25 +08:00
103fc4dd24 Merge branch 'dev' 2025-07-15 14:49:47 +08:00
6eac1e5d7b perf: openapi token有效期配置 2025-07-15 14:49:03 +08:00
c171073174 Merge branch 'dev' 2025-07-15 14:45:54 +08:00
5c0e96df27 perf: openapi token有效期配置 2025-07-15 14:45:16 +08:00
4a91a4f673 Merge branch 'dev' 2025-07-15 14:21:30 +08:00
9e22cde6ba perf: openapi client授权过期 2025-07-15 14:21:16 +08:00
7ee92398cf fix: admapi 角色菜单权力model 2025-07-15 14:21:04 +08:00
f2c09736de perf: openapi client授权过期 2025-07-15 14:20:36 +08:00
7957b56f29 fix: admapi 角色菜单权力model 2025-07-15 13:50:50 +08:00
dcfa73bdc1 Merge branch 'dev' 2025-07-15 10:48:30 +08:00
3b4f747cb1 perf: .gitignore 2025-07-15 10:46:17 +08:00
401683be6b fix: mobile subcategory.html 2025-07-15 09:54:58 +08:00
a8e80dfa26 fix: mobile subcategory.html 2025-07-15 09:54:38 +08:00
ca38a3a7b6 smallImgDown相对位置修改 2025-07-15 09:36:55 +08:00
6b1ec81bd0 bsImg 高度自适应 2025-07-15 09:32:07 +08:00
01952774b1 Merge branch 'master' of https://gitea.f2b211.com/jsasg/orico-official-website 2025-07-15 09:30:05 +08:00
ee35e2ce4b .bttj 修改 2025-07-15 09:30:02 +08:00
d20e703bfe fix: mobile subcategory.html 2025-07-15 09:25:25 +08:00
2ac0708ac1 perf: mobile 产品分类与搜索页改为缩略图 2025-07-15 09:22:21 +08:00
f292e68b5b Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-07-15 09:19:14 +08:00
fe1a07f56a 顶部底部自适应 2025-07-15 09:18:11 +08:00
be7d256007 fix: mobile view问题 2025-07-15 09:16:01 +08:00
d3c8ffaeb1 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-07-14 17:18:35 +08:00
14f0cc0607 1 2025-07-14 17:06:45 +08:00
ae52ae7de5 去掉新品的标题和更多 2025-07-14 17:05:44 +08:00
510f89b84c sceneitem1 2025-07-14 16:41:58 +08:00
0aa4e82095 1 2025-07-14 16:39:59 +08:00
14e611d02e 1 2025-07-14 16:34:11 +08:00
8c1ff8da6e 1 2025-07-14 16:26:35 +08:00
bb37146be8 1 2025-07-14 16:25:49 +08:00
5fbdbda62d 1 2025-07-14 16:24:22 +08:00
3688c0efd4 1 2025-07-14 16:23:25 +08:00
1de9769edb 1 2025-07-14 16:09:53 +08:00
e0dd787bf3 1 2025-07-14 16:09:02 +08:00
a9681677a9 1 2025-07-14 16:08:13 +08:00
761a033b46 1 2025-07-14 16:07:06 +08:00
554a87d0cf Merge branch 'master' of https://gitea.f2b211.com/jsasg/orico-official-website 2025-07-14 16:02:37 +08:00
26c60ae751 样式修改6 2025-07-14 16:02:35 +08:00
902f40e28c fix: 底部备案信息 2025-07-14 15:52:13 +08:00
d8744d2070 fix: 底部备案信息 2025-07-14 15:51:22 +08:00
f23ab69761 fix: 底部备案信息 2025-07-14 15:50:13 +08:00
470a3e1687 pref: 数据同步 2025-07-14 15:50:13 +08:00
932f2b8705 fix: 底部备案信息 2025-07-14 15:49:50 +08:00
b14a75c3a2 1 2025-07-14 15:46:47 +08:00
f0b02ba83b 样式修改4 2025-07-14 15:46:18 +08:00
a36633a559 样式修改2 2025-07-14 15:35:57 +08:00
7daaf12908 Merge branch 'master' of https://gitea.f2b211.com/jsasg/orico-official-website 2025-07-14 15:13:12 +08:00
4111e05957 样式修改 2025-07-14 15:13:09 +08:00
ce665b9ea4 pref: 数据同步 2025-07-14 14:30:59 +08:00
082ed00d74 Merge branch 'dev' 2025-07-14 14:06:16 +08:00
6c3f6247f8 fix: 顶部搜索产品图片显示缩略图 2025-07-14 14:06:04 +08:00
881ce98a72 Merge branch 'dev' 2025-07-14 13:44:06 +08:00
51ceeff0d7 fix: 视频描述字段长度校验 2025-07-14 13:43:52 +08:00
c0bd15cbd9 perf: 数据同步 2025-07-14 13:42:50 +08:00
e26b0094ee Merge branch 'dev' 2025-07-14 11:47:51 +08:00
afc70e1b68 fix: 产品详情购买链接排序 2025-07-14 11:47:41 +08:00
a96e3780fb fix: pc 顶部“店铺”新标签打开 2025-07-14 11:27:59 +08:00
7c781f4eaa Merge branch 'master' of https://gitea.f2b211.com/jsasg/orico-official-website 2025-07-14 11:27:38 +08:00
c938438838 style 2025-07-14 11:27:34 +08:00
10024e40c2 fix: pc 顶部“店铺”新标签打开 2025-07-14 11:25:02 +08:00
612fb44215 fix: pc 底部导航新标签页打开无效 2025-07-14 11:04:34 +08:00
b9ed1a7328 perf: 数据同步 2025-07-14 11:04:34 +08:00
9c71a45f8b fix: pc 底部导航新标签页打开无效 2025-07-14 11:01:14 +08:00
a751f3aa97 perf: 数据同步 2025-07-14 11:00:43 +08:00
jsasg
bb8f42774f Merge branch 'dev' 2025-07-11 22:39:16 +08:00
jsasg
d902abe57d fix: 图片上传 2025-07-11 22:34:32 +08:00
873d873b4e fix: 底部 2025-07-11 20:15:38 +08:00
3dd4aa231e fix: 文章验证 2025-07-11 20:15:20 +08:00
87d943fc63 Merge branch 'dev' 2025-07-11 18:07:42 +08:00
d6f5c19616 fix: 底部 2025-07-11 18:07:25 +08:00
2fc9e7650a fix: 文章验证 2025-07-11 18:07:25 +08:00
0cc4ea2936 style:样式修改3 2025-07-11 17:15:44 +08:00
1ce367999e style:样式调整 2025-07-11 16:52:09 +08:00
f15f27e17a style:样式修改 2025-07-11 16:32:58 +08:00
31d4e641f8 fix:如果底部没有内容就不要显示li这个标签 2025-07-11 16:22:34 +08:00
9b211f804f style:样式修改 2025-07-11 16:13:49 +08:00
c3e1f08908 ljm master分支 2025-07-11 15:57:27 +08:00
76bd01e458 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-07-11 15:44:59 +08:00
7dbe56da33 fix:pc导航修改 2025-07-11 15:44:56 +08:00
f5bc3980dd fix: mobile下没有jquery 2025-07-11 15:22:01 +08:00
b37d795818 fix: 上传 2025-07-11 11:47:05 +08:00
4b64eb35aa fix: 导航未过滤已禁用 2025-07-11 10:32:05 +08:00
ee161ee7f6 add:本地添加jQuery和swiper文件,防止cdn失效 2025-07-10 17:38:17 +08:00
b0d241c22e perf: mysql递归查询兼容处理 2025-07-10 17:30:01 +08:00
9ac90d3fef perf: 修改analytics 2025-07-09 17:30:02 +08:00
17ce193e43 pref: think文件 2025-07-09 16:34:27 +08:00
8c64c35afd perf: redis env配置 2025-07-09 16:15:58 +08:00
8d4163ed88 perf: env 2025-07-09 13:58:59 +08:00
91a38dee28 fix: 同步产品目录产品分类及产品 2025-07-09 11:32:59 +08:00
ca6296a276 · 2025-07-09 10:25:02 +08:00
b1fd726226 样式修改 2025-07-09 10:21:48 +08:00
d924716d6a Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-07-08 10:54:46 +08:00
a383d56291 style:样式修改 2025-07-08 10:54:43 +08:00
c1a443efba fix: 后台图片上传路径bug 2025-07-07 18:01:12 +08:00
e77b72381b fix: 后台图片上传路径bug 2025-07-07 17:50:52 +08:00
91883e132f pref: 图片大小调整 2025-07-07 16:39:02 +08:00
91b2474981 pref: 图片大小调整 2025-07-07 16:22:51 +08:00
a19e8f6e34 1 2025-07-07 15:27:22 +08:00
895f2040aa 1 2025-07-07 15:23:30 +08:00
b6174e1055 style:单词换行 2025-07-07 15:21:44 +08:00
040237447a Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-07-07 15:15:32 +08:00
7df2727fed style:品类换行 2025-07-07 15:15:30 +08:00
0fb0c35d1d feat: migration/seed 2025-07-05 18:01:22 +08:00
db05632769 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-07-05 17:35:30 +08:00
f1af59b09a style:样式调整 2025-07-05 17:35:27 +08:00
861ecabcac feat: 后台 图片上传新增webp格式转换处理 2025-07-05 15:19:46 +08:00
3117a5b047 fix: openapi 文章详情取消author,source字段输出 2025-07-05 14:41:32 +08:00
d90acae078 fix: openapi 文章分类取消short_name, desc字段输出 2025-07-05 14:02:17 +08:00
26b1f21e86 fix: openapi 取消产品分类icon,desc字段输出 2025-07-05 14:01:51 +08:00
555996d72e fix: openapi取消产品详情sku->sort字段输出 2025-07-05 13:47:57 +08:00
9f9941f45d fix: openapi 产品接口取消desc输出 2025-07-05 11:49:55 +08:00
fd1db5134a 返回顶部 只要页面过长就显示 2025-07-05 11:12:52 +08:00
ff83880144 id修改为类,间距调整 2025-07-05 10:20:59 +08:00
21c295b26a Merge branch 'cssupdate' into dev 2025-07-05 09:53:30 +08:00
673f208792 产品详情切换问题修改 2025-07-05 09:51:50 +08:00
7a82081af2 doc: 注释 2025-07-05 09:18:24 +08:00
68e56eead0 fix: openapi 产品详情不输出links->platform问题 2025-07-04 17:42:29 +08:00
d8ac9530d8 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-07-04 17:17:41 +08:00
63c3d47f3d style:样式修改 2025-07-04 17:17:38 +08:00
8116315a6d fix: pc nas专题页 导航选中状态 2025-07-04 16:40:03 +08:00
b892a1346d fix: openapi 产品列表 2025-07-04 10:34:23 +08:00
4942af1fae fix: mobile 产品详情页图册中视频放第一个sku下最后一个 2025-07-03 14:01:34 +08:00
b0935db1b8 fix: pc 产品详情页图册中视频放第一个sku下最后一个 2025-07-03 13:56:57 +08:00
3d83c95a72 fix: mobile nas专题顶部导航 2025-07-02 15:30:25 +08:00
96bd1fc4c2 fix: pc 文章提交提示语言未区分语言 2025-07-02 15:06:28 +08:00
45218f85af pref: pc 产品详情表单提交 2025-07-02 14:44:35 +08:00
27f23cd321 fix: pc 产品搜索结果分布 2025-07-02 14:19:53 +08:00
aba102d63c perf: 关键词高亮 2025-07-02 10:59:01 +08:00
326ea8ae7e Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-07-02 10:57:38 +08:00
bc87c88b25 swiper uc兼容 2025-07-02 10:57:35 +08:00
c08900c694 feat: 从产品目录同步产品相关数据 2025-07-02 10:44:55 +08:00
401658981c fix: pc nas专题页 联系我们第一项无js效果 2025-07-02 10:44:55 +08:00
b6115b7a16 style:样式问题修改8 2025-07-02 10:44:29 +08:00
e9d40dcd82 style:样式问题修改7 2025-07-02 10:44:29 +08:00
e36660c93e style:样式问题修改8 2025-07-01 17:03:46 +08:00
4a84558d2e Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-07-01 16:43:00 +08:00
08862ff98e style:样式问题修改7 2025-07-01 16:42:57 +08:00
219 changed files with 10085 additions and 1489 deletions

5
.env
View File

@@ -1,5 +0,0 @@
[JWT]
TTL=3600
REFRESH_TTL=20160
SECRET=b43e6276644ed60e65c50d1b324ba10b

View File

@@ -1,32 +0,0 @@
APP_DEBUG = true
DB_TYPE = mysql
DB_HOST = localhost
DB_NAME = orico-official-website
DB_USER = orico-ow
DB_PASS = 14Xi17NIK8V2qAXE8oMataHEsaR8lE
DB_PORT = 3306
DB_CHARSET = utf8mb4
DB_PREFIX = ow_
DEFAULT_LANG = zh-cn
# 前端代理服务器ip影响使用代理访问情况下的客户端ip获取
PROXY_SERVER_IP[] = 120.79.27.160
[JWT]
TTL=3600
REFRESH_TTL=20160
SECRET=b43e6276644ed60e65c50d1b324ba10b
# 后台不需要登录的接口
[ADMIN_AUTH]
WHITE_LIST[] = v1/user/login
WHITE_LIST[] = v1/user/captcha
# 不需记录日志的接口
[ADMIN_API]
IGNORE_LOGGING_LIST[] = v1/OperateLog/index
MAX_IMAGE_SIZE = 5mb # 图片上传最大限制
MAX_VIDEO_SIZE = 150mb # 视频上传最大限制
MAX_ATTACHMENT_SIZE = 100mb # 附件上传最大限制

View File

@@ -1,55 +0,0 @@
APP_DEBUG = true
DB_TYPE = mysql
DB_HOST = 127.0.0.1
DB_NAME = orico-official-website
DB_USER = orico-official-website
DB_PASS = 14Xi17NIK8V2qAXE8oMataHEsaR8lE
DB_PORT = 3306
DB_CHARSET = utf8mb4
DB_PREFIX = ow_
DEFAULT_LANG = zh-cn
# 前端代理服务器ip影响使用代理访问情况下的客户端ip获取
PROXY_SERVER_IP[] = 120.79.27.160
[JWT]
TTL=3600
REFRESH_TTL=20160
SECRET=b43e6276644ed60e65c50d1b324ba10b
# 后台不需要登录的接口
[ADMIN_AUTH]
WHITE_LIST[] = v1/user/login
WHITE_LIST[] = v1/user/captcha
# 不需记录日志的接口
[ADMIN_API]
IGNORE_LOGGING_LIST[] = v1/OperateLog/index
MAX_IMAGE_SIZE = 5mb; # 图片上传最大限制
MAX_VIDEO_SIZE = 150mb; # 视频上传最大限制
MAX_ATTACHMENT_SIZE = 100mb; # 附件上传最大限制
# 开放API
[OPENAPI]
RESOURCE_IMAGES_DOMAIN = http://local.orico.com; # 图片资源服务器地址
RESOURCE_VIDEOS_DOMAIN = http://local.orico.com; # 视频资源服务器地址
# 视图模板规则配置
[VIEW_TPL]
# 视图目录
# query 规则URL参数 mtpl=1 表示移动端访问
# 例如http://xxxx.com?mtpl=1
# domain 规则:根据特定域名,判断是否移动端访问
# 例如http://mobile.orico.cn
RULE = query
# query 规则参数名
RULE_QUERY_NAME = mtpl
# query 规则参数值
RULE_QUERY_VALUE = 1
# domain 规则协议
RULE_DOMAIN_SCHEME[] = http
RULE_DOMAIN_SCHEME[] = https
# domain 规则域名
RULE_DOMAIN_HOST = mobile.orico.cn

View File

@@ -1,11 +1,96 @@
APP_DEBUG = true APP_DEBUG = true
DB_TYPE = mysql DB_TYPE = mysql
DB_HOST = 127.0.0.1 DB_HOST = localhost
DB_NAME = test DB_NAME = orico-official-website
DB_USER = username DB_USER = orico-ow
DB_PASS = password DB_PASS = 14Xi17NIK8V2qAXE8oMataHEsaR8lE
DB_PORT = 3306 DB_PORT = 3306
DB_CHARSET = utf8 DB_CHARSET = utf8mb4
DB_PREFIX = ow_
DB_VERSION = 8
DEFAULT_LANG = zh-cn DEFAULT_LANG = zh-cn
# 前端代理服务器ip影响使用代理访问情况下的客户端ip获取
PROXY_SERVER_IP[] = 120.79.27.160
# redis 配置
REDIS_HOST = 127.0.0.1
REDIS_PORT = 6379
REDIS_PASSWORD = ''
REDIS_PREFIX = ow:
[JWT]
TTL=3600
REFRESH_TTL=20160
SECRET=b43e6276644ed60e65c50d1b324ba10b
# 七牛云存储配置
[QINIU_CLOUD]
BUCKET = orico-official-website
BASE_URL = //ow.static.f2b211.com
ACCESS_KEY = dOsTum4a5qvhPTBbZRPX0pIOU7PZWRX7htKjztms
SECRET_KEY = KFxsGbnErkALFfeGdMa8QWTdodJbamMX0iznLe-q
# 后台不需要登录的接口
[ADMIN_AUTH]
WHITE_LIST[] = v1/user/login
WHITE_LIST[] = v1/user/captcha
WHITE_LIST[] = receive_sync/category
WHITE_LIST[] = receive_sync/product
# 不需记录日志的接口
[ADMIN_API]
IGNORE_LOGGING_LIST[] = v1/OperateLog/index
MAX_IMAGE_SIZE = 5mb; # 图片上传最大限制
MAX_VIDEO_SIZE = 150mb; # 视频上传最大限制
MAX_ATTACHMENT_SIZE = 100mb; # 附件上传最大限制
# 开放API
[OPENAPI]
ACCESS_TOKEN_LIFETIME = 3600; # 访问令牌有效期
REFRESH_TOKEN_LIFETIME = 1209600; # 刷新令牌有效期
RESOURCE_IMAGES_DOMAIN = http://local.orico.com; # 图片资源服务器地址
RESOURCE_VIDEOS_DOMAIN = http://local.orico.com; # 视频资源服务器地址
# 七牛云存储配置
[QINUI]
BUCKET = orico
BASE_URL = http://local.orico.com
ACCESS_KEY = 1234567890
SECRET_KEY = 1234567890
# 前台视图模板规则配置
[INDEX_VIEW_TPL]
# 视图目录
# query 规则URL参数 mtpl=1 表示移动端访问
# 例如http://xxxx.com?mtpl=1
# domain 规则:根据特定域名,判断是否移动端访问
# 例如http://mobile.orico.cn
RULE = query
# query 规则参数名
RULE_QUERY_NAME = mtpl
# query 规则参数值
RULE_QUERY_VALUE = 1
# domain 规则协议
RULE_DOMAIN_SCHEME[] = http
RULE_DOMAIN_SCHEME[] = https
# domain 规则域名
RULE_DOMAIN_HOST = mobile.orico.cn
# 前台语言检测配置
[INDEX_LANG_DETECT]
# 是否启用域名检测方式来检测语言
DOMAIN_DETECT = true
# 域名规则
# 格式:域名=语言
# 例如mobile.orico.cn=zh-cn
DOMAIN_RULE[] = orico.cn=zh-cn
DOMAIN_RULE[] = orico.com.cn=zh-cn
DOMAIN_RULE[] = www.orico.cn=zh-cn
DOMAIN_RULE[] = www.orico.com.cn=zh-cn
DOMAIN_RULE[] = ow.f2b211.com=zh-cn
DOMAIN_RULE[] = orico.cc=en-us
DOMAIN_RULE[] = www.orico.cc=en-us
DOMAIN_RULE[] = ow.us.f2b211.com=en-us

View File

@@ -0,0 +1,26 @@
name: Gitea Actions Official-website
run-name: Deploy to ${{ inputs.deploy_target }} by @${{ gitea.actor }}
on:
push:
branches:
- dev
jobs:
deploy-dev:
runs-on: ubuntu-latest
steps:
- name: Setup SSH
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SERVER_SSH_KEY }}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H ${{ secrets.SERVER_HOST }} >> ~/.ssh/known_hosts
- name: Deploy application
run: |
ssh ${{ secrets.SERVER_USER }}@${{ secrets.SERVER_HOST }} << 'EOF'
set -e
cd /www/wwwroot/dev.ow.f2b211.com
# 拉取最新代码
git pull --rebase
EOF

13
.gitignore vendored
View File

@@ -3,9 +3,22 @@ composer.phar
composer.lock composer.lock
.DS_Store .DS_Store
Thumbs.db Thumbs.db
.env
.env.dev
.env.local
.env.prod
.htaccess
.user.ini
404.html
index.html
public/dist*
public/opendoc
public/logo.png
public/.well-known
/.idea /.idea
/.vscode /.vscode
/.zed
/vendor /vendor
/.settings /.settings
/.buildpath /.buildpath

24
.zed/settings.json Normal file
View File

@@ -0,0 +1,24 @@
// Folder-specific settings
//
// For a full list of overridable settings, and general information on folder-specific settings,
// see the documentation: https://zed.dev/docs/configuring-zed#settings-files
{
"languages": {
"PHP": {
"language_servers": ["intelephense","!phpactor"]
}
},
"lsp": {
"intelephense": {
"initialization_options": {
"stubs": [
"wordpress",
"laravel",
"symfony",
"codeigniter",
"thinkphp"
]
}
}
}
}

208
LICENSE Normal file
View File

@@ -0,0 +1,208 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION,
AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, and distribution
as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by the copyright
owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all other entities
that control, are controlled by, or are under common control with that entity.
For the purposes of this definition, "control" means (i) the power, direct
or indirect, to cause the direction or management of such entity, whether
by contract or otherwise, or (ii) ownership of fifty percent (50%) or more
of the outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions
granted by this License.
"Source" form shall mean the preferred form for making modifications, including
but not limited to software source code, documentation source, and configuration
files.
"Object" form shall mean any form resulting from mechanical transformation
or translation of a Source form, including but not limited to compiled object
code, generated documentation, and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or Object form,
made available under the License, as indicated by a copyright notice that
is included in or attached to the work (an example is provided in the Appendix
below).
"Derivative Works" shall mean any work, whether in Source or Object form,
that is based on (or derived from) the Work and for which the editorial revisions,
annotations, elaborations, or other modifications represent, as a whole, an
original work of authorship. For the purposes of this License, Derivative
Works shall not include works that remain separable from, or merely link (or
bind by name) to the interfaces of, the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including the original version
of the Work and any modifications or additions to that Work or Derivative
Works thereof, that is intentionally submitted to Licensor for inclusion in
the Work by the copyright owner or by an individual or Legal Entity authorized
to submit on behalf of the copyright owner. For the purposes of this definition,
"submitted" means any form of electronic, verbal, or written communication
sent to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems, and
issue tracking systems that are managed by, or on behalf of, the Licensor
for the purpose of discussing and improving the Work, but excluding communication
that is conspicuously marked or otherwise designated in writing by the copyright
owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
of whom a Contribution has been received by Licensor and subsequently incorporated
within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of this
License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable copyright license to reproduce, prepare
Derivative Works of, publicly display, publicly perform, sublicense, and distribute
the Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of this License,
each Contributor hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable (except as stated in this section) patent
license to make, have made, use, offer to sell, sell, import, and otherwise
transfer the Work, where such license applies only to those patent claims
licensable by such Contributor that are necessarily infringed by their Contribution(s)
alone or by combination of their Contribution(s) with the Work to which such
Contribution(s) was submitted. If You institute patent litigation against
any entity (including a cross-claim or counterclaim in a lawsuit) alleging
that the Work or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses granted to You
under this License for that Work shall terminate as of the date such litigation
is filed.
4. Redistribution. You may reproduce and distribute copies of the Work or
Derivative Works thereof in any medium, with or without modifications, and
in Source or Object form, provided that You meet the following conditions:
(a) You must give any other recipients of the Work or Derivative Works a copy
of this License; and
(b) You must cause any modified files to carry prominent notices stating that
You changed the files; and
(c) You must retain, in the Source form of any Derivative Works that You distribute,
all copyright, patent, trademark, and attribution notices from the Source
form of the Work, excluding those notices that do not pertain to any part
of the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its distribution,
then any Derivative Works that You distribute must include a readable copy
of the attribution notices contained within such NOTICE file, excluding those
notices that do not pertain to any part of the Derivative Works, in at least
one of the following places: within a NOTICE text file distributed as part
of the Derivative Works; within the Source form or documentation, if provided
along with the Derivative Works; or, within a display generated by the Derivative
Works, if and wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and do not modify the
License. You may add Your own attribution notices within Derivative Works
that You distribute, alongside or as an addendum to the NOTICE text from the
Work, provided that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and may provide
additional or different license terms and conditions for use, reproduction,
or distribution of Your modifications, or for any such Derivative Works as
a whole, provided Your use, reproduction, and distribution of the Work otherwise
complies with the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise, any
Contribution intentionally submitted for inclusion in the Work by You to the
Licensor shall be under the terms and conditions of this License, without
any additional terms or conditions. Notwithstanding the above, nothing herein
shall supersede or modify the terms of any separate license agreement you
may have executed with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade names,
trademarks, service marks, or product names of the Licensor, except as required
for reasonable and customary use in describing the origin of the Work and
reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or agreed to
in writing, Licensor provides the Work (and each Contributor provides its
Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied, including, without limitation, any warranties
or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR
A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness
of using or redistributing the Work and assume any risks associated with Your
exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory, whether
in tort (including negligence), contract, or otherwise, unless required by
applicable law (such as deliberate and grossly negligent acts) or agreed to
in writing, shall any Contributor be liable to You for damages, including
any direct, indirect, special, incidental, or consequential damages of any
character arising as a result of this License or out of the use or inability
to use the Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all other commercial
damages or losses), even if such Contributor has been advised of the possibility
of such damages.
9. Accepting Warranty or Additional Liability. While redistributing the Work
or Derivative Works thereof, You may choose to offer, and charge a fee for,
acceptance of support, warranty, indemnity, or other liability obligations
and/or rights consistent with this License. However, in accepting such obligations,
You may act only on Your own behalf and on Your sole responsibility, not on
behalf of any other Contributor, and only if You agree to indemnify, defend,
and hold each Contributor harmless for any liability incurred by, or claims
asserted against, such Contributor by reason of your accepting any such warranty
or additional liability. END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following boilerplate
notice, with the fields enclosed by brackets "[]" replaced with your own identifying
information. (Don't include the brackets!) The text should be enclosed in
the appropriate comment syntax for the file format. We also recommend that
a file or class name and description of purpose be included on the same "printed
page" as the copyright notice for easier identification within third-party
archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -0,0 +1,219 @@
<?php
declare (strict_types = 1);
namespace app\admin\controller;
use app\admin\model\v1\LanguageModel;
use app\admin\model\v1\ProductCategoryModel;
use app\admin\model\v1\ProductModel;
use app\admin\model\v1\ProductTcoCategoryModel;
use think\facade\Db;
class Operate_Of_ReceiveSync
{
const Add = 'add';
const Update = 'update';
const Enable = 'enable';
const Disable = 'disable';
}
/**
* 接收产品相关同步数据
*/
class ReceiveProductSync
{
// 接收产品目录分类同步数据
public function category()
{
$data = request()->post();
if (empty($data)) return error('请确认同步数据');
$validate = validate([
'name|分类名称' => 'require',
'erp_code|分类ERP编码' => 'require',
]);
if (!$validate->check($data)) {
return error((string)$validate->getError());
}
// 获取对应语言ID
$lang_id = LanguageModel::where('code', $data['lang'])->value('id');
if (empty($lang_id)) {
return error('语言不存在');
}
Db::startTrans();
try {
$tco_category_data = [
'language_id' => $lang_id,
'name' => $data['name'],
'tco_id' => $data['tco_id'],
'tco_pid' => $data['tco_pid'],
'tco_path' => $data['tco_path'],
'erp_id' => $data['erp_id'],
'erp_pid' => $data['erp_pid'],
'erp_code' => $data['erp_code'],
'erp_path' => $data['erp_path'],
'disabled' => Operate_Of_ReceiveSync::Disable == $data['operate'] ? 1 : 0,
'sync_time' => strtotime($data['created_at'])
];
$tco_category = ProductTcoCategoryModel::language($lang_id)->erpCode($tco_category_data['erp_code'])->find();
if (empty($tco_category)) {
$tco_category = ProductTcoCategoryModel::create($tco_category_data);
if ($tco_category->isEmpty()) {
throw new \Exception('产品目录分类创建失败');
}
$category_data = [
'language_id' => $lang_id,
'unique_id' => uniqid('PRO_CATE_'),
'pid' => 0,
'path' => '',
'name' => $tco_category_data['name'],
'icon' => '',
'desc' => '',
'related_tco_category' => $tco_category['id'],
'sort' => 0,
'level' => 1,
'is_show' => 1,
];
$tco_parent = ProductTcoCategoryModel::language($lang_id)->tcoId($tco_category['tco_pid'])->find();
if (!empty($tco_parent)) {
$parent = ProductCategoryModel::language($lang_id)->tcoId($tco_parent['id'])->find();
if ($parent->isEmpty()) {
throw new \Exception('产品分类父级不存在');
}
$category_data['pid'] = $parent['id'];
$category_data['path'] = $parent['path'] . $parent['pid'];
$category_data['level'] = $parent['level'] + 1;
}
$category = ProductCategoryModel::create($category_data);
if ($category->isEmpty()) {
throw new \Exception('产品分类创建失败');
}
}
else if ($tco_category['sync_time'] < $tco_category_data['sync_time']) {
$success = $tco_category->save($tco_category_data);
if (!$success) {
throw new \Exception('产品目录分类更新失败');
}
$category = ProductCategoryModel::language($lang_id)->tcoId($tco_category['id'])->find();
if (!empty($category)) {
$tco_parent = ProductTcoCategoryModel::language($lang_id)->tcoId($tco_category['tco_pid'])->find();
if (!empty($tco_parent)) {
$parent = ProductCategoryModel::language($lang_id)->tcoId($tco_parent['id'])->find();
if ($parent->isEmpty()) {
throw new \Exception('产品分类父级不存在');
}
$category['pid'] = $parent['id'];
$category['path'] = $parent['path'] . $parent['pid'];
$category['level'] = $parent['level'] + 1;
}
if (!$category->save()) {
throw new \Exception('产品分类更新失败');
}
}
}
Db::commit();
} catch (\Throwable $th) {
Db::rollback();
return error(sprintf('%s %s:%d', $th->getMessage(), $th->getFile(), $th->getLine()));
}
return success('同步成功');
}
// 接收产品同步数据
public function product()
{
$data = request()->post();
if (empty($data)) {
return error('请确认同步数据');
}
$validate = validate([
'spu' => 'require',
'name' => 'require',
'category_erp_code' => 'require',
'lang' => 'require',
'created_at' => 'require',
]);
if (!$validate->check($data)) {
return error((string)$validate->getError());
}
// 获取对应语言ID
$lang_id = LanguageModel::where('code', $data['lang'])->value('id');
if (empty($lang_id)) {
return error('语言不存在');
}
// 如果 spu_before_modification 存在则根据 spu_before_modification 更新型号
$product = null;
if (!empty($data['spu_before_modification'])) {
$product = ProductModel::language($lang_id)->spu($data['spu_before_modification'])->find();
}
if (
(empty($data['spu_before_modification']) && !empty($data['spu']))
// 避免 spu_before_modification 更新型号时,人为删除了旧型号导致的新增,从而出现重复型号问题,而进行再次验证
|| (!empty($data['spu_before_modification']) && empty($product))
) {
$product = ProductModel::language($lang_id)->spu($data['spu'])->find();
}
try {
$product_tco_category = ProductTcoCategoryModel::language($lang_id)->erpCode($data['category_erp_code'])->find();
if (empty($product_tco_category)) {
throw new \Exception('官网未找到产品目录同步分类');
}
$product_category = ProductCategoryModel::language($lang_id)->tcoId($product_tco_category['id'])->find();
if (empty($product_category)) {
throw new \Exception('官网未找到产品目录同步分类关联的分类');
}
if (empty($product)) {
$product = ProductModel::create([
'language_id' => $lang_id,
'category_id' => $product_category['id'],
'spu' => $data['spu'],
'name' => $data['name'],
'short_name' => '',
'cover_image' => '',
'desc' => '',
'video_img' => '',
'video_url' => '',
'is_sale' => 1,
'is_new' => 0,
'is_hot' => 0,
'is_show' => 0,
'sort' => 0,
'detail' => '',
'status' => Operate_Of_ReceiveSync::Disable == $data['operate'] ? -1 : 1,
'seo_title' => '',
'seo_keywords' => '',
'seo_desc' => '',
'updated_at' => $data['created_at'],
]);
if ($product->isEmpty()) {
throw new \Exception('产品创建失败');
}
}
else if (strtotime($product['updated_at']) < strtotime($data['created_at'])) {
$product->spu = $data['spu'];
$product->name = $data['name'];
$product->category_id = $product_category['id'];
$product->status = Operate_Of_ReceiveSync::Disable == $data['operate'] ? -1 : 1;
if (!$product->save()) {
throw new \Exception('产品更新失败');
}
}
} catch (\Throwable $th) {
return error(sprintf('%s %s:%d', $th->getMessage(), $th->getFile(), $th->getLine()));
}
return success('同步成功');
}
}

View File

@@ -1,71 +0,0 @@
<?php
declare (strict_types = 1);
namespace app\admin\controller;
use app\admin\model\v1\LanguageModel;
use app\admin\model\v1\ProductCategoryModel;
use app\admin\model\v1\ProductTcoCategoryModel;
use think\facade\Db;
class Operate_Of_ReceiveSync
{
const Add = 'add';
const Update = 'update';
const Enable = 'enable';
const Disable = 'disable';
}
/**
* 接收同步数据
*/
class ReceiveSync
{
public function product_category()
{
$data = request()->post();
if (empty($data)) return error('请确认同步数据');
$lang_id = LanguageModel::where('code', $data['lang'])->value('id');
$record = [
'language_id' => $lang_id,
'name' => $data['name'],
'tco_id' => $data['tco_id'],
'tco_pid' => $data['tco_pid'],
'tco_path' => $data['tco_path'],
'erp_id' => $data['erp_id'],
'erp_pid' => $data['erp_pid'],
'erp_code' => $data['erp_code'],
'erp_path' => $data['erp_path'],
'disabled' => 0,
'sync_time' => strtotime($data['created_at'])
];
if (Operate_Of_ReceiveSync::Disable == $data['operate']) {
$record['disabled'] = 1;
}
$validate = validate([
'name|分类名称' => 'require',
'erp_code|分类ERP编码' => 'require',
]);
if (!$validate->check($record)) {
throw new \Exception((string)$validate->getError());
}
$category = ProductTcoCategoryModel::language($lang_id)->erpCode($record['erp_code'])->find();
Db::startTrans();
try {
if (empty($category)) {
ProductTcoCategoryModel::create($record);
ProductCategoryModel::where
} else {
$category->save($record);
}
} catch (\Throwable $th) {
Db::rollback();
throw $th;
}
}
}

View File

@@ -228,7 +228,7 @@ class Article
private function getExportArticleData() private function getExportArticleData()
{ {
$server = request()->server(); $server = request()->server();
$image_host = $server['REQUEST_SCHEME'] . "://" . $server['SERVER_NAME'] . config('filesystem.disks.public.url') . '/'; $image_host = $server['REQUEST_SCHEME'] . "://" . $server['SERVER_NAME'] . '/';
$param = request()->param(['title', 'category_id', 'release_time']); $param = request()->param(['title', 'category_id', 'release_time']);
$data = ArticleModel::field([ $data = ArticleModel::field([
'*', '*',
@@ -253,7 +253,7 @@ class Article
]) ])
->bindAttr('category', ['category_name' => 'name']) ->bindAttr('category', ['category_name' => 'name'])
->each(function ($item) use($image_host) { ->each(function ($item) use($image_host) {
$item->image = !empty($item->image) ? $image_host . $item->image : ''; $item->image = !empty($item->image) ? url_join($image_host, $item->image) : '';
return $item; return $item;
}); });

View File

@@ -101,6 +101,8 @@ class BannerItem
'rel_prod_cate_id', 'rel_prod_cate_id',
'title', 'title',
'title_txt_color', 'title_txt_color',
'short_title',
'short_title_txt_color',
'desc', 'desc',
'desc_txt_color', 'desc_txt_color',
'type', 'type',
@@ -156,6 +158,8 @@ class BannerItem
'rel_prod_cate_id', 'rel_prod_cate_id',
'title', 'title',
'title_txt_color', 'title_txt_color',
'short_title',
'short_title_txt_color',
'desc', 'desc',
'desc_txt_color', 'desc_txt_color',
'type', 'type',
@@ -240,6 +244,7 @@ class BannerItem
'banner_name' => '分类名称', 'banner_name' => '分类名称',
'title' => '横幅名称', 'title' => '横幅名称',
'title_txt_color' => '横幅名称字体颜色', 'title_txt_color' => '横幅名称字体颜色',
'short_title' => '横幅简称',
'desc' => '描述', 'desc' => '描述',
'desc_txt_color' => '描述字体颜色', 'desc_txt_color' => '描述字体颜色',
'type' => '前台显示类型', 'type' => '前台显示类型',
@@ -262,17 +267,16 @@ class BannerItem
// 获取导出数据 // 获取导出数据
private function getBannerExportData() private function getBannerExportData()
{ {
$param = request()->param([ $server = request()->server();
'title', $image_host = $server['REQUEST_SCHEME'] . "://" . $server['SERVER_NAME'] . '/';
'banner_id', $param = request()->param(['title', 'banner_id', 'created_at']);
'created_at'
]);
return SysBannerItemModel::alias('item') return SysBannerItemModel::alias('item')
->field([ ->field([
'item.id', 'item.id',
'banner.name' => 'banner_name', 'banner.name' => 'banner_name',
'item.title', 'item.title',
'item.title_txt_color', 'item.title_txt_color',
'item.short_title',
'item.desc', 'item.desc',
'item.desc_txt_color', 'item.desc_txt_color',
'item.type', 'item.type',
@@ -311,7 +315,13 @@ class BannerItem
} }
}) })
->order(['item.sort' => 'asc', 'item.id' => 'desc']) ->order(['item.sort' => 'asc', 'item.id' => 'desc'])
->select(); ->select()
->each(function($item) use($image_host) {
$item->image = !empty($item->image) ? url_join($image_host, $item->image) : '';
$item->extra_image = !empty($item->extra_image) ? url_join($image_host, $item->extra_image) : '';
$item->video = !empty($item->video) ? url_join($image_host, $item->video) : '';
return $item;
});
} }
// 删除 // 删除

View File

@@ -57,7 +57,7 @@ class Product
]) ])
->categoryNullable($param['category_id']??null) ->categoryNullable($param['category_id']??null)
->isShowNullable(isset($param['is_show']) ? (bool)$param['is_show'] : null) ->isShowNullable(isset($param['is_show']) ? (bool)$param['is_show'] : null)
->order(['sort' => 'asc', 'id' => 'desc']) ->order(['sort' => 'asc', 'created_at' => 'desc', 'id' => 'desc'])
->paginate([ ->paginate([
'list_rows' => $param['size'], 'list_rows' => $param['size'],
'page' => $param['page'], 'page' => $param['page'],
@@ -171,7 +171,7 @@ class Product
// 更新产品参数 // 更新产品参数
if ($put['params'] != "") { if ($put['params'] != "") {
ProductParamsModel::productId($id)->delete(); ProductParamsModel::productId($id)->delete();
if (preg_match_all('/(\S+):(.[^\s]+)/', $put['params'], $match_result)) { if (preg_match_all('/(.+):(.+)/', $put['params'], $match_result)) {
$params = []; $params = [];
for ($i = 0; $i < count($match_result[0]); $i++) { for ($i = 0; $i < count($match_result[0]); $i++) {
$params[] = [ $params[] = [
@@ -346,7 +346,7 @@ class Product
private function getExportProductData() private function getExportProductData()
{ {
$server = request()->server(); $server = request()->server();
$image_host = $server['REQUEST_SCHEME'] . "://" . $server['SERVER_NAME'] . config('filesystem.disks.public.url') . '/'; $image_host = $server['REQUEST_SCHEME'] . "://" . $server['SERVER_NAME'] . '/';
$param = request()->param([ $param = request()->param([
'name', 'name',
'spu', 'spu',
@@ -360,10 +360,10 @@ class Product
'spu', 'spu',
'name', 'name',
'short_name', 'short_name',
'CONCAT("' . $image_host . '", `cover_image`)' => 'cover_image', 'cover_image',
'desc', 'desc',
'CONCAT("' . $image_host . '", `video_img`)' => 'video_img', 'video_img',
'CONCAT("' . $image_host . '", `video_url`)' => 'video_url', 'video_url',
'CASE WHEN is_new = 1 THEN "是" ELSE "否" END' => 'is_new', 'CASE WHEN is_new = 1 THEN "是" ELSE "否" END' => 'is_new',
'CASE WHEN is_hot = 1 THEN "是" ELSE "否" END' => 'is_hot', 'CASE WHEN is_hot = 1 THEN "是" ELSE "否" END' => 'is_hot',
'CASE WHEN is_sale = 1 THEN "是" ELSE "否" END' => 'is_sale', 'CASE WHEN is_sale = 1 THEN "是" ELSE "否" END' => 'is_sale',
@@ -390,7 +390,18 @@ class Product
->order(['id' => 'asc']) ->order(['id' => 'asc'])
->select() ->select()
->bindAttr('category', ['category_name' => 'name']) ->bindAttr('category', ['category_name' => 'name'])
->hidden(['category_id', 'category']); ->hidden(['category_id', 'category'])
->each(function($item) use($image_host) {
if (!empty($item["cover_image"])) {
$item["cover_image"] = url_join($image_host, $item["cover_image"]);
}
if (!empty($item["video_img"])) {
$item["video_img"] = url_join($image_host, $item["video_img"]);
}
if (!empty($item["video_url"])) {
$item["video_url"] = url_join($image_host, $item["video_url"]);
}
});
if (!$products->isEmpty()) { if (!$products->isEmpty()) {
// 产品参数 // 产品参数

View File

@@ -16,8 +16,9 @@ class ProductTcoCategory
$param = request()->param(['name']); $param = request()->param(['name']);
$categorys = ProductTcoCategoryModel::field([ $categorys = ProductTcoCategoryModel::field([
'tco_id' => 'id', 'id',
'tco_pid' => 'pid', 'tco_id',
'tco_pid',
'name', 'name',
]) ])
->withSearch(['name'], [ ->withSearch(['name'], [
@@ -25,10 +26,10 @@ class ProductTcoCategory
]) ])
->language(request()->lang_id) ->language(request()->lang_id)
->enabled() ->enabled()
->order(['id' => 'asc']) ->order(['tco_id' => 'asc'])
->select() ->select()
->toArray(); ->toArray();
return success('获取成功', array_to_tree($categorys, 0, 'pid', false)); return success('获取成功', array_to_tree($categorys, 0, 'tco_pid', false, true, 'tco_id'));
} }
} }

View File

@@ -342,6 +342,18 @@ class System
'url' => (string)url('/index/topic/nas/download') 'url' => (string)url('/index/topic/nas/download')
] ]
] ]
],
[
'id' => 8,
'name' => '电力品线专题',
'url' => '',
'children' => [
[
'id' => 81,
'name' => '首页',
'url' => (string)url('/index/topic/power_prodline/index')
],
]
] ]
]; ];
} }

View File

@@ -9,6 +9,7 @@ use app\admin\model\v1\SysAttachmentUploadRecordModel;
use Intervention\Image\ImageManager; use Intervention\Image\ImageManager;
use Intervention\Image\Typography\FontFactory; use Intervention\Image\Typography\FontFactory;
use think\facade\Filesystem; use think\facade\Filesystem;
use filesystem\Qiniu;
/** /**
* 文件上传控制器 * 文件上传控制器
@@ -45,17 +46,39 @@ class Upload
// 获取图片上传配置 // 获取图片上传配置
list( list(
'filename_keep' => $filename_keep, 'filename_keep' => $filename_keep,
'filemd5_unique' => $filemd5_unique 'filemd5_unique' => $filemd5_unique,
'filetype_to' => $filetype_to,
) = $this->getUploadOptions('upload_image'); ) = $this->getUploadOptions('upload_image');
// 获取文件大小
$file_size = $file->getSize();
// 获取文件mime类型
$mime_type = $file->getOriginalMime();
// 是否需要根据文件MD5值检查文件是否已存在 // 是否需要根据文件MD5值检查文件是否已存在
$image_model = $filemd5_unique ? SysImageUploadRecordModel::md5($filemd5)->find() : null; $image_model = $filemd5_unique ? SysImageUploadRecordModel::md5($filemd5)->find() : null;
if (is_null($image_model)) { if (is_null($image_model)) {
// 检查是否需要保留原文件名生成器 // 检查是否需要保留原文件名生成器
$name_rule = fn() => $filename_keep ? $this->filenameGenerator($file) : null; $name_rule = fn() => $filename_keep ? $this->filenameGenerator($file) : null;
$filename = Filesystem::disk('image')->putFile($param['module'], $file, $name_rule());
// 处理图片 // 处理图片
$image_manager = ImageManager::imagick(); $image_manager = ImageManager::gd();
if ($filetype_to == 'original') {
$filename = Filesystem::disk('image')->putFile($param['module'], $file, $name_rule());
$image = $image_manager->read('.' . $storage . '/' . $filename); $image = $image_manager->read('.' . $storage . '/' . $filename);
}
else if ($filetype_to == 'webp') {
$image = $image_manager->read($file->getRealPath());
// 转换为webp格式
$webp = $image->toWebp(75);
$root = config('filesystem.disks.image.root');
$filename = $param['module'] . '/' . ($name_rule() ? $name_rule()() : date('Ymd') . '/' . md5((string)time() . random_str(8))) . '.webp';
$webp->save($this->checkPath($root . '/' . $filename));
// 获取webp文件大小
$file_size = $webp->size();
// 获取webp文件mime类型
$mime_type = $webp->mimetype();
}
// 水印 // 水印
list( list(
@@ -131,10 +154,10 @@ class Upload
$image_model = new SysImageUploadRecordModel(); $image_model = new SysImageUploadRecordModel();
$image_model->language_id = request()->lang_id; $image_model->language_id = request()->lang_id;
$image_model->module = $param['module']; $image_model->module = $param['module'];
$image_model->image_path = $filename; $image_model->image_path = $storage . '/' . $filename;
$image_model->image_thumb = $thumb_filename; $image_model->image_thumb = $storage . '/' . $thumb_filename;
$image_model->file_size = $file->getSize(); $image_model->file_size = $file_size;
$image_model->file_type = $file->getOriginalMime(); $image_model->file_type = $mime_type;
$image_model->file_md5 = $filemd5; $image_model->file_md5 = $filemd5;
$image_model->file_sha1 = $filesha1; $image_model->file_sha1 = $filesha1;
if (!$image_model->save()) { if (!$image_model->save()) {
@@ -143,8 +166,8 @@ class Upload
} }
return success('操作成功', [ return success('操作成功', [
'path' => $storage . '/' . $image_model->image_path, 'path' => $image_model->image_path,
'thumb_path' => $storage . '/' . $image_model->image_thumb, 'thumb_path' => $image_model->image_thumb,
'filemd5' => $image_model->file_md5, 'filemd5' => $image_model->file_md5,
'filesha1' => $image_model->file_sha1 'filesha1' => $image_model->file_sha1
]); ]);
@@ -154,6 +177,30 @@ class Upload
return error('上传失败'); return error('上传失败');
} }
/**
* 检查路径
*
* @param string $path
* @return string
*/
private function checkPath($path): string
{
$ok = true;
$filename = basename($path);
$dirname = dirname($path);
if (!is_dir($dirname)) {
$ok = @mkdir($dirname, 0755, true);
}
else if (!is_writable($dirname)) {
$ok = @chmod($dirname,0755);
}
if ($ok) {
return $dirname . '/' . $filename;
}
throw new \Exception("上传目标目录不可用");
}
/** /**
* 文件名生成回调 * 文件名生成回调
* *
@@ -175,10 +222,12 @@ class Upload
$config_model = new \app\admin\controller\v1\SiteConfig; $config_model = new \app\admin\controller\v1\SiteConfig;
$config = $config_model->getByGroupUniqueLabel('upload'); $config = $config_model->getByGroupUniqueLabel('upload');
$options = data_get($config, $module, []); $options = data_get($config, $module, []);
throw_if(empty($options), '上传配置错误');
return [ return [
'filename_keep' => (int)data_get($options, 'filename_keep.value', 0) == 1, 'filename_keep' => (int)data_get($options, 'filename_keep.value', 0) == 1,
'filemd5_unique' => (int)data_get($options, 'filemd5_unique.value', 0) == 1, 'filemd5_unique' => (int)data_get($options, 'filemd5_unique.value', 0) == 1,
'filetype_to' => data_get($options, 'filetype_to.value', 'original'),
'save_to' => data_get($options, 'save_to.value', 'local'),
]; ];
} }
/** /**
@@ -220,7 +269,7 @@ class Upload
]; ];
} }
/** /**
* 计算文水印偏移量 * 计算文水印偏移量
* *
* @param string $position * @param string $position
* @param integer $offset_x * @param integer $offset_x
@@ -298,20 +347,35 @@ class Upload
// 获取视频上传配置 // 获取视频上传配置
list( list(
'filename_keep' => $filename_keep, 'filename_keep' => $filename_keep,
'filemd5_unique' => $filemd5_unique 'filemd5_unique' => $filemd5_unique,
'save_to' => $save_to,
) = $this->getUploadOptions('upload_video'); ) = $this->getUploadOptions('upload_video');
// 是否需要根据文件MD5值检查文件是否已存在 // 是否需要根据文件MD5值检查文件是否已存在
$video = $filemd5_unique ? SysVideoUploadRecordModel::md5($filemd5)->find() : null; $video = $filemd5_unique ? SysVideoUploadRecordModel::md5($filemd5)->find() : null;
if (is_null($video)) { if (is_null($video)) {
// 保存位置配置 key
$disk = 'video';
// 检查是否需要保留原文件名 // 检查是否需要保留原文件名
$name_rule = fn() => $filename_keep ? $this->filenameGenerator($file) : null; $name_rule = fn() => $filename_keep ? $this->filenameGenerator($file) : null;
$filename = Filesystem::disk('video')->putFile($param['module'], $file, $name_rule());
// 保存到七牛云
if ($save_to == 'qiniu_cloud') {
$disk = 'video_qiniu';
$storage = config('filesystem.disks.video_qiniu.path_prefix');
}
$filename = Filesystem::disk($disk)->putFile($param['module'], $file, $name_rule());
// 组装视频路径
$video_path = $storage . '/' . $filename;
if ($save_to == 'qiniu_cloud') {
$video_path = Filesystem::disk($disk)->url($filename);
}
// 保存视频 // 保存视频
$video = new SysVideoUploadRecordModel(); $video = new SysVideoUploadRecordModel();
$video->language_id = request()->lang_id; $video->language_id = request()->lang_id;
$video->module = $param['module']; $video->module = $param['module'];
$video->video_path = $filename; $video->video_path = $video_path;
$video->file_size = $file->getSize(); $video->file_size = $file->getSize();
$video->file_type = $file->getOriginalMime(); $video->file_type = $file->getOriginalMime();
$video->file_md5 = $filemd5; $video->file_md5 = $filemd5;
@@ -322,7 +386,7 @@ class Upload
} }
return success('上传成功', [ return success('上传成功', [
'path' => $storage . '/' . $video->video_path, 'path' => $video->video_path,
'file_md5' => $video->file_md5, 'file_md5' => $video->file_md5,
'file_sha1' => $video->file_sha1 'file_sha1' => $video->file_sha1
]); ]);
@@ -352,25 +416,42 @@ class Upload
return error($validate->getError()); return error($validate->getError());
} }
$storage = config('filesystem.disks.public.url');
$filemd5 = $file->md5(); $filemd5 = $file->md5();
$filesha1 = $file->sha1(); $filesha1 = $file->sha1();
// 获取附件上传配置 // 获取附件上传配置
list( list(
'filename_keep' => $filename_keep, 'filename_keep' => $filename_keep,
'filemd5_unique' => $filemd5_unique 'filemd5_unique' => $filemd5_unique,
'save_to' => $save_to
) = $this->getUploadOptions('upload_attachment'); ) = $this->getUploadOptions('upload_attachment');
// 是否需要根据文件MD5值检查文件是否已存在 // 是否需要根据文件MD5值检查文件是否已存在
$attachment = $filemd5_unique ? SysAttachmentUploadRecordModel::md5($filemd5)->find() : null; $attachment = $filemd5_unique ? SysAttachmentUploadRecordModel::md5($filemd5)->find() : null;
if (is_null($attachment)) { if (is_null($attachment)) {
// 保存位置配置 key
$disk = 'public';
// 检查是否需要保留原文件名 // 检查是否需要保留原文件名
$name_rule = fn() => $filename_keep ? $this->filenameGenerator($file) : null; $name_rule = fn() => $filename_keep ? $this->filenameGenerator($file) : null;
$filename = Filesystem::disk('public')->putFile('attachments', $file, $name_rule());
// 保存视频 // 保存到七牛云
if ($save_to == 'qiniu_cloud') {
$disk = 'public_qiniu';
$storage = config('filesystem.disks.public_qiniu.path_prefix');
}
$filename = Filesystem::disk($disk)->putFile('attachments', $file, $name_rule());
// 组装附件路径
$attachment_path = $storage . '/' . $filename;
if ($save_to == 'qiniu_cloud') {
$attachment_path = Filesystem::disk($disk)->url($filename);
}
// 保存附件
$attachment = new SysAttachmentUploadRecordModel(); $attachment = new SysAttachmentUploadRecordModel();
$attachment->language_id = request()->lang_id; $attachment->language_id = request()->lang_id;
$attachment->attachment_path = $filename; $attachment->attachment_path = $attachment_path;
$attachment->file_size = $file->getSize(); $attachment->file_size = $file->getSize();
$attachment->file_type = $file->getOriginalMime(); $attachment->file_type = $file->getOriginalMime();
$attachment->file_md5 = $filemd5; $attachment->file_md5 = $filemd5;
@@ -380,9 +461,8 @@ class Upload
} }
} }
$storage = config('filesystem.disks.public.url');
return success('上传成功', [ return success('上传成功', [
'path' => $storage . '/' . $attachment->attachment_path, 'path' => $attachment->attachment_path,
'file_md5' => $attachment->file_md5, 'file_md5' => $attachment->file_md5,
'file_sha1' => $attachment->file_sha1 'file_sha1' => $attachment->file_sha1
]); ]);

View File

@@ -209,9 +209,6 @@ class Video
]); ]);
$domain = request()->domain(); $domain = request()->domain();
$image_path = Config::get('filesystem.disks.image.url');
$video_path = Config::get('filesystem.disks.video.url');
return VideoModel::withoutField([ return VideoModel::withoutField([
'language_id', 'language_id',
'updated_at', 'updated_at',
@@ -230,13 +227,9 @@ class Video
->select() ->select()
->bindAttr('category', ['category_name' => 'name']) ->bindAttr('category', ['category_name' => 'name'])
->hidden(['category_id', 'category']) ->hidden(['category_id', 'category'])
->each(function ($item) use($domain, $image_path, $video_path) { ->each(function ($item) use($domain) {
if (!empty($item->image)) { $item->image = !empty($item->image) ? url_join($domain, $item->image) : '';
$item->image = $domain . $image_path . '/' . $item->image; $item->video = !empty($item->video) ? url_join($domain, $item->video) : '';
}
if (!empty($item->video)) {
$item->video = $domain . $video_path . '/' . $item->video;
}
$item->recommend = $item->recommend == 1 ? '是' : '否'; $item->recommend = $item->recommend == 1 ? '是' : '否';
$item->status = $item->status == 1 ? '启用' : '禁用'; $item->status = $item->status == 1 ? '启用' : '禁用';
return $item; return $item;

View File

@@ -29,6 +29,12 @@ class ProductCategoryModel extends ProductCategoryBaseModel
$query->where('language_id', '=', $value); $query->where('language_id', '=', $value);
} }
// 所属产品目录分类id查询
public function scopeTcoId($query, $value)
{
$query->where('related_tco_category', '=', $value);
}
/** /**
* 根据是否显示查询 * 根据是否显示查询
* @param $query * @param $query

View File

@@ -80,6 +80,12 @@ class ProductModel extends ProductBaseModel
$query->where('category_id', '=', $value); $query->where('category_id', '=', $value);
} }
// 规格型号查询
public function scopeSpu($query, $spu)
{
$query->where('spu', '=', $spu);
}
// 启用状态查询 // 启用状态查询
public function scopeEnabled($query) public function scopeEnabled($query)
{ {

View File

@@ -23,6 +23,12 @@ class ProductTcoCategoryModel extends ProductTcoCategoryBaseModel
$query->where('language_id', '=', $value); $query->where('language_id', '=', $value);
} }
// 根据 tco_id 查询
public function scopeTcoId($query, $value)
{
$query->where('tco_id', '=', $value);
}
// 根据ERP Code查询 // 根据ERP Code查询
public function scopeErpCode($query, $value) public function scopeErpCode($query, $value)
{ {

View File

@@ -616,6 +616,12 @@ Route::group('v1', function () {
}); });
})->prefix('v1.'); })->prefix('v1.');
// 接收产品目录同步数据
Route::group('receive_sync', function () {
Route::post('category', 'ReceiveProductSync/category');
Route::post('product', 'ReceiveProductSync/product');
});
Route::miss(function() { Route::miss(function() {
return '404 Not Found!'; return '404 Not Found!';
}); });

View File

@@ -64,7 +64,10 @@ class ArticleCategoryValidate extends Validate
if ($value == 0) { if ($value == 0) {
return true; return true;
} }
$children = [];
$table_name = (new ArticleCategoryModel)->getTable(); $table_name = (new ArticleCategoryModel)->getTable();
if (env('DB_VERSION', '5') == '8') {
$children = Db::query( $children = Db::query(
preg_replace( preg_replace(
'/\s+/u', '/\s+/u',
@@ -77,6 +80,20 @@ class ArticleCategoryValidate extends Validate
SELECT id FROM article_tree_by WHERE id <> {$data['id']};" SELECT id FROM article_tree_by WHERE id <> {$data['id']};"
) )
); );
} else {
$children = \think\facade\Db::query("
SELECT t2.id
FROM (
SELECT
@r AS _id, (SELECT @r := GROUP_CONCAT(id) FROM $table_name WHERE FIND_IN_SET(pid, _id)) AS parent_id
FROM
(SELECT @r := {$data['id']}) vars, $table_name h
WHERE @r <> 0) t1
JOIN $table_name t2
ON FIND_IN_SET(t2.pid, t1._id)
ORDER BY t2.id;
");
}
if (!empty($children) && in_array($data['pid'], array_column($children, 'id'))) { if (!empty($children) && in_array($data['pid'], array_column($children, 'id'))) {
return false; return false;
} }

View File

@@ -23,9 +23,9 @@ class ArticleValidate extends Validate
'sort' => 'integer', 'sort' => 'integer',
'recommend' => 'require|in:0,1', 'recommend' => 'require|in:0,1',
'release_time' => 'dateFormat:Y-m-d H:i:s', 'release_time' => 'dateFormat:Y-m-d H:i:s',
'seo_title' => 'max:255', 'seo_title' => 'max:512',
'seo_keywords' => 'max:255', 'seo_keywords' => 'max:512',
'seo_desc' => 'max:255' 'seo_desc' => 'max:1024'
]; ];
/** /**
@@ -48,8 +48,8 @@ class ArticleValidate extends Validate
'recommend.require' => '推荐状态不能为空', 'recommend.require' => '推荐状态不能为空',
'recommend.in' => '推荐状态的值必须是0或1', 'recommend.in' => '推荐状态的值必须是0或1',
'release_time.dateFormat' => '发布时间格式不正确', 'release_time.dateFormat' => '发布时间格式不正确',
'seo_title.max' => 'SEO标题长度不能超过255个字符', 'seo_title.max' => 'SEO标题长度不能超过512个字符',
'seo_keywords.max' => 'SEO关键字长度不能超过255个字符', 'seo_keywords.max' => 'SEO关键字长度不能超过512个字符',
'seo_desc.max' => 'SEO描述长度不能超过255个字符' 'seo_desc.max' => 'SEO描述长度不能超过1024个字符'
]; ];
} }

View File

@@ -49,7 +49,10 @@ class AttachmentCategoryValidate extends Validate
if ($value == 0) { if ($value == 0) {
return true; return true;
} }
$children = [];
$table_name = (new AttachmentCategoryModel)->getTable(); $table_name = (new AttachmentCategoryModel)->getTable();
if (env('DB_VERSION', '5') == '8') {
$children = Db::query( $children = Db::query(
preg_replace( preg_replace(
'/\s+/u', '/\s+/u',
@@ -62,6 +65,20 @@ class AttachmentCategoryValidate extends Validate
SELECT id FROM attachment_tree_by WHERE id <> {$data['id']};" SELECT id FROM attachment_tree_by WHERE id <> {$data['id']};"
) )
); );
} else {
$children = \think\facade\Db::query("
SELECT t2.id
FROM (
SELECT
@r AS _id, (SELECT @r := GROUP_CONCAT(id) FROM $table_name WHERE FIND_IN_SET(pid, _id)) AS parent_id
FROM
(SELECT @r := {$data['id']}) vars, $table_name h
WHERE @r <> 0) t1
JOIN $table_name t2
ON FIND_IN_SET(t2.pid, t1._id)
ORDER BY t2.id;
");
}
if (!empty($children) && in_array($data['pid'], array_column($children, 'id'))) { if (!empty($children) && in_array($data['pid'], array_column($children, 'id'))) {
return false; return false;
} }

View File

@@ -61,7 +61,10 @@ class NavigationItemValidate extends Validate
if ($value == 0) { if ($value == 0) {
return true; return true;
} }
$children = [];
$table_name = (new SysNavigationItemModel)->getTable(); $table_name = (new SysNavigationItemModel)->getTable();
if (env('DB_VERSION', '5') == '8') {
$children = Db::query( $children = Db::query(
preg_replace( preg_replace(
'/\s+/u', '/\s+/u',
@@ -74,6 +77,20 @@ class NavigationItemValidate extends Validate
SELECT id FROM tree_by WHERE id <> {$data['id']};" SELECT id FROM tree_by WHERE id <> {$data['id']};"
) )
); );
} else {
$children = \think\facade\Db::query("
SELECT t2.id
FROM (
SELECT
@r AS _id, (SELECT @r := GROUP_CONCAT(id) FROM $table_name WHERE FIND_IN_SET(pid, _id)) AS parent_id
FROM
(SELECT @r := {$data['id']}) vars, $table_name h
WHERE @r <> 0) t1
JOIN $table_name t2
ON FIND_IN_SET(t2.pid, t1._id)
ORDER BY t2.id;
");
}
if (!empty($children) && in_array($data['pid'], array_column($children, 'id'))) { if (!empty($children) && in_array($data['pid'], array_column($children, 'id'))) {
return false; return false;
} }

View File

@@ -15,7 +15,7 @@ class SysBannerItemValidate extends Validate
*/ */
protected $rule = [ protected $rule = [
'id' => 'require|integer', 'id' => 'require|integer',
'banner_id' => 'require|integer', 'banner_id' => 'require|integer|gt:0',
'title' => 'require|max:256', 'title' => 'require|max:256',
'title_txt_color' => 'max:7', 'title_txt_color' => 'max:7',
'desc' => 'max:1024', 'desc' => 'max:1024',
@@ -25,7 +25,7 @@ class SysBannerItemValidate extends Validate
'extra_image' => 'max:255', 'extra_image' => 'max:255',
'video' => 'max:255', 'video' => 'max:255',
'link_to' => 'requireIf:type,image|max:64|in:article,article_category,product,product_category,system_page,custom', 'link_to' => 'requireIf:type,image|max:64|in:article,article_category,product,product_category,system_page,custom',
'link' => 'max:255', 'link' => 'max:510',
'sort' => 'integer', 'sort' => 'integer',
'status' => 'in:-1,1' 'status' => 'in:-1,1'
]; ];
@@ -41,6 +41,7 @@ class SysBannerItemValidate extends Validate
'id.integer' => 'ID必须是整数', 'id.integer' => 'ID必须是整数',
'banner_id.require' => '横幅项分类不能为空', 'banner_id.require' => '横幅项分类不能为空',
'banner_id.integer' => '横幅项分类必须是整数', 'banner_id.integer' => '横幅项分类必须是整数',
'banner_id.gt' => '该横幅分类不可选',
'title.require' => '名称不能为空', 'title.require' => '名称不能为空',
'title.max' => '名称最多不能超过256个字符', 'title.max' => '名称最多不能超过256个字符',
'title_txt_color.max' => '名称字体颜色最多不能超过7个字符', 'title_txt_color.max' => '名称字体颜色最多不能超过7个字符',
@@ -53,7 +54,7 @@ class SysBannerItemValidate extends Validate
'link_to.requireIf' => '链接类型不能为空', 'link_to.requireIf' => '链接类型不能为空',
'link_to.max' => '链接类型最多不能超过64个字符', 'link_to.max' => '链接类型最多不能超过64个字符',
'link_to.in' => '链接类型必须是article,article_category,product,product_category,system_page,custom中之一', 'link_to.in' => '链接类型必须是article,article_category,product,product_category,system_page,custom中之一',
'link.max' => '链接最多不能超过255个字符', 'link.max' => '链接最多不能超过512个字符',
'sort.integer' => '排序值必须是整数', 'sort.integer' => '排序值必须是整数',
'status.in' => '状态必须是-1或1' 'status.in' => '状态必须是-1或1'
]; ];

View File

@@ -74,7 +74,10 @@ class SysMenuValidate extends Validate
if ($value == 0) { if ($value == 0) {
return true; return true;
} }
$children = [];
$table_name = (new SysMenuModel)->getTable(); $table_name = (new SysMenuModel)->getTable();
if (env('DB_VERSION', '5') == '8') {
$children = Db::query( $children = Db::query(
preg_replace( preg_replace(
'/\s+/u', '/\s+/u',
@@ -87,6 +90,20 @@ class SysMenuValidate extends Validate
SELECT id FROM menu_tree_by WHERE id <> {$data['id']};" SELECT id FROM menu_tree_by WHERE id <> {$data['id']};"
) )
); );
} else {
$children = \think\facade\Db::query("
SELECT t2.id
FROM (
SELECT
@r AS _id, (SELECT @r := GROUP_CONCAT(id) FROM $table_name WHERE FIND_IN_SET(pid, _id)) AS parent_id
FROM
(SELECT @r := {$data['id']}) vars, $table_name h
WHERE @r <> 0) t1
JOIN $table_name t2
ON FIND_IN_SET(t2.pid, t1._id)
ORDER BY t2.id;
");
}
if (!empty($children) && in_array($data['pid'], array_column($children, 'id'))) { if (!empty($children) && in_array($data['pid'], array_column($children, 'id'))) {
return false; return false;
} }

View File

@@ -17,8 +17,8 @@ class VideoValidate extends Validate
'id' => 'require|integer', 'id' => 'require|integer',
'language_id' => 'require|integer', 'language_id' => 'require|integer',
'category_id' => 'require|integer', 'category_id' => 'require|integer',
'name' => 'require|max:64', 'name' => 'require|max:128',
'desc' => 'max:255', 'desc' => 'max:512',
'image' => 'max:125', 'image' => 'max:125',
'video' => 'max:125', 'video' => 'max:125',
'link' => 'url|max:125', 'link' => 'url|max:125',
@@ -43,8 +43,8 @@ class VideoValidate extends Validate
'category_id.require' => '分类不能为空', 'category_id.require' => '分类不能为空',
'category_id.integer' => '分类参数类型错误', 'category_id.integer' => '分类参数类型错误',
'name.require' => '名称不能为空', 'name.require' => '名称不能为空',
'name.max' => '名称不能超过64个字符', 'name.max' => '名称不能超过128个字符',
'desc.max' => '描述不能超过255个字符', 'desc.max' => '描述不能超过512个字符',
'image.max' => '图片不能超过125个字符', 'image.max' => '图片不能超过125个字符',
'video.max' => '视频不能超过125个字符', 'video.max' => '视频不能超过125个字符',
'link.url' => '链接格式错误', 'link.url' => '链接格式错误',

View File

@@ -61,6 +61,9 @@ class DataMigration extends Command
// 迁移文章 // 迁移文章
// $this->migrateArticle([ // $this->migrateArticle([
// 16 => 7,
// 31 => 9,
// 32 => 8,
// 68 => 10, // 68 => 10,
// 69 => 11, // 69 => 11,
// 70 => 12, // 70 => 12,
@@ -290,14 +293,15 @@ class DataMigration extends Command
$old_db = Db::connect('old'); $old_db = Db::connect('old');
$success_map = []; $success_map = [];
$success_arr = include_once(runtime_path() . 'product_success.php'); $success_arr = []; // include_once(runtime_path() . 'product_success.php');
foreach ($success_arr as $so) { foreach ($success_arr as $so) {
$success_map['p_' . $so['cod_product_id']] = $so; $success_map['p_' . $so['cod_product_id']] = $so;
} }
$arr = include_once(runtime_path() . 'product_ids.php');
$products = $old_db->name('product') $products = $old_db->name('product')
->where('country_code', 'in', ['ZH', 'US']) ->where('country_code', 'in', ['ZH', 'US'])
->where('id', '>', 15789) ->where('id', 'in', array_unique(array_column($arr, 'cod_product_id')))
->order(['id' => 'asc']) ->order(['id' => 'asc'])
->cursor(); ->cursor();
@@ -374,7 +378,16 @@ class DataMigration extends Command
'deleted_at' => $v['stat'] == -1 ? date('Y-m-d H:i:s') : null, 'deleted_at' => $v['stat'] == -1 ? date('Y-m-d H:i:s') : null,
]; ];
// 保存产品数据 // 保存产品数据
$prod = Db::name('product')
->where('language_id', '=', $item['language_id'])
->where('spu', '=', $item['spu'])
->find();
if (!empty($prod)) {
$id = $prod['id'];
Db::name('product')->where('id', '=', $prod['id'])->update($item);
} else {
$id = Db::name('product')->insertGetId($item); $id = Db::name('product')->insertGetId($item);
}
// 保存产品参数数据 // 保存产品参数数据
if (!empty($v['product_view'])) { if (!empty($v['product_view'])) {
@@ -387,7 +400,21 @@ class DataMigration extends Command
'value' => $p['desc_desc'] 'value' => $p['desc_desc']
]; ];
} }
$old_params = Db::name('product_params')
->where('product_id', '=', $id)
->select();
if ($old_params->isEmpty()) {
Db::name('product_params')->insertAll($prarms); Db::name('product_params')->insertAll($prarms);
} else {
foreach ($old_params as $op) {
Db::name('product_params')
->where('product_id', '=', $id)
->where('name', '=', $op['name'])
->update([
'value' => $op['value']
]);
}
}
} }
// 保存sku数据 // 保存sku数据
@@ -444,7 +471,7 @@ class DataMigration extends Command
if ($k != 'sort') { if ($k != 'sort') {
$attr_value = $at; $attr_value = $at;
if (in_array($k, ['颜色', 'Color'])) { if (in_array($k, ['颜色', 'Color'])) {
if ($k == 'Color') $k = '颜色'; // if ($k == 'Color') $k = '颜色';
$attr_value = '/static/common/images/colors/' . $at . '.png'; $attr_value = '/static/common/images/colors/' . $at . '.png';
} }
$images[$pkey]['color'] = $at; $images[$pkey]['color'] = $at;
@@ -468,7 +495,7 @@ class DataMigration extends Command
} }
} }
$attr_arr = [ $attr_arr = [
'attr_id' => $attrs_map[$v['country_code']]['颜色'], 'attr_id' => $attrs_map[$v['country_code']][$v['country_code'] == 'ZH' ? '颜色' : 'Color'],
'attr_value' => $attr_value, 'attr_value' => $attr_value,
]; ];
if ( if (
@@ -514,7 +541,7 @@ class DataMigration extends Command
$skus[] = [ $skus[] = [
'main_image' => $ti['image_url'], 'main_image' => $ti['image_url'],
'attrs' => [[ 'attrs' => [[
'attr_id' => $attrs_map[$v['country_code']]['颜色'], 'attr_id' => $attrs_map[$v['country_code']][$v['country_code'] == 'ZH' ? '颜色' : 'Color'],
'attr_value' => $ti['image_color'], 'attr_value' => $ti['image_color'],
]], ]],
'pkey' => $tpkey 'pkey' => $tpkey
@@ -575,6 +602,7 @@ class DataMigration extends Command
} }
foreach ($skus as $sku) { foreach ($skus as $sku) {
Db::name('product_sku')->where('product_id', '=', $sku['product_id'])->delete();
$sku_id = Db::name('product_sku')->insertGetId([ $sku_id = Db::name('product_sku')->insertGetId([
'product_id' => $sku['product_id'], 'product_id' => $sku['product_id'],
'sku' => $sku['sku']??'', 'sku' => $sku['sku']??'',
@@ -584,6 +612,7 @@ class DataMigration extends Command
if (!empty($sku['attrs'])) { if (!empty($sku['attrs'])) {
foreach ($sku['attrs'] as $attr) { foreach ($sku['attrs'] as $attr) {
if (!empty($sku_id)) { if (!empty($sku_id)) {
Db::name('product_sku_attr')->where('sku_id', '=', $sku_id)->delete();
Db::name('product_sku_attr')->insert([ Db::name('product_sku_attr')->insert([
'sku_id' => $sku_id, 'sku_id' => $sku_id,
'attr_id' => $attr['attr_id'], 'attr_id' => $attr['attr_id'],
@@ -601,7 +630,7 @@ class DataMigration extends Command
sprintf('["ow_product_id" => %d, "cod_product_id" => %d]'.PHP_EOL, $id, $v['id']), sprintf('["ow_product_id" => %d, "cod_product_id" => %d]'.PHP_EOL, $id, $v['id']),
FILE_APPEND FILE_APPEND
); );
$this->println(sprintf('迁移产品ID%s => %s 【耗时:%s】', $v['id'], 0, round(microtime(true) - $start, 2) . 's')); $this->println(sprintf('迁移产品ID%s => %s 【耗时:%s】', $v['id'], $id, round(microtime(true) - $start, 2) . 's'));
} catch (\Throwable $th) { } catch (\Throwable $th) {
Db::rollback(); Db::rollback();
file_put_contents( file_put_contents(
@@ -694,9 +723,12 @@ class DataMigration extends Command
throw new \Exception('请确认分类ID'); throw new \Exception('请确认分类ID');
} }
// 1634
$article = Db::connect('old') $article = Db::connect('old')
->name('article') ->name('article')
->where('country_code', 'in', ['ZH', 'US'])
->where('cid', 'in', array_keys($category_map)) ->where('cid', 'in', array_keys($category_map))
->where('id', '=', 351)
->order(['id' => 'asc']) ->order(['id' => 'asc'])
->cursor(); ->cursor();
@@ -745,7 +777,16 @@ class DataMigration extends Command
'release_time' => date('Y-m-d H:i:s', $v['createtime']), 'release_time' => date('Y-m-d H:i:s', $v['createtime']),
'deleted_at' => $v['stat'] == -1 ? date('Y-m-d H:i:s') : null 'deleted_at' => $v['stat'] == -1 ? date('Y-m-d H:i:s') : null
]; ];
$ret = Db::name('article')
->where('language_id', '=', $item['language_id'])
->where('title', '=', $item['title'])
->find();
if (empty($ret)) {
$id = Db::name('article')->insertGetId($item); $id = Db::name('article')->insertGetId($item);
} else {
$id = $ret['id'];
Db::name('article')->where('id', '=', $ret['id'])->update($item);
}
$this->println(sprintf('迁移文章ID%s => %s', $v['id'], $id)); $this->println(sprintf('迁移文章ID%s => %s', $v['id'], $id));
} }
@@ -779,7 +820,15 @@ class DataMigration extends Command
'recommend' => $val['is_home'], 'recommend' => $val['is_home'],
'sort' => $val['sort'] == 9999 ? 0 : $val['sort'] 'sort' => $val['sort'] == 9999 ? 0 : $val['sort']
]; ];
$ret = Db::name('faq')
->where('language_id', '=', $item['language_id'])
->where('question', '=', $item['question'])
->find();
if (empty($ret)) {
Db::name('faq')->insert($item); Db::name('faq')->insert($item);
} else {
Db::name('faq')->where('id', '=', $ret['id'])->update($item);
}
} }
} }
@@ -881,7 +930,8 @@ class DataMigration extends Command
{ {
$videos = Db::connect('old') $videos = Db::connect('old')
->name('video') ->name('video')
->where('id', '>', 844) // ->where('id', '>', 844)
->where('cid', 'in', array_keys($category_map))
->where('country_code', 'in', ['ZH', 'US']) ->where('country_code', 'in', ['ZH', 'US'])
->order(['id' => 'asc']) ->order(['id' => 'asc'])
->cursor(); ->cursor();
@@ -921,7 +971,16 @@ class DataMigration extends Command
'seo_desc' => $val['seo_description'], 'seo_desc' => $val['seo_description'],
'deleted_at' => $val['stat'] == -1 ? date('Y-m-d H:i:s') : null 'deleted_at' => $val['stat'] == -1 ? date('Y-m-d H:i:s') : null
]; ];
$ret = Db::name('video')
->where('language_id', '=', $item['language_id'])
->where('name', '=', $item['name'])
->find();
if (empty($ret)) {
Db::name('video')->insert($item); Db::name('video')->insert($item);
} else {
Db::name('video')->where('id', '=', $ret['id'])->update($item);
}
$this->println('迁移视频ID' . $val['id']); $this->println('迁移视频ID' . $val['id']);
} }
} }
@@ -929,7 +988,7 @@ class DataMigration extends Command
class UploadMannager class UploadMannager
{ {
const UPLOAD_BASE_API = 'http://dev.ow.f2b211.com'; const UPLOAD_BASE_API = 'http://ow.f2b211.com';
const DOWNLOAD_BASE_API = 'http://www.orico.com.cn'; const DOWNLOAD_BASE_API = 'http://www.orico.com.cn';
const DOWNLOAD_TEMP_PATH = '/var/www/html/orico-official-website/public/migrate_temp_images'; const DOWNLOAD_TEMP_PATH = '/var/www/html/orico-official-website/public/migrate_temp_images';
private $username = 'admin'; private $username = 'admin';
@@ -942,7 +1001,7 @@ class UploadMannager
{ {
// 登录获取token // 登录获取token
$this->token = $this->getAuthorization(); $this->token = $this->getAuthorization();
$this->maps = include_once(runtime_path() . 'fiber_product_image_mapping.php'); $this->maps = []; // include_once(runtime_path() . 'fiber_product_image_mapping.php');
} }
// 下载图片 // 下载图片
@@ -1062,6 +1121,7 @@ class UploadMannager
print($http_code . PHP_EOL); print($http_code . PHP_EOL);
} }
file_put_contents(runtime_path() . 'upload.txt', $response . PHP_EOL, FILE_APPEND);
$ret = json_decode($response, true); $ret = json_decode($response, true);
if (empty($ret)) { if (empty($ret)) {
throw new \Exception($response); throw new \Exception($response);

View File

@@ -0,0 +1,42 @@
<?php
declare (strict_types = 1);
namespace app\command\OpenApiMgr;
use oauth\OAuthStorage;
use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\input\Option;
use think\console\Output;
class AddClient extends Command
{
protected function configure()
{
// 指令配置
$this->setName('OpenApiMgr:AddClient')
->addArgument('salt', Argument::OPTIONAL, "开放API的client_secret密钥的盐值")
->setDescription('开放API的client管理');
}
protected function execute(Input $input, Output $output)
{
$salt = $input->getArgument('salt');
$salt = empty($salt) ? null : trim($salt);
// 指令输出
$oauth = new OAuthStorage($salt);
$client_id = random_str(13, 'all', 0);
$client_secret = random_str(32, 'all', 0);
$ok = $oauth->addClient($client_id, $client_secret, null);
if (!$ok) {
$output->writeln("添加失败");
return;
}
$output->writeln("添加成功:\nClientID: {$client_id}\nClientSecret: {$client_secret}\n");
}
}

View File

@@ -80,7 +80,7 @@ if (!function_exists('array_to_tree')) {
* @param bool $keep_pid 是否保留pid * @param bool $keep_pid 是否保留pid
* @return array * @return array
*/ */
function array_to_tree(array $data, int $pid, string $with = 'pid', int|bool $level = 1, bool $keep_pid = true) function array_to_tree(array $data, int $pid, string $with = 'pid', int|bool $level = 1, bool $keep_pid = true, $with_ref = 'id')
{ {
$ret = []; $ret = [];
foreach ($data as $item) { foreach ($data as $item) {
@@ -93,7 +93,7 @@ if (!function_exists('array_to_tree')) {
if ($keep_pid === false) { if ($keep_pid === false) {
unset($item[$with]); unset($item[$with]);
} }
$children = array_to_tree($data, $item['id'], $with, $lv, $keep_pid); $children = array_to_tree($data, $item[$with_ref], $with, $lv, $keep_pid, $with_ref);
if ($children) { if ($children) {
$item['children'] = $children; $item['children'] = $children;
} }
@@ -145,3 +145,90 @@ if (!function_exists('thumb')) {
return mb_substr($url, 0, $idx, 'utf-8') . '_thumb' . mb_substr($url, $idx, $len - $idx, 'utf-8'); return mb_substr($url, 0, $idx, 'utf-8') . '_thumb' . mb_substr($url, $idx, $len - $idx, 'utf-8');
} }
} }
if (!function_exists('get_filesystem_url')) {
/**
* 获取文件系统的访问 URL
* @param string $url 文件地址
* @param string $disk 磁盘配置 key
* @return string
*/
function get_filesystem_url(string|null $url, string $disk): string
{
if (is_null($url)) {
return '';
}
if (\think\helper\Str::startsWith($url, ['http://', 'https://', '//'])) {
return $url;
}
if (empty($disk)) {
return '';
}
return \think\facade\Filesystem::disk($disk)->url($url);
}
}
if (!function_exists('url_filesystem_detect')) {
/**
* 检测文件地址并根据情况转换为文件系统地址
* @param string $url 文件地址
* @return string
*/
function url_filesystem_detect(string|null $url): string
{
if (is_null($url)) {
return '';
}
$idx = strrpos($url, '.');
if ($idx === false) {
return $url;
}
$disks = [
'public_qiniu' => '_' . base64_encode('public_qiniu'),
'video_qiniu' => '_' . base64_encode('video_qiniu')
];
foreach ($disks as $disk => $marker) {
if (str_ends_with(mb_substr($url, 0, $idx), $marker)) {
return get_filesystem_url($url, $disk);
}
}
return $url;
}
}
if (!function_exists('url_join')) {
/**
* 合并URL
* @param string $url 基础URL
* @param string $path 路径
* @param bool $remove_slash 是否移除首尾的斜杠
* @return string
*/
function url_join(string $url, string $path, bool $remove_slash = true): string
{
if (empty($url)) {
return $path;
}
if (empty($path)) {
return $url;
}
if (\think\helper\Str::startsWith($path, ['http://', 'https://', '//'])) {
return $path;
}
if ($remove_slash) {
if (str_ends_with($url, '/') && str_starts_with($path, '/')) {
return $url . substr($path, 1);
}
if (!str_ends_with($url, '/') && !str_starts_with($path, '/')) {
return $url . '/' . $path;
}
}
return $url . $path;
}
}

View File

@@ -21,6 +21,8 @@ class SysBannerItemBaseModel extends BaseModel
'banner_id' => 'int', 'banner_id' => 'int',
'title' => 'string', 'title' => 'string',
'title_txt_color' => 'string', 'title_txt_color' => 'string',
'short_title' => 'string',
'short_title_txt_color' => 'string',
'desc' => 'string', 'desc' => 'string',
'desc_txt_color' => 'string', 'desc_txt_color' => 'string',
'type' => 'string', 'type' => 'string',

View File

@@ -18,6 +18,6 @@ class SysRoleAuthorityBaseModel extends Model
protected $schema = [ protected $schema = [
'role_id' => 'int', 'role_id' => 'int',
'menu_id' => 'int', 'menu_id' => 'int',
'permission' => 'int', 'permission' => 'string',
]; ];
} }

View File

@@ -194,14 +194,14 @@ if (!function_exists('get_platform')) {
} else { } else {
// 在非移动端环境,根据配置规则判断是否要显示移动端 // 在非移动端环境,根据配置规则判断是否要显示移动端
$view_cfg = $view_cfg = [ $view_cfg = $view_cfg = [
'rule' => env('VIEW_TPL.RULE', 'query'), 'rule' => env('INDEX_VIEW_TPL.RULE', 'query'),
'query' => [ 'query' => [
'name' => env('VIEW_TPL.RULE_QUERY_NAME', 'mtpl'), 'name' => env('INDEX_VIEW_TPL.RULE_QUERY_NAME', 'mtpl'),
'value' => env('VIEW_TPL.RULE_QUERY_VALUE', '1'), 'value' => env('INDEX_VIEW_TPL.RULE_QUERY_VALUE', '1'),
], ],
'domain' => [ 'domain' => [
'scheme' => env('VIEW_TPL.RULE_DOMAIN_SCHEME', ['http']), 'scheme' => env('INDEX_VIEW_TPL.RULE_DOMAIN_SCHEME', ['http']),
'host' => env('VIEW_TPL.RULE_DOMAIN_HOST'), 'host' => env('INDEX_VIEW_TPL.RULE_DOMAIN_HOST'),
], ],
]; ];
if ($view_cfg['rule'] == 'query') { if ($view_cfg['rule'] == 'query') {
@@ -226,18 +226,18 @@ if (!function_exists('get_platform')) {
if (!function_exists('highlight_keywords')) { if (!function_exists('highlight_keywords')) {
/** /**
* 高亮关键词 * 高亮关键词
* @param string $item * @param string $text // 要处理的文本
* @param string $keywords * @param string $keyword // 关键词
* @param array $class * @param string|callable $repalce // 替换函数或字符串
* @return string * @return string
*/ */
function highlight_keywords(string $text, string $keywords, array $class=[]): string function highlight_keywords(string $text, string $keyword, string|callable $replace): string
{ {
return preg_replace_callback('/' . preg_quote($keywords, '/') . '+/i', function($match) use($text, $class) { return preg_replace_callback('/' . preg_quote($keyword, '/') . '+/i', function($match) use($text, $replace) {
if (empty($match)) { if (empty($match)) return $text;
return $text; if (is_string($replace)) return '<strong>' . $match[0] . '</strong>';
} if (is_callable($replace)) return $replace($match[0]);
return '<strong class="' . implode(' ', $class) . '">' . $match[0] . '</strong>'; return $match[0];
}, $text); }, $text);
} }
} }

View File

@@ -170,9 +170,9 @@ class Article extends Common
]); ]);
$ret = ArticleLeaveMessageModel::create($data); $ret = ArticleLeaveMessageModel::create($data);
if ($ret->isEmpty()) { if ($ret->isEmpty()) {
return error(lang('留言提交失败')); return error(lang('信息提交失败'));
} }
return success(lang('留言提交成功')); return success(lang('信息已成功提交!'));
} }
} }

View File

@@ -21,7 +21,7 @@ class Attachment extends Common
{ {
$param = request()->param([ $param = request()->param([
'id', 'id',
'keyword', 'keyword' => '',
'page/d' => 1, 'page/d' => 1,
'size/d' => 12, 'size/d' => 12,
]); ]);
@@ -48,9 +48,9 @@ class Attachment extends Common
'support_platform', 'support_platform',
'attach', 'attach',
]) ])
->withSearch(['name'], ['name' => $param['keyword']??null]) ->withSearch(['name'], ['name' => !empty($param['keyword']) ? trim($param['keyword']) : null])
->language($this->lang_id) ->language($this->lang_id)
->category($param['id']??null) ->category(!empty($param['id']) ? $param['id'] : $categorys[0]['id']??null)
->order(['sort' => 'asc', 'id' => 'desc']) ->order(['sort' => 'asc', 'id' => 'desc'])
->paginate([ ->paginate([
'list_rows' => $param['size'], 'list_rows' => $param['size'],
@@ -59,6 +59,16 @@ class Attachment extends Common
'id' => $param['id']??null 'id' => $param['id']??null
] ]
]); ]);
if (!$attachements->isEmpty()) {
$attachements->each(function($item) {
if (is_array($item->attach)) {
$item->attach = array_map(function($v) {
$v['file_path'] = url_filesystem_detect($v['file_path']);
return $v;
}, $item->attach);
}
});
}
View::assign('attachements', $attachements); View::assign('attachements', $attachements);
View::assign('page', $attachements->render()); View::assign('page', $attachements->render());
@@ -109,9 +119,9 @@ class Attachment extends Common
'video', 'video',
'link' 'link'
]) ])
->withSearch(['name'], ['name' => $param['keyword']??null]) ->withSearch(['name'], ['name' => !empty($param['keyword']) ? trim($param['keyword']) : null])
->language($this->lang_id) ->language($this->lang_id)
->category($param['id']??$video_categorys[0]['id']??null) ->category(!empty($param['id']) ? $param['id'] : $video_categorys[0]['id']??null)
->order(['sort' => 'asc', 'id' => 'desc']) ->order(['sort' => 'asc', 'id' => 'desc'])
->paginate([ ->paginate([
'list_rows' => $param['size'], 'list_rows' => $param['size'],
@@ -122,6 +132,9 @@ class Attachment extends Common
]); ]);
if (!$videos->isEmpty()) { if (!$videos->isEmpty()) {
$videos->each(function($item) {
$item->video = url_filesystem_detect($item->video);
});
$videos->setCollection($videos->getCollection()->chunk(2)); $videos->setCollection($videos->getCollection()->chunk(2));
} }
View::assign('videos', $videos); View::assign('videos', $videos);

View File

@@ -106,6 +106,7 @@ abstract class Common extends BaseController
'language_id' => $language, 'language_id' => $language,
'status' => 1 'status' => 1
]) ])
->where('status', '=', 1)
->order(['sort' => 'asc', 'id' => 'asc']) ->order(['sort' => 'asc', 'id' => 'asc'])
->select(); ->select();
if ($nav->isEmpty()) { if ($nav->isEmpty()) {

View File

@@ -104,7 +104,7 @@ class Product extends Common
->onSale(true) ->onSale(true)
->onShelves(true) ->onShelves(true)
->append(['p' => $list[0]['id']]) ->append(['p' => $list[0]['id']])
->order(['sort' => 'asc', 'id' => 'desc']) ->order(['sort' => 'asc', 'created_at' => 'desc', 'id' => 'desc'])
->limit(5) ->limit(5)
->buildSql(); ->buildSql();
$query = \think\facade\Db::table("($sql) as a"); $query = \think\facade\Db::table("($sql) as a");
@@ -126,7 +126,7 @@ class Product extends Common
->enabled(true) ->enabled(true)
->onSale(true) ->onSale(true)
->onShelves(true) ->onShelves(true)
->order(['sort' => 'asc', 'id' => 'desc']) ->order(['sort' => 'asc', 'created_at' => 'desc', 'id' => 'desc'])
->limit(5); ->limit(5);
}); });
} }
@@ -192,7 +192,7 @@ class Product extends Common
->enabled(true) ->enabled(true)
->onSale(true) ->onSale(true)
->onShelves(true) ->onShelves(true)
->order(['sort' => 'asc', 'id' => 'desc']) ->order(['sort' => 'asc', 'created_at' => 'desc', 'id' => 'desc'])
->select(); ->select();
if (!$products->isEmpty()) { if (!$products->isEmpty()) {
// 获取sku信息 // 获取sku信息
@@ -277,7 +277,7 @@ class Product extends Common
'page/d' => 1, 'page/d' => 1,
'size/d' => 10 'size/d' => 10
]); ]);
$keywords = $param['keywords'] ?? ''; $keywords = !empty($param['keywords']) ? trim($param['keywords']) : '';
// 关键词搜索 // 关键词搜索
$products = ProductModel::field([ $products = ProductModel::field([
@@ -299,9 +299,10 @@ class Product extends Common
'query' => request()->param() 'query' => request()->param()
]) ])
->each(function ($item) use($keywords) { ->each(function ($item) use($keywords) {
$item['spu'] = highlight_keywords($item['spu'], $keywords, ['redpoint']); $replace = fn($txt) => '<strong class="redpoint">' . $txt . '</strong>';
$item['name'] = highlight_keywords($item['name'], $keywords, ['redpoint']); $item['spu'] = highlight_keywords($item['spu'], $keywords, $replace);
$item['short_name'] = highlight_keywords($item['short_name'], $keywords, ['redpoint']); $item['name'] = highlight_keywords($item['name'], $keywords, $replace);
$item['short_name'] = highlight_keywords($item['short_name'], $keywords, $replace);
return $item; return $item;
}); });
View::assign('products', $products); View::assign('products', $products);
@@ -395,8 +396,11 @@ class Product extends Common
->order(['sort' => 'asc', 'id' => 'desc']) ->order(['sort' => 'asc', 'id' => 'desc'])
->select() ->select()
->hidden(['platform']) ->hidden(['platform'])
->bindAttr('platform', ['platform_name' => 'platform']) ->bindAttr('platform', ['platform_name' => 'platform', 'platform_sort' => 'sort'])
->toArray(); ->toArray();
// 根据购买链接平台排序
$sort_by_arr = array_column($product_purchase_links, 'platform_sort');
array_multisort($sort_by_arr, SORT_ASC, $product_purchase_links);
// 获取相关产品信息 // 获取相关产品信息
$related = ProductRelatedModel::with(['product' => function($query) { $related = ProductRelatedModel::with(['product' => function($query) {

View File

@@ -192,6 +192,8 @@ class TopicNas extends Common
{ {
// 获取文章分类及文章数据 // 获取文章分类及文章数据
$parent = ArticleCategoryModel::uniqueLabel('CATEGORY_681182e0a4529')->language($this->lang_id)->value('id'); $parent = ArticleCategoryModel::uniqueLabel('CATEGORY_681182e0a4529')->language($this->lang_id)->value('id');
$parent_two = ArticleCategoryModel::parent($parent)->language($this->lang_id)->column('id');//二级分类id
array_push($parent_two,$parent);
$article_categorys = ArticleCategoryModel::with(['article' => function($query) { $article_categorys = ArticleCategoryModel::with(['article' => function($query) {
$query->field(['id', 'title', 'category_id']) $query->field(['id', 'title', 'category_id'])
->order(['sort' => 'asc', 'id' => 'desc']) ->order(['sort' => 'asc', 'id' => 'desc'])
@@ -199,14 +201,48 @@ class TopicNas extends Common
}]) }])
->field([ ->field([
'id', 'id',
'pid',
'name', 'name',
'icon' 'icon'
]) ])
->language($this->lang_id) ->language($this->lang_id)
->parent($parent) // ->parent($parent)
->parentChild($parent_two)
->isShow(true) ->isShow(true)
->order(['sort' => 'asc', 'id' => 'desc'])
->select(); ->select();
View::assign('article_categorys', $article_categorys); //查询三级分类
$article_categorys_new = [];
$article_categorys_two = [];
// dump($article_categorys->toArray());exit;
if (!$article_categorys->isEmpty()) {
foreach ($article_categorys->toArray() as $kk=>$vv) {
if ( $parent == $vv['pid'] ) {
array_push($article_categorys_new,$vv);
} else {
$article_categorys_two[$vv['pid']][] = $vv;
}
}
if ( !empty($article_categorys_two) ) {
foreach ($article_categorys_new as &$vvv) {
$articles = $vvv['article'];
if ( isset($article_categorys_two[$vvv['id']]) ) {
foreach ($article_categorys_two[$vvv['id']] as $v) {
foreach ($v['article'] as $av) {
if ( count($articles) < 3 ) {
array_push($articles,$av);
}
}
}
}
$vvv['article'] = $articles;
}
}
}
View::assign('article_categorys', $article_categorys_new);
// View::assign('article_categorys', $article_categorys);
$contacts = []; $contacts = [];
// 获取banner数据 // 获取banner数据
@@ -245,19 +281,46 @@ class TopicNas extends Common
// 获取文章分类及文章数据 // 获取文章分类及文章数据
$parent = ArticleCategoryModel::uniqueLabel('CATEGORY_681182e0a4529')->language($this->lang_id)->value('id'); $parent = ArticleCategoryModel::uniqueLabel('CATEGORY_681182e0a4529')->language($this->lang_id)->value('id');
$parent_two = ArticleCategoryModel::parent($parent)->language($this->lang_id)->column('id');//二级分类id
array_push($parent_two,$parent);
$article_categorys = ArticleCategoryModel::with(['article' => function ($query) { $article_categorys = ArticleCategoryModel::with(['article' => function ($query) {
$query->field(['id', 'title', 'category_id'])->order(['sort' => 'asc', 'id' => 'desc']); $query->field(['id', 'title', 'category_id'])->order(['sort' => 'asc', 'id' => 'desc']);
}]) }])
->field([ ->field([
'id', 'id',
'pid',
'name', 'name',
'icon' 'icon'
]) ])
->language($this->lang_id) ->language($this->lang_id)
->parent($parent) // ->parent($parent)
->parentChild($parent_two)
->isShow(true) ->isShow(true)
->order(['sort' => 'asc', 'id' => 'desc'])
->select(); ->select();
View::assign('article_categorys', $article_categorys); // dump($article_categorys->toArray());exit;
//查询三级分类
$article_categorys_new = [];
$article_categorys_two = [];
if (!$article_categorys->isEmpty()) {
foreach ($article_categorys->toArray() as $kk=>$vv) {
if ( $parent == $vv['pid'] ) {
$vv['child'] = '';
array_push($article_categorys_new,$vv);
} else {
$article_categorys_two[$vv['pid']][] = $vv;
}
}
if ( !empty($article_categorys_two) ) {
foreach ($article_categorys_new as &$vvv) {
$vvv['child'] = isset($article_categorys_two[$vvv['id']])?$article_categorys_two[$vvv['id']]:'';
}
}
}
// dump($article_categorys_new);exit;
// dump($article_categorys_two);exit;
View::assign('article_categorys', $article_categorys_new);
return View::fetch('help_detail'); return View::fetch('help_detail');
} }
@@ -274,7 +337,9 @@ class TopicNas extends Common
->value('id'); ->value('id');
// 获取帮且中心分类子分类 // 获取帮且中心分类子分类
$categorys = [];
$table_name = (new ArticleCategoryModel)->getTable(); $table_name = (new ArticleCategoryModel)->getTable();
if (env('DB_VERSION', '5') == '8') {
$categorys = \think\facade\Db::query(preg_replace( $categorys = \think\facade\Db::query(preg_replace(
'/\s+/u', '/\s+/u',
' ', ' ',
@@ -285,6 +350,20 @@ class TopicNas extends Common
) )
SELECT id FROM article_tree_by WHERE id <> {$parent}" SELECT id FROM article_tree_by WHERE id <> {$parent}"
)); ));
} else {
$categorys = \think\facade\Db::query("
SELECT t2.id
FROM (
SELECT
@r AS _id, (SELECT @r := GROUP_CONCAT(id) FROM $table_name WHERE FIND_IN_SET(pid, _id)) AS parent_id
FROM
(SELECT @r := $parent) vars, $table_name h
WHERE @r <> 0) t1
JOIN $table_name t2
ON FIND_IN_SET(t2.pid, t1._id)
ORDER BY t2.id;
");
}
if (empty($categorys)) return success('success', []); if (empty($categorys)) return success('success', []);
// 获取文章数据 // 获取文章数据
@@ -299,8 +378,18 @@ class TopicNas extends Common
->language($this->lang_id) ->language($this->lang_id)
->where('category_id', 'IN', array_column($categorys, 'id')) ->where('category_id', 'IN', array_column($categorys, 'id'))
->select(); ->select();
//查询上级id
$parent_two = ArticleCategoryModel::parentColumn(array_column($categorys, 'id'))->language($this->lang_id)->column('pid','id');//二级分类id
$articles_data = $articles->toArray();
foreach ($articles_data as &$v) {
$v['pid'] = 0;
if ( $parent_two[$v['category_id']] !== $parent ) {
$v['pid'] = $v['category_id'];
$v['category_id'] = $parent_two[$v['category_id']];
}
}
return success('success', $articles->toArray()); return success('success', $articles_data);
} }
/** /**

View File

@@ -0,0 +1,127 @@
<?php
declare(strict_types=1);
namespace app\index\controller;
use app\index\model\SysBannerModel;
use think\facade\View;
use think\Request;
/**
* 专题 - 电力品线控制器
*/
class TopicPowerProdline extends Common
{
/**
* 重写控制器初始化
*/
public function initialize()
{
// 获取国家/语言列表
$languages = $this->getLanguages();
// 输出国家/语言列表
if (get_platform() == 'mobile') {
View::assign('header_languages', $languages);
}
// 获取当前语言
$current_language = $this->getCurrentLanguage($languages);
if (!empty($current_language)) {
$this->lang_id = $current_language['id'];
}
// 获取产品分类
$categorys = $this->getProductCategory($this->lang_id);
// 输出产品分类
View::assign('header_categorys', $categorys);
// 获取系统配置
$configs = $this->getSysConfig($this->lang_id, ['basic', 'contact', 'media']);
$this->basic_config = $configs['basic'];
// 输出系统配置
View::assign('basic_config', $configs['basic']);
View::assign('contact_config', $configs['contact']);
View::assign('media_config', $configs['media']);
// 获取底部导航
$footer_navigation = $this->getNavigation('NAV_67f60be43df8d', $this->lang_id);
// 输出底部导航
View::assign('footer_navigation', $footer_navigation);
}
/**
* 专题 - 电力品线首页
*/
public function index()
{
$banners = SysBannerModel::with([
'items' => function ($query) {
$query->withoutField(['sort', 'created_at', 'updated_at', 'deleted_at'])
->order(['sort' => 'asc', 'id' => 'desc'])
->enabled(true);
}
])
->atPlatform(request()->from)
->uniqueLabel([
'BANNER_691e729f2428d',
'BANNER_691e732e4ad69',
'BANNER_691e752d2bbe2',
'BANNER_691e75561c4d3',
'BANNER_691e75ec9391c',
'BANNER_691e7616545bf',
'BANNER_691e763fc08f4',
'BANNER_691e765a27eba',
'BANNER_691e76b6af393',
])
->language($this->lang_id)
->enabled(true)
->order(['sort' => 'asc', 'id' => 'desc'])
->select();
$data = [];
if (!$banners->isEmpty()) {
$banners_map = [];
foreach ($banners as $banner) {
$banners_map[$banner->unique_label] = $banner;
}
// 焦点轮播图
$focus_image = data_get($banners_map, 'BANNER_691e729f2428d')?->items->toArray();
if (!empty($focus_image)) $data['focus_image'] = $focus_image;
// 分类
$category = data_get($banners_map, 'BANNER_691e732e4ad69')?->items->toArray();
if (!empty($category)) $data['category'] = $category;
// 为什么选择奥睿科相关数据
$why_choose = data_get($banners_map, 'BANNER_691e752d2bbe2')?->items->toArray();
if (!empty($why_choose)) $data['why_choose'] = $why_choose;
// 差旅充
$travel_charger = data_get($banners_map, 'BANNER_691e75561c4d3')?->items->toArray();
if (!empty($travel_charger)) $data['travel_charger'] = $travel_charger;
// 家居充
$home_charger = data_get($banners_map, 'BANNER_691e75ec9391c')?->items->toArray();
if (!empty($home_charger)) $data['home_charger'] = $home_charger;
// 桌面充
$desktop_charger = data_get($banners_map, 'BANNER_691e7616545bf')?->items->toArray();
if (!empty($desktop_charger)) $data['desktop_charger'] = $desktop_charger;
// 墙充
$wall_charger = data_get($banners_map, 'BANNER_691e763fc08f4')?->items->toArray();
if (!empty($wall_charger)) $data['wall_charger'] = $wall_charger;
// 转换器
$converter = data_get($banners_map, 'BANNER_691e765a27eba')?->items->toArray();
if (!empty($converter)) $data['converter'] = $converter;
// 底部介绍
$footer_info = data_get($banners_map, 'BANNER_691e76b6af393')?->items->toArray();
if (!empty($footer_info)) $data['footer_info'] = $footer_info;
}
View::assign('data', $data);
return View::fetch('index');
}
}

View File

@@ -53,6 +53,7 @@ return [
'product/search' => [ 'product/search' => [
'搜索' => 'Search', '搜索' => 'Search',
'请搜索' => 'Please search...', '请搜索' => 'Please search...',
'暂无数据' => 'No data',
], ],
// 产品详情 // 产品详情
'product/detail' => [ 'product/detail' => [

View File

@@ -161,6 +161,7 @@ return [
// 产品 - 产品详情页 // 产品 - 产品详情页
'product/detail' => [ 'product/detail' => [
'首页' => 'Home', '首页' => 'Home',
'型号' => 'Product Model',
'产品详情' => 'Product Description', '产品详情' => 'Product Description',
'相关产品' => 'Related Products', '相关产品' => 'Related Products',
'发送查询' => 'Send Inquiry', '发送查询' => 'Send Inquiry',
@@ -177,6 +178,11 @@ return [
'提交' => 'SUBMIT', '提交' => 'SUBMIT',
], ],
// 产品 - 搜索
'product/search' => [
'暂无数据' => 'No data',
],
// 产品 - 分类 // 产品 - 分类
'product/category' => [ 'product/category' => [
'查看全部' => 'View All', '查看全部' => 'View All',

View File

@@ -2,7 +2,8 @@
// 这是系统自动生成的middleware定义文件 // 这是系统自动生成的middleware定义文件
return [ return [
// 启用多语言支持 // 启用多语言支持
think\middleware\LoadLangPack::class, // think\middleware\LoadLangPack::class,
app\index\middleware\LoadLangPack::class,
// 确认请求来源 // 确认请求来源
app\index\middleware\ConfirmRequestFrom::class, app\index\middleware\ConfirmRequestFrom::class,
]; ];

View File

@@ -0,0 +1,39 @@
<?php
declare (strict_types = 1);
namespace app\index\middleware;
use think\Request;
class LoadLangPack extends \think\middleware\LoadLangPack
{
// 重写检测语言方法
protected function detect(Request $request): string
{
$domain_detect = env('INDEX_LANG_DETECT.DOMAIN_DETECT', false);
if ($domain_detect) {
$lang = $this->getLangSet($request, env('INDEX_LANG_DETECT.DOMAIN_RULE', []));
if ($lang != '') {
return $lang;
}
}
return parent::detect($request);
}
// 根据请求及规则获取语言
private function getLangSet(Request $request, array $rules): string
{
$map = [];
foreach ($rules as $v) {
$val = str_replace('', ',', $v);
$item = explode(',', $v);
foreach ($item as $val) {
$it = explode('=', $val);
$map[$it[0]] = $it[1];
}
}
return $map[$request->host()] ?? '';
}
}

View File

@@ -46,6 +46,27 @@ class ArticleCategoryModel extends ArticleCategoryBaseModel
$query->where('pid', '=', $parent); $query->where('pid', '=', $parent);
} }
// 所属上级分类范围查询
public function scopeParentChild($query, $parent)
{
if (is_array($parent)) {
$query->where('pid', 'IN', $parent);
return;
}
$query->where('pid', '=', $parent);
}
// 所属上级分类查询
public function scopeParentColumn($query, $parent)
{
if (is_array($parent)) {
$query->where('id', 'IN', $parent);
return;
}
$query->where('id', '=', $parent);
}
// 所属子分类范围查询 // 所属子分类范围查询
public function scopeChild($query, $id, $merge_self = false) public function scopeChild($query, $id, $merge_self = false)
{ {

View File

@@ -105,6 +105,12 @@ Route::group('topic', function () {
// 专题-Nas软件下载页 // 专题-Nas软件下载页
Route::get('download', 'TopicNas/download'); Route::get('download', 'TopicNas/download');
}); });
// 专题 - 电力品线
Route::group("power_prodline", function() {
// 专题 - 电力品线首页
Route::get('index', 'TopicPowerProdline/index');
});
}); });
// 数据迁移 // 数据迁移

View File

@@ -14,7 +14,7 @@
<div class="tabs"> <div class="tabs">
{notempty name="video_categorys"} {notempty name="video_categorys"}
{volist name="video_categorys" id="va"} {volist name="video_categorys" id="va"}
<a href="{:url('attachment/index', ['id' => $va.id])}"><div class="tabit active">{$va.name}</div></a> <a href="{:url('attachment/video', ['id' => $va.id])}"><div class="tabit active">{$va.name}</div></a>
{/volist} {/volist}
{/notempty} {/notempty}
</div> </div>

View File

@@ -101,7 +101,7 @@
<a href="{$sc.link}"><img src="{$sc.image}" /></a> <a href="{$sc.link}"><img src="{$sc.image}" /></a>
<div class="position_a text_center wow animated bounceInLeft"> <div class="position_a text_center wow animated bounceInLeft">
<p class="f_weight_500 timetitle" {:style(['color'=>$sc['title_txt_color']])}>{$sc.title}</p> <p class="f_weight_500 timetitle" {:style(['color'=>$sc['title_txt_color']])}>{$sc.title}</p>
<p class=" margin-top-14 f_weight_400 timedesin" {:style(['color'=>$sc['desc_txt_color']])}>{$sc.desc}</p> <p class=" margin-top-14 f_weight_400 timedesin" {:style(['color'=>$sc['desc_txt_color']])}>{$sc.desc|raw}</p>
<p class=" margin-top-20 f_weight_400"> <p class=" margin-top-20 f_weight_400">
<a href="{$sc.link}" class="timeblue"> {:lang_i18n('了解更多')} <img src="__IMAGES__/more-r.png"></a> <a href="{$sc.link}" class="timeblue"> {:lang_i18n('了解更多')} <img src="__IMAGES__/more-r.png"></a>
</p> </p>
@@ -141,7 +141,7 @@
<img src="{$bs.image}"> <img src="{$bs.image}">
<div class="position_a text_center"> <div class="position_a text_center">
<p class=" timetitle" {:style(['color'=>$bs['title_txt_color']])}>{$bs.title} </p> <p class=" timetitle" {:style(['color'=>$bs['title_txt_color']])}>{$bs.title} </p>
<p class=" margin-top-14 f_weight_400 timedesin" {:style(['color'=>$bs['desc_txt_color']])}>{$bs.desc}</p> <p class=" margin-top-14 f_weight_400 timedesin" {:style(['color'=>$bs['desc_txt_color']])}>{$bs.desc|raw}</p>
<p class=" margin-top-20 f_weight_400"> <p class=" margin-top-20 f_weight_400">
<span class=" timeblue"> <span class=" timeblue">
<a href="{$bs.link}">{:lang_i18n('了解更多')}<img src="__IMAGES__/more-r.png"></a> <a href="{$bs.link}">{:lang_i18n('了解更多')}<img src="__IMAGES__/more-r.png"></a>

View File

@@ -46,7 +46,7 @@
{volist name="vo.products" id="pro" length="4"} {volist name="vo.products" id="pro" length="4"}
<li class="img-responsive"> <li class="img-responsive">
<a href="{:url('product/detail', ['id' => $pro.id])}"> <a href="{:url('product/detail', ['id' => $pro.id])}">
<img src="{$pro.cover_image}"> <img src="{:thumb($pro.cover_image)}">
<span class="title">{$pro.name}</span> <span class="title">{$pro.name}</span>
<span class="subtitle">{$pro.spu}</span> <span class="subtitle">{$pro.spu}</span>
</a> </a>

View File

@@ -22,7 +22,11 @@
<a class="href_01">{:lang_i18n('首页')}</a> <a class="href_01">{:lang_i18n('首页')}</a>
{volist name="product_categorys" id="ca"} {volist name="product_categorys" id="ca"}
<span class="icon-arrow arrow_address"></span> <span class="icon-arrow arrow_address"></span>
{eq name="ca.pid" value="0"}
<a class="href_02" href="{:url('product/category', ['id' => $ca.id])}">{$ca.name}</a> <a class="href_02" href="{:url('product/category', ['id' => $ca.id])}">{$ca.name}</a>
{else /}
<a class="href_02" href="{:url('product/subcategory', ['id' => $ca.id])}">{$ca.name}</a>
{/eq}
{/volist} {/volist}
</div> </div>
</div> </div>
@@ -37,6 +41,13 @@
<img src="{$photo}" alt="" /> <img src="{$photo}" alt="" />
</div> </div>
{/volist} {/volist}
{if condition="!empty($product.video_img) && !empty($product.video_url) && $idx == 1"}
<div class="swiper-slide">
<video poster="{$product.video_img}" autoplay="autoplay" muted="muted" loop="loop" id="video" controls>
<source src="{$product.video_url}" type="video/mp4"/>
</video>
</div>
{/if}
</div> </div>
<!-- 如果需要分页器 --> <!-- 如果需要分页器 -->
<div class="swiper-pagination"></div> <div class="swiper-pagination"></div>

View File

@@ -17,7 +17,7 @@
<div class="prlist"> <div class="prlist">
{volist name="products" id="pro"} {volist name="products" id="pro"}
<a class="pritem" href="{:url('product/detail',['id'=>$pro.id])}"> <a class="pritem" href="{:url('product/detail',['id'=>$pro.id])}">
<img src="{$pro.cover_image}" class="primg"/> <img src="{:thumb($pro.cover_image)}" class="primg"/>
<div class="prinfo"> <div class="prinfo">
<span class="t1">{$pro.name|raw}</span> <span class="t1">{$pro.name|raw}</span>
<span class="t2">{$pro.spu|raw}</span> <span class="t2">{$pro.spu|raw}</span>
@@ -28,7 +28,7 @@
<!-- 分页 --> <!-- 分页 -->
<div>{$page|raw}</div> <div>{$page|raw}</div>
{else/} {else/}
<div style="text-align: center; padding: 10%;">暂无数据</div> <div style="text-align: center; padding: 10%;">{:lang_i18n('暂无数据')}</div>
{/notempty} {/notempty}
</div> </div>
</div> </div>

View File

@@ -19,6 +19,7 @@
<div class="m_Container"> <div class="m_Container">
{notempty name="categorys_data"} {notempty name="categorys_data"}
<div class="product_list"> <div class="product_list">
{if condition="in_array('products', array_keys($categorys_data[0]))"}
<ul> <ul>
{assign name="products" value=":\think\helper\Arr::flatMap(fn($pro) => $pro['products'], $categorys_data)" /} {assign name="products" value=":\think\helper\Arr::flatMap(fn($pro) => $pro['products'], $categorys_data)" /}
{volist name="products" id="pr"} {volist name="products" id="pr"}
@@ -55,6 +56,7 @@
</li> </li>
{/volist} {/volist}
</ul> </ul>
{/if}
</div> </div>
{/notempty} {/notempty}
</div> </div>

View File

@@ -11,13 +11,12 @@
<link rel="stylesheet" type="text/css" href="__CSS__/public.css" /> <link rel="stylesheet" type="text/css" href="__CSS__/public.css" />
<link rel="stylesheet" type="text/css" href="__CSS__/font.css" /> <link rel="stylesheet" type="text/css" href="__CSS__/font.css" />
<link rel="stylesheet" type="text/css" href="__CSS__/style.css" /> <link rel="stylesheet" type="text/css" href="__CSS__/style.css" />
<!-- <link rel="stylesheet" type="text/css" href="__CSS__/fonts.css" /> -->
<link rel="stylesheet" type="text/css" href="__CSS__/header.css" /> <link rel="stylesheet" type="text/css" href="__CSS__/header.css" />
<link rel="stylesheet" type="text/css" href="__CSS__/footer.css" /> <link rel="stylesheet" type="text/css" href="__CSS__/footer.css" />
{block name="style"}{/block} {block name="style"}{/block}
<link rel="stylesheet" href="https://unpkg.com/swiper@9/swiper-bundle.min.css"> <link rel="stylesheet" href="__JS__/swiper-bundle9.4.1.min.css" />
<script type="text/javascript" src='https://code.jquery.com/jquery-3.6.0.min.js'></script> <script src="__JS__/jquery-3.6.0.min.js"></script>
<script type="text/javascript" src="https://unpkg.com/swiper@9.4.1/swiper-bundle.min.js"></script> <script type="text/javascript" src="__JS__/swiper-bundle9.4.1.min.js"></script>
<!-- Matomo --> <!-- Matomo -->
<script> <script>
var _paq = window._paq = window._paq || []; var _paq = window._paq = window._paq || [];
@@ -27,7 +26,7 @@
(function() { (function() {
var u="//analytics.f2b211.com/"; var u="//analytics.f2b211.com/";
_paq.push(['setTrackerUrl', u+'matomo.php']); _paq.push(['setTrackerUrl', u+'matomo.php']);
_paq.push(['setSiteId', '1']); _paq.push(['setSiteId', '2']);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s); g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
})(); })();
@@ -37,7 +36,7 @@
<body> <body>
<noscript> <noscript>
<!-- Matomo Image Tracker--> <!-- Matomo Image Tracker-->
<img referrerpolicy="no-referrer-when-downgrade" src="https://analytics.f2b211.com/matomo.php?idsite=1&amp;rec=1" style="border:0" alt="" /> <img referrerpolicy="no-referrer-when-downgrade" src="https://analytics.f2b211.com/matomo.php?idsite=2&amp;rec=1" style="border:0" alt="" />
<!-- End Matomo --> <!-- End Matomo -->
</noscript> </noscript>
{block name="header"} {block name="header"}

View File

@@ -29,12 +29,13 @@
{/notempty} {/notempty}
<li> <li>
<h3>{:lang_i18n('联系方式')}</h3> <h3>{:lang_i18n('联系方式')}</h3>
{notempty name="contact_config.website_email"} {if condition="!empty($contact_config)"}
<p>{$contact_config.website_email.title} {$contact_config.website_email.value}</p> {volist name="contact_config" id="vo"}
{/notempty} {if condition="$vo.type != 'image'"}
{notempty name="contact_config.website_hotline_office_hours"} <p>{$vo.value}</p>
<p>{$contact_config.website_hotline_office_hours.title} {$contact_config.website_hotline_office_hours.value}</p> {/if}
{/notempty} {/volist}
{/if}
</li> </li>
</ul> </ul>
</div> </div>
@@ -64,7 +65,7 @@
<div class="copy-text"> <div class="copy-text">
{$basic_config.website_powerby.value} {$basic_config.website_powerby.value}
{notempty name="$basic_config.website_icp"} {notempty name="$basic_config.website_icp"}
<a href="https://beian.miit.gov.cn/" style="color:white;">{$basic_config.website_icp.value}</a> <a href="https://beian.miit.gov.cn/" style="color:white;">{$basic_config.website_icp.value|raw}</a>
{/notempty} {/notempty}
</div> </div>
{/notempty} {/notempty}

View File

@@ -12,22 +12,30 @@
<span class="icon-category cursor_p top-menu-toggle"><i class="icon-menu-svg"></i></span> <span class="icon-category cursor_p top-menu-toggle"><i class="icon-menu-svg"></i></span>
</div> </div>
</div> </div>
</div>
<!-- 顶部国家选择-->
<div class="top-country">
<div class="mask"></div>
<div class="action-sheet">
<div class="menu-title">
<div class="menu-name">{:lang_i18n('请择地区')}</div>
<div class="close-icon">
<img src="__IMAGES__/close.png">
</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 class="top-menu"> <div class="top-menu">
<div class="it-ct">
<div class="it-1"><a href="/">{:lang_i18n('首页')}</a></div>
</div>
<div class="it-ct">
<div class="it-1">
<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>
{notempty name="header_navigation"} {notempty name="header_navigation"}
{volist name="header_navigation" id="nav"} {volist name="header_navigation" id="nav"}
<div class="it-ct"> <div class="it-ct">
@@ -59,7 +67,13 @@
$(this).siblings('.it-1-2').slideToggle(800); $(this).siblings('.it-1-2').slideToggle(800);
$(this).find('.icon-arrow').addClass('rotate'); $(this).find('.icon-arrow').addClass('rotate');
}); });
// 顶部国家选择
$('.top-country-toggle').click(function(){
$(".mask,.action-sheet").show();
})
$('.top-country .close-icon').click(function(){
$(".mask,.action-sheet").hide();
})
// 移动端顶部宽度设置和主体内容宽度一致 // 移动端顶部宽度设置和主体内容宽度一致
var pageWidth = $('.oricoEGapp').outerWidth(); var pageWidth = $('.oricoEGapp').outerWidth();
// 设置.header-PC元素的宽度 // 设置.header-PC元素的宽度

View File

@@ -33,14 +33,17 @@
</div> </div>
</div> </div>
<!-- 文章内容 --> <!-- 文章内容 -->
<div id="rendered-content" class="nhlp-app-content"> <div class="ql-container">
<div id="rendered-content" class="nhlp-app-content ql-editor">
{$article.content|raw|default=''} {$article.content|raw|default=''}
</div> </div>
</div>
<!-- 搜索 --> <!-- 搜索 -->
<div class="nhlpapp-search"> <div class="nhlpapp-search">
<div class="nhlpappshtop"> <div class="nhlpappshtop">
<div class="nhlpapp-shdiv"> <div class="nhlpapp-shdiv">
<input class="nhlp-ipt" id="search-input" placeholder="{:lang_i18n('请输入搜索关键字,如安装赛博云空间,影视库')}" autocomplete="off"> <input class="nhlp-ipt" id="search-input" placeholder="{:lang_i18n('请输入搜索关键字,如安装赛博云空间,影视库')}"
autocomplete="off">
<img src="__IMAGES__/ssapp.png" class="searchimg"> <img src="__IMAGES__/ssapp.png" class="searchimg">
</div> </div>
<span class="closetx">{:lang_i18n('取消')}</span> <span class="closetx">{:lang_i18n('取消')}</span>
@@ -50,22 +53,44 @@
<div class="dropdown" id="dropdown"></div> <div class="dropdown" id="dropdown"></div>
</div> </div>
<!-- 分类文章目录 --> <!-- 分类文章目录 -->
<div class="nhlpapp-pagescate" {:style(['display' => $Request.get.view == 'more' ? 'block' : 'none'])}> <div class="nhlpapp-pagescate" {:style(['display'=> $Request.get.view == 'more' ? 'block' : 'none'])}>
<div class="nars-hlpdt-ml"> <div class="nars-hlpdt-ml">
{notempty name="article_categorys"} {notempty name="article_categorys"}
<div class="nav-tree"> <div class="nav-tree">
{volist name="article_categorys" id="ac" key="idx"} {volist name="article_categorys" id="ac" key="idx"}
<div class="categoryhelp"> <div class="categoryhelp">
<div class="categoryhelp-title"> <div class="categoryhelp-title">
<div> <div>
<img src="__IMAGES__/nars-jt.png" class="arrow {if condition='$ac.id == $Request.get.cid'}rotate{/if}"> <img src="__IMAGES__/nars-jt.png"
class="arrow {if condition='$ac.id == $Request.get.cid'}rotate{/if}">
</div> </div>
<span>{$ac.name}</span> <span>{$ac.name}</span>
</div> </div>
<ul class="sub-list" {if condition='$ac.id == $Request.get.cid'}style="display: block;"{/if}> <ul class="sub-list" {if condition='$ac.id == $Request.get.cid' }style="display: block;" {/if}>
{volist name="ac.child" id="ad"}
<li class="two-mues">
<a href="#" class="two-a">
<div><img src="__IMAGES__/nars-jt.png"
class="arrow {if condition='$ad.id == $Request.get.pid'}rotate{/if}">
</div>
<span>{$ad.name}</span>
</a>
<ul class="thress-mues" {if condition='$ad.id == $Request.get.pid' }style="display: block;" {/if}>
{volist name="ad.article" id="ae"}
<li>
<a href="{:url('/index/topic/nas/help_detail', ['cid' => $ac.id ,'pid' => $ad.id, 'id' => $ae.id])}"
style="margin-left:18%;padding: 0.4rem;">{$ae.title}</a>
</li>
{/volist}
</ul>
</li>
{/volist}
{volist name="ac.article" id="ar"} {volist name="ac.article" id="ar"}
<li> <li>
<a href="{:url('/index/topic/nas/help_detail', ['cid' => $ac.id , 'id' => $ar.id])}" style="padding-top: 6px;"> <a href="{:url('/index/topic/nas/help_detail', ['cid' => $ac.id , 'id' => $ar.id])}"
style="padding-top: 6px;">
{$ar.title} {$ar.title}
</a> </a>
</li> </li>
@@ -73,10 +98,34 @@
</ul> </ul>
</div> </div>
{/volist} {/volist}
</div> </div>
{/notempty} {/notempty}
</div> </div>
</div> </div>
<!-- 顶部国家选择-->
<div class="top-country">
<div class="mask"></div>
<div class="action-sheet">
<div class="menu-title">
<div class="menu-name">{:lang_i18n('请择地区')}</div>
<div class="close-icon">
<img src="__IMAGES__/close.png">
</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>
</div> </div>
{/block} {/block}
@@ -102,6 +151,17 @@
$(this).next('.sub-list').slideToggle(); $(this).next('.sub-list').slideToggle();
$(this).find('.arrow').toggleClass('rotate'); $(this).find('.arrow').toggleClass('rotate');
}); });
//分类二三级交互
$('.two-a').click(function(e) {
e.preventDefault();
e.stopPropagation(); // 阻止事件冒泡
// 切换当前二级菜单的箭头方向
$(this).find('.arrow').toggleClass('rotate');
// 切换对应的三级菜单显示/隐藏
$(this).siblings('.thress-mues').slideToggle();
});
// 点击顶部搜索图标-点击取消关闭 // 点击顶部搜索图标-点击取消关闭
$('#ssico').click(function () { $('#ssico').click(function () {
$('.nhlpapp-pagescate').hide(); $('.nhlpapp-pagescate').hide();
@@ -110,6 +170,13 @@
$('.closetx').click(function () { $('.closetx').click(function () {
$('.nhlpapp-search').hide(); $('.nhlpapp-search').hide();
}); });
// 顶部国家选择
$('.top-country-toggle').click(function () {
$(".mask,.action-sheet").show();
})
$('.top-country .close-icon').click(function () {
$(".mask,.action-sheet").hide();
})
// 搜索 // 搜索
var timeout = null; var timeout = null;
$('#search-input').on('focus input', function () { $('#search-input').on('focus input', function () {
@@ -142,6 +209,27 @@
}) })
}, 300); }, 300);
}); });
// 英文截断处理
// 目标容器:富文本内容所在的元素
const contentContainer = $('#rendered-content');
// 遍历所有包含文本内容的标签p、h1-h6、strong等
contentContainer.find('*').each(function () {
const $element = $(this);
const htmlContent = $element.html();
// 条件1排除内容仅为一个&nbsp;的标签(如<p>&nbsp;</p>
if (htmlContent.trim() === '&nbsp;') {
return; // 不处理,继续下一个元素
}
// 条件2检查是否包含&nbsp;且有实际文本内容
if (htmlContent.includes('&nbsp;')) {
// 将所有&nbsp;替换为普通空格(有效占位符,支持单词完整换行)
const newContent = htmlContent.replace(/&nbsp;/g, ' ');
$element.html(newContent);
}
});
}); });
</script> </script>
{/block} {/block}

View File

@@ -0,0 +1,765 @@
{extend name="public/base" /}
{block name="style"}
<!-- 将rem适配JS移到这里确保优先执行 -->
<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 / 7.5; // 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>
<link rel="stylesheet" href="__CSS__/topic_power_prodline/index.css">
<link rel="stylesheet" href="__CSS__/topic_power_prodline/swiper.css">
<link rel="stylesheet" href="__CSS__/topic_power_prodline/nav.css">
<link rel="stylesheet" href="__CSS__/topic_power_prodline/advantage.css">
<link rel="stylesheet" href="__CSS__/topic_power_prodline/moren.css">
<link rel="stylesheet" href="__CSS__/topic_power_prodline/mask.css">
<link rel="stylesheet" href="__CSS__/topic_power_prodline/product.css">
<link rel="stylesheet" href="__CSS__/topic_power_prodline/product_list.css">
<link rel="stylesheet" href="__CSS__/topic_power_prodline/product_card.css">
<link rel="stylesheet" href="__CSS__/topic_power_prodline/footer.css">
{/block}
{block name="header"}
<!-- 重置header头为空 -->
{/block}
{block name="main"}
<a class="header" href="/">
<div class="header-img">
<img src="__IMAGES__/logo.png" alt="">
</div>
</a>
<!-- 轮播核心容器 -->
<div class="swiper-container auto-swiper-container" >
{notempty name="data.focus_image"}
<div class="swiper-wrapper">
{volist name="data.focus_image" id="fo"}
<a class="swiper-slide auto-swiper-slide" href="{$fo.link}">
<img src="{$fo.image}" alt="{$fo.title}" />
</a>
{/volist}
</div>
<div class="swiper-pagination"></div>
{/notempty}
</div>
<!-- 分类 -->
{notempty name="data.category"}
<div class="nav-box">
{volist name="data.category" id="ca"}
<a class="nav-item" href="{$ca.link}">
<img src="{$ca.image}" alt="{$ca.title}">
<p {:style(['color'=>$ca.title_txt_color])}>{$ca.title}</p>
</a>
{/volist}
</div>
{/notempty}
<!-- 500万 -->
{notempty name="data.why_choose"}
<div class="advantage-section">
{assign name="why_choose_title" value=":array_shift($data.why_choose)" /}
<h2 class="advantage-section__title">{$why_choose_title.title|default=''|raw}</h2>
<div class="advantage-section__list">
{volist name="data.why_choose" id="ch"}
<div class="advantage-card-wrap">
<div class="advantage-card" data-target="design">
<img src="{$ch.image}" alt="{$ch.title}:{$ch.short_title}" class="advantage-card__img">
<div class="advantage-card__content">
<!-- 标题+箭头容器:水平+垂直双居中,内部文字左、箭头右 -->
<div class="advantage-card__heading-wrap">
<div class="advantage-card__heading" {:style(['color'=>$ch.title_txt_color])}>{$ch.title}</div>
<img src="__IMAGES__/jiant.png" alt="" class="card-arrow">
</div>
<div class="advantage-card__description" {:style(['color'=>$ch.short_title_txt_color])}>{$ch.short_title}</div>
<div style="display:none;" class="mack-conten-text">{$ch.desc|raw}</div>
</div>
</div>
</div>
{/volist}
</div>
</div>
{/notempty}
<!-- 产品差旅充 -->
{notempty name="data.travel_charger"}
<div class="product-box">
{assign name="tc_title" value=":array_shift($data.travel_charger)" /}
<div class="product-title">
<h2 class="product-title-h2">{$tc_title.title|default=''}</h2>
<p class="product-title-p">{$tc_title.short_title|default=''}</p>
</div>
<div class="product-container" >
{assign name="tc_first_section_lf" value=":array_shift($data.travel_charger)" /}
{notempty name="tc_first_section_lf"}
<a class="product-left" href="{$tc_first_section_lf.link}">
<img src="{$tc_first_section_lf.image}" alt="{$tc_first_section_lf.title}" class="product-img">
<!-- 公共类+定位类:尺寸统一,定位不同 -->
<div class="product-img-hover top55">
<img src="{$tc_first_section_lf.extra_image}" alt="{$tc_first_section_lf.short_title}" class="img1">
</div>
</a>
{/notempty}
{assign name="tc_first_section_lr" value=":array_shift($data.travel_charger)" /}
{notempty name="tc_first_section_lr"}
<div class="product-right">
<img src="{$tc_first_section_lr.image}" alt="{$tc_first_section_lr.title}" class="right-content right-img">
<video src="{$tc_first_section_lr.video}" class="right-content right-video" muted loop playsinline>
您的浏览器不支持HTML5视频播放请升级浏览器
</video>
<button class="video-play-btn">
<span class="play-icon">
<svg width="40" height="40" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="10" fill="rgba(0,0,0,0.5)" />
<path d="M9 7L16 12L9 17V7Z" fill="white" />
</svg>
</span>
<!-- 暂停图标(默认隐藏) -->
<span class="pause-icon">
<svg width="40" height="40" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="10" fill="rgba(0,0,0,0.5)" />
<rect x="8" y="7" width="3" height="10" fill="white" />
<rect x="13" y="7" width="3" height="10" fill="white" />
</svg>
</span>
</button>
</div>
{/notempty}
</div>
{assign name="tc_second_section" value=":array_splice($data.travel_charger, 0, 4)" /}
{notempty name="tc_second_section"}
<div class="product-card-box">
<div class="product-card-container">
{volist name="tc_second_section" id="tss"}
<a class="product-card-wrap" href="{$tss.link}">
<div class="product-card" >
<div class="product-card-img">
<img src="{$tss.image}" alt="{$tss.title}">
</div>
<div class="product-card-text">
<div class="product-card-title" {:style(['color'=>$tss.title_txt_color])}>{$tss.title}</div>
<div class="product-card-desc" {:style(['color'=>$tss.short_title_txt_color])}>{$tss.short_title}</div>
</div>
<div class="product-card-link">
<img src="__IMAGES__/ljgd.png" alt="查看更多">
</div>
</div>
</a>
{/volist}
</div>
</div>
{assign name="tc_three_section" value=":array_shift($data.travel_charger)" /}
{notempty name="tc_three_section"}
<a href="{$tc_three_section.link}" class="more">
<div class="more-img">
{$tc_three_section.title}
</div>
</a>
{/notempty}
{/notempty}
</div>
{/notempty}
<!-- 产品 家居充-->
{notempty name="data.home_charger"}
<div class="product-box">
{assign name="hc_title" value=":array_shift($data.home_charger)" /}
<div class="product-title">
<h2 class="product-title-h2">{$hc_title.title|default=''}</h2>
<p class="product-title-p">{$hc_title.short_title|default=''}</p>
</div>
<div class="product-container">
{assign name="hc_first_section_lf" value=":array_shift($data.home_charger)" /}
{notempty name="hc_first_section_lf"}
<a class="product-left" href="{$hc_first_section_lf.link}">
<img src="{$hc_first_section_lf.image}" alt="{$hc_first_section_lf.title}" class="product-img">
<!-- 公共类+定位类:尺寸统一,定位不同 -->
<div class="product-img-hover">
<img src="{$hc_first_section_lf.extra_image}" alt="{$hc_first_section_lf.short_title}" class="img2">
</div>
</a>
{/notempty}
{assign name="hc_first_section_lr" value=":array_shift($data.home_charger)" /}
{notempty name="hc_first_section_lr"}
<div class="product-right">
<img src="{$hc_first_section_lr.image}" alt="{$hc_first_section_lr.title}" class="right-content right-img">
<video src="{$hc_first_section_lr.video}" class="right-content right-video" muted loop playsinline >
您的浏览器不支持HTML5视频播放请升级浏览器
</video>
<button class="video-play-btn">
<span class="play-icon">
<svg width="40" height="40" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="10" fill="rgba(0,0,0,0.5)" />
<path d="M9 7L16 12L9 17V7Z" fill="white" />
</svg>
</span>
<!-- 暂停图标(默认隐藏) -->
<span class="pause-icon">
<svg width="40" height="40" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="10" fill="rgba(0,0,0,0.5)" />
<rect x="8" y="7" width="3" height="10" fill="white" />
<rect x="13" y="7" width="3" height="10" fill="white" />
</svg>
</span>
</button>
</div>
{/notempty}
</div>
{assign name="hc_second_section" value=":array_splice($data.home_charger, 0, 4)" /}
{notempty name="hc_second_section"}
<div class="product-card-box">
<div class="product-card-container">
{volist name="hc_second_section" id="hcs"}
<a class="product-card-wrap" href="{$hcs.link}">
<div class="product-card" href="#">
<div class="product-card-img">
<img src="{$hcs.image}" alt="{$hcs.short_title}">
</div>
<div class="product-card-text">
<div class="product-card-title">{$hcs.title}</div>
<div class="product-card-desc">{$hcs.short_title}</div>
</div>
<div class="product-card-link">
<img src="__IMAGES__/ljgd.png" alt="查看更多">
</div>
</div>
</a>
{/volist}
</div>
</div>
{/notempty}
{assign name="hc_three_section" value=":array_shift($data.home_charger)" /}
{notempty name="hc_three_section"}
<a href="{$hc_three_section.link}" class="more">
<div class="more-img">
{$hc_three_section.title}
</div>
</a>
{/notempty}
</div>
{/notempty}
<!-- 产品 桌面充(悬浮图上右超出)底部列表样式不一样(左文右图) -->
<div class="product-box">
{assign name="dc_title" value=":array_shift($data.desktop_charger)" /}
<div class="product-title">
<h2 class="product-title-h2">{$dc_title.title|default=''}</h2>
<p class="product-title-p">{$dc_title.short_title|default=''}</p>
</div>
<div class="product-container">
{assign name="dc_first_section_lf" value=":array_shift($data.desktop_charger)" /}
{notempty name="dc_first_section_lf"}
<a class="product-left" href="{$dc_first_section_lf.link}">
<img src="{$dc_first_section_lf.image}" alt="{$dc_first_section_lf.short_title}" class="product-img">
<!-- 公共类+定位类:尺寸和第一个完全一致,仅定位不同 -->
<div class="product-img-hover right" >
<img src="{$dc_first_section_lf.extra_image}" alt="{$dc_first_section_lf.short_title}" class="img3">
</div>
</a>
{/notempty}
{assign name="dc_first_section_lr" value=":array_shift($data.desktop_charger)" /}
{notempty name="dc_first_section_lr"}
<div class="product-right">
<img src="{$dc_first_section_lr.image}"
alt="使用场景" class="right-content right-img">
<!--muted loop playsinline controls-->
<video
src="{$dc_first_section_lr.video}"
class="right-content right-video" muted loop playsinline >
您的浏览器不支持HTML5视频播放请升级浏览器
</video>
<button class="video-play-btn">
<span class="play-icon">
<svg width="40" height="40" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="10" fill="rgba(0,0,0,0.5)" />
<path d="M9 7L16 12L9 17V7Z" fill="white" />
</svg>
</span>
<!-- 暂停图标(默认隐藏) -->
<span class="pause-icon">
<svg width="40" height="40" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="10" fill="rgba(0,0,0,0.5)" />
<rect x="8" y="7" width="3" height="10" fill="white" />
<rect x="13" y="7" width="3" height="10" fill="white" />
</svg>
</span>
</button>
</div>
{/notempty}
</div>
{assign name="dc_second_section" value=":array_splice($data.desktop_charger, 0, 2)" /}
{notempty name="dc_second_section"}
<div class="product-card-box">
<div class="product-card-container">
{volist name="dc_second_section" id="dcs"}
<a class="product-card-wrap" href="{$dcs.link}">
<div class="product-card" href="#">
<div class="product-card-img">
<img src="{$dcs.image}" alt="{$dcs.short_title}">
</div>
<div class="product-card-text">
<div class="product-card-title">{$dcs.title}</div>
<div class="product-card-desc">{$dcs.short_title}</div>
</div>
<div class="product-card-link">
<img src="__IMAGES__/ljgd.png" alt="查看更多">
</div>
</div>
</a>
{/volist}
</div>
</div>
{/notempty}
{assign name="dc_three_section" value=":array_shift($data.desktop_charger)" /}
{notempty name="dc_three_section"}
<a href="{$dc_three_section.link}" class="more">
<div class="more-img">
{$dc_three_section.title}
</div>
</a>
{/notempty}
</div>
<!-- 墙插 -->
{notempty name="data.wall_charger"}
<div class="product-box">
{assign name="wc_title" value=":array_shift($data.wall_charger)" /}
<div class="product-title">
<h2 class="product-title-h2">{$wc_title.title|default=''}</h2>
<p class="product-title-p">{$wc_title.short_title|default=''}</p>
</div>
<div class="product-container">
{assign name="wc_first_section_lf" value=":array_shift($data.wall_charger)" /}
{notempty name="wc_first_section_lf"}
<a class="product-left" href="{$wc_first_section_lf.link}">
<img src="{$wc_first_section_lf.image}" alt="{$wc_first_section_lf.title}" class="product-img">
<!-- 公共类+定位类:尺寸统一,定位不同 -->
<div class="product-img-hover top70">
<img src="{$wc_first_section_lf.extra_image}" alt="{$wc_first_section_lf.title}" class="img4">
</div>
</a>
{/notempty}
{assign name="wc_first_section_lr" value=":array_shift($data.wall_charger)" /}
{notempty name="wc_first_section_lr"}
<div class="product-right">
<img src="{$wc_first_section_lr.image}" alt="{$wc_first_section_lr.title}" class="right-content right-img">
<video src="{$wc_first_section_lr.video}" class="right-content right-video" muted loop playsinline>
您的浏览器不支持HTML5视频播放请升级浏览器
</video>
<button class="video-play-btn">
<span class="play-icon">
<svg width="40" height="40" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="10" fill="rgba(0,0,0,0.5)" />
<path d="M9 7L16 12L9 17V7Z" fill="white" />
</svg>
</span>
<!-- 暂停图标(默认隐藏) -->
<span class="pause-icon">
<svg width="40" height="40" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="10" fill="rgba(0,0,0,0.5)" />
<rect x="8" y="7" width="3" height="10" fill="white" />
<rect x="13" y="7" width="3" height="10" fill="white" />
</svg>
</span>
</button>
</div>
{/notempty}
</div>
{assign name="wc_more_section" value=":array_shift($data.wall_charger)" /}
{notempty name="wc_more_section"}
<a href="{$wc_more_section.link}" class="more">
<div class="more-img">
{$wc_more_section.title}
</div>
</a>
{/notempty}
</div>
{/notempty}
{notempty name="data.converter"}
<!-- 转换器 -->
<div class="product-box">
{assign name="ct_title" value=":array_shift($data.converter)" /}
<div class="product-title">
<h2 class="product-title-h2">{$ct_title.title|default=''}</h2>
<p class="product-title-p">{$ct_title.short_title|default=''}</p>
</div>
{assign name="ct_more_section" value=":array_pop($data.converter)" /}
{assign name="ct_chunk_section" value=":array_chunk($data.converter, 2)" /}
{assign name="ct_chunk_section_len" value=":count($ct_chunk_section)" /}
{volist name="ct_chunk_section" id="cts" key="k"}
<div class="product-container">
{assign name="cts_lf" value=":array_shift($cts)" /}
{notempty name="cts_lf"}
<a class="product-left" href="{$cts_lf.link}">
<img src="{$cts_lf.image}" alt="{$cts_lf.title}" class="product-img">
<!-- 公共类+定位类:尺寸统一,定位不同 -->
<!--style="display:flex;justify-content: center;"-->
<div class="product-img-hover top40" >
<!-- style="width:70%"-->
<img src="{$cts_lf.extra_image}" alt="{$cts_lf.title}" class="img5">
</div>
</a>
{/notempty}
{assign name="cts_lr" value=":array_shift($cts)" /}
{notempty name="cts_lr"}
<div class="product-right">
<img src="{$cts_lr.image}" alt="{$cts_lr.title}" class="right-content right-img">
<video src="{$cts_lr.video}" class="right-content right-video" muted loop playsinline>
您的浏览器不支持HTML5视频播放请升级浏览器
</video>
<!-- 播放图标 -->
<button class="video-play-btn">
<span class="play-icon">
<svg width="40" height="40" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="10" fill="rgba(0,0,0,0.5)" />
<path d="M9 7L16 12L9 17V7Z" fill="white" />
</svg>
</span>
<!-- 暂停图标(默认隐藏) -->
<span class="pause-icon">
<svg width="40" height="40" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="10" fill="rgba(0,0,0,0.5)" />
<rect x="8" y="7" width="3" height="10" fill="white" />
<rect x="13" y="7" width="3" height="10" fill="white" />
</svg>
</span>
</button>
</div>
{/notempty}
</div>
{neq name="k" value="$ct_chunk_section_len"}
<div class="line"></div>
{/neq}
{/volist}
{notempty name="ct_more_section"}
<a href="{$ct_more_section.link}" class="more">
<div class="more-img">
{$ct_more_section.title}
</div>
</a>
{/notempty}
</div>
{/notempty}
{notempty name="data.footer_info"}
<!-- 底部 -->
<div class="prodline-footer-box">
<div class="prodline-footer-box-img">
<img src="{$data.footer_info.0.image}" alt="">
</div>
</div>
{/notempty}
<!-- 蒙版 -->
<div class="mask" id="mask">
<div class="mask-content" >
<span class="close-btn">&times;</span>
<div class="mask-scroll-content"></div>
</div>
</div>
{/block}
{block name="script"}
<script type="text/javascript">
let swiper=null;
const advantageItems = document.querySelectorAll('.advantage-card');
let scrollTop = 0; // 保存页面滚动位置
let closeBtnHtml = null; // 关闭按钮元素(全局变量,避免重复创建)
const mask = document.getElementById('mask');
const maskContent = document.querySelector('.mask-content');
const maskScrollContent = document.querySelector('.mask-scroll-content'); // 滚动容器(关键!)
const closeBtn = document.querySelector('.close-btn')
// 初始化:确保 maskScrollContent 存在于 maskContent 中(避免被清空)
if (!maskScrollContent) {
// 如果页面没有 mask-scroll-content动态创建确保结构稳定
const scrollContent = document.createElement('div');
scrollContent.className = 'mask-scroll-content';
maskContent.appendChild(scrollContent);
}
function createCloseBtn() {
if (closeBtnHtml) {
closeBtnHtml.remove();
}
closeBtnHtml = document.createElement('span');
closeBtnHtml.className = 'close-btn';
closeBtnHtml.innerHTML = '&times;';
closeBtnHtml.addEventListener('click', hideMask);
// 挂载到 maskContent而非 scrollContent避免被滚动影响位置
maskContent.prepend(closeBtnHtml);
}
function showMask(contentHtml) {
// 保存页面滚动位置
scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
// 关键:将内容填充到 scrollContent 中(而非直接替换 maskContent
maskScrollContent.innerHTML = contentHtml;
createCloseBtn();
// 显示蒙版
mask.style.display = 'flex';
// 禁止滚动(复用你的逻辑)
document.documentElement.classList.add('no-scroll');
document.body.classList.add('no-scroll');
document.body.style.top = `-${scrollTop}px`;
// 额外:打开蒙版时就重置滚动位置(避免残留上次滚动状态)
maskScrollContent.scrollTop = 0;
}
function hideMask() {
// 关键步骤 1先重置 scrollContent 的滚动位置(此时元素还未被销毁)
maskScrollContent.scrollTop = 0;
// 关键步骤 2清空 scrollContent 的内容(而非 maskContent
maskScrollContent.innerHTML = "";
// 隐藏蒙版
mask.style.display = 'none';
// 恢复滚动(复用你的逻辑)
document.documentElement.classList.remove('no-scroll');
document.body.classList.remove('no-scroll');
document.body.style.top = '';
// 还原页面滚动位置
window.scrollTo(0, scrollTop);
// 移除关闭按钮(可选,避免残留)
if (closeBtnHtml) {
closeBtnHtml.remove();
closeBtnHtml = null;
}
}
// 点击卡片显示详情
advantageItems.forEach((item) => {
item.addEventListener('click', (e) => {
// 获取当前点击卡片内的.mack-conten-text元素
const currentMackContent = e.currentTarget.querySelector('.mack-conten-text');
if (currentMackContent) {
// 关键修改获取该元素的子内容innerHTML 本身就是内部HTML不含当前元素标签
// 若想更彻底,可遍历子节点拼接内容(兼容特殊场景)
let contentHtml = '';
Array.from(currentMackContent.childNodes).forEach(child => {
// 只保留元素节点和文本节点(过滤空节点)
if (child.nodeType === 1 || child.nodeType === 3) {
contentHtml += child.outerHTML || child.textContent;
}
});
// 显示蒙版并传入纯净的子内容
showMask(contentHtml);
}
});
});
// 关闭按钮事件
closeBtn.addEventListener('click', hideMask);
// 点击蒙版背景关闭(可选)
mask.addEventListener('click', (e) => {
if (e.target === mask) hideMask();
});
// ESC 键关闭(可选)
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && mask.style.display === 'flex') hideMask();
});
// 关键:拦截蒙版的 touchmove 事件,阻止滚动穿透(移动端核心)
mask.addEventListener(
'touchmove',
(e) => {
// 只有点击蒙版背景(不是内容区域)才阻止滚动
if (e.target === mask) {
e.preventDefault(); // 阻止默认触摸滚动行为
e.stopPropagation(); // 阻止事件冒泡
}
},
{ passive: false }
); // passive: false 必须,否则 preventDefault 无效
document.addEventListener('DOMContentLoaded', () => {
// 初始化所有视频容器
function initVideoContainers() {
const productRights = document.querySelectorAll('.product-right');
// 支持的视频格式
const supportedVideoFormats = ['.mp4', '.webm', '.ogg', '.mov', '.avi', '.mkv', '.flv', '.wmv'];
productRights.forEach((container, index) => {
const video = container.querySelector('.right-video');
const btn = container.querySelector('.video-play-btn');
const img = container.querySelector('.right-img');
if (!video || !btn || !img) return;
const videoSrc = video.src.trim()
console.log(videoSrc,'=videoSrc=')
// 修复:正确检测有效视频地址
// 排除空字符串、null、undefined
const hasValidVideo = !!videoSrc && videoSrc.trim() !== '' && videoSrc !== 'undefined' && videoSrc !== 'null';
// 验证视频格式是否有效
const isValidFormat = supportedVideoFormats.some(format =>
videoSrc.toLowerCase().endsWith(format) ||
(videoSrc.includes('?') && videoSrc.toLowerCase().split('?')[0].endsWith(format))
);
// 初始化状态:无视频或格式无效则保持图片显示,永不切换
if (!hasValidVideo || !isValidFormat) {
img.style.display = 'block';
video.style.display = 'none';
btn.style.display = 'none';
// 绑定空方法,防止调用报错
video.switchMedia = function() {};
console.log(`容器${index}:无有效视频(src="${videoSrc}"),保持图片显示`);
return;
}
// 有有效视频的情况
console.log(`容器${index}:有有效视频(src="${videoSrc}"),初始化播放逻辑`);
// 初始状态
video.style.display = 'none';
video.pause();
img.style.display = 'block';
btn.style.display = 'none';
btn.style.opacity = '0';
// 同步状态函数
function syncMediaState() {
if (img.style.display === 'block') {
btn.style.display = 'none';
btn.style.opacity = '0';
} else {
btn.style.display = 'block';
btn.style.opacity = '1';
btn.classList.toggle('paused', !video.paused && !video.ended);
}
}
// 按钮点击事件
btn.addEventListener('click', () => {
if (video.paused) {
video.play().catch(() => syncMediaState());
} else {
video.pause();
}
syncMediaState();
});
// 视频事件监听
['play', 'pause', 'ended', 'playing', 'waiting'].forEach(event => {
video.addEventListener(event, syncMediaState);
});
// 滚动切换函数
video.switchMedia = function(showVideo) {
// 只处理有有效视频的情况
if (showVideo) {
img.style.display = 'none';
video.style.display = 'block';
video.play().catch(() => {
console.log(`容器${index}:自动播放失败,需要用户交互`);
syncMediaState();
});
} else {
video.pause();
img.style.display = 'block';
video.style.display = 'none';
}
syncMediaState();
};
// 初始同步
syncMediaState();
});
}
// 滚动监听 - 优化版
function setupScrollWatcher() {
let ticking = false;
function updateVideoVisibility() {
const productRights = document.querySelectorAll('.product-right');
productRights.forEach(container => {
const video = container.querySelector('.right-video');
if (!video || !video.switchMedia) return;
// 检查是否在视口中
const rect = container.getBoundingClientRect();
const viewHeight = window.innerHeight || document.documentElement.clientHeight;
const isInView = rect.top < viewHeight * 0.7 && rect.bottom > viewHeight * 0.3;
// 只对有有效视频的元素调用switchMedia
video.switchMedia(isInView);
});
ticking = false;
}
// 使用requestAnimationFrame优化性能
window.addEventListener('scroll', () => {
if (!ticking) {
requestAnimationFrame(updateVideoVisibility);
ticking = true;
}
});
}
// 初始化
initVideoContainers();
setupScrollWatcher();
// 初始检查一次
setTimeout(() => {
const event = new Event('scroll');
window.dispatchEvent(event);
}, 300);
});
window.onload = function () {
if (typeof Swiper === 'undefined') {
console.error('Swiper加载失败请刷新页面重试');
return;
}
swiper = new Swiper('.auto-swiper-container', {
autoplay: {
delay: 3000, // 3秒切换
disableOnInteraction: false,
},
loop: false,
slidesPerView: 1,
spaceBetween: 0,
// 启用分页指示标(核心配置)
pagination: {
el: '.swiper-pagination', // 对应 HTML 中的指示标容器
clickable: true, // 允许点击指示标切换
// dynamicBullets: true, // 动态指示标(当前激活放大)
//dynamicMainBullets: 3, // 动态模式显示3个核心指示标
},
navigation: false,
scrollbar: false,
on: {
resize: function () {
this.update();
},
},
});
window.addEventListener('resize', function () {
swiper.update();
});
// 初始化时触发滚动事件,确保状态正确
window.dispatchEvent(new Event('scroll'));
};
</script>
{/block}

View File

@@ -5,7 +5,7 @@
{block name="main"} {block name="main"}
<div class="orico_Page_achievement"> <div class="orico_Page_achievement">
<div class="achievementMain"> <div class="achievementMain">
<img src="__IMAGES__/Achievement.png" class="acvImg" /> <img src="__IMAGES__/Achievement.webp" class="acvImg" />
<div class="achInfo"> <div class="achInfo">
<div class="title">{:lang_i18n('ORICO荣耀')}</div> <div class="title">{:lang_i18n('ORICO荣耀')}</div>
{notempty name="achievement"} {notempty name="achievement"}

View File

@@ -6,7 +6,7 @@
<div class="orico_Page_brand"> <div class="orico_Page_brand">
<!--内容 --> <!--内容 -->
<div class="brandMain"> <div class="brandMain">
<img src="__IMAGES__/OurBrand.png" class="img-responsive" /> <img src="__IMAGES__/OurBrand.webp" class="img-responsive" />
{notempty name="banners"} {notempty name="banners"}
<div class="our_brand_con"> <div class="our_brand_con">
{volist name="banners" id="ba" offset="0" length="2"} {volist name="banners" id="ba" offset="0" length="2"}

View File

@@ -24,7 +24,9 @@
<p>{$detail.release_time|date_format_i18n}</p> <p>{$detail.release_time|date_format_i18n}</p>
</div> </div>
<!-- 文本渲染--> <!-- 文本渲染-->
<div class="blog_content">{$detail.content|raw}</div> <div class="ql-container">
<div class="blog_content ql-editor">{$detail.content|raw}</div>
</div>
</div> </div>
<!-- 评论只显示前面五条---> <!-- 评论只显示前面五条--->
{notempty name="comments"} {notempty name="comments"}

View File

@@ -7,14 +7,14 @@
<!-- 内容 --> <!-- 内容 -->
<div class="downloadMain"> <div class="downloadMain">
<div class="topimg"> <div class="topimg">
<img src="__IMAGES__/banner_01.png" /> <img src="__IMAGES__/banner_01.webp" />
<div class="banner_title">{:lang_i18n('软件和驱动程序')}</div> <div class="banner_title">{:lang_i18n('软件和驱动程序')}</div>
</div> </div>
<div class="contact_c"> <div class="contact_c">
<!-- 搜索 --> <!-- 搜索 -->
<form action="{:url('attachment/index')}" method="get"> <form action="{:url('attachment/index')}" method="get">
<div class="search_all"> <div class="search_all">
<input type="hidden" name="id" value="{$Request.get.id}" /> <input type="hidden" name="id" value="{$Request.get.id??$categorys[0]['id']??''}" />
<input type="text" name="keyword" placeholder="{:lang_i18n('搜索')}" /> <input type="text" name="keyword" placeholder="{:lang_i18n('搜索')}" />
<button class="searchbtn" type="submit"><img src="__IMAGES__/search_blue.png" /></button> <button class="searchbtn" type="submit"><img src="__IMAGES__/search_blue.png" /></button>
</div> </div>

View File

@@ -7,14 +7,14 @@
<!-- 内容 --> <!-- 内容 -->
<div class="downloadMain"> <div class="downloadMain">
<div class="topimg"> <div class="topimg">
<img src="__IMAGES__/banner_01.png" /> <img src="__IMAGES__/banner_01.webp" />
<div class="banner_title">{:lang_i18n('软件和驱动程序')}</div> <div class="banner_title">{:lang_i18n('软件和驱动程序')}</div>
</div> </div>
<div class="contact_c"> <div class="contact_c">
<!-- 搜索 --> <!-- 搜索 -->
<form action="{:url('attachment/video')}" method="get"> <form action="{:url('attachment/video')}" method="get">
<div class="search_all"> <div class="search_all">
<input type="hidden" name="id" value="{$Request.get.id}" /> <input type="hidden" name="id" value="{$Request.get.id??$video_categorys[0]['id']??''}" />
<input type="text" name="keyword" placeholder="{:lang_i18n('搜索')}" /> <input type="text" name="keyword" placeholder="{:lang_i18n('搜索')}" />
<button class="searchbtn" type="submit"><img src="__IMAGES__/search_blue.png" /></button> <button class="searchbtn" type="submit"><img src="__IMAGES__/search_blue.png" /></button>
</div> </div>

View File

@@ -112,13 +112,13 @@
<div class="sceneIntroduction"> <div class="sceneIntroduction">
{volist name="scenes" id="scene"} {volist name="scenes" id="scene"}
<div class="sceneitem"> <div class="sceneitem">
<div class="sceneInfo"> <a class="sceneInfo" href="{$scene.link}">
<p class="scenetitle" {notempty name="scene.title_txt_color" }style="color:{$scene.title_txt_color};" {/notempty}>{$scene.title}</p> <!-- <p class="scenetitle" {notempty name="scene.title_txt_color" }style="color:{$scene.title_txt_color};" {/notempty}>{$scene.title}</p>
<p class="subtitle" {notempty name="scene.desc_txt_color" }style="color:{$scene.desc_txt_color};" {/notempty}> <p class="subtitle" {notempty name="scene.desc_txt_color" }style="color:{$scene.desc_txt_color};" {/notempty}>
{$scene.desc|raw}</p> {$scene.desc|raw}</p>
<a class="sceneMore" href="{$scene.link}">{:lang_i18n('了解更多')} ></a> <a class="sceneMore" href="{$scene.link}">{:lang_i18n('了解更多')} ></a> -->
</div>
<div style="background-image: url('{$scene.image}');" class="sceneimg"></div> <div style="background-image: url('{$scene.image}');" class="sceneimg"></div>
</a>
</div> </div>
{/volist} {/volist}
</div> </div>
@@ -129,10 +129,10 @@
<span class="otsbtitle">{:lang_i18n('强大功能、简单使用')}</span> <span class="otsbtitle">{:lang_i18n('强大功能、简单使用')}</span>
<div class="beforeafter ba-slider"> <div class="beforeafter ba-slider">
<!-- 对比前的图片 --> <!-- 对比前的图片 -->
<img src="__IMAGES__/indeximg1.jpg"> <img src="__IMAGES__/indeximg1.webp">
<div class="resize"> <div class="resize">
<!-- 对比后的图片 --> <!-- 对比后的图片 -->
<img src="__IMAGES__/indeximg2.jpg"> <img src="__IMAGES__/indeximg2.webp">
</div> </div>
<!-- 可拖动的分隔条 --> <!-- 可拖动的分隔条 -->
<span class="handle"></span> <span class="handle"></span>

View File

@@ -22,7 +22,11 @@
<a class="pathname" href="/">{:lang_i18n('首页')}</a> <a class="pathname" href="/">{:lang_i18n('首页')}</a>
{volist name="product_categorys" id="ca"} {volist name="product_categorys" id="ca"}
<div class="arrow"></div> <div class="arrow"></div>
{eq name="ca.pid" value="0"}
<a class="pathname" href="{:url('product/category', ['id' => $ca.id])}">{$ca.name}</a> <a class="pathname" href="{:url('product/category', ['id' => $ca.id])}">{$ca.name}</a>
{else /}
<a class="pathname" href="{:url('product/subcategory', ['id' => $ca.id])}">{$ca.name}</a>
{/eq}
{/volist} {/volist}
</div> </div>
<!-- 产品主图切换和参数详情--> <!-- 产品主图切换和参数详情-->
@@ -35,33 +39,26 @@
<!-- 左边切换按钮 --> <!-- 左边切换按钮 -->
<div class="scrollbutton smallImgUp disabled"></div> <div class="scrollbutton smallImgUp disabled"></div>
<!-- 小图片预览 --> <!-- 小图片预览 -->
<div id="imageMenu"> <div id="imageMenu_{$sku.id}" class="imageMenu">
<ul class="image_list"> <ul class="image_list">
{volist name="sku.photo_album" id="photo"}
<li class="onlickImg"><img src="{:thumb($photo)}" data-url="{$photo}" /></li>
{/volist}
{if condition="!empty($product.video_img) && !empty($product.video_url) && $idx == 1"} {if condition="!empty($product.video_img) && !empty($product.video_url) && $idx == 1"}
<!-- 产品视频 --> <!-- 产品视频 -->
<li id="onlickImg"><img src="{:thumb($product.video_img)}" data-url="{$product.video_url}" /></li> <li class="onlickImg"><img src="{:thumb($product.video_img)}" data-url="{$product.video_url}" /></li>
{/if} {/if}
{volist name="sku.photo_album" id="photo"}
<li id="onlickImg"><img src="{:thumb($photo)}" data-url="{$photo}" /></li>
{/volist}
</ul> </ul>
</div> </div>
<!-- 右边切换按钮 --> <!-- 右边切换按钮 -->
<div class="scrollbutton smallImgDown"></div> <div class="scrollbutton smallImgDown"></div>
</div> </div>
<!-- 产品大图 --> <!-- 产品大图 -->
<div class="bigImg" id="vertical"> <div class="bigImg" id="vertical" style="width: 75%;">
<!-- 主图 --> <!-- 主图 -->
{if condition="!empty($product.video_img) && !empty($product.video_url) && $idx == 1"}
<!-- 如果有视频情况下默认先显示视频 -->
<video poster="{$product.video_img}" autoplay="autoplay" muted="muted" loop="loop" id="video" controls style="width: 510px;height: 510px; position: relative;z-index: 998;">
<source src="{$product.video_url}" type="video/mp4"/>
</video>
{else/}
{notempty name="sku.photo_album[0]"} {notempty name="sku.photo_album[0]"}
<img src="{$sku.photo_album[0]}" id="midimg" /> <img src="{$sku.photo_album[0]}" id="midimg" />
{/notempty} {/notempty}
{/if}
</div> </div>
</div> </div>
{/volist} {/volist}
@@ -73,6 +70,11 @@
<p>{$product.short_name|default=''}</p> <p>{$product.short_name|default=''}</p>
<div class="proTfg"> <div class="proTfg">
<ul class="swt-Table"> <ul class="swt-Table">
<li class="Table-Row">
<div class="ms3 Table-Cell">{:lang_i18n('型号')}</div>
<div class="ms2 Table-Cell"></div>
<div class="ms4 Table-Cell">{$product.spu}</div>
</li>
{volist name="product_params" id="pp"} {volist name="product_params" id="pp"}
<li class="Table-Row"> <li class="Table-Row">
<div class="ms3 Table-Cell">{$pp.name}</div> <div class="ms3 Table-Cell">{$pp.name}</div>
@@ -114,6 +116,7 @@
{volist name="product_purchase_links" id="ppp" key="k"} {volist name="product_purchase_links" id="ppp" key="k"}
<a class="thebt bttype{$k}" href="{$ppp.link}">{$ppp.platform_name}</a> <a class="thebt bttype{$k}" href="{$ppp.link}">{$ppp.platform_name}</a>
{/volist} {/volist}
<br/>
{eq name=":cookie('think_lang')" value="en-us"} {eq name=":cookie('think_lang')" value="en-us"}
<a class="thebt bttype3" id="open_form_modal">{:lang_i18n('发送查询')}</a> <a class="thebt bttype3" id="open_form_modal">{:lang_i18n('发送查询')}</a>
{/eq} {/eq}
@@ -130,10 +133,12 @@
{/notempty} {/notempty}
</div> </div>
<!-- 富文本渲染--> <!-- 富文本渲染-->
<div class="products_des" id="detail"> <div class="ql-container">
<div class="products_des ql-editor" id="detail">
{$product.detail|default=''|raw} {$product.detail|default=''|raw}
</div> </div>
</div> </div>
</div>
<!-- 关联产品 --> <!-- 关联产品 -->
{notempty name="product_related"} {notempty name="product_related"}
<div class="glcpmain" id="related"> <div class="glcpmain" id="related">
@@ -225,7 +230,7 @@
<textarea name="message" id="message"></textarea> <textarea name="message" id="message"></textarea>
</div> </div>
</div> </div>
<button type="button" id="send" class="submit-btn">{:lang_i18n('提交')}</button> <button type="submit" class="submit-btn">{:lang_i18n('提交')}</button>
</form> </form>
</div> </div>
</div> </div>
@@ -273,21 +278,12 @@
// 处理表单提交 // 处理表单提交
modal.find("form").submit(function(e) { modal.find("form").submit(function(e) {
e.preventDefault(); e.preventDefault();
var form = $(this)
var formData = $(this).serialize(); var formData = $(this).serialize();
// 这里可以添加代码将formData发送到服务器
// 例如通过AJAX
console.log("提交的数据: " + formData);
// 提交成功后可以选择关闭模态框
modal.hide();
});
// 提交询盘
$('#send').click(function() {
var form = $(this).parents('form');
$.ajax({ $.ajax({
url: "{:url('product/inquiry')}", url: "{:url('product/inquiry')}",
type: 'POST', type: 'POST',
data: form.serialize(), data: formData,
success: function(r) { success: function(r) {
if (r.code == 0) { if (r.code == 0) {
form[0].reset(); // 重置表单 form[0].reset(); // 重置表单
@@ -297,6 +293,7 @@
}, },
error: function(e) { error: function(e) {
console.error(e); console.error(e);
modal.hide();
} }
}) })
}); });

View File

@@ -14,6 +14,7 @@
</div> </div>
</form> </form>
<!-- 搜索结果列表--> <!-- 搜索结果列表-->
{notempty name="products"}
<ul class="seul"> <ul class="seul">
{volist name="products" id="pro"} {volist name="products" id="pro"}
<a href="{:url('product/detail', ['id' => $pro['id']])}"> <a href="{:url('product/detail', ['id' => $pro['id']])}">
@@ -32,6 +33,10 @@
</a> </a>
{/volist} {/volist}
</ul> </ul>
<div>{$page|raw}</div>
{else/}
<div style="text-align: center; padding: 10%;">{:lang_i18n('暂无数据')}</div>
{/notempty}
</div> </div>
</div> </div>
{/block} {/block}

View File

@@ -24,23 +24,19 @@
// 直接输出对应的 HTML 标签 // 直接输出对应的 HTML 标签
if (isUCBrowser()) { if (isUCBrowser()) {
document.write(` document.write(`
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@8.4.7/swiper-bundle.min.css"> <link rel="stylesheet" href="__JS__/swiper-bundle8.4.7.min.css">
<script src="https://cdn.jsdelivr.net/npm/swiper@8.4.7/swiper-bundle.min.js"><\/script> <script type="text/javascript" src="__JS__/swiper-bundle8.4.7.min.js"><\/script>
`); `);
} else { } else {
document.write(` document.write(`
<link rel="stylesheet" href="https://unpkg.com/swiper@9.4.1/swiper-bundle.min.css"> <link rel="stylesheet" href="__JS__/swiper-bundle9.4.1.min.css">
<script src="https://unpkg.com/swiper@9.4.1/swiper-bundle.min.js"><\/script> <script type="text/javascript" src="__JS__/swiper-bundle9.4.1.min.js"><\/script>
`); `);
} }
</script> </script>
<!-- 你的 jQuery 和其他脚本 --> <!-- 你的 jQuery 和其他脚本 -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <script src="__JS__/jquery-3.6.0.min.js"></script>
<!-- <link rel="stylesheet" href="https://unpkg.com/swiper@9/swiper-bundle.min.css">
<script type="text/javascript" src="https://unpkg.com/swiper@9.4.1/swiper-bundle.min.js"></script> -->
<script type="text/javascript" src='https://code.jquery.com/jquery-3.6.0.min.js'></script>
<script type="text/javascript" src="__JS__/before-after.min.js"></script> <script type="text/javascript" src="__JS__/before-after.min.js"></script>
<script type="text/javascript" src="__JS__/large.js"></script> <script type="text/javascript" src="__JS__/large.js"></script>
<script type="text/javascript"> <script type="text/javascript">
@@ -112,7 +108,7 @@
(function() { (function() {
var u="//analytics.f2b211.com/"; var u="//analytics.f2b211.com/";
_paq.push(['setTrackerUrl', u+'matomo.php']); _paq.push(['setTrackerUrl', u+'matomo.php']);
_paq.push(['setSiteId', '1']); _paq.push(['setSiteId', '2']);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s); g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
})(); })();
@@ -124,7 +120,7 @@
<body> <body>
<noscript> <noscript>
<!-- Matomo Image Tracker--> <!-- Matomo Image Tracker-->
<img referrerpolicy="no-referrer-when-downgrade" src="https://analytics.f2b211.com/matomo.php?idsite=1&amp;rec=1" style="border:0" alt="" /> <img referrerpolicy="no-referrer-when-downgrade" src="https://analytics.f2b211.com/matomo.php?idsite=2&amp;rec=1" style="border:0" alt="" />
<!-- End Matomo --> <!-- End Matomo -->
</noscript> </noscript>
{block name="header"} {block name="header"}

View File

@@ -27,7 +27,9 @@
<p class="ftitle">{:lang_i18n('产品')}</p> <p class="ftitle">{:lang_i18n('产品')}</p>
<ul> <ul>
{volist name="header_categorys" id="vo"} {volist name="header_categorys" id="vo"}
{if condition="!empty($vo.name)"}
<li><a href="{:url('product/category', ['id' => $vo.id])}" class="fline">{$vo.name}</a></li> <li><a href="{:url('product/category', ['id' => $vo.id])}" class="fline">{$vo.name}</a></li>
{/if}
{/volist} {/volist}
</ul> </ul>
</div> </div>
@@ -38,7 +40,7 @@
{if condition="!empty($vo.children)"} {if condition="!empty($vo.children)"}
<ul> <ul>
{volist name="vo.children" id="vc"} {volist name="vo.children" id="vc"}
<li><a href="{$vc.link}" class="fline">{$vc.name}</a></li> <li><a href="{$vc.link}" class="fline" target="{$vc.blank==1?'_blank':'_self'}">{$vc.name}</a></li>
{/volist} {/volist}
</ul> </ul>
{/if} {/if}
@@ -80,7 +82,7 @@
<div class="ftcopyright"> <div class="ftcopyright">
<span>{$basic_config.website_powerby.value}</span> <span>{$basic_config.website_powerby.value}</span>
{if condition="!empty($basic_config.website_icp)"} {if condition="!empty($basic_config.website_icp)"}
<a href="https://beian.miit.gov.cn/">{$basic_config.website_icp.value}</a> <a href="https://beian.miit.gov.cn/">{$basic_config.website_icp.value|raw}</a>
{/if} {/if}
</div> </div>
{/if} {/if}
@@ -89,12 +91,10 @@
</div> </div>
</div> </div>
</footer> </footer>
{eq name=":cookie('think_lang')" value="zh-cn"}
<div class="backtop"> <div class="backtop">
<img src="__IMAGES__/ic-backtop.png" class="ictop" /> <img src="__IMAGES__/ic-backtop.png" class="ictop" />
<span>返回顶部</span> <span>{eq name=":cookie('think_lang')" value="en-us"}TOP{else /}返回顶部{/eq}</span>
</div> </div>
{/eq}
<script> <script>
$(document).ready(function () { $(document).ready(function () {
// 获取窗口高度 // 获取窗口高度

View File

@@ -92,7 +92,7 @@
</div> </div>
{eq name=":cookie('think_lang')" value="en-us"} {eq name=":cookie('think_lang')" value="en-us"}
{notempty name="basic_config['navigation_store_url']['value']"} {notempty name="basic_config['navigation_store_url']['value']"}
<a class="storetopbt" href="{$basic_config['navigation_store_url']['value']}"> <a class="storetopbt" href="{$basic_config['navigation_store_url']['value']}" target="_blank">
<img src="__IMAGES__/shopico.png" class="storeImgico" />{:lang_i18n('店铺')} <img src="__IMAGES__/shopico.png" class="storeImgico" />{:lang_i18n('店铺')}
</a> </a>
{/notempty} {/notempty}
@@ -115,7 +115,7 @@
<div class="popmain"> <div class="popmain">
{volist name="header_hot_products" id="vo"} {volist name="header_hot_products" id="vo"}
<div class="popitem"> <div class="popitem">
<a href="{:url('product/detail', ['id' => $vo.id])}"><img src="{$vo.cover_image}" class="popimg" /></a> <a href="{:url('product/detail', ['id' => $vo.id])}"><img src="{:thumb($vo.cover_image)}" class="popimg" /></a>
<div class="productName">{$vo.name}</div> <div class="productName">{$vo.name}</div>
<div class="produc-dec">{$vo.short_name}</div> <div class="produc-dec">{$vo.short_name}</div>
</div> </div>
@@ -155,26 +155,53 @@
return history; return history;
} }
// 封装一个函数用于处理鼠标悬停显示和隐藏内容 // 封装一个函数用于处理鼠标悬停显示和隐藏内容
function handleHover($element, $content) { function handleHover($element, $content) {
$element.mouseenter(function () { // 同时支持鼠标悬停和点击事件
$element
.mouseenter(function () {
$content.stop(true, true).slideDown(60); $content.stop(true, true).slideDown(60);
}).mouseleave(function () { })
.mouseleave(function () {
$content.stop(true, true).slideUp(60); $content.stop(true, true).slideUp(60);
})
.click(function (e) {
// 阻止链接默认跳转(如果有链接的话)
if ($content.is(':visible')) {
$content.stop(true, true).slideUp(60);
} else {
$content.stop(true, true).slideDown(60);
}
// 防止点击事件冒泡到a标签
e.preventDefault();
e.stopPropagation();
}); });
} }
// 处理第一个导航项
handleHover($('.navItem').eq(0), $('.navItem').eq(0).find('.navItemConten')); // 处理产品列表的下拉菜单
// 鼠标移入navItem_cyleft里面的li标签添加类移除其他li的类 var $firstNav = $('.navItem').eq(0);
if ($firstNav.find('.navItemConten').length) {
handleHover($firstNav, $firstNav.find('.navItemConten'));
}
// 鼠标移入左侧子菜单切换显示
$('.navItem_cyleft li').mouseenter(function () { $('.navItem_cyleft li').mouseenter(function () {
$(this).addClass('it_active').siblings().removeClass('it_active'); $(this).addClass('it_active').siblings().removeClass('it_active');
$('.navItem_cyright').hide(); $('.navItem_cyright').hide();
$('.navItem_cyright').eq($(this).index()).show(); $('.navItem_cyright').eq($(this).index()).show();
}); });
// 处理第5 - 8个导航项
for (let i = 4; i < 8; i++) { // 动态处理所有带有navItemConten1的导航项
handleHover($('.navItem').eq(i), $('.navItem').eq(i).find('.navItemConten1')); $('.navItem').each(function () {
var $this = $(this);
var $dropdown = $this.find('.navItemConten1');
// 只给有下拉菜单的导航项绑定事件
if ($dropdown.length) {
handleHover($this, $dropdown);
} }
});
// 点击搜索 // 点击搜索
$('#openModalBtn').click(function () { $('#openModalBtn').click(function () {
$('#scmodal').toggle(); $('#scmodal').toggle();
@@ -182,8 +209,20 @@
$('.close-btn').click(function () { $('.close-btn').click(function () {
$('#scmodal').hide(); $('#scmodal').hide();
}); });
// 点击空白处关闭下拉菜单
$(document).click(function () {
$('.navItemConten, .navItemConten1, #top-country').slideUp(60);
});
// 防止下拉菜单内部点击触发空白处关闭事件
$('.navItemConten, .navItemConten1, #top-country').click(function (e) {
e.stopPropagation();
});
// 搜索历史记录回显 // 搜索历史记录回显
history(); history();
// 执行搜索 // 执行搜索
$('#serrchinput').keydown(function (event) { $('#serrchinput').keydown(function (event) {
if (event.originalEvent.keyCode == 13) { if (event.originalEvent.keyCode == 13) {
@@ -198,9 +237,11 @@
window.location.href = "{:url('product/search')}" + '?keywords=' + keywords; window.location.href = "{:url('product/search')}" + '?keywords=' + keywords;
} }
}); });
// 点击选择国家 // 点击选择国家
$('#countrycheck').click(function () { $('#countrycheck').click(function (e) {
$('#top-country').toggle(); $('#top-country').toggle();
e.stopPropagation();
}); });
$('.closecountrybt').click(function () { $('.closecountrybt').click(function () {
$('#top-country').hide(); $('#top-country').hide();

View File

@@ -13,7 +13,27 @@
<link rel="stylesheet" type="text/css" href="__CSS__/topic_nas_header.css" /> <link rel="stylesheet" type="text/css" href="__CSS__/topic_nas_header.css" />
<link rel="stylesheet" type="text/css" href="__CSS__/orico_footer.css" /> <link rel="stylesheet" type="text/css" href="__CSS__/orico_footer.css" />
{block name="style"}{/block} {block name="style"}{/block}
<script type="text/javascript" src='https://code.jquery.com/jquery-3.6.0.min.js'></script> <script src="__JS__/jquery-3.6.0.min.js"></script>
<script>
// 增强型 UC 浏览器检测
function isUCBrowser() {
const ua = navigator.userAgent.toLowerCase();
return ua.includes('ubrowser');
}
// 直接输出对应的 HTML 标签
if (isUCBrowser()) {
document.write(`
<link rel="stylesheet" href="__JS__/swiper-bundle8.4.7.min.css">
<script type="text/javascript" src="__JS__/swiper-bundle8.4.7.min.js"><\/script>
`);
} else {
document.write(`
<link rel="stylesheet" href="__JS__/swiper-bundle9.4.1.min.css">
<script type="text/javascript" src="__JS__/swiper-bundle9.4.1.min.js"><\/script>
`);
}
</script>
<script> <script>
$(window).ready(function () { $(window).ready(function () {
if ($(window).width() < 1024) { if ($(window).width() < 1024) {
@@ -35,7 +55,7 @@
(function() { (function() {
var u="//analytics.f2b211.com/"; var u="//analytics.f2b211.com/";
_paq.push(['setTrackerUrl', u+'matomo.php']); _paq.push(['setTrackerUrl', u+'matomo.php']);
_paq.push(['setSiteId', '1']); _paq.push(['setSiteId', '2']);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s); g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
})(); })();
@@ -45,7 +65,7 @@
<body> <body>
<noscript> <noscript>
<!-- Matomo Image Tracker--> <!-- Matomo Image Tracker-->
<img referrerpolicy="no-referrer-when-downgrade" src="https://analytics.f2b211.com/matomo.php?idsite=1&amp;rec=1" style="border:0" alt="" /> <img referrerpolicy="no-referrer-when-downgrade" src="https://analytics.f2b211.com/matomo.php?idsite=2&amp;rec=1" style="border:0" alt="" />
<!-- End Matomo --> <!-- End Matomo -->
</noscript> </noscript>
{block name="header"} {block name="header"}

View File

@@ -63,7 +63,7 @@
<div class="ftcopyright"> <div class="ftcopyright">
<span>{$basic_config.website_powerby.value}</span> <span>{$basic_config.website_powerby.value}</span>
{if condition="!empty($basic_config.website_icp)"} {if condition="!empty($basic_config.website_icp)"}
<a href="https://beian.miit.gov.cn/">{$basic_config.website_icp.value}</a> <a href="https://beian.miit.gov.cn/">{$basic_config.website_icp.value|raw}</a>
{/if} {/if}
</div> </div>
{/if} {/if}

View File

@@ -1,6 +1,10 @@
<header class="narsPage-head"> <header class="narsPage-head">
<div class="headcenter"> <div class="headcenter">
{eq name=":cookie('think_lang')" value="zh-cn"}
<a href="{:url('/index/topic/nas/index')}">
{else/}
<a> <a>
{/eq}
<img class="logico" style="cursor:pointer;" src="__IMAGES__/logo_nas_{:cookie('think_lang')}.png" /> <img class="logico" style="cursor:pointer;" src="__IMAGES__/logo_nas_{:cookie('think_lang')}.png" />
</a> </a>
{notempty name="header_navigation"} {notempty name="header_navigation"}
@@ -17,9 +21,10 @@
<script type="text/javascript"> <script type="text/javascript">
$(function() { $(function() {
$('.headnav .navitem').each(function(idx, item) { $('.headnav .navitem').each(function(idx, item) {
$(item).removeClass('hover'); var _item = $(item);
if (compareUrls(location.href, item.href)) { _item.removeClass('hover');
$(item).addClass('hover').siblings(); if (_item.attr('href') && compareUrls(location.href, _item.get(0).href)) {
_item.addClass('hover').siblings();
} }
}); });

View File

@@ -8,7 +8,7 @@
<div class="narshelpCenterPc"> <div class="narshelpCenterPc">
<!-- banner-搜索 --> <!-- banner-搜索 -->
<div class="pagetopbg"> <div class="pagetopbg">
<img src="__IMAGES__/nas_help_banner.jpg" class="hpbgimg" /> <img src="__IMAGES__/nas_help_banner.webp" class="hpbgimg" />
<div class='nhlp-search'> <div class='nhlp-search'>
<input class="nhlp-ipt" id="search-input" placeholder="{:lang_i18n('请输入搜索关键字,如安装赛博云空间,影视库')}" autocomplete="off" /> <input class="nhlp-ipt" id="search-input" placeholder="{:lang_i18n('请输入搜索关键字,如安装赛博云空间,影视库')}" autocomplete="off" />
<img src="__IMAGES__/nas_help_search.png" class="searchimg" /> <img src="__IMAGES__/nas_help_search.png" class="searchimg" />
@@ -26,13 +26,13 @@
<h1 class="helph1">{:lang_i18n('使用教程')}</h1> <h1 class="helph1">{:lang_i18n('使用教程')}</h1>
<div class="nhlp-row"> <div class="nhlp-row">
{volist name="article_categorys" id="vo" key="idx"} {volist name="article_categorys" id="vo" key="idx"}
<div class="nhlpit {gt name='idx' value='6'}nhlpit-w{/gt}"> <div class="nhlpit">
<div class="nhlptl"> <div class="nhlptl">
<img src="{$vo.icon}" class="bhlpicoimg" />{$vo.name} <img src="{$vo.icon}" class="bhlpicoimg" />{$vo.name}
</div> </div>
<div class="nhlp-tx-list"> <div class="nhlp-tx-list">
{volist name="vo.article" id="va" key="index"} {volist name="vo.article" id="va" key="index"}
<a class="txrow" href="{:url('/index/topic/nas/help_detail', ['cid' => $vo.id, 'id' => $va.id])}"> <a class="txrow" href="{:url('/index/topic/nas/help_detail', ['pid' => $va.category_id,'cid' => $vo.id, 'id' => $va.id])}">
<div class="nhlp-point"></div> <div class="nhlp-point"></div>
<span class="nhlpsp">{$va.title}</span> <span class="nhlpsp">{$va.title}</span>
<span class="narhelpgoimg"> <span class="narhelpgoimg">
@@ -41,7 +41,7 @@
</a> </a>
{/volist} {/volist}
{if condition="count($vo.article) >= 3"} {if condition="count($vo.article) >= 3"}
<a class="ckgdbt" href="{:url('/index/topic/nas/help_detail', ['cid' => $vo.id, 'id' => isset($vo.article[0])?$vo.article[0]['id']:0])}"> <a class="ckgdbt" href="{:url('/index/topic/nas/help_detail', ['cid' => $vo.id, 'id' => isset($vo.article[0])?$vo.article[0]['id']:0,'pid' => isset($vo.article[0])?$vo.article[0]['category_id']:0])}">
{:lang_i18n('查看更多')} > {:lang_i18n('查看更多')} >
</a> </a>
{/if} {/if}
@@ -136,7 +136,7 @@
$('#dropdown').hide(); $('#dropdown').hide();
} }
}); });
$('.nhlplxwmit:not(:first)').hover(function () { $('.nhlplxwmit').hover(function () {
// 当鼠标移入时,显示.lxewmimg 并隐藏.lximg // 当鼠标移入时,显示.lxewmimg 并隐藏.lximg
var lxe = $(this).find('.lxewmimg'); var lxe = $(this).find('.lxewmimg');
var lxi = $(this).find('.lximg'); var lxi = $(this).find('.lximg');

View File

@@ -1,17 +1,17 @@
{extend name="public/nas_base" /} {extend name="public/nas_base" /}
{block name="title"} {block name="title"}
{notempty name="article.seo_title"}<title>{$article.seo_title}</title>{else /}{__BLOCK__}{/notempty} {notempty name="article.seo_title"}<title>{$article.seo_title}</title>{else /}{__BLOCK__}{/notempty}
{/block} {/block}
{block name="seo"} {block name="seo"}
{notempty name="article.seo_keywords"} {notempty name="article.seo_keywords"}
<meta name="keywords" content="{$article.seo_keywords}" /> <meta name="keywords" content="{$article.seo_keywords}" />
<meta name="description" content="{$article.seo_desc}" /> <meta name="description" content="{$article.seo_desc}" />
{else /} {else /}
{__BLOCK__} {__BLOCK__}
{/notempty} {/notempty}
{/block} {/block}
{block name="style"} {block name="style"}
<link rel="stylesheet" href="__CSS__/topic_nas_help-detail.css"/> <link rel="stylesheet" href="__CSS__/topic_nas_help-detail.css" />
{/block} {/block}
{block name="main"} {block name="main"}
<div class="orico_Page_index"> <div class="orico_Page_index">
@@ -32,33 +32,49 @@
<div class="nars-hlpdt-ml"> <div class="nars-hlpdt-ml">
{notempty name="article_categorys"} {notempty name="article_categorys"}
<div class="nav-tree"> <div class="nav-tree">
<!-- start 三级菜单 -->
{volist name="article_categorys" id="ac"} {volist name="article_categorys" id="ac"}
<div class="category"> <div class="category">
<!-- 一级 -->
<div class="category-title"> <div class="category-title">
<div class="arrow {if condition='$ac.id == $Request.get.cid'}rotate{/if}"> <div class="arrow {if condition='$ac.id == $Request.get.cid'}rotate{/if}"><img src="__IMAGES__/nas-jt.png" class="arrow {if condition='$ac.id == $Request.get.cid'}rotate{/if}" /></div>
<img src="__IMAGES__/nas-jt.png" class="arrow {if condition='$ac.id == $Request.get.cid'}rotate{/if}" />
</div>
<span>{$ac.name}</span> <span>{$ac.name}</span>
</div> </div>
<ul class="sub-list" {if condition='$ac.id == $Request.get.cid'}style="display: block;"{/if}> <!-- 二级-->
{volist name="ac.article" id="ar"} <ul class="sub-list" {if condition='$ac.id == $Request.get.cid' }style="display: block;" {/if}>
<li> {volist name="ac.child" id="ad"}
<a <li class="two-mues">
href="{:url('/index/topic/nas/help_detail', ['cid' => $ac.id, 'id' => $ar.id])}" <a href="#" class="two-a">
{eq name="ar.id" value="$Request.get.id"}class="active"{/eq} <div class="arrow {if condition='$ad.id == $Request.get.pid'}rotate{/if}"><img src="__IMAGES__/nas-jt.png" class="arrow {if condition='$ad.id == $Request.get.pid'}rotate{/if}" /></div>
> <span>{$ad.name}</span>
{$ar.title}
</a> </a>
<ul class="thress-mues" {if condition='$ad.id == $Request.get.pid' }style="display: block;" {/if}>
{volist name="ad.article" id="ae"}
<li style="margin-left: 30px;"><a href="{:url('/index/topic/nas/help_detail', ['cid' => $ac.id,'pid' => $ad.id, 'id' => $ae.id])}" style="padding-top: 6px;" {eq
name="ae.id" value="$Request.get.id" }class="active" {/eq}>{$ae.title}</a></li>
{/volist}
</ul>
<!-- 三级-->
</li> </li>
{/volist} {/volist}
{volist name="ac.article" id="ar"}
<li><a href="{:url('/index/topic/nas/help_detail', ['cid' => $ac.id, 'id' => $ar.id])}" style="padding-top: 6px;" {eq
name="ar.id" value="$Request.get.id" }class="active" {/eq}>{$ar.title}</a></li>
{/volist}
</ul> </ul>
</div> </div>
{/volist} {/volist}
<!-- end 三级菜单 -->
</div> </div>
{/notempty} {/notempty}
</div> </div>
<!--文章详情 --> <!--文章详情 -->
<div class="nars-hlpdt-mm" id="rendered-content">{$article.content|raw|default=''}</div> <div class="ql-container ">
<div class="nars-hlpdt-mm ql-editor" id="rendered-content">{$article.content|raw|default=''}</div>
</div>
<!--锚点定位 --> <!--锚点定位 -->
<div class="nars-hlpdt-mr"> <div class="nars-hlpdt-mr">
<div id="title-list"> <div id="title-list">
@@ -73,10 +89,22 @@
{block name="script"} {block name="script"}
<script type="text/javascript"> <script type="text/javascript">
$(document).ready(function () { $(document).ready(function () {
// 一级菜单点击事件
$('.category-title').click(function () { $('.category-title').click(function () {
$(this).next('.sub-list').slideToggle(); $(this).next('.sub-list').slideToggle();
$(this).find('.arrow').toggleClass('rotate'); $(this).find('.arrow').toggleClass('rotate');
}); });
// 二级菜单点击事件
$('.two-a').click(function(e) {
e.preventDefault();
e.stopPropagation(); // 阻止事件冒泡
// 切换当前二级菜单的箭头方向
$(this).find('.arrow').toggleClass('rotate');
// 切换对应的三级菜单显示/隐藏
$(this).siblings('.thress-mues').slideToggle();
});
// 搜索 // 搜索
$(document).on('click', function (e) { $(document).on('click', function (e) {
var target = $(e.target); var target = $(e.target);
@@ -107,7 +135,7 @@
html = '<ul>' html = '<ul>'
$.each(r.data, function (k, v) { $.each(r.data, function (k, v) {
html += html +=
'<li><a href="{:url(\'/index/topic/nas/help_detail\')}?cid=' + v.category_id + '&id=' + v.id + '">' + v.title + '</a></li>' '<li><a href="{:url(\'/index/topic/nas/help_detail\')}?cid=' + v.category_id + '&id=' + v.id + '&pid=' + v.pid + '">' + v.title + '</a></li>'
}) })
html += '</ul>' html += '</ul>'
} }
@@ -119,11 +147,15 @@
// 内容 // 内容
// 清空标题列表 // 清空标题列表
$("#title-list ul").empty(); $("#title-list ul").empty();
// 提取 h1 标题 // 提取 h3 标题
var h1Titles = $("#rendered-content").find("h3"); var h1Titles = $("#rendered-content").find("h3");
// 只有当找到h3标题且内容不为空时才进行处理
if (h1Titles.length > 0) {
h1Titles.each(function (index) { h1Titles.each(function (index) {
var title = $(this); var title = $(this);
var titleText = title.text(); var titleText = title.text().trim(); // 使用trim()去除空白字符
// 只有当标题文本不为空时才添加到列表
if (titleText) {
var titleId = "title-" + index; var titleId = "title-" + index;
title.attr("id", titleId); title.attr("id", titleId);
var listItem = $("<li>"); var listItem = $("<li>");
@@ -133,7 +165,9 @@
}); });
listItem.append(link); listItem.append(link);
$("#title-list ul").append(listItem); $("#title-list ul").append(listItem);
}
}); });
}
}); });
</script> </script>
{/block} {/block}

View File

@@ -113,7 +113,7 @@
</div> </div>
{/block} {/block}
{block name="script"} {block name="script"}
<script type="text/javascript" src="https://unpkg.com/swiper@9.4.1/swiper-bundle.min.js"></script> <!-- <script type="text/javascript" src="https://unpkg.com/swiper@9.4.1/swiper-bundle.min.js"></script> -->
<script type="text/javascript"> <script type="text/javascript">
$(document).ready(function () { $(document).ready(function () {
// banner轮播 // banner轮播

View File

@@ -0,0 +1,581 @@
{extend name="public/nas_base" /}
{block name="style"}
<link rel="stylesheet" href="__CSS__/topic_power_prodline/index.css">
<link rel="stylesheet" href="__CSS__/topic_power_prodline/swiper.css">
<link rel="stylesheet" href="__CSS__/topic_power_prodline/nav.css">
<link rel="stylesheet" href="__CSS__/topic_power_prodline/advantage.css">
<link rel="stylesheet" href="__CSS__/topic_power_prodline/mask.css">
<link rel="stylesheet" href="__CSS__/topic_power_prodline/product.css">
<link rel="stylesheet" href="__CSS__/topic_power_prodline/product_list.css">
<link rel="stylesheet" href="__CSS__/topic_power_prodline/product_card.css">
<link rel="stylesheet" href="__CSS__/topic_power_prodline/footer.css">
{/block}
{block name="header"}
<!-- 重置header头为空 -->
{/block}
{block name="main"}
<a class="header" href="/">
<div class="header-img">
<img src="__IMAGES__/logo.png" alt="">
</div>
</a>
<!-- 轮播核心容器 -->
<div class="swiper-container auto-swiper-container" >
{notempty name="data.focus_image"}
<div class="swiper-wrapper">
{volist name="data.focus_image" id="fo"}
<a class="swiper-slide auto-swiper-slide" href="{$fo.link}">
<img src="{$fo.image}" alt="{$fo.title}" />
</a>
{/volist}
</div>
<div class="swiper-pagination"></div>
{/notempty}
</div>
<!-- 分类 -->
{notempty name="data.category"}
<div class="nav-box">
{volist name="data.category" id="ca"}
<a class="nav-item" href="{$ca.link}">
<img src="{$ca.image}" alt="{$ca.title}">
<p {:style(['color'=>$ca.title_txt_color])}>{$ca.title}</p>
</a>
{/volist}
</div>
{/notempty}
<!-- 500万 -->
{notempty name="data.why_choose"}
<div class="advantage-section">
{assign name="why_choose_title" value=":array_shift($data.why_choose)" /}
<h2 class="advantage-section__title">{$why_choose_title.title|default=''|raw}</h2>
<div class="advantage-section__list">
{volist name="data.why_choose" id="ch"}
<div class="advantage-card-wrap">
<div class="advantage-card" data-target="design">
<img src="{$ch.image}" alt="{$ch.title}:{$ch.short_title}" class="advantage-card__img">
<div class="advantage-card__content">
<!-- 标题+箭头容器:水平+垂直双居中,内部文字左、箭头右 -->
<div class="advantage-card__heading-wrap">
<div class="advantage-card__heading" {:style(['color'=>$ch.title_txt_color])}>{$ch.title}</div>
<img src="__IMAGES__/jiant.png" alt="" class="card-arrow">
</div>
<div class="advantage-card__description" {:style(['color'=>$ch.short_title_txt_color])}>{$ch.short_title}</div>
<div style="display:none;" class="mack-conten-text">{$ch.desc|raw}</div>
</div>
</div>
</div>
{/volist}
</div>
</div>
{/notempty}
<!-- 产品差旅充 -->
{notempty name="data.travel_charger"}
<div class="product-box">
{assign name="tc_title" value=":array_shift($data.travel_charger)" /}
<div class="product-title">
<h2 class="product-title-h2">{$tc_title.title|default=''}</h2>
<p class="product-title-p">{$tc_title.short_title|default=''}</p>
</div>
<div class="product-container" >
{assign name="tc_first_section_lf" value=":array_shift($data.travel_charger)" /}
{notempty name="tc_first_section_lf"}
<a class="product-left" href="{$tc_first_section_lf.link}">
<img src="{$tc_first_section_lf.image}" alt="{$tc_first_section_lf.title}" class="product-img">
<!-- 公共类+定位类:尺寸统一,定位不同 -->
<div class="product-img-hover product-img-1">
<img src="{$tc_first_section_lf.extra_image}" alt="{$tc_first_section_lf.short_title}">
</div>
</a>
{/notempty}
{assign name="tc_first_section_lr" value=":array_shift($data.travel_charger)" /}
{notempty name="tc_first_section_lr"}
<div class="product-right">
<img src="{$tc_first_section_lr.image}" alt="{$tc_first_section_lr.title}" class="right-content right-img">
<video src="{$tc_first_section_lr.video}" class="right-content right-video" muted loop playsinline controls>
您的浏览器不支持HTML5视频播放请升级浏览器
</video>
</div>
{/notempty}
</div>
{assign name="tc_second_section" value=":array_splice($data.travel_charger, 0, 4)" /}
{notempty name="tc_second_section"}
<div class="product-card-box">
<div class="product-card-container">
{volist name="tc_second_section" id="tss"}
<a class="product-card-wrap" href="{$tss.link}">
<div class="product-card" >
<div class="product-card-img">
<img src="{$tss.image}" alt="{$tss.title}">
</div>
<div class="product-card-text">
<div class="product-card-title" {:style(['color'=>$tss.title_txt_color])}>{$tss.title}</div>
<div class="product-card-desc" {:style(['color'=>$tss.short_title_txt_color])}>{$tss.short_title}</div>
</div>
<div class="product-card-link">
<img src="__IMAGES__/ljgd.png" alt="查看更多">
</div>
</div>
</a>
{/volist}
</div>
</div>
{assign name="tc_three_section" value=":array_shift($data.travel_charger)" /}
{notempty name="tc_three_section"}
<a href="{$tc_three_section.link}" class="more">
<div class="more-img">
{$tc_three_section.title}
</div>
</a>
{/notempty}
{/notempty}
</div>
{/notempty}
<!-- 产品 家居充-->
{notempty name="data.home_charger"}
<div class="product-box">
{assign name="hc_title" value=":array_shift($data.home_charger)" /}
<div class="product-title">
<h2 class="product-title-h2">{$hc_title.title|default=''}</h2>
<p class="product-title-p">{$hc_title.short_title|default=''}</p>
</div>
<div class="product-container">
{assign name="hc_first_section_lf" value=":array_shift($data.home_charger)" /}
{notempty name="hc_first_section_lf"}
<a class="product-left" href="{$hc_first_section_lf.link}">
<img src="{$hc_first_section_lf.image}" alt="{$hc_first_section_lf.title}" class="product-img">
<!-- 公共类+定位类:尺寸统一,定位不同 -->
<div class="product-img-hover product-img-1">
<img src="{$hc_first_section_lf.extra_image}" alt="{$hc_first_section_lf.short_title}">
</div>
</a>
{/notempty}
{assign name="hc_first_section_lr" value=":array_shift($data.home_charger)" /}
{notempty name="hc_first_section_lr"}
<div class="product-right">
<img src="{$hc_first_section_lr.image}" alt="{$hc_first_section_lr.title}" class="right-content right-img">
<video src="{$hc_first_section_lr.video}" class="right-content right-video" muted loop playsinline controls>
您的浏览器不支持HTML5视频播放请升级浏览器
</video>
</div>
{/notempty}
</div>
{assign name="hc_second_section" value=":array_splice($data.home_charger, 0, 4)" /}
{notempty name="hc_second_section"}
<div class="product-card-box">
<div class="product-card-container">
{volist name="hc_second_section" id="hcs"}
<a class="product-card-wrap" href="{$hcs.link}">
<div class="product-card" href="#">
<div class="product-card-img">
<img src="{$hcs.image}" alt="{$hcs.short_title}">
</div>
<div class="product-card-text">
<div class="product-card-title">{$hcs.title}</div>
<div class="product-card-desc">{$hcs.short_title}</div>
</div>
<div class="product-card-link">
<img src="__IMAGES__/ljgd.png" alt="查看更多">
</div>
</div>
</a>
{/volist}
</div>
</div>
{/notempty}
{assign name="hc_three_section" value=":array_shift($data.home_charger)" /}
{notempty name="hc_three_section"}
<a href="{$hc_three_section.link}" class="more">
<div class="more-img">
{$hc_three_section.title}
</div>
</a>
{/notempty}
</div>
{/notempty}
<!-- 产品 桌面充(悬浮图上右超出)底部列表样式不一样(左文右图) -->
<div class="product-box">
{assign name="dc_title" value=":array_shift($data.desktop_charger)" /}
<div class="product-title">
<h2 class="product-title-h2">{$dc_title.title|default=''}</h2>
<p class="product-title-p">{$dc_title.short_title|default=''}</p>
</div>
<div class="product-container">
{assign name="dc_first_section_lf" value=":array_shift($data.desktop_charger)" /}
{notempty name="dc_first_section_lf"}
<a class="product-left" href="{$dc_first_section_lf.link}">
<img src="{$dc_first_section_lf.image}" alt="{$dc_first_section_lf.short_title}" class="product-img">
<!-- 公共类+定位类:尺寸和第一个完全一致,仅定位不同 -->
<div class="product-img-hover product-img-2" >
<img src="{$dc_first_section_lf.extra_image}" alt="{$dc_first_section_lf.short_title}">
</div>
</a>
{/notempty}
{assign name="dc_first_section_lr" value=":array_shift($data.desktop_charger)" /}
{notempty name="dc_first_section_lr"}
<div class="product-right">
<img src="{$dc_first_section_lr.image}"
alt="使用场景" class="right-content right-img">
<!--muted loop playsinline controls-->
<video
src="{$dc_first_section_lr.video}"
class="right-content right-video" muted loop playsinline controls>
您的浏览器不支持HTML5视频播放请升级浏览器
</video>
</div>
{/notempty}
</div>
{assign name="dc_second_section" value=":array_splice($data.desktop_charger, 0, 2)" /}
{notempty name="dc_second_section"}
<div class="product-card-box">
<div class="product-card-container2">
{volist name="dc_second_section" id="dcs"}
<a class="product-card2" href="{$dcs.link}">
<div class="product-text2">
<!-- 新增内部容器,确保所有内容左对齐一致性 -->
<div class="product-text-content2">
<div class="product-card-title2">{$dcs.title}</div> <!-- 测试超出一行省略 -->
<div class="product-card-desc2">{$dcs.short_title}</div> <!-- 测试超出2行省略 -->
<div class="product-card-link2">
<img src="__IMAGES__/ljgd.png" alt="了解更多">
</div>
</div>
</div>
<div class="product-card-img2">
<img src="{$dcs.image}" alt="{$dcs.title}">
</div>
</a>
{/volist}
</div>
</div>
{/notempty}
{assign name="dc_three_section" value=":array_shift($data.desktop_charger)" /}
{notempty name="dc_three_section"}
<a href="{$dc_three_section.link}" class="more">
<div class="more-img">
{$dc_three_section.title}
</div>
</a>
{/notempty}
</div>
<!-- 墙插 -->
{notempty name="data.wall_charger"}
<div class="product-box">
{assign name="wc_title" value=":array_shift($data.wall_charger)" /}
<div class="product-title">
<h2 class="product-title-h2">{$wc_title.title|default=''}</h2>
<p class="product-title-p">{$wc_title.short_title|default=''}</p>
</div>
<div class="product-container">
{assign name="wc_first_section_lf" value=":array_shift($data.wall_charger)" /}
{notempty name="wc_first_section_lf"}
<a class="product-left" href="{$wc_first_section_lf.link}">
<img src="{$wc_first_section_lf.image}" alt="{$wc_first_section_lf.title}" class="product-img">
<!-- 公共类+定位类:尺寸统一,定位不同 -->
<div class="product-img-hover product-img-1">
<img src="{$wc_first_section_lf.extra_image}" alt="{$wc_first_section_lf.title}">
</div>
</a>
{/notempty}
{assign name="wc_first_section_lr" value=":array_shift($data.wall_charger)" /}
{notempty name="wc_first_section_lr"}
<div class="product-right">
<img src="{$wc_first_section_lr.image}" alt="{$wc_first_section_lr.title}" class="right-content right-img">
<video src="{$wc_first_section_lr.video}" class="right-content right-video" muted loop playsinline controls>
您的浏览器不支持HTML5视频播放请升级浏览器
</video>
</div>
{/notempty}
</div>
{assign name="wc_more_section" value=":array_shift($data.wall_charger)" /}
{notempty name="wc_more_section"}
<a href="{$wc_more_section.link}" class="more">
<div class="more-img">
{$wc_more_section.title}
</div>
</a>
{/notempty}
</div>
{/notempty}
{notempty name="data.converter"}
<!-- 转换器 -->
<div class="product-box">
{assign name="ct_title" value=":array_shift($data.converter)" /}
<div class="product-title">
<h2 class="product-title-h2">{$ct_title.title|default=''}</h2>
<p class="product-title-p">{$ct_title.short_title|default=''}</p>
</div>
{assign name="ct_more_section" value=":array_pop($data.converter)" /}
{assign name="ct_chunk_section" value=":array_chunk($data.converter, 2)" /}
{assign name="ct_chunk_section_len" value=":count($ct_chunk_section)" /}
{volist name="ct_chunk_section" id="cts" key="k"}
<div class="product-container">
{assign name="cts_lf" value=":array_shift($cts)" /}
{notempty name="cts_lf"}
<a class="product-left" href="{$cts_lf.link}">
<img src="{$cts_lf.image}" alt="{$cts_lf.title}" class="product-img">
<!-- 公共类+定位类:尺寸统一,定位不同 -->
<!--style="display:flex;justify-content: center;"-->
<div class="product-img-hover product-img-1" >
<!-- style="width:70%"-->
<img src="{$cts_lf.extra_image}" alt="{$cts_lf.title}">
</div>
</a>
{/notempty}
{assign name="cts_lr" value=":array_shift($cts)" /}
{notempty name="cts_lr"}
<div class="product-right">
<img src="{$cts_lr.image}" alt="{$cts_lr.title}" class="right-content right-img">
<video src="{$cts_lr.video}" class="right-content right-video" muted loop playsinline controls>
您的浏览器不支持HTML5视频播放请升级浏览器
</video>
</div>
{/notempty}
</div>
{neq name="k" value="$ct_chunk_section_len"}
<div class="line"></div>
{/neq}
{/volist}
{notempty name="ct_more_section"}
<a href="{$ct_more_section.link}" class="more" style="padding: clamp(1.5rem, 3vw, 3rem) 0">
<div class="more-img">
{$ct_more_section.title}
</div>
</a>
{/notempty}
</div>
{/notempty}
{notempty name="data.footer_info"}
<!-- 底部 -->
<div class="prodline-footer-box">
<div class="prodline-footer-box-img">
<img src="{$data.footer_info.0.image}" alt="">
</div>
</div>
{/notempty}
<!-- 蒙版 -->
<div class="mask" id="mask">
<div class="mask-content" >
<span class="close-btn">&times;</span>
<div class="mask-scroll-content"></div>
</div>
</div>
{/block}
{block name="script"}
<script type="text/javascript">
let swiper=null;
const advantageItems = document.querySelectorAll('.advantage-card');
let scrollTop = 0; // 保存页面滚动位置
let closeBtnHtml = null; // 关闭按钮元素(全局变量,避免重复创建)
const mask = document.getElementById('mask');
const maskContent = document.querySelector('.mask-content');
const maskScrollContent = document.querySelector('.mask-scroll-content'); // 滚动容器(关键!)
const closeBtn = document.querySelector('.close-btn')
// 初始化:确保 maskScrollContent 存在于 maskContent 中(避免被清空)
if (!maskScrollContent) {
// 如果页面没有 mask-scroll-content动态创建确保结构稳定
const scrollContent = document.createElement('div');
scrollContent.className = 'mask-scroll-content';
maskContent.appendChild(scrollContent);
}
function createCloseBtn() {
if (closeBtnHtml) {
closeBtnHtml.remove();
}
closeBtnHtml = document.createElement('span');
closeBtnHtml.className = 'close-btn';
closeBtnHtml.innerHTML = '&times;';
closeBtnHtml.addEventListener('click', hideMask);
// 挂载到 maskContent而非 scrollContent避免被滚动影响位置
maskContent.prepend(closeBtnHtml);
}
function showMask(contentHtml) {
// 保存页面滚动位置
scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
// 关键:将内容填充到 scrollContent 中(而非直接替换 maskContent
maskScrollContent.innerHTML = contentHtml;
createCloseBtn();
// 显示蒙版
mask.style.display = 'flex';
// 禁止滚动(复用你的逻辑)
document.documentElement.classList.add('no-scroll');
document.body.classList.add('no-scroll');
document.body.style.top = `-${scrollTop}px`;
// 额外:打开蒙版时就重置滚动位置(避免残留上次滚动状态)
maskScrollContent.scrollTop = 0;
}
function hideMask() {
// 关键步骤 1先重置 scrollContent 的滚动位置(此时元素还未被销毁)
maskScrollContent.scrollTop = 0;
// 关键步骤 2清空 scrollContent 的内容(而非 maskContent
maskScrollContent.innerHTML = "";
// 隐藏蒙版
mask.style.display = 'none';
// 恢复滚动(复用你的逻辑)
document.documentElement.classList.remove('no-scroll');
document.body.classList.remove('no-scroll');
document.body.style.top = '';
// 还原页面滚动位置
window.scrollTo(0, scrollTop);
// 移除关闭按钮(可选,避免残留)
if (closeBtnHtml) {
closeBtnHtml.remove();
closeBtnHtml = null;
}
}
// 点击卡片显示详情
advantageItems.forEach((item) => {
item.addEventListener('click', (e) => {
// 获取当前点击卡片内的.mack-conten-text元素
const currentMackContent = e.currentTarget.querySelector('.mack-conten-text');
if (currentMackContent) {
// 关键修改获取该元素的子内容innerHTML 本身就是内部HTML不含当前元素标签
// 若想更彻底,可遍历子节点拼接内容(兼容特殊场景)
let contentHtml = '';
Array.from(currentMackContent.childNodes).forEach(child => {
// 只保留元素节点和文本节点(过滤空节点)
if (child.nodeType === 1 || child.nodeType === 3) {
contentHtml += child.outerHTML || child.textContent;
}
});
// 显示蒙版并传入纯净的子内容
showMask(contentHtml);
}
});
});
// 关闭按钮事件
closeBtn.addEventListener('click', hideMask);
// 点击蒙版背景关闭(可选)
mask.addEventListener('click', (e) => {
if (e.target === mask) hideMask();
});
// ESC 键关闭(可选)
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && mask.style.display === 'flex') hideMask();
});
// 关键:拦截蒙版的 touchmove 事件,阻止滚动穿透(移动端核心)
mask.addEventListener(
'touchmove',
(e) => {
// 只有点击蒙版背景(不是内容区域)才阻止滚动
if (e.target === mask) {
e.preventDefault(); // 阻止默认触摸滚动行为
e.stopPropagation(); // 阻止事件冒泡
}
},
{ passive: false }
); // passive: false 必须,否则 preventDefault 无效
const allVideos = document.querySelectorAll('.right-video');
// 停止所有视频播放
function stopAllVideos() {
allVideos.forEach((video) => {
video.pause();
video.style.display = 'none';
// 显示对应图片
const img = video.parentElement.querySelector('.right-img');
if (img) img.style.display = 'block';
});
}
// 滚动切换图片/视频(核心逻辑)
window.addEventListener('scroll', function () {
const productRights = document.querySelectorAll('.product-right');
let activeVideo = null;
// 找出当前在视口中的视频容器
productRights.forEach((rightContainer) => {
const img = rightContainer.querySelector('.right-img');
const video = rightContainer.querySelector('.right-video');
const videoSrc = video.src.trim()
if (!img || !video) return;
if(!videoSrc) return;
const rect = rightContainer.getBoundingClientRect();
// 视口判断容器进入视口50%以上视为活跃
const isInView =
rect.top < window.innerHeight * 0.7 &&
rect.bottom > window.innerHeight * 0.3;
if (isInView) {
activeVideo = video;
}
});
// 处理活跃视频
if (activeVideo) {
stopAllVideos(); // 先停止其他视频
const img = activeVideo.parentElement.querySelector('.right-img');
img.style.display = 'none';
activeVideo.style.display = 'block';
// 自动播放(兼容原生控制栏,用户手动暂停后不会强制播放)
if (activeVideo.paused) {
activeVideo.play().catch((err) => {
console.log('视频播放失败(浏览器限制):', err);
// 播放失败时回退到图片
activeVideo.style.display = 'none';
img.style.display = 'block';
});
}
} else {
stopAllVideos(); // 无活跃视频时停止所有播放
}
});
window.onload = function () {
if (typeof Swiper === 'undefined') {
console.error('Swiper加载失败请刷新页面重试');
return;
}
swiper = new Swiper('.auto-swiper-container', {
autoplay: {
delay: 3000, // 3秒切换
disableOnInteraction: false,
},
loop: false,
slidesPerView: 1,
spaceBetween: 0,
// 启用分页指示标(核心配置)
pagination: {
el: '.swiper-pagination', // 对应 HTML 中的指示标容器
clickable: true, // 允许点击指示标切换
// dynamicBullets: true, // 动态指示标(当前激活放大)
//dynamicMainBullets: 3, // 动态模式显示3个核心指示标
},
navigation: false,
scrollbar: false,
on: {
resize: function () {
this.update();
},
},
});
window.addEventListener('resize', function () {
swiper.update();
});
// 初始化时触发滚动事件,确保状态正确
window.dispatchEvent(new Event('scroll'));
};
</script>
{/block}

View File

@@ -18,7 +18,7 @@ if (!function_exists('image_domain_concat')) {
return $path; return $path;
} }
return rtrim($domain, '/') . '/' . ltrim($path, '/'); return url_join($domain, $path);
} }
} }
@@ -39,7 +39,7 @@ if (!function_exists('video_domain_concat')) {
return $path; return $path;
} }
return rtrim($domain, '/') . '/' . ltrim($path, '/'); return url_join($domain, $path);
} }
} }

View File

@@ -66,6 +66,8 @@ class Article
]) ])
->withoutField([ ->withoutField([
'language_id', 'language_id',
'author',
'source',
'seo_title', 'seo_title',
'seo_keywords', 'seo_keywords',
'seo_desc', 'seo_desc',

View File

@@ -27,6 +27,8 @@ class ArticleCategory
$categories = ArticleCategoryModel::withoutField([ $categories = ArticleCategoryModel::withoutField([
'language_id', 'language_id',
'unique_label', 'unique_label',
'short_name',
'desc',
'created_at', 'created_at',
'updated_at', 'updated_at',
]) ])

View File

@@ -25,7 +25,10 @@ class Authorize
$server = request()->server(); $server = request()->server();
$request = new Request([], $post, [], [], [], $server); $request = new Request([], $post, [], [], [], $server);
$storage = new OAuthStorage; $storage = new OAuthStorage;
$oauth = new OAuth2($storage); $oauth = new OAuth2($storage, [
'access_token_lifetime' => intval(env('OPENAPI.ACCESS_TOKEN_LIFETIME', 3600)),
'refresh_token_lifetime' => intval(env('OPENAPI.REFRESH_TOKEN_LIFETIME', 1209600)),
]);
$token = $oauth->grantAccessToken($request); $token = $oauth->grantAccessToken($request);
return success('success', json_decode($token->getContent(), true)); return success('success', json_decode($token->getContent(), true));
} catch (OAuth2ServerException $e) { } catch (OAuth2ServerException $e) {

View File

@@ -12,6 +12,7 @@ class Product
*/ */
public function list() public function list()
{ {
$params = request()->get([ $params = request()->get([
'category_id', 'category_id',
'language' => 'zh-cn', 'language' => 'zh-cn',
@@ -34,7 +35,6 @@ class Product
'name', 'name',
'short_name', 'short_name',
'cover_image', 'cover_image',
'desc',
'deleted_at' 'deleted_at'
]) ])
->where(function($query) use($params) { ->where(function($query) use($params) {
@@ -47,7 +47,7 @@ class Product
->order(['sort' => 'asc', 'id' => 'desc']) ->order(['sort' => 'asc', 'id' => 'desc'])
->hidden(['category_id']) ->hidden(['category_id'])
->paginate([ ->paginate([
'list_row' => $params['size'], 'list_rows' => $params['size'],
'page' => $params['page'] 'page' => $params['page']
]) ])
->each(function($item) { ->each(function($item) {
@@ -75,14 +75,16 @@ class Product
'params' => fn($query) => $query->field(['product_id', 'name', 'value']) 'params' => fn($query) => $query->field(['product_id', 'name', 'value'])
->hidden(['product_id']), ->hidden(['product_id']),
// 关联sku // 关联sku
'skus' => fn($query) => $query->withoutField(['created_at', 'updated_at']) 'skus' => fn($query) => $query->withoutField(['sort', 'created_at', 'updated_at'])
->with([ ->with([
'sku_attr' => fn($query) => $query->with('attr')->hidden(['sku_id', 'attr_id']) 'sku_attr' => fn($query) => $query->with('attr')->hidden(['sku_id', 'attr_id'])
]) ])
->hidden(['id', 'product_id']), ->hidden(['id', 'product_id']),
// 关联购买链接 // 关联购买链接
'links' => fn($query) => $query->field(['product_id', 'platform_id', 'link']) 'links' => fn($query) => $query->field(['product_id', 'platform_id', 'link'])
->with(['platform' => fn($query) => $query->field(['id', 'platform'])]) ->with([
'platforms' => fn($query) => $query->field(['id', 'platform'])
])
->hidden(['product_id', 'platform_id']), ->hidden(['product_id', 'platform_id']),
// 关联相关产品 // 关联相关产品
'related' => fn($query) => $query->field(['product_id', 'related_product_id']) 'related' => fn($query) => $query->field(['product_id', 'related_product_id'])
@@ -98,6 +100,7 @@ class Product
]) ])
->withoutField([ ->withoutField([
'language_id', 'language_id',
'desc',
'stock_qty', 'stock_qty',
'seo_title', 'seo_title',
'seo_keywords', 'seo_keywords',

View File

@@ -27,6 +27,8 @@ class ProductCategory
$categories = ProductCategoryModel::withoutField([ $categories = ProductCategoryModel::withoutField([
'language_id', 'language_id',
'unique_id', 'unique_id',
'icon',
'desc',
'related_tco_category', 'related_tco_category',
'created_at', 'created_at',
'updated_at' 'updated_at'

View File

@@ -19,7 +19,10 @@ class Auth
public function handle($request, \Closure $next) public function handle($request, \Closure $next)
{ {
try { try {
$oauth = new OAuth2(new OAuthStorage); $oauth = new OAuth2(new OAuthStorage, [
'access_token_lifetime' => intval(env('OPENAPI.ACCESS_TOKEN_LIFETIME', 3600)),
'refresh_token_lifetime' => intval(env('OPENAPI.REFRESH_TOKEN_LIFETIME', 1209600)),
]);
$token = $oauth->getBearerToken(); $token = $oauth->getBearerToken();
$oauth->verifyAccessToken($token); $oauth->verifyAccessToken($token);
} catch (OAuth2ServerException $e) { } catch (OAuth2ServerException $e) {

View File

@@ -20,6 +20,8 @@ class OAuthClientModel extends Model
'client_secret' => 'string', 'client_secret' => 'string',
'redirect_uri' => 'string', 'redirect_uri' => 'string',
'enabled' => 'int', 'enabled' => 'int',
'expired_at' => 'datetime',
'remark' => 'string',
'created_at' => 'datetime', 'created_at' => 'datetime',
'updated_at' => 'datetime', 'updated_at' => 'datetime',
'deleted_at' => 'datetime' 'deleted_at' => 'datetime'

View File

@@ -12,7 +12,7 @@ use app\common\model\ProductPurchaseLinkBaseModel;
class ProductPurchaseLinkModel extends ProductPurchaseLinkBaseModel class ProductPurchaseLinkModel extends ProductPurchaseLinkBaseModel
{ {
// 关联购买平台 // 关联购买平台
public function platform() public function platforms()
{ {
return $this->belongsTo(ProductPurchasePlatformModel::class, 'platform_id', 'id')->bind(['platform']); return $this->belongsTo(ProductPurchasePlatformModel::class, 'platform_id', 'id')->bind(['platform']);
} }

View File

@@ -31,7 +31,11 @@ Route::group('v1', function() {
->middleware(\app\openapi\middleware\Auth::class); ->middleware(\app\openapi\middleware\Auth::class);
}) })
->middleware(\think\middleware\Throttle::class, [ ->middleware(\think\middleware\Throttle::class, [
'prefix' => 'throttle_',
'visit_rate' => '5/m', 'visit_rate' => '5/m',
'key' => function($throttle, $request) {
return '__CONTROLLER__/__ACTION__/__IP__';
},
'visit_fail_response' => function (\think\middleware\Throttle $throttle, \think\Request $request, int $wait_seconds) { 'visit_fail_response' => function (\think\middleware\Throttle $throttle, \think\Request $request, int $wait_seconds) {
return \think\Response::create('您的操作过于频繁, 请在 ' . $wait_seconds . ' 秒后再试。')->code(429); return \think\Response::create('您的操作过于频繁, 请在 ' . $wait_seconds . ' 秒后再试。')->code(429);
}, },

View File

@@ -21,9 +21,9 @@
], ],
"require": { "require": {
"php": ">=8.0.0", "php": ">=8.0.0",
"topthink/framework": "^8.0", "topthink/framework": "8.1.2",
"topthink/think-orm": "v3.0.34", "topthink/think-orm": "v3.0.34",
"topthink/think-filesystem": "^2.0", "topthink/think-filesystem": "^3.0",
"topthink/think-multi-app": "^1.1", "topthink/think-multi-app": "^1.1",
"topthink/think-migration": "^3.1", "topthink/think-migration": "^3.1",
"topthink/think-view": "^2.0", "topthink/think-view": "^2.0",
@@ -34,7 +34,8 @@
"topthink/think-cors": "^1.0", "topthink/think-cors": "^1.0",
"phpoffice/phpspreadsheet": "^3.8", "phpoffice/phpspreadsheet": "^3.8",
"friendsofsymfony/oauth2-php": "^1.3", "friendsofsymfony/oauth2-php": "^1.3",
"mobiledetect/mobiledetectlib": "4.8.09" "mobiledetect/mobiledetectlib": "4.8.09",
"qiniu/php-sdk": "^7.14"
}, },
"require-dev": { "require-dev": {
"symfony/var-dumper": ">=4.2", "symfony/var-dumper": ">=4.2",

View File

@@ -29,15 +29,15 @@ return [
// 驱动方式 // 驱动方式
'type' => 'redis', 'type' => 'redis',
// 服务器地址 // 服务器地址
'host' => '127.0.0.1', 'host' => env('REDIS_HOST', '127.0.0.1'),
// 端口 // 端口
'port' => 6379, 'port' => env('REDIS_PORT', 6379),
// 密码 // 密码
'password' => 'orico@f2b211', 'password' => env('REDIS_PASSWORD', 'orico@f2b211'),
// 缓存有效期 0表示永久缓存 // 缓存有效期 0表示永久缓存
'expire' => 0, 'expire' => 0,
// 缓存前缀 // 缓存前缀
'prefix' => 'ow:', 'prefix' => env('REDIS_PREFIX', 'ow:'),
] ]
], ],
]; ];

View File

@@ -2,9 +2,12 @@
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | 控制台配置 // | 控制台配置
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
return [ return [
// 指令定义 // 指令定义
'commands' => [ 'commands' => [
'data:migrate' => \app\command\DataMigration::class, 'data:migrate' => \app\command\DataMigration::class,
'openapi:addclient' => \app\command\OpenApiMgr\AddClient::class,
], ],
]; ];

View File

@@ -39,6 +39,60 @@ return [
// 可见性 // 可见性
'visibility' => 'public', 'visibility' => 'public',
], ],
'public_qiniu' => [
// 磁盘类型
'type' => \filesystem\driver\Qiniu::class,
// bucker 名称
'bucket' => env('QINIU_CLOUD.BUCKET', 'orico-official-website'),
// 访问密钥
'access_key' => env('QINIU_CLOUD.ACCESS_KEY', 'dOsTum4a5qvhPTBbZRPX0pIOU7PZWRX7htKjztms'),
// 密钥
'secret_key' => env('QINIU_CLOUD.SECRET_KEY', 'KFxsGbnErkALFfeGdMa8QWTdodJbamMX0iznLe-q'),
// 外部URL
'base_url' => env('QINIU_CLOUD.BASE_URL', '//szw73dlk3.hn-bkt.clouddn.com'),
// 路径
'path_prefix' => '/storage',
// 文件名称回调,可为文件名添加特定标志,以便可以在后续识别
'filename_generator' => function (\think\file\UploadedFile $file, callable $context_generator = null): callable {
return function() use ($file, $context_generator) {
// 为文件名称添加配置名,以为后续可能根据文件名识别文件所属存储配置信息
$marker = '_' . base64_encode('public_qiniu');
$filename = $context_generator ? $context_generator($file) : null;
if ($filename == null) {
return date('Ymd') . '/' . md5(microtime(true) . $file->getPathname()) . $marker;
}
return $filename . $marker;
};
},
],
'video_qiniu' => [
// 磁盘类型
'type' => \filesystem\driver\Qiniu::class,
// bucker 名称
'bucket' => env('QINIU_CLOUD.BUCKET', 'orico-official-website'),
// 访问密钥
'access_key' => env('QINIU_CLOUD.ACCESS_KEY', 'dOsTum4a5qvhPTBbZRPX0pIOU7PZWRX7htKjztms'),
// 密钥
'secret_key' => env('QINIU_CLOUD.SECRET_KEY', 'KFxsGbnErkALFfeGdMa8QWTdodJbamMX0iznLe-q'),
// 外部URL
'base_url' => env('QINIU_CLOUD.BASE_URL', '//szw73dlk3.hn-bkt.clouddn.com'),
// 路径
'path_prefix' => '/storage/videos',
// 文件名称回调,可为文件名添加特定标志,以便可以在后续识别
'filename_generator' => function (\think\file\UploadedFile $file, callable $context_generator = null): callable {
return function() use ($file, $context_generator) {
// 为文件名称添加配置名,以为后续可能根据文件名识别文件所属存储配置信息
$marker = '_' . base64_encode('video_qiniu');
$filename = $context_generator ? $context_generator() : null;
if ($filename == null) {
return date('Ymd') . '/' . md5(microtime(true) . $file->getPathname()) . $marker;
}
return $filename . $marker;
};
},
]
// 更多的磁盘配置信息 // 更多的磁盘配置信息
], ],
]; ];

View File

@@ -31,7 +31,7 @@ class CreateVideo extends Migrator
$table = $this->table('video', ['engine' => 'MyISAM', 'comment' => '视频表']); $table = $this->table('video', ['engine' => 'MyISAM', 'comment' => '视频表']);
$table->addColumn('language_id', 'integer', ['signed' => false , 'null' => false, 'comment' => '语言ID']) $table->addColumn('language_id', 'integer', ['signed' => false , 'null' => false, 'comment' => '语言ID'])
->addColumn('category_id', 'integer', ['signed' => false , 'null' => false, 'comment' => '分类ID']) ->addColumn('category_id', 'integer', ['signed' => false , 'null' => false, 'comment' => '分类ID'])
->addColumn('name', 'string', ['limit' => 64 , 'null' => false, 'comment' => '名称']) ->addColumn('name', 'string', ['limit' => 128 , 'null' => false, 'comment' => '名称'])
->addColumn('desc', 'string', ['limit' => 512, 'null' => true, 'default' => null, 'comment' => '描述信息']) ->addColumn('desc', 'string', ['limit' => 512, 'null' => true, 'default' => null, 'comment' => '描述信息'])
->addColumn('image', 'string', ['limit' => 125, 'null' => true, 'default' => null, 'comment' => '封面图片']) ->addColumn('image', 'string', ['limit' => 125, 'null' => true, 'default' => null, 'comment' => '封面图片'])
->addColumn('video', 'string', ['limit' => 125, 'null' => true, 'default' => null, 'comment' => '视频地址']) ->addColumn('video', 'string', ['limit' => 125, 'null' => true, 'default' => null, 'comment' => '视频地址'])

View File

@@ -32,6 +32,8 @@ class CreateSysBannerItem extends Migrator
$table->addColumn('banner_id', 'integer', ['null' => false, 'comment' => '所属Banner ID']) $table->addColumn('banner_id', 'integer', ['null' => false, 'comment' => '所属Banner ID'])
->addColumn('title', 'string', ['limit' => 256, 'null' => false, 'comment' => '标题']) ->addColumn('title', 'string', ['limit' => 256, 'null' => false, 'comment' => '标题'])
->addColumn('title_txt_color', 'string', ['limit' => 7, 'null' => false, 'default' => '', 'comment' => '标题文本颜色']) ->addColumn('title_txt_color', 'string', ['limit' => 7, 'null' => false, 'default' => '', 'comment' => '标题文本颜色'])
->addColumn('short_title', 'string', ['limit' => 255, 'null' => true, 'comment' => 'banner简称'])
->addColumn('short_title_txt_color', 'string', ['limit' => 7, 'null' => true, 'comment' => 'banner简称文本颜色'])
->addColumn('desc', MysqlAdapter::PHINX_TYPE_TEXT, ['null' => true, 'default' => null, 'comment' => '描述']) ->addColumn('desc', MysqlAdapter::PHINX_TYPE_TEXT, ['null' => true, 'default' => null, 'comment' => '描述'])
->addColumn('desc_txt_color', 'string', ['limit' => 7, 'null' => false, 'default' => '', 'comment' => '描述文本颜色']) ->addColumn('desc_txt_color', 'string', ['limit' => 7, 'null' => false, 'default' => '', 'comment' => '描述文本颜色'])
->addColumn('type', 'string', ['limit' => 16, 'null' => false, 'comment' => '类型: image为图片, video为视频']) ->addColumn('type', 'string', ['limit' => 16, 'null' => false, 'comment' => '类型: image为图片, video为视频'])
@@ -39,7 +41,7 @@ class CreateSysBannerItem extends Migrator
->addColumn('extra_image', 'string', ['limit' => 255, 'null' => true, 'default' => null, 'comment' => '额外的图片']) ->addColumn('extra_image', 'string', ['limit' => 255, 'null' => true, 'default' => null, 'comment' => '额外的图片'])
->addColumn('video', 'string', ['limit' => 255, 'null' => true, 'default' => null, 'comment' => '视频']) ->addColumn('video', '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' => 510, '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' => '排序'])
->addColumn('status', 'boolean', ['limit' => 1, 'null' => false, 'default' => 1, 'comment' => '-1为禁用, 1为启用']) ->addColumn('status', 'boolean', ['limit' => 1, 'null' => false, 'default' => 1, 'comment' => '-1为禁用, 1为启用'])
->addColumn('created_at', 'timestamp', ['null' => false, 'default' =>'CURRENT_TIMESTAMP', 'comment' => '创建时间']) ->addColumn('created_at', 'timestamp', ['null' => false, 'default' =>'CURRENT_TIMESTAMP', 'comment' => '创建时间'])

View File

@@ -30,9 +30,9 @@ class SysConfigInit extends Seeder
["id" => 13, "group_id" => 7, "title" => "热线在线时段", "name" => "website_hotline_office_hours", "value" => "周一至周五:东部时间 9:00 - 18:00", "extra" => "", "type" => "textarea", "sort" => 2, "remark" => "售后与技术支持热线在线时段", "created_at" => "2025-04-08 16:11:10", "updated_at" => "2025-06-11 17:09:13", "deleted_at" => null], ["id" => 13, "group_id" => 7, "title" => "热线在线时段", "name" => "website_hotline_office_hours", "value" => "周一至周五:东部时间 9:00 - 18:00", "extra" => "", "type" => "textarea", "sort" => 2, "remark" => "售后与技术支持热线在线时段", "created_at" => "2025-04-08 16:11:10", "updated_at" => "2025-06-11 17:09:13", "deleted_at" => null],
["id" => 14, "group_id" => 7, "title" => "售后与技术支持热线", "name" => "website_hotline_number", "value" => "售后与技术支持热线:400-6696-298", "extra" => "", "type" => "textarea", "sort" => 3, "remark" => "售后与技术支持热线号码", "created_at" => "2025-04-08 16:12:05", "updated_at" => "2025-06-11 17:09:13", "deleted_at" => null], ["id" => 14, "group_id" => 7, "title" => "售后与技术支持热线", "name" => "website_hotline_number", "value" => "售后与技术支持热线:400-6696-298", "extra" => "", "type" => "textarea", "sort" => 3, "remark" => "售后与技术支持热线号码", "created_at" => "2025-04-08 16:12:05", "updated_at" => "2025-06-11 17:09:13", "deleted_at" => null],
["id" => 15, "group_id" => 7, "title" => "微信公众号二维码", "name" => "website_wx_qrcode", "value" => "/storage/images/webSet/20250408/073545e4b54f4f97c7c790498014d6a0.png", "extra" => "width:126px;height:126px;", "type" => "image", "sort" => 4, "remark" => "微信公众号二维码", "created_at" => "2025-04-08 16:13:58", "updated_at" => "2025-06-11 17:09:13", "deleted_at" => null], ["id" => 15, "group_id" => 7, "title" => "微信公众号二维码", "name" => "website_wx_qrcode", "value" => "/storage/images/webSet/20250408/073545e4b54f4f97c7c790498014d6a0.png", "extra" => "width:126px;height:126px;", "type" => "image", "sort" => 4, "remark" => "微信公众号二维码", "created_at" => "2025-04-08 16:13:58", "updated_at" => "2025-06-11 17:09:13", "deleted_at" => null],
["id" => 16, "group_id" => 8, "title" => "Technical Support", "name" => "website_technical_support_email", "value" => "Technical Support:supports@orico.com.cn1", "extra" => "", "type" => "text", "sort" => 1, "remark" => "", "created_at" => "2025-04-08 16:18:24", "updated_at" => "2025-06-12 09:55:21", "deleted_at" => null], ["id" => 16, "group_id" => 8, "title" => "Technical Support", "name" => "website_technical_support_email", "value" => "Technical Support:supports@orico.com.cn", "extra" => "", "type" => "text", "sort" => 1, "remark" => "", "created_at" => "2025-04-08 16:18:24", "updated_at" => "2025-06-12 09:55:21", "deleted_at" => null],
["id" => 17, "group_id" => 8, "title" => "Business", "name" => "website_business_email", "value" => "Business:oversea-bu@orico.com.cn1", "extra" => "", "type" => "text", "sort" => 2, "remark" => "", "created_at" => "2025-04-08 16:20:12", "updated_at" => "2025-06-12 09:55:21", "deleted_at" => null], ["id" => 17, "group_id" => 8, "title" => "Business", "name" => "website_business_email", "value" => "Business:oversea-bu@orico.com.cn", "extra" => "", "type" => "text", "sort" => 2, "remark" => "", "created_at" => "2025-04-08 16:20:12", "updated_at" => "2025-06-12 09:55:21", "deleted_at" => null],
["id" => 18, "group_id" => 8, "title" => "ODM/OEM Service", "name" => "website_ODM/OEM_service_email", "value" => "ODM/OEM Service:odmmarket@orico.com.cn1", "extra" => "", "type" => "text", "sort" => 3, "remark" => "", "created_at" => "2025-04-08 16:27:00", "updated_at" => "2025-06-12 09:55:21", "deleted_at" => null], ["id" => 18, "group_id" => 8, "title" => "ODM/OEM Service", "name" => "website_ODM/OEM_service_email", "value" => "ODM/OEM Service:odmmarket@orico.com.cn", "extra" => "", "type" => "text", "sort" => 3, "remark" => "", "created_at" => "2025-04-08 16:27:00", "updated_at" => "2025-06-12 09:55:21", "deleted_at" => null],
["id" => 19, "group_id" => 8, "title" => "Monday-Friday", "name" => "website_hotline_office_hours", "value" => "Monday-Friday,9a.m.-6p.m.GMT+81", "extra" => "", "type" => "text", "sort" => 4, "remark" => "", "created_at" => "2025-04-08 16:28:52", "updated_at" => "2025-06-12 09:55:21", "deleted_at" => null], ["id" => 19, "group_id" => 8, "title" => "Monday-Friday", "name" => "website_hotline_office_hours", "value" => "Monday-Friday,9a.m.-6p.m.GMT+81", "extra" => "", "type" => "text", "sort" => 4, "remark" => "", "created_at" => "2025-04-08 16:28:52", "updated_at" => "2025-06-12 09:55:21", "deleted_at" => null],
["id" => 20, "group_id" => 9, "title" => "知呼Icon", "name" => "website_media_zhihu.image", "value" => "/storage/images/webSet/20250408/45ef8e4863d63620ab903cf6557c3469.png", "extra" => "width:30px;height:30px;", "type" => "image", "sort" => 1, "remark" => "", "created_at" => "2025-04-08 16:45:57", "updated_at" => "2025-06-11 17:09:13", "deleted_at" => null], ["id" => 20, "group_id" => 9, "title" => "知呼Icon", "name" => "website_media_zhihu.image", "value" => "/storage/images/webSet/20250408/45ef8e4863d63620ab903cf6557c3469.png", "extra" => "width:30px;height:30px;", "type" => "image", "sort" => 1, "remark" => "", "created_at" => "2025-04-08 16:45:57", "updated_at" => "2025-06-11 17:09:13", "deleted_at" => null],
["id" => 21, "group_id" => 9, "title" => "小红书Icon", "name" => "website_media_xiaohongshu.image", "value" => "/storage/images/webSet/20250408/120f13d01c89864fd20df0e24212c214.png", "extra" => "width:30px;height:30px;", "type" => "image", "sort" => 3, "remark" => "", "created_at" => "2025-04-08 16:46:40", "updated_at" => "2025-06-11 17:09:13", "deleted_at" => null], ["id" => 21, "group_id" => 9, "title" => "小红书Icon", "name" => "website_media_xiaohongshu.image", "value" => "/storage/images/webSet/20250408/120f13d01c89864fd20df0e24212c214.png", "extra" => "width:30px;height:30px;", "type" => "image", "sort" => 3, "remark" => "", "created_at" => "2025-04-08 16:46:40", "updated_at" => "2025-06-11 17:09:13", "deleted_at" => null],
@@ -73,12 +73,12 @@ class SysConfigInit extends Seeder
["id" => 56, "group_id" => 12, "title" => "Instagram URL新窗口打开", "name" => "article_share_to_instagram.is_blank", "value" => "1", "extra" => "1:是\n0:否", "type" => "radio", "sort" => 6, "remark" => "", "created_at" => "2025-04-23 17:49:12", "updated_at" => "2025-06-12 09:55:21", "deleted_at" => null], ["id" => 56, "group_id" => 12, "title" => "Instagram URL新窗口打开", "name" => "article_share_to_instagram.is_blank", "value" => "1", "extra" => "1:是\n0:否", "type" => "radio", "sort" => 6, "remark" => "", "created_at" => "2025-04-23 17:49:12", "updated_at" => "2025-06-12 09:55:21", "deleted_at" => null],
["id" => 57, "group_id" => 12, "title" => "Twitter URL新窗口打开", "name" => "article_share_to_twitter.is_blank", "value" => "1", "extra" => "1:是\n0:否", "type" => "radio", "sort" => 9, "remark" => "", "created_at" => "2025-04-23 17:49:12", "updated_at" => "2025-06-12 09:55:21", "deleted_at" => null], ["id" => 57, "group_id" => 12, "title" => "Twitter URL新窗口打开", "name" => "article_share_to_twitter.is_blank", "value" => "1", "extra" => "1:是\n0:否", "type" => "radio", "sort" => 9, "remark" => "", "created_at" => "2025-04-23 17:49:12", "updated_at" => "2025-06-12 09:55:21", "deleted_at" => null],
["id" => 58, "group_id" => 12, "title" => "Reddit URL新窗口打开", "name" => "article_share_to_reddit.is_blank", "value" => "1", "extra" => "1:是\n0:否", "type" => "radio", "sort" => 12, "remark" => "", "created_at" => "2025-04-23 17:49:12", "updated_at" => "2025-06-12 09:55:21", "deleted_at" => null], ["id" => 58, "group_id" => 12, "title" => "Reddit URL新窗口打开", "name" => "article_share_to_reddit.is_blank", "value" => "1", "extra" => "1:是\n0:否", "type" => "radio", "sort" => 12, "remark" => "", "created_at" => "2025-04-23 17:49:12", "updated_at" => "2025-06-12 09:55:21", "deleted_at" => null],
["id" => 59, "group_id" => 1, "title" => "产品询盘可选国家", "name" => "optional_country_for_product_inquiry", "value" => "Afghanistan\nAlbania\nAlgeria\nAmerican Samoa\nAndorra\nAngola\nAnguilla\nAntigua and Barbuda\nArgentina\nArmenia\nAruba\nAustralia\nAustria\nAzerbaijan\nAzores\nBahamas\nBahrain\nBangladesh\nBarbados\nBelarus\nBelgium\nBelize\nBenin\nBermuda\nBhutan\nBolivia\nBosnia and Herzegovina\nBotswana\nBrazil\nBrunei\nBulgaria\nBurkina Faso\nBurundi\nCambodia\nCameroon\nCanada\nCanarias\nCape Verde\nCayman\nCentral African Republic\nChad\nChile\nChina\nColombia\nComoros\nCongo (Congo-Kinshasa)\nCongo\nCook Islands\nCosta Rica\nCote D'Ivoire\nCroatia\nCuba\nCyprus\nCzech\nDenmark\nDjibouti\nDominica\nDominican\nEcuador\nEgypt\nEl Salvador\nEquatorial Guinea\nEritrea\nEstonia\nEthiopia\nFiji\nFinland\nFrance\nFrench Guiana\nFrench Polynesia\nGabon\nGambia\nGeorgia\nGermany\nGhana\nGreece\nGreenland\nGrenada\nGuadeloupe\nGuam\nGuatemala\nGuinea\nGuinea-Bissau\nGuyana\nHaiti\nHonduras\nHungary\nIceland\nIndia\nIndonesia\nIran\nIraq\nIreland\nIsrael\nItaly\nJamaica\nJapan\nJordan\nKazakhstan\nKenya\nKiribati\nKorea (North)\nKorea (South)\nKuwait\nKyrgyzstan\nLaos\nLatvia\nLebanon\nLesotho\nLiberia\nLibya\nLiechtenstein\nLithuania\nLuxembourg\nMacedonia\nMadagascar\nMadeira\nMalawi\nMalaysia\nMaldives\nMali\nMalta\nMarshall Islands\nMartinique\nMauritania\nMauritius\nMexico\nMicronesia\nMoldova\nMonaco\nMongolia\nMontserrat\nMorocco\nMozambique\nMyanmar\nNamibia\nNauru\nNepal\nNetherlands Antilles\nNetherlands\nNew Caledonia\nNew Zealand\nNicaragua\nNiger\nNiue\nNorthern Mariana\nNorway\nOman\nPakistan\nPalau\nPalestine\nPanama\nPapua New Guinea\nParaguay\nPeru\nPhilippines\nPitcairn Islands\nPoland\nPortugal\nPuerto Rico\nQatar\nReunion\nRomania\nRussian Federation\nRwanda\nSaint Helena\nSaint Kitts-Nevis\nSaint Lucia\nSaint Vincent and the Grenadines\nSamoa\nSan Marino\nSao Tome and Principe\nSaudi Arabia\nSenegal\nSerbia\nSeychelles\nSierra Leone\nSingapore\nSlovakia\nSlovenia\nSolomon Islands\nSomalia\nSouth Africa\nSpain\nSri Lanka\nSudan\nSuriname\nSwaziland\nSweden\nSwitzerland\nSyria\nTajikistan\nTanzania\nThailand\nThe British Virgin Islands\nThe United States Virgin Islands\nTimor-Leste\nTogo\nTokelau\nTonga\nTrinidad and Tobago\nTunisia\nTurkey\nTurkmenistan\nTurks and Caicos Islands\nTuvalu\nUganda\nUkraine\nUnited Arab Emirates\nUnited Kingdom\nUnited States\nUruguay\nUzbekistan\nVanuatu\nVatican City\nVenezuela\nVietnam\nWallis and Futuna\nWestern Sahara\nYemen\nZambia\nZimbabwe", "extra" => "", "type" => "textarea", "sort" => 7, "remark" => "", "created_at" => "2025-04-27 11:10:22", "updated_at" => "2025-06-11 17:09:13", "deleted_at" => null], ["id" => 59, "group_id" => 1, "title" => "产品询盘可选国家", "name" => "optional_country_for_product_inquiry", "value" => "Afghanistan\nAlbania\nAlgeria\nAmerican Samoa\nAndorra\nAngola\nAnguilla\nAntigua and Barbuda\nArgentina\nArmenia\nAruba\nAustralia\nAustria\nAzerbaijan\nAzores\nBahamas\nBahrain\nBangladesh\nBarbados\nBelarus\nBelgium\nBelize\nBenin\nBermuda\nBhutan\nBolivia\nBosnia and Herzegovina\nBotswana\nBrazil\nBrunei\nBulgaria\nBurkina Faso\nBurundi\nCambodia\nCameroon\nCanada\nCanarias\nCape Verde\nCayman\nCentral African Republic\nChad\nChile\nChina\nColombia\nComoros\nCongo (Congo-Kinshasa)\nCongo\nCook Islands\nCosta Rica\nCote D'Ivoire\nCroatia\nCuba\nCyprus\nCzech\nDenmark\nDjibouti\nDominica\nDominican\nEcuador\nEgypt\nEl Salvador\nEquatorial Guinea\nEritrea\nEstonia\nEthiopia\nFiji\nFinland\nFrance\nFrench Guiana\nFrench Polynesia\nGabon\nGambia\nGeorgia\nGermany\nGhana\nGreece\nGreenland\nGrenada\nGuadeloupe\nGuam\nGuatemala\nGuinea\nGuinea-Bissau\nGuyana\nHaiti\nHonduras\nHungary\nIceland\nIndia\nIndonesia\nIran\nIraq\nIreland\nIsrael\nItaly\nJamaica\nJapan\nJordan\nKazakhstan\nKenya\nKiribati\nKorea (North)\nKorea (South)\nKuwait\nKyrgyzstan\nLaos\nLatvia\nLebanon\nLesotho\nLiberia\nLibya\nLiechtenstein\nLithuania\nLuxembourg\nMacedonia\nMadagascar\nMadeira\nMalawi\nMalaysia\nMaldives\nMali\nMalta\nMarshall Islands\nMartinique\nMauritania\nMauritius\nMexico\nMicronesia\nMoldova\nMonaco\nMongolia\nMetropolis\nMorocco\nMozambique\nMyanmar\nNamibia\nNauru\nNepal\nNetherlands Antilles\nNetherlands\nNew Caledonia\nNew Zealand\nNicaragua\nNiger\nNiue\nNorthern Mariana\nNorway\nOman\nPakistan\nPalau\nPalestine\nPanama\nPapua New Guinea\nParaguay\nPeru\nPhilippines\nPitcairn Islands\nPoland\nPortugal\nPuerto Rico\nQatar\nReunion\nRomania\nRussian Federation\nRwanda\nSaint Helena\nSaint Kitts-Nevis\nSaint Lucia\nSaint Vincent and the Grenadines\nSamoa\nSan Marino\nSao Tome and Principe\nSaudi Arabia\nSenegal\nSerbia\nSeychelles\nSierra Leone\nSingapore\nSlovakia\nSlovenia\nSolomon Islands\nSomalia\nSouth Africa\nSpain\nSri Lanka\nSudan\nSuriname\nSwaziland\nSweden\nSwitzerland\nSyria\nTajikistan\nTanzania\nThailand\nThe British Virgin Islands\nThe United States Virgin Islands\nTimor-Leste\nTogo\nTokelau\nTonga\nTrinidad and Tobago\nTunisia\nTurkey\nTurkmenistan\nTurks and Caicos Islands\nTuvalu\nUganda\nUkraine\nUnited Arab Emirates\nUnited Kingdom\nUnited States\nUruguay\nUzbekistan\nVanuatu\nVatican City\nVenezuela\nVietnam\nWallis and Futuna\nWestern Sahara\nYemen\nZambia\nZimbabwe", "extra" => "", "type" => "textarea", "sort" => 7, "remark" => "", "created_at" => "2025-04-27 11:10:22", "updated_at" => "2025-06-11 17:09:13", "deleted_at" => null],
["id" => 60, "group_id" => 4, "title" => "产品询盘可选国家", "name" => "optional_country_for_product_inquiry", "value" => "Afghanistan\nAlbania\nAlgeria\nAmerican Samoa\nAndorra\nAngola\nAnguilla\nAntigua and Barbuda\nArgentina\nArmenia\nAruba\nAustralia\nAustria\nAzerbaijan\nAzores\nBahamas\nBahrain\nBangladesh\nBarbados\nBelarus\nBelgium\nBelize\nBenin\nBermuda\nBhutan\nBolivia\nBosnia and Herzegovina\nBotswana\nBrazil\nBrunei\nBulgaria\nBurkina Faso\nBurundi\nCambodia\nCameroon\nCanada\nCanarias\nCape Verde\nCayman\nCentral African Republic\nChad\nChile\nChina\nColombia\nComoros\nCongo (Congo-Kinshasa)\nCongo\nCook Islands\nCosta Rica\nCote D'Ivoire\nCroatia\nCuba\nCyprus\nCzech\nDenmark\nDjibouti\nDominica\nDominican\nEcuador\nEgypt\nEl Salvador\nEquatorial Guinea\nEritrea\nEstonia\nEthiopia\nFiji\nFinland\nFrance\nFrench Guiana\nFrench Polynesia\nGabon\nGambia\nGeorgia\nGermany\nGhana\nGreece\nGreenland\nGrenada\nGuadeloupe\nGuam\nGuatemala\nGuinea\nGuinea-Bissau\nGuyana\nHaiti\nHonduras\nHungary\nIceland\nIndia\nIndonesia\nIran\nIraq\nIreland\nIsrael\nItaly\nJamaica\nJapan\nJordan\nKazakhstan\nKenya\nKiribati\nKorea (North)\nKorea (South)\nKuwait\nKyrgyzstan\nLaos\nLatvia\nLebanon\nLesotho\nLiberia\nLibya\nLiechtenstein\nLithuania\nLuxembourg\nMacedonia\nMadagascar\nMadeira\nMalawi\nMalaysia\nMaldives\nMali\nMalta\nMarshall Islands\nMartinique\nMauritania\nMauritius\nMexico\nMicronesia\nMoldova\nMonaco\nMongolia\nMontserrat\nMorocco\nMozambique\nMyanmar\nNamibia\nNauru\nNepal\nNetherlands Antilles\nNetherlands\nNew Caledonia\nNew Zealand\nNicaragua\nNiger\nNiue\nNorthern Mariana\nNorway\nOman\nPakistan\nPalau\nPalestine\nPanama\nPapua New Guinea\nParaguay\nPeru\nPhilippines\nPitcairn Islands\nPoland\nPortugal\nPuerto Rico\nQatar\nReunion\nRomania\nRussian Federation\nRwanda\nSaint Helena\nSaint Kitts-Nevis\nSaint Lucia\nSaint Vincent and the Grenadines\nSamoa\nSan Marino\nSao Tome and Principe\nSaudi Arabia\nSenegal\nSerbia\nSeychelles\nSierra Leone\nSingapore\nSlovakia\nSlovenia\nSolomon Islands\nSomalia\nSouth Africa\nSpain\nSri Lanka\nSudan\nSuriname\nSwaziland\nSweden\nSwitzerland\nSyria\nTajikistan\nTanzania\nThailand\nThe British Virgin Islands\nThe United States Virgin Islands\nTimor-Leste\nTogo\nTokelau\nTonga\nTrinidad and Tobago\nTunisia\nTurkey\nTurkmenistan\nTurks and Caicos Islands\nTuvalu\nUganda\nUkraine\nUnited Arab Emirates\nUnited Kingdom\nUnited States\nUruguay\nUzbekistan\nVanuatu\nVatican City\nVenezuela\nVietnam\nWallis and Futuna\nWestern Sahara\nYemen\nZambia\nZimbabwe", "extra" => null, "type" => "textarea", "sort" => 7, "remark" => null, "created_at" => "2025-04-27 11:23:25", "updated_at" => "2025-06-12 09:55:21", "deleted_at" => null], ["id" => 60, "group_id" => 4, "title" => "产品询盘可选国家", "name" => "optional_country_for_product_inquiry", "value" => "Afghanistan\nAlbania\nAlgeria\nAmerican Samoa\nAndorra\nAngola\nAnguilla\nAntigua and Barbuda\nArgentina\nArmenia\nAruba\nAustralia\nAustria\nAzerbaijan\nAzores\nBahamas\nBahrain\nBangladesh\nBarbados\nBelarus\nBelgium\nBelize\nBenin\nBermuda\nBhutan\nBolivia\nBosnia and Herzegovina\nBotswana\nBrazil\nBrunei\nBulgaria\nBurkina Faso\nBurundi\nCambodia\nCameroon\nCanada\nCanarias\nCape Verde\nCayman\nCentral African Republic\nChad\nChile\nChina\nColombia\nComoros\nCongo (Congo-Kinshasa)\nCongo\nCook Islands\nCosta Rica\nCote D'Ivoire\nCroatia\nCuba\nCyprus\nCzech\nDenmark\nDjibouti\nDominica\nDominican\nEcuador\nEgypt\nEl Salvador\nEquatorial Guinea\nEritrea\nEstonia\nEthiopia\nFiji\nFinland\nFrance\nFrench Guiana\nFrench Polynesia\nGabon\nGambia\nGeorgia\nGermany\nGhana\nGreece\nGreenland\nGrenada\nGuadeloupe\nGuam\nGuatemala\nGuinea\nGuinea-Bissau\nGuyana\nHaiti\nHonduras\nHungary\nIceland\nIndia\nIndonesia\nIran\nIraq\nIreland\nIsrael\nItaly\nJamaica\nJapan\nJordan\nKazakhstan\nKenya\nKiribati\nKorea (North)\nKorea (South)\nKuwait\nKyrgyzstan\nLaos\nLatvia\nLebanon\nLesotho\nLiberia\nLibya\nLiechtenstein\nLithuania\nLuxembourg\nMacedonia\nMadagascar\nMadeira\nMalawi\nMalaysia\nMaldives\nMali\nMalta\nMarshall Islands\nMartinique\nMauritania\nMauritius\nMexico\nMicronesia\nMoldova\nMonaco\nMongolia\nMetropolis\nMorocco\nMozambique\nMyanmar\nNamibia\nNauru\nNepal\nNetherlands Antilles\nNetherlands\nNew Caledonia\nNew Zealand\nNicaragua\nNiger\nNiue\nNorthern Mariana\nNorway\nOman\nPakistan\nPalau\nPalestine\nPanama\nPapua New Guinea\nParaguay\nPeru\nPhilippines\nPitcairn Islands\nPoland\nPortugal\nPuerto Rico\nQatar\nReunion\nRomania\nRussian Federation\nRwanda\nSaint Helena\nSaint Kitts-Nevis\nSaint Lucia\nSaint Vincent and the Grenadines\nSamoa\nSan Marino\nSao Tome and Principe\nSaudi Arabia\nSenegal\nSerbia\nSeychelles\nSierra Leone\nSingapore\nSlovakia\nSlovenia\nSolomon Islands\nSomalia\nSouth Africa\nSpain\nSri Lanka\nSudan\nSuriname\nSwaziland\nSweden\nSwitzerland\nSyria\nTajikistan\nTanzania\nThailand\nThe British Virgin Islands\nThe United States Virgin Islands\nTimor-Leste\nTogo\nTokelau\nTonga\nTrinidad and Tobago\nTunisia\nTurkey\nTurkmenistan\nTurks and Caicos Islands\nTuvalu\nUganda\nUkraine\nUnited Arab Emirates\nUnited Kingdom\nUnited States\nUruguay\nUzbekistan\nVanuatu\nVatican City\nVenezuela\nVietnam\nWallis and Futuna\nWestern Sahara\nYemen\nZambia\nZimbabwe", "extra" => null, "type" => "textarea", "sort" => 7, "remark" => null, "created_at" => "2025-04-27 11:23:25", "updated_at" => "2025-06-12 09:55:21", "deleted_at" => null],
["id" => 61, "group_id" => 1, "title" => "导航位置店铺URL", "name" => "navigation_store_url", "value" => "https://oricotechs.com/", "extra" => null, "type" => "text", "sort" => 6, "remark" => null, "created_at" => "2025-05-13 17:45:46", "updated_at" => "2025-06-11 17:09:13", "deleted_at" => null], ["id" => 61, "group_id" => 1, "title" => "导航位置店铺URL", "name" => "navigation_store_url", "value" => "https://oricotechs.com/", "extra" => null, "type" => "text", "sort" => 6, "remark" => null, "created_at" => "2025-05-13 17:45:46", "updated_at" => "2025-06-11 17:09:13", "deleted_at" => null],
["id" => 62, "group_id" => 4, "title" => "导航位置店铺URL", "name" => "navigation_store_url", "value" => "https://oricotechs.com/", "extra" => null, "type" => "text", "sort" => 6, "remark" => null, "created_at" => "2025-05-13 17:45:46", "updated_at" => "2025-06-12 09:55:21", "deleted_at" => null], ["id" => 62, "group_id" => 4, "title" => "导航位置店铺URL", "name" => "navigation_store_url", "value" => "https://oricotechs.com/", "extra" => null, "type" => "text", "sort" => 6, "remark" => null, "created_at" => "2025-05-13 17:45:46", "updated_at" => "2025-06-12 09:55:21", "deleted_at" => null],
["id" => 63, "group_id" => 7, "title" => "邮箱", "name" => "website_email", "value" => "supports@orico.com.cn", "extra" => null, "type" => "text", "sort" => 1, "remark" => null, "created_at" => "2025-05-23 17:06:53", "updated_at" => "2025-06-11 17:09:13", "deleted_at" => null], ["id" => 63, "group_id" => 7, "title" => "邮箱", "name" => "website_email", "value" => "supports@orico.com.cn", "extra" => null, "type" => "text", "sort" => 1, "remark" => null, "created_at" => "2025-05-23 17:06:53", "updated_at" => "2025-06-11 17:09:13", "deleted_at" => null],
["id" => 64, "group_id" => 8, "title" => "Technical Support", "name" => "website_email", "value" => "supports@orico.com.cn1", "extra" => null, "type" => "text", "sort" => 1, "remark" => null, "created_at" => "2025-05-23 17:06:53", "updated_at" => "2025-06-12 09:55:21", "deleted_at" => null], ["id" => 64, "group_id" => 8, "title" => "Technical Support", "name" => "website_email", "value" => "supports@orico.com.cn", "extra" => null, "type" => "text", "sort" => 1, "remark" => null, "created_at" => "2025-05-23 17:06:53", "updated_at" => "2025-06-12 09:55:21", "deleted_at" => null],
["id" => 65, "group_id" => 3, "title" => "是否开启", "name" => "watermark_enabled", "value" => "0", "extra" => "0:否\n1:是", "type" => "radio", "sort" => 1, "remark" => "", "created_at" => "2025-06-10 15:41:54", "updated_at" => "2025-06-11 17:09:13", "deleted_at" => null], ["id" => 65, "group_id" => 3, "title" => "是否开启", "name" => "watermark_enabled", "value" => "0", "extra" => "0:否\n1:是", "type" => "radio", "sort" => 1, "remark" => "", "created_at" => "2025-06-10 15:41:54", "updated_at" => "2025-06-11 17:09:13", "deleted_at" => null],
["id" => 66, "group_id" => 3, "title" => "类型", "name" => "watermark_type", "value" => "TEXT", "extra" => "TEXT:文本['watermark_text_value','watermark_text_font','watermark_text_size','watermark_text_color']\nIMAGE:图片['watermark_image_value','watermark_image_width','watermark_image_height']", "type" => "radio", "sort" => 2, "remark" => "", "created_at" => "2025-06-10 15:44:15", "updated_at" => "2025-06-11 17:09:13", "deleted_at" => null], ["id" => 66, "group_id" => 3, "title" => "类型", "name" => "watermark_type", "value" => "TEXT", "extra" => "TEXT:文本['watermark_text_value','watermark_text_font','watermark_text_size','watermark_text_color']\nIMAGE:图片['watermark_image_value','watermark_image_width','watermark_image_height']", "type" => "radio", "sort" => 2, "remark" => "", "created_at" => "2025-06-10 15:44:15", "updated_at" => "2025-06-11 17:09:13", "deleted_at" => null],
["id" => 67, "group_id" => 3, "title" => "水印文本", "name" => "watermark_text_value", "value" => "ORICO", "extra" => "", "type" => "text", "sort" => 3, "remark" => "中文情况下,请确认字体支持中文", "created_at" => "2025-06-10 15:48:38", "updated_at" => "2025-06-11 17:09:13", "deleted_at" => null], ["id" => 67, "group_id" => 3, "title" => "水印文本", "name" => "watermark_text_value", "value" => "ORICO", "extra" => "", "type" => "text", "sort" => 3, "remark" => "中文情况下,请确认字体支持中文", "created_at" => "2025-06-10 15:48:38", "updated_at" => "2025-06-11 17:09:13", "deleted_at" => null],
@@ -105,18 +105,24 @@ class SysConfigInit extends Seeder
["id" => 88, "group_id" => 6, "title" => "位置", "name" => "watermark_position", "value" => "top-left", "extra" => "top-left:左上角\ntop-right:右上角\ntop:上 - 水平居中\nleft:左 - 垂直居中\ncenter:水平垂直居中\nright:右 - 垂直居中\nbottom:下 - 水平居中\nbottom-left:左下角\nbottom-right:右下角", "type" => "radio", "sort" => 11, "remark" => null, "created_at" => "2025-06-10 16:02:17", "updated_at" => "2025-06-12 09:55:21", "deleted_at" => null], ["id" => 88, "group_id" => 6, "title" => "位置", "name" => "watermark_position", "value" => "top-left", "extra" => "top-left:左上角\ntop-right:右上角\ntop:上 - 水平居中\nleft:左 - 垂直居中\ncenter:水平垂直居中\nright:右 - 垂直居中\nbottom:下 - 水平居中\nbottom-left:左下角\nbottom-right:右下角", "type" => "radio", "sort" => 11, "remark" => null, "created_at" => "2025-06-10 16:02:17", "updated_at" => "2025-06-12 09:55:21", "deleted_at" => null],
["id" => 89, "group_id" => 6, "title" => "外边距 - X轴", "name" => "watermark_offset_x", "value" => "", "extra" => "", "type" => "text", "sort" => 12, "remark" => "在“位置”基础上的x轴偏移量", "created_at" => "2025-06-10 16:06:23", "updated_at" => "2025-06-12 09:55:21", "deleted_at" => null], ["id" => 89, "group_id" => 6, "title" => "外边距 - X轴", "name" => "watermark_offset_x", "value" => "", "extra" => "", "type" => "text", "sort" => 12, "remark" => "在“位置”基础上的x轴偏移量", "created_at" => "2025-06-10 16:06:23", "updated_at" => "2025-06-12 09:55:21", "deleted_at" => null],
["id" => 90, "group_id" => 6, "title" => "外边距 - Y轴", "name" => "watermark_offset_y", "value" => "", "extra" => null, "type" => "text", "sort" => 13, "remark" => "在“位置”基础上的y轴偏移量", "created_at" => "2025-06-10 16:09:23", "updated_at" => "2025-06-12 09:55:21", "deleted_at" => null], ["id" => 90, "group_id" => 6, "title" => "外边距 - Y轴", "name" => "watermark_offset_y", "value" => "", "extra" => null, "type" => "text", "sort" => 13, "remark" => "在“位置”基础上的y轴偏移量", "created_at" => "2025-06-10 16:09:23", "updated_at" => "2025-06-12 09:55:21", "deleted_at" => null],
["id" => 91, "group_id" => 14, "title" => "图片 - 保留原文件名", "name" => "upload_image.filename_keep", "value" => NULL, "extra" => "0:否\n1:是", "type" => "radio", "sort" => 1, "remark" => "默认为否", "created_at" => "2025-06-12 11:08:59", "updated_at" => "2025-06-12 11:29:24", "deleted_at" => NULL], ["id" => 91, "group_id" => 14, "title" => "图片 - 格式转为", "name" => "upload_image.filetype_to", "value" => "original", "extra" => "original:原格式\nwebp:webp格式", "type" => "radio", "sort" => 1, "remark" => "webp格式图片质量为原图75%可大幅压缩图片大小选定webp格式时所有上传图片均保存为webp格式。", "created_at" => "2025-06-12 11:28:22", "updated_at" => "2025-06-12 11:28:22", "deleted_at" => NULL],
["id" => 92, "group_id" => 14, "title" => "图片 - 唯一性保持", "name" => "upload_image.filemd5_unique", "value" => "1", "extra" => "0:否\n1:是", "type" => "radio", "sort" => 2, "remark" => "如果保持图片文件唯一,重复图片上传时将立即返回已存在的地址;如果不保持,则同模块同一天上传的文件根据原文件名覆盖。", "created_at" => "2025-06-12 11:16:46", "updated_at" => "2025-06-12 11:24:39", "deleted_at" => NULL], ["id" => 92, "group_id" => 14, "title" => "图片 - 保留原文件名", "name" => "upload_image.filename_keep", "value" => NULL, "extra" => "0:否\n1:是", "type" => "radio", "sort" => 2, "remark" => "默认为否", "created_at" => "2025-06-12 11:08:59", "updated_at" => "2025-06-12 11:29:24", "deleted_at" => NULL],
["id" => 93, "group_id" => 14, "title" => "视频 - 保留原文件名", "name" => "upload_video.filename_keep", "value" => NULL, "extra" => "0:否\n1:是", "type" => "radio", "sort" => 3, "remark" => "默认为否", "created_at" => "2025-06-12 11:22:27", "updated_at" => "2025-06-12 11:29:24", "deleted_at" => NULL], ["id" => 93, "group_id" => 14, "title" => "图片 - 唯一性保持", "name" => "upload_image.filemd5_unique", "value" => "1", "extra" => "0:否\n1:是", "type" => "radio", "sort" => 3, "remark" => "如果保持图片文件唯一,重复图片上传时将立即返回已存在的地址;如果不保持,则同模块同一天上传的文件根据原文件名覆盖。", "created_at" => "2025-06-12 11:16:46", "updated_at" => "2025-06-12 11:24:39", "deleted_at" => NULL],
["id" => 94, "group_id" => 14, "title" => "视频 - 唯一性保持", "name" => "upload_video.filemd5_unique", "value" => "1", "extra" => "0:否\n1:是", "type" => "radio", "sort" => 4, "remark" => "如果保持视频文件唯一,重复视频上传时将立即返回已存在的地址;如果不保持,则同模块同一天上传的文件根据原文件名覆盖。", "created_at" => "2025-06-12 11:24:24", "updated_at" => "2025-06-12 11:25:18", "deleted_at" => NULL], ["id" => 94, "group_id" => 14, "title" => "视频 - 保留原文件名", "name" => "upload_video.filename_keep", "value" => NULL, "extra" => "0:否\n1:是", "type" => "radio", "sort" => 4, "remark" => "默认为否", "created_at" => "2025-06-12 11:22:27", "updated_at" => "2025-06-12 11:29:24", "deleted_at" => NULL],
["id" => 95, "group_id" => 14, "title" => "附件 - 保留原文件名", "name" => "upload_attachment.filename_keep", "value" => NULL, "extra" => "0:否\n1:是", "type" => "radio", "sort" => 5, "remark" => "默认为否", "created_at" => "2025-06-12 11:27:08", "updated_at" => "2025-06-12 11:27:08", "deleted_at" => NULL], ["id" => 95, "group_id" => 14, "title" => "视频 - 唯一性保持", "name" => "upload_video.filemd5_unique", "value" => "1", "extra" => "0:否\n1:是", "type" => "radio", "sort" => 5, "remark" => "如果保持视频文件唯一,重复视频上传时将立即返回已存在的地址;如果不保持,则同模块同一天上传的文件根据原文件名覆盖。", "created_at" => "2025-06-12 11:24:24", "updated_at" => "2025-06-12 11:25:18", "deleted_at" => NULL],
["id" => 96, "group_id" => 14, "title" => "附件 - 唯一性保持", "name" => "upload_attachment.filemd5_unique", "value" => "1", "extra" => "0:否\n1:是", "type" => "radio", "sort" => 6, "remark" => "如果保持附件文件唯一,重复附件上传时将立即返回已存在的地址;如果不保持,则同模块同一天上传的文件根据原文件名覆盖。", "created_at" => "2025-06-12 11:28:22", "updated_at" => "2025-06-12 11:28:22", "deleted_at" => NULL], ["id" => 96, "group_id" => 14, "title" => "附件 - 保留原文件名", "name" => "upload_attachment.filename_keep", "value" => NULL, "extra" => "0:否\n1:是", "type" => "radio", "sort" => 6, "remark" => "默认为否", "created_at" => "2025-06-12 11:27:08", "updated_at" => "2025-06-12 11:27:08", "deleted_at" => NULL],
["id" => 97, "group_id" => 13, "title" => "图片 - 保留原文件名", "name" => "upload_image.filename_keep", "value" => NULL, "extra" => "0:否\n1:是", "type" => "radio", "sort" => 1, "remark" => "默认为否", "created_at" => "2025-06-12 11:08:59", "updated_at" => "2025-06-12 11:29:24", "deleted_at" => NULL], ["id" => 97, "group_id" => 14, "title" => "附件 - 唯一性保持", "name" => "upload_attachment.filemd5_unique", "value" => "1", "extra" => "0:否\n1:是", "type" => "radio", "sort" => 7, "remark" => "如果保持附件文件唯一,重复附件上传时将立即返回已存在的地址;如果不保持,则同模块同一天上传的文件根据原文件名覆盖。", "created_at" => "2025-06-12 11:28:22", "updated_at" => "2025-06-12 11:28:22", "deleted_at" => NULL],
["id" => 98, "group_id" => 13, "title" => "图片 - 唯一性保持", "name" => "upload_image.filemd5_unique", "value" => "1", "extra" => "0:否\n1:是", "type" => "radio", "sort" => 2, "remark" => "如果保持图片文件唯一,重复图片上传时将立即返回已存在的地址;如果不保持,则同模块同一天上传的文件根据原文件名覆盖", "created_at" => "2025-06-12 11:16:46", "updated_at" => "2025-06-12 11:24:39", "deleted_at" => NULL], ["id" => 98, "group_id" => 13, "title" => "图片 - 格式转为", "name" => "upload_image.filetype_to", "value" => "original", "extra" => "original:原格式\nwebp:webp格式", "type" => "radio", "sort" => 1, "remark" => "webp格式图片质量为原图75%可大幅压缩图片大小选定webp格式时所有上传图片均保存为webp格式", "created_at" => "2025-06-12 11:28:22", "updated_at" => "2025-06-12 11:28:22", "deleted_at" => NULL],
["id" => 99, "group_id" => 13, "title" => "视频 - 保留原文件名", "name" => "upload_video.filename_keep", "value" => NULL, "extra" => "0:否\n1:是", "type" => "radio", "sort" => 3, "remark" => "默认为否", "created_at" => "2025-06-12 11:22:27", "updated_at" => "2025-06-12 11:29:24", "deleted_at" => NULL], ["id" => 99, "group_id" => 13, "title" => "图片 - 保留原文件名", "name" => "upload_image.filename_keep", "value" => NULL, "extra" => "0:否\n1:是", "type" => "radio", "sort" => 2, "remark" => "默认为否", "created_at" => "2025-06-12 11:08:59", "updated_at" => "2025-06-12 11:29:24", "deleted_at" => NULL],
["id" => 100, "group_id" => 13, "title" => "视频 - 唯一性保持", "name" => "upload_video.filemd5_unique", "value" => "1", "extra" => "0:否\n1:是", "type" => "radio", "sort" => 4, "remark" => "如果保持视频文件唯一,重复视频上传时将立即返回已存在的地址;如果不保持,则同模块同一天上传的文件根据原文件名覆盖。", "created_at" => "2025-06-12 11:24:24", "updated_at" => "2025-06-12 11:25:18", "deleted_at" => NULL], ["id" => 100, "group_id" => 13, "title" => "图片 - 唯一性保持", "name" => "upload_image.filemd5_unique", "value" => "1", "extra" => "0:否\n1:是", "type" => "radio", "sort" => 3, "remark" => "如果保持图片文件唯一,重复图片上传时将立即返回已存在的地址;如果不保持,则同模块同一天上传的文件根据原文件名覆盖。", "created_at" => "2025-06-12 11:16:46", "updated_at" => "2025-06-12 11:24:39", "deleted_at" => NULL],
["id" => 101, "group_id" => 13, "title" => "附件 - 保留原文件名", "name" => "upload_attachment.filename_keep", "value" => NULL, "extra" => "0:否\n1:是", "type" => "radio", "sort" => 5, "remark" => "默认为否", "created_at" => "2025-06-12 11:27:08", "updated_at" => "2025-06-12 11:27:08", "deleted_at" => NULL], ["id" => 101, "group_id" => 13, "title" => "视频 - 保留原文件名", "name" => "upload_video.filename_keep", "value" => NULL, "extra" => "0:否\n1:是", "type" => "radio", "sort" => 4, "remark" => "默认为否", "created_at" => "2025-06-12 11:22:27", "updated_at" => "2025-06-12 11:29:24", "deleted_at" => NULL],
["id" => 102, "group_id" => 13, "title" => "附件 - 唯一性保持", "name" => "upload_attachment.filemd5_unique", "value" => "1", "extra" => "0:否\n1:是", "type" => "radio", "sort" => 6, "remark" => "如果保持附件文件唯一,重复附件上传时将立即返回已存在的地址;如果不保持,则同模块同一天上传的文件根据原文件名覆盖。", "created_at" => "2025-06-12 11:28:22", "updated_at" => "2025-06-12 11:28:22", "deleted_at" => NULL], ["id" => 102, "group_id" => 13, "title" => "视频 - 唯一性保持", "name" => "upload_video.filemd5_unique", "value" => "1", "extra" => "0:否\n1:是", "type" => "radio", "sort" => 5, "remark" => "如果保持视频文件唯一,重复视频上传时将立即返回已存在的地址;如果不保持,则同模块同一天上传的文件根据原文件名覆盖。", "created_at" => "2025-06-12 11:24:24", "updated_at" => "2025-06-12 11:25:18", "deleted_at" => NULL],
["id" => 103, "group_id" => 13, "title" => "附件 - 保留原文件名", "name" => "upload_attachment.filename_keep", "value" => NULL, "extra" => "0:否\n1:是", "type" => "radio", "sort" => 6, "remark" => "默认为否", "created_at" => "2025-06-12 11:27:08", "updated_at" => "2025-06-12 11:27:08", "deleted_at" => NULL],
["id" => 104, "group_id" => 13, "title" => "附件 - 唯一性保持", "name" => "upload_attachment.filemd5_unique", "value" => "1", "extra" => "0:否\n1:是", "type" => "radio", "sort" => 7, "remark" => "如果保持附件文件唯一,重复附件上传时将立即返回已存在的地址;如果不保持,则同模块同一天上传的文件根据原文件名覆盖。", "created_at" => "2025-06-12 11:28:22", "updated_at" => "2025-06-12 11:28:22", "deleted_at" => NULL],
['id' => 105, 'group_id' => 13, 'title' => '视频 - 保存到', 'name' => 'upload_video.save_to', 'value' => 'qiniu_cloud', 'extra' => 'local:本地\nqiniu_cloud:七牛云', 'type' => 'radio', 'sort' => 6, 'remark' => '视频保存位置,默认为“本地” - [本地为本站点服务器;七牛云为七牛云对象存储,上传经这本站点服务器中转,上传耗时稍慢于“本地”]', 'created_at' => '2025-07-25 10:46:24', 'updated_at' => '2025-07-25 16:41:53', 'deleted_at' => NULL],
['id' => 106, 'group_id' => 13, 'title' => '附件 - 保存到', 'name' => 'upload_attachment.save_to', 'value' => 'local', 'extra' => 'local:本地\nqiniu_cloud:七牛云', 'type' => 'radio', 'sort' => 9, 'remark' => '附件保存位置,默认为“本地” - [本地为本站点服务器;七牛云为七牛云对象存储,上传经这本站点服务器中转,上传耗时稍慢于“本地”]', 'created_at' => '2025-07-25 10:51:34', 'updated_at' => '2025-07-25 16:41:53', 'deleted_at' => NULL],
['id' => 107, 'group_id' => 14, 'title' => '视频 - 保存到', 'name' => 'upload_video.save_to', 'value' => 'qiniu_cloud', 'extra' => 'local:本地\nqiniu_cloud:七牛云', 'type' => 'radio', 'sort' => 6, 'remark' => '视频保存位置,默认为“本地” - [本地为本站点服务器;七牛云为七牛云对象存储,上传经这本站点服务器中转,上传耗时稍慢于“本地”]', 'created_at' => '2025-07-25 10:46:24', 'updated_at' => '2025-07-25 16:41:53', 'deleted_at' => NULL],
['id' => 108, 'group_id' => 14, 'title' => '附件 - 保存到', 'name' => 'upload_attachment.save_to', 'value' => 'local', 'extra' => 'local:本地\nqiniu_cloud:七牛云', 'type' => 'radio', 'sort' => 9, 'remark' => '附件保存位置,默认为“本地” - [本地为本站点服务器;七牛云为七牛云对象存储,上传经这本站点服务器中转,上传耗时稍慢于“本地”]', 'created_at' => '2025-07-25 10:51:34', 'updated_at' => '2025-07-25 16:41:53', 'deleted_at' => NULL],
]; ];
$table = $this->table('sys_config'); $table = $this->table('sys_config');

View File

@@ -0,0 +1,295 @@
<?php
namespace filesystem\adapter;
use League\Flysystem\Config;
use League\Flysystem\FilesystemAdapter;
use League\Flysystem\FileAttributes;
use League\Flysystem\UnableToCopyFile;
use League\Flysystem\UnableToDeleteFile;
use League\Flysystem\UnableToMoveFile;
use League\Flysystem\UnableToReadFile;
use League\Flysystem\UnableToRetrieveMetadata;
use League\Flysystem\UnableToSetVisibility;
use League\Flysystem\UnableToWriteFile;
use Qiniu\Auth;
use Qiniu\Storage\UploadManager;
use Qiniu\Storage\BucketManager;
class QiniuAdapter implements FilesystemAdapter
{
protected ?Auth $authMgr;
protected ?UploadManager $uploadMgr;
protected ?BucketManager $bucketMgr;
public function __construct(protected string $access_key, protected string $secret_key, protected string $bucket, protected string $base_url, protected string $path)
{
}
private function getAuthMgr(): Auth
{
return $this->authMgr ?? new Auth($this->access_key, $this->secret_key);
}
private function getUploadMgr(): UploadManager
{
return $this->uploadMgr ?? new UploadManager();
}
private function getBucketMgr(): BucketManager
{
return $this->bucketMgr ?? new BucketManager($this->authMgr);
}
private function getPathPrefix(): string
{
$path = ltrim($this->path, '\\/');
if ($path !== '' && !str_ends_with($path, '/')) {
$path = $path . '/';
}
return $path;
}
private function applyPathPrefix(string $path): string
{
$path = ltrim($path, '\\/');
return $this->getPathPrefix() . $path;
}
private static function parseUrl($url): array
{
$result = [];
// Build arrays of values we need to decode before parsing
$entities = [
'%21',
'%2A',
'%27',
'%28',
'%29',
'%3B',
'%3A',
'%40',
'%26',
'%3D',
'%24',
'%2C',
'%2F',
'%3F',
'%23',
'%5B',
'%5D',
'%5C'
];
$replacements = ['!', '*', "'", '(', ')', ';', ':', '@', '&', '=', '$', ',', '/', '?', '#', '[', ']', '/'];
// Create encoded URL with special URL characters decoded so it can be parsed
// All other characters will be encoded
$encodedURL = str_replace($entities, $replacements, urlencode($url));
// Parse the encoded URL
$encodedParts = parse_url($encodedURL);
// Now, decode each value of the resulting array
if ($encodedParts) {
foreach ($encodedParts as $key => $value) {
$result[$key] = urldecode(str_replace($replacements, $entities, $value));
}
}
return $result;
}
private function normalizeHost($domain): string
{
if (0 !== stripos($domain, 'https://') && 0 !== stripos($domain, 'http://')) {
$domain = "http://{$domain}";
}
return rtrim($domain, '/') . '/';
}
private function getUrl(string $path): string
{
$segments = $this->parseUrl($path);
$query = empty($segments['query']) ? '' : '?' . $segments['query'];
return $this->normalizeHost($this->base_url) . ltrim(implode('/', array_map('rawurlencode', explode('/', $segments['path']))), '/') . $query;
}
private function privateDownloadUrl(string $path, int $expires = 3600): string
{
return $this->getAuthMgr()->privateDownloadUrl($this->getUrl($path), $expires);
}
private function getMetadata($path): FileAttributes|array
{
$result = $this->getBucketMgr()->stat($this->bucket, $path);
$result[0]['key'] = $path;
return $this->normalizeFileInfo($result[0]);
}
private function normalizeFileInfo(array $stats): FileAttributes
{
return new FileAttributes(
$stats['key'],
$stats['fsize'] ?? null,
null,
isset($stats['putTime']) ? floor($stats['putTime'] / 10000000) : null,
$stats['mimeType'] ?? null
);
}
public function fileExists(string $path): bool
{
[, $error] = $this->getBucketMgr()->stat($this->bucket, $this->applyPathPrefix($path));
return is_null($error);
}
public function directoryExists(string $path): bool
{
return $this->fileExists($path);
}
public function write(string $path, string $contents, Config $config): void
{
$mime = $config->get('mime', 'application/octet-stream');
/**
* @var Error|null $error
*/
[, $error] = $this->getUploadMgr()->put(
$this->getAuthMgr()->uploadToken($this->bucket),
$this->applyPathPrefix($path),
$contents,
null,
$mime,
$path
);
if ($error) {
throw UnableToWriteFile::atLocation($path, $error->message());
}
}
public function writeStream(string $path, $resource, Config $config): void
{
$data = '';
while (!feof($resource)) {
$data .= fread($resource, 1024);
}
$this->write($path, $data, $config);
}
public function read(string $path): string
{
try {
$result = file_get_contents($this->privateDownloadUrl($path));
} catch (\Exception $th) {
throw UnableToReadFile::fromLocation($path);
}
if (false === $result) {
throw UnableToReadFile::fromLocation($path);
}
return $result;
}
public function readStream(string $path)
{
if (ini_get('allow_url_fopen')) {
if ($result = fopen($this->privateDownloadUrl($path), 'r')) {
return $result;
}
}
throw UnableToReadFile::fromLocation($path);
}
public function delete(string $path): void
{
[, $error] = $this->getBucketMgr()->delete($this->bucket, $this->applyPathPrefix($path));
if (!is_null($error)) {
throw UnableToDeleteFile::atLocation($path);
}
}
public function deleteDirectory(string $path): void
{
$this->delete($path);
}
public function createDirectory(string $path, Config $config): void
{
}
public function setVisibility(string $path, string $visibility): void
{
throw UnableToSetVisibility::atLocation($path);
}
public function visibility(string $path): FileAttributes
{
throw UnableToRetrieveMetadata::visibility($path);
}
public function mimeType(string $path): FileAttributes
{
$meta = $this->getMetadata($path);
if ($meta->mimeType() === null) {
throw UnableToRetrieveMetadata::mimeType($path);
}
return $meta;
}
public function lastModified(string $path): FileAttributes
{
$meta = $this->getMetadata($path);
if ($meta->lastModified() === null) {
throw UnableToRetrieveMetadata::lastModified($path);
}
return $meta;
}
public function fileSize(string $path): FileAttributes
{
$meta = $this->getMetadata($path);
if ($meta->fileSize() === null) {
throw UnableToRetrieveMetadata::fileSize($path);
}
return $meta;
}
public function listContents(string $path, bool $deep): iterable
{
$result = $this->getBucketMgr()->listFiles($this->bucket, $path);
foreach ($result[0]['items'] ?? [] as $files) {
yield $this->normalizeFileInfo($files);
}
}
public function move(string $source, string $destination, Config $config): void
{
[, $error] = $this->getBucketMgr()->rename($this->bucket, $source, $destination);
if (!is_null($error)) {
throw UnableToMoveFile::fromLocationTo($source, $destination);
}
}
public function copy(string $source, string $destination, Config $config): void
{
[, $error] = $this->getBucketMgr()->copy($this->bucket, $source, $this->bucket, $destination);
if (!is_null($error)) {
throw UnableToCopyFile::fromLocationTo($source, $destination);
}
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace filesystem\driver;
use Closure;
use filesystem\adapter\QiniuAdapter;
use League\Flysystem\FilesystemAdapter;
class Qiniu extends \think\filesystem\Driver
{
protected function createAdapter(): FilesystemAdapter
{
return new QiniuAdapter(
$this->config['access_key'],
$this->config['secret_key'],
$this->config['bucket'],
$this->config['base_url'],
$this->config['path_prefix']
);
}
/**
* 保存文件
* @param string $path 路径
* @param \think\File $file 文件
* @param null|string|\Closure $rule 文件名规则
* @param array $options 参数
* @return bool|string
*/
public function putFile(string $path, \think\File $file, $rule = null, array $options = [])
{
if (!empty($this->config['filename_generator']) && $this->config['filename_generator'] instanceof Closure) {
$rule = $this->config['filename_generator']($file, $rule);
}
return $this->putFileAs($path, $file, $file->hashName($rule), $options);
}
public function url(string $path): string
{
if (str_starts_with($path, 'http://') || str_starts_with($path, 'https://')) {
return $path;
}
if (!str_starts_with($path, $this->config['path_prefix'])) {
$path = $this->config['path_prefix'] . '/' . $path;
}
return $this->concatPathToUrl($this->config['base_url'], $path);
}
public function path(string $path): string
{
if (!str_starts_with($path, $this->config['path_prefix'])) {
$path = $this->config['path_prefix'] . '/' . $path;
}
return $path;
}
}

View File

@@ -78,15 +78,18 @@ class OAuthStorage implements IOAuth2GrantCode, IOAuth2RefreshTokens, IOAuth2Gra
public function getClient($client_id): IOAuth2Client public function getClient($client_id): IOAuth2Client
{ {
// 实现获取客户端的逻辑 // 实现获取客户端的逻辑
$ret = OAuthClientModel::clientId($client_id)->find(); $client = OAuthClientModel::clientId($client_id)->find();
if (is_null($ret)) { if (is_null($client)) {
throw new \Exception('客户端不存在'); throw new \Exception('客户端不存在');
} }
if ($ret->enabled != 1) { if ($client->enabled != 1) {
throw new \Exception('客户端已禁用'); throw new \Exception('客户端已禁用');
} }
if (strtotime($client->expired_at) < time()) {
throw new \Exception('client_id 授权已过期');
}
return new OAuth2Client($ret->client_id, $ret->client_secret, [$ret->redirect_uri]); return new OAuth2Client($client->client_id, $client->client_secret, [$client->redirect_uri]);
} }
public function checkClientCredentials(IOAuth2Client $client, $client_secret = null): bool public function checkClientCredentials(IOAuth2Client $client, $client_secret = null): bool
@@ -96,6 +99,9 @@ class OAuthStorage implements IOAuth2GrantCode, IOAuth2RefreshTokens, IOAuth2Gra
if (is_null($client)) { if (is_null($client)) {
return false; return false;
} }
if (strtotime($client->expired_at) < time()) {
throw new \Exception('client_id 授权已过期');
}
return $client->client_secret == hash('sha1', $client->client_id . $client_secret . $this->salt); return $client->client_secret == hash('sha1', $client->client_id . $client_secret . $this->salt);
} }

View File

@@ -1,54 +0,0 @@
<?php
try {
$secret_key = 'Aa-1221';
// check for POST request
if ($_SERVER['REQUEST_METHOD'] != 'POST') {
throw new \Exception('FAILED - not POST - '. $_SERVER['REQUEST_METHOD']);
}
// get content type
$content_type = isset($_SERVER['CONTENT_TYPE']) ? strtolower(trim($_SERVER['CONTENT_TYPE'])) : '';
if ($content_type != 'application/json') {
throw new \Exception('FAILED - not application/json - '. $content_type);
}
// get payload
$payload = trim(file_get_contents("php://input"));
if (empty($payload)) {
throw new \Exception('FAILED - no payload');
}
// get header signature
$header_signature = isset($_SERVER['HTTP_X_GITEA_SIGNATURE']) ? $_SERVER['HTTP_X_GITEA_SIGNATURE'] : '';
if (empty($header_signature)) {
throw new \Exception('FAILED - header signature missing');
}
// calculate payload signature
$payload_signature = hash_hmac('sha256', $payload, $secret_key, false);
// check payload signature against header signature
if ($header_signature !== $payload_signature) {
throw new \Exception('FAILED - payload signature');
}
// convert json to array
$decoded = json_decode($payload, true);
// check for json decode errors
if (json_last_error() !== JSON_ERROR_NONE) {
throw new \Exception('FAILED - json decode - '. json_last_error());
}
exec('git pull origin dev --rebase 2>&1', $result);
var_export($result);exit;
} catch (\Throwable $th) {
var_export($th->getMessage());
}

View File

@@ -1,2 +0,0 @@
*
!.gitignore

View File

@@ -23,7 +23,7 @@
.iotbt1 { .iotbt1 {
font-size: 18px; font-size: 18px;
font-family: Montserrat-Bold, Montserrat; font-family: HarmonyOS-Bold, HarmonyOS;
padding-bottom: 24px; padding-bottom: 24px;
padding-top: 40px; padding-top: 40px;
font-weight: 700; font-weight: 700;
@@ -60,7 +60,7 @@
.iotbtp1 { .iotbtp1 {
font-size: 14px; font-size: 14px;
font-family: Montserrat-Bold, Montserrat; font-family: HarmonyOS-Bold, HarmonyOS;
font-weight: bold; font-weight: bold;
padding-bottom: 11px; padding-bottom: 11px;
word-break: break-word; word-break: break-word;
@@ -69,7 +69,7 @@
.iotbts1 { .iotbts1 {
text-align: center; text-align: center;
font-size: 12px; font-size: 12px;
font-family: Montserrat-Medium, Montserrat; font-family: HarmonyOS-Medium, HarmonyOS;
color: #9e9e9f; color: #9e9e9f;
word-break: break-word; word-break: break-word;
} }
@@ -140,7 +140,7 @@
font-size: 14px; font-size: 14px;
padding-top: 12px; padding-top: 12px;
text-align: center; text-align: center;
font-family: Montserrat-Medium, Montserrat; font-family: HarmonyOS-Medium, HarmonyOS;
overflow-wrap: break-word; overflow-wrap: break-word;
word-break: break-word; word-break: break-word;
} }
@@ -152,7 +152,7 @@
.sfbt1 { .sfbt1 {
text-align: center; text-align: center;
font-size: 18px; font-size: 18px;
font-family: Montserrat-Bold, Montserrat; font-family: HarmonyOS-Bold, HarmonyOS;
font-weight: bold; font-weight: bold;
padding-bottom: 24px; padding-bottom: 24px;
padding-top: 24px; padding-top: 24px;
@@ -183,7 +183,7 @@
.cit { .cit {
width: 100%; width: 100%;
font-size: 14px; font-size: 14px;
font-family: Montserrat-Regular, Montserrat; font-family: HarmonyOS-Regular, HarmonyOS;
color: #000; color: #000;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
@@ -237,7 +237,7 @@
.wcu_s1 { .wcu_s1 {
color: #000; color: #000;
font: 16px; font: 16px;
font-family: Montserrat-Medium, Montserrat; font-family: HarmonyOS-Medium, HarmonyOS;
font-weight: 500; font-weight: 500;
padding: 20px 24px; padding: 20px 24px;
} }

View File

@@ -598,7 +598,7 @@
margin: auto; margin: auto;
font-size: 1rem; font-size: 1rem;
color: #707070; color: #707070;
font-family: "Montserrat-Medium"; font-family: "HarmonyOS-Medium";
} }
.oricoCnLc .gallery-thumbs .swiper-slide-thumb-active { .oricoCnLc .gallery-thumbs .swiper-slide-thumb-active {
@@ -632,14 +632,14 @@
.oricoCnLc .info h5 { .oricoCnLc .info h5 {
font-size: #000; font-size: #000;
font-size: 1rem; font-size: 1rem;
font-family: "Montserrat-Bold"; font-family: "HarmonyOS-Bold";
padding: 2rem 2rem 0 1rem; padding: 2rem 2rem 0 1rem;
margin: 0; margin: 0;
} }
.oricoCnLc .info p { .oricoCnLc .info p {
color: #707070; color: #707070;
font-family: "Montserrat-Regular"; font-family: "HarmonyOS-Regular";
padding: 1rem; padding: 1rem;
} }
@@ -651,7 +651,7 @@
.oricoCnLc .m_ach-b .title { .oricoCnLc .m_ach-b .title {
font-size: 1.125rem; font-size: 1.125rem;
font-family: "Montserrat-Bold"; font-family: "HarmonyOS-Bold";
width: 98%; width: 98%;
margin: auto; margin: auto;
position: relative; position: relative;
@@ -660,7 +660,7 @@
.oricoCnLc .m_ach-b .chtitle { .oricoCnLc .m_ach-b .chtitle {
font-size: 1.125rem; font-size: 1.125rem;
font-family: "Montserrat-Bold"; font-family: "HarmonyOS-Bold";
width: 98%; width: 98%;
margin: auto; margin: auto;
position: relative; position: relative;
@@ -677,7 +677,7 @@
.oricoCnLc .m_ch-title { .oricoCnLc .m_ch-title {
font-size: 1rem; font-size: 1rem;
font-family: "Montserrat-Bold"; font-family: "HarmonyOS-Bold";
width: 98%; width: 98%;
margin: 1rem auto 0; margin: 1rem auto 0;
padding: 1rem 0 0; padding: 1rem 0 0;
@@ -690,7 +690,7 @@
font-size: 0.875; font-size: 0.875;
line-height: 1.5rem; line-height: 1.5rem;
color: #707070; color: #707070;
font-family: "Montserrat-Regular"; font-family: "HarmonyOS-Regular";
width: 98%; width: 98%;
margin: 1rem auto; margin: 1rem auto;
padding: 0; padding: 0;

View File

@@ -156,7 +156,7 @@
border-radius: 8px; border-radius: 8px;
height: 48px; height: 48px;
box-shadow: none; box-shadow: none;
font-family: Montserrat-Regular, Montserrat; font-family: HarmonyOS-Regular, HarmonyOS;
} }
.oricoEGapp-articledetail .content .share_box .comment .comment_form>div input { .oricoEGapp-articledetail .content .share_box .comment .comment_form>div input {

Some files were not shown because too many files have changed in this diff Show More