1006 Commits

Author SHA1 Message Date
ouzhiqiang
9d8e22d270 修改分类 2025-09-19 15:14:55 +08:00
ouzhiqiang
f154f3ddf6 修改分类 2025-09-19 14:59:45 +08:00
ouzhiqiang
2073a27ef7 修改分类 2025-09-19 14:48:10 +08:00
ouzhiqiang
e301fc7e94 修改分类 2025-09-19 14:47:05 +08:00
ouzhiqiang
f9251d944e 修改分类 2025-09-19 14:36:08 +08:00
ouzhiqiang
1e6187801d Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-09-19 14:32:27 +08:00
ouzhiqiang
346dcebba3 修改分类 2025-09-19 14:31:56 +08:00
ab7b23e5d1 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-09-19 14:13:56 +08:00
325221bd67 1 2025-09-19 14:13:53 +08:00
ouzhiqiang
721e91dc31 修改分类 2025-09-19 11:37:41 +08:00
ouzhiqiang
12e51c6f46 修改分类 2025-09-19 11:22:25 +08:00
ouzhiqiang
8ba9006173 修改分类 2025-09-19 11:17:49 +08:00
ouzhiqiang
a7d413871b 修改分类 2025-09-19 11:09:29 +08:00
ouzhiqiang
ce6a882c17 修改分类 2025-09-19 10:41:41 +08:00
ouzhiqiang
7e7cfeacbc 修改分类 2025-09-19 10:36:26 +08:00
ouzhiqiang
e1961d2a83 修改分类 2025-09-19 10:29:25 +08:00
ouzhiqiang
d313617e2b 修改分类 2025-09-19 10:23:02 +08:00
ouzhiqiang
f0c82c848f 修改分类 2025-09-19 10:04:47 +08:00
ouzhiqiang
f606dc64a5 修改分类 2025-09-19 09:53:40 +08:00
ouzhiqiang
39dc3c4e1a 修改分类 2025-09-19 09:51:29 +08:00
ouzhiqiang
52b265f36e Merge branch 'ozq-dev' into dev 2025-09-18 17:50:21 +08:00
ouzhiqiang
5f9fb74696 修改分类 2025-09-18 17:50:05 +08:00
ouzhiqiang
b251c78b2f Merge branch 'ozq-dev' into dev 2025-09-18 17:47:43 +08:00
ouzhiqiang
2e1c83d46d 修改分类 2025-09-18 17:47:22 +08:00
ouzhiqiang
0265cb0629 Merge branch 'ozq-dev' into dev 2025-09-18 17:23:25 +08:00
ouzhiqiang
68d66b2925 修改分类 2025-09-18 17:23:07 +08:00
ouzhiqiang
601403bf3e Merge branch 'ozq-dev' into dev 2025-09-18 17:16:43 +08:00
ouzhiqiang
bbe2197b38 修改分类 2025-09-18 17:16:24 +08:00
ouzhiqiang
ee955a3674 Merge branch 'ozq-dev' into dev 2025-09-18 17:13:22 +08:00
ouzhiqiang
e45df84312 修改分类 2025-09-18 17:13:04 +08:00
ouzhiqiang
4e0891da97 Merge branch 'ozq-dev' into dev 2025-09-18 17:04:44 +08:00
ouzhiqiang
f9b895732e 修改分类 2025-09-18 17:04:29 +08:00
ouzhiqiang
e72ecd55f4 Merge branch 'ozq-dev' into dev 2025-09-18 17:01:25 +08:00
ouzhiqiang
558b418d18 修改分类 2025-09-18 17:01:08 +08:00
ouzhiqiang
78cbc5de6e Merge branch 'ozq-dev' into dev 2025-09-18 16:56:01 +08:00
ouzhiqiang
f7b610df72 修改分类 2025-09-18 16:55:47 +08:00
ouzhiqiang
b1522a365b Merge branch 'ozq-dev' into dev 2025-09-18 16:53:26 +08:00
ouzhiqiang
48906ec9cd 修改分类 2025-09-18 16:53:06 +08:00
ouzhiqiang
b57cb8dfd6 Merge branch 'ozq-dev' into dev 2025-09-18 16:49:16 +08:00
ouzhiqiang
7d70c55557 修改分类 2025-09-18 16:48:45 +08:00
ouzhiqiang
4fdda8435d Merge branch 'ozq-dev' into dev 2025-09-18 16:11:52 +08:00
ouzhiqiang
7a96809bfe 修改分类 2025-09-18 16:11:36 +08:00
ouzhiqiang
62b3e2798f Merge branch 'ozq-dev' into dev 2025-09-18 16:08:29 +08:00
ouzhiqiang
bfeac736ea 修改分类 2025-09-18 16:08:05 +08:00
ouzhiqiang
74ea50b7e4 Merge branch 'ozq-dev' into dev 2025-09-18 16:01:20 +08:00
ouzhiqiang
8a617c06e9 修改分类 2025-09-18 16:00:49 +08:00
ouzhiqiang
efa8acaf12 Merge branch 'ozq-dev' into dev 2025-09-18 15:39:28 +08:00
ouzhiqiang
d3ff726e90 修改分类 2025-09-18 15:38:59 +08:00
ouzhiqiang
7880c18e85 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-09-18 15:26:19 +08:00
ouzhiqiang
e80ba5ac7d 修改分类 2025-09-18 15:25:29 +08:00
20b5fd901b 1 2025-09-18 11:59:32 +08:00
e38c00f59c 1 2025-09-18 11:57:52 +08:00
a18706f7d4 分类补充 2025-09-18 11:56:48 +08:00
4ff2ee4a76 移动端帮助中心三级菜单 2025-09-18 11:52:52 +08:00
2ca708fd4a 帮助中心文档三级菜单 2025-09-18 11:21:50 +08:00
29761f551d Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-09-18 11:15:12 +08:00
fdb75e4888 feat: 添加open api client管理命令 2025-09-17 17:48:27 +08:00
5fde7159e0 Merge branch 'dev' 2025-08-26 14:53:55 +08:00
6068efa03f refactor: 去掉多余目录 2025-08-26 14:53:38 +08:00
738b293ea2 fix: 修复“检测文件地址并根据情况转换为文件系统地址”url为null情况报错问题 2025-08-26 14:53:17 +08:00
a1be105c31 Merge branch 'dev' 2025-08-08 09:18:33 +08:00
03374856e4 fix: 修复产品目录分类树数据问题 2025-08-08 09:18:13 +08:00
f07741ff19 Merge branch 'dev' 2025-08-07 17:08:12 +08:00
c64450d74c fix: admapi - 产品目录同步分类更新时bug修复 2025-08-07 17:07:52 +08:00
e462b38ff9 Merge branch 'dev' 2025-08-07 11:38:17 +08:00
b96021d21d Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-08-07 11:37:49 +08:00
12d6fdc3a6 style:底部样式修改 2025-08-07 11:36:56 +08:00
cd3f651a2a Merge branch 'dev' 2025-08-07 10:20:10 +08:00
1e4b416cac fix: mobile - 底部“联系我们”取值问题 2025-08-07 10:19:45 +08:00
e38446f3fd Merge branch 'dev' 2025-08-07 10:18:06 +08:00
06b9d42ae4 fix: mobile - 底部“联系我们”取值问题 2025-08-07 10:17:24 +08:00
99d78069d5 Merge branch 'dev' 2025-08-07 10:12:04 +08:00
342a3754aa fix: mobile - 底部“联系我们”取值问题 2025-08-07 10:03:50 +08:00
51e9c8ced1 Merge branch 'dev' 2025-08-06 15:35:50 +08:00
b13d481e1e perf openapi 图片地址拼接 2025-08-06 15:35:35 +08:00
da8f204167 Merge branch 'dev' 2025-08-06 12:45:49 +08:00
c1979da1af fix: 后台 - 修改各导出图片等文件地址拼接问题 2025-08-06 11:56:53 +08:00
1802f57906 fix: 后台 产品导出数据问题 2025-08-06 11:40:44 +08:00
3fa3b8fb63 fix: 后台 产品导出数据问题 2025-08-06 11:40:09 +08:00
fb2b1455bc fix: mobile下视频分类地址错误 2025-07-31 10:55:26 +08:00
cc497b2ebc fix: 获取文件系统磁盘未判断“//”情况 2025-07-31 09:03:49 +08:00
2b450a2e9c fix: 首页“场景介绍”无法点击 2025-07-31 09:03:48 +08:00
e266e89a97 1 2025-07-30 17:08:22 +08:00
2a94a7ecec 1 2025-07-30 17:07:37 +08:00
9137335ce3 1 2025-07-30 16:48:54 +08:00
b8946f223a 1 2025-07-30 16:47:15 +08:00
3a8440c2b9 1 2025-07-30 16:44:32 +08:00
c2bba7fb56 1 2025-07-30 16:40:36 +08:00
052570fefa Merge branch 'dev' 2025-07-30 16:33:15 +08:00
09b1f9f14c style:产品详情 2025-07-30 16:13:37 +08:00
37825f88ba .ql-editor 2025-07-30 15:43:42 +08:00
1c7434b591 .ql-editor 2025-07-30 15:42:52 +08:00
585da730ea style:产品详情样式 2025-07-30 15:35:16 +08:00
2ecc51d8a9 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-07-30 15:25:01 +08:00
ad7148ccfc style:详情样式 2025-07-30 15:24:58 +08:00
1e00db6c97 fix: 字段长度限制 2025-07-30 11:13:17 +08:00
5ed692a672 fix: 字段长度限制 2025-07-30 11:12:13 +08:00
5f7156470e Merge branch 'dev' 2025-07-29 17:52:13 +08:00
3aafcd3f3b refactor: 修改后台banner的“链接地址”长度限制为510 2025-07-29 17:51:56 +08:00
9b339d6364 Merge branch 'dev' 2025-07-29 16:48:16 +08:00
09f9bfd301 Merge branch 'dev' 2025-07-29 16:47:31 +08:00
910a07ea47 1 2025-07-29 14:16:51 +08:00
c27d529b82 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-07-29 14:14:29 +08:00
b6724c0361 视频宽度限制 2025-07-29 14:14:27 +08:00
d4fa15f671 文章视频限制 2025-07-29 14:13:34 +08:00
60229ae058 fix: migrations/seeds 2025-07-29 14:08:30 +08:00
6cf27d8b9c fix: 产品详情-分类路径bug 2025-07-29 12:00:22 +08:00
6ba7181e3f 文章详情样式调整 2025-07-28 14:50:26 +08:00
4334c2db66 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-07-28 14:34:41 +08:00
d7e93567d9 style:宽度调整 2025-07-28 14:25:01 +08:00
e19ba6a315 chore: composer.json 2025-07-28 11:13:56 +08:00
a8e7ad2430 perf: migrations/seeds 2025-07-28 10:33:44 +08:00
55c8762255 perf: 七牛云存储 2025-07-28 10:16:03 +08:00
43a4f5f706 refactor: .gitignore 2025-07-25 18:00:14 +08:00
c6cff2e97d refactor: 七牛云上传 2025-07-25 17:57:31 +08:00
5d53c26c5c 文章详情样式调整 2025-07-25 13:47:37 +08:00
857bb4ad21 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-07-24 10:00:18 +08:00
b3dfb8655b 1 2025-07-24 10:00:14 +08:00
10c1f86708 feat: 七牛云上传开发 2025-07-23 17:55:34 +08:00
62a67bf7a0 perf: 自定义语言检测中间件 2025-07-23 17:55:34 +08:00
4dc4932ac7 补充 2025-07-23 17:25:57 +08:00
b99c07ef4a .ql-editor 关于tab切换的样式同步 2025-07-23 17:20:11 +08:00
f8dc645048 样式修改 2025-07-22 16:48:22 +08:00
051b4ed7e7 Merge branch 'dev' 2025-07-21 17:32:36 +08:00
142e29f131 fix: pc 产品,附件,视频搜索去除首尾空格 2025-07-21 17:31:17 +08:00
7f58f1ff83 fix: pc 视频搜索 2025-07-21 16:55:06 +08:00
5650ccfb87 fix: pc 视频搜索 2025-07-21 16:54:43 +08:00
439c9073b8 Merge branch 'dev' 2025-07-19 10:48:27 +08:00
f78f164326 fix: 图片上传命名问题 2025-07-19 10:48:07 +08:00
5fb2abe818 Merge branch 'master' of https://gitea.f2b211.com/jsasg/orico-official-website 2025-07-18 15:19:02 +08:00
1f3b646495 1 2025-07-18 15:16:09 +08:00
a1ea5c6752 标题提取空过滤 样式修改 2025-07-18 15:15:13 +08:00
beb86140a6 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-07-18 14:42:10 +08:00
0d442fb63a Merge branch 'dev' 2025-07-18 14:40:20 +08:00
af139f6bf4 fix: nas 文章详情页分类排序问题 2025-07-18 14:40:02 +08:00
1d79ea5186 Merge branch 'master' of https://gitea.f2b211.com/jsasg/orico-official-website 2025-07-18 14:39:33 +08:00
de02e59b0e 1 2025-07-18 14:39:30 +08:00
f4fa5afcfa Merge branch 'dev' 2025-07-18 13:36:33 +08:00
2c40c95794 fix: 横幅分类不可小于0 2025-07-18 13:33:58 +08:00
70534f2a97 fix: nas帮助中心文件分类未排序 2025-07-18 12:36:20 +08:00
4515939fcf Achievement 2025-07-18 10:29:26 +08:00
b16971e6f1 nas_help_banner 格式修改 2025-07-18 10:16:58 +08:00
3840206e7c indeximg1 ,indeximg2压缩webp 2025-07-18 09:44:41 +08:00
ef1f786417 编辑器样式修改 2025-07-17 17:45:34 +08:00
745671bd33 1 2025-07-17 17:04:16 +08:00
a13ea053e3 1 2025-07-17 17:02:29 +08:00
0725cd779f 1 2025-07-17 17:01:17 +08:00
47caddad60 Merge branch 'master' of https://gitea.f2b211.com/jsasg/orico-official-website 2025-07-17 16:49:08 +08:00
5db4263f67 1 2025-07-17 16:49:03 +08:00
9887bd1045 Merge branch 'dev' 2025-07-17 16:43:24 +08:00
768e1ed786 perf: 优化openapi接口频率限制 2025-07-17 16:43:02 +08:00
f211f58068 1 2025-07-17 16:40:23 +08:00
e7e7386054 1 2025-07-17 16:35:38 +08:00
9c7f2d394b 1 2025-07-17 16:31:42 +08:00
a1f1716f5c 1还原 2025-07-17 16:27:14 +08:00
250f78593a 还原 2025-07-17 16:25:01 +08:00
2c0b8161a5 1 2025-07-17 16:22:28 +08:00
83fa83e00d 1 2025-07-17 16:18:57 +08:00
176b25b631 1 2025-07-17 16:11:01 +08:00
4b2566b762 ·1 2025-07-17 16:02:31 +08:00
a25f87de9f 1 2025-07-17 15:40:29 +08:00
ec6fa7fe77 1 2025-07-17 15:39:17 +08:00
0ba299ee05 1 2025-07-17 15:32:22 +08:00
d06002d95c 1 2025-07-17 15:30:43 +08:00
1758fb8995 格式化 2025-07-17 15:22:56 +08:00
52259d573c Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-07-17 15:20:55 +08:00
a680391d86 1 2025-07-17 15:19:56 +08:00
0e0ed64b38 1 2025-07-17 15:18:38 +08:00
5221b43c01 1 2025-07-17 15:13:01 +08:00
a373ee7163 1 2025-07-17 15:08:52 +08:00
07692a2a29 1 2025-07-17 12:00:46 +08:00
3c7bdd8ac9 Merge branch 'master' of https://gitea.f2b211.com/jsasg/orico-official-website 2025-07-17 11:49:33 +08:00
db3b704d89 1 2025-07-17 11:49:31 +08:00
6e3e0d59db fix: database/seeds 2025-07-17 11:44:00 +08:00
f8884aa736 1 2025-07-17 11:42:38 +08:00
8e62d95864 1 2025-07-17 11:30:52 +08:00
7bf88fd578 英文单词不换行 2025-07-17 10:48:37 +08:00
a4330c1216 1 2025-07-17 10:36:36 +08:00
6c80dbabeb 图片拉伸 2025-07-17 10:24:00 +08:00
e0009fb8fa .header-PC #header .nav3 .choesCountry 2025-07-17 10:19:09 +08:00
66da36a907 fix: database/seeds 2025-07-17 09:58:09 +08:00
702e874e08 1 2025-07-17 09:49:05 +08:00
7e486e463c Merge branch 'master' of https://gitea.f2b211.com/jsasg/orico-official-website 2025-07-17 09:45:12 +08:00
df482bbcdd .ql-editor>* 2025-07-17 09:45:07 +08:00
7651b056b6 feat: 后台管理 产品列表添加按添加时间倒序 2025-07-16 18:12:11 +08:00
877c8835d9 img {display: block;}注释 2025-07-16 17:44:55 +08:00
67865d64cd .narshelpCenterdetail-app .nhlpapp-search 2025-07-16 16:55:44 +08:00
699e55ffa1 Merge branch 'master' of https://gitea.f2b211.com/jsasg/orico-official-website 2025-07-16 16:53:03 +08:00
4ca9a84d14 移动端nas文章底部操作 2025-07-16 16:53:01 +08:00
ac5d0143ac fix: pc nas专题logo中文下要点击,英文下不要 2025-07-16 15:39:35 +08:00
6434d031ca fix: 产品分类排序 2025-07-16 15:32:38 +08:00
8f2dbf8798 fix: pc 产品详情添加型号 2025-07-16 15:24:22 +08:00
7a0c3af1e9 .ql-container 2025-07-16 14:51:07 +08:00
7879f7914b 文章样式 2025-07-16 14:45:33 +08:00
99a69bad5f Merge branch 'master' of https://gitea.f2b211.com/jsasg/orico-official-website 2025-07-16 14:14:05 +08:00
38783d80a0 1 2025-07-16 14:13:13 +08:00
b37ed217cd 1 2025-07-16 14:11:52 +08:00
dd687120cc 还原footer 2025-07-16 14:07:43 +08:00
004a007148 1 2025-07-16 14:02:32 +08:00
5018a59045 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-07-16 13:39:34 +08:00
8fa1399902 Merge branch 'dev' 2025-07-16 13:39:07 +08:00
e781887d16 fix: 产品目录同步产品默认在售 2025-07-16 13:38:54 +08:00
55d72124a0 Merge branch 'dev' 2025-07-16 11:52:05 +08:00
3eda90f2d1 fix: 后台管理 产品参数问题 2025-07-16 11:51:48 +08:00
6b815ea414 Merge branch 'dev' 2025-07-16 10:31:39 +08:00
df8001d43e fix: 产品搜索无数据语言问题 2025-07-16 10:31:14 +08:00
7f8ad55c97 pc编辑文本样式 2025-07-16 10:19:42 +08:00
14f0f1609e 添加编辑器样式 2025-07-16 10:17:58 +08:00
6c176a1039 1 2025-07-16 10:12:28 +08:00
bffe804a7b 1 2025-07-16 10:10:36 +08:00
a9beb0dda6 1 2025-07-16 10:04:54 +08:00
3c72fa125f 1 2025-07-16 10:02:47 +08:00
81df05e0f9 1 2025-07-16 09:56:05 +08:00
e0fd77837d 1 2025-07-16 09:54:28 +08:00
7344691613 Merge branch 'master' of https://gitea.f2b211.com/jsasg/orico-official-website 2025-07-16 09:51:31 +08:00
fe697df167 1 2025-07-16 09:51:26 +08:00
b743688d99 Merge branch 'dev' 2025-07-15 15:00:51 +08:00
51b6841a3a fix: openapi token有效期配置 2025-07-15 15:00:38 +08:00
768ed5b0fb fix: openapi token有效期配置 2025-07-15 14:59:25 +08:00
0c2f96fd49 Merge branch 'dev' 2025-07-15 14:49:47 +08:00
40c8385776 perf: openapi token有效期配置 2025-07-15 14:49:03 +08:00
80c6647a0c Merge branch 'dev' 2025-07-15 14:45:54 +08:00
187a0affcc perf: openapi token有效期配置 2025-07-15 14:45:16 +08:00
a35288dd0b Merge branch 'dev' 2025-07-15 14:21:30 +08:00
b36627ec25 perf: openapi client授权过期 2025-07-15 14:21:16 +08:00
eb2a98e7fe fix: admapi 角色菜单权力model 2025-07-15 14:21:04 +08:00
5c79e33ce1 perf: openapi client授权过期 2025-07-15 14:20:36 +08:00
341e1f54fb fix: admapi 角色菜单权力model 2025-07-15 13:50:50 +08:00
78925fbbab Merge branch 'dev' 2025-07-15 10:48:30 +08:00
d9e056972c perf: .gitignore 2025-07-15 10:46:17 +08:00
b09f7d1e6f fix: mobile subcategory.html 2025-07-15 09:54:58 +08:00
ad0f7f4b87 fix: mobile subcategory.html 2025-07-15 09:54:38 +08:00
61e4ac1fb3 smallImgDown相对位置修改 2025-07-15 09:36:55 +08:00
728730433b bsImg 高度自适应 2025-07-15 09:32:07 +08:00
3a50a23cfa Merge branch 'master' of https://gitea.f2b211.com/jsasg/orico-official-website 2025-07-15 09:30:05 +08:00
1dc03040f1 .bttj 修改 2025-07-15 09:30:02 +08:00
900d73d389 fix: mobile subcategory.html 2025-07-15 09:25:25 +08:00
5b69987d6e perf: mobile 产品分类与搜索页改为缩略图 2025-07-15 09:22:21 +08:00
512c07b5a8 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-07-15 09:19:14 +08:00
885b86ded9 顶部底部自适应 2025-07-15 09:18:11 +08:00
c04b7cae38 fix: mobile view问题 2025-07-15 09:16:01 +08:00
3ff0137e4b Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-07-14 17:18:35 +08:00
15c18d5fc1 1 2025-07-14 17:06:45 +08:00
91b083f0e5 去掉新品的标题和更多 2025-07-14 17:05:44 +08:00
a3dccb8b19 sceneitem1 2025-07-14 16:41:58 +08:00
974f561c5a 1 2025-07-14 16:39:59 +08:00
8fd52854cc 1 2025-07-14 16:34:11 +08:00
b9b865ece0 1 2025-07-14 16:26:35 +08:00
acc39f4580 1 2025-07-14 16:25:49 +08:00
e82c201a2b 1 2025-07-14 16:24:22 +08:00
6927bdbc42 1 2025-07-14 16:23:25 +08:00
3db7e42e71 1 2025-07-14 16:09:53 +08:00
12bd511d0b 1 2025-07-14 16:09:02 +08:00
4a52be183c 1 2025-07-14 16:08:13 +08:00
58324ebb33 1 2025-07-14 16:07:06 +08:00
8ccca36e44 Merge branch 'master' of https://gitea.f2b211.com/jsasg/orico-official-website 2025-07-14 16:02:37 +08:00
d6c7a0f11e 样式修改6 2025-07-14 16:02:35 +08:00
858468b72b fix: 底部备案信息 2025-07-14 15:52:13 +08:00
3eadc5c3eb fix: 底部备案信息 2025-07-14 15:51:22 +08:00
fe324bc62f fix: 底部备案信息 2025-07-14 15:50:13 +08:00
2329b1bbe6 pref: 数据同步 2025-07-14 15:50:13 +08:00
134c75cc4b fix: 底部备案信息 2025-07-14 15:49:50 +08:00
3b737b0bd0 1 2025-07-14 15:46:47 +08:00
2da153e935 样式修改4 2025-07-14 15:46:18 +08:00
271f22ea18 样式修改2 2025-07-14 15:35:57 +08:00
4a56e7e980 Merge branch 'master' of https://gitea.f2b211.com/jsasg/orico-official-website 2025-07-14 15:13:12 +08:00
b3475a7d06 样式修改 2025-07-14 15:13:09 +08:00
988bdde6f1 pref: 数据同步 2025-07-14 14:30:59 +08:00
456209121e Merge branch 'dev' 2025-07-14 14:06:16 +08:00
8186a424de fix: 顶部搜索产品图片显示缩略图 2025-07-14 14:06:04 +08:00
ebc8c6431a Merge branch 'dev' 2025-07-14 13:44:06 +08:00
7cbb6adb5d fix: 视频描述字段长度校验 2025-07-14 13:43:52 +08:00
4b8963161f perf: 数据同步 2025-07-14 13:42:50 +08:00
eeb79e8c56 Merge branch 'dev' 2025-07-14 11:47:51 +08:00
2a503cbf68 fix: 产品详情购买链接排序 2025-07-14 11:47:41 +08:00
aed2ce4655 fix: pc 顶部“店铺”新标签打开 2025-07-14 11:27:59 +08:00
70b524ce04 Merge branch 'master' of https://gitea.f2b211.com/jsasg/orico-official-website 2025-07-14 11:27:38 +08:00
db82564148 style 2025-07-14 11:27:34 +08:00
386cd613ee fix: pc 顶部“店铺”新标签打开 2025-07-14 11:25:02 +08:00
dc8a3dc5da fix: pc 底部导航新标签页打开无效 2025-07-14 11:04:34 +08:00
4ec2985468 perf: 数据同步 2025-07-14 11:04:34 +08:00
796231a50e fix: pc 底部导航新标签页打开无效 2025-07-14 11:01:14 +08:00
6b082f2de9 perf: 数据同步 2025-07-14 11:00:43 +08:00
jsasg
83287098ee Merge branch 'dev' 2025-07-11 22:39:16 +08:00
jsasg
b67959163b fix: 图片上传 2025-07-11 22:34:32 +08:00
ba04d6d220 fix: 底部 2025-07-11 20:15:38 +08:00
457c06948c fix: 文章验证 2025-07-11 20:15:20 +08:00
c0b6ddf11b Merge branch 'dev' 2025-07-11 18:07:42 +08:00
a8960c3c81 fix: 底部 2025-07-11 18:07:25 +08:00
d4be3ff937 fix: 文章验证 2025-07-11 18:07:25 +08:00
751d4ce12d style:样式修改3 2025-07-11 17:15:44 +08:00
9c6e26ce05 style:样式调整 2025-07-11 16:52:09 +08:00
25a55ba9f5 style:样式修改 2025-07-11 16:32:58 +08:00
b8b8d8d58f fix:如果底部没有内容就不要显示li这个标签 2025-07-11 16:22:34 +08:00
31d747a0b0 style:样式修改 2025-07-11 16:13:49 +08:00
0025b0a5bb ljm master分支 2025-07-11 15:57:27 +08:00
002c4055eb Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-07-11 15:44:59 +08:00
738986d715 fix:pc导航修改 2025-07-11 15:44:56 +08:00
673f4d101a fix: mobile下没有jquery 2025-07-11 15:22:01 +08:00
553c010dc2 fix: 上传 2025-07-11 11:47:05 +08:00
c18ba2dbc2 fix: 导航未过滤已禁用 2025-07-11 10:32:05 +08:00
13eec27ea9 add:本地添加jQuery和swiper文件,防止cdn失效 2025-07-10 17:38:17 +08:00
84e41958c9 perf: mysql递归查询兼容处理 2025-07-10 17:30:01 +08:00
7045bd7b7d perf: 修改analytics 2025-07-09 17:30:02 +08:00
cbb0699fae pref: think文件 2025-07-09 16:34:27 +08:00
e1b4266513 perf: redis env配置 2025-07-09 16:15:58 +08:00
c109748c79 perf: env 2025-07-09 13:58:59 +08:00
87b4917120 fix: 同步产品目录产品分类及产品 2025-07-09 11:32:59 +08:00
48e3ec7c2c · 2025-07-09 10:25:02 +08:00
92679d3ebc 样式修改 2025-07-09 10:21:48 +08:00
dc94ec5f5f Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-07-08 10:54:46 +08:00
c67ae635d8 style:样式修改 2025-07-08 10:54:43 +08:00
4233efe422 fix: 后台图片上传路径bug 2025-07-07 18:01:12 +08:00
28e3e08f86 fix: 后台图片上传路径bug 2025-07-07 17:50:52 +08:00
be7f4edff5 pref: 图片大小调整 2025-07-07 16:39:02 +08:00
3d777043e8 pref: 图片大小调整 2025-07-07 16:22:51 +08:00
9aafb6321b 1 2025-07-07 15:27:22 +08:00
942a5706ca 1 2025-07-07 15:23:30 +08:00
73b8562d71 style:单词换行 2025-07-07 15:21:44 +08:00
c3733e24a7 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-07-07 15:15:32 +08:00
eeaa27d0b4 style:品类换行 2025-07-07 15:15:30 +08:00
a5e20a3840 feat: migration/seed 2025-07-05 18:01:22 +08:00
d809690724 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-07-05 17:35:30 +08:00
447885529e style:样式调整 2025-07-05 17:35:27 +08:00
89e46189bd feat: 后台 图片上传新增webp格式转换处理 2025-07-05 15:19:46 +08:00
29ea63199c fix: openapi 文章详情取消author,source字段输出 2025-07-05 14:41:32 +08:00
3ef1fc634a fix: openapi 文章分类取消short_name, desc字段输出 2025-07-05 14:02:17 +08:00
4d988c316b fix: openapi 取消产品分类icon,desc字段输出 2025-07-05 14:01:51 +08:00
9182ed29d9 fix: openapi取消产品详情sku->sort字段输出 2025-07-05 13:47:57 +08:00
e1388c63b7 fix: openapi 产品接口取消desc输出 2025-07-05 11:49:55 +08:00
a0d53c6e0e 返回顶部 只要页面过长就显示 2025-07-05 11:12:52 +08:00
6603af60ef id修改为类,间距调整 2025-07-05 10:20:59 +08:00
336f27bc23 Merge branch 'cssupdate' into dev 2025-07-05 09:53:30 +08:00
65ccd97e32 产品详情切换问题修改 2025-07-05 09:51:50 +08:00
256975b592 doc: 注释 2025-07-05 09:18:24 +08:00
90508c8c37 fix: openapi 产品详情不输出links->platform问题 2025-07-04 17:42:29 +08:00
f7ced51c4d Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-07-04 17:17:41 +08:00
aee2f81601 style:样式修改 2025-07-04 17:17:38 +08:00
cc85925f37 fix: pc nas专题页 导航选中状态 2025-07-04 16:40:03 +08:00
8897a3bbda fix: openapi 产品列表 2025-07-04 10:34:23 +08:00
9292e40053 fix: mobile 产品详情页图册中视频放第一个sku下最后一个 2025-07-03 14:01:34 +08:00
57058dd150 fix: pc 产品详情页图册中视频放第一个sku下最后一个 2025-07-03 13:56:57 +08:00
8237fb50ce fix: mobile nas专题顶部导航 2025-07-02 15:30:25 +08:00
6e2800f361 fix: pc 文章提交提示语言未区分语言 2025-07-02 15:06:28 +08:00
8bb323f3f5 pref: pc 产品详情表单提交 2025-07-02 14:44:35 +08:00
c85cb4af54 fix: pc 产品搜索结果分布 2025-07-02 14:19:53 +08:00
5d4baf4383 perf: 关键词高亮 2025-07-02 10:59:01 +08:00
dbf0a65ee7 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-07-02 10:57:38 +08:00
46c72f1146 swiper uc兼容 2025-07-02 10:57:35 +08:00
2a1c9f7825 feat: 从产品目录同步产品相关数据 2025-07-02 10:44:55 +08:00
878885377c fix: pc nas专题页 联系我们第一项无js效果 2025-07-02 10:44:55 +08:00
8841bc9819 style:样式问题修改8 2025-07-02 10:44:29 +08:00
bde8ef0bb3 style:样式问题修改7 2025-07-02 10:44:29 +08:00
8e78f95002 feat: 同步数据接收 2025-07-01 17:15:40 +08:00
77706e5bcb fix: 产品搜索关键词高亮 2025-07-01 17:14:04 +08:00
1100e347bd style:样式问题修改8 2025-07-01 17:03:46 +08:00
de44890014 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-07-01 16:43:00 +08:00
f970f22539 style:样式问题修改7 2025-07-01 16:42:57 +08:00
90c1be254b fix: mobile nas专题header头 2025-07-01 16:04:35 +08:00
87c3180229 fix: nas专题 head_languages变量报错 2025-07-01 16:04:35 +08:00
74942de6db style:样式问题修改6 2025-07-01 15:48:15 +08:00
04fb5cd3b1 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-07-01 15:42:07 +08:00
52a1756d5b style:样式问题修改6 2025-07-01 15:42:04 +08:00
ecd2602d35 fix: nas专题 移动端去掉顶部搜索与语言切换 2025-07-01 15:26:53 +08:00
0de7fce26d Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-07-01 15:24:21 +08:00
46b85b090d style:样式问题修改5 2025-07-01 15:24:18 +08:00
1074b99bb8 fix: nas专题 移动端去掉顶部搜索与语言切换 2025-07-01 15:20:44 +08:00
11091d46e9 style:样式问题修改4 2025-07-01 15:18:24 +08:00
4836f113fa 1 2025-07-01 15:14:53 +08:00
9b7679af30 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-07-01 15:09:10 +08:00
cc8258c764 1 2025-07-01 15:09:07 +08:00
f8734366f1 fix: 后台banner分类接口 2025-07-01 15:08:22 +08:00
b625a23856 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-07-01 15:03:28 +08:00
6d2135b862 style:样式问题修改3 2025-07-01 15:03:25 +08:00
927c4c65a9 fix: mobile 产品详情页相关产品 2025-07-01 14:08:21 +08:00
1295706704 fix: pc/mobile 联系我们页面文字颜色 2025-07-01 13:53:42 +08:00
50dade9517 style:样式问题修改2 2025-07-01 11:41:54 +08:00
26af0577ad Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-07-01 11:28:00 +08:00
ec20bd3741 style:样式问题修改 2025-07-01 11:27:56 +08:00
c13fcc2cb9 feat: analytics代码添加 2025-07-01 09:47:21 +08:00
74e7e145af fix: pc 视频搜索 2025-06-30 16:48:35 +08:00
b68c56cfdc refactor: 修改各表user_agent字段长度 2025-06-30 15:18:54 +08:00
e60b2a02a1 fix: pc/mobile banner的extra_image取值 2025-06-30 10:57:06 +08:00
9a9b714aad refactor: 后台横幅添加extra_image字段 2025-06-30 09:24:44 +08:00
9ab935e1ff Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-06-27 18:04:03 +08:00
c5e28d153d 样式修改 2025-06-27 18:04:00 +08:00
023ebf775d fix: 产品分类颜色 2025-06-27 17:30:01 +08:00
f086dd0239 fix: pc 文章分类搜索 2025-06-27 17:17:26 +08:00
69e45a8044 fix: ipd访问时lang错误 2025-06-27 17:11:03 +08:00
bdd6a7ba34 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-06-27 17:03:21 +08:00
cbb6bc144b 判读uc浏览器swiper的问题 2025-06-27 17:03:18 +08:00
304f7c460d fix: 产品底盘提示语 2025-06-27 15:20:25 +08:00
163de38bb2 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-06-27 14:54:30 +08:00
62baecc835 富文本图片排版 2025-06-27 14:54:27 +08:00
1dd37485a2 fix: pc/mobile 产品详情页属性 2025-06-27 14:49:24 +08:00
6eca3790d3 样式优化 2025-06-27 11:42:01 +08:00
61c7d32f50 .hd .clearfix 2025-06-27 10:59:15 +08:00
35e11d7d8f 还原 2025-06-27 10:58:14 +08:00
999bb94f70 样式 2025-06-27 10:57:26 +08:00
bd9e6009ad 1 2025-06-27 10:44:55 +08:00
47d9be28fa 1 2025-06-27 10:44:17 +08:00
e765dd4153 1 2025-06-27 10:43:05 +08:00
207106c34c 1 2025-06-27 10:41:56 +08:00
2af9f63b83 还原 2025-06-27 10:39:48 +08:00
dd3672c93a 样式优化 2025-06-27 10:29:25 +08:00
9fb3d8f874 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-06-27 10:20:20 +08:00
519160218f style:样式优化,手机详情页测试 2025-06-27 10:20:17 +08:00
81ec4a84a8 fix: 后台附件上传修改webp格式 2025-06-27 09:55:20 +08:00
a29e95a951 refactor: 后台图片上传添加允许webp格式 2025-06-27 09:54:30 +08:00
239bb64e9a fix: pc 去除多条console.log 2025-06-27 09:38:53 +08:00
774954c70b fix: mobile 产品搜索 2025-06-27 09:37:50 +08:00
a3f8668b60 fix: mobile 底部导航 2025-06-27 09:37:32 +08:00
3cb48fd462 refactor: ipd平台访问重定向为mobile view改为可配置 2025-06-27 09:18:09 +08:00
778e7180d7 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-06-26 16:31:59 +08:00
1eecd80340 视频被遮盖样式调整 2025-06-26 16:31:56 +08:00
0ba42e2a20 fix: 跳转mtpl时会去掉正常参数导致无数据问题 2025-06-26 16:04:56 +08:00
55e8e914fb fix: mobile nas专题 下载页样式问题 2025-06-26 15:50:01 +08:00
37828dafca 样式补充3 2025-06-26 15:40:56 +08:00
043bac94ec 样式修改2补充 2025-06-26 15:38:13 +08:00
e7b76ea68b 样式修改2 2025-06-26 15:34:33 +08:00
e9a2165b10 样式修改 2025-06-26 15:29:42 +08:00
93bf23f307 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-06-26 15:22:31 +08:00
fcd562595d bug修改 2025-06-26 15:22:28 +08:00
4444875e48 fix: mobile nas专题 下载页中英文样式区别 2025-06-26 15:07:51 +08:00
5e8f7549e0 style:样式修改 2025-06-26 15:02:10 +08:00
f0ea57c19f Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-06-26 13:44:43 +08:00
ed8ae64cac 还原 2025-06-26 13:44:40 +08:00
4b3f626443 fix: pc/mobile 产品详情“发送查询”按钮问题 2025-06-26 11:42:52 +08:00
d06378e264 style: 代码格式 2025-06-26 11:40:05 +08:00
0ea2fbeb1f Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-06-26 11:37:10 +08:00
cb5f4a6148 前端测试pad和pc 2025-06-26 11:36:30 +08:00
fed50a9f4c style: openapi模块代码对齐 2025-06-26 10:52:55 +08:00
d94ceb2a9c fix: ipd中区分mobile与pc问题bug 2025-06-26 10:14:32 +08:00
307c80e5e9 fix: mobile 非nas专题页顶部logo无法点击问题 2025-06-25 16:55:54 +08:00
873d043e45 fix: mobile 产品详情去除空字符串属性 2025-06-25 16:52:49 +08:00
dd328e5b9e fix: mobile nas 专题相关页面Logo点击区分中英文 2025-06-25 16:46:10 +08:00
fb135cd1dc fix: mobile nas专题 帮助中心去除logo点击链接 2025-06-25 13:37:48 +08:00
65b1fcd992 fix: mobile 产品详情相关产品输出 2025-06-25 09:52:06 +08:00
7ac132a182 fix: mobile 产品详情页属性选项 2025-06-25 09:44:47 +08:00
929608b9b9 fix: mobile 文章详情页评论记录 2025-06-25 09:27:12 +08:00
cdd4d0d09f fix: mobile 成功经销商校验bug 2025-06-24 18:02:56 +08:00
9c4cccbeb0 fix: mobile 批量购买提交bug 2025-06-24 18:00:43 +08:00
34866fd9fe fix: mobile 联系我们留言 2025-06-24 17:55:05 +08:00
c9107fce72 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-06-24 17:52:08 +08:00
4953163892 fix: mobile 文章详情英文发布时间 2025-06-24 17:51:34 +08:00
cb87b3203c Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-06-24 17:51:31 +08:00
35329b25e8 feat:样式bug移动端修改 2025-06-24 17:51:29 +08:00
6dd714dce7 fix: mobile 产品搜索无数据情况 2025-06-24 17:50:07 +08:00
80dbc75c73 fix: mobile nas专题帮助中心 2025-06-24 17:29:03 +08:00
e76fcf2822 fix: pc顶部导航 2025-06-24 14:22:38 +08:00
b4b36aa988 fix: 文章详情推荐文章 2025-06-24 14:13:55 +08:00
20deefdd5f fix: 获取系统页面url 2025-06-24 13:52:41 +08:00
迷和油
475fe19d84 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-06-21 15:21:59 +08:00
迷和油
eba4a1c96e style:样式修改 2025-06-21 15:21:56 +08:00
b4903854c1 fix: pc/mobile 批量够买问题 2025-06-21 14:12:04 +08:00
44057cb26d fix: pc/mobile 成功经销商留言问题 2025-06-21 14:05:57 +08:00
c5f88098f5 fix: mobile 文章列表发布时间问题 2025-06-21 10:21:32 +08:00
9a220bbb97 fix: mobile 产品搜索页无法点击进入详情问题 2025-06-21 10:15:17 +08:00
8ec4fd9dce fix: mobile搜索历史问题 2025-06-20 14:21:34 +08:00
迷和油
2566538006 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-06-20 14:10:46 +08:00
迷和油
64ff307d57 style:顶部宽度修改 2025-06-20 13:56:59 +08:00
e7a232ac9c refactor: cookir开启httponly 2025-06-20 13:43:20 +08:00
4f3c5fdee2 fix: 文章详情页推荐未区分中英文问题 2025-06-20 13:42:55 +08:00
eea5f83afc fix: 屏幕宽度确定pc/mobile模板问题 2025-06-20 11:29:41 +08:00
53086a83d1 fix: pc nas专题帮助中心文件分类选中问题 2025-06-19 16:51:58 +08:00
96d693af64 fix: 文章列表及详情发布时间中英显示区分 2025-06-19 16:26:17 +08:00
迷和油
70266967e1 reset:还原 2025-06-19 11:50:22 +08:00
迷和油
24959571b7 test:环境测试2 2025-06-19 11:34:53 +08:00
迷和油
5c4aa9e21d test:环境测试 2025-06-19 11:24:57 +08:00
迷和油
474ea78de5 test:测试环境 2025-06-19 11:07:24 +08:00
迷和油
addf3abae0 测试环境 2025-06-19 11:06:31 +08:00
ed5f6e0642 fix: migrations seed命名错误 2025-06-19 11:02:33 +08:00
d2bb8fbf01 fix: 无法判断出是ipad的情况 2025-06-19 10:08:40 +08:00
421d549109 docs: 修改migrations文件中的表注释 2025-06-16 15:04:26 +08:00
a2d03eb0d0 fix: “代理商企业规模类型表”migrations文件表命名错误 2025-06-13 14:44:54 +08:00
425a438852 fix: 返回顶部按钮只在中文下显示 2025-06-13 14:22:55 +08:00
迷和油
ac249f0a18 add:新增一个中文官网返回顶部的功能 2025-06-13 11:44:10 +08:00
55b45a0aef refactor: 上传 2025-06-12 17:56:20 +08:00
e6733ba338 refactor: 配置seeder 2025-06-12 17:51:52 +08:00
cea787d493 fix: pc产品详情 2025-06-12 17:46:45 +08:00
a8f3aa3a1b refactor: 处理优化migration文件与model文件字段不一致 2025-06-12 17:41:14 +08:00
c8284568aa feat: 系统用户seeder 2025-06-12 16:26:25 +08:00
306d3faa71 feat: 系统菜单seeder 2025-06-12 16:21:59 +08:00
49193a8d52 feat: 系统语言seeder 2025-06-12 16:04:35 +08:00
c138bf501e feat: 国家seeder 2025-06-12 16:00:08 +08:00
127901d864 feat: 代理商企业规模类型seeder 2025-06-12 15:54:54 +08:00
5a15210d00 feat: 代理商业务类型seeder 2025-06-12 15:50:21 +08:00
184595ce36 feat: 产品属性特性seeder 2025-06-12 15:41:51 +08:00
4214331fd3 feat: 产品购买平台seeder 2025-06-12 15:31:36 +08:00
57aba229cd feat: 配置项类型seeder 2025-06-12 15:20:27 +08:00
add17eac0c feat: 配置组/配置数据seeder 2025-06-12 15:09:59 +08:00
8f9e304294 refactor: 上传相关优化 2025-06-12 14:50:20 +08:00
6bd988f257 fix: pc nas专题帮且中心 2025-06-11 18:14:00 +08:00
0665cccd9d fix: pc文章详情发布时间中英文区分 2025-06-11 18:09:08 +08:00
80e4b502d1 fix: pc nas专题logo不需要点击 2025-06-11 18:02:12 +08:00
d522c862c2 fix: pc产品详情 2025-06-11 17:40:43 +08:00
8eee103fb6 fix: pc产品详情 2025-06-11 17:32:57 +08:00
迷和油
41fc9b81e7 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-06-11 17:24:11 +08:00
迷和油
414b79c2d8 style:搜索更改,样式更改 bug提交 2025-06-11 17:23:30 +08:00
f48f2265d8 fix: 上传图片水印 2025-06-11 17:09:41 +08:00
50f4e5db55 fix: 上传图片水印 2025-06-11 16:59:02 +08:00
adbfc34f9d fix: 上传图片水印处理 2025-06-11 16:51:13 +08:00
aa4d5b5f31 fix: 上传 2025-06-11 15:40:37 +08:00
0c4c40ce7f fix: 水印处理 2025-06-11 15:36:44 +08:00
abef3a7750 fix: pc成功经销商 2025-06-11 15:17:14 +08:00
e746952c11 refactor: 附件上传允许上传ttf字体 2025-06-11 14:47:21 +08:00
00f3cda6ac feat: 图片上传水印处理 2025-06-11 14:46:40 +08:00
a823cfaf91 fix: nas帮助中心搜索 2025-06-11 11:38:25 +08:00
51d31a1a6d fix: 后台产品分类系统urls 2025-06-11 11:03:00 +08:00
b2699a952e fix: pc附件搜索placeholder 2025-06-11 10:55:55 +08:00
5c84e7e390 refactor: 系统其他urls回显 2025-06-10 15:00:50 +08:00
648fde842c refactor: 后台视频列表/回收站输出缩略图 2025-06-10 14:33:37 +08:00
e927468965 refactor: 添加其他内页系统urls 2025-06-10 14:31:06 +08:00
a8f7758f46 fix: 顶部“店铺”按钮不能显示在中文 2025-06-10 10:08:08 +08:00
a79adefd64 fix: 首页热点产品无点击链接 2025-06-10 10:01:50 +08:00
76a652661c fix: nas主题相关页排序bug 2025-06-10 09:58:22 +08:00
迷和油
357f1fa4c0 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-06-09 16:42:40 +08:00
迷和油
c0227d2d4a style:样式bug修改 2025-06-09 16:42:34 +08:00
4779280647 refactor: 修改样式文件命名 2025-06-09 14:58:39 +08:00
776163bb09 style: mobile 语言文件 2025-06-09 14:50:59 +08:00
2333dcd188 feat: mobile nas主题客户合作页 2025-06-09 14:47:28 +08:00
9f0ad41e8f feat: mobile nas主题下载页 2025-06-09 13:49:58 +08:00
230114d110 feat: mobile nas主题产品体验页 2025-06-09 10:56:54 +08:00
迷和油
0f1e480935 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-06-09 10:55:08 +08:00
迷和油
e70b793e2b 6.9 2025-06-09 10:55:02 +08:00
a1412898af refactor: mobile nas主题帮助中心页 2025-06-09 10:37:20 +08:00
69fe15b9b6 feat: mobile nas主题帮助中心详情页 2025-06-09 10:29:50 +08:00
ae8fc70e97 fix: 横幅查询未过滤禁用的数据 2025-06-07 16:56:43 +08:00
56029236bd fix: 修复后台横幅关联产品分类提交 2025-06-07 16:48:01 +08:00
fa2b67c5b5 fix: nas 产品体验页焦点图bug 2025-06-07 16:41:51 +08:00
38ad0bc700 feat: mobile nas主题帮助中心页 2025-06-07 16:36:44 +08:00
迷和油
3e8643d1fa Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-06-07 16:04:29 +08:00
迷和油
bd0cde3261 优化 2025-06-07 16:04:25 +08:00
76b987205f fix: mobile 修复获取产品分类焦点图未区分pc与mobile数据 2025-06-07 16:04:20 +08:00
迷和油
77bd7e1031 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-06-07 15:50:27 +08:00
迷和油
65d636177f 评论样式修复 2025-06-07 15:50:22 +08:00
888fed6390 fix: pc nas帮助页 2025-06-07 15:44:43 +08:00
迷和油
8891017cef Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-06-07 15:15:20 +08:00
迷和油
a0045f2397 优化 2025-06-07 15:14:44 +08:00
01a2bfe4c7 fix: 修复文章详情没有评论数据显示问题 2025-06-07 15:00:46 +08:00
dbf693d674 fix: 批量购买bug修复 2025-06-07 14:45:15 +08:00
迷和油
e6d851395f Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-06-07 14:34:11 +08:00
迷和油
79d4c4976b style:bug样式修改 2025-06-07 14:32:56 +08:00
20431def5a fix: 产品分类bug修复 2025-06-07 14:27:32 +08:00
2c00fb46ad fix: 文章模块bug修复 2025-06-07 14:24:10 +08:00
3f3d68c275 refactor: 修改array_flatten函数相关 2025-06-07 14:03:47 +08:00
3ba64348f5 feat: mobile nas主题首页 2025-06-07 13:54:34 +08:00
7c5342a42b feat: mobile问答中心页 2025-06-06 16:28:01 +08:00
433734fe5e feat: mobile文章详情页 2025-06-06 16:10:51 +08:00
7833052186 refactor: 修改接口记录表迁移文件及更新其数据 2025-06-06 15:10:27 +08:00
9c149e7373 refactor: 修改几在模块支持多分类筛选 2025-06-06 11:55:33 +08:00
be359bcb85 refactor: 优化横幅分页列表查询 2025-06-06 11:05:55 +08:00
7bbb0cd60e refactor: 优化日志分页查询性能 2025-06-06 10:29:53 +08:00
93e3ac5d70 refactor: 后台问答分页图片改缩略图输出 2025-06-05 17:01:48 +08:00
8815809b68 refactor: 后台产品分页图片改缩略图输出 2025-06-05 16:59:47 +08:00
a89b64d62b refactor: 后台文章回收站分页图片改为缩略图输出 2025-06-05 16:57:50 +08:00
978367fd2e refactor: 后台附件分页列表图片改缩略图输出 2025-06-05 16:53:49 +08:00
4c6643797d refactor: 后台文章列表图片改为缩略图输出 2025-06-05 16:50:19 +08:00
9d1f60b998 feat: mobile文章首页 2025-06-05 16:34:23 +08:00
9fbada3be5 feat: mobile联系我们 - 售后政策页 2025-06-05 15:54:09 +08:00
d824e694a4 fix: pc产品详情页面“首页”文本多语言处理 2025-06-05 15:34:51 +08:00
dc7db65139 feat: mobile联系我们 - 品牌故事页 2025-06-05 15:28:32 +08:00
c5d450907c feat: mobile联系我们 - 品牌里程页 2025-06-05 15:01:24 +08:00
d2fb349766 feat: mobile联系我们 - 文化介绍页 2025-06-05 12:01:15 +08:00
c238bbc588 refactor: pc联系我们 - 文化介绍页修改 2025-06-05 12:01:06 +08:00
28e586b57f feat: mobile联系我们 - 品牌介绍 2025-06-05 10:35:21 +08:00
1bf60294f0 chore: 配置配置环境变量文件 2025-06-05 10:34:48 +08:00
ba0904069c refactor: 后面横幅分页接口图片输出修改为缩略图 2025-06-05 09:51:23 +08:00
迷和油
c40b4ea632 style:样式修改 2025-06-04 18:01:01 +08:00
迷和油
e9e36fa8da style:样式修改 2025-06-04 14:46:44 +08:00
迷和油
3db6e21f5a 测试 2025-06-04 14:36:29 +08:00
杨丹华
7dd1559918 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-06-04 14:25:32 +08:00
杨丹华
455fdfca87 撤销提交 2025-06-04 14:25:24 +08:00
杨丹华
0363dfcc1d Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-06-04 14:12:58 +08:00
杨丹华
ce795402f2 style:样式修改 2025-06-04 14:12:55 +08:00
03dbb3273d refactor: pc多语言 - 语言包调整 2025-06-03 11:43:19 +08:00
2a19ee5317 fix: Nas专题 - 帮助中心详情bug 2025-06-03 11:43:19 +08:00
杨丹华
90ef0e44b8 补充 2025-05-30 16:05:26 +08:00
杨丹华
86888caaf9 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-05-30 15:28:42 +08:00
杨丹华
c1751410c8 style:样式修改 2025-05-30 15:28:01 +08:00
ac7c1948ec refactor: think-orm回退版本 2025-05-30 13:49:54 +08:00
e0ef5d1dfe refactor: 修改验证多语言提示 2025-05-30 13:36:57 +08:00
a998237ad2 refactor: 修改多语言相关 2025-05-30 10:30:34 +08:00
d74b1d78fe feat: mobile视频页 2025-05-30 09:57:11 +08:00
9498bde772 refactor: 修改样式文件命名 2025-05-30 09:56:48 +08:00
faa1e27b30 refactor: 去除多余文件 2025-05-30 09:16:14 +08:00
7d8f8f670e feat: mobile 联系我们留言页 2025-05-30 09:12:48 +08:00
杨丹华
ce013a9379 style:去掉多余的样式 2025-05-28 11:47:44 +08:00
杨丹华
5faabaa05f style:关联产品样式不从 2025-05-28 11:45:09 +08:00
0ba655692c refactor: 修改验证器语言包提示 2025-05-28 10:47:01 +08:00
ad55d14f8e feat: mobile批量购买留言页 2025-05-28 10:31:02 +08:00
96d05e0d09 refactor: 修改横幅列表接口 2025-05-27 15:49:27 +08:00
f8c8092562 feat: mobile成功分销商页面 2025-05-27 15:37:51 +08:00
ee8f5017a5 refactor: 区分联系我们banner的pc与mobile数据 2025-05-27 14:13:06 +08:00
d43892b912 refactor: 进一步区分banner的pc与mobile 2025-05-27 14:02:14 +08:00
b30ec56bec feat: mobile联系我们页 2025-05-27 11:13:03 +08:00
01c14bb718 refactor: pc联系我们 2025-05-27 10:33:35 +08:00
b219ce3ee8 refactor: mobile底部国家筛选 2025-05-26 17:54:23 +08:00
6d9a0f3d06 feat: mobile产品搜索页 2025-05-26 17:39:23 +08:00
7273594013 feat: mobile附件下载页 2025-05-26 17:13:51 +08:00
a749ae588e feat: mobile新品上市页 2025-05-26 11:58:02 +08:00
0c3af5bdf1 feat: mobile产品详情 2025-05-26 11:40:02 +08:00
0ea206d460 fix: pc产品详情 2025-05-26 11:40:02 +08:00
杨丹华
427097d704 stlye:首页样式底部调白 2025-05-26 11:00:14 +08:00
杨丹华
4882cb26a6 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-05-24 18:08:03 +08:00
杨丹华
6a5e7c0bfc stlye:产品指南样式调整 2025-05-24 18:06:43 +08:00
杨丹华
bc50e84897 stlye:样式修改 2025-05-24 18:05:42 +08:00
74548ffac9 feat: mobile产品分类 2025-05-24 17:24:58 +08:00
194d16f4f9 fix: 文章描述长度限制 2025-05-24 14:53:33 +08:00
74d7235cd2 fix: 文章标题长度限制 2025-05-24 14:53:33 +08:00
1af3b8bbd2 feat: mobile首页 2025-05-24 14:53:33 +08:00
cc3b4bb140 fix: pc首页推荐文章链接 2025-05-24 14:07:38 +08:00
91762a9045 fix: 获取系统url 2025-05-24 11:20:54 +08:00
3d28b10f89 fix: pc index.html 2025-05-24 09:51:29 +08:00
99ba8449c8 refactor: hidden内容修改 2025-05-24 09:47:56 +08:00
b29a8b7f7d refactor: 字段类型 2025-05-24 09:34:05 +08:00
e23bf4ff31 refactor: pc index.html 2025-05-23 14:46:05 +08:00
0cf80dc197 refactor: 样式 2025-05-23 14:32:49 +08:00
e9754efe34 refactor: 去掉临时图片 2025-05-23 14:27:47 +08:00
dfadeef58c refactor: 调整图片目录 2025-05-23 14:25:33 +08:00
a64151dbe6 refactor: 调整图片目录 2025-05-23 14:12:46 +08:00
4329bc056f refactor: 调整图片目录 2025-05-23 14:11:22 +08:00
66275cb3f6 refactor: photo_album格式问题 2025-05-23 14:05:40 +08:00
2dd4011067 refactor: 国家/语言图片调整目录 2025-05-23 11:22:35 +08:00
da770b823e refactor: 字体目录调整 2025-05-23 10:37:08 +08:00
eb6ead0b9c feat: 模板区分pc与mobile 2025-05-23 10:37:08 +08:00
2423d876d7 fix: 开放API鉴权 2025-05-23 10:37:08 +08:00
杨丹华
7a34c2fb07 style:zoom自适应 2025-05-23 10:13:50 +08:00
杨丹华
bdbfce8c23 style:字体样式兼容修改 2025-05-22 17:33:10 +08:00
杨丹华
d368941d9e . 2025-05-22 17:19:36 +08:00
杨丹华
3ebc455be3 stlye:首页 nas首页样式调整 2025-05-22 17:14:13 +08:00
杨丹华
8ed90c9c1c Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-05-22 16:20:32 +08:00
杨丹华
2dbd13ae85 style:样式修改 2025-05-22 16:20:01 +08:00
41c88d3388 fix: nas主题下载页样式 2025-05-22 15:07:41 +08:00
68ec2820c1 fix: 文章详情遗漏点击链接问题 2025-05-22 14:51:10 +08:00
29003d3386 feat: 开放API文章相关 2025-05-22 13:53:49 +08:00
019e65d7db feat: 开放API产品相关 2025-05-22 13:53:49 +08:00
7ffbc6d173 feat: 开放API授权相关 2025-05-22 13:53:49 +08:00
杨丹华
970ff14c22 5.20 2025-05-20 14:03:31 +08:00
杨丹华
32e8e0ace4 视频点击问题 2025-05-19 16:27:55 +08:00
杨丹华
3d17bb9225 stlye:视频大小调整 2025-05-19 16:08:00 +08:00
杨丹华
dae2ba254e Merge branch 'cssupdate' into dev 2025-05-19 16:00:32 +08:00
杨丹华
349e53104d style:首页视频 产品详情样式修改 2025-05-19 15:59:40 +08:00
cc81bc8525 refactor: 产品详情页相关产品 2025-05-19 14:49:56 +08:00
37aed84d88 refactor: 分类列表显示缩略图 2025-05-19 14:40:16 +08:00
81e711b993 refactor: 数据迁移 2025-05-19 14:35:56 +08:00
eefc81784f refactor: 合并冲突代码找回 2025-05-19 09:33:12 +08:00
杨丹华
64969ea916 style:nas 样式修改 2025-05-16 17:10:33 +08:00
杨丹华
1e0565cdb6 Merge branch 'cssupdate' into dev 2025-05-16 16:44:54 +08:00
杨丹华
0a0a20ebf9 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-05-16 16:44:28 +08:00
杨丹华
b83a5c665c 产品详情页添加关联产品轮播;点击图册显示大图的问题,还有点击大图可以弹框观看 2025-05-16 16:42:30 +08:00
ce5d0244c0 refactor: 产品分类 2025-05-16 11:14:11 +08:00
杨丹华
306109422f Merge branch 'dev' into cssupdate 2025-05-16 10:03:33 +08:00
杨丹华
cd465f0024 stlye:样式修改 2025-05-16 10:01:53 +08:00
6bdd6c02a9 refactor: 产品分类 2025-05-16 09:58:33 +08:00
c510e6bc4c refactor: nas主题中英文logo问题 2025-05-15 15:27:36 +08:00
d807b52f11 refactor: nas主题帮助页效果问题 2025-05-15 15:18:46 +08:00
7ff7f88708 refactor: nas主题下载页 2025-05-15 15:01:09 +08:00
a159313702 refactor: 首页视频不显示问题 2025-05-14 18:06:23 +08:00
768b41aa70 refactor: 视频分类默认选中问题 2025-05-14 17:48:58 +08:00
2aaf80ffbd refactor: nas主题页logo点击链接 2025-05-14 17:36:36 +08:00
a105d575b8 refactor: 调整新品页数据 2025-05-14 17:27:59 +08:00
d04f7b035a refactor: 数据迁移程序 2025-05-14 16:25:55 +08:00
efb0dc2042 refactor: 产品分类页名称下显示spu 2025-05-14 15:44:33 +08:00
d203a3ad3b refactor: 产品详情页 2025-05-14 15:22:00 +08:00
67fc583bd3 refactor: 产品购买链接迁移程序 2025-05-14 15:20:30 +08:00
dc8331340f refactor: 新品页面是否新品处理 2025-05-14 15:20:30 +08:00
78d8ec5d4a refactor: 导航左侧分类加可点击 2025-05-14 15:20:30 +08:00
e6d32e7575 refactor: 相关产品问题 2025-05-14 15:20:30 +08:00
a66f4ca6dc refactor: 同步程序 2025-05-14 15:20:30 +08:00
f727bcae70 refactor: 英文底部提示问题 2025-05-14 15:20:30 +08:00
69e9993701 refactor: 首页底部cookie询问调整为只在英文页面显示 2025-05-14 15:20:30 +08:00
eb9b566bcd style: 格式问题 2025-05-14 15:20:30 +08:00
80c549a72f refactor: 首页品牌故事年份显示问题 2025-05-14 15:20:30 +08:00
7f0547ff61 refactor: 调整数据字段长度 2025-05-14 15:20:30 +08:00
255ad67fe9 refactor: 调活顶部导航中店铺按钮 2025-05-14 15:20:30 +08:00
ee0f9f25e3 refactor: 搜索框 - 热销产品显示简称 2025-05-14 15:20:30 +08:00
bfae1a23fc feat: 产品搜索页 2025-05-14 15:20:30 +08:00
5a576b2da3 refactor: 新增依赖 2025-05-14 15:20:30 +08:00
fc5b64e68b refactor: 修改产品表及产品参数表相应字段长度 2025-05-14 15:20:30 +08:00
杨丹华
8caa362e95 style:fqa下载和联系我们样式修改 2025-05-13 17:17:50 +08:00
杨丹华
4eb972e2d1 stlye:主要nas专题下载页面和帮助中心页面样式调整 2025-05-13 16:54:43 +08:00
杨丹华
b44bf2362b style:search 页面样式调整 2025-05-13 10:44:30 +08:00
e201814094 style:category 和产品详情和详情样式调整 2025-05-12 15:37:24 +08:00
fccf4a3fc3 style:样式修改 2025-05-10 18:03:33 +08:00
a17afcf113 Merge branch 'cssupdate' into dev 2025-05-10 17:34:08 +08:00
b7c79da318 style:修改首页,产品详情,新品,nasr首页样式
refactor:重构了产品详情的图片预览功能,coyp了一个页面
add:新增了large.js这个是产品详情图片预览相关的
2025-05-10 17:33:35 +08:00
caaf9966d4 Merge branch 'dev' of https://gitea.f2b211.com/jsasg/orico-official-website into dev 2025-05-09 15:48:40 +08:00
999c8adc9d feat:新增加product_subcategory和product_category,两个页面,category这个页面可能要废弃了,英文官网添底部一个公共部分 2025-05-09 15:46:55 +08:00
e648538fe5 refactor: 修改产品sku属性值长度限制 2025-05-09 11:08:52 +08:00
ab462e88af fix:首页切换图片bug 2025-05-09 11:05:21 +08:00
c7f95bba3a refactor: 产品详情页取副标题 2025-05-09 10:53:22 +08:00
1d71ee3548 refactor: 修改更新时产品参数正则解析 2025-05-09 10:47:25 +08:00
5dbb434018 feat: 数据迁移程序 2025-05-08 18:19:46 +08:00
31982f2486 fix:还原ignore文件 2025-05-08 16:02:02 +08:00
1eae15631b 添加搜索产品页面 2025-05-08 15:49:37 +08:00
76501fedae refactor: 系统url 2025-05-07 10:57:39 +08:00
b38879539a feat: Nas专题帮助中心 2025-05-06 16:25:53 +08:00
10d1bea7bc refactor: 文章详情接口添加可空处理 2025-04-30 16:51:21 +08:00
5c86e0ec74 refactor: 去除文章回收站中enabled字段 2025-04-30 11:19:13 +08:00
7b1870a4fb refactor: 文章分类新增/更新添加图片及唯一标识 2025-04-30 10:13:12 +08:00
b3c9fef907 feat: nas下载页 2025-04-30 09:24:36 +08:00
ce49d3d480 refactor: 修改TopicNas中banner获取排序 2025-04-29 14:43:20 +08:00
039c3a9136 feat: nas客户合作页 2025-04-29 14:40:50 +08:00
aeabb46e44 feat: nas产品体验页 2025-04-29 11:52:04 +08:00
07687e62b3 feat: nas首页 2025-04-28 18:03:11 +08:00
4e7623194d refactor: 修改static文件夹 2025-04-28 17:04:23 +08:00
c2ab16fe33 refactor: 修改public/base.html 2025-04-28 11:00:35 +08:00
d34a250ac6 refactor: 修改Common.php 2025-04-28 11:00:03 +08:00
0eb715f074 refactor: 修改header.html 2025-04-28 10:25:28 +08:00
e277cd12a8 refactor: 修改路由命名 2025-04-27 16:55:12 +08:00
74788dcf55 feat: 产品
refactor: 产品详情页seo
2025-04-27 16:46:05 +08:00
7e7ea4dda7 feat: 新品上市 2025-04-27 15:38:28 +08:00
31744ee208 refactor: 修改额外配置项 2025-04-27 14:03:29 +08:00
jsasg
9808b1ae25 refactor: 修改顷部导航链接 2025-04-25 21:55:05 +08:00
8e7bb8c51a refactor: 产品分类添加path层级路径逻辑 2025-04-24 16:04:39 +08:00
865193475b refactor: 文章 2025-04-23 18:03:41 +08:00
d5d86679a3 refactor: 去除多余图片 2025-04-23 17:05:25 +08:00
cf0b4e4ccf refactor: 配置项值字数限制修改 2025-04-23 16:56:01 +08:00
875c178e5f perf: 获取系统页面url为空情况处理 2025-04-23 15:39:23 +08:00
8d1a803359 refactor: banner新增相关分类数据字段 2025-04-23 15:33:17 +08:00
8dad8dd35f feat: 文化介绍 2025-04-22 17:57:40 +08:00
7e4dc980e9 refactor: 删除多余图片 2025-04-22 16:35:55 +08:00
2e27fbde5e feat: 品牌介绍 2025-04-22 16:15:50 +08:00
b74d20279e feat: 批量购买 2025-04-22 15:17:36 +08:00
83a817b9cf refactor: header的logo连接 2025-04-22 14:41:49 +08:00
f9afb177eb feat: 售后政策 2025-04-22 11:25:08 +08:00
fa8feaacbd refactor: 横幅标题字数限制改为256 2025-04-22 11:25:08 +08:00
685fcf9686 refactor: 修改css文件名称 2025-04-21 18:43:52 +08:00
15cb14d976 feat: 联系我们 - 客户服务 2025-04-21 18:43:52 +08:00
9b174c2874 refactor: message template 2025-04-21 18:43:52 +08:00
df1583e689 feat: 联系我们成为分销商 2025-04-21 18:43:40 +08:00
5f39cbc30a refactor: 修改agent的migration文件 2025-04-21 11:13:55 +08:00
e907583a32 refactor: 指定合法代理ip解决无法在代理情况获取真实客户端ip问题 2025-04-21 09:40:02 +08:00
d81bce3d09 refactor: Index 2025-04-18 17:07:56 +08:00
aa3e4e81e6 refactor: AboutUs 2025-04-18 17:02:53 +08:00
9e939763fd feat: 联系我们 2025-04-18 16:57:28 +08:00
c83ccee13f feat: 品牌里程 2025-04-17 16:37:09 +08:00
55f56699e3 feat: 品牌故事 2025-04-16 16:57:13 +08:00
ec0c2f4702 refactor: banner的desc校验修改 2025-04-16 16:13:39 +08:00
23d30fb43c refactor: 修改无数据时显示内容 2025-04-16 14:41:21 +08:00
7d6331ffd5 refactor: 数据迁移程序 2025-04-16 14:19:13 +08:00
24f0973f7a refactor: 调整视频输出 2025-04-16 14:07:33 +08:00
fa91ee8351 refactor: 附件/视频搜索 2025-04-16 13:39:19 +08:00
07a722c000 refactor: 修改video表desc长度 2025-04-16 11:48:29 +08:00
fb5534753a refactor: 去除视频回收站status字段 2025-04-16 11:05:22 +08:00
2c172e89a9 refactor: 修改附件及视频分类查询 2025-04-16 10:59:42 +08:00
c3b872ba7b refacotr: 视频 2025-04-16 09:38:27 +08:00
2c1767b03c refactor: 修改附件排序 2025-04-15 17:57:44 +08:00
2a9f8ab28f refactor: 去除视频表中status字段 2025-04-15 17:54:21 +08:00
551313bf23 refactor: 附件 2025-04-15 17:43:10 +08:00
5936301e82 refactor: 品牌动态 2025-04-15 17:23:31 +08:00
f8aa1495e8 refactor: 修改附件基类模型 2025-04-15 16:59:38 +08:00
d3e3271fbe refactor: 修改附件验证 2025-04-15 16:55:45 +08:00
6f1cf8dc52 refactor: 数据迁移 2025-04-15 16:28:31 +08:00
f7ff91889d refactor: 修改图片上传时生成缩略图使用Imagick扩展 2025-04-15 16:16:51 +08:00
1d30d725e2 refactor: 修改附件上传文件类型限制 2025-04-15 15:17:46 +08:00
0bdfed92f3 refactor: 修改attachment表的migration文件 2025-04-15 14:12:58 +08:00
c5bdaa8354 refactor: 去除附件中status字段相关 2025-04-15 11:12:39 +08:00
dd22f6fc3e refactor: 修改附件上传文件类型限制 2025-04-15 10:23:01 +08:00
1814cc0b9a refactor: 去除附件中status字段相关 2025-04-15 10:14:27 +08:00
fbd479f69a feat: 附件(软件/手册下载) 2025-04-14 18:12:27 +08:00
26b83d2037 refactor: 修改文章分类验证 2025-04-14 18:11:53 +08:00
941e103258 refactor: 修改附件分类支持上下级 2025-04-14 18:11:41 +08:00
e6e5f260fb refactor: 修改banner的desc为富文本方式输出 2025-04-14 16:33:45 +08:00
2824148ae1 refactor: 修改migration文件 2025-04-14 16:25:29 +08:00
d2c755fd0a feat: 问答中心 2025-04-14 16:03:39 +08:00
03b7e0d288 refactor: 设置模板输出替换 2025-04-12 17:26:51 +08:00
abbd36c84d style: 添加首页注释 2025-04-12 17:12:35 +08:00
57d414fd0a refactor: 文章详情留言 2025-04-12 17:11:32 +08:00
d1fd52361a refactor: 文章详情页 2025-04-12 16:31:39 +08:00
b984f1448a refactor: 数据迁移命令 2025-04-12 14:49:20 +08:00
0833636868 refactor: 修改页面底部文件 2025-04-12 14:46:47 +08:00
14ff19ea93 feat: 文章相关 2025-04-12 14:34:07 +08:00
111b648b36 refactor: 首页控制器 2025-04-12 14:33:30 +08:00
a2dc146130 refactor: 去除文章enabled字段相关 2025-04-12 10:15:30 +08:00
be4001ad4d refactor: 修改静态文件路径 2025-04-12 09:12:17 +08:00
9874fbc49a feat: 数据迁移命令 2025-04-11 18:15:12 +08:00
0b420601f1 refactor: 修改base.html 2025-04-11 18:13:29 +08:00
286bb6171c refactor: 修改header.html 2025-04-11 18:09:48 +08:00
c9fc6b95ad refactor: 修改migration文件 2025-04-11 17:54:42 +08:00
d798a749dc refactor: 修改migration文件 2025-04-11 14:45:48 +08:00
ac022803e4 refactor: 修改dev配置文件 2025-04-11 10:05:28 +08:00
98754be311 refactor: 修改后台文章分类验证 2025-04-11 10:01:43 +08:00
36f4e317cf refactor: 修改上传限制可配置 2025-04-11 09:47:42 +08:00
c73981344f refactor: 修改后台文章分类树接口 2025-04-11 09:30:17 +08:00
b16d90c946 feat: index - 首页及顶部导航处理 2025-04-10 17:50:01 +08:00
f809ee2c2b refactor: 修改后台文章分类相关接口 2025-04-10 17:50:01 +08:00
fc0d159041 refactor: 修改后台文章分类相关接口 2025-04-10 17:47:32 +08:00
1f11c047f1 refactor: 修改后台文章分类相关接口 2025-04-10 17:43:29 +08:00
08afcd551a refactor: 修改视频上传大小限制 2025-04-10 10:33:10 +08:00
73726e10c2 refactor: 修改config_group的migration文件 2025-04-09 17:26:19 +08:00
517844bc25 refactor: 修改banner的migration文件 2025-04-09 17:26:19 +08:00
10882f135c refactor: 修改后台banner分页接口排序 2025-04-09 17:26:19 +08:00
22deab0aba refactor: 修改日志配置 2025-04-09 16:10:47 +08:00
033af1cbc6 refactor: 修改导航分页排序 2025-04-09 14:10:16 +08:00
8196490dad refactor: 修改后台配置项分页接口排序 2025-04-08 18:14:31 +08:00
0153020fd0 refactor: 修改配置项标识唯一验证 2025-04-08 18:14:31 +08:00
6ad39e0764 refactor: 修改后台批量询盘采购可选品类接口 2025-04-08 18:14:31 +08:00
c6bc9a19de refactor: 修改后台配置列表标识唯一验证 2025-04-08 18:14:31 +08:00
f9125a4024 refactor: 修改admin中导航model 2025-04-07 16:40:03 +08:00
5eb34aab3b refactor: 修改array_to_tree函数所在文件 2025-04-07 15:45:39 +08:00
0bfed25aaf refactor: 修改后台接口中获取语言信息 2025-04-07 14:53:50 +08:00
40aaca9f4e feat: 添加index应用模块 2025-04-03 16:55:30 +08:00
8eaa5559ef refactor: 修改各模块导出 2025-04-02 16:39:45 +08:00
7b8d0b085f refactor: 修复文章导出添加时间筛选bug 2025-04-02 15:52:19 +08:00
106deeb224 refactor: 修改跨域暴露的header 2025-04-01 14:44:28 +08:00
03104468a0 refactor: 修改附件下载模型 2025-04-01 13:53:55 +08:00
53da5b8a99 refactor: 修改产品详情接口添加分类名称字段输出 2025-04-01 12:00:05 +08:00
7b82db2366 refactor: 修改视频详情接口添加分类名称字段输出 2025-04-01 11:52:23 +08:00
336b332572 refactor: 修改文件详情接口添加分类名称字段输出 2025-04-01 11:51:17 +08:00
efdff61306 refactor: 修改附件下载接口添加分类名称字段输出 2025-04-01 11:44:37 +08:00
91b401870c refactor: 去除产品更新接口启用状态字段输入 2025-03-31 17:24:42 +08:00
036fa21944 refactor: 横幅分布接口添加状态字段输出 2025-03-31 17:00:39 +08:00
3df439ef1b refactor: 去除文章/视频/附件下载新增/编辑接口启用状态字段输入 2025-03-31 16:53:27 +08:00
33db39d5b0 refactor: 新增/编辑操作中未有启用状态字段的添加启用状态字段 2025-03-31 16:04:11 +08:00
2798951f45 refactor: 系统页面url获取修改 2025-03-31 15:39:55 +08:00
0b03e51c12 fix: 修复横幅更新bug 2025-03-31 15:38:47 +08:00
c4b2b005e0 fix: 修复横幅编辑保存时验证bug 2025-03-31 15:06:50 +08:00
9e48a8b314 fix: 修复视频导出时间筛选bug 2025-03-31 14:58:22 +08:00
c849c2db3e refactor: 修改附件分类列表接口 2025-03-31 14:31:28 +08:00
95d671c161 refactor: 修改产品分类列表接口 2025-03-31 14:11:10 +08:00
237d786cbe refactor: 修改视频分类列表接口 2025-03-31 14:03:34 +08:00
40f3f293e3 refactor: 修改文章分类列表接口 2025-03-31 13:58:58 +08:00
68f7ed356f refactor: 修改跨域返回头中允许Authorization暴露 2025-03-29 15:43:20 +08:00
f671dbbd19 refactor: 修改环境变量配置文件 2025-03-29 15:07:14 +08:00
ae6a927d45 feat: 新增产品分类用于下拉框场景列表树接口 2025-03-28 17:44:46 +08:00
7ec6f9861f refactor: 修改视频分类列表接口 2025-03-28 16:47:04 +08:00
9fcf6d0228 refactor: 修改产品属性添加接口 2025-03-28 16:28:27 +08:00
56f4e34d9a refactor: 修改文章导出接口 2025-03-28 16:11:57 +08:00
bbbe28d9e6 feat: 添加横幅列表接口 2025-03-28 15:43:58 +08:00
d9f3b64ae9 refactor: 下载分类列表接口调整 2025-03-28 15:37:40 +08:00
baa5addc60 refactor: 修改横幅连接类型校验 2025-03-28 15:31:45 +08:00
63c3f39e7d refactor: 修改文章发布时间搜索 2025-03-28 15:28:31 +08:00
5696b25705 fix: 修复产品购买链接分页排序及上下架状态问题 2025-03-28 09:45:22 +08:00
f275c447ac fix: 修复视频导出时图片及视频地址拼接错误 2025-03-28 09:26:51 +08:00
5736e5c757 refactor: 修改产品购买链接分布接口 2025-03-27 14:56:55 +08:00
4c2ea0e11a feat: 添加产品购买链接添加接口 2025-03-26 21:43:07 +08:00
6efd309160 refactor: 修改视频上传图片可空 2025-03-26 17:22:33 +08:00
133650a9ea refactor: 修改代理商导出表头 2025-03-26 17:14:19 +08:00
67c87761a6 refactor: 处理文章发布时间为空情况 2025-03-26 17:12:49 +08:00
fb33688dc7 refactor: 修改导航分类区分语言查询 2025-03-26 14:21:13 +08:00
7565fe311d refactor: 修改配置列表支持区分语言查询 2025-03-26 14:17:28 +08:00
2222db92fa refactor: 修改导航按语言区分查询 2025-03-26 14:04:24 +08:00
6cd342ea56 refactor: banner添加语言区分查询 2025-03-26 13:53:19 +08:00
b97f08f1ac refactor: 去除多条引用 2025-03-26 10:52:26 +08:00
70ed1624f1 refactor: 修改登录不再返回nickname而是返回username 2025-03-26 09:35:47 +08:00
e008d8d2db refactor: 修改banner字段默认值 2025-03-25 17:38:43 +08:00
7601564d99 refactor: 修改用户更新场景密码验证问题 2025-03-25 17:20:26 +08:00
f06bc9afa1 refactor: 国家列表接口添加按名称搜索 2025-03-25 16:50:58 +08:00
9a6e7c02ba refactor: 修改角色描述可为空 2025-03-25 16:44:34 +08:00
1121fd3405 refactor: 修改代理商申请记录导出表头字段顺序 2025-03-25 15:57:57 +08:00
6ed3398e87 refactor: 修改banner分类搜索 2025-03-25 15:55:47 +08:00
03110541c5 refactor: 修改文章分类列表只显示非隐藏数据 2025-03-25 15:52:29 +08:00
f19927ca33 refactor: 修改登录验证码为可空 2025-03-25 15:46:57 +08:00
915dc2fdb2 refactor: 修改日志列表为倒序 2025-03-25 13:52:34 +08:00
245c90f31f refactor: 修改留言记录支持某天搜索 2025-03-25 11:53:15 +08:00
a5168055bf refactor: 修改导出excel标题文件错误 2025-03-25 11:41:52 +08:00
d6cf444637 refactor: 修改记录日志中间件 2025-03-25 11:19:36 +08:00
6c5fe7e4da refactor: 修改.env.dev环境变量文件 2025-03-25 11:10:39 +08:00
4cbe27668f refactor: 修改auth配置文件 2025-03-25 11:09:28 +08:00
d71f27e64c refactor: 添加不需要记录日志的api过滤 2025-03-25 11:09:12 +08:00
efb6151fc1 refactor: 修改文章验证 2025-03-25 10:47:53 +08:00
900b94ca78 refactor: 修改migration文件 2025-03-25 10:36:42 +08:00
9ac1fe00d9 refactor: 验证码去除大小写区别 2025-03-25 10:36:23 +08:00
7305baf66b refactor: 修改migration文件 2025-03-20 11:00:05 +08:00
2445fb7e14 refactor: 修改后台接口分组名映射 2025-03-19 09:12:53 +08:00
4d43c26a5e refactor: 横幅及导航新增用于回显的链接数据字段 2025-03-18 15:23:14 +08:00
36f9c8431b refactor: 完善日志记录处理 2025-03-18 15:22:50 +08:00
e4c600c3e6 refactor: 修改系统接口migration文件 2025-03-18 09:53:37 +08:00
783599b3b4 feat: 新增系统接口表模型 2025-03-18 09:52:42 +08:00
3add58d6ec refactor: 修改migration文件 2025-03-15 16:55:32 +08:00
71b93c3f82 refactor: 修改系统页面链接接口 2025-03-15 15:33:28 +08:00
a5f35ac4be refactor: 修改数组转树状结构封装 2025-03-15 11:11:45 +08:00
218d5f0bbe refactor: 修改导航项链接类型字段 2025-03-15 10:36:25 +08:00
e4e5b6583f feat: 添加获取系统前台各模块url接口 2025-03-14 17:58:36 +08:00
32ccd0e367 feat: 添加获取系统信息接口 2025-03-14 15:50:51 +08:00
9710a7be12 feat: 添加退出接口 2025-03-14 14:40:56 +08:00
37c34c9896 feat: 添加国家列表接口 2025-03-14 14:07:46 +08:00
ca14923a3e refactor: 修改国家及语言表和模型 2025-03-14 11:54:08 +08:00
d797850feb feat: 添加产品询盘分页接口 2025-03-14 11:47:16 +08:00
91cb6124a3 feat: 添加批量采购询盘可选品类列表接口 2025-03-14 10:43:54 +08:00
7785661837 feat: 添加代理商分页/导出/企业规模列表接口 2025-03-13 18:01:36 +08:00
ced7db61b5 feat: 添加批量采购询盘分页/导出接口 2025-03-13 16:58:25 +08:00
8db458523e refactor: 修改递归函数level计算错误 2025-03-13 15:29:06 +08:00
694199ccd4 feat: 添加留言记录(联系我们)分页/导出接口 2025-03-13 11:54:42 +08:00
825ea45eb7 feat: 添加问答分页/详情/新增/更新/设置排序值/删除接口 2025-03-13 11:15:22 +08:00
27c04754aa refactor: 修改横幅(分类)校验规则 2025-03-13 10:49:56 +08:00
da9feeeb35 feat: 添加基类校验规则 2025-03-13 10:49:35 +08:00
073b94d3b9 refactor: 修改横幅(分类)关于唯一标识验证规则 2025-03-13 09:56:26 +08:00
78933cd7c3 refactor: 横幅分页接口添加图片字段输出 2025-03-13 09:15:40 +08:00
e14d348813 refactor: 修改横幅项分页接口 2025-03-12 18:08:13 +08:00
c2cd2219cc refactor: 修改站点配置项输出接口 2025-03-12 17:33:03 +08:00
76529d2428 refactor: 修改额外配置项联动配置验证 2025-03-12 17:31:51 +08:00
b9f52748cf refactor: 修改横幅验证 2025-03-12 16:55:19 +08:00
e5230f5a3b feat: 添加对应成本分类树接口 2025-03-12 16:36:24 +08:00
4cf9354864 feat: 添加横幅(分类)分页/详情/新增/更新/删除接口 2025-03-12 16:01:42 +08:00
b50333e558 refactor: 修改网站配置输出项接口 2025-03-12 15:27:06 +08:00
c4b53a7b3f refactor: 修改横幅项分页接口排序 2025-03-12 14:50:33 +08:00
32f5f52c5e refactor: 修改横幅项接口 2025-03-12 14:39:30 +08:00
1e0004a5c4 refactor: 修改横幅导出接口 2025-03-12 14:33:52 +08:00
dbe0a0176e refactor: 修改横幅数据项接口命名 2025-03-12 14:27:03 +08:00
ad110bdf7f refactor: 操作日志分页接口添加操作人筛选条件 2025-03-12 14:22:03 +08:00
02f673e259 refactor: 修改网站配置输出接口 2025-03-12 14:20:46 +08:00
310e4745f5 feat: 添加横幅相关接口 2025-03-12 13:48:40 +08:00
bae3206689 refactor: 修改model注释 2025-03-10 17:26:47 +08:00
aae1d094c9 feat: 添加操作日志列表接口 2025-03-10 17:02:23 +08:00
58892cf612 feat: 添加记录操作日志功能 2025-03-10 16:21:49 +08:00
2e7c0887ea refactor: 修改xlsx导出 2025-03-10 15:04:50 +08:00
43630eb20a refactor: 修改配置列表详情接口 2025-03-10 09:57:10 +08:00
307bb2bcf4 refactor: 修改处理配置附加项 2025-03-10 09:18:51 +08:00
00539bbe03 feat: 添加站点配置/配置项分页/配置项详情/配置项新增/配置项更新/配置项删除接口/站点配置项
接口/站点配置项更新接口/配置项类型接口/配置项分组接口
2025-03-07 17:57:02 +08:00
ed82819d6d refactor: 修改导航新增唯一标识处理 2025-03-07 10:30:54 +08:00
76e71741f1 refactor: 修改产品分类新增唯一标识处理 2025-03-07 10:30:54 +08:00
41444abd51 refactor: 修改导航新增验证场景 2025-03-07 09:45:41 +08:00
38938628ee refactor: 修改导航验证场景 2025-03-07 09:41:49 +08:00
7de54e5550 refactor: 修改产品分类父级不为自身或子分类 2025-03-06 18:02:23 +08:00
e7ea53c78e refactor: 修改导航父级不能为自身或子导航 2025-03-06 17:55:50 +08:00
1ad2f9b016 rrefactor: 修改菜父级不能为自身或子孙级 2025-03-06 17:52:03 +08:00
cd98608188 Revert "refactor: 去除导航树中pid字段输出"
This reverts commit d58a5df1fe.
2025-03-06 16:42:38 +08:00
d58a5df1fe refactor: 去除导航树中pid字段输出 2025-03-06 16:34:27 +08:00
8d159ebd0f refactor: 导航树/详情接口输出字段调整 2025-03-06 14:50:11 +08:00
b427627f77 feat: 新增导航分类分页/详情/新增/更新/删除接口 2025-03-06 11:19:20 +08:00
3835bdc1cc refactor: 完善导航相关接口 2025-03-05 17:14:32 +08:00
f9f8db24b6 feat: 新增菜单设置排序值接口 2025-03-04 11:05:30 +08:00
6c1d992046 refactor: 修改用户菜单权限接口 2025-03-04 11:00:39 +08:00
919e740073 refactor: 角色添加是否系统角色字段相关逻辑 2025-03-04 09:46:57 +08:00
c7e1cc5bed refactor: 修改用户分页接口 2025-03-04 09:39:43 +08:00
f9e5f6ceb2 refactor: 修改用户菜单权限输出结构 2025-03-03 17:53:20 +08:00
fd2d246736 refactor: 修改菜单状态校验 2025-03-03 14:15:39 +08:00
b5dae966e6 feat: 新增导航相关接口 2025-03-01 18:05:35 +08:00
d21a69d99d refactor: 菜单树形数据添加访问路径/组件路径输出 2025-03-01 17:13:18 +08:00
326da1eba3 refactor: 修改角色校验 2025-03-01 16:28:55 +08:00
d13796a549 feat: 新增获取用户菜单权限接口 2025-03-01 15:42:42 +08:00
25bcd0fdf3 refactor: 修改角色新增/更新 2025-03-01 14:49:27 +08:00
4536761024 refactor: 角色分类新增权限字段输出 2025-03-01 14:43:46 +08:00
dc752915d1 refactor: 修改角色相关接口 2025-03-01 14:23:56 +08:00
437f144e79 refactor: 修改菜单相关接口 2025-03-01 14:23:44 +08:00
b05c03281d refactor: 用户分页添加最后登录ip/最后登录时间输出 2025-03-01 14:23:05 +08:00
ecdb5c1976 feat: 新增菜单列表/分页/详情/新增/更新/导入/导出/删除接口 2025-03-01 11:41:14 +08:00
bc0db74bd7 refactor: 修改角色新增接口 2025-02-28 15:31:39 +08:00
199ef65cdf feat: 新增附件列表接口 2025-02-28 15:27:49 +08:00
eaa6781357 refactor: 修改产品属性特征模型 2025-02-28 15:16:30 +08:00
99604ebac9 feat: 新增视频分类列表接口 2025-02-28 15:07:48 +08:00
8032e26fd3 refactor: 修改产品属性列表接口 2025-02-28 14:55:30 +08:00
084b003abf feat: 新增附件上传接口 2025-02-28 14:48:35 +08:00
ca40dc3cc8 refactor: 修改图片/视频上传记录表名 2025-02-28 14:21:17 +08:00
c28363eef6 feat: 新增菜单能力权限表 2025-02-28 13:42:34 +08:00
9e6d1948fc refactor: 修改菜单表结构 2025-02-28 13:42:13 +08:00
96094004d9 refactor: 修改角色权限新增/更新接口 2025-02-28 13:41:31 +08:00
a0e90d0a06 refactor: 去除分类管理seo相关项 2025-02-28 10:09:47 +08:00
27935b529f refactor: 修改角色校验及新增/更新 2025-02-27 18:05:49 +08:00
eb3ca0b501 refactor: 修改系统菜单表结构及迁移文件 2025-02-27 17:53:53 +08:00
465405261e refactor: 视频分页接口添加视频分类返回 2025-02-27 17:34:41 +08:00
b3c90135fe refactor: 产品购买链接上传校验同型号同平台重复问题 2025-02-27 16:53:14 +08:00
bf5a50cc6a refactor: 修改产品购买链接分页接口 2025-02-27 16:25:38 +08:00
8fc50394fb refactor: 修改产品sku排序值可空 2025-02-27 14:57:51 +08:00
163c29f57b refactor: 修改角色相关接口 2025-02-27 14:49:21 +08:00
3cb69bb32a refactor: 修改自动写入时间的格式为在数据库配置文件配置 2025-02-27 14:36:26 +08:00
348d51a2c4 Revert "refactor: 导入xlsx添加exit防止因debug导致导出文件有问题"
This reverts commit 61bf631c2d.
2025-02-27 13:59:05 +08:00
61bf631c2d refactor: 导入xlsx添加exit防止因debug导致导出文件有问题 2025-02-27 10:41:47 +08:00
7df84dcbc2 refactor: 产品购买链接同型号平台校验 2025-02-26 13:42:19 +08:00
43b4332d57 feat: 添加角色分页/详情/新增/更新/删除接口 2025-02-26 12:01:32 +08:00
2bdfb24505 feat: 添加用户分页/详情/新增/更新/删除接口 2025-02-25 17:22:44 +08:00
3efff936fa refactor: 修改user相关模型名称 2025-02-25 15:44:32 +08:00
682998aceb feat: 附件(下载管理)添加禁/启用操作接口 2025-02-25 09:49:25 +08:00
7402c0a57f feat: 添加附件(下载管理)回收站分页/恢复/删除接口 2025-02-24 18:07:52 +08:00
c66d7a6761 feat: 添加附件(下载管理)分类分页/详情/新增/更新/设置排序值/删除接口 2025-02-21 15:49:39 +08:00
49eb6a8d3c style: 注释修改 2025-02-21 15:45:17 +08:00
8a5b1bdaa7 refactor: 视频信息分类添加软件删除 2025-02-21 15:35:51 +08:00
af9ab0f4ab feat: 添加附件(下载管理)分页/详情/新增/更新/设置排序值/删除接口 2025-02-21 15:28:26 +08:00
202c4a8a2c style: 注释修改 2025-02-21 15:11:30 +08:00
68a29ca984 refactor: 视频信息相关接口添加语言查询支持 2025-02-21 14:33:22 +08:00
f2c3200d28 style: 添加注释 2025-02-21 11:48:21 +08:00
aac33677cd feat: 添加视频回收站分页/恢复/删除接口 2025-02-21 10:56:28 +08:00
25005da55c style: 视频管理控制器代码格式调整 2025-02-21 09:45:17 +08:00
44710716a9 refactor: 视频分类删除检查分类下是否存在视频 2025-02-21 09:40:50 +08:00
e22b574ad1 refactor: 修改视频分类相关接口 2025-02-20 18:11:42 +08:00
2aa3af798c refactor: 修改视频分类分页接口输出字段 2025-02-20 18:00:15 +08:00
f800e5cd8b refactor: 修改视频分类控制器名称 2025-02-20 17:59:07 +08:00
e169da28a8 refactor: 去除视频信息删除时验证 2025-02-20 17:56:46 +08:00
248f97e5cd feat: 添加视频分类分页/详情/添加/更新/设置排序值/删除接口 2025-02-20 17:55:28 +08:00
1466cde240 refactor: xlsx导入封装 2025-02-20 17:19:48 +08:00
89dc6affcd refactor: 导出xlsx封装 2025-02-20 16:13:06 +08:00
aebdb3c799 feat: 添加视频信息导出接口 2025-02-20 15:46:41 +08:00
13d75dcb2c feat: 添加新增/更新/设置排序值/删除接口 2025-02-20 14:17:51 +08:00
e1697fd66d feat: 添加视频分页/详情接口 2025-02-20 09:16:11 +08:00
fd06b1b1ac refactor: 调整图片/视频上传 2025-02-19 17:02:06 +08:00
5c15fb83e1 feat: 添加视频上传接口 2025-02-19 16:33:36 +08:00
df0c545b90 refactor: 调整图片上传 2025-02-19 16:31:32 +08:00
8913ec7e5e feat: 添加产品购买链接相关接口 2025-02-19 14:36:42 +08:00
0ec541ab76 refactor: 产品导出健壮性修改 2025-02-18 10:52:46 +08:00
cbd0ca2086 refactor: 修改产品model字段信息 2025-02-18 10:45:22 +08:00
14c6a14628 style: 去除空格 2025-02-18 10:36:49 +08:00
jsasg
e46aabea97 refactor: 产品分类树添加排序值字段输出 2025-02-17 16:25:21 +08:00
jsasg
c112402c80 feat: 新增产品回收站相关接口 2025-02-17 15:26:46 +08:00
1798f6ea8c refactor: 修改产品验证器 2025-02-13 16:12:50 +08:00
d39df73150 refactor: 添加操作器注释 2025-02-13 15:49:46 +08:00
8582d93678 refactor: 修改语言接口 2025-02-13 15:39:55 +08:00
7bfb0cca1e feat: 新增文章分类设置排序值接口 2025-02-13 15:38:24 +08:00
2227390f77 refactor: 文章回收站相关接口修改 2025-02-13 15:30:19 +08:00
d3594feabd refactor: 文章评论相关接口修改 2025-02-13 15:29:34 +08:00
1724bf8afb feat: 新增文章设置排序值接口 2025-02-13 15:25:51 +08:00
94572876fb feat: 新增产品设置排序值接口 2025-02-13 15:18:11 +08:00
d469a2c9f2 refactor: 产品详细接口添加sku数据返回 2025-02-13 15:08:46 +08:00
a16e1a0ff1 refactor: 更新产品接口添加sku处理逻辑 2025-02-13 11:57:54 +08:00
585 changed files with 45443 additions and 756 deletions

View File

@@ -1,22 +0,0 @@
APP_DEBUG = true
DB_TYPE = mysql
DB_HOST = 120.79.27.160
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
[JWT]
JWT_TTL=3600
JWT_REFRESH_TTL=20160
SECRET=b43e6276644ed60e65c50d1b324ba10b
# 后台不需要登录的接口
[ADMIN_AUTH]
WHITE_LIST[] = v1/user/login
WHITE_LIST[] = v1/user/captcha

View File

@@ -1,22 +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
[JWT]
JWT_TTL=3600
JWT_REFRESH_TTL=20160
SECRET=b43e6276644ed60e65c50d1b324ba10b
# 后台不需要登录的接口
[ADMIN_AUTH]
WHITE_LIST[] = v1/user/login
WHITE_LIST[] = v1/user/captcha

View File

@@ -1,11 +1,96 @@
APP_DEBUG = true
DB_TYPE = mysql
DB_HOST = 127.0.0.1
DB_NAME = test
DB_USER = username
DB_PASS = password
DB_HOST = localhost
DB_NAME = orico-official-website
DB_USER = orico-ow
DB_PASS = 14Xi17NIK8V2qAXE8oMataHEsaR8lE
DB_PORT = 3306
DB_CHARSET = utf8
DB_CHARSET = utf8mb4
DB_PREFIX = ow_
DB_VERSION = 8
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

7
.gitignore vendored
View File

@@ -1,10 +1,15 @@
*.log
.env
composer.phar
composer.lock
.DS_Store
Thumbs.db
.env
.env.dev
.env.local
.env.prod
public/dist
public/opendoc
/.idea
/.vscode
/vendor

View File

@@ -51,6 +51,7 @@ class ExceptionHandle extends Handle
public function render($request, Throwable $e): Response
{
// 添加自定义异常处理机制
$request->exception_info = $e;
// 其他错误交给系统处理
return parent::render($request, $e);

View File

@@ -4,5 +4,12 @@ namespace app;
// 应用请求对象类
class Request extends \think\Request
{
protected $proxyServerIp = [];
public function __construct()
{
parent::__construct();
// 设置前端代理服务器IP地址让代理访问情况下正确获取真实客户端IP地址
$this->proxyServerIp = env('PROXY_SERVER_IP', ['120.79.27.160']);
}
}

View File

@@ -1,28 +1,135 @@
<?php
// 这是系统自动生成的公共文件
if (!function_exists('array_to_tree')) {
if (!function_exists('xlsx_reader')) {
/**
* 数组转换为树状结构
* @param array $data 数据
* @param int $pid 父级ID
* @param string $with 转换依据字段
* @param int $level 层级
* 读取Excel数据
* @param string $file Excel文件路径
* @param int $initial_row 初始行
* @param array $keys_map 列 -> 键 例:['A' => 'name', 'B' => 'age', 'C' => 'sex']
* @return array
*/
function array_to_tree(array $data, int $pid, string $with = 'pid', int $level = 1)
function xlsx_reader(string $file, int $initial_row = 1, array $keys_map = []): array
{
$ret = [];
foreach ($data as $item) {
if ($item[$with] == $pid) {
$item['level'] = $level;
$children = array_to_tree($data, $item['id'], $with, $level + 1);
if ($children) {
$item['children'] = $children;
$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader('Xlsx');
// 读取文件
$spreadsheet = $reader->load($file);
// 获取活动sheet
$sheet = $spreadsheet->getActiveSheet();
// 获取最大行
$max_row = $sheet->getHighestRow();
// 获取最大列
$max_col = $sheet->getHighestColumn();
$xlsx_data = [];
$first_col = ord('A');
$last_col = ord($max_col);
for ($row = $initial_row; $row <= $max_row; $row++) {
for ($col = $first_col; $col <= $last_col; $col++) {
$key = $col_chr = chr($col);
if (!empty($keys_map)) {
$key = $keys_map[$col_chr] ?? $col_chr;
}
$ret[] = $item;
$xlsx_data[$row][$key] = $sheet->getCell($col_chr . $row)->getValue();
}
}
return $ret;
return $xlsx_data;
}
}
if (!function_exists('xlsx_writer')) {
/**
* 构建Excel数据
* @param array $data 数据 例:[['name'=>'张三','age'=>18, 'sex'=>'男']]
* @param array $schema 表头信息 例:['name' => '姓名', 'age' => '年龄', 'sex' => '性别']
* @param string $filename 下载文件名称 默认为YmdHis时间
* @param string $output 输出方式 php://output 或 文件路径
* @return \think\Response
*/
function xlsx_writer(array|\think\model\Collection $data, array $schema, string $filename = '', string $output = 'php://output'): \think\Response
{
// 获取Spreadsheet对象
$spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
// 写入表头
$title = array_values($schema);
$title_col = 'A';
foreach ($title as $value) {
// 单元格内容写入
$sheet->setCellValue($title_col . '1', $value);
$title_col++;
}
// 写入数据
$row = 2;
$keys = array_keys($schema);
foreach ($data as $item) {
$data_col = 'A';
foreach ($keys as $key) {
$sheet->setCellValue($data_col . $row, $item[$key] ?? '');
$data_col++;
}
$row++;
}
flush();
ob_flush();
if ($filename == '') $filename = date('YmdHis');
$filename = urlencode($filename);
// 文件写入缓冲区
\PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, 'Xlsx')->save($output);
// 从缓冲区获取导出文件并通过Response返回解决thinkphp框架的Response无法获取设置的header问题
$file_data = ob_get_clean();
$response = \think\Response::create($file_data)->header([
'Access-Control-Expose-Headers' => 'Content-Disposition',
'Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; charset=UTF-8',
'Content-Disposition' => 'attachment;filename=' . $filename . '.xlsx',
'Cache-Control' => 'max-age=0'
]);
return $response;
}
}
if (!function_exists('strtobytes')) {
/**
* 字符串转字节
* @param string $str_size 字符串 例:'1MB' '2GB'
* @return int
*/
function strtobytes(string $str_size): int
{
preg_match('/(\d+)(\w+)/', $str_size, $matches);
if (!isset($matches[1])) {
throw new \Exception('请确认size大小');
}
if (!isset($matches[2])) {
throw new \Exception('请确认单位');
}
$size = intval($matches[1]);
$unit = $matches[2];
switch (strtolower($unit)) {
case 'kb':
$size *= 1024;
break;
case 'mb':
$size *= 1024 * 1024;
break;
case 'gb':
$size *= 1024 * 1024 * 1024;
break;
case 'tb':
$size *= 1024 * 1024 * 1024 * 1024;
default:
throw new \Exception('不支持[' . $unit . ']单位');
}
return $size;
}
}

View File

@@ -5,5 +5,5 @@
return [
// 不需要登录验证的接口
'white_list' => env('ADMIN_AUTH.WHITE_LIST', ['v1/user/login','1/user/captcha']),
'white_list' => env('ADMIN_AUTH.WHITE_LIST', ['v1/user/login','v1/user/captcha']),
];

68
app/admin/config/log.php Normal file
View File

@@ -0,0 +1,68 @@
<?php
// +----------------------------------------------------------------------
// | 日志设置
// +----------------------------------------------------------------------
return [
// 默认日志记录通道
'default' => 'MyFileLogger',
// 日志记录级别
'level' => [],
// 日志类型记录的通道 ['error'=>'email',...]
'type_channel' => [],
// 关闭全局日志写入
'close' => false,
// 全局日志处理 支持闭包
'processor' => null,
// 日志通道列表
'channels' => [
'file' => [
// 日志记录方式
'type' => 'File',
// 日志保存目录
'path' => '',
// 单文件日志写入
'single' => false,
// 独立日志级别
'apart_level' => [],
// 最大日志文件数量
'max_files' => 0,
// 使用JSON格式记录
'json' => false,
// 日志处理
'processor' => null,
// 关闭通道日志写入
'close' => false,
// 日志输出格式化
'format' => '[%s][%s] %s',
// 是否实时写入
'realtime_write' => false,
],
'MyFileLogger' => [
// 日志记录方式
'type' => \app\admin\driver\Logger::class,
// 日志保存目录
'path' => '',
// 单文件日志写入
'single' => false,
// 独立日志级别
'apart_level' => [],
// 最大日志文件数量
'max_files' => 0,
// 使用JSON格式记录
'json' => false,
// 日志处理
'processor' => null,
// 关闭通道日志写入
'close' => false,
// 日志输出格式化
'format' => '[%s][%s][%s] %s',
// 是否实时写入
'realtime_write' => false,
]
// 其它日志通道配置
],
];

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

@@ -0,0 +1,139 @@
<?php
declare (strict_types = 1);
namespace app\admin\controller\v1;
use app\admin\model\v1\AgentEnterpriseSizeTypeModel;
use app\admin\model\v1\AgentModel;
/**
* 代理商数据控制器
*/
class Agent
{
// 企业规模列表
public function enterpriseSizeTypes()
{
$types = AgentEnterpriseSizeTypeModel::field([
'name',
'value'
])
->language(request()->lang_id)
->order(['sort' => 'asc', 'id' => 'desc'])
->select();
return success('获取成功', $types);
}
// 分页
public function index()
{
$param = request()->param([
'corp_name',
'size_type',
'created_at',
'page/d' => 1,
'size/d' => 10
]);
$agents = AgentModel::field([
'id',
'ip',
'corp_name',
'email',
'phone',
'business_type',
'enterprise_size',
'address',
'message',
'created_at'
])
->with([
'businessType' => function ($query) {
$query->field(['name' => 'business_type_name', 'value']);
},
'enterpriseSizeType' => function ($query) {
$query->field(['name' => 'enterprise_size_name', 'value']);
}
])
->withSearch(['corp_name', 'created_at'], [
'corp_name' => $param['corp_name'] ?? null,
'created_at' => !empty($param['created_at']) ? explode(',', $param['created_at']) : null
])
->language(request()->lang_id)
->enterpriseSize($param['size_type'] ?? null)
->order(['id' => 'desc'])
->paginate([
'list_rows' => $param['size'],
'page' => $param['page'],
])
->bindAttr('businessType', ['business_type_name'])
->bindAttr('enterpriseSizeType', ['enterprise_size_name'])
->hidden(['business_type', 'businessType', 'enterprise_size', 'enterpriseSizeType']);
return success('获取成功', $agents);
}
// 导出
public function export()
{
$schema = [
'id' => 'ID',
'created_at' => '提交时间',
'ip' => 'IP',
'corp_name' => '公司名称',
'email' => '邮箱',
'phone' => '手机号',
'business_type_name' => '业务类型',
'enterprise_size_name' => '企业规模',
'address' => '公司地址',
'message' => '留言内容'
];
// 获取要导出的代理商数据
$agents = $this->getAgentExportData();
// 导出
return xlsx_writer($agents, $schema, '代理商申请列表' . date('YmdHis'));
}
// 获取要导出的代理商数据
private function getAgentExportData()
{
$param = request()->param([
'corp_name',
'size_type',
'created_at'
]);
return AgentModel::field([
'id',
'ip',
'corp_name',
'email',
'phone',
'business_type',
'enterprise_size',
'address',
'message',
'created_at'
])
->with([
'businessType' => function ($query) {
$query->field(['name' => 'business_type_name', 'value']);
},
'enterpriseSizeType' => function ($query) {
$query->field(['name' => 'enterprise_size_name', 'value']);
}
])
->withSearch(['corp_name', 'created_at'], [
'corp_name' => $param['corp_name'] ?? null,
'created_at' => !empty($param['created_at']) ? explode(',', $param['created_at']) : null
])
->language(request()->lang_id)
->enterpriseSize($param['size_type'] ?? null)
->order(['id' => 'desc'])
->select()
->bindAttr('businessType', ['business_type_name'])
->bindAttr('enterpriseSizeType', ['enterprise_size_name'])
->hidden(['business_type', 'businessType', 'enterprise_size', 'enterpriseSizeType']);
}
}

View File

@@ -5,9 +5,10 @@ namespace app\admin\controller\v1;
use app\admin\model\v1\ArticleModel;
use app\admin\validate\v1\ArticleValidate;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
/**
* 文章管理控制器
*/
class Article
{
// 文章列表
@@ -16,7 +17,7 @@ class Article
$param = request()->param([
'title',
'category_id',
'created_at',
'release_time',
'page/d' => 1,
'size/d' => 10,
]);
@@ -38,21 +39,17 @@ class Article
->language(request()->lang_id)
->where(function($query) use($param) {
if (isset($param['category_id'])) {
$query->where('category_id', '=', $param['category_id']);
if (str_contains($param['category_id'], ',') || is_array($param['category_id'])) {
$query->whereIn('category_id', $param['category_id']);
} else {
$query->where('category_id', '=', $param['category_id']);
}
}
})
->withSearch(['title', 'created_at'], (function() use($param) {
$condition = [];
if (isset($param['title'])) {
$condition['title'] = $param['title'];
}
if (isset($param['created_at'])) {
$condition = [
'created_at' => explode(',', $param['created_at'])
];
}
return $condition;
})())
->withSearch(['title', 'release_time'], [
'title' => $param['title']??null,
'release_time' => !empty($param['release_time']) ? explode(',', $param['release_time']) : null
])
->order('sort', 'desc')
->paginate([
'page' => $param['page'],
@@ -62,7 +59,8 @@ class Article
'category',
'category_id'
])
->bindAttr('category', ['category_name' => 'name']);
->bindAttr('category', ['category_name' => 'name'])
->each(fn($item) => $item->image = thumb($item->image));
return success('获取成功', $article);
}
@@ -70,13 +68,18 @@ class Article
// 文章详情
public function read()
{
$article = ArticleModel::withoutField([
$article = ArticleModel::with(['category' => function($query) {
$query->field(['id', 'name' => 'category_name']);
}])
->withoutField([
'language_id',
'updated_at',
'deleted_at',
])
->id(request()->param('id'))
->find();
->bypk(request()->param('id'))
->find()
?->bindAttr('category', ['category_name'])
->hidden(['category']);
if (is_null($article)) {
return error('文章不存在');
}
@@ -143,7 +146,7 @@ class Article
return error($validate->getError());
}
$article = ArticleModel::id($id)->find();
$article = ArticleModel::bypk($id)->find();
if (is_null($article)) {
return error('请确认操作对象是否存在');
}
@@ -154,11 +157,31 @@ class Article
return success('操作成功');
}
// 设置排序值
public function sort()
{
$id = request()->param('id');
$sort = request()->post('sort');
$article = ArticleModel::bypk($id)->find();
if (empty($article)) {
return error('请确认操作对象是否存在');
}
if ($sort != $article->sort) {
$article->sort = $sort;
if (!$article->save()) {
return error('操作失败');
}
}
return success('操作成功');
}
// 删除文章
public function delete()
{
$id = request()->param('id');
$article = ArticleModel::id($id)->find();
$article = ArticleModel::bypk($id)->find();
if (is_null($article)) {
return error('请确认操作对象是否存在');
}
@@ -174,13 +197,13 @@ class Article
{
$schema = [
'id' => 'ID',
'category_name' => '分类名称',
'title' => '标题',
'category_name' => '文章分类',
'title' => '文章名称',
'author' => '作者',
'source' => '来源',
'image' => '封面图片',
'link' => '外链',
'desc' => '描述',
'link' => '跳转链接',
'desc' => '文章描述',
'content' => '内容详情',
'recommend' => '是否推荐',
'sort' => '排序值',
@@ -189,7 +212,6 @@ class Article
'seo_title' => 'seo标题',
'seo_keywords' => 'seo关键词',
'seo_desc' => 'seo描述',
'enabled' => '是否启用',
'release_time' => '发布时间',
'created_at' => '添加时间',
'updated_at' => '最后更新时间'
@@ -198,62 +220,29 @@ class Article
// 获取导出数据
$data = $this->getExportArticleData();
// 获取Spreadsheet对象
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
// 写入表头
$title = array_values($schema);
$title_col = 'A';
foreach ($title as $value) {
// 单元格内容写入
$sheet->setCellValue($title_col . '1', $value);
$title_col++;
}
// 写入数据
$row = 2;
$keys = array_keys($schema);
foreach ($data as $item) {
$data_col = 'A';
foreach ($keys as $key) {
$sheet->setCellValue($data_col . $row, $item[$key]);
$data_col++;
}
$row++;
}
flush();
ob_flush();
$filename = date('YmdHms');
header('Access-Control-Expose-Headers: Content-Disposition');
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; Charset=UTF-8');
header('Content-Disposition: attachment;filename=' . $filename . '.xlsx');
header('Cache-Control: max-age=0');
$writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
$writer->save('php://output');
// 导出
return xlsx_writer($data, $schema, '文章列表' . date('YmdHis'));
}
// 获取文章导出数据
private function getExportArticleData()
{
$server = request()->server();
$image_host = $server['REQUEST_SCHEME'] . "://" . $server['SERVER_NAME'] . config('filesystem.disks.public.url') . '/';
$param = request()->param(['title', 'category_id', 'created_at']);
$image_host = $server['REQUEST_SCHEME'] . "://" . $server['SERVER_NAME'] . '/';
$param = request()->param(['title', 'category_id', 'release_time']);
$data = ArticleModel::field([
'*',
'CONCAT("' . $image_host . '", `image`)' => 'image',
'CASE WHEN recommend = 1 THEN "是" ELSE "否" END' => 'recommend',
'CASE WHEN enabled = 1 THEN "是" ELSE "否" END' => 'enabled',
'image',
'CASE WHEN recommend = 1 THEN "是" ELSE "否" END' => 'recommend'
])
->with('category', function($query) {
return $query->field(['id', 'name']);
})
->language(request()->lang_id)
->categoryNullable($param['category_id']??null)
->withSearch(['title', 'created_at'], [
'title' => $param['title']??'',
'created_at' => !empty($param['created_at'])?explode(',', $param['created_at']):''
->withSearch(['title', 'release_time'], [
'title' => $param['title']??null,
'release_time' => !empty($param['release_time'])?explode(',', $param['release_time']):null
])
->order('sort', 'desc')
->select()
@@ -262,7 +251,11 @@ class Article
'category_id',
'category',
])
->bindAttr('category', ['category_name' => 'name']);
->bindAttr('category', ['category_name' => 'name'])
->each(function ($item) use($image_host) {
$item->image = !empty($item->image) ? url_join($image_host, $item->image) : '';
return $item;
});
return $data->toArray();
}

View File

@@ -6,38 +6,46 @@ namespace app\admin\controller\v1;
use app\admin\model\v1\ArticleCategoryModel;
use app\admin\validate\v1\ArticleCategoryValidate;
/**
* 文章分类管理控制器
*/
class ArticleCategory
{
// 分类列表
public function list()
{
$param = request()->param(['name' => '']);
$param = request()->param([
'name' => '',
'is_show',
]);
$categorys = ArticleCategoryModel::field([
'id',
'pid',
'name'
])
->language(request()->lang_id)
->withSearch(['name'], ['name' => $param['name']])
->where(function($query) use($param) {
if (isset($param['is_show'])) {
$query->where('is_show', '=', $param['is_show']);
}
})
->order('sort', 'asc')
->select();
return success('获取成功', $categorys);
return success('获取成功',array_to_tree($categorys->toArray(), 0, 'pid', 1, false));
}
// 分类分页列表
public function index()
{
$param = request()->param([
'name',
'page/d' => 1,
'limit/d' => 10,
'name'
]);
$category = ArticleCategoryModel::withoutField([
'language_id',
'deleted_at',
'seo_title',
'seo_keywords',
'seo_desc',
'deleted_at'
])
->language(request()->lang_id)
->where(function($query) use($param) {
@@ -46,20 +54,17 @@ class ArticleCategory
}
})
->order('sort', 'asc')
->paginate([
'page' => $param['page'],
'list_rows' => $param['limit']
]);
->select();
return success('获取成功', $category);
return success('获取成功', array_to_tree($category->toArray(), 0, 'pid', 1, false));
}
// 分类详情
public function read()
{
$id = request()->param('id');
$category = ArticleCategoryModel::where('id', '=', $id)
->withoutField(['language_id', 'deleted_at'])
$category = ArticleCategoryModel::withoutField(['language_id', 'deleted_at'])
->bypk($id)
->find();
if (is_null($category)) {
return error('文章分类不存在');
@@ -71,17 +76,20 @@ class ArticleCategory
public function save()
{
$post = request()->post([
'unique_label',
'name',
'icon',
'pid',
'sort' => 0,
'is_show' => 1,
'seo_title',
'seo_keywords',
'seo_desc',
'is_show' => 1
]);
$data = array_merge($post, ['language_id' => request()->lang_id]);
$data = array_merge($post, [
'language_id' => request()->lang_id,
'unique_label' => $post['unique_label'] ?? uniqid('BANNER_')
]);
$valiate = new ArticleCategoryValidate;
if (!$valiate->check($data)) {
if (!$valiate->scene('add')->check($data)) {
return error($valiate->getError());
}
@@ -98,21 +106,20 @@ class ArticleCategory
$id = request()->param('id');
$put = request()->put([
'name',
'icon',
'pid',
'sort',
'is_show',
'seo_title',
'seo_keywords',
'seo_desc',
'is_show'
]);
$data = array_merge($put, ['language_id' => request()->lang_id]);
$valiate = new ArticleCategoryValidate;
if (!$valiate->check(array_merge($data, ['id' => $id]))) {
if (!$valiate->scene('edit')->check(array_merge($data, ['id' => $id]))) {
return error($valiate->getError());
}
$category = ArticleCategoryModel::where('id', '=', $id)->find();
if (is_null($category)) {
$category = ArticleCategoryModel::bypk($id)->find();
if (empty($category)) {
return error('请确认操作对象是否存在');
}
if (!$category->save($data)) {
@@ -122,11 +129,31 @@ class ArticleCategory
return success('操作成功');
}
// 设置排序值
public function sort()
{
$id = request()->param('id');
$sort = request()->post('sort');
$category = ArticleCategoryModel::bypk($id)->find();
if (empty($category)) {
return error('请确认操作对象是否存在');
}
if ($sort != $category->sort) {
$category->sort = $sort;
if (!$category->save()) {
return error('操作失败');
}
}
return success('操作成功');
}
// 删除分类
public function delete()
{
$id = request()->param('id');
$category = ArticleCategoryModel::where('id', '=', $id)->find();
$category = ArticleCategoryModel::bypk($id)->find();
if (is_null($category)) {
return error('请确认操作对象是否存在');
}

View File

@@ -4,9 +4,10 @@ declare (strict_types = 1);
namespace app\admin\controller\v1;
use app\admin\model\v1\ArticleLeaveMessageModel;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
/**
* 文章留言(评论)管理控制器
*/
class ArticleLeaveMessage
{
// 文章留言分页列表
@@ -56,7 +57,7 @@ class ArticleLeaveMessage
$id = request()->param('id');
// 审核/反审核
$message = ArticleLeaveMessageModel::id($id)->find();
$message = ArticleLeaveMessageModel::bypk($id)->find();
if (is_null($message)) {
return error('请确认操作对象');
}
@@ -75,7 +76,7 @@ class ArticleLeaveMessage
$id = request()->param('id');
// 删除
$message = ArticleLeaveMessageModel::id($id)->find();
$message = ArticleLeaveMessageModel::bypk($id)->find();
if (is_null($message)) {
return error('请确认操作对象');
}
@@ -103,40 +104,8 @@ class ArticleLeaveMessage
// 获取导出数据
$data = $this->getExportMessageData();
// 获取Spreadsheet对象
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
// 写入表头
$title = array_values($schema);
$title_col = 'A';
foreach ($title as $value) {
// 单元格内容写入
$sheet->setCellValue($title_col . '1', $value);
$title_col++;
}
// 写入数据
$row = 2;
$keys = array_keys($schema);
foreach ($data as $item) {
$data_col = 'A';
foreach ($keys as $key) {
$sheet->setCellValue($data_col . $row, $item[$key]);
$data_col++;
}
$row++;
}
flush();
ob_flush();
$filename = date('YmdHms');
header('Access-Control-Expose-Headers: Content-Disposition');
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; Charset=UTF-8');
header('Content-Disposition: attachment;filename=' . $filename . '.xlsx');
header('Cache-Control: max-age=0');
$writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
$writer->save('php://output');
// 导出
return xlsx_writer($data, $schema, '文章评论列表' . date('YmdHis'));
}
private function getExportMessageData()
{
@@ -146,7 +115,7 @@ class ArticleLeaveMessage
'CASE WHEN is_audited = 1 THEN "已审核" ELSE "未审核" END' => 'is_audited'
])
->withJoin(['article' => function($query) use($param) {
$query->where('article.language_id', '=', $param['lang_id']);
$query->where('article.language_id', '=', request()->lang_id);
if (!empty($param['title'])) {
$query->where('article.title', 'like', '%' . $param['title'] . '%');
}

View File

@@ -5,6 +5,9 @@ namespace app\admin\controller\v1;
use app\admin\model\v1\ArticleModel;
/**
* 文章回收站管理控制器
*/
class ArticleTrash
{
// 文章回收站分页列表
@@ -24,7 +27,6 @@ class ArticleTrash
'sort',
'recommend',
'release_time',
'enabled',
])
->with('category')
->language(request()->lang_id)
@@ -37,7 +39,8 @@ class ArticleTrash
'list_rows' => $param['size']
])
->hidden(['category_id', 'category'])
->bindAttr('category', ['category_name' => 'name']);
->bindAttr('category', ['category_name' => 'name'])
?->each(fn($item) => $item->image = thumb($item->image));
return success('获取成功', $data);
}
@@ -50,7 +53,7 @@ class ArticleTrash
return error('参数错误');
}
$article = ArticleModel::onlyTrashed()->id($id)->find();
$article = ArticleModel::onlyTrashed()->bypk($id)->find();
if (is_null($article)) {
return error('请确认操作对象');
}
@@ -69,7 +72,7 @@ class ArticleTrash
return error('参数错误');
}
$article = ArticleModel::onlyTrashed()->id($id)->find();
$article = ArticleModel::onlyTrashed()->bypk($id)->find();
if (is_null($article)) {
return error('请确认操作对象');
}

View File

@@ -0,0 +1,207 @@
<?php
declare (strict_types = 1);
namespace app\admin\controller\v1;
use app\admin\model\v1\AttachmentModel;
use app\admin\validate\v1\AttachmentValidate;
/**
* 附件(下载管理)管理控制器
*/
class Attachment
{
/**
* 分页数据
*/
public function index()
{
$params = request()->param([
'name',
'category_id',
'created_at',
'page/d' => 1,
'size/d' => 10
]);
$attachments = AttachmentModel::field([
'id',
'image',
'name',
'category_id',
'sort',
'recommend',
'created_at'
])
->with(['category' => function ($query) {
$query->field(['id', 'name' => 'category_name']);
}])
->withSearch(['name', 'created_at'], [
'name' => $params['name']??null,
'created_at' => !empty($params['created_at']) ? explode(',', $params['created_at']) : null
])
->language(request()->lang_id)
->categoryId($params['category_id']??null)
->order(['sort' => 'asc', 'id' => 'desc'])
->paginate([
'list_rows' => $params['size'],
'page' => $params['page']
])
->bindAttr('category', ['category_name'])
->hidden(['category_id', 'category'])
?->each(fn($item) => $item->image = thumb($item->image));
return success('获取成功', $attachments);
}
/**
* 附件详情
*/
public function read()
{
$id = request()->param('id');
$attachment = AttachmentModel::with(['category' => function ($query) {
$query->field(['id', 'name' => 'category_name']);
}])
->field([
'id',
'category_id',
'name',
'desc',
'image',
'applicable_to',
'support_platform',
'attach',
'sort',
'recommend',
'seo_title',
'seo_keywords',
'seo_desc'
])
->bypk($id)
->find()
->bindAttr('category', ['category_name'])
->hidden(['category']);
if (empty($attachment)) {
return error('附件不存在');
}
return success('获取成功', $attachment);
}
/**
* 附件添加
*/
public function save()
{
$post = request()->post([
'name',
'desc',
'category_id',
'sort',
'recommend',
'image',
'applicable_to',
'support_platform',
'attach',
'seo_title',
'seo_keywords',
'seo_desc'
]);
$data = array_merge($post, ['language_id' => request()->lang_id]);
$validate = new AttachmentValidate;
$data['attach'] = json_decode($data['attach'], true);
if (!$validate->scene('create')->check($data)) {
return error($validate->getError());
}
$attachment = AttachmentModel::create($data);
if ($attachment->isEmpty()) {
return error('操作失败');
}
return success('操作成功');
}
/**
* 附件更新
*/
public function update()
{
$id = request()->param('id');
$put = request()->put([
'name',
'desc',
'category_id',
'sort',
'recommend',
'image',
'applicable_to',
'support_platform',
'attach',
'seo_title',
'seo_keywords',
'seo_desc'
]);
$validate = new AttachmentValidate;
$put['attach'] = json_decode($put['attach'], true);
if (!$validate->scene('update')->check(array_merge($put, ['id' => $id]))) {
return error($validate->getError());
}
$attachment = AttachmentModel::bypk($id)->find();
if (empty($attachment)) {
return error('请确认操作对象是否存在');
}
if (!$attachment->save($put)) {
return error('操作失败');
}
return success('操作成功');
}
/**
* 设置排序值
*/
public function sort()
{
$id = request()->param('id');
$sort = request()->post('sort');
$validate = new AttachmentValidate;
if (!$validate->scene('sort')->check(['sort' => $sort])) {
return error($validate->getError());
}
$attachment = AttachmentModel::bypk($id)->find();
if (empty($attachment)) {
return error('请确认操作对象是否存在');
}
$attachment->sort = $sort;
if (!$attachment->save()) {
return error('操作失败');
}
return success('操作成功');
}
/**
* 附件删除
*/
public function delete()
{
$id = request()->param('id');
$attachment = AttachmentModel::bypk($id)->find();
if (empty($attachment)) {
return error('请确认操作对象是否存在');
}
if (!$attachment->delete()) {
return error('操作失败');
}
return success('操作成功');
}
}

View File

@@ -0,0 +1,193 @@
<?php
declare (strict_types = 1);
namespace app\admin\controller\v1;
use app\admin\model\v1\AttachmentCategoryModel;
use app\admin\validate\v1\AttachmentCategoryValidate;
/**
* 附件(下载管理)分类管理控制器
*/
class AttachmentCategory
{
/**
* 列表树形结构
*/
public function tree()
{
$params = request()->param([
'name',
'is_show'
]);
$categorys = AttachmentCategoryModel::field([
'id',
'pid',
'name'
])
->withSearch(['name'], [
'name' => $params['name']??null
])
->where(function($query) use($params) {
if (isset($params['is_show'])) {
$query->where('is_show', '=', $params['is_show']);
}
})
->language(request()->lang_id)
->order(['sort' => 'asc', 'id' => 'desc'])
->select();
return success('获取成功', array_to_tree($categorys->toArray(), 0, 'pid', 1, false));
}
/**
* 分页数据
*/
public function index()
{
$params = request()->param([
'name'
]);
$categorys = AttachmentCategoryModel::field([
'id',
'pid',
'name',
'sort',
'is_show'
])
->withSearch(['name'], [
'name' => $params['name']??null
])
->language(request()->lang_id)
->order(['sort' => 'asc', 'id' => 'desc'])
->select();
return success('获取成功', array_to_tree($categorys->toArray(), 0, 'pid', 1, false));
}
/**
* 附件分类详情
*/
public function read()
{
$id = request()->param('id');
$category = AttachmentCategoryModel::withoutField([
'language_id',
'created_at',
'updated_at',
'deleted_at'
])
->bypk($id)
->find();
if (empty($category)) {
return error('分类不存在');
}
return success('获取成功', $category);
}
/**
* 附件分类添加
*/
public function save()
{
$post = request()->post([
'pid' => 0,
'name',
'sort',
'is_show'
]);
$data = array_merge($post, ['language_id' => request()->lang_id]);
$validate = new AttachmentCategoryValidate;
if (!$validate->scene('create')->check($data)) {
return error($validate->getError());
}
$category = AttachmentCategoryModel::create($data);
if ($category->isEmpty()) {
return error('操作失败');
}
return success('操作成功');
}
/**
* 附件分类更新
*/
public function update()
{
$id = request()->param('id');
$put = request()->put([
'pid' => 0,
'name',
'sort',
'is_show'
]);
$validate = new AttachmentCategoryValidate;
if (!$validate->scene('update')->check(array_merge($put, ['id' => $id]))) {
return error($validate->getError());
}
$category = AttachmentCategoryModel::bypk($id)->find();
if (empty($category)) {
return error('请确认操作对象是否存在');
}
if (!$category->save($put)) {
return error('操作失败');
}
return success('操作成功');
}
/**
* 设置排序值
*/
public function sort()
{
$id = request()->param('id');
$sort = request()->param('sort');
$validate = new AttachmentCategoryValidate;
if (!$validate->scene('sort')->check(['sort' => $sort])) {
return error($validate->getError());
}
$category = AttachmentCategoryModel::bypk($id)->find();
if (empty($category)) {
return error('请确认操作对象是否存在');
}
$category->sort = $sort;
if (!$category->save()) {
return error('操作失败');
}
return success('操作成功');
}
/**
* 附件分类删除
*/
public function delete()
{
$id = request()->param('id');
$category = AttachmentCategoryModel::bypk($id)->find();
if (empty($category)) {
return error('请确认操作对象是否存在');
}
// 检查分类下是否存在附件
if (!$category->attachment()->count()) {
return error('分类下存在附件,请先删除附件');
}
if (!$category->delete()) {
return error('操作失败');
}
return success('操作成功');
}
}

View File

@@ -0,0 +1,90 @@
<?php
declare (strict_types = 1);
namespace app\admin\controller\v1;
use app\admin\model\v1\AttachmentModel;
/**
* 附件(下载管理)回收站控制器
*/
class AttachmentTrash
{
/**
* 分页数据
*/
public function index()
{
$params = request()->param([
'name',
'category_id',
'page/d' => 1,
'size/d' => 10
]);
$attachments = AttachmentModel::field([
'id',
'image',
'name',
'category_id',
'sort',
'recommend',
'created_at'
])
->with(['category' => function ($query) {
$query->field(['id', 'name' => 'category_name']);
}])
->withSearch(['name'], [
'name' => $params['name']??null
])
->language(request()->lang_id)
->categoryId($params['category_id']??null)
->order(['sort' => 'asc', 'id' => 'desc'])
->onlyTrashed()
->paginate([
'list_rows' => $params['size'],
'page' => $params['page']
])
->bindAttr('category', ['category_name'])
->hidden(['category_id', 'category'])
?->each(fn($item) => $item->image = thumb($item->image));
return success('获取成功', $attachments);
}
/**
* 恢复操作
*/
public function restore()
{
$id = request()->param('id');
$attachment = AttachmentModel::onlyTrashed()->bypk($id)->find();
if (empty($attachment)) {
return error("请确认操作对象是否正确");
}
if (!$attachment->restore()) {
return error("操作失败");
}
return success("操作成功");
}
/**
* 删除操作
*/
public function delete()
{
$id = request()->param('id');
$attachment = AttachmentModel::onlyTrashed()->bypk($id)->find();
if (empty($attachment)) {
return error("请确认操作对象是否正确");
}
if (!$attachment->force()->delete()) {
return error("操作失败");
}
return success("操作成功");
}
}

View File

@@ -0,0 +1,180 @@
<?php
declare (strict_types = 1);
namespace app\admin\controller\v1;
use app\admin\model\v1\SysBannerModel;
use app\admin\validate\v1\SysBannerValidate;
/**
* 横幅(分类)模型
*/
class Banner
{
// 列表
public function list()
{
$param = request()->param([
'name'
]);
$banners = SysBannerModel::field([
'id',
'name',
'at_platform'
])
->withSearch(['name'], [
'name' => $param['name'] ?? null
])
->language(request()->lang_id)
->enabled()
->order(['sort' => 'asc', 'id' => 'desc'])
->select();
$datas = [];
if (!$banners->isEmpty()) {
$temp = [];
$map = ['pc' => ['id' => -1, 'name' => 'PC端'], 'mobile' => ['id' => -2, 'name' => '移动端']];
foreach ($banners as $banner) {
if (!isset($temp[$banner->at_platform])) {
$temp[$banner->at_platform] = [
'id' => $map[$banner->at_platform]['id'] ?? 0,
'name' => $map[$banner->at_platform]['name'] ?? '未知平台',
'children' => []
];
}
$temp[$banner->at_platform]['children'][] = [
'id' => $banner->id,
'name' => $banner->name
];
}
$datas = array_values($temp);
}
return success('获取成功', $datas);
}
// 分页
public function index()
{
$param = request()->param([
'name',
'page/d' => 1,
'size/d' => 10
]);
$banners = SysBannerModel::field([
'id',
'name',
'recommend',
'at_platform',
'desc',
'created_at'
])
->withSearch(['name'], [
'name' => $param['name'] ?? null
])
->language(request()->lang_id)
->order(['sort' => 'asc', 'id' => 'desc'])
->paginate([
'list_rows' => $param['size'],
'page' => $param['page']
]);
return success('获取成功', $banners);
}
// 详情
public function read()
{
$id = request()->param('id');
$banner = SysBannerModel::withoutField([
'at_page',
'unique_label',
'language_id',
'created_at',
'updated_at',
'deleted_at'
])
->bypk($id)
->find();
if (empty($banner)) {
return error('横幅(分类)不存在');
}
return success('获取成功', $banner);
}
// 新增
public function save()
{
$post = request()->post([
'name',
'desc',
'recommend',
'unique_label',
'at_platform' => 'pc',
'status' => 1
]);
$data = array_merge($post, [
'language_id' => request()->lang_id,
'unique_label' => $post['unique_label'] ?? uniqid('BANNER_')
]);
$validate = new SysBannerValidate;
if (!$validate->scene('add')->check($data)) {
return error($validate->getError());
}
$banner = SysBannerModel::create($data);
if ($banner->isEmpty()) {
return error('操作失败');
}
return success('操作成功');
}
// 修改
public function update()
{
$id = request()->param('id');
$put = request()->put([
'name',
'desc',
'recommend',
'at_platform' => 'pc',
'status' => 1
]);
$validate = new SysBannerValidate;
if (!$validate->scene('edit')->check(array_merge($put, ['id' => $id]))) {
return error($validate->getError());
}
$banner = SysBannerModel::bypk($id)->find();
if (empty($banner)) {
return error('请确认操作对象是否存在');
}
if (!$banner->save($put)) {
return error('操作失败');
}
return success('操作成功');
}
// 删除
public function delete()
{
$id = request()->param('id');
$banner = SysBannerModel::bypk($id)->find();
if (empty($banner)) {
return error('请确认操作对象是否存在');
}
if (!$banner->delete()) {
return error('操作失败');
}
return success('操作成功');
}
}

View File

@@ -0,0 +1,336 @@
<?php
declare (strict_types = 1);
namespace app\admin\controller\v1;
use app\admin\exception\InvalidOperateException;
use app\admin\model\v1\SysBannerItemModel;
use app\admin\model\v1\SysBannerProdCateMappingModel;
use app\admin\validate\v1\SysBannerItemValidate;
use think\facade\Log;
/**
* 横幅数据项控制器
*/
class BannerItem
{
// 分页
public function index()
{
$param = request()->param([
'title',
'banner_id',
'created_at',
'page/d' => 1,
'size/d' => 10
]);
$banner_items = SysBannerItemModel::alias('item')
->field([
'item.id',
'item.title',
'item.image',
'item.sort',
'item.status',
'item.created_at',
'bnr.name' => 'banner_name'
])
->join('sys_banner bnr', 'bnr.id = item.banner_id')
->where('bnr.language_id', '=', request()->lang_id)
->where(function($query) use($param){
if (!empty($param['banner_id'])) {
if (is_array($param['banner_id']) || str_contains($param['banner_id'], ',')) {
$query->whereIn('item.banner_id', $param['banner_id']);
} else {
$query->where('item.banner_id', '=', $param['banner_id']);
}
}
if (!empty($param['title'])) {
$query->where('item.title', 'like', "%{$param['title']}%");
}
if (!empty($param['created_at'])) {
$value = explode(',', $param['created_at']);
if (count($value) > 1) {
if ($value[1] == $value[0]) {
$value[1] = date('Y-m-d 23:59:59', strtotime($value[1]));
}
$query->whereBetweenTime('item.created_at', $value[0], $value[1]);
} else {
$query->whereTime('item.created_at', '>=', $value[0]);
}
}
})
->order(['item.banner_id' => 'asc', 'item.sort' => 'asc', 'item.id' => 'desc'])
->paginate([
'list_rows' => $param['size'],
'page' => $param['page']
])
->each(fn($item) => $item->image = thumb($item->image));
return success('获取成功', $banner_items);
}
// 详情
public function read()
{
$id = request()->param('id');
$banner_item = SysBannerItemModel::with('prodMapping.category')
->withoutField([
'created_at',
'updated_at',
'deleted_at'
])
->append(['rel_prod_cate_id', 'rel_prod_cate_name']) // 绑定产品分类关联模型中字段到父模型中
->find($id);
if (empty($banner_item)) {
return error('横幅不存在');
}
// 组装用于前台回显的链接数据
$banner_item['link_echo_data'] = System::getEchoDataBySystemPageUrl($banner_item['link_to'], $banner_item['link']);
return success('获取成功', $banner_item);
}
// 新增
public function save()
{
$post = request()->post([
'banner_id',
'rel_prod_cate_id',
'title',
'title_txt_color',
'desc',
'desc_txt_color',
'type',
'image',
'extra_image',
'video',
'link_to' => 'custom',
'link',
'sort',
'status' => 1
]);
$validate = new SysBannerItemValidate;
if (!$validate->scene('add')->check($post)) {
return error($validate->getError());
}
SysBannerItemModel::startTrans();
try {
$banner_item = SysBannerItemModel::create($post);
if ($banner_item->isEmpty()) {
throw new InvalidOperateException('新增横幅失败');
}
if (!empty($post['rel_prod_cate_id'])) {
$mapping = SysBannerProdCateMappingModel::create([
'banner_item_id' => $banner_item->id,
'product_category_id' => $post['rel_prod_cate_id']
]);
if ($mapping->isEmpty()) {
throw new InvalidOperateException('新增横幅与产品分类关联失败');
}
}
SysBannerItemModel::commit();
} catch (InvalidOperateException $e) {
SysBannerItemModel::rollback();
return error($e->getMessage());
} catch (\Throwable $th) {
SysBannerItemModel::rollback();
Log::error(sprintf('%s:%s %s', $th->getFile(), $th->getLine(), $th->getMessage()));
return error('操作失败');
}
return success('操作成功');
}
// 修改
public function update()
{
$id = request()->param('id');
$put = request()->put([
'banner_id',
'rel_prod_cate_id',
'title',
'title_txt_color',
'desc',
'desc_txt_color',
'type',
'image',
'extra_image',
'video',
'link_to',
'link',
'sort',
'status'
]);
$validate = new SysBannerItemValidate;
if (!$validate->check(array_merge($put, ['id' => $id]))) {
return error($validate->getError());
}
SysBannerItemModel::startTrans();
try {
$banner_item = SysBannerItemModel::bypk($id)->find();
if (empty($banner_item)) {
throw new InvalidOperateException('请确认操作对象是否存在');
}
if ($put['type'] == 'video') {
unset($put['link']);
unset($put['link_to']);
}
if (!$banner_item->save($put)) {
throw new InvalidOperateException('操作失败');
}
// 更新关联的产品分类
if (!empty($put['rel_prod_cate_id'])) {
SysBannerProdCateMappingModel::where('banner_item_id', '=', $id)->delete();
$mapping = SysBannerProdCateMappingModel::create([
'banner_item_id' => $id,
'product_category_id' => $put['rel_prod_cate_id']
]);
if ($mapping->isEmpty()) {
throw new InvalidOperateException('更新横幅与产品分类关联失败');
}
}
SysBannerItemModel::commit();
} catch (InvalidOperateException $e) {
SysBannerItemModel::rollback();
return error($e->getMessage());
} catch (\Throwable $th) {
SysBannerItemModel::rollback();
Log::error(sprintf('%s:%s %s', $th->getFile(), $th->getLine(), $th->getMessage()));
return error('操作失败');
}
return success('操作成功');
}
// 设置排序值
public function sort()
{
$id = request()->param('id');
$sort = request()->param('sort');
$banner_item = SysBannerItemModel::bypk($id)->find();
if (empty($banner_item)) {
return error('请确认操作对象是否存在');
}
if ($banner_item->sort != $sort) {
$banner_item->sort = $sort;
if (!$banner_item->save()) {
return error('操作失败');
}
}
return success('操作成功');
}
// 导出
public function export()
{
$schema = [
'id' => 'ID',
'banner_name' => '分类名称',
'title' => '横幅名称',
'title_txt_color' => '横幅名称字体颜色',
'desc' => '描述',
'desc_txt_color' => '描述字体颜色',
'type' => '前台显示类型',
'image' => '图片地址',
'extra_image' => '额外图片地址',
'video' => '视频地址',
'link_to' => '链接类型',
'link' => '链接地址',
'sort' => '排序值',
'status' => '状态',
'created_at' => '添加时间'
];
// 获取要导出的横幅项数据
$banner_items = $this->getBannerExportData();
// 导出
return xlsx_writer($banner_items, $schema, 'banner列表' . date('YmdHis'));
}
// 获取导出数据
private function getBannerExportData()
{
$server = request()->server();
$image_host = $server['REQUEST_SCHEME'] . "://" . $server['SERVER_NAME'] . '/';
$param = request()->param(['title', 'banner_id', 'created_at']);
return SysBannerItemModel::alias('item')
->field([
'item.id',
'banner.name' => 'banner_name',
'item.title',
'item.title_txt_color',
'item.desc',
'item.desc_txt_color',
'item.type',
'item.image',
'item.extra_image',
'item.video',
'item.link_to',
'item.link',
'item.sort' ,
'item.status',
'item.created_at'
])
->join('sys_banner banner', 'banner.id = item.banner_id')
->where('banner.language_id', '=', request()->lang_id)
->where(function($query) use($param){
if (!empty($param['banner_id'])) {
if (is_array($param['banner_id']) || str_contains($param['banner_id'], ',')) {
$query->whereIn('item.banner_id', $param['banner_id']);
} else {
$query->where('item.banner_id', '=', $param['banner_id']);
}
}
if (!empty($param['title'])) {
$query->where('item.title', 'like', "%{$param['title']}%");
}
if (!empty($param['created_at'])) {
$value = explode(',', $param['created_at']);
if (count($value) > 1) {
if ($value[1] == $value[0]) {
$value[1] = date('Y-m-d 23:59:59', strtotime($value[1]));
}
$query->whereBetweenTime('item.created_at', $value[0], $value[1]);
} else {
$query->whereTime('item.created_at', '>=', $value[0]);
}
}
})
->order(['item.sort' => 'asc', 'item.id' => 'desc'])
->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;
});
}
// 删除
public function delete()
{
$id = request()->param('id');
$banner_item = SysBannerItemModel::bypk($id)->find();
if (empty($banner_item)) {
return error('请确认操作对象是否存在');
}
if (!$banner_item->delete()) {
return error('操作失败');
}
return success('操作成功');
}
}

View File

@@ -0,0 +1,120 @@
<?php
declare (strict_types = 1);
namespace app\admin\controller\v1;
use app\admin\model\v1\BulkPurchaseInquiryModel;
use app\admin\model\v1\LanguageModel;
use app\admin\model\v1\SysConfigModel;
/**
* 批量采购询盘控制器
*/
class BulkPurchaseInquiry
{
// 采购可选品类
public function interested()
{
$config_name = 'bulk_purchase_inquiry_interested';
$config = SysConfigModel::hasWhere('group', function($query) {
$query->where('language_id', '=', request()->lang_id);
})
->byName($config_name)
->find();
if (empty($config)) {
return error('当前选定语言的采购可选品类配置出错');
}
return success('获取成功', explode(',', preg_replace('/\r?\n/', ',', $config->value)));
}
// 分页
public function index()
{
$param = request()->param([
'corp_name',
'interested',
'created_at',
'page/d' => 1,
'size/d' => 10
]);
$msgs = BulkPurchaseInquiryModel::field([
'id',
'ip',
'corp_name',
'first_name',
'last_name',
'email',
'phone',
'interested',
'message',
'created_at'
])
->withSearch(['corp_name', 'interested', 'created_at'], [
'corp_name' => $param['corp_name'] ?? null,
'interested' => $param['interested'] ?? null,
'created_at' => !empty($param['created_at']) ? explode(',', $param['created_at']) : null
])
->language(request()->lang_id)
->order(['id' => 'desc'])
->paginate([
'list_rows' => $param['size'],
'page' => $param['page'],
]);
return success('获取成功', $msgs);
}
// 导出
public function export()
{
$schema = [
'id' => 'ID',
'created_at' => '提交时间',
'ip' => 'IP',
'corp_name' => '公司名称',
'username' => '姓名',
'email' => '邮箱',
'phone' => '手机号码',
'interested' => '想采购的产品',
'message' => '询问内容',
];
// 获取要导出的采购询盘数据
$data = $this->getExportData();
// 导出
return xlsx_writer($data, $schema, '批量购买询盘列表' . date('YmdHis'));
}
// 获取要导出的采购询盘数据
private function getExportData()
{
$param = request()->param([
'corp_name',
'interested',
'created_at',
]);
return BulkPurchaseInquiryModel::field([
'id',
'ip',
'corp_name',
'CONCAT(first_name, last_name)' => 'username',
'last_name',
'email',
'phone',
'interested',
'message',
'created_at'
])
->withSearch(['corp_name', 'interested', 'created_at'], [
'corp_name' => $param['corp_name'] ?? null,
'interested' => $param['interested'] ?? null,
'created_at' => !empty($param['created_at']) ? explode(',', $param['created_at']) : null
])
->language(request()->lang_id)
->order(['id' => 'desc'])
->select();
}
}

View File

@@ -6,6 +6,9 @@ namespace app\admin\controller\v1;
use think\facade\Cache;
use think\facade\Config;
/**
* 验证码控制器
*/
class Captcha
{
/**

View File

@@ -0,0 +1,29 @@
<?php
declare (strict_types = 1);
namespace app\admin\controller\v1;
use app\admin\model\v1\CountryModel;
/**
* 国家列表控制器
*/
class Country
{
// 获取国家列表
public function list()
{
$country = CountryModel::field([
'id',
'name',
'en_name',
])
->withSearch(['name'], [
'name' => request()->param('name') ?? null
])
->order(['sort' => 'asc', 'id' => 'asc'])
->select();
return success('获取成功', $country);
}
}

View File

@@ -0,0 +1,155 @@
<?php
declare (strict_types = 1);
namespace app\admin\controller\v1;
use app\admin\model\v1\FaqModel;
use app\admin\validate\v1\FaqValidate;
/**
* 常见问题控制器
*/
class Faq
{
// 分页
public function index()
{
$param = request()->param([
'question',
'created_at',
'page/d' => 1,
'size/d' => 10
]);
$faqs = FaqModel::field([
'id',
'image',
'question',
'recommend',
'sort',
'created_at'
])
->withSearch(['question', 'created_at'], [
'question' => $param['question'] ?? null,
'created_at' => !empty($param['created_at']) ? explode(',', $param['created_at']) : null
])
->language(request()->lang_id)
->order(['sort' => 'asc', 'id' => 'desc'])
->paginate([
'list_rows' => $param['size'],
'page' => $param['page'],
])
?->each(fn($item) => $item->image = thumb($item->image));
return success('获取成功', $faqs);
}
// 详情
public function read()
{
$id = request()->param('id');
$faq = FaqModel::withoutField([
'language_id',
'created_at',
'updated_at',
'deleted_at',
])
->bypk($id)
->find();
if (empty($faq)) {
return error('数据不存在');
}
return success('获取成功', $faq);
}
// 新增
public function save()
{
$post = request()->post([
'question',
'image',
'recommend',
'sort',
'answer'
]);
$data = array_merge($post, ['language_id' => request()->lang_id]);
$validate = new FaqValidate;
if (!$validate->scene('add')->check($data)) {
return error($validate->getError());
}
$faq = FaqModel::create($data);
if ($faq->isEmpty()) {
return error('操作失败');
}
return success('操作成功');
}
// 修改
public function update()
{
$id = request()->param('id');
$put = request()->put([
'question',
'image',
'recommend',
'sort',
'answer'
]);
$validate = new FaqValidate;
if (!$validate->scene('edit')->check(array_merge($put, ['id' => $id]))) {
return error($validate->getError());
}
$faq = FaqModel::bypk($id)->find();
if (empty($faq)) {
return error('请确认操作对象是否存在');
}
if (!$faq->save($put)) {
return error('操作失败');
}
return success('操作成功');
}
// 设置排序值
public function sort()
{
$id = request()->param('id');
$sort = request()->post('sort');
$faq = FaqModel::bypk($id)->find();
if (empty($faq)) {
return error('请确认操作对象是否存在');
}
if ($faq->sort != $sort) {
$faq->sort = $sort;
if (!$faq->save()) {
return error('操作失败');
}
}
return success('操作成功');
}
// 删除
public function delete()
{
$id = request()->param('id');
$faq = FaqModel::bypk($id)->find();
if (empty($faq)) {
return error('请确认操作对象是否存在');
}
if (!$faq->delete()) {
return error('操作失败');
}
return success('操作成功');
}
}

View File

@@ -3,74 +3,10 @@ declare (strict_types = 1);
namespace app\admin\controller\v1;
use app\admin\model\v1\ImageModel;
use Intervention\Image\ImageManager;
use think\facade\Filesystem;
/**
* 图片管理控制器
*/
class Images
{
// 上传
public function upload()
{
$param = request()->param(['module' => 'unknown']);
if (is_null($param)) {
return error('请确定请求参数正确');
}
$file = request()->file('image');
if (is_null($file)) {
return error('请确定上传对象或字段是否正确');
}
try {
$validate = validate([
'module' => 'require|max:64',
'image' => 'fileSize:1048576|fileExt:jpg,jpeg,png,gif'
]);
if (!$validate->check(['module' => $param['module'], 'image' => $file])) {
return error($validate->getError());
}
$storage = config('filesystem.disks.image.url');
$filemd5 = $file->md5();
$filesha1 = $file->sha1();
$image_model = ImageModel::md5($filemd5)->find();
if (is_null($image_model)) {
$filename = Filesystem::disk('image')->putFile($param['module'], $file);
// 生成缩略图
$image_manager = new ImageManager(new \Intervention\Image\Drivers\Gd\Driver());
$image = $image_manager->read('.' . $storage . '/' . $filename);
$image->scale(200, 200);
$idx = strrpos($filename, '.');
$thumb_filename = mb_substr($filename, 0, $idx) . '_thumb.' . mb_substr($filename, $idx + 1);
$image->save('.' . $storage . '/' . $thumb_filename);
// 保存图片
$image_model = new ImageModel();
$image_model->language_id = request()->lang_id;
$image_model->module = $param['module'];
$image_model->image_path = $filename;
$image_model->image_thumb = $thumb_filename;
$image_model->image_size = $file->getSize();
$image_model->image_type = $file->getOriginalMime();
$image_model->image_md5 = $filemd5;
$image_model->image_sha1 = $filesha1;
if (!$image_model->save()) {
return error('上传失败');
}
}
return success('操作成功', [
'url' => $storage . '/' . $image_model->image_path,
'thumb_url' => $storage . '/' . $image_model->image_thumb,
'filemd5' => $image_model->image_md5,
'filesha1' => $image_model->image_sha1
]);
} catch (\Throwable $th) {
return error($th->getMessage());
}
return error('上传失败');
}
}

View File

@@ -7,6 +7,9 @@ use app\admin\model\v1\LanguageModel;
use think\facade\Cookie;
use think\facade\Log;
/**
* 语言管理控制器
*/
class Language
{
// 语言列表
@@ -32,7 +35,7 @@ class Language
public function cutover()
{
$id = request()->param('id');
$language = LanguageModel::id($id)->find();
$language = LanguageModel::bypk($id)->find();
if (is_null($language)) {
return error('语言不存在');
}

View File

@@ -0,0 +1,75 @@
<?php
declare (strict_types = 1);
namespace app\admin\controller\v1;
use app\admin\model\v1\LeaveMessageModel;
/**
* 留言记录(联系我们)控制器
*/
class LeaveMessage
{
// 分页
public function index()
{
$param = request()->param([
'created_at',
'page/d' => 1,
'size/d' => 10
]);
$msgs = LeaveMessageModel::withoutField([
'language_id',
'user_agent'
])
->withSearch(['created_at'], [
'created_at' => !empty($param['created_at']) ? explode(',', $param['created_at']) : null
])
->language(request()->lang_id)
->order(['id' => 'desc'])
->paginate([
'list_rows' => $param['size'],
'page' => $param['page']
]);
return success('获取成功', $msgs);
}
// 导出
public function export()
{
$schema = [
'created_at' => '提交时间',
'id' => 'ID',
'name' => '姓名',
'email' => '邮箱',
'ip' => 'IP',
'content' => '留言内容'
];
// 获取留言导出数据
$msgs = $this->getLeaveMessageExportData();
// 导出
return xlsx_writer($msgs, $schema, '联系我们列表' . date('YmdHis'));
}
// 获取留言导出数据
private function getLeaveMessageExportData()
{
$param = request()->param([
'created_at'
]);
return LeaveMessageModel::withoutField([
'language_id',
'user_agent'
])
->withSearch(['created_at'], [
'created_at' => !empty($param['created_at']) ? explode(',', $param['created_at']) : null
])
->language(request()->lang_id)
->order(['id' => 'desc'])
->select();
}
}

View File

@@ -0,0 +1,509 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\v1;
use app\admin\exception\InvalidOperateException;
use app\admin\model\v1\SysMenuAbilityPermissionModel;
use app\admin\model\v1\SysMenuModel;
use app\admin\validate\v1\SysMenuValidate;
use think\facade\Db;
use think\facade\Log;
/**
* 菜单控制器
*/
class Menu
{
// 菜单列表
public function list()
{
$params = request()->param(['title' => '']);
$menus = SysMenuModel::field([
'id',
'pid',
'title'
])
->with(['menu_ability_permission'])
->withSearch(['title'], [
'title' => $params['title'] ?? null
])
->enabled()
->order(['sort' => 'asc', 'id' => 'desc'])
->select()
->hidden(['menu_ability_permission.menu_id']);
return success('获取成功', array_to_tree($menus->toArray(), 0, 'pid'));
}
// 分页数据
public function index()
{
$params = request()->param(['title' => '']);
$menus = SysMenuModel::field([
'id',
'pid',
'icon',
'title',
'path',
'component',
'sort',
'hidden',
'status'
])
->withSearch(['title'], [
'title' => $params['title'] ?? null
])
->order(['sort' => 'asc', 'id' => 'desc'])
->select();
return success('获取成功', array_to_tree($menus->toArray(), 0, 'pid'));
}
// 详情数据
public function read()
{
$id = request()->param('id');
$menu = SysMenuModel::withoutField([
'created_at',
'updated_at',
'deleted_at'
])
->with(['menu_ability_permission'])
->bypk($id)
->find()
->hidden(['menu_ability_permission.menu_id']);
if (empty($menu)) {
return error('菜单不存在');
}
return success('获取成功', $menu);
}
// 新增数据
public function save()
{
$post = request()->post([
'pid' => 0,
'title',
'name',
'path',
'icon',
'redirect',
'component',
'hidden' => 0,
'actived' => 0,
'keep_alive' => 0,
'sort' => 0,
'status' => 1,
// $[*].ability_name 能力名称
// $[*].permission 权限标志
// $[*].sort 排序
'menu_ability_permission' => '[]'
]);
$menu_ability_permission = [];
if (!empty($post['menu_ability_permission'])) {
$menu_ability_permission = json_decode($post['menu_ability_permission'], true);
unset($post['menu_ability_permission']);
}
$validate = new SysMenuValidate;
$check_data = array_merge($post, ['menu_ability_permission' => $menu_ability_permission]);
if (!$validate->scene('create')->check($check_data)) {
return error($validate->getError());
}
SysMenuModel::startTrans();
try {
// 新增菜单
$menu = SysMenuModel::create($post);
if ($menu->isEmpty()) {
throw new InvalidOperateException('新增菜单失败');
}
// 新增菜单能力权限
if (!empty($menu_ability_permission)) {
foreach ($menu_ability_permission as &$item) {
$item['menu_id'] = $menu->id;
}
unset($item);
$permission = $menu->menuAbilityPermission()->saveAll($menu_ability_permission);
if (empty($permission)) {
throw new InvalidOperateException('新增菜单能力权限失败');
}
}
SysMenuModel::commit();
} catch (InvalidOperateException $e) {
SysMenuModel::rollback();
return error($e->getMessage());
} catch (\Throwable $th) {
SysMenuModel::rollback();
Log::error($th->getMessage());
return error('操作失败');
}
return success('操作成功');
}
// 更新数据
public function update()
{
$id = request()->param('id');
$put = request()->put([
'pid' => 0,
'title',
'name',
'path',
'icon',
'redirect',
'component',
'hidden' => 0,
'actived' => 0,
'keep_alive' => 0,
'sort' => 0,
'status' => 1,
// $[*].ability_name 能力名称
// $[*].permission 权限标志
// $[*].sort 排序
'menu_ability_permission' => '[]'
]);
$menu_ability_permission = [];
if (!empty($put['menu_ability_permission'])) {
$menu_ability_permission = json_decode($put['menu_ability_permission'], true);
unset($put['menu_ability_permission']);
}
$validate = new SysMenuValidate;
$check_data = array_merge($put, ['id' => $id, 'menu_ability_permission' => $menu_ability_permission]);
if (!$validate->check($check_data)) {
return error($validate->getError());
}
SysMenuModel::startTrans();
try {
$menu = SysMenuModel::bypk($id)->find();
if ($menu->isEmpty()) {
throw new InvalidOperateException('请确认操作对象是否存在');
}
// 更新菜单
if (!$menu->save($put)) {
throw new InvalidOperateException('更新菜单失败');
}
// 更新菜单能力权限
$exists = SysMenuAbilityPermissionModel::menuId($menu->id)->count();
if ($exists) {
$deleted = SysMenuAbilityPermissionModel::menuId($menu->id)->delete();
if (!$deleted) {
throw new InvalidOperateException('删除旧菜单能力权限失败');
}
}
if (!empty($menu_ability_permission)) {
foreach ($menu_ability_permission as &$item) {
$item['menu_id'] = $menu->id;
}
unset($item);
$permission = (new SysMenuAbilityPermissionModel)->saveAll($menu_ability_permission);
if (empty($permission)) {
throw new InvalidOperateException('更新菜单能力权限失败');
}
}
SysMenuModel::commit();
} catch (InvalidOperateException $e) {
SysMenuModel::rollback();
return error($e->getMessage());
} catch (\Throwable $th) {
SysMenuModel::rollback();
Log::error($th->getMessage());
return error('操作失败');
}
return success('操作成功');
}
// 导入数据
public function import()
{
// 获取上传文件
$file = request()->file('file');
if (empty($file)) {
return error('请上传文件');
}
// 读取文件
$keys_map = [
'A' => 'id',
'B' => 'pid',
'C' => 'title',
'D' => 'name',
'E' => 'path',
'F' => 'icon',
'G' => 'redirect',
'H' => 'component',
'I' => 'hidden',
'J' => 'actived',
'K' => 'keep_alive',
'L' => 'sort',
'M' => 'status',
'N' => 'menu_ability_permission'
];
$xlsx_data = xlsx_reader($file->getRealPath(), 2, $keys_map);
if (empty($xlsx_data)) {
return success('操作成功');
}
// 处理行序号,防止后续被打乱
foreach ($xlsx_data as $row => &$item) {
$item['seq_no'] = $row;
}
unset($item);
// 数据组装成树形结构,方便处理上下级关系
$xlsx_data_tree = array_to_tree($xlsx_data, 0, 'pid');
// 处理导入菜单数据
$handle_errors = $this->handleImportData($xlsx_data_tree);
if (!empty($handle_errors)) {
return error(implode(PHP_EOL, $handle_errors));
}
return success('操作成功');
}
// 处理导入数据
private function handleImportData($menus)
{
$errors = [];
$menu_model = new SysMenuModel;
$chunks = array_chunk($menus, 100, true);
foreach ($chunks as $chunk) {
// 执行保存
if (!$this->executeReplaceMenu($chunk)) {
$errors[] = sprintf('第【%s】行保存失败', implode(',', array_column($chunk, 'seq_no')));
continue;
}
// 获取该批次菜单以获取新增情况的菜单的id
$menus_map = $menu_model->whereIn('name', array_column($chunk, 'name'))->column('id', 'name');
foreach ($chunk as &$it) {
// 更改菜单的子菜单pid值为新值
if (!empty($it['children'])) {
foreach ($it['children'] as &$child) {
if (isset($menus_map[$child['name']])) {
$child['pid'] = $menus_map[$child['name']];
}
}
unset($child);
}
// 更改菜单能力权限的menu_id
if (!empty($it['menu_ability_permission'])) {
$menu_ability_permission = json_decode($it['menu_ability_permission'], true);
foreach ($menu_ability_permission as &$permission) {
if (isset($menus_map[$it['name']])) {
$permission['menu_id'] = $menus_map[$it['name']];
}
}
unset($permission);
$it['menu_ability_permission'] = $menu_ability_permission;
}
}
unset($it);
// 处理子菜单
$childrens = array_reduce(array_column($chunk, 'children'), 'array_merge', []);
$handle_errors = $this->handleImportData($childrens);
if (!empty($handler_ret)) {
$errors = array_merge($errors, $handle_errors);
}
// 更新菜单能力权限
$menu_ability_permissions = array_reduce(array_column($chunk, 'menu_ability_permission'), 'array_merge', []);
if (!empty($menu_ability_permissions)) {
// 菜单能力权限模型实例
$ability_permission_model = new SysMenuAbilityPermissionModel;
// 删除旧的菜单能力权限
$menus_id = array_column($menu_ability_permissions, 'menu_id');
$exists = $ability_permission_model->whereIn('menu_id', $menus_id)->count();
if ($exists) {
$deleted = $ability_permission_model->whereIn('menu_id', $menus_id)->delete();
if (!$deleted) {
$errors[] = sprintf('第【%s】行删除旧菜单能力权限失败', implode(',', array_column($chunk, 'seq_no')));
continue;
}
}
// 保存新的菜单能力权限
$ability_updated = $ability_permission_model->saveAll($menu_ability_permissions);
if ($ability_updated->isEmpty()) {
$errors[] = sprintf('第【%s】行保存新菜单能力权限失败', implode(',', array_column($chunk, 'seq_no')));
continue;
}
}
}
return $errors;
}
// 组装repalce语句的values
private function buildReplaceMenuValues($menu)
{
return sprintf(
'(%d, %d, "%s", "%s", "%s", "%s", "%s", "%s", %d, %d, %d, %d, %d)',
$menu['id'],
$menu['pid'],
$menu['title'],
$menu['name'],
$menu['path'],
$menu['icon'],
$menu['redirect'],
$menu['component'],
$menu['hidden'],
$menu['actived'],
$menu['keep_alive'],
$menu['sort'],
$menu['status']
);
}
// 执行repalce语句
private function executeReplaceMenu($menus)
{
// 获取已存在的菜单
$menus_map = SysMenuModel::whereIn('name', array_column($menus, 'name'))->column('id', 'name');
// 组装保存sql语句
$values = [];
foreach ($menus as &$it) {
if (isset($menus_map[$it['name']])) {
$it['id'] = $menus_map[$it['name']];
}
if ($it['level'] == 1) {
$it['pid'] = 0;
}
$values[] = $this->buildReplaceMenuValues($it);
}
unset($it);
return Db::execute(
sprintf(
'REPLACE INTO %s (
`id`,
`pid`,
`title`,
`name`,
`path`,
`icon`,
`redirect`,
`component`,
`hidden`,
`actived`,
`keep_alive`,
`sort`,
`status`
) VALUES %s;',
(new SysMenuModel)->getTable(),
implode(',', $values)
)
);
}
// 导出数据
public function export()
{
$schema = [
'id' => '菜单ID',
'pid' => '父级ID',
'title' => '菜单标题',
'name' => '菜单名称',
'path' => '菜单路径',
'icon' => '菜单图标',
'redirect' => '菜单重定向路径',
'component' => '菜单组件路径',
'hidden' => '是否隐藏',
'actived' => '是否激活',
'keep_alive' => '是否缓存',
'sort' => '排序',
'status' => '状态',
'menu_ability_permission' => '菜单能力权限'
];
// 获取导出数据
$data = $this->getExportMenuData();
// 导出
return xlsx_writer($data, $schema, '菜单列表' . date('YmdHis'), 'php://output', 'file');
}
private function getExportMenuData()
{
$param = request()->param(['title' => '']);
$menus = SysMenuModel::withoutField([
'created_at',
'updated_at',
'deleted_at'
])
->with(['menu_ability_permission'])
->withSearch(['title'], [
'title' => $param['title'] ?? null
])
->order(['id' => 'asc'])
->select()
->hidden(['menu_ability_permission.menu_id']);
if (!$menus->isEmpty()) {
$menus->each(function ($item) {
$item->menu_ability_permission = json_encode($item->menu_ability_permission);
});
}
return $menus;
}
// 设置排序值
public function sort()
{
$id = request()->param('id');
$sort = request()->post('sort');
$menu = SysMenuModel::bypk($id)->find();
if ($menu->isEmpty()) {
return error('请确认操作对象是否存在');
}
$menu->sort = $sort;
if (!$menu->save()) {
return error('操作失败');
}
return success('操作成功');
}
// 删除数据
public function delete()
{
$id = request()->param('id');
SysMenuModel::startTrans();
try {
$menu = SysMenuModel::bypk($id)->find();
if ($menu->isEmpty()) {
throw new InvalidOperateException('请确认操作对象是否存在');
}
if (!$menu->delete()) {
throw new InvalidOperateException('删除菜单失败');
}
SysMenuModel::commit();
} catch (InvalidOperateException $e) {
SysMenuModel::rollback();
return error($e->getMessage());
} catch (\Throwable $th) {
SysMenuModel::rollback();
Log::error($th->getMessage());
return error('操作失败');
}
return success('操作成功');
}
}

View File

@@ -0,0 +1,149 @@
<?php
declare (strict_types = 1);
namespace app\admin\controller\v1;
use app\admin\model\v1\SysNavigationModel;
use app\admin\validate\v1\NavigationValidate;
class Navigation
{
// 列表
public function list()
{
$name = request()->param([
'name',
]);
$navs = SysNavigationModel::field([
'id',
'name'
])
->withSearch(['name'], ['name' => $name['name']??null])
->language(request()->lang_id)
->select();
return success('获取成功', $navs);
}
// 分页
public function index()
{
$name = request()->param([
'name',
'page/d' => 1,
'size/d' => 10,
]);
$navs = SysNavigationModel::field([
'id',
'name',
'desc',
'created_at'
])
->withSearch(['name'], ['name' => $name['name']??null])
->language(request()->lang_id)
->paginate([
'list_rows' => $name['size'],
'page' => $name['page'],
]);
return success('获取成功', $navs);
}
// 详情
public function read()
{
$id = request()->param('id');
$nav = SysNavigationModel::withoutField([
'at_page',
'unique_label',
'language_id',
'created_at',
'updated_at',
'deleted_at'
])
->bypk($id)
->find();
if (empty($nav)) {
return error('导航不存在');
}
return success('获取成功', $nav);
}
// 新增
public function save()
{
$post = request()->post([
'unique_label' => '',
'name',
'at_platform' => 'pc',
'desc',
'status' => 1
]);
if (empty($post['unique_label'])) {
$post['unique_label'] = uniqid("NAV_");
}
$data = array_merge($post, ['language_id' => request()->lang_id]);
$validate = new NavigationValidate;
if (!$validate->scene('add')->check($data)) {
return error($validate->getError());
}
$nav = SysNavigationModel::create($data);
if ($nav->isEmpty()) {
return error('操作失败');
}
return success('操作成功');
}
// 修改
public function update()
{
$id = request()->param('id');
$put = request()->put([
'name',
'at_platform' => 'pc',
'desc',
'status' => 1
]);
$validate = new NavigationValidate;
if (!$validate->scene('edit')->check(array_merge($put, ['id' => $id]))) {
return error($validate->getError());
}
$nav = SysNavigationModel::bypk($id)->find();
if (empty($nav)) {
return error('请确认要操作对象是否存在');
}
if (!$nav->save($put)) {
return error('操作失败');
}
return success('操作成功');
}
// 删除
public function delete()
{
$id = request()->param('id');
$nav = SysNavigationModel::bypk($id)->find();
if (empty($nav)) {
return error('请确认要操作对象是否存在');
}
if (!$nav->delete()) {
return error('操作失败');
}
return success('操作成功');
}
}

View File

@@ -0,0 +1,181 @@
<?php
declare (strict_types = 1);
namespace app\admin\controller\v1;
use app\admin\model\v1\SysNavigationItemModel;
use app\admin\validate\v1\NavigationItemValidate;
/**
* 导航项管理控制器
*/
class NavigationItem
{
// 导航列表树
public function index()
{
$param = request()->get([
'name',
'nav_id',
'created_at'
]);
$navigations = SysNavigationItemModel::alias('item')
->field([
'item.id',
'item.pid',
'item.name',
'item.sort',
'item.blank',
'item.status',
'item.created_at',
'nav.name' => 'nav_name'
])
->join('sys_navigation nav', 'nav.id = item.nav_id')
->where('nav.language_id', '=', request()->lang_id)
->where(function($query) use($param) {
if (!empty($param['nav_id'])) {
$query->where('item.nav_id', '=', $param['nav_id']);
}
if (!empty($param['name'])) {
$query->where('item.name', 'like', "%{$param['name']}%");
}
if (!empty($param['created_at'])) {
$value = explode(',', $param['created_at']);
if (count($value) > 1) {
if ($value[1] == $value[0]) {
$value[1] = date('Y-m-d 23:59:59', strtotime($value[1]));
}
$query->whereBetweenTime('item.created_at', $value[0], $value[1]);
} else {
$query->whereTime('item.created_at', '>=', $value[0]);
}
}
})
->order(['item.nav_id' => 'asc', 'item.sort' => 'asc', 'item.id' => 'asc'])
->select();
return success('获取成功', array_to_tree($navigations->toArray(), 0, 'pid'));
}
// 导航详情
public function read()
{
$id = request()->param('id');
$nav = SysNavigationItemModel::field([
'id',
'pid',
'name',
'nav_id',
'sort',
'status',
'blank',
'link_to',
'link'
])
->bypk($id)
->find();
if (empty($nav)) {
return error('导航不存在');
}
// 组装用于前台回显的链接数据
$nav['link_echo_data'] = System::getEchoDataBySystemPageUrl($nav['link_to'], $nav['link']);
return success('获取成功', $nav);
}
// 导航新增
public function save()
{
$post = request()->post([
'pid',
'nav_id',
'name',
'icon',
'link_to' => 'custom',
'link',
'sort',
'blank' => 0,
'status' => 1
]);
$validate = new NavigationItemValidate;
if (!$validate->scene('add')->check($post)) {
return error($validate->getError());
}
$nav_item = SysNavigationItemModel::create($post);
if ($nav_item->isEmpty()) {
return error('操作失败');
}
return success('操作成功');
}
// 导航更新
public function update()
{
$id = request()->param('id');
$put = request()->put([
'pid',
'nav_id',
'name',
'icon',
'link_to',
'link',
'sort',
'blank' => 0,
'status' => 1
]);
$validate = new NavigationItemValidate;
if (!$validate->scene('edit')->check(array_merge($put, ['id' => $id]))) {
return error($validate->getError());
}
$nav_item = SysNavigationItemModel::bypk($id)->find();
if (empty($nav_item)) {
return error('请确认要操作对象是否存在');
}
if (!$nav_item->save($put)) {
return error('操作失败');
}
return success('操作成功');
}
// 设置排序值
public function sort()
{
$id = request()->param('id');
$sort = request()->post('sort');
$nav = SysNavigationItemModel::bypk($id)->find();
if (empty($nav)) {
return error('请确认要操作对象是否存在');
}
$nav->sort = $sort;
if (!$nav->save()) {
return error('操作失败');
}
return success('操作成功');
}
// 导航删除
public function delete()
{
$id = request()->param('id');
$nav = SysNavigationItemModel::bypk($id)->find();
if (empty($nav)) {
return error('请确认要操作对象是否存在');
}
if (!$nav->delete()) {
return error('操作失败');
}
return success('操作成功');
}
}

View File

@@ -0,0 +1,48 @@
<?php
declare (strict_types = 1);
namespace app\admin\controller\v1;
use app\admin\model\v1\SysOperateLog;
use app\admin\model\v1\SysUserModel;
/**
* 操作日志控制器
*/
class OperateLog
{
// 分页
public function index()
{
$param = request()->param([
'title',
'operator',
'created_at',
'page/d',
'size/d'
]);
$logs = SysOperateLog::field([
'SysOperateLog.*',
'SysUserModel.username'
])
->hasWhere('user', function($query) use($param) {
if (!empty($param['operator'])) {
$query->where('username', 'like', "%{$param['operator']}%");
}
$query->withTrashed()->field(['id', 'username']);
})
->withSearch(['title', 'created_at'], [
'title' => $param['title'] ?? null,
'created_at' => $param['created_at'] ?? null
])
->order(['id' => 'desc'])
->paginate([
'list_rows' => $param['size'] ?? 10,
'page' => $param['page'] ?? 1,
])
->hidden(['user_id', 'user']);
return success('获取成功', $logs);
}
}

View File

@@ -6,10 +6,13 @@ namespace app\admin\controller\v1;
use app\admin\model\v1\ProductModel;
use app\admin\model\v1\ProductParamsModel;
use app\admin\model\v1\ProductRelatedModel;
use app\admin\model\v1\ProductSkuAttrModel;
use app\admin\model\v1\ProductSkuModel;
use app\admin\validate\v1\ProductValidate;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
/**
* 产品管理控制器
*/
class Product
{
// 分页列表
@@ -54,13 +57,14 @@ class Product
])
->categoryNullable($param['category_id']??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([
'list_rows' => $param['size'],
'page' => $param['page'],
])
->bindAttr('category', ['category_name' => 'name'])
->hidden(['category_id', 'category']);
->hidden(['category_id', 'category'])
?->each(fn($item) => $item->cover_image = thumb($item->cover_image));
return success('获取成功', $products);
}
@@ -68,14 +72,19 @@ class Product
// 产品详情
public function read()
{
$product = ProductModel::withoutField([
$product = ProductModel::with(['category' => function($query) {
$query->field(['id', 'name' => 'category_name']);
}])
->withoutField([
'language_id',
'created_at',
'updated_at',
'deleted_at'
])
->bypk(request()->param('id'))
->find();
->find()
->bindAttr('category', ['category_name'])
->hidden(['category']);
if (empty($product)) {
return error('产品不存在');
}
@@ -88,6 +97,15 @@ class Product
}
$product->params = implode(PHP_EOL, $params);
// 获取sku数据
$product->skus = ProductSkuModel::withoutField(['created_at', 'updated_at'])
->with(['attrs' => function($query) {
$query->hidden(['sku_id']);
}])
->productId($product->id)
->select()
->hidden(['id', 'product_id']);
// 获取关联产品
$product->related = ProductRelatedModel::field([
'related_product_id',
@@ -123,19 +141,21 @@ class Product
'is_show',
'sort',
'detail',
'params' => '',
'params' => '',
'skus' => '',
'related' => '',
'status' => 1,
'seo_title',
'seo_keywords',
'seo_desc'
]);
$put = array_merge(
$put,
['skus' => json_decode($put['skus'], true)],
['related' => json_decode($put['related'], true)],
);
$validate = new ProductValidate();
$check_data = array_merge($put, [
'id' => $id,
'language_id' => request()->lang_id
]);
$check_data = array_merge($put, ['id' => $id, 'language_id' => request()->lang_id]);
if (!$validate->scene('update')->check($check_data)) {
return error($validate->getError());
}
@@ -151,7 +171,7 @@ class Product
// 更新产品参数
if ($put['params'] != "") {
ProductParamsModel::productId($id)->delete();
if (preg_match_all('/(\w+):(.[^\n|\r|\r\n]+)/', $put['params'], $match_result)) {
if (preg_match_all('/(.+):(.+)/', $put['params'], $match_result)) {
$params = [];
for ($i = 0; $i < count($match_result[0]); $i++) {
$params[] = [
@@ -160,24 +180,92 @@ class Product
'value' => $match_result[2][$i]
];
}
if (!empty($params)) ProductParamsModel::insertAll($params);
if (!empty($params)) {
ProductParamsModel::insertAll($params);
}
}
}
// 更新SKU
if (!empty($put['skus'])) {
$skus = [];
$attrs_group = [];
foreach ($put['skus'] as $val) {
$skus[] = [
'product_id' => $id,
'sku' => $val['sku'],
'main_image' => $val['main_image'],
'photo_album' => $val['photo_album'],
'sort' => $val['sort']??0
];
foreach ($val['attrs'] as $v) {
$attrs_group[$val['sku']][] = [
'attr_id' => $v['attr_id'],
'attr_value' => $v['attr_value']
];
}
}
if (!empty($skus)) {
$sku_model = new ProductSkuModel;
// 删除原有SKU
$sku_model->productId($id)->delete();
// 添加SKU
$save_ret = $sku_model->saveAll($skus);
if (!$save_ret->isEmpty()) {
$sku_map = [];
foreach ($save_ret as $val) {
$sku_map[$val->sku] = $val->id;
}
$attrs = [];
foreach ($attrs_group as $sku => $sku_attrs) {
if (empty($sku_map[$sku])) {
unset($attrs_group[$sku]);
continue;
}
foreach ($sku_attrs as $k => $v) {
$attrs[] = array_merge($v, ['sku_id' => $sku_map[$sku]]);
}
}
(new ProductSkuAttrModel)->saveAll($attrs);
}
}
}
// 更新关联产品
if ($put['related'] != "") {
if (!empty($put['related'])) {
// 删除原有关联产品
ProductRelatedModel::productId($id)->delete();
$encode_result = json_decode($put['related'], true);
if (!empty($encode_result)) {
$related = [];
foreach ($encode_result as $val) {
$related[] = [
'product_id' => $id,
'related_product_id' => $val['related_product_id'],
'sort' => $val['sort']
];
}
if (!empty($related)) ProductRelatedModel::insertAll($related);
// 添加关联产品
$related = [];
foreach ($put['related'] as $val) {
$related[] = [
'product_id' => $id,
'related_product_id' => $val['related_product_id'],
'sort' => $val['sort']
];
}
if (!empty($related)) {
ProductRelatedModel::insertAll($related);
}
}
return success('操作成功');
}
// 设置排序值
public function sort()
{
$id = request()->param('id');
$sort = request()->post('sort');
$product = ProductModel::bypk($id)->find();
if (empty($product)) {
return error('请确认操作对象是否存在');
}
if ($sort != $product->sort) {
$product->sort = $sort;
if (!$product->save()) {
return error('操作失败');
}
}
@@ -188,7 +276,7 @@ class Product
public function updownShelves()
{
$id = request()->param('id');
$product = ProductModel::bypk($id )->find();
$product = ProductModel::bypk($id)->find();
if (empty($product)) {
return error('请确认操作对象是否存在');
}
@@ -251,46 +339,14 @@ class Product
// 获取导出数据
$data = $this->getExportProductData();
// 获取Spreadsheet对象
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
// 写入表头
$title = array_values($schema);
$title_col = 'A';
foreach ($title as $value) {
// 单元格内容写入
$sheet->setCellValue($title_col . '1', $value);
$title_col++;
}
// 写入数据
$row = 2;
$keys = array_keys($schema);
foreach ($data as $item) {
$data_col = 'A';
foreach ($keys as $key) {
$sheet->setCellValue($data_col . $row, $item[$key]??'');
$data_col++;
}
$row++;
}
flush();
ob_flush();
$filename = date('YmdHms');
header('Access-Control-Expose-Headers: Content-Disposition');
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; Charset=UTF-8');
header('Content-Disposition: attachment;filename=' . $filename . '.xlsx');
header('Cache-Control: max-age=0');
$writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
$writer->save('php://output');
// 导出
return xlsx_writer($data, $schema, '产品列表' . date('YmdHis'));
}
// 获取产品导出数据
private function getExportProductData()
{
$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([
'name',
'spu',
@@ -304,10 +360,10 @@ class Product
'spu',
'name',
'short_name',
'CONCAT("' . $image_host . '", `cover_image`)' => 'cover_image',
'cover_image',
'desc',
'CONCAT("' . $image_host . '", `video_img`)' => 'video_img',
'CONCAT("' . $image_host . '", `video_url`)' => 'video_url',
'video_img',
'video_url',
'CASE WHEN is_new = 1 THEN "是" ELSE "否" END' => 'is_new',
'CASE WHEN is_hot = 1 THEN "是" ELSE "否" END' => 'is_hot',
'CASE WHEN is_sale = 1 THEN "是" ELSE "否" END' => 'is_sale',
@@ -334,7 +390,18 @@ class Product
->order(['id' => 'asc'])
->select()
->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()) {
// 产品参数
@@ -365,7 +432,9 @@ class Product
if (!$product_related->isEmpty()) {
$related = [];
foreach ($product_related as $item) {
$related[$item['product_id']][] = $item->product->spu;
if (!empty($item->product)) {
$related[$item['product_id']][] = $item->product->spu;
}
}
$products->each(function($product) use($related) {
if (empty($related[$product->id])) {

View File

@@ -7,6 +7,9 @@ use app\admin\model\v1\ProductAttrModel;
use app\admin\model\v1\ProductAttrPropModel;
use app\admin\validate\v1\ProductAttrValidate;
/**
* 产品属性管理控制器
*/
class ProductAttr
{
/**
@@ -37,7 +40,7 @@ class ProductAttr
'list_rows' => $params['size'],
'page' => $params['page'],
]);
} else if ('list' == request()->param('scene')) {
} else if ('all' == request()->param('scene')) {
$attrs = $attrs->select();
}
@@ -74,13 +77,12 @@ class ProductAttr
public function save()
{
$post = request()->post([
'attr_type' => 2,
'attr_type' => 1,
'attr_name' => '',
'is_system' => 0,
]);
$attr = array_merge($post, ['language_id' => request()->lang_id]);
// $[*].prop_type
// $[*].prop_name
// $[*].prop_value
$props = json_decode(request()->post('props/s', ''), true);
@@ -134,7 +136,6 @@ class ProductAttr
]);
$attr = array_merge($put, ['language_id' => request()->lang_id]);
// $[*].prop_type
// $[*].prop_name
// $[*].prop_value
$props = json_decode(request()->post('props/s', ''), true);

View File

@@ -6,6 +6,9 @@ namespace app\admin\controller\v1;
use app\admin\model\v1\ProductCategoryModel;
use app\admin\validate\v1\ProductCategoryValidate;
/**
* 产品分类管理控制器
*/
class ProductCategory
{
/**
@@ -14,6 +17,7 @@ class ProductCategory
public function index()
{
$params = request()->param([
'is_show',
'keywords' => '',
]);
@@ -22,12 +26,18 @@ class ProductCategory
'pid',
'name',
'level',
'sort',
'is_show'
])
->language(request()->lang_id)
->withSearch(['name_nullable'], [
'name_nullable' => $params['keywords']
'name_nullable' => $params['keywords']??null
])
->where(function($query) use($params) {
if (isset($params['is_show'])) {
$query->where('is_show', '=', $params['is_show']);
}
})
->select();
if ($ret->isEmpty()) {
return success('获取成功');
@@ -70,18 +80,15 @@ class ProductCategory
'related_tco_category',
'sort',
'level' => 1,
'is_show' => 1,
'seo_title',
'seo_keywords',
'seo_desc'
'is_show' => 1
]);
if (empty($post['unique_id'])) {
$post['unique_id'] = md5(uniqid());
$post['unique_id'] = uniqid('PRO_CATE_');
}
$data = array_merge($post, ['language_id' => request()->lang_id]);
$validate = new ProductCategoryValidate;
if (!$validate->check($data)) {
if (!$validate->scene('add')->check($data)) {
return error($validate->getError());
}
@@ -91,6 +98,9 @@ class ProductCategory
if ($data['pid'] > 0) {
$parent = $category->bypk($data['pid'])->find();
if (!empty($parent)) {
if (!empty($parent->path)) {
$data['path'] = $parent->path . ',' . $parent->id;
}
$data['level'] = $parent->level + 1;
}
}
@@ -116,10 +126,7 @@ class ProductCategory
'related_tco_category',
'sort',
'level' => 1,
'is_show' => 1,
'seo_title',
'seo_keywords',
'seo_desc'
'is_show' => 1
]);
$data = array_merge($put, [
'id' => $id,
@@ -141,6 +148,10 @@ class ProductCategory
if ($data['pid'] != $category->pid) {
$parent = ProductCategoryModel::bypk($data['pid'])->find();
if (!empty($parent)) {
$data['path'] = $parent->id;
if (!empty($parent->path)) {
$data['path'] = $parent->path . ',' . $data['path'];
}
$data['level'] = $parent->level + 1;
$updated_level = true;
}
@@ -152,21 +163,24 @@ class ProductCategory
// 处理子分类层级
if ($updated_level) {
$this->handle_children($category->id, $data['level']);
$this->handle_children($category->id, $data['path'], $data['level']);
}
return success('操作成功');
}
private function handle_children($pid, $level)
private function handle_children($pid, $path, $level)
{
$children = ProductCategoryModel::pid($pid)->select();
if ($children->isEmpty()) {
return;
}
foreach ($children as $child) {
if (!empty($path)) {
$child->path = $path. ','. $child->pid;
}
$child->level = $level + 1;
if ($child->save()) {
$this->handle_children($child->id, $child->level);
$this->handle_children($child->id, $child->path, $child->level);
}
}
}

View File

@@ -0,0 +1,50 @@
<?php
declare (strict_types = 1);
namespace app\admin\controller\v1;
use app\admin\model\v1\ProductInquiryModel;
/**
* 产品询盘记录控制器
*/
class ProductInquiry
{
// 分页
public function index()
{
$param = request()->param([
'corp_name',
'country_name',
'created_at',
'page/d' => 1,
'size/d' => 10
]);
$msgs = ProductInquiryModel::field([
'id',
'first_name',
'last_name',
'email',
'phone',
'corp_name',
'country_name',
'industry',
'message',
'created_at'
])
->withSearch(['corp_name', 'country_name', 'created_at'], [
'corp_name' => $param['corp_name'] ?? null,
'country_name' => $param['country_name'] ?? null,
'created_at' => !empty($param['created_at']) ? explode(',', $param['created_at']) : null,
])
->language(request()->lang_id)
->order(['id' => 'desc'])
->paginate([
'list_rows' => $param['size'],
'page' => $param['page'],
]);
return success('获取成功', $msgs);
}
}

View File

@@ -0,0 +1,357 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\v1;
use app\admin\model\v1\ProductModel;
use app\admin\model\v1\ProductPurchaseLinkModel;
use app\admin\model\v1\ProductPurchaseLinkPlatformModel;
use app\admin\validate\v1\ProductPurchaseLinkValidate;
use think\facade\Db;
class ProductPurchaseLink
{
/**
* 获取购买平台
*/
public function platforms()
{
$platforms = ProductPurchaseLinkPlatformModel::withoutField([
'language_id',
'desc',
'sort',
'created_at'
])
->language(request()->lang_id)
->order(['sort' => 'asc', 'id' => 'desc'])
->select();
return success('获取成功', $platforms);
}
/**
* 购买链接分页数据
*/
public function index()
{
$params = request()->param([
'name' => '',
'spu' => '',
'page/d' => 1,
'size/d' => 10
]);
$links = ProductModel::alias('pd')
->field([
'pd.id',
'IFNULL(pl.sort, 0)' => 'sort',
'pd.spu',
'pd.name',
'pd.is_show'
])
->leftJoin('product_purchase_link pl', 'pl.product_id = pd.id')
->leftJoin('product_purchase_platform pf', 'pf.id = pl.platform_id')
->where(function ($query) use ($params) {
$query->where('pd.is_show', '=', 1);
if (!empty($params['name'])) {
$query->where('pd.name', 'like', '%' . $params['name'] . '%');
}
if (!empty($params['spu'])) {
$query->where('pd.spu', 'like', '%' . $params['spu'] . '%');
}
})
->where('pd.language_id', '=', request()->lang_id)
->group('pd.id')
->order(['sort' => 'asc', 'pd.id' => 'desc'])
->hidden(['sort'])
->paginate([
'list_rows' => $params['size'],
'page' => $params['page'],
]);
if ($links->isEmpty()) {
return success('获取成功', []);
}
// 获取购买链接数据
$others = ProductPurchaseLinkModel::alias('pl')
->field([
'pl.product_id',
'pl.platform_id',
'pf.platform' => 'platform_name',
'pl.id' => 'link_id',
'pl.link'
])
->join('product_purchase_platform pf', 'pf.id = pl.platform_id')
->where('pl.language_id', '=', request()->lang_id)
->where('pl.product_id', 'in', array_column($links->items(), 'id'))
->select();
if (!$others->isEmpty()) {
$others_map = [];
$others_arr = $others->toArray();
foreach ($others_arr as $other) {
$product_id = $other['product_id'];
unset($other['product_id']);
$others_map[$product_id][] = $other;
}
$links->each(function ($item) use ($others_map) {
if (!empty($others_map[$item['id']])) {
$item['rowspan'] = $others_map[$item['id']];
}
return $item;
});
}
// 获取平台数据
$platforms = ProductPurchaseLinkPlatformModel::field(['id', 'platform'])
->language(request()->lang_id)
->select()
->toArray();
$platforms_map = [];
foreach ($platforms as $k => $v) {
$platforms_map[$v['id']] = [
'platform_id' => $v['id'],
'platform_name' => $v['platform'],
'link_id' => 0,
'link' => ''
];
}
// 没有购买链接的,补全平台数据
$links->each(function ($item) use($platforms_map) {
if (empty($item['rowspan'])) {
$item['rowspan'] = array_values($platforms_map);
} else if (count($item['rowspan']) < count($platforms_map)) {
foreach ($item['rowspan'] as $k => $v) {
if (!empty($platforms_map[$v['platform_id']])) {
unset($platforms_map[$v['platform_id']]);
}
}
$item['rowspan'] = array_merge($item['rowspan'], array_values($platforms_map));
}
return $item;
});
return success('获取成功', $links);
}
/**
* 导入
*/
public function import()
{
$file = request()->file('file');
if ($file->getSize() > 20 * 1024 * 1024) {
return error('上传文件不能超过20M');
}
// 从表格获取数据
$xlsx_data = xlsx_reader($file->getRealPath(), 2, ['A' => 'spu', 'B' => 'platform', 'C' => 'link']);
$platforms_name = array_unique(array_column($xlsx_data, 'platform'));
$platforms_map = ProductPurchaseLinkPlatformModel::language(request()->lang_id)
->where('platform', 'in', $platforms_name)
->column('id', 'platform');
// 表格中spu的平台重复次数
$spu_platform_map = [];
foreach ($xlsx_data as $v) {
if (!isset($spu_platform_map[$v['spu'] . '_' . $v['platform']])) {
$spu_platform_map[$v['spu'] . '_' . $v['platform']] = 0;
}
$spu_platform_map[$v['spu'] . '_' . $v['platform']]++;
}
$data = [];
$errors = [];
$chunks = array_chunk($xlsx_data, 500, true);
// 分组验证每行,并组装数据
foreach ($chunks as $chunk) {
$spus = array_unique(array_column($chunk, 'spu'));
$products_map = ProductModel::language(request()->lang_id)
->where('spu', 'in', $spus)
->column('id', 'spu');
$items = [];
foreach ($chunk as $r => $it) {
if (empty($platforms_map[$it['platform']])) {
$errors[] = sprintf("第%d行平台名称错误", $r);
continue;
}
if (empty($products_map[$it['spu']])) {
$errors[] = sprintf("第%d行型号不存在", $r);
continue;
}
if ($spu_platform_map[$it['spu'] . '_' . $it['platform']] > 1) {
$errors[] = sprintf("第%d行型号【%s】和平台【%s】有重复", $r, $it['spu'], $it['platform']);
continue;
}
$items[] = [
'language_id' => request()->lang_id,
'product_id' => $products_map[$it['spu']],
'platform_id' => $platforms_map[$it['platform']],
'link' => $it['link'],
'sort' => 0,
];
}
if (!empty($items)) {
$data[] = $items;
}
}
// 组装sql并执行
if (!empty($data)) {
$link_model = new ProductPurchaseLinkModel();
$sql = sprintf(
'REPLACE INTO %s (id, language_id, product_id, platform_id, link, sort) VALUES ',
$link_model->getTable(),
);
foreach ($data as $items) {
$products_id = array_unique(array_column($items, 'product_id'));
$links = $link_model->field([
'id',
'product_id',
'platform_id'
])
->where('product_id', 'in', $products_id)
->select();
$links_map = [];
if (!$links->isEmpty()) {
foreach ($links as $link) {
$links_map[$link['product_id'] . '_' . $link['platform_id']] = $link['id'];
}
}
foreach ($items as $item) {
$item['id'] = null;
if (!empty($links_map[$item['product_id'] . '_' . $item['platform_id']])) {
$item['id'] = $links_map[$item['product_id'] . '_' . $item['platform_id']];
}
$sql .= sprintf(
'(%d, %d, %d, %d, "%s", %d),',
$item['id'],
$item['language_id'],
$item['product_id'],
$item['platform_id'],
$item['link'],
$item['sort']
);
}
}
Db::execute(rtrim($sql, ','));
}
if (!empty($errors)) {
return error(implode(";\n", $errors));
}
return success('导入成功');
}
/**
* 导出
*/
public function export()
{
$schema = [
'spu' => '型号',
'platform' => '平台',
'link' => '链接'
];
// 获取导出数据
$data = $this->getExportLinkData();
/// 导出
return xlsx_writer($data, $schema, '产品购买链接' . date('YmdHis'));
}
private function getExportLinkData()
{
$params = request()->param([
'name' => '',
'spu' => ''
]);
return ProductModel::alias('pd')
->field([
'pd.spu',
'pf.platform',
'pl.link'
])
->leftJoin('product_purchase_link pl', 'pl.product_id = pd.id')
->leftJoin('product_purchase_platform pf', 'pf.id = pl.platform_id')
->where(function ($query) use ($params) {
$query->where('pd.is_show', '=', 1);
if (!empty($params['name'])) {
$query->where('pd.name', 'like', '%' . $params['name'] . '%');
}
if (!empty($params['spu'])) {
$query->where('pd.spu', 'like', '%' . $params['spu'] . '%');
}
})
->where('pd.language_id', '=', request()->lang_id)
->order(['pl.sort' => 'asc', 'pl.id' => 'desc', 'pd.id' => 'desc'])
->select();
}
/**
* 添加购买链接
*/
public function save()
{
$post = request()->post([
'link',
'product_id',
'platform_id'
]);
$data = [
'link' => $post['link'],
'language_id' => request()->lang_id,
'product_id' => $post['product_id'],
'platform_id' => $post['platform_id']
];
$validate = new ProductPurchaseLinkValidate;
if (!$validate->scene('add')->check($data)) {
return error($validate->getError());
}
$link = ProductPurchaseLinkModel::create($data);
if ($link->isEmpty()) {
return error('操作失败');
}
return success('操作成功');
}
/**
* 更新购买链接
*/
public function update()
{
$id = request()->param('id');
$put = request()->put([
'link',
'platform_id'
]);
$data = [
'id' => $id,
'link' => $put['link'],
'language_id' => request()->lang_id,
'platform_id' => $put['platform_id']
];
$validate = new ProductPurchaseLinkValidate;
if (!$validate->scene('edit')->check($data)) {
return error($validate->getError());
}
$link = ProductPurchaseLinkModel::bypk($data['id'])->find();
if (empty($link)) {
return error('请确认操作对象是否存在');
}
if (!$link->save($data)) {
return error('操作失败');
}
return success('操作成功');
}
}

View File

@@ -0,0 +1,35 @@
<?php
declare (strict_types = 1);
namespace app\admin\controller\v1;
use app\admin\model\v1\ProductTcoCategoryModel;
/**
* 产品 - 产品目录分类控制器
*/
class ProductTcoCategory
{
// 分类树
public function tree()
{
$param = request()->param(['name']);
$categorys = ProductTcoCategoryModel::field([
'id',
'tco_id',
'tco_pid',
'name',
])
->withSearch(['name'], [
'name' => $param['name'] ?? null,
])
->language(request()->lang_id)
->enabled()
->order(['tco_id' => 'asc'])
->select()
->toArray();
return success('获取成功', array_to_tree($categorys, 0, 'tco_pid', false, true, 'tco_id'));
}
}

View File

@@ -0,0 +1,102 @@
<?php
declare (strict_types = 1);
namespace app\admin\controller\v1;
use app\admin\model\v1\ProductModel;
use app\admin\validate\v1\ProductValidate;
/**
* 产品回收站管理控制器
*/
class ProductTrash
{
// 产品回回站分页列表
public function index()
{
$param = request()->param([
'name',
'spu',
'category_id',
'page/d' => 1,
'size/d' => 10
]);
$validate = new ProductValidate();
if (!$validate->scene('trash')->check($param)) {
return error($validate->getError());
}
$products = ProductModel::field([
'id',
'name',
'cover_image',
'spu',
'category_id',
'sort',
'is_new',
'is_sale',
'stock_qty',
'status',
'created_at',
'deleted_at'
])
->with(['category'])
->language(request()->lang_id)
->withSearch(['name_nullable', 'spu_nullable'], [
'name_nullable' => $param['name']??null,
'spu_nullable' => $param['spu']??null,
])
->categoryNullable($param['category_id']??null)
->onlyTrashed()
->order(['sort' => 'asc', 'id' => 'desc'])
->paginate([
'list_rows' => $param['size'],
'page' => $param['page'],
])
->bindAttr('category', ['category_name' => 'name'])
->hidden(['category_id', 'category'])
?->each(fn($item) => $item->cover_image = thumb($item->cover_image));
return success('获取成功', $products);
}
// 恢复
public function restore()
{
$id = request()->param('id');
if (!$id) {
return error('参数错误');
}
$product = ProductModel::onlyTrashed()->bypk($id)->find();
if (is_null($product)) {
return error('请确认操作对象');
}
if (!$product->restore()) {
return error('操作失败');
}
return success('操作成功');
}
// 删除
public function delete()
{
$id = request()->param('id');
if (!$id) {
return error('参数错误');
}
$product = ProductModel::onlyTrashed()->bypk($id)->find();
if (is_null($product)) {
return error('请确认操作对象');
}
if (!$product->force()->delete()) {
return error('操作失败');
}
return success('操作成功');
}
}

View File

@@ -0,0 +1,240 @@
<?php
declare (strict_types = 1);
namespace app\admin\controller\v1;
use app\admin\exception\InvalidOperateException;
use app\admin\model\v1\SysMenuModel;
use app\admin\model\v1\SysRoleAuthorityModel;
use app\admin\model\v1\SysRoleModel;
use app\admin\validate\v1\SysRoleValidate;
use think\facade\Log;
/**
* 角色管理控制器
*/
class Role
{
// 角色分页
public function index()
{
$params = request()->get([
'name',
'page/d' => 1,
'size/d' => 10
]);
$roles = SysRoleModel::withoutField([
'pid',
'created_at',
'updated_at',
'deleted_at'
])
->withSearch(['name'], [
'name' => $params['name']??null
])
->order('id', 'desc');
if (!request()->has('scene')) {
$roles = $roles->paginate([
'list_rows' => $params['size'],
'page' => $params['page']
]);
if (!$roles->isEmpty()) {
$roles_id = array_column($roles->items(), 'id');
$authority = SysMenuModel::alias('menu')
->field(['menu.title', 'authority.role_id'])
->join('sys_role_authority authority', 'authority.menu_id = menu.id')
->whereIn('authority.role_id', $roles_id)
->select();
if (!empty($authority)) {
$authority_map = [];
foreach ($authority as $v) {
$authority_map[$v['role_id']][] = $v['title'];
}
$roles->each(function($item) use($authority_map) {
$item['authority'] = [];
if (isset($authority_map[$item['id']])) {
$item['authority'] = array_unique($authority_map[$item['id']]);
}
});
}
}
} else if ('all' == request()->param('scene')) {
$roles = $roles->select();
}
return success('获取成功', $roles);
}
// 角色详情
public function read()
{
$id = request()->param('id');
$role = SysRoleModel::withoutField([
'pid',
'created_at',
'updated_at',
'deleted_at'
])
->with(['authorities'])
->bypk($id)
->find()
->hidden(['authorities.role_id']);
if (empty($role)) {
return error('角色不存在');
}
return success('获取成功', $role);
}
// 角色新增
public function save()
{
$post = request()->post([
'name',
'desc',
// $[*].menu_id
// $[*].permissions
'menu_permission' => '[]'
]);
$post['menu_permission'] = json_decode($post['menu_permission'], true);
$validate = new SysRoleValidate;
if (!$validate->scene('create')->check($post)) {
return error($validate->getError());
}
SysRoleModel::startTrans();
try {
$role = SysRoleModel::create($post, ['name', 'desc']);
if ($role->isEmpty()) {
throw new InvalidOperateException('角色新增失败');
}
if (!empty($post['menu_permission'])) {
$permissions = [];
foreach ($post['menu_permission'] as $menu) {
if (!isset($menu['permissions'])) {
$permissions[] = [
'role_id' => $role->id,
'menu_id' => $menu['menu_id'],
];
continue;
}
foreach ($menu['permissions'] as $permission) {
$permissions[] = [
'role_id' => $role->id,
'menu_id' => $menu['menu_id'],
'permission' => $permission
];
}
}
$authority = (new SysRoleAuthorityModel)->saveAll($permissions);
if ($authority->isEmpty()) {
throw new InvalidOperateException('角色权限新增失败');
}
}
SysRoleModel::commit();
} catch (InvalidOperateException $e) {
SysRoleModel::rollback();
return error($e->getMessage());
} catch (\Throwable $th) {
SysRoleModel::rollback();
Log::error($th->getMessage());
return error('操作失败');
}
return success('操作成功');
}
// 角色更新
public function update()
{
$id = request()->param('id');
$put = request()->put([
'name',
'desc',
// $[*].menu_id
// $[*].permissions
'menu_permission' => '[]'
]);
$put['menu_permission'] = json_decode($put['menu_permission'], true);
$validate = new SysRoleValidate;
if (!$validate->scene('update')->check(array_merge($put, ['id' => $id]))) {
return error($validate->getError());
}
SysRoleModel::startTrans();
try {
$role = SysRoleModel::bypk($id)->find();
if (empty($role)) {
throw new InvalidOperateException('角色不存在');
}
if (!$role->allowField(['name', 'desc'])->save($put)) {
throw new InvalidOperateException('角色更新失败');
}
if (!empty($put['menu_permission'])) {
$permissions = [];
foreach ($put['menu_permission'] as $menu) {
if (!isset($menu['permissions'])) {
$permissions[] = [
'role_id' => $role->id,
'menu_id' => $menu['menu_id'],
];
continue;
}
foreach ($menu['permissions'] as $permission) {
$permissions[] = [
'role_id' => $role->id,
'menu_id' => $menu['menu_id'],
'permission' => $permission
];
}
}
SysRoleAuthorityModel::roleId($role->id)->delete();
$authority = (new SysRoleAuthorityModel)->saveAll($permissions);
if ($authority->isEmpty()) {
throw new InvalidOperateException('角色权限更新失败');
}
}
SysRoleModel::commit();
} catch (InvalidOperateException $e) {
SysRoleModel::rollback();
return error($e->getMessage());
} catch (\Throwable $th) {
SysRoleModel::rollback();
Log::error($th->getMessage());
return error('操作失败');
}
return success('操作成功');
}
// 角色删除
public function delete()
{
$id = request()->param('id');
$role = SysRoleModel::bypk($id)->find();
if (empty($role)) {
return error('请确认要操作对象是否存在');
}
if (1 == $role->is_system) {
return error('该角色禁止删除');
}
if (!$role->delete()) {
return error('操作失败');
}
return success('操作成功');
}
}

View File

@@ -0,0 +1,230 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\v1;
use app\admin\model\v1\SysConfigGroupModel;
use app\admin\model\v1\SysConfigModel;
/**
* 站酷配置控制器
*/
class SiteConfig
{
// 获取配置项
public function index()
{
// 按语言获取分组
$groups = SysConfigGroupModel::field([
'id',
'name'
])
->language(request()->lang_id)
->enabled()
->order(['sort' => 'asc', 'id' => 'desc'])
->select()
->toArray();
if (empty($groups)) {
return error('配置分组不存在');
}
// 根据分组获取配置项
$configs = SysConfigModel::field([
'id',
'group_id',
'title',
'name',
'value',
'extra',
'type',
'remark'
])
->groupId(array_column($groups, 'id'))
->order(['sort' => 'asc', 'id' => 'desc'])
->select()
->each(function ($item) {
// 修改字段为null的输出为空字符串
$keys = array_keys($item->toArray());
foreach ($keys as $key) {
if (is_null($item[$key])) {
$item[$key] = '';
}
}
return $item;
})
->toArray();
// 处理附加配置项及联动项
$configs = $this->handleExtra($configs);
// 组合数据
$config_group_map = [];
foreach ($configs as $config) {
$group_id = $config['group_id'];
unset($config['group_id']);
$config_group_map[$group_id][] = $config;
}
// 组合分组和配置项
foreach ($groups as &$group) {
$group['configs'] = $config_group_map[$group['id']] ?? [];
}
unset($group);
return success('获取成功', $groups);
}
// 处理配置联动项数据
private function handleExtra($data)
{
list($list, $map) = $this->parseExtra($data);
return $this->buildExtra($list, $map);
}
private function parseExtra($data)
{
$linkage_names = [];
foreach ($data as &$val) {
if (!empty($val['extra'])) {
$extra = explode(PHP_EOL, $val['extra']);
$val['extra'] = [];
foreach ($extra as $v) {
if (preg_match('/^([^:]+):(.*?)(?:\[(.*?)\])?$/i', trim($v), $match)) {
$item = [
'name' => $match[2],
'value' => $match[1],
'linkage_names' => [],
];
if (isset($match[3])) {
$item['linkage_names'] = array_map(function ($it) {
return str_replace(['"', "'"], '', trim($it));
}, explode(',', $match[3]));
}
$linkage_names = array_merge($linkage_names, $item['linkage_names']);
$val['extra'][] = $item;
}
}
}
}
unset($val);
$linkage_data = [];
foreach ($data as $key => $val) {
if (in_array($val['name'], $linkage_names)) {
$linkage_data[$val['name']] = $val;
unset($data[$key]);
}
}
return [$data, $linkage_data];
}
private function buildExtra($data, $linkage_data)
{
$ret = [];
foreach ($data as $val) {
if (!empty($val['extra'])) {
foreach ($val['extra'] as &$v) {
$linkage_names = $v['linkage_names'];
unset($v['linkage_names']);
$children = [];
foreach ($linkage_names as $name) {
if (!empty($linkage_data[$name])) {
$children[] = $linkage_data[$name];
}
}
$v['children'] = $this->handleExtra($children);
}
unset($v);
}
$ret[] = $val;
}
return $ret;
}
// 根据分组获取配置
public function getByGroupUniqueLabel($unique_label)
{
$configs = SysConfigModel::alias('c')
->field([
'c.id',
'c.title',
'c.name',
'c.value',
])
->join(SysConfigGroupModel::getTable(). ' g', 'g.id = c.group_id')
->where('g.language_id', '=', request()->lang_id)
->where('g.unique_label', '=', $unique_label)
->where('g.status', '=', 1)
->order(['c.sort' => 'asc', 'c.id' => 'desc'])
->select()
->each(function ($item) {
// 修改字段为null的输出为空字符串
$keys = array_keys($item->toArray());
foreach ($keys as $key) {
if (is_null($item[$key])) {
$item[$key] = '';
}
}
return $item;
})
->toArray();
if (!empty($configs)) {
$configs_map = [];
foreach ($configs as $cfg) {
$current = &$configs_map;
// 根据name中"."拆分为多维数组
$parts = explode('.', $cfg['name']);
foreach ($parts as $part) {
if (!isset($current[$part])) {
$current[$part] = [];
}
$current = &$current[$part];
}
$current = [
'id' => $cfg['id'],
'title' => $cfg['title'],
'name' => $cfg['name'],
'value' => $cfg['value']
];
}
unset($current);
return $configs_map;
}
return [];
}
// 更新配置
public function update()
{
$put = request()->put();
if (empty($put)) {
return error('参数错误');
}
$validate = \think\facade\Validate::rule([
'id' => 'require|integer',
'value' => 'max:5120'
])
->message([
'id.require' => '配置项ID不能为空',
'id.integer' => '配置项ID必须是整数',
'value.max' => '配置值不能超过5120个字符'
]);
foreach ($put as $val) {
if (!$validate->check($val)) {
return error($validate->getError());
}
}
$configs = (new SysConfigModel)->saveAll($put);
if ($configs->isEmpty()) {
return error('操作失败');
}
return success('操作成功');
}
}

View File

@@ -0,0 +1,171 @@
<?php
declare (strict_types = 1);
namespace app\admin\controller\v1;
use app\admin\model\v1\SysConfigGroupModel;
use app\admin\model\v1\SysConfigModel;
use app\admin\model\v1\SysConfigTypeModel;
use app\admin\validate\v1\SysConfigValidate;
/**
* 配置项控制器
*/
class SysConfig
{
// 获取分组列表
public function groups()
{
$groups = SysConfigGroupModel::field([
'id',
'name'
])
->language(request()->lang_id)
->enabled()
->order(['sort' => 'asc', 'id' => 'desc'])
->select();
return success('获取成功', $groups);
}
// 获取配置类型
public function types()
{
$types = SysConfigTypeModel::field([
'name',
'value'
])
->order(['sort' => 'asc'])
->select();
return success('获取成功', $types);
}
// 配置项分页
public function index()
{
$param = request()->param([
'title',
'page/d' => 1,
'size/d' => 10
]);
$configs = SysConfigModel::alias('cfg')
->field([
'cfg.id',
'cfg.title',
'cfg.name',
'cfg.type',
'cfg.sort',
'grp.name' => 'group_name',
'type.name' => 'type_name'
])
->join('sys_config_group grp', 'grp.id = cfg.group_id')
->join('sys_config_type type', 'type.value = cfg.type')
->where('grp.language_id', '=', request()->lang_id)
->where(function($query) use($param) {
if (!empty($param['title'])) {
$query->where('cfg.title', 'like', "%{$param['title']}%");
}
})
->order(['cfg.group_id' => 'asc', 'cfg.sort' => 'asc', 'cfg.id' => 'desc'])
->paginate([
'list_rows' => $param['size'],
'page' => $param['page']
]);
return success('获取成功', $configs);
}
// 配置项详情
public function read()
{
$id = request()->param('id');
$config = SysConfigModel::withoutField([
'created_at',
'updated_at',
'deleted_at'
])
->bypk($id)
->find();
if (empty($config)) {
return error('配置项不存在');
}
return success('获取成功', $config);
}
// 配置项新增
public function save()
{
$post = request()->post([
'group_id',
'title',
'name',
'value',
'extra',
'type' => 'text',
'sort',
'remark'
]);
$validate = new SysConfigValidate;
if (!$validate->scene('add')->check($post)) {
return error($validate->getError());
}
$config = SysConfigModel::create($post);
if ($config->isEmpty()) {
return error('操作失败');
}
return success('操作成功');
}
// 配置项更新
public function update()
{
$id = request()->param('id');
$put = request()->put([
'group_id',
'title',
'name',
'value',
'extra',
'type' => 'text',
'sort',
'remark'
]);
$validate = new SysConfigValidate;
if (!$validate->check(array_merge($put, ['id' => $id]))) {
return error($validate->getError());
}
$config = SysConfigModel::bypk($id)->find();
if (empty($config)) {
return error('请确认要操作对象是否存在');
}
if (!$config->save($put)) {
return error('操作失败');
}
return success('操作成功');
}
// 配置项删除
public function delete()
{
$id = request()->param('id');
$config = SysConfigModel::bypk($id)->find();
if (empty($config)) {
return error('请确认要操作对象是否存在');
}
if (!$config->delete()) {
return error('操作失败');
}
return success('操作成功');
}
}

View File

@@ -0,0 +1,417 @@
<?php
declare (strict_types = 1);
namespace app\admin\controller\v1;
use app\admin\model\v1\ArticleCategoryModel;
use app\admin\model\v1\ArticleModel;
use app\admin\model\v1\ProductCategoryModel;
use app\admin\model\v1\ProductModel;
use think\facade\Db;
class System
{
// 系统信息
public function info()
{
$mysql_version = Db::query('select version() as version');
$info = [
[
'name' => '操作系统',
'value' => PHP_OS
],
[
'name' => '运行环境',
'value' => $_SERVER["SERVER_SOFTWARE"]
],
[
'name' => 'PHP版本',
'value' => PHP_VERSION
],
[
'name' => '上传附件限制',
'value' => ini_get('upload_max_filesize')
],
[
'name' => 'MySQL版本',
'value' => !empty($mysql_version) ? $mysql_version[0]['version'] : '未知'
],
[
'name' => '最大执行时间',
'value' => ini_get('max_execution_time') . 's'
],
[
'name' => 'GD版本',
'value' => function_exists('gd_info') ? (gd_info()['GD Version']) : '未知'
],
[
'name' => '最大占用内存',
'value' => ini_get('memory_limit')
],
[
'name' => '当前时间',
'value' => date('Y-m-d H:i:s')
],
[
'name' => '服务器时区',
'value' => function_exists("date_default_timezone_get") ? date_default_timezone_get() : '未知'
],
[
'name' => '是否开启安全模式',
'value' => ini_get('safe_mode') ? 'YES' : 'NO'
],
[
'name' => '允许打开远程连接',
'value' => ini_get("allow_url_fopen") ? 'YES' : 'NO'
],
[
'name' => 'CURL支持',
'value' => function_exists('curl_init') ? 'YES' : 'NO'
],
[
'name' => 'Zlib支持',
'value' => function_exists('gzclose') ? 'YES' : 'NO'
],
[
'name' => '自身版本',
'value' => '1.0.0'
],
[
'name' => '服务器域名/IP',
'value' => $_SERVER['HTTP_HOST'] . '[' . gethostbyname($_SERVER['SERVER_NAME']) . ']'
]
];
return success('获取成功', $info);
}
// 组装系统内页面URL
public function urls()
{
$lang_id = request()->lang_id;
if (request()->has('link_to')) {
$param = request()->get([
'link_to',
'id'
]);
$urls = [];
switch ($param['link_to']) {
case 'article':
$articles = $this->getArticleByCategory($lang_id, $param['id']);
$urls = array_map(function($item) {
$item['url'] = (string)url('/index/article/detail/' . $item['id']);
return $item;
}, $articles);
break;
case 'product':
$products = $this->getProductByCategory($lang_id, $param['id']);
$urls = array_map(function($item) {
$item['url'] = (string)url('/index/product/detail/' . $item['id']);
return $item;
}, $products);
break;
default:
return error('请确认link_to参数');
break;
}
return success('获取成功', $urls);
} else {
// 获取文章分类
$article_category = $this->getArticleCategory($lang_id);
// 获取产品分类
$product_category = $this->getProductCategory($lang_id);
$urls = [
[
'name' => '自定义',
'link_to' => 'custom',
'data' => []
],
[
'name' => '文章分类',
'link_to' => 'article_category',
'data' => array_to_tree(array_map(function($item) {
$item['url'] = (string)url('/index/article/index/' . $item['id']);
return $item;
}, $article_category), 0, 'pid', false, false)
],
[
'name' => '文章管理',
'link_to' => 'article',
'data' => array_to_tree($article_category, 0, 'pid', false, false)
],
[
'name' => '产品分类',
'link_to' => 'product_category',
'data' => array_to_tree(array_map(function($item) {
if ($item['pid'] == 0) {
$item['url'] = (string)url('/index/product/category/'. $item['id']);
} else {
$item['url'] = (string)url('/index/product/subcategory/' . $item['id']);
}
return $item;
}, $product_category), 0, 'pid', false, false)
],
[
'name' => '产品',
'link_to' => 'product',
'data' => array_to_tree($product_category, 0, 'pid', false, false)
],
[
'name' => '其他内页',
'link_to' => 'system_page',
'data' => self::getSystemOtherPages()
]
];
return success('获取成功', $urls);
}
}
// 获取文章分类数据
private function getArticleCategory($lang_id)
{
$data = ArticleCategoryModel::field([
'id',
'pid',
'name'
])
->language($lang_id)
->isShow(true)
->order(['sort' => 'asc', 'id' => 'desc'])
->select();
return $data->toArray();
}
// 获取产品分类数据
private function getProductCategory($lang_id)
{
$data = ProductCategoryModel::field([
'id',
'pid',
'name'
])
->language($lang_id)
->isShow(true)
->order(['sort' => 'asc', 'id' => 'desc'])
->select();
return $data->toArray();
}
// 根据文章分类获取文章
private function getArticleByCategory($lang_id, $category_id)
{
$data = ArticleModel::field([
'id',
'title' => 'name'
])
->language($lang_id)
->category($category_id)
->select();
return $data->toArray();
}
// 根据产品分类获取产品
private function getProductByCategory($lang_id, $category_id)
{
$data = ProductModel::field([
'id',
'name'
])
->language($lang_id)
->category($category_id)
->enabled()
->isShow(true)
->select();
return $data->toArray();
}
// 获取系统其他内页
static private function getSystemOtherPages()
{
return [
[
'id' => 1,
'name' => '首页',
'url' => (string)url('/index/index/index')
],
[
'id' => 2,
'name' => '新品上市',
'url' => (string)url('/index/product/newpro')
],
[
'id' => 3,
'name' => '附件下载',
'url' => (string)url('/index/attachment/index')
],
[
'id' => 4,
'name' => '问答中心',
'url' => (string)url('/index/faq/index')
],
[
'id' => 5,
'name' => '关于我们',
'url' => '',
'children' => [
[
'id' => 51,
'name' => '品牌介绍',
'url' => (string)url('/index/aboutus/introduction')
],
[
'id' => 52,
'name' => '品牌故事',
'url' => (string)url('/index/aboutus/story')
],
[
'id' => 53,
'name' => '品牌历程',
'url' => (string)url('/index/aboutus/mileage')
],
[
'id' => 54,
'name' => '文化介绍',
'url' => (string)url('/index/aboutus/culture')
],
[
'id' => 55,
'name' => '售后政策',
'url' => (string)url('/index/aboutus/policy')
]
]
],
[
'id' => 6,
'name' => '联系我们',
'url' => '',
'children' => [
[
'id' => 61,
'name' => '联系我们',
'url' => (string)url('/index/contactus/index')
],
[
'id' => 62,
'name' => '留言联系我们',
'url' => (string)url('/index/contactus/message')
],
[
'id' => 63,
'name' => '留言成为分销商',
'url' => (string)url('/index/contactus/distributor')
],
[
'id' => 64,
'name' => '留言批量购买',
'url' => (string)url('/index/contactus/bulkbuy')
]
]
],
[
'id' => 7,
'name' => 'NAS专题',
'url' => '',
'children' => [
[
'id' => 71,
'name' => '首页',
'url' => (string)url('/index/topic/nas/index')
],
[
'id' => 72,
'name' => '产品体验',
'url' => (string)url('/index/topic/nas/product')
],
[
'id' => 73,
'name' => '客户合作',
'url' => (string)url('/index/topic/nas/cooperation')
],
[
'id' => 74,
'name' => '帮助中心',
'url' => (string)url('/index/topic/nas/help')
],
[
'id' => 75,
'name' => '软件下载',
'url' => (string)url('/index/topic/nas/download')
]
]
]
];
}
// 根据系统页面url获取回显数据项
static public function getEchoDataBySystemPageUrl($link_to, $link)
{
if ('custom' == $link_to || empty($link)) return [];
$data = [];
$params = [];
$url = parse_url($link, PHP_URL_QUERY);
if (empty($url)) {
$parts = explode('/', trim($link, '/'));
$params['id'] = (int)str_replace('.html', '', end($parts));
} else {
parse_str($url, $params);
}
switch ($link_to) {
case 'article_category':
if (empty($params['id'])) return [];
$data = ArticleCategoryModel::field(['id', 'name'])->bypk($params['id'])->find();
break;
case 'article':
if (empty($params['id'])) return [];
$data = ArticleModel::field(['id', 'title' => 'name'])->bypk($params['id'])->find();
break;
case 'product_category':
if (empty($params['id'])) return [];
$data = ProductCategoryModel::field(['id', 'name'])->bypk($params['id'])->find();
break;
case 'product':
if (empty($params['id'])) return [];
$data = ProductModel::field(['id', 'name'])->bypk($params['id'])->find();
break;
case 'system_page':
$data = self::filterSystemOtherPage(self::getSystemOtherPages(), function($item) use ($params, $link) {
if (empty($params['id'])) return $item['url'] == $link;
return $item['id'] == $params['id'];
});
break;
default:
return [];
break;
}
if (empty($data)) return [];
return [
'id' => $data['id'],
'name' => $data['name'],
'link' => $link
];
}
// 根据条件过滤结果
static private function filterSystemOtherPage(array $data, callable $callback): array
{
foreach ($data as $it) {
if ($callback($it)) {
return $it;
}
if (isset($it['children'])) {
$child = self::filterSystemOtherPage($it['children'], $callback);
if (!empty($child)) {
return $child;
}
}
}
return [];
}
}

View File

@@ -0,0 +1,475 @@
<?php
declare (strict_types = 1);
namespace app\admin\controller\v1;
use app\admin\model\v1\SysImageUploadRecordModel;
use app\admin\model\v1\SysVideoUploadRecordModel;
use app\admin\model\v1\SysAttachmentUploadRecordModel;
use Intervention\Image\ImageManager;
use Intervention\Image\Typography\FontFactory;
use think\facade\Filesystem;
use filesystem\Qiniu;
/**
* 文件上传控制器
*/
class Upload
{
// 上传图片
public function image()
{
$param = request()->param(['module' => 'unknown']);
if (is_null($param)) {
return error('请确定请求参数正确');
}
$file = request()->file('image');
if (is_null($file)) {
return error('请确定上传对象或字段是否正确');
}
try {
$max_size = strtobytes(env('ADMIN_API.MAX_IMAGE_SIZE', '1mb'));
$validate = validate([
'module' => 'require|max:64',
'image' => "fileSize:$max_size|fileExt:jpg,jpeg,png,gif,webp"
]);
if (!$validate->check(['module' => $param['module'], 'image' => $file])) {
return error($validate->getError());
}
$storage = config('filesystem.disks.image.url');
$filemd5 = $file->md5();
$filesha1 = $file->sha1();
// 获取图片上传配置
list(
'filename_keep' => $filename_keep,
'filemd5_unique' => $filemd5_unique,
'filetype_to' => $filetype_to,
) = $this->getUploadOptions('upload_image');
// 获取文件大小
$file_size = $file->getSize();
// 获取文件mime类型
$mime_type = $file->getOriginalMime();
// 是否需要根据文件MD5值检查文件是否已存在
$image_model = $filemd5_unique ? SysImageUploadRecordModel::md5($filemd5)->find() : null;
if (is_null($image_model)) {
// 检查是否需要保留原文件名生成器
$name_rule = fn() => $filename_keep ? $this->filenameGenerator($file) : null;
// 处理图片
$image_manager = ImageManager::gd();
if ($filetype_to == 'original') {
$filename = Filesystem::disk('image')->putFile($param['module'], $file, $name_rule());
$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(
'enabled' => $enabled,
'type' => $type,
'text_options' => $text_options,
'image_options' => $image_options
) = $this->getWatermarkOptions();
if ($enabled) {
// 图片水印
if ($type == 'IMAGE' && $image_options['image'] != '') {
// 读取水印图片
$watermark_image = $image_manager->read(public_path() . $image_options['image']);
// 缩放水印图片
$watermark_image->scale($image_options['width'], $image_options['height']);
// 绘制水印图片
$image->place(
$watermark_image,
$image_options['position'],
$image_options['offset_x'],
$image_options['offset_y'],
$image_options['opacity']
);
}
// 文字水印
else if ($type == 'TEXT' && $text_options['txt'] != '') {
// 原图宽度
$origin_width = $image->width();
// 原图高度
$origin_height = $image->height();
$font_factory = new FontFactory(function(FontFactory $font) use($text_options) {
// 设置字体
$font->filename(public_path() . $text_options['font']);
// 设置字体大小
$font->size($text_options['size']);
// 设置字体颜色及透明度
$opacity = $text_options['opacity'] > 0 ? dechex((int)ceil(255 * ($text_options['opacity'] / 100))) : '00';
$font->color($text_options['color'] . $opacity);
$font->align('left');
$font->valign('top');
});
// 文字尺寸
$font_rect = $image->driver()->fontProcessor()->boxSize($text_options['txt'], $font_factory());
// 计算偏移量
list($offset_x, $offset_y) = $this->scaleTxtOffsetXYByPosition(
$text_options['position'],
$text_options['offset_x'],
$text_options['offset_y'],
$origin_width,
$origin_height,
$font_rect->width(),
$font_rect->height()
);
// 绘制文字
$image->text(
$text_options['txt'],
$offset_x,
$offset_y,
$font_factory()
);
}
$image->save('.'. $storage. '/'. $filename);
}
// 缩略图
$image->scale(200, 200);
$idx = strrpos($filename, '.');
$thumb_filename = mb_substr($filename, 0, $idx) . '_thumb.' . mb_substr($filename, $idx + 1);
$image->save('.' . $storage . '/' . $thumb_filename);
// 保存图片
$image_model = new SysImageUploadRecordModel();
$image_model->language_id = request()->lang_id;
$image_model->module = $param['module'];
$image_model->image_path = $storage . '/' . $filename;
$image_model->image_thumb = $storage . '/' . $thumb_filename;
$image_model->file_size = $file_size;
$image_model->file_type = $mime_type;
$image_model->file_md5 = $filemd5;
$image_model->file_sha1 = $filesha1;
if (!$image_model->save()) {
return error('上传失败');
}
}
return success('操作成功', [
'path' => $image_model->image_path,
'thumb_path' => $image_model->image_thumb,
'filemd5' => $image_model->file_md5,
'filesha1' => $image_model->file_sha1
]);
} catch (\Throwable $th) {
return error($th->getMessage());
}
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("上传目标目录不可用");
}
/**
* 文件名生成回调
*
* @param \think\file\UploadedFile $file
* @return callable
*/
private function filenameGenerator(\think\file\UploadedFile $file): callable
{
return fn() => date('Ymd') . '/' . pathinfo($file->getOriginalName(), PATHINFO_FILENAME);
}
/**
* 获取上传配置
*
* @param string $module
* @return array
*/
private function getUploadOptions($module)
{
$config_model = new \app\admin\controller\v1\SiteConfig;
$config = $config_model->getByGroupUniqueLabel('upload');
$options = data_get($config, $module, []);
return [
'filename_keep' => (int)data_get($options, 'filename_keep.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'),
];
}
/**
* 获取水印配置
*
* @return array
*/
private function getWatermarkOptions(): array
{
$config_model = new \app\admin\controller\v1\SiteConfig;
$watermark_config = $config_model->getByGroupUniqueLabel('watermark');
$opacity = data_get($watermark_config, 'watermark_opacity.value', 100);
if ($opacity == '') {
$opacity = 100;
}
return [
'enabled' => data_get($watermark_config, 'watermark_enabled.value', 0) == 1,
'type' => data_get($watermark_config, 'watermark_type.value', ''),
'text_options' => [
'txt' => data_get($watermark_config, 'watermark_text_value.value', ''),
'font' => data_get($watermark_config, 'watermark_text_font.value', ''),
'size' => (float)data_get($watermark_config, 'watermark_text_size.value', 12)?:12,
'color' => data_get($watermark_config, 'watermark_text_color.value', '#000000')?:'#000000',
'position' => data_get($watermark_config, 'watermark_position.value', 'top-left')?:'top-left',
'offset_x' => (int)data_get($watermark_config, 'watermark_offset_x.value', 0),
'offset_y' => (int)data_get($watermark_config, 'watermark_offset_y.value', 0),
'opacity' => (int)$opacity,
],
'image_options' => [
'image' => data_get($watermark_config, 'watermark_image_value.value', ''),
'width' => (int)data_get($watermark_config, 'watermark_image_width.value')?:null,
'height' => (int)data_get($watermark_config, 'watermark_image_height.value')?:null,
'position' => data_get($watermark_config, 'watermark_position.value', 'top-left')?:'top-left',
'offset_x' => (int)data_get($watermark_config, 'watermark_offset_x.value', 0),
'offset_y' => (int)data_get($watermark_config, 'watermark_offset_y.value', 0),
'opacity' => (int)$opacity,
]
];
}
/**
* 计算文本水印偏移量
*
* @param string $position
* @param integer $offset_x
* @param integer $offset_y
* @param integer $image_width
* @param integer $image_height
* @param integer $txt_width
* @param integer $txt_height
* @return array
*/
private function scaleTxtOffsetXYByPosition(string $position, int $offset_x, int $offset_y, int $image_width, int $image_height, int $txt_width, int $txt_height)
{
switch ($position) {
case 'top-left':
// top-left:左上角
return [$offset_x, $offset_y];
case 'top-right':
// top-right:右上角
return [(int)($image_width-$txt_width-$offset_x), $offset_y];
case 'top':
// top:上 - 水平居中
return [(int)(($image_width-$txt_width+$offset_x)/2), $offset_y];
case 'left':
// left:左 - 垂直居中
return [$offset_x, (int)(($image_height-$txt_height)/2+$offset_y)];
case 'center':
// center:水平垂直居中
return [(int)(($image_width-$txt_width)/2+$offset_x), (int)(($image_height-$txt_height)/2+$offset_y)];
case 'right':
// right:右 - 垂直居中
return [(int)($image_width-$txt_width-$offset_x), (int)(($image_height-$txt_height)/2+$offset_y)];
case'bottom':
// bottom:下 - 水平居中
return [(int)(($image_width-$txt_width+$offset_x)/2), (int)($image_height-$txt_height-$offset_y)];
case'bottom-left':
// bottom-left:左下角
return [$offset_x, (int)($image_height-$txt_height-$offset_y)];
case'bottom-right':
// bottom-right:右下角
return [(int)($image_width-$txt_width-$offset_x), (int)($image_height-$txt_height-$offset_y)];
default:
throw new \InvalidArgumentException('Invalid position');
}
}
/**
* 上传视频
*/
public function video()
{
$param = request()->param(['module' => 'unknown']);
if (is_null($param)) {
return error('请确定请求参数正确');
}
$file = request()->file('video');
if (is_null($file)) {
return error('请确定上传对象或字段是否正确');
}
try {
$max_size = strtobytes(env('ADMIN_API.MAX_VIDEO_SIZE', '100mb'));
$validate = validate([
'module' => 'require|max:64',
'video' => "fileSize:$max_size|fileExt:mp4"
]);
if (!$validate->check(['module' => $param['module'], 'video' => $file])) {
return error($validate->getError());
}
$storage = config('filesystem.disks.video.url');
$filemd5 = $file->md5();
$filesha1 = $file->sha1();
// 获取视频上传配置
list(
'filename_keep' => $filename_keep,
'filemd5_unique' => $filemd5_unique,
'save_to' => $save_to,
) = $this->getUploadOptions('upload_video');
// 是否需要根据文件MD5值检查文件是否已存在
$video = $filemd5_unique ? SysVideoUploadRecordModel::md5($filemd5)->find() : null;
if (is_null($video)) {
// 保存位置配置 key
$disk = 'video';
// 检查是否需要保留原文件名
$name_rule = fn() => $filename_keep ? $this->filenameGenerator($file) : null;
// 保存到七牛云
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->language_id = request()->lang_id;
$video->module = $param['module'];
$video->video_path = $video_path;
$video->file_size = $file->getSize();
$video->file_type = $file->getOriginalMime();
$video->file_md5 = $filemd5;
$video->file_sha1 = $filesha1;
if (!$video->save()) {
return error('上传失败');
}
}
return success('上传成功', [
'path' => $video->video_path,
'file_md5' => $video->file_md5,
'file_sha1' => $video->file_sha1
]);
} catch (\Throwable $th) {
return error($th->getMessage());
}
return error('上传失败');
}
/**
* 附件上传
*/
public function attachment()
{
$file = request()->file('attachment');
if (is_null($file)) {
return error('请确定上传对象或字段是否正确');
}
try {
$max_size = strtobytes(env('ADMIN_API.MAX_ATTACHMENT_SIZE', '100mb'));
$validate = validate([
'attachment' => "fileSize:$max_size|fileExt:biz,bz,bz2,gz,tgz,zip,rar,7z,doc,docx,xls,xlsx,csv,ppt,pptx,pdf,txt,jpg,jpeg,png,webp,ttf"
]);
if (!$validate->check(['attachment' => $file])) {
return error($validate->getError());
}
$storage = config('filesystem.disks.public.url');
$filemd5 = $file->md5();
$filesha1 = $file->sha1();
// 获取附件上传配置
list(
'filename_keep' => $filename_keep,
'filemd5_unique' => $filemd5_unique,
'save_to' => $save_to
) = $this->getUploadOptions('upload_attachment');
// 是否需要根据文件MD5值检查文件是否已存在
$attachment = $filemd5_unique ? SysAttachmentUploadRecordModel::md5($filemd5)->find() : null;
if (is_null($attachment)) {
// 保存位置配置 key
$disk = 'public';
// 检查是否需要保留原文件名
$name_rule = fn() => $filename_keep ? $this->filenameGenerator($file) : null;
// 保存到七牛云
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->language_id = request()->lang_id;
$attachment->attachment_path = $attachment_path;
$attachment->file_size = $file->getSize();
$attachment->file_type = $file->getOriginalMime();
$attachment->file_md5 = $filemd5;
$attachment->file_sha1 = $filesha1;
if (!$attachment->save()) {
return error('上传失败');
}
}
return success('上传成功', [
'path' => $attachment->attachment_path,
'file_md5' => $attachment->file_md5,
'file_sha1' => $attachment->file_sha1
]);
} catch (\Throwable $th) {
return error($th->getMessage());
}
return error('上传失败');
}
}

View File

@@ -0,0 +1,255 @@
<?php
declare (strict_types = 1);
namespace app\admin\controller\v1;
use app\admin\model\v1\SysMenuModel;
use app\admin\model\v1\SysRoleAuthorityModel;
use app\admin\model\v1\SysUserLoginLogModel;
use app\admin\model\v1\SysUserModel;
use app\admin\validate\v1\SysUserValidate;
/**
* 用户管理控制器
*/
class User
{
// 获取用户菜单权限
public function menu()
{
$id = request()->param('id');
$user = SysUserModel::bypk($id)->find();
if (empty($user)) {
return error('用户不存在');
}
$menus = SysMenuModel::alias('menu')->field([
'menu.id',
'menu.pid',
'menu.title',
'menu.name',
'menu.path',
'menu.icon',
'menu.redirect',
'menu.component',
'menu.hidden',
'menu.actived',
'menu.keep_alive'
])
->where('menu.status', '=', 1)
->whereExists(function($query) use($user) {
$query->table((new SysRoleAuthorityModel)->getTable())->alias('authority')
->where('authority.role_id', '=', $user->role_id)
->where('authority.menu_id = menu.id')
->group('authority.menu_id');
})
->order(['menu.sort' => 'asc', 'menu.id' => 'desc'])
->select();
if (!$menus->isEmpty()) {
$authoritys = SysRoleAuthorityModel::field([
'menu_id',
'permission'
])
->roleId($user->role_id)
->whereNotNull('permission')
->select();
$authoritys_map = [];
foreach ($authoritys as $val) {
$authoritys_map[$val['menu_id']][] = $val['permission'];
}
$menus->each(function($item) use($authoritys_map) {
$meta = [
'title' => $item['title'],
'icon' => $item['icon'],
'isKeepAlive' => !!$item['keep_alive'],
'actived' => !!$item['actived'],
'permissions' => []
];
unset($item['title'], $item['icon'], $item['keep_alive'], $item['actived']);
if (isset($authoritys_map[$item['id']])) {
$meta['permissions'] = $authoritys_map[$item['id']];
}
$item['hidden'] = !!$item['hidden'];
$item['meta'] = $meta;
return $item;
});
}
return success('获取成功', array_to_tree($menus->toArray(), 0, 'pid', false));
}
// 用户分页数据
public function index()
{
$params = request()->get([
'username',
'status',
'page/d' => 1,
'size/d' => 10
]);
$users = SysUserModel::withoutField([
'password',
'salt',
'created_at',
'updated_at',
'deleted_at'
])
->with(['role' => function($query) {
$query->field(['id', 'name' => 'role_name']);
}])
->withSearch(['username'], [
'username' => $params['username']??null,
])
->status($params['status']??null)
->order('id', 'desc')
->paginate([
'list_rows' => $params['size'],
'page' => $params['page'],
])
->bindAttr('role', ['role_name'])
->hidden(['role_id', 'role']);
if ($users->isEmpty()) {
return success('获取成功');
}
// 查询用户最后登录记录
$logs = SysUserLoginLogModel::where('id', 'IN', function($query) use($users) {
$query->table((new SysUserLoginLogModel)->getTable())
->field(['MAX(id)'])
->whereIn('user_id', array_column($users->items(), 'id'))
->group('user_id');
})
->column([
'ip',
'created_at'
], 'user_id');
if (!empty($logs)) {
$users->each(function($item) use($logs) {
$item['last_login_ip'] = '';
$item['last_login_at'] = '';
if (!empty($logs[$item['id']])) {
$item['last_login_ip'] = long2ip($logs[$item['id']]['ip']);
$item['last_login_at'] = $logs[$item['id']]['created_at'];
}
return $item;
});
}
return success('获取成功', $users);
}
// 用户详情
public function read($id)
{
$id = request()->param('id');
$user = SysUserModel::withoutField([
'password',
'salt',
'delete_disable',
'created_at',
'updated_at',
'deleted_at',
])
->bypk($id)
->find();
if (empty($user)) {
return error('用户不存在');
}
return success('获取成功', $user);
}
// 用户新增
public function save()
{
$post = request()->post([
'username',
'password',
'repassword',
'nickname',
'avatar',
'mobile',
'email',
'role_id',
'status' => 1,
]);
$post = array_merge($post, ['salt' => random_str(16)]);
$validate = new SysUserValidate;
if (!$validate->scene('create')->check($post)) {
return error($validate->getError());
}
$post['password'] = password_with_salt($post['password'], $post['salt']);
unset($post['repassword']);
$user = SysUserModel::create($post);
if ($user->isEmpty()) {
return error('新增失败');
}
return success('新增成功');
}
// 用户更新
public function update()
{
$id = request()->param('id');
$put = request()->put([
'username',
'password',
'repassword',
'nickname',
'avatar',
'mobile',
'email',
'role_id',
'status' => 1,
]);
$data = array_merge($put, ['id' => $id]);
$validate = new SysUserValidate;
if (!$validate->scene('update')->check($data)) {
return error($validate->getError());
}
$user = SysUserModel::bypk($id)->find();
if (empty($user)) {
return error('请确认要操作的对象是否存在');
}
if (!empty($data['password'])) {
$data['salt'] = random_str(16);
$data['password'] = password_with_salt($data['password'], $data['salt']);
} else {
unset($data['password']);
}
unset($data['repassword']);
if (!$user->save($data)) {
return error('操作失败');
}
return success('操作成功');
}
// 用户删除
public function delete()
{
$id = request()->param('id');
$user = SysUserModel::bypk($id)->find();
if (empty($user)) {
return error('请确认要操作的对象是否存在');
}
if ($user->delete_disable == 1) {
return error('该用户禁止删除');
}
if (!$user->delete()) {
return error('操作失败');
}
return success('操作成功');
}
}

View File

@@ -4,18 +4,21 @@ declare (strict_types = 1);
namespace app\admin\controller\v1;
use app\admin\exception\InvalidLoginException;
use app\admin\model\v1\UserLoginLogModel;
use app\admin\model\v1\UserModel;
use app\admin\model\v1\SysUserLoginLogModel;
use app\admin\model\v1\SysUserModel;
use app\admin\validate\v1\LoginValidate;
use thans\jwt\facade\JWTAuth;
use think\facade\Cache;
class Login
/**
* 用户中心控制器
*/
class UserCenter
{
/**
* 登录验证接口
*/
public function index()
public function login()
{
// 获取参数
$post = request()->post([
@@ -25,8 +28,8 @@ class Login
'captcha'
]);
$user = new UserModel();
$msg = '';
$user = new SysUserModel();
$msg = '';
try {
// 验证参数
$validate = new LoginValidate();
@@ -34,20 +37,22 @@ class Login
throw new InvalidLoginException($validate->getError());
}
// 校验验证码
$code = Cache::get('captcha:token.' . $post['token']);
if (!$code) {
throw new InvalidLoginException('验证码不存在或已过期');
}
Cache::delete('captcha:token.' . $post['token']);
if (!empty($post['captcha'])) {
// 校验验证码
$code = Cache::get('captcha:token.' . $post['token']);
if (!$code) {
throw new InvalidLoginException('验证码不存在或已过期');
}
Cache::delete('captcha:token.' . $post['token']);
// 校验
if (!password_verify($post['captcha'], $code)) {
throw new InvalidLoginException('验证码错误');
// 校验
if (!password_verify($post['captcha'], $code)) {
throw new InvalidLoginException('验证码错误');
}
}
// 验证用户
$user = UserModel::usernameOrMobile($post['username'])->find();
$user = SysUserModel::usernameOrMobile($post['username'])->find();
if (!$user) {
throw new InvalidLoginException('用户不存在');
}
@@ -70,7 +75,7 @@ class Login
}
// 记录登录日志
UserLoginLogModel::create([
SysUserLoginLogModel::create([
'user_id' => $user['id'],
'ip' => ip2long(request()->ip()),
'user_agent' => request()->header('user-agent'),
@@ -81,9 +86,23 @@ class Login
return success('登录成功', [
'uid' => $user['id'],
'nickname' => $user['nickname'],
'username' => $user['username'],
'avatar' => $user['avatar'],
'token' => $token,
]);
}
// 退出登录
public function logout()
{
$token = request()->header('Authorization');
if (\think\helper\Str::startsWith($token, 'Bearer ')) {
$token = substr($token, 7);
}
// token 加入黑名单
JWTAuth::invalidate($token);
return success('操作成功');
}
}

View File

@@ -0,0 +1,255 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\v1;
use app\admin\model\v1\VideoModel;
use app\admin\validate\v1\VideoValidate;
use think\facade\Config;
/**
* 视频管理控制器
*/
class Video
{
/**
* 视频信息分页数据
*/
public function index()
{
$params = request()->param([
'name',
'category_id',
'created_at',
'page/d' => 1,
'size/d' => 10
]);
$videos = VideoModel::field([
'id',
'image',
'name',
'category_id',
'sort',
'recommend',
'created_at'
])
->with(['category' => function ($query) {
$query->field(['id', 'name' => 'category_name']);
}])
->withSearch(['name', 'created_at'], [
'name' => $params['name'] ?? null,
'created_at' => !empty($params['created_at']) ? explode(",", $params['created_at']) : null
])
->language(request()->lang_id)
->categoryId($params['category_id'] ?? null)
->order(['sort' => 'desc', 'id' => 'desc'])
->paginate([
'list_rows' => $params['size'],
'page' => $params['page'],
])
->bindAttr('category', ['category_name'])
->hidden(['category', 'category_id'])
?->each(fn($item) => $item->image = thumb($item->image));
return success('获取成功', $videos);
}
/**
* 视频信息详情
*/
public function read()
{
$video = VideoModel::with(['category' => function($query) {
$query->field(['id', 'name' => 'category_name']);
}])
->withoutField([
'created_at',
'updated_at',
'deleted_at'
])
->bypk(request()->param('id'))
->find()
->bindAttr('category', ['category_name'])
->hidden(['category']);
if (empty($video)) {
return error('视频不存在');
}
return success('获取成功', $video);
}
/**
* 添加视频信息
*/
public function save()
{
$post = request()->post([
'name',
'category_id',
'sort',
'recommend',
'desc',
'image',
'video',
'link',
'seo_title',
'seo_keywords',
'seo_desc'
]);
$data = array_merge($post, ['language_id' => request()->lang_id]);
$validate = new VideoValidate;
if (!$validate->scene('create')->check($data)) {
return error($validate->getError());
}
$video = VideoModel::create($data);
if ($video->isEmpty()) {
return error('操作失败');
}
return success('操作成功');
}
/**
* 更新视频信息
*/
public function update()
{
$id = request()->param('id');
$put = request()->put([
'name',
'category_id',
'sort',
'recommend',
'desc',
'image',
'video',
'link',
'seo_title',
'seo_keywords',
'seo_desc'
]);
$validate = new VideoValidate;
if (!$validate->scene('update')->check(array_merge($put, ['id' => $id]))) {
return error($validate->getError());
}
$video = VideoModel::bypk($id)->find();
if (empty($video)) {
return error('请确认操作对象是不是存在');
}
if (!$video->save($put)) {
return error('操作失败');
}
return success('操作成功');
}
/**
* 设置排序值
*/
public function sort()
{
$id = request()->param('id');
$sort = request()->post('sort');
$validate = new VideoValidate;
if (!$validate->scene('sort')->check(['id' => $id, 'sort' => $sort])) {
return error($validate->getError());
}
$video = VideoModel::bypk($id)->find();
if (empty($video)) {
return error('请确认操作对象是不是存在');
}
$video->sort = $sort;
if (!$video->save()) {
return error('操作失败');
}
return success('操作成功');
}
/**
* 导出接口
*/
public function export()
{
$schema = [
'category_name' => '分类',
'name' => '视频名称',
'desc' => '描述',
'image' => '封面图片',
'video' => '视频URL',
'link' => '外链',
'sort' => '排序',
'recommend' => '是否推荐',
'seo_title' => 'seo标题',
'seo_keywords' => 'seo关键词',
'seo_desc' => 'seo描述',
'created_at' => '创建时间'
];
// 获取导出数据
$data = $this->getExportVideoData();
// 导出
return xlsx_writer($data, $schema, '视频列表' . date('YmdHis'));
}
private function getExportVideoData()
{
$params = request()->param([
'name',
'category_id',
'created_at'
]);
$domain = request()->domain();
return VideoModel::withoutField([
'language_id',
'updated_at',
'deleted_at'
])
->with(['category' => function ($query) {
$query->field(['id', 'name']);
}])
->withSearch(['name', 'created_at'], [
'name' => $params['name']??null,
'created_at' => !empty($params['created_at']) ? explode(',', $params['created_at']) : null,
])
->language(request()->lang_id)
->categoryId($params['category_id']??null)
->order(['sort' => 'asc', 'id' => 'desc'])
->select()
->bindAttr('category', ['category_name' => 'name'])
->hidden(['category_id', 'category'])
->each(function ($item) use($domain) {
$item->image = !empty($item->image) ? url_join($domain, $item->image) : '';
$item->video = !empty($item->video) ? url_join($domain, $item->video) : '';
$item->recommend = $item->recommend == 1 ? '是' : '否';
$item->status = $item->status == 1 ? '启用' : '禁用';
return $item;
});
}
/**
* 视频信息删除
*/
public function delete()
{
$id = request()->param('id');
$video = VideoModel::bypk($id)->find();
if (empty($video)) {
return error('请确认操作对象是不存在');
}
if (!$video->delete()) {
return error('操作失败');
}
return success('操作成功');
}
}

View File

@@ -0,0 +1,191 @@
<?php
declare (strict_types = 1);
namespace app\admin\controller\v1;
use app\admin\model\v1\VideoCategoryModel;
use app\admin\validate\v1\VideoCategoryValidate;
/**
* 视频分类控制器
*/
class VideoCategory
{
/**
* 视频分类列表数据
*/
public function list()
{
$param = request()->param([
'name' => '',
'is_show'
]);
$categorys = VideoCategoryModel::field([
'id',
'name',
])
->withSearch(['name'], [
'name' => $param['name']??null
])
->language(request()->lang_id)
->where(function($query) use($param) {
if (isset($param['is_show'])) {
$query->where('is_show', '=', $param['is_show']);
}
})
->order(['sort' => 'asc', 'id' => 'desc'])
->select();
return success('获取成功', $categorys);
}
/**
* 视频分类分页数据
*/
public function index()
{
$param = request()->param([
'name' => '',
'page/d' => 1,
'size/d' => 10,
]);
$categorys = VideoCategoryModel::withoutField([
'language_id',
'created_at',
'updated_at'
])
->withSearch(['name'], [
'name' => $param['name']??null
])
->language(request()->lang_id)
->order(['sort' => 'asc', 'id' => 'desc'])
->paginate([
'list_rows' => $param['size'],
'page' => $param['page'],
]);
return success('获取成功', $categorys);
}
/**
* 视频分类详情
*/
public function read()
{
$id = request()->param('id');
$category = VideoCategoryModel::withoutField([
'language_id',
'created_at',
'updated_at'
])
->bypk($id)
->find();
if (empty($category)) {
return error('分类不存在');
}
return success('获取成功', $category);
}
/**
* 视频分类添加
*/
public function save()
{
$post = request()->post([
'name',
'sort',
'is_show'
]);
$data = array_merge($post, ['language_id' => request()->lang_id]);
$validate = new VideoCategoryValidate;
if (!$validate->scene('create')->check($data)) {
return error($validate->getError());
}
$category = new VideoCategoryModel;
if (!$category->save($data)) {
return error('操作失败');
}
return success('操作成功');
}
/**
* 视频分类更新
*/
public function update()
{
$id = request()->param('id');
$put = request()->put([
'name',
'sort',
'is_show'
]);
$validate = new VideoCategoryValidate;
if (!$validate->scene('update')->check(array_merge($put, ['id' => $id]))) {
return error($validate->getError());
}
$category = VideoCategoryModel::bypk($id)->find();
if (empty($category)) {
return error('请确认操作对象是否存在');
}
if (!$category->save($put)) {
return error('操作失败');
}
return success('操作成功');
}
/**
* 设置视频分类排序值
*/
public function sort()
{
$id = request()->param('id');
$sort = request()->post('sort');
$validate = new VideoCategoryValidate;
if (!$validate->scene('sort')->check(['id' => $id,'sort' => $sort])) {
return error($validate::getError());
}
$category = VideoCategoryModel::bypk($id)->find();
if (!$category) {
return error('请确认操作对象是否存在');
}
$category->sort = $sort;
if (!$category->save()) {
return error('操作失败');
}
return success('操作成功');
}
/**
* 视频分类删除
*/
public function delete()
{
$id = request()->param('id');
$category = VideoCategoryModel::bypk($id)->find();
if (!$category) {
return error('请确认操作对象是否存在');
}
// 检查该分类下是否存在视频
if ($category->video()->count()) {
return error('该分类下存在视频,请先删除视频');
}
if (!$category->delete()) {
return error('操作失败');
}
return success('操作成功');
}
}

View File

@@ -0,0 +1,91 @@
<?php
declare (strict_types = 1);
namespace app\admin\controller\v1;
use app\admin\model\v1\VideoModel;
/**
* 视频回收站控制器
*/
class VideoTrash
{
/**
* 分页数据
*/
public function index()
{
$params = request()->param([
'name',
'category_id',
'page/d' => 1,
'size/d' => 10,
]);
$videos = VideoModel::field([
'id',
'category_id',
'name',
'image',
'video',
'sort',
'recommend',
'created_at',
])
->with(['category' => function($query) {
$query->field(['id', 'name' => 'category_name']);
}])
->withSearch(['name'], [
'name' => $params['name']??null
])
->language(request()->lang_id)
->categoryId($params['category_id']??null)
->onlyTrashed()
->order(['sort' => 'asc', 'id' => 'desc'])
->paginate([
'list_rows' => $params['size'],
'page' => $params['page'],
])
->bindAttr('category', ['category_name'])
->hidden(['category_id', 'category'])
?->each(fn($item) => $item->image = thumb($item->image));
return success('获取成功', $videos);
}
/**
* 恢复操作
*/
public function restore()
{
$id = request()->param('id');
$video = VideoModel::onlyTrashed()->bypk($id)->find();
if (empty($video)) {
return error('请确认操作对象是否存在');
}
if (!$video->restore()) {
return error('操作失败');
}
return success('操作成功');
}
/**
* 删除操作
*/
public function delete()
{
$id = request()->param('id');
$video = VideoModel::onlyTrashed()->bypk($id)->find();
if (empty($video)) {
return error('请确认操作对象是否存在');
}
if (!$video->force()->delete()) {
return error('操作失败');
}
return success('操作成功');
}
}

View File

@@ -0,0 +1,10 @@
<?php
namespace app\admin\exception;
class InvalidOperateException extends \Exception
{
public function __construct($message = '', $code = 0, ?\Throwable $previous = null)
{
parent::__construct($message, $code, $previous);
}
}

View File

@@ -4,4 +4,5 @@ return [
// 登录验证
app\admin\middleware\v1\Auth::class,
app\admin\middleware\v1\RequestId::class,
app\admin\middleware\v1\OperateLog::class,
];

View File

@@ -9,7 +9,7 @@ class Auth extends \thans\jwt\middleware\BaseMiddleware
// 获取语言信息
private function setLanguage($request)
{
$code = $request->cookie('lang', 'zh_cn');
$code = $request->cookie('lang', 'zh-cn');
$lang = LanguageModel::cache('lang:code.' . $code, 3600, 'lang')
->field(['id', 'name', 'code', 'icon', 'url'])
->withJoin(['country' => ['id', 'name', 'code', 'icon']])

View File

@@ -0,0 +1,72 @@
<?php
declare (strict_types = 1);
namespace app\admin\middleware\v1;
use app\admin\model\v1\SysOperateLog;
use app\admin\model\v1\SysRestfulApiModel;
use think\facade\Cache;
class OperateLog
{
/**
* 处理请求
*
* @param \think\Request $request
* @param \Closure $next
* @return Response
*/
public function handle($request, \Closure $next)
{
$response = $next($request);
$restful_api_name_map = Cache::get('sys_restful_api:map:name');
if (empty($restful_api_name_map)) {
$restful_api_name_map = [];
$restful_api = SysRestfulApiModel::cache('sys_restful_api')->select();
foreach ($restful_api as $item) {
$restful_api_name_map[$item['method'] . '-' . $item['layer'] . '.' . $item['controller'] . '/' . $item['action']] = $item['name'];
}
Cache::set('sys_restful_api:map:name', $restful_api_name_map);
}
// 忽略日志记录的Api
$ignore_apis = env('ADMIN_API.IGNORE_LOGGING_LIST');
$current_api = sprintf("%s/%s/%s", $request->layer(), $request->controller(base:true), $request->action());
if (in_array($current_api, $ignore_apis)) {
return $response;
}
$log = [
'user_id' => $request->uid,
'title' => $restful_api_name_map[$request->method() . '-' . $request->controller() . '/' . $request->action()] ?? '',
'version' => $request->layer(),
'method' => $request->method(),
'controller' => $request->controller(base: true),
'action' => $request->action(),
'url' => $request->url(),
'ip' => $request->ip(),
'params' => json_encode($request->param()),
'status' => $response->getCode()
];
if (empty($request->exception_info)) {
// 记录正常返回信息
$contentDisposition = $response->getHeader('Content-Disposition');
if (!empty($contentDisposition) && \think\helper\Str::startsWith($contentDisposition, 'attachment')) {
$log['message'] = $contentDisposition;
} else {
$log['message'] = $response->getData();
if (is_array($log['message'])) {
$log['message'] = json_encode($log['message'], JSON_UNESCAPED_UNICODE);
}
}
} else {
// 记录异常返回信息
$log['message'] = (string)$request->exception_info;
}
SysOperateLog::create($log);
return $response;
}
}

View File

@@ -0,0 +1,19 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\AgentBusinessTypeBaseModel;
/**
* 代理商业务类型模型
* @mixin \think\Model
*/
class AgentBusinessTypeModel extends AgentBusinessTypeBaseModel
{
// 按语言查询
public function scopeLanguage($query, $value)
{
$query->where('language_id', '=', $value);
}
}

View File

@@ -0,0 +1,19 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\AgentEnterpriseSizeTypeBaseModel;
/**
* 代理商企业规模类型模型
* @mixin \think\Model
*/
class AgentEnterpriseSizeTypeModel extends AgentEnterpriseSizeTypeBaseModel
{
// 按语言查询
public function scopeLanguage($query, $value)
{
$query->where('language_id', '=', $value);
}
}

View File

@@ -0,0 +1,58 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\AgentBaseModel;
/**
* 代理商数据模型
* @mixin \think\Model
*/
class AgentModel extends AgentBaseModel
{
// 关联业务类型
public function businessType()
{
return $this->belongsTo(AgentBusinessTypeModel::class, 'business_type', 'value');
}
// 关联企业规模类型
public function enterpriseSizeType()
{
return $this->belongsTo(AgentEnterpriseSizeTypeModel::class, 'enterprise_size', 'value');
}
// 按放言查询
public function scopeLanguage($query, $value)
{
$query->where('language_id', '=', $value);
}
// 按企业规模查询
public function scopeEnterpriseSize($query, $value)
{
if (is_null($value)) return;
$query->where('enterprise_size', '=', $value);
}
// 根据公司名称搜索
public function searchCorpNameAttr($query, $value)
{
if (is_null($value)) return;
$query->where('corp_name', 'like', "%{$value}%");
}
// 根据提交时间搜索
public function searchCreatedAtAttr($query, $value)
{
if (is_null($value)) return;
if (is_array($value)) {
if (count($value) > 1) {
$query->whereBetweenTime('created_at', $value[0], $value[1]);
} else {
$query->whereTime('created_at', '>=', $value[0]);
}
}
}
}

View File

@@ -22,6 +22,12 @@ class ArticleCategoryModel extends ArticleCategoryBaseModel
$query->where('language_id', '=', $lang_id);
}
// 根据是否显示查询
public function scopeIsShow($query, bool $is_show)
{
$query->where('is_show', '=', (int)$is_show);
}
// 搜索分类名
public function searchNameAttr($query, $value, $data)
{

View File

@@ -11,7 +11,7 @@ use think\model\concern\SoftDelete;
*/
class ArticleLeaveMessageModel extends ArticleLeaveMessageBaseModel
{
// 启用软删除
// 启用软删除
use SoftDelete;
// 软删除标记数据字段
protected $deleteTime = 'deleted_at';
@@ -22,12 +22,6 @@ class ArticleLeaveMessageModel extends ArticleLeaveMessageBaseModel
return $this->belongsTo(ArticleModel::class, 'article_id', 'id');
}
// 根据id查询
public function scopeId($query, $value)
{
$query->where('id', '=', $value);
}
// 审核状态查询
public function scopeIsAudited($query, $is_audited)
{

View File

@@ -11,11 +11,17 @@ use think\model\concern\SoftDelete;
*/
class ArticleModel extends ArticleBaseModel
{
// 启用软删除
// 启用软删除
use SoftDelete;
// 软删除标记数据字段
protected $deleteTime = 'deleted_at';
// 写入前,处理发布时间为空情况
public static function onBeforeWrite(ArticleModel $article)
{
$article->release_time = empty($article->release_time) ? null : $article->release_time;
}
// 关联分类
public function category()
{
@@ -25,14 +31,41 @@ class ArticleModel extends ArticleBaseModel
// 搜索名称
public function searchTitleAttr($query, $value, $data)
{
if (is_null($value)) {
return;
}
$query->where('title', 'like', '%' . $value . '%');
}
// 搜索发布时间
public function searchCreatedAtAttr($query, $value, $data)
public function searchReleaseTimeAttr($query, $value, $data)
{
if (is_null($value)) {
return;
}
if (is_array($value)) {
if (count($value) == 2) {
if ($value[0] == $value[1]) {
$value[1] = date('Y-m-d 23:59:59', strtotime($value[1]));
}
$query->whereBetweenTime('release_time', $value[0], $value[1]);
} else {
$query->whereTime('release_time', '>=', $value[0]);
}
}
}
// 搜索新增时间
public function searchCreatedAtAttr($query, $value, $data)
{
if (is_null($value)) {
return;
}
if (is_array($value)) {
if (count($value) == 2) {
if ($value[0] == $value[1]) {
$value[1] = date('Y-m-d 23:59:59', strtotime($value[1]));
}
$query->whereBetweenTime('created_at', $value[0], $value[1]);
} else {
$query->whereTime('created_at', '>=', $value[0]);
@@ -40,12 +73,6 @@ class ArticleModel extends ArticleBaseModel
}
}
// 主键查询
public function scopeId($query, $value)
{
$query->where('id', '=', $value);
}
// 语言查询
public function scopeLanguage($query, $value)
{
@@ -58,6 +85,10 @@ class ArticleModel extends ArticleBaseModel
// 分类查询
public function scopeCategory($query, $value)
{
if (is_array($value) || str_contains($value, ',')) {
$query->whereIn('category_id', $value);
return;
}
$query->where('category_id', '=', $value);
}
public function scopeCategoryNullable($query, $value)
@@ -65,6 +96,10 @@ class ArticleModel extends ArticleBaseModel
if (is_null($value)) {
return;
}
if (is_array($value) || str_contains($value, ',')) {
$query->whereIn('category_id', $value);
return;
}
$query->where('category_id', '=', $value);
}
}

View File

@@ -0,0 +1,37 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\AttachmentCategoryBaseModel;
use think\model\concern\SoftDelete;
/**
* 附件(下载管理)分类模型
* @mixin \think\Model
*/
class AttachmentCategoryModel extends AttachmentCategoryBaseModel
{
// 启用软删除
use SoftDelete;
// 软删除字段
protected $deleteTime = 'deleted_at';
// 关联附件
public function attachment()
{
return $this->hasMany(AttachmentModel::class, 'category_id', 'id');
}
// 语言查询
public function scopeLanguage($query, $value)
{
$query->where('language_id', '=', $value);
}
// 查询显示状态数据
public function scopeIsShow($query, bool $value = true)
{
$query->where('is_show', '=', (int)$value);
}
}

View File

@@ -0,0 +1,67 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\AttachmentBaseModel;
use think\model\concern\SoftDelete;
/**
* 附件(下载管理)模型
* @mixin \think\Model
*/
class AttachmentModel extends AttachmentBaseModel
{
// 启用软删除
use SoftDelete;
// 软删除字段
protected $deleteTime = 'deleted_at';
// JSON字段
protected $json = ['attach'];
// 设置JSON数据返回数组
protected $jsonAssoc = true;
// 关联附件分类模型
public function category()
{
return $this->belongsTo(AttachmentCategoryModel::class, 'category_id', 'id');
}
// 名称搜索
public function searchNameAttr($query, $value)
{
if (empty($value)) return;
$query->where('name', 'like', '%' . $value . '%');
}
// 新增时间搜索
public function searchCreatedAtAttr($query, $value)
{
if (empty($value)) return;
if (is_array($value)) {
if (count($value) > 1) {
$query->whereBetweenTime('created_at', $value[0], $value[1]);
} else {
$query->whereTime('created_at', '>=', $value[0]);
}
}
}
// 语言查询
public function scopeLanguage($query, $value)
{
$query->where('language_id', '=', $value);
}
// 分类查询
public function scopeCategoryId($query, $value)
{
if (empty($value)) return;
if (is_array($value) || str_contains($value, ',')) {
$query->whereIn('category_id', $value);
return;
}
$query->where('category_id', '=', $value);
}
}

View File

@@ -0,0 +1,46 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\BulkPurchaseInquiryBaseModel;
/**
* 批量采购询盘模型
* @mixin \think\Model
*/
class BulkPurchaseInquiryModel extends BulkPurchaseInquiryBaseModel
{
// 按语言查询
public function scopeLanguage($query, $value)
{
$query->where('language_id', '=', $value);
}
// 公司名称搜索
public function searchCorpNameAttr($query, $value)
{
if (is_null($value)) return;
$query->where('corp_name', 'like', "%{$value}%");
}
// 兴趣(感兴趣的产品分类)
public function searchInterestedAttr($query, $value)
{
if (is_null($value)) return;
$query->where('interested', 'like', "%{$value}%");
}
// 添加时间搜索
public function searchCreatedAtAttr($query, $value)
{
if (is_null($value)) return;
if (is_array($value)) {
if (count($value) > 1) {
$query->whereBetweenTime('created_at', $value[0], $value[1]);
} else {
$query->whereTime('created_at', $value[0]);
}
}
}
}

View File

@@ -10,5 +10,10 @@ use app\common\model\CountryBaseModel;
*/
class CountryModel extends CountryBaseModel
{
// 根据名称搜索
public function searchNameAttr($query, $value)
{
if (is_null($value)) return;
$query->where('name', 'like', '%' . $value . '%');
}
}

View File

@@ -0,0 +1,48 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\FaqBaseModel;
use think\model\concern\SoftDelete;
/**
* @mixin \think\Model
*/
class FaqModel extends FaqBaseModel
{
// 启用软删除
use SoftDelete;
// 软删除字段
protected $deleteTime = 'deleted_at';
// 根据语言查询
public function scopeLanguage($query, $value)
{
$query->where('language_id', '=', $value);
}
// 根据问题搜索
public function searchQuestionAttr($query, $value, $data)
{
if (is_null($value)) {
return;
}
$query->where('question', 'like', "%$value%");
}
// 根据添加时间搜索
public function searchCreatedAtAttr($query, $value, $data)
{
if (is_null($value)) {
return;
}
if (is_array($value)) {
if (count($value) > 1) {
$query->whereBetweenTime('created_at', $value[0], $value[1]);
} else {
$query->whereTime('created_at', '>=', $value[0]);
}
}
}
}

View File

@@ -1,18 +0,0 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\ImageBaseModel;
/**
* @mixin \think\Model
*/
class ImageModel extends ImageBaseModel
{
// 根据md5获取图片
public function scopeMd5($query, $md5)
{
$query->where('image_md5', '=', $md5);
}
}

View File

@@ -15,10 +15,4 @@ class LanguageModel extends LanguageBaseModel
{
return $this->belongsTo(CountryModel::class, 'country_id', 'id');
}
// 根据id查询
public function scopeId($query, $value)
{
$query->where('id', '=', $value);
}
}

View File

@@ -0,0 +1,35 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\LeaveMessageBaseModel;
/**
* 留言记录(联系我们)模型
* @mixin \think\Model
*/
class LeaveMessageModel extends LeaveMessageBaseModel
{
// 根据语言查询
public function scopeLanguage($query, $value)
{
$query->where('language_id', '=', $value);
}
// 按添加时间搜索
public function searchCreatedAtAttr($query, $value, $data)
{
if (is_null($value)) return;
if (is_array($value)) {
if (count($value) > 1) {
if ($value[0] == $value[1]) {
$value[1] = date("Y-m-d 23:59:59", strtotime($value[1]));
}
$query->whereBetweenTime('created_at', $value[0], $value[1]);
} else {
$query->whereTime('created_at', '>=', $value[0]);
}
}
}
}

View File

@@ -16,8 +16,6 @@ class ProductAttrModel extends ProductAttrBaseModel
use SoftDelete;
// 软删除字段
protected $deleteTime = 'deleted_at';
// 修改自动写入时间格式
protected $autoWriteTimestamp = 'datetime';
// 关联属性特征
public function props()

View File

@@ -12,12 +12,10 @@ use think\model\concern\SoftDelete;
*/
class ProductCategoryModel extends ProductCategoryBaseModel
{
// 启用软删除
// 启用软删除
use SoftDelete;
// 软件字段
protected $deleteTime = 'deleted_at';
// 修改自动写入时间格式
protected $autoWriteTimestamp = 'datetime';
// 根据pid查询
public function scopePid($query, $pid)
@@ -31,6 +29,26 @@ class ProductCategoryModel extends ProductCategoryBaseModel
$query->where('language_id', '=', $value);
}
// 所属产品目录分类id查询
public function scopeTcoId($query, $value)
{
$query->where('related_tco_category', '=', $value);
}
/**
* 根据是否显示查询
* @param $query
* @param \Closure:bool|bool $value
*/
public function scopeIsShow($query, \Closure|bool $value)
{
if (is_callable($value)) {
$query->where('is_show', '=', $value());
return;
}
$query->where('is_show', '=', (int)$value);
}
// 搜索分类名称
public function searchNameNullableAttr($query, $value, $data)
{

View File

@@ -0,0 +1,46 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\ProductInquiryBaseModel;
/**
* 产品询盘记录模型
* @mixin \think\Model
*/
class ProductInquiryModel extends ProductInquiryBaseModel
{
// 根据语言查询
public function scopeLanguage($query, $value)
{
$query->where('language_id', '=', $value);
}
// 按公司名称搜索
public function searchCorpNameAttr($query, $value, $data)
{
if (is_null($value)) return;
$query->where('corp_name', 'like', '%'.$value.'%');
}
// 按国家名称搜索
public function searchCountryNameAttr($query, $value, $data)
{
if (is_null($value)) return;
$query->where('country_name', 'like', '%'.$value.'%');
}
// 按提交时间搜索
public function searchCreatedAtAttr($query, $value, $data)
{
if (is_null($value)) return;
if (is_array($value)) {
if (count($value) > 1) {
$query->whereBetweenTime('created_at', $value[0], $value[1]);
} else {
$query->whereTime('created_at', '>=', $value[0]);
}
}
}
}

View File

@@ -12,12 +12,10 @@ use think\model\concern\SoftDelete;
*/
class ProductModel extends ProductBaseModel
{
// 启用软删除
// 启用软删除
use SoftDelete;
// 软件字段
protected $deleteTime = 'deleted_at';
// 修改自动写入时间格式
protected $autoWriteTimestamp = 'datetime';
// 分类关联查询
public function category()
@@ -67,8 +65,32 @@ class ProductModel extends ProductBaseModel
if (is_null($value)) {
return;
}
if (is_array($value) || str_contains($value, ',')) {
$query->whereIn('category_id', $value);
return;
}
$query->where('category_id', '=', $value);
}
public function scopeCategory($query, $value)
{
if (is_array($value) || str_contains($value, ',')) {
$query->whereIn('category_id', $value);
return;
}
$query->where('category_id', '=', $value);
}
// 规格型号查询
public function scopeSpu($query, $spu)
{
$query->where('spu', '=', $spu);
}
// 启用状态查询
public function scopeEnabled($query)
{
$query->where('status', '=', 1);
}
// 上架状态查询
public function scopeIsShowNullable($query, bool|null $value)
@@ -78,4 +100,8 @@ class ProductModel extends ProductBaseModel
}
$query->where('is_show', '=', (int)$value);
}
public function scopeIsShow($query, bool $value)
{
$query->where('is_show', '=', (int)$value);
}
}

View File

@@ -0,0 +1,31 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\ProductPurchaseLinkBaseModel;
/**
* 产品 - 购买链接模型
* @mixin \think\Model
*/
class ProductPurchaseLinkModel extends ProductPurchaseLinkBaseModel
{
// 关联产品
public function product()
{
return $this->belongsTo(ProductModel::class, 'product_id', 'id');
}
// 关联平台
public function platform()
{
return $this->belongsTo(ProductPurchaseLinkPlatformModel::class, 'platform_id', 'id');
}
// 根据语言查询
public function scopeLanguage($query, $value)
{
$query->where('language_id', '=', $value);
}
}

View File

@@ -0,0 +1,19 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\ProductPurchasePlatformBaseModel;
/**
* 产品 - 采购链接平台模型
* @mixin \think\Model
*/
class ProductPurchaseLinkPlatformModel extends ProductPurchasePlatformBaseModel
{
// 根据语言查询
public function scopeLanguage($query, $value)
{
$query->where('language_id', '=', $value);
}
}

View File

@@ -0,0 +1,15 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\ProductSkuAttrBaseModel;
/**
* 产品 - sku属性模型
* @mixin \think\Model
*/
class ProductSkuAttrModel extends ProductSkuAttrBaseModel
{
//
}

View File

@@ -0,0 +1,30 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\ProductSkuBaseModel;
/**
* 产品 - sku模型
* @mixin \think\Model
*/
class ProductSkuModel extends ProductSkuBaseModel
{
// 设置json类型字段
protected $json = ['photo_album'];
// 设置JSON数据返回数组
protected $jsonAssoc = true;
// 关联产品sku属性
public function attrs()
{
return $this->hasMany(ProductSkuAttrModel::class, 'sku_id', 'id');
}
// 所属产品查询
public function scopeProductId($query, $id)
{
$query->where('product_id', '=', $id);
}
}

View File

@@ -0,0 +1,52 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\ProductTcoCategoryBaseModel;
use think\model\concern\SoftDelete;
/**
* 产品 - 产品目录分类同步记录模型
* @mixin \think\Model
*/
class ProductTcoCategoryModel extends ProductTcoCategoryBaseModel
{
// 启用软删除
use SoftDelete;
// 软删除字段
protected $deleteTime = 'deleted_at';
// 根据语言查询
public function scopeLanguage($query, $value)
{
$query->where('language_id', '=', $value);
}
// 根据 tco_id 查询
public function scopeTcoId($query, $value)
{
$query->where('tco_id', '=', $value);
}
// 根据ERP Code查询
public function scopeErpCode($query, $value)
{
$query->where('erp_code', '=', $value);
}
// 按分类名称搜索
public function searchNameAttr($query, $value, $data)
{
if (is_null($value)) {
return;
}
$query->where('name', 'like', "%{$value}%");
}
// 只查询启用的
public function scopeEnabled($query)
{
$query->where('disabled', '=', 0);
}
}

View File

@@ -0,0 +1,19 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\SysAttachmentUploadRecordBaseModel;
/**
* 附件上传模型
* @mixin \think\Model
*/
class SysAttachmentUploadRecordModel extends SysAttachmentUploadRecordBaseModel
{
// 根据md5获取
public function scopeMd5($query, $md5)
{
$query->where('file_md5', '=', $md5);
}
}

View File

@@ -0,0 +1,80 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\SysBannerItemBaseModel;
use think\model\concern\SoftDelete;
/**
* 横幅数据项模型
* @mixin \think\Model
*/
class SysBannerItemModel extends SysBannerItemBaseModel
{
// 启用软删除
use SoftDelete;
// 软删除字段
protected $deleteTime = 'deleted_at';
// 要隐藏的字段或关联模型数据字段
protected $hidden = ['prodMapping'];
// 关联分类
public function banner()
{
return $this->belongsTo(SysBannerModel::class, 'banner_id', 'id');
}
// 关联相关产品分类中间表模型
public function prodMapping()
{
return $this->hasOne(SysBannerProdCateMappingModel::class, 'banner_item_id', 'id');
}
// 从产品分类关联模型中获取id字段
public function getRelProdCateIdAttr()
{
return $this->prodMapping?->category?->id;
}
// 从产品分类关联模型中获取name字段
public function getRelProdCateNameAttr()
{
return $this->prodMapping?->category?->name;
}
// 按横幅标题搜索
public function searchTitleAttr($query, $value, $data)
{
if (empty($value)) {
return;
}
$query->where('title', 'like', "%{$value}%");
}
// 按添加时间搜索
public function searchCreatedAtAttr($query, $value, $data)
{
if (empty($value)) {
return;
}
if (is_array($value)) {
if (count($value) > 1) {
$query->whereBetweenTime('created_at', $value[0], $value[1]);
} else {
$query->whereTime('created_at', '>=', $value[0]);
}
}
}
// 根据横幅id查询
public function scopeBannerId($query, $value)
{
if (is_null($value)) return;
if (is_array($value) || str_contains($value, ',')) {
$query->whereIn('banner_id', $value);
return;
}
$query->where('banner_id', '=', $value);
}
}

View File

@@ -0,0 +1,47 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\SysBannerBaseModel;
use think\model\concern\SoftDelete;
/**
* 横幅模型
* @mixin \think\Model
*/
class SysBannerModel extends SysBannerBaseModel
{
// 启用软删除
use SoftDelete;
// 软删除字段
protected $deleteTime = 'deleted_at';
// 按语言查询
public function scopeLanguage($query, $value)
{
if (is_null($value)) return;
$query->where('language_id', $value);
}
// 查询启用状态
public function scopeEnabled($query)
{
$query->where('status', '=', 1);
}
// 查询禁用状态
public function scopeDisabled($query)
{
$query->where('status', '=', -1);
}
// 按名称搜索
public function searchNameAttr($query, $value, $data)
{
if (is_null($value)) {
return;
}
$query->where('name', 'like', "%{$value}%");
}
}

View File

@@ -0,0 +1,19 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\SysBannerProdCateMappingBaseModel;
/**
* banner与产品分类关联表模型
* @mixin \think\Model
*/
class SysBannerProdCateMappingModel extends SysBannerProdCateMappingBaseModel
{
// 关联产品分类
public function category()
{
return $this->belongsTo(ProductCategoryModel::class, 'product_category_id', 'id');
}
}

View File

@@ -0,0 +1,31 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\SysConfigGroupBaseModel;
use think\model\concern\SoftDelete;
/**
* 系统配置分组模型
* @mixin \think\Model
*/
class SysConfigGroupModel extends SysConfigGroupBaseModel
{
// 启用软删除
use SoftDelete;
// 软删除字段
protected $deleteTime = 'deleted_at';
// 按语言搜索
public function scopeLanguage($query, $value)
{
$query->where('language_id', '=', $value);
}
// 获取启用的配置分组
public function scopeEnabled($query)
{
$query->where('status', '=', 1);
}
}

View File

@@ -0,0 +1,60 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\SysConfigBaseModel;
use think\model\concern\SoftDelete;
/**
* 系统配置模型
* @mixin \think\Model
*/
class SysConfigModel extends SysConfigBaseModel
{
// 启用软删除
use SoftDelete;
// 软删除字段
protected $deleteTime = 'deleted_at';
// 关联分组
public function group()
{
return $this->belongsTo('SysConfigGroupModel', 'group_id', 'id');
}
// 关联类型
public function type()
{
return $this->belongsTo('SysConfigTypeModel', 'type', 'value');
}
// 按title搜索
public function searchTitleAttr($query, $value, $data)
{
if (empty($value)) {
return;
}
$query->where('title', 'like', "%{$value}%");
}
// 按group_id查询
public function scopeGroupId($query, $value)
{
if (is_array($value)) {
$query->where('group_id', 'in', $value);
return;
}
$query->where('group_id', '=', $value);
}
// 按name查询
public function scopeByName($query, $value)
{
if (is_array($value)) {
$query->where('name', 'in', $value);
return;
}
$query->where('name', '=', $value);
}
}

View File

@@ -0,0 +1,15 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\SysConfigTypeBaseModel;
/**
* 配置类型模型
* @mixin \think\Model
*/
class SysConfigTypeModel extends SysConfigTypeBaseModel
{
//
}

View File

@@ -0,0 +1,19 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\SysImageUploadRecordBaseModel;
/**
* 图片上传模型
* @mixin \think\Model
*/
class SysImageUploadRecordModel extends SysImageUploadRecordBaseModel
{
// 根据md5获取图片
public function scopeMd5($query, $md5)
{
$query->where('file_md5', '=', $md5);
}
}

View File

@@ -0,0 +1,19 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\SysMenuAbilityPermissionBaseModel;
/**
* 菜单能力权限模型
* @mixin \think\Model
*/
class SysMenuAbilityPermissionModel extends SysMenuAbilityPermissionBaseModel
{
// 按menu_id查询
public function scopeMenuId($query, $value)
{
return $query->where('menu_id', $value);
}
}

View File

@@ -0,0 +1,51 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\SysMenuBaseModel;
use think\model\concern\SoftDelete;
/**
* 菜单模型
* @mixin \think\Model
*/
class SysMenuModel extends SysMenuBaseModel
{
// 启用软删除
use SoftDelete;
// 软删除字段
protected $deleteTime = 'deleted_at';
// 关联菜单能力权限
public function menuAbilityPermission()
{
return $this->hasMany(SysMenuAbilityPermissionModel::class, 'menu_id', 'id');
}
// 菜单标题查询
public function searchTitleAttr($query, $value, $data)
{
if (empty($value)) {
return;
}
$query->where('title', 'like', "%$value%");
}
// 状态查询
public function scopeStatus($query, $value)
{
if (empty($value)) {
return;
}
return $query->where('status', '=', $value);
}
// 获取启用的菜单
public function scopeEnabled($query)
{
$query->where('status', '=', 1);
}
}

View File

@@ -0,0 +1,46 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\SysNavigationItemBaseModel;
/**
* 导航数据模型
* @mixin \think\Model
*/
class SysNavigationItemModel extends SysNavigationItemBaseModel
{
// 关联导航
public function navigation()
{
return $this->belongsTo(SysNavigationModel::class, 'nav_id', 'id');
}
// 名称搜索
public function searchNameAttr($query, $value, $data)
{
if (empty($value)) return;
$query->where('name', 'like', "%{$value}%");
}
// 新增时间搜索
public function searchCreatedAtAttr($query, $value, $data)
{
if (empty($value)) return;
if (is_array($value)) {
if (count($value) > 1) {
$query->whereBetweenTime('created_at', $value[0], $value[1]);
} else {
$query->whereTime('created_at', '>=', $value[0]);
}
}
}
// 导航查询
public function scopeNavId($query, $value)
{
if (empty($value)) return;
$query->where('nav_id', '=', $value);
}
}

View File

@@ -0,0 +1,15 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\SysNavigationLinkTypeBaseModel;
/**
* 导航链接类型模型
* @mixin \think\Model
*/
class SysNavigationLinkTypeModel extends SysNavigationLinkTypeBaseModel
{
//
}

View File

@@ -0,0 +1,35 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\SysNavigationBaseModel;
use think\model\concern\SoftDelete;
/**
* 导航模型
* @mixin \think\Model
*/
class SysNavigationModel extends SysNavigationBaseModel
{
// 启用软删除
use SoftDelete;
// 软删除字段
protected $deleteTime = 'deleted_at';
// 按语言查询
public function scopeLanguage($query, $value)
{
if (is_null($value)) return;
$query->where('language_id', '=', $value);
}
// 名称搜索
public function searchNameAttr($query, $value)
{
if (empty($value)) {
return;
}
$query->where('name', 'like', "%{$value}%");
}
}

View File

@@ -0,0 +1,66 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use think\Model;
/**
* @mixin \think\Model
*/
class SysOperateLog extends Model
{
// 表名
protected $name = 'sys_operate_log';
// 主键
protected $pk = 'id';
// 字段信息
protected $schema = [
'id' => 'int',
'user_id' => 'int',
'title' => 'string',
'version' => 'string',
'method' => 'string',
'controller' => 'string',
'action' => 'string',
'url' => 'string',
'ip' => 'string',
'params' => 'string',
'status' => 'int',
'message' => 'string',
'created_at' => 'datetime'
];
// 关联操作人
public function user()
{
return $this->belongsTo(SysUserModel::class, 'user_id', 'id');
}
// 按标题搜索
public function searchTitleAttr($query, $value, $data)
{
if (empty($value)) {
return;
}
$query->where('title', 'like', "%$value%");
}
// 按操作时间搜索
public function searchCreatedAtAttr($query, $value, $data)
{
if (empty($value)) return;
if (is_string($value)) {
$value = explode(',', $value);
}
if (is_array($value)) {
if (count($value) == 2) {
$query->whereBetweenTime('created_at', $value[0], $value[1]);
} else {
$query->whereTime('created_at', '>=', $value);
}
}
}
}

View File

@@ -0,0 +1,15 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\SysRestfulApiBaseModel;
/**
* 系统接口表模型
* @mixin \think\Model
*/
class SysRestfulApiModel extends SysRestfulApiBaseModel
{
//
}

View File

@@ -0,0 +1,24 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\SysRoleAuthorityBaseModel;
/**
* @mixin \think\Model
*/
class SysRoleAuthorityModel extends SysRoleAuthorityBaseModel
{
// role_id查询
public function scopeRoleId($query, $value)
{
return $query->where('role_id', $value);
}
// menu_id查询
public function scopeMenuId($query, $value)
{
return $query->where('menu_id', $value);
}
}

View File

@@ -0,0 +1,34 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\SysRoleBaseModel;
use think\model\concern\SoftDelete;
/**
* 角色模型
* @mixin \think\Model
*/
class SysRoleModel extends SysRoleBaseModel
{
// 启用软删除
use SoftDelete;
// 软删除字段
protected $deleteTime = 'deleted_at';
// 关联权限
public function authorities()
{
return $this->hasMany(SysRoleAuthorityModel::class, 'role_id', 'id');
}
// 角色名称搜索
public function searchNameAttr($query, $value, $data)
{
if (empty($value)) {
return;
}
$query->where('name', 'like', "%$value%");
}
}

View File

@@ -0,0 +1,14 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\SysUserLoginLogBaseModel;
/**
* @mixin \think\Model
*/
class SysUserLoginLogModel extends SysUserLoginLogBaseModel
{
}

View File

@@ -0,0 +1,52 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\SysUserBaseModel;
use think\model\concern\SoftDelete;
/**
* 用户模型
* @mixin \think\Model
*/
class SysUserModel extends SysUserBaseModel
{
// 启用软删除
use SoftDelete;
// 软删除字段
protected $deleteTime = 'deleted_at';
// 隐藏字段
protected $hidden = ['password', 'salt'];
// 关联角色
public function role()
{
return $this->belongsTo(SysRoleModel::class, 'role_id', 'id');
}
// 用户名搜索
public function searchUsernameAttr($query, $value)
{
if (empty($value)) {
return;
}
$query->where('username', 'like', "%{$value}%");
}
// 用户名查询范围
public function scopeUsernameOrMobile($query, $username)
{
$query->where('username', '=', $username)->whereOr('mobile', '=', $username);
}
// 状态查询
public function scopeStatus($query, $status)
{
if (empty($status)) {
return;
}
$query->where('status', '=', $status);
}
}

View File

@@ -0,0 +1,19 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\SysVideoUploadRecordBaseModel;
/**
* 视频上传模型
* @mixin \think\Model
*/
class SysVideoUploadRecordModel extends SysVideoUploadRecordBaseModel
{
// 根据md5查询
public function scopeMd5($query, $value)
{
$query->where('file_md5', '=', $value);
}
}

View File

@@ -1,14 +0,0 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\UserLoginLogBaseModel;
/**
* @mixin \think\Model
*/
class UserLoginLogModel extends UserLoginLogBaseModel
{
}

View File

@@ -1,21 +0,0 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\UserBaseModel;
/**
* @mixin \think\Model
*/
class UserModel extends UserBaseModel
{
// 隐藏字段
protected $hidden = ['password', 'salt'];
// 用户名查询范围
public function scopeUsernameOrMobile($query, $username)
{
return $query->where('username', '=', $username)->whereOr('mobile', '=', $username);
}
}

View File

@@ -0,0 +1,46 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\VideoCategoryBaseModel;
use think\model\concern\SoftDelete;
/**
* 视频分类模型
* @mixin \think\Model
*/
class VideoCategoryModel extends VideoCategoryBaseModel
{
// 启用软删除
use SoftDelete;
// 软删除字段
protected $deleteTime = 'deleted_at';
// 关联视频模型
public function video()
{
return $this->hasMany(VideoModel::class, 'category_id', 'id');
}
// 分类名称搜索
public function searchNameAttr($query, $value)
{
if (empty($value)) {
return;
}
$query->where('name', 'like', '%' . $value . '%');
}
// 语言查询
public function scopeLanguage($query, $value)
{
$query->where('language_id', '=', $value);
}
// 显示状态查询
public function scopeIsShow($query, bool $value = true)
{
$query->where('is_show', '=', (int)$value);
}
}

View File

@@ -0,0 +1,71 @@
<?php
declare (strict_types = 1);
namespace app\admin\model\v1;
use app\common\model\VideoBaseModel;
use think\model\concern\SoftDelete;
/**
* 视频信息模型
* @mixin \think\Model
*/
class VideoModel extends VideoBaseModel
{
// 启用软删除
use SoftDelete;
// 软删除字段
protected $deleteTime = 'deleted_at';
// 关联分类
public function category()
{
return $this->belongsTo(VideoCategoryModel::class, 'category_id', 'id');
}
// 名称搜索器
public function searchNameAttr($query, $value, $data)
{
if (empty($value)) {
return;
}
$query->where('name', 'like', '%' . $value . '%');
}
// 搜索发布时间
public function searchCreatedAtAttr($query, $value, $data)
{
if (empty($value)) {
return;
}
if (is_array($value)) {
if (count($value) == 2) {
if ($value[0] == $value[1]) {
$value[1] = date('Y-m-d 23:59:59', strtotime($value[1]));
}
$query->whereBetweenTime('created_at', $value[0], $value[1]);
} else {
$query->whereTime('created_at', '>=', $value[0]);
}
}
}
// 语言查询
public function scopeLanguage($query, $value)
{
$query->where('language_id', '=', $value);
}
// 分类查询
public function scopeCategoryId($query, $value)
{
if (empty($value)) {
return;
}
if (is_array($value) || str_contains($value, ',')) {
$query->whereIn('category_id', $value);
return;
}
$query->where('category_id', '=', $value);
}
}

View File

@@ -12,20 +12,38 @@ use think\facade\Route;
// v1版本路由定义
Route::group('v1', function () {
// 获取系统信息
Route::group('system', function() {
// 获取系统信息
Route::get('info', 'System/info');
// 组装系统内页面URL
Route::get('urls', 'System/urls');
});
// 用户模块
Route::group('user', function () {
// 获取验证码
Route::get('captcha', 'Captcha/index')->middleware(\think\middleware\Throttle::class, [
'visit_rate' => '5/m',
'visit_rate' => '5/m',
'visit_fail_response' => function (\think\middleware\Throttle $throttle, \think\Request $request, int $wait_seconds) {
return \think\Response::create('您的操作过于频繁, 请在 ' . $wait_seconds . ' 秒后再试。')->code(429);
},
]);
// 登录接口
Route::post('login', 'Login/index');
Route::post('login', 'UserCenter/login');
// 登出接口
Route::get('logout', 'UserCenter/logout');
// 获取用户菜单权限
Route::get('{id}/menu', 'User/menu');
});
// 国家模块
Route::get('country/list', 'Country/list');
// 语言模块
Route::group('language', function () {
// 语言列表
@@ -38,7 +56,70 @@ Route::group('v1', function () {
// 图片管理
Route::group('images', function () {
// 图片上传
Route::post('/:module/upload', 'Images/upload');
Route::post('/:module/upload', 'Upload/image');
});
// 视频管理
Route::group('video', function () {
// 视频上传
Route::post('/:module/upload', 'Upload/video');
// 视频信息列表
Route::get('index', 'Video/index');
// 视频信息详情
Route::get('read/:id', 'Video/read');
// 视频信息添加
Route::post('save', 'Video/save');
// 视频信息更新
Route::put('update/:id', 'Video/update');
// 视频信息设置排序值
Route::post('sort/:id', 'Video/sort');
// 视频信息导出
Route::get('export', 'Video/export');
// 视频信息删除
Route::delete('delete/:id', 'Video/delete');
// 视频分类列表
Route::get('categorys', 'VideoCategory/list');
// 视频分类
Route::group('category', function () {
// 视频分类分页数据
Route::get('index', 'VideoCategory/index');
// 视频分类详情
Route::get('read/:id', 'VideoCategory/read');
// 视频分类添加
Route::post('save', 'VideoCategory/save');
// 视频分类更新
Route::put('update/:id', 'VideoCategory/update');
// 设置视频分类排序值
Route::post('sort/:id', 'VideoCategory/sort');
// 视频分类删除
Route::delete('delete/:id', 'VideoCategory/delete');
});
// 视频回收站
Route::group('trash', function () {
// 视频回收站分页列表
Route::get('index', 'VideoTrash/index');
// 视频回收站还原
Route::get('restore/:id', 'VideoTrash/restore');
// 视频回收站删除
Route::delete('delete/:id', 'VideoTrash/delete');
});
});
// 文章模块
@@ -55,6 +136,9 @@ Route::group('v1', function () {
// 文章更新
Route::put('update/:id', 'Article/update');
// 设置排序值
Route::post('sort/:id', 'Article/sort');
// 文章删除
Route::delete('delete/:id', 'Article/delete');
@@ -78,6 +162,9 @@ Route::group('v1', function () {
// 分类更新
Route::put('update/:id', 'ArticleCategory/update');
// 设置排序值
Route::post('sort/:id', 'ArticleCategory/sort');
// 分类删除
Route::delete('delete/:id', 'ArticleCategory/delete');
});
@@ -110,6 +197,50 @@ Route::group('v1', function () {
});
});
// 横幅管理
Route::group('banner', function() {
// 横幅(分类)列表
Route::get('list', 'Banner/list');
// 横幅(分类)分页
Route::get('index', 'Banner/index');
// 横幅详情
Route::get('read/:id', 'Banner/read');
// 横幅新增
Route::post('save', 'Banner/save');
// 横幅更新
Route::put('update/:id', 'Banner/update');
// 横幅删除
Route::delete('delete/:id', 'Banner/delete');
Route::group('items', function() {
// 横幅数据项分页
Route::get('index', 'BannerItem/index');
// 横幅数据项详情
Route::get('read/:id', 'BannerItem/read');
// 横幅数据项新增
Route::post('save', 'BannerItem/save');
// 横幅数据项更新
Route::put('update/:id', 'BannerItem/update');
// 设置数据项排序值
Route::post('sort/:id', 'BannerItem/sort');
// 横幅数据项导出
Route::get('export', 'BannerItem/export');
// 横幅数据项删除
Route::delete('delete/:id', 'BannerItem/delete');
});
});
// 产品模块
Route::group('product', function () {
// 产品分页列表
@@ -121,6 +252,9 @@ Route::group('v1', function () {
// 产品更新
Route::put('update/:id', 'Product/update');
// 设置排序值
Route::post('sort/:id', 'Product/sort');
// 上/下架操作
Route::get('updown_shelves/:id', 'Product/updownShelves');
@@ -131,7 +265,7 @@ Route::group('v1', function () {
Route::get('export', 'Product/export');
// 产品属性特征
Route::get('attrs', 'ProductAttr/index')->append(['scene' => 'list']);
Route::get('attrs', 'ProductAttr/index')->append(['scene' => 'all']);
// 产品属性管理
Route::group('attr', function () {
@@ -152,8 +286,12 @@ Route::group('v1', function () {
});
// 产品分类
Route::get('categorys', 'ProductCategory/index');
Route::group('category', function () {
// 分类列表
// tco分类树
Route::get('tco/tree', 'ProductTcoCategory/tree');
// 分类树
Route::get('index', 'ProductCategory/index');
// 分类详情
@@ -174,9 +312,316 @@ Route::group('v1', function () {
// 分类删除
Route::delete('delete/:id', 'ProductCategory/delete');
});
// 产品购买链接
Route::group('buypass', function () {
// 购买链接平台列表
Route::get('platforms', 'ProductPurchaseLink/platforms');
// 购买链接列表
Route::get('index', 'ProductPurchaseLink/index');
// 购买链接导入
Route::post('import', 'ProductPurchaseLink/import');
// 购买链接导出
Route::get('export', 'ProductPurchaseLink/export');
// 购买链接添加
Route::post('save', 'ProductPurchaseLink/save');
// 购买链接更新
Route::put('update/:id', 'ProductPurchaseLink/update');
});
// 产品回收站
Route::group('trash', function () {
// 产品回收站列表
Route::get('index', 'ProductTrash/index');
// 产品回收站还原
Route::get('restore/:id', 'ProductTrash/restore');
// 产品回收站删除
Route::delete('delete/:id', 'ProductTrash/delete');
});
});
// 附件(下载管理)
Route::group('attachment', function () {
// 附件(下载管理)上传
Route::post('/upload', 'Upload/attachment');
// 附件(下载管理)列表
Route::get('index', 'Attachment/index');
// 附件(下载管理)详情
Route::get('read/:id', 'Attachment/read');
// 附件(下载管理)新增
Route::post('save', 'Attachment/save');
// 附件(下载管理)更新
Route::put('update/:id', 'Attachment/update');
// 附件(下载管理)设置排序值
Route::post('sort/:id', 'Attachment/sort');
// 附件(下载管理)禁/启用
Route::get('enable/:id', 'Attachment/enable');
// 附件(下载管理)删除
Route::delete('delete/:id', 'Attachment/delete');
// 附件(下载管理)分类列表树
Route::get('tree', 'AttachmentCategory/tree');
// 附件(下载管理)分类
Route::group('category', function () {
// 附件(下载管理)分类树
Route::get('index', 'AttachmentCategory/index');
// 附件(下载管理)分类详情
Route::get('read/:id', 'AttachmentCategory/read');
// 附件(下载管理)分类新增
Route::post('save', 'AttachmentCategory/save');
// 附件(下载管理)分类更新
Route::put('update/:id','AttachmentCategory/update');
// 附件(下载管理)分类设置排序值
Route::post('sort/:id', 'AttachmentCategory/sort');
// 附件(下载管理)分类删除
Route::delete('delete/:id', 'AttachmentCategory/delete');
});
// 附件(下载管理)回收站
Route::group('trash', function () {
// 附件(下载管理)回收站列表
Route::get('index', 'AttachmentTrash/index');
// 附件(下载管理)回收站还原
Route::get('restore/:id', 'AttachmentTrash/restore');
// 附件(下载管理)回收站删除
Route::delete('delete/:id', 'AttachmentTrash/delete');
});
});
// 用户管理
Route::group('user', function() {
// 用户分页
Route::get('index', 'User/index');
// 用户详情
Route::get('read/:id', 'User/read');
// 用户新增
Route::post('save', 'User/save');
// 用户更新
Route::put('update/:id', 'User/update');
// 用户删除
Route::delete('delete/:id', 'User/delete');
});
// 角色管理
Route::get('roles', 'Role/index')->append(['scene' => 'all']); // 角色列表
Route::group('role', function() {
// 角色分页
Route::get('index', 'Role/index');
// 角色详情
Route::get('read/:id', 'Role/read');
// 角色新增
Route::post('save', 'Role/save');
// 角色更新
Route::put('update/:id', 'Role/update');
// 角色删除
Route::delete('delete/:id', 'Role/delete');
});
// 菜单管理
// 菜单列表
Route::get('menus', 'Menu/list');
Route::group('menu', function() {
// 菜单分页
Route::get('index', 'Menu/index');
// 菜单详情
Route::get('read/:id', 'Menu/read');
// 菜单新增
Route::post('save', 'Menu/save');
// 菜单更新
Route::put('update/:id', 'Menu/update');
// 菜单导入
Route::post('import', 'Menu/import');
// 菜单导出
Route::get('export', 'Menu/export');
// 设置排序值
Route::post('sort/:id', 'Menu/sort');
// 菜单删除
Route::delete('delete/:id', 'Menu/delete');
});
// 导航管理
Route::group('navigation', function() {
// 列表
Route::get('list', 'Navigation/list');
// 分页
Route::get('index', 'Navigation/index');
// 导航详情
Route::get('read/:id', 'Navigation/read');
// 导航新增
Route::post('save', 'Navigation/save');
// 导航更新
Route::put('update/:id', 'Navigation/update');
// 导航删除
Route::delete('delete/:id', 'Navigation/delete');
Route::group('items', function() {
// 导航分页
Route::get('index', 'NavigationItem/index');
// 导航详情
Route::get('read/:id', 'NavigationItem/read');
// 导航新增
Route::post('save', 'NavigationItem/save');
// 导航更新
Route::put('update/:id', 'NavigationItem/update');
// 导航设置排序值
Route::post('sort/:id', 'NavigationItem/sort');
// 导航删除
Route::delete('delete/:id', 'NavigationItem/delete');
});
});
// 问答管理
Route::group('faq', function() {
// 问答分页
Route::get('index', 'Faq/index');
// 问答详情
Route::get('read/:id', 'Faq/read');
// 问答新增
Route::post('save', 'Faq/save');
// 问答更新
Route::put('update/:id', 'Faq/update');
// 设置问答排序值
Route::post('sort/:id', 'Faq/sort');
// 问答删除
Route::delete('delete/:id', 'Faq/delete');
});
// 反馈管理 - 留言记录(联系我们)
Route::group('leavemsg', function() {
// 留言记录(联系我们)分页
Route::get('index', 'LeaveMessage/index');
// 留言记录(联系我们)导出
Route::get('export', 'LeaveMessage/export');
});
// 反馈管理 - 批量采购底盘列表
Route::group('bp/inquiry', function() {
// 批量采购询盘可选品类
Route::get('interested', 'BulkPurchaseInquiry/interested');
// 批量采购底盘列表分页
Route::get('index', 'BulkPurchaseInquiry/index');
// 批量采购底盘列表导出
Route::get('export', 'BulkPurchaseInquiry/export');
});
// 反馈管理 - 代理商申请列表
Route::group('agent', function() {
// 代理商企业规模类型
Route::get('enterprise_size_types', 'Agent/enterpriseSizeTypes');
// 代理商申请列表分页
Route::get('index', 'Agent/index');
// 代理商申请列表导出
Route::get('export', 'Agent/export');
});
// 反馈管理 - 产品询盘列表
Route::get('product/inquiry/index', 'ProductInquiry/index');
// 配置项列表
Route::group('config', function() {
// 配置分组
Route::get('groups', 'SysConfig/groups');
// 配置类型
Route::get('types', 'SysConfig/types');
// 配置项分页
Route::get('index', 'SysConfig/index');
// 配置项详情
Route::get('read/:id', 'SysConfig/read');
// 配置项新增
Route::post('save', 'SysConfig/save');
// 配置项更新
Route::put('update/:id', 'SysConfig/update');
// 配置项删除
Route::delete('delete/:id', 'SysConfig/delete');
});
// 站点配置
Route::group('site', function() {
Route::group('config', function() {
// 站点配置项
Route::get('index', 'SiteConfig/index');
// 站点配置更新
Route::put('update', 'SiteConfig/update');
});
});
// 日志管理
Route::group('logs', function() {
// 操作日志列表
Route::get('operate/index', 'OperateLog/index');
});
})->prefix('v1.');
// 接收产品目录同步数据
Route::group('receive_sync', function () {
Route::post('category', 'ReceiveProductSync/category');
Route::post('product', 'ReceiveProductSync/product');
});
Route::miss(function() {
return '404 Not Found!';
});

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